KMOS Pipeline Reference Manual  1.3.7
kmos_wave_cal.c
00001 /*
00002  * This file is part of the KMOS Pipeline
00003  * Copyright (C) 2002,2003 European Southern Observatory
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018  */
00019 
00020 #ifdef HAVE_CONFIG_H
00021 #include <config.h>
00022 #endif
00023 
00024 /*-----------------------------------------------------------------------------
00025  *                              Includes
00026  *----------------------------------------------------------------------------*/
00027 
00028 #include <string.h>
00029 #include <math.h>
00030 
00031 #ifdef __USE_XOPEN2K
00032 #include <stdlib.h>
00033 #define GGG
00034 #else
00035 #define __USE_XOPEN2K /* to get the definition for setenv in stdlib.h */
00036 #include <stdlib.h>
00037 #undef __USE_XOPEN2K
00038 #endif
00039 
00040 #include <cpl.h>
00041 
00042 #include "kmo_utils.h"
00043 #include "kmos_pfits.h"
00044 #include "kmo_functions.h"
00045 #include "kmo_priv_wave_cal.h"
00046 #include "kmo_priv_functions.h"
00047 #include "kmo_cpl_extensions.h"
00048 #include "kmo_dfs.h"
00049 #include "kmo_error.h"
00050 #include "kmo_constants.h"
00051 #include "kmo_debug.h"
00052 
00053 /*-----------------------------------------------------------------------------
00054  *                          Functions prototypes
00055  *----------------------------------------------------------------------------*/
00056 
00057 static int kmos_wave_cal_check_inputs(cpl_frameset *, int *, int *, int *, 
00058         double *, int *, lampConfiguration *);
00059 
00060 static int kmos_wave_cal_create(cpl_plugin *);
00061 static int kmos_wave_cal_exec(cpl_plugin *);
00062 static int kmos_wave_cal_destroy(cpl_plugin *);
00063 static int kmos_wave_cal(cpl_parameterlist *, cpl_frameset *);
00064 
00065 /*-----------------------------------------------------------------------------
00066  *                          Static variables
00067  *----------------------------------------------------------------------------*/
00068 
00069 static char kmos_wave_cal_description[] =
00070 "This recipe creates the wavelength calibration frame needed for all three\n"
00071 "detectors. It must be called after the kmo_flat recipe, which generates the\n"
00072 "two spatial calibration frames needed in this recipe. As input a lamp-on \n"
00073 "frame, a lamp-off frame, the spatial calibration frames and the list with \n"
00074 "the reference arclines are required.\n"
00075 "An additional output frame is the resampled image of the reconstructed arc\n"
00076 "frame. All slitlets of all IFUs are aligned one next to the other. This \n"
00077 "frame serves for quality control. One can immediately see if the \n"
00078 "calibration was successful.\n"
00079 "The lists of reference arclines are supposed to contain the lines for both\n"
00080 "available calibration arc-lamps, i.e. Argon and Neon. The list is supposed\n"
00081 "to be a F2L KMOS FITS file with three columns:\n"
00082 "\t1. Reference wavelength\n"
00083 "\t2. Relative strength\n"
00084 "\t3. String either containing “Ar” or “Ne”\n"
00085 "The recipe extracts, based on the header keywords, either the applying\n"
00086 "argon and/or neon emission lines. Below are the plots of the emission lines\n"
00087 "for both argon and neon. The marked lines are the ones used for wavelength \n"
00088 "calibration.\n"
00089 "\n"
00090 "Furthermore frames can be provided for several rotator angles. In this case\n"
00091 "the resulting calibration frames for each detector are repeatedly saved as \n"
00092 "extension for every angle.\n"
00093 "\n"
00094 "BASIC PARAMETERS:\n"
00095 "-----------------\n"
00096 "--order\n"
00097 "The polynomial order to use for the fit of the wavelength solution.\n"
00098 "0: (default) The appropriate order is choosen automatically depending on\n"
00099 "the waveband (4 for IZ band, 5 for HK, 6 for the others)\n"
00100 "\n"
00101 "ADVANCED PARAMETERS\n"
00102 "-------------------\n"
00103 "--b_samples\n"
00104 "The number of samples in spectral direction for the reconstructed cube.\n"
00105 "Ideally this number should be greater than 2048, the detector size.\n"
00106 "\n"
00107 "--b_start\n"
00108 "--b_end\n"
00109 "Used to define manually the start and end wavelength for the reconstructed\n"
00110 "cube. By default the internally defined values are used.\n"
00111 "\n"
00112 "--suppress_extension\n"
00113 "If set to TRUE, the arbitrary filename extensions are supressed. If\n"
00114 "multiple products with the same category are produced, they will be numered\n"
00115 "consecutively starting from 0.\n"
00116 "\n"
00117 "--lines_estimation\n"
00118 "If set to TRUE, the lines estimation method is used\n"
00119 "\n"
00120 "----------------------------------------------------------------------------\n"
00121 "Input files:\n"
00122 "\n"
00123 "   DO category       Type   Explanation                    Required #Frames\n"
00124 "   -----------       -----  -----------                    -------- -------\n"
00125 "   ARC_ON            RAW    Arclamp-on exposure                Y        >=1\n"
00126 "   ARC_OFF           RAW    Arclamp-off exposure               Y          1\n"
00127 "   XCAL              F2D    x calibration frame                Y          1\n"
00128 "   YCAL              F2D    y calibration frame                Y          1\n"
00129 "   ARC_LIST          F2L    List of arclines                   Y          1\n"
00130 "   FLAT_EDGE         F2L    Fitted edge parameters             Y          1\n"
00131 "   REF_LINES         F2L    Reference line table               Y          1\n"
00132 "   WAVE_BAND         F2L    Table with start-/end-wavelengths  Y          1\n"
00133 "\n"
00134 "Output files:\n"
00135 "\n"
00136 "   DO category       Type   Explanation\n"
00137 "   -----------       -----  -----------\n"
00138 "   LCAL              F2D    Wavelength calibration frame\n"
00139 "                            (3 Extensions)\n"
00140 "   DET_IMG_WAVE      F2D    reconstructed arclamp-on exposure\n"
00141 "                            (4 extensions: 3 detector images + \n"
00142 "                            the arclines list table)\n"
00143 "----------------------------------------------------------------------------\n"
00144 "\n";
00145 
00146 /*-----------------------------------------------------------------------------
00147  *                              Functions code
00148  *----------------------------------------------------------------------------*/
00149 
00156 /*----------------------------------------------------------------------------*/
00165 /*----------------------------------------------------------------------------*/
00166 int cpl_plugin_get_info(cpl_pluginlist *list)
00167 {
00168     cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe);
00169     cpl_plugin *plugin = &recipe->interface;
00170 
00171     cpl_plugin_init(plugin,
00172             CPL_PLUGIN_API,
00173             KMOS_BINARY_VERSION,
00174             CPL_PLUGIN_TYPE_RECIPE,
00175             "kmos_wave_cal",
00176             "Create a wavelength calibration frame",
00177             kmos_wave_cal_description,
00178             "Alex Agudo Berbel, Yves Jung",
00179             "usd-help@eso.org",
00180             kmos_get_license(),
00181             kmos_wave_cal_create,
00182             kmos_wave_cal_exec,
00183             kmos_wave_cal_destroy);
00184     cpl_pluginlist_append(list, plugin);
00185 
00186     return 0;
00187 }
00188 
00189 /*----------------------------------------------------------------------------*/
00197 /*----------------------------------------------------------------------------*/
00198 static int kmos_wave_cal_create(cpl_plugin *plugin)
00199 {
00200     cpl_recipe *recipe;
00201     cpl_parameter *p;
00202 
00203     // Check that the plugin is part of a valid recipe
00204     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00205         recipe = (cpl_recipe *)plugin;
00206     else
00207         return -1;
00208 
00209     // Create the parameters list in the cpl_recipe object
00210     recipe->parameters = cpl_parameterlist_new();
00211 
00212     // Fill the parameters list
00213     p = cpl_parameter_new_value("kmos.kmos_wave_cal.order", CPL_TYPE_INT,
00214             "The fitting polynomial order used for the wavelength solution. "
00215             "By default, 4 for IZ band, 5 for HK, 6 for the others",
00216             "kmos.kmos_wave_cal", 0);
00217     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "order");
00218     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00219     cpl_parameterlist_append(recipe->parameters, p);
00220 
00221     /* --suppress_extension */
00222     p = cpl_parameter_new_value("kmos.kmos_wave_cal.suppress_extension",
00223             CPL_TYPE_BOOL, "Suppress arbitrary filename extension",
00224             "kmos.kmos_wave_cal", FALSE);
00225     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "suppress_extension");
00226     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00227     cpl_parameterlist_append(recipe->parameters, p);
00228 
00229     /* --lines_estimation */
00230     p = cpl_parameter_new_value("kmos.kmos_wave_cal.lines_estimation",
00231             CPL_TYPE_BOOL, "Trigger lines estimation method",
00232             "kmos.kmos_wave_cal", FALSE);
00233     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "lines_estimation");
00234     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00235     cpl_parameterlist_append(recipe->parameters, p);
00236 
00237     /* Add parameters for band-definition */
00238     kmos_band_pars_create(recipe->parameters, "kmos.kmos_wave_cal");
00239 
00240     /* --detector */
00241     p = cpl_parameter_new_value("kmos.kmos_wave_cal.detector",
00242             CPL_TYPE_INT, "Only reduce the specified detector",
00243             "kmos.kmos_wave_cal", 0);
00244     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "det");
00245     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00246     cpl_parameterlist_append(recipe->parameters, p);
00247 
00248     /* --angle */
00249     p = cpl_parameter_new_value("kmos.kmos_wave_cal.angle",
00250             CPL_TYPE_DOUBLE, "Only reduce the specified angle",
00251             "kmos.kmos_wave_cal", 370.0);
00252     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "angle");
00253     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00254     cpl_parameterlist_append(recipe->parameters, p);
00255 
00256     return 0;
00257 }
00258 
00259 /*----------------------------------------------------------------------------*/
00265 /*----------------------------------------------------------------------------*/
00266 static int kmos_wave_cal_exec(cpl_plugin *plugin)
00267 {
00268     cpl_recipe  *recipe;
00269 
00270     // Get the recipe out of the plugin
00271     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00272         recipe = (cpl_recipe *)plugin;
00273     else return -1;
00274 
00275     return kmos_wave_cal(recipe->parameters, recipe->frames);
00276 }
00277 
00278 /*----------------------------------------------------------------------------*/
00284 /*----------------------------------------------------------------------------*/
00285 static int kmos_wave_cal_destroy(cpl_plugin *plugin)
00286 {
00287     cpl_recipe *recipe;
00288 
00289     // Get the recipe out of the plugin
00290     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00291         recipe = (cpl_recipe *)plugin;
00292     else return -1 ;
00293 
00294     cpl_parameterlist_delete(recipe->parameters);
00295     return 0 ;
00296 }
00297 
00298 /*----------------------------------------------------------------------------*/
00312 /*----------------------------------------------------------------------------*/
00313 static int kmos_wave_cal(cpl_parameterlist *parlist, cpl_frameset *frameset)
00314 {
00315     const cpl_parameter     *   par ;
00316     int                         suppress_extension, fit_order_par, fit_order ;
00317     int                         nx, ny, ne, reduce_det, lines_estimation ;
00318     double                      exptime, gain, angle_found, reduce_angle ;
00319     cpl_frame               *   frame ; 
00320     cpl_propertylist        *   mh_on ;
00321     cpl_propertylist        *   plist ;
00322     char                    *   suffix ;
00323     lampConfiguration           lamp_config;
00324     char                    **  filter_ids ;
00325     int                     *   angles_array ;
00326     int                         nb_angles ;
00327     int                         non_dest_rom ;
00328 
00329     cpl_propertylist        **  stored_sub_headers_lcal ;
00330     cpl_propertylist        **  stored_sub_headers_det_img ;
00331     cpl_image               **  stored_lcal ;
00332     cpl_image               **  stored_det_img ;
00333     int                     *   stored_qc_arc_sat ;
00334     double                  *   stored_qc_ar_eff ;
00335     double                  *   stored_qc_ne_eff ;
00336     cpl_table               *   detector_edges[KMOS_IFUS_PER_DETECTOR] ;
00337 
00338     int                         a, i, j, x, y ;
00339 
00340     cpl_image               *   det_lamp_on ;
00341     cpl_image               *   det_lamp_off ;
00342     cpl_image               *   det_lamp_on_copy ;
00343 
00344     cpl_table               *   arclines ;
00345     cpl_table               *   reflines ;
00346     cpl_bivector            *   lines ;
00347 
00348     cpl_image               *   bad_pix_mask ;
00349     float                   *   pbad_pix_mask ;
00350     cpl_image               *   xcal ;
00351     cpl_image               *   ycal ;
00352     cpl_image               *   lcal ;
00353 
00354     int                         nr_sat ;
00355 
00356     cpl_propertylist        *   qc_header ;
00357 
00358     cpl_array               **  unused_ifus_before ;
00359     cpl_array               **  unused_ifus_after ;
00360     char                    *   extname ;
00361     char                    *   fn_suffix ;
00362     char                    *   last_env ;
00363     const char              *   tmp_str ;
00364     cpl_error_code              err ;
00365 
00366     /* Check entries */
00367     if (parlist == NULL || frameset == NULL) {
00368         cpl_msg_error(__func__, "Null Inputs") ;
00369         cpl_error_set(__func__, CPL_ERROR_NULL_INPUT) ;
00370         return -1 ;
00371     }
00372     
00373     /* Get Parameters */
00374     par = cpl_parameterlist_find_const(parlist, "kmos.kmos_wave_cal.order");
00375     fit_order_par = cpl_parameter_get_int(par);
00376     par=cpl_parameterlist_find_const(parlist, 
00377             "kmos.kmos_wave_cal.lines_estimation");
00378     lines_estimation = cpl_parameter_get_bool(par);
00379     par=cpl_parameterlist_find_const(parlist, 
00380             "kmos.kmos_wave_cal.suppress_extension");
00381     suppress_extension = cpl_parameter_get_bool(par);
00382     par = cpl_parameterlist_find_const(parlist, "kmos.kmos_wave_cal.angle");
00383     reduce_angle = cpl_parameter_get_double(par);
00384     par = cpl_parameterlist_find_const(parlist, "kmos.kmos_wave_cal.detector");
00385     reduce_det = cpl_parameter_get_int(par);
00386 
00387     kmos_band_pars_load(parlist, "kmos.kmos_wave_cal");
00388 
00389     /* Check Parameters */
00390     if (fit_order_par < 0 || fit_order_par > 7) {
00391         cpl_msg_error(__func__, "Fitting Order must be in [0,7]") ;
00392         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00393         return -1 ;
00394     }
00395     if (reduce_det < 0 || reduce_det > 3) {
00396         cpl_msg_error(__func__, "detector must be in [1,3]") ;
00397         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00398         return -1 ;
00399     }
00400 
00401     /* Identify the RAW and CALIB frames in the input frameset */
00402     if (kmo_dfs_set_groups(frameset, "kmos_wave_cal") != 1) {
00403         cpl_msg_error(__func__, "Cannot identify RAW and CALIB frames") ;
00404         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00405         return -1 ;
00406     }
00407 
00408     /* Check the inputs consistency */
00409     if (kmos_wave_cal_check_inputs(frameset, &nx, &ny, &ne, &exptime,
00410                 &non_dest_rom, &lamp_config) != 1) {
00411         cpl_msg_error(__func__, "Input frameset is not consistent") ;
00412         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00413         return -1 ;
00414     }
00415    
00416     /* Instrument setup */
00417     suffix = kmo_dfs_get_suffix(kmo_dfs_get_frame(frameset, XCAL), TRUE, FALSE);
00418     cpl_msg_info(__func__, "Detected instrument setup:   %s", suffix+1);
00419 
00420     /* Check that filter and grating match for each detector */
00421     /* filter/grating can be different for each detector */
00422     frame = kmo_dfs_get_frame(frameset, ARC_ON);
00423     mh_on = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
00424     filter_ids =  kmo_get_filter_setup(mh_on, ne, TRUE) ;
00425     cpl_propertylist_delete(mh_on);
00426     if (filter_ids == NULL) {
00427         cpl_free(suffix);
00428         cpl_msg_error(__func__, "Cannot get Filter informations") ;
00429         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00430         return -1 ;
00431     }
00432 
00433     /* Get Rotator angles */
00434     if ((angles_array = kmos_get_angles(frameset, &nb_angles, ARC_ON)) == NULL){
00435         cpl_msg_error(__func__, "Cannot get Angles informations") ;
00436         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00437         for (i = 0; i < ne ; i++) cpl_free(filter_ids[i]);
00438         cpl_free(filter_ids);
00439         cpl_free(suffix);
00440         return -1 ;
00441     }
00442 
00443     /* Check the ARC_LIST filter */
00444     frame = kmo_dfs_get_frame(frameset, ARC_LIST);
00445     plist = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
00446     tmp_str = cpl_propertylist_get_string(plist, FILT_ID);
00447     if (strcmp(filter_ids[0], tmp_str) != 0) {
00448         cpl_msg_error(__func__, "Wrong ARC_LIST filter") ;
00449         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00450         for (i = 0; i < ne ; i++) cpl_free(filter_ids[i]);
00451         cpl_free(filter_ids);
00452         cpl_free(angles_array);
00453         cpl_propertylist_delete(plist); 
00454         return -1 ;
00455     }
00456     cpl_propertylist_delete(plist); 
00457 
00458     /* Load the lines as a CPL table */
00459     arclines = kmo_dfs_load_table(frameset, ARC_LIST, 1, 0);
00460     lines = kmos_get_lines(arclines, lamp_config);
00461     cpl_table_delete(arclines);
00462     /* TODO : check not null */
00463     cpl_msg_info(__func__, "Arc lines: %lld", cpl_bivector_get_size(lines));
00464 
00465     /* Load REFLINES */
00466     if (lines_estimation == 0) {
00467         reflines = kmo_dfs_load_table(frameset, REF_LINES, 1, 0);
00468     }
00469 
00470     /* Check which IFUs are active for all FLAT frames */
00471     unused_ifus_before = kmo_get_unused_ifus(frameset, 0, 0);
00472     unused_ifus_after = kmo_duplicate_unused_ifus(unused_ifus_before);
00473     kmo_print_unused_ifus(unused_ifus_before, FALSE);
00474     if (unused_ifus_before != NULL) kmo_free_unused_ifus(unused_ifus_before);
00475 
00476     /* make sure no reconstruction lookup table (LUT) is used */
00477     if (getenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE") != NULL) {
00478         last_env = getenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE");
00479     }
00480     setenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE","NONE",1);
00481 
00482     /* the frames have to be stored temporarily because the QC params */
00483     /* for the main header are calculated per detector. So they can be */
00484     /* stored only when all detectors are processed */
00485     stored_lcal = (cpl_image**)cpl_calloc(ne * nb_angles, sizeof(cpl_image*));
00486     stored_det_img = (cpl_image**)cpl_calloc(ne * nb_angles,sizeof(cpl_image*));
00487     stored_sub_headers_lcal = (cpl_propertylist**)cpl_calloc(ne * nb_angles,
00488             sizeof(cpl_propertylist*));
00489     stored_sub_headers_det_img = (cpl_propertylist**)cpl_calloc(ne * nb_angles,
00490             sizeof(cpl_propertylist*));
00491     stored_qc_arc_sat = (int*)cpl_calloc(ne, nb_angles * sizeof(int));
00492     stored_qc_ar_eff=(double*)cpl_calloc(ne, nb_angles * sizeof(double));
00493     stored_qc_ne_eff=(double*)cpl_calloc(ne, nb_angles * sizeof(double));
00494 
00495     /* Loop all Rotator Angles and Detectors  */
00496     for (a = 0; a < nb_angles; a++) {
00497         /* Reduce only one angle */
00498         if (reduce_angle <= 360 && angles_array[a] != reduce_angle) continue ;
00499 
00500         cpl_msg_info(__func__, "Processing rotator angle %d -> %d degree", 
00501                 a, angles_array[a]);
00502         cpl_msg_indent_more();
00503         for (i = 1; i <= ne ; i++) {
00504             /* Compute only one detetor */
00505             if (reduce_det != 0 && i != reduce_det) continue ;
00506 
00507             cpl_msg_info(__func__,"Processing detector No. %d", i);
00508             cpl_msg_indent_more();
00509 
00510             /* Load edge parameters */
00511             frame=kmo_dfs_get_frame(frameset, FLAT_EDGE);
00512             for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
00513                 detector_edges[j] = kmclipm_cal_table_load(
00514                         cpl_frame_get_filename(frame), 
00515                         (i-1) * KMOS_IFUS_PER_DETECTOR + j + 1,
00516                         angles_array[a], 0, &angle_found);
00517                 
00518                 /* IFU is inactive: proceed */
00519                 if (cpl_error_get_code() == CPL_ERROR_ILLEGAL_INPUT) {
00520                     cpl_error_reset();
00521                 }
00522             }
00523 
00524             /* Set default fit orders for the different bands */
00525             if (fit_order_par == 0) {
00526                 if ((strcmp(filter_ids[i-1], "H") == 0) ||
00527                     (strcmp(filter_ids[i-1], "K") == 0) ||
00528                     (strcmp(filter_ids[i-1], "YJ") == 0)) {
00529                     fit_order = 6;
00530                 } else if (strcmp(filter_ids[i-1], "IZ") == 0) {
00531                     fit_order = 4;
00532                 } else if (strcmp(filter_ids[i-1], "HK") == 0) {
00533                     fit_order = 5;
00534                 }
00535                 cpl_msg_info(__func__, 
00536                         "Order of wavelength spectrum fit for %s-band: %d",
00537                         filter_ids[i-1], fit_order);
00538             } else {
00539                 fit_order = fit_order_par;
00540             }
00541 
00542             /* Get ARC_ON frame and Load it */
00543             frame = kmos_get_angle_frame(frameset, angles_array[a], ARC_ON);
00544             det_lamp_on = kmo_dfs_load_image_frame(frame,i,FALSE, TRUE,&nr_sat);
00545             int sx = a * ne + (i - 1);
00546 
00547             /* Count saturated pixels for each detector */
00548             if (non_dest_rom)   
00549                 stored_qc_arc_sat[sx] = nr_sat;
00550             else 
00551                 stored_qc_arc_sat[sx] = kmo_image_get_saturated(det_lamp_on,
00552                         KMO_FLAT_SATURATED);
00553 
00554             det_lamp_on_copy = cpl_image_duplicate(det_lamp_on);
00555 
00556             /* Get ARC_OFF frame and Load it */
00557             frame = kmo_dfs_get_frame(frameset, ARC_OFF);
00558             det_lamp_off = kmo_dfs_load_image_frame(frame, i, FALSE, FALSE, 
00559                     NULL);
00560 
00561             /* ARC_ON = ARC_ON - ARC_OFF */
00562             cpl_image_subtract(det_lamp_on, det_lamp_off);
00563 
00564             /* Load XCAL,YCAL */
00565             xcal = kmo_dfs_load_cal_image(frameset, XCAL, i, 0, 
00566                     (double)angles_array[a], FALSE, NULL, &angle_found, -1,0,0);
00567             ycal = kmo_dfs_load_cal_image(frameset, YCAL, i, 0, 
00568                     (double)angles_array[a], FALSE, NULL, &angle_found, -1,0,0);
00569             if (xcal == NULL || ycal == NULL) {
00570                 /* Missing calibration for this detector */
00571                 cpl_error_reset() ;
00572                 stored_det_img[sx] = NULL ;
00573                 stored_lcal[sx] = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
00574                 kmo_image_fill(stored_lcal[sx], 0.0);
00575                 if (xcal != NULL) cpl_image_delete(xcal) ;
00576                 if (ycal != NULL) cpl_image_delete(ycal) ;
00577                 cpl_image_delete(det_lamp_on_copy) ;
00578                 cpl_image_delete(det_lamp_on) ;
00579                 cpl_image_delete(det_lamp_off) ;
00580                 continue ;
00581             }
00582 
00583             /* Derive BPM from XCAL : NaNs to 0, Others to 1  */
00584             bad_pix_mask = cpl_image_duplicate(xcal);
00585             pbad_pix_mask = cpl_image_get_data_float(bad_pix_mask);
00586             for (x = 0; x < nx; x++) {
00587                 for (y = 0; y < ny; y++) {
00588                     if (isnan(pbad_pix_mask[x+nx*y])) {
00589                         pbad_pix_mask[x+nx*y] = 0.;
00590                     } else {
00591                         pbad_pix_mask[x+nx*y] = 1.;
00592                     }
00593                 }
00594             }
00595 
00596             /* Compute wavelength calibration */
00597             err = kmos_calc_wave_calib(det_lamp_on, bad_pix_mask,
00598                     filter_ids[i-1], lamp_config, i, unused_ifus_after[i-1], 
00599                     detector_edges, lines, reflines, &lcal, 
00600                     &(stored_qc_ar_eff[sx]), &(stored_qc_ne_eff[sx]), fit_order,
00601                     lines_estimation);
00602             cpl_image_delete(det_lamp_on); 
00603             for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
00604                 cpl_table_delete(detector_edges[j]); 
00605             }
00606             if (err == CPL_ERROR_NONE) {
00607                 /* Update QC parameters */
00608                 if (stored_qc_ar_eff[sx] != -1.0) 
00609                     stored_qc_ar_eff[sx] /= exptime;
00610                 if (stored_qc_ne_eff[sx] != -1.0) 
00611                     stored_qc_ne_eff[sx] /= exptime;
00612 
00613                 /* Apply the badpixel mask to the produced frame */
00614                 cpl_image_multiply(lcal, bad_pix_mask);
00615                 kmo_image_reject_from_mask(lcal, bad_pix_mask);
00616 
00617                 /* Store Result frame */
00618                 stored_lcal[sx] = lcal;
00619             } else if (err == CPL_ERROR_UNSPECIFIED) {
00620                 /* All IFUs seem to be deactivated */
00621                 /* Continue processing - just save empty frame */
00622                 cpl_error_reset();
00623                 stored_lcal[sx] = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
00624                 kmo_image_fill(stored_lcal[sx], 0.0);
00625             } else {
00626                 cpl_error_reset();
00627                 cpl_msg_warning(__func__,
00628                         "Couldn't identify any line - Check the line list");
00629                 cpl_msg_warning(__func__,
00630                         "Band defined in header of detector %d: %s",
00631                         i, filter_ids[i-1]);
00632                 cpl_msg_warning(__func__, "Arc line file defined: %s",
00633                         cpl_frame_get_filename(kmo_dfs_get_frame(frameset, 
00634                                 ARC_LIST)));
00635             }
00636             cpl_image_delete(bad_pix_mask);
00637 
00638             /* CREATE RECONSTRUCTED AND RESAMPLED ARC FRAME */
00639             stored_det_img[sx] = kmo_reconstructed_arc_image(frameset,
00640                     det_lamp_on_copy, det_lamp_off, xcal, ycal, stored_lcal[sx],
00641                     unused_ifus_after[i-1], FALSE, i, suffix, filter_ids[i-1], 
00642                     lamp_config, &qc_header);
00643             cpl_image_delete(det_lamp_on_copy);
00644             cpl_image_delete(det_lamp_off); 
00645             cpl_image_delete(xcal);
00646             cpl_image_delete(ycal);
00647             if (cpl_error_get_code() != CPL_ERROR_NONE) {
00648                 cpl_msg_error(__func__,"Cannot reconstruct IFUs on detector %d",
00649                         i);
00650                 cpl_error_reset();
00651             }
00652 
00653             /* CREATE EXTENSION HEADER FOR THE PRODUCT */
00654             stored_sub_headers_lcal[sx] = kmo_dfs_load_sub_header(frameset, 
00655                     ARC_ON, i, FALSE);
00656             /* update EXTNAME */
00657             extname = kmo_extname_creator(detector_frame, i, EXT_DATA);
00658             kmclipm_update_property_string(stored_sub_headers_lcal[sx], EXTNAME,
00659                     extname, "FITS extension name");
00660             cpl_free(extname); 
00661 
00662             kmclipm_update_property_int(stored_sub_headers_lcal[sx], EXTVER, 
00663                     sx+1, "FITS extension ver");
00664 
00665             // add first QC parameters
00666             kmclipm_update_property_int(stored_sub_headers_lcal[sx], 
00667                     QC_ARC_SAT, stored_qc_arc_sat[sx], 
00668                     "[] nr. saturated pixels of arc exp.");
00669 
00670             gain=kmo_dfs_get_property_double(stored_sub_headers_lcal[sx],GAIN);
00671 
00672             if (stored_qc_ar_eff[sx] != -1.0) {
00673                 kmclipm_update_property_double(stored_sub_headers_lcal[sx], 
00674                         QC_ARC_AR_EFF, stored_qc_ar_eff[sx]/gain,
00675                         "[e-/s] Argon lamp efficiency");
00676             }
00677 
00678             if (stored_qc_ne_eff[sx] != -1.0) {
00679                 kmclipm_update_property_double(stored_sub_headers_lcal[sx], 
00680                         QC_ARC_NE_EFF, stored_qc_ne_eff[sx]/gain,
00681                         "[e-/s] Neon lamp efficiency");
00682             }
00683 
00684             kmclipm_update_property_double(stored_sub_headers_lcal[sx],
00685                     CAL_ROTANGLE, ((double) angles_array[a]),
00686                     "[deg] Rotator relative to nasmyth");
00687 
00688             /* append QC parameters */
00689             cpl_propertylist_append(stored_sub_headers_lcal[sx], qc_header);
00690             cpl_propertylist_delete(qc_header); 
00691 
00692             stored_sub_headers_det_img[sx]=cpl_propertylist_duplicate(
00693                     stored_sub_headers_lcal[sx]);
00694 
00695             cpl_propertylist_erase(stored_sub_headers_lcal[sx], CRVAL1);
00696             cpl_propertylist_erase(stored_sub_headers_lcal[sx], CRVAL2);
00697             cpl_propertylist_erase(stored_sub_headers_lcal[sx], CTYPE1);
00698             cpl_propertylist_erase(stored_sub_headers_lcal[sx], CTYPE2);
00699             cpl_propertylist_erase(stored_sub_headers_lcal[sx], CDELT1);
00700             cpl_propertylist_erase(stored_sub_headers_lcal[sx], CDELT2);
00701 
00702             cpl_msg_indent_less();
00703         } // for i devices
00704         cpl_msg_indent_less() ;
00705     } // for a angles
00706     
00707     /* Free */
00708     cpl_free(angles_array) ;
00709     for (i = 0; i < ne; i++) cpl_free(filter_ids[i]);
00710     cpl_free(filter_ids);
00711     cpl_bivector_delete(lines);
00712     
00713     cpl_free(stored_qc_arc_sat);
00714     cpl_free(stored_qc_ar_eff);
00715     cpl_free(stored_qc_ne_eff);
00716     if (lines_estimation == 0) cpl_table_delete(reflines); 
00717     
00718     /* QC parameters & saving */
00719     cpl_msg_info(__func__, "Saving data...");
00720 
00721     /* load, update & save primary header */
00722     if (!suppress_extension)    fn_suffix = cpl_sprintf("%s", suffix);
00723     else                        fn_suffix = cpl_sprintf("%s", "");
00724     cpl_free(suffix);
00725 
00726     /* update which IFUs are not used */
00727     frame = kmo_dfs_get_frame(frameset, ARC_ON);
00728     mh_on = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0);
00729     kmo_set_unused_ifus(unused_ifus_after, mh_on, "kmos_wave_cal");
00730     kmo_dfs_save_main_header(frameset, LCAL, fn_suffix, frame, mh_on, parlist, 
00731             cpl_func);
00732     kmo_dfs_save_main_header(frameset, DET_IMG_WAVE, fn_suffix, frame, mh_on, 
00733             parlist, cpl_func);
00734     cpl_propertylist_delete(mh_on); 
00735 
00736     /* Save sub-frames */
00737     for (a = 0; a < nb_angles; a++) {
00738         for (i = 1; i <= ne ; i++) {
00739             int sx = a * ne + (i - 1);
00740             /* save lcal-frame */
00741             kmo_dfs_save_image(stored_lcal[sx], LCAL, fn_suffix, 
00742                     stored_sub_headers_lcal[sx], 0./0.);
00743 
00744             /* save detector image */
00745             kmo_dfs_save_image(stored_det_img[sx], DET_IMG_WAVE, fn_suffix, 
00746                     stored_sub_headers_det_img[sx], 0./0.);
00747         } // for i = ne
00748     } // for a angles
00749    
00750     /* Free */
00751     cpl_free(fn_suffix);
00752     for (i = 0; i < ne * nb_angles; i++) {
00753         cpl_image_delete(stored_lcal[i]);
00754         cpl_image_delete(stored_det_img[i]);
00755         cpl_propertylist_delete(stored_sub_headers_lcal[i]);
00756         cpl_propertylist_delete(stored_sub_headers_det_img[i]);
00757     }
00758     cpl_free(stored_lcal);
00759     cpl_free(stored_det_img);
00760     cpl_free(stored_sub_headers_lcal); 
00761     cpl_free(stored_sub_headers_det_img);
00762 
00763     /* print which IFUs are not used */
00764     kmo_print_unused_ifus(unused_ifus_after, TRUE);
00765     if (unused_ifus_after != NULL) kmo_free_unused_ifus(unused_ifus_after);
00766 
00767     if (last_env != NULL) {
00768         setenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE",last_env,1);
00769     } else {
00770         unsetenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE");
00771     }
00772     return 0;
00773 }
00774 
00777 /*----------------------------------------------------------------------------*/
00789 /*----------------------------------------------------------------------------*/
00790 static int kmos_wave_cal_check_inputs(
00791         cpl_frameset            *   frameset, 
00792         int                     *   nx, 
00793         int                     *   ny,
00794         int                     *   ne,
00795         double                  *   exptime,
00796         int                     *   non_dest_rom,
00797         lampConfiguration       *   lamp_config)
00798 {
00799     const cpl_frame     *   frame_off ;
00800     const cpl_frame     *   frame_on ;
00801     cpl_propertylist    *   mh_off ;
00802     cpl_propertylist    *   mh_on ;
00803     cpl_propertylist    *   eh_off ;
00804     cpl_propertylist    *   eh_on ;
00805     int                     ext, next_off, next_on, nx_on, ny_on, nx_off,ny_off;
00806     double                  ndit_off, ndit_on, exptime_off, exptime_on ;
00807     const char          *   readmode_off ;
00808     const char          *   readmode_on ;
00809 
00810     /* TODO Check Lamps TODO */
00811 
00812     /* Check Entries */
00813     if (nx == NULL || ny == NULL || ne == NULL || frameset == NULL) return -1;
00814 
00815     /* Setup lamp config */
00816     frame_on = kmo_dfs_get_frame(frameset, ARC_ON);
00817     mh_on = cpl_propertylist_load(cpl_frame_get_filename(frame_on), 0);
00818     if ((kmo_check_lamp(mh_on, INS_LAMP1_ST) == TRUE) &&
00819         (kmo_check_lamp(mh_on, INS_LAMP2_ST) == FALSE)) {
00820         *lamp_config = ARGON;
00821         cpl_msg_info(__func__, "Arc lamp: Argon");
00822     } else if ((kmo_check_lamp(mh_on, INS_LAMP1_ST) == FALSE) &&
00823                (kmo_check_lamp(mh_on, INS_LAMP2_ST) == TRUE)) {
00824         *lamp_config = NEON;
00825         cpl_msg_info(__func__, "Arc lamp: Neon");
00826     } else if ((kmo_check_lamp(mh_on, INS_LAMP1_ST) == TRUE) &&
00827                (kmo_check_lamp(mh_on, INS_LAMP2_ST) == TRUE)) {
00828         *lamp_config = ARGON_NEON;
00829         cpl_msg_info(__func__, "Arc lamp: Argon + Neon");
00830     } else {
00831         *lamp_config = -1 ;
00832         cpl_warning_info(__func__, "Arc lamp: UNDEFINED");
00833     }
00834 
00835     /* Check READ OUT MODE */
00836     readmode_on = kmos_pfits_get_readmode(mh_on);
00837     if (!strcmp(readmode_on, "Nondest")) {
00838         *non_dest_rom = 1 ;
00839     } else {
00840         *non_dest_rom = 0 ;
00841     }
00842     cpl_propertylist_delete(mh_on);
00843 
00844     /* Get ARC_OFF */
00845     frame_off = kmo_dfs_get_frame(frameset, ARC_OFF);
00846     if (frame_off == NULL) {
00847         cpl_msg_error(__func__, "No ARC_OFF frame found") ;
00848         return -1 ;
00849     }
00850 
00851     /* Get ARC_OFF main header infos */
00852     next_off = cpl_frame_get_nextensions(frame_off);
00853     mh_off = cpl_propertylist_load(cpl_frame_get_filename(frame_off), 0);
00854     ndit_off = kmos_pfits_get_ndit(mh_off) ;
00855     exptime_off = kmos_pfits_get_exptime(mh_off) ;
00856     readmode_off = kmos_pfits_get_readmode(mh_off);
00857 
00858     /* Get ARC_ON frames and loop on them */
00859     frame_on = kmo_dfs_get_frame(frameset, ARC_ON);
00860     if (frame_on == NULL) {
00861         cpl_msg_error(__func__, "No ARC_ON frame found") ;
00862         cpl_propertylist_delete(mh_off);
00863         return -1 ;
00864     }
00865     while (frame_on != NULL) {
00866         /* Get ARC_ON main header infos */
00867         next_on = cpl_frame_get_nextensions(frame_on);
00868         mh_on = cpl_propertylist_load(cpl_frame_get_filename(frame_on), 0);
00869         ndit_on = kmos_pfits_get_ndit(mh_on) ;
00870         exptime_on = kmos_pfits_get_exptime(mh_on) ;
00871         readmode_on = kmos_pfits_get_readmode(mh_on);
00872 
00873         /* Check consistency */
00874         if (ndit_on != ndit_off || strcmp(readmode_on, readmode_off) || 
00875                 fabs(exptime_on-exptime_off) > 0.01 || next_off != next_on) {
00876             cpl_msg_warning(__func__, "Inconsistency for frame %s", 
00877                     cpl_frame_get_filename(frame_on)) ;
00878             cpl_propertylist_delete(mh_off);
00879             cpl_propertylist_delete(mh_on);
00880             return 0 ;
00881         }
00882         cpl_propertylist_delete(mh_on);
00883 
00884         /* Get next frame */
00885         frame_on = kmo_dfs_get_frame(frameset, NULL);
00886     }
00887     cpl_propertylist_delete(mh_off);
00888 
00889     /* Check the extensions */
00890     for (ext = 1; ext <= next_off ; ext++) {
00891         eh_off = cpl_propertylist_load(cpl_frame_get_filename(frame_off), ext);
00892         nx_off = kmos_pfits_get_naxis1(eh_off) ;
00893         ny_off = kmos_pfits_get_naxis2(eh_off) ;
00894 
00895         frame_on = kmo_dfs_get_frame(frameset, ARC_ON);
00896         while (frame_on != NULL) {
00897             eh_on = cpl_propertylist_load(cpl_frame_get_filename(frame_on),ext);
00898             nx_on = kmos_pfits_get_naxis1(eh_on) ;
00899             ny_on = kmos_pfits_get_naxis2(eh_on) ;
00900             /* Check consistency */
00901             if (nx_on != nx_off || ny_off != ny_on) { 
00902                 cpl_msg_warning(__func__, "Inconsistency for frame %s", 
00903                         cpl_frame_get_filename(frame_on)) ;
00904                 cpl_propertylist_delete(eh_off);
00905                 cpl_propertylist_delete(eh_on);
00906                 return 0 ;
00907             }
00908             cpl_propertylist_delete(eh_on);
00909 
00910             /* Get next frame */
00911             frame_on = kmo_dfs_get_frame(frameset, NULL);
00912         }
00913         cpl_propertylist_delete(eh_off);
00914     }
00915  
00916     /* FLAT_EDGE Checks */
00917     frame_on = kmo_dfs_get_frame(frameset, FLAT_EDGE);
00918     if (cpl_frame_get_nextensions(frame_on) % 24 != 0) {
00919         cpl_msg_warning(__func__, "FLAT_EDGE frame is not consistent") ;
00920         return 0 ;
00921     }
00922 
00923     /* Checks on XCAL YCAL */
00924     kmo_check_frame_setup(frameset, ARC_ON, XCAL, TRUE, FALSE, FALSE);
00925     kmo_check_frame_setup(frameset, ARC_ON, YCAL, TRUE, FALSE, FALSE);
00926     kmo_check_frame_setup_md5_xycal(frameset);
00927     if (cpl_error_get_code() != CPL_ERROR_NONE) {
00928         cpl_msg_warning(__func__, "XCAL / YCAL checks failed") ;
00929         return 0 ;
00930     }
00931 
00932     /* Return */
00933     *nx = nx_off ;
00934     *ny = ny_off ;
00935     *ne = next_off ;
00936     *exptime = exptime_off ;
00937     return 1 ;
00938 }
00939 
00940