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