KMOS Pipeline Reference Manual  1.3.4
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 *, enum 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 "----------------------------------------------------------------------------\n"
00118 "Input files:\n"
00119 "\n"
00120 "   DO category       Type   Explanation                    Required #Frames\n"
00121 "   -----------       -----  -----------                    -------- -------\n"
00122 "   ARC_ON            RAW    Arclamp-on exposure                Y        >=1\n"
00123 "   ARC_OFF           RAW    Arclamp-off exposure               Y          1\n"
00124 "   XCAL              F2D    x calibration frame                Y          1\n"
00125 "   YCAL              F2D    y calibration frame                Y          1\n"
00126 "   ARC_LIST          F2L    List of arclines                   Y          1\n"
00127 "   FLAT_EDGE         F2L    Fitted edge parameters             Y          1\n"
00128 "   REF_LINES         F2L    Reference line table               Y          1\n"
00129 "   WAVE_BAND         F2L    Table with start-/end-wavelengths  Y          1\n"
00130 "\n"
00131 "Output files:\n"
00132 "\n"
00133 "   DO category       Type   Explanation\n"
00134 "   -----------       -----  -----------\n"
00135 "   LCAL              F2D    Wavelength calibration frame\n"
00136 "                            (3 Extensions)\n"
00137 "   DET_IMG_WAVE      F2D    reconstructed arclamp-on exposure\n"
00138 "                            (4 extensions: 3 detector images + \n"
00139 "                            the arclines list table)\n"
00140 "----------------------------------------------------------------------------\n"
00141 "\n";
00142 
00143 /*-----------------------------------------------------------------------------
00144  *                              Functions code
00145  *----------------------------------------------------------------------------*/
00146 
00153 /*----------------------------------------------------------------------------*/
00162 /*----------------------------------------------------------------------------*/
00163 int cpl_plugin_get_info(cpl_pluginlist *list)
00164 {
00165     cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe);
00166     cpl_plugin *plugin = &recipe->interface;
00167 
00168     cpl_plugin_init(plugin,
00169             CPL_PLUGIN_API,
00170             KMOS_BINARY_VERSION,
00171             CPL_PLUGIN_TYPE_RECIPE,
00172             "kmos_wave_cal",
00173             "Create a wavelength calibration frame",
00174             kmos_wave_cal_description,
00175             "Alex Agudo Berbel, Yves Jung",
00176             "usd-help@eso.org",
00177             kmos_get_license(),
00178             kmos_wave_cal_create,
00179             kmos_wave_cal_exec,
00180             kmos_wave_cal_destroy);
00181     cpl_pluginlist_append(list, plugin);
00182 
00183     return 0;
00184 }
00185 
00186 /*----------------------------------------------------------------------------*/
00194 /*----------------------------------------------------------------------------*/
00195 static int kmos_wave_cal_create(cpl_plugin *plugin)
00196 {
00197     cpl_recipe *recipe;
00198     cpl_parameter *p;
00199 
00200     // Check that the plugin is part of a valid recipe
00201     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00202         recipe = (cpl_recipe *)plugin;
00203     else
00204         return -1;
00205 
00206     // Create the parameters list in the cpl_recipe object
00207     recipe->parameters = cpl_parameterlist_new();
00208 
00209     // Fill the parameters list
00210     p = cpl_parameter_new_value("kmos.kmos_wave_cal.order", CPL_TYPE_INT,
00211             "The fitting polynomial order used for the wavelength solution. "
00212             "By default, 4 for IZ band, 5 for HK, 6 for the others",
00213             "kmos.kmos_wave_cal", 0);
00214     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "order");
00215     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00216     cpl_parameterlist_append(recipe->parameters, p);
00217 
00218     /* --suppress_extension */
00219     p = cpl_parameter_new_value("kmos.kmos_wave_cal.suppress_extension",
00220             CPL_TYPE_BOOL, "Suppress arbitrary filename extension",
00221             "kmos.kmos_wave_cal", FALSE);
00222     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "suppress_extension");
00223     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00224     cpl_parameterlist_append(recipe->parameters, p);
00225 
00226     /* Add parameters for band-definition */
00227     kmos_band_pars_create(recipe->parameters, "kmos.kmos_wave_cal");
00228 
00229     /* --detector */
00230     p = cpl_parameter_new_value("kmos.kmos_wave_cal.detector",
00231             CPL_TYPE_INT, "Only reduce the specified detector",
00232             "kmos.kmos_wave_cal", 0);
00233     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "det");
00234     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00235     cpl_parameterlist_append(recipe->parameters, p);
00236 
00237     /* --angle */
00238     p = cpl_parameter_new_value("kmos.kmos_wave_cal.angle",
00239             CPL_TYPE_DOUBLE, "Only reduce the specified angle",
00240             "kmos.kmos_wave_cal", 370.0);
00241     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "angle");
00242     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00243     cpl_parameterlist_append(recipe->parameters, p);
00244 
00245     return 0;
00246 }
00247 
00248 /*----------------------------------------------------------------------------*/
00254 /*----------------------------------------------------------------------------*/
00255 static int kmos_wave_cal_exec(cpl_plugin *plugin)
00256 {
00257     cpl_recipe  *recipe;
00258 
00259     // Get the recipe out of the plugin
00260     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00261         recipe = (cpl_recipe *)plugin;
00262     else return -1;
00263 
00264     return kmos_wave_cal(recipe->parameters, recipe->frames);
00265 }
00266 
00267 /*----------------------------------------------------------------------------*/
00273 /*----------------------------------------------------------------------------*/
00274 static int kmos_wave_cal_destroy(cpl_plugin *plugin)
00275 {
00276     cpl_recipe *recipe;
00277 
00278     // Get the recipe out of the plugin
00279     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00280         recipe = (cpl_recipe *)plugin;
00281     else return -1 ;
00282 
00283     cpl_parameterlist_delete(recipe->parameters);
00284     return 0 ;
00285 }
00286 
00287 /*----------------------------------------------------------------------------*/
00301 /*----------------------------------------------------------------------------*/
00302 static int kmos_wave_cal(cpl_parameterlist *parlist, cpl_frameset *frameset)
00303 {
00304     char                    *   str_line_estimate_method ;
00305     int                         line_estimate_method ;
00306     const cpl_parameter     *   par ;
00307     int                         suppress_extension, fit_order_par, fit_order ;
00308     int                         nx, ny, ne, reduce_det ;
00309     double                      exptime, gain, angle_found, reduce_angle ;
00310     cpl_frame               *   frame ; 
00311     cpl_propertylist        *   mh_on ;
00312     cpl_propertylist        *   plist ;
00313     char                    *   suffix ;
00314     enum lampConfiguration      lamp_config;
00315     char                    **  filter_ids ;
00316     int                     *   angles_array ;
00317     int                         nb_angles ;
00318     int                         non_dest_rom ;
00319 
00320     cpl_propertylist        **  stored_sub_headers_lcal ;
00321     cpl_propertylist        **  stored_sub_headers_det_img ;
00322     cpl_image               **  stored_lcal ;
00323     cpl_image               **  stored_det_img ;
00324     int                     *   stored_qc_arc_sat ;
00325     double                  *   stored_qc_ar_eff ;
00326     double                  *   stored_qc_ne_eff ;
00327     cpl_table               *   detector_edges[KMOS_IFUS_PER_DETECTOR] ;
00328 
00329     int                         a, i, j, x, y ;
00330 
00331     cpl_image               *   det_lamp_on ;
00332     cpl_image               *   det_lamp_off ;
00333     cpl_image               *   det_lamp_on_copy ;
00334 
00335     cpl_table               *   arclines ;
00336     cpl_table               *   reflines ;
00337     cpl_bivector            *   lines ;
00338 
00339     cpl_image               *   bad_pix_mask ;
00340     float                   *   pbad_pix_mask ;
00341     cpl_image               *   xcal ;
00342     cpl_image               *   ycal ;
00343     cpl_image               *   lcal ;
00344 
00345     int                         nr_sat ;
00346 
00347     cpl_propertylist        *   qc_header ;
00348 
00349     cpl_array               **  unused_ifus_before ;
00350     cpl_array               **  unused_ifus_after ;
00351     char                    *   extname ;
00352     char                    *   fn_suffix ;
00353     char                    *   last_env ;
00354     const char              *   tmp_str ;
00355     cpl_error_code              err ;
00356 
00357 
00358     /* Lines method TODO */
00359     str_line_estimate_method = getenv("KMO_WAVE_LINE_ESTIMATE");
00360     if (str_line_estimate_method != NULL) {
00361         line_estimate_method = atoi(str_line_estimate_method);
00362     } else {
00363         line_estimate_method = 2 ;
00364     }
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.suppress_extension");
00378     suppress_extension = cpl_parameter_get_bool(par);
00379     par = cpl_parameterlist_find_const(parlist, "kmos.kmos_wave_cal.angle");
00380     reduce_angle = cpl_parameter_get_double(par);
00381     par = cpl_parameterlist_find_const(parlist, "kmos.kmos_wave_cal.detector");
00382     reduce_det = cpl_parameter_get_int(par);
00383 
00384     kmos_band_pars_load(parlist, "kmos.kmos_wave_cal");
00385 
00386     /* Check Parameters */
00387     if (fit_order_par < 0 || fit_order_par > 7) {
00388         cpl_msg_error(__func__, "Fitting Order must be in [0,7]") ;
00389         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00390         return -1 ;
00391     }
00392     if (reduce_det < 0 || reduce_det > 3) {
00393         cpl_msg_error(__func__, "detector must be in [1,3]") ;
00394         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00395         return -1 ;
00396     }
00397 
00398     /* Identify the RAW and CALIB frames in the input frameset */
00399     if (kmo_dfs_set_groups(frameset, "kmos_wave_cal") != 1) {
00400         cpl_msg_error(__func__, "Cannot identify RAW and CALIB frames") ;
00401         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00402         return -1 ;
00403     }
00404 
00405     /* Check the inputs consistency */
00406     if (kmos_wave_cal_check_inputs(frameset, &nx, &ny, &ne, &exptime,
00407                 &non_dest_rom, &lamp_config) != 1) {
00408         cpl_msg_error(__func__, "Input frameset is not consistent") ;
00409         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00410         return -1 ;
00411     }
00412    
00413     /* Instrument setup */
00414     suffix = kmo_dfs_get_suffix(kmo_dfs_get_frame(frameset, XCAL), TRUE, FALSE);
00415     cpl_msg_info(__func__, "Detected instrument setup:   %s", suffix+1);
00416 
00417     /* Check that filter and grating match for each detector */
00418     /* filter/grating can be different for each detector */
00419     frame = kmo_dfs_get_frame(frameset, ARC_ON);
00420     mh_on = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
00421     filter_ids =  kmo_get_filter_setup(mh_on, ne, TRUE) ;
00422     cpl_propertylist_delete(mh_on);
00423     if (filter_ids == NULL) {
00424         cpl_free(suffix);
00425         cpl_msg_error(__func__, "Cannot get Filter informations") ;
00426         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00427         return -1 ;
00428     }
00429 
00430     /* Get Rotator angles */
00431     if ((angles_array = kmos_get_angles(frameset, &nb_angles, ARC_ON)) == NULL){
00432         cpl_msg_error(__func__, "Cannot get Angles informations") ;
00433         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00434         for (i = 0; i < ne ; i++) cpl_free(filter_ids[i]);
00435         cpl_free(filter_ids);
00436         cpl_free(suffix);
00437         return -1 ;
00438     }
00439 
00440     /* Check the ARC_LIST filter */
00441     frame = kmo_dfs_get_frame(frameset, ARC_LIST);
00442     plist = cpl_propertylist_load(cpl_frame_get_filename(frame), 0);
00443     tmp_str = cpl_propertylist_get_string(plist, FILT_ID);
00444     if (strcmp(filter_ids[0], tmp_str) != 0) {
00445         cpl_msg_error(__func__, "Wrong ARC_LIST filter") ;
00446         cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
00447         for (i = 0; i < ne ; i++) cpl_free(filter_ids[i]);
00448         cpl_free(filter_ids);
00449         cpl_free(angles_array);
00450         cpl_propertylist_delete(plist); 
00451         return -1 ;
00452     }
00453     cpl_propertylist_delete(plist); 
00454 
00455     /* Load the lines as a CPL table */
00456     arclines = kmo_dfs_load_table(frameset, ARC_LIST, 1, 0);
00457     lines = kmo_get_lines(arclines, lamp_config);
00458     cpl_table_delete(arclines);
00459     /* TODO : check not null */
00460     cpl_msg_info(__func__, "Arc lines: %lld", cpl_bivector_get_size(lines));
00461 
00462     /* Load REFLINES */
00463     if (line_estimate_method == 2) {
00464         reflines = kmo_dfs_load_table(frameset, REF_LINES, 1, 0);
00465         /* TODO : check not null */
00466     }
00467 
00468     /* Check which IFUs are active for all FLAT frames */
00469     unused_ifus_before = kmo_get_unused_ifus(frameset, 0, 0);
00470     unused_ifus_after = kmo_duplicate_unused_ifus(unused_ifus_before);
00471     kmo_print_unused_ifus(unused_ifus_before, FALSE);
00472     if (unused_ifus_before != NULL) kmo_free_unused_ifus(unused_ifus_before);
00473 
00474     /* make sure no reconstruction lookup table (LUT) is used */
00475     if (getenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE") != NULL) {
00476         last_env = getenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE");
00477     }
00478     setenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE","NONE",1);
00479 
00480     /* the frames have to be stored temporarily because the QC params */
00481     /* for the main header are calculated per detector. So they can be */
00482     /* stored only when all detectors are processed */
00483     stored_lcal = (cpl_image**)cpl_calloc(ne * nb_angles, sizeof(cpl_image*));
00484     stored_det_img = (cpl_image**)cpl_calloc(ne * nb_angles,sizeof(cpl_image*));
00485     stored_sub_headers_lcal = (cpl_propertylist**)cpl_calloc(ne * nb_angles,
00486             sizeof(cpl_propertylist*));
00487     stored_sub_headers_det_img = (cpl_propertylist**)cpl_calloc(ne * nb_angles,
00488             sizeof(cpl_propertylist*));
00489     stored_qc_arc_sat = (int*)cpl_calloc(ne, nb_angles * sizeof(int));
00490     stored_qc_ar_eff=(double*)cpl_calloc(ne, nb_angles * sizeof(double));
00491     stored_qc_ne_eff=(double*)cpl_calloc(ne, nb_angles * sizeof(double));
00492 
00493     /* Loop all Rotator Angles and Detectors  */
00494     for (a = 0; a < nb_angles; a++) {
00495         /* Reduce only one angle */
00496         if (reduce_angle <= 360 && angles_array[a] != reduce_angle) continue ;
00497 
00498         cpl_msg_info(__func__, "Processing rotator angle %d -> %d degree", 
00499                 a, angles_array[a]);
00500         cpl_msg_indent_more();
00501         for (i = 1; i <= ne ; i++) {
00502             /* Compute only one detector */
00503             if (reduce_det != 0 && i != reduce_det) continue ;
00504 
00505             cpl_msg_info(__func__,"Processing detector No. %d", i);
00506             cpl_msg_indent_more();
00507 
00508             /* Load edge parameters */
00509             frame=kmo_dfs_get_frame(frameset, FLAT_EDGE);
00510             for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
00511                 detector_edges[j] = kmclipm_cal_table_load(
00512                         cpl_frame_get_filename(frame), 
00513                         (i-1) * KMOS_IFUS_PER_DETECTOR + j + 1,
00514                         angles_array[a], 0, &angle_found);
00515                 
00516                 /* IFU is inactive: proceed */
00517                 if (cpl_error_get_code() == CPL_ERROR_ILLEGAL_INPUT) {
00518                     cpl_error_reset();
00519                 }
00520             }
00521 
00522             /* Set default fit orders for the different bands */
00523             if (fit_order_par == 0) {
00524                 if ((strcmp(filter_ids[i-1], "H") == 0) ||
00525                     (strcmp(filter_ids[i-1], "K") == 0) ||
00526                     (strcmp(filter_ids[i-1], "YJ") == 0)) {
00527                     fit_order = 6;
00528                 } else if (strcmp(filter_ids[i-1], "IZ") == 0) {
00529                     fit_order = 4;
00530                 } else if (strcmp(filter_ids[i-1], "HK") == 0) {
00531                     fit_order = 5;
00532                 }
00533                 cpl_msg_info(__func__, 
00534                         "Order of wavelength spectrum fit for %s-band: %d",
00535                         filter_ids[i-1], fit_order);
00536             } else {
00537                 fit_order = fit_order_par;
00538             }
00539 
00540             /* Get ARC_ON frame and Load it */
00541             frame = kmos_get_angle_frame(frameset, angles_array[a], ARC_ON);
00542             det_lamp_on = kmo_dfs_load_image_frame(frame,i,FALSE, TRUE,&nr_sat);
00543             int sx = a * ne + (i - 1);
00544 
00545             /* Count saturated pixels for each detector */
00546             if (non_dest_rom)   
00547                 stored_qc_arc_sat[sx] = nr_sat;
00548             else 
00549                 stored_qc_arc_sat[sx] = kmo_image_get_saturated(det_lamp_on,
00550                         KMO_FLAT_SATURATED);
00551 
00552             det_lamp_on_copy = cpl_image_duplicate(det_lamp_on);
00553 
00554             /* Get ARC_OFF frame and Load it */
00555             frame = kmo_dfs_get_frame(frameset, ARC_OFF);
00556             det_lamp_off = kmo_dfs_load_image_frame(frame, i, FALSE, FALSE, 
00557                     NULL);
00558 
00559             /* ARC_ON = ARC_ON - ARC_OFF */
00560             cpl_image_subtract(det_lamp_on, det_lamp_off);
00561 
00562             /* Load XCAL,YCAL */
00563             xcal = kmo_dfs_load_cal_image(frameset, XCAL, i, 0, 
00564                     (double)angles_array[a], FALSE, NULL, &angle_found, -1,0,0);
00565             ycal = kmo_dfs_load_cal_image(frameset, YCAL, i, 0, 
00566                     (double)angles_array[a], FALSE, NULL, &angle_found, -1,0,0);
00567 
00568             /* Derive BPM from XCAL : NaNs to 0, Others to 1  */
00569             bad_pix_mask = cpl_image_duplicate(xcal);
00570             pbad_pix_mask = cpl_image_get_data_float(bad_pix_mask);
00571             for (x = 0; x < nx; x++) {
00572                 for (y = 0; y < ny; y++) {
00573                     if (isnan(pbad_pix_mask[x+nx*y])) {
00574                         pbad_pix_mask[x+nx*y] = 0.;
00575                     } else {
00576                         pbad_pix_mask[x+nx*y] = 1.;
00577                     }
00578                 }
00579             }
00580 
00581             /* Compute wavelength calibration */
00582             err = kmos_calc_wave_calib(det_lamp_on, bad_pix_mask, xcal, ycal, 
00583                     filter_ids[i-1], lamp_config, i, unused_ifus_after[i-1], 
00584                     detector_edges, lines, reflines, -1.0, &lcal, 
00585                     &(stored_qc_ar_eff[sx]), &(stored_qc_ne_eff[sx]), fit_order,
00586                     line_estimate_method);
00587             cpl_image_delete(det_lamp_on); 
00588             for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
00589                 cpl_table_delete(detector_edges[j]); 
00590             }
00591             if (err == CPL_ERROR_NONE) {
00592                 /* Update QC parameters */
00593                 if (stored_qc_ar_eff[sx] != -1.0) 
00594                     stored_qc_ar_eff[sx] /= exptime;
00595                 if (stored_qc_ne_eff[sx] != -1.0) 
00596                     stored_qc_ne_eff[sx] /= exptime;
00597 
00598                 /* Apply the badpixel mask to the produced frame */
00599                 cpl_image_multiply(lcal, bad_pix_mask);
00600                 kmo_image_reject_from_mask(lcal, bad_pix_mask);
00601 
00602                 /* Store Result frame */
00603                 stored_lcal[sx] = lcal;
00604             } else if (err == CPL_ERROR_UNSPECIFIED) {
00605                 /* All IFUs seem to be deactivated */
00606                 /* Continue processing - just save empty frame */
00607                 cpl_error_reset();
00608                 stored_lcal[sx] = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
00609                 kmo_image_fill(stored_lcal[sx], 0.0);
00610             } else {
00611                 cpl_error_reset();
00612                 cpl_msg_warning(__func__,
00613                         "Couldn't identify any line - Check the line list");
00614                 cpl_msg_warning(__func__,
00615                         "Band defined in header of detector %d: %s",
00616                         i, filter_ids[i-1]);
00617                 cpl_msg_warning(__func__, "Arc line file defined: %s",
00618                         cpl_frame_get_filename(kmo_dfs_get_frame(frameset, 
00619                                 ARC_LIST)));
00620             }
00621             cpl_image_delete(bad_pix_mask);
00622 
00623             /* CREATE RECONSTRUCTED AND RESAMPLED ARC FRAME */
00624             stored_det_img[sx] = kmo_reconstructed_arc_image(frameset,
00625                     det_lamp_on_copy, det_lamp_off, xcal, ycal, stored_lcal[sx],
00626                     unused_ifus_after[i-1], FALSE, i, suffix, filter_ids[i-1], 
00627                     lamp_config, &qc_header);
00628             cpl_image_delete(det_lamp_on_copy);
00629             cpl_image_delete(det_lamp_off); 
00630             cpl_image_delete(xcal);
00631             cpl_image_delete(ycal);
00632             if (cpl_error_get_code() != CPL_ERROR_NONE) {
00633                 cpl_msg_error(__func__,"Cannot reconstruct IFUs on detector %d",
00634                         i);
00635                 cpl_error_reset();
00636             }
00637 
00638             /* CREATE EXTENSION HEADER FOR THE PRODUCT */
00639             stored_sub_headers_lcal[sx] = kmo_dfs_load_sub_header(frameset, 
00640                     ARC_ON, i, FALSE);
00641             /* update EXTNAME */
00642             extname = kmo_extname_creator(detector_frame, i, EXT_DATA);
00643             kmclipm_update_property_string(stored_sub_headers_lcal[sx], EXTNAME,
00644                     extname, "FITS extension name");
00645             cpl_free(extname); 
00646 
00647             kmclipm_update_property_int(stored_sub_headers_lcal[sx], EXTVER, 
00648                     sx+1, "FITS extension ver");
00649 
00650             // add first QC parameters
00651             kmclipm_update_property_int(stored_sub_headers_lcal[sx], 
00652                     QC_ARC_SAT, stored_qc_arc_sat[sx], 
00653                     "[] nr. saturated pixels of arc exp.");
00654 
00655             gain=kmo_dfs_get_property_double(stored_sub_headers_lcal[sx],GAIN);
00656 
00657             if (stored_qc_ar_eff[sx] != -1.0) {
00658                 kmclipm_update_property_double(stored_sub_headers_lcal[sx], 
00659                         QC_ARC_AR_EFF, stored_qc_ar_eff[sx]/gain,
00660                         "[e-/s] Argon lamp efficiency");
00661             }
00662 
00663             if (stored_qc_ne_eff[sx] != -1.0) {
00664                 kmclipm_update_property_double(stored_sub_headers_lcal[sx], 
00665                         QC_ARC_NE_EFF, stored_qc_ne_eff[sx]/gain,
00666                         "[e-/s] Neon lamp efficiency");
00667             }
00668 
00669             kmclipm_update_property_double(stored_sub_headers_lcal[sx],
00670                     CAL_ROTANGLE, ((double) angles_array[a]),
00671                     "[deg] Rotator relative to nasmyth");
00672 
00673             /* append QC parameters */
00674             cpl_propertylist_append(stored_sub_headers_lcal[sx], qc_header);
00675             cpl_propertylist_delete(qc_header); 
00676 
00677             stored_sub_headers_det_img[sx]=cpl_propertylist_duplicate(
00678                     stored_sub_headers_lcal[sx]);
00679 
00680             cpl_propertylist_erase(stored_sub_headers_lcal[sx], CRVAL1);
00681             cpl_propertylist_erase(stored_sub_headers_lcal[sx], CRVAL2);
00682             cpl_propertylist_erase(stored_sub_headers_lcal[sx], CTYPE1);
00683             cpl_propertylist_erase(stored_sub_headers_lcal[sx], CTYPE2);
00684             cpl_propertylist_erase(stored_sub_headers_lcal[sx], CDELT1);
00685             cpl_propertylist_erase(stored_sub_headers_lcal[sx], CDELT2);
00686 
00687             cpl_msg_indent_less();
00688         } // for i devices
00689         cpl_msg_indent_less() ;
00690     } // for a angles
00691     
00692     /* Free */
00693     cpl_free(angles_array) ;
00694     for (i = 0; i < ne; i++) cpl_free(filter_ids[i]);
00695     cpl_free(filter_ids);
00696     cpl_bivector_delete(lines);
00697     
00698     cpl_free(stored_qc_arc_sat);
00699     cpl_free(stored_qc_ar_eff);
00700     cpl_free(stored_qc_ne_eff);
00701     if (line_estimate_method == 2) cpl_table_delete(reflines); 
00702     
00703     /* QC parameters & saving */
00704     cpl_msg_info(__func__, "Saving data...");
00705 
00706     /* load, update & save primary header */
00707     if (!suppress_extension)    fn_suffix = cpl_sprintf("%s", suffix);
00708     else                        fn_suffix = cpl_sprintf("%s", "");
00709     cpl_free(suffix);
00710 
00711     /* update which IFUs are not used */
00712     frame = kmo_dfs_get_frame(frameset, ARC_ON);
00713     mh_on = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0);
00714     kmo_set_unused_ifus(unused_ifus_after, mh_on, "kmos_wave_cal");
00715     kmo_dfs_save_main_header(frameset, LCAL, fn_suffix, frame, mh_on, parlist, 
00716             cpl_func);
00717     kmo_dfs_save_main_header(frameset, DET_IMG_WAVE, fn_suffix, frame, mh_on, 
00718             parlist, cpl_func);
00719     cpl_propertylist_delete(mh_on); 
00720 
00721     /* Save sub-frames */
00722     for (a = 0; a < nb_angles; a++) {
00723         for (i = 1; i <= ne ; i++) {
00724             int sx = a * ne + (i - 1);
00725             /* save lcal-frame */
00726             kmo_dfs_save_image(stored_lcal[sx], LCAL, fn_suffix, 
00727                     stored_sub_headers_lcal[sx], 0./0.);
00728 
00729             /* save detector image */
00730             kmo_dfs_save_image(stored_det_img[sx], DET_IMG_WAVE, fn_suffix, 
00731                     stored_sub_headers_det_img[sx], 0./0.);
00732         } // for i = ne
00733     } // for a angles
00734    
00735     /* Free */
00736     cpl_free(fn_suffix);
00737     for (i = 0; i < ne * nb_angles; i++) {
00738         cpl_image_delete(stored_lcal[i]);
00739         cpl_image_delete(stored_det_img[i]);
00740         cpl_propertylist_delete(stored_sub_headers_lcal[i]);
00741         cpl_propertylist_delete(stored_sub_headers_det_img[i]);
00742     }
00743     cpl_free(stored_lcal);
00744     cpl_free(stored_det_img);
00745     cpl_free(stored_sub_headers_lcal); 
00746     cpl_free(stored_sub_headers_det_img);
00747 
00748     /* print which IFUs are not used */
00749     kmo_print_unused_ifus(unused_ifus_after, TRUE);
00750     if (unused_ifus_after != NULL) kmo_free_unused_ifus(unused_ifus_after);
00751 
00752     if (last_env != NULL) {
00753         setenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE",last_env,1);
00754     } else {
00755         unsetenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE");
00756     }
00757     return 0;
00758 }
00759 
00762 /*----------------------------------------------------------------------------*/
00774 /*----------------------------------------------------------------------------*/
00775 static int kmos_wave_cal_check_inputs(
00776         cpl_frameset            *   frameset, 
00777         int                     *   nx, 
00778         int                     *   ny,
00779         int                     *   ne,
00780         double                  *   exptime,
00781         int                     *   non_dest_rom,
00782         enum lampConfiguration  *   lamp_config)
00783 {
00784     const cpl_frame     *   frame_off ;
00785     const cpl_frame     *   frame_on ;
00786     cpl_propertylist    *   mh_off ;
00787     cpl_propertylist    *   mh_on ;
00788     cpl_propertylist    *   eh_off ;
00789     cpl_propertylist    *   eh_on ;
00790     int                     ext, next_off, next_on, nx_on, ny_on, nx_off,ny_off;
00791     double                  ndit_off, ndit_on, exptime_off, exptime_on ;
00792     const char          *   readmode_off ;
00793     const char          *   readmode_on ;
00794 
00795     /* TODO Check Lamps TODO */
00796 
00797     /* Check Entries */
00798     if (nx == NULL || ny == NULL || ne == NULL || frameset == NULL) return -1;
00799 
00800     /* Setup lamp config */
00801     frame_on = kmo_dfs_get_frame(frameset, ARC_ON);
00802     mh_on = cpl_propertylist_load(cpl_frame_get_filename(frame_on), 0);
00803     if ((kmo_check_lamp(mh_on, INS_LAMP1_ST) == TRUE) &&
00804         (kmo_check_lamp(mh_on, INS_LAMP2_ST) == FALSE)) {
00805         *lamp_config = ARGON;
00806         cpl_msg_info(__func__, "Arc lamp: Argon");
00807     } else if ((kmo_check_lamp(mh_on, INS_LAMP1_ST) == FALSE) &&
00808                (kmo_check_lamp(mh_on, INS_LAMP2_ST) == TRUE)) {
00809         *lamp_config = NEON;
00810         cpl_msg_info(__func__, "Arc lamp: Neon");
00811     } else if ((kmo_check_lamp(mh_on, INS_LAMP1_ST) == TRUE) &&
00812                (kmo_check_lamp(mh_on, INS_LAMP2_ST) == TRUE)) {
00813         *lamp_config = ARGON_NEON;
00814         cpl_msg_info(__func__, "Arc lamp: Argon + Neon");
00815     } else {
00816         *lamp_config = -1 ;
00817         cpl_warning_info(__func__, "Arc lamp: UNDEFINED");
00818     }
00819 
00820     /* Check READ OUT MODE */
00821     readmode_on = kmos_pfits_get_readmode(mh_on);
00822     if (!strcmp(readmode_on, "Nondest")) {
00823         *non_dest_rom = 1 ;
00824     } else {
00825         *non_dest_rom = 0 ;
00826     }
00827     cpl_propertylist_delete(mh_on);
00828 
00829     /* Get ARC_OFF */
00830     frame_off = kmo_dfs_get_frame(frameset, ARC_OFF);
00831     if (frame_off == NULL) {
00832         cpl_msg_error(__func__, "No ARC_OFF frame found") ;
00833         return -1 ;
00834     }
00835 
00836     /* Get ARC_OFF main header infos */
00837     next_off = cpl_frame_get_nextensions(frame_off);
00838     mh_off = cpl_propertylist_load(cpl_frame_get_filename(frame_off), 0);
00839     ndit_off = kmos_pfits_get_ndit(mh_off) ;
00840     exptime_off = kmos_pfits_get_exptime(mh_off) ;
00841     readmode_off = kmos_pfits_get_readmode(mh_off);
00842 
00843     /* Get ARC_ON frames and loop on them */
00844     frame_on = kmo_dfs_get_frame(frameset, ARC_ON);
00845     if (frame_on == NULL) {
00846         cpl_msg_error(__func__, "No ARC_ON frame found") ;
00847         cpl_propertylist_delete(mh_off);
00848         return -1 ;
00849     }
00850     while (frame_on != NULL) {
00851         /* Get ARC_ON main header infos */
00852         next_on = cpl_frame_get_nextensions(frame_on);
00853         mh_on = cpl_propertylist_load(cpl_frame_get_filename(frame_on), 0);
00854         ndit_on = kmos_pfits_get_ndit(mh_on) ;
00855         exptime_on = kmos_pfits_get_exptime(mh_on) ;
00856         readmode_on = kmos_pfits_get_readmode(mh_on);
00857 
00858         /* Check consistency */
00859         if (ndit_on != ndit_off || strcmp(readmode_on, readmode_off) || 
00860                 fabs(exptime_on-exptime_off) > 0.01 || next_off != next_on) {
00861             cpl_msg_warning(__func__, "Inconsistency for frame %s", 
00862                     cpl_frame_get_filename(frame_on)) ;
00863             cpl_propertylist_delete(mh_off);
00864             cpl_propertylist_delete(mh_on);
00865             return 0 ;
00866         }
00867         cpl_propertylist_delete(mh_on);
00868 
00869         /* Get next frame */
00870         frame_on = kmo_dfs_get_frame(frameset, NULL);
00871     }
00872     cpl_propertylist_delete(mh_off);
00873 
00874     /* Check the extensions */
00875     for (ext = 1; ext <= next_off ; ext++) {
00876         eh_off = cpl_propertylist_load(cpl_frame_get_filename(frame_off), ext);
00877         nx_off = kmos_pfits_get_naxis1(eh_off) ;
00878         ny_off = kmos_pfits_get_naxis2(eh_off) ;
00879 
00880         frame_on = kmo_dfs_get_frame(frameset, ARC_ON);
00881         while (frame_on != NULL) {
00882             eh_on = cpl_propertylist_load(cpl_frame_get_filename(frame_on),ext);
00883             nx_on = kmos_pfits_get_naxis1(eh_on) ;
00884             ny_on = kmos_pfits_get_naxis2(eh_on) ;
00885             /* Check consistency */
00886             if (nx_on != nx_off || ny_off != ny_on) { 
00887                 cpl_msg_warning(__func__, "Inconsistency for frame %s", 
00888                         cpl_frame_get_filename(frame_on)) ;
00889                 cpl_propertylist_delete(eh_off);
00890                 cpl_propertylist_delete(eh_on);
00891                 return 0 ;
00892             }
00893             cpl_propertylist_delete(eh_on);
00894 
00895             /* Get next frame */
00896             frame_on = kmo_dfs_get_frame(frameset, NULL);
00897         }
00898         cpl_propertylist_delete(eh_off);
00899     }
00900  
00901     /* FLAT_EDGE Checks */
00902     frame_on = kmo_dfs_get_frame(frameset, FLAT_EDGE);
00903     if (cpl_frame_get_nextensions(frame_on) % 24 != 0) {
00904         cpl_msg_warning(__func__, "FLAT_EDGE frame is not consistent") ;
00905         return 0 ;
00906     }
00907 
00908     /* Checks on XCAL YCAL */
00909     kmo_check_frame_setup(frameset, ARC_ON, XCAL, TRUE, FALSE, FALSE);
00910     kmo_check_frame_setup(frameset, ARC_ON, YCAL, TRUE, FALSE, FALSE);
00911     kmo_check_frame_setup_md5_xycal(frameset);
00912     if (cpl_error_get_code() != CPL_ERROR_NONE) {
00913         cpl_msg_warning(__func__, "XCAL / YCAL checks failed") ;
00914         return 0 ;
00915     }
00916 
00917     /* Return */
00918     *nx = nx_off ;
00919     *ny = ny_off ;
00920     *ne = next_off ;
00921     *exptime = exptime_off ;
00922     return 1 ;
00923 }
00924 
00925