KMOS Pipeline Reference Manual  1.3.10
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     } else {
00480         last_env = NULL ;
00481     }
00482     setenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE","NONE",1);
00483 
00484     /* the frames have to be stored temporarily because the QC params */
00485     /* for the main header are calculated per detector. So they can be */
00486     /* stored only when all detectors are processed */
00487     stored_lcal = (cpl_image**)cpl_calloc(ne * nb_angles, sizeof(cpl_image*));
00488     stored_det_img = (cpl_image**)cpl_calloc(ne * nb_angles,sizeof(cpl_image*));
00489     stored_sub_headers_lcal = (cpl_propertylist**)cpl_calloc(ne * nb_angles,
00490             sizeof(cpl_propertylist*));
00491     stored_sub_headers_det_img = (cpl_propertylist**)cpl_calloc(ne * nb_angles,
00492             sizeof(cpl_propertylist*));
00493     stored_qc_arc_sat = (int*)cpl_calloc(ne, nb_angles * sizeof(int));
00494     stored_qc_ar_eff=(double*)cpl_calloc(ne, nb_angles * sizeof(double));
00495     stored_qc_ne_eff=(double*)cpl_calloc(ne, nb_angles * sizeof(double));
00496 
00497     /* Loop all Rotator Angles and Detectors  */
00498     for (a = 0; a < nb_angles; a++) {
00499         /* Reduce only one angle */
00500         if (reduce_angle <= 360 && angles_array[a] != reduce_angle) continue ;
00501 
00502         cpl_msg_info(__func__, "Processing rotator angle %d -> %d degree", 
00503                 a, angles_array[a]);
00504         cpl_msg_indent_more();
00505         for (i = 1; i <= ne ; i++) {
00506             /* Compute only one detetor */
00507             if (reduce_det != 0 && i != reduce_det) continue ;
00508 
00509             cpl_msg_info(__func__,"Processing detector No. %d", i);
00510             cpl_msg_indent_more();
00511 
00512             /* Load edge parameters */
00513             frame=kmo_dfs_get_frame(frameset, FLAT_EDGE);
00514             for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
00515                 detector_edges[j] = kmclipm_cal_table_load(
00516                         cpl_frame_get_filename(frame), 
00517                         (i-1) * KMOS_IFUS_PER_DETECTOR + j + 1,
00518                         angles_array[a], 0, &angle_found);
00519                 
00520                 /* IFU is inactive: proceed */
00521                 if (cpl_error_get_code() == CPL_ERROR_ILLEGAL_INPUT) {
00522                     cpl_error_reset();
00523                 }
00524             }
00525 
00526             /* Set default fit orders for the different bands */
00527             if (fit_order_par == 0) {
00528                 if ((strcmp(filter_ids[i-1], "H") == 0) ||
00529                     (strcmp(filter_ids[i-1], "K") == 0) ||
00530                     (strcmp(filter_ids[i-1], "YJ") == 0)) {
00531                     fit_order = 6;
00532                 } else if (strcmp(filter_ids[i-1], "IZ") == 0) {
00533                     fit_order = 4;
00534                 } else if (strcmp(filter_ids[i-1], "HK") == 0) {
00535                     fit_order = 5;
00536                 }
00537                 cpl_msg_info(__func__, 
00538                         "Order of wavelength spectrum fit for %s-band: %d",
00539                         filter_ids[i-1], fit_order);
00540             } else {
00541                 fit_order = fit_order_par;
00542             }
00543 
00544             /* Get ARC_ON frame and Load it */
00545             frame = kmos_get_angle_frame(frameset, angles_array[a], ARC_ON);
00546             det_lamp_on = kmo_dfs_load_image_frame(frame,i,FALSE, TRUE,&nr_sat);
00547             int sx = a * ne + (i - 1);
00548 
00549             /* Count saturated pixels for each detector */
00550             if (non_dest_rom)   
00551                 stored_qc_arc_sat[sx] = nr_sat;
00552             else 
00553                 stored_qc_arc_sat[sx] = kmo_image_get_saturated(det_lamp_on,
00554                         KMO_FLAT_SATURATED);
00555 
00556             det_lamp_on_copy = cpl_image_duplicate(det_lamp_on);
00557 
00558             /* Get ARC_OFF frame and Load it */
00559             frame = kmo_dfs_get_frame(frameset, ARC_OFF);
00560             det_lamp_off = kmo_dfs_load_image_frame(frame, i, FALSE, FALSE, 
00561                     NULL);
00562 
00563             /* ARC_ON = ARC_ON - ARC_OFF */
00564             cpl_image_subtract(det_lamp_on, det_lamp_off);
00565 
00566             /* Load XCAL,YCAL */
00567             xcal = kmo_dfs_load_cal_image(frameset, XCAL, i, 0, 
00568                     (double)angles_array[a], FALSE, NULL, &angle_found, -1,0,0);
00569             ycal = kmo_dfs_load_cal_image(frameset, YCAL, i, 0, 
00570                     (double)angles_array[a], FALSE, NULL, &angle_found, -1,0,0);
00571             if (xcal == NULL || ycal == NULL) {
00572                 /* Missing calibration for this detector */
00573                 cpl_error_reset() ;
00574                 stored_det_img[sx] = NULL ;
00575                 stored_lcal[sx] = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
00576                 kmo_image_fill(stored_lcal[sx], 0.0);
00577                 if (xcal != NULL) cpl_image_delete(xcal) ;
00578                 if (ycal != NULL) cpl_image_delete(ycal) ;
00579                 cpl_image_delete(det_lamp_on_copy) ;
00580                 cpl_image_delete(det_lamp_on) ;
00581                 cpl_image_delete(det_lamp_off) ;
00582                 continue ;
00583             }
00584 
00585             /* Derive BPM from XCAL : NaNs to 0, Others to 1  */
00586             bad_pix_mask = cpl_image_duplicate(xcal);
00587             pbad_pix_mask = cpl_image_get_data_float(bad_pix_mask);
00588             for (x = 0; x < nx; x++) {
00589                 for (y = 0; y < ny; y++) {
00590                     if (isnan(pbad_pix_mask[x+nx*y])) {
00591                         pbad_pix_mask[x+nx*y] = 0.;
00592                     } else {
00593                         pbad_pix_mask[x+nx*y] = 1.;
00594                     }
00595                 }
00596             }
00597 
00598             /* Compute wavelength calibration */
00599             err = kmos_calc_wave_calib(det_lamp_on, bad_pix_mask,
00600                     filter_ids[i-1], lamp_config, i, unused_ifus_after[i-1], 
00601                     detector_edges, lines, reflines, &lcal, 
00602                     &(stored_qc_ar_eff[sx]), &(stored_qc_ne_eff[sx]), fit_order,
00603                     lines_estimation);
00604             cpl_image_delete(det_lamp_on); 
00605             for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
00606                 cpl_table_delete(detector_edges[j]); 
00607             }
00608             if (err == CPL_ERROR_NONE) {
00609                 /* Update QC parameters */
00610                 if (stored_qc_ar_eff[sx] != -1.0) 
00611                     stored_qc_ar_eff[sx] /= exptime;
00612                 if (stored_qc_ne_eff[sx] != -1.0) 
00613                     stored_qc_ne_eff[sx] /= exptime;
00614 
00615                 /* Apply the badpixel mask to the produced frame */
00616                 cpl_image_multiply(lcal, bad_pix_mask);
00617                 kmo_image_reject_from_mask(lcal, bad_pix_mask);
00618 
00619                 /* Store Result frame */
00620                 stored_lcal[sx] = lcal;
00621             } else if (err == CPL_ERROR_UNSPECIFIED) {
00622                 /* All IFUs seem to be deactivated */
00623                 /* Continue processing - just save empty frame */
00624                 cpl_error_reset();
00625                 stored_lcal[sx] = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
00626                 kmo_image_fill(stored_lcal[sx], 0.0);
00627             } else {
00628                 cpl_error_reset();
00629                 cpl_msg_warning(__func__,
00630                         "Couldn't identify any line - Check the line list");
00631                 cpl_msg_warning(__func__,
00632                         "Band defined in header of detector %d: %s",
00633                         i, filter_ids[i-1]);
00634                 cpl_msg_warning(__func__, "Arc line file defined: %s",
00635                         cpl_frame_get_filename(kmo_dfs_get_frame(frameset, 
00636                                 ARC_LIST)));
00637             }
00638             cpl_image_delete(bad_pix_mask);
00639 
00640             /* CREATE RECONSTRUCTED AND RESAMPLED ARC FRAME */
00641             stored_det_img[sx] = kmo_reconstructed_arc_image(frameset,
00642                     det_lamp_on_copy, det_lamp_off, xcal, ycal, stored_lcal[sx],
00643                     unused_ifus_after[i-1], FALSE, i, suffix, filter_ids[i-1], 
00644                     lamp_config, &qc_header);
00645             cpl_image_delete(det_lamp_on_copy);
00646             cpl_image_delete(det_lamp_off); 
00647             cpl_image_delete(xcal);
00648             cpl_image_delete(ycal);
00649             if (cpl_error_get_code() != CPL_ERROR_NONE) {
00650                 cpl_msg_error(__func__,"Cannot reconstruct IFUs on detector %d",
00651                         i);
00652                 cpl_error_reset();
00653             }
00654 
00655             /* CREATE EXTENSION HEADER FOR THE PRODUCT */
00656             stored_sub_headers_lcal[sx] = kmo_dfs_load_sub_header(frameset, 
00657                     ARC_ON, i, FALSE);
00658             /* update EXTNAME */
00659             extname = kmo_extname_creator(detector_frame, i, EXT_DATA);
00660             kmclipm_update_property_string(stored_sub_headers_lcal[sx], EXTNAME,
00661                     extname, "FITS extension name");
00662             cpl_free(extname); 
00663 
00664             kmclipm_update_property_int(stored_sub_headers_lcal[sx], EXTVER, 
00665                     sx+1, "FITS extension ver");
00666 
00667             // add first QC parameters
00668             kmclipm_update_property_int(stored_sub_headers_lcal[sx], 
00669                     QC_ARC_SAT, stored_qc_arc_sat[sx], 
00670                     "[] nr. saturated pixels of arc exp.");
00671 
00672             gain=kmo_dfs_get_property_double(stored_sub_headers_lcal[sx],GAIN);
00673 
00674             if (stored_qc_ar_eff[sx] != -1.0) {
00675                 kmclipm_update_property_double(stored_sub_headers_lcal[sx], 
00676                         QC_ARC_AR_EFF, stored_qc_ar_eff[sx]/gain,
00677                         "[e-/s] Argon lamp efficiency");
00678             }
00679 
00680             if (stored_qc_ne_eff[sx] != -1.0) {
00681                 kmclipm_update_property_double(stored_sub_headers_lcal[sx], 
00682                         QC_ARC_NE_EFF, stored_qc_ne_eff[sx]/gain,
00683                         "[e-/s] Neon lamp efficiency");
00684             }
00685 
00686             kmclipm_update_property_double(stored_sub_headers_lcal[sx],
00687                     CAL_ROTANGLE, ((double) angles_array[a]),
00688                     "[deg] Rotator relative to nasmyth");
00689 
00690             /* append QC parameters */
00691             cpl_propertylist_append(stored_sub_headers_lcal[sx], qc_header);
00692             cpl_propertylist_delete(qc_header); 
00693 
00694             stored_sub_headers_det_img[sx]=cpl_propertylist_duplicate(
00695                     stored_sub_headers_lcal[sx]);
00696 
00697             cpl_propertylist_erase(stored_sub_headers_lcal[sx], CRVAL1);
00698             cpl_propertylist_erase(stored_sub_headers_lcal[sx], CRVAL2);
00699             cpl_propertylist_erase(stored_sub_headers_lcal[sx], CTYPE1);
00700             cpl_propertylist_erase(stored_sub_headers_lcal[sx], CTYPE2);
00701             cpl_propertylist_erase(stored_sub_headers_lcal[sx], CDELT1);
00702             cpl_propertylist_erase(stored_sub_headers_lcal[sx], CDELT2);
00703 
00704             cpl_msg_indent_less();
00705         } // for i devices
00706         cpl_msg_indent_less() ;
00707     } // for a angles
00708     
00709     /* Free */
00710     cpl_free(angles_array) ;
00711     for (i = 0; i < ne; i++) cpl_free(filter_ids[i]);
00712     cpl_free(filter_ids);
00713     cpl_bivector_delete(lines);
00714     
00715     cpl_free(stored_qc_arc_sat);
00716     cpl_free(stored_qc_ar_eff);
00717     cpl_free(stored_qc_ne_eff);
00718     if (lines_estimation == 0) cpl_table_delete(reflines); 
00719     
00720     /* QC parameters & saving */
00721     cpl_msg_info(__func__, "Saving data...");
00722 
00723     /* load, update & save primary header */
00724     if (!suppress_extension)    fn_suffix = cpl_sprintf("%s", suffix);
00725     else                        fn_suffix = cpl_sprintf("%s", "");
00726     cpl_free(suffix);
00727 
00728     /* update which IFUs are not used */
00729     frame = kmo_dfs_get_frame(frameset, ARC_ON);
00730     mh_on = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0);
00731     kmo_set_unused_ifus(unused_ifus_after, mh_on, "kmos_wave_cal");
00732     kmo_dfs_save_main_header(frameset, LCAL, fn_suffix, frame, mh_on, parlist, 
00733             cpl_func);
00734     kmo_dfs_save_main_header(frameset, DET_IMG_WAVE, fn_suffix, frame, mh_on, 
00735             parlist, cpl_func);
00736     cpl_propertylist_delete(mh_on); 
00737 
00738     /* Save sub-frames */
00739     for (a = 0; a < nb_angles; a++) {
00740         for (i = 1; i <= ne ; i++) {
00741             int sx = a * ne + (i - 1);
00742             /* save lcal-frame */
00743             kmo_dfs_save_image(stored_lcal[sx], LCAL, fn_suffix, 
00744                     stored_sub_headers_lcal[sx], 0./0.);
00745 
00746             /* save detector image */
00747             kmo_dfs_save_image(stored_det_img[sx], DET_IMG_WAVE, fn_suffix, 
00748                     stored_sub_headers_det_img[sx], 0./0.);
00749         } // for i = ne
00750     } // for a angles
00751    
00752     /* Free */
00753     cpl_free(fn_suffix);
00754     for (i = 0; i < ne * nb_angles; i++) {
00755         cpl_image_delete(stored_lcal[i]);
00756         cpl_image_delete(stored_det_img[i]);
00757         cpl_propertylist_delete(stored_sub_headers_lcal[i]);
00758         cpl_propertylist_delete(stored_sub_headers_det_img[i]);
00759     }
00760     cpl_free(stored_lcal);
00761     cpl_free(stored_det_img);
00762     cpl_free(stored_sub_headers_lcal); 
00763     cpl_free(stored_sub_headers_det_img);
00764 
00765     /* print which IFUs are not used */
00766     kmo_print_unused_ifus(unused_ifus_after, TRUE);
00767     if (unused_ifus_after != NULL) kmo_free_unused_ifus(unused_ifus_after);
00768 
00769     if (last_env != NULL) {
00770         setenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE",last_env,1);
00771     } else {
00772         unsetenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE");
00773     }
00774     return 0;
00775 }
00776 
00779 /*----------------------------------------------------------------------------*/
00791 /*----------------------------------------------------------------------------*/
00792 static int kmos_wave_cal_check_inputs(
00793         cpl_frameset            *   frameset, 
00794         int                     *   nx, 
00795         int                     *   ny,
00796         int                     *   ne,
00797         double                  *   exptime,
00798         int                     *   non_dest_rom,
00799         lampConfiguration       *   lamp_config)
00800 {
00801     const cpl_frame     *   frame_off ;
00802     const cpl_frame     *   frame_on ;
00803     cpl_propertylist    *   mh_off ;
00804     cpl_propertylist    *   mh_on ;
00805     cpl_propertylist    *   eh_off ;
00806     cpl_propertylist    *   eh_on ;
00807     int                     ext, next_off, next_on, nx_on, ny_on, nx_off,ny_off;
00808     double                  ndit_off, ndit_on, exptime_off, exptime_on ;
00809     const char          *   readmode_off ;
00810     const char          *   readmode_on ;
00811 
00812     /* TODO Check Lamps TODO */
00813 
00814     /* Check Entries */
00815     if (nx == NULL || ny == NULL || ne == NULL || frameset == NULL) return -1;
00816 
00817     /* Setup lamp config */
00818     frame_on = kmo_dfs_get_frame(frameset, ARC_ON);
00819     mh_on = cpl_propertylist_load(cpl_frame_get_filename(frame_on), 0);
00820     if ((kmo_check_lamp(mh_on, INS_LAMP1_ST) == TRUE) &&
00821         (kmo_check_lamp(mh_on, INS_LAMP2_ST) == FALSE)) {
00822         *lamp_config = ARGON;
00823         cpl_msg_info(__func__, "Arc lamp: Argon");
00824     } else if ((kmo_check_lamp(mh_on, INS_LAMP1_ST) == FALSE) &&
00825                (kmo_check_lamp(mh_on, INS_LAMP2_ST) == TRUE)) {
00826         *lamp_config = NEON;
00827         cpl_msg_info(__func__, "Arc lamp: Neon");
00828     } else if ((kmo_check_lamp(mh_on, INS_LAMP1_ST) == TRUE) &&
00829                (kmo_check_lamp(mh_on, INS_LAMP2_ST) == TRUE)) {
00830         *lamp_config = ARGON_NEON;
00831         cpl_msg_info(__func__, "Arc lamp: Argon + Neon");
00832     } else {
00833         *lamp_config = -1 ;
00834         cpl_warning_info(__func__, "Arc lamp: UNDEFINED");
00835     }
00836 
00837     /* Check READ OUT MODE */
00838     readmode_on = kmos_pfits_get_readmode(mh_on);
00839     if (!strcmp(readmode_on, "Nondest")) {
00840         *non_dest_rom = 1 ;
00841     } else {
00842         *non_dest_rom = 0 ;
00843     }
00844     cpl_propertylist_delete(mh_on);
00845 
00846     /* Get ARC_OFF */
00847     frame_off = kmo_dfs_get_frame(frameset, ARC_OFF);
00848     if (frame_off == NULL) {
00849         cpl_msg_error(__func__, "No ARC_OFF frame found") ;
00850         return -1 ;
00851     }
00852 
00853     /* Get ARC_OFF main header infos */
00854     next_off = cpl_frame_get_nextensions(frame_off);
00855     mh_off = cpl_propertylist_load(cpl_frame_get_filename(frame_off), 0);
00856     ndit_off = kmos_pfits_get_ndit(mh_off) ;
00857     exptime_off = kmos_pfits_get_exptime(mh_off) ;
00858     readmode_off = kmos_pfits_get_readmode(mh_off);
00859 
00860     /* Get ARC_ON frames and loop on them */
00861     frame_on = kmo_dfs_get_frame(frameset, ARC_ON);
00862     if (frame_on == NULL) {
00863         cpl_msg_error(__func__, "No ARC_ON frame found") ;
00864         cpl_propertylist_delete(mh_off);
00865         return -1 ;
00866     }
00867     while (frame_on != NULL) {
00868         /* Get ARC_ON main header infos */
00869         next_on = cpl_frame_get_nextensions(frame_on);
00870         mh_on = cpl_propertylist_load(cpl_frame_get_filename(frame_on), 0);
00871         ndit_on = kmos_pfits_get_ndit(mh_on) ;
00872         exptime_on = kmos_pfits_get_exptime(mh_on) ;
00873         readmode_on = kmos_pfits_get_readmode(mh_on);
00874 
00875         /* Check consistency */
00876         if (ndit_on != ndit_off || strcmp(readmode_on, readmode_off) || 
00877                 fabs(exptime_on-exptime_off) > 0.01 || next_off != next_on) {
00878             cpl_msg_warning(__func__, "Inconsistency for frame %s", 
00879                     cpl_frame_get_filename(frame_on)) ;
00880             cpl_propertylist_delete(mh_off);
00881             cpl_propertylist_delete(mh_on);
00882             return 0 ;
00883         }
00884         cpl_propertylist_delete(mh_on);
00885 
00886         /* Get next frame */
00887         frame_on = kmo_dfs_get_frame(frameset, NULL);
00888     }
00889     cpl_propertylist_delete(mh_off);
00890 
00891     /* Check the extensions */
00892     for (ext = 1; ext <= next_off ; ext++) {
00893         eh_off = cpl_propertylist_load(cpl_frame_get_filename(frame_off), ext);
00894         nx_off = kmos_pfits_get_naxis1(eh_off) ;
00895         ny_off = kmos_pfits_get_naxis2(eh_off) ;
00896 
00897         frame_on = kmo_dfs_get_frame(frameset, ARC_ON);
00898         while (frame_on != NULL) {
00899             eh_on = cpl_propertylist_load(cpl_frame_get_filename(frame_on),ext);
00900             nx_on = kmos_pfits_get_naxis1(eh_on) ;
00901             ny_on = kmos_pfits_get_naxis2(eh_on) ;
00902             /* Check consistency */
00903             if (nx_on != nx_off || ny_off != ny_on) { 
00904                 cpl_msg_warning(__func__, "Inconsistency for frame %s", 
00905                         cpl_frame_get_filename(frame_on)) ;
00906                 cpl_propertylist_delete(eh_off);
00907                 cpl_propertylist_delete(eh_on);
00908                 return 0 ;
00909             }
00910             cpl_propertylist_delete(eh_on);
00911 
00912             /* Get next frame */
00913             frame_on = kmo_dfs_get_frame(frameset, NULL);
00914         }
00915         cpl_propertylist_delete(eh_off);
00916     }
00917  
00918     /* FLAT_EDGE Checks */
00919     frame_on = kmo_dfs_get_frame(frameset, FLAT_EDGE);
00920     if (cpl_frame_get_nextensions(frame_on) % 24 != 0) {
00921         cpl_msg_warning(__func__, "FLAT_EDGE frame is not consistent") ;
00922         return 0 ;
00923     }
00924 
00925     /* Checks on XCAL YCAL */
00926     kmo_check_frame_setup(frameset, ARC_ON, XCAL, TRUE, FALSE, FALSE);
00927     kmo_check_frame_setup(frameset, ARC_ON, YCAL, TRUE, FALSE, FALSE);
00928     kmo_check_frame_setup_md5_xycal(frameset);
00929     if (cpl_error_get_code() != CPL_ERROR_NONE) {
00930         cpl_msg_warning(__func__, "XCAL / YCAL checks failed") ;
00931         return 0 ;
00932     }
00933 
00934     /* Return */
00935     *nx = nx_off ;
00936     *ny = ny_off ;
00937     *ne = next_off ;
00938     *exptime = exptime_off ;
00939     return 1 ;
00940 }
00941 
00942