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