FORS Pipeline Reference Manual 4.9.20
fors_wave_calib_lss.c
00001 /* $Id: fors_wave_calib_lss.c,v 1.8 2013/02/28 15:16:58 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:58 $
00024  * $Revision: 1.8 $
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_wave_calib_lss_create(cpl_plugin *);
00038 static int fors_wave_calib_lss_exec(cpl_plugin *);
00039 static int fors_wave_calib_lss_destroy(cpl_plugin *);
00040 static int fors_wave_calib_lss(cpl_parameterlist *, cpl_frameset *);
00041 
00042 static char fors_wave_calib_lss_description[] =
00043 "This recipe is used to wavelength calibrate one long slit spectrum, i.e.,\n"
00044 "a FORS spectral obtained either in LSS mode or in MOS/MXU mode with all\n"
00045 "slits at the same offset. A pattern-matching algorithm is applied as in\n"
00046 "recipe fors_detect_spectra. For more details on this data reduction\n"
00047 "strategy please refer to the FORS Pipeline User's Manual.\n"
00048 "\n"
00049 "Note that specifying an input GRISM_TABLE will set some of the recipe\n"
00050 "configuration parameters to default values valid for a particular grism.\n"
00051 "\n"
00052 "In the table below the LSS acronym can be alternatively read as MOS or\n"
00053 "MXU.\n\n"
00054 "Input files:\n\n"
00055 "  DO category:               Type:       Explanation:         Required:\n"
00056 "  LAMP_UNBIAS_LSS            Calib       Arc lamp exposure       Y\n"
00057 "  MASTER_LINECAT             Calib       Line catalog            Y\n"
00058 "  GRISM_TABLE                Calib       Grism table             .\n\n"
00059 "Output files:\n\n"
00060 "  DO category:               Data type:  Explanation:\n"
00061 "  REDUCED_LAMP_LSS           FITS image  Calibrated arc lamp exposure\n"
00062 "  DISP_COEFF_LSS             FITS table  Inverse dispersion coefficients\n"
00063 "  DISP_RESIDUALS_LSS         FITS image  Image of modeling residuals\n"
00064 "  WAVELENGTH_MAP_LSS         FITS image  Wavelengths mapped on CCD\n"
00065 "  SLIT_LOCATION_LSS          FITS image  Background subtracted arc frame\n"
00066 "  SPECTRAL_RESOLUTION_LSS    FITS table  Spectral resolution table\n\n";
00067 
00068 #define fors_wave_calib_lss_exit(message)     \
00069 {                                             \
00070 if (message) cpl_msg_error(recipe, message);  \
00071 cpl_image_delete(spectra);                    \
00072 cpl_image_delete(residual);                   \
00073 cpl_image_delete(rectified);                  \
00074 cpl_image_delete(wavemap);                    \
00075 cpl_table_delete(grism_table);                \
00076 cpl_table_delete(wavelengths);                \
00077 cpl_table_delete(maskslits);                  \
00078 cpl_table_delete(idscoeff);                   \
00079 cpl_table_delete(idscoeff_all);               \
00080 cpl_table_delete(restab);                     \
00081 cpl_table_delete(slits);                      \
00082 cpl_vector_delete(lines);                     \
00083 cpl_propertylist_delete(header);              \
00084 cpl_propertylist_delete(save_header);         \
00085 cpl_msg_indent_less();                        \
00086 return -1;                                    \
00087 }
00088 
00089 #define fors_wave_calib_lss_exit_memcheck(message)  \
00090 {                                                   \
00091 if (message) cpl_msg_info(recipe, message);         \
00092 printf("free spectra (%p)\n", spectra);             \
00093 cpl_image_delete(spectra);                          \
00094 printf("free residual (%p)\n", residual);           \
00095 cpl_image_delete(residual);                         \
00096 printf("free rectified (%p)\n", rectified);         \
00097 cpl_image_delete(rectified);                        \
00098 printf("free wavemap (%p)\n", wavemap);             \
00099 cpl_image_delete(wavemap);                          \
00100 printf("free grism_table (%p)\n", grism_table);     \
00101 cpl_table_delete(grism_table);                      \
00102 printf("free wavelengths (%p)\n", wavelengths);     \
00103 cpl_table_delete(wavelengths);                      \
00104 printf("free maskslits (%p)\n", maskslits);         \
00105 cpl_table_delete(maskslits);                        \
00106 printf("free idscoeff (%p)\n", idscoeff);           \
00107 cpl_table_delete(idscoeff);                         \
00108 printf("free idscoeff_all (%p)\n", idscoeff_all);   \
00109 cpl_table_delete(idscoeff_all);                     \
00110 printf("free restab (%p)\n", restab);               \
00111 cpl_table_delete(restab);                           \
00112 printf("free slits (%p)\n", slits);                 \
00113 cpl_table_delete(slits);                            \
00114 printf("free lines (%p)\n", lines);                 \
00115 cpl_vector_delete(lines);                           \
00116 printf("free header (%p)\n", header);               \
00117 cpl_propertylist_delete(header);                    \
00118 printf("free save_header (%p)\n", save_header);     \
00119 cpl_propertylist_delete(save_header);               \
00120 cpl_msg_indent_less();                              \
00121 return 0;                                           \
00122 }
00123 
00124 
00136 int cpl_plugin_get_info(cpl_pluginlist *list)
00137 {
00138     cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe );
00139     cpl_plugin *plugin = &recipe->interface;
00140 
00141     cpl_plugin_init(plugin,
00142                     CPL_PLUGIN_API,
00143                     FORS_BINARY_VERSION,
00144                     CPL_PLUGIN_TYPE_RECIPE,
00145                     "fors_wave_calib_lss",
00146                     "Derive dispersion relation from long-slit arc lamp frame",
00147                     fors_wave_calib_lss_description,
00148                     "Carlo Izzo",
00149                     PACKAGE_BUGREPORT,
00150     "This file is currently part of the FORS Instrument Pipeline\n"
00151     "Copyright (C) 2002-2010 European Southern Observatory\n\n"
00152     "This program is free software; you can redistribute it and/or modify\n"
00153     "it under the terms of the GNU General Public License as published by\n"
00154     "the Free Software Foundation; either version 2 of the License, or\n"
00155     "(at your option) any later version.\n\n"
00156     "This program is distributed in the hope that it will be useful,\n"
00157     "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
00158     "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
00159     "GNU General Public License for more details.\n\n"
00160     "You should have received a copy of the GNU General Public License\n"
00161     "along with this program; if not, write to the Free Software Foundation,\n"
00162     "Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\n",
00163                     fors_wave_calib_lss_create,
00164                     fors_wave_calib_lss_exec,
00165                     fors_wave_calib_lss_destroy);
00166 
00167     cpl_pluginlist_append(list, plugin);
00168     
00169     return 0;
00170 }
00171 
00172 
00183 static int fors_wave_calib_lss_create(cpl_plugin *plugin)
00184 {
00185     cpl_recipe    *recipe;
00186     cpl_parameter *p;
00187 
00188     /* 
00189      * Check that the plugin is part of a valid recipe 
00190      */
00191 
00192     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00193         recipe = (cpl_recipe *)plugin;
00194     else 
00195         return -1;
00196 
00197     /* 
00198      * Create the (empty) parameters list in the cpl_recipe object 
00199      */
00200 
00201     recipe->parameters = cpl_parameterlist_new(); 
00202 
00203     /*
00204      * Dispersion
00205      */
00206 
00207     p = cpl_parameter_new_value("fors.fors_wave_calib_lss.dispersion",
00208                                 CPL_TYPE_DOUBLE,
00209                                 "Expected spectral dispersion (Angstrom/pixel)",
00210                                 "fors.fors_wave_calib_lss",
00211                                 0.0);
00212     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dispersion");
00213     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00214     cpl_parameterlist_append(recipe->parameters, p);
00215 
00216     /*
00217      * Peak detection level
00218      */
00219 
00220     p = cpl_parameter_new_value("fors.fors_wave_calib_lss.peakdetection",
00221                                 CPL_TYPE_DOUBLE,
00222                                 "Initial peak detection threshold (ADU)",
00223                                 "fors.fors_wave_calib_lss",
00224                                 0.0);
00225     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "peakdetection");
00226     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00227     cpl_parameterlist_append(recipe->parameters, p);
00228 
00229     /*
00230      * Degree of wavelength calibration polynomial
00231      */
00232 
00233     p = cpl_parameter_new_value("fors.fors_wave_calib_lss.wdegree",
00234                                 CPL_TYPE_INT,
00235                                 "Degree of wavelength calibration polynomial",
00236                                 "fors.fors_wave_calib_lss",
00237                                 0);
00238     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wdegree");
00239     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00240     cpl_parameterlist_append(recipe->parameters, p);
00241 
00242     /*
00243      * Reference lines search radius
00244      */
00245 
00246     p = cpl_parameter_new_value("fors.fors_wave_calib_lss.wradius",
00247                                 CPL_TYPE_INT,
00248                                 "Search radius if iterating pattern-matching "
00249                                 "with first-guess method",
00250                                 "fors.fors_wave_calib_lss",
00251                                 4);
00252     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wradius");
00253     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00254     cpl_parameterlist_append(recipe->parameters, p);
00255 
00256     /*
00257      * Rejection threshold in dispersion relation polynomial fitting
00258      */
00259 
00260     p = cpl_parameter_new_value("fors.fors_wave_calib_lss.wreject",
00261                                 CPL_TYPE_DOUBLE,
00262                                 "Rejection threshold in dispersion "
00263                                 "relation fit (pixel)",
00264                                 "fors.fors_wave_calib_lss",
00265                                 0.7);
00266     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wreject");
00267     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00268     cpl_parameterlist_append(recipe->parameters, p);
00269 
00270     /*
00271      * Line catalog table column containing the reference wavelengths
00272      */
00273 
00274     p = cpl_parameter_new_value("fors.fors_wave_calib_lss.wcolumn",
00275                                 CPL_TYPE_STRING,
00276                                 "Name of line catalog table column "
00277                                 "with wavelengths",
00278                                 "fors.fors_wave_calib_lss",
00279                                 "WLEN");
00280     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wcolumn");
00281     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00282     cpl_parameterlist_append(recipe->parameters, p);
00283 
00284     /*
00285      * Start wavelength for spectral extraction
00286      */
00287 
00288     p = cpl_parameter_new_value("fors.fors_wave_calib_lss.startwavelength",
00289                                 CPL_TYPE_DOUBLE,
00290                                 "Start wavelength in spectral extraction",
00291                                 "fors.fors_wave_calib_lss",
00292                                 0.0);
00293     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "startwavelength");
00294     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00295     cpl_parameterlist_append(recipe->parameters, p);
00296 
00297     /*
00298      * End wavelength for spectral extraction
00299      */
00300 
00301     p = cpl_parameter_new_value("fors.fors_wave_calib_lss.endwavelength",
00302                                 CPL_TYPE_DOUBLE,
00303                                 "End wavelength in spectral extraction",
00304                                 "fors.fors_wave_calib_lss",
00305                                 0.0);
00306     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "endwavelength");
00307     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00308     cpl_parameterlist_append(recipe->parameters, p);
00309 
00310     /*
00311      * Wavelength solution interpolation
00312      */
00313  
00314     p = cpl_parameter_new_value("fors.fors_wave_calib_lss.wmode",
00315                                 CPL_TYPE_INT,
00316                                 "Interpolation mode of wavelength solution "
00317                                 "(0 = no interpolation, 1 = fill gaps, "
00318                                 "2 = global model)",
00319                                 "fors.fors_wave_calib_lss",
00320                                 2);
00321     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "wmode");
00322     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00323     cpl_parameterlist_append(recipe->parameters, p);
00324 
00325     return 0;
00326 }
00327 
00328 
00337 static int fors_wave_calib_lss_exec(cpl_plugin *plugin)
00338 {
00339     cpl_recipe *recipe;
00340     
00341     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00342         recipe = (cpl_recipe *)plugin;
00343     else 
00344         return -1;
00345 
00346     return fors_wave_calib_lss(recipe->parameters, recipe->frames);
00347 }
00348 
00349 
00358 static int fors_wave_calib_lss_destroy(cpl_plugin *plugin)
00359 {
00360     cpl_recipe *recipe;
00361     
00362     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00363         recipe = (cpl_recipe *)plugin;
00364     else 
00365         return -1;
00366 
00367     cpl_parameterlist_delete(recipe->parameters); 
00368 
00369     return 0;
00370 }
00371 
00372 
00382 static int fors_wave_calib_lss(cpl_parameterlist *parlist, 
00383                                cpl_frameset *frameset)
00384 {
00385 
00386     const char *recipe = "fors_wave_calib_lss";
00387 
00388 
00389     /*
00390      * Input parameters
00391      */
00392 
00393     double      dispersion;
00394     double      peakdetection;
00395     int         wdegree;
00396     int         wradius;
00397     double      wreject;
00398     int         wmode;
00399     const char *wcolumn;
00400     double      startwavelength;
00401     double      endwavelength;
00402 
00403     /*
00404      * CPL objects
00405      */
00406 
00407     cpl_image        *spectra      = NULL;
00408     cpl_image        *rectified    = NULL;
00409     cpl_image        *wavemap      = NULL;
00410     cpl_image        *residual     = NULL;
00411     cpl_image        *dummy        = NULL;
00412     cpl_table        *grism_table  = NULL;
00413     cpl_table        *wavelengths  = NULL;
00414     cpl_table        *slits        = NULL;
00415     cpl_table        *idscoeff     = NULL;
00416     cpl_table        *idscoeff_all = NULL;
00417     cpl_table        *maskslits    = NULL;
00418     cpl_table        *restab       = NULL;
00419     cpl_vector       *lines        = NULL;
00420     cpl_propertylist *header       = NULL;
00421     cpl_propertylist *save_header  = NULL;
00422 
00423     /*
00424      * Auxiliary variables
00425      */
00426 
00427     char        version[80];
00428     const char *arc_tag;
00429     const char *reduced_lamp_tag;
00430     const char *wavelength_map_tag;
00431     const char *disp_residuals_tag;
00432     const char *disp_coeff_tag;
00433     const char *slit_location_tag;
00434     const char *spectral_resolution_tag;
00435     int         mxu;
00436     int         mos;
00437     int         lss;
00438     int         treat_as_lss = 0;
00439     int         nslits;
00440     double      mean_rms;
00441     double     *xpos;
00442     double      mxpos;
00443     int         narc;
00444     int         nlines;
00445     int         rebin;
00446     double     *line;
00447     double     *fiterror = NULL;
00448     int        *fitlines = NULL;
00449     int         nx, ny;
00450     int         first_row, last_row;
00451     int         ylow, yhig;
00452     double      reference;
00453     int         i;
00454 
00455     char       *instrume = NULL;
00456 
00457 
00458     cpl_msg_set_indentation(2);
00459 
00460     if (dfs_files_dont_exist(frameset))
00461         fors_wave_calib_lss_exit(NULL);
00462 
00463 
00464     /*
00465      * Get configuration parameters
00466      */
00467 
00468     cpl_msg_info(recipe, "Recipe %s configuration parameters:", recipe);
00469     cpl_msg_indent_more();
00470     
00471     if (cpl_frameset_count_tags(frameset, "GRISM_TABLE") > 1)
00472         fors_wave_calib_lss_exit("Too many in input: GRISM_TABLE"); 
00473 
00474     grism_table = dfs_load_table(frameset, "GRISM_TABLE", 1);
00475 
00476     dispersion = dfs_get_parameter_double(parlist,
00477                     "fors.fors_wave_calib_lss.dispersion", grism_table);
00478 
00479     if (dispersion <= 0.0)
00480         fors_wave_calib_lss_exit("Invalid spectral dispersion value");
00481 
00482     peakdetection = dfs_get_parameter_double(parlist,
00483                     "fors.fors_wave_calib_lss.peakdetection", grism_table);
00484     if (peakdetection <= 0.0)
00485         fors_wave_calib_lss_exit("Invalid peak detection level");
00486 
00487     wdegree = dfs_get_parameter_int(parlist,
00488                     "fors.fors_wave_calib_lss.wdegree", grism_table);
00489 
00490     if (wdegree < 1)
00491         fors_wave_calib_lss_exit("Invalid polynomial degree");
00492 
00493     if (wdegree > 5)
00494         fors_wave_calib_lss_exit("Max allowed polynomial degree is 5");
00495 
00496     wradius = dfs_get_parameter_int(parlist, 
00497                                     "fors.fors_wave_calib_lss.wradius", NULL);
00498 
00499     if (wradius < 0)
00500         fors_wave_calib_lss_exit("Invalid search radius");
00501 
00502     wreject = dfs_get_parameter_double(parlist, 
00503                                     "fors.fors_wave_calib_lss.wreject", NULL);
00504 
00505     if (wreject <= 0.0)
00506         fors_wave_calib_lss_exit("Invalid rejection threshold");
00507 
00508     wmode = dfs_get_parameter_int(parlist, 
00509                                     "fors.fors_wave_calib_lss.wmode", NULL);
00510 
00511     if (wmode < 0 || wmode > 2)
00512         fors_wave_calib_lss_exit("Invalid interpolation mode");
00513 
00514     wcolumn = dfs_get_parameter_string(parlist, 
00515                                     "fors.fors_wave_calib_lss.wcolumn", NULL);
00516 
00517     startwavelength = dfs_get_parameter_double(parlist,
00518                     "fors.fors_wave_calib_lss.startwavelength", grism_table);
00519     if (startwavelength > 1.0)
00520         if (startwavelength < 3000.0 || startwavelength > 13000.0)
00521             fors_wave_calib_lss_exit("Invalid wavelength");
00522 
00523     endwavelength = dfs_get_parameter_double(parlist,
00524                     "fors.fors_wave_calib_lss.endwavelength", grism_table);
00525     if (endwavelength > 1.0) {
00526         if (endwavelength < 3000.0 || endwavelength > 13000.0)
00527             fors_wave_calib_lss_exit("Invalid wavelength");
00528         if (startwavelength < 1.0)
00529             fors_wave_calib_lss_exit("Invalid wavelength interval");
00530     }
00531 
00532     if (startwavelength > 1.0)
00533         if (endwavelength - startwavelength <= 0.0)
00534             fors_wave_calib_lss_exit("Invalid wavelength interval");
00535 
00536     cpl_table_delete(grism_table); grism_table = NULL;
00537 
00538     if (cpl_error_get_code())
00539         fors_wave_calib_lss_exit("Failure reading the configuration "
00540                                  "parameters");
00541 
00542 
00543     cpl_msg_indent_less();
00544     cpl_msg_info(recipe, "Check input set-of-frames:");
00545     cpl_msg_indent_more();
00546 
00547     if (cpl_frameset_count_tags(frameset, "MASTER_LINECAT") == 0)
00548         fors_wave_calib_lss_exit("Missing required input: MASTER_LINECAT");
00549 
00550     if (cpl_frameset_count_tags(frameset, "MASTER_LINECAT") > 1)
00551         fors_wave_calib_lss_exit("Too many in input: MASTER_LINECAT");
00552 
00553     mxu = cpl_frameset_count_tags(frameset, "LAMP_UNBIAS_MXU");
00554     mos = cpl_frameset_count_tags(frameset, "LAMP_UNBIAS_MOS");
00555     lss = cpl_frameset_count_tags(frameset, "LAMP_UNBIAS_LSS");
00556 
00557     narc = mxu + mos + lss;
00558 
00559     if (narc == 0) {
00560         fors_wave_calib_lss_exit("Missing input long-slit arc lamp frame");
00561     }
00562     if (narc > 1) {
00563         cpl_msg_error(recipe, "Too many input arc lamp frames (%d > 1)", narc);
00564         fors_wave_calib_lss_exit(NULL);
00565     }
00566 
00567     if (mxu) {
00568         arc_tag                 = "LAMP_UNBIAS_MXU";
00569         slit_location_tag       = "SLIT_LOCATION_MXU";
00570         reduced_lamp_tag        = "REDUCED_LAMP_MXU";
00571         disp_residuals_tag      = "DISP_RESIDUALS_MXU";
00572         disp_coeff_tag          = "DISP_COEFF_MXU";
00573         wavelength_map_tag      = "WAVELENGTH_MAP_MXU";
00574         spectral_resolution_tag = "SPECTRAL_RESOLUTION_MXU";
00575     }
00576     else if (mos) {
00577         arc_tag                 = "LAMP_UNBIAS_MOS";
00578         slit_location_tag       = "SLIT_LOCATION_MOS";
00579         reduced_lamp_tag        = "REDUCED_LAMP_MOS";
00580         disp_residuals_tag      = "DISP_RESIDUALS_MOS";
00581         disp_coeff_tag          = "DISP_COEFF_MOS";
00582         wavelength_map_tag      = "WAVELENGTH_MAP_MOS";
00583         spectral_resolution_tag = "SPECTRAL_RESOLUTION_MOS";
00584     }
00585     else if (lss) {
00586         arc_tag                 = "LAMP_UNBIAS_LSS";
00587         slit_location_tag       = "SLIT_LOCATION_LSS";
00588         reduced_lamp_tag        = "REDUCED_LAMP_LSS";
00589         disp_residuals_tag      = "DISP_RESIDUALS_LSS";
00590         disp_coeff_tag          = "DISP_COEFF_LSS";
00591         wavelength_map_tag      = "WAVELENGTH_MAP_LSS";
00592         spectral_resolution_tag = "SPECTRAL_RESOLUTION_LSS";
00593     }
00594 
00595 
00596     if (!dfs_equal_keyword(frameset, "ESO INS GRIS1 ID"))
00597         fors_wave_calib_lss_exit("Input frames are not from the same grism");
00598 
00599     if (!dfs_equal_keyword(frameset, "ESO INS FILT1 ID"))
00600         fors_wave_calib_lss_exit("Input frames are not from the same filter");
00601 
00602     if (!dfs_equal_keyword(frameset, "ESO DET CHIP1 ID"))
00603         fors_wave_calib_lss_exit("Input frames are not from the same chip");
00604 
00605 
00606     /*
00607      * Get the reference wavelength and the rebin factor along the
00608      * dispersion direction from the arc lamp exposure
00609      */
00610 
00611     header = dfs_load_header(frameset, arc_tag, 0);
00612 
00613     if (header == NULL)
00614         fors_wave_calib_lss_exit("Cannot load arc lamp header");
00615 
00616     instrume = (char *)cpl_propertylist_get_string(header, "INSTRUME");
00617     if (instrume == NULL)
00618         fors_wave_calib_lss_exit("Missing keyword INSTRUME in arc lamp header");
00619 
00620     if (instrume[4] == '1')
00621         snprintf(version, 80, "%s/%s", "fors1", VERSION);
00622     if (instrume[4] == '2')
00623         snprintf(version, 80, "%s/%s", "fors2", VERSION);
00624 
00625     reference = cpl_propertylist_get_double(header, "ESO INS GRIS1 WLEN");
00626 
00627     if (cpl_error_get_code() != CPL_ERROR_NONE)
00628         fors_wave_calib_lss_exit("Missing keyword ESO INS GRIS1 WLEN "
00629                                  "in arc lamp frame header");
00630 
00631     if (reference < 3000.0)   /* Perhaps in nanometers... */
00632         reference *= 10;
00633 
00634     if (reference < 3000.0 || reference > 13000.0) {
00635         cpl_msg_error(recipe, "Invalid central wavelength %.2f read from "
00636                       "keyword ESO INS GRIS1 WLEN in arc lamp frame header",
00637                       reference);
00638         fors_wave_calib_lss_exit(NULL);
00639     }
00640 
00641     cpl_msg_info(recipe, "The central wavelength is: %.2f", reference);
00642 
00643     rebin = cpl_propertylist_get_int(header, "ESO DET WIN1 BINX");
00644 
00645     if (cpl_error_get_code() != CPL_ERROR_NONE)
00646         fors_wave_calib_lss_exit("Missing keyword ESO DET WIN1 BINX "
00647                                  "in arc lamp frame header");
00648 
00649     if (rebin != 1) {
00650         dispersion *= rebin;
00651         cpl_msg_warning(recipe, "The rebin factor is %d, and therefore the "
00652                         "working dispersion used is %f A/pixel", rebin,
00653                         dispersion);
00654     }
00655 
00656 
00657     if (mos || mxu) {
00658 
00659         int nslits_out_det = 0;
00660 
00661         /*
00662          * Check if all slits have the same X offset. If not, this is the
00663          * wrong recipe...
00664          */
00665 
00666         if (mos)
00667             maskslits = mos_load_slits_fors_mos(header, &nslits_out_det);
00668         else
00669             maskslits = mos_load_slits_fors_mxu(header);
00670 
00671         treat_as_lss = fors_mos_is_lss_like(maskslits, nslits_out_det);
00672 
00673         cpl_table_delete(maskslits); maskslits = NULL;
00674 
00675         if (!treat_as_lss)
00676             fors_wave_calib_lss_exit("Input data are not long-slit data");
00677     }
00678 
00679 
00680     cpl_msg_indent_less();
00681     cpl_msg_info(recipe, "Load arc lamp exposure...");
00682     cpl_msg_indent_more();
00683 
00684     spectra = dfs_load_image(frameset, arc_tag, CPL_TYPE_FLOAT, 0, 0);
00685 
00686     if (spectra == NULL)
00687         fors_wave_calib_lss_exit("Cannot load arc lamp exposure");
00688 
00689 
00690     cpl_msg_indent_less();
00691     cpl_msg_info(recipe, "Load input line catalog...");
00692     cpl_msg_indent_more();
00693 
00694     wavelengths = dfs_load_table(frameset, "MASTER_LINECAT", 1);
00695 
00696     if (wavelengths == NULL)
00697         fors_wave_calib_lss_exit("Cannot load line catalog");
00698 
00699 
00700     /*
00701      * Cast the wavelengths into a (double precision) CPL vector
00702      */
00703 
00704     nlines = cpl_table_get_nrow(wavelengths);
00705 
00706     if (nlines == 0)
00707         fors_wave_calib_lss_exit("Empty input line catalog");
00708 
00709     if (cpl_table_has_column(wavelengths, wcolumn) != 1) {
00710         cpl_msg_error(recipe, "Missing column %s in input line catalog table",
00711                       wcolumn);
00712         fors_wave_calib_lss_exit(NULL);
00713     }
00714 
00715     line = cpl_malloc(nlines * sizeof(double));
00716 
00717     for (i = 0; i < nlines; i++)
00718         line[i] = cpl_table_get(wavelengths, wcolumn, i, NULL);
00719 
00720     cpl_table_delete(wavelengths); wavelengths = NULL;
00721 
00722     lines = cpl_vector_wrap(nlines, line);
00723 
00724 
00725     cpl_msg_indent_less();
00726     cpl_msg_info(recipe, "Perform wavelength calibration...");
00727     cpl_msg_indent_more();
00728 
00729     nx = cpl_image_get_size_x(spectra);
00730     ny = cpl_image_get_size_y(spectra);
00731 
00732     wavemap = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
00733     idscoeff_all = cpl_table_new(ny);
00734 
00735     if (mos_saturation_process(spectra))
00736     fors_wave_calib_lss_exit("Cannot process saturation");
00737 
00738     if (mos_subtract_background(spectra))
00739     fors_wave_calib_lss_exit("Cannot subtract the background");
00740 
00741     rectified = mos_wavelength_calibration_raw(spectra, lines, dispersion,
00742                                                peakdetection, wradius,
00743                                                wdegree, wreject, reference,
00744                                                &startwavelength,
00745                                                &endwavelength, NULL,
00746                                                NULL, idscoeff_all, wavemap,
00747                                                NULL, NULL, NULL);
00748 
00749     if (rectified == NULL)
00750         fors_wave_calib_lss_exit("Wavelength calibration failure.");
00751 
00752     cpl_image_delete(rectified); rectified = NULL;
00753 
00754     first_row = 0;
00755     while (!cpl_table_is_valid(idscoeff_all, "c0", first_row))
00756         first_row++;
00757 
00758     last_row = ny - 1;
00759     while (!cpl_table_is_valid(idscoeff_all, "c0", last_row))
00760         last_row--;
00761 
00762     ylow = first_row + 1;
00763     yhig = last_row + 1;
00764 
00765     dummy = cpl_image_extract(spectra, 1, ylow, nx, yhig);
00766     cpl_image_delete(spectra); spectra = dummy;
00767 
00768     ny = cpl_image_get_size_y(spectra);
00769 
00770     residual = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
00771 
00772     fiterror = cpl_calloc(ny, sizeof(double));
00773     fitlines = cpl_calloc(ny, sizeof(int));
00774     idscoeff = cpl_table_new(ny);
00775 
00776     if (mos_saturation_process(spectra))
00777     fors_wave_calib_lss_exit("Cannot process saturation");
00778 
00779     if (mos_subtract_background(spectra))
00780     fors_wave_calib_lss_exit("Cannot subtract the background");
00781 
00782     rectified = mos_wavelength_calibration_raw(spectra, lines, dispersion,
00783                                                peakdetection, wradius,
00784                                                wdegree, wreject, reference,
00785                                                &startwavelength,
00786                                                &endwavelength, fitlines,
00787                                                fiterror, idscoeff, NULL,
00788                                                residual, NULL, NULL);
00789 
00790     if (rectified == NULL)
00791         fors_wave_calib_lss_exit("Wavelength calibration failure.");
00792 
00793     /*
00794      * A dummy slit locations table
00795      */
00796 
00797     slits = cpl_table_new(1);
00798     cpl_table_new_column(slits, "slit_id", CPL_TYPE_INT);
00799     cpl_table_new_column(slits, "xtop", CPL_TYPE_DOUBLE);
00800     cpl_table_new_column(slits, "ytop", CPL_TYPE_DOUBLE);
00801     cpl_table_new_column(slits, "xbottom", CPL_TYPE_DOUBLE);
00802     cpl_table_new_column(slits, "ybottom", CPL_TYPE_DOUBLE);
00803     cpl_table_new_column(slits, "position", CPL_TYPE_INT);
00804     cpl_table_new_column(slits, "length", CPL_TYPE_INT);
00805     cpl_table_set_column_unit(slits, "xtop", "pixel");
00806     cpl_table_set_column_unit(slits, "ytop", "pixel");
00807     cpl_table_set_column_unit(slits, "xbottom", "pixel");
00808     cpl_table_set_column_unit(slits, "ybottom", "pixel");
00809     cpl_table_set_column_unit(slits, "position", "pixel");
00810     cpl_table_set_column_unit(slits, "length", "pixel");
00811     cpl_table_set_int(slits, "slit_id", 0, 0);
00812     cpl_table_set_double(slits, "xtop", 0, 0);
00813     cpl_table_set_double(slits, "ytop", 0, last_row);
00814     cpl_table_set_double(slits, "xbottom", 0, 0);
00815     cpl_table_set_double(slits, "ybottom", 0, first_row);
00816     cpl_table_set_int(slits, "position", 0, 0);
00817     cpl_table_set_int(slits, "length", 0, ny);
00818 
00819     if (dfs_save_table(frameset, slits, slit_location_tag, NULL,
00820                        parlist, recipe, version))
00821         fors_wave_calib_lss_exit(NULL);
00822 
00823     cpl_table_delete(slits); slits = NULL;
00824 
00825     if (wmode) {
00826         cpl_image_delete(rectified); rectified = NULL;
00827         cpl_image_delete(wavemap); wavemap = NULL;
00828 
00829         /*
00830          * Wavemap is intentionally NULL in the next two calls
00831          */
00832 
00833         mos_interpolate_wavecalib(idscoeff, wavemap, wmode, 2);
00834         mos_interpolate_wavecalib(idscoeff_all, wavemap, wmode, 2);
00835 
00836         wavemap = mos_map_idscoeff(idscoeff_all, nx, reference,
00837                                    startwavelength, endwavelength);
00838         rectified = mos_wavelength_calibration(spectra, reference,
00839                                                startwavelength,
00840                                                endwavelength, dispersion,
00841                                                idscoeff, 0);
00842     }
00843 
00844     cpl_table_delete(idscoeff_all); idscoeff_all = NULL;
00845 
00846     cpl_table_wrap_double(idscoeff, fiterror, "error"); fiterror = NULL;
00847     cpl_table_set_column_unit(idscoeff, "error", "pixel");
00848     cpl_table_wrap_int(idscoeff, fitlines, "nlines"); fitlines = NULL;
00849 
00850     for (i = 0; i < ny; i++)
00851         if (!cpl_table_is_valid(idscoeff, "c0", i))
00852             cpl_table_set_invalid(idscoeff, "error", i);
00853 
00854     cpl_msg_info(recipe, "Valid solutions found: %d out of %d rows",
00855                  ny - cpl_table_count_invalid(idscoeff, "c0"), ny);
00856 
00857     cpl_image_delete(spectra); spectra = NULL;
00858 
00859     mean_rms = mos_distortions_rms(rectified, lines, startwavelength,
00860                                    dispersion, 6, 0);
00861 
00862     cpl_msg_info(recipe, "Mean residual: %f pixel", mean_rms);
00863 
00864     mean_rms = cpl_table_get_column_mean(idscoeff, "error");
00865 
00866     cpl_msg_info(recipe, "Mean model accuracy: %f pixel (%f A)",
00867                  mean_rms, mean_rms * dispersion);
00868 
00869     restab = mos_resolution_table(rectified, startwavelength, dispersion,
00870                                   60000, lines);
00871 
00872     if (restab) {
00873         cpl_msg_info(recipe, "Mean spectral resolution: %.2f",
00874               cpl_table_get_column_mean(restab, "resolution"));
00875         cpl_msg_info(recipe,
00876               "Mean reference lines FWHM: %.2f +/- %.2f pixel",
00877               cpl_table_get_column_mean(restab, "fwhm") / dispersion,
00878               cpl_table_get_column_mean(restab, "fwhm_rms") / dispersion);
00879 
00880         if (dfs_save_table(frameset, restab, spectral_resolution_tag,
00881                            NULL, parlist, recipe, version))
00882             fors_wave_calib_lss_exit(NULL);
00883 
00884         cpl_table_delete(restab); restab = NULL;
00885 
00886     }
00887     else
00888         fors_wave_calib_lss_exit("Cannot compute the spectral "
00889                                  "resolution table");
00890 
00891     cpl_vector_delete(lines); lines = NULL;
00892 
00893 
00894     /*
00895      * Save rectified arc lamp spectrum to disk
00896      */
00897 
00898     save_header = cpl_propertylist_new();
00899     cpl_propertylist_update_double(save_header, "CRPIX1", 1.0);
00900     cpl_propertylist_update_double(save_header, "CRPIX2", 1.0);
00901     cpl_propertylist_update_double(save_header, "CRVAL1",
00902                                    startwavelength + dispersion/2);
00903     cpl_propertylist_update_double(save_header, "CRVAL2", 1.0);
00904     /* cpl_propertylist_update_double(save_header, "CDELT1", dispersion);
00905     cpl_propertylist_update_double(save_header, "CDELT2", 1.0); */
00906     cpl_propertylist_update_double(save_header, "CD1_1", dispersion);
00907     cpl_propertylist_update_double(save_header, "CD1_2", 0.0);
00908     cpl_propertylist_update_double(save_header, "CD2_1", 0.0);
00909     cpl_propertylist_update_double(save_header, "CD2_2", 1.0);
00910     cpl_propertylist_update_string(save_header, "CTYPE1", "LINEAR");
00911     cpl_propertylist_update_string(save_header, "CTYPE2", "PIXEL");
00912     cpl_propertylist_update_int(save_header, "ESO PRO DATANCOM", 1);
00913 
00914     if (dfs_save_image(frameset, rectified, reduced_lamp_tag, save_header,
00915                        parlist, recipe, version))
00916         fors_wave_calib_lss_exit(NULL);
00917 
00918     cpl_image_delete(rectified); rectified = NULL;
00919     cpl_propertylist_delete(save_header); save_header = NULL;
00920 
00921     if (dfs_save_table(frameset, idscoeff, disp_coeff_tag, NULL,
00922                        parlist, recipe, version))
00923         fors_wave_calib_lss_exit(NULL);
00924 
00925     cpl_table_delete(idscoeff); idscoeff = NULL;
00926 
00927     if (dfs_save_image(frameset, wavemap, wavelength_map_tag, header,
00928                        parlist, recipe, version))
00929         fors_wave_calib_lss_exit(NULL);
00930 
00931     cpl_image_delete(wavemap); wavemap = NULL;
00932     cpl_propertylist_delete(header); header = NULL;
00933     header = cpl_propertylist_new();
00934 
00935     cpl_propertylist_update_double(header, "CRPIX2", 1.0);
00936     cpl_propertylist_update_double(header, "CRVAL2", 1.0);
00937     /* cpl_propertylist_update_double(header, "CDELT2", 1.0); */
00938     cpl_propertylist_update_double(header, "CD1_1", 1.0);
00939     cpl_propertylist_update_double(header, "CD1_2", 0.0);
00940     cpl_propertylist_update_double(header, "CD2_1", 0.0);
00941     cpl_propertylist_update_double(header, "CD2_2", 1.0);
00942     cpl_propertylist_update_string(header, "CTYPE1", "LINEAR");
00943     cpl_propertylist_update_string(header, "CTYPE2", "PIXEL");
00944 
00945     if (dfs_save_image(frameset, residual, disp_residuals_tag, header,
00946                        parlist, recipe, version))
00947         fors_wave_calib_lss_exit(NULL);
00948 
00949     cpl_image_delete(residual); residual = NULL;
00950     cpl_propertylist_delete(header); header = NULL;
00951 
00952     return 0;
00953 }