recipes/fors_extract.c

00001 /* $Id: fors_extract.c,v 1.12 2013-10-09 15:59:38 cgarcia Exp $
00002  *
00003  * This file is part of the FORS Data Reduction Pipeline
00004  * Copyright (C) 2002-2010 European Southern Observatory
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00019  */
00020 
00021 /*
00022  * $Author: cgarcia $
00023  * $Date: 2013-10-09 15:59:38 $
00024  * $Revision: 1.12 $
00025  * $Name: not supported by cvs2svn $
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 
00032 #include <string.h>
00033 #include <math.h>
00034 #include <cpl.h>
00035 #include <moses.h>
00036 #include <fors_dfs.h>
00037 
00038 static int fors_extract_create(cpl_plugin *);
00039 static int fors_extract_exec(cpl_plugin *);
00040 static int fors_extract_destroy(cpl_plugin *);
00041 static int fors_extract(cpl_parameterlist *, cpl_frameset *);
00042 
00043 static char fors_extract_description[] =
00044 "This recipe is used to reduce scientific spectra using the global\n"
00045 "distortion table created by the recipe fors_calib. The spectra are\n"
00046 "bias subtracted, flat fielded (if a normalised flat field is specified)\n"
00047 "and remapped eliminating the optical distortions. The wavelength calibration\n"
00048 "can be optionally upgraded using a number of sky lines: if no sky lines\n"
00049 "catalog of wavelengths is specified, an internal one is used instead.\n"
00050 "If the alignment to the sky lines is performed, the applied dispersion\n"
00051 "coefficient table is upgraded and saved to disk, and a new CCD wavelengths\n"
00052 "map is created.\n"
00053 "This recipe accepts both FORS1 and FORS2 frames. A grism table (typically\n"
00054 "depending on the instrument mode, and in particular on the grism used)\n"
00055 "may also be specified: this table contains a default recipe parameter\n" 
00056 "setting to control the way spectra are extracted for a specific instrument\n"
00057 "mode, as it is used for automatic run of the pipeline on Paranal and in\n" 
00058 "Garching. If this table is specified, it will modify the default recipe\n" 
00059 "parameter setting, with the exception of those parameters which have been\n" 
00060 "explicitly modifyed on the command line. If a grism table is not specified,\n"
00061 "the input recipe parameters values will always be read from the command\n" 
00062 "line, or from an esorex configuration file if present, or from their\n" 
00063 "generic default values (that are rarely meaningful).\n" 
00064 "In the table below the MXU acronym can be read alternatively as MOS\n"
00065 "and LSS, depending on the instrument mode of the input data. Either a\n"
00066 "scientific or a standard star exposure can be specified in input (not\n"
00067 "both).\n\n"
00068 "Input files:\n\n"
00069 "  DO category:               Type:       Explanation:         Required:\n"
00070 "  SCIENCE_MXU                Raw         Scientific exposure     Y\n"
00071 "  or STANDARD_MXU            Raw         Standard star exposure  Y\n"
00072 "  MASTER_BIAS                Calib       Master bias             Y\n"
00073 "  GRISM_TABLE                Calib       Grism table             .\n"
00074 "  MASTER_SKYLINECAT          Calib       Sky lines catalog       .\n"
00075 "\n"
00076 "  MASTER_NORM_FLAT_MXU       Calib       Normalised flat field   .\n"
00077 "  MASTER_DISTORTION_TABLE    Calib       Global distortion model .\n"
00078 "\n"
00079 "  or, in case of LSS-like MOS/MXU data,\n"
00080 "\n"
00081 "  MASTER_NORM_FLAT_LONG_MXU  Calib       Normalised flat field   .\n"
00082 "Output files:\n\n"
00083 "  DO category:               Data type:  Explanation:\n"
00084 "  REDUCED_SCI_MXU            FITS image  Extracted scientific spectra\n"
00085 "  REDUCED_SKY_SCI_MXU        FITS image  Extracted sky spectra\n"
00086 "  REDUCED_ERROR_SCI_MXU      FITS image  Errors on extracted spectra\n"
00087 "  UNMAPPED_SCI_MXU           FITS image  Sky subtracted scientific spectra\n"
00088 "  MAPPED_SCI_MXU             FITS image  Rectified scientific spectra\n"
00089 "  MAPPED_ALL_SCI_MXU         FITS image  Rectified science spectra with sky\n"
00090 "  MAPPED_SKY_SCI_MXU         FITS image  Rectified sky spectra\n"
00091 "  UNMAPPED_SKY_SCI_MXU           FITS image  Sky on CCD\n"
00092 "  GLOBAL_SKY_SPECTRUM_MXU    FITS table  Global sky spectrum\n"
00093 "  OBJECT_TABLE_SCI_MXU       FITS table  Positions of detected objects\n"
00094 "\n"
00095 "  Only if the sky-alignment of the wavelength solution is requested:\n"
00096 "  SKY_SHIFTS_LONG_SCI_MXU    FITS table  Sky lines offsets (LSS-like data)\n"
00097 "  or SKY_SHIFTS_SLIT_SCI_MXU FITS table  Sky lines offsets (MOS-like data)\n"
00098 "  DISP_COEFF_SCI_MXU         FITS table  Upgraded dispersion coefficients\n"
00099 "  WAVELENGTH_MAP_SCI_MXU     FITS image  Upgraded wavelength map\n\n";
00100 
00101 #define fors_extract_exit(message)            \
00102 {                                             \
00103 if ((const char *)message != NULL) cpl_msg_error(recipe, message);  \
00104 cpl_free(exptime);                            \
00105 cpl_free(instrume);                           \
00106 cpl_image_delete(dummy);                      \
00107 cpl_image_delete(mapped);                     \
00108 cpl_image_delete(mapped_sky);                 \
00109 cpl_image_delete(mapped_cleaned);             \
00110 cpl_image_delete(skylocalmap);                \
00111 cpl_image_delete(skymap);                     \
00112 cpl_image_delete(smapped);                    \
00113 cpl_table_delete(offsets);                    \
00114 cpl_table_delete(global);                     \
00115 cpl_table_delete(sky);                        \
00116 cpl_image_delete(bias);                       \
00117 cpl_image_delete(spectra);                    \
00118 cpl_image_delete(coordinate);                 \
00119 cpl_image_delete(norm_flat);                  \
00120 cpl_image_delete(rainbow);                    \
00121 cpl_image_delete(rectified);                  \
00122 cpl_image_delete(wavemap);                    \
00123 cpl_propertylist_delete(header);              \
00124 cpl_propertylist_delete(save_header);         \
00125 cpl_table_delete(grism_table);                \
00126 cpl_table_delete(idscoeff);                   \
00127 cpl_table_delete(maskslits);                  \
00128 cpl_table_delete(overscans);                  \
00129 cpl_table_delete(polytraces);                 \
00130 cpl_table_delete(slits);                      \
00131 cpl_table_delete(wavelengths);                \
00132 cpl_vector_delete(lines);                     \
00133 cpl_msg_indent_less();                        \
00134 return -1;                                    \
00135 }
00136 
00137 
00138 #define fors_extract_exit_memcheck(message)            \
00139 {                                                      \
00140 if ((const char *)message != NULL) cpl_msg_info(recipe, message);            \
00141 printf("free exptime (%p)\n", exptime);                \
00142 cpl_free(exptime);                                     \
00143 printf("free instrume (%p)\n", instrume);              \
00144 cpl_free(instrume);                                    \
00145 printf("free dummy (%p)\n", dummy);                    \
00146 cpl_image_delete(dummy);                               \
00147 printf("free mapped (%p)\n", mapped);                  \
00148 cpl_image_delete(mapped);                              \
00149 printf("free mapped_cleaned (%p)\n", mapped_cleaned);  \
00150 cpl_image_delete(mapped_cleaned);                      \
00151 printf("free mapped_sky (%p)\n", mapped_sky);          \
00152 cpl_image_delete(mapped_sky);                          \
00153 printf("free skylocalmap (%p)\n", skylocalmap);        \
00154 cpl_image_delete(skylocalmap);                         \
00155 printf("free skymap (%p)\n", skymap);                  \
00156 cpl_image_delete(skymap);                              \
00157 printf("free smapped (%p)\n", smapped);                \
00158 cpl_image_delete(smapped);                             \
00159 printf("free offsets (%p)\n", offsets);                \
00160 cpl_table_delete(offsets);                             \
00161 printf("free global (%p)\n", global);                  \
00162 cpl_table_delete(global);                              \
00163 printf("free sky (%p)\n", sky);                        \
00164 cpl_table_delete(sky);                                 \
00165 printf("free bias (%p)\n", bias);                      \
00166 cpl_image_delete(bias);                                \
00167 printf("free spectra (%p)\n", spectra);                \
00168 cpl_image_delete(spectra);                             \
00169 printf("free coordinate (%p)\n", coordinate);          \
00170 cpl_image_delete(coordinate);                          \
00171 printf("free norm_flat (%p)\n", norm_flat);            \
00172 cpl_image_delete(norm_flat);                           \
00173 printf("free rainbow (%p)\n", rainbow);                \
00174 cpl_image_delete(rainbow);                             \
00175 printf("free rectified (%p)\n", rectified);            \
00176 cpl_image_delete(rectified);                           \
00177 printf("free wavemap (%p)\n", wavemap);                \
00178 cpl_image_delete(wavemap);                             \
00179 printf("free header (%p)\n", header);                  \
00180 cpl_propertylist_delete(header);                       \
00181 printf("free save_header (%p)\n", save_header);        \
00182 cpl_propertylist_delete(save_header);                  \
00183 printf("free grism_table (%p)\n", grism_table);        \
00184 cpl_table_delete(grism_table);                         \
00185 printf("free idscoeff (%p)\n", idscoeff);              \
00186 cpl_table_delete(idscoeff);                            \
00187 printf("free maskslits (%p)\n", maskslits);            \
00188 cpl_table_delete(maskslits);                           \
00189 printf("free overscans (%p)\n", overscans);            \
00190 cpl_table_delete(overscans);                           \
00191 printf("free polytraces (%p)\n", polytraces);          \
00192 cpl_table_delete(polytraces);                          \
00193 printf("free slits (%p)\n", slits);                    \
00194 cpl_table_delete(slits);                               \
00195 printf("free wavelengths (%p)\n", wavelengths);        \
00196 cpl_table_delete(wavelengths);                         \
00197 printf("free lines (%p)\n", lines);                    \
00198 cpl_vector_delete(lines);                              \
00199 cpl_msg_indent_less();                                 \
00200 return 0;                                              \
00201 }
00202 
00203 
00215 int cpl_plugin_get_info(cpl_pluginlist *list)
00216 {
00217     cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe );
00218     cpl_plugin *plugin = &recipe->interface;
00219 
00220     cpl_plugin_init(plugin,
00221                     CPL_PLUGIN_API,
00222                     FORS_BINARY_VERSION,
00223                     CPL_PLUGIN_TYPE_RECIPE,
00224                     "fors_extract",
00225                     "Extraction of scientific spectra",
00226                     fors_extract_description,
00227                     "Carlo Izzo",
00228                     PACKAGE_BUGREPORT,
00229     "This file is currently part of the FORS Instrument Pipeline\n"
00230     "Copyright (C) 2002-2010 European Southern Observatory\n\n"
00231     "This program is free software; you can redistribute it and/or modify\n"
00232     "it under the terms of the GNU General Public License as published by\n"
00233     "the Free Software Foundation; either version 2 of the License, or\n"
00234     "(at your option) any later version.\n\n"
00235     "This program is distributed in the hope that it will be useful,\n"
00236     "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
00237     "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
00238     "GNU General Public License for more details.\n\n"
00239     "You should have received a copy of the GNU General Public License\n"
00240     "along with this program; if not, write to the Free Software Foundation,\n"
00241     "Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n",
00242                     fors_extract_create,
00243                     fors_extract_exec,
00244                     fors_extract_destroy);
00245 
00246     cpl_pluginlist_append(list, plugin);
00247     
00248     return 0;
00249 }
00250 
00251 
00262 static int fors_extract_create(cpl_plugin *plugin)
00263 {
00264     cpl_recipe    *recipe;
00265     cpl_parameter *p;
00266 
00267 
00268     /* 
00269      * Check that the plugin is part of a valid recipe 
00270      */
00271 
00272     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00273         recipe = (cpl_recipe *)plugin;
00274     else 
00275         return -1;
00276 
00277     /* 
00278      * Create the parameters list in the cpl_recipe object 
00279      */
00280 
00281     recipe->parameters = cpl_parameterlist_new(); 
00282 
00283 
00284     /*
00285      * Dispersion
00286      */
00287 
00288     p = cpl_parameter_new_value("fors.fors_extract.dispersion",
00289                                 CPL_TYPE_DOUBLE,
00290                                 "Resampling step (Angstrom/pixel)",
00291                                 "fors.fors_extract",
00292                                 0.0);
00293     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dispersion");
00294     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00295     cpl_parameterlist_append(recipe->parameters, p);
00296 
00297     /*
00298      * Sky lines alignment
00299      */
00300 
00301     p = cpl_parameter_new_value("fors.fors_extract.skyalign",
00302                                 CPL_TYPE_INT,
00303                                 "Polynomial order for sky lines alignment, "
00304                                 "or -1 to avoid alignment",
00305                                 "fors.fors_extract",
00306                                 0);
00307     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skyalign");
00308     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00309     cpl_parameterlist_append(recipe->parameters, p);
00310 
00311     /*
00312      * Line catalog table column containing the sky reference wavelengths
00313      */
00314 
00315     p = cpl_parameter_new_value("fors.fors_extract.wcolumn",
00316                                 CPL_TYPE_STRING,
00317                                 "Name of sky line catalog table column "
00318                                 "with wavelengths",
00319                                 "fors.fors_extract",
00320                                 "WLEN");
00321     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcolumn");
00322     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00323     cpl_parameterlist_append(recipe->parameters, p);
00324 
00325     /*
00326      * Start wavelength for spectral extraction
00327      */
00328 
00329     p = cpl_parameter_new_value("fors.fors_extract.startwavelength",
00330                                 CPL_TYPE_DOUBLE,
00331                                 "Start wavelength in spectral extraction",
00332                                 "fors.fors_extract",
00333                                 0.0);
00334     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "startwavelength");
00335     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00336     cpl_parameterlist_append(recipe->parameters, p);
00337 
00338     /*
00339      * End wavelength for spectral extraction
00340      */
00341 
00342     p = cpl_parameter_new_value("fors.fors_extract.endwavelength",
00343                                 CPL_TYPE_DOUBLE,
00344                                 "End wavelength in spectral extraction",
00345                                 "fors.fors_extract",
00346                                 0.0);
00347     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "endwavelength");
00348     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00349     cpl_parameterlist_append(recipe->parameters, p);
00350 
00351     /*
00352      * Flux conservation
00353      */
00354 
00355     p = cpl_parameter_new_value("fors.fors_extract.flux",
00356                                 CPL_TYPE_BOOL,
00357                                 "Apply flux conservation",
00358                                 "fors.fors_extract",
00359                                 TRUE);
00360     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "flux");
00361     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00362     cpl_parameterlist_append(recipe->parameters, p);
00363 
00364     /*
00365      * Apply flat field
00366      */
00367 
00368     p = cpl_parameter_new_value("fors.fors_extract.flatfield",
00369                                 CPL_TYPE_BOOL,
00370                                 "Apply flat field",
00371                                 "fors.fors_extract",
00372                                 FALSE);
00373     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "flatfield");
00374     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00375     cpl_parameterlist_append(recipe->parameters, p);
00376 
00377     /*
00378      * Global sky subtraction
00379      */
00380 
00381     p = cpl_parameter_new_value("fors.fors_extract.skyglobal",
00382                                 CPL_TYPE_BOOL,
00383                                 "Subtract global sky spectrum from CCD",
00384                                 "fors.fors_extract",
00385                                 FALSE);
00386     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skyglobal");
00387     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00388     cpl_parameterlist_append(recipe->parameters, p);
00389 
00390     /*
00391      * Local sky subtraction on extracted spectra
00392      */
00393 
00394 /*** New sky subtraction (search NSS)
00395     p = cpl_parameter_new_value("fors.fors_extract.skymedian",
00396                                 CPL_TYPE_INT,
00397                                 "Degree of sky fitting polynomial for "
00398                                 "sky subtraction from extracted "
00399                                 "slit spectra (MOS/MXU only, -1 to disable it)",
00400                                 "fors.fors_extract",
00401                                 0);
00402     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skymedian");
00403     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00404     cpl_parameterlist_append(recipe->parameters, p);
00405 ***/
00406 
00407     p = cpl_parameter_new_value("fors.fors_extract.skymedian",
00408                                 CPL_TYPE_BOOL,
00409                                 "Sky subtraction from extracted slit spectra",
00410                                 "fors.fors_extract",
00411                                 FALSE);
00412     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skymedian");
00413     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00414     cpl_parameterlist_append(recipe->parameters, p);
00415 
00416     /*
00417      * Local sky subtraction on CCD spectra
00418      */
00419 
00420     p = cpl_parameter_new_value("fors.fors_extract.skylocal",
00421                                 CPL_TYPE_BOOL,
00422                                 "Sky subtraction from CCD slit spectra",
00423                                 "fors.fors_extract",
00424                                 TRUE);
00425     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "skylocal");
00426     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00427     cpl_parameterlist_append(recipe->parameters, p);
00428 
00429     /*
00430      * Cosmic rays removal
00431      */
00432 
00433     p = cpl_parameter_new_value("fors.fors_extract.cosmics",
00434                                 CPL_TYPE_BOOL,
00435                                 "Eliminate cosmic rays hits (only if global "
00436                                 "sky subtraction is also requested)",
00437                                 "fors.fors_extract",
00438                                 FALSE);
00439     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "cosmics");
00440     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00441     cpl_parameterlist_append(recipe->parameters, p);
00442 
00443     /*
00444      * Slit margin
00445      */
00446 
00447     p = cpl_parameter_new_value("fors.fors_extract.slit_margin",
00448                                 CPL_TYPE_INT,
00449                                 "Number of pixels to exclude at each slit "
00450                                 "in object detection and extraction",
00451                                 "fors.fors_extract",
00452                                 3);
00453     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "slit_margin");
00454     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00455     cpl_parameterlist_append(recipe->parameters, p);
00456 
00457     /*
00458      * Extraction radius
00459      */
00460 
00461     p = cpl_parameter_new_value("fors.fors_extract.ext_radius",
00462                                 CPL_TYPE_INT,
00463                                 "Maximum extraction radius for detected "
00464                                 "objects (pixel)",
00465                                 "fors.fors_extract",
00466                                 6);
00467     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ext_radius");
00468     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00469     cpl_parameterlist_append(recipe->parameters, p);
00470 
00471     /*
00472      * Contamination radius
00473      */
00474 
00475     p = cpl_parameter_new_value("fors.fors_extract.cont_radius",
00476                                 CPL_TYPE_INT,
00477                                 "Minimum distance at which two objects "
00478                                 "of equal luminosity do not contaminate "
00479                                 "each other (pixel)",
00480                                 "fors.fors_extract",
00481                                 0);
00482     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "cont_radius");
00483     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00484     cpl_parameterlist_append(recipe->parameters, p);
00485 
00486     /*
00487      * Object extraction method
00488      */
00489 
00490     p = cpl_parameter_new_value("fors.fors_extract.ext_mode",
00491                                 CPL_TYPE_INT,
00492                                 "Object extraction method: 0 = aperture, "
00493                                 "1 = Horne optimal extraction",
00494                                 "fors.fors_extract",
00495                                 1);
00496     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ext_mode");
00497     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00498     cpl_parameterlist_append(recipe->parameters, p);
00499 
00500     /*
00501      * Normalise output by exposure time
00502      */
00503 
00504     p = cpl_parameter_new_value("fors.fors_extract.time_normalise",
00505                                 CPL_TYPE_BOOL,
00506                                 "Normalise output spectra by the exposure time",
00507                                 "fors.fors_extract",
00508                                 TRUE);
00509     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "time_normalise");
00510     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00511     cpl_parameterlist_append(recipe->parameters, p);
00512 
00513     return 0;
00514 }
00515 
00516 
00525 static int fors_extract_exec(cpl_plugin *plugin)
00526 {
00527     cpl_recipe *recipe;
00528     
00529     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00530         recipe = (cpl_recipe *)plugin;
00531     else 
00532         return -1;
00533 
00534     return fors_extract(recipe->parameters, recipe->frames);
00535 }
00536 
00537 
00546 static int fors_extract_destroy(cpl_plugin *plugin)
00547 {
00548     cpl_recipe *recipe;
00549     
00550     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00551         recipe = (cpl_recipe *)plugin;
00552     else 
00553         return -1;
00554 
00555     cpl_parameterlist_delete(recipe->parameters); 
00556 
00557     return 0;
00558 }
00559 
00560 
00570 static int fors_extract(cpl_parameterlist *parlist, cpl_frameset *frameset)
00571 {
00572 
00573     const char *recipe = "fors_extract";
00574 
00575 
00576     /*
00577      * Input parameters
00578      */
00579 
00580     double      dispersion;
00581     int         skyalign;
00582     const char *wcolumn;
00583     double      startwavelength;
00584     double      endwavelength;
00585     int         flux;
00586     int         flatfield;
00587     int         skyglobal;
00588     int         skylocal;
00589     int         skymedian;
00590     int         cosmics;
00591     int         slit_margin;
00592     int         ext_radius;
00593     int         cont_radius;
00594     int         ext_mode;
00595     int         time_normalise;
00596 
00597 
00598     /*
00599      * CPL objects
00600      */
00601 
00602     cpl_imagelist    *all_science;
00603     cpl_image       **images;
00604 
00605     cpl_image        *bias           = NULL;
00606     cpl_image        *norm_flat      = NULL;
00607     cpl_image        *spectra        = NULL;
00608     cpl_image        *rectified      = NULL;
00609     cpl_image        *coordinate     = NULL;
00610     cpl_image        *rainbow        = NULL;
00611     cpl_image        *mapped         = NULL;
00612     cpl_image        *mapped_sky     = NULL;
00613     cpl_image        *mapped_cleaned = NULL;
00614     cpl_image        *smapped        = NULL;
00615     cpl_image        *wavemap        = NULL;
00616     cpl_image        *skymap         = NULL;
00617     cpl_image        *skylocalmap    = NULL;
00618     cpl_image        *dummy          = NULL;
00619 
00620     cpl_table        *grism_table    = NULL;
00621     cpl_table        *overscans      = NULL;
00622     cpl_table        *wavelengths    = NULL;
00623     cpl_table        *idscoeff       = NULL;
00624     cpl_table        *slits          = NULL;
00625     cpl_table        *maskslits      = NULL;
00626     cpl_table        *polytraces     = NULL;
00627     cpl_table        *offsets        = NULL;
00628     cpl_table        *sky            = NULL;
00629     cpl_table        *global         = NULL;
00630 
00631     cpl_vector       *lines          = NULL;
00632 
00633     cpl_propertylist *header         = NULL;
00634     cpl_propertylist *save_header    = NULL;
00635 
00636     /*
00637      * Auxiliary variables
00638      */
00639 
00640     char         version[80];
00641     char        *instrume = NULL;
00642     char        *wheel4 = NULL;
00643     const char  *science_tag;
00644     const char  *master_norm_flat_tag;
00645     const char  *disp_coeff_sky_tag;
00646     const char  *wavelength_map_sky_tag;
00647     const char  *reduced_science_tag;
00648     const char  *reduced_sky_tag;
00649     const char  *reduced_error_tag;
00650     const char  *mapped_science_tag;
00651     const char  *unmapped_science_tag;
00652     const char  *mapped_science_sky_tag;
00653     const char  *mapped_sky_tag;
00654     const char  *unmapped_sky_tag;
00655     const char  *global_sky_spectrum_tag;
00656     const char  *object_table_tag;
00657     const char  *skylines_offsets_tag;
00658     const char  *global_distortion_tag = "MASTER_DISTORTION_TABLE";
00659     char        *coll;
00660     int         mxu, mos, lss;
00661     int         nscience;
00662     double     *exptime = NULL;
00663     double      alltime;
00664     double      mean_rms;
00665     int         nlines;
00666     int         rebin;
00667     double     *line;
00668     int         nx, ny;
00669     double      reference;
00670     double      gain;
00671     double      ron;
00672     int         standard;
00673     int         highres;
00674     int         narrow = 0;
00675     int         i;
00676 
00677 
00678     snprintf(version, 80, "%s-%s", PACKAGE, PACKAGE_VERSION);
00679 
00680     cpl_msg_set_indentation(2);
00681 
00682     /* 
00683      * Get configuration parameters
00684      */
00685 
00686     cpl_msg_info(recipe, "Recipe %s configuration parameters:", recipe);
00687     cpl_msg_indent_more();
00688 
00689     if (cpl_frameset_count_tags(frameset, "GRISM_TABLE") > 1)
00690         fors_extract_exit("Too many in input: GRISM_TABLE");
00691 
00692     grism_table = dfs_load_table(frameset, "GRISM_TABLE", 1);
00693 
00694     dispersion = dfs_get_parameter_double(parlist, 
00695                     "fors.fors_extract.dispersion", grism_table);
00696 
00697     if (dispersion <= 0.0)
00698         fors_extract_exit("Invalid resampling step");
00699 
00700     skyalign = dfs_get_parameter_int(parlist, 
00701                     "fors.fors_extract.skyalign", NULL);
00702 
00703     if (skyalign > 2)
00704         fors_extract_exit("Max polynomial degree for sky alignment is 2");
00705 
00706     wcolumn = dfs_get_parameter_string(parlist, 
00707                     "fors.fors_extract.wcolumn", NULL);
00708 
00709     startwavelength = dfs_get_parameter_double(parlist, 
00710                     "fors.fors_extract.startwavelength", grism_table);
00711     if (startwavelength < 3000.0 || startwavelength > 13000.0)
00712         fors_extract_exit("Invalid wavelength");
00713 
00714     endwavelength = dfs_get_parameter_double(parlist, 
00715                     "fors.fors_extract.endwavelength", grism_table);
00716     if (endwavelength < 3000.0 || endwavelength > 13000.0)
00717         fors_extract_exit("Invalid wavelength");
00718 
00719     if (endwavelength - startwavelength <= 0.0)
00720         fors_extract_exit("Invalid wavelength interval");
00721 
00722     flux = dfs_get_parameter_bool(parlist, "fors.fors_extract.flux", NULL);
00723 
00724     flatfield = dfs_get_parameter_bool(parlist, "fors.fors_extract.flatfield", 
00725                                        NULL);
00726 
00727     skyglobal = dfs_get_parameter_bool(parlist, "fors.fors_extract.skyglobal", 
00728                                        NULL);
00729     skylocal  = dfs_get_parameter_bool(parlist, "fors.fors_extract.skylocal", 
00730                                        NULL);
00731     skymedian = dfs_get_parameter_bool(parlist, "fors.fors_extract.skymedian", 
00732                                        NULL);
00733 /* NSS
00734     skymedian = dfs_get_parameter_int(parlist, "fors.fors_extract.skymedian", 
00735                                        NULL);
00736 */
00737 
00738     if (skylocal && skyglobal)
00739         fors_extract_exit("Cannot apply both local and global sky subtraction");
00740 
00741     if (skylocal && skymedian)
00742         fors_extract_exit("Cannot apply sky subtraction both on extracted "
00743                           "and non-extracted spectra");
00744 
00745     cosmics = dfs_get_parameter_bool(parlist, 
00746                                      "fors.fors_extract.cosmics", NULL);
00747 
00748     if (cosmics)
00749         if (!(skyglobal || skylocal))
00750             fors_extract_exit("Cosmic rays correction requires "
00751                               "either skylocal=true or skyglobal=true");
00752 
00753     slit_margin = dfs_get_parameter_int(parlist, 
00754                                         "fors.fors_extract.slit_margin",
00755                                         NULL);
00756     if (slit_margin < 0)
00757         fors_extract_exit("Value must be zero or positive");
00758 
00759     ext_radius = dfs_get_parameter_int(parlist, 
00760                                        "fors.fors_extract.ext_radius",
00761                                        NULL);
00762     if (ext_radius < 0)
00763         fors_extract_exit("Value must be zero or positive");
00764 
00765     cont_radius = dfs_get_parameter_int(parlist, 
00766                                         "fors.fors_extract.cont_radius",
00767                                        NULL);
00768     if (cont_radius < 0)
00769         fors_extract_exit("Value must be zero or positive");
00770 
00771     ext_mode = dfs_get_parameter_int(parlist, "fors.fors_extract.ext_mode",
00772                                        NULL);
00773     if (ext_mode < 0 || ext_mode > 1)
00774         fors_extract_exit("Invalid object extraction mode");
00775 
00776     time_normalise = dfs_get_parameter_bool(parlist, 
00777                              "fors.fors_extract.time_normalise", NULL);
00778 
00779     cpl_table_delete(grism_table); grism_table = NULL;
00780 
00781     if (cpl_error_get_code())
00782         fors_extract_exit("Failure getting the configuration parameters");
00783 
00784     
00785     /* 
00786      * Check input set-of-frames
00787      */
00788 
00789     cpl_msg_indent_less();
00790     cpl_msg_info(recipe, "Check input set-of-frames:");
00791     cpl_msg_indent_more();
00792 
00793     if (!dfs_equal_keyword(frameset, "ESO INS GRIS1 ID"))
00794         cpl_msg_warning(cpl_func,"Input frames are not from the same grism");
00795 
00796     if (!dfs_equal_keyword(frameset, "ESO INS FILT1 ID"))
00797         cpl_msg_warning(cpl_func,"Input frames are not from the same filter");
00798 
00799     if (!dfs_equal_keyword(frameset, "ESO DET CHIP1 ID"))
00800         cpl_msg_warning(cpl_func,"Input frames are not from the same chip");
00801 
00802     mxu = cpl_frameset_count_tags(frameset, "SCIENCE_MXU");
00803     mos = cpl_frameset_count_tags(frameset, "SCIENCE_MOS");
00804     lss = cpl_frameset_count_tags(frameset, "SCIENCE_LSS");
00805     standard = 0;
00806 
00807     if (mxu + mos + lss == 0) {
00808         mxu = cpl_frameset_count_tags(frameset, "STANDARD_MXU");
00809         mos = cpl_frameset_count_tags(frameset, "STANDARD_MOS");
00810         lss = cpl_frameset_count_tags(frameset, "STANDARD_LSS");
00811         standard = 1;
00812     }
00813 
00814     if (mxu + mos + lss == 0)
00815         fors_extract_exit("Missing input scientific frame");
00816 
00817     nscience = mxu + mos + lss;
00818 
00819     if (mxu && mxu < nscience)
00820         fors_extract_exit("Input scientific frames must be of the same type"); 
00821 
00822     if (mos && mos < nscience)
00823         fors_extract_exit("Input scientific frames must be of the same type"); 
00824 
00825     if (lss && lss < nscience)
00826         fors_extract_exit("Input scientific frames must be of the same type"); 
00827 
00828     if (mxu) {
00829         if (standard) {
00830             cpl_msg_info(recipe, "MXU data found");
00831             science_tag            = "STANDARD_MXU";
00832             reduced_science_tag    = "REDUCED_STD_MXU";
00833             unmapped_science_tag   = "UNMAPPED_STD_MXU";
00834             mapped_science_tag     = "MAPPED_STD_MXU";
00835             mapped_science_sky_tag = "MAPPED_ALL_STD_MXU";
00836             skylines_offsets_tag   = "SKY_SHIFTS_SLIT_STD_MXU";
00837             wavelength_map_sky_tag = "WAVELENGTH_MAP_STD_MXU";
00838             disp_coeff_sky_tag     = "DISP_COEFF_STD_MXU";
00839             mapped_sky_tag         = "MAPPED_SKY_STD_MXU";
00840             unmapped_sky_tag       = "UNMAPPED_SKY_STD_MXU";
00841             object_table_tag       = "OBJECT_TABLE_STD_MXU";
00842             reduced_sky_tag        = "REDUCED_SKY_STD_MXU";
00843             reduced_error_tag      = "REDUCED_ERROR_STD_MXU";
00844         }
00845         else {
00846             cpl_msg_info(recipe, "MXU data found");
00847             science_tag            = "SCIENCE_MXU";
00848             reduced_science_tag    = "REDUCED_SCI_MXU";
00849             unmapped_science_tag   = "UNMAPPED_SCI_MXU";
00850             mapped_science_tag     = "MAPPED_SCI_MXU";
00851             mapped_science_sky_tag = "MAPPED_ALL_SCI_MXU";
00852             skylines_offsets_tag   = "SKY_SHIFTS_SLIT_SCI_MXU";
00853             wavelength_map_sky_tag = "WAVELENGTH_MAP_SCI_MXU";
00854             disp_coeff_sky_tag     = "DISP_COEFF_SCI_MXU";
00855             mapped_sky_tag         = "MAPPED_SKY_SCI_MXU";
00856             unmapped_sky_tag       = "UNMAPPED_SKY_SCI_MXU";
00857             object_table_tag       = "OBJECT_TABLE_SCI_MXU";
00858             reduced_sky_tag        = "REDUCED_SKY_SCI_MXU";
00859             reduced_error_tag      = "REDUCED_ERROR_SCI_MXU";
00860         }
00861 
00862         master_norm_flat_tag    = "MASTER_NORM_FLAT_MXU";
00863         global_sky_spectrum_tag = "GLOBAL_SKY_SPECTRUM_MXU";
00864 
00865         if (!cpl_frameset_count_tags(frameset, master_norm_flat_tag)) {
00866             master_norm_flat_tag    = "MASTER_NORM_FLAT_LONG_MXU";
00867         }
00868     }
00869 
00870     if (lss) {
00871 
00872         if (cosmics && !skyglobal)
00873             fors_extract_exit("Cosmic rays correction for LSS "
00874                               "data requires --skyglobal=true");
00875 
00876         cpl_msg_info(recipe, "LSS data found");
00877 
00878         if (standard) {
00879             science_tag             = "STANDARD_LSS";
00880             reduced_science_tag     = "REDUCED_STD_LSS";
00881             unmapped_science_tag    = "UNMAPPED_STD_LSS";
00882             mapped_science_tag      = "MAPPED_STD_LSS";
00883             mapped_science_sky_tag  = "MAPPED_ALL_STD_LSS";
00884             skylines_offsets_tag    = "SKY_SHIFTS_LONG_STD_LSS";
00885             wavelength_map_sky_tag  = "WAVELENGTH_MAP_STD_LSS";
00886             disp_coeff_sky_tag      = "DISP_COEFF_STD_LSS";
00887             mapped_sky_tag          = "MAPPED_SKY_STD_LSS";
00888             unmapped_sky_tag        = "UNMAPPED_SKY_STD_LSS";
00889             object_table_tag        = "OBJECT_TABLE_STD_LSS";
00890             reduced_sky_tag         = "REDUCED_SKY_STD_LSS";
00891             reduced_error_tag       = "REDUCED_ERROR_STD_LSS";
00892         }
00893         else {
00894             science_tag             = "SCIENCE_LSS";
00895             reduced_science_tag     = "REDUCED_SCI_LSS";
00896             unmapped_science_tag    = "UNMAPPED_SCI_LSS";
00897             mapped_science_tag      = "MAPPED_SCI_LSS";
00898             mapped_science_sky_tag  = "MAPPED_ALL_SCI_LSS";
00899             skylines_offsets_tag    = "SKY_SHIFTS_LONG_SCI_LSS";
00900             wavelength_map_sky_tag  = "WAVELENGTH_MAP_SCI_LSS";
00901             disp_coeff_sky_tag      = "DISP_COEFF_SCI_LSS";
00902             mapped_sky_tag          = "MAPPED_SKY_SCI_LSS";
00903             unmapped_sky_tag        = "UNMAPPED_SKY_SCI_LSS";
00904             object_table_tag        = "OBJECT_TABLE_SCI_LSS";
00905             reduced_sky_tag         = "REDUCED_SKY_SCI_LSS";
00906             reduced_error_tag       = "REDUCED_ERROR_SCI_LSS";
00907         }
00908 
00909         master_norm_flat_tag    = "MASTER_NORM_FLAT_LSS";
00910         global_sky_spectrum_tag = "GLOBAL_SKY_SPECTRUM_LSS";
00911     }
00912 
00913     if (mos) {
00914         cpl_msg_info(recipe, "MOS data found");
00915         if (standard) {
00916             science_tag             = "STANDARD_MOS";
00917             reduced_science_tag     = "REDUCED_STD_MOS";
00918             unmapped_science_tag    = "UNMAPPED_STD_MOS";
00919             mapped_science_tag      = "MAPPED_STD_MOS";
00920             mapped_science_sky_tag  = "MAPPED_ALL_STD_MOS";
00921             skylines_offsets_tag    = "SKY_SHIFTS_SLIT_STD_MOS";
00922             wavelength_map_sky_tag  = "WAVELENGTH_MAP_STD_MOS";
00923             disp_coeff_sky_tag      = "DISP_COEFF_STD_MOS";
00924             mapped_sky_tag          = "MAPPED_SKY_STD_MOS";
00925             unmapped_sky_tag        = "UNMAPPED_SKY_STD_MOS";
00926             object_table_tag        = "OBJECT_TABLE_STD_MOS";
00927             reduced_sky_tag         = "REDUCED_SKY_STD_MOS";
00928             reduced_error_tag       = "REDUCED_ERROR_STD_MOS";
00929         }
00930         else {
00931             science_tag             = "SCIENCE_MOS";
00932             reduced_science_tag     = "REDUCED_SCI_MOS";
00933             unmapped_science_tag    = "UNMAPPED_SCI_MOS";
00934             mapped_science_tag      = "MAPPED_SCI_MOS";
00935             mapped_science_sky_tag  = "MAPPED_ALL_SCI_MOS";
00936             skylines_offsets_tag    = "SKY_SHIFTS_SLIT_SCI_MOS";
00937             wavelength_map_sky_tag  = "WAVELENGTH_MAP_SCI_MOS";
00938             disp_coeff_sky_tag      = "DISP_COEFF_SCI_MOS";
00939             mapped_sky_tag          = "MAPPED_SKY_SCI_MOS";
00940             unmapped_sky_tag        = "UNMAPPED_SKY_SCI_MOS";
00941             object_table_tag        = "OBJECT_TABLE_SCI_MOS";
00942             reduced_sky_tag         = "REDUCED_SKY_SCI_MOS";
00943             reduced_error_tag       = "REDUCED_ERROR_SCI_MOS";
00944         }
00945 
00946         master_norm_flat_tag    = "MASTER_NORM_FLAT_MOS";
00947         global_sky_spectrum_tag = "GLOBAL_SKY_SPECTRUM_MOS";
00948 
00949         if (!cpl_frameset_count_tags(frameset, master_norm_flat_tag)) {
00950             master_norm_flat_tag    = "MASTER_NORM_FLAT_LONG_MOS";
00951         }
00952     }
00953 
00954     if (cpl_frameset_count_tags(frameset, "MASTER_BIAS") == 0)
00955         fors_extract_exit("Missing required input: MASTER_BIAS");
00956 
00957     if (cpl_frameset_count_tags(frameset, "MASTER_BIAS") > 1)
00958         fors_extract_exit("Too many in input: MASTER_BIAS");
00959 
00960     if (skyalign >= 0)
00961         if (cpl_frameset_count_tags(frameset, "MASTER_SKYLINECAT") > 1)
00962             fors_extract_exit("Too many in input: MASTER_SKYLINECAT");
00963 
00964     if (cpl_frameset_count_tags(frameset, global_distortion_tag) == 0)
00965         fors_extract_exit("Missing required input: MASTER_DISTORTION_TABLE");
00966 
00967     if (cpl_frameset_count_tags(frameset, global_distortion_tag) > 1)
00968         fors_extract_exit("Too many in input: MASTER_DISTORTION_TABLE");
00969 
00970     if (cpl_frameset_count_tags(frameset, master_norm_flat_tag) > 1) {
00971         if (flatfield) {
00972             cpl_msg_error(recipe, "Too many in input: %s", 
00973                           master_norm_flat_tag);
00974             fors_extract_exit(NULL);
00975         }
00976         else {
00977             cpl_msg_warning(recipe, "%s in input are ignored, "
00978                             "since flat field correction was not requested", 
00979                             master_norm_flat_tag);
00980         }
00981     }
00982 
00983     if (cpl_frameset_count_tags(frameset, master_norm_flat_tag) == 1) {
00984         if (!flatfield) {
00985             cpl_msg_warning(recipe, "%s in input is ignored, "
00986                             "since flat field correction was not requested", 
00987                             master_norm_flat_tag);
00988         }
00989     }
00990 
00991     if (cpl_frameset_count_tags(frameset, master_norm_flat_tag) == 0) {
00992         if (flatfield) {
00993             cpl_msg_error(recipe, "Flat field correction was requested, "
00994                           "but no %s are found in input",
00995                           master_norm_flat_tag);
00996             fors_extract_exit(NULL);
00997         }
00998     }
00999 
01000     cpl_msg_indent_less();
01001 
01002 
01003     /*
01004      * Loading input data
01005      */
01006 
01007     exptime = cpl_calloc(nscience, sizeof(double));
01008 
01009     if (nscience > 1) {
01010 
01011         cpl_msg_info(recipe, "Load %d scientific frames and median them...",
01012                      nscience);
01013         cpl_msg_indent_more();
01014 
01015         all_science = cpl_imagelist_new();
01016 
01017         header = dfs_load_header(frameset, science_tag, 0);
01018 
01019         if (header == NULL)
01020             fors_extract_exit("Cannot load scientific frame header");
01021 
01022         alltime = exptime[0] = cpl_propertylist_get_double(header, "EXPTIME");
01023 
01024         if (cpl_error_get_code() != CPL_ERROR_NONE)
01025             fors_extract_exit("Missing keyword EXPTIME in scientific "
01026                               "frame header");
01027 
01028         cpl_propertylist_delete(header); header = NULL;
01029 
01030         cpl_msg_info(recipe, "Scientific frame 1 exposure time: %.2f s", 
01031                      exptime[0]);
01032 
01033         for (i = 1; i < nscience; i++) {
01034 
01035             header = dfs_load_header(frameset, NULL, 0);
01036 
01037             if (header == NULL)
01038                 fors_extract_exit("Cannot load scientific frame header");
01039     
01040             exptime[i] = cpl_propertylist_get_double(header, "EXPTIME");
01041 
01042             alltime += exptime[i];
01043     
01044             if (cpl_error_get_code() != CPL_ERROR_NONE)
01045                 fors_extract_exit("Missing keyword EXPTIME in scientific "
01046                                   "frame header");
01047     
01048             cpl_propertylist_delete(header); header = NULL;
01049 
01050             cpl_msg_info(recipe, "Scientific frame %d exposure time: %.2f s", 
01051                          i+1, exptime[i]);
01052         }
01053 
01054         spectra = dfs_load_image(frameset, science_tag, CPL_TYPE_FLOAT, 0, 0);
01055 
01056         if (spectra == NULL)
01057             fors_extract_exit("Cannot load scientific frame");
01058 
01059         cpl_image_divide_scalar(spectra, exptime[0]);
01060         cpl_imagelist_set(all_science, spectra, 0); spectra = NULL;
01061 
01062         for (i = 1; i < nscience; i++) {
01063 
01064             spectra = dfs_load_image(frameset, NULL, CPL_TYPE_FLOAT, 0, 0);
01065 
01066             if (spectra) {
01067                 cpl_image_divide_scalar(spectra, exptime[i]);
01068                 cpl_imagelist_set(all_science, spectra, i); spectra = NULL;
01069             }
01070             else
01071                 fors_extract_exit("Cannot load scientific frame");
01072 
01073         }
01074 
01075         spectra = cpl_imagelist_collapse_median_create(all_science);
01076         cpl_image_multiply_scalar(spectra, alltime);
01077 
01078         cpl_imagelist_delete(all_science);
01079     }
01080     else {
01081         cpl_msg_info(recipe, "Load scientific exposure...");
01082         cpl_msg_indent_more();
01083 
01084         header = dfs_load_header(frameset, science_tag, 0);
01085 
01086         if (header == NULL)
01087             fors_extract_exit("Cannot load scientific frame header");
01088 
01089 
01090         /*
01091          * Insert here a check on supported filters:
01092          */
01093 
01094         wheel4 = (char *)cpl_propertylist_get_string(header,
01095                                                      "ESO INS OPTI9 TYPE");
01096         if (cpl_error_get_code() != CPL_ERROR_NONE) {
01097             fors_extract_exit("Missing ESO INS OPTI9 TYPE in flat header");
01098         }
01099 
01100         if (strcmp("FILT", wheel4) == 0) {
01101             wheel4 = (char *)cpl_propertylist_get_string(header,
01102                                                          "ESO INS OPTI9 NAME");
01103             cpl_msg_error(recipe, "Unsupported filter: %s", wheel4);
01104             fors_extract_exit(NULL);
01105         }
01106 
01107 
01108         alltime = exptime[0] = cpl_propertylist_get_double(header, "EXPTIME");
01109 
01110         if (cpl_error_get_code() != CPL_ERROR_NONE)
01111             fors_extract_exit("Missing keyword EXPTIME in scientific "
01112                               "frame header");
01113 
01114         cpl_propertylist_delete(header); header = NULL;
01115 
01116         cpl_msg_info(recipe, "Scientific frame exposure time: %.2f s", 
01117                      exptime[0]);
01118 
01119         spectra = dfs_load_image(frameset, science_tag, CPL_TYPE_FLOAT, 0, 0);
01120     }
01121 
01122     if (spectra == NULL)
01123         fors_extract_exit("Cannot load scientific frame");
01124 
01125     cpl_free(exptime); exptime = NULL;
01126 
01127     cpl_msg_indent_less();
01128 
01129 
01130     /*
01131      * Get the reference wavelength and the rebin factor along the
01132      * dispersion direction from a scientific exposure
01133      */
01134 
01135     header = dfs_load_header(frameset, science_tag, 0);
01136 
01137     if (header == NULL)
01138         fors_extract_exit("Cannot load scientific frame header");
01139 
01140     instrume = (char *)cpl_propertylist_get_string(header, "INSTRUME");
01141     if (instrume == NULL)
01142         fors_extract_exit("Missing keyword INSTRUME in sientific header");
01143     instrume = cpl_strdup(instrume);
01144 
01145     if (instrume[4] == '1')
01146         snprintf(version, 80, "%s/%s", "fors1", VERSION);
01147     if (instrume[4] == '2')
01148         snprintf(version, 80, "%s/%s", "fors2", VERSION);
01149 
01150     cpl_free(instrume); instrume = NULL;
01151 
01152     reference = cpl_propertylist_get_double(header, "ESO INS GRIS1 WLEN");
01153 
01154     if (cpl_error_get_code() != CPL_ERROR_NONE)
01155         fors_extract_exit("Missing keyword ESO INS GRIS1 WLEN in scientific "
01156                         "frame header");
01157 
01158     if (reference < 3000.0)   /* Perhaps in nanometers... */
01159         reference *= 10;
01160 
01161     if (reference < 3000.0 || reference > 13000.0) {
01162         cpl_msg_error(recipe, "Invalid central wavelength %.2f read from "
01163                       "keyword ESO INS GRIS1 WLEN in scientific frame header",
01164                       reference);
01165         fors_extract_exit(NULL);
01166     }
01167 
01168     cpl_msg_info(recipe, "The central wavelength is: %.2f", reference);
01169 
01170     rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
01171 
01172     if (cpl_error_get_code() != CPL_ERROR_NONE)
01173         fors_extract_exit("Missing keyword ESO DET WIN1 BINX in scientific "
01174                         "frame header");
01175 
01176     if (rebin != 1) {
01177         dispersion *= rebin;
01178         cpl_msg_warning(recipe, "The rebin factor is %d, and therefore the "
01179                         "resampling step used is %f A/pixel", rebin, 
01180                         dispersion);
01181     }
01182 
01183     gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
01184 
01185     if (cpl_error_get_code() != CPL_ERROR_NONE)
01186         fors_extract_exit("Missing keyword ESO DET OUT1 CONAD in scientific "
01187                           "frame header");
01188 
01189     cpl_msg_info(recipe, "The gain factor is: %.2f e-/ADU", gain);
01190 
01191     ron = cpl_propertylist_get_double(header, "ESO DET OUT1 RON");
01192 
01193     if (cpl_error_get_code() != CPL_ERROR_NONE)
01194         fors_extract_exit("Missing keyword ESO DET OUT1 RON in scientific "
01195                           "frame header");
01196 
01197     ron /= gain;     /* Convert from electrons to ADU */
01198 
01199     cpl_msg_info(recipe, "The read-out-noise is: %.2f ADU", ron);
01200 
01201     coll = (char *)cpl_propertylist_get_string(header, "ESO INS COLL NAME");
01202 
01203     if (cpl_error_get_code() != CPL_ERROR_NONE)
01204         fors_extract_exit("Missing keyword ESO INS COLL NAME in scientific "
01205                           "frame header");
01206 
01207     cpl_msg_info(recipe, "The collimator is : %s", coll);
01208 
01209     if (strcmp(coll, "COLL_HR") == 0)
01210         fors_extract_exit("HR collimator is not yet supported by this recipe");
01211     
01212 
01213     if (mos)
01214     {   
01215         int nslits_out_det;
01216         maskslits = mos_load_slits_fors_mos(header, &nslits_out_det);
01217     }
01218     else if (lss)
01219         maskslits = mos_load_slits_fors_lss(header);
01220     else
01221         maskslits = mos_load_slits_fors_mxu(header);
01222 
01223     if (lss) {
01224         if (skylocal) {
01225             if (cosmics)
01226                 fors_extract_exit("Cosmic rays correction for LSS "
01227                                   "data requires --skyglobal=true");
01228             skymedian = skylocal;
01229             skylocal = 0;
01230         }
01231     }
01232 
01233     global = dfs_load_table(frameset, global_distortion_tag, 1);
01234     if (global == NULL)
01235         fors_extract_exit("Cannot load global distortion table");
01236 
01237     /* Leave the header on for the next step... */
01238 
01239 
01240     /*
01241      * Remove the master bias
01242      */
01243 
01244     cpl_msg_info(recipe, "Remove the master bias...");
01245 
01246     bias = dfs_load_image(frameset, "MASTER_BIAS", CPL_TYPE_FLOAT, 0, 1);
01247 
01248     if (bias == NULL)
01249         fors_extract_exit("Cannot load master bias");
01250 
01251     overscans = mos_load_overscans_vimos(header, 1);
01252     cpl_propertylist_delete(header); header = NULL;
01253     dummy = mos_remove_bias(spectra, bias, overscans);
01254     cpl_image_delete(spectra); spectra = dummy; dummy = NULL;
01255     cpl_image_delete(bias); bias = NULL;
01256     cpl_table_delete(overscans); overscans = NULL;
01257 
01258     if (spectra == NULL)
01259         fors_extract_exit("Cannot remove bias from scientific frame");
01260 
01261     nx = cpl_image_get_size_x(spectra);
01262     ny = cpl_image_get_size_y(spectra);
01263 
01264     if (ny == 400 && nx == 2048)
01265         narrow = 1;
01266 
01267     if (narrow) {
01268         ny = 2048;
01269         dummy = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01270         cpl_image_copy(dummy, spectra, 1, 825);     /* (2048 - 400)/2 + 1 */
01271         if (cpl_error_get_code())
01272             fors_extract_exit("Problems expanding scientific image");
01273         cpl_image_delete(spectra); spectra = dummy; dummy = NULL;
01274     }
01275 
01276     cpl_msg_indent_less();
01277     cpl_msg_info(recipe, "Load normalised flat field (if present)...");
01278     cpl_msg_indent_more();
01279 
01280     if (flatfield) {
01281 
01282         norm_flat = dfs_load_image(frameset, master_norm_flat_tag, 
01283                                    CPL_TYPE_FLOAT, 0, 1);
01284 
01285         if (norm_flat) {
01286             if (narrow) {
01287                 dummy = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01288                 cpl_image_copy(dummy, norm_flat, 1, 825);
01289                 if (cpl_error_get_code())
01290                     fors_extract_exit("Problems expanding flat image");
01291                 cpl_image_delete(norm_flat); norm_flat = dummy; dummy = NULL;
01292             }
01293             cpl_msg_info(recipe, "Apply flat field correction...");
01294             if (cpl_image_divide(spectra, norm_flat) != CPL_ERROR_NONE) {
01295                 cpl_msg_error(recipe, "Failure of flat field correction: %s",
01296                               cpl_error_get_message());
01297                 fors_extract_exit(NULL);
01298             }
01299             cpl_image_delete(norm_flat); norm_flat = NULL;
01300         }
01301         else {
01302             cpl_msg_error(recipe, "Cannot load input %s for flat field "
01303                           "correction", master_norm_flat_tag);
01304             fors_extract_exit(NULL);
01305         }
01306 
01307     }
01308 
01309 
01310     if (skyalign >= 0) {
01311         cpl_msg_indent_less();
01312         cpl_msg_info(recipe, "Load input sky line catalog...");
01313         cpl_msg_indent_more();
01314 
01315         wavelengths = dfs_load_table(frameset, "MASTER_SKYLINECAT", 1);
01316 
01317         if (wavelengths) {
01318 
01319             /*
01320              * Cast the wavelengths into a (double precision) CPL vector
01321              */
01322 
01323             nlines = cpl_table_get_nrow(wavelengths);
01324 
01325             if (nlines == 0)
01326                 fors_extract_exit("Empty input sky line catalog");
01327 
01328             if (cpl_table_has_column(wavelengths, wcolumn) != 1) {
01329                 cpl_msg_error(recipe, "Missing column %s in input line "
01330                               "catalog table", wcolumn);
01331                 fors_extract_exit(NULL);
01332             }
01333 
01334             line = cpl_malloc(nlines * sizeof(double));
01335     
01336             for (i = 0; i < nlines; i++)
01337                 line[i] = cpl_table_get(wavelengths, wcolumn, i, NULL);
01338 
01339             cpl_table_delete(wavelengths); wavelengths = NULL;
01340 
01341             lines = cpl_vector_wrap(nlines, line);
01342         }
01343         else {
01344             cpl_msg_info(recipe, "No sky line catalog found in input - fine!");
01345         }
01346     }
01347 
01348 
01349     /*
01350      * Load the slit location table, or provide a dummy one in case
01351      * of LSS data (single slit spanning whole image)
01352      */
01353 
01354     slits = mos_build_slit_location(global, maskslits, ny);
01355     if (slits == NULL)
01356         fors_extract_exit("Cannot create slits location table");
01357 
01358 
01359     /*
01360      * Load the spectral curvature table in case of MOS or MXU data
01361      */
01362 
01363     polytraces = mos_build_curv_coeff(global, maskslits, slits);
01364     if (polytraces == NULL)
01365         fors_extract_exit("Cannot create spectral curvature table");
01366 
01367     cpl_table_delete(maskslits); maskslits = NULL;
01368 
01369     cpl_msg_indent_less();
01370     cpl_msg_info(recipe, "Processing scientific spectra...");
01371     cpl_msg_indent_more();
01372 
01373     /*
01374      * This one will also generate the spatial map from the spectral 
01375      * curvature table (in the case of multislit data)
01376      */
01377 
01378     coordinate = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01379 
01380     smapped = mos_spatial_calibration(spectra, slits, polytraces, reference,
01381                                       startwavelength, endwavelength,
01382                                       dispersion, flux, coordinate);
01383 
01384 
01385     /*
01386      * Load the wavelength calibration table
01387      */
01388 
01389     idscoeff = mos_build_disp_coeff(global, slits);
01390     if (idscoeff == NULL)
01391         fors_extract_exit("Cannot create wavelength calibration table");
01392 
01393     cpl_table_delete(global); global = NULL;
01394 
01395 
01396     /*
01397      * Generate a rectified wavelength map from the wavelength calibration 
01398      * table
01399      */
01400 
01401     rainbow = mos_map_idscoeff(idscoeff, nx, reference, startwavelength, 
01402                                endwavelength);
01403 
01404     if (dispersion > 1.0)
01405         highres = 0;
01406     else
01407         highres = 1;
01408 
01409     if (skyalign >= 0) {
01410         if (skyalign) {
01411             cpl_msg_info(recipe, "Align wavelength solution to reference "
01412             "skylines applying %d order residual fit...", skyalign);
01413         }
01414         else {
01415             cpl_msg_info(recipe, "Align wavelength solution to reference "
01416             "skylines applying median offset...");
01417         }
01418 
01419         if (lss) {
01420             offsets = mos_wavelength_align_lss(smapped, reference, 
01421                                                startwavelength, endwavelength, 
01422                                                idscoeff, lines, highres, 
01423                                                skyalign, rainbow, 4);
01424         }
01425         else {
01426             offsets = mos_wavelength_align(smapped, slits, reference, 
01427                                            startwavelength, endwavelength, 
01428                                            idscoeff, lines, highres, skyalign, 
01429                                            rainbow, 4);
01430         }
01431 
01432         cpl_vector_delete(lines); lines = NULL;
01433 
01434         if (offsets) {
01435             if (standard)
01436                 cpl_msg_warning(recipe, "Alignment of the wavelength solution "
01437                                 "to reference sky lines may be unreliable in "
01438                                 "this case!");
01439 
01440             if (dfs_save_table(frameset, offsets, skylines_offsets_tag, NULL, 
01441                                parlist, recipe, version))
01442                 fors_extract_exit(NULL);
01443 
01444             cpl_table_delete(offsets); offsets = NULL;
01445         }
01446         else {
01447             cpl_msg_warning(recipe, "Alignment of the wavelength solution "
01448                             "to reference sky lines could not be done!");
01449             skyalign = -1;
01450         }
01451 
01452     }
01453 
01454     wavemap = mos_map_wavelengths(coordinate, rainbow, slits, 
01455                                   polytraces, reference, 
01456                                   startwavelength, endwavelength,
01457                                   dispersion);
01458 
01459     cpl_image_delete(rainbow); rainbow = NULL;
01460     cpl_image_delete(coordinate); coordinate = NULL;
01461 
01462     /*
01463      * Here the wavelength calibrated slit spectra are created. This frame
01464      * contains sky_science.
01465      */
01466 
01467     mapped_sky = mos_wavelength_calibration(smapped, reference,
01468                                             startwavelength, endwavelength,
01469                                             dispersion, idscoeff, flux);
01470 
01471     cpl_msg_indent_less();
01472     cpl_msg_info(recipe, "Check applied wavelength against skylines...");
01473     cpl_msg_indent_more();
01474 
01475     mean_rms = mos_distortions_rms(mapped_sky, NULL, startwavelength,
01476                                    dispersion, 6, highres);
01477 
01478     cpl_msg_info(recipe, "Mean residual: %f", mean_rms);
01479 
01480     mean_rms = cpl_table_get_column_mean(idscoeff, "error");
01481 
01482     header = cpl_propertylist_new();
01483     cpl_propertylist_update_double(header, "CRPIX1", 1.0);
01484     cpl_propertylist_update_double(header, "CRPIX2", 1.0);
01485     cpl_propertylist_update_double(header, "CRVAL1", 
01486                                    startwavelength + dispersion/2);
01487     cpl_propertylist_update_double(header, "CRVAL2", 1.0);
01488     /* cpl_propertylist_update_double(header, "CDELT1", dispersion);
01489     cpl_propertylist_update_double(header, "CDELT2", 1.0); */
01490     cpl_propertylist_update_double(header, "CD1_1", dispersion);
01491     cpl_propertylist_update_double(header, "CD1_2", 0.0);
01492     cpl_propertylist_update_double(header, "CD2_1", 0.0);
01493     cpl_propertylist_update_double(header, "CD2_2", 1.0);
01494     cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
01495     cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
01496 
01497     if (time_normalise) {
01498         dummy = cpl_image_divide_scalar_create(mapped_sky, alltime);
01499         if (dfs_save_image(frameset, dummy, mapped_science_sky_tag, header, 
01500                            parlist, recipe, version))
01501             fors_extract_exit(NULL);
01502         cpl_image_delete(dummy); dummy = NULL;
01503     }
01504     else {
01505         if (dfs_save_image(frameset, mapped_sky, mapped_science_sky_tag, 
01506                            header, parlist, recipe, version))
01507             fors_extract_exit(NULL);
01508     }
01509 
01510 /*    if (skyglobal == 0 && skymedian < 0) {    NSS */
01511     if (skyglobal == 0 && skymedian == 0 && skylocal == 0) {
01512         cpl_image_delete(mapped_sky); mapped_sky = NULL;
01513     }
01514 
01515     if (skyglobal || skylocal) {
01516 
01517         cpl_msg_indent_less();
01518 
01519         if (skyglobal) {
01520             cpl_msg_info(recipe, "Global sky determination...");
01521             cpl_msg_indent_more();
01522             skymap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01523             sky = mos_sky_map_super(spectra, wavemap, dispersion, 
01524                                     2.0, 50, skymap);
01525             if (sky)
01526                 cpl_image_subtract(spectra, skymap);
01527             else
01528                 cpl_image_delete(skymap); skymap = NULL;
01529         }
01530         else {
01531             cpl_msg_info(recipe, "Local sky determination...");
01532             cpl_msg_indent_more();
01533             skymap = mos_subtract_sky(spectra, slits, polytraces, reference,
01534                            startwavelength, endwavelength, dispersion);
01535         }
01536 
01537         if (skymap) {
01538             if (skyglobal) {
01539                 if (time_normalise)
01540                     cpl_table_divide_scalar(sky, "sky", alltime);
01541                 if (dfs_save_table(frameset, sky, global_sky_spectrum_tag, 
01542                                    NULL, parlist, recipe, version))
01543                     fors_extract_exit(NULL);
01544     
01545                 cpl_table_delete(sky); sky = NULL;
01546             }
01547 
01548             save_header = dfs_load_header(frameset, science_tag, 0);
01549 
01550             if (time_normalise)
01551                 cpl_image_divide_scalar(skymap, alltime);
01552             if (dfs_save_image(frameset, skymap, unmapped_sky_tag,
01553                                save_header, parlist, recipe, version))
01554                 fors_extract_exit(NULL);
01555 
01556             cpl_image_delete(skymap); skymap = NULL;
01557 
01558             if (dfs_save_image(frameset, spectra, unmapped_science_tag,
01559                                save_header, parlist, recipe, version))
01560                 fors_extract_exit(NULL);
01561 
01562             cpl_propertylist_delete(save_header); save_header = NULL;
01563 
01564             if (cosmics) {
01565                 cpl_msg_info(recipe, "Removing cosmic rays...");
01566                 mos_clean_cosmics(spectra, gain, -1., -1.);
01567             }
01568 
01569             /*
01570              * The spatially rectified image, that contained the sky,
01571              * is replaced by a sky-subtracted spatially rectified image:
01572              */
01573 
01574             cpl_image_delete(smapped); smapped = NULL;
01575 
01576             if (lss) {
01577                 smapped = cpl_image_duplicate(spectra);
01578             }
01579             else {
01580                 smapped = mos_spatial_calibration(spectra, slits, polytraces, 
01581                                                   reference, startwavelength, 
01582                                                   endwavelength, dispersion, 
01583                                                   flux, NULL);
01584             }
01585         }
01586         else {
01587             cpl_msg_warning(recipe, "Sky subtraction failure");
01588             if (cosmics)
01589                 cpl_msg_warning(recipe, "Cosmic rays removal not performed!");
01590             cosmics = skylocal = skyglobal = 0;
01591         }
01592     }
01593 
01594     cpl_image_delete(spectra); spectra = NULL;
01595     cpl_table_delete(polytraces); polytraces = NULL;
01596 
01597     if (skyalign >= 0) {
01598         save_header = dfs_load_header(frameset, science_tag, 0);
01599         if (dfs_save_image(frameset, wavemap, wavelength_map_sky_tag,
01600                            save_header, parlist, recipe, version))
01601             fors_extract_exit(NULL);
01602         cpl_propertylist_delete(save_header); save_header = NULL;
01603     }
01604 
01605     cpl_image_delete(wavemap); wavemap = NULL;
01606 
01607     mapped = mos_wavelength_calibration(smapped, reference,
01608                                         startwavelength, endwavelength,
01609                                         dispersion, idscoeff, flux);
01610 
01611     cpl_image_delete(smapped); smapped = NULL;
01612 
01613 /*    if (skymedian >= 0) {    NSS */
01614     if (skymedian) {
01615             cpl_msg_indent_less();
01616             cpl_msg_info(recipe, "Local sky determination...");
01617             cpl_msg_indent_more();
01618        
01619 /*   NSS      skylocalmap = mos_sky_local(mapped, slits, skymedian); */
01620 /*            skylocalmap = mos_sky_local(mapped, slits, 0);        */
01621             skylocalmap = mos_sky_local_old(mapped, slits);       
01622             cpl_image_subtract(mapped, skylocalmap);
01623 /*
01624             if (dfs_save_image(frameset, skylocalmap, mapped_sky_tag, header, 
01625                                parlist, recipe, version))
01626                 fors_extract_exit(NULL);
01627 */
01628             cpl_image_delete(skylocalmap); skylocalmap = NULL;
01629     }
01630 
01631 /*    if (skyglobal || skymedian >= 0 || skylocal) {   NSS */
01632     if (skyglobal || skymedian || skylocal) {
01633 
01634         skylocalmap = cpl_image_subtract_create(mapped_sky, mapped);
01635 
01636         cpl_image_delete(mapped_sky); mapped_sky = NULL;
01637 
01638         if (time_normalise) {
01639             dummy = cpl_image_divide_scalar_create(skylocalmap, alltime);
01640             if (dfs_save_image(frameset, dummy, mapped_sky_tag, header,
01641                                parlist, recipe, version))
01642                 fors_extract_exit(NULL);
01643             cpl_image_delete(dummy); dummy = NULL;
01644         }
01645         else {
01646             if (dfs_save_image(frameset, skylocalmap, mapped_sky_tag, header,
01647                                parlist, recipe, version))
01648                 fors_extract_exit(NULL);
01649         }
01650 
01651         cpl_msg_indent_less();
01652         cpl_msg_info(recipe, "Object detection...");
01653         cpl_msg_indent_more();
01654 
01655         if (cosmics || nscience > 1) {
01656             dummy = mos_detect_objects(mapped, slits, slit_margin, ext_radius, 
01657                                        cont_radius);
01658         }
01659         else {
01660             mapped_cleaned = cpl_image_duplicate(mapped);
01661             mos_clean_cosmics(mapped_cleaned, gain, -1., -1.);
01662             dummy = mos_detect_objects(mapped_cleaned, slits, slit_margin, 
01663                                        ext_radius, cont_radius);
01664 
01665             cpl_image_delete(mapped_cleaned); mapped_cleaned = NULL;
01666         }
01667 
01668         cpl_image_delete(dummy); dummy = NULL;
01669 
01670         if (dfs_save_table(frameset, slits, object_table_tag, NULL, parlist, 
01671                            recipe, version))
01672             fors_extract_exit(NULL);
01673 
01674         cpl_msg_indent_less();
01675         cpl_msg_info(recipe, "Object extraction...");
01676         cpl_msg_indent_more();
01677 
01678         //TODO: Add the real propagated variance here (the second argument)
01679         //It should be done like in fors_science  
01680         images = mos_extract_objects(mapped, NULL, skylocalmap, slits, 
01681                                      ext_mode, ron, gain, 1);
01682 
01683         cpl_image_delete(skylocalmap); skylocalmap = NULL;
01684 
01685         if (images) {
01686             if (time_normalise)
01687                 cpl_image_divide_scalar(images[0], alltime);
01688             if (dfs_save_image(frameset, images[0], reduced_science_tag, header,
01689                                parlist, recipe, version))
01690                 fors_extract_exit(NULL);
01691             cpl_image_delete(images[0]);
01692     
01693             if (time_normalise)
01694                 cpl_image_divide_scalar(images[1], alltime);
01695             if (dfs_save_image(frameset, images[1], reduced_sky_tag, header,
01696                                parlist, recipe, version))
01697                 fors_extract_exit(NULL);
01698             cpl_image_delete(images[1]);
01699     
01700             if (time_normalise)
01701                 cpl_image_divide_scalar(images[2], alltime);
01702             if (dfs_save_image(frameset, images[2], reduced_error_tag, header,
01703                                parlist, recipe, version))
01704                 fors_extract_exit(NULL);
01705             cpl_image_delete(images[2]);
01706     
01707             cpl_free(images);
01708         }
01709         else {
01710             cpl_msg_warning(recipe, "No objects found: the products "
01711                             "%s, %s, and %s are not created", 
01712                             reduced_science_tag, reduced_sky_tag, 
01713                             reduced_error_tag);
01714         }
01715 
01716     }
01717 
01718     cpl_table_delete(slits); slits = NULL;
01719 
01720     if (skyalign >= 0) {
01721         if (dfs_save_table(frameset, idscoeff, disp_coeff_sky_tag, NULL, 
01722                            parlist, recipe, version))
01723             fors_extract_exit(NULL);
01724     }
01725 
01726     cpl_table_delete(idscoeff); idscoeff = NULL;
01727 
01728 /*    if (skyglobal || skymedian >= 0) {   NSS */
01729     if (skyglobal || skymedian || skylocal) {
01730         if (time_normalise)
01731             cpl_image_divide_scalar(mapped, alltime);
01732         if (dfs_save_image(frameset, mapped, mapped_science_tag, header, 
01733                            parlist, recipe, version))
01734             fors_extract_exit(NULL);
01735     }
01736 
01737     cpl_image_delete(mapped); mapped = NULL;
01738     cpl_propertylist_delete(header); header = NULL;
01739 
01740     if (cpl_error_get_code()) {
01741         cpl_msg_error(cpl_error_get_where(), "%s", cpl_error_get_message());
01742         fors_extract_exit(NULL);
01743     }
01744 
01745     return 0;
01746 }

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