FORS Pipeline Reference Manual 4.9.20
fors_pmos_calib.c
00001 /* $Id: fors_pmos_calib.c,v 1.37 2013/02/28 15:15:10 cgarcia Exp $
00002  *
00003  * This file is part of the FORS Data Reduction Pipeline
00004  * Copyright (C) 2002-2010 European Southern Observatory
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00019  */
00020 
00021 /*
00022  * $Author: cgarcia $
00023  * $Date: 2013/02/28 15:15:10 $
00024  * $Revision: 1.37 $
00025  * $Name: fors-4_9_20 $
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 
00032 #include <math.h>
00033 #include <string.h>
00034 #include <ctype.h>
00035 #include <cpl.h>
00036 #include <moses.h>
00037 #include <fors_stack.h>
00038 #include <fors_dfs.h>
00039 #include <fors_header.h>
00040 
00041 #define OFFSET    50
00042 #define TOLERANCE 10
00043 
00044 static int fors_pmos_calib_create(cpl_plugin *);
00045 static int fors_pmos_calib_exec(cpl_plugin *);
00046 static int fors_pmos_calib_destroy(cpl_plugin *);
00047 static int fors_pmos_calib(cpl_parameterlist *, cpl_frameset *);
00048 
00049 static char fors_pmos_calib_description[] =
00050 "This recipe is used to identify reference lines on PMOS arc lamp\n"
00051 "exposures, and trace the spectral edges on the corresponding flat field\n"
00052 "exposures. This information is used to determine the spectral extraction\n"
00053 "mask to be applied in the scientific data reduction, performed with the\n"
00054 "recipe fors_science.\n"
00055 "This recipe accepts both FORS1 and FORS2 frames. The input arc lamps and\n"
00056 "flat field exposures are assumed to be obtained quasi-simultaneously, so\n"
00057 "that they would be described by exactly the same instrument distortions.\n"
00058 "A line catalog must be specified, containing the wavelengths of the\n"
00059 "reference arc lamp lines used for the wavelength calibration. A grism\n"
00060 "table (typically depending on the instrument mode, and in particular on\n"
00061 "the grism used) may also be specified: this table contains a default\n"
00062 "recipe parameter setting to control the way spectra are extracted for\n"
00063 "a specific instrument mode, as it is used for automatic run of the\n"
00064 "pipeline on Paranal and in Garching. If this table is specified, it\n"
00065 "will modify the default recipe parameter setting, with the exception of\n"
00066 "those parameters which have been explicitly modifyed on the command line.\n"
00067 "If a grism table is not specified, the input recipe parameters values\n"
00068 "will always be read from the command line, or from an esorex configuration\n"
00069 "file if present, or from their generic default values (that are rarely\n"
00070 "meaningful). Finally a master bias frame must be input to this recipe.\n" 
00071 "The products SPECTRA_DETECTION_PMOS, SLIT_MAP_PMOS, and DISP_RESIDUALS_PMOS,\n"
00072 "are just created if the --check parameter is set to true.\n"
00073 "The MASTER_DISTORTION_TABLE is marked as required, but it is not so if all\n"
00074 "slits have different offsets, and in the case of FORS1 observations made\n"
00075 "with the old TK2048EB4-1 1604 chip read in windowed mode (2048x400)\n\n" 
00076 "Input files:\n\n"
00077 "  DO category:              Type:       Explanation:          Required:\n"
00078 "  SCREEN_FLAT_PMOS          Raw         Flat field exposures     Y\n"
00079 "  LAMP_PMOS                 Raw         Arc lamp exposure        Y\n"
00080 "  MASTER_BIAS or BIAS       Calib       Bias frame               Y\n"
00081 "  MASTER_LINECAT            Calib       Line catalog             Y\n"
00082 "  GRISM_TABLE               Calib       Grism table              .\n"
00083 "  MASTER_DISTORTION_TABLE   Calib       Master distortions table Y\n\n"
00084 "Output files:\n\n" 
00085 "  DO category:              Data type:  Explanation:\n"
00086 "  MASTER_SCREEN_FLAT_PMOS   FITS image  Combined (sum) flat field\n"
00087 "  MASTER_NORM_FLAT_PMOS     FITS image  Normalised flat field\n"
00088 "  MAPPED_SCREEN_FLAT_PMOS   FITS image  Wavelength calibrated flat field\n"
00089 "  MAPPED_NORM_FLAT_PMOS     FITS image  Wavelength calibrated normalised flat\n"
00090 "  REDUCED_LAMP_PMOS         FITS image  Wavelength calibrated arc spectrum\n"
00091 "  DISP_COEFF_PMOS           FITS table  Inverse dispersion coefficients\n"
00092 "  DISP_RESIDUALS_PMOS       FITS image  Residuals in wavelength calibration\n"
00093 "  DISP_RESIDUALS_TABLE_PMOS FITS table  Residuals in wavelength calibration\n"
00094 "  DELTA_IMAGE_PMOS          FITS image  Offset vs linear wavelength calib\n"
00095 "  WAVELENGTH_MAP_PMOS       FITS image  Wavelength for each pixel on CCD\n"
00096 "  SPECTRA_DETECTION_PMOS    FITS image  Check for preliminary detection\n"
00097 "  SLIT_MAP_PMOS             FITS image  Map of central wavelength on CCD\n"
00098 "  CURV_TRACES_PMOS          FITS table  Spectral curvature traces\n"
00099 "  CURV_COEFF_PMOS           FITS table  Spectral curvature coefficients\n"
00100 "  SPATIAL_MAP_PMOS          FITS image  Spatial position along slit on CCD\n"
00101 "  SPECTRAL_RESOLUTION_PMOS  FITS table  Resolution at reference arc lines\n"
00102 "  SLIT_LOCATION_PMOS        FITS table  Slits on product frames and CCD\n\n";
00103 
00104 #define fors_pmos_calib_exit(message)              \
00105 {                                             \
00106 if (message) cpl_msg_error(recipe, message);  \
00107 cpl_free(instrume);                           \
00108 cpl_free(fiterror);                           \
00109 cpl_free(fitlines);                           \
00110 cpl_image_delete(bias);                       \
00111 cpl_image_delete(master_bias);                \
00112 cpl_image_delete(coordinate);                 \
00113 cpl_image_delete(checkwave);                  \
00114 cpl_image_delete(flat);                       \
00115 cpl_image_delete(master_flat);                \
00116 cpl_image_delete(added_flat);                 \
00117 cpl_image_delete(norm_flat);                  \
00118 cpl_image_delete(mapped_flat);                \
00119 cpl_image_delete(mapped_nflat);               \
00120 cpl_image_delete(rainbow);                    \
00121 cpl_image_delete(rectified);                  \
00122 cpl_image_delete(residual);                   \
00123 cpl_image_delete(smo_flat);                   \
00124 cpl_image_delete(spatial);                    \
00125 cpl_image_delete(spectra);                    \
00126 cpl_image_delete(wavemap);                    \
00127 cpl_image_delete(delta);                      \
00128 cpl_image_delete(rect_flat);                  \
00129 cpl_image_delete(rect_nflat);                 \
00130 cpl_image_delete(mapped_flat);                \
00131 cpl_image_delete(mapped_nflat);               \
00132 cpl_mask_delete(refmask);                     \
00133 cpl_propertylist_delete(header);              \
00134 cpl_propertylist_delete(save_header);         \
00135 cpl_propertylist_delete(qclist);              \
00136 cpl_table_delete(grism_table);                \
00137 cpl_table_delete(idscoeff);                   \
00138 cpl_table_delete(idscoeff_all);               \
00139 cpl_table_delete(restable);                   \
00140 cpl_table_delete(maskslits);                  \
00141 cpl_table_delete(overscans);                  \
00142 cpl_table_delete(traces);                     \
00143 cpl_table_delete(polytraces);                 \
00144 cpl_table_delete(slits);                      \
00145 cpl_table_delete(restab);                     \
00146 cpl_table_delete(global);                     \
00147 cpl_table_delete(wavelengths);                \
00148 cpl_vector_delete(lines);                     \
00149 cpl_msg_indent_less();                        \
00150 return -1;                                    \
00151 }
00152 
00153 #define fors_pmos_calib_exit_memcheck(message)       \
00154 {                                               \
00155 if (message) cpl_msg_info(recipe, message);     \
00156 printf("free instrume (%p)\n", instrume);       \
00157 cpl_free(instrume);                             \
00158 printf("free pipefile (%p)\n", pipefile);       \
00159 cpl_free(pipefile);                             \
00160 printf("free fiterror (%p)\n", fiterror);       \
00161 cpl_free(fiterror);                             \
00162 printf("free fitlines (%p)\n", fitlines);       \
00163 cpl_free(fitlines);                             \
00164 printf("free bias (%p)\n", bias);               \
00165 cpl_image_delete(bias);                         \
00166 printf("free master_bias (%p)\n", master_bias); \
00167 cpl_image_delete(master_bias);                  \
00168 printf("free coordinate (%p)\n", coordinate);   \
00169 cpl_image_delete(coordinate);                   \
00170 printf("free checkwave (%p)\n", checkwave);     \
00171 cpl_image_delete(checkwave);                    \
00172 printf("free flat (%p)\n", flat);               \
00173 cpl_image_delete(flat);                         \
00174 printf("free master_flat (%p)\n", master_flat); \
00175 cpl_image_delete(master_flat);                  \
00176 printf("free norm_flat (%p)\n", norm_flat);     \
00177 cpl_image_delete(norm_flat);                    \
00178 printf("free mapped_flat (%p)\n", mapped_flat); \
00179 cpl_image_delete(mapped_flat);                  \
00180 printf("free mapped_nflat (%p)\n", mapped_nflat); \
00181 cpl_image_delete(mapped_nflat);                 \
00182 printf("free rainbow (%p)\n", rainbow);         \
00183 cpl_image_delete(rainbow);                      \
00184 printf("free rectified (%p)\n", rectified);     \
00185 cpl_image_delete(rectified);                    \
00186 printf("free residual (%p)\n", residual);       \
00187 cpl_image_delete(residual);                     \
00188 printf("free smo_flat (%p)\n", smo_flat);       \
00189 cpl_image_delete(smo_flat);                     \
00190 printf("free spatial (%p)\n", spatial);         \
00191 cpl_image_delete(spatial);                      \
00192 printf("free spectra (%p)\n", spectra);         \
00193 cpl_image_delete(spectra);                      \
00194 printf("free wavemap (%p)\n", wavemap);         \
00195 cpl_image_delete(wavemap);                      \
00196 printf("free delta (%p)\n", delta);             \
00197 cpl_image_delete(delta);                        \
00198 printf("free rect_flat (%p)\n", rect_flat);     \
00199 cpl_image_delete(rect_flat);                    \
00200 printf("free rect_nflat (%p)\n", rect_nflat);   \
00201 cpl_image_delete(rect_nflat);                   \
00202 printf("free refmask (%p)\n", refmask);         \
00203 cpl_mask_delete(refmask);                       \
00204 printf("free header (%p)\n", header);           \
00205 cpl_propertylist_delete(header);                \
00206 printf("free save_header (%p)\n", save_header); \
00207 cpl_propertylist_delete(save_header);           \
00208 printf("free qclist (%p)\n", qclist);           \
00209 cpl_propertylist_delete(qclist);                \
00210 printf("free grism_table (%p)\n", grism_table); \
00211 cpl_table_delete(grism_table);                  \
00212 printf("free idscoeff (%p)\n", idscoeff);       \
00213 cpl_table_delete(idscoeff);                     \
00214 printf("free idscoeff_all (%p)\n", idscoeff_all);  \
00215 cpl_table_delete(idscoeff_all);                 \
00216 printf("free restable (%p)\n", restable);       \
00217 cpl_table_delete(restable);                     \
00218 printf("free maskslits (%p)\n", maskslits);     \
00219 cpl_table_delete(maskslits);                    \
00220 printf("free overscans (%p)\n", overscans);     \
00221 cpl_table_delete(overscans);                    \
00222 printf("free traces (%p)\n", traces);           \
00223 cpl_table_delete(traces);                       \
00224 printf("free polytraces (%p)\n", polytraces);   \
00225 cpl_table_delete(polytraces);                   \
00226 printf("free slits (%p)\n", slits);             \
00227 cpl_table_delete(slits);                        \
00228 printf("free restab (%p)\n", restab);           \
00229 cpl_table_delete(restab);                       \
00230 printf("free global (%p)\n", global);           \
00231 cpl_table_delete(global);                       \
00232 printf("free wavelengths (%p)\n", wavelengths); \
00233 cpl_table_delete(wavelengths);                  \
00234 printf("free lines (%p)\n", lines);             \
00235 cpl_vector_delete(lines);                       \
00236 cpl_msg_indent_less();                          \
00237 return 0;                                       \
00238 }
00239 
00240 
00252 int cpl_plugin_get_info(cpl_pluginlist *list)
00253 {
00254     cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe );
00255     cpl_plugin *plugin = &recipe->interface;
00256 
00257     cpl_plugin_init(plugin,
00258                     CPL_PLUGIN_API,
00259                     FORS_BINARY_VERSION,
00260                     CPL_PLUGIN_TYPE_RECIPE,
00261                     "fors_pmos_calib",
00262                     "Determination of the extraction mask",
00263                     fors_pmos_calib_description,
00264                     "Carlo Izzo",
00265                     PACKAGE_BUGREPORT,
00266     "This file is currently part of the FORS Instrument Pipeline\n"
00267     "Copyright (C) 2002-2010 European Southern Observatory\n\n"
00268     "This program is free software; you can redistribute it and/or modify\n"
00269     "it under the terms of the GNU General Public License as published by\n"
00270     "the Free Software Foundation; either version 2 of the License, or\n"
00271     "(at your option) any later version.\n\n"
00272     "This program is distributed in the hope that it will be useful,\n"
00273     "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
00274     "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
00275     "GNU General Public License for more details.\n\n"
00276     "You should have received a copy of the GNU General Public License\n"
00277     "along with this program; if not, write to the Free Software Foundation,\n"
00278     "Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n",
00279                     fors_pmos_calib_create,
00280                     fors_pmos_calib_exec,
00281                     fors_pmos_calib_destroy);
00282 
00283     cpl_pluginlist_append(list, plugin);
00284     
00285     return 0;
00286 }
00287 
00288 
00299 static int fors_pmos_calib_create(cpl_plugin *plugin)
00300 {
00301     cpl_recipe    *recipe;
00302     cpl_parameter *p;
00303 
00304 
00305     /* 
00306      * Check that the plugin is part of a valid recipe 
00307      */
00308 
00309     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00310         recipe = (cpl_recipe *)plugin;
00311     else 
00312         return -1;
00313 
00314     /* 
00315      * Create the parameters list in the cpl_recipe object 
00316      */
00317 
00318     recipe->parameters = cpl_parameterlist_new(); 
00319 
00320 
00321     /*
00322      * Dispersion
00323      */
00324 
00325     p = cpl_parameter_new_value("fors.fors_pmos_calib.dispersion",
00326                                 CPL_TYPE_DOUBLE,
00327                                 "Expected spectral dispersion (Angstrom/pixel)",
00328                                 "fors.fors_pmos_calib",
00329                                 0.0);
00330     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dispersion");
00331     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00332     cpl_parameterlist_append(recipe->parameters, p);
00333 
00334     /*
00335      * Peak detection level
00336      */
00337 
00338     p = cpl_parameter_new_value("fors.fors_pmos_calib.peakdetection",
00339                                 CPL_TYPE_DOUBLE,
00340                                 "Initial peak detection threshold (ADU)",
00341                                 "fors.fors_pmos_calib",
00342                                 0.0);
00343     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "peakdetection");
00344     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00345     cpl_parameterlist_append(recipe->parameters, p);
00346 
00347     /* 
00348      * Degree of wavelength calibration polynomial
00349      */
00350 
00351     p = cpl_parameter_new_value("fors.fors_pmos_calib.wdegree",
00352                                 CPL_TYPE_INT,
00353                                 "Degree of wavelength calibration polynomial",
00354                                 "fors.fors_pmos_calib",
00355                                 0);
00356     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wdegree");
00357     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00358     cpl_parameterlist_append(recipe->parameters, p);
00359 
00360     /*
00361      * Reference lines search radius
00362      */
00363 
00364     p = cpl_parameter_new_value("fors.fors_pmos_calib.wradius",
00365                                 CPL_TYPE_INT,
00366                                 "Search radius if iterating pattern-matching "
00367                                 "with first-guess method",
00368                                 "fors.fors_pmos_calib",
00369                                 4);
00370     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wradius");
00371     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00372     cpl_parameterlist_append(recipe->parameters, p);
00373 
00374     /*
00375      * Rejection threshold in dispersion relation polynomial fitting
00376      */
00377 
00378     p = cpl_parameter_new_value("fors.fors_pmos_calib.wreject",
00379                                 CPL_TYPE_DOUBLE,
00380                                 "Rejection threshold in dispersion "
00381                                 "relation fit (pixel)",
00382                                 "fors.fors_pmos_calib",
00383                                 0.7);
00384     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wreject");
00385     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00386     cpl_parameterlist_append(recipe->parameters, p);
00387 
00388     /*
00389      * Line catalog table column containing the reference wavelengths
00390      */
00391 
00392     p = cpl_parameter_new_value("fors.fors_pmos_calib.wcolumn",
00393                                 CPL_TYPE_STRING,
00394                                 "Name of line catalog table column "
00395                                 "with wavelengths",
00396                                 "fors.fors_pmos_calib",
00397                                 "WLEN");
00398     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcolumn");
00399     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00400     cpl_parameterlist_append(recipe->parameters, p);
00401 
00402     /*
00403      * Degree of spectral curvature polynomial
00404      */
00405 
00406     p = cpl_parameter_new_value("fors.fors_pmos_calib.cdegree",
00407                                 CPL_TYPE_INT,
00408                                 "Degree of spectral curvature polynomial",
00409                                 "fors.fors_pmos_calib",
00410                                 0);
00411     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "cdegree");
00412     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00413     cpl_parameterlist_append(recipe->parameters, p);
00414 
00415     /*
00416      * Curvature solution interpolation
00417      */
00418  
00419     p = cpl_parameter_new_value("fors.fors_pmos_calib.cmode",
00420                                 CPL_TYPE_INT,
00421                                 "Interpolation mode of curvature solution "
00422                                 "(0 = no "
00423                                 "interpolation, 1 = fill gaps, 2 = global "
00424                                 "model)",
00425                                 "fors.fors_pmos_calib",
00426                                 1);
00427     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "cmode");
00428     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00429     cpl_parameterlist_append(recipe->parameters, p);
00430 
00431     /*
00432      * Start wavelength for spectral extraction
00433      */
00434 
00435     p = cpl_parameter_new_value("fors.fors_pmos_calib.startwavelength",
00436                                 CPL_TYPE_DOUBLE,
00437                                 "Start wavelength in spectral extraction",
00438                                 "fors.fors_pmos_calib",
00439                                 0.0);
00440     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "startwavelength");
00441     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00442     cpl_parameterlist_append(recipe->parameters, p);
00443 
00444     /*
00445      * End wavelength for spectral extraction
00446      */
00447 
00448     p = cpl_parameter_new_value("fors.fors_pmos_calib.endwavelength",
00449                                 CPL_TYPE_DOUBLE,
00450                                 "End wavelength in spectral extraction",
00451                                 "fors.fors_pmos_calib",
00452                                 0.0);
00453     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "endwavelength");
00454     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00455     cpl_parameterlist_append(recipe->parameters, p);
00456 
00457     /*
00458      * Flat field frames stack parameters
00459      */
00460  
00461     fors_stack_define_parameters(recipe->parameters, "fors.fors_pmos_calib", 
00462                                  "average");
00463 
00464     /*
00465      * Degree of flat field fitting polynomial along dispersion direction
00466      */
00467 
00468     p = cpl_parameter_new_value("fors.fors_pmos_calib.ddegree",
00469                                 CPL_TYPE_INT,
00470                                 "Degree of flat field fitting polynomial "
00471                                 "along dispersion direction",
00472                                 "fors.fors_pmos_calib",
00473                                 -1);
00474     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ddegree");
00475     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00476     cpl_parameterlist_append(recipe->parameters, p);
00477 
00478     /*
00479      * Smooth box radius for flat field along dispersion direction
00480      */
00481 
00482     p = cpl_parameter_new_value("fors.fors_pmos_calib.dradius",
00483                                 CPL_TYPE_INT,
00484                                 "Smooth box radius for flat field along "
00485                                 "dispersion direction",
00486                                 "fors.fors_pmos_calib",
00487                                 10);
00488     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dradius");
00489     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00490     cpl_parameterlist_append(recipe->parameters, p);
00491 
00492     /*
00493      * Computation of QC1 parameters
00494      */
00495 
00496     p = cpl_parameter_new_value("fors.fors_pmos_calib.qc",
00497                                 CPL_TYPE_BOOL,
00498                                 "Compute QC1 parameters",
00499                                 "fors.fors_pmos_calib",
00500                                 TRUE);
00501     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "qc");
00502     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00503     cpl_parameterlist_append(recipe->parameters, p);
00504 
00505     /*
00506      * Create check products
00507      */
00508 
00509     p = cpl_parameter_new_value("fors.fors_pmos_calib.check",
00510                                 CPL_TYPE_BOOL,
00511                                 "Create intermediate products",
00512                                 "fors.fors_pmos_calib",
00513                                 FALSE);
00514     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "check");
00515     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00516     cpl_parameterlist_append(recipe->parameters, p);
00517 
00518     return 0;
00519 }
00520 
00521 
00530 static int fors_pmos_calib_exec(cpl_plugin *plugin)
00531 {
00532     cpl_recipe *recipe;
00533     
00534     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00535         recipe = (cpl_recipe *)plugin;
00536     else 
00537         return -1;
00538 
00539     return fors_pmos_calib(recipe->parameters, recipe->frames);
00540 }
00541 
00542 
00551 static int fors_pmos_calib_destroy(cpl_plugin *plugin)
00552 {
00553     cpl_recipe *recipe;
00554     
00555     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00556         recipe = (cpl_recipe *)plugin;
00557     else 
00558         return -1;
00559 
00560     cpl_parameterlist_delete(recipe->parameters); 
00561 
00562     return 0;
00563 }
00564 
00565 
00575 static int fors_pmos_calib(cpl_parameterlist *parlist, cpl_frameset *frameset)
00576 {
00577 
00578     const char *recipe = "fors_pmos_calib";
00579 
00580 
00581     /*
00582      * Input parameters
00583      */
00584 
00585     double      dispersion;
00586     double      peakdetection;
00587     int         wdegree;
00588     int         wradius;
00589     double      wreject;
00590     const char *wcolumn;
00591     int         cdegree;
00592     int         cmode;
00593     double      startwavelength;
00594     double      endwavelength;
00595     int         ddegree;
00596     int         dradius;
00597     int         qc;
00598     int         check;
00599     const char *stack_method;
00600     int         min_reject;
00601     int         max_reject;
00602     double      klow;
00603     double      khigh;
00604     int         kiter;
00605 
00606 
00607     /*
00608      * CPL objects
00609      */
00610 
00611     cpl_imagelist    *biases       = NULL;
00612     cpl_image        *bias         = NULL;
00613     cpl_image        *master_bias  = NULL;
00614     cpl_image        *multi_bias   = NULL;
00615     cpl_image        *flat         = NULL;
00616     cpl_image        *master_flat  = NULL;
00617     cpl_image        *added_flat   = NULL;
00618     cpl_image        *trace_flat   = NULL;
00619     cpl_image        *smo_flat     = NULL;
00620     cpl_image        *norm_flat    = NULL;
00621     cpl_image        *spectra      = NULL;
00622     cpl_image        *wavemap      = NULL;
00623     cpl_image        *delta        = NULL;
00624     cpl_image        *residual     = NULL;
00625     cpl_image        *checkwave    = NULL;
00626     cpl_image        *rectified    = NULL;
00627     cpl_image        *dummy        = NULL;
00628     cpl_image        *add_dummy    = NULL;
00629     cpl_image        *refimage     = NULL;
00630     cpl_image        *coordinate   = NULL;
00631     cpl_image        *rainbow      = NULL;
00632     cpl_image        *spatial      = NULL;
00633     cpl_image        *rect_flat    = NULL;
00634     cpl_image        *rect_nflat   = NULL;
00635     cpl_image        *mapped_flat  = NULL;
00636     cpl_image        *mapped_nflat = NULL;
00637 
00638     cpl_mask         *refmask      = NULL;
00639 
00640     cpl_table        *grism_table  = NULL;
00641     cpl_table        *overscans    = NULL;
00642     cpl_table        *wavelengths  = NULL;
00643     cpl_table        *idscoeff     = NULL;
00644     cpl_table        *idscoeff_all = NULL;
00645     cpl_table        *restable     = NULL;
00646     cpl_table        *slits        = NULL;
00647     cpl_table        *positions    = NULL;
00648     cpl_table        *maskslits    = NULL;
00649     cpl_table        *traces       = NULL;
00650     cpl_table        *polytraces   = NULL;
00651     cpl_table        *restab       = NULL;
00652     cpl_table        *global       = NULL;
00653 
00654     cpl_vector       *lines        = NULL;
00655 
00656     cpl_propertylist *header_dist  = NULL;
00657     cpl_propertylist *header       = NULL;
00658     cpl_propertylist *save_header  = NULL;
00659     cpl_propertylist *qclist       = NULL;
00660 
00661     /*
00662      * Auxiliary variables
00663      */
00664 
00665     char    version[80];
00666     const char   *arc_tag;
00667     const char   *flat_tag;
00668     const char   *master_screen_flat_tag;
00669     const char   *master_norm_flat_tag;
00670     const char   *reduced_lamp_tag;
00671     const char   *disp_residuals_tag;
00672     const char   *disp_coeff_tag;
00673     const char   *wavelength_map_tag;
00674     const char   *spectra_detection_tag;
00675     const char   *spectral_resolution_tag;
00676     const char   *slit_map_tag;
00677     const char   *curv_traces_tag;
00678     const char   *curv_coeff_tag;
00679     const char   *spatial_map_tag;
00680     const char   *slit_location_tag;
00681     const char   *master_distortion_tag = "MASTER_DISTORTION_TABLE";
00682     const char   *disp_residuals_table_tag;
00683     const char   *delta_image_tag;
00684     const char   *mapped_screen_flat_tag;
00685     const char   *mapped_norm_flat_tag;
00686     const char   *keyname;
00687     int     pmos;
00688     int     same_offset = 0;
00689     int     nslits;
00690     float  *data;
00691     double *xpos;
00692     double  mxpos;
00693     double  mean_rms;
00694     double  mean_rms_err;
00695     double  alltime;
00696     int     nflats;
00697     int     nbias;
00698     int     nlines;
00699     int     rebin, rebin_dist;
00700     double *line;
00701     double *fiterror = NULL;
00702     int    *fitlines = NULL;
00703     int     nx, ny;
00704     double  reference;
00705     double  gain;
00706     int     ccd_xsize, ccd_ysize;
00707     int     i, j;
00708 
00709     char   *instrume = NULL;
00710 
00711     /*
00712      * Variables just related to bagoo
00713      */
00714 
00715     int     bagoo = 0;
00716     int     doit = 0;
00717     double  blevel = 0.0;
00718     double  ron = 0.0;
00719 
00720     snprintf(version, 80, "%s-%s", PACKAGE, PACKAGE_VERSION);
00721 
00722     cpl_msg_set_indentation(2);
00723 
00724     if (dfs_files_dont_exist(frameset))
00725         fors_pmos_calib_exit(NULL);
00726 
00727     fors_dfs_set_groups(frameset);
00728 
00729     /* 
00730      * Get configuration parameters
00731      */
00732 
00733     cpl_msg_info(recipe, "Recipe %s configuration parameters:", recipe);
00734     cpl_msg_indent_more();
00735 
00736     if (cpl_frameset_count_tags(frameset, "GRISM_TABLE") > 1)
00737         fors_pmos_calib_exit("Too many in input: GRISM_TABLE");
00738 
00739     grism_table = dfs_load_table(frameset, "GRISM_TABLE", 1);
00740 
00741     dispersion = dfs_get_parameter_double(parlist, 
00742                     "fors.fors_pmos_calib.dispersion", grism_table);
00743 
00744     if (dispersion <= 0.0)
00745         fors_pmos_calib_exit("Invalid spectral dispersion value");
00746 
00747     peakdetection = dfs_get_parameter_double(parlist, 
00748                     "fors.fors_pmos_calib.peakdetection", grism_table);
00749     if (peakdetection <= 0.0)
00750         fors_pmos_calib_exit("Invalid peak detection level");
00751 
00752     wdegree = dfs_get_parameter_int(parlist, 
00753                     "fors.fors_pmos_calib.wdegree", grism_table);
00754 
00755     if (wdegree < 1)
00756         fors_pmos_calib_exit("Invalid polynomial degree");
00757 
00758     if (wdegree > 5)
00759         fors_pmos_calib_exit("Max allowed polynomial degree is 5");
00760 
00761     wradius = dfs_get_parameter_int(parlist, 
00762                                     "fors.fors_pmos_calib.wradius", NULL);
00763 
00764     if (wradius < 0)
00765         fors_pmos_calib_exit("Invalid search radius");
00766 
00767     wreject = dfs_get_parameter_double(parlist, 
00768                                        "fors.fors_pmos_calib.wreject", NULL);
00769 
00770     if (wreject <= 0.0)
00771         fors_pmos_calib_exit("Invalid rejection threshold");
00772 
00773     wcolumn = dfs_get_parameter_string(parlist, 
00774                                        "fors.fors_pmos_calib.wcolumn", NULL);
00775 
00776     cdegree = dfs_get_parameter_int(parlist, 
00777                     "fors.fors_pmos_calib.cdegree", grism_table);
00778 
00779     if (cdegree < 1)
00780         fors_pmos_calib_exit("Invalid polynomial degree");
00781 
00782     if (cdegree > 5)
00783         fors_pmos_calib_exit("Max allowed polynomial degree is 5");
00784 
00785     cmode = dfs_get_parameter_int(parlist, "fors.fors_pmos_calib.cmode", NULL);
00786 
00787     if (cmode < 0 || cmode > 2)
00788         fors_pmos_calib_exit("Invalid curvature solution interpolation mode");
00789 
00790     startwavelength = dfs_get_parameter_double(parlist, 
00791                     "fors.fors_pmos_calib.startwavelength", grism_table);
00792     if (startwavelength > 1.0)
00793         if (startwavelength < 3000.0 || startwavelength > 13000.0)
00794             fors_pmos_calib_exit("Invalid wavelength");
00795 
00796     endwavelength = dfs_get_parameter_double(parlist, 
00797                     "fors.fors_pmos_calib.endwavelength", grism_table);
00798     if (endwavelength > 1.0) {
00799         if (endwavelength < 3000.0 || endwavelength > 13000.0)
00800             fors_pmos_calib_exit("Invalid wavelength");
00801         if (startwavelength < 1.0)
00802             fors_pmos_calib_exit("Invalid wavelength interval");
00803     }
00804 
00805     if (startwavelength > 1.0)
00806         if (endwavelength - startwavelength <= 0.0)
00807             fors_pmos_calib_exit("Invalid wavelength interval");
00808 
00809     stack_method = dfs_get_parameter_string(parlist,
00810                                             "fors.fors_pmos_calib.stack_method",
00811                                             NULL);
00812 
00813     if (strcmp(stack_method, "minmax") == 0) {
00814         min_reject = dfs_get_parameter_int(parlist,
00815                                    "fors.fors_pmos_calib.minrejection", NULL);
00816         if (min_reject < 0)
00817             fors_pmos_calib_exit("Invalid number of lower rejections");
00818 
00819         max_reject = dfs_get_parameter_int(parlist,
00820                                    "fors.fors_pmos_calib.maxrejection", NULL);
00821         if (max_reject < 0)
00822             fors_pmos_calib_exit("Invalid number of upper rejections");
00823     }
00824 
00825     if (strcmp(stack_method, "ksigma") == 0) {
00826         klow  = dfs_get_parameter_double(parlist,
00827                                          "fors.fors_pmos_calib.klow", NULL);
00828         if (klow < 0.1)
00829             fors_pmos_calib_exit("Invalid lower K-sigma");
00830 
00831         khigh = dfs_get_parameter_double(parlist,
00832                                          "fors.fors_pmos_calib.khigh", NULL);
00833         if (khigh < 0.1)
00834             fors_pmos_calib_exit("Invalid lower K-sigma");
00835 
00836         kiter = dfs_get_parameter_int(parlist,
00837                                       "fors.fors_pmos_calib.kiter", NULL);
00838         if (kiter < 1)
00839             fors_pmos_calib_exit("Invalid number of iterations");
00840     }
00841 
00842     ddegree = dfs_get_parameter_int(parlist, 
00843                                     "fors.fors_pmos_calib.ddegree", NULL);
00844     dradius = dfs_get_parameter_int(parlist, 
00845                                     "fors.fors_pmos_calib.dradius", NULL);
00846 
00847     if (dradius < 1)
00848         fors_pmos_calib_exit("Invalid smoothing box radius");
00849 
00850     qc = dfs_get_parameter_bool(parlist, "fors.fors_pmos_calib.qc", NULL);
00851 
00852     check = dfs_get_parameter_bool(parlist, "fors.fors_pmos_calib.check", NULL);
00853 
00854     cpl_table_delete(grism_table); grism_table = NULL;
00855 
00856     if (cpl_error_get_code())
00857         fors_pmos_calib_exit("Failure getting the configuration parameters");
00858 
00859 
00860     /* 
00861      * Check input set-of-frames
00862      */
00863 
00864     cpl_msg_indent_less();
00865     cpl_msg_info(recipe, "Check input set-of-frames:");
00866     cpl_msg_indent_more();
00867 
00868     {
00869         cpl_frameset *subframeset = cpl_frameset_duplicate(frameset);
00870         cpl_frameset_erase(subframeset, "BIAS");
00871         cpl_frameset_erase(subframeset, "MASTER_BIAS");
00872 
00873         if (!dfs_equal_keyword(subframeset, "ESO INS GRIS1 ID")) 
00874             fors_pmos_calib_exit("Input frames are not from the same grism");
00875     
00876         if (!dfs_equal_keyword(subframeset, "ESO INS FILT1 ID")) 
00877             fors_pmos_calib_exit("Input frames are not from the same filter");
00878     
00879         if (!dfs_equal_keyword(subframeset, "ESO DET CHIP1 ID")) 
00880             fors_pmos_calib_exit("Input frames are not from the same chip");
00881 
00882         cpl_frameset_delete(subframeset);
00883     }
00884 
00885     pmos = cpl_frameset_count_tags(frameset, "LAMP_PMOS");
00886 
00887     if (pmos == 0)
00888         fors_pmos_calib_exit("Missing input arc lamp frame");
00889 
00890     if (pmos) {
00891         cpl_msg_info(recipe, "PMOS data found");
00892         arc_tag                  = "LAMP_PMOS";
00893         flat_tag                 = "SCREEN_FLAT_PMOS";
00894         master_screen_flat_tag   = "MASTER_SCREEN_FLAT_PMOS";
00895         master_norm_flat_tag     = "MASTER_NORM_FLAT_PMOS";
00896         reduced_lamp_tag         = "REDUCED_LAMP_PMOS";
00897         disp_residuals_tag       = "DISP_RESIDUALS_PMOS";
00898         disp_coeff_tag           = "DISP_COEFF_PMOS";
00899         wavelength_map_tag       = "WAVELENGTH_MAP_PMOS";
00900         spectra_detection_tag    = "SPECTRA_DETECTION_PMOS";
00901         spectral_resolution_tag  = "SPECTRAL_RESOLUTION_PMOS";
00902         slit_map_tag             = "SLIT_MAP_PMOS";
00903         curv_traces_tag          = "CURV_TRACES_PMOS";
00904         curv_coeff_tag           = "CURV_COEFF_PMOS";
00905         spatial_map_tag          = "SPATIAL_MAP_PMOS";
00906         slit_location_tag        = "SLIT_LOCATION_PMOS";
00907         disp_residuals_table_tag = "DISP_RESIDUALS_TABLE_PMOS";
00908         delta_image_tag          = "DELTA_IMAGE_PMOS";
00909         mapped_screen_flat_tag   = "MAPPED_SCREEN_FLAT_PMOS";
00910         mapped_norm_flat_tag     = "MAPPED_NORM_FLAT_PMOS";
00911     }
00912 
00913     nbias = 0;
00914     if (cpl_frameset_count_tags(frameset, "MASTER_BIAS") == 0) {
00915         if (cpl_frameset_count_tags(frameset, "BIAS") == 0)
00916             fors_pmos_calib_exit("Missing required input: MASTER_BIAS or BIAS");
00917         nbias = cpl_frameset_count_tags(frameset, "BIAS");
00918     }
00919 
00920     if (cpl_frameset_count_tags(frameset, "MASTER_BIAS") > 1)
00921         fors_pmos_calib_exit("Too many in input: MASTER_BIAS");
00922 
00923     if (cpl_frameset_count_tags(frameset, "MASTER_LINECAT") == 0)
00924         fors_pmos_calib_exit("Missing required input: MASTER_LINECAT");
00925 
00926     if (cpl_frameset_count_tags(frameset, "MASTER_LINECAT") > 1)
00927         fors_pmos_calib_exit("Too many in input: MASTER_LINECAT");
00928 
00929     if (cpl_frameset_count_tags(frameset, "MASTER_LINECAT") == 0)
00930         fors_pmos_calib_exit("Missing required input: MASTER_LINECAT");
00931 
00932     if (cpl_frameset_count_tags(frameset, "MASTER_LINECAT") > 1)
00933         fors_pmos_calib_exit("Too many in input: MASTER_LINECAT");
00934 
00935 /*
00936     if (cpl_frameset_count_tags(frameset, master_distortion_tag) == 0)
00937         fors_pmos_calib_exit("Missing required input: MASTER_DISTORTION_TABLE");
00938 */
00939 
00940     if (cpl_frameset_count_tags(frameset, master_distortion_tag) > 1)
00941         fors_pmos_calib_exit("Too many in input: MASTER_DISTORTION_TABLE");
00942 
00943     nflats = cpl_frameset_count_tags(frameset, flat_tag);
00944 
00945     if (nflats < 1) {
00946         cpl_msg_error(recipe, "Missing required input: %s", flat_tag);
00947         fors_pmos_calib_exit(NULL);
00948     }
00949 
00950     cpl_msg_indent_less();
00951 
00952     if (nflats > 1)
00953         cpl_msg_info(recipe, "Load %d flat field frames and stack them "
00954                      "with method \"%s\"", nflats, stack_method);
00955     else
00956         cpl_msg_info(recipe, "Load flat field exposure...");
00957 
00958     cpl_msg_indent_more();
00959 
00960     header = dfs_load_header(frameset, flat_tag, 0);
00961 
00962     if (header == NULL)
00963         fors_pmos_calib_exit("Cannot load flat field frame header");
00964 
00965     alltime = cpl_propertylist_get_double(header, "EXPTIME");
00966 
00967     if (cpl_error_get_code() != CPL_ERROR_NONE)
00968         fors_pmos_calib_exit("Missing keyword EXPTIME in flat field "
00969                              "frame header");
00970 
00971     cpl_propertylist_delete(header);
00972 
00973     for (i = 1; i < nflats; i++) {
00974 
00975         header = dfs_load_header(frameset, NULL, 0);
00976 
00977         if (header == NULL)
00978             fors_pmos_calib_exit("Cannot load flat field frame header");
00979 
00980         alltime += cpl_propertylist_get_double(header, "EXPTIME");
00981 
00982         if (cpl_error_get_code() != CPL_ERROR_NONE)
00983             fors_pmos_calib_exit("Missing keyword EXPTIME in flat field "
00984                             "frame header");
00985 
00986         cpl_propertylist_delete(header);
00987 
00988     }
00989 
00990     if (bagoo) {
00991         char *montecarlo = getenv("MONTECARLO");
00992 
00993         if (montecarlo)
00994             doit = atoi(montecarlo);
00995 
00996         if (doit) {
00997             master_bias = dfs_load_image(frameset, "MASTER_BIAS",
00998                                      CPL_TYPE_FLOAT, 0, 1);
00999             if (master_bias == NULL)
01000                 fors_pmos_calib_exit("Cannot load master bias");
01001 
01002             blevel = cpl_image_get_mean(master_bias);
01003 
01004             cpl_image_delete(master_bias);
01005         }
01006     }
01007 
01008     master_flat = dfs_load_image(frameset, flat_tag, CPL_TYPE_FLOAT, 0, 0);
01009 
01010     if (master_flat == NULL)
01011         fors_pmos_calib_exit("Cannot load flat field");
01012 
01013     if (doit) {
01014         header = dfs_load_header(frameset, flat_tag, 0);
01015 
01016         gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
01017 
01018         if (cpl_error_get_code() != CPL_ERROR_NONE)
01019             fors_pmos_calib_exit("Missing keyword ESO DET OUT1 CONAD "
01020                                  "in flat field frame header");
01021 
01022         ron = cpl_propertylist_get_double(header, "ESO DET OUT1 RON");
01023 
01024         if (cpl_error_get_code() != CPL_ERROR_NONE)
01025             fors_pmos_calib_exit("Missing keyword ESO DET OUT1 RON "
01026                                  "in flat field frame header");
01027 
01028         cpl_propertylist_delete(header);
01029 
01030         ron /= gain;   // RON converted from electrons to ADU
01031 
01032         mos_randomise_image(master_flat, ron, gain, blevel);
01033     }
01034 
01035     ny = cpl_image_get_size_y(master_flat);
01036 
01037     if (nflats > 1) {
01038         if (strcmp(stack_method, "average") == 0) {
01039             for (i = 1; i < nflats; i++) {
01040                 flat = dfs_load_image(frameset, NULL, CPL_TYPE_FLOAT, 0, 0);
01041                 if (flat) {
01042                     if (doit) {
01043                         mos_randomise_image(flat, ron, gain, blevel);
01044                     }
01045                     cpl_image_add(master_flat, flat);
01046                     cpl_image_delete(flat); flat = NULL;
01047                 }
01048                 else
01049                     fors_pmos_calib_exit("Cannot load flat field");
01050             }
01051 
01052         /***
01053             if (nflats > 1)
01054                 cpl_image_divide_scalar(master_flat, nflats);
01055         ***/
01056 
01057         }
01058         else {
01059             cpl_imagelist *flatlist = NULL;
01060             double rflux, flux;
01061 
01062             added_flat = cpl_image_duplicate(master_flat);
01063 
01064             flatlist = cpl_imagelist_new();
01065             cpl_imagelist_set(flatlist, master_flat, 
01066                               cpl_imagelist_get_size(flatlist));
01067 
01068             /*
01069              * Stacking with rejection requires normalization
01070              * at the same flux. We normalise according to mean
01071              * flux. This is equivalent to determining the
01072              * flux ratio for each image as the average of the
01073              * flux ratio of all pixels weighted on the actual
01074              * flux of each pixel.
01075              */
01076 
01077             rflux = cpl_image_get_mean(master_flat);
01078 
01079             for (i = 1; i < nflats; i++) {
01080                 flat = dfs_load_image(frameset, NULL, CPL_TYPE_FLOAT, 0, 0);
01081                 if (flat) {
01082                     if (doit) {
01083                         mos_randomise_image(flat, ron, gain, blevel);
01084                     }
01085                     cpl_image_add(added_flat, flat);
01086                     flux = cpl_image_get_mean(flat);
01087                     cpl_image_multiply_scalar(flat, rflux / flux);
01088                     cpl_imagelist_set(flatlist, flat, 
01089                                       cpl_imagelist_get_size(flatlist));
01090                 }
01091                 else {
01092                     fors_pmos_calib_exit("Cannot load flat field");
01093                 }
01094             }
01095             
01096             if (strcmp(stack_method, "median") == 0) {
01097                 master_flat = cpl_imagelist_collapse_median_create(flatlist);
01098             }
01099 
01100             if (strcmp(stack_method, "minmax") == 0) {
01101                 master_flat = cpl_imagelist_collapse_minmax_create(flatlist, 
01102                                                                    min_reject,
01103                                                                    max_reject);
01104             }
01105 
01106             if (strcmp(stack_method, "ksigma") == 0) {
01107                 master_flat = mos_ksigma_stack(flatlist, 
01108                                                klow, khigh, kiter, NULL);
01109             }
01110         }
01111     }
01112 
01113 
01114     /*
01115      * Get the reference wavelength and the rebin factor along the
01116      * dispersion direction from the arc lamp exposure
01117      */
01118 
01119     header = dfs_load_header(frameset, arc_tag, 0);
01120 
01121     if (header == NULL)
01122         fors_pmos_calib_exit("Cannot load arc lamp header");
01123 
01124     instrume = (char *)cpl_propertylist_get_string(header, "INSTRUME");
01125     if (instrume == NULL)
01126         fors_pmos_calib_exit("Missing keyword INSTRUME in arc lamp header");
01127 
01128     instrume = cpl_strdup(instrume);
01129 
01130     if (instrume[4] == '1')
01131         snprintf(version, 80, "%s/%s", "fors1", VERSION);
01132     if (instrume[4] == '2')
01133         snprintf(version, 80, "%s/%s", "fors2", VERSION);
01134 
01135     reference = cpl_propertylist_get_double(header, "ESO INS GRIS1 WLEN");
01136 
01137     if (cpl_error_get_code() != CPL_ERROR_NONE)
01138         fors_pmos_calib_exit("Missing keyword ESO INS GRIS1 WLEN in arc lamp "
01139                         "frame header");
01140 
01141     if (reference < 3000.0)   /* Perhaps in nanometers... */
01142         reference *= 10;
01143 
01144     if (reference < 3000.0 || reference > 13000.0) {
01145         cpl_msg_error(recipe, "Invalid central wavelength %.2f read from "
01146                       "keyword ESO INS GRIS1 WLEN in arc lamp frame header",
01147                       reference);
01148         fors_pmos_calib_exit(NULL);
01149     }
01150 
01151     cpl_msg_info(recipe, "The central wavelength is: %.2f", reference);
01152 
01153     rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
01154 
01155     if (cpl_error_get_code() != CPL_ERROR_NONE)
01156         fors_pmos_calib_exit("Missing keyword ESO DET WIN1 BINX in arc lamp "
01157                         "frame header");
01158 
01159     if (rebin != 1) {
01160         dispersion *= rebin;
01161         cpl_msg_warning(recipe, "The rebin factor is %d, and therefore the "
01162                         "working dispersion used is %f A/pixel", rebin, 
01163                         dispersion);
01164     }
01165 
01166     gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
01167 
01168     if (cpl_error_get_code() != CPL_ERROR_NONE)
01169         fors_pmos_calib_exit("Missing keyword ESO DET OUT1 CONAD in arc lamp "
01170                         "frame header");
01171 
01172     cpl_msg_info(recipe, "The gain factor is: %.2f e-/ADU", gain);
01173 
01174     if (pmos) {
01175         int nslits_out_det;
01176         cpl_msg_info(recipe, "Produce mask slit position table...");
01177 
01178         maskslits = mos_load_slits_fors_mos(header, &nslits_out_det);
01179 
01180         /*
01181          * Check if all slits have the same X offset: in such case, 
01182          * treat the observation as a long-slit one!
01183          */
01184 
01185         mxpos = cpl_table_get_column_median(maskslits, "xtop");
01186         xpos = cpl_table_get_data_double(maskslits, "xtop");
01187         nslits = cpl_table_get_nrow(maskslits);
01188 
01189         same_offset = 1;
01190         for (i = 0; i < nslits; i++) {
01191             if (fabs(mxpos-xpos[i]) > 0.01) {
01192                 same_offset = 0;
01193                 break;
01194             }
01195         }
01196         //If not all the slits are illuminated, then we cannot say that
01197         //all have the same offsets.
01198         if(nslits_out_det != 0)
01199             same_offset = 0;
01200 
01201         if (same_offset) {
01202             cpl_msg_info(recipe, "All slits have same offset: %.2f", mxpos);
01203         }
01204         else {
01205             cpl_msg_info(recipe, "All slits have different offsets");
01206         }
01207 
01208         if (ny != 400 && ny != 500) {
01209             if (cpl_frameset_count_tags(frameset, 
01210                                         master_distortion_tag) == 0)
01211                 fors_pmos_calib_exit(
01212                 "Missing required input: MASTER_DISTORTION_TABLE");
01213 
01214             header_dist = dfs_load_header(frameset, 
01215                                           master_distortion_tag, 0);
01216             rebin_dist = cpl_propertylist_get_int(header_dist,
01217                                                   "ESO DET WIN1 BINX");
01218             cpl_propertylist_delete(header_dist);
01219         }
01220     }
01221 
01222     /* Leave the header on for the next step... */
01223 
01224 
01225     /*
01226      * Remove the master bias
01227      */
01228 
01229     if (nbias) {
01230 
01231         /*
01232          * Set of raw BIASes in input, need to create master bias!
01233          */
01234 
01235         cpl_msg_info(recipe, "Generate the master from input raw biases...");
01236 
01237         if (nbias > 1) {
01238 
01239             biases = cpl_imagelist_new();
01240 
01241             bias = dfs_load_image(frameset, "BIAS", CPL_TYPE_FLOAT, 0, 0);
01242     
01243             if (bias == NULL)
01244                 fors_pmos_calib_exit("Cannot load bias frame");
01245 
01246             cpl_imagelist_set(biases, bias, 0);
01247     
01248             for (i = 1; i < nbias; i++) {
01249                 bias = dfs_load_image(frameset, NULL, CPL_TYPE_FLOAT, 0, 0);
01250                 if (bias)
01251                     cpl_imagelist_set(biases, bias, i);
01252                 else
01253                     fors_pmos_calib_exit("Cannot load bias frame");
01254             }
01255     
01256             master_bias = cpl_imagelist_collapse_median_create(biases);
01257 
01258             cpl_imagelist_delete(biases);
01259         }
01260         else {
01261             master_bias = dfs_load_image(frameset, "BIAS", 
01262                                          CPL_TYPE_FLOAT, 0, 1);
01263             if (master_bias == NULL)
01264                 fors_pmos_calib_exit("Cannot load bias");
01265         }
01266 
01267     }
01268     else {
01269         master_bias = dfs_load_image(frameset, "MASTER_BIAS", 
01270                                      CPL_TYPE_FLOAT, 0, 1);
01271         if (master_bias == NULL)
01272             fors_pmos_calib_exit("Cannot load master bias");
01273     }
01274 
01275     cpl_msg_info(recipe, "Remove the master bias...");
01276 
01277     overscans = mos_load_overscans_fors(header);
01278     cpl_propertylist_delete(header); header = NULL;
01279 
01280     if (nbias) {
01281         int xlow = cpl_table_get_int(overscans, "xlow", 0, NULL);
01282         int ylow = cpl_table_get_int(overscans, "ylow", 0, NULL);
01283         int xhig = cpl_table_get_int(overscans, "xhig", 0, NULL);
01284         int yhig = cpl_table_get_int(overscans, "yhig", 0, NULL);
01285         dummy = cpl_image_extract(master_bias, xlow+1, ylow+1, xhig, yhig);
01286         cpl_image_delete(master_bias); master_bias = dummy;
01287 
01288         if (dfs_save_image(frameset, master_bias, "MASTER_BIAS",
01289                            NULL, parlist, recipe, version))
01290             fors_pmos_calib_exit(NULL);
01291     }
01292 
01293     if (nflats > 1) {
01294         multi_bias = cpl_image_multiply_scalar_create(master_bias, nflats);
01295         dummy = mos_remove_bias(master_flat, multi_bias, overscans);
01296         if (added_flat)
01297             add_dummy = mos_remove_bias(added_flat, multi_bias, overscans);
01298         cpl_image_delete(multi_bias);
01299     }
01300     else {
01301         dummy = mos_remove_bias(master_flat, master_bias, overscans);
01302     }
01303     cpl_image_delete(master_flat);
01304     master_flat = dummy;
01305 
01306     if (master_flat == NULL)
01307         fors_pmos_calib_exit("Cannot remove bias from flat field");
01308 
01309     if (added_flat) {
01310         cpl_image_delete(added_flat);
01311         added_flat = add_dummy;
01312 
01313         if (added_flat == NULL)
01314             fors_pmos_calib_exit("Cannot remove bias from added flat field");
01315 
01316         trace_flat = added_flat;
01317     }
01318     else
01319         trace_flat = master_flat;
01320 
01321     wavelengths = dfs_load_table(frameset, "MASTER_LINECAT", 1);
01322 
01323     if (wavelengths == NULL)
01324         fors_pmos_calib_exit("Cannot load line catalog");
01325 
01326     /*
01327      * Cast the wavelengths into a (double precision) CPL vector
01328      */
01329 
01330     nlines = cpl_table_get_nrow(wavelengths);
01331 
01332     if (nlines == 0)
01333         fors_pmos_calib_exit("Empty input line catalog");
01334 
01335     if (cpl_table_has_column(wavelengths, wcolumn) != 1) {
01336         cpl_msg_error(recipe, "Missing column %s in input line catalog table",
01337                       wcolumn);
01338         fors_pmos_calib_exit(NULL);
01339     }
01340 
01341     line = cpl_malloc(nlines * sizeof(double));
01342     
01343     for (i = 0; i < nlines; i++)
01344         line[i] = cpl_table_get(wavelengths, wcolumn, i, NULL);
01345 
01346     lines = cpl_vector_wrap(nlines, line);
01347 
01348     for (j = 0; j < pmos; j++) {
01349         int k;
01350 
01351         cpl_msg_indent_less();
01352         cpl_msg_info(recipe, "Processing arc lamp nb %d out of %d ...",
01353                      j + 1, pmos);
01354         cpl_msg_indent_more();
01355 
01356         cpl_msg_info(recipe, "Load arc lamp exposure...");
01357         cpl_msg_indent_more();
01358 
01359         spectra = dfs_load_image(frameset, arc_tag, CPL_TYPE_FLOAT, 0, 0);
01360 
01361         /*
01362          * FIXME: Horrible workaround to avoid the problem because of the
01363          * multiple encapsulation of cpl_frameset_find() in different 
01364          * loading functions
01365          */
01366         for (k = 0; k < j; k ++) {
01367             cpl_image_delete(spectra);
01368             spectra = dfs_load_image(frameset, NULL, CPL_TYPE_FLOAT, 0, 0);
01369         }
01370 
01371         if (spectra == NULL)
01372             fors_pmos_calib_exit("Cannot load arc lamp exposure");
01373 
01374         if (doit) {
01375             mos_randomise_image(spectra, ron, gain, blevel);
01376         }
01377 
01378         cpl_msg_info(recipe, "Remove the master bias...");
01379 
01380         dummy = mos_remove_bias(spectra, master_bias, overscans);
01381         cpl_image_delete(spectra); spectra = dummy;
01382 
01383         if (spectra == NULL)
01384             fors_pmos_calib_exit("Cannot remove bias from arc lamp exposure");
01385 
01386         cpl_msg_indent_less();
01387         cpl_msg_info(recipe, "Load input line catalog...");
01388         cpl_msg_indent_more();
01389 
01390         /*
01391          * Here the PMOS calibration is carried out.
01392          */
01393 
01394         if (mos_saturation_process(spectra))
01395             fors_pmos_calib_exit("Cannot process saturation");
01396 
01397         if (mos_subtract_background(spectra))
01398             fors_pmos_calib_exit("Cannot subtract the background");
01399 
01400         if (!j) {
01401             /*
01402              * Detecting spectra on the CCD
01403              */
01404 
01405             cpl_msg_indent_less();
01406             cpl_msg_info(recipe, "Detecting spectra on CCD...");
01407             cpl_msg_indent_more();
01408 
01409             ccd_xsize = nx = cpl_image_get_size_x(spectra);
01410             ccd_ysize = ny = cpl_image_get_size_y(spectra);
01411 
01412             refmask = cpl_mask_new(nx, ny);
01413 
01414             checkwave =
01415                 mos_wavelength_calibration_raw(spectra, lines, dispersion, 
01416                                                peakdetection, wradius, 
01417                                                wdegree, wreject, reference,
01418                                                &startwavelength, &endwavelength,
01419                                                NULL, NULL, NULL, NULL, NULL, 
01420                                                NULL, refmask);
01421 
01422             if (checkwave == NULL)
01423                 fors_pmos_calib_exit("Wavelength calibration failure.");
01424 
01425             /*
01426              * Save check image to disk
01427              */
01428 
01429             header = cpl_propertylist_new();
01430             cpl_propertylist_update_double(header, "CRPIX1", 1.0);
01431             cpl_propertylist_update_double(header, "CRPIX2", 1.0);
01432             cpl_propertylist_update_double(header, "CRVAL1", 
01433                                            startwavelength + dispersion/2);
01434             cpl_propertylist_update_double(header, "CRVAL2", 1.0);
01435             /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
01436                cpl_propertylist_update_double(header, "CDELT2", 1.0); */
01437             cpl_propertylist_update_double(header, "CD1_1", dispersion);
01438             cpl_propertylist_update_double(header, "CD1_2", 0.0);
01439             cpl_propertylist_update_double(header, "CD2_1", 0.0);
01440             cpl_propertylist_update_double(header, "CD2_2", 1.0);
01441             cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
01442             cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
01443 
01444             if (check) {
01445                 if (!j) {
01446                     if(dfs_save_image_null(frameset, parlist,
01447                                            spectra_detection_tag,
01448                                            recipe, version)) {
01449                         fors_pmos_calib_exit(NULL);
01450                     }
01451                 }
01452 
01453                 if (dfs_save_image_ext(checkwave, 
01454                                        spectra_detection_tag, header)) {
01455                     fors_pmos_calib_exit(NULL);
01456                 }
01457             }
01458 
01459             cpl_image_delete(checkwave); checkwave = NULL;
01460             cpl_propertylist_delete(header); header = NULL;
01461 
01462             if (cpl_mask_is_empty(refmask))
01463                 fors_pmos_calib_exit("Wavelength calibration failure.");
01464 
01465             if (mos_refmask_find_gaps(refmask, trace_flat, -1.0))
01466                 fors_pmos_calib_exit("The gaps could not be found");
01467 
01468             cpl_msg_info(recipe,
01469                          "Locate slits at reference wavelength on CCD...");
01470             slits = mos_locate_spectra(refmask);
01471 
01472             if (!slits) {
01473                 cpl_msg_error(cpl_error_get_where(), cpl_error_get_message());
01474                 fors_pmos_calib_exit("No slits could be detected!");
01475             }
01476 
01477             if (same_offset) {
01478                 if (ny != 400 && ny != 500) {
01479                     float rescale = (float) rebin_dist / rebin;
01480                     if (mos_check_slits(slits, rescale)) {
01481                         fors_pmos_calib_exit("Some slits are missing. "
01482                                              "Cannot recover!");
01483                     }
01484                 }
01485             }
01486 
01487             refimage = cpl_image_new_from_mask(refmask);
01488             cpl_mask_delete(refmask); refmask = NULL;
01489 
01490             if (check) {
01491                 if (!j) {
01492                     if(dfs_save_image_null(frameset, parlist,
01493                                            slit_map_tag,
01494                                            recipe, version)) {
01495                         fors_pmos_calib_exit(NULL);
01496                     }
01497                 }
01498 
01499                 save_header = dfs_load_header(frameset, arc_tag, 0);
01500 
01501                 for (k = 0; k < j; k ++) {
01502                     cpl_propertylist_delete(save_header);
01503                     save_header = dfs_load_header(frameset, NULL, 0);
01504                 }
01505 
01506                 if (dfs_save_image_ext(refimage, slit_map_tag, save_header)) {
01507                     fors_pmos_calib_exit(NULL);
01508                 }
01509                 cpl_propertylist_delete(save_header); save_header = NULL;
01510             }
01511 
01512             cpl_image_delete(refimage); refimage = NULL;
01513 
01514 //          if (same_offset == 0) {
01515 
01516             same_offset = 1; // Added, see next line comment.
01517             if (0) { // This part is eliminated: a successful
01518                      // pattern matching would identify just 
01519                      // one of the two beams!!! It needs to be FIXED.
01520 
01521                 /*
01522                  * Attempt slit identification: this recipe may continue even
01523                  * in case of failed identification (i.e., the position table
01524                  * is not produced, but an error is not set). In case of 
01525                  * failure, the spectra would be still extracted, even if they
01526                  * would not be associated to slits on the mask.
01527                  * 
01528                  * The reason for making the slit identification an user option
01529                  * (via the parameter slit_ident) is to offer the possibility 
01530                  * to avoid identifications that are only apparently successful
01531                  * as it would happen in the case of an incorrect slit
01532                  * description in the data header.
01533                  */
01534 
01535                 cpl_msg_indent_less();
01536                 cpl_msg_info(recipe, 
01537                              "Attempt slit identification (optional)...");
01538                 cpl_msg_indent_more();
01539 
01540                 positions = mos_identify_slits(slits, maskslits, NULL);
01541 
01542                 if (positions) {
01543                     cpl_table_delete(slits);
01544                     slits = positions;
01545 
01546                    /*
01547                     * Eliminate slits which are not _entirely_ inside the CCD
01548                     */
01549 
01550                     cpl_table_and_selected_double(slits, 
01551                                                  "ytop", CPL_GREATER_THAN, ny);
01552                     cpl_table_or_selected_double(slits, 
01553                                                  "ybottom", CPL_LESS_THAN, 0);
01554                     cpl_table_erase_selected(slits);
01555 
01556                     nslits = cpl_table_get_nrow(slits);
01557 
01558                     if (nslits == 0)
01559                         fors_pmos_calib_exit("No slits found on the CCD");
01560 
01561                     cpl_msg_info(recipe,
01562                                  "%d slits are entirely contained in CCD", 
01563                                  nslits);
01564                 }
01565                 else {
01566                     same_offset = 1; /* FIXLANDER slit_ident = 0; */
01567                     cpl_msg_info(recipe, 
01568                                  "Global distortion model cannot be computed");
01569                     if (cpl_error_get_code() != CPL_ERROR_NONE) {
01570                         fors_pmos_calib_exit(NULL);
01571                     }
01572                 }
01573             }
01574 
01575 
01576             if (ny == 400 || ny == 500) {
01577 
01578                /*
01579                 * For the FORS1 special case (old chip 2048x400 readout)
01580                 * keep the central slits only
01581                 */
01582 
01583                 nslits = cpl_table_get_nrow(slits);
01584 
01585                 if (nslits > 4) {
01586                     cpl_table_unselect_all(slits);
01587                     for (k = 0; k < cpl_table_get_nrow(slits); k++) {
01588                         double jump = cpl_table_get(slits, "ytop", k, NULL) 
01589                                     - cpl_table_get(slits, "ybottom", k, NULL);
01590                         if (jump < 50.) {
01591                             cpl_table_select_row(slits, k);
01592                         }
01593                     }
01594                     cpl_table_erase_selected(slits);
01595                     nslits = cpl_table_get_nrow(slits);
01596                 }
01597 
01598                 if (nslits == 0)
01599                     fors_pmos_calib_exit("No slits found on the CCD");
01600 
01601                 if (nslits == 4) {
01602                     cpl_table_unselect_all(slits);
01603                     cpl_table_select_row(slits, 0);
01604                     cpl_table_select_row(slits, cpl_table_get_nrow(slits)-1);
01605                     cpl_table_erase_selected(slits);
01606                 }
01607 
01608                 cpl_msg_info(recipe, 
01609                              "%d slits are entirely contained in CCD", nslits);
01610             }
01611             else {
01612                 cpl_table_unselect_all(slits);
01613                 for (k = 0; k < cpl_table_get_nrow(slits); k++) {
01614                     double jump = cpl_table_get(slits, "ytop", k, NULL)
01615                                 - cpl_table_get(slits, "ybottom", k, NULL);
01616                     if (jump < 10.) {
01617                         cpl_table_select_row(slits, k);
01618                     }
01619                 }
01620                 cpl_table_erase_selected(slits);
01621                 nslits = cpl_table_get_nrow(slits);
01622             }
01623 
01624 
01625             /*
01626              * Determination of spectral curvature
01627              */
01628 
01629             cpl_msg_indent_less();
01630             cpl_msg_info(recipe, "Determining spectral curvature...");
01631             cpl_msg_indent_more();
01632 
01633             cpl_msg_info(recipe, "Tracing master flat field spectra edges...");
01634             traces = mos_trace_flat(trace_flat, slits, reference, 
01635                                     startwavelength, endwavelength, dispersion);
01636 
01637             if (!traces)
01638                 fors_pmos_calib_exit("Tracing failure");
01639 
01640             cpl_image_delete(added_flat); added_flat = NULL;
01641 
01642             cpl_msg_info(recipe, "Fitting flat field spectra edges...");
01643             polytraces = mos_poly_trace(slits, traces, cdegree);
01644 
01645             if (!polytraces)
01646                 fors_pmos_calib_exit("Trace fitting failure");
01647 
01648             if (cmode) {
01649                 cpl_msg_info(recipe, 
01650                              "Computing global spectral curvature model...");
01651                 mos_global_trace(slits, polytraces, cmode);
01652             }
01653 
01654             if (!j) {
01655                 if(dfs_save_image_null(frameset, parlist, curv_traces_tag,
01656                                        recipe, version)) {
01657                     fors_pmos_calib_exit(NULL);
01658                 }
01659             }
01660 
01661             if (dfs_save_table_ext(traces, curv_traces_tag, NULL)) {
01662                 fors_pmos_calib_exit(NULL);
01663             }
01664 
01665             cpl_table_delete(traces); traces = NULL;
01666 
01667             coordinate = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01668 
01669         }
01670 //
01671         spatial = mos_spatial_calibration(spectra, slits, polytraces,
01672                                           reference, 
01673                                           startwavelength, endwavelength, 
01674                                           dispersion, 0, j ? NULL: coordinate);
01675 
01676         if (!j) {
01677 //
01678             if (same_offset) { /* FIXLANDER It was !slit_ident */
01679                 cpl_image_delete(spectra); spectra = NULL;
01680             }
01681 
01682             /*
01683              * Flat field normalisation is done directly on the master flat
01684              * field (without spatial rectification first). The spectral
01685              * curvature model may be provided in input, in future releases.
01686              */
01687 
01688             cpl_msg_indent_less();
01689             cpl_msg_info(recipe, "Perform flat field normalisation...");
01690             cpl_msg_indent_more();
01691 
01692             norm_flat = cpl_image_duplicate(master_flat);
01693 
01694             smo_flat = mos_normalise_flat(norm_flat, coordinate, slits,
01695                                           polytraces, reference,
01696                                           startwavelength, endwavelength,
01697                                           dispersion, dradius, ddegree);
01698 
01699             /* This may be a product */
01700             cpl_image_delete(smo_flat); smo_flat = NULL; 
01701 
01702  
01703             save_header = dfs_load_header(frameset, flat_tag, 0);
01704             cpl_propertylist_update_int(save_header, "ESO PRO DATANCOM",
01705                                         nflats);
01706 
01707             rect_flat = mos_spatial_calibration(master_flat, slits, polytraces,
01708                                                 reference, startwavelength, 
01709                                                 endwavelength, dispersion, 0,
01710                                                 NULL);
01711             rect_nflat = mos_spatial_calibration(norm_flat, slits, polytraces, 
01712                                                  reference, startwavelength, 
01713                                                  endwavelength, dispersion, 0,
01714                                                  NULL);
01715 
01716 
01717             if (dfs_save_image(frameset, master_flat, master_screen_flat_tag,
01718                                save_header, parlist, recipe, version))
01719                 fors_pmos_calib_exit(NULL);
01720 
01721 
01722             if (dfs_save_image(frameset, norm_flat, master_norm_flat_tag,
01723                                save_header, parlist, recipe, version))
01724                 fors_pmos_calib_exit(NULL);
01725 
01726             cpl_image_delete(norm_flat); norm_flat = NULL;
01727             cpl_propertylist_delete(save_header); save_header = NULL;
01728 
01729         }
01730 
01731 
01732         /*
01733          * Final wavelength calibration of spectra having their curvature
01734          * removed
01735          */
01736 
01737         cpl_msg_indent_less();
01738         cpl_msg_info(recipe, "Perform final wavelength calibration...");
01739         cpl_msg_indent_more();
01740 
01741         nx = cpl_image_get_size_x(spatial);
01742         ny = cpl_image_get_size_y(spatial);
01743 
01744         idscoeff = cpl_table_new(ny);
01745         restable = cpl_table_new(nlines);
01746         rainbow = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01747         if (check)
01748             residual = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01749         fiterror = cpl_calloc(ny, sizeof(double));
01750         fitlines = cpl_calloc(ny, sizeof(int));
01751 
01752         rectified = mos_wavelength_calibration_final(spatial, slits, lines, 
01753                                                      dispersion, peakdetection,
01754                                                      wradius, wdegree, wreject,
01755                                                      reference,
01756                                                      &startwavelength, 
01757                                                      &endwavelength, fitlines, 
01758                                                      fiterror, idscoeff,
01759                                                      rainbow, 
01760                                                      residual, restable);
01761 
01762         if (rectified == NULL)
01763             fors_pmos_calib_exit("Wavelength calibration failure.");
01764 
01765         if (!j) {
01766             if(dfs_save_image_null(frameset, parlist, disp_residuals_table_tag,
01767                                    recipe, version)) {
01768                 fors_pmos_calib_exit(NULL);
01769             }
01770         }
01771 
01772         header = dfs_load_header(frameset, arc_tag, 0);
01773 
01774         for (k = 0; k < j; k ++) {
01775             cpl_propertylist_delete(header);
01776             header = dfs_load_header(frameset, NULL, 0);
01777         }
01778 
01779         if (dfs_save_table_ext(restable, disp_residuals_table_tag, header)) {
01780             fors_pmos_calib_exit(NULL);
01781         }
01782 
01783         cpl_propertylist_delete(header);
01784 
01785         cpl_table_delete(restable); restable = NULL;
01786 
01787         cpl_table_wrap_double(idscoeff, fiterror, "error"); fiterror = NULL;
01788         cpl_table_set_column_unit(idscoeff, "error", "pixel");
01789         cpl_table_wrap_int(idscoeff, fitlines, "nlines"); fitlines = NULL;
01790 
01791         for (i = 0; i < ny; i++)
01792             if (!cpl_table_is_valid(idscoeff, "c0", i))
01793                 cpl_table_set_invalid(idscoeff, "error", i);
01794 
01795         delta = mos_map_pixel(idscoeff, reference, startwavelength,
01796                               endwavelength, dispersion, 2);
01797 
01798         header = dfs_load_header(frameset, arc_tag, 0);
01799 
01800         for (k = 0; k < j; k ++) {
01801             cpl_propertylist_delete(header);
01802             header = dfs_load_header(frameset, NULL, 0);
01803         }
01804 
01805         cpl_propertylist_update_double(header, "CRPIX1", 1.0);
01806         cpl_propertylist_update_double(header, "CRPIX2", 1.0);
01807         cpl_propertylist_update_double(header, "CRVAL1",
01808                                        startwavelength + dispersion/2);
01809         cpl_propertylist_update_double(header, "CRVAL2", 1.0);
01810         /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
01811            cpl_propertylist_update_double(header, "CDELT2", 1.0); */
01812         cpl_propertylist_update_double(header, "CD1_1", dispersion);
01813         cpl_propertylist_update_double(header, "CD1_2", 0.0);
01814         cpl_propertylist_update_double(header, "CD2_1", 0.0);
01815         cpl_propertylist_update_double(header, "CD2_2", 1.0);
01816         cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
01817         cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
01818 
01819         if (!j) {
01820             if(dfs_save_image_null(frameset, parlist, delta_image_tag,
01821                                    recipe, version)) {
01822                 fors_pmos_calib_exit(NULL);
01823             }
01824         }
01825 
01826         if (dfs_save_image_ext(delta, delta_image_tag, header)) {
01827             fors_pmos_calib_exit(NULL);
01828         }
01829 
01830         cpl_image_delete(delta); delta = NULL;
01831         cpl_propertylist_delete(header); header = NULL;
01832 
01833         mean_rms = mos_distortions_rms(rectified, lines, startwavelength, 
01834                                        dispersion, 6, 0);
01835 
01836         cpl_msg_info(recipe, "Mean residual: %f pixel", mean_rms);
01837 
01838         mean_rms = cpl_table_get_column_mean(idscoeff, "error");
01839         mean_rms_err = cpl_table_get_column_stdev(idscoeff, "error");
01840 
01841         cpl_msg_info(recipe, "Mean model accuracy: %f pixel (%f A)", 
01842                      mean_rms, mean_rms * dispersion);
01843 
01844         restab = mos_resolution_table(rectified, startwavelength, dispersion, 
01845                                       60000, lines);
01846 
01847         if (restab) {
01848             cpl_msg_info(recipe, "Mean spectral resolution: %.2f", 
01849                          cpl_table_get_column_mean(restab, "resolution"));
01850             cpl_msg_info(recipe,
01851                   "Mean reference lines FWHM: %.2f +/- %.2f pixel",
01852                   cpl_table_get_column_mean(restab, "fwhm") / dispersion,
01853                   cpl_table_get_column_mean(restab, "fwhm_rms") / dispersion);
01854 
01855             cpl_table_duplicate_column(restab, "dlambda", 
01856                                        restab, "fwhm");
01857             cpl_table_multiply_scalar(restab, "dlambda", dispersion);
01858             cpl_table_duplicate_column(restab, "dlambda_rms", 
01859                                        restab, "fwhm_rms");
01860             cpl_table_multiply_scalar(restab, "dlambda_rms", dispersion);
01861 
01862             if (qc) {
01863 
01864                 qclist = cpl_propertylist_new();
01865 
01866                 /*
01867                  * QC1 parameters
01868                  */
01869                 keyname = "QC.DID";
01870 
01871                 if (fors_header_write_string(qclist,
01872                                              keyname,
01873                                              "2.0",
01874                                              "QC1 dictionary")) {
01875                     fors_pmos_calib_exit("Cannot write dictionary version "
01876                                          "to QC log file");
01877                 }
01878 
01879                 
01880                 keyname = "QC.PMOS.RESOLUTION";
01881 
01882                 if (fors_header_write_double(qclist, 
01883                                             cpl_table_get_column_mean(restab,
01884                                             "resolution"),
01885                                             keyname,
01886                                             "Angstrom",
01887                                             "Mean spectral resolution")) {
01888                     fors_pmos_calib_exit("Cannot write mean spectral "
01889                                          "resolution to QC log file");
01890                 }
01891 
01892                 keyname = "QC.PMOS.RESOLUTION.RMS";
01893 
01894                 if (fors_header_write_double(qclist, 
01895                                             cpl_table_get_column_stdev(restab, 
01896                                             "resolution"),
01897                                             keyname,
01898                                             "Angstrom", 
01899                                             "Scatter of spectral resolution")) {
01900                     fors_pmos_calib_exit("Cannot write spectral resolution "
01901                                          "scatter to QC log file");
01902                 }
01903 
01904                 keyname = "QC.PMOS.RESOLUTION.NWAVE";
01905 
01906                 if (fors_header_write_int(qclist, cpl_table_get_nrow(restab) -
01907                                          cpl_table_count_invalid(restab, 
01908                                                                  "resolution"),
01909                                          keyname,
01910                                          NULL,
01911                                          "Number of examined wavelengths "
01912                                          "for resolution computation")) {
01913                     fors_pmos_calib_exit("Cannot write number of lines used "
01914                                          "in spectral resolution computation "
01915                                          "to QC log file");
01916                 }
01917 
01918                 keyname = "QC.PMOS.RESOLUTION.MEANRMS";
01919                     
01920                 if (fors_header_write_double(qclist,
01921                                             cpl_table_get_column_mean(restab,
01922                                             "resolution_rms"),
01923                                             keyname, NULL,
01924                                             "Mean error on spectral "
01925                                             "resolution computation")) {
01926                     fors_pmos_calib_exit("Cannot write mean error in "
01927                                          "spectral resolution computation "
01928                                          "to QC log file");
01929                 }
01930 
01931                 keyname = "QC.PMOS.RESOLUTION.NLINES";
01932 
01933                 if (fors_header_write_int(qclist,
01934                                          cpl_table_get_column_mean(restab, 
01935                                                                    "nlines") *
01936                                          cpl_table_get_nrow(restab),
01937                                          keyname, NULL,
01938                                          "Number of lines for spectral "
01939                                          "resolution computation")) {
01940                     fors_pmos_calib_exit("Cannot write number of examined "
01941                                          "wavelengths in spectral resolution "
01942                                          "computation to QC log file");
01943                 }
01944 
01945             }
01946 
01947             if (!j) {
01948                 if(dfs_save_image_null(frameset, parlist, 
01949                                        spectral_resolution_tag,
01950                                        recipe, version)) {
01951                     fors_pmos_calib_exit(NULL);
01952                 }
01953             }
01954 
01955             header = dfs_load_header(frameset, arc_tag, 0);
01956 
01957             for (k = 0; k < j; k ++) {
01958                 cpl_propertylist_delete(header);
01959                 header = dfs_load_header(frameset, NULL, 0);
01960             }
01961 
01962             cpl_propertylist_append(header, qclist);
01963 
01964             if (dfs_save_table_ext(restab, spectral_resolution_tag, header)) {
01965                 fors_pmos_calib_exit(NULL);
01966             }
01967 
01968             cpl_table_delete(restab); restab = NULL;
01969             cpl_propertylist_delete(qclist); qclist = NULL;
01970             cpl_propertylist_delete(header); header = NULL;
01971 
01972         }
01973         else
01974             fors_pmos_calib_exit("Cannot compute the spectral "
01975                                  "resolution table");
01976 
01977         if (!j) {
01978             if(dfs_save_image_null(frameset, parlist, disp_coeff_tag,
01979                                    recipe, version)) {
01980                 fors_pmos_calib_exit(NULL);
01981             }
01982         }
01983 
01984         header = dfs_load_header(frameset, arc_tag, 0);
01985 
01986         for (k = 0; k < j; k ++) {
01987             cpl_propertylist_delete(header);
01988             header = dfs_load_header(frameset, NULL, 0);
01989         }
01990 
01991         if (dfs_save_table_ext(idscoeff, disp_coeff_tag, header)) {
01992             fors_pmos_calib_exit(NULL);
01993         }
01994 
01995         cpl_propertylist_delete(header);
01996 
01997         if (!j) {
01998             mapped_flat = mos_wavelength_calibration(rect_flat, reference,
01999                                                      startwavelength, 
02000                                                      endwavelength,
02001                                                      dispersion, idscoeff, 0);
02002 
02003             mapped_nflat = mos_wavelength_calibration(rect_nflat, reference,
02004                                                       startwavelength, 
02005                                                       endwavelength,
02006                                                       dispersion, idscoeff, 0);
02007 
02008             cpl_image_delete(rect_flat); rect_flat = NULL;
02009             cpl_image_delete(rect_nflat); rect_nflat = NULL;
02010         }
02011 
02012         /* Global removed */
02013 
02014         cpl_table_delete(idscoeff); idscoeff = NULL;
02015 
02016         header = dfs_load_header(frameset, arc_tag, 0);
02017 
02018         for (k = 0; k < j; k ++) {
02019             cpl_propertylist_delete(header);
02020             header = dfs_load_header(frameset, NULL, 0);
02021         }
02022 
02023         cpl_propertylist_update_double(header, "CRPIX1", 1.0);
02024         cpl_propertylist_update_double(header, "CRPIX2", 1.0);
02025         cpl_propertylist_update_double(header, "CRVAL1", 
02026                                        startwavelength + dispersion/2);
02027         cpl_propertylist_update_double(header, "CRVAL2", 1.0);
02028         /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
02029            cpl_propertylist_update_double(header, "CDELT2", 1.0); */
02030         cpl_propertylist_update_double(header, "CD1_1", dispersion);
02031         cpl_propertylist_update_double(header, "CD1_2", 0.0);
02032         cpl_propertylist_update_double(header, "CD2_1", 0.0);
02033         cpl_propertylist_update_double(header, "CD2_2", 1.0);
02034         cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
02035         cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
02036         cpl_propertylist_update_int(header, "ESO PRO DATANCOM", 1);
02037 
02038         if (!j) {
02039             if(dfs_save_image_null(frameset, parlist, reduced_lamp_tag,
02040                                    recipe, version)) {
02041                 fors_pmos_calib_exit(NULL);
02042             }
02043         }
02044 
02045         if (dfs_save_image_ext(rectified, reduced_lamp_tag, header)) {
02046             fors_pmos_calib_exit(NULL);
02047         }
02048 
02049         cpl_image_delete(rectified); rectified = NULL;
02050 
02051         cpl_propertylist_update_int(header, "ESO PRO DATANCOM", nflats);
02052 
02053         if (!j) {
02054             if (dfs_save_image(frameset, mapped_flat, mapped_screen_flat_tag,
02055                                header, parlist, recipe, version))
02056                 fors_pmos_calib_exit(NULL);
02057             cpl_image_delete(mapped_flat); mapped_flat = NULL;
02058 
02059             if (dfs_save_image(frameset, mapped_nflat, mapped_norm_flat_tag,
02060                                header, parlist, recipe, version))
02061                 fors_pmos_calib_exit(NULL);
02062             cpl_image_delete(mapped_nflat); mapped_nflat = NULL;
02063         }
02064 
02065         cpl_propertylist_delete(header); header = NULL;
02066 
02067         if (check) {
02068             save_header = dfs_load_header(frameset, arc_tag, 0);
02069             for (k = 0; k < j; k ++) {
02070                 cpl_propertylist_delete(save_header);
02071                 save_header = dfs_load_header(frameset, NULL, 0);
02072             }
02073 
02074             cpl_propertylist_update_double(save_header, "CRPIX2", 1.0);
02075             cpl_propertylist_update_double(save_header, "CRVAL2", 1.0);
02076             /* cpl_propertylist_update_double(save_header, "CDELT2", 1.0); */
02077             cpl_propertylist_update_double(save_header, "CD1_1", 1.0);
02078             cpl_propertylist_update_double(save_header, "CD1_2", 0.0);
02079             cpl_propertylist_update_double(save_header, "CD2_1", 0.0);
02080             cpl_propertylist_update_double(save_header, "CD2_2", 1.0);
02081             cpl_propertylist_update_string(save_header, "CTYPE1", "LINEAR");
02082             cpl_propertylist_update_string(save_header, "CTYPE2", "PIXEL");
02083 
02084             if (!j) {
02085                 if(dfs_save_image_null(frameset, parlist, disp_residuals_tag,
02086                                        recipe, version)) {
02087                     fors_pmos_calib_exit(NULL);
02088                 }
02089             }
02090 
02091             if (dfs_save_image_ext(residual, disp_residuals_tag, save_header)) {
02092                 fors_pmos_calib_exit(NULL);
02093             }
02094 
02095             cpl_image_delete(residual); residual = NULL;
02096             cpl_propertylist_delete(save_header); save_header = NULL;
02097         }
02098 
02099         wavemap = mos_map_wavelengths(coordinate, rainbow, slits, polytraces, 
02100                                       reference, startwavelength, endwavelength, 
02101                                       dispersion);
02102 
02103         cpl_image_delete(rainbow); rainbow = NULL;
02104 
02105         save_header = dfs_load_header(frameset, arc_tag, 0);
02106 
02107         for (k = 0; k < j; k ++) {
02108             cpl_propertylist_delete(save_header);
02109             save_header = dfs_load_header(frameset, NULL, 0);
02110         }
02111 
02112         if (qc) {
02113 
02114             /*
02115              * QC1 parameters
02116              */
02117             if (fors_header_write_string(save_header,
02118                                          "QC.DID",
02119                                          "2.0",
02120                                          "QC1 dictionary")) {
02121                 fors_pmos_calib_exit("Cannot write dictionary version "
02122                                      "to QC log file");
02123             }
02124 
02125             if (fors_header_write_double(save_header,
02126                                         mean_rms,
02127                                         "QC.WAVE.ACCURACY",
02128                                         "pixel",
02129                                         "Mean accuracy of wavecalib model")) {
02130                 fors_pmos_calib_exit("Cannot write mean wavelength calibration "
02131                                      "accuracy to QC log file");
02132             }
02133 
02134 
02135             if (fors_header_write_double(save_header,
02136                                         mean_rms_err,
02137                                         "QC.WAVE.ACCURACY.ERROR",
02138                                         "pixel",
02139                                         "Error on accuracy of wavecalib model")) {
02140                 fors_pmos_calib_exit("Cannot write error on wavelength "
02141                                      "calibration accuracy to QC log file");
02142             }
02143 
02144             if (same_offset && fabs(mxpos) < 0.05) { 
02145                                              /* Only if same offset is 0.0 */
02146 
02147                 data = cpl_image_get_data(wavemap);
02148 
02149                 if (fors_header_write_double(save_header,
02150                                            data[nx/2 + ccd_ysize*nx/2],
02151                                            "QC.PMOS.CENTRAL.WAVELENGTH",
02152                                            "Angstrom",
02153                                            "Wavelength at CCD center")) {
02154                     fors_pmos_calib_exit("Cannot write central wavelength "
02155                                          "to QC log file");
02156                 }
02157             }
02158 
02159         }
02160 
02161         if (!j) {
02162             if(dfs_save_image_null(frameset, parlist, wavelength_map_tag,
02163                                    recipe, version)) {
02164                 fors_pmos_calib_exit(NULL);
02165             }
02166         }
02167 
02168         if (dfs_save_image_ext(wavemap, wavelength_map_tag, save_header)) {
02169             fors_pmos_calib_exit(NULL);
02170         }
02171 
02172         cpl_image_delete(wavemap); wavemap = NULL;
02173 
02174         cpl_propertylist_erase_regexp(save_header, "^ESO QC ", 0);
02175 
02176         cpl_propertylist_delete(save_header); save_header = NULL;
02177 
02178         cpl_msg_indent_less();
02179 
02180     }
02181 
02182     if (dfs_save_image(frameset, coordinate, spatial_map_tag, save_header,
02183                        parlist, recipe, version))
02184         fors_pmos_calib_exit(NULL);
02185 
02186     cpl_image_delete(coordinate); coordinate = NULL;
02187     cpl_propertylist_delete(save_header); save_header = NULL;
02188 
02189     header = NULL;
02190 
02191     if (qc) {
02192 
02193         double maxpos, maxneg, maxcurve, maxslope;
02194 
02195         header = dfs_load_header(frameset, arc_tag, 0);
02196 
02197         /*
02198          * QC1 parameters
02199          */
02200         if (fors_header_write_string(header,
02201                                      "QC.DID",
02202                                      "2.0",
02203                                      "QC1 dictionary")) {
02204             fors_pmos_calib_exit("Cannot write dictionary version "
02205                                  "to QC log file");
02206         }
02207 
02208         maxpos = fabs(cpl_table_get_column_max(polytraces, "c2"));
02209         maxneg = fabs(cpl_table_get_column_min(polytraces, "c2"));
02210         maxcurve = maxpos > maxneg ? maxpos : maxneg;
02211         if (fors_header_write_double(header,
02212                                    maxcurve,
02213                                    "QC.TRACE.MAX.CURVATURE",
02214                                    "Y pixel / X pixel ^2",
02215                                    "Max observed curvature "
02216                                    "in spectral tracing")) {
02217             fors_pmos_calib_exit("Cannot write max observed curvature in "
02218                                  "spectral tracing to QC log file");
02219         }
02220 
02221         maxpos = fabs(cpl_table_get_column_max(polytraces, "c1"));
02222         maxneg = fabs(cpl_table_get_column_min(polytraces, "c1"));
02223         maxslope = maxpos > maxneg ? maxpos : maxneg;
02224 
02225         if (fors_header_write_double(header,
02226                                    maxslope,
02227                                    "QC.TRACE.MAX.SLOPE",
02228                                    "Y pixel / X pixel",
02229                                    "Max observed slope in spectral tracing")) {
02230             fors_pmos_calib_exit("Cannot write max observed slope in spectral "
02231                                  "tracing to QC log file");
02232         }
02233     }
02234 
02235     if (dfs_save_table(frameset, polytraces, curv_coeff_tag, header,
02236                        parlist, recipe, version)) {
02237         fors_pmos_calib_exit(NULL);
02238     }
02239 
02240     cpl_propertylist_delete(header); header = NULL;
02241     cpl_table_delete(polytraces); polytraces = NULL;
02242 
02243     /* FIXLANDER It was slit_ident == 0 and 
02244        it was in a different place above in the code */
02245 
02246     if (same_offset) {
02247         cpl_table *globaltbl;
02248         cpl_table *slitpos;
02249         double    *l_ytop;
02250         int       *l_id;
02251         int        npairs;
02252         double    *ytop   = cpl_table_get_data_double(slits, "ytop");
02253         double    *ybot   = cpl_table_get_data_double(slits, "ybottom");
02254         int        k;
02255 // int    *p_id;
02256 
02257         /* Just in case it has been modified */
02258         nslits = cpl_table_get_nrow(slits);
02259 
02260         cpl_table_new_column(slits, "pair_id", CPL_TYPE_INT);
02261 // p_id   = cpl_table_get_data_int(slits, "pair_id");
02262 
02263         if (ccd_ysize == 400 || ccd_ysize == 500) {
02264             
02265             /*
02266              * Special case with old FORS1 chip
02267              */
02268 
02269             l_ytop = cpl_malloc(sizeof(double));
02270             l_ytop[0] = 255.0;
02271             l_id = cpl_malloc(sizeof(double));
02272             l_id[0] = 10;
02273             npairs = 1;
02274         }
02275         else {
02276             globaltbl = dfs_load_table(frameset, master_distortion_tag, 1);
02277             slitpos = mos_build_slit_location(globaltbl, maskslits, ccd_ysize);
02278             l_ytop = cpl_table_get_data_double(slitpos, "ytop");
02279             l_id   = cpl_table_get_data_int(slitpos, "slit_id");
02280             npairs = cpl_table_get_nrow(slitpos);
02281             if (rebin_dist != rebin) {
02282                 float rescale = (float)rebin_dist / rebin;
02283                 for (i = 0; i < npairs; i++) {
02284                    l_ytop[i] *= rescale;
02285                 }
02286             }
02287         }
02288 
02289         for (k = 0; k < npairs; k++) {
02290             int h;
02291 
02292             for (h = 0; h < nslits; h++) {
02293 
02294                 if (l_ytop[k] < ytop[h] && l_ytop[k] > ybot[h]) {
02295                     if (h + 1 < nslits) {
02296                         cpl_table_set_int(slits, "pair_id", h, l_id[k]);
02297                         cpl_table_set_int(slits, "pair_id", h + 1, l_id[k]);
02298                     }
02299                 }
02300             }
02301         }
02302 
02303 /* %%% */
02304 
02305         cpl_table_fill_invalid_int(slits, "pair_id", -1);
02306 
02307         if (ccd_ysize == 400 || ccd_ysize == 500) {
02308             cpl_free(l_ytop);
02309             cpl_free(l_id);
02310         }
02311         else {
02312             cpl_table_delete(slitpos);   slitpos   = NULL;
02313             cpl_table_delete(globaltbl); globaltbl = NULL;
02314     
02315             cpl_table_delete(maskslits); maskslits = NULL;
02316         }
02317     }
02318 
02319     if (dfs_save_table(frameset, slits, slit_location_tag, NULL,
02320                        parlist, recipe, version)) {
02321         fors_pmos_calib_exit(NULL);
02322     }
02323 
02324     cpl_table_delete(slits); slits = NULL;
02325 
02326     cpl_image_delete(spatial); spatial = NULL;
02327 
02328     cpl_free(instrume); instrume = NULL;
02329 
02330     cpl_table_delete(overscans); overscans = NULL;
02331     cpl_image_delete(master_bias); master_bias = NULL;
02332     cpl_image_delete(master_flat); master_flat = NULL;
02333 
02334     cpl_table_delete(wavelengths); wavelengths = NULL;
02335     cpl_vector_delete(lines); lines = NULL;
02336 
02337     if (cpl_error_get_code()) {
02338         cpl_msg_error(cpl_error_get_where(), cpl_error_get_message());
02339         fors_pmos_calib_exit(NULL);
02340     }
02341 
02342     return 0;
02343 }