KMOS Pipeline Reference Manual  1.1.0
kmo_illumination.c
00001 /* $Id: kmo_illumination.c,v 1.43 2013/03/18 14:02:30 aagudo Exp $
00002  *
00003  * This file is part of the KMOS Pipeline
00004  * Copyright (C) 2002,2003 European Southern Observatory
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  */
00020 
00021 /*
00022  * $Author: aagudo $
00023  * $Date: 2013/03/18 14:02:30 $
00024  * $Revision: 1.43 $
00025  * $Name: HEAD $
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 
00032 /*-----------------------------------------------------------------------------
00033  *                              Includes
00034  *----------------------------------------------------------------------------*/
00035 
00036 #include <math.h>
00037 #include <string.h>
00038 
00039 #include <cpl.h>
00040 
00041 #include "kmo_priv_reconstruct.h"
00042 #include "kmo_priv_functions.h"
00043 #include "kmo_priv_flat.h"
00044 #include "kmo_priv_wave_cal.h"
00045 #include "kmclipm_priv_splines.h"
00046 #include "kmo_functions.h"
00047 #include "kmo_cpl_extensions.h"
00048 #include "kmo_dfs.h"
00049 #include "kmo_error.h"
00050 #include "kmo_constants.h"
00051 #include "kmo_debug.h"
00052 
00053 /*-----------------------------------------------------------------------------
00054  *                          Functions prototypes
00055  *----------------------------------------------------------------------------*/
00056 
00057 static int kmo_illumination_create(cpl_plugin *);
00058 static int kmo_illumination_exec(cpl_plugin *);
00059 static int kmo_illumination_destroy(cpl_plugin *);
00060 static int kmo_illumination(cpl_parameterlist *, cpl_frameset *);
00061 
00062 /*-----------------------------------------------------------------------------
00063  *                          Static variables
00064  *----------------------------------------------------------------------------*/
00065 
00066 static char kmo_illumination_description[] =
00067 "This recipe creates the spatial non-uniformity calibration frame needed for\n"
00068 "all three detectors. It must be called after the kmo_wave_cal-recipe, which\n"
00069 "generates the spectral calibration frame needed in this recipe. As input at\n"
00070 "least a sky, a master dark, a master flat and the spatial and spectral cali-\n"
00071 "bration frames are required.\n"
00072 "\n"
00073 "BASIC PARAMETERS:\n"
00074 "-----------------\n"
00075 "--imethod\n"
00076 "The interpolation method used for reconstruction.\n"
00077 "\n"
00078 "--range\n"
00079 "The spectral ranges to combine when collapsing the reconstructed cubes. e.g.\n"
00080 "\"x1_start,x1_end;x2_start,x2_end\" (microns)\n"
00081 "\n"
00082 "ADVANCED PARAMETERS\n"
00083 "-------------------\n"
00084 "--add-all\n"
00085 "By default the first FLAT_SKY frame is omitted, since in the\n"
00086 "KMOS_spec_cal_skyflat template this is an acquisition frame to estimate the\n"
00087 "needed exposure time for the subsequent FLAT_SKY frames. If anyway all frames\n"
00088 "should be considered, set this parameter to TRUE.\n"
00089 "\n"
00090 "--neighborhoodRange\n"
00091 "Defines the range to search for neighbors during reconstruction\n"
00092 "\n"
00093 "--b_samples\n"
00094 "The number of samples in spectral direction for the reconstructed cube.\n"
00095 "Ideally this number should be greater than 2048, the detector size.\n"
00096 "\n"
00097 "--b_start\n"
00098 "--b_end\n"
00099 "Used to define manually the start and end wavelength for the reconstructed\n"
00100 "cube. By default the internally defined values are used.\n"
00101 "\n"
00102 "--cmethod\n"
00103 "Following methods of frame combination are available:\n"
00104 "   * 'ksigma' (Default)\n"
00105 "   An iterative sigma clipping. For each position all pixels in the spectrum\n"
00106 "   are examined. If they deviate significantly, they will be rejected according\n"
00107 "   to the conditions:\n"
00108 "       val > mean + stdev * cpos_rej\n"
00109 "   and\n"
00110 "       val < mean - stdev * cneg_rej\n"
00111 "   where --cpos_rej, --cneg_rej and --citer are the corresponding configuration\n"
00112 "   parameters. In the first iteration median and percentile level are used.\n"
00113 "\n"
00114 "   * 'median'\n"
00115 "   At each pixel position the median is calculated.\n"
00116 "\n"
00117 "   * 'average'\n"
00118 "   At each pixel position the average is calculated.\n"
00119 "\n"
00120 "   * 'sum'\n"
00121 "   At each pixel position the sum is calculated.\n"
00122 "\n"
00123 "   * 'min_max'\n"
00124 "   The specified number of minimum and maximum pixel values will be rejected.\n"
00125 "   --cmax and --cmin apply to this method.\n"
00126 "\n"
00127 "--cpos_rej\n"
00128 "--cneg_rej\n"
00129 "--citer\n"
00130 "see --cmethod='ksigma'\n"
00131 "\n"
00132 "--cmax\n"
00133 "--cmin\n"
00134 "see --cmethod='min_max'\n"
00135 "\n"
00136 "-------------------------------------------------------------------------------\n"
00137 "  Input files:\n"
00138 "\n"
00139 "   DO                    KMOS                                                  \n"
00140 "   category              Type   Explanation                    Required #Frames\n"
00141 "   --------              -----  -----------                    -------- -------\n"
00142 "   FLAT_SKY               F2D   Sky exposures                     Y      1-n   \n"
00143 "                                (at least 3 frames recommended)                \n"
00144 "   MASTER_DARK            F2D   Master dark                       Y       1    \n"
00145 "   MASTER_FLAT            F2D   Master flat                       Y       1    \n"
00146 "   XCAL                   F2D   x calibration frame               Y       1    \n"
00147 "   YCAL                   F2D   y calibration frame               Y       1    \n"
00148 "   LCAL                   F2D   Wavelength calib. frame           Y       1    \n"
00149 "   WAVE_BAND              F2L   Table with start-/end-wavelengths Y       1    \n"
00150 "   FLAT_EDGE              F2L   Table with fitted slitlet edges   N      0,1   \n"
00151 "\n"
00152 "  Output files:\n"
00153 "\n"
00154 "   DO                    KMOS\n"
00155 "   category              Type   Explanation\n"
00156 "   --------              -----  -----------\n"
00157 "   ILLUM_CORR            F2I    Illumination calibration frame   \n"
00158 "   If FLAT_EDGE is provided: \n"
00159 "   SKYFLAT_EDGE          F2L    Frame containing parameters of fitted \n"
00160 "                                slitlets of all IFUs of all detectors\n"
00161 "-------------------------------------------------------------------------------\n"
00162 "\n";
00163 
00164 /*-----------------------------------------------------------------------------
00165  *                              Functions code
00166  *----------------------------------------------------------------------------*/
00167 
00184 int cpl_plugin_get_info(cpl_pluginlist *list)
00185 {
00186     cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe);
00187     cpl_plugin *plugin = &recipe->interface;
00188 
00189     cpl_plugin_init(plugin,
00190                         CPL_PLUGIN_API,
00191                         KMOS_BINARY_VERSION,
00192                         CPL_PLUGIN_TYPE_RECIPE,
00193                         "kmo_illumination",
00194                         "Create a calibration frame to correct spatial "
00195                         "non-uniformity of flatfield.",
00196                         kmo_illumination_description,
00197                         "Alex Agudo Berbel",
00198                         "agudo@mpe.mpg.de",
00199                         kmos_get_license(),
00200                         kmo_illumination_create,
00201                         kmo_illumination_exec,
00202                         kmo_illumination_destroy);
00203 
00204     cpl_pluginlist_append(list, plugin);
00205 
00206     return 0;
00207 }
00208 
00216 static int kmo_illumination_create(cpl_plugin *plugin)
00217 {
00218     cpl_recipe *recipe;
00219     cpl_parameter *p;
00220 
00221     /* Check that the plugin is part of a valid recipe */
00222     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00223         recipe = (cpl_recipe *)plugin;
00224     else
00225         return -1;
00226 
00227     /* Create the parameters list in the cpl_recipe object */
00228     recipe->parameters = cpl_parameterlist_new();
00229 
00230     /* Fill the parameters list */
00231     /* --imethod */
00232     p = cpl_parameter_new_value("kmos.kmo_illumination.imethod",
00233                                 CPL_TYPE_STRING,
00234                                 "Method to use for interpolation: "
00235                                 "[\"NN\" (nearest neighbour), "
00236                                 "\"lwNN\" (linear weighted nearest neighbor),"
00237                                 "\"swNN\" (square weighted nearest neighbor)"
00238                                 "\"MS\" (Modified Shepard's method)"
00239                                 "\"CS\" (Cubic spline)]",
00240                                 "kmos.kmo_illumination",
00241                                 "CS");
00242     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "imethod");
00243     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00244     cpl_parameterlist_append(recipe->parameters, p);
00245 
00246     /* --neighborhoodRange */
00247     p = cpl_parameter_new_value("kmos.kmo_illumination.neighborhoodRange",
00248                                 CPL_TYPE_DOUBLE,
00249                                 "Defines the range to search for neighbors."
00250                                 "in pixels",
00251                                 "kmos.kmo_illumination",
00252                                 1.001);
00253     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "neighborhoodRange");
00254     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00255     cpl_parameterlist_append(recipe->parameters, p);
00256 
00257     /* --range */
00258     p = cpl_parameter_new_value("kmos.kmo_illumination.range",
00259                                 CPL_TYPE_STRING,
00260                                 "The spectral ranges to combine when collapsing"
00261                                 "the reconstructed cubes. e.g."
00262                                 "\"x1_start,x1_end;x2_start,x2_end\" (microns)",
00263                                 "kmos.kmo_illumination",
00264                                 "");
00265     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "range");
00266     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00267     cpl_parameterlist_append(recipe->parameters, p);
00268 
00269     /* --flux */
00270     p = cpl_parameter_new_value("kmos.kmo_illumination.flux",
00271                                 CPL_TYPE_BOOL,
00272                                 "TRUE: Apply flux conservation. FALSE: otherwise",
00273                                 "kmos.kmo_illumination",
00274                                 FALSE);
00275     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "flux");
00276     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00277     cpl_parameterlist_append(recipe->parameters, p);
00278 
00279     /* --add-all */
00280     p = cpl_parameter_new_value("kmos.kmo_illumination.add-all",
00281                                 CPL_TYPE_BOOL,
00282                                 "FALSE: omit 1st FLAT_SKY frame (acquisition), "
00283                                 "TRUE: don't perform any checks, add them all",
00284                                 "kmos.kmo_illumination",
00285                                 FALSE);
00286     cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "add-all");
00287     cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
00288     cpl_parameterlist_append(recipe->parameters, p);
00289 
00290     // add parameters for band-definition
00291     kmo_band_pars_create(recipe->parameters,
00292                          "kmos.kmo_illumination");
00293 
00294     // add parameters for combining
00295     return kmo_combine_pars_create(recipe->parameters,
00296                                    "kmos.kmo_illumination",
00297                                    DEF_REJ_METHOD,
00298                                    FALSE);
00299 }
00300 
00306 static int kmo_illumination_exec(cpl_plugin *plugin)
00307 {
00308     cpl_recipe  *recipe;
00309 
00310     /* Get the recipe out of the plugin */
00311     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00312         recipe = (cpl_recipe *)plugin;
00313     else return -1;
00314 
00315     return kmo_illumination(recipe->parameters, recipe->frames);
00316 }
00317 
00323 static int kmo_illumination_destroy(cpl_plugin *plugin)
00324 {
00325     cpl_recipe *recipe;
00326 
00327     /* Get the recipe out of the plugin */
00328     if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
00329         recipe = (cpl_recipe *)plugin;
00330     else return -1 ;
00331 
00332     cpl_parameterlist_delete(recipe->parameters);
00333     return 0 ;
00334 }
00335 
00350 static int kmo_illumination(cpl_parameterlist *parlist, cpl_frameset *frameset)
00351 {
00352     int              ret_val                    = 0,
00353                      nr_devices                 = 0,
00354                      ifu_nr                     = 0,
00355                      nx                         = 0,
00356                      ny                         = 0,
00357                      process_noise              = FALSE,
00358                      cmax                       = 0,
00359                      cmin                       = 0,
00360                      citer                      = 0,
00361                      *bounds                    = NULL,
00362                      cnt                        = 0,
00363                      qc_max_dev_id              = 0,
00364                      qc_max_nonunif_id          = 0,
00365                      flux                       = FALSE,
00366                      add_all_sky                = FALSE,
00367                      same_exptime               = TRUE,
00368                      has_flat_edge              = FALSE;
00369     const int        *punused_ifus              = NULL;
00370     float            *pbad_pix_mask             = NULL;
00371     double           exptime                    = 0.,
00372                      exptime1                   = 0.,
00373                      exptime2                   = 0.,
00374                      cpos_rej                   = 0.0,
00375                      cneg_rej                   = 0.0,
00376                      neighborhoodRange          = 1.001,
00377                      mean_data                  = 0.0,
00378                      ifu_crpix                  = 0.0,
00379                      ifu_crval                  = 0.0,
00380                      ifu_cdelt                  = 0.0,
00381                      qc_spat_unif               = 0.0,
00382                      qc_max_dev                 = 0.0,
00383                      qc_max_nonunif             = 0.0,
00384                      tmp_stdev                  = 0.0,
00385                      tmp_mean                   = 0.0,
00386                      rotangle                   = 0.0,
00387                      tmp_rotangle               = 0.0,
00388                      rotangle_found             = 0.0;
00389     char             *keyword                   = NULL,
00390                      *fn_lut                    = NULL,
00391                      *suffix                    = NULL,
00392                      *extname                   = NULL,
00393                      *filter                    = NULL;
00394     const char       *method                    = NULL,
00395                      *cmethod                   = NULL,
00396                      *filter_id_l               = NULL,
00397                      *filter_id                 = NULL,
00398                      *ranges_txt                = NULL;
00399     cpl_array        *calTimestamp              = NULL,
00400                      **unused_ifus_before       = NULL,
00401                      **unused_ifus_after        = NULL;
00402     cpl_frame        *frame                     = NULL,
00403                      *xcalFrame                 = NULL,
00404                      *ycalFrame                 = NULL,
00405                      *lcalFrame                 = NULL;
00406     cpl_frameset     *frameset_sky              = NULL;
00407     cpl_image        *img_in                    = NULL,
00408                      *img_dark                  = NULL,
00409                      *img_dark_noise            = NULL,
00410                      *img_flat                  = NULL,
00411                      *img_flat_noise            = NULL,
00412                      *combined_data             = NULL,
00413                      *combined_noise            = NULL,
00414                      *xcal                      = NULL,
00415                      *ycal                      = NULL,
00416                      *lcal                      = NULL,
00417                      *bad_pix_mask              = NULL,
00418                      *data_ifu                  = NULL,
00419                      *noise_ifu                 = NULL,
00420                      **stored_data_images       = NULL,
00421                      **stored_noise_images      = NULL;
00422     cpl_imagelist    *cube_data                 = NULL,
00423                      *cube_noise                = NULL,
00424                      *detector_in               = NULL,
00425                      **stored_data_cubes        = NULL,
00426                      **stored_noise_cubes       = NULL;
00427     cpl_matrix       **edgepars                 = NULL;
00428     cpl_propertylist *main_header               = NULL,
00429                      *tmp_header                = NULL,
00430                      *sub_header                = NULL,
00431                      **stored_sub_data_headers  = NULL,
00432                      **stored_sub_noise_headers = NULL;
00433     cpl_table        *band_table                = NULL,
00434                      ***edge_table_sky          = NULL,
00435                      **edge_table_flat          = NULL;
00436     cpl_vector       *ranges                    = NULL,
00437                      *identified_slices         = NULL,
00438                      *calAngles                 = NULL,
00439                      **slitlet_ids              = NULL,
00440                      *shift_vec                 = NULL,
00441                      *edge_vec                  = NULL;
00442     main_fits_desc   desc_sky,
00443                      desc_dark,
00444                      desc_flat,
00445                      desc_xcal,
00446                      desc_ycal,
00447                      desc_lcal;
00448     gridDefinition   gd;
00449 
00450     KMO_TRY
00451     {
00452         kmo_init_fits_desc(&desc_sky);
00453         kmo_init_fits_desc(&desc_dark);
00454         kmo_init_fits_desc(&desc_flat);
00455         kmo_init_fits_desc(&desc_xcal);
00456         kmo_init_fits_desc(&desc_ycal);
00457         kmo_init_fits_desc(&desc_lcal);
00458 
00459         /* --- check input --- */
00460         KMO_TRY_ASSURE((parlist != NULL) &&
00461                        (frameset != NULL),
00462                        CPL_ERROR_NULL_INPUT,
00463                        "Not all input data is provided!");
00464 
00465         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, FLAT_SKY) >= 1,
00466                        CPL_ERROR_ILLEGAL_INPUT,
00467                        "One or more FLAT_SKY frames are required!");
00468 
00469         if (cpl_frameset_count_tags(frameset, FLAT_SKY) < 3) {
00470             cpl_msg_warning(cpl_func, "It is recommended to provide at least "
00471                                       "3 FLAT_SKY frames!");
00472         }
00473 
00474         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, MASTER_DARK) == 1,
00475                        CPL_ERROR_ILLEGAL_INPUT,
00476                        "Exactly one MASTER_DARK frame is required!");
00477 
00478         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, MASTER_FLAT) == 1,
00479                        CPL_ERROR_ILLEGAL_INPUT,
00480                        "Exactly one MASTER_FLAT frame is required!");
00481 
00482         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, XCAL) == 1,
00483                        CPL_ERROR_ILLEGAL_INPUT,
00484                        "Exactly one XCAL frame is required!");
00485 
00486         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, YCAL) == 1,
00487                        CPL_ERROR_ILLEGAL_INPUT,
00488                        "Exactly one YCAL frame is required!");
00489 
00490         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, LCAL) == 1,
00491                        CPL_ERROR_ILLEGAL_INPUT,
00492                        "Exactly one LCAL frame is required!");
00493 
00494         KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, WAVE_BAND) == 1,
00495                        CPL_ERROR_ILLEGAL_INPUT,
00496                        "Exactly one WAVE_BAND frame is required!");
00497 
00498         KMO_TRY_ASSURE((cpl_frameset_count_tags(frameset, FLAT_EDGE) == 1) ||
00499                        (cpl_frameset_count_tags(frameset, FLAT_EDGE) == 0),
00500                        CPL_ERROR_ILLEGAL_INPUT,
00501                        "Exactly one FLAT_EDGE frame is required!");
00502 
00503         has_flat_edge = cpl_frameset_count_tags(frameset, FLAT_EDGE);
00504 
00505         KMO_TRY_ASSURE(kmo_dfs_set_groups(frameset, "kmo_illumination") == 1,
00506                        CPL_ERROR_ILLEGAL_INPUT,
00507                        "Cannot identify RAW and CALIB frames!");
00508 
00509         /* --- get parameters --- */
00510         cpl_msg_info("", "--- Parameter setup for kmo_illumination ---");
00511 
00512         KMO_TRY_EXIT_IF_NULL(
00513             method = kmo_dfs_get_parameter_string(parlist,
00514                                               "kmos.kmo_illumination.imethod"));
00515 
00516         KMO_TRY_ASSURE((strcmp(method, "NN") == 0) ||
00517                        (strcmp(method, "lwNN") == 0) ||
00518                        (strcmp(method, "swNN") == 0) ||
00519                        (strcmp(method, "MS") == 0) ||
00520                        (strcmp(method, "CS") == 0),
00521                        CPL_ERROR_ILLEGAL_INPUT,
00522                        "method must be either \"NN\", \"lwNN\", "
00523                        "\"swNN\", \"MS\" or \"CS\"!");
00524 
00525         KMO_TRY_EXIT_IF_ERROR(
00526             kmo_dfs_print_parameter_help(parlist,
00527                                         "kmos.kmo_illumination.imethod"));
00528 
00529         neighborhoodRange = kmo_dfs_get_parameter_double(parlist,
00530                 "kmos.kmo_illumination.neighborhoodRange");
00531         KMO_TRY_CHECK_ERROR_STATE();
00532 
00533         KMO_TRY_ASSURE(neighborhoodRange > 0.0,
00534                 CPL_ERROR_ILLEGAL_INPUT,
00535                 "neighborhoodRange must be greater than 0.0");
00536 
00537         KMO_TRY_EXIT_IF_ERROR(
00538             kmo_dfs_print_parameter_help(parlist,
00539                                     "kmos.kmo_illumination.neighborhoodRange"));
00540 
00541         ranges_txt = kmo_dfs_get_parameter_string(parlist,
00542                                                   "kmos.kmo_illumination.range");
00543         KMO_TRY_CHECK_ERROR_STATE();
00544 
00545         KMO_TRY_EXIT_IF_ERROR(
00546             kmo_dfs_print_parameter_help(parlist, "kmos.kmo_illumination.range"));
00547 
00548         ranges = kmo_identify_ranges(ranges_txt);
00549         KMO_TRY_CHECK_ERROR_STATE();
00550 
00551         flux = kmo_dfs_get_parameter_bool(parlist,
00552                                           "kmos.kmo_illumination.flux");
00553 
00554         KMO_TRY_ASSURE((flux == 0) ||
00555                        (flux == 1),
00556                        CPL_ERROR_ILLEGAL_INPUT,
00557                        "flux must be either FALSE or TRUE!");
00558 
00559         KMO_TRY_EXIT_IF_ERROR(
00560             kmo_dfs_print_parameter_help(parlist, "kmos.kmo_illumination.flux"));
00561 
00562         add_all_sky = kmo_dfs_get_parameter_bool(parlist,
00563                                                  "kmos.kmo_illumination.add-all");
00564 
00565         KMO_TRY_ASSURE((add_all_sky == 0) ||
00566                        (add_all_sky == 1),
00567                        CPL_ERROR_ILLEGAL_INPUT,
00568                        "add_all must be either FALSE or TRUE!");
00569 
00570         KMO_TRY_EXIT_IF_ERROR(
00571             kmo_dfs_print_parameter_help(parlist, "kmos.kmo_illumination.add-all"));
00572 
00573         kmo_band_pars_load(parlist, "kmos.kmo_illumination");
00574 
00575         KMO_TRY_EXIT_IF_ERROR(
00576             kmo_combine_pars_load(parlist,
00577                                   "kmos.kmo_illumination",
00578                                   &cmethod,
00579                                   &cpos_rej,
00580                                   &cneg_rej,
00581                                   &citer,
00582                                   &cmin,
00583                                   &cmax,
00584                                   FALSE));
00585         cpl_msg_info("", "-------------------------------------------");
00586 
00587         // check if filter_id, grating_id and rotator offset match for all
00588         // detectors
00589         KMO_TRY_EXIT_IF_ERROR(
00590             kmo_check_frameset_setup(frameset, FLAT_SKY,
00591                                        TRUE, FALSE, TRUE));
00592         KMO_TRY_EXIT_IF_ERROR(
00593             kmo_check_frame_setup(frameset, FLAT_SKY, XCAL,
00594                                        TRUE, FALSE, TRUE));
00595         KMO_TRY_EXIT_IF_ERROR(
00596             kmo_check_frame_setup(frameset, XCAL, YCAL,
00597                                        TRUE, FALSE, TRUE));
00598         KMO_TRY_EXIT_IF_ERROR(
00599             kmo_check_frame_setup(frameset, XCAL, LCAL,
00600                                        TRUE, FALSE, TRUE));
00601         KMO_TRY_EXIT_IF_ERROR(
00602             kmo_check_frame_setup(frameset, XCAL, MASTER_FLAT,
00603                                        TRUE, FALSE, TRUE));
00604 
00605         KMO_TRY_EXIT_IF_NULL(
00606             frame = kmo_dfs_get_frame(frameset, XCAL));
00607         KMO_TRY_EXIT_IF_NULL(
00608             suffix = kmo_dfs_get_suffix(frame, TRUE, FALSE));
00609 
00610         KMO_TRY_EXIT_IF_ERROR(
00611             kmo_check_frame_setup_md5(frameset));
00612 
00613         cpl_msg_info("", "Detected instrument setup:   %s", suffix+1);
00614         cpl_msg_info("", "(grating 1, 2 & 3)");
00615 
00616         // check which IFUs are active for all frames
00617         KMO_TRY_EXIT_IF_NULL(
00618             unused_ifus_before = kmo_get_unused_ifus(frameset, 0, 0));
00619 
00620         KMO_TRY_EXIT_IF_NULL(
00621             unused_ifus_after = kmo_duplicate_unused_ifus(unused_ifus_before));
00622 
00623         kmo_print_unused_ifus(unused_ifus_before, FALSE);
00624 
00625         // load desc for MASTER_DARK and check
00626         KMO_TRY_EXIT_IF_NULL(
00627             frame = kmo_dfs_get_frame(frameset, MASTER_DARK));
00628         desc_dark = kmo_identify_fits_header(
00629                     cpl_frame_get_filename(frame));
00630         KMO_TRY_CHECK_ERROR_STATE_MSG("MASTER_DARK frame doesn't seem to "
00631                                       "be in KMOS-format!");
00632         KMO_TRY_ASSURE((desc_dark.nr_ext == 6) &&
00633                        (desc_dark.ex_badpix == FALSE) &&
00634                        (desc_dark.fits_type == f2d_fits) &&
00635                        (desc_dark.frame_type == detector_frame),
00636                        CPL_ERROR_ILLEGAL_INPUT,
00637                        "MASTER_DARK isn't in the correct format!!!");
00638         nx = desc_dark.naxis1;
00639         ny = desc_dark.naxis2;
00640 
00641         // load desc for MASTER_FLAT and check
00642         KMO_TRY_EXIT_IF_NULL(
00643             frame = kmo_dfs_get_frame(frameset, MASTER_FLAT));
00644         desc_flat = kmo_identify_fits_header(cpl_frame_get_filename(frame));
00645         KMO_TRY_CHECK_ERROR_STATE_MSG("MASTER_FLAT frame doesn't seem to "
00646                                       "be in KMOS-format!");
00647         KMO_TRY_ASSURE((desc_flat.nr_ext % 6 == 0) &&
00648                        (desc_flat.ex_badpix == FALSE) &&
00649                        (desc_flat.fits_type == f2d_fits) &&
00650                        (desc_flat.frame_type == detector_frame),
00651                        CPL_ERROR_ILLEGAL_INPUT,
00652                        "MASTER_FLAT isn't in the correct format!!!");
00653 
00654         // load desc for XCAL and check
00655         KMO_TRY_EXIT_IF_NULL(
00656             xcalFrame = kmo_dfs_get_frame(frameset, XCAL));
00657         desc_xcal = kmo_identify_fits_header(cpl_frame_get_filename(xcalFrame));
00658         KMO_TRY_CHECK_ERROR_STATE_MSG("XCAL frame doesn't seem to "
00659                                       "be in KMOS-format!");
00660         KMO_TRY_ASSURE((desc_xcal.nr_ext % 3 == 0) &&
00661                        (desc_xcal.ex_badpix == FALSE) &&
00662                        (desc_xcal.fits_type == f2d_fits) &&
00663                        (desc_xcal.frame_type == detector_frame),
00664                        CPL_ERROR_ILLEGAL_INPUT,
00665                        "XCAL isn't in the correct format!!!");
00666         KMO_TRY_ASSURE((desc_xcal.naxis1 == nx) &&
00667                        (desc_xcal.naxis2 == ny),
00668                        CPL_ERROR_ILLEGAL_INPUT,
00669                        "MASTER_DARK and XCAL frame haven't same dimensions! "
00670                        "(x,y): (%d,%d) vs (%d,%d)",
00671                        nx, ny, desc_xcal.naxis1, desc_xcal.naxis2);
00672 
00673         nr_devices = desc_xcal.nr_ext;
00674 
00675         // load desc for YCAL and check
00676         KMO_TRY_EXIT_IF_NULL(
00677             ycalFrame = kmo_dfs_get_frame(frameset, YCAL));
00678         desc_ycal = kmo_identify_fits_header(cpl_frame_get_filename(ycalFrame));
00679         KMO_TRY_CHECK_ERROR_STATE_MSG("YCAL frame doesn't seem to "
00680                                       "be in KMOS-format!");
00681         KMO_TRY_ASSURE((desc_ycal.nr_ext % 3 == 0) &&
00682                        (desc_ycal.ex_badpix == FALSE) &&
00683                        (desc_ycal.fits_type == f2d_fits) &&
00684                        (desc_ycal.frame_type == detector_frame),
00685                        CPL_ERROR_ILLEGAL_INPUT,
00686                        "YCAL isn't in the correct format!!!");
00687         KMO_TRY_ASSURE((desc_ycal.naxis1 == nx) &&
00688                        (desc_ycal.naxis2 == ny) &&
00689                        (desc_ycal.nr_ext == nr_devices),
00690                        CPL_ERROR_ILLEGAL_INPUT,
00691                        "MASTER_DARK and YCAL frame haven't same dimensions! "
00692                        "(x,y): (%d,%d) vs (%d,%d)",
00693                        nx, ny, desc_ycal.naxis1, desc_ycal.naxis2);
00694 
00695         // load desc for LCAL and check
00696         KMO_TRY_EXIT_IF_NULL(
00697             lcalFrame = kmo_dfs_get_frame(frameset, LCAL));
00698         desc_lcal = kmo_identify_fits_header(cpl_frame_get_filename(lcalFrame));
00699         KMO_TRY_CHECK_ERROR_STATE_MSG("LCAL frame doesn't seem to "
00700                                       "be in KMOS-format!");
00701         KMO_TRY_ASSURE((desc_lcal.nr_ext % 3 == 0) &&
00702                        (desc_lcal.ex_badpix == FALSE) &&
00703                        (desc_lcal.fits_type == f2d_fits) &&
00704                        (desc_lcal.frame_type == detector_frame),
00705                        CPL_ERROR_ILLEGAL_INPUT,
00706                        "LCAL isn't in the correct format!!!");
00707         KMO_TRY_ASSURE((desc_lcal.naxis1 == nx) &&
00708                        (desc_lcal.naxis2 == ny) &&
00709                        (desc_lcal.nr_ext == nr_devices),
00710                        CPL_ERROR_ILLEGAL_INPUT,
00711                        "MASTER_DARK and LCAL frame haven't same dimensions! "
00712                        "(x,y): (%d,%d) vs (%d,%d)",
00713                        nx, ny, desc_lcal.naxis1, desc_lcal.naxis2);
00714         KMO_TRY_EXIT_IF_NULL(
00715             tmp_header = kmo_dfs_load_primary_header(frameset, LCAL));
00716 
00717         // load desc for FLAT_SKY and check
00718         nr_devices = KMOS_NR_DETECTORS;
00719         KMO_TRY_EXIT_IF_NULL(
00720             frame = kmo_dfs_get_frame(frameset, FLAT_SKY));
00721 
00722         KMO_TRY_EXIT_IF_NULL(
00723             main_header = kmclipm_propertylist_load(
00724                                          cpl_frame_get_filename(frame), 0));
00725         rotangle = cpl_propertylist_get_double(main_header, ROTANGLE);
00726         KMO_TRY_CHECK_ERROR_STATE_MSG("Cannot retrieve ROTANGLE FITS keyword from sky frame!");
00727         kmclipm_strip_angle(&rotangle);
00728         cpl_propertylist_delete(main_header); main_header = NULL;
00729 
00730         cnt = 1;
00731         while (frame != NULL) {
00732             KMO_TRY_EXIT_IF_NULL(
00733                 main_header = kmclipm_propertylist_load(
00734                                              cpl_frame_get_filename(frame), 0));
00735 
00736             desc_sky = kmo_identify_fits_header(
00737                         cpl_frame_get_filename(frame));
00738             KMO_TRY_CHECK_ERROR_STATE_MSG("FLAT_SKY frame doesn't seem to "
00739                                           "be in KMOS-format!");
00740             KMO_TRY_ASSURE((desc_sky.nr_ext == 3) &&
00741                            (desc_sky.ex_badpix == FALSE) &&
00742                            (desc_sky.fits_type == raw_fits) &&
00743                            (desc_sky.frame_type == detector_frame),
00744                            CPL_ERROR_ILLEGAL_INPUT,
00745                            "FLAT_SKY isn't in the correct format!!!");
00746             KMO_TRY_ASSURE((desc_sky.naxis1 == nx) &&
00747                            (desc_sky.naxis2 == ny) &&
00748                            (desc_sky.nr_ext == nr_devices),
00749                            CPL_ERROR_ILLEGAL_INPUT,
00750                            "MASTER_DARK and FLAT_SKY (no. %d) frame haven't "
00751                            "same dimensions! (x,y): (%d,%d) vs (%d,%d)",
00752                            cnt, nx, ny, desc_flat.naxis1, desc_flat.naxis2);
00753             kmo_free_fits_desc(&desc_sky);
00754             kmo_init_fits_desc(&desc_sky);
00755 
00756             KMO_TRY_ASSURE(
00757                 (kmo_check_lamp(main_header, INS_LAMP1_ST) == FALSE) &&
00758                 (kmo_check_lamp(main_header, INS_LAMP2_ST) == FALSE) &&
00759                 (kmo_check_lamp(main_header, INS_LAMP3_ST) == FALSE) &&
00760                 (kmo_check_lamp(main_header, INS_LAMP4_ST) == FALSE),
00761                 CPL_ERROR_ILLEGAL_INPUT,
00762                 "All lamps must be switched off for the FLAT_SKY frames!");
00763 
00764             // assert that filters have correct IDs and that all detectors of
00765             // all input frames have the same filter set
00766             for (int i = 1; i <= KMOS_NR_DETECTORS; i++) {
00767                 // ESO INS FILTi ID
00768                 KMO_TRY_EXIT_IF_NULL(
00769                     keyword = cpl_sprintf("%s%d%s", IFU_FILTID_PREFIX, i, IFU_FILTID_POSTFIX));
00770                 KMO_TRY_EXIT_IF_NULL(
00771                     filter_id = cpl_propertylist_get_string(main_header, keyword));
00772 
00773                 KMO_TRY_EXIT_IF_NULL(
00774                     filter_id_l = cpl_propertylist_get_string(tmp_header, keyword));
00775                 cpl_free(keyword); keyword = NULL;
00776 
00777                 KMO_TRY_ASSURE((strcmp(filter_id, "IZ") == 0) ||
00778                                (strcmp(filter_id, "YJ") == 0) ||
00779                                (strcmp(filter_id, "H") == 0) ||
00780                                (strcmp(filter_id, "K") == 0) ||
00781                                (strcmp(filter_id, "HK") == 0),
00782                                CPL_ERROR_ILLEGAL_INPUT,
00783                                "Filter ID in primary header must be either 'IZ', "
00784                                "'YJ', 'H', 'K' or " "'HK' !");
00785 
00786                 KMO_TRY_ASSURE(strcmp(filter_id, filter_id_l) == 0,
00787                                CPL_ERROR_ILLEGAL_INPUT,
00788                                "Filter IDs must be the same for FLAT_SKY frame"
00789                                " and lcal frame!"
00790                                "Detector No.: %d\n%s: %s\nLCAL: %s\n",
00791                                i, cpl_frame_get_filename(frame),
00792                                filter_id, filter_id_l);
00793 
00794                 // ESO INS GRATi ID
00795                 KMO_TRY_EXIT_IF_NULL(
00796                     keyword = cpl_sprintf("%s%d%s", IFU_GRATID_PREFIX, i, IFU_GRATID_POSTFIX));
00797                 KMO_TRY_EXIT_IF_NULL(
00798                     filter_id = cpl_propertylist_get_string(main_header, keyword));
00799 
00800                 KMO_TRY_EXIT_IF_NULL(
00801                     filter_id_l = cpl_propertylist_get_string(tmp_header, keyword));
00802                 cpl_free(keyword); keyword = NULL;
00803 
00804                 KMO_TRY_ASSURE((strcmp(filter_id, "IZ") == 0) ||
00805                                (strcmp(filter_id, "YJ") == 0) ||
00806                                (strcmp(filter_id, "H") == 0) ||
00807                                (strcmp(filter_id, "K") == 0) ||
00808                                (strcmp(filter_id, "HK") == 0),
00809                                CPL_ERROR_ILLEGAL_INPUT,
00810                                "Grating ID in primary header must be either "
00811                                "'IZ', 'YJ', 'H', 'K' or " "'HK' !");
00812 
00813                 KMO_TRY_ASSURE(strcmp(filter_id, filter_id_l) == 0,
00814                                CPL_ERROR_ILLEGAL_INPUT,
00815                                "Grating IDs must be the same for FLAT_SKY frame"
00816                                " and lcal frame!"
00817                                "Detector No.: %d\n%s: %s\nLCAL: %s\n",
00818                                i, cpl_frame_get_filename(frame),
00819                                filter_id, filter_id_l);
00820 
00821                 tmp_rotangle = cpl_propertylist_get_double(main_header, ROTANGLE);
00822                 KMO_TRY_CHECK_ERROR_STATE_MSG("Cannot retrieve ROTANGLE FITS keyword from sky frame!");
00823                 kmclipm_strip_angle(&tmp_rotangle);
00824                 KMO_TRY_ASSURE((abs(rotangle - tmp_rotangle) < 10.0) ||
00825                                (abs(rotangle - tmp_rotangle) > 360.-10.) ,
00826                         CPL_ERROR_ILLEGAL_INPUT,
00827                         "OCS ROT NAANGLE of sky flat frames differ to much: %f %f",
00828                         rotangle, tmp_rotangle);
00829             }
00830             cpl_propertylist_delete(main_header); main_header = NULL;
00831 
00832             // get next FLAT_SKY frame
00833             frame = kmo_dfs_get_frame(frameset, NULL);
00834             KMO_TRY_CHECK_ERROR_STATE();
00835             cnt++;
00836         }
00837 
00838         cpl_propertylist_delete(tmp_header); tmp_header = NULL;
00839 
00840         //
00841         // noise will be propagated when:
00842         // MASTER_DARK and MASTER_FLAT have noise extensions and if at least
00843         // 2 FLAT_SKY frames are provided.
00844         // Otherwise noise will be ignored.
00845         //
00846         if (desc_dark.ex_noise &&
00847             desc_flat.ex_noise &&
00848             (cpl_frameset_count_tags(frameset, FLAT_SKY) >= 2)) {
00849             process_noise = TRUE;
00850         }
00851 
00852         if (cpl_frameset_count_tags(frameset, FLAT_SKY) == 1) {
00853             cpl_msg_warning(cpl_func, "cmethod is changed to 'average' "
00854                             "since there is only one input frame! (The output "
00855                             "file won't have any noise extensions)");
00856 
00857             cmethod = "average";
00858         }
00859 
00860         //
00861         // Check whether 1st FLAT_SKY should be omitted
00862         //
00863         KMO_TRY_EXIT_IF_NULL(
00864             frameset_sky = cpl_frameset_new());
00865 
00866         if (add_all_sky) {
00867             // just add all FLAT_SKY frames without check
00868             frame = kmo_dfs_get_frame(frameset, FLAT_SKY);
00869             while (frame != NULL) {
00870                 KMO_TRY_EXIT_IF_ERROR(
00871                     cpl_frameset_insert(frameset_sky, cpl_frame_duplicate(frame)));
00872                 frame = kmo_dfs_get_frame(frameset, NULL);
00873             }
00874             cpl_msg_info("", "Add all FLAT_SKY without checking for acquisition frame.");
00875         } else {
00876             // check if 1st FLAT_SKY has different exposure time and whether to omit it
00877             KMO_TRY_EXIT_IF_NULL(
00878                 frame = kmo_dfs_get_frame(frameset, FLAT_SKY));
00879 
00880             if (cpl_frameset_count_tags(frameset, FLAT_SKY) == 1) {
00881                 // just one FLAT_SKY, always add
00882                 KMO_TRY_EXIT_IF_ERROR(
00883                     cpl_frameset_insert(frameset_sky, cpl_frame_duplicate(frame)));
00884                 KMO_TRY_CHECK_ERROR_STATE();
00885             } else {
00886                 // several FLAT_SKY frames, check exptime
00887 
00888                 // get exptime 1
00889                 KMO_TRY_EXIT_IF_NULL(
00890                     main_header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0));
00891                 exptime1 = cpl_propertylist_get_double(main_header, EXPTIME);
00892                 KMO_TRY_CHECK_ERROR_STATE();
00893                 cpl_propertylist_delete(main_header); main_header = NULL;
00894 
00895                 // get exptime 2
00896                 frame = kmo_dfs_get_frame(frameset, NULL);
00897                 KMO_TRY_EXIT_IF_NULL(
00898                     main_header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0));
00899                 exptime2 = cpl_propertylist_get_double(main_header, EXPTIME);
00900                 KMO_TRY_CHECK_ERROR_STATE();
00901                 cpl_propertylist_delete(main_header); main_header = NULL;
00902 
00903                 // loop remaining frames
00904                 same_exptime = TRUE;
00905                 frame = kmo_dfs_get_frame(frameset, NULL);
00906                 while (same_exptime && (frame != NULL)) {
00907                     KMO_TRY_EXIT_IF_NULL(
00908                         main_header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0));
00909                     exptime = cpl_propertylist_get_double(main_header, EXPTIME);
00910                     KMO_TRY_CHECK_ERROR_STATE();
00911                     cpl_propertylist_delete(main_header); main_header = NULL;
00912                     if (fabs(exptime-exptime2) > 0.01) {
00913                         // not same
00914                         same_exptime = FALSE;
00915                     }
00916                     frame = kmo_dfs_get_frame(frameset, NULL);
00917                 }
00918 
00919                 if (same_exptime) {
00920                     // frame [2,n] have same exptime, add them
00921                     frame = kmo_dfs_get_frame(frameset, FLAT_SKY);
00922                     KMO_TRY_EXIT_IF_NULL(
00923                         main_header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0));
00924                     exptime = cpl_propertylist_get_double(main_header, EXPTIME);
00925                     KMO_TRY_CHECK_ERROR_STATE();
00926                     cpl_propertylist_delete(main_header); main_header = NULL;
00927                     cpl_msg_info("", "Omit FLAT_SKY: %s with EXPTIME of %g sec (acquisition), other frame(s) have EXPTIME of %g sec", cpl_frame_get_filename(frame), exptime, exptime2);
00928                     frame = kmo_dfs_get_frame(frameset, NULL);
00929                     while (frame != NULL) {
00930                         KMO_TRY_EXIT_IF_ERROR(
00931                             cpl_frameset_insert(frameset_sky, cpl_frame_duplicate(frame)));
00932                         frame = kmo_dfs_get_frame(frameset, NULL);
00933                     }
00934                     if (fabs(exptime1-exptime2) < 0.01) {
00935                         cpl_msg_warning("", "The 1st FLAT_SKY has the same exposure time as the following ones. "
00936                                             "It has anyway been omitted since we assume it is an acquisition frame. "
00937                                             "If you want to add it anyway call this recipe with the --add-all parameter");
00938                     }
00939                 } else {
00940                     cpl_msg_error("", "The exposure times of the FLAT_SKY frames don't match!");
00941                     cpl_msg_error("", "We assume that the 1st frame is an acquisition frame and would be omitted.");
00942                     cpl_msg_error("", "The following frames should have the same exposure time if they originate from the same template.");
00943                     cpl_msg_error("", "If you want to reduce them anyway call this recipe with the --add-all parameter");
00944                     frame = kmo_dfs_get_frame(frameset, FLAT_SKY);
00945                     while (frame != NULL) {
00946                         KMO_TRY_EXIT_IF_NULL(
00947                             main_header = kmclipm_propertylist_load(cpl_frame_get_filename(frame), 0));
00948                         exptime = cpl_propertylist_get_double(main_header, EXPTIME);
00949                         KMO_TRY_CHECK_ERROR_STATE();
00950                         cpl_propertylist_delete(main_header); main_header = NULL;
00951                         cpl_msg_error("", "FLAT_SKY: %s, EXPTIME: %g", cpl_frame_get_filename(frame), exptime);
00952                         frame = kmo_dfs_get_frame(frameset, NULL);
00953                     }
00954                     cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT);
00955                     KMO_TRY_CHECK_ERROR_STATE();
00956                 }
00957             }
00958         }
00959 
00960         KMO_TRY_EXIT_IF_NULL(
00961             frame = kmo_dfs_get_frame(frameset_sky, FLAT_SKY));
00962         KMO_TRY_EXIT_IF_NULL(
00963             main_header = kmo_dfs_load_primary_header(frameset_sky, FLAT_SKY));
00964         KMO_TRY_EXIT_IF_NULL(
00965             keyword = cpl_sprintf("%s%d%s", IFU_GRATID_PREFIX, 1, IFU_GRATID_POSTFIX));
00966         KMO_TRY_EXIT_IF_NULL(
00967             filter = cpl_sprintf("%s", cpl_propertylist_get_string(main_header, keyword)));
00968         cpl_free(keyword); keyword = NULL;
00969 
00970         //
00971         // set default band-specific ranges for collapsing
00972         //
00973         if (ranges == NULL) {
00974             if (strcmp(filter, "IZ") == 0) {
00975                 ranges_txt = "0.81,1.05";
00976             } else if (strcmp(filter, "YJ") == 0) {
00977                 ranges_txt = "1.025,1.3";
00978             } else if (strcmp(filter, "H") == 0) {
00979                 ranges_txt = "1.5,1.7";
00980             } else if (strcmp(filter, "K") == 0) {
00981                 ranges_txt = "2.1,2.35";
00982             } else if (strcmp(filter, "HK") == 0) {
00983                 ranges_txt = "1.5,1.7;2.1,2.35";
00984 //                ranges_txt = "1.5,1.7";
00985             } else {
00986                 KMO_TRY_ASSURE(1 == 0,
00987                                CPL_ERROR_ILLEGAL_INPUT,
00988                                "We really shouldn't get here...");
00989             }
00990             cpl_msg_info("", "Spectral range to collapse has been set to %s um for this band.", ranges_txt);
00991             ranges = kmo_identify_ranges(ranges_txt);
00992             KMO_TRY_CHECK_ERROR_STATE();
00993         }
00994 
00995         // setup grid definition, wavelength start and end points will be set
00996         // in the detector loop
00997         KMO_TRY_EXIT_IF_ERROR(
00998             kmclipm_setup_grid(&gd, method, neighborhoodRange));
00999 
01000         // create filename for LUT
01001         KMO_TRY_EXIT_IF_NULL(
01002             fn_lut = cpl_sprintf("%s%s", "lut", suffix));
01003 
01004         // extract bounds
01005         KMO_TRY_EXIT_IF_NULL(
01006             tmp_header = kmclipm_propertylist_load(
01007                                          cpl_frame_get_filename(xcalFrame), 0));
01008         KMO_TRY_EXIT_IF_NULL(
01009             bounds = kmclipm_extract_bounds(tmp_header));
01010         cpl_propertylist_delete(tmp_header); tmp_header = NULL;
01011 
01012         // get timestamps of xcal, ycal & lcal
01013         KMO_TRY_EXIT_IF_NULL(
01014             calTimestamp = kmo_get_timestamps(xcalFrame, ycalFrame, lcalFrame));
01015 
01016         // create arrays to hold reconstructed data and noise cubes and
01017         // their headers
01018         KMO_TRY_EXIT_IF_NULL(
01019             stored_data_cubes = (cpl_imagelist**)cpl_calloc(nr_devices * KMOS_IFUS_PER_DETECTOR,
01020                                                             sizeof(cpl_imagelist*)));
01021         KMO_TRY_EXIT_IF_NULL(
01022             stored_noise_cubes = (cpl_imagelist**)cpl_calloc(nr_devices * KMOS_IFUS_PER_DETECTOR,
01023                                                              sizeof(cpl_imagelist*)));
01024         KMO_TRY_EXIT_IF_NULL(
01025             stored_data_images = (cpl_image**)cpl_calloc(nr_devices * KMOS_IFUS_PER_DETECTOR,
01026                                                          sizeof(cpl_image*)));
01027         KMO_TRY_EXIT_IF_NULL(
01028             stored_noise_images = (cpl_image**)cpl_calloc(nr_devices * KMOS_IFUS_PER_DETECTOR,
01029                                                           sizeof(cpl_image*)));
01030         KMO_TRY_EXIT_IF_NULL(
01031             stored_sub_data_headers = (cpl_propertylist**)cpl_calloc(nr_devices * KMOS_IFUS_PER_DETECTOR,
01032                                                                      sizeof(cpl_propertylist*)));
01033         KMO_TRY_EXIT_IF_NULL(
01034             stored_sub_noise_headers = (cpl_propertylist**)cpl_calloc(nr_devices * KMOS_IFUS_PER_DETECTOR,
01035                                                                       sizeof(cpl_propertylist*)));
01036         KMO_TRY_EXIT_IF_NULL(
01037             edge_table_sky = (cpl_table***)cpl_calloc(KMOS_NR_DETECTORS,
01038                                                       sizeof(cpl_table**)));
01039         KMO_TRY_EXIT_IF_NULL(
01040             edge_table_flat = (cpl_table**)cpl_calloc(KMOS_IFUS_PER_DETECTOR,
01041                                                       sizeof(cpl_table*)));
01042         KMO_TRY_EXIT_IF_NULL(
01043             calAngles = cpl_vector_new(3));
01044 
01045         //
01046         // loop through all detectors
01047         //
01048         for (int det_nr = 1; det_nr <= nr_devices; det_nr++) {
01049             cpl_msg_info("","Processing detector No. %d", det_nr);
01050 
01051             KMO_TRY_EXIT_IF_NULL(
01052                 detector_in = cpl_imagelist_new());
01053 
01054             // load data of det_nr of all FLAT_SKY frames into an imagelist
01055             KMO_TRY_EXIT_IF_NULL(
01056                 img_in = kmo_dfs_load_image(frameset_sky, FLAT_SKY, det_nr, FALSE, TRUE, NULL));
01057 
01058             cnt = 0;
01059             while (img_in != NULL) {
01060                 cpl_imagelist_set(detector_in, img_in, cnt);
01061                 KMO_TRY_CHECK_ERROR_STATE();
01062 
01063                 /* load same extension of next FLAT_SKY frame*/
01064                 img_in = kmo_dfs_load_image(frameset_sky, NULL, det_nr, FALSE, TRUE, NULL);
01065                 KMO_TRY_CHECK_ERROR_STATE();
01066 
01067                 cnt++;
01068             }
01069 
01070             //
01071             // process imagelist
01072             //
01073 
01074             // combine imagelist (data only) and create noise (stdev of data)
01075             cpl_msg_info("","Combining frames...");
01076             if (process_noise) {
01077                 KMO_TRY_EXIT_IF_ERROR(
01078                     kmclipm_combine_frames(detector_in,
01079                                            NULL,
01080                                            NULL,
01081                                            cmethod,
01082                                            cpos_rej,
01083                                            cneg_rej,
01084                                            citer,
01085                                            cmax,
01086                                            cmin,
01087                                            &combined_data,
01088                                            &combined_noise,
01089                                            -1.0));
01090             } else {
01091                 KMO_TRY_EXIT_IF_ERROR(
01092                     kmclipm_combine_frames(detector_in,
01093                                            NULL,
01094                                            NULL,
01095                                            cmethod,
01096                                            cpos_rej,
01097                                            cneg_rej,
01098                                            citer,
01099                                            cmax,
01100                                            cmin,
01101                                            &combined_data,
01102                                            NULL,
01103                                            -1.0));
01104             }
01105 
01106             if (kmclipm_omit_warning_one_slice > 10) {
01107                 cpl_msg_warning(cpl_func, "Previous warning (number of "
01108                                           "identified slices) occured %d times.",
01109                                 kmclipm_omit_warning_one_slice);
01110                 kmclipm_omit_warning_one_slice = FALSE;
01111             }
01112 
01113             cpl_imagelist_delete(detector_in); detector_in = NULL;
01114 
01115             // load calibration files
01116             KMO_TRY_EXIT_IF_NULL(
01117                 xcal = kmo_dfs_load_cal_image(frameset, XCAL, det_nr, FALSE, rotangle,
01118                                               FALSE, NULL, &rotangle_found));
01119 
01120             KMO_TRY_EXIT_IF_ERROR(
01121                 cpl_vector_set(calAngles, 0, rotangle_found));
01122             KMO_TRY_EXIT_IF_NULL(
01123                 ycal = kmo_dfs_load_cal_image(frameset, YCAL, det_nr, FALSE, rotangle,
01124                                               FALSE, NULL, &rotangle_found));
01125             KMO_TRY_EXIT_IF_ERROR(
01126                 cpl_vector_set(calAngles, 1, rotangle_found));
01127             KMO_TRY_EXIT_IF_NULL(
01128                 lcal = kmo_dfs_load_cal_image(frameset, LCAL, det_nr, FALSE, rotangle,
01129                                               FALSE, NULL, &rotangle_found));
01130             KMO_TRY_EXIT_IF_ERROR(
01131                 cpl_vector_set(calAngles, 2, rotangle_found));
01132 
01133             // load bad pixel mask from XCAL and set NaNs to 0 and all other values to 1
01134             KMO_TRY_EXIT_IF_NULL(
01135                 bad_pix_mask = cpl_image_duplicate(xcal));
01136 
01137             KMO_TRY_EXIT_IF_NULL(
01138                 pbad_pix_mask = cpl_image_get_data_float(bad_pix_mask));
01139             for (int x = 0; x < nx; x++) {
01140                 for (int y = 0; y < ny; y++) {
01141                     if (isnan(pbad_pix_mask[x+nx*y])) {
01142                         pbad_pix_mask[x+nx*y] = 0.;
01143                     } else {
01144                         pbad_pix_mask[x+nx*y] = 1.;
01145                     }
01146                 }
01147             }
01148             KMO_TRY_CHECK_ERROR_STATE();
01149 
01150             //
01151             // calculate SKYFLAT_EDGE
01152             //
01153             if (has_flat_edge) {
01154                 // get edge-edgepars from FLAT_SKY
01155                 KMO_TRY_EXIT_IF_ERROR(
01156                     kmo_calc_edgepars(combined_data,
01157                                       unused_ifus_after[det_nr-1],
01158                                       bad_pix_mask,
01159                                       det_nr,
01160                                       &slitlet_ids,
01161                                       &edgepars));
01162                 KMO_TRY_CHECK_ERROR_STATE();
01163 
01164                 // copy edgepars to table for saving later on
01165                 KMO_TRY_EXIT_IF_NULL(
01166                     edge_table_sky[det_nr-1] = kmo_edgepars_to_table(slitlet_ids, edgepars));
01167 
01168                 if (edgepars != NULL) {
01169                     for (int i = 0; i < KMOS_IFUS_PER_DETECTOR; i++) {
01170                         cpl_matrix_delete(edgepars[i]); edgepars[i] = NULL;
01171                     }
01172                     cpl_free(edgepars); edgepars = NULL;
01173                 }
01174                 if (slitlet_ids != NULL) {
01175                     for (int i = 0; i < KMOS_IFUS_PER_DETECTOR; i++) {
01176                         cpl_vector_delete(slitlet_ids[i]); slitlet_ids[i] = NULL;
01177                     }
01178                     cpl_free(slitlet_ids); slitlet_ids = NULL;
01179                 }
01180                 KMO_TRY_CHECK_ERROR_STATE();
01181 
01182                 //
01183                 // correlate FLAT_EDGE and SKYFLAT_EDGE
01184                 //
01185 
01186                 // load flat_edge from MASTER_FLAT
01187                 KMO_TRY_EXIT_IF_NULL(
01188                     frame = kmo_dfs_get_frame(frameset, FLAT_EDGE));
01189                 for (int j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01190                     ifu_nr = (det_nr-1)*KMOS_IFUS_PER_DETECTOR + j + 1;
01191                     KMO_TRY_EXIT_IF_NULL(
01192                         edge_table_flat[j] = kmclipm_cal_table_load(cpl_frame_get_filename(frame),
01193                                                                     ifu_nr, rotangle, 0, &tmp_rotangle));
01194                 }
01195 
01196                 //
01197                 // calculate shift value
01198                 //
01199 
01200                 KMO_TRY_EXIT_IF_NULL(
01201                     shift_vec = cpl_vector_new(KMOS_IFUS_PER_DETECTOR));
01202                 KMO_TRY_EXIT_IF_NULL(
01203                     edge_vec = cpl_vector_new(2*KMOS_SLITLET_X));
01204 
01205                 // get shift values for each IFU by comparing all edge parameters,
01206                 // rejecting and applying median
01207                 int row = 1024; // middle of frame
01208                 for (int j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01209                     ifu_nr = (det_nr-1)*KMOS_IFUS_PER_DETECTOR + j + 1;
01210                     for (int edgeNr = 0; edgeNr < 2*KMOS_SLITLET_X; edgeNr++) {
01211                         double flatval = kmo_calc_fitted_slitlet_edge(edge_table_flat[j], edgeNr, row);
01212                         double skyval  = kmo_calc_fitted_slitlet_edge(edge_table_sky[det_nr-1][j], edgeNr, row);
01213                         cpl_vector_set(edge_vec, edgeNr, flatval-skyval);
01214                     }
01215 
01216                     // reject deviating edge-differences
01217                     kmclipm_vector *kv = NULL;
01218                     KMO_TRY_EXIT_IF_NULL(
01219                         kv = kmclipm_vector_create(cpl_vector_duplicate(edge_vec)));
01220                     kmclipm_reject_deviant(kv, 3, 3, NULL, NULL);
01221 
01222                     // set shift value for each IFU
01223                     cpl_vector_set(shift_vec, j, kmclipm_vector_get_median(kv, KMCLIPM_ARITHMETIC));
01224                     kmclipm_vector_delete(kv); kv = NULL;
01225                 }
01226                 cpl_vector_delete(edge_vec); edge_vec = NULL;
01227                 KMO_TRY_CHECK_ERROR_STATE();
01228 
01229                 for (int j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01230                     cpl_table_delete(edge_table_flat[j]);
01231                     edge_table_flat[j] = NULL;
01232                 }
01233 
01234                 // take median of all IFU-shift-values
01235                 double shift_val = -cpl_vector_get_median(shift_vec);
01236                 cpl_vector_delete(shift_vec); shift_vec = NULL;
01237 
01238                 cpl_msg_info("", "Shift detector %d by %g pixels.", det_nr, shift_val);
01239 
01240                 int     xdim                = cpl_image_get_size_x(combined_data),
01241                         ydim                = cpl_image_get_size_y(combined_data);
01242                 double  *array_in           = cpl_calloc(xdim, sizeof(double)),
01243                         *array_out          = NULL;
01244                 float   *pcombined_data     = cpl_image_get_data_float(combined_data),
01245                         *pcombined_noise    = NULL;
01246     //            float   *tmpArray           = cpl_calloc(xdim, sizeof(float));
01247                 if (process_noise) {
01248                     pcombined_noise = cpl_image_get_data_float(combined_noise);
01249                 }
01250 
01251                 for (int iy = 0; iy < ydim; iy++) {
01252                     // cubic spline
01253                     for (int ix = 0; ix < xdim; ix++) {
01254                         array_in[ix] = pcombined_data[ix+iy*xdim];
01255                     }
01256                     array_out = cubicspline_reg_reg(xdim, 0., 1., array_in,
01257                                                     xdim, shift_val, 1.0,
01258                                                     NATURAL);
01259                     for (int ix = 0; ix < xdim; ix++) {
01260                       pcombined_data[ix+iy*xdim] = array_out[ix];
01261                     }
01262                     cpl_free(array_out);
01263 
01264     //                // linear
01265     //                for (int ix = 1; ix < xdim; ix++) {
01266     //                    tmpArray[ix-1] = (pcombined_data[ix+iy*xdim]-pcombined_data[(ix-1)+iy*xdim])*shift_val +
01267     //                                     pcombined_data[(ix-1)+iy*xdim];
01268     //                }
01269     //                for (int ix = 1; ix < xdim; ix++) {
01270     //                    pcombined_data[ix+iy*xdim] = tmpArray[ix];
01271     //                }
01272 
01273                     if (process_noise) {
01274                         // cubic spline
01275                         for (int ix = 0; ix < xdim; ix++) {
01276                             array_in[ix] = pcombined_noise[ix+iy*xdim];
01277                         }
01278                         array_out = cubicspline_reg_reg(xdim, 0., 1., array_in,
01279                                                         xdim, shift_val, 1.0,
01280                                                         NATURAL);
01281                         for (int ix = 0; ix < xdim; ix++) {
01282                           pcombined_noise[ix+iy*xdim] = array_out[ix];
01283                         }
01284                         cpl_free(array_out);
01285 
01286     //                    // linear
01287     //                    for (int ix = 1; ix < xdim; ix++) {
01288     //                        tmpArray[ix-1] = (pcombined_noise[ix+iy*xdim]-pcombined_noise[(ix-1)+iy*xdim])*shift_val +
01289     //                                         pcombined_noise[(ix-1)+iy*xdim];
01290     //                    }
01291     //                    for (int ix = 1; ix < xdim; ix++) {
01292     //                        pcombined_noise[ix+iy*xdim] = tmpArray[ix];
01293     //                    }
01294                     }
01295                 }
01296                 cpl_free(array_in); array_in = NULL;
01297             }
01298             //
01299             // reconstruct
01300             //
01301             // load MASTER_DARK and MASTER_FLAT
01302             KMO_TRY_EXIT_IF_NULL(
01303                 img_dark = kmo_dfs_load_image(frameset, MASTER_DARK,
01304                                               det_nr, FALSE, FALSE, NULL));
01305 
01306             if (process_noise) {
01307                 KMO_TRY_EXIT_IF_NULL(
01308                     img_dark_noise = kmo_dfs_load_image(frameset, MASTER_DARK,
01309                                                         det_nr, TRUE, FALSE, NULL));
01310             }
01311 
01312             KMO_TRY_EXIT_IF_NULL(
01313                 img_flat = kmo_dfs_load_cal_image(frameset, MASTER_FLAT, det_nr, FALSE,
01314                                                   rotangle, FALSE, NULL, &rotangle_found));
01315 
01316             if (process_noise) {
01317                 KMO_TRY_EXIT_IF_NULL(
01318                     img_flat_noise = kmo_dfs_load_cal_image(frameset, MASTER_FLAT, det_nr, TRUE,
01319                                                             rotangle, FALSE, NULL, &rotangle_found));
01320             }
01321 
01322             char *tmp_band_method = getenv("KMO_BAND_METHOD");
01323             int band_method = 0;
01324             if (tmp_band_method != NULL) {
01325                 band_method = atoi(tmp_band_method);
01326             }
01327 
01328             // ESO INS FILTi ID
01329             KMO_TRY_EXIT_IF_NULL(
01330                 keyword = cpl_sprintf("%s%d%s", IFU_FILTID_PREFIX, det_nr,
01331                                       IFU_FILTID_POSTFIX));
01332             KMO_TRY_EXIT_IF_NULL(
01333                 filter_id = cpl_propertylist_get_string(main_header, keyword));
01334             cpl_free(keyword); keyword = NULL;
01335 
01336             KMO_TRY_EXIT_IF_NULL(
01337                 band_table = kmo_dfs_load_table(frameset, WAVE_BAND, 1, 0));
01338             KMO_TRY_EXIT_IF_ERROR(
01339                 kmclipm_setup_grid_band_lcal(&gd, lcal, filter_id, band_method,
01340                                              band_table));
01341             cpl_table_delete(band_table); band_table = NULL;
01342 
01343             cpl_msg_info("","Reconstructing cubes...");
01344             for (int j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01345                 // update sub-header
01346                 ifu_nr = (det_nr-1)*KMOS_IFUS_PER_DETECTOR + j + 1;
01347 
01348                 // load raw image and sub-header
01349                 KMO_TRY_EXIT_IF_NULL(
01350                     sub_header = kmo_dfs_load_sub_header(frameset_sky, FLAT_SKY,
01351                                                          det_nr, FALSE));
01352 
01353                 KMO_TRY_EXIT_IF_NULL(
01354                     punused_ifus = cpl_array_get_data_int_const(
01355                                                   unused_ifus_after[det_nr-1]));
01356 
01357                 // check if IFU is valid according to main header keywords &
01358                 // calibration files
01359                 KMO_TRY_EXIT_IF_NULL(
01360                     keyword = cpl_sprintf("%s%d%s", IFU_VALID_PREFIX, ifu_nr,
01361                                           IFU_VALID_POSTFIX));
01362                 KMO_TRY_CHECK_ERROR_STATE();
01363                 ranges_txt = cpl_propertylist_get_string(main_header, keyword);
01364                 cpl_free(keyword); keyword = NULL;
01365 
01366                 if ((cpl_error_get_code() == CPL_ERROR_DATA_NOT_FOUND) &&
01367                     (bounds[2*(ifu_nr-1)] != -1) &&
01368                     (bounds[2*(ifu_nr-1)+1] != -1) &&
01369                     (punused_ifus[j] == 0))
01370                 {
01371                     // IFU is valid
01372                     cpl_error_reset();
01373 
01374                     // calculate WCS
01375                     KMO_TRY_EXIT_IF_ERROR(
01376                         kmo_calc_wcs(main_header, sub_header, ifu_nr,
01377                                      gd.l.start, gd.l.delta));
01378 
01379                     // reconstruct data
01380                     KMO_TRY_EXIT_IF_ERROR(
01381                         kmo_reconstruct_sci_image(ifu_nr,
01382                                                 bounds[2*(ifu_nr-1)],
01383                                                 bounds[2*(ifu_nr-1)+1],
01384                                                 combined_data,
01385                                                 combined_noise,
01386                                                 img_dark,
01387                                                 img_dark_noise,
01388                                                 img_flat,
01389                                                 img_flat_noise,
01390                                                 xcal,
01391                                                 ycal,
01392                                                 lcal,
01393                                                 &gd,
01394                                                 calTimestamp,
01395                                                 calAngles,
01396                                                 fn_lut,
01397                                                 &cube_data,
01398                                                 &cube_noise,
01399                                                 flux));
01400                     KMO_TRY_CHECK_ERROR_STATE();
01401                 } else {
01402                     // IFU is invalid
01403                     cpl_error_reset();
01404                 } // if ((cpl_error_get_code() == CPL_ERROR_DATA_NOT_FOUND) ...
01405 
01406                 // save output
01407                 KMO_TRY_EXIT_IF_NULL(
01408                     extname = kmo_extname_creator(ifu_frame, ifu_nr, EXT_DATA));
01409 
01410                 KMO_TRY_EXIT_IF_ERROR(
01411                     kmclipm_update_property_string(sub_header, EXTNAME,
01412                                                    extname,
01413                                                    "FITS extension name"));
01414 
01415                 cpl_free(extname); extname = NULL;
01416 
01417                 // store cube and sub header into array for later
01418                 stored_data_cubes[ifu_nr - 1] = cube_data;
01419                 stored_sub_data_headers[ifu_nr - 1] = sub_header;
01420 
01421                 if (process_noise) {
01422                     KMO_TRY_EXIT_IF_NULL(
01423                         sub_header = cpl_propertylist_duplicate(
01424                                            stored_sub_data_headers[ifu_nr - 1]));
01425                     KMO_TRY_EXIT_IF_NULL(
01426                         extname = kmo_extname_creator(ifu_frame, ifu_nr,
01427                                                       EXT_NOISE));
01428 
01429                     KMO_TRY_EXIT_IF_ERROR(
01430                         kmclipm_update_property_string(sub_header,
01431                                                 EXTNAME,
01432                                                 extname,
01433                                                 "FITS extension name"));
01434 
01435                     cpl_free(extname); extname = NULL;
01436 
01437                     stored_noise_cubes[ifu_nr - 1] = cube_noise;
01438                     stored_sub_noise_headers[ifu_nr - 1] = sub_header;
01439                 }
01440                 cpl_image_delete(data_ifu); data_ifu = NULL;
01441                 cpl_image_delete(noise_ifu); noise_ifu = NULL;
01442                 cube_data = NULL;
01443                 cube_noise = NULL;
01444             } // for j IFUs
01445 
01446             // free memory
01447             cpl_image_delete(combined_data); combined_data = NULL;
01448             cpl_image_delete(combined_noise); combined_noise = NULL;
01449             cpl_image_delete(xcal); xcal = NULL;
01450             cpl_image_delete(ycal); ycal = NULL;
01451             cpl_image_delete(lcal); lcal = NULL;
01452             cpl_image_delete(img_dark); img_dark = NULL;
01453             cpl_image_delete(img_flat); img_flat = NULL;
01454             cpl_image_delete(bad_pix_mask); bad_pix_mask = NULL;
01455             if (process_noise) {
01456                 cpl_image_delete(img_dark_noise); img_dark_noise = NULL;
01457                 cpl_image_delete(img_flat_noise); img_flat_noise = NULL;
01458             }
01459         } // for nr_devices
01460 
01461         cpl_free(edge_table_flat); edge_table_flat = NULL;
01462 
01463         // collapse cubes using rejection
01464         cpl_msg_info("","Collapsing cubes...");
01465         for (int det_nr = 1; det_nr <= nr_devices; det_nr++) {
01466             for (int j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01467                 ifu_nr = (det_nr-1)*KMOS_IFUS_PER_DETECTOR + j + 1;
01468 
01469                 KMO_TRY_EXIT_IF_NULL(
01470                     punused_ifus = cpl_array_get_data_int_const(
01471                                                   unused_ifus_after[det_nr-1]));
01472                 if (punused_ifus[j] == 0) {
01473                     if (stored_sub_data_headers[ifu_nr-1] != NULL) {
01474                     // IFU is valid
01475                     ifu_crpix = cpl_propertylist_get_double(stored_sub_data_headers[ifu_nr-1],
01476                                                             CRPIX3);
01477                     KMO_TRY_CHECK_ERROR_STATE_MSG(
01478                                    "CRPIX3 keyword in FITS-header is missing!");
01479 
01480                     ifu_crval = cpl_propertylist_get_double(stored_sub_data_headers[ifu_nr-1],
01481                                                             CRVAL3);
01482                     KMO_TRY_CHECK_ERROR_STATE_MSG(
01483                                    "CRVAL3 keyword in FITS-header is missing!");
01484 
01485                     ifu_cdelt = cpl_propertylist_get_double(stored_sub_data_headers[ifu_nr-1],
01486                                                             CDELT3);
01487                     KMO_TRY_CHECK_ERROR_STATE_MSG(
01488                                    "CDELT3 keyword in FITS-header is missing!");
01489 
01490                     KMO_TRY_EXIT_IF_NULL(
01491                         identified_slices = kmo_identify_slices(ranges,
01492                                                                 ifu_crpix,
01493                                                                 ifu_crval,
01494                                                                 ifu_cdelt,
01495                                                                 gd.l.dim));
01496                     }/* else {
01497                         KMO_TRY_EXIT_IF_NULL(
01498                             identified_slices = cpl_vector_new(gd.l.dim));
01499                         cpl_vector_fill(identified_slices, 1.);
01500                     }*/
01501 
01502                     if (stored_data_cubes[ifu_nr-1] != NULL) {
01503                         KMO_TRY_EXIT_IF_ERROR(
01504                             kmclipm_make_image(stored_data_cubes[ifu_nr-1],
01505                                                stored_noise_cubes[ifu_nr-1],
01506                                                &stored_data_images[ifu_nr-1],
01507                                                &stored_noise_images[ifu_nr-1],
01508                                                identified_slices,
01509                                                cmethod, cpos_rej, cneg_rej,
01510                                                citer, cmax, cmin));
01511                     }
01512                     cpl_vector_delete(identified_slices); identified_slices = NULL;
01513                 } else {
01514                     // IFU is invalid
01515                 }
01516             }
01517         }
01518 
01519         // normalise all IFUs as a group.
01520         // Calculate mean of each IFU, add up and divide by number of successful
01521         // averaged IFUs.
01522         // Then divide all valid IFUs with mean value
01523         cnt = 0;
01524         for (int i = 0; i < nr_devices * KMOS_IFUS_PER_DETECTOR; i++) {
01525             if (stored_data_images[i] != NULL) {
01526                 KMO_TRY_ASSURE(cpl_image_count_rejected(stored_data_images[i]) <
01527                                cpl_image_get_size_x(stored_data_images[i])*
01528                                cpl_image_get_size_y(stored_data_images[i]),
01529                                CPL_ERROR_ILLEGAL_INPUT,
01530                                "The collapsed, dark-subtracted image contains "
01531                                "only invalid values! Probably the provided "
01532                                "FLAT_SKY frames are exactly the same as the "
01533                                "frames used for MASTER_DARK calculation.");
01534 
01535                 mean_data += cpl_image_get_mean(stored_data_images[i]);
01536                 KMO_TRY_CHECK_ERROR_STATE();
01537                 cnt++;
01538             }
01539 
01540         }
01541         mean_data /= cnt;
01542 
01543         if (mean_data != 0.0) {
01544             for (int i = 0; i < nr_devices * KMOS_IFUS_PER_DETECTOR; i++) {
01545                 if (stored_data_images[i] != NULL) {
01546                     KMO_TRY_EXIT_IF_ERROR(
01547                         cpl_image_divide_scalar(stored_data_images[i],
01548                                                 mean_data));
01549                 }
01550             }
01551         } else {
01552             cpl_msg_warning(cpl_func, "Data couldn't be normalised "
01553                                       "(mean = 0.0)!");
01554         }
01555 
01556         if (process_noise) {
01557             if (mean_data != 0.0) {
01558                 for (int i = 0; i < nr_devices * KMOS_IFUS_PER_DETECTOR; i++) {
01559                     if (stored_noise_images[i] != NULL) {
01560                         KMO_TRY_EXIT_IF_ERROR(
01561                             cpl_image_divide_scalar(stored_noise_images[i],
01562                                                     mean_data));
01563                     }
01564                 }
01565             } else {
01566                 cpl_msg_warning(cpl_func, "Noise couldn't be normalised "
01567                                           "(mean = 0.0)!");
01568             }
01569         }
01570 
01571         // calculate qc parameters on normalised data
01572         qc_spat_unif = 0.0;
01573         cnt = 0;
01574         for (int i = 0; i < nr_devices * KMOS_IFUS_PER_DETECTOR; i++) {
01575             if (stored_data_images[i] != NULL) {
01576                 tmp_mean = cpl_image_get_mean(stored_data_images[i]);
01577                 tmp_stdev = cpl_image_get_stdev (stored_data_images[i]);
01578 
01579                 qc_spat_unif += pow(tmp_mean-1, 2);
01580                 if (fabs(tmp_mean) > qc_max_dev) {
01581                     qc_max_dev = tmp_mean-1;
01582                     qc_max_dev_id = i+1;
01583                 }
01584                 if (fabs(tmp_stdev) > qc_max_nonunif) {
01585                     qc_max_nonunif = tmp_stdev;
01586                     qc_max_nonunif_id = i+1;
01587                 }
01588                 KMO_TRY_CHECK_ERROR_STATE();
01589                 cnt++;
01590             }
01591         }
01592         qc_spat_unif = sqrt(qc_spat_unif / cnt);
01593 
01594         //
01595         // save data
01596         //
01597 
01598         // update which IFUs are not used
01599         kmo_print_unused_ifus(unused_ifus_after, TRUE);
01600 
01601         KMO_TRY_EXIT_IF_ERROR(
01602             kmo_set_unused_ifus(unused_ifus_after, main_header,
01603                                 "kmo_illumination"));
01604 
01605         cpl_msg_info("","Saving data...");
01606 
01607         KMO_TRY_EXIT_IF_ERROR(
01608             kmclipm_update_property_double(main_header, QC_SPAT_UNIF, qc_spat_unif,
01609                                            "[adu] uniformity of illumination correction"));
01610         KMO_TRY_EXIT_IF_ERROR(
01611             kmclipm_update_property_double(main_header, QC_SPAT_MAX_DEV, qc_max_dev,
01612                                            "[adu] max. deviation from unity"));
01613         KMO_TRY_EXIT_IF_ERROR(
01614             kmclipm_update_property_int(main_header, QC_SPAT_MAX_DEV_ID, qc_max_dev_id,
01615                                         "[] IFU ID with max. dev. from unity"));
01616         KMO_TRY_EXIT_IF_ERROR(
01617             kmclipm_update_property_double(main_header, QC_SPAT_MAX_NONUNIF, qc_max_nonunif,
01618                                            "[adu] max. stdev of illumination corr."));
01619         KMO_TRY_EXIT_IF_ERROR(
01620             kmclipm_update_property_int(main_header, QC_SPAT_MAX_NONUNIF_ID, qc_max_nonunif_id,
01621                                         "[] IFU ID with max. stdev in illum. corr."));
01622 
01623         KMO_TRY_EXIT_IF_ERROR(
01624             kmo_dfs_save_main_header(frameset, ILLUM_CORR, suffix, frame,
01625                                      main_header, parlist, cpl_func));
01626 
01627         KMO_TRY_EXIT_IF_ERROR(
01628             kmo_dfs_save_main_header(frameset, SKYFLAT_EDGE, suffix, frame,
01629                                      main_header, parlist, cpl_func));
01630 
01631         for (int i = 0; i < nr_devices * KMOS_IFUS_PER_DETECTOR; i++) {
01632             KMO_TRY_EXIT_IF_ERROR(
01633                 kmo_dfs_save_image(stored_data_images[i], ILLUM_CORR, suffix,
01634                                    stored_sub_data_headers[i], 0./0.));
01635 
01636             if (process_noise) {
01637                 KMO_TRY_EXIT_IF_ERROR(
01638                     kmo_dfs_save_image(stored_noise_images[i], ILLUM_CORR,
01639                                        suffix, stored_sub_noise_headers[i], 0./0.));
01640             }
01641         }
01642 
01643         for (int det_nr = 1; det_nr <= nr_devices; det_nr++) {
01644             for (int ifu_nr = 0; ifu_nr < KMOS_IFUS_PER_DETECTOR; ifu_nr++) {
01645                 KMO_TRY_EXIT_IF_ERROR(
01646                     kmclipm_update_property_int(stored_sub_data_headers[(det_nr-1)*KMOS_IFUS_PER_DETECTOR+ifu_nr],
01647                                                 CAL_IFU_NR,
01648                                                 ifu_nr+1+(det_nr-1)*KMOS_IFUS_PER_DETECTOR,
01649                                                 "IFU Number {1..24}"));
01650                 KMO_TRY_EXIT_IF_ERROR(
01651                     kmclipm_update_property_double(
01652                                                 stored_sub_data_headers[(det_nr-1)*KMOS_IFUS_PER_DETECTOR+ifu_nr],
01653                                                 CAL_ROTANGLE,
01654                                                 rotangle_found,
01655                                                 "[deg] Rotator relative to nasmyth"));
01656                 if (has_flat_edge) {
01657                     // save edge-parameters as product
01658                     KMO_TRY_EXIT_IF_ERROR(
01659                         kmo_dfs_save_table(edge_table_sky[det_nr-1][ifu_nr], SKYFLAT_EDGE, suffix,
01660                                            stored_sub_data_headers[(det_nr-1)*KMOS_IFUS_PER_DETECTOR+ifu_nr]));
01661                 }
01662             }
01663         }
01664     }
01665     KMO_CATCH
01666     {
01667         KMO_CATCH_MSG();
01668         ret_val = -1;
01669     }
01670     kmo_free_fits_desc(&desc_sky);
01671     kmo_free_fits_desc(&desc_dark);
01672     kmo_free_fits_desc(&desc_flat);
01673     kmo_free_fits_desc(&desc_xcal);
01674     kmo_free_fits_desc(&desc_ycal);
01675     kmo_free_fits_desc(&desc_lcal);
01676     cpl_image_delete(combined_data); combined_data = NULL;
01677     cpl_image_delete(combined_noise); combined_noise = NULL;
01678     cpl_image_delete(xcal); xcal = NULL;
01679     cpl_image_delete(ycal); ycal = NULL;
01680     cpl_image_delete(lcal); lcal = NULL;
01681     cpl_image_delete(img_dark); img_dark = NULL;
01682     cpl_image_delete(img_dark_noise); img_dark_noise = NULL;
01683     cpl_image_delete(img_flat); img_flat = NULL;
01684     cpl_image_delete(img_flat_noise); img_flat_noise = NULL;
01685     cpl_array_delete(calTimestamp); calTimestamp = NULL;
01686     cpl_free(bounds); bounds = NULL;
01687     kmo_free_unused_ifus(unused_ifus_before); unused_ifus_before = NULL;
01688     kmo_free_unused_ifus(unused_ifus_after); unused_ifus_after = NULL;
01689     cpl_free(fn_lut); fn_lut = NULL;
01690     cpl_free(suffix); suffix = NULL;
01691     cpl_frameset_delete(frameset_sky); frameset_sky = NULL;
01692     cpl_vector_delete(ranges); ranges = NULL;
01693     cpl_free(filter); filter = NULL;
01694     if (calAngles != NULL) {
01695         cpl_vector_delete(calAngles); calAngles = NULL;
01696     }
01697     cpl_propertylist_delete(main_header); main_header = NULL;
01698     for (int i = 0; i < nr_devices * KMOS_IFUS_PER_DETECTOR; i++) {
01699         if (stored_data_cubes != NULL) {
01700             cpl_imagelist_delete(stored_data_cubes[i]);
01701             stored_data_cubes[i] = NULL;
01702         }
01703         if (stored_noise_cubes != NULL) {
01704             cpl_imagelist_delete(stored_noise_cubes[i]);
01705             stored_noise_cubes[i] = NULL;
01706         }
01707         if (stored_data_images != NULL) {
01708             cpl_image_delete(stored_data_images[i]);
01709             stored_data_images[i] = NULL;
01710         }
01711         if (stored_noise_images != NULL) {
01712             cpl_image_delete(stored_noise_images[i]);
01713             stored_noise_images[i] = NULL;
01714         }
01715         if (stored_sub_data_headers != NULL) {
01716             cpl_propertylist_delete(stored_sub_data_headers[i]);
01717             stored_sub_data_headers[i] = NULL;
01718         }
01719         if (stored_sub_noise_headers != NULL) {
01720             cpl_propertylist_delete(stored_sub_noise_headers[i]);
01721             stored_sub_noise_headers[i] = NULL;
01722         }
01723     }
01724     cpl_free(stored_data_cubes); stored_data_cubes = NULL;
01725     cpl_free(stored_noise_cubes); stored_noise_cubes = NULL;
01726     cpl_free(stored_data_images); stored_data_images = NULL;
01727     cpl_free(stored_noise_images); stored_noise_images = NULL;
01728     cpl_free(stored_sub_data_headers); stored_sub_data_headers = NULL;
01729     cpl_free(stored_sub_noise_headers); stored_sub_noise_headers = NULL;
01730     if (edge_table_sky != NULL) {
01731         for (int i = 0; i < KMOS_NR_DETECTORS; i++) {
01732             if (edge_table_sky[i] != NULL) {
01733                 for (int j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01734                     cpl_table_delete(edge_table_sky[i][j]);
01735                     edge_table_sky[i][j] = NULL;
01736                 }
01737                 cpl_free(edge_table_sky[i]); edge_table_sky[i] = NULL;
01738             }
01739         }
01740         cpl_free(edge_table_sky); edge_table_sky = NULL;
01741     }
01742     if (edge_table_flat != NULL) {
01743         for (int j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
01744             cpl_table_delete(edge_table_flat[j]);
01745             edge_table_flat[j] = NULL;
01746         }
01747         cpl_free(edge_table_flat); edge_table_flat = NULL;
01748     }
01749 
01750     return ret_val;
01751 }
01752