KMOS Pipeline Reference Manual  1.1.3
kmo_wave_cal.c
00001 /* $Id: kmo_wave_cal.c,v 1.45 2013/05/02 11:13:01 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/05/02 11:13:01 $
00024  * $Revision: 1.45 $
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         if (cpl_frameset_count_tags(frameset, ARC_OFF) >= 1) {
00402             cpl_msg_warning("", "Exactly one ARC_OFF frame is required"
00403                                 "Just the first frame will be used");
00404         }
00405 
00406         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, XCAL) == 1,
00407                        CPL_ERROR_FILE_NOT_FOUND,
00408                        "Exactly one XCAL frame is required!");
00409 
00410         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, YCAL) == 1,
00411                        CPL_ERROR_FILE_NOT_FOUND,
00412                        "Exactly one YCAL frame is required!");
00413 
00414         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, ARC_LIST) == 1,
00415                        CPL_ERROR_FILE_NOT_FOUND,
00416                        "Exactly one ARC_LIST frame is required!");
00417 
00418         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, FLAT_EDGE) == 1,
00419                        CPL_ERROR_FILE_NOT_FOUND,
00420                        "Exactly one FLAT_EDGE frame is required!");
00421 
00422         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, WAVE_BAND) == 1,
00423                        CPL_ERROR_FILE_NOT_FOUND,
00424                        "Exactly one WAVE_BAND frame is required!");
00425 
00426         if (line_estimate_method == 2) {
00427             KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, REF_LINES) == 1,
00428                            CPL_ERROR_FILE_NOT_FOUND,
00429                            "Exactly one REF_LINES frame is required!");
00430         }
00431         
00432         KMO_TRY_ASSURE(kmo_dfs_set_groups(frameset, "kmo_wave_cal") == 1,
00433                        CPL_ERROR_ILLEGAL_INPUT,
00434                        "Cannot identify RAW and CALIB frames!");
00435 
00436         //
00437         // ------------ get parameters ------------
00438         //
00439         cpl_msg_info("", "--- Parameter setup for kmo_wave_cal ------");
00440 
00441         fit_order_par = kmo_dfs_get_parameter_int(parlist,
00442                                               "kmos.kmo_wave_cal.order");
00443         KMO_TRY_CHECK_ERROR_STATE();
00444         KMO_TRY_EXIT_IF_ERROR(
00445             kmo_dfs_print_parameter_help(parlist, "kmos.kmo_wave_cal.order"));
00446         KMO_TRY_ASSURE((fit_order_par >= 0) &&
00447                        (fit_order_par <= 8),
00448                        CPL_ERROR_ILLEGAL_INPUT,
00449                        "order must be between 1 and 8!");
00450 
00451         flip_trace = kmo_dfs_get_parameter_bool(parlist,
00452                                            "kmos.kmo_wave_cal.dev_flip");
00453         KMO_TRY_CHECK_ERROR_STATE();
00454         KMO_TRY_EXIT_IF_ERROR(
00455             kmo_dfs_print_parameter_help(parlist, "kmos.kmo_wave_cal.dev_flip"));
00456         KMO_TRY_ASSURE((flip_trace == TRUE) ||
00457                        (flip_trace == FALSE),
00458                        CPL_ERROR_ILLEGAL_INPUT,
00459                        "flip must be TRUE or FALSE!");
00460 
00461         disp = kmo_dfs_get_parameter_double(parlist,
00462                                            "kmos.kmo_wave_cal.dev_disp");
00463         KMO_TRY_CHECK_ERROR_STATE();
00464         KMO_TRY_EXIT_IF_ERROR(
00465             kmo_dfs_print_parameter_help(parlist, "kmos.kmo_wave_cal.dev_disp"));
00466 
00467         KMO_TRY_ASSURE((disp > 0.0) || (disp == -1.0),
00468                        CPL_ERROR_ILLEGAL_INPUT,
00469                        "dispersion must be greater than 0.0!");
00470 
00471         kmo_band_pars_load(parlist, "kmos.kmo_wave_cal");
00472 
00473         cpl_msg_info("", "-------------------------------------------");
00474 
00475         //
00476         // ------------ check EXPTIME, NDIT, READMODE and lamps ------------
00477         //
00478 
00479         // check ARC_OFF
00480         KMO_TRY_EXIT_IF_NULL(
00481             frame = kmo_dfs_get_frame(frameset, ARC_OFF));
00482 
00483         main_header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0);
00484 
00485         ndit = cpl_propertylist_get_int(main_header, NDIT);
00486         KMO_TRY_CHECK_ERROR_STATE("NDIT keyword in main header "
00487                                   "missing!");
00488 
00489         exptime = cpl_propertylist_get_double(main_header, EXPTIME);
00490         KMO_TRY_CHECK_ERROR_STATE("EXPTIME keyword in main header "
00491                                   "missing!");
00492 
00493         readmode = cpl_strdup(cpl_propertylist_get_string(main_header, READMODE));
00494         KMO_TRY_CHECK_ERROR_STATE("ESO DET READ CURNAME keyword in main "
00495                                   "header missing!");
00496 
00497         desc1 = kmo_identify_fits_header(
00498                     cpl_frame_get_filename(frame));
00499         KMO_TRY_CHECK_ERROR_STATE_MSG("ARC_OFF frame doesn't seem to "
00500                                       "be in KMOS-format!");
00501 
00502         KMO_TRY_ASSURE(desc1.fits_type == raw_fits,
00503                        CPL_ERROR_ILLEGAL_INPUT,
00504                        "ARC_OFF frame hasn't correct data type "
00505                        "(%s must be a raw, unprocessed file)!",
00506                        cpl_frame_get_filename(frame));
00507 
00508         nx = desc1.naxis1;
00509         ny = desc1.naxis2;
00510         nz = desc1.naxis3;
00511         nr_devices = desc1.nr_ext;
00512 
00513         // assure that flat lamps are off
00514         KMO_TRY_ASSURE((kmo_check_lamp(main_header, INS_LAMP3_ST) == FALSE) &&
00515                        (kmo_check_lamp(main_header, INS_LAMP4_ST) == FALSE),
00516                        CPL_ERROR_ILLEGAL_INPUT,
00517                        "Flat lamps must be switched off (%s)!",
00518                        cpl_frame_get_filename(frame));
00519 
00520         // check if arc lamps are off (or at least blocked by filter wheel)
00521         if ((kmo_check_lamp(main_header, INS_LAMP1_ST) == TRUE) ||
00522             (kmo_check_lamp(main_header, INS_LAMP2_ST) == TRUE))
00523         {
00524             if (!(strcmp(cpl_propertylist_get_string(main_header, "ESO INS FILT1 ID"), "Block") == 0) ||
00525                 !(strcmp(cpl_propertylist_get_string(main_header, "ESO INS FILT3 ID"), "Block") == 0) ||
00526                 !(strcmp(cpl_propertylist_get_string(main_header, "ESO INS FILT3 ID"), "Block") == 0))
00527             {
00528                 cpl_msg_warning("","At least one arc lamp is on! Check if the lamps are blocked!");
00529             }
00530         }
00531         cpl_propertylist_delete(main_header); main_header = NULL;
00532 
00533         // check REF_LINES
00534         if (line_estimate_method == 2) {
00535             KMO_TRY_EXIT_IF_NULL(
00536                 frame = kmo_dfs_get_frame(frameset, REF_LINES));
00537             desc2 = kmo_identify_fits_header(
00538                         cpl_frame_get_filename(frame));
00539             KMO_TRY_CHECK_ERROR_STATE_MSG("REF_LINES frame doesn't seem to "
00540                                           "be in KMOS-format!");
00541 
00542             KMO_TRY_ASSURE(desc2.fits_type == f2l_fits,
00543                            CPL_ERROR_ILLEGAL_INPUT,
00544                            "REF_LINES frame hasn't correct frame type "
00545                            "(%s must be a F2L frame)!",
00546                            cpl_frame_get_filename(frame));
00547             kmo_free_fits_desc(&desc2);
00548         }
00549 
00550         // check ARC_ON
00551         KMO_TRY_EXIT_IF_NULL(
00552             frame = kmo_dfs_get_frame(frameset, ARC_ON));
00553 
00554         while (frame != NULL) {
00555             main_header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0);
00556 
00557             KMO_TRY_ASSURE(cpl_propertylist_get_int(main_header, NDIT) == ndit,
00558                     CPL_ERROR_ILLEGAL_INPUT,
00559                     "NDIT isn't the same for all frames: (is %d and %d).",
00560                     cpl_propertylist_get_int(main_header, NDIT), ndit);
00561 
00562             KMO_TRY_ASSURE(cpl_propertylist_get_double(main_header, EXPTIME) == exptime,
00563                     CPL_ERROR_ILLEGAL_INPUT,
00564                     "EXPTIME isn't the same for all frames: (is %g and %g).",
00565                     cpl_propertylist_get_double(main_header, EXPTIME), exptime);
00566 
00567             KMO_TRY_ASSURE(strcmp(cpl_propertylist_get_string(main_header, READMODE), readmode) == 0,
00568                     CPL_ERROR_ILLEGAL_INPUT,
00569                     "ESO DET READ CURNAME isn't the same for all frames: (is %s and %s).",
00570                     cpl_propertylist_get_string(main_header, READMODE), readmode);
00571 
00572             desc2 = kmo_identify_fits_header(
00573                     cpl_frame_get_filename(frame));
00574             KMO_TRY_CHECK_ERROR_STATE_MSG("ARC_ON frame doesn't seem to "
00575                     "be in KMOS-format!");
00576 
00577             KMO_TRY_ASSURE(desc2.fits_type == raw_fits,
00578                     CPL_ERROR_ILLEGAL_INPUT,
00579                     "ARC_ON frame hasn't correct data type "
00580                     "(%s must be a raw, unprocessed file)!",
00581                     cpl_frame_get_filename(frame));
00582 
00583             KMO_TRY_ASSURE((desc2.naxis1 == nx) &&
00584                     (desc2.naxis2 == ny) &&
00585                     (desc2.naxis3 == nz),
00586                     CPL_ERROR_ILLEGAL_INPUT,
00587                     "ARC_ON frame hasn't correct dimensions! (x,y): (%d,%d) "
00588                     "vs (%d,%d)", desc2.naxis1, desc2.naxis2, nx, ny);
00589 
00590 
00591             // assure that flat lamp is off (LAMP3 and 4)
00592             // and that either arc lamp is on (LAMP1 and 2)
00593             KMO_TRY_ASSURE(((kmo_check_lamp(main_header, INS_LAMP1_ST) == TRUE) ||
00594                     (kmo_check_lamp(main_header, INS_LAMP2_ST) == TRUE)) &&
00595                     (kmo_check_lamp(main_header, INS_LAMP3_ST) == FALSE) &&
00596                     (kmo_check_lamp(main_header, INS_LAMP4_ST) == FALSE),
00597                     CPL_ERROR_ILLEGAL_INPUT,
00598                     "Lamp1 or Lamp2 must be switched on, 3 and 4 must be "
00599                     "off for the ARC_ON frame");
00600 
00601             frame = kmo_dfs_get_frame(frameset, NULL);
00602             KMO_TRY_CHECK_ERROR_STATE();
00603 
00604             kmo_free_fits_desc(&desc2);
00605             kmo_init_fits_desc(&desc2);
00606             cpl_propertylist_delete(main_header); main_header = NULL;
00607         }
00608 
00609         // load first ARC_ON main header
00610         KMO_TRY_EXIT_IF_NULL(
00611             frame = kmo_dfs_get_frame(frameset, ARC_ON));
00612         KMO_TRY_EXIT_IF_NULL(
00613             main_header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0));
00614 
00615         // check FLAT_EDGE
00616         KMO_TRY_EXIT_IF_NULL(
00617             frame = kmo_dfs_get_frame(frameset, FLAT_EDGE));
00618 
00619         desc2 = kmo_identify_fits_header(cpl_frame_get_filename(frame));
00620         KMO_TRY_CHECK_ERROR_STATE();
00621 
00622         KMO_TRY_ASSURE((desc2.nr_ext % 24== 0) &&
00623                        (desc2.fits_type == f2l_fits),
00624                        CPL_ERROR_ILLEGAL_INPUT,
00625                        "FLAT_EDGE isn't in the correct format!!!");
00626 
00627         kmo_free_fits_desc(&desc2);
00628         kmo_init_fits_desc(&desc2);
00629 
00630         //
00631         // ------------ check filter_id, grating_id and rotator offset ---------
00632         // assure that filters, grating and rotation offsets match for
00633         // ARC_ON, XCAL and YCAL
00634         // check if filter_id, grating_id and rotator offset match for all
00635         // detectors
00636         //
00637         KMO_TRY_EXIT_IF_ERROR(
00638             kmo_check_frame_setup(frameset, ARC_ON, XCAL,
00639                                        TRUE, FALSE, FALSE));
00640         KMO_TRY_EXIT_IF_ERROR(
00641             kmo_check_frame_setup(frameset, ARC_ON, YCAL,
00642                                        TRUE, FALSE, FALSE));
00643 
00644         KMO_TRY_EXIT_IF_ERROR(
00645             kmo_check_frame_setup_md5_xycal(frameset));
00646 
00647         strcpy(filename_lcal, LCAL);
00648         strcpy(filename_det_img, DET_IMG_WAVE);
00649 
00650         KMO_TRY_EXIT_IF_NULL(
00651             frame = kmo_dfs_get_frame(frameset, XCAL));
00652         KMO_TRY_EXIT_IF_NULL(
00653             suffix = kmo_dfs_get_suffix(frame, TRUE, FALSE));
00654 
00655         cpl_msg_info("", "Detected instrument setup:   %s", suffix+1);
00656         cpl_msg_info("", "(grating 1, 2 & 3)");
00657 
00658         // setup lamp config
00659         if ((kmo_check_lamp(main_header, INS_LAMP1_ST) == TRUE) &&
00660             (kmo_check_lamp(main_header, INS_LAMP2_ST) == FALSE))
00661         {
00662             lamp_config = ARGON;
00663             strcpy(tmpstr, "Argon");
00664         } else if ((kmo_check_lamp(main_header, INS_LAMP1_ST) == FALSE) &&
00665                    (kmo_check_lamp(main_header, INS_LAMP2_ST) == TRUE))
00666         {
00667            lamp_config = NEON;
00668            strcpy(tmpstr, "Neon");
00669         } else if ((kmo_check_lamp(main_header, INS_LAMP1_ST) == TRUE) &&
00670                    (kmo_check_lamp(main_header, INS_LAMP2_ST) == TRUE))
00671         {
00672            lamp_config = ARGON_NEON;
00673            strcpy(tmpstr, "Argon + Neon");
00674         }
00675 
00676         cpl_msg_info("", "Detected arc lamp configuration: %s", tmpstr);
00677 
00678         //assert that filter and grating match for each detector
00679         // filter/grating can be different for each detector
00680         KMO_TRY_EXIT_IF_NULL(
00681             filter_ids =  kmo_get_filter_setup(main_header, nr_devices, TRUE));
00682 
00683         //
00684         // ---- scan for rotator angles
00685         //
00686 #define ANGLE_DIM 360
00687        int rotang_found[ANGLE_DIM];
00688        int rotang_cnt[ANGLE_DIM];
00689         for (int i=0; i<ANGLE_DIM; i++) {
00690             rotang_found[i] = 0;
00691             rotang_cnt[i] = 0;
00692         }
00693         KMO_TRY_EXIT_IF_NULL(
00694             frame = kmo_dfs_get_frame(frameset, ARC_ON));
00695         while (frame != NULL) {
00696             header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0);
00697             if (cpl_propertylist_has(header, ROTANGLE)) {
00698                 int rot_angle = (int) rint(cpl_propertylist_get_double(header, ROTANGLE));
00699                 if (rot_angle < 0) {
00700                     rot_angle += 360;
00701                 }
00702                 if (rot_angle < 360 && rot_angle >= 0) {
00703                     rotang_cnt[rot_angle]++;
00704 //                    char * tag = cpl_sprintf("FLAT_ON_%3.3d",rot_angle);
00705 //                    KMO_TRY_EXIT_IF_ERROR(
00706 //                            cpl_frame_set_tag(frame, tag));
00707 //                    cpl_free(tag);
00708                 }
00709             } else {
00710                 cpl_msg_warning("","File %s has no keyword \"ROTANGLE\"",
00711                         cpl_frame_get_filename(frame));
00712             }
00713 
00714             cpl_propertylist_delete(header); header = NULL;
00715             frame = kmo_dfs_get_frame(frameset, NULL);
00716             KMO_TRY_CHECK_ERROR_STATE();
00717         }
00718         for (int ax=0; ax<ANGLE_DIM; ax++) {
00719             if (rotang_cnt[ax] != 0) {
00720                 if (rotang_cnt[ax] == 1 ) {
00721                     cpl_msg_info("","Found %d frame with rotator angle %d",rotang_cnt[ax],ax);
00722                 } else {
00723                     cpl_msg_warning("","Found %d frames with rotator angle %d but only one will be used",
00724                                     rotang_cnt[ax],ax);
00725                 }
00726                 rotang_found[nr_angles] = ax;
00727                 nr_angles++;
00728             }
00729         }
00730 
00731         KMO_TRY_EXIT_IF_NULL (
00732             angle_frameset = (cpl_frameset **) cpl_malloc(nr_angles * sizeof(cpl_frameset*)));
00733         for (int i =0; i<nr_angles; i++) {
00734             angle_frameset[i] = cpl_frameset_new();
00735         }
00736 
00737         KMO_TRY_EXIT_IF_NULL(
00738             frame = kmo_dfs_get_frame(frameset, ARC_ON));
00739         while (frame != NULL) {
00740             header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0);
00741             if (cpl_propertylist_has(header, ROTANGLE)) {
00742                 int rot_angle = (int) rint(cpl_propertylist_get_double(header, ROTANGLE));
00743                 if (rot_angle < 0) {
00744                     rot_angle += 360;
00745                 }
00746                 int ix = -1;
00747                 for (ix = 0; ix<nr_angles; ix++) {
00748                     if (rotang_found[ix] == rot_angle) {
00749                         break;
00750                     }
00751                 }
00752                 if (ix<nr_angles) {
00753                     KMO_TRY_EXIT_IF_ERROR(
00754                         cpl_frameset_insert(angle_frameset[ix], cpl_frame_duplicate(frame)));
00755                 }
00756             }
00757 
00758             cpl_propertylist_delete(header); header = NULL;
00759             frame = kmo_dfs_get_frame(frameset, NULL);
00760             KMO_TRY_CHECK_ERROR_STATE();
00761         }
00762 
00763 // #############################################################################
00764 // ###           allocate temporary memory
00765 // #############################################################################
00766         // allocate here a edge table structure for all detectors
00767         KMO_TRY_EXIT_IF_NULL(
00768             edge_table = (cpl_table***)cpl_calloc(nr_devices,
00769                                                   sizeof(cpl_table**)));
00770         for (int i = 0; i < nr_devices; i++) {
00771             KMO_TRY_EXIT_IF_NULL(
00772                 edge_table[i] = (cpl_table**)cpl_calloc(KMOS_IFUS_PER_DETECTOR,
00773                                                         sizeof(cpl_table*)));
00774         }
00775 
00776         // the frames have to be stored temporarily because the QC parameters
00777         // for the main header are calculated from each detector. So they can be
00778         // stored only when all detectors are processed
00779         KMO_TRY_EXIT_IF_NULL(
00780             stored_lcal = (cpl_image**)cpl_calloc(nr_devices * nr_angles,
00781                                                     sizeof(cpl_image*)));
00782         KMO_TRY_EXIT_IF_NULL(
00783             stored_det_img = (cpl_image**)cpl_calloc(nr_devices * nr_angles,
00784                                                     sizeof(cpl_image*)));
00785         KMO_TRY_EXIT_IF_NULL(
00786             stored_sub_headers_lcal = (cpl_propertylist**)cpl_calloc(nr_devices * nr_angles,
00787                                                     sizeof(cpl_propertylist*)));
00788         KMO_TRY_EXIT_IF_NULL(
00789             stored_sub_headers_det_img = (cpl_propertylist**)cpl_calloc(nr_devices * nr_angles,
00790                                                     sizeof(cpl_propertylist*)));
00791         KMO_TRY_EXIT_IF_NULL(
00792             stored_qc_arc_sat =
00793                             (int*)cpl_calloc(nr_devices, nr_angles * sizeof(int)));
00794         KMO_TRY_EXIT_IF_NULL(
00795             stored_qc_ar_eff =
00796                             (double*)cpl_calloc(nr_devices, nr_angles * sizeof(double)));
00797         KMO_TRY_EXIT_IF_NULL(
00798             stored_qc_ne_eff =
00799                             (double*)cpl_calloc(nr_devices, nr_angles * sizeof(double)));
00800 
00801 // #############################################################################
00802 // ###           process data
00803 // #############################################################################
00804         // load arclines and reference lines
00805         KMO_TRY_EXIT_IF_NULL(
00806             frame = kmo_dfs_get_frame(frameset, ARC_LIST));
00807 
00808         // check if ARC_LIST is the filter_id-one
00809         KMO_TRY_EXIT_IF_NULL(
00810             tmp_header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0));
00811         KMO_TRY_EXIT_IF_NULL(
00812             tmp_str = cpl_propertylist_get_string(tmp_header, FILT_ID));
00813         KMO_TRY_ASSURE(strcmp(filter_ids[0], tmp_str) == 0,
00814                        CPL_ERROR_ILLEGAL_INPUT,
00815                        "ARC_LIST model must have primary "
00816                        "keyword '%s' equal '%s'!!!", FILT_ID, filter_ids[0]);
00817         cpl_propertylist_delete(tmp_header); tmp_header = NULL;
00818 
00819         desc2 = kmo_identify_fits_header(cpl_frame_get_filename(frame));
00820         KMO_TRY_CHECK_ERROR_STATE();
00821 
00822         KMO_TRY_ASSURE((desc2.nr_ext == 1) &&
00823                        (desc2.fits_type == f2l_fits),
00824                        CPL_ERROR_ILLEGAL_INPUT,
00825                        "ARC_LIST isn't in the correct format!!!");
00826         kmo_free_fits_desc(&desc2);
00827         kmo_init_fits_desc(&desc2);
00828 
00829         KMO_TRY_EXIT_IF_NULL(
00830             arclines = kmo_dfs_load_table(frameset, ARC_LIST, 1, 0));
00831 
00832         KMO_TRY_EXIT_IF_NULL(
00833             lines = kmo_get_lines(arclines, lamp_config));
00834 
00835         cpl_msg_info("", "Nr. of lines in arclist for this configuration: %lld",
00836                      cpl_bivector_get_size(lines));
00837 
00838         if (line_estimate_method == 2) {
00839             KMO_TRY_EXIT_IF_NULL(
00840                 reflines = kmo_dfs_load_table(frameset, REF_LINES, 1, 0));
00841         }
00842 
00843         // check which IFUs are active for all FLAT frames
00844         KMO_TRY_EXIT_IF_NULL(
00845             unused_ifus_before = kmo_get_unused_ifus(frameset, 0, 0));
00846 
00847         KMO_TRY_EXIT_IF_NULL(
00848             unused_ifus_after = kmo_duplicate_unused_ifus(unused_ifus_before));
00849 
00850         kmo_print_unused_ifus(unused_ifus_before, FALSE);
00851 
00852         // make sure no reconstruction lookup table (LUT) is used
00853         if (getenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE") != NULL) {
00854             last_env = getenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE");
00855         }
00856         setenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE","NONE",1);
00857 
00858         //
00859         // ------------ loop all rotator angles and detectors ------------
00860         //
00861         for (int a = 0; a<nr_angles; a++) {
00862             cpl_msg_info("","Processing rotator angle %d -> %d degree", a,rotang_found[a]);
00863 
00864             for (int i = 1; i <= nr_devices; i++) {
00865                 // use loop below for line identification
00866                 cpl_msg_info("","Processing detector No. %d", i);
00867 
00868                 int sx = a * nr_devices + (i - 1);
00869 
00870                 // load edge parameters
00871                 KMO_TRY_EXIT_IF_NULL(
00872                     frame = kmo_dfs_get_frame(frameset, FLAT_EDGE));
00873 
00874                 for (int j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
00875                     edge_table[i-1][j] = kmclipm_cal_table_load(cpl_frame_get_filename(frame),
00876                                                                 (i-1) * KMOS_IFUS_PER_DETECTOR + j + 1,
00877                                                                 rotang_found[a], 0, &angle_found);
00878                     if (cpl_error_get_code() == CPL_ERROR_ILLEGAL_INPUT) {
00879                         // IFU is inactive: proceed
00880                         cpl_error_reset();
00881                     }
00882                 }
00883 
00884                 if (fit_order_par == 0) {
00885                     // set default fit orders for the different bands
00886                     if ((strcmp(filter_ids[i-1], "H") == 0) ||
00887                         (strcmp(filter_ids[i-1], "K") == 0) ||
00888                         (strcmp(filter_ids[i-1], "YJ") == 0))
00889                     {
00890                         fit_order = 6;
00891                     } else if (strcmp(filter_ids[i-1], "IZ") == 0) {
00892                         fit_order = 4;
00893                     } else if (strcmp(filter_ids[i-1], "HK") == 0) {
00894                         fit_order = 5;
00895                     }
00896                     cpl_msg_info("", "Order of wavelength spectrum fit for %s-band: %d",
00897                                  filter_ids[i-1],
00898                                  fit_order);
00899                 } else {
00900                     fit_order = fit_order_par;
00901                 }
00902 
00903                 // lamp_on
00904                 KMO_TRY_EXIT_IF_NULL(
00905                     frame = kmo_dfs_get_frame(angle_frameset[a], ARC_ON));
00906 
00907                 // if sat_mode is set to TRUE here, then the calculation of the
00908                 // saturated pixels has to be updated like in kmo_flat-recipe
00909                 KMO_TRY_EXIT_IF_NULL(
00910                     det_lamp_on = kmo_dfs_load_image_frame(frame, i, FALSE, TRUE, &nr_sat));
00911 
00912                 // count saturated pixels for each detector
00913                 if (strcmp(cpl_propertylist_get_string(main_header, READMODE), "Nondest") == 0) {
00914                     // NDR: non-destructive readout mode
00915                     stored_qc_arc_sat[sx] = nr_sat;
00916                 } else {
00917                     // normal readout mode
00918                     stored_qc_arc_sat[sx] = kmo_image_get_saturated(det_lamp_on,
00919                                                                     KMO_FLAT_SATURATED);
00920                 }
00921                 KMO_TRY_CHECK_ERROR_STATE();
00922 
00923 
00924                 KMO_TRY_EXIT_IF_NULL(
00925                     det_lamp_on_copy = cpl_image_duplicate(det_lamp_on));
00926 
00927                 // lamp_off
00928                 KMO_TRY_EXIT_IF_NULL(
00929                     frame = kmo_dfs_get_frame(frameset, ARC_OFF));
00930 
00931                 KMO_TRY_EXIT_IF_NULL(
00932                     det_lamp_off = kmo_dfs_load_image_frame(frame, i, FALSE, FALSE, NULL));
00933 
00934                 //
00935                 // process imagelist
00936                 //
00937                 KMO_TRY_CHECK_ERROR_STATE();
00938 
00939                 // subtract lamp_off from lamp_on
00940                 KMO_TRY_EXIT_IF_ERROR(
00941                     cpl_image_subtract(det_lamp_on, det_lamp_off));
00942 
00943                 // load flat calibration frames
00944                 KMO_TRY_EXIT_IF_NULL(
00945                     xcal = kmo_dfs_load_cal_image(frameset, XCAL, i, 0,
00946                                                   (double) rotang_found[a], FALSE,
00947                                                   NULL, &angle_found));
00948 
00949                 KMO_TRY_EXIT_IF_NULL(
00950                     ycal = kmo_dfs_load_cal_image(frameset, YCAL, i, 0,
00951                                                   (double) rotang_found[a], FALSE,
00952                                                   NULL,  &angle_found));
00953 
00954                 // load bad pixel mask from XCAL and set NaNs to 0 and all other values to 1
00955                 KMO_TRY_EXIT_IF_NULL(
00956                     bad_pix_mask = cpl_image_duplicate(xcal));
00957 
00958                 KMO_TRY_EXIT_IF_NULL(
00959                     pbad_pix_mask = cpl_image_get_data_float(bad_pix_mask));
00960                 for (int x = 0; x < nx; x++) {
00961                     for (int y = 0; y < ny; y++) {
00962                         if (isnan(pbad_pix_mask[x+nx*y])) {
00963                             pbad_pix_mask[x+nx*y] = 0.;
00964                         } else {
00965                             pbad_pix_mask[x+nx*y] = 1.;
00966                         }
00967                     }
00968                 }
00969 
00970                 // calculate spectral curvature here
00971                 err = kmo_calc_wave_calib(det_lamp_on,
00972                                           bad_pix_mask,
00973                                           xcal,
00974                                           ycal,
00975                                           filter_ids[i-1],
00976                                           lamp_config,
00977                                           i,
00978                                           unused_ifus_after[i-1],
00979                                           edge_table[i-1],
00980                                           lines,
00981                                           reflines,
00982                                           disp,
00983                                           &lcal,
00984                                           &(stored_qc_ar_eff[sx]),
00985                                           &(stored_qc_ne_eff[sx]),
00986                                           flip_trace,
00987                                           fit_order,
00988                                           line_estimate_method);
00989 
00990                 if (err == CPL_ERROR_NONE) {
00991                     // calculate QC parameters
00992                     if (stored_qc_ar_eff[sx] != -1.0) {
00993                         stored_qc_ar_eff[sx] /= exptime;
00994                     }
00995                     if (stored_qc_ne_eff[sx] != -1.0) {
00996                         stored_qc_ne_eff[sx] /= exptime;
00997                     }
00998 
00999                     // apply the badpixel mask to the produced frame
01000 
01001                     KMO_TRY_EXIT_IF_ERROR(
01002                         cpl_image_multiply(lcal, bad_pix_mask));
01003 
01004                     KMO_TRY_EXIT_IF_ERROR(
01005                         kmo_image_reject_from_mask(lcal, bad_pix_mask));
01006 
01007                     // store flat frames, badpixel mask and calibration frames
01008                     stored_lcal[sx] = lcal;
01009                 } else if (err == CPL_ERROR_UNSPECIFIED) {
01010                     // all IFUs seem to be deativated, continue processing
01011                     // just save empty frame
01012                     cpl_error_reset();
01013                     stored_lcal[sx] = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
01014                     kmo_image_fill(stored_lcal[sx], 0.0);
01015                 } else {
01016                     cpl_msg_warning(cpl_func,
01017                                     "Couldn't identify any lines! Is the line "
01018                                     "list defined correctly?");
01019                     cpl_msg_warning(cpl_func,
01020                                     "Band defined in header of detector %d: %s",
01021                                     i, filter_ids[i-1]);
01022                     cpl_msg_warning(cpl_func,
01023                                     "Arc line file defined: %s",
01024                                     cpl_frame_get_filename(
01025                                           kmo_dfs_get_frame(frameset, ARC_LIST)));
01026                     cpl_error_reset();
01027                 }
01028 
01029 
01030                 //
01031                 // create reconstructed and resampled arc frame
01032                 //
01033                 stored_det_img[sx] = kmo_reconstructed_arc_image(frameset,
01034                                                                  det_lamp_on_copy,
01035                                                                  det_lamp_off,
01036                                                                  xcal,
01037                                                                  ycal,
01038                                                                  stored_lcal[sx],
01039                                                                  unused_ifus_after[i-1],
01040                                                                  flip_trace,
01041                                                                  i,
01042                                                                  suffix,
01043                                                                  filter_ids[i-1],
01044                                                                  lamp_config,
01045                                                                  rotang_found[a],
01046                                                                  &qc_header);
01047                 if (cpl_error_get_code() != CPL_ERROR_NONE) {
01048                     // couldn't reconstruct
01049                     cpl_msg_error("","Couldn't reconstruct IFUs on detector %d", i);
01050                     cpl_error_reset();
01051                 }
01052 
01053                 // load and update sub_header with QC parameters
01054                 KMO_TRY_EXIT_IF_NULL(
01055                     stored_sub_headers_lcal[sx] = kmo_dfs_load_sub_header(frameset,
01056                                                                           ARC_ON, i,
01057                                                                           FALSE));
01058 
01059                 // update EXTNAME
01060                 KMO_TRY_EXIT_IF_NULL(
01061                     extname = kmo_extname_creator(detector_frame, i, EXT_DATA));
01062                 KMO_TRY_EXIT_IF_ERROR(
01063                     kmclipm_update_property_string(stored_sub_headers_lcal[sx],
01064                                                    EXTNAME,
01065                                                    extname,
01066                                                    "FITS extension name"));
01067                 cpl_free(extname); extname = NULL;
01068 
01069                 // add first QC parameters
01070                 KMO_TRY_EXIT_IF_ERROR(
01071                     kmclipm_update_property_int(stored_sub_headers_lcal[sx],
01072                                                 QC_ARC_SAT,
01073                                                 stored_qc_arc_sat[sx],
01074                                                 "[] nr. saturated pixels of arc exp."));
01075 
01076                 gain = kmo_dfs_get_property_double(stored_sub_headers_lcal[sx], GAIN);
01077                 KMO_TRY_CHECK_ERROR_STATE_MSG(
01078                     "GAIN-keyword in fits-header is missing!");
01079 
01080                 if (stored_qc_ar_eff[sx] != -1.0) {
01081                     KMO_TRY_EXIT_IF_ERROR(
01082                         kmclipm_update_property_double(stored_sub_headers_lcal[sx],
01083                                                        QC_ARC_AR_EFF,
01084                                                        stored_qc_ar_eff[sx]/gain,
01085                                                        "[e-/s] Argon lamp efficiency"));
01086                 }
01087 
01088                 if (stored_qc_ne_eff[sx] != -1.0) {
01089                     KMO_TRY_EXIT_IF_ERROR(
01090                         kmclipm_update_property_double(stored_sub_headers_lcal[sx],
01091                                                        QC_ARC_NE_EFF,
01092                                                        stored_qc_ne_eff[sx]/gain,
01093                                                        "[e-/s] Neon lamp efficiency"));
01094                 }
01095 
01096                 KMO_TRY_EXIT_IF_ERROR(
01097                         kmclipm_update_property_double(stored_sub_headers_lcal[sx],
01098                                                        CAL_ROTANGLE,
01099                                                        ((double) rotang_found[a]),
01100                                                        "[deg] Rotator relative to nasmyth"));
01101 
01102                 // append QC parameters
01103                 KMO_TRY_EXIT_IF_ERROR(
01104                     cpl_propertylist_append(stored_sub_headers_lcal[sx],
01105                                             qc_header));
01106                 cpl_propertylist_delete(qc_header); qc_header = NULL;
01107 
01108                 KMO_TRY_EXIT_IF_NULL(
01109                     stored_sub_headers_det_img[sx] = cpl_propertylist_duplicate(
01110                                                      stored_sub_headers_lcal[sx]));
01111 
01112                 cpl_propertylist_erase(stored_sub_headers_lcal[sx], CRVAL1);
01113                 cpl_propertylist_erase(stored_sub_headers_lcal[sx], CRVAL2);
01114                 cpl_propertylist_erase(stored_sub_headers_lcal[sx], CTYPE1);
01115                 cpl_propertylist_erase(stored_sub_headers_lcal[sx], CTYPE2);
01116                 cpl_propertylist_erase(stored_sub_headers_lcal[sx], CDELT1);
01117                 cpl_propertylist_erase(stored_sub_headers_lcal[sx], CDELT2);
01118 
01119                 cpl_propertylist_erase(stored_sub_headers_lcal[sx], CRPIX1);
01120                 cpl_propertylist_erase(stored_sub_headers_lcal[sx], CRPIX2);
01121                 if (cpl_propertylist_has(stored_sub_headers_lcal[sx], CRPIX1)) {
01122                     cpl_propertylist_erase(stored_sub_headers_lcal[sx], CRPIX1);
01123                 }
01124                 if (cpl_propertylist_has(stored_sub_headers_lcal[sx], CRPIX2)) {
01125                     cpl_propertylist_erase(stored_sub_headers_lcal[sx], CRPIX2);
01126                 }
01127 
01128                 // free memory
01129                 cpl_image_delete(det_lamp_on); det_lamp_on = NULL;
01130                 cpl_image_delete(det_lamp_on_copy); det_lamp_on_copy = NULL;
01131                 cpl_image_delete(det_lamp_off); det_lamp_off = NULL;
01132                 cpl_image_delete(bad_pix_mask); bad_pix_mask = NULL;
01133                 cpl_image_delete(xcal); xcal = NULL;
01134                 cpl_image_delete(ycal); ycal = NULL;
01135 
01136                 for (int j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01137                     cpl_table_delete(edge_table[i-1][j]); edge_table[i-1][j] = NULL;
01138                 }
01139             } // for i devices
01140         } // for a angles
01141 
01142         if (line_estimate_method == 2) {
01143             cpl_table_delete(reflines); reflines = NULL;
01144         }
01145 // ###########################################################################
01146 // ###           QC parameters & saving
01147 // ###########################################################################
01148         cpl_msg_info("","Saving data...");
01149 
01150         //
01151         // ------------ load, update & save primary header ------------
01152         //
01153 
01154         // update which IFUs are not used
01155         KMO_TRY_EXIT_IF_ERROR(
01156             kmo_set_unused_ifus(unused_ifus_after, main_header,
01157                                 "kmo_wave_cal"));
01158 
01159         KMO_TRY_EXIT_IF_NULL(
01160             frame = kmo_dfs_get_frame(frameset, ARC_ON));
01161 
01162         KMO_TRY_EXIT_IF_ERROR(
01163             kmo_dfs_save_main_header(frameset, filename_lcal, suffix, frame,
01164                                      main_header, parlist, cpl_func));
01165 
01166         KMO_TRY_EXIT_IF_ERROR(
01167             kmo_dfs_save_main_header(frameset, filename_det_img, suffix, frame,
01168                                      main_header, parlist, cpl_func));
01169 
01170         cpl_propertylist_delete(main_header); main_header = NULL;
01171 
01172         //
01173         // --- save sub-frames ---
01174         //
01175         for (int a = 0; a<nr_angles; a++) {
01176             for (int i = 1; i <= nr_devices; i++) {
01177                 int sx = a * nr_devices + (i - 1);
01178                 // save lcal-frame
01179                 KMO_TRY_EXIT_IF_ERROR(
01180                     kmo_dfs_save_image(stored_lcal[sx], filename_lcal, suffix,
01181                                        stored_sub_headers_lcal[sx], 0./0.));
01182 
01183                 // save detector image
01184                 KMO_TRY_EXIT_IF_ERROR(
01185                     kmo_dfs_save_image(stored_det_img[sx], filename_det_img, suffix,
01186                                        stored_sub_headers_det_img[sx], 0./0.));
01187             } // for i = nr_devices
01188         } // for a angles
01189 
01190 // Alex: was ist das alles?
01191 //        KMO_TRY_EXIT_IF_NULL(
01192 //                sub_header = cpl_propertylist_new());
01193 //
01194 //        KMO_TRY_EXIT_IF_NULL(
01195 //            extname = kmo_extname_creator(list_frame, -1, EXT_LIST));
01196 //        KMO_TRY_EXIT_IF_ERROR(
01197 //            kmclipm_update_property_string(sub_header,
01198 //                                    EXTNAME,
01199 //                                    extname,
01200 //                                    "FITS extension name"));
01201 //        cpl_free(extname); extname = NULL;
01202 //
01203 //        // save table with wavelengths and
01204 //        KMO_TRY_EXIT_IF_ERROR(
01205 //                kmo_dfs_save_table(arclines, filename_img, sub_header));
01206 
01207         // print which IFUs are not used
01208         kmo_print_unused_ifus(unused_ifus_after, TRUE);
01209     }
01210     KMO_CATCH
01211     {
01212         KMO_CATCH_MSG();
01213         ret_val = -1;
01214     }
01215 
01216     if (last_env != NULL) {
01217         setenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE",last_env,1);
01218     } else {
01219         unsetenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE");
01220     }
01221 
01222     kmo_free_fits_desc(&desc1);
01223     kmo_free_fits_desc(&desc2);
01224     if (unused_ifus_before != NULL) {
01225        kmo_free_unused_ifus(unused_ifus_before); unused_ifus_before = NULL;
01226     }
01227     if (unused_ifus_after != NULL) {
01228        kmo_free_unused_ifus(unused_ifus_after); unused_ifus_after = NULL;
01229     }
01230     cpl_propertylist_delete(main_header); main_header = NULL;
01231     cpl_image_delete(det_lamp_on); det_lamp_on = NULL;
01232     cpl_image_delete(det_lamp_off); det_lamp_off = NULL;
01233     cpl_image_delete(bad_pix_mask); bad_pix_mask = NULL;
01234     cpl_table_delete(arclines); arclines = NULL;
01235     cpl_table_delete(reflines); reflines = NULL;
01236     cpl_free(stored_qc_arc_sat); stored_qc_arc_sat = NULL;
01237     cpl_free(stored_qc_ar_eff); stored_qc_ar_eff = NULL;
01238     cpl_free(stored_qc_ne_eff); stored_qc_ne_eff = NULL;
01239     for (int i = 0; i < nr_devices * nr_angles; i++) {
01240         cpl_image_delete(stored_lcal[i]); stored_lcal[i] = NULL;
01241         cpl_image_delete(stored_det_img[i]); stored_det_img[i] = NULL;
01242         cpl_propertylist_delete(stored_sub_headers_lcal[i]);
01243             stored_sub_headers_lcal[i] = NULL;
01244         cpl_propertylist_delete(stored_sub_headers_det_img[i]);
01245             stored_sub_headers_det_img[i] = NULL;
01246     }
01247     for (int i = 0; i < nr_angles; i++) {
01248         cpl_frameset_delete(angle_frameset[i]); angle_frameset[i] = NULL;
01249     }
01250     for (int i = 0; i < nr_devices; i++) {
01251         cpl_free(filter_ids[i]); filter_ids[i] = NULL;
01252         cpl_free(edge_table[i]); edge_table[i] = NULL;
01253     }
01254     cpl_free(angle_frameset); angle_frameset = NULL;
01255     cpl_free(stored_lcal); stored_lcal = NULL;
01256     cpl_free(stored_det_img); stored_det_img = NULL;
01257     cpl_free(stored_sub_headers_lcal); stored_sub_headers_lcal = NULL;
01258     cpl_free(stored_sub_headers_det_img); stored_sub_headers_det_img = NULL;
01259     cpl_free(filter_ids); filter_ids = NULL;
01260     cpl_free(edge_table); edge_table = NULL;
01261     cpl_free(readmode); readmode = NULL;
01262     cpl_bivector_delete(lines); lines = NULL;
01263     cpl_free(suffix); suffix = NULL;
01264 
01265     return ret_val;
01266 }
01267