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