KMOS Pipeline Reference Manual  1.2.6
kmo_wave_cal.c
00001 /* $Id: kmo_wave_cal.c,v 1.54 2013-09-17 08:54:03 aagudo Exp $
00002  *
00003  * This file is part of the KMOS Pipeline
00004  * Copyright (C) 2002,2003 European Southern Observatory
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  */
00020 
00021 /*
00022  * $Author: aagudo $
00023  * $Date: 2013-09-17 08:54:03 $
00024  * $Revision: 1.54 $
00025  * $Name: not supported by cvs2svn $
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 
00032 /*-----------------------------------------------------------------------------
00033  *                              Includes
00034  *----------------------------------------------------------------------------*/
00035 #include <string.h>
00036 #include <math.h>
00037 
00038 #ifdef __USE_XOPEN2K
00039 #include <stdlib.h>
00040 #define GGG
00041 #else
00042 #define __USE_XOPEN2K /* to get the definition for setenv in stdlib.h */
00043 #include <stdlib.h>
00044 #undef __USE_XOPEN2K
00045 #endif
00046 
00047 #include <cpl.h>
00048 
00049 #include "kmo_utils.h"
00050 #include "kmo_functions.h"
00051 #include "kmo_priv_wave_cal.h"
00052 #include "kmo_priv_functions.h"
00053 #include "kmo_cpl_extensions.h"
00054 #include "kmo_dfs.h"
00055 #include "kmo_error.h"
00056 #include "kmo_constants.h"
00057 #include "kmo_debug.h"
00058 
00059 /*-----------------------------------------------------------------------------
00060  *                          Functions prototypes
00061  *----------------------------------------------------------------------------*/
00062 
00063 static int kmo_wave_cal_create(cpl_plugin *);
00064 static int kmo_wave_cal_exec(cpl_plugin *);
00065 static int kmo_wave_cal_destroy(cpl_plugin *);
00066 static int kmo_wave_cal(cpl_parameterlist *, cpl_frameset *);
00067 
00068 /*-----------------------------------------------------------------------------
00069  *                          Static variables
00070  *----------------------------------------------------------------------------*/
00071 
00072 static char kmo_wave_cal_description[] =
00073 "This recipe creates the wavelength calibration frame needed for all three\n"
00074 "detectors. It must be called after the kmo_flat recipe, which generates the\n"
00075 "two spatial calibration frames needed in this recipe. As input a lamp-on frame,\n"
00076 "a lamp-off frame, the spatial calibration frames and the list with the\n"
00077 "reference arclines are required."
00078 "An additional output frame is the resampled image of the reconstructed arc\n"
00079 "frame. All slitlets of all IFUs are aligned one next to the other. This frame\n"
00080 "serves for quality control. One can immediately see if the calibration was\n"
00081 "successful.\n"
00082 "The lists of reference arclines are supposed to contain the lines for both\n"
00083 "available calibration arc-lamps, i.e. Argon and Neon. The list is supposed to\n"
00084 "be a F2L KMOS FITS file with three columns:\n"
00085 "1. Reference wavelength\n"
00086 "2. Relative strength\n"
00087 "3. String either containing “Ar” or “Ne”\n"
00088 "The recipe extracts, based on the header keywords, either the applying argon\n"
00089 "and/or neon emission lines. Below are the plots of the emission lines for both\n"
00090 "argon and neon. The marked lines are the ones used for wavelength calibration.\n"
00091 "\n"
00092 "Furthermore frames can be provided for several rotator angles. In this case\n"
00093 "the resulting calibration frames for each detector are repeatedly saved as \n"
00094 "extension for every angle.\n"
00095 "\n"
00096 "BASIC PARAMETERS:\n"
00097 "-----------------\n"
00098 "--order\n"
00099 "The polynomial order to use for the fit of the wavelength solution.\n"
00100 "0: (default) The appropriate order is choosen automatically depending on the\n"
00101 "waveband. Otherwise an order of 6 is recommended, except for IZ-band, there\n"
00102 "order 4 should be used.\n"
00103 "\n"
00104 "ADVANCED PARAMETERS\n"
00105 "-------------------\n"
00106 "--b_samples\n"
00107 "The number of samples in spectral direction for the reconstructed cube.\n"
00108 "Ideally this number should be greater than 2048, the detector size.\n"
00109 "\n"
00110 "--b_start\n"
00111 "--b_end\n"
00112 "Used to define manually the start and end wavelength for the reconstructed\n"
00113 "cube. By default the internally defined values are used.\n"
00114 "\n"
00115 "--suppress_extension\n"
00116 "If set to TRUE, the arbitrary filename extensions are supressed. If multiple\n"
00117 "products with the same category are produced, they will be numered consecutively\n"
00118 "starting from 0.\n"
00119 "\n"
00120 "-------------------------------------------------------------------------------\n"
00121 "  Input files:\n"
00122 "\n"
00123 "   DO                    KMOS                                                  \n"
00124 "   category              Type   Explanation                    Required #Frames\n"
00125 "   --------              -----  -----------                    -------- -------\n"
00126 "   ARC_ON                RAW    Arclamp-on exposure                Y        >=1\n"
00127 "   ARC_OFF               RAW    Arclamp-off exposure               Y          1\n"
00128 "   XCAL                  F2D    x calibration frame                Y          1\n"
00129 "   YCAL                  F2D    y calibration frame                Y          1\n"
00130 "   ARC_LIST              F2L    List of arclines                   Y          1\n"
00131 "   FLAT_EDGE             F2L    Fitted edge parameters             Y          1\n"
00132 "   REF_LINES             F2L    Reference line table               Y          1\n"
00133 "   WAVE_BAND             F2L    Table with start-/end-wavelengths  Y          1\n"
00134 "\n"
00135 "  Output files:\n"
00136 "\n"
00137 "   DO                    KMOS\n"
00138 "   category              Type   Explanation\n"
00139 "   --------              -----  -----------\n"
00140 "   LCAL                  F2D    Wavelength calibration frame\n"
00141 "                                (3 Extensions)\n"
00142 "   DET_IMG_WAVE          F2D    reconstructed arclamp-on exposure\n"
00143 "                                (4 extensions: 3 detector images + \n"
00144 "                                the arclines list table)\n"
00145 "-------------------------------------------------------------------------------\n"
00146 "\n";
00147 
00148 /*-----------------------------------------------------------------------------
00149  *                              Functions code
00150  *----------------------------------------------------------------------------*/
00151 
00168 int cpl_plugin_get_info(cpl_pluginlist *list)
00169 {
00170     cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe);
00171     cpl_plugin *plugin = &recipe->interface;
00172 
00173     cpl_plugin_init(plugin,
00174                         CPL_PLUGIN_API,
00175                         KMOS_BINARY_VERSION,
00176                         CPL_PLUGIN_TYPE_RECIPE,
00177                         "kmo_wave_cal",
00178                         "Create a calibration frame encoding the spectral "
00179                         "position (i.e. wavelength) of each pixel on the "
00180                         "detector.",
00181                         kmo_wave_cal_description,
00182                         "Alex Agudo Berbel",
00183                         "kmos-spark@mpe.mpg.de",
00184                         kmos_get_license(),
00185                         kmo_wave_cal_create,
00186                         kmo_wave_cal_exec,
00187                         kmo_wave_cal_destroy);
00188 
00189     cpl_pluginlist_append(list, plugin);
00190 
00191     return 0;
00192 }
00193 
00201 static int kmo_wave_cal_create(cpl_plugin *plugin)
00202 {
00203     cpl_recipe *recipe;
00204     cpl_parameter *p;
00205 
00206     // Check that the plugin is part of a valid recipe
00207     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00208         recipe = (cpl_recipe *)plugin;
00209     else
00210         return -1;
00211 
00212     // Create the parameters list in the cpl_recipe object
00213     recipe->parameters = cpl_parameterlist_new();
00214 
00215     // Fill the parameters list
00216     p = cpl_parameter_new_value("kmos.kmo_wave_cal.order",
00217                                 CPL_TYPE_INT,
00218                                 "The polynomial order to use for the fit of "
00219                                 "the wavelength solution. 0: (default) The "
00220                                 "appropriate order is choosen automatically "
00221                                 "depending on the waveband. Otherwise an order "
00222                                 "of 6 is recommended, except for IZ-band, there "
00223                                 "order 4 should be used",
00224                                 "kmos.kmo_wave_cal",
00225                                 0);
00226     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "order");
00227     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00228     cpl_parameterlist_append(recipe->parameters, p);
00229 
00230     p = cpl_parameter_new_value("kmos.kmo_wave_cal.dev_flip",
00231                                 CPL_TYPE_BOOL,
00232                                 "Set this parameter to TRUE if the wavelengths"
00233                                 " are ascending on the detector from top to "
00234                                 "bottom.",
00235                                 "kmos.kmo_wave_cal",
00236                                 FALSE);
00237     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dev_flip");
00238     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00239     cpl_parameterlist_append(recipe->parameters, p);
00240 
00241     p = cpl_parameter_new_value("kmos.kmo_wave_cal.dev_disp",
00242                                 CPL_TYPE_DOUBLE,
00243                                 "The expected dispersion of the wavelength "
00244                                 "in microns/pixel. If the default isn't "
00245                                 "changed it will automatically be selected "
00246                                 "upon header keywords.",
00247                                 "kmos.kmo_wave_cal",
00248                                 -1.0);
00249     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dev_disp");
00250     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00251     cpl_parameterlist_append(recipe->parameters, p);
00252 
00253     /* --suppress_extension */
00254     p = cpl_parameter_new_value("kmos.kmo_wave_cal.suppress_extension",
00255                                 CPL_TYPE_BOOL,
00256                                 "Suppress arbitrary filename extension."
00257                                 "(TRUE (apply) or FALSE (don't apply)",
00258                                 "kmos.kmo_wave_cal",
00259                                 FALSE);
00260     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "suppress_extension");
00261     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00262     cpl_parameterlist_append(recipe->parameters, p);
00263 
00264     // add parameters for band-definition
00265     kmo_band_pars_create(recipe->parameters,
00266                          "kmos.kmo_wave_cal");
00267 
00268     return 0;
00269 }
00270 
00276 static int kmo_wave_cal_exec(cpl_plugin *plugin)
00277 {
00278     cpl_recipe  *recipe;
00279 
00280     // Get the recipe out of the plugin
00281     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00282         recipe = (cpl_recipe *)plugin;
00283     else return -1;
00284 
00285     return kmo_wave_cal(recipe->parameters, recipe->frames);
00286 }
00287 
00293 static int kmo_wave_cal_destroy(cpl_plugin *plugin)
00294 {
00295     cpl_recipe *recipe;
00296 
00297     // Get the recipe out of the plugin
00298     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00299         recipe = (cpl_recipe *)plugin;
00300     else return -1 ;
00301 
00302     cpl_parameterlist_delete(recipe->parameters);
00303     return 0 ;
00304 }
00305 
00320 static int kmo_wave_cal(cpl_parameterlist *parlist, cpl_frameset *frameset)
00321 {
00322     cpl_image        *det_lamp_on                   = NULL,
00323                      *det_lamp_on_copy              = NULL,
00324                      *det_lamp_off                  = NULL,
00325                      *bad_pix_mask                  = NULL,
00326                      *xcal                          = NULL,
00327                      *ycal                          = NULL,
00328                      *lcal                          = NULL;
00329 
00330     cpl_image        **stored_lcal                  = NULL,
00331                      **stored_det_img               = NULL;
00332 
00333     cpl_frame        *frame                         = NULL;
00334     cpl_frameset     ** angle_frameset     = NULL;
00335 
00336     int              ret_val                        = 0,
00337                      nr_devices                     = 0,
00338                      nr_angles                      = 0,
00339                      nx                             = 0,
00340                      ny                             = 0,
00341                      nz                             = 0,
00342                      *stored_qc_arc_sat             = NULL,
00343                      flip_trace                     = FALSE,
00344                      fit_order_par                  = 0,
00345                      fit_order                      = 0,
00346                      ndit                           = 0,
00347                      suppress_extension             = FALSE,
00348                      line_estimate_method           = 2,
00349                      nr_sat                         = 0,
00350                      i = 0, j = 0, a = 0, ax = 0, x = 0, y = 0;
00351     float            *pbad_pix_mask                 = NULL;
00352     double           exptime                        = 0.0,
00353                      gain                           = 0.0,
00354                      *stored_qc_ar_eff              = NULL,
00355                      *stored_qc_ne_eff              = NULL,
00356                      disp                           = 0.0,
00357                      angle_found                    = 0.0;
00358 
00359     cpl_table        *arclines                      = NULL,
00360                      *reflines                      = NULL,
00361                      ***edge_table                  = NULL;
00362 
00363     cpl_propertylist *main_header                   = NULL,
00364                      **stored_sub_headers_lcal      = NULL,
00365                      **stored_sub_headers_det_img   = NULL,
00366                      *qc_header                     = NULL,
00367                      *tmp_header                    = NULL,
00368                      *header                        = NULL;
00369 
00370     cpl_array        **unused_ifus_before           = NULL,
00371                      **unused_ifus_after            = NULL;
00372 
00373     cpl_bivector     *lines                         = NULL;
00374 
00375     main_fits_desc   desc1,
00376                      desc2;
00377 
00378     char             *extname                       = NULL,
00379                      filename_lcal[256],
00380                      filename_det_img[256],
00381                      *suffix                        = NULL,
00382                      *fn_suffix                     = NULL,
00383                      tmpstr[256],
00384                      **filter_ids                   = NULL,
00385                      *readmode                      = NULL,
00386                      *str_line_estimate_method      = NULL,
00387                      *last_env                      = NULL;
00388 
00389     const char       *tmp_str                       = NULL;
00390 
00391     cpl_error_code   err                            = CPL_ERROR_NONE;
00392 
00393     enum lampConfiguration lamp_config;
00394 
00395     KMO_TRY
00396     {
00397         kmo_init_fits_desc(&desc1);
00398         kmo_init_fits_desc(&desc2);
00399 
00400         str_line_estimate_method = getenv("KMO_WAVE_LINE_ESTIMATE");
00401         if (str_line_estimate_method != NULL) {
00402             line_estimate_method = atoi(str_line_estimate_method);
00403         }
00404         cpl_msg_debug(cpl_func, "Line estimation method: %d\n",
00405                                 line_estimate_method);
00406 
00407 // #############################################################################
00408 // ###           check inputs
00409 // #############################################################################
00410         KMO_TRY_ASSURE((parlist != NULL) &&
00411                        (frameset != NULL),
00412                        CPL_ERROR_NULL_INPUT,
00413                        "Not all input data is provided!");
00414 
00415         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, ARC_ON) >= 1,
00416                        CPL_ERROR_NULL_INPUT,
00417                        "At least one ARC_ON frame is required!");
00418 
00419         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, ARC_OFF) >= 1,
00420                        CPL_ERROR_NULL_INPUT,
00421                        "Exactly one ARC_OFF frame is required!");
00422 
00423         if (cpl_frameset_count_tags(frameset, ARC_OFF) > 1) {
00424             cpl_msg_warning("", "Exactly one ARC_OFF frame is required. "
00425                                 "Just the first frame will be used");
00426         }
00427 
00428         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, XCAL) == 1,
00429                        CPL_ERROR_FILE_NOT_FOUND,
00430                        "Exactly one XCAL frame is required!");
00431 
00432         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, YCAL) == 1,
00433                        CPL_ERROR_FILE_NOT_FOUND,
00434                        "Exactly one YCAL frame is required!");
00435 
00436         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, ARC_LIST) == 1,
00437                        CPL_ERROR_FILE_NOT_FOUND,
00438                        "Exactly one ARC_LIST frame is required!");
00439 
00440         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, FLAT_EDGE) == 1,
00441                        CPL_ERROR_FILE_NOT_FOUND,
00442                        "Exactly one FLAT_EDGE frame is required!");
00443 
00444         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, WAVE_BAND) == 1,
00445                        CPL_ERROR_FILE_NOT_FOUND,
00446                        "Exactly one WAVE_BAND frame is required!");
00447 
00448         if (line_estimate_method == 2) {
00449             KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, REF_LINES) == 1,
00450                            CPL_ERROR_FILE_NOT_FOUND,
00451                            "Exactly one REF_LINES frame is required!");
00452         }
00453         
00454         KMO_TRY_ASSURE(kmo_dfs_set_groups(frameset, "kmo_wave_cal") == 1,
00455                        CPL_ERROR_ILLEGAL_INPUT,
00456                        "Cannot identify RAW and CALIB frames!");
00457 
00458         //
00459         // ------------ get parameters ------------
00460         //
00461         cpl_msg_info("", "--- Parameter setup for kmo_wave_cal ------");
00462 
00463         fit_order_par = kmo_dfs_get_parameter_int(parlist,
00464                                               "kmos.kmo_wave_cal.order");
00465         KMO_TRY_CHECK_ERROR_STATE();
00466         KMO_TRY_EXIT_IF_ERROR(
00467             kmo_dfs_print_parameter_help(parlist, "kmos.kmo_wave_cal.order"));
00468         KMO_TRY_ASSURE((fit_order_par >= 0) &&
00469                        (fit_order_par <= 8),
00470                        CPL_ERROR_ILLEGAL_INPUT,
00471                        "order must be between 1 and 8!");
00472 
00473         flip_trace = kmo_dfs_get_parameter_bool(parlist,
00474                                            "kmos.kmo_wave_cal.dev_flip");
00475         KMO_TRY_CHECK_ERROR_STATE();
00476         KMO_TRY_EXIT_IF_ERROR(
00477             kmo_dfs_print_parameter_help(parlist, "kmos.kmo_wave_cal.dev_flip"));
00478         KMO_TRY_ASSURE((flip_trace == TRUE) ||
00479                        (flip_trace == FALSE),
00480                        CPL_ERROR_ILLEGAL_INPUT,
00481                        "flip must be TRUE or FALSE!");
00482 
00483         disp = kmo_dfs_get_parameter_double(parlist,
00484                                            "kmos.kmo_wave_cal.dev_disp");
00485         KMO_TRY_CHECK_ERROR_STATE();
00486         KMO_TRY_EXIT_IF_ERROR(
00487             kmo_dfs_print_parameter_help(parlist, "kmos.kmo_wave_cal.dev_disp"));
00488 
00489         KMO_TRY_ASSURE((disp > 0.0) || (disp == -1.0),
00490                        CPL_ERROR_ILLEGAL_INPUT,
00491                        "dispersion must be greater than 0.0!");
00492 
00493         suppress_extension = kmo_dfs_get_parameter_bool(parlist,
00494                                           "kmos.kmo_wave_cal.suppress_extension");
00495         KMO_TRY_CHECK_ERROR_STATE();
00496         KMO_TRY_EXIT_IF_ERROR(
00497             kmo_dfs_print_parameter_help(parlist, "kmos.kmo_wave_cal.suppress_extension"));
00498 
00499         KMO_TRY_ASSURE((suppress_extension == TRUE) || (suppress_extension == FALSE),
00500                        CPL_ERROR_ILLEGAL_INPUT,
00501                        "suppress_extension must be TRUE or FALSE!");
00502 
00503         kmo_band_pars_load(parlist, "kmos.kmo_wave_cal");
00504 
00505         cpl_msg_info("", "-------------------------------------------");
00506 
00507         //
00508         // ------------ check EXPTIME, NDIT, READMODE and lamps ------------
00509         //
00510 
00511         // check ARC_OFF
00512         KMO_TRY_EXIT_IF_NULL(
00513             frame = kmo_dfs_get_frame(frameset, ARC_OFF));
00514 
00515         main_header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0);
00516 
00517         ndit = cpl_propertylist_get_int(main_header, NDIT);
00518         KMO_TRY_CHECK_ERROR_STATE("NDIT keyword in main header "
00519                                   "missing!");
00520 
00521         exptime = cpl_propertylist_get_double(main_header, EXPTIME);
00522         KMO_TRY_CHECK_ERROR_STATE("EXPTIME keyword in main header "
00523                                   "missing!");
00524 
00525         readmode = cpl_strdup(cpl_propertylist_get_string(main_header, READMODE));
00526         KMO_TRY_CHECK_ERROR_STATE("ESO DET READ CURNAME keyword in main "
00527                                   "header missing!");
00528 
00529         desc1 = kmo_identify_fits_header(
00530                     cpl_frame_get_filename(frame));
00531         KMO_TRY_CHECK_ERROR_STATE_MSG("ARC_OFF frame doesn't seem to "
00532                                       "be in KMOS-format!");
00533 
00534         KMO_TRY_ASSURE(desc1.fits_type == raw_fits,
00535                        CPL_ERROR_ILLEGAL_INPUT,
00536                        "ARC_OFF frame hasn't correct data type "
00537                        "(%s must be a raw, unprocessed file)!",
00538                        cpl_frame_get_filename(frame));
00539 
00540         nx = desc1.naxis1;
00541         ny = desc1.naxis2;
00542         nz = desc1.naxis3;
00543         nr_devices = desc1.nr_ext;
00544 
00545         // assure that flat lamps are off
00546         KMO_TRY_ASSURE((kmo_check_lamp(main_header, INS_LAMP3_ST) == FALSE) &&
00547                        (kmo_check_lamp(main_header, INS_LAMP4_ST) == FALSE),
00548                        CPL_ERROR_ILLEGAL_INPUT,
00549                        "Flat lamps must be switched off (%s)!",
00550                        cpl_frame_get_filename(frame));
00551 
00552         // check if arc lamps are off (or at least blocked by filter wheel)
00553         if ((kmo_check_lamp(main_header, INS_LAMP1_ST) == TRUE) ||
00554             (kmo_check_lamp(main_header, INS_LAMP2_ST) == TRUE))
00555         {
00556             if (!(strcmp(cpl_propertylist_get_string(main_header, "ESO INS FILT1 ID"), "Block") == 0) ||
00557                 !(strcmp(cpl_propertylist_get_string(main_header, "ESO INS FILT3 ID"), "Block") == 0) ||
00558                 !(strcmp(cpl_propertylist_get_string(main_header, "ESO INS FILT3 ID"), "Block") == 0))
00559             {
00560                 cpl_msg_warning("","At least one arc lamp is on! Check if the lamps are blocked!");
00561             }
00562         }
00563         cpl_propertylist_delete(main_header); main_header = NULL;
00564 
00565         // check REF_LINES
00566         if (line_estimate_method == 2) {
00567             KMO_TRY_EXIT_IF_NULL(
00568                 frame = kmo_dfs_get_frame(frameset, REF_LINES));
00569             desc2 = kmo_identify_fits_header(
00570                         cpl_frame_get_filename(frame));
00571             KMO_TRY_CHECK_ERROR_STATE_MSG("REF_LINES frame doesn't seem to "
00572                                           "be in KMOS-format!");
00573 
00574             KMO_TRY_ASSURE(desc2.fits_type == f2l_fits,
00575                            CPL_ERROR_ILLEGAL_INPUT,
00576                            "REF_LINES frame hasn't correct frame type "
00577                            "(%s must be a F2L frame)!",
00578                            cpl_frame_get_filename(frame));
00579             kmo_free_fits_desc(&desc2);
00580         }
00581 
00582         // check ARC_ON
00583         KMO_TRY_EXIT_IF_NULL(
00584             frame = kmo_dfs_get_frame(frameset, ARC_ON));
00585 
00586         while (frame != NULL) {
00587             main_header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0);
00588 
00589             KMO_TRY_ASSURE(cpl_propertylist_get_int(main_header, NDIT) == ndit,
00590                     CPL_ERROR_ILLEGAL_INPUT,
00591                     "NDIT isn't the same for all frames: (is %d and %d).",
00592                     cpl_propertylist_get_int(main_header, NDIT), ndit);
00593 
00594             KMO_TRY_ASSURE(cpl_propertylist_get_double(main_header, EXPTIME) == exptime,
00595                     CPL_ERROR_ILLEGAL_INPUT,
00596                     "EXPTIME isn't the same for all frames: (is %g and %g).",
00597                     cpl_propertylist_get_double(main_header, EXPTIME), exptime);
00598 
00599             KMO_TRY_ASSURE(strcmp(cpl_propertylist_get_string(main_header, READMODE), readmode) == 0,
00600                     CPL_ERROR_ILLEGAL_INPUT,
00601                     "ESO DET READ CURNAME isn't the same for all frames: (is %s and %s).",
00602                     cpl_propertylist_get_string(main_header, READMODE), readmode);
00603 
00604             desc2 = kmo_identify_fits_header(
00605                     cpl_frame_get_filename(frame));
00606             KMO_TRY_CHECK_ERROR_STATE_MSG("ARC_ON frame doesn't seem to "
00607                     "be in KMOS-format!");
00608 
00609             KMO_TRY_ASSURE(desc2.fits_type == raw_fits,
00610                     CPL_ERROR_ILLEGAL_INPUT,
00611                     "ARC_ON frame hasn't correct data type "
00612                     "(%s must be a raw, unprocessed file)!",
00613                     cpl_frame_get_filename(frame));
00614 
00615             KMO_TRY_ASSURE((desc2.naxis1 == nx) &&
00616                     (desc2.naxis2 == ny) &&
00617                     (desc2.naxis3 == nz),
00618                     CPL_ERROR_ILLEGAL_INPUT,
00619                     "ARC_ON frame hasn't correct dimensions! (x,y): (%d,%d) "
00620                     "vs (%d,%d)", desc2.naxis1, desc2.naxis2, nx, ny);
00621 
00622 
00623             // assure that flat lamp is off (LAMP3 and 4)
00624             // and that either arc lamp is on (LAMP1 and 2)
00625             KMO_TRY_ASSURE(((kmo_check_lamp(main_header, INS_LAMP1_ST) == TRUE) ||
00626                     (kmo_check_lamp(main_header, INS_LAMP2_ST) == TRUE)) &&
00627                     (kmo_check_lamp(main_header, INS_LAMP3_ST) == FALSE) &&
00628                     (kmo_check_lamp(main_header, INS_LAMP4_ST) == FALSE),
00629                     CPL_ERROR_ILLEGAL_INPUT,
00630                     "Lamp1 or Lamp2 must be switched on, 3 and 4 must be "
00631                     "off for the ARC_ON frame");
00632 
00633             frame = kmo_dfs_get_frame(frameset, NULL);
00634             KMO_TRY_CHECK_ERROR_STATE();
00635 
00636             kmo_free_fits_desc(&desc2);
00637             kmo_init_fits_desc(&desc2);
00638             cpl_propertylist_delete(main_header); main_header = NULL;
00639         }
00640 
00641         // load first ARC_ON main header
00642         KMO_TRY_EXIT_IF_NULL(
00643             frame = kmo_dfs_get_frame(frameset, ARC_ON));
00644         KMO_TRY_EXIT_IF_NULL(
00645             main_header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0));
00646 
00647         // check FLAT_EDGE
00648         KMO_TRY_EXIT_IF_NULL(
00649             frame = kmo_dfs_get_frame(frameset, FLAT_EDGE));
00650 
00651         desc2 = kmo_identify_fits_header(cpl_frame_get_filename(frame));
00652         KMO_TRY_CHECK_ERROR_STATE();
00653 
00654         KMO_TRY_ASSURE((desc2.nr_ext % 24== 0) &&
00655                        (desc2.fits_type == f2l_fits),
00656                        CPL_ERROR_ILLEGAL_INPUT,
00657                        "FLAT_EDGE isn't in the correct format!!!");
00658 
00659         kmo_free_fits_desc(&desc2);
00660         kmo_init_fits_desc(&desc2);
00661 
00662         //
00663         // ------------ check filter_id, grating_id and rotator offset ---------
00664         // assure that filters, grating and rotation offsets match for
00665         // ARC_ON, XCAL and YCAL
00666         // check if filter_id, grating_id and rotator offset match for all
00667         // detectors
00668         //
00669         KMO_TRY_EXIT_IF_ERROR(
00670             kmo_check_frame_setup(frameset, ARC_ON, XCAL,
00671                                        TRUE, FALSE, FALSE));
00672         KMO_TRY_EXIT_IF_ERROR(
00673             kmo_check_frame_setup(frameset, ARC_ON, YCAL,
00674                                        TRUE, FALSE, FALSE));
00675 
00676         KMO_TRY_EXIT_IF_ERROR(
00677             kmo_check_frame_setup_md5_xycal(frameset));
00678 
00679         strcpy(filename_lcal, LCAL);
00680         strcpy(filename_det_img, DET_IMG_WAVE);
00681 
00682         KMO_TRY_EXIT_IF_NULL(
00683             frame = kmo_dfs_get_frame(frameset, XCAL));
00684         KMO_TRY_EXIT_IF_NULL(
00685             suffix = kmo_dfs_get_suffix(frame, TRUE, FALSE));
00686 
00687         cpl_msg_info("", "Detected instrument setup:   %s", suffix+1);
00688         cpl_msg_info("", "(grating 1, 2 & 3)");
00689 
00690         // setup lamp config
00691         if ((kmo_check_lamp(main_header, INS_LAMP1_ST) == TRUE) &&
00692             (kmo_check_lamp(main_header, INS_LAMP2_ST) == FALSE))
00693         {
00694             lamp_config = ARGON;
00695             strcpy(tmpstr, "Argon");
00696         } else if ((kmo_check_lamp(main_header, INS_LAMP1_ST) == FALSE) &&
00697                    (kmo_check_lamp(main_header, INS_LAMP2_ST) == TRUE))
00698         {
00699            lamp_config = NEON;
00700            strcpy(tmpstr, "Neon");
00701         } else if ((kmo_check_lamp(main_header, INS_LAMP1_ST) == TRUE) &&
00702                    (kmo_check_lamp(main_header, INS_LAMP2_ST) == TRUE))
00703         {
00704            lamp_config = ARGON_NEON;
00705            strcpy(tmpstr, "Argon + Neon");
00706         }
00707 
00708         cpl_msg_info("", "Detected arc lamp configuration: %s", tmpstr);
00709 
00710         //assert that filter and grating match for each detector
00711         // filter/grating can be different for each detector
00712         KMO_TRY_EXIT_IF_NULL(
00713             filter_ids =  kmo_get_filter_setup(main_header, nr_devices, TRUE));
00714 
00715         //
00716         // ---- scan for rotator angles
00717         //
00718         #define ANGLE_DIM 360
00719         int rotang_found[ANGLE_DIM];
00720         int rotang_cnt[ANGLE_DIM];
00721         for (i = 0; i < ANGLE_DIM; i++) {
00722             rotang_found[i] = 0;
00723             rotang_cnt[i] = 0;
00724         }
00725         KMO_TRY_EXIT_IF_NULL(
00726             frame = kmo_dfs_get_frame(frameset, ARC_ON));
00727         while (frame != NULL) {
00728             header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0);
00729             if (cpl_propertylist_has(header, ROTANGLE)) {
00730                 int rot_angle = (int) rint(cpl_propertylist_get_double(header, ROTANGLE));
00731                 if (rot_angle < 0) {
00732                     rot_angle += 360;
00733                 }
00734                 if (rot_angle < 360 && rot_angle >= 0) {
00735                     rotang_cnt[rot_angle]++;
00736 //                    char * tag = cpl_sprintf("FLAT_ON_%3.3d",rot_angle);
00737 //                    KMO_TRY_EXIT_IF_ERROR(
00738 //                            cpl_frame_set_tag(frame, tag));
00739 //                    cpl_free(tag);
00740                 }
00741             } else {
00742                 cpl_msg_warning("","File %s has no keyword \"ROTANGLE\"",
00743                         cpl_frame_get_filename(frame));
00744             }
00745 
00746             cpl_propertylist_delete(header); header = NULL;
00747             frame = kmo_dfs_get_frame(frameset, NULL);
00748             KMO_TRY_CHECK_ERROR_STATE();
00749         }
00750         for (ax = 0; ax < ANGLE_DIM; ax++) {
00751             if (rotang_cnt[ax] != 0) {
00752                 if (rotang_cnt[ax] == 1 ) {
00753                     cpl_msg_info("","Found %d frame with rotator angle %d",rotang_cnt[ax],ax);
00754                 } else {
00755                     cpl_msg_warning("","Found %d frames with rotator angle %d but only one will be used",
00756                                     rotang_cnt[ax],ax);
00757                 }
00758                 rotang_found[nr_angles] = ax;
00759                 nr_angles++;
00760             }
00761         }
00762 
00763         KMO_TRY_EXIT_IF_NULL (
00764             angle_frameset = (cpl_frameset **) cpl_malloc(nr_angles * sizeof(cpl_frameset*)));
00765         for (i = 0; i < nr_angles; i++) {
00766             angle_frameset[i] = cpl_frameset_new();
00767         }
00768 
00769         KMO_TRY_EXIT_IF_NULL(
00770             frame = kmo_dfs_get_frame(frameset, ARC_ON));
00771         while (frame != NULL) {
00772             header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0);
00773             if (cpl_propertylist_has(header, ROTANGLE)) {
00774                 int rot_angle = (int) rint(cpl_propertylist_get_double(header, ROTANGLE));
00775                 if (rot_angle < 0) {
00776                     rot_angle += 360;
00777                 }
00778                 int ix = -1;
00779                 for (ix = 0; ix<nr_angles; ix++) {
00780                     if (rotang_found[ix] == rot_angle) {
00781                         break;
00782                     }
00783                 }
00784                 if (ix<nr_angles) {
00785                     KMO_TRY_EXIT_IF_ERROR(
00786                         cpl_frameset_insert(angle_frameset[ix], cpl_frame_duplicate(frame)));
00787                 }
00788             }
00789 
00790             cpl_propertylist_delete(header); header = NULL;
00791             frame = kmo_dfs_get_frame(frameset, NULL);
00792             KMO_TRY_CHECK_ERROR_STATE();
00793         }
00794 
00795 // #############################################################################
00796 // ###           allocate temporary memory
00797 // #############################################################################
00798         // allocate here a edge table structure for all detectors
00799         KMO_TRY_EXIT_IF_NULL(
00800             edge_table = (cpl_table***)cpl_calloc(nr_devices,
00801                                                   sizeof(cpl_table**)));
00802         for (i = 0; i < nr_devices; i++) {
00803             KMO_TRY_EXIT_IF_NULL(
00804                 edge_table[i] = (cpl_table**)cpl_calloc(KMOS_IFUS_PER_DETECTOR,
00805                                                         sizeof(cpl_table*)));
00806         }
00807 
00808         // the frames have to be stored temporarily because the QC parameters
00809         // for the main header are calculated from each detector. So they can be
00810         // stored only when all detectors are processed
00811         KMO_TRY_EXIT_IF_NULL(
00812             stored_lcal = (cpl_image**)cpl_calloc(nr_devices * nr_angles,
00813                                                     sizeof(cpl_image*)));
00814         KMO_TRY_EXIT_IF_NULL(
00815             stored_det_img = (cpl_image**)cpl_calloc(nr_devices * nr_angles,
00816                                                     sizeof(cpl_image*)));
00817         KMO_TRY_EXIT_IF_NULL(
00818             stored_sub_headers_lcal = (cpl_propertylist**)cpl_calloc(nr_devices * nr_angles,
00819                                                     sizeof(cpl_propertylist*)));
00820         KMO_TRY_EXIT_IF_NULL(
00821             stored_sub_headers_det_img = (cpl_propertylist**)cpl_calloc(nr_devices * nr_angles,
00822                                                     sizeof(cpl_propertylist*)));
00823         KMO_TRY_EXIT_IF_NULL(
00824             stored_qc_arc_sat =
00825                             (int*)cpl_calloc(nr_devices, nr_angles * sizeof(int)));
00826         KMO_TRY_EXIT_IF_NULL(
00827             stored_qc_ar_eff =
00828                             (double*)cpl_calloc(nr_devices, nr_angles * sizeof(double)));
00829         KMO_TRY_EXIT_IF_NULL(
00830             stored_qc_ne_eff =
00831                             (double*)cpl_calloc(nr_devices, nr_angles * sizeof(double)));
00832 
00833 // #############################################################################
00834 // ###           process data
00835 // #############################################################################
00836         // load arclines and reference lines
00837         KMO_TRY_EXIT_IF_NULL(
00838             frame = kmo_dfs_get_frame(frameset, ARC_LIST));
00839 
00840         // check if ARC_LIST is the filter_id-one
00841         KMO_TRY_EXIT_IF_NULL(
00842             tmp_header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0));
00843         KMO_TRY_EXIT_IF_NULL(
00844             tmp_str = cpl_propertylist_get_string(tmp_header, FILT_ID));
00845         KMO_TRY_ASSURE(strcmp(filter_ids[0], tmp_str) == 0,
00846                        CPL_ERROR_ILLEGAL_INPUT,
00847                        "ARC_LIST model must have primary "
00848                        "keyword '%s' equal '%s'!!!", FILT_ID, filter_ids[0]);
00849         cpl_propertylist_delete(tmp_header); tmp_header = NULL;
00850 
00851         desc2 = kmo_identify_fits_header(cpl_frame_get_filename(frame));
00852         KMO_TRY_CHECK_ERROR_STATE();
00853 
00854         KMO_TRY_ASSURE((desc2.nr_ext == 1) &&
00855                        (desc2.fits_type == f2l_fits),
00856                        CPL_ERROR_ILLEGAL_INPUT,
00857                        "ARC_LIST isn't in the correct format!!!");
00858         kmo_free_fits_desc(&desc2);
00859         kmo_init_fits_desc(&desc2);
00860 
00861         KMO_TRY_EXIT_IF_NULL(
00862             arclines = kmo_dfs_load_table(frameset, ARC_LIST, 1, 0));
00863 
00864         KMO_TRY_EXIT_IF_NULL(
00865             lines = kmo_get_lines(arclines, lamp_config));
00866 
00867         cpl_msg_info("", "Nr. of lines in arclist for this configuration: %lld",
00868                      cpl_bivector_get_size(lines));
00869 
00870         if (line_estimate_method == 2) {
00871             KMO_TRY_EXIT_IF_NULL(
00872                 reflines = kmo_dfs_load_table(frameset, REF_LINES, 1, 0));
00873         }
00874 
00875         // check which IFUs are active for all FLAT frames
00876         KMO_TRY_EXIT_IF_NULL(
00877             unused_ifus_before = kmo_get_unused_ifus(frameset, 0, 0));
00878 
00879         KMO_TRY_EXIT_IF_NULL(
00880             unused_ifus_after = kmo_duplicate_unused_ifus(unused_ifus_before));
00881 
00882         kmo_print_unused_ifus(unused_ifus_before, FALSE);
00883 
00884         // make sure no reconstruction lookup table (LUT) is used
00885         if (getenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE") != NULL) {
00886             last_env = getenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE");
00887         }
00888         setenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE","NONE",1);
00889 
00890         //
00891         // ------------ loop all rotator angles and detectors ------------
00892         //
00893         for (a = 0; a < nr_angles; a++) {
00894             cpl_msg_info("","Processing rotator angle %d -> %d degree", a,rotang_found[a]);
00895 
00896             for (i = 1; i <= nr_devices; i++) {
00897                 // use loop below for line identification
00898                 cpl_msg_info("","Processing detector No. %d", i);
00899 
00900                 int sx = a * nr_devices + (i - 1);
00901 
00902                 // load edge parameters
00903                 KMO_TRY_EXIT_IF_NULL(
00904                     frame = kmo_dfs_get_frame(frameset, FLAT_EDGE));
00905 
00906                 for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
00907                     edge_table[i-1][j] = kmclipm_cal_table_load(cpl_frame_get_filename(frame),
00908                                                                 (i-1) * KMOS_IFUS_PER_DETECTOR + j + 1,
00909                                                                 rotang_found[a], 0, &angle_found);
00910                     if (cpl_error_get_code() == CPL_ERROR_ILLEGAL_INPUT) {
00911                         // IFU is inactive: proceed
00912                         cpl_error_reset();
00913                     }
00914                 }
00915 
00916                 if (fit_order_par == 0) {
00917                     // set default fit orders for the different bands
00918                     if ((strcmp(filter_ids[i-1], "H") == 0) ||
00919                         (strcmp(filter_ids[i-1], "K") == 0) ||
00920                         (strcmp(filter_ids[i-1], "YJ") == 0))
00921                     {
00922                         fit_order = 6;
00923                     } else if (strcmp(filter_ids[i-1], "IZ") == 0) {
00924                         fit_order = 4;
00925                     } else if (strcmp(filter_ids[i-1], "HK") == 0) {
00926                         fit_order = 5;
00927                     }
00928                     cpl_msg_info("", "Order of wavelength spectrum fit for %s-band: %d",
00929                                  filter_ids[i-1],
00930                                  fit_order);
00931                 } else {
00932                     fit_order = fit_order_par;
00933                 }
00934 
00935                 // lamp_on
00936                 KMO_TRY_EXIT_IF_NULL(
00937                     frame = kmo_dfs_get_frame(angle_frameset[a], ARC_ON));
00938 
00939                 // if sat_mode is set to TRUE here, then the calculation of the
00940                 // saturated pixels has to be updated like in kmo_flat-recipe
00941                 KMO_TRY_EXIT_IF_NULL(
00942                     det_lamp_on = kmo_dfs_load_image_frame(frame, i, FALSE, TRUE, &nr_sat));
00943 
00944                 // count saturated pixels for each detector
00945                 if (strcmp(cpl_propertylist_get_string(main_header, READMODE), "Nondest") == 0) {
00946                     // NDR: non-destructive readout mode
00947                     stored_qc_arc_sat[sx] = nr_sat;
00948                 } else {
00949                     // normal readout mode
00950                     stored_qc_arc_sat[sx] = kmo_image_get_saturated(det_lamp_on,
00951                                                                     KMO_FLAT_SATURATED);
00952                 }
00953                 KMO_TRY_CHECK_ERROR_STATE();
00954 
00955 
00956                 KMO_TRY_EXIT_IF_NULL(
00957                     det_lamp_on_copy = cpl_image_duplicate(det_lamp_on));
00958 
00959                 // lamp_off
00960                 KMO_TRY_EXIT_IF_NULL(
00961                     frame = kmo_dfs_get_frame(frameset, ARC_OFF));
00962 
00963                 KMO_TRY_EXIT_IF_NULL(
00964                     det_lamp_off = kmo_dfs_load_image_frame(frame, i, FALSE, FALSE, NULL));
00965 
00966                 //
00967                 // process imagelist
00968                 //
00969                 KMO_TRY_CHECK_ERROR_STATE();
00970 
00971                 // subtract lamp_off from lamp_on
00972                 KMO_TRY_EXIT_IF_ERROR(
00973                     cpl_image_subtract(det_lamp_on, det_lamp_off));
00974 
00975                 // load flat calibration frames
00976                 KMO_TRY_EXIT_IF_NULL(
00977                     xcal = kmo_dfs_load_cal_image(frameset, XCAL, i, 0,
00978                                                   (double) rotang_found[a], FALSE,
00979                                                   NULL, &angle_found, -1, 0, 0));
00980 
00981                 KMO_TRY_EXIT_IF_NULL(
00982                     ycal = kmo_dfs_load_cal_image(frameset, YCAL, i, 0,
00983                                                   (double) rotang_found[a], FALSE,
00984                                                   NULL,  &angle_found, -1, 0, 0));
00985 
00986                 // load bad pixel mask from XCAL and set NaNs to 0 and all other values to 1
00987                 KMO_TRY_EXIT_IF_NULL(
00988                     bad_pix_mask = cpl_image_duplicate(xcal));
00989 
00990                 KMO_TRY_EXIT_IF_NULL(
00991                     pbad_pix_mask = cpl_image_get_data_float(bad_pix_mask));
00992                 for (x = 0; x < nx; x++) {
00993                     for (y = 0; y < ny; y++) {
00994                         if (isnan(pbad_pix_mask[x+nx*y])) {
00995                             pbad_pix_mask[x+nx*y] = 0.;
00996                         } else {
00997                             pbad_pix_mask[x+nx*y] = 1.;
00998                         }
00999                     }
01000                 }
01001 
01002                 // calculate spectral curvature here
01003                 err = kmo_calc_wave_calib(det_lamp_on,
01004                                           bad_pix_mask,
01005                                           xcal,
01006                                           ycal,
01007                                           filter_ids[i-1],
01008                                           lamp_config,
01009                                           i,
01010                                           unused_ifus_after[i-1],
01011                                           edge_table[i-1],
01012                                           lines,
01013                                           reflines,
01014                                           disp,
01015                                           &lcal,
01016                                           &(stored_qc_ar_eff[sx]),
01017                                           &(stored_qc_ne_eff[sx]),
01018                                           flip_trace,
01019                                           fit_order,
01020                                           line_estimate_method);
01021 
01022                 if (err == CPL_ERROR_NONE) {
01023                     // calculate QC parameters
01024                     if (stored_qc_ar_eff[sx] != -1.0) {
01025                         stored_qc_ar_eff[sx] /= exptime;
01026                     }
01027                     if (stored_qc_ne_eff[sx] != -1.0) {
01028                         stored_qc_ne_eff[sx] /= exptime;
01029                     }
01030 
01031                     // apply the badpixel mask to the produced frame
01032 
01033                     KMO_TRY_EXIT_IF_ERROR(
01034                         cpl_image_multiply(lcal, bad_pix_mask));
01035 
01036                     KMO_TRY_EXIT_IF_ERROR(
01037                         kmo_image_reject_from_mask(lcal, bad_pix_mask));
01038 
01039                     // store flat frames, badpixel mask and calibration frames
01040                     stored_lcal[sx] = lcal;
01041                 } else if (err == CPL_ERROR_UNSPECIFIED) {
01042                     // all IFUs seem to be deativated, continue processing
01043                     // just save empty frame
01044                     cpl_error_reset();
01045                     stored_lcal[sx] = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01046                     kmo_image_fill(stored_lcal[sx], 0.0);
01047                 } else {
01048                     cpl_msg_warning(cpl_func,
01049                                     "Couldn't identify any lines! Is the line "
01050                                     "list defined correctly?");
01051                     cpl_msg_warning(cpl_func,
01052                                     "Band defined in header of detector %d: %s",
01053                                     i, filter_ids[i-1]);
01054                     cpl_msg_warning(cpl_func,
01055                                     "Arc line file defined: %s",
01056                                     cpl_frame_get_filename(
01057                                           kmo_dfs_get_frame(frameset, ARC_LIST)));
01058                     cpl_error_reset();
01059                 }
01060 
01061 
01062                 //
01063                 // create reconstructed and resampled arc frame
01064                 //
01065                 stored_det_img[sx] = kmo_reconstructed_arc_image(frameset,
01066                                                                  det_lamp_on_copy,
01067                                                                  det_lamp_off,
01068                                                                  xcal,
01069                                                                  ycal,
01070                                                                  stored_lcal[sx],
01071                                                                  unused_ifus_after[i-1],
01072                                                                  flip_trace,
01073                                                                  i,
01074                                                                  suffix,
01075                                                                  filter_ids[i-1],
01076                                                                  lamp_config,
01077                                                                  &qc_header);
01078                 if (cpl_error_get_code() != CPL_ERROR_NONE) {
01079                     // couldn't reconstruct
01080                     cpl_msg_error("","Couldn't reconstruct IFUs on detector %d", i);
01081                     cpl_error_reset();
01082                 }
01083 
01084                 // load and update sub_header with QC parameters
01085                 KMO_TRY_EXIT_IF_NULL(
01086                     stored_sub_headers_lcal[sx] = kmo_dfs_load_sub_header(frameset,
01087                                                                           ARC_ON, i,
01088                                                                           FALSE));
01089 
01090                 // update EXTNAME
01091                 KMO_TRY_EXIT_IF_NULL(
01092                     extname = kmo_extname_creator(detector_frame, i, EXT_DATA));
01093                 KMO_TRY_EXIT_IF_ERROR(
01094                     kmclipm_update_property_string(stored_sub_headers_lcal[sx],
01095                                                    EXTNAME,
01096                                                    extname,
01097                                                    "FITS extension name"));
01098                 cpl_free(extname); extname = NULL;
01099 
01100                 KMO_TRY_EXIT_IF_ERROR(
01101                     kmclipm_update_property_int(stored_sub_headers_lcal[sx],
01102                                                 EXTVER,
01103                                                 sx+1,
01104                                                 "FITS extension ver"));
01105 
01106                 // add first QC parameters
01107                 KMO_TRY_EXIT_IF_ERROR(
01108                     kmclipm_update_property_int(stored_sub_headers_lcal[sx],
01109                                                 QC_ARC_SAT,
01110                                                 stored_qc_arc_sat[sx],
01111                                                 "[] nr. saturated pixels of arc exp."));
01112 
01113                 gain = kmo_dfs_get_property_double(stored_sub_headers_lcal[sx], GAIN);
01114                 KMO_TRY_CHECK_ERROR_STATE_MSG(
01115                     "GAIN-keyword in fits-header is missing!");
01116 
01117                 if (stored_qc_ar_eff[sx] != -1.0) {
01118                     KMO_TRY_EXIT_IF_ERROR(
01119                         kmclipm_update_property_double(stored_sub_headers_lcal[sx],
01120                                                        QC_ARC_AR_EFF,
01121                                                        stored_qc_ar_eff[sx]/gain,
01122                                                        "[e-/s] Argon lamp efficiency"));
01123                 }
01124 
01125                 if (stored_qc_ne_eff[sx] != -1.0) {
01126                     KMO_TRY_EXIT_IF_ERROR(
01127                         kmclipm_update_property_double(stored_sub_headers_lcal[sx],
01128                                                        QC_ARC_NE_EFF,
01129                                                        stored_qc_ne_eff[sx]/gain,
01130                                                        "[e-/s] Neon lamp efficiency"));
01131                 }
01132 
01133                 KMO_TRY_EXIT_IF_ERROR(
01134                     kmclipm_update_property_double(stored_sub_headers_lcal[sx],
01135                                                    CAL_ROTANGLE,
01136                                                    ((double) rotang_found[a]),
01137                                                    "[deg] Rotator relative to nasmyth"));
01138 
01139                 // append QC parameters
01140                 KMO_TRY_EXIT_IF_ERROR(
01141                     cpl_propertylist_append(stored_sub_headers_lcal[sx],
01142                                             qc_header));
01143                 cpl_propertylist_delete(qc_header); qc_header = NULL;
01144 
01145                 KMO_TRY_EXIT_IF_NULL(
01146                     stored_sub_headers_det_img[sx] = cpl_propertylist_duplicate(
01147                                                      stored_sub_headers_lcal[sx]));
01148 
01149                 cpl_propertylist_erase(stored_sub_headers_lcal[sx], CRVAL1);
01150                 cpl_propertylist_erase(stored_sub_headers_lcal[sx], CRVAL2);
01151                 cpl_propertylist_erase(stored_sub_headers_lcal[sx], CTYPE1);
01152                 cpl_propertylist_erase(stored_sub_headers_lcal[sx], CTYPE2);
01153                 cpl_propertylist_erase(stored_sub_headers_lcal[sx], CDELT1);
01154                 cpl_propertylist_erase(stored_sub_headers_lcal[sx], CDELT2);
01155 
01156 // leave keywords in for proper fitsverify output
01157 //                cpl_propertylist_erase(stored_sub_headers_lcal[sx], CRPIX1);
01158 //                cpl_propertylist_erase(stored_sub_headers_lcal[sx], CRPIX2);
01159 //                if (cpl_propertylist_has(stored_sub_headers_lcal[sx], CRPIX1)) {
01160 //                    cpl_propertylist_erase(stored_sub_headers_lcal[sx], CRPIX1);
01161 //                }
01162 //                if (cpl_propertylist_has(stored_sub_headers_lcal[sx], CRPIX2)) {
01163 //                    cpl_propertylist_erase(stored_sub_headers_lcal[sx], CRPIX2);
01164 //                }
01165 
01166                 // free memory
01167                 cpl_image_delete(det_lamp_on); det_lamp_on = NULL;
01168                 cpl_image_delete(det_lamp_on_copy); det_lamp_on_copy = NULL;
01169                 cpl_image_delete(det_lamp_off); det_lamp_off = NULL;
01170                 cpl_image_delete(bad_pix_mask); bad_pix_mask = NULL;
01171                 cpl_image_delete(xcal); xcal = NULL;
01172                 cpl_image_delete(ycal); ycal = NULL;
01173 
01174                 for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01175                     cpl_table_delete(edge_table[i-1][j]); edge_table[i-1][j] = NULL;
01176                 }
01177             } // for i devices
01178         } // for a angles
01179 
01180         if (line_estimate_method == 2) {
01181             cpl_table_delete(reflines); reflines = NULL;
01182         }
01183 // ###########################################################################
01184 // ###           QC parameters & saving
01185 // ###########################################################################
01186         cpl_msg_info("","Saving data...");
01187 
01188         //
01189         // ------------ load, update & save primary header ------------
01190         //
01191         if (!suppress_extension) {
01192             KMO_TRY_EXIT_IF_NULL(
01193                 fn_suffix = cpl_sprintf("%s", suffix));
01194         } else {
01195             KMO_TRY_EXIT_IF_NULL(
01196                 fn_suffix = cpl_sprintf("%s", ""));
01197         }
01198 
01199         // update which IFUs are not used
01200         KMO_TRY_EXIT_IF_ERROR(
01201             kmo_set_unused_ifus(unused_ifus_after, main_header,
01202                                 "kmo_wave_cal"));
01203 
01204         KMO_TRY_EXIT_IF_NULL(
01205             frame = kmo_dfs_get_frame(frameset, ARC_ON));
01206 
01207         KMO_TRY_EXIT_IF_ERROR(
01208             kmo_dfs_save_main_header(frameset, filename_lcal, fn_suffix, frame,
01209                                      main_header, parlist, cpl_func));
01210 
01211         KMO_TRY_EXIT_IF_ERROR(
01212             kmo_dfs_save_main_header(frameset, filename_det_img, fn_suffix, frame,
01213                                      main_header, parlist, cpl_func));
01214 
01215         cpl_propertylist_delete(main_header); main_header = NULL;
01216 
01217         //
01218         // --- save sub-frames ---
01219         //
01220         for (a = 0; a < nr_angles; a++) {
01221             for (i = 1; i <= nr_devices; i++) {
01222                 int sx = a * nr_devices + (i - 1);
01223                 // save lcal-frame
01224                 KMO_TRY_EXIT_IF_ERROR(
01225                     kmo_dfs_save_image(stored_lcal[sx], filename_lcal, fn_suffix,
01226                                        stored_sub_headers_lcal[sx], 0./0.));
01227 
01228                 // save detector image
01229                 KMO_TRY_EXIT_IF_ERROR(
01230                     kmo_dfs_save_image(stored_det_img[sx], filename_det_img, fn_suffix,
01231                                        stored_sub_headers_det_img[sx], 0./0.));
01232             } // for i = nr_devices
01233         } // for a angles
01234 
01235         // print which IFUs are not used
01236         kmo_print_unused_ifus(unused_ifus_after, TRUE);
01237     }
01238     KMO_CATCH
01239     {
01240         KMO_CATCH_MSG();
01241         ret_val = -1;
01242     }
01243 
01244     if (last_env != NULL) {
01245         setenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE",last_env,1);
01246     } else {
01247         unsetenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE");
01248     }
01249 
01250     kmo_free_fits_desc(&desc1);
01251     kmo_free_fits_desc(&desc2);
01252     if (unused_ifus_before != NULL) {
01253        kmo_free_unused_ifus(unused_ifus_before); unused_ifus_before = NULL;
01254     }
01255     if (unused_ifus_after != NULL) {
01256        kmo_free_unused_ifus(unused_ifus_after); unused_ifus_after = NULL;
01257     }
01258     cpl_propertylist_delete(main_header); main_header = NULL;
01259     cpl_image_delete(det_lamp_on); det_lamp_on = NULL;
01260     cpl_image_delete(det_lamp_off); det_lamp_off = NULL;
01261     cpl_image_delete(bad_pix_mask); bad_pix_mask = NULL;
01262     cpl_table_delete(arclines); arclines = NULL;
01263     cpl_table_delete(reflines); reflines = NULL;
01264     cpl_free(stored_qc_arc_sat); stored_qc_arc_sat = NULL;
01265     cpl_free(stored_qc_ar_eff); stored_qc_ar_eff = NULL;
01266     cpl_free(stored_qc_ne_eff); stored_qc_ne_eff = NULL;
01267     for (i = 0; i < nr_devices * nr_angles; i++) {
01268         cpl_image_delete(stored_lcal[i]); stored_lcal[i] = NULL;
01269         cpl_image_delete(stored_det_img[i]); stored_det_img[i] = NULL;
01270         cpl_propertylist_delete(stored_sub_headers_lcal[i]);
01271             stored_sub_headers_lcal[i] = NULL;
01272         cpl_propertylist_delete(stored_sub_headers_det_img[i]);
01273             stored_sub_headers_det_img[i] = NULL;
01274     }
01275     for (i = 0; i < nr_angles; i++) {
01276         cpl_frameset_delete(angle_frameset[i]); angle_frameset[i] = NULL;
01277     }
01278     if (filter_ids != NULL) {
01279         for (i = 0; i < nr_devices; i++) {
01280             cpl_free(filter_ids[i]); filter_ids[i] = NULL;
01281 
01282         }
01283         cpl_free(filter_ids); filter_ids = NULL;
01284     }
01285     if (edge_table != NULL) {
01286         for (i = 0; i < nr_devices; i++) {
01287             cpl_free(edge_table[i]); edge_table[i] = NULL;
01288         }
01289         cpl_free(edge_table); edge_table = NULL;
01290     }
01291 
01292     cpl_free(angle_frameset); angle_frameset = NULL;
01293     cpl_free(stored_lcal); stored_lcal = NULL;
01294     cpl_free(stored_det_img); stored_det_img = NULL;
01295     cpl_free(stored_sub_headers_lcal); stored_sub_headers_lcal = NULL;
01296     cpl_free(stored_sub_headers_det_img); stored_sub_headers_det_img = NULL;
01297     cpl_free(readmode); readmode = NULL;
01298     cpl_bivector_delete(lines); lines = NULL;
01299     cpl_free(suffix); suffix = NULL;
01300     cpl_free(fn_suffix); fn_suffix = NULL;
01301 
01302     return ret_val;
01303 }
01304