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