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