/* $Id: kmo_std_star.c,v 1.79 2013/10/08 14:55:01 erw Exp $
 *
 * This file is part of the KMOS Pipeline
 * Copyright (C) 2002,2003 European Southern Observatory
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * $Author: erw $
 * $Date: 2013/10/08 14:55:01 $
 * $Revision: 1.79 $
 * $Name:  $
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

/*-----------------------------------------------------------------------------
 *                              Includes
 *----------------------------------------------------------------------------*/

#include <math.h>
#include <string.h>

#include <cpl.h>
#include "kmclipm_math.h"

#include "kmo_constants.h"
#include "kmo_cpl_extensions.h"
#include "kmo_utils.h"
#include "kmo_functions.h"
#include "kmo_priv_std_star.h"
#include "kmo_priv_fit_profile.h"
#include "kmo_priv_extract_spec.h"
#include "kmo_priv_functions.h"
#include "kmo_dfs.h"
#include "kmo_error.h"
#include "kmo_debug.h"
#include "kmo_priv_reconstruct.h"

/*-----------------------------------------------------------------------------
 *              Lines (all vacuum)
 *
# Line lists for use in telluric transmission correction for KMOS, etc.
# based on OBA standard stars.
#
# 30/01/2013   NMFS
#
#
# - H lines of the Paschen and Brackett series (perhaps also Pfund series
#   at the very red edge of K band) will be most prominent for late-O to
#   A types.
#
# - HeI lines in absorption are mostly for O types (with some dependence
#   on luminosity class).
# - HeII lines will only be relevant in the earliest O types
# - HeI and HeII lines may also appear in emission.
#
# The note "weak - irrelevant?" indicates lines that tend to be much
# weaker, and would only be discernable in R > 5000 spectra with very
# high S/N ratio.  They may cause asymmetric wings for neighbouring
# stronger features depending on the star/spectral type.
# They are included here for completeness, but can probably be ignored
# in the context of KMOS telluric calibration.
#
# It is important, however, to include the stronger HeI and HeII features,
# experience with SINFONI shows they are frequently there (esp. in H band).
#
#
# N.B.
#  The H line list in this file is complete within the Iz - K coverage
#  of KMOS (excluding the highest Pa, Br, abd Pf transitions, which
#  become very weak).
#  The He line for >= 1.0um is fairly complete (strongest common lines
#  are included).
#  HOWEVER: the He line list at < 1.0um, relevant for Iz band, is missing.
#
#
# Useful references:
#  Wallace et al. 2000, ApJ, 535, 325
#  Wallace & Hinkle 1997,
#  Meyer et al. 1998,
#  Hanson et al. 2005, ApJS, 161, 154
#
# In the future: planned XShooter stellar library (PI S. Trager) will
# provide a cross-check over the full Iz - K band, as well as allow the
# identification of potentially missing He features in the range 0.8-1um.

 *-----------------------------------------------------------------------------*/
const int         nr_lines_h     = 10;
const double      lines_center_h[]      = {
                                    1.7001,     // HeI          // triplet
                                    // 1.52616,    // Br-19        // (weak - irrelevant?)
                                    1.53429,    // Br-18
                                    1.54400,    // Br-17
                                    1.55576,    // Br-16
                                    1.57018,    // Br-15
                                    1.58817,    // Br-14
                                    1.61105,    // Br-13
                                    1.64084,    // Br-12
                                    1.68077,    // Br-11
                                    1.73634    // Br-10
                                    // 1.6918,     // HeII     // weak
                                    // 1.81754,    // Br-epsilon   // (in band, but low transmission)
                                    // 1.87524     // Pa-alpha     // (out of H-band? useful for HK?)
                                   };
const double      lines_width_h[]  = {
                                    0.025,    // HeI
                                    // 0.015,    // Br-19
                                    0.003,    // Br-18
                                    0.015,    // Br-17
                                    0.015,    // Br-16
                                    0.015,    // Br-15
                                    0.025,    // Br-14
                                    0.015,    // Br-13
                                    0.025,    // Br-12
                                    0.025,    // Br-11
                                    0.05    // Br-10
                                    // 0.015,    // HeII
                                    // 0.015,    // Br-epsilon
                                    // 0.015     // Pa-alpha
                                       };
const int         nr_lines_k     = 2;
const double      lines_center_k[]      = {
                                    // 1.94470,    // Br-delta                  // out of K-band
                                    // 2.0581,     // HeI          // singlet   // very faint, non detectable
                                    2.1120,     // HeI          // triplet
                                    //2.1132,     // HeI          // singlet
                                    // 2.1494,     // HeI        // (weak - irrelevant?)
                                    // 2.1607,     // HeI        // triplet (weak - irrelevant?)
                                    // 2.1617,     // HeI        // singlet (weak - irrelevant?)
                                    // 2.1641,     // HeI        // triplet (weak - irrelevant?)
                                    2.16569    // Br-gamma
                                    // 2.1815,     // HeI        // (weak - irrelevant?)
                                    // 2.1840,     // HeI        // (weak - irrelevant?)
// wo ?!?                                    2.1885,     // HeII
                                    // 2.43087,    // Pf-20      // (weak - irrelevant?)
                                    // 2.44851,    // Pf-19      // (weak - irrelevant?)
                                    // 2.46949,    // Pf-18      // (weak - irrelevant?)
                                    // 2.49475     // Pf-17      // (weak - irrelevant?)  // out of band
                                   };
const double      lines_width_k[]   = {
                                    // 0.015,    // Br-delta     // (out of K-band? useful for HK?)
  //                                  0.008,     // HeI          // singlet
                                    0.01,     // HeI          // triplet
                                    //0.0015,     // HeI          // singlet
                                    // 0.003,     // HeI        // (weak - irrelevant?)
                                    // 0.003,     // HeI        // triplet (weak - irrelevant?)
                                    // 0.003,     // HeI        // singlet (weak - irrelevant?)
                                    // 0.015,     // HeI        // triplet (weak - irrelevant?)
                                    0.015    // Br-gamma
                                    // 0.003,     // HeI        // (weak - irrelevant?)
                                    // 0.003,     // HeI        // (weak - irrelevant?)
                                    // 0.015,     // HeII
                                    // 0.015,    // Pf-20      // (weak - irrelevant?)
                                    // 0.015,    // Pf-19      // (weak - irrelevant?)
                                    // 0.015,    // Pf-18      // (weak - irrelevant?)
                                    // 0.015     // Pf-17      // (weak - irrelevant?)
                                       };
const int         nr_lines_hk    = 12;
const double      lines_center_hk[]     = {
                        // H
                                    1.7001,     // HeI          // triplet

                                    1.53429,    // Br-18
                                    1.54400,    // Br-17
                                    1.55576,    // Br-16
                                    1.57018,    // Br-15
                                    1.58817,    // Br-14
                                    1.61105,    // Br-13
                                    1.64084,    // Br-12
                                    1.68077,    // Br-11
                                    1.73634,    // Br-10
                        // K
                                    2.1120,     // HeI          // triplet
                                    2.16569     // Br-gamma
                                   };
const double      lines_width_hk[]  = {
                        // H
                                        0.025,    // HeI
                                        0.003,    // Br-18
                                        0.015,    // Br-17
                                        0.015,    // Br-16
                                        0.015,    // Br-15
                                        0.025,    // Br-14
                                        0.015,    // Br-13
                                        0.025,    // Br-12
                                        0.025,    // Br-11
                                        0.05,     // Br-10
                        // K
                                        0.015,     // HeI          // triplet
                                        0.015    // Br-gamma
                                       };
const int         nr_lines_iz    = 12;
const double      lines_center_iz[]  = {
                                        0.84386,    // Pa-18
                                        0.84679,    // Pa-17
                                        0.85031,    // Pa-16
                                        0.85460,    // Pa-15
                                        0.85990,    // Pa-14
                                        0.86657,    // Pa-13
                                        0.87511,    // Pa-12
                                        0.88635,    // Pa-11
                                        0.90156,    // Pa-10
                                        0.92297,    // Pa-9
                                        0.95467,    // Pa-epsilon
                                        1.00501     // Pa-delta
                                       };
const double      lines_width_iz[]  = {
                                         0.0008,     // Pa-18
                                        0.003225,    // Pa-17
                                        0.0039,     // Pa-16
                                        0.0048,     // Pa-15
                                        0.006,     // Pa-14
                                        0.0076,     // Pa-13
                                        0.001,     // Pa-12
                                        0.013,     // Pa-11
                                         0.01,     // Pa-10
                                         0.013,     // Pa-9
                                         0.02,     // Pa-epsilon
                                         0.025       // Pa-delta
                                       };
const int         nr_lines_yj    = 7;
const double      lines_center_yj[]     = {
                                    // 1.00501,    // Pa-delta     // (out of band?)
                                    1.08331,    // HeI
                                    1.09160,    // HeI
                                    1.09389,    // Pa-gamma

                                    1.19723,    // HeI

                                    1.28191,    // Pa-beta
                                    1.27882,    // HeI
                                    // 1.28495,    // HeI   // faint
                                    1.29720     // HeI
                                   };
const double      lines_width_yj[]  = {
                                    // 0.015,    // Pa-delta     // (out of band?)
                                    .01,//0.005,    // HeI
                                    .01,//0.002,    // HeI
                                    0.02,    // Pa-gamma

                                    0.003,    // HeI

                                    0.02,    // Pa-beta
                                    0.0025,    // HeI
                                    // 0.0007,    // HeI
                                    0.002     // HeI
                                       };

/*-----------------------------------------------------------------------------
 *                          Functions prototypes
 *----------------------------------------------------------------------------*/

static int kmo_std_star_create(cpl_plugin *);
static int kmo_std_star_exec(cpl_plugin *);
static int kmo_std_star_destroy(cpl_plugin *);
static int kmo_std_star(cpl_parameterlist *, cpl_frameset *);

/*-----------------------------------------------------------------------------
 *                          Static variables
 *----------------------------------------------------------------------------*/

static char kmo_std_star_description[] =
"This recipe creates a telluric calibration frame and a PSF frame. It must be\n"
"called after the kmo_illumination-recipe.\n"
"Since there won’t be enough standard stars to observe for all IFUs in one ex-\n"
"posure, one has to do several exposures in a way that there is at least one\n"
"standard star and one sky exposure in each IFU. A internal data organiser will\n"
"analyse the provided exposures and select the appropriate frames as follows:\n"
"1. For each IFU the first standard star in the list of provided exposures is\n"
"   taken. All subsequent standard star exposures for this IFU will be ignored\n"
"2. A corresponding sky exposure will be chosen which will be as close in time\n"
"   to the standard star exposure as possible.\n"
"3. For any IFUs not containing a standard star and a sky exposure an empty\n"
"   frame will be returned.\n"
"\n"
"BASIC PARAMETERS:\n"
"-----------------\n"
"--startype\n"
"If this parameter is specified, the stored star types of the observed obejcts \n"
"in the FITS headers are overridden. This value applies to all objects exa-\n"
"mined in the input frames. Examples would be “A3I”, “G3IV” or “K0I”. The first\n"
"letter defines the star type, the second letter the spectral class and the last\n"
"letters the luminosity class.\n"
"\n"
"--magnitude\n"
"If this parameter is specified, the stored magnitudes in the FITS headers are \n"
"overridden. For HK two magnitudes for each H and K have to be specified. All \n"
"other gratings just use a single magnitude. If two values are provided, they \n"
"have to be separated with a comma. \n"
"\n"
"--fmethod\n"
"The type of function that should be fitted spatially to the collapsed image.\n"
"This fit is used to create a mask to extract the spectrum of the object. Valid\n"
"values are “gauss” and “moffat”.\n"
"\n"
"--imethod\n"
"The interpolation method used for reconstruction. As default 'CS' is selected.\n"
"Note that no error spectra will be generated for this interpolation method.\n"
"Select a nearest neighbour method otherwise\n"
"\n"
"--range\n"
"The spectral range [um] to combine when collapsing the reconstructed cubes.\n"
"\n"
"--save_cubes\n"
"Set to TRUE if the intermediate reconstructed cubes (eventually divided by "
"illumination correction) should be saved as well. Default is FALSE.\n"
"\n"
"ADVANCED PARAMETERS\n"
"-------------------\n"
"--flux\n"
"Specify if flux conservation should be applied.\n"
"\n"
"--neighborhoodRange\n"
"Defines the range to search for neighbors during reconstruction\n"
"\n"
"--b_samples\n"
"The number of samples in spectral direction for the reconstructed cube.\n"
"Ideally this number should be greater than 2048, the detector size.\n"
"\n"
"--b_start\n"
"--b_end\n"
"Used to define manually the start and end wavelength for the reconstructed\n"
"cube. By default the internally defined values are used.\n"
"\n"
"--cmethod\n"
"Following methods of frame combination are available:\n"
"   * 'ksigma' (Default)\n"
"   An iterative sigma clipping. For each position all pixels in the spectrum\n"
"   are examined. If they deviate significantly, they will be rejected according\n"
"   to the conditions:\n"
"       val > mean + stdev * cpos_rej\n"
"   and\n"
"       val < mean - stdev * cneg_rej\n"
"   where --cpos_rej, --cneg_rej and --citer are the corresponding configuration\n"
"   parameters. In the first iteration median and percentile level are used.\n"
"\n"
"   * 'median'\n"
"   At each pixel position the median is calculated.\n"
"\n"
"   * 'average'\n"
"   At each pixel position the average is calculated.\n"
"\n"
"   * 'sum'\n"
"   At each pixel position the sum is calculated.\n"
"\n"
"   * 'min_max'\n"
"   The specified number of minimum and maximum pixel values will be rejected.\n"
"   --cmax and --cmin apply to this method.\n"
"\n"
"--cpos_rej\n"
"--cneg_rej\n"
"--citer\n"
"see --cmethod='ksigma'\n"
"\n"
"--cmax\n"
"--cmin\n"
"see --cmethod='min_max'\n"
"\n"
"--xcal_interpolation\n"
"If true interpolate the pixel position in the slitlet (xcal) using the two\n"
"closest rotator angles in the calibration file. Otherwise take the values\n"
"of the closest rotator angle\n"
"\n"
"--suppress_extension\n"
"If set to TRUE, the arbitrary filename extensions are supressed. If multiple\n"
"products with the same category are produced, they will be numered consecutively\n"
"starting from 0.\n"
"\n"
"-------------------------------------------------------------------------------\n"
"  Input files:\n"
"\n"
"   DO                      KMOS                                                \n"
"   category                Type  Explanation                   Required #Frames\n"
"   --------                ----- -----------                   -------- -------\n"
"   STD                     RAW   Std. star & sky exposures         Y     >=1   \n"
"   XCAL                    F2D   x calibration frame               Y      1    \n"
"   YCAL                    F2D   y calibration frame               Y      1    \n"
"   LCAL                    F2D   Wavelength calib. frame           Y      1    \n"
"   MASTER_FLAT             F2D   Master flat frame                 Y      1    \n"
"   WAVE_BAND               F2L   Table with start-/end-wavelengths Y      1    \n"
"   ILLUM_CORR              F2I   Illumination correction           N     0,1   \n"
"   SOLAR_SPEC              F1S   Solar spectrum                    N     0,1   \n"
"                                 (only for G stars)                            \n"
"   ATMOS_MODEL             F1S   Model atmospheric transmisson     N     0,1   \n"
"                                 (only for OBAF stars in K band)               \n"
"   SPEC_TYPE_LOOKUP        F2L   LUT  eff. stellar temperature     N     0,1   \n"
"\n"
"  Output files:\n"
"\n"
"   DO                      KMOS\n"
"   category                Type   Explanation\n"
"   --------                -----  -----------\n"
"   TELLURIC                F1I    The normalised telluric spectrum            \n"
"                                  (including errors)                          \n"
"   STAR_SPEC               F1I    The extracted star spectrum                 \n"
"                                  (including errors)                          \n"
"   STD_IMAGE               F2I    The standard star PSF images                \n"
"   STD_MASK                F2I    The generated mask used to extract the star \n"
"                                  spectrum                                    \n"
"-------------------------------------------------------------------------------\n"
"\n";

/*-----------------------------------------------------------------------------
 *                              Functions code
 *----------------------------------------------------------------------------*/

/**
 * @defgroup kmo_std_star kmo_std_star Create the telluric correction frame
 *
 * See recipe description for details.
 */

/**@{*/

/**
  @brief    Build the list of available plugins, for this module. 
  @param    list    the plugin list
  @return   0 if everything is ok, -1 otherwise

  Create the recipe instance and make it available to the application using the 
  interface. This function is exported.
 */
int cpl_plugin_get_info(cpl_pluginlist *list)
{
    cpl_recipe *recipe = cpl_calloc(1, sizeof *recipe);
    cpl_plugin *plugin = &recipe->interface;

    cpl_plugin_init(plugin,
                        CPL_PLUGIN_API,
                        KMOS_BINARY_VERSION,
                        CPL_PLUGIN_TYPE_RECIPE,
                        "kmo_std_star",
                        "Create the telluric correction frame.",
                        kmo_std_star_description,
                        "Alex Agudo Berbel",
                        "kmos-spark@mpe.mpg.de",
                        kmos_get_license(),
                        kmo_std_star_create,
                        kmo_std_star_exec,
                        kmo_std_star_destroy);

    cpl_pluginlist_append(list, plugin);

    return 0;
}

/**
  @brief    Setup the recipe options    
  @param    plugin  the plugin
  @return   0 if everything is ok

  Defining the command-line/configuration parameters for the recipe.
 */
static int kmo_std_star_create(cpl_plugin *plugin)
{
    cpl_recipe *recipe;
    cpl_parameter *p;

    /* Check that the plugin is part of a valid recipe */
    if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
        recipe = (cpl_recipe *)plugin;
    else
        return -1;

    /* Create the parameters list in the cpl_recipe object */
    recipe->parameters = cpl_parameterlist_new();

    /* --startype */
    p = cpl_parameter_new_value("kmos.kmo_std_star.startype",
                                CPL_TYPE_STRING,
                                "The spectral type of the star (O, B, A, F, G)"
                                " Format: G4V etc.",
                                "kmos.kmo_std_star",
                                "");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "startype");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --imethod */
    p = cpl_parameter_new_value("kmos.kmo_std_star.imethod",
                                CPL_TYPE_STRING,
                                "Method to use for interpolation. "
                                "[\"NN\" (nearest neighbour), "
                                "\"lwNN\" (linear weighted nearest neighbor), "
                                "\"swNN\" (square weighted nearest neighbor), "
                                "\"MS\" (Modified Shepard's method), "
                                "\"CS\" (Cubic spline)]",
                                "kmos.kmo_std_star",
                                "CS");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "imethod");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --fmethod */
    p = cpl_parameter_new_value("kmos.kmo_std_star.fmethod",
                                CPL_TYPE_STRING,
                                "Either fit a 'gauss' or 'moffat' profile.",
                                "kmos.kmo_std_star",
                                "gauss");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "fmethod");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --neighborhoodRange */
    p = cpl_parameter_new_value("kmos.kmo_std_star.neighborhoodRange",
                                CPL_TYPE_DOUBLE,
                                "Defines the range to search for neighbors "
                                "in pixels",
                                "kmos.kmo_std_star",
                                1.001);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "neighborhoodRange");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --magnitude */
    p = cpl_parameter_new_value("kmos.kmo_std_star.magnitude",
                                CPL_TYPE_STRING,
                                "The magnitude of the std star. For HK two "
                                "values have to provided (eg. 12.1,13.2)",
                                "kmos.kmo_std_star",
                                "");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "magnitude");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --flux */
    p = cpl_parameter_new_value("kmos.kmo_std_star.flux",
                                CPL_TYPE_BOOL,
                                "TRUE: Apply flux conservation. FALSE: otherwise",
                                "kmos.kmo_std_star",
                                TRUE);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "flux");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --save_cubes */
    p = cpl_parameter_new_value("kmos.kmo_std_star.save_cubes",
                                CPL_TYPE_BOOL,
                                "TRUE: Save reconstructed cubes, FALSE: otherwise",
                                "kmos.kmo_std_star",
                                FALSE);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "save_cubes");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --xcal_interpolation */
    p = cpl_parameter_new_value("kmos.kmo_std_star.xcal_interpolation",
                                CPL_TYPE_BOOL,
                                "TRUE: Interpolate xcal between rotator angles. FALSE: otherwise",
                                "kmos.kmo_std_star",
                                TRUE);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "xcal_interpolation");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --suppress_extension */
    p = cpl_parameter_new_value("kmos.kmo_std_star.suppress_extension",
                                CPL_TYPE_BOOL,
                                "Suppress arbitrary filename extension."
                                "(TRUE (apply) or FALSE (don't apply)",
                                "kmos.kmo_std_star",
                                FALSE);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "suppress_extension");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    // add parameters for band-definition
    kmo_band_pars_create(recipe->parameters,
                         "kmos.kmo_std_star");

    // add parameters for combining
    return kmo_combine_pars_create(recipe->parameters,
                                   "kmos.kmo_std_star",
                                   DEF_REJ_METHOD,
                                   FALSE);
}

/**
  @brief    Execute the plugin instance given by the interface
  @param    plugin  the plugin
  @return   0 if everything is ok
 */
static int kmo_std_star_exec(cpl_plugin *plugin)
{
    cpl_recipe  *recipe;

    /* Get the recipe out of the plugin */
    if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
        recipe = (cpl_recipe *)plugin;
    else return -1;

    return kmo_std_star(recipe->parameters, recipe->frames);
}

/**
  @brief    Destroy what has been created by the 'create' function
  @param    plugin  the plugin
  @return   0 if everything is ok
 */
static int kmo_std_star_destroy(cpl_plugin *plugin)
{
    cpl_recipe *recipe;

    /* Get the recipe out of the plugin */
    if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE) 
        recipe = (cpl_recipe *)plugin;
    else return -1 ;

    cpl_parameterlist_delete(recipe->parameters);
    return 0 ;
}

/**
  @brief    Interpret the command line options and execute the data processing
  @param    parlist     the parameters list
  @param    frameset   the frames list
  @return   0 if everything is ok

  Possible _cpl_error_code_ set in this function:

    @li CPL_ERROR_ILLEGAL_INPUT      if operator not valid,
                                     if first operand not 3d or
                                     if second operand not valid
    @li CPL_ERROR_INCOMPATIBLE_INPUT if the dimensions of the two operands
                                     do not match
 */
static int kmo_std_star(cpl_parameterlist *parlist, cpl_frameset *frameset)
{
    cpl_imagelist    **stored_data_cube     = NULL,
                     **stored_noise_cube    = NULL;
    cpl_image        **stored_psf_data      = NULL,
                     *illum_corr            = NULL,
                     **stored_mask          = NULL,
                     *lcal                  = NULL;
    cpl_frame        *xcal_frame            = NULL,
                     *ycal_frame            = NULL,
                     *lcal_frame            = NULL,
                     *flat_frame            = NULL,
                     *illum_frame           = NULL,
                     *obj_frame             = NULL,
                     *sky_frame             = NULL,
                     *tmp_frame             = NULL;
    cpl_vector       *solar_spec             = NULL,
                     *atmos_model            = NULL,
                     **stored_telluric_data  = NULL,
                     **stored_telluric_noise = NULL,
                     **stored_starspec_data  = NULL,
                     **stored_starspec_noise = NULL,
                     *tmp_spec_data          = NULL,
                     *spec_qc     = NULL,
                     *tmp_spec_noise         = NULL,
                     *identified_slices      = NULL,
                     *tmp_vec                = NULL,
                     *lambda_x               = NULL;
    int              ret_val                = 0,
                     nr_devices             = 0,
                     nr_exp                 = 0,
                     j                      = 0,
                     *bounds                = NULL,
                     ifu_nr                 = 0,
                     citer                  = 0,
                     cmax                   = 0,
                     cmin                   = 0,
                     line_warning           = FALSE,
                     nr_std_stars           = 0,
                     print_warning_once     = TRUE,
                     flux                   = FALSE,
                     background             = FALSE,
                     band_method            = 0,
                     save_cubes             = FALSE,
                     has_magnitude          = TRUE,
                     xcal_interpolation     = FALSE,
                     suppress_extension     = FALSE,
                     nr_split_mag           = 0,
                     i                      = 0,
                     l                      = 0,
                     gx                     = 0,
                     gy                     = 0,
                     k                      = 0;
    const int        *punused_ifus          = NULL;
    objSkyStruct     *obj_sky_struct        = NULL;
    double           *stored_qc_throughput  = NULL,
                     star_temperature       = 0.0,
                     neighborhoodRange      = 1.001,
                     cpos_rej               = 0.0,
                     cneg_rej               = 0.0,
                     zeropoint              = -1.0,
                     throughput_mean        = -1.0,
                     throughput_sdv         = -1.0,
                     std_trace              = -1.0,
                     counts1                = 0.0,
                     counts2                = 0.0,
                     magnitude1             = 0.0,
                     magnitude2             = 0.0,
                     exptime                = 0.,
                     cdelt3                 = 0.,
                     mean_data              = 0.,
                     mean_noise             = 0.,
                     *ptmp_spec_noise       = NULL,
                     crpix1                 = 0.,
                     crval1                 = 0.,
                     cdelt1                 = 0.;
    const double     *ptmp_spec_data        = NULL;
    cpl_propertylist *main_header_tel                    = NULL,
                     *main_header_psf                    = NULL,
                     *sub_header_orig                    = NULL,
                     *tmp_sub_header                     = NULL,
                     *tmp_header                         = NULL,
                     **stored_sub_tel_data_headers       = NULL,
                     **stored_sub_tel_noise_headers      = NULL,
                     **stored_sub_cube_data_headers      = NULL,
                     **stored_sub_cube_noise_headers     = NULL,
                     **stored_sub_psf_headers            = NULL,
                     *pl_psf                             = NULL;
    cpl_table        *spec_type_LUT         = NULL,
                     *band_table            = NULL;;
    main_fits_desc   desc1,
                     desc2;
    char             *extname               = NULL,
                     *keyword               = NULL,
                     filename_telluric[256],
                     filename_starspec[256],
                     filename_psf[256],
                     filename_mask[256],
                     filename_cubes[256],
                     *suffix                = NULL,
                     *fn_suffix             = NULL,
                     spec_class[256],
                     lum_class[256],
                     star_type[2],
                     *tmp_band_method       = getenv("KMO_BAND_METHOD"),
                     **split_mag            = NULL,
                     *grat_id               = NULL;
    const char       *filter_id             = NULL,
                     *spec_type             = NULL,
                     *magnitude_txt         = NULL,
                     *imethod               = NULL,
                     *cmethod               = NULL,
                     *fmethod               = NULL,
                     *tmp_str               = NULL;
    gridDefinition   gd;
    cpl_array        **unused_ifus_before   = NULL,
                     **unused_ifus_after    = NULL;
    cpl_frameset     *frameset_std          = NULL;

    KMO_TRY
    {
        kmo_init_fits_desc(&desc1);
        kmo_init_fits_desc(&desc2);

        /* --- check input --- */
        KMO_TRY_ASSURE((parlist != NULL) &&
                       (frameset != NULL),
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, STD) >= 1,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "At least one STD frame is required!");
        if (cpl_frameset_count_tags(frameset, STD) == 1) {
            cpl_msg_warning("", "At least two STD frames should be provided "
                                "in order to apply sky subtraction!");
        }

        KMO_TRY_ASSURE((cpl_frameset_count_tags(frameset, ILLUM_CORR) == 1) ||
                       (cpl_frameset_count_tags(frameset, ILLUM_CORR) == 0),
                       CPL_ERROR_FILE_NOT_FOUND,
                       "Exactly one ILLUM_CORR frame is required!");
        KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, XCAL) == 1,
                       CPL_ERROR_FILE_NOT_FOUND,
                       "Exactly one XCAL frame is required!");
        KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, YCAL) == 1,
                       CPL_ERROR_FILE_NOT_FOUND,
                       "Exactly one YCAL frame is required!");
        KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, LCAL) == 1,
                       CPL_ERROR_FILE_NOT_FOUND,
                       "Exactly one LCAL frame is required!");
        KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, MASTER_FLAT) == 1,
                       CPL_ERROR_FILE_NOT_FOUND,
                       "Exactly one MASTER_FLAT frame is required!");
        KMO_TRY_ASSURE(cpl_frameset_count_tags(frameset, WAVE_BAND) == 1,
                       CPL_ERROR_FILE_NOT_FOUND,
                       "Exactly one WAVE_BAND frame is required!");
        KMO_TRY_ASSURE(kmo_dfs_set_groups(frameset, "kmo_std_star") == 1,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "Cannot identify RAW and CALIB frames!");

        /* --- get parameters --- */
        cpl_msg_info("", "--- Parameter setup for kmo_std_star ------");

        KMO_TRY_EXIT_IF_NULL(
            spec_type = kmo_dfs_get_parameter_string(parlist,
                                                     "kmos.kmo_std_star.startype"));
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist,
                                         "kmos.kmo_std_star.startype"));

        KMO_TRY_EXIT_IF_NULL(
            imethod = kmo_dfs_get_parameter_string(parlist,
                                                   "kmos.kmo_std_star.imethod"));
        KMO_TRY_ASSURE((strcmp(imethod, "NN") == 0) ||
                       (strcmp(imethod, "lwNN") == 0) ||
                       (strcmp(imethod, "swNN") == 0) ||
                       (strcmp(imethod, "MS") == 0) ||
                       (strcmp(imethod, "CS") == 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "method must be either \"NN\", \"lwNN\", "
                       "\"swNN\", \"MS\" or \"CS\"!");
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist,
                                         "kmos.kmo_std_star.imethod"));

        KMO_TRY_EXIT_IF_NULL(
            fmethod = kmo_dfs_get_parameter_string(parlist,
                                                   "kmos.kmo_std_star.fmethod"));
        KMO_TRY_ASSURE((strcmp(fmethod, "gauss") == 0) ||
                       (strcmp(fmethod, "moffat") == 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "fmethod must be either 'gauss' or "
                       "'moffat' !");
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist,
                                        "kmos.kmo_std_star.method"));

        neighborhoodRange = kmo_dfs_get_parameter_double(parlist,
                                                         "kmos.kmo_std_star.neighborhoodRange");
        KMO_TRY_CHECK_ERROR_STATE();
        KMO_TRY_ASSURE(neighborhoodRange > 0.0,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "neighborhoodRange must be greater than 0.0");
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist,
                                         "kmos.kmo_std_star.neighborhoodRange"));

        magnitude_txt = kmo_dfs_get_parameter_string(parlist,
                                                     "kmos.kmo_std_star.magnitude");
        KMO_TRY_CHECK_ERROR_STATE();
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist,
                                         "kmos.kmo_std_star.magnitude"));

        flux = kmo_dfs_get_parameter_bool(parlist,
                                          "kmos.kmo_std_star.flux");
        KMO_TRY_ASSURE((flux == FALSE) || (flux == TRUE),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "flux must be either FALSE or TRUE!");
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist, "kmos.kmo_std_star.flux"));

        save_cubes = kmo_dfs_get_parameter_bool(parlist,
                                          "kmos.kmo_std_star.save_cubes");
        KMO_TRY_ASSURE((save_cubes == FALSE) || (save_cubes == TRUE),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "save_cubes must be either FALSE or TRUE!");
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist, "kmos.kmo_std_star.save_cubes"));

        xcal_interpolation = kmo_dfs_get_parameter_bool(parlist,
                                           "kmos.kmo_std_star.xcal_interpolation");
        KMO_TRY_CHECK_ERROR_STATE();
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist, "kmos.kmo_std_star.xcal_interpolation"));
        KMO_TRY_ASSURE((xcal_interpolation == TRUE) ||
                       (xcal_interpolation == FALSE),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "xcal_interpolation must be TRUE or FALSE!");

        suppress_extension = kmo_dfs_get_parameter_bool(parlist,
                                          "kmos.kmo_std_star.suppress_extension");
        KMO_TRY_CHECK_ERROR_STATE();
        KMO_TRY_EXIT_IF_ERROR(
            kmo_dfs_print_parameter_help(parlist, "kmos.kmo_std_star.suppress_extension"));

        KMO_TRY_ASSURE((suppress_extension == TRUE) || (suppress_extension == FALSE),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "suppress_extension must be TRUE or FALSE!");

        kmo_band_pars_load(parlist, "kmos.kmo_std_star");

        KMO_TRY_EXIT_IF_ERROR(
            kmo_combine_pars_load(parlist,
                                  "kmos.kmo_std_star",
                                  &cmethod,
                                  &cpos_rej,
                                  &cneg_rej,
                                  &citer,
                                  &cmin,
                                  &cmax,
                                  FALSE));
        cpl_msg_info("", "-------------------------------------------");

        //
        // Check if magnitude/frameset is valid and if throughput and zeropoint should be calculated
        //

        // Check if all STD frames have the same GRAT-ID
        // if not: don't calculate zeropoint and throughput
        KMO_TRY_EXIT_IF_NULL(
            frameset_std = cpl_frameset_new());

        KMO_TRY_EXIT_IF_NULL(
            tmp_frame = kmo_dfs_get_frame(frameset, STD));
        KMO_TRY_EXIT_IF_NULL(
            tmp_header = kmclipm_propertylist_load(cpl_frame_get_filename(tmp_frame), 0));
        KMO_TRY_EXIT_IF_NULL(
            grat_id = cpl_sprintf("%s", cpl_propertylist_get_string(tmp_header, "ESO INS GRAT1 ID")));
        KMO_TRY_EXIT_IF_ERROR(
            cpl_frameset_insert(frameset_std, cpl_frame_duplicate(tmp_frame)));
        cpl_propertylist_delete(tmp_header); tmp_header = NULL;
KMO_TRY_CHECK_ERROR_STATE();
        KMO_TRY_EXIT_IF_NULL(
            tmp_frame = kmo_dfs_get_frame(frameset, NULL));
        while (tmp_frame != NULL ) {
            KMO_TRY_EXIT_IF_NULL(
                tmp_header = kmclipm_propertylist_load(cpl_frame_get_filename(tmp_frame), 0));
            if (strcmp(grat_id, cpl_propertylist_get_string(tmp_header, "ESO INS GRAT1 ID")) == 0) {
                // same grating
                KMO_TRY_EXIT_IF_ERROR(
                    cpl_frameset_insert(frameset_std, cpl_frame_duplicate(tmp_frame)));
            } else {
                // there are STD frames with different gratings
                if (has_magnitude) {
                    cpl_msg_warning(cpl_func, "The STD frames have different gratings,"
                                            "following QC parameters won't be "
                                            "calculated: QC ZEROPOINT, QC THROUGHPUT,"
                                            "QC THROUGHPUT MEAN and QC THROUGHPUT STD");
                }
                has_magnitude = FALSE;
            }
            cpl_propertylist_delete(tmp_header); tmp_header = NULL;

            tmp_frame = kmo_dfs_get_frame(frameset, NULL);
            KMO_TRY_CHECK_ERROR_STATE();
        }
        KMO_TRY_CHECK_ERROR_STATE();

        if (cpl_frameset_count_tags(frameset, ATMOS_MODEL) == 1) {
            // check if ATMOS_MODEL is the band as the STD frames
            KMO_TRY_EXIT_IF_NULL(
                tmp_frame = kmo_dfs_get_frame(frameset, ATMOS_MODEL));
            KMO_TRY_EXIT_IF_NULL(
                tmp_sub_header = kmclipm_propertylist_load( cpl_frame_get_filename(tmp_frame), 0));
            KMO_TRY_EXIT_IF_NULL(
                tmp_str = cpl_propertylist_get_string(tmp_sub_header, FILT_ID));
            KMO_TRY_ASSURE(strcmp(grat_id, tmp_str) == 0,
                           CPL_ERROR_ILLEGAL_INPUT,
                           "ATMOS model must have primary "
                           "keyword '%s' equal '%s'!!!",
                           FILT_ID, grat_id);
            cpl_propertylist_delete(tmp_sub_header);
            tmp_sub_header = NULL;
        }

        if (has_magnitude) {
            // all STD frames have the same GRAT-ID
            // now check source of magnitude (user or keyword)
            KMO_TRY_EXIT_IF_NULL(
                tmp_frame = kmo_dfs_get_frame(frameset, STD));
            KMO_TRY_EXIT_IF_NULL(
                tmp_header = kmclipm_propertylist_load(cpl_frame_get_filename(tmp_frame), 0));

            if (strcmp(magnitude_txt, "") == 0) {
                // no user defined magnitude

                // check for magnitude-keyword
                if ((cpl_propertylist_has(tmp_header, STDSTAR_MAG)) &&
                    (cpl_propertylist_get_type(tmp_header, STDSTAR_MAG) == CPL_TYPE_STRING))
                {
                    KMO_TRY_EXIT_IF_NULL(
                        magnitude_txt = cpl_propertylist_get_string(tmp_header, STDSTAR_MAG));
                    KMO_TRY_EXIT_IF_NULL(
                        split_mag = kmo_strsplit(magnitude_txt, ",", &nr_split_mag));

                    // check if band and number of magnitudes matches
                    if ((nr_split_mag == 2) &&
                        (strcmp(grat_id, "HK") == 0))
                    {
                        magnitude1 = atof(split_mag[0]);
                        magnitude2 = atof(split_mag[1]);
                        cpl_msg_info("", "Magnitude in H: %g", magnitude1);
                        cpl_msg_info("", "Magnitude in K: %g", magnitude2);
                    } else if ((nr_split_mag >= 1) &&
                               ((strcmp(grat_id, "K") == 0) ||
                                (strcmp(grat_id, "H") == 0) ||
                                (strcmp(grat_id, "IZ") == 0) ||
                                (strcmp(grat_id, "YJ") == 0)))
                    {
                        magnitude1 = atof(split_mag[0]);
                        cpl_msg_info("", "Magnitude in %s: %g", grat_id, magnitude1);
                    } else {
                        // keyword STDSTAR_MAG doesn't match filter
                        has_magnitude = FALSE;
                        cpl_msg_warning(cpl_func, "The keyword %s doesn't match to grating',"
                                                  "following QC parameters won't be "
                                                  "calculated: QC ZEROPOINT, QC THROUGHPUT,"
                                                  "QC THROUGHPUT MEAN and QC THROUGHPUT STD", STDSTAR_MAG);
                    }
                    kmo_strfreev(split_mag);
                } else {
                    // keyword STDSTAR_MAG unavailable or wrong type
                    has_magnitude = FALSE;
                    cpl_msg_warning(cpl_func, "The keyword %s is not set or of wrong type,"
                                              "following QC parameters won't be "
                                              "calculated: QC ZEROPOINT, QC THROUGHPUT,"
                                              "QC THROUGHPUT MEAN and QC THROUGHPUT STD", STDSTAR_MAG);
                }
            } else {
                // magnitude is user specified
                cpl_msg_info(cpl_func, "Magnitude has been specified by user. Any "
                                       "value in keyword %s will be ignored.", STDSTAR_MAG);

                KMO_TRY_EXIT_IF_NULL(
                    split_mag = kmo_strsplit(magnitude_txt, ",", &nr_split_mag));
                switch (nr_split_mag) {
                case 1:
                    magnitude1 = atof(split_mag[0]);
                    cpl_msg_info("", "Magnitude in %s: %g", grat_id, magnitude1);
                    break;
                case 2:
                    magnitude1 = atof(split_mag[0]);
                    magnitude2 = atof(split_mag[1]);
                    cpl_msg_info("", "Magnitude in H: %g", magnitude1);
                    cpl_msg_info("", "Magnitude in K: %g", magnitude2);
                    break;
                default:
                    KMO_TRY_ASSURE(1 == 0,
                                   CPL_ERROR_ILLEGAL_INPUT,
                                   "Provided magnitude was in wrong format! "
                                   "Either a single float value or two separated by comma");
                }
                kmo_strfreev(split_mag);
            }
            cpl_propertylist_delete(tmp_header); tmp_header = NULL;
        } // if (has_magnitude)
        cpl_msg_info("", "-------------------------------------------");
        KMO_TRY_CHECK_ERROR_STATE();

        //
        // check for spectral type (--startype) (user or keyword)
        //
        if (strcmp(spec_type, "") == 0) {
            // no user defined startype

            KMO_TRY_EXIT_IF_NULL(
                tmp_frame = kmo_dfs_get_frame(frameset, STD));
            KMO_TRY_EXIT_IF_NULL(
                tmp_header = kmclipm_propertylist_load(cpl_frame_get_filename(tmp_frame), 0));

            // check for startype-keyword
            if ((cpl_propertylist_has(tmp_header, STDSTAR_TYPE)) &&
                (cpl_propertylist_get_type(tmp_header, STDSTAR_TYPE) == CPL_TYPE_STRING))
            {
                KMO_TRY_EXIT_IF_NULL(
                    spec_type = cpl_propertylist_get_string(tmp_header, STDSTAR_TYPE));
            } else {
                // keyword STDSTAR_TYPE unavailable or wrong type
            }
        } else {
            // startype is user specified
            cpl_msg_info(cpl_func, "Type of star has been specified by user. Any "
                                   "value in keyword %s will be ignored.", STDSTAR_TYPE);
        }
        KMO_TRY_CHECK_ERROR_STATE();

        if (strlen(spec_type) > 0) {
            if (kmo_get_spec_type(spec_type, spec_class, lum_class) != CPL_ERROR_NONE) {
                cpl_error_reset();
                spec_class[0] = '\0';
                lum_class[0] = '\0';
                star_type[0] = '\0';
                cpl_msg_warning("", "The keyword %s is not set or of wrong type nor was it provided by the user. "
                                    "Can't divide solar spectrum for G stars or fit a profile "
                                    "to atmospheric transmission for OBAF stars and can't "
                                    "divide blackbody for any star.", STDSTAR_TYPE);
                cpl_msg_warning("", "%s = '%s' (should be something like e.g.'G2V' odr 'A9III')", STDSTAR_TYPE, spec_type);
            } else {
                strncpy(star_type, spec_class, 1);
                star_type[1] = '\0';
                cpl_msg_info("", "Spectral class:   %s", spec_class);
                cpl_msg_info("", "Luminosity class: %s", lum_class);
            }
        } else {
            spec_class[0] = '\0';
            lum_class[0] = '\0';
            star_type[0] = '\0';
            cpl_msg_warning("", "The keyword %s is not set nor was it provided by the user. "
                                "Can't divide solar spectrum for G stars or fit a profile "
                                "to atmospheric transmission for OBAF stars and can't "
                                "divide blackbody for any star.", STDSTAR_TYPE);
        }
        cpl_propertylist_delete(tmp_header); tmp_header = NULL;
        cpl_msg_info("", "-------------------------------------------");
        KMO_TRY_CHECK_ERROR_STATE();

        // assure that filters, grating and rotation offsets match for
        // XCAL, YCAL, LCAL and for data frame to reconstruct (except DARK
        // frames)
        // check if filter_id and grating_id match for all detectors
        KMO_TRY_EXIT_IF_ERROR(
            kmo_check_frameset_setup(frameset, XCAL, FALSE, FALSE, TRUE));
        KMO_TRY_EXIT_IF_ERROR(
            kmo_check_frame_setup(frameset, XCAL, YCAL, TRUE, FALSE, TRUE));
        KMO_TRY_EXIT_IF_ERROR(
            kmo_check_frame_setup(frameset, XCAL, LCAL, TRUE, FALSE, TRUE));
        KMO_TRY_EXIT_IF_ERROR(
            kmo_check_frame_setup(frameset, XCAL, MASTER_FLAT, TRUE, FALSE, TRUE));
        KMO_TRY_EXIT_IF_ERROR(
            kmo_check_frame_setup(frameset, XCAL, STD, FALSE, FALSE, TRUE));

        if (cpl_frameset_count_tags(frameset, ILLUM_CORR) == 1) {
            KMO_TRY_EXIT_IF_ERROR(
                kmo_check_frame_setup(frameset, XCAL, ILLUM_CORR, TRUE, FALSE, FALSE));
        }

        // check descriptors of all frames
        KMO_TRY_EXIT_IF_NULL(
            xcal_frame = kmo_dfs_get_frame(frameset, XCAL));

        desc1 = kmo_identify_fits_header(cpl_frame_get_filename(xcal_frame));
        KMO_TRY_CHECK_ERROR_STATE();

        KMO_TRY_ASSURE((desc1.nr_ext % 3 == 0) &&
                       (desc1.ex_badpix == FALSE) &&
                       (desc1.fits_type == f2d_fits) &&
                       (desc1.frame_type == detector_frame),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "XCAL isn't in the correct format!!!");

        KMO_TRY_EXIT_IF_NULL(
            ycal_frame = kmo_dfs_get_frame(frameset, YCAL));
        desc2 = kmo_identify_fits_header(cpl_frame_get_filename(ycal_frame));
        KMO_TRY_CHECK_ERROR_STATE();

        KMO_TRY_ASSURE((desc1.nr_ext == desc2.nr_ext) &&
                       (desc1.ex_badpix == desc2.ex_badpix) &&
                       (desc1.fits_type == desc2.fits_type) &&
                       (desc1.frame_type == desc2.frame_type),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "YCAL isn't in the correct format!!!");
        kmo_free_fits_desc(&desc2);
        kmo_init_fits_desc(&desc2);

        KMO_TRY_EXIT_IF_NULL(
            lcal_frame = kmo_dfs_get_frame(frameset, LCAL));
        desc2 = kmo_identify_fits_header(cpl_frame_get_filename(lcal_frame));
        KMO_TRY_CHECK_ERROR_STATE();

        KMO_TRY_ASSURE((desc1.nr_ext == desc2.nr_ext) &&
                       (desc1.ex_badpix == desc2.ex_badpix) &&
                       (desc1.fits_type == desc2.fits_type) &&
                       (desc1.frame_type == desc2.frame_type),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "YCAL isn't in the correct format!!!");
        kmo_free_fits_desc(&desc2);
        kmo_init_fits_desc(&desc2);

        KMO_TRY_EXIT_IF_NULL(
            flat_frame = kmo_dfs_get_frame(frameset, MASTER_FLAT));
        desc2 = kmo_identify_fits_header(cpl_frame_get_filename(flat_frame));
        KMO_TRY_CHECK_ERROR_STATE();

        KMO_TRY_ASSURE((desc2.nr_ext % 6 == 0) &&
                       (desc1.ex_badpix == desc2.ex_badpix) &&
                       (desc1.fits_type == desc2.fits_type) &&
                       (desc1.frame_type == desc2.frame_type),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "MASTER_FLAT isn't in the correct format!!!");
        kmo_free_fits_desc(&desc2);
        kmo_init_fits_desc(&desc2);

        if (cpl_frameset_count_tags(frameset, ILLUM_CORR) == 1) {
            KMO_TRY_EXIT_IF_NULL(
                illum_frame = kmo_dfs_get_frame(frameset, ILLUM_CORR));
            desc2 = kmo_identify_fits_header(cpl_frame_get_filename(illum_frame));
            KMO_TRY_CHECK_ERROR_STATE();
            KMO_TRY_ASSURE(((desc2.nr_ext == 24) || (desc2.nr_ext == 48)) &&
                           (desc2.ex_badpix == FALSE) &&
                           (desc2.fits_type == f2i_fits) &&
                           (desc2.frame_type == ifu_frame),
                           CPL_ERROR_ILLEGAL_INPUT,
                           "ILLUM_CORR isn't in the correct format!!!");
            kmo_free_fits_desc(&desc2);
            kmo_init_fits_desc(&desc2);
        }

        if (cpl_frameset_count_tags(frameset, SPEC_TYPE_LOOKUP) == 1) {
            KMO_TRY_EXIT_IF_NULL(
                tmp_frame = kmo_dfs_get_frame(frameset, SPEC_TYPE_LOOKUP));
            desc2 = kmo_identify_fits_header(cpl_frame_get_filename(tmp_frame));
            KMO_TRY_CHECK_ERROR_STATE();
            KMO_TRY_ASSURE((desc2.nr_ext == 1) &&
                           (desc2.ex_badpix == FALSE) &&
                           (desc2.fits_type == f2l_fits) &&
                           (desc2.frame_type == list_frame),
                           CPL_ERROR_ILLEGAL_INPUT,
                           "SPEC_TYPE_LOOKUP isn't in the correct format!!!");
            kmo_free_fits_desc(&desc2);
            kmo_init_fits_desc(&desc2);
        }

        if (cpl_frameset_count_tags(frameset, SOLAR_SPEC) == 1) {
            KMO_TRY_EXIT_IF_NULL(
                tmp_frame = kmo_dfs_get_frame(frameset, SOLAR_SPEC));
            desc2 = kmo_identify_fits_header(cpl_frame_get_filename(tmp_frame));
            KMO_TRY_CHECK_ERROR_STATE();
            KMO_TRY_ASSURE((desc2.nr_ext == 1) &&
                           (desc2.ex_badpix == FALSE) &&
                           (desc2.fits_type == f1s_fits) &&
                           (desc2.frame_type == spectrum_frame),
                           CPL_ERROR_ILLEGAL_INPUT,
                           "SOLAR_SPEC isn't in the correct format!!!");
            kmo_free_fits_desc(&desc2);
            kmo_init_fits_desc(&desc2);
        }

        if (cpl_frameset_count_tags(frameset, ATMOS_MODEL) == 1) {
            KMO_TRY_EXIT_IF_NULL(
                tmp_frame = kmo_dfs_get_frame(frameset, ATMOS_MODEL));
            desc2 = kmo_identify_fits_header(cpl_frame_get_filename(tmp_frame));
            KMO_TRY_CHECK_ERROR_STATE();
            KMO_TRY_ASSURE((desc2.nr_ext == 1) &&
                           (desc2.ex_badpix == FALSE) &&
                           (desc2.fits_type == f1s_fits) &&
                           (desc2.frame_type == spectrum_frame),
                           CPL_ERROR_ILLEGAL_INPUT,
                           "ATMOS_MODEL isn't in the correct format!!!");
            kmo_free_fits_desc(&desc2);
            kmo_init_fits_desc(&desc2);
        }

        KMO_TRY_EXIT_IF_NULL(
            tmp_frame = kmo_dfs_get_frame(frameset, STD));
        while (tmp_frame != NULL ) {
            desc2 = kmo_identify_fits_header(cpl_frame_get_filename(tmp_frame));
            KMO_TRY_CHECK_ERROR_STATE();
            KMO_TRY_ASSURE((desc2.nr_ext == 3) &&
                           (desc2.ex_badpix == FALSE) &&
                           (desc2.fits_type == raw_fits) &&
                           (desc2.frame_type == detector_frame),
                           CPL_ERROR_ILLEGAL_INPUT,
                           "STD isn't in the correct format!!!");
            nr_devices = desc2.nr_ext;
            kmo_free_fits_desc(&desc2);
            kmo_init_fits_desc(&desc2);

            tmp_frame = kmo_dfs_get_frame(frameset, NULL);
            KMO_TRY_CHECK_ERROR_STATE();
        }
        KMO_TRY_EXIT_IF_NULL(
            tmp_frame = kmo_dfs_get_frame(frameset, STD));
        KMO_TRY_EXIT_IF_NULL(
            suffix = kmo_dfs_get_suffix(tmp_frame, TRUE, FALSE));

        KMO_TRY_EXIT_IF_ERROR(
            kmo_check_frame_setup_md5_xycal(frameset));
        KMO_TRY_EXIT_IF_ERROR(
            kmo_check_frame_setup_md5(frameset));

        cpl_msg_info("", "Detected instrument setup:   %s", suffix+1);
        cpl_msg_info("", "(grating 1, 2 & 3)");

        // check which IFUs are active for all frames
        KMO_TRY_EXIT_IF_NULL(
            unused_ifus_before = kmo_get_unused_ifus(frameset, 0, 0));

        KMO_TRY_EXIT_IF_NULL(
            unused_ifus_after = kmo_duplicate_unused_ifus(unused_ifus_before));

        kmo_print_unused_ifus(unused_ifus_before, FALSE);

        /* --- load data --- */

        if ((cpl_frameset_count_tags(frameset, SPEC_TYPE_LOOKUP) == 1) &&
            ((strlen(spec_class) > 0) || (strlen(lum_class) > 0)))
        {
            // get star temperature out of SPEC_TYPE_LOOKUP table
            KMO_TRY_EXIT_IF_NULL(
                spec_type_LUT = kmo_dfs_load_table(frameset, SPEC_TYPE_LOOKUP, 1, 0));
            star_temperature = kmo_get_temperature(spec_type_LUT, spec_class, lum_class);
            KMO_TRY_CHECK_ERROR_STATE();
        } else if (cpl_frameset_count_tags(frameset, SPEC_TYPE_LOOKUP) != 1) {
            cpl_msg_warning("","No SPEC_TYPE_LOOKUP was provided! Can't divide blackbody.");
        } else if ((strlen(spec_class) == 0) || (strlen(lum_class) == 0)) {
//            cpl_msg_warning("","No startype was provided! Can't "
//                            "divide blackbody.");
        }

        // allocate intermediate memory
        KMO_TRY_EXIT_IF_NULL(
            stored_telluric_data    = (cpl_vector**)cpl_calloc(nr_devices*KMOS_IFUS_PER_DETECTOR,
                                                               sizeof(cpl_vector*)));
        KMO_TRY_EXIT_IF_NULL(
            stored_telluric_noise   = (cpl_vector**)cpl_calloc(nr_devices*KMOS_IFUS_PER_DETECTOR,
                                                               sizeof(cpl_vector*)));
        KMO_TRY_EXIT_IF_NULL(
            stored_starspec_data    = (cpl_vector**)cpl_calloc(nr_devices*KMOS_IFUS_PER_DETECTOR,
                                                               sizeof(cpl_vector*)));
        KMO_TRY_EXIT_IF_NULL(
            stored_starspec_noise   = (cpl_vector**)cpl_calloc(nr_devices*KMOS_IFUS_PER_DETECTOR,
                                                               sizeof(cpl_vector*)));
        KMO_TRY_EXIT_IF_NULL(
            stored_psf_data         = (cpl_image**)cpl_calloc(nr_devices*KMOS_IFUS_PER_DETECTOR,
                                                              sizeof(cpl_image*)));
        KMO_TRY_EXIT_IF_NULL(
            stored_mask             = (cpl_image**)cpl_calloc(nr_devices*KMOS_IFUS_PER_DETECTOR,
                                                              sizeof(cpl_image*)));
        KMO_TRY_EXIT_IF_NULL(
            stored_data_cube        = (cpl_imagelist**)cpl_calloc(nr_devices*KMOS_IFUS_PER_DETECTOR,
                                                              sizeof(cpl_imagelist*)));
        KMO_TRY_EXIT_IF_NULL(
            stored_noise_cube       = (cpl_imagelist**)cpl_calloc(nr_devices*KMOS_IFUS_PER_DETECTOR,
                                                              sizeof(cpl_imagelist*)));
        KMO_TRY_EXIT_IF_NULL(
            stored_qc_throughput    = (double*)cpl_calloc(nr_devices*KMOS_IFUS_PER_DETECTOR,
                                                          sizeof(double)));
        KMO_TRY_EXIT_IF_NULL(
            stored_sub_psf_headers  = (cpl_propertylist**)cpl_calloc(nr_devices*KMOS_IFUS_PER_DETECTOR,
                                                                     sizeof(cpl_propertylist*)));
        KMO_TRY_EXIT_IF_NULL(
            stored_sub_tel_data_headers = (cpl_propertylist**)cpl_calloc(nr_devices*KMOS_IFUS_PER_DETECTOR,
                                                                         sizeof(cpl_propertylist*)));
        KMO_TRY_EXIT_IF_NULL(
            stored_sub_tel_noise_headers = (cpl_propertylist**)cpl_calloc(nr_devices*KMOS_IFUS_PER_DETECTOR,
                                                                         sizeof(cpl_propertylist*)));

        if (save_cubes) {
            KMO_TRY_EXIT_IF_NULL(
                stored_sub_cube_data_headers = (cpl_propertylist**)cpl_calloc(nr_devices*KMOS_IFUS_PER_DETECTOR,
                                                                             sizeof(cpl_propertylist*)));
            KMO_TRY_EXIT_IF_NULL(
                stored_sub_cube_noise_headers = (cpl_propertylist**)cpl_calloc(nr_devices*KMOS_IFUS_PER_DETECTOR,
                                                                             sizeof(cpl_propertylist*)));
        }

        // get bounds
        KMO_TRY_EXIT_IF_NULL(
            tmp_header = kmo_dfs_load_primary_header(frameset, XCAL));
        KMO_TRY_EXIT_IF_NULL(
            bounds = kmclipm_extract_bounds(tmp_header));
        cpl_propertylist_delete(tmp_header); tmp_header = NULL;

        // setup grid definition, wavelength start and end points will be set
        // in the detector loop
        KMO_TRY_EXIT_IF_ERROR(
            kmclipm_setup_grid(&gd, imethod, neighborhoodRange, KMOS_PIX_RESOLUTION, 0.));

        // get valid STD frames with objects in it and associated sky exposures
        KMO_TRY_EXIT_IF_NULL(
            obj_sky_struct = kmo_create_objSkyStruct(frameset_std, STD, FALSE));
        kmo_print_objSkyStruct(obj_sky_struct);

        // loop the object-sky pairs
        if (obj_sky_struct->size == 0) {
            cpl_msg_warning(cpl_func,"Not a single frame contains an object");
        } else {
            strcpy(filename_telluric, TELLURIC);
            strcpy(filename_starspec, STAR_SPEC);
            strcpy(filename_psf, STD_IMAGE);
            strcpy(filename_mask, STD_MASK);
            strcpy(filename_cubes, STD_CUBE);

            obj_frame = obj_sky_struct->table[nr_exp].objFrame;
            KMO_TRY_EXIT_IF_NULL(
                main_header_tel = kmclipm_propertylist_load(cpl_frame_get_filename(obj_frame), 0));

            exptime = cpl_propertylist_get_double(main_header_tel, EXPTIME);
            KMO_TRY_CHECK_ERROR_STATE();

            // load, process & store frames

            for (i = 1; i <= nr_devices; i++) {
                // extract LCAL image close to ROTANGLE 0. assuming that the wavelength range
                // doesn't differ too much with different ROTANGLEs.
                print_cal_angle_msg_once = FALSE;
                print_xcal_angle_msg_once = FALSE;
                double rotangle_found;
                KMO_TRY_EXIT_IF_NULL(
                    lcal = kmo_dfs_load_cal_image(frameset, LCAL, i, FALSE, 0., FALSE, NULL,
                            &rotangle_found, -1, 0, 0));
                if (i==1) {
                    print_cal_angle_msg_once = TRUE;
                    print_xcal_angle_msg_once = TRUE;
                }
                if (tmp_band_method != NULL) {
                    band_method = atoi(tmp_band_method);
                }

                // get filter for this detector
                // ESO INS FILTi ID
                KMO_TRY_EXIT_IF_NULL(
                    keyword = cpl_sprintf("%s%d%s", IFU_FILTID_PREFIX, i, IFU_FILTID_POSTFIX));
                filter_id = cpl_propertylist_get_string(main_header_tel, keyword);
                cpl_free(keyword); keyword = NULL;

                KMO_TRY_EXIT_IF_NULL(
                    band_table = kmo_dfs_load_table(frameset, WAVE_BAND, 1, 0));
                KMO_TRY_EXIT_IF_ERROR(
                    kmclipm_setup_grid_band_lcal(&gd, lcal, filter_id,
                                                 band_method, band_table));
                cpl_image_delete(lcal); lcal = NULL;
                cpl_table_delete(band_table); band_table = NULL;

                // load sub_header of original F2D image
                KMO_TRY_EXIT_IF_NULL(
                    sub_header_orig = kmclipm_propertylist_load( cpl_frame_get_filename(obj_frame), i));

                for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
                    ifu_nr = (i-1)*KMOS_IFUS_PER_DETECTOR + j + 1;
                    // check if IFU is valid according to main header keywords &
                    // calibration files
                    // AND check if there is a sky frame available for this IFU
                    kmo_collapse_objSkyStruct(obj_sky_struct, ifu_nr,
                                              &obj_frame, &sky_frame);

                    KMO_TRY_EXIT_IF_NULL(
                        punused_ifus = cpl_array_get_data_int_const(unused_ifus_after[i-1]));

                    // Search for keyword ESO OCS ARMi NOTUSED
                    // If not present (CPL_ERROR_DATA_NOT_FOUND) we will eventually
                    // process standard star
                    KMO_TRY_EXIT_IF_NULL(
                        keyword = cpl_sprintf("%s%d%s", IFU_VALID_PREFIX, ifu_nr, IFU_VALID_POSTFIX));
                    tmp_str = cpl_propertylist_get_string(main_header_tel, keyword);
                    cpl_free(keyword); keyword = NULL;

                    if ((cpl_error_get_code() == CPL_ERROR_DATA_NOT_FOUND) &&
                        (bounds[2*(ifu_nr-1)] != -1) &&
                        (bounds[2*(ifu_nr-1)+1] != -1) &&
                        (sky_frame != NULL) &&
                        (punused_ifus[j] == 0))
                    {
                        cpl_error_reset();
                        // IFU is valid

                        if (sky_frame != NO_CORRESPONDING_SKYFRAME) {
                            cpl_msg_info("","Processing standard star in IFU %d "
                                         "(obj: %s, sky: %s)", ifu_nr,
                                         cpl_frame_get_filename(obj_frame),
                                         cpl_frame_get_filename(sky_frame));
                        } else {
                            sky_frame = NULL;
                            cpl_msg_warning("","Processing standard star in IFU %d "
                                         "(obj: %s, no corresponding sky frame",
                                         ifu_nr, cpl_frame_get_filename(obj_frame));
                        }

                        nr_std_stars++;

                        char *ggg = cpl_sprintf("%s%d", PRO_STD, ifu_nr);
                        KMO_TRY_EXIT_IF_ERROR(
                            cpl_propertylist_update_int(main_header_tel, ggg, 1));
                        cpl_free(ggg); ggg = NULL;

                        // calculate WCS and make copies of sub_header
                        KMO_TRY_EXIT_IF_NULL(
                            tmp_sub_header = cpl_propertylist_duplicate(sub_header_orig));
                        KMO_TRY_EXIT_IF_ERROR(
                            kmo_calc_wcs_gd(main_header_tel, tmp_sub_header, ifu_nr, gd));
                        KMO_TRY_EXIT_IF_NULL(
                            stored_sub_tel_data_headers[ifu_nr-1] =
                                   cpl_propertylist_duplicate(tmp_sub_header));
                        KMO_TRY_EXIT_IF_NULL(
                            stored_sub_psf_headers[ifu_nr-1] =
                                   cpl_propertylist_duplicate(tmp_sub_header));
                        if (save_cubes) {
                            KMO_TRY_EXIT_IF_NULL(
                                stored_sub_cube_data_headers[ifu_nr-1] =
                                       cpl_propertylist_duplicate(tmp_sub_header));
                        }
                        cpl_propertylist_delete(tmp_sub_header);
                        tmp_sub_header = NULL;

                        //
                        // adjust telluric-headers: copy CRPIX3 to CRPIX1,
                        //
                        cpl_propertylist_update_double(stored_sub_tel_data_headers[ifu_nr-1], CRVAL1,
                                cpl_propertylist_get_double(stored_sub_tel_data_headers[ifu_nr-1], CRVAL3));
                        cpl_propertylist_erase(stored_sub_tel_data_headers[ifu_nr-1], CRVAL2);
                        cpl_propertylist_erase(stored_sub_tel_data_headers[ifu_nr-1], CRVAL3);
                        KMO_TRY_CHECK_ERROR_STATE();

                        // CRPIX
                        cpl_propertylist_update_double(stored_sub_tel_data_headers[ifu_nr-1], CRPIX1,
                                cpl_propertylist_get_double(stored_sub_tel_data_headers[ifu_nr-1], CRPIX3));
                        cpl_propertylist_erase(stored_sub_tel_data_headers[ifu_nr-1], CRPIX2);
                        cpl_propertylist_erase(stored_sub_tel_data_headers[ifu_nr-1], CRPIX3);
                        KMO_TRY_CHECK_ERROR_STATE();

                        // CDELT
                        cdelt3 = cpl_propertylist_get_double(stored_sub_tel_data_headers[ifu_nr-1], CDELT3);
                        cpl_propertylist_update_double(stored_sub_tel_data_headers[ifu_nr-1], CDELT1,
                                cdelt3);
                        cpl_propertylist_erase(stored_sub_tel_data_headers[ifu_nr-1], CDELT2);
                        cpl_propertylist_erase(stored_sub_tel_data_headers[ifu_nr-1], CDELT3);
                        KMO_TRY_CHECK_ERROR_STATE();

                        // CTYPE
                        cpl_propertylist_update_string(stored_sub_tel_data_headers[ifu_nr-1], CTYPE1,
                                cpl_propertylist_get_string(stored_sub_tel_data_headers[ifu_nr-1], CTYPE3));
                        cpl_propertylist_erase(stored_sub_tel_data_headers[ifu_nr-1], CTYPE2);
                        cpl_propertylist_erase(stored_sub_tel_data_headers[ifu_nr-1], CTYPE3);
                        KMO_TRY_CHECK_ERROR_STATE();

                        // CUNIT
                        cpl_propertylist_update_string(stored_sub_tel_data_headers[ifu_nr-1], CUNIT1,
                                cpl_propertylist_get_string(stored_sub_tel_data_headers[ifu_nr-1], CUNIT3));
                        cpl_propertylist_erase(stored_sub_tel_data_headers[ifu_nr-1], CUNIT2);
                        cpl_propertylist_erase(stored_sub_tel_data_headers[ifu_nr-1], CUNIT3);

                        // CDx_x
                        cpl_propertylist_erase(stored_sub_tel_data_headers[ifu_nr-1], CD1_1);
                        cpl_propertylist_erase(stored_sub_tel_data_headers[ifu_nr-1], CD1_2);
                        cpl_propertylist_erase(stored_sub_tel_data_headers[ifu_nr-1], CD1_3);
                        cpl_propertylist_erase(stored_sub_tel_data_headers[ifu_nr-1], CD2_1);
                        cpl_propertylist_erase(stored_sub_tel_data_headers[ifu_nr-1], CD2_2);
                        cpl_propertylist_erase(stored_sub_tel_data_headers[ifu_nr-1], CD2_3);
                        cpl_propertylist_erase(stored_sub_tel_data_headers[ifu_nr-1], CD3_1);
                        cpl_propertylist_erase(stored_sub_tel_data_headers[ifu_nr-1], CD3_2);
                        cpl_propertylist_erase(stored_sub_tel_data_headers[ifu_nr-1], CD3_3);
                        KMO_TRY_CHECK_ERROR_STATE();

                        //
                        // adjust psf-headers: delete CRPIX3 etc.
                        //
                        cpl_propertylist_erase(stored_sub_psf_headers[ifu_nr-1], CRPIX3);
                        cpl_propertylist_erase(stored_sub_psf_headers[ifu_nr-1], CRPIX3);
                        cpl_propertylist_erase(stored_sub_psf_headers[ifu_nr-1], CDELT3);
                        cpl_propertylist_erase(stored_sub_psf_headers[ifu_nr-1], CRVAL3);
                        cpl_propertylist_erase(stored_sub_psf_headers[ifu_nr-1], CTYPE3);
                        cpl_propertylist_erase(stored_sub_psf_headers[ifu_nr-1], CUNIT3);
                        cpl_propertylist_erase(stored_sub_psf_headers[ifu_nr-1], CD1_3);
                        cpl_propertylist_erase(stored_sub_psf_headers[ifu_nr-1], CD2_3);
                        cpl_propertylist_erase(stored_sub_psf_headers[ifu_nr-1], CD3_1);
                        cpl_propertylist_erase(stored_sub_psf_headers[ifu_nr-1], CD3_2);
                        cpl_propertylist_erase(stored_sub_psf_headers[ifu_nr-1], CD3_3);
                        KMO_TRY_CHECK_ERROR_STATE();

                        KMO_TRY_EXIT_IF_ERROR(
                            kmo_reconstruct_sci(ifu_nr,
                                                bounds[2*(ifu_nr-1)],
                                                bounds[2*(ifu_nr-1)+1],
                                                obj_frame,
                                                STD,
                                                sky_frame,
                                                STD,
                                                flat_frame,
                                                xcal_frame,
                                                ycal_frame,
                                                lcal_frame,
                                                NULL,
                                                &gd,
                                                &stored_data_cube[ifu_nr-1],
                                                &stored_noise_cube[ifu_nr-1],
                                                flux,
                                                background,
                                                xcal_interpolation));

                        // divide illumination correction from the data_cube
                        // (illumination noise will be very small versus
                        // noise_cube, so it is skipped here)
                        if (cpl_frameset_count_tags(frameset, ILLUM_CORR) == 1) {
                            KMO_TRY_EXIT_IF_NULL(
                                illum_corr = kmo_dfs_load_image_frame(illum_frame, ifu_nr,
                                                                      FALSE, FALSE, NULL));
                            KMO_TRY_EXIT_IF_ERROR(
                                cpl_imagelist_divide_image(stored_data_cube[ifu_nr-1], illum_corr));
                            cpl_image_delete(illum_corr); illum_corr = NULL;
                        }

                        // calculate QC_STD_TRACE
                        // (the distance of the PSF to the centre)
                        KMO_TRY_EXIT_IF_ERROR(
                            kmo_calculate_std_trace(stored_data_cube[ifu_nr-1], fmethod, &std_trace));

                        KMO_TRY_EXIT_IF_ERROR(
                            kmclipm_update_property_double(stored_sub_psf_headers[ifu_nr-1],
                                                           QC_STD_TRACE, std_trace,
                                                           "[pix] distance of PSF and centre of IFU"));

                        KMO_TRY_EXIT_IF_NULL(
                            identified_slices = cpl_vector_new(cpl_imagelist_get_size(stored_data_cube[ifu_nr-1])));
                        KMO_TRY_EXIT_IF_ERROR(
                            cpl_vector_fill(identified_slices, 1.0));

                        // collapse cube and get PSF image
                        KMO_TRY_EXIT_IF_ERROR(
                            kmclipm_make_image(stored_data_cube[ifu_nr-1], NULL,
                                               &stored_psf_data[ifu_nr-1], NULL,
                                               identified_slices,
                                               cmethod,
                                               cpos_rej, cneg_rej, citer,
                                               cmax, cmin));
                        cpl_vector_delete(identified_slices);
                        identified_slices= NULL;

                        // fit a 2D profile to get a mask and fwhm in x and y,
                        KMO_TRY_EXIT_IF_NULL(
                            tmp_vec = kmo_fit_profile_2D(stored_psf_data[ifu_nr-1],
                                                         NULL,
                                                         fmethod,
                                                         &stored_mask[ifu_nr-1],
                                                         &pl_psf));

                        // normalise mask to 1 and clip values below 0.5
                        cpl_image_divide_scalar(stored_mask[ifu_nr-1], cpl_image_get_max(stored_mask[ifu_nr-1]));
                        KMO_TRY_CHECK_ERROR_STATE();

                        int dummy=0;
                        for (gx = 1; gx <= cpl_image_get_size_x(stored_mask[ifu_nr-1]); gx++) {
                            for (gy = 1; gy <= cpl_image_get_size_y(stored_mask[ifu_nr-1]); gy++) {
                                if (cpl_image_get(stored_mask[ifu_nr-1], gx, gy, &dummy) < 0.5) {
                                    cpl_image_set(stored_mask[ifu_nr-1], gx, gy, 0.);
                                } else {
                                    cpl_image_set(stored_mask[ifu_nr-1], gx, gy, 1.);
                                }
                            }
                        }
                        KMO_TRY_CHECK_ERROR_STATE();

                        // update subheader with fit parameters
                        KMO_TRY_EXIT_IF_ERROR(
                            cpl_propertylist_append(stored_sub_tel_data_headers[ifu_nr-1], pl_psf));
                        cpl_propertylist_delete(pl_psf); pl_psf = NULL;

                        // store QC_SPAT_RES (RMS of fwhm_x and fwhm_y)
                        double factor_fwhm = 2*sqrt(2*log(2));
                        double spat_res = pow(cpl_vector_get(tmp_vec, 4) * factor_fwhm, 2);
                        spat_res += pow(cpl_vector_get(tmp_vec, 5) * factor_fwhm, 2);
                        spat_res /= 2;
                        KMO_TRY_EXIT_IF_ERROR(
                            kmclipm_update_property_double(stored_sub_psf_headers[ifu_nr-1],
                                                           QC_SPAT_RES,
                                                           sqrt(spat_res)*KMOS_PIX_RESOLUTION,
                                                           "[arcsec] mean fwhm resolution of PSF"));
                        cpl_vector_delete(tmp_vec); tmp_vec = NULL;

                        // extract spectrum in masked area
                        KMO_TRY_EXIT_IF_ERROR(
                            kmo_priv_extract_spec(stored_data_cube[ifu_nr-1],
                                                  stored_noise_cube[ifu_nr-1],
                                                  stored_mask[ifu_nr-1],
                                                  &tmp_spec_data,
                                                  &tmp_spec_noise));

                        // store to save to disk later on
                        stored_starspec_data[ifu_nr-1] = cpl_vector_duplicate(tmp_spec_data);
                        if (tmp_spec_noise != NULL) {
                            stored_starspec_noise[ifu_nr-1] = cpl_vector_duplicate(tmp_spec_noise);
                        }
                        KMO_TRY_CHECK_ERROR_STATE();

                        // if magnitude is provided
                        // calculate zeropoint and throughput
                        if (has_magnitude) {
                            // extract spectrum of whole are for QC THRUHput and ZEROPOINT
                            KMO_TRY_EXIT_IF_ERROR(
                                kmo_priv_extract_spec(stored_data_cube[ifu_nr-1],
                                                      NULL,
                                                      NULL,
                                                      &spec_qc,
                                                      NULL));

                            // multiply spectrum with area of IFU (196) to get the sum
                            const cpl_image *tmp_img = cpl_imagelist_get(stored_data_cube[ifu_nr-1], 0);
                            int tmpx = cpl_image_get_size_x(tmp_img),
                                tmpy = cpl_image_get_size_x(tmp_img);
                            cpl_vector_multiply_scalar(spec_qc, tmpx*tmpy);
                        }

                        // calculate abscissa of output spectrum
                        KMO_TRY_EXIT_IF_NULL(
                            lambda_x = kmo_create_lambda_vec(gd.l.dim, 1,
                                                            gd.l.start,
                                                            gd.l.delta));
                        //
                        // spectrum correction
                        //
                        if ((strcmp(star_type, "O") == 0) ||
                            (strcmp(star_type, "B") == 0) ||
                            (strcmp(star_type, "A") == 0) ||
                            (strcmp(star_type, "F") == 0))
                        {
                            // we have a OBAF star

                            // if ATMOS_MODEL is present, lines will be removed
                            if (cpl_frameset_count_tags(frameset, ATMOS_MODEL) == 1) {
                                // interpolate ATMOS_MODEL to same scale as data
                                KMO_TRY_EXIT_IF_NULL(
                                    tmp_frame = kmo_dfs_get_frame(frameset, ATMOS_MODEL));

                                KMO_TRY_EXIT_IF_NULL(
                                    atmos_model = kmo_interpolate_vector_wcs(tmp_frame, lambda_x));
cpl_vector *tmp_spec_data_orig = NULL;
int plot_it = 0;
if (plot_it) {
    // store original spectrum
    KMO_TRY_EXIT_IF_NULL(
        tmp_spec_data_orig = cpl_vector_duplicate(tmp_spec_data));
}
                                // remove band-specific lines
                                if (strcmp(filter_id, "H") == 0) {
                                    for (l = 0; l < nr_lines_h; l++) {
                                        KMO_TRY_EXIT_IF_ERROR(
                                            kmo_remove_line(tmp_spec_data, lambda_x, atmos_model, lines_center_h[l], lines_width_h[l]));
                                    }
                                } else if (strcmp(filter_id, "HK") == 0) {
                                    for (l = 0; l < nr_lines_hk; l++) {
                                        KMO_TRY_EXIT_IF_ERROR(
                                            kmo_remove_line(tmp_spec_data, lambda_x, atmos_model, lines_center_hk[l], lines_width_hk[l]));
                                    }
                                } else if (strcmp(filter_id, "K") == 0) {
                                    for (l = 0; l < nr_lines_k; l++) {
                                        KMO_TRY_EXIT_IF_ERROR(
                                            kmo_remove_line(tmp_spec_data, lambda_x, atmos_model, lines_center_k[l], lines_width_k[l]));
                                    }
                                } else if (strcmp(filter_id, "IZ") == 0) {
                                    for (l = 0; l < nr_lines_iz; l++) {
                                        KMO_TRY_EXIT_IF_ERROR(
                                            kmo_remove_line(tmp_spec_data, lambda_x, atmos_model, lines_center_iz[l], lines_width_iz[l]));
                                    }
                                } else if (strcmp(filter_id, "YJ") == 0) {
                                    for (l = 0; l < nr_lines_yj; l++) {
                                        KMO_TRY_EXIT_IF_ERROR(
                                            kmo_remove_line(tmp_spec_data, lambda_x, atmos_model, lines_center_yj[l], lines_width_yj[l]));
                                    }
                                }
if (plot_it) {
    cpl_vector *tmp_spec_data_atmo = NULL;
    cpl_vector *tmp_spec_data_new = NULL;
    KMO_TRY_EXIT_IF_NULL(
        tmp_spec_data_atmo = cpl_vector_duplicate(tmp_spec_data_orig));
    KMO_TRY_EXIT_IF_NULL(
        tmp_spec_data_new = cpl_vector_duplicate(tmp_spec_data));
    KMO_TRY_EXIT_IF_ERROR(
        cpl_vector_divide(tmp_spec_data_atmo, atmos_model));

    char *sss = cpl_sprintf("atmo_div_%s.fits", filter_id);
    if (i == 1) {
        cpl_vector_save(tmp_spec_data_atmo, sss, CPL_BPP_IEEE_DOUBLE, stored_sub_tel_data_headers[ifu_nr-1], CPL_IO_CREATE);
    } else {
        cpl_vector_save(tmp_spec_data_atmo, sss, CPL_BPP_IEEE_DOUBLE, stored_sub_tel_data_headers[ifu_nr-1], CPL_IO_EXTEND);
    }

    cpl_vector *med_vec = cpl_vector_duplicate(tmp_spec_data_orig);
    double 	median = cpl_vector_get_median(med_vec);
    cpl_vector_delete(med_vec);
    int ii = 0;
    for (ii = 0; ii < cpl_vector_get_size(tmp_spec_data_orig); ii++) {
        if (cpl_vector_get(tmp_spec_data_orig, ii) < median/8)
            cpl_vector_set(tmp_spec_data_orig, ii, 0);
        if (cpl_vector_get(tmp_spec_data_atmo, ii) < median/8)
            cpl_vector_set(tmp_spec_data_atmo, ii, 0);
        if (cpl_vector_get(tmp_spec_data_new, ii) < median/8)
            cpl_vector_set(tmp_spec_data_new, ii, 0);

        if (cpl_vector_get(tmp_spec_data_orig, ii) > 3*median)
            cpl_vector_set(tmp_spec_data_orig, ii, 3*median);
        if (cpl_vector_get(tmp_spec_data_atmo, ii) > 3*median)
            cpl_vector_set(tmp_spec_data_atmo, ii, 3*median);
        if (cpl_vector_get(tmp_spec_data_new, ii) > 3*median)
            cpl_vector_set(tmp_spec_data_new, ii, 3*median);
    }

    double *pspec_dup = cpl_vector_get_data(tmp_spec_data_atmo);
    for (ii = 0; ii < cpl_vector_get_size(tmp_spec_data_atmo); ii++) {
        if (kmclipm_is_nan_or_inf(pspec_dup[ii])) {
            pspec_dup[ii] = 0.;
        }
    }

    cpl_bivector *plots[3];
    plots[0] = cpl_bivector_wrap_vectors((cpl_vector*)lambda_x, tmp_spec_data_orig);
    plots[1] = cpl_bivector_wrap_vectors((cpl_vector*)lambda_x, tmp_spec_data_atmo);
    plots[2] = cpl_bivector_wrap_vectors((cpl_vector*)lambda_x, tmp_spec_data_new);
    char *options[3] = {"w l t 'original'",
                        "w l t 'atmo divided'",
                        "w l t 'lines removed'"};
    sss = cpl_sprintf("set title '%s-band line removal (DET #%d)';", filter_id, i);
    cpl_plot_bivectors(sss,
                       (const char**)options, "", (const cpl_bivector**)plots, 3);
//    cpl_plot_bivectors("set title 'Spectrum with lines removed'; set xrange [2.14:2.19];",
//                       (const char**)options, "", (const cpl_bivector**)plots, 2);
    cpl_bivector_unwrap_vectors(plots[0]);
    cpl_bivector_unwrap_vectors(plots[1]);
    cpl_bivector_unwrap_vectors(plots[2]);
    cpl_free(sss); sss = NULL;
    cpl_vector_delete(tmp_spec_data_orig); tmp_spec_data_orig = NULL;
    cpl_vector_delete(tmp_spec_data_atmo); tmp_spec_data_atmo = NULL;
    cpl_vector_delete(tmp_spec_data_new); tmp_spec_data_new = NULL;
}
                                cpl_vector_delete(atmos_model); atmos_model = NULL;
                            } else {
                                if (line_warning == FALSE) {
                                    cpl_msg_warning("", "No atmospheric model (ATMOS_MODEL) provided! "
                                                        "Won't remove any lines.");
                                    line_warning = TRUE;
                                }
                            }
                        } else if (strcmp(star_type, "G") == 0) {
                            // we have a G star
                            if (cpl_frameset_count_tags(frameset, SOLAR_SPEC) == 1) {
                                // interpolate SOLAR_SPEC to same scale as data
                                // and divide it
                                KMO_TRY_EXIT_IF_NULL(
                                    tmp_frame = kmo_dfs_get_frame(frameset, SOLAR_SPEC));

                                // check if SOLAR_SPEC is the filter_id-one
                                KMO_TRY_EXIT_IF_NULL(
                                    tmp_sub_header = kmclipm_propertylist_load(cpl_frame_get_filename(tmp_frame), 0));
                                KMO_TRY_EXIT_IF_NULL(
                                    tmp_str = cpl_propertylist_get_string(tmp_sub_header, FILT_ID));
                                KMO_TRY_ASSURE(strcmp(filter_id, tmp_str) == 0,
                                               CPL_ERROR_ILLEGAL_INPUT,
                                               "SOLAR_SPEC model must have primary "
                                               "keyword '%s' equal '%s'!!!",
                                               FILT_ID, filter_id);
                                cpl_propertylist_delete(tmp_sub_header); tmp_sub_header = NULL;

                                KMO_TRY_EXIT_IF_NULL(
                                    solar_spec = kmo_interpolate_vector_wcs(tmp_frame, lambda_x));

                                // values are set to zero if solar_spec isn't
                                // overlapping wavelength range of star apectrum
                                // completely
                                KMO_TRY_EXIT_IF_ERROR(
                                    cpl_vector_divide(tmp_spec_data, solar_spec));
                                cpl_vector_delete(solar_spec); solar_spec = NULL;
                            } else {
                                if (print_warning_once == TRUE) {
                                    cpl_msg_warning("","No solar spectrum (SOLAR_SPEC) provided! "
                                                       "Can't divide it from extracted "
                                                       "standard star spectrum!");
                                    print_warning_once = FALSE;
                                }
                            }
                        } else {
//                            cpl_msg_warning("","No startype was provided! Can't"
//                                            " divide solar spectrum for G stars "
//                                            "or fit a profile to atmospheric "
//                                            "transmission for OBAF stars.");
                        }

                        if (star_temperature > 0.0) {
                            // divide blackbody from tmp_spec_data
                            KMO_TRY_EXIT_IF_ERROR(
                                kmo_divide_blackbody(tmp_spec_data, lambda_x, star_temperature));
                        }

                        cpl_vector_delete(lambda_x); lambda_x = NULL;

                        // normalise telluric and its noise
                        // mean is taken in lambda defined range
                        KMO_TRY_EXIT_IF_ERROR(
                            kmo_calc_band_mean(stored_sub_tel_data_headers[ifu_nr-1],
                                               filter_id,
                                               tmp_spec_data,
                                               tmp_spec_noise,
                                               &mean_data,
                                               &mean_noise));

                        KMO_TRY_EXIT_IF_ERROR(
                            cpl_vector_divide_scalar(tmp_spec_data, mean_data));

                        if (tmp_spec_noise != NULL) {
                            KMO_TRY_EXIT_IF_ERROR(
                                cpl_vector_divide_scalar(tmp_spec_noise, mean_noise));

                            // set noise spectrum also to zero when solar_spec is too short
                            KMO_TRY_EXIT_IF_NULL(
                                ptmp_spec_data = cpl_vector_get_data_const(tmp_spec_data));
                            KMO_TRY_EXIT_IF_NULL(
                                ptmp_spec_noise = cpl_vector_get_data(tmp_spec_noise));
                            for (k = 0; k < cpl_vector_get_size(tmp_spec_data); k++) {
                                if (ptmp_spec_data[k] == 0.0) {
                                    ptmp_spec_noise[k] = 0.0;
                                }
                            }
                        }
                        KMO_TRY_CHECK_ERROR_STATE();

                        // store telluric & error spectrum
                        stored_telluric_data[ifu_nr-1] = tmp_spec_data;
                        stored_telluric_noise[ifu_nr-1] = tmp_spec_noise;

                        // if magnitude is provided
                        // calculate zeropoint and throughput
                        if (has_magnitude) {
                            // calculate QC THROUGHPUT
                            crpix1 = cpl_propertylist_get_double(stored_sub_tel_data_headers[ifu_nr-1], CRPIX1);
                            crval1 = cpl_propertylist_get_double(stored_sub_tel_data_headers[ifu_nr-1], CRVAL1);
                            cdelt1 = cpl_propertylist_get_double(stored_sub_tel_data_headers[ifu_nr-1], CDELT1);
                            KMO_TRY_CHECK_ERROR_STATE();

                            KMO_TRY_EXIT_IF_ERROR(
                                kmo_calc_counts(spec_qc, filter_id,
                                                crpix1, crval1, cdelt1,
                                                &counts1, &counts2));
                            KMO_TRY_CHECK_ERROR_STATE();

                            counts1 /= exptime;
                            counts2 /= exptime;

                            stored_qc_throughput[ifu_nr-1] =
                                kmo_calc_throughput(magnitude1, magnitude2, counts1, counts2,
                                                    cpl_propertylist_get_double(stored_sub_tel_data_headers[ifu_nr-1], GAIN),
                                                    filter_id);
                            KMO_TRY_CHECK_ERROR_STATE();

                            if (kmclipm_is_nan_or_inf(stored_qc_throughput[ifu_nr-1])) {
                                stored_qc_throughput[ifu_nr-1] = -1;
                            }
                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_double(stored_sub_tel_data_headers[ifu_nr-1],
                                                               QC_THROUGHPUT,
                                                               stored_qc_throughput[ifu_nr-1],
                                                               "[] IFU throughput"));

                            // calculate QC ZEROPOINT
                            zeropoint = kmo_calc_zeropoint(magnitude1, magnitude2, counts1, counts2, cdelt3, filter_id);
                            if (kmclipm_is_nan_or_inf(zeropoint)) {
                                zeropoint = -1;
                            }
                            KMO_TRY_CHECK_ERROR_STATE();

                            KMO_TRY_EXIT_IF_ERROR(
                                kmclipm_update_property_double(stored_sub_tel_data_headers[ifu_nr-1],
                                                               QC_ZEROPOINT,
                                                               zeropoint,
                                                               "[mag] IFU zeropoint"));
                        }
                        cpl_vector_delete(spec_qc);spec_qc = NULL;
                    } else {
                        cpl_error_reset();
                        // IFU is invalid
                        KMO_TRY_EXIT_IF_NULL(
                            stored_sub_tel_data_headers[ifu_nr-1] =
                                cpl_propertylist_duplicate(sub_header_orig));
                        KMO_TRY_EXIT_IF_NULL(
                            stored_sub_tel_noise_headers[ifu_nr-1] =
                                cpl_propertylist_duplicate(sub_header_orig));
                        KMO_TRY_EXIT_IF_NULL(
                            stored_sub_psf_headers[ifu_nr-1] =
                                cpl_propertylist_duplicate(sub_header_orig));
                        if (save_cubes) {
                            KMO_TRY_EXIT_IF_NULL(
                                stored_sub_cube_data_headers[ifu_nr-1] =
                                    cpl_propertylist_duplicate(sub_header_orig));
                            KMO_TRY_EXIT_IF_NULL(
                                stored_sub_cube_noise_headers[ifu_nr-1] =
                                    cpl_propertylist_duplicate(sub_header_orig));
                        }
                    }

                    // create EXTNAME keyword as DATA
                    KMO_TRY_EXIT_IF_NULL(
                        extname = kmo_extname_creator(ifu_frame, ifu_nr, EXT_DATA));
                    KMO_TRY_EXIT_IF_ERROR(
                        kmclipm_update_property_string(stored_sub_tel_data_headers[ifu_nr-1],
                                                       EXTNAME, extname, "FITS extension name"));
                    KMO_TRY_EXIT_IF_ERROR(
                        kmclipm_update_property_string(stored_sub_psf_headers[ifu_nr-1],
                                                       EXTNAME, extname, "FITS extension name"));
                    if (save_cubes) {
                        KMO_TRY_EXIT_IF_ERROR(
                            kmclipm_update_property_string(stored_sub_cube_data_headers[ifu_nr-1],
                                                           EXTNAME, extname, "FITS extension name"));
                    }
                    cpl_free(extname); extname = NULL;

                    // create EXTNAME keyword as NOISE
                    if (stored_sub_tel_noise_headers[ifu_nr-1] == NULL) {
                        KMO_TRY_EXIT_IF_NULL(
                            stored_sub_tel_noise_headers[ifu_nr-1] =
                                    cpl_propertylist_duplicate(
                                            stored_sub_tel_data_headers[ifu_nr-1]));
                    }
                    KMO_TRY_EXIT_IF_NULL(
                        extname = kmo_extname_creator(ifu_frame, ifu_nr, EXT_NOISE));
                    KMO_TRY_EXIT_IF_ERROR(
                        kmclipm_update_property_string(stored_sub_tel_noise_headers[ifu_nr-1],
                                                       EXTNAME, extname, "FITS extension name"));
                    if (save_cubes) {
                        KMO_TRY_EXIT_IF_NULL(
                            stored_sub_cube_noise_headers[ifu_nr-1] =
                                    cpl_propertylist_duplicate(
                                            stored_sub_cube_data_headers[ifu_nr-1]));
                        KMO_TRY_EXIT_IF_ERROR(
                            kmclipm_update_property_string(stored_sub_cube_noise_headers[ifu_nr-1],
                                                           EXTNAME, extname, "FITS extension name"));
                    }
                    cpl_free(extname); extname = NULL;
                } // for j ifus (load, process & store)
                cpl_propertylist_delete(sub_header_orig); sub_header_orig = NULL;
            } // for i detectors (load, process & store)
            KMO_TRY_CHECK_ERROR_STATE();

            // write QC parameter: nr of std stars
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_update_property_int(main_header_tel, QC_NR_STD_STARS,
                                        nr_std_stars, "[] Nr. of std stars"));

            // update which IFUs are not used
            kmo_print_unused_ifus(unused_ifus_after, TRUE);

            KMO_TRY_EXIT_IF_ERROR(
                kmo_set_unused_ifus(unused_ifus_after, main_header_tel, "kmo_std_star"));

            KMO_TRY_EXIT_IF_NULL(
                main_header_psf = cpl_propertylist_duplicate(main_header_tel));

            if (has_magnitude) {
                // calculate QC THROUGHPUT MEAN and QC THROUGHPUT SDV
                // and update main header
                KMO_TRY_EXIT_IF_ERROR(
                    kmo_calc_mean_throughput(stored_qc_throughput,
                                             nr_devices * KMOS_IFUS_PER_DETECTOR,
                                             &throughput_mean,
                                             &throughput_sdv));
                KMO_TRY_EXIT_IF_ERROR(
                    kmclipm_update_property_double(main_header_tel, QC_THROUGHPUT_MEAN,
                                                   throughput_mean, "[] mean throughput for all detectors"));
                KMO_TRY_EXIT_IF_ERROR(
                    kmclipm_update_property_double(main_header_tel, QC_THROUGHPUT_SDV,
                                                   throughput_sdv, "[] stdev throughput for all detectors"));
            }
            KMO_TRY_CHECK_ERROR_STATE();

            //
            // save output data
            //
            if (!suppress_extension) {
                KMO_TRY_EXIT_IF_NULL(
                    fn_suffix = cpl_sprintf("%s", suffix));
            } else {
                KMO_TRY_EXIT_IF_NULL(
                    fn_suffix = cpl_sprintf("%s", ""));
            }

            // save primary extension
            cpl_msg_info("","Saving STD exposure No. %d", nr_exp+1);
            KMO_TRY_EXIT_IF_ERROR(
                kmo_dfs_save_main_header(frameset, filename_telluric, fn_suffix,
                                         obj_frame, main_header_tel, parlist,
                                         cpl_func));
            KMO_TRY_EXIT_IF_ERROR(
                kmo_dfs_save_main_header(frameset, filename_starspec, fn_suffix,
                                         obj_frame, main_header_tel, parlist,
                                         cpl_func));
            KMO_TRY_EXIT_IF_ERROR(
                kmo_dfs_save_main_header(frameset, filename_mask, fn_suffix,
                                         obj_frame, main_header_psf, parlist,
                                         cpl_func));
            KMO_TRY_EXIT_IF_ERROR(
                kmo_dfs_save_main_header(frameset, filename_psf, fn_suffix,
                                         obj_frame, main_header_psf, parlist,
                                         cpl_func));
            if (save_cubes) {
                KMO_TRY_EXIT_IF_ERROR(
                    kmo_dfs_save_main_header(frameset, filename_cubes, fn_suffix,
                                             obj_frame, main_header_psf, parlist,
                                             cpl_func));
            }

            // save stored frames
            for (i = 1; i <= nr_devices; i++) {
                for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
                    ifu_nr = (i-1)*KMOS_IFUS_PER_DETECTOR + j + 1;

                    // save telluric-vector
kmclipm_vector *ddd = NULL;
if (stored_telluric_data[ifu_nr-1] != NULL)
ddd = kmclipm_vector_create(cpl_vector_duplicate(stored_telluric_data[ifu_nr-1]));
                    KMO_TRY_EXIT_IF_ERROR(
                        kmo_dfs_save_vector(ddd, filename_telluric, fn_suffix,
                                            stored_sub_tel_data_headers[ifu_nr-1],
                                            0./0.));
kmclipm_vector_delete(ddd); ddd =NULL;

if (stored_telluric_noise[ifu_nr-1] != NULL)
ddd = kmclipm_vector_create(cpl_vector_duplicate(stored_telluric_noise[ifu_nr-1]));
                    KMO_TRY_EXIT_IF_ERROR(
                        kmo_dfs_save_vector(ddd, filename_telluric, fn_suffix,
                                            stored_sub_tel_noise_headers[ifu_nr-1],
                                            0./0.));
kmclipm_vector_delete(ddd); ddd =NULL;

                    // save star_spec-vector
if (stored_starspec_data[ifu_nr-1] != NULL)
ddd = kmclipm_vector_create(cpl_vector_duplicate(stored_starspec_data[ifu_nr-1]));
                    KMO_TRY_EXIT_IF_ERROR(
                        kmo_dfs_save_vector(ddd, filename_starspec, fn_suffix,
                                            stored_sub_tel_data_headers[ifu_nr-1],
                                            0./0.));
kmclipm_vector_delete(ddd); ddd =NULL;

if (stored_starspec_noise[ifu_nr-1] != NULL)
ddd = kmclipm_vector_create(cpl_vector_duplicate(stored_starspec_noise[ifu_nr-1]));
                    KMO_TRY_EXIT_IF_ERROR(
                        kmo_dfs_save_vector(ddd, filename_starspec, fn_suffix,
                                            stored_sub_tel_noise_headers[ifu_nr-1],
                                            0./0.));
kmclipm_vector_delete(ddd); ddd =NULL;

                    // save psf-image
                    KMO_TRY_EXIT_IF_ERROR(
                        kmo_dfs_save_image(stored_psf_data[ifu_nr-1],
                                           filename_psf, fn_suffix,
                                           stored_sub_psf_headers[ifu_nr-1],
                                           0./0.));

                    // save mask-image
                    KMO_TRY_EXIT_IF_ERROR(
                        kmo_dfs_save_image(stored_mask[ifu_nr-1],
                                           filename_mask, fn_suffix,
                                           stored_sub_psf_headers[ifu_nr-1],
                                           0./0.));
                    // save reonstructed cubes
                    if (save_cubes) {
                        KMO_TRY_EXIT_IF_ERROR(
                            kmo_dfs_save_cube(stored_data_cube[ifu_nr-1],
                                              filename_cubes, fn_suffix,
                                              stored_sub_cube_data_headers[ifu_nr-1],
                                              0./0.));
                        KMO_TRY_EXIT_IF_ERROR(
                            kmo_dfs_save_cube(stored_noise_cube[ifu_nr-1],
                                              filename_cubes, fn_suffix,
                                              stored_sub_cube_noise_headers[ifu_nr-1],
                                              0./0.));
                    }
                } // for j ifus (save stored)
            } // for i detectors (save stored)
            KMO_TRY_CHECK_ERROR_STATE();
        } // if (frameCnt == 0)
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        ret_val = -1;
    }

    kmo_delete_objSkyStruct(obj_sky_struct);
    kmo_free_fits_desc(&desc1);
    kmo_free_fits_desc(&desc2);
    kmo_free_unused_ifus(unused_ifus_before); unused_ifus_before = NULL;
    kmo_free_unused_ifus(unused_ifus_after); unused_ifus_after = NULL;
    cpl_free(bounds); bounds = NULL;
    cpl_propertylist_delete(main_header_tel); main_header_tel = NULL;
    cpl_propertylist_delete(main_header_psf); main_header_psf = NULL;
    cpl_vector_delete(atmos_model); atmos_model = NULL;
    cpl_vector_delete(solar_spec); solar_spec = NULL;
    cpl_table_delete(spec_type_LUT); spec_type_LUT = NULL;
    cpl_vector_delete(identified_slices); identified_slices = NULL;
    cpl_propertylist_delete(sub_header_orig); sub_header_orig = NULL;
    for (i = 0; i < nr_devices * KMOS_IFUS_PER_DETECTOR; i++) {
        cpl_vector_delete(stored_telluric_data[i]); stored_telluric_data[i] = NULL;
        cpl_vector_delete(stored_telluric_noise[i]); stored_telluric_noise[i] = NULL;
        cpl_vector_delete(stored_starspec_data[i]); stored_starspec_data[i] = NULL;
        cpl_vector_delete(stored_starspec_noise[i]); stored_starspec_noise[i] = NULL;
        cpl_image_delete(stored_psf_data[i]); stored_psf_data[i] = NULL;
        cpl_propertylist_delete(stored_sub_tel_data_headers[i]); stored_sub_tel_data_headers[i] = NULL;
        cpl_propertylist_delete(stored_sub_tel_noise_headers[i]); stored_sub_tel_noise_headers[i] = NULL;
        if (save_cubes) {
            cpl_propertylist_delete(stored_sub_cube_data_headers[i]); stored_sub_cube_data_headers[i] = NULL;
            cpl_propertylist_delete(stored_sub_cube_noise_headers[i]); stored_sub_cube_noise_headers[i] = NULL;
        }
        cpl_propertylist_delete(stored_sub_psf_headers[i]); stored_sub_psf_headers[i] = NULL;
        cpl_image_delete(stored_mask[i]); stored_mask[i] = NULL;
        cpl_imagelist_delete(stored_data_cube[i]); stored_data_cube[i] = NULL;
        cpl_imagelist_delete(stored_noise_cube[i]); stored_noise_cube[i] = NULL;
    }
    cpl_free(stored_telluric_data); stored_telluric_data = NULL;
    cpl_free(stored_telluric_noise); stored_telluric_noise = NULL;
    cpl_free(stored_starspec_data); stored_starspec_data = NULL;
    cpl_free(stored_starspec_noise); stored_starspec_noise = NULL;
    cpl_free(stored_psf_data); stored_psf_data = NULL;
    cpl_free(stored_sub_tel_data_headers); stored_sub_tel_data_headers = NULL;
    cpl_free(stored_sub_tel_noise_headers); stored_sub_tel_noise_headers = NULL;
    if (save_cubes) {
        cpl_free(stored_sub_cube_data_headers); stored_sub_cube_data_headers = NULL;
        cpl_free(stored_sub_cube_noise_headers); stored_sub_cube_noise_headers = NULL;
    }
    cpl_free(stored_sub_psf_headers); stored_sub_psf_headers = NULL;
    cpl_free(stored_qc_throughput); stored_qc_throughput = NULL;
    cpl_free(suffix); suffix = NULL;
    cpl_free(fn_suffix); fn_suffix = NULL;
    cpl_free(stored_mask); stored_mask = NULL;
    cpl_free(stored_data_cube); stored_data_cube = NULL;
    cpl_free(stored_noise_cube); stored_noise_cube = NULL;
    cpl_free(grat_id); grat_id = NULL;
    cpl_frameset_delete(frameset_std); frameset_std = NULL;

    return ret_val;
}

/**@}*/
