FORS Pipeline Reference Manual 4.9.20
fors_subtract_sky.c
00001 /* $Id: fors_subtract_sky.c,v 1.7 2013/02/28 15:16:34 cgarcia Exp $
00002  *
00003  * This file is part of the FORS Data Reduction Pipeline
00004  * Copyright (C) 2002-2010 European Southern Observatory
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00019  */
00020 
00021 /*
00022  * $Author: cgarcia $
00023  * $Date: 2013/02/28 15:16:34 $
00024  * $Revision: 1.7 $
00025  * $Name: fors-4_9_20 $
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 
00032 #include <math.h>
00033 #include <cpl.h>
00034 #include <moses.h>
00035 #include <fors_dfs.h>
00036 
00037 static int fors_subtract_sky_create(cpl_plugin *);
00038 static int fors_subtract_sky_exec(cpl_plugin *);
00039 static int fors_subtract_sky_destroy(cpl_plugin *);
00040 static int fors_subtract_sky(cpl_parameterlist *, cpl_frameset *);
00041 
00042 static char fors_subtract_sky_description[] =
00043 "This recipe is used to subtract the sky emission from unrebinned slit\n"
00044 "spectra. This is obtained by robust fitting (i.e., excluding the signal\n"
00045 "from possible point-like objects in slit) of the emission along the CCD\n"
00046 "columns within each spectrum). This method doesn't work if extended\n"
00047 "objects are in slit (it really destroys the object spectra), and is\n"
00048 "not applicable to LSS data. The input scientific frames are produced\n"
00049 "by the recipes fors_remove_bias and fors_flatfield.\n"
00050 "\n"
00051 "This recipe cannot be applied to LSS or long-slit like data (MOS/MXU with\n"
00052 "all slits at the same offset). No automatic recipe is available for this.\n"
00053 "Please refer to the FORS Pipeline User's Manual for more details.\n"
00054 "\n"
00055 "In the table below the MXU acronym can be alternatively read as MOS, and\n"
00056 "SCI as STD.\n\n"
00057 "Input files:\n\n"
00058 "  DO category:               Type:       Explanation:         Required:\n"
00059 "  SCIENCE_UNBIAS_MXU\n"
00060 "  or SCIENCE_UNFLAT_MXU\n"
00061 "  or STANDARD_UNBIAS_MXU\n"
00062 "  or STANDARD_UNFLAT_MXU     Calib       Frame with sky lines    Y\n"
00063 "  CURV_COEFF_MXU             Calib       Spectral curvature      Y\n"
00064 "  SLIT_LOCATION_MXU          Calib       Slit location on CCD    Y\n"
00065 "  GRISM_TABLE                Calib       Grism table             .\n\n"
00066 "Output files:\n\n"
00067 "  DO category:               Data type:  Explanation:\n"
00068 "  UNMAPPED_SCI_MXU\n"
00069 "  or UNMAPPED_STD_MXU        FITS image  Sky subtracted scientific frame\n"
00070 "  UNMAPPED_SKY_SCI_MXU\n"
00071 "  or UNMAPPED_SKY_STD_MXU    FITS image  Subtracted sky frame\n\n";
00072 
00073 #define fors_subtract_sky_exit(message)       \
00074 {                                             \
00075 if (message) cpl_msg_error(recipe, message);  \
00076 cpl_image_delete(spectra);                    \
00077 cpl_image_delete(skymap);                     \
00078 cpl_table_delete(grism_table);                \
00079 cpl_table_delete(maskslits);                  \
00080 cpl_table_delete(slits);                      \
00081 cpl_table_delete(polytraces);                 \
00082 cpl_propertylist_delete(header);              \
00083 cpl_msg_indent_less();                        \
00084 return -1;                                    \
00085 }
00086 
00087 #define fors_subtract_sky_exit_memcheck(message)   \
00088 {                                               \
00089 if (message) cpl_msg_info(recipe, message);     \
00090 printf("free spectra (%p)\n", spectra);         \
00091 cpl_image_delete(spectra);                      \
00092 printf("free skymap (%p)\n", skymap);           \
00093 cpl_image_delete(skymap);                       \
00094 printf("free grism_table (%p)\n", grism_table); \
00095 cpl_table_delete(grism_table);                  \
00096 printf("free maskslits (%p)\n", maskslits);     \
00097 cpl_table_delete(maskslits);                    \
00098 printf("free slits (%p)\n", slits);             \
00099 cpl_table_delete(slits);                        \
00100 printf("free polytraces (%p)\n", polytraces);   \
00101 cpl_table_delete(polytraces);                   \
00102 printf("free header (%p)\n", header);           \
00103 cpl_propertylist_delete(header);                \
00104 cpl_msg_indent_less();                          \
00105 return 0;                                       \
00106 }
00107 
00108 
00120 int cpl_plugin_get_info(cpl_pluginlist *list)
00121 {
00122     cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe );
00123     cpl_plugin *plugin = &recipe->interface;
00124 
00125     cpl_plugin_init(plugin,
00126                     CPL_PLUGIN_API,
00127                     FORS_BINARY_VERSION,
00128                     CPL_PLUGIN_TYPE_RECIPE,
00129                     "fors_subtract_sky",
00130                     "Subtract sky from scientific spectra",
00131                     fors_subtract_sky_description,
00132                     "Carlo Izzo",
00133                     PACKAGE_BUGREPORT,
00134     "This file is currently part of the FORS Instrument Pipeline\n"
00135     "Copyright (C) 2002-2010 European Southern Observatory\n\n"
00136     "This program is free software; you can redistribute it and/or modify\n"
00137     "it under the terms of the GNU General Public License as published by\n"
00138     "the Free Software Foundation; either version 2 of the License, or\n"
00139     "(at your option) any later version.\n\n"
00140     "This program is distributed in the hope that it will be useful,\n"
00141     "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
00142     "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
00143     "GNU General Public License for more details.\n\n"
00144     "You should have received a copy of the GNU General Public License\n"
00145     "along with this program; if not, write to the Free Software Foundation,\n"
00146     "Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n",
00147                     fors_subtract_sky_create,
00148                     fors_subtract_sky_exec,
00149                     fors_subtract_sky_destroy);
00150 
00151     cpl_pluginlist_append(list, plugin);
00152     
00153     return 0;
00154 }
00155 
00156 
00167 static int fors_subtract_sky_create(cpl_plugin *plugin)
00168 {
00169     cpl_recipe    *recipe;
00170     cpl_parameter *p;
00171 
00172     /* 
00173      * Check that the plugin is part of a valid recipe 
00174      */
00175 
00176     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00177         recipe = (cpl_recipe *)plugin;
00178     else 
00179         return -1;
00180 
00181     /* 
00182      * Create the (empty) parameters list in the cpl_recipe object 
00183      */
00184 
00185     recipe->parameters = cpl_parameterlist_new(); 
00186 
00187     /*
00188      * Dispersion
00189      */
00190 
00191     p = cpl_parameter_new_value("fors.fors_subtract_sky.dispersion",
00192                                 CPL_TYPE_DOUBLE,
00193                                 "Expected spectral dispersion (Angstrom/pixel)",
00194                                 "fors.fors_subtract_sky",
00195                                 0.0);
00196     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dispersion");
00197     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00198     cpl_parameterlist_append(recipe->parameters, p);
00199 
00200     /*
00201      * Start wavelength for spectral extraction
00202      */
00203 
00204     p = cpl_parameter_new_value("fors.fors_subtract_sky.startwavelength",
00205                                 CPL_TYPE_DOUBLE,
00206                                 "Start wavelength in spectral extraction",
00207                                 "fors.fors_subtract_sky",
00208                                 0.0);
00209     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "startwavelength");
00210     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00211     cpl_parameterlist_append(recipe->parameters, p);
00212 
00213     /*
00214      * End wavelength for spectral extraction
00215      */
00216 
00217     p = cpl_parameter_new_value("fors.fors_subtract_sky.endwavelength",
00218                                 CPL_TYPE_DOUBLE,
00219                                 "End wavelength in spectral extraction",
00220                                 "fors.fors_subtract_sky",
00221                                 0.0);
00222     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "endwavelength");
00223     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00224     cpl_parameterlist_append(recipe->parameters, p);
00225 
00226     /*
00227      * Cosmic rays removal
00228      */
00229 
00230     p = cpl_parameter_new_value("fors.fors_subtract_sky.cosmics",
00231                                 CPL_TYPE_BOOL,
00232                                 "Eliminate cosmic rays hits",
00233                                 "fors.fors_subtract_sky",
00234                                 FALSE);
00235     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "cosmics");
00236     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00237     cpl_parameterlist_append(recipe->parameters, p);
00238 
00239     return 0;
00240 }
00241 
00242 
00251 static int fors_subtract_sky_exec(cpl_plugin *plugin)
00252 {
00253     cpl_recipe *recipe;
00254     
00255     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00256         recipe = (cpl_recipe *)plugin;
00257     else 
00258         return -1;
00259 
00260     return fors_subtract_sky(recipe->parameters, recipe->frames);
00261 }
00262 
00263 
00272 static int fors_subtract_sky_destroy(cpl_plugin *plugin)
00273 {
00274     cpl_recipe *recipe;
00275     
00276     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00277         recipe = (cpl_recipe *)plugin;
00278     else 
00279         return -1;
00280 
00281     cpl_parameterlist_delete(recipe->parameters); 
00282 
00283     return 0;
00284 }
00285 
00286 
00296 static int fors_subtract_sky(cpl_parameterlist *parlist, 
00297                                cpl_frameset *frameset)
00298 {
00299 
00300     const char *recipe = "fors_subtract_sky";
00301 
00302 
00303     /*
00304      * Input parameters
00305      */
00306 
00307     double      dispersion;
00308     double      startwavelength;
00309     double      endwavelength;
00310     int         cosmics;
00311 
00312     /*
00313      * CPL objects
00314      */
00315 
00316     cpl_image        *spectra     = NULL;
00317     cpl_image        *skymap      = NULL;
00318     cpl_table        *grism_table = NULL;
00319     cpl_table        *polytraces  = NULL;
00320     cpl_table        *slits       = NULL;
00321     cpl_table        *maskslits   = NULL;
00322     cpl_propertylist *header      = NULL;
00323 
00324     /*
00325      * Auxiliary variables
00326      */
00327 
00328     char        version[80];
00329     const char *slit_location_tag;
00330     const char *input_tag;
00331     const char *curv_coeff_tag;
00332     const char *unmapped_tag;
00333     const char *unmapped_sky_tag;
00334     int         nframes;
00335     int         rebin;
00336     int         nslits;
00337     int         treat_as_lss;
00338     int         i;
00339     double      reference;
00340     double      gain;
00341     double     *xpos;
00342     double      mxpos;
00343     int         mxu, mos, lss;
00344     int         rec_scib;
00345     int         rec_stdb;
00346     int         rec_scif;
00347     int         rec_stdf;
00348     int         nslits_out_det = 0;
00349 
00350     char       *instrume = NULL;
00351 
00352 
00353     cpl_msg_set_indentation(2);
00354 
00355     if (dfs_files_dont_exist(frameset))
00356         fors_subtract_sky_exit(NULL);
00357 
00358 
00359     /*
00360      * Get configuration parameters
00361      */
00362 
00363     cpl_msg_info(recipe, "Recipe %s configuration parameters:", recipe);
00364     cpl_msg_indent_more();
00365     
00366     if (cpl_frameset_count_tags(frameset, "GRISM_TABLE") > 1)
00367         fors_subtract_sky_exit("Too many in input: GRISM_TABLE"); 
00368 
00369     grism_table = dfs_load_table(frameset, "GRISM_TABLE", 1);
00370 
00371     dispersion = dfs_get_parameter_double(parlist,
00372                     "fors.fors_subtract_sky.dispersion", grism_table);
00373 
00374     if (dispersion <= 0.0)
00375         fors_subtract_sky_exit("Invalid spectral dispersion value");
00376 
00377     startwavelength = dfs_get_parameter_double(parlist,
00378                     "fors.fors_subtract_sky.startwavelength", grism_table);
00379     if (startwavelength > 1.0)
00380         if (startwavelength < 3000.0 || startwavelength > 13000.0)
00381             fors_subtract_sky_exit("Invalid wavelength");
00382 
00383     endwavelength = dfs_get_parameter_double(parlist,
00384                     "fors.fors_subtract_sky.endwavelength", grism_table);
00385     if (endwavelength > 1.0) {
00386         if (endwavelength < 3000.0 || endwavelength > 13000.0)
00387             fors_subtract_sky_exit("Invalid wavelength");
00388         if (startwavelength < 1.0)
00389             fors_subtract_sky_exit("Invalid wavelength interval");
00390     }
00391 
00392     if (startwavelength > 1.0)
00393         if (endwavelength - startwavelength <= 0.0)
00394             fors_subtract_sky_exit("Invalid wavelength interval");
00395 
00396     cosmics = dfs_get_parameter_bool(parlist, 
00397                                      "fors.fors_subtract_sky.cosmics", NULL);
00398 
00399     cpl_table_delete(grism_table); grism_table = NULL;
00400 
00401     if (cpl_error_get_code())
00402         fors_subtract_sky_exit("Failure reading the configuration parameters");
00403 
00404 
00405     cpl_msg_indent_less();
00406     cpl_msg_info(recipe, "Check input set-of-frames:");
00407     cpl_msg_indent_more();
00408 
00409     mxu  = cpl_frameset_count_tags(frameset, "SLIT_LOCATION_MXU");
00410     mos  = cpl_frameset_count_tags(frameset, "SLIT_LOCATION_MOS");
00411     lss  = cpl_frameset_count_tags(frameset, "SLIT_LOCATION_LSS");
00412 
00413     if (lss)
00414         fors_subtract_sky_exit("Use this recipe just with MOS/MXU data.");
00415 
00416     nframes = mos + mxu;
00417 
00418     if (nframes == 0) {
00419         fors_subtract_sky_exit("Missing input slit location table");
00420     }
00421     if (nframes > 1) {
00422         cpl_msg_error(recipe, 
00423                       "Too many input slit location tables (%d > 1)", nframes);
00424         fors_subtract_sky_exit(NULL);
00425     }
00426 
00427     if (mxu)
00428         curv_coeff_tag = "CURV_COEFF_MXU";
00429     else
00430         curv_coeff_tag = "CURV_COEFF_MXU";
00431 
00432     
00433     nframes = cpl_frameset_count_tags(frameset, curv_coeff_tag);
00434 
00435     if (nframes == 0) {
00436         cpl_msg_error(recipe, "Missing input %s", curv_coeff_tag);
00437         fors_subtract_sky_exit(NULL);
00438     }
00439     if (nframes > 1) {
00440         cpl_msg_error(recipe, "Too many input %s (%d > 1)", curv_coeff_tag,
00441                       nframes);
00442         fors_subtract_sky_exit(NULL);
00443     }
00444 
00445     if (mxu) {
00446         rec_scib = cpl_frameset_count_tags(frameset, "SCIENCE_UNBIAS_MXU");
00447         rec_stdb = cpl_frameset_count_tags(frameset, "STANDARD_UNBIAS_MXU");
00448         rec_scif = cpl_frameset_count_tags(frameset, "SCIENCE_UNFLAT_MXU");
00449         rec_stdf = cpl_frameset_count_tags(frameset, "STANDARD_UNFLAT_MXU");
00450     }
00451     else {
00452         rec_scib = cpl_frameset_count_tags(frameset, "SCIENCE_UNBIAS_MOS");
00453         rec_stdb = cpl_frameset_count_tags(frameset, "STANDARD_UNBIAS_MOS");
00454         rec_scif = cpl_frameset_count_tags(frameset, "SCIENCE_UNFLAT_MOS");
00455         rec_stdf = cpl_frameset_count_tags(frameset, "STANDARD_UNFLAT_MOS");
00456     }
00457 
00458     nframes = rec_scib + rec_stdb + rec_scif + rec_stdf;
00459 
00460     if (nframes == 0) {
00461         fors_subtract_sky_exit("Missing input scientific spectra");
00462     }
00463     if (nframes > 1) {
00464         cpl_msg_error(recipe, "Too many input scientific spectra (%d > 1)", 
00465                       nframes);
00466         fors_subtract_sky_exit(NULL);
00467     }
00468 
00469     if (rec_scib) {
00470         if (mxu) {
00471             input_tag         = "SCIENCE_UNBIAS_MXU";
00472             slit_location_tag = "SLIT_LOCATION_MXU";
00473             unmapped_tag      = "UNMAPPED_SCI_MXU";
00474             unmapped_sky_tag  = "UNMAPPED_SKY_SCI_MXU";
00475         }
00476         else {
00477             input_tag         = "SCIENCE_UNBIAS_MOS";
00478             slit_location_tag = "SLIT_LOCATION_MOS";
00479             unmapped_tag      = "UNMAPPED_SCI_MOS";
00480             unmapped_sky_tag  = "UNMAPPED_SKY_SCI_MOS";
00481         }
00482     }
00483     else if (rec_stdb) {
00484         if (mxu) {
00485             input_tag         = "STANDARD_UNBIAS_MXU";
00486             slit_location_tag = "SLIT_LOCATION_MXU";
00487             unmapped_tag      = "UNMAPPED_STD_MXU";
00488             unmapped_sky_tag  = "UNMAPPED_SKY_STD_MXU";
00489         }
00490         else {
00491             input_tag         = "STANDARD_UNBIAS_MOS";
00492             slit_location_tag = "SLIT_LOCATION_MOS";
00493             unmapped_tag      = "UNMAPPED_STD_MOS";
00494             unmapped_sky_tag  = "UNMAPPED_SKY_STD_MOS";
00495         }
00496     }
00497     else if (rec_scif) {
00498         if (mxu) {
00499             input_tag         = "SCIENCE_UNFLAT_MXU";
00500             slit_location_tag = "SLIT_LOCATION_MXU";
00501             unmapped_tag      = "UNMAPPED_SCI_MXU";
00502             unmapped_sky_tag  = "UNMAPPED_SKY_SCI_MXU";
00503         }
00504         else {   
00505             input_tag         = "SCIENCE_UNFLAT_MOS";
00506             slit_location_tag = "SLIT_LOCATION_MOS";
00507             unmapped_tag      = "UNMAPPED_SCI_MOS";
00508             unmapped_sky_tag  = "UNMAPPED_SKY_SCI_MOS";
00509         }
00510     }
00511     else if (rec_stdf) {
00512         if (mxu) {
00513             input_tag         = "STANDARD_UNFLAT_MXU";
00514             slit_location_tag = "SLIT_LOCATION_MXU";
00515             unmapped_tag      = "UNMAPPED_STD_MXU";
00516             unmapped_sky_tag  = "UNMAPPED_SKY_STD_MXU";
00517         }
00518         else {   
00519             input_tag         = "STANDARD_UNFLAT_MOS";
00520             slit_location_tag = "SLIT_LOCATION_MOS";
00521             unmapped_tag      = "UNMAPPED_STD_MOS";
00522             unmapped_sky_tag  = "UNMAPPED_SKY_STD_MOS";
00523         }
00524     }
00525 
00526 
00527     header = dfs_load_header(frameset, input_tag, 0);
00528 
00529     if (header == NULL)
00530         fors_subtract_sky_exit("Cannot load scientific frame header");
00531 
00532     if (mos)
00533         maskslits = mos_load_slits_fors_mos(header, &nslits_out_det);
00534     else
00535         maskslits = mos_load_slits_fors_mxu(header);
00536 
00537     /*
00538      * Check if all slits have the same X offset: if not, abort!
00539      */
00540 
00541     treat_as_lss = fors_mos_is_lss_like(maskslits, nslits_out_det);
00542 
00543     cpl_table_delete(maskslits); maskslits = NULL;
00544 
00545     if (treat_as_lss)
00546         fors_subtract_sky_exit("This recipe cannot process MOS/MXU "
00547                                "data with all slits at the same offset.");
00548 
00549 
00550     if (!dfs_equal_keyword(frameset, "ESO INS GRIS1 ID"))
00551         fors_subtract_sky_exit("Input frames are not from the same grism");
00552 
00553     if (!dfs_equal_keyword(frameset, "ESO INS FILT1 ID"))
00554         fors_subtract_sky_exit("Input frames are not from the same filter");
00555 
00556     if (!dfs_equal_keyword(frameset, "ESO DET CHIP1 ID"))
00557         fors_subtract_sky_exit("Input frames are not from the same chip");
00558 
00559 
00560     /*
00561      * Get the reference wavelength and the rebin factor along the
00562      * dispersion direction from the reference frame
00563      */
00564 
00565     instrume = (char *)cpl_propertylist_get_string(header, "INSTRUME");
00566     if (instrume == NULL)
00567         fors_subtract_sky_exit("Missing keyword INSTRUME in reference frame "
00568                             "header");
00569 
00570     if (instrume[4] == '1')
00571         snprintf(version, 80, "%s/%s", "fors1", VERSION);
00572     if (instrume[4] == '2')
00573         snprintf(version, 80, "%s/%s", "fors2", VERSION);
00574 
00575     reference = cpl_propertylist_get_double(header, "ESO INS GRIS1 WLEN");
00576 
00577     if (cpl_error_get_code() != CPL_ERROR_NONE)
00578         fors_subtract_sky_exit("Missing keyword ESO INS GRIS1 WLEN "
00579                             "in reference frame header");
00580 
00581     if (reference < 3000.0)   /* Perhaps in nanometers... */
00582         reference *= 10;
00583 
00584     if (reference < 3000.0 || reference > 13000.0) {
00585         cpl_msg_error(recipe, "Invalid central wavelength %.2f read from "
00586                       "keyword ESO INS GRIS1 WLEN in reference frame header",
00587                       reference);
00588         fors_subtract_sky_exit(NULL);
00589     }
00590 
00591     cpl_msg_info(recipe, "The central wavelength is: %.2f", reference);
00592 
00593     rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
00594 
00595     if (cpl_error_get_code() != CPL_ERROR_NONE)
00596         fors_subtract_sky_exit("Missing keyword ESO DET WIN1 BINX "
00597                             "in reference frame header");
00598 
00599     if (rebin != 1) {
00600         dispersion *= rebin;
00601         cpl_msg_warning(recipe, "The rebin factor is %d, and therefore the "
00602                         "working dispersion used is %f A/pixel", rebin,
00603                         dispersion);
00604     }
00605 
00606     if (cosmics) {
00607         gain = cpl_propertylist_get_double(header, "ESO DET OUT1 CONAD");
00608 
00609         if (cpl_error_get_code() != CPL_ERROR_NONE)
00610             fors_subtract_sky_exit("Missing keyword ESO DET OUT1 CONAD in "
00611                                    "scientific frame header");
00612 
00613         cpl_msg_info(recipe, "The gain factor is: %.2f e-/ADU", gain);
00614     }
00615 
00616 
00617     cpl_msg_indent_less();
00618     cpl_msg_info(recipe, "Load input frames...");
00619     cpl_msg_indent_more();
00620 
00621     spectra = dfs_load_image(frameset, input_tag, CPL_TYPE_FLOAT, 0, 0);
00622     if (spectra == NULL)
00623         fors_subtract_sky_exit("Cannot load input scientific frame");
00624 
00625     slits = dfs_load_table(frameset, slit_location_tag, 1);
00626     if (slits == NULL)
00627         fors_subtract_sky_exit("Cannot load slits location table");
00628 
00629     polytraces = dfs_load_table(frameset, curv_coeff_tag, 1);
00630     if (polytraces == NULL)
00631         fors_subtract_sky_exit("Cannot load spectral curvature table");
00632 
00633     cpl_msg_indent_less();
00634     cpl_msg_info(recipe, "Local sky determination...");
00635     cpl_msg_indent_more();
00636     skymap = mos_subtract_sky(spectra, slits, polytraces, reference,
00637                               startwavelength, endwavelength, dispersion);
00638 
00639     cpl_table_delete(polytraces); polytraces = NULL;
00640     cpl_table_delete(slits); slits = NULL;
00641 
00642     if (cosmics) {
00643         cpl_msg_info(recipe, "Removing cosmic rays...");
00644         mos_clean_cosmics(spectra, gain, -1., -1.);
00645     }
00646 
00647     if (dfs_save_image(frameset, spectra, unmapped_tag,
00648                        header, parlist, recipe, version))
00649         fors_subtract_sky_exit(NULL);
00650 
00651     cpl_image_delete(spectra); spectra = NULL;
00652 
00653     if (dfs_save_image(frameset, skymap, unmapped_sky_tag,
00654                        header, parlist, recipe, version))
00655         fors_subtract_sky_exit(NULL);
00656 
00657     cpl_image_delete(skymap); skymap = NULL;
00658 
00659     cpl_propertylist_delete(header); header = NULL;
00660 
00661     return 0;
00662 }