KMOS Pipeline Reference Manual  1.3.1
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 position "
00170             "(i.e. wavelength) of each pixel on the detector.",
00171             kmo_wave_cal_description,
00172             "Alex Agudo Berbel",
00173             "usd-help@eso.org",
00174             kmos_get_license(),
00175             kmo_wave_cal_create,
00176             kmo_wave_cal_exec,
00177             kmo_wave_cal_destroy);
00178     cpl_pluginlist_append(list, plugin);
00179 
00180     return 0;
00181 }
00182 
00183 /*----------------------------------------------------------------------------*/
00191 /*----------------------------------------------------------------------------*/
00192 static int kmo_wave_cal_create(cpl_plugin *plugin)
00193 {
00194     cpl_recipe *recipe;
00195     cpl_parameter *p;
00196 
00197     // Check that the plugin is part of a valid recipe
00198     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00199         recipe = (cpl_recipe *)plugin;
00200     else
00201         return -1;
00202 
00203     // Create the parameters list in the cpl_recipe object
00204     recipe->parameters = cpl_parameterlist_new();
00205 
00206     // Fill the parameters list
00207     p = cpl_parameter_new_value("kmos.kmo_wave_cal.order", CPL_TYPE_INT,
00208             "The fitting polynomial order used for the wavelength solution. "
00209             "By default, 4 for IZ band, 5 for HK, 6 for the others",
00210             "kmos.kmo_wave_cal", 0);
00211     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "order");
00212     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00213     cpl_parameterlist_append(recipe->parameters, p);
00214 
00215     /* --suppress_extension */
00216     p = cpl_parameter_new_value("kmos.kmo_wave_cal.suppress_extension",
00217             CPL_TYPE_BOOL, "Suppress arbitrary filename extension",
00218             "kmos.kmo_wave_cal", FALSE);
00219     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "suppress_extension");
00220     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00221     cpl_parameterlist_append(recipe->parameters, p);
00222 
00223     // add parameters for band-definition
00224     kmo_band_pars_create(recipe->parameters, "kmos.kmo_wave_cal");
00225 
00226     return 0;
00227 }
00228 
00229 /*----------------------------------------------------------------------------*/
00235 /*----------------------------------------------------------------------------*/
00236 static int kmo_wave_cal_exec(cpl_plugin *plugin)
00237 {
00238     cpl_recipe  *recipe;
00239 
00240     // Get the recipe out of the plugin
00241     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00242         recipe = (cpl_recipe *)plugin;
00243     else return -1;
00244 
00245     return kmo_wave_cal(recipe->parameters, recipe->frames);
00246 }
00247 
00248 /*----------------------------------------------------------------------------*/
00254 /*----------------------------------------------------------------------------*/
00255 static int kmo_wave_cal_destroy(cpl_plugin *plugin)
00256 {
00257     cpl_recipe *recipe;
00258 
00259     // Get the recipe out of the plugin
00260     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00261         recipe = (cpl_recipe *)plugin;
00262     else return -1 ;
00263 
00264     cpl_parameterlist_delete(recipe->parameters);
00265     return 0 ;
00266 }
00267 
00268 /*----------------------------------------------------------------------------*/
00283 /*----------------------------------------------------------------------------*/
00284 static int kmo_wave_cal(cpl_parameterlist *parlist, cpl_frameset *frameset)
00285 {
00286     cpl_image        *det_lamp_on                   = NULL,
00287                      *det_lamp_on_copy              = NULL,
00288                      *det_lamp_off                  = NULL,
00289                      *bad_pix_mask                  = NULL,
00290                      *xcal                          = NULL,
00291                      *ycal                          = NULL,
00292                      *lcal                          = NULL;
00293 
00294     cpl_image        **stored_lcal                  = NULL,
00295                      **stored_det_img               = NULL;
00296 
00297     cpl_frame        *frame                         = NULL;
00298     cpl_frameset     ** angle_frameset     = NULL;
00299 
00300     int              ret_val                        = 0,
00301                      nr_devices                     = 0,
00302                      nr_angles                      = 0,
00303                      nx                             = 0,
00304                      ny                             = 0,
00305                      nz                             = 0,
00306                      *stored_qc_arc_sat             = NULL,
00307                      fit_order_par                  = 0,
00308                      fit_order                      = 0,
00309                      ndit                           = 0,
00310                      suppress_extension             = FALSE,
00311                      line_estimate_method           = 2,
00312                      nr_sat                         = 0,
00313                      i = 0, j = 0, a = 0, ax = 0, x = 0, y = 0;
00314     float            *pbad_pix_mask                 = NULL;
00315     double           exptime                        = 0.0,
00316                      gain                           = 0.0,
00317                      *stored_qc_ar_eff              = NULL,
00318                      *stored_qc_ne_eff              = NULL,
00319                      angle_found                    = 0.0;
00320 
00321     cpl_table        *arclines                      = NULL,
00322                      *reflines                      = NULL,
00323                      ***edge_table                  = NULL;
00324 
00325     cpl_propertylist *main_header                   = NULL,
00326                      **stored_sub_headers_lcal      = NULL,
00327                      **stored_sub_headers_det_img   = NULL,
00328                      *qc_header                     = NULL,
00329                      *tmp_header                    = NULL,
00330                      *header                        = NULL;
00331 
00332     cpl_array        **unused_ifus_before           = NULL,
00333                      **unused_ifus_after            = NULL;
00334 
00335     cpl_bivector     *lines                         = NULL;
00336 
00337     main_fits_desc   desc1,
00338                      desc2;
00339 
00340     char             *extname                       = NULL,
00341                      filename_lcal[256],
00342                      filename_det_img[256],
00343                      *suffix                        = NULL,
00344                      *fn_suffix                     = NULL,
00345                      tmpstr[256],
00346                      **filter_ids                   = NULL,
00347                      *readmode                      = NULL,
00348                      *str_line_estimate_method      = NULL,
00349                      *last_env                      = NULL;
00350 
00351     const char       *tmp_str                       = NULL;
00352 
00353     cpl_error_code   err                            = CPL_ERROR_NONE;
00354 
00355     enum lampConfiguration lamp_config;
00356 
00357     KMO_TRY
00358     {
00359         kmo_init_fits_desc(&desc1);
00360         kmo_init_fits_desc(&desc2);
00361 
00362         str_line_estimate_method = getenv("KMO_WAVE_LINE_ESTIMATE");
00363         if (str_line_estimate_method != NULL) {
00364             line_estimate_method = atoi(str_line_estimate_method);
00365         }
00366         cpl_msg_debug(__func__, "Line estimation method: %d\n",
00367                 line_estimate_method);
00368 
00369         /* Check inputs */
00370         KMO_TRY_ASSURE((parlist != NULL) && (frameset != NULL),
00371                 CPL_ERROR_NULL_INPUT, "Not all input data is provided!");
00372 
00373         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, ARC_ON) >= 1,
00374                 CPL_ERROR_NULL_INPUT, "At least 1 ARC_ON frame is required!");
00375 
00376         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, ARC_OFF) >= 1,
00377                 CPL_ERROR_NULL_INPUT, "Exactly 1 ARC_OFF frame is required!");
00378 
00379         if (cpl_frameset_count_tags(frameset, ARC_OFF) > 1) {
00380             cpl_msg_warning(__func__, 
00381                     "Only 1 ARC_OFF frame required. Frame #1 will be used");
00382         }
00383 
00384         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, XCAL) == 1,
00385                 CPL_ERROR_FILE_NOT_FOUND, "Exactly 1 XCAL frame is required!");
00386 
00387         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, YCAL) == 1,
00388                 CPL_ERROR_FILE_NOT_FOUND, "Exactly 1 YCAL frame is required!");
00389 
00390         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, ARC_LIST) == 1,
00391                 CPL_ERROR_FILE_NOT_FOUND, "Exactly 1 ARC_LIST is required!");
00392 
00393         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, FLAT_EDGE) == 1,
00394                 CPL_ERROR_FILE_NOT_FOUND, "Exactly 1 FLAT_EDGE is required!");
00395 
00396         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, WAVE_BAND) == 1,
00397                 CPL_ERROR_FILE_NOT_FOUND, "Exactly 1 WAVE_BAND is required!");
00398 
00399         if (line_estimate_method == 2) {
00400             KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, REF_LINES) == 1,
00401                     CPL_ERROR_FILE_NOT_FOUND, "Exactly 1 REF_LINES required!");
00402         }
00403         
00404         KMO_TRY_ASSURE(kmo_dfs_set_groups(frameset, "kmo_wave_cal") == 1,
00405                 CPL_ERROR_ILLEGAL_INPUT, 
00406                 "Cannot identify RAW and CALIB frames!");
00407 
00408         /* Get Parameters */
00409         cpl_msg_info(__func__, "--- Parameter setup for kmo_wave_cal ------");
00410 
00411         fit_order_par = kmo_dfs_get_parameter_int(parlist,
00412                 "kmos.kmo_wave_cal.order");
00413         KMO_TRY_CHECK_ERROR_STATE();
00414         KMO_TRY_EXIT_IF_ERROR(
00415             kmo_dfs_print_parameter_help(parlist, "kmos.kmo_wave_cal.order"));
00416         KMO_TRY_ASSURE((fit_order_par >= 0) && (fit_order_par <= 7),
00417                 CPL_ERROR_ILLEGAL_INPUT, "order must be between 1 and 7");
00418 
00419         suppress_extension = kmo_dfs_get_parameter_bool(parlist,
00420                 "kmos.kmo_wave_cal.suppress_extension");
00421         KMO_TRY_CHECK_ERROR_STATE();
00422         KMO_TRY_EXIT_IF_ERROR(
00423             kmo_dfs_print_parameter_help(parlist, 
00424                 "kmos.kmo_wave_cal.suppress_extension"));
00425 
00426         KMO_TRY_ASSURE((suppress_extension == TRUE) || 
00427                 (suppress_extension == FALSE), CPL_ERROR_ILLEGAL_INPUT,
00428                 "suppress_extension must be TRUE or FALSE!");
00429 
00430         kmo_band_pars_load(parlist, "kmos.kmo_wave_cal");
00431 
00432         cpl_msg_info(__func__, "-------------------------------------------");
00433 
00434         /* check EXPTIME, NDIT, READMODE and lamps  */
00435         /* check ARC_OFF */
00436         KMO_TRY_EXIT_IF_NULL(frame = kmo_dfs_get_frame(frameset, ARC_OFF));
00437 
00438         main_header=kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0);
00439 
00440         ndit = cpl_propertylist_get_int(main_header, NDIT);
00441         KMO_TRY_CHECK_ERROR_STATE("NDIT keyword in main header missing");
00442 
00443         exptime = cpl_propertylist_get_double(main_header, EXPTIME);
00444         KMO_TRY_CHECK_ERROR_STATE("EXPTIME keyword in main header missing");
00445 
00446         readmode = cpl_strdup(cpl_propertylist_get_string(main_header, 
00447                     READMODE));
00448         KMO_TRY_CHECK_ERROR_STATE("ESO DET READ CURNAME missing from header");
00449 
00450         desc1 = kmo_identify_fits_header(cpl_frame_get_filename(frame));
00451         KMO_TRY_CHECK_ERROR_STATE_MSG("ARC_OFF frame has wrong format");
00452 
00453         KMO_TRY_ASSURE(desc1.fits_type == raw_fits, CPL_ERROR_ILLEGAL_INPUT,
00454                 "ARC_OFF frame type is wrong (%s must be raw)",
00455                 cpl_frame_get_filename(frame));
00456 
00457         nx = desc1.naxis1;
00458         ny = desc1.naxis2;
00459         nz = desc1.naxis3;
00460         nr_devices = desc1.nr_ext;
00461 
00462         /* assure that flat lamps are off */
00463         KMO_TRY_ASSURE((kmo_check_lamp(main_header, INS_LAMP3_ST) == FALSE) &&
00464                 (kmo_check_lamp(main_header, INS_LAMP4_ST) == FALSE),
00465                 CPL_ERROR_ILLEGAL_INPUT,"Flat lamps must be switched off (%s)!",
00466                 cpl_frame_get_filename(frame));
00467 
00468         /* check if arc lamps are off (or at least blocked by filter wheel) */
00469         if ((kmo_check_lamp(main_header, INS_LAMP1_ST) == TRUE) ||
00470             (kmo_check_lamp(main_header, INS_LAMP2_ST) == TRUE)) {
00471             if (!(strcmp(cpl_propertylist_get_string(main_header, 
00472                                 "ESO INS FILT1 ID"), "Block") == 0) ||
00473                 !(strcmp(cpl_propertylist_get_string(main_header, 
00474                             "ESO INS FILT3 ID"), "Block") == 0) ||
00475                 !(strcmp(cpl_propertylist_get_string(main_header, 
00476                             "ESO INS FILT3 ID"), "Block") == 0)) {
00477                 cpl_msg_warning(__func__, 
00478                 "At least one arc lamp is on! Check if the lamps are blocked!");
00479             }
00480         }
00481         cpl_propertylist_delete(main_header); main_header = NULL;
00482 
00483         /* check REF_LINES */
00484         if (line_estimate_method == 2) {
00485             KMO_TRY_EXIT_IF_NULL(frame=kmo_dfs_get_frame(frameset, REF_LINES));
00486             desc2 = kmo_identify_fits_header(cpl_frame_get_filename(frame));
00487             KMO_TRY_CHECK_ERROR_STATE_MSG("REF_LINES frame has wrong format");
00488 
00489             KMO_TRY_ASSURE(desc2.fits_type == f2l_fits, CPL_ERROR_ILLEGAL_INPUT,
00490                     "REF_LINES frame hasn't correct frame type "
00491                     "(%s must be a F2L frame)!",
00492                     cpl_frame_get_filename(frame));
00493             kmo_free_fits_desc(&desc2);
00494         }
00495 
00496         /* check ARC_ON */
00497         KMO_TRY_EXIT_IF_NULL(frame = kmo_dfs_get_frame(frameset, ARC_ON));
00498 
00499         while (frame != NULL) {
00500             main_header = kmclipm_propertylist_load(
00501                     cpl_frame_get_filename(frame), 0);
00502 
00503             KMO_TRY_ASSURE(cpl_propertylist_get_int(main_header, NDIT) == ndit,
00504                     CPL_ERROR_ILLEGAL_INPUT,
00505                     "NDIT varies: (is %d and %d).",
00506                     cpl_propertylist_get_int(main_header, NDIT), ndit);
00507 
00508             KMO_TRY_ASSURE(
00509                     cpl_propertylist_get_double(main_header, EXPTIME)==exptime,
00510                     CPL_ERROR_ILLEGAL_INPUT,
00511                     "EXPTIME varies: (is %g and %g).",
00512                     cpl_propertylist_get_double(main_header, EXPTIME), exptime);
00513 
00514             KMO_TRY_ASSURE(
00515                     strcmp(cpl_propertylist_get_string(main_header, READMODE), 
00516                         readmode) == 0, CPL_ERROR_ILLEGAL_INPUT,
00517                     "ESO DET READ CURNAME varies: (is %s and %s).",
00518                     cpl_propertylist_get_string(main_header, READMODE), 
00519                     readmode);
00520 
00521             desc2 = kmo_identify_fits_header(cpl_frame_get_filename(frame));
00522             KMO_TRY_CHECK_ERROR_STATE_MSG("ARC_ON frame wrong format");
00523 
00524             KMO_TRY_ASSURE(desc2.fits_type == raw_fits, CPL_ERROR_ILLEGAL_INPUT,
00525                     "ARC_ON frame wrong data type (%s must be a raw)",
00526                     cpl_frame_get_filename(frame));
00527 
00528             KMO_TRY_ASSURE((desc2.naxis1 == nx) && (desc2.naxis2 == ny) &&
00529                     (desc2.naxis3 == nz), CPL_ERROR_ILLEGAL_INPUT,
00530                     "ARC_ON has wrong dimensions (x,y): (%d,%d) vs (%d,%d)",
00531                     desc2.naxis1, desc2.naxis2, nx, ny);
00532 
00533             // assure that flat lamp is off (LAMP3 and 4)
00534             // and that either arc lamp is on (LAMP1 and 2)
00535             KMO_TRY_ASSURE(
00536                     ((kmo_check_lamp(main_header, INS_LAMP1_ST) == TRUE) ||
00537                     (kmo_check_lamp(main_header, INS_LAMP2_ST) == TRUE)) &&
00538                     (kmo_check_lamp(main_header, INS_LAMP3_ST) == FALSE) &&
00539                     (kmo_check_lamp(main_header, INS_LAMP4_ST) == FALSE),
00540                     CPL_ERROR_ILLEGAL_INPUT,
00541                     "Lamp1 or 2 must be on, 3 and 4 must be off for ARC_ON");
00542 
00543             frame = kmo_dfs_get_frame(frameset, NULL);
00544             KMO_TRY_CHECK_ERROR_STATE();
00545 
00546             kmo_free_fits_desc(&desc2);
00547             kmo_init_fits_desc(&desc2);
00548             cpl_propertylist_delete(main_header); main_header = NULL;
00549         }
00550 
00551         // load first ARC_ON main header
00552         KMO_TRY_EXIT_IF_NULL(frame = kmo_dfs_get_frame(frameset, ARC_ON));
00553         KMO_TRY_EXIT_IF_NULL(main_header = 
00554                 kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0));
00555 
00556         // check FLAT_EDGE
00557         KMO_TRY_EXIT_IF_NULL(frame = kmo_dfs_get_frame(frameset, FLAT_EDGE));
00558         desc2 = kmo_identify_fits_header(cpl_frame_get_filename(frame));
00559         KMO_TRY_CHECK_ERROR_STATE();
00560         KMO_TRY_ASSURE((desc2.nr_ext % 24==0) && (desc2.fits_type == f2l_fits),
00561                 CPL_ERROR_ILLEGAL_INPUT, "FLAT_EDGE has a wrong format.");
00562 
00563         kmo_free_fits_desc(&desc2);
00564         kmo_init_fits_desc(&desc2);
00565 
00566         // ------------ check filter_id, grating_id and rotator offset ---------
00567         // assure that filters, grating and rotation offsets match for
00568         // ARC_ON, XCAL and YCAL
00569         // check if filter_id, grating_id and rotator offset match for all
00570         // detectors
00571         KMO_TRY_EXIT_IF_ERROR(
00572                 kmo_check_frame_setup(frameset, ARC_ON, XCAL,TRUE,FALSE,FALSE));
00573         KMO_TRY_EXIT_IF_ERROR(
00574             kmo_check_frame_setup(frameset, ARC_ON, YCAL, TRUE, FALSE, FALSE));
00575 
00576         KMO_TRY_EXIT_IF_ERROR(kmo_check_frame_setup_md5_xycal(frameset));
00577 
00578         strcpy(filename_lcal, LCAL);
00579         strcpy(filename_det_img, DET_IMG_WAVE);
00580 
00581         KMO_TRY_EXIT_IF_NULL(frame = kmo_dfs_get_frame(frameset, XCAL));
00582         KMO_TRY_EXIT_IF_NULL(suffix = kmo_dfs_get_suffix(frame, TRUE, FALSE));
00583 
00584         cpl_msg_info(__func__, "Detected instrument setup:   %s", suffix+1);
00585         cpl_msg_info(__func__, "(grating 1, 2 & 3)");
00586 
00587         // setup lamp config
00588         if ((kmo_check_lamp(main_header, INS_LAMP1_ST) == TRUE) &&
00589             (kmo_check_lamp(main_header, INS_LAMP2_ST) == FALSE)) {
00590             lamp_config = ARGON;
00591             strcpy(tmpstr, "Argon");
00592         } else if ((kmo_check_lamp(main_header, INS_LAMP1_ST) == FALSE) &&
00593                    (kmo_check_lamp(main_header, INS_LAMP2_ST) == TRUE)) {
00594            lamp_config = NEON;
00595            strcpy(tmpstr, "Neon");
00596         } else if ((kmo_check_lamp(main_header, INS_LAMP1_ST) == TRUE) &&
00597                    (kmo_check_lamp(main_header, INS_LAMP2_ST) == TRUE)) {
00598            lamp_config = ARGON_NEON;
00599            strcpy(tmpstr, "Argon + Neon");
00600         }
00601 
00602         cpl_msg_info(__func__, "Detected arc lamp configuration: %s", tmpstr);
00603 
00604         /* assert that filter and grating match for each detector */
00605         /* filter/grating can be different for each detector */
00606         KMO_TRY_EXIT_IF_NULL(
00607             filter_ids =  kmo_get_filter_setup(main_header, nr_devices, TRUE));
00608 
00609         /* scan for rotator angles */
00610         #define ANGLE_DIM 360
00611         int rotang_found[ANGLE_DIM];
00612         int rotang_cnt[ANGLE_DIM];
00613         for (i = 0; i < ANGLE_DIM; i++) {
00614             rotang_found[i] = 0;
00615             rotang_cnt[i] = 0;
00616         }
00617         KMO_TRY_EXIT_IF_NULL(frame = kmo_dfs_get_frame(frameset, ARC_ON));
00618         while (frame != NULL) {
00619             header = kmclipm_propertylist_load(cpl_frame_get_filename(frame),0);
00620             if (cpl_propertylist_has(header, ROTANGLE)) {
00621                 int rot_angle = (int)rint(cpl_propertylist_get_double(
00622                             header, ROTANGLE));
00623                 if (rot_angle < 0)                      rot_angle += 360;
00624                 if (rot_angle < 360 && rot_angle >= 0)  rotang_cnt[rot_angle]++;
00625             } else {
00626                 cpl_msg_warning(__func__,"File %s has no keyword \"ROTANGLE\"",
00627                         cpl_frame_get_filename(frame));
00628             }
00629 
00630             cpl_propertylist_delete(header); header = NULL;
00631             frame = kmo_dfs_get_frame(frameset, NULL);
00632             KMO_TRY_CHECK_ERROR_STATE();
00633         }
00634         for (ax = 0; ax < ANGLE_DIM; ax++) {
00635             if (rotang_cnt[ax] != 0) {
00636                 if (rotang_cnt[ax] == 1 ) {
00637                     cpl_msg_info(__func__, 
00638                             "Found %d frame with rotator angle %d",
00639                             rotang_cnt[ax],ax);
00640                 } else {
00641                     cpl_msg_warning(__func__,
00642             "Found %d frames with rotator angle %d but only one will be used",
00643                                     rotang_cnt[ax],ax);
00644                 }
00645                 rotang_found[nr_angles] = ax;
00646                 nr_angles++;
00647             }
00648         }
00649 
00650         KMO_TRY_EXIT_IF_NULL(angle_frameset = (cpl_frameset **)cpl_malloc(
00651                     nr_angles * sizeof(cpl_frameset*)));
00652         for (i = 0; i < nr_angles; i++) {
00653             angle_frameset[i] = cpl_frameset_new();
00654         }
00655 
00656         KMO_TRY_EXIT_IF_NULL(frame = kmo_dfs_get_frame(frameset, ARC_ON));
00657         while (frame != NULL) {
00658             header=kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0);
00659             if (cpl_propertylist_has(header, ROTANGLE)) {
00660                 int rot_angle = (int)rint(cpl_propertylist_get_double(header, 
00661                             ROTANGLE));
00662                 if (rot_angle < 0)  rot_angle += 360;
00663                 int ix = -1;
00664                 for (ix = 0; ix<nr_angles; ix++) {
00665                     if (rotang_found[ix] == rot_angle) {
00666                         break;
00667                     }
00668                 }
00669                 if (ix<nr_angles) {
00670                     KMO_TRY_EXIT_IF_ERROR(
00671                             cpl_frameset_insert(angle_frameset[ix], 
00672                                 cpl_frame_duplicate(frame)));
00673                 }
00674             }
00675 
00676             cpl_propertylist_delete(header); header = NULL;
00677             frame = kmo_dfs_get_frame(frameset, NULL);
00678             KMO_TRY_CHECK_ERROR_STATE();
00679         }
00680 
00681         /* allocate temporary memory */
00682         /* allocate here a edge table structure for all detectors */
00683         KMO_TRY_EXIT_IF_NULL(edge_table = (cpl_table***)cpl_calloc(nr_devices, 
00684                     sizeof(cpl_table**)));
00685         for (i = 0; i < nr_devices; i++) {
00686             KMO_TRY_EXIT_IF_NULL(
00687                 edge_table[i] = (cpl_table**)cpl_calloc(KMOS_IFUS_PER_DETECTOR,
00688                     sizeof(cpl_table*)));
00689         }
00690 
00691         /* the frames have to be stored temporarily because the QC params */
00692         /* for the main header are calculated per detector. So they can be */
00693         /* stored only when all detectors are processed */
00694         KMO_TRY_EXIT_IF_NULL(stored_lcal = (cpl_image**)cpl_calloc(
00695                     nr_devices * nr_angles, sizeof(cpl_image*)));
00696         KMO_TRY_EXIT_IF_NULL(stored_det_img = (cpl_image**)cpl_calloc(
00697                     nr_devices * nr_angles, sizeof(cpl_image*)));
00698         KMO_TRY_EXIT_IF_NULL(stored_sub_headers_lcal = (cpl_propertylist**)
00699                 cpl_calloc(nr_devices * nr_angles, sizeof(cpl_propertylist*)));
00700         KMO_TRY_EXIT_IF_NULL(stored_sub_headers_det_img = (cpl_propertylist**)
00701                 cpl_calloc(nr_devices * nr_angles, sizeof(cpl_propertylist*)));
00702         KMO_TRY_EXIT_IF_NULL(stored_qc_arc_sat = (int*)cpl_calloc(nr_devices, 
00703                 nr_angles * sizeof(int)));
00704         KMO_TRY_EXIT_IF_NULL(stored_qc_ar_eff = (double*)cpl_calloc(nr_devices, 
00705                 nr_angles * sizeof(double)));
00706         KMO_TRY_EXIT_IF_NULL(stored_qc_ne_eff = (double*)cpl_calloc(nr_devices, 
00707                 nr_angles * sizeof(double)));
00708 
00709         /* process data */
00710         /* load arclines and reference lines */
00711         KMO_TRY_EXIT_IF_NULL(frame = kmo_dfs_get_frame(frameset, ARC_LIST));
00712 
00713         /* check if ARC_LIST is the filter_id-one */
00714         KMO_TRY_EXIT_IF_NULL(tmp_header = kmclipm_propertylist_load(
00715                     cpl_frame_get_filename(frame), 0));
00716         KMO_TRY_EXIT_IF_NULL(
00717             tmp_str = cpl_propertylist_get_string(tmp_header, FILT_ID));
00718         KMO_TRY_ASSURE(strcmp(filter_ids[0], tmp_str) == 0,
00719                 CPL_ERROR_ILLEGAL_INPUT, 
00720                 "ARC_LIST model must have keyword %s = %s", 
00721                 FILT_ID, filter_ids[0]);
00722         cpl_propertylist_delete(tmp_header); tmp_header = NULL;
00723 
00724         desc2 = kmo_identify_fits_header(cpl_frame_get_filename(frame));
00725         KMO_TRY_CHECK_ERROR_STATE();
00726 
00727         KMO_TRY_ASSURE((desc2.nr_ext == 1) && (desc2.fits_type == f2l_fits),
00728                 CPL_ERROR_ILLEGAL_INPUT, "ARC_LIST has wrong format");
00729         kmo_free_fits_desc(&desc2);
00730         kmo_init_fits_desc(&desc2);
00731 
00732         KMO_TRY_EXIT_IF_NULL(
00733             arclines = kmo_dfs_load_table(frameset, ARC_LIST, 1, 0));
00734 
00735         KMO_TRY_EXIT_IF_NULL(lines = kmo_get_lines(arclines, lamp_config));
00736 
00737         cpl_msg_info(__func__, 
00738                 "Nr. of lines in arclist for this configuration: %lld",
00739                 cpl_bivector_get_size(lines));
00740 
00741         if (line_estimate_method == 2) {
00742             KMO_TRY_EXIT_IF_NULL(
00743                 reflines = kmo_dfs_load_table(frameset, REF_LINES, 1, 0));
00744         }
00745 
00746         /* check which IFUs are active for all FLAT frames */
00747         KMO_TRY_EXIT_IF_NULL(
00748             unused_ifus_before = kmo_get_unused_ifus(frameset, 0, 0));
00749 
00750         KMO_TRY_EXIT_IF_NULL(
00751             unused_ifus_after = kmo_duplicate_unused_ifus(unused_ifus_before));
00752 
00753         kmo_print_unused_ifus(unused_ifus_before, FALSE);
00754 
00755         /* make sure no reconstruction lookup table (LUT) is used */
00756         if (getenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE") != NULL) {
00757             last_env = getenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE");
00758         }
00759         setenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE","NONE",1);
00760 
00761         /* Loop all Rotator Angles and Detectors  */
00762         for (a = 0; a < nr_angles; a++) {
00763             cpl_msg_info(__func__, "Processing rotator angle %d -> %d degree", 
00764                     a, rotang_found[a]);
00765             cpl_msg_indent_more();
00766             for (i = 1; i <= nr_devices; i++) {
00767                 cpl_msg_info(__func__,"Processing detector No. %d", i);
00768                 cpl_msg_indent_more();
00769 
00770                 /* Load edge parameters */
00771                 KMO_TRY_EXIT_IF_NULL(frame=kmo_dfs_get_frame(frameset, 
00772                             FLAT_EDGE));
00773 
00774                 for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
00775                     edge_table[i-1][j] = kmclipm_cal_table_load(
00776                             cpl_frame_get_filename(frame), 
00777                             (i-1) * KMOS_IFUS_PER_DETECTOR + j + 1,
00778                             rotang_found[a], 0, &angle_found);
00779                     
00780                     /* IFU is inactive: proceed */
00781                     if (cpl_error_get_code() == CPL_ERROR_ILLEGAL_INPUT) {
00782                         cpl_error_reset();
00783                     }
00784                 }
00785 
00786                 if (fit_order_par == 0) {
00787                     /* set default fit orders for the different bands */
00788                     if ((strcmp(filter_ids[i-1], "H") == 0) ||
00789                         (strcmp(filter_ids[i-1], "K") == 0) ||
00790                         (strcmp(filter_ids[i-1], "YJ") == 0))
00791                     {
00792                         fit_order = 6;
00793                     } else if (strcmp(filter_ids[i-1], "IZ") == 0) {
00794                         fit_order = 4;
00795                     } else if (strcmp(filter_ids[i-1], "HK") == 0) {
00796                         fit_order = 5;
00797                     }
00798                     cpl_msg_info(__func__, 
00799                             "Order of wavelength spectrum fit for %s-band: %d",
00800                             filter_ids[i-1], fit_order);
00801                 } else {
00802                     fit_order = fit_order_par;
00803                 }
00804 
00805                 /* lamp_on */
00806                 KMO_TRY_EXIT_IF_NULL(
00807                     frame = kmo_dfs_get_frame(angle_frameset[a], ARC_ON));
00808 
00809                 /* if sat_mode is set to TRUE here, then the calc. of the */
00810                 /* saturated pixels has to be updated like in kmo_flat-recipe */
00811                 KMO_TRY_EXIT_IF_NULL(det_lamp_on = kmo_dfs_load_image_frame(
00812                             frame, i, FALSE, TRUE, &nr_sat));
00813 
00814                 int sx = a * nr_devices + (i - 1);
00815 
00816                 /* count saturated pixels for each detector */
00817                 if (strcmp(cpl_propertylist_get_string(main_header, 
00818                                 READMODE), "Nondest") == 0) {
00819                     /* NDR: non-destructive readout mode */
00820                     stored_qc_arc_sat[sx] = nr_sat;
00821                 } else {
00822                     /* normal readout mode */
00823                     stored_qc_arc_sat[sx] = kmo_image_get_saturated(
00824                             det_lamp_on, KMO_FLAT_SATURATED);
00825                 }
00826                 KMO_TRY_CHECK_ERROR_STATE();
00827 
00828                 KMO_TRY_EXIT_IF_NULL(
00829                     det_lamp_on_copy = cpl_image_duplicate(det_lamp_on));
00830 
00831                 // lamp_off
00832                 KMO_TRY_EXIT_IF_NULL(
00833                     frame = kmo_dfs_get_frame(frameset, ARC_OFF));
00834 
00835                 KMO_TRY_EXIT_IF_NULL(
00836                     det_lamp_off = kmo_dfs_load_image_frame(frame, i, FALSE, 
00837                         FALSE, NULL));
00838 
00839                 /* process imagelist */
00840                 KMO_TRY_CHECK_ERROR_STATE();
00841 
00842                 /* subtract lamp_off from lamp_on */
00843                 KMO_TRY_EXIT_IF_ERROR(
00844                     cpl_image_subtract(det_lamp_on, det_lamp_off));
00845 
00846                 /* load flat calibration frames */
00847                 KMO_TRY_EXIT_IF_NULL(
00848                     xcal = kmo_dfs_load_cal_image(frameset, XCAL, i, 0,
00849                         (double)rotang_found[a], FALSE, NULL, &angle_found, -1,
00850                         0, 0));
00851 
00852                 KMO_TRY_EXIT_IF_NULL(
00853                     ycal = kmo_dfs_load_cal_image(frameset, YCAL, i, 0,
00854                         (double)rotang_found[a], FALSE, NULL,  &angle_found, -1,
00855                         0, 0));
00856 
00857                 /* load bad pixel mask from XCAL and set NaNs to 0  */
00858                 /* and all other values to 1 */
00859                 KMO_TRY_EXIT_IF_NULL(bad_pix_mask = cpl_image_duplicate(xcal));
00860 
00861                 KMO_TRY_EXIT_IF_NULL(
00862                     pbad_pix_mask = cpl_image_get_data_float(bad_pix_mask));
00863                 for (x = 0; x < nx; x++) {
00864                     for (y = 0; y < ny; y++) {
00865                         if (isnan(pbad_pix_mask[x+nx*y])) {
00866                             pbad_pix_mask[x+nx*y] = 0.;
00867                         } else {
00868                             pbad_pix_mask[x+nx*y] = 1.;
00869                         }
00870                     }
00871                 }
00872 
00873                 /* ---------------------------- */
00874                 /* CALCULATE SPECTRAL CURVATURE */
00875                 /* ---------------------------- */
00876                 err = kmo_calc_wave_calib(det_lamp_on, bad_pix_mask, xcal,
00877                         ycal, filter_ids[i-1], lamp_config, i,
00878                         unused_ifus_after[i-1], edge_table[i-1], lines,
00879                         reflines, -1.0, &lcal, &(stored_qc_ar_eff[sx]),
00880                         &(stored_qc_ne_eff[sx]), FALSE, fit_order,
00881                         line_estimate_method);
00882 
00883                 if (err == CPL_ERROR_NONE) {
00884                     /* Update QC parameters */
00885                     if (stored_qc_ar_eff[sx] != -1.0) 
00886                         stored_qc_ar_eff[sx] /= exptime;
00887                     if (stored_qc_ne_eff[sx] != -1.0) 
00888                         stored_qc_ne_eff[sx] /= exptime;
00889 
00890                     /* Apply the badpixel mask to the produced frame */
00891                     KMO_TRY_EXIT_IF_ERROR(
00892                         cpl_image_multiply(lcal, bad_pix_mask));
00893                     KMO_TRY_EXIT_IF_ERROR(
00894                         kmo_image_reject_from_mask(lcal, bad_pix_mask));
00895 
00896                     /* Store Result frame */
00897                     stored_lcal[sx] = lcal;
00898                 } else if (err == CPL_ERROR_UNSPECIFIED) {
00899                     /* All IFUs seem to be deactivated */
00900                     /* Continue processing - just save empty frame */
00901                     cpl_error_reset();
00902                     stored_lcal[sx] = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
00903                     kmo_image_fill(stored_lcal[sx], 0.0);
00904                 } else {
00905                     cpl_error_reset();
00906                     cpl_msg_warning(__func__,
00907                             "Couldn't identify any line - Check the line list");
00908                     cpl_msg_warning(__func__,
00909                             "Band defined in header of detector %d: %s",
00910                             i, filter_ids[i-1]);
00911                     cpl_msg_warning(__func__, "Arc line file defined: %s",
00912                             cpl_frame_get_filename(kmo_dfs_get_frame(frameset, 
00913                                     ARC_LIST)));
00914                 }
00915 
00916                 /* -------------------------------------------- */
00917                 /* CREATE RECONSTRUCTED AND RESAMPLED ARC FRAME */
00918                 /* -------------------------------------------- */
00919                 stored_det_img[sx] = kmo_reconstructed_arc_image(frameset,
00920                         det_lamp_on_copy, det_lamp_off, xcal, ycal,
00921                         stored_lcal[sx], unused_ifus_after[i-1], FALSE, i,
00922                         suffix, filter_ids[i-1], lamp_config, &qc_header);
00923                 if (cpl_error_get_code() != CPL_ERROR_NONE) {
00924                     /* couldn't reconstruct */
00925                     cpl_msg_error(__func__, 
00926                             "Couldn't reconstruct IFUs on detector %d", i);
00927                     cpl_error_reset();
00928                 }
00929 
00930                 /* --------------------------------------- */
00931                 /* CREATE EXTENSION HEADER FOR THE PRODUCT */
00932                 /* --------------------------------------- */
00933                 KMO_TRY_EXIT_IF_NULL(stored_sub_headers_lcal[sx] = 
00934                         kmo_dfs_load_sub_header(frameset, ARC_ON, i, FALSE));
00935                 /* update EXTNAME */
00936                 KMO_TRY_EXIT_IF_NULL(
00937                     extname = kmo_extname_creator(detector_frame, i, EXT_DATA));
00938                 KMO_TRY_EXIT_IF_ERROR(
00939                     kmclipm_update_property_string(stored_sub_headers_lcal[sx],
00940                         EXTNAME, extname, "FITS extension name"));
00941                 cpl_free(extname); extname = NULL;
00942 
00943                 KMO_TRY_EXIT_IF_ERROR(
00944                     kmclipm_update_property_int(stored_sub_headers_lcal[sx],
00945                         EXTVER, sx+1, "FITS extension ver"));
00946 
00947                 // add first QC parameters
00948                 KMO_TRY_EXIT_IF_ERROR(
00949                     kmclipm_update_property_int(stored_sub_headers_lcal[sx],
00950                         QC_ARC_SAT, stored_qc_arc_sat[sx], 
00951                         "[] nr. saturated pixels of arc exp."));
00952 
00953                 gain = kmo_dfs_get_property_double(stored_sub_headers_lcal[sx],
00954                         GAIN);
00955                 KMO_TRY_CHECK_ERROR_STATE_MSG("GAIN is missing in header");
00956 
00957                 if (stored_qc_ar_eff[sx] != -1.0) {
00958                     KMO_TRY_EXIT_IF_ERROR(kmclipm_update_property_double(
00959                                 stored_sub_headers_lcal[sx], QC_ARC_AR_EFF, 
00960                                 stored_qc_ar_eff[sx]/gain,
00961                                 "[e-/s] Argon lamp efficiency"));
00962                 }
00963 
00964                 if (stored_qc_ne_eff[sx] != -1.0) {
00965                     KMO_TRY_EXIT_IF_ERROR(kmclipm_update_property_double(
00966                                 stored_sub_headers_lcal[sx], QC_ARC_NE_EFF, 
00967                                 stored_qc_ne_eff[sx]/gain,
00968                                 "[e-/s] Neon lamp efficiency"));
00969                 }
00970 
00971                 KMO_TRY_EXIT_IF_ERROR(
00972                     kmclipm_update_property_double(stored_sub_headers_lcal[sx],
00973                         CAL_ROTANGLE, ((double) rotang_found[a]),
00974                         "[deg] Rotator relative to nasmyth"));
00975 
00976                 /* append QC parameters */
00977                 KMO_TRY_EXIT_IF_ERROR(cpl_propertylist_append(
00978                             stored_sub_headers_lcal[sx], qc_header));
00979                 cpl_propertylist_delete(qc_header); qc_header = NULL;
00980 
00981                 KMO_TRY_EXIT_IF_NULL(
00982                     stored_sub_headers_det_img[sx]=cpl_propertylist_duplicate(
00983                         stored_sub_headers_lcal[sx]));
00984 
00985                 cpl_propertylist_erase(stored_sub_headers_lcal[sx], CRVAL1);
00986                 cpl_propertylist_erase(stored_sub_headers_lcal[sx], CRVAL2);
00987                 cpl_propertylist_erase(stored_sub_headers_lcal[sx], CTYPE1);
00988                 cpl_propertylist_erase(stored_sub_headers_lcal[sx], CTYPE2);
00989                 cpl_propertylist_erase(stored_sub_headers_lcal[sx], CDELT1);
00990                 cpl_propertylist_erase(stored_sub_headers_lcal[sx], CDELT2);
00991 
00992                 /* free memory */
00993                 cpl_image_delete(det_lamp_on); det_lamp_on = NULL;
00994                 cpl_image_delete(det_lamp_on_copy); det_lamp_on_copy = NULL;
00995                 cpl_image_delete(det_lamp_off); det_lamp_off = NULL;
00996                 cpl_image_delete(bad_pix_mask); bad_pix_mask = NULL;
00997                 cpl_image_delete(xcal); xcal = NULL;
00998                 cpl_image_delete(ycal); ycal = NULL;
00999                 for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01000                     cpl_table_delete(edge_table[i-1][j]); 
01001                     edge_table[i-1][j] = NULL;
01002                 }
01003                 cpl_msg_indent_less();
01004             } // for i devices
01005             cpl_msg_indent_less() ;
01006         } // for a angles
01007 
01008         if (line_estimate_method == 2) {
01009             cpl_table_delete(reflines); reflines = NULL;
01010         }
01011         
01012         /* QC parameters & saving */
01013         cpl_msg_info(__func__, "Saving data...");
01014 
01015         /* load, update & save primary header */
01016         if (!suppress_extension) {
01017             KMO_TRY_EXIT_IF_NULL(fn_suffix = cpl_sprintf("%s", suffix));
01018         } else {
01019             KMO_TRY_EXIT_IF_NULL(fn_suffix = cpl_sprintf("%s", ""));
01020         }
01021 
01022         /* update which IFUs are not used */
01023         KMO_TRY_EXIT_IF_ERROR(
01024             kmo_set_unused_ifus(unused_ifus_after, main_header,
01025                 "kmo_wave_cal"));
01026 
01027         KMO_TRY_EXIT_IF_NULL(frame = kmo_dfs_get_frame(frameset, ARC_ON));
01028 
01029         KMO_TRY_EXIT_IF_ERROR(
01030             kmo_dfs_save_main_header(frameset, filename_lcal, fn_suffix, frame,
01031                 main_header, parlist, cpl_func));
01032 
01033         KMO_TRY_EXIT_IF_ERROR(
01034             kmo_dfs_save_main_header(frameset, filename_det_img, fn_suffix, 
01035                 frame, main_header, parlist, cpl_func));
01036 
01037         cpl_propertylist_delete(main_header); main_header = NULL;
01038 
01039         /* save sub-frames */
01040         for (a = 0; a < nr_angles; a++) {
01041             for (i = 1; i <= nr_devices; i++) {
01042                 int sx = a * nr_devices + (i - 1);
01043                 /* save lcal-frame */
01044                 KMO_TRY_EXIT_IF_ERROR(
01045                     kmo_dfs_save_image(stored_lcal[sx], filename_lcal, 
01046                         fn_suffix, stored_sub_headers_lcal[sx], 0./0.));
01047 
01048                 /* save detector image */
01049                 KMO_TRY_EXIT_IF_ERROR(
01050                     kmo_dfs_save_image(stored_det_img[sx], filename_det_img, 
01051                         fn_suffix, stored_sub_headers_det_img[sx], 0./0.));
01052             } // for i = nr_devices
01053         } // for a angles
01054 
01055         /* print which IFUs are not used */
01056         kmo_print_unused_ifus(unused_ifus_after, TRUE);
01057     }
01058     KMO_CATCH
01059     {
01060         KMO_CATCH_MSG();
01061         ret_val = -1;
01062     }
01063 
01064     if (last_env != NULL) {
01065         setenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE",last_env,1);
01066     } else {
01067         unsetenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE");
01068     }
01069 
01070     kmo_free_fits_desc(&desc1);
01071     kmo_free_fits_desc(&desc2);
01072     if (unused_ifus_before != NULL) {
01073        kmo_free_unused_ifus(unused_ifus_before); unused_ifus_before = NULL;
01074     }
01075     if (unused_ifus_after != NULL) {
01076        kmo_free_unused_ifus(unused_ifus_after); unused_ifus_after = NULL;
01077     }
01078     cpl_propertylist_delete(main_header); main_header = NULL;
01079     cpl_image_delete(det_lamp_on); det_lamp_on = NULL;
01080     cpl_image_delete(det_lamp_off); det_lamp_off = NULL;
01081     cpl_image_delete(bad_pix_mask); bad_pix_mask = NULL;
01082     cpl_table_delete(arclines); arclines = NULL;
01083     cpl_table_delete(reflines); reflines = NULL;
01084     cpl_free(stored_qc_arc_sat); stored_qc_arc_sat = NULL;
01085     cpl_free(stored_qc_ar_eff); stored_qc_ar_eff = NULL;
01086     cpl_free(stored_qc_ne_eff); stored_qc_ne_eff = NULL;
01087     for (i = 0; i < nr_devices * nr_angles; i++) {
01088         cpl_image_delete(stored_lcal[i]); stored_lcal[i] = NULL;
01089         cpl_image_delete(stored_det_img[i]); stored_det_img[i] = NULL;
01090         cpl_propertylist_delete(stored_sub_headers_lcal[i]);
01091             stored_sub_headers_lcal[i] = NULL;
01092         cpl_propertylist_delete(stored_sub_headers_det_img[i]);
01093             stored_sub_headers_det_img[i] = NULL;
01094     }
01095     for (i = 0; i < nr_angles; i++) {
01096         cpl_frameset_delete(angle_frameset[i]); angle_frameset[i] = NULL;
01097     }
01098     if (filter_ids != NULL) {
01099         for (i = 0; i < nr_devices; i++) {
01100             cpl_free(filter_ids[i]); filter_ids[i] = NULL;
01101 
01102         }
01103         cpl_free(filter_ids); filter_ids = NULL;
01104     }
01105     if (edge_table != NULL) {
01106         for (i = 0; i < nr_devices; i++) {
01107             cpl_free(edge_table[i]); edge_table[i] = NULL;
01108         }
01109         cpl_free(edge_table); edge_table = NULL;
01110     }
01111 
01112     cpl_free(angle_frameset); angle_frameset = NULL;
01113     cpl_free(stored_lcal); stored_lcal = NULL;
01114     cpl_free(stored_det_img); stored_det_img = NULL;
01115     cpl_free(stored_sub_headers_lcal); stored_sub_headers_lcal = NULL;
01116     cpl_free(stored_sub_headers_det_img); stored_sub_headers_det_img = NULL;
01117     cpl_free(readmode); readmode = NULL;
01118     cpl_bivector_delete(lines); lines = NULL;
01119     cpl_free(suffix); suffix = NULL;
01120     cpl_free(fn_suffix); fn_suffix = NULL;
01121 
01122     return ret_val;
01123 }
01124