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