/* 
 * 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
 */

#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"

const int       nr_lines_h = 10;
const double    lines_center_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
};
const double    lines_width_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
};
const int       nr_lines_k = 2;
const double    lines_center_k[] = {
    2.1120,     // HeI          // triplet
    2.16569     // Br-gamma
};
const double lines_width_k[] = {
    0.01,       // HeI          // triplet
    0.015       // Br-gamma
};
const int       nr_lines_hk = 12;
const double    lines_center_hk[] = {
    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
    2.1120,     // HeI          // triplet
    2.16569     // Br-gamma
};
const double lines_width_hk[] = {
    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
    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.08331,    // HeI
    1.09160,    // HeI
    1.09389,    // Pa-gamma
    1.19723,    // HeI
    1.28191,    // Pa-beta
    1.27882,    // HeI
    1.29720     // HeI
};
const double    lines_width_yj[] = {
    .01,        //0.005,    // HeI
    .01,        //0.002,    // HeI
    0.02,       // Pa-gamma
    0.003,      // HeI
    0.02,       // Pa-beta
    0.0025,     // HeI
    0.002       // HeI
};

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

static int kmos_std_star_compute_ifu(
        cpl_propertylist    *   sub_header_orig,
        cpl_frame           *   obj_frame,
        cpl_frame           *   sky_frame,
        cpl_frame           *   flat_frame,
        cpl_frame           *   xcal_frame,
        cpl_frame           *   ycal_frame,
        cpl_frame           *   lcal_frame,
        cpl_frame           *   illum_frame,
        cpl_frame           *   atmos_frame,
        cpl_frame           *   solar_frame,
        int                     ifu_nr,
        cpl_propertylist    *   main_header_tel,
        gridDefinition          gd,
        int                     low_bound,
        int                     high_bound,
        const char          *   fmethod,
        int                     flux,
        int                     xcal_interpolation,
        const char          *   mask_method,
        const char          *   cmethod,
        double                  cpos_rej,
        double                  cneg_rej,
        int                     citer,
        int                     cmax,
        int                     cmin,
        double                  cen_x,
        double                  cen_y,
        double                  radius,
        const char          *   filter_id,
        char                    star_type,
        int                     no_noise,
        int                     is_stdstarscipatt,
        skySkyStruct            sky_sky_struct,
        double                  star_temp,
        cpl_vector          **  spec_qc,
        cpl_propertylist    **  out_sub_tel_data_header,
        cpl_propertylist    **  out_sub_psf_header,
        cpl_propertylist    **  out_sub_cube_data_header,
        cpl_imagelist       **  out_data_cube,
        cpl_imagelist       **  out_noise_cube,
        cpl_image           **  out_psf_data,
        cpl_image           **  out_mask,
        cpl_vector          **  out_starspec_data,
        cpl_vector          **  out_starspec_noise,
        cpl_vector          **  out_noisespec,
        cpl_vector          **  out_telluric_data,
        cpl_vector          **  out_telluric_noise) ;
static int kmos_std_star_check_inputs(
        cpl_frameset            *   frameset,
        const char              *   magnitude_txt,
        int                     *   is_stdstarscipatt,
        int                     *   compute_qcs,
        double                  *   magnitude1,
        double                  *   magnitude2) ;
static int kmos_std_star_plot(void) ;
static int kmos_std_star_adjust_double(
        cpl_propertylist    *   header,
        const char          *   key1,
        const char          *   key2,
        const char          *   key3) ;
static int kmos_std_star_adjust_string(
        cpl_propertylist    *   header,
        const char          *   key1,
        const char          *   key2,
        const char          *   key3) ;
static cpl_frameset * kmos_std_star_extract_same_grat_stds(
        cpl_frameset        *   in,
        int                 *   same_gratings) ;

static int kmos_std_star_create(cpl_plugin *);
static int kmos_std_star_exec(cpl_plugin *);
static int kmos_std_star_destroy(cpl_plugin *);
static int kmos_std_star(cpl_parameterlist *, cpl_frameset *);

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

static char kmos_std_star_description[] =
"This recipe creates a telluric frame and a PSF frames.\n"
"Since there cannot be 1 std star per IFU in one exposure, we use several\n"
"exposures in order to have at least one standard star and one sky\n"
"in each IFU. The frames are organised following this logic:\n"
"1. For each IFU the first standard star in the list of frames is\n"
"   taken. All subsequent standard star exposures for this IFU are ignored\n"
"2. A closest in time sky exposure is uѕed\n"
"3. IFUs not containing a standard star and a sky will be empty in the result\n"
"\n"
"NOISE_SPEC contains the shot noise [sqrt(counts*gain)/gain]\n"
"If the exposures have been taken with KMOS_spec_cal_stdstarscipatt, an\n"
"additional noise component is added: All existing sky exposures for an IFU\n"
"are subtracted pairwise, spectra are extracted and the std deviation is \n"
"computed\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-wl       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 mask used to extract the star spec   \n"
"   NOISE_SPEC              F1I    The extracted noise spectrum             \n"
"---------------------------------------------------------------------------\n"
"\n";

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

/*----------------------------------------------------------------------------*/
/**
    @defgroup    kmos_std_star   Create the telluric correction frame
 */
/*----------------------------------------------------------------------------*/

/**@{*/

/*----------------------------------------------------------------------------*/
/**
  @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,
            "kmos_std_star",
            "Create the telluric correction frame.",
            kmos_std_star_description,
            "Alex Agudo Berbel, Y. Jung",
            "usd-help@eso.org",
            kmos_get_license(),
            kmos_std_star_create,
            kmos_std_star_exec,
            kmos_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 kmos_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.kmos_std_star.startype", CPL_TYPE_STRING,
            "The spectral type of the star (O, B, A, F, G) e.g. G4V",
            "kmos.kmos_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.kmos_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.kmos_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.kmos_std_star.fmethod", CPL_TYPE_STRING,
            "Fitting method (gauss, moffat, profile", "kmos.kmos_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.kmos_std_star.neighborhoodRange",
            CPL_TYPE_DOUBLE,
            "Defines the range to search for neighbors in pixels",
            "kmos.kmos_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.kmos_std_star.magnitude", CPL_TYPE_STRING,
            "Star magnitude (2 values in HK, eg. 12.1,13.2)",
            "kmos.kmos_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.kmos_std_star.flux", CPL_TYPE_BOOL,
            "TRUE: Apply flux conservation. FALSE: otherwise",
            "kmos.kmos_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.kmos_std_star.save_cubes", CPL_TYPE_BOOL,
            "Flag to save reconstructed cubes", "kmos.kmos_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);

    /* --no_noise */
    p = cpl_parameter_new_value("kmos.kmos_std_star.no_noise", CPL_TYPE_BOOL,
            "Skip the noise computation on sky exposures", "kmos.kmos_std_star",
            FALSE);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "no_noise");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --xcal_interpolation */
    p = cpl_parameter_new_value("kmos.kmos_std_star.xcal_interpolation",
            CPL_TYPE_BOOL, "Flag to Interpolate xcal between rotator angles",
            "kmos.kmos_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.kmos_std_star.suppress_extension",
            CPL_TYPE_BOOL, "Flag to Suppress filename extension",
            "kmos.kmos_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 */
    kmos_band_pars_create(recipe->parameters, "kmos.kmos_std_star");

    /* --mask_method */
    p = cpl_parameter_new_value("kmos.kmos_std_star.mask_method",
            CPL_TYPE_STRING, "Method used : mask, integrated or optimal",
            "kmos.kmos_std_star", "optimal");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "mask_method");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --centre */
    p = cpl_parameter_new_value("kmos.kmos_std_star.centre",
            CPL_TYPE_STRING, "The centre of the circular mask (pixel)",
            "kmos.kmos_std_star", "7.5,7.5");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "centre");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* --radius */
    p = cpl_parameter_new_value("kmos.kmos_std_star.radius",
            CPL_TYPE_DOUBLE, "The radius of the circular mask (pixel)",
            "kmos.kmos_std_star", 3.0);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "radius");
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
    cpl_parameterlist_append(recipe->parameters, p);

    /* Add parameters for combining */
    return kmos_combine_pars_create(recipe->parameters, "kmos.kmos_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 kmos_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 kmos_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 kmos_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 kmos_std_star(cpl_parameterlist *parlist, cpl_frameset *frameset)
{
    const cpl_parameter *   par ;
    /*********************/
    /* Parsed Parameters */
    const char          *   imethod ;        
    const char          *   cmethod ;        
    const char          *   fmethod ;        
    const char          *   mask_method ;        
    const char          *   centre_txt ;
    cpl_vector          *   centre ;
    const char          *   spec_type ;        
    const char          *   magnitude_txt ;        
    double                  cpos_rej, cneg_rej, neighborhoodRange, cen_x, cen_y,
                            radius ;
    int                     flux, save_cubes, no_noise, citer, cmin, cmax,
                            xcal_interpolation, suppress_extension ;
    /*********************/
    char                *   suffix ;
    char                *   keyword ;
    char                *   extname ;
    char                *   fn_suffix ;
    const char          *   filter_id ;
    cpl_array           **  unused_ifus_before ;
    cpl_array           **  unused_ifus_after ;
    const int           *   punused_ifus ;
    gridDefinition          gd;
    cpl_propertylist    *   tmp_header ;
    cpl_propertylist    *   main_header_tel ;
    cpl_propertylist    *   sub_header_orig ;
    cpl_propertylist    *   main_header_psf ;
    int                 *   bounds ;
    objSkyStruct        *   obj_sky_struct ;
    skySkyStruct        *   sky_sky_struct ;

    cpl_imagelist       **  stored_data_cube ;
    cpl_imagelist       **  stored_noise_cube ;
    cpl_image           **  stored_psf_data ;
    cpl_image           **  stored_mask ;
    cpl_vector          **  stored_telluric_data ;
    cpl_vector          **  stored_telluric_noise ;
    cpl_vector          **  stored_starspec_data ;
    cpl_vector          **  stored_starspec_noise ;
    cpl_vector          **  stored_noisespec ;
    double              *   stored_qc_throughput ;
    cpl_propertylist    **  stored_sub_tel_data_headers ;
    cpl_propertylist    **  stored_sub_tel_noise_headers ;
    cpl_propertylist    **  stored_sub_cube_data_headers ;
    cpl_propertylist    **  stored_sub_cube_noise_headers ;
    cpl_propertylist    **  stored_sub_psf_headers ;
    char                    filename_telluric[256],
                            filename_starspec[256],
                            filename_psf[256],
                            filename_mask[256],
                            filename_cubes[256],
                            filename_noise[256] ;
    cpl_frame           *   obj_frame ;
    cpl_frame           *   sky_frame ;
    cpl_frame           *   xcal_frame ;
    cpl_frame           *   ycal_frame ;
    cpl_frame           *   lcal_frame ;
    cpl_frame           *   flat_frame ;
    cpl_frame           *   illum_frame ;
    cpl_frame           *   solar_frame ;
    cpl_frame           *   atmos_frame ;
    cpl_table           *   band_table ;
    cpl_frameset        *   frameset_std ;
    cpl_vector          *   spec_qc ;
    int                     is_stdstarscipatt, compute_qcs,
                            ifu_nr, nifus, nr_std_stars ;
    double                  magnitude1, magnitude2, star_temp, exptime, 
                            throughput_mean, throughput_sdv, cdelt1, crpix1, 
                            crval1, zeropoint, tmp_data, tmp_noise, counts1, 
                            counts2, gain ;
    char                    star_type ;
    kmclipm_vector      *   ddd  ;
    int                     i, j ;
    
    /* Initialise */
    nr_std_stars = 0 ;
    zeropoint = throughput_mean = throughput_sdv = -1.0 ;
    magnitude1 = magnitude2 = -1.0 ;

    /* Check entries */
    if (parlist == NULL || frameset == NULL) {
        cpl_msg_error(__func__, "Null Inputs") ;
        cpl_error_set(__func__, CPL_ERROR_NULL_INPUT) ;
        return -1 ;
    }

    /* Get parameters */
    par = cpl_parameterlist_find_const(parlist, "kmos.kmos_std_star.imethod");
    imethod = cpl_parameter_get_string(par) ;
    par = cpl_parameterlist_find_const(parlist, "kmos.kmos_std_star.startype");
    spec_type = cpl_parameter_get_string(par) ;
    par = cpl_parameterlist_find_const(parlist, "kmos.kmos_std_star.fmethod");
    fmethod = cpl_parameter_get_string(par) ;
    par = cpl_parameterlist_find_const(parlist,
            "kmos.kmos_std_star.neighborhoodRange");
    neighborhoodRange = cpl_parameter_get_double(par) ;
    par = cpl_parameterlist_find_const(parlist, "kmos.kmos_std_star.magnitude");
    magnitude_txt = cpl_parameter_get_string(par) ;
    par = cpl_parameterlist_find_const(parlist, "kmos.kmos_std_star.flux");
    flux = cpl_parameter_get_bool(par);
    par = cpl_parameterlist_find_const(parlist,"kmos.kmos_std_star.save_cubes");
    save_cubes = cpl_parameter_get_bool(par);
    par = cpl_parameterlist_find_const(parlist, "kmos.kmos_std_star.no_noise");
    no_noise = cpl_parameter_get_bool(par);
    par = cpl_parameterlist_find_const(parlist,
        "kmos.kmos_std_star.xcal_interpolation");
    xcal_interpolation = cpl_parameter_get_bool(par);
    par = cpl_parameterlist_find_const(parlist,
        "kmos.kmos_std_star.suppress_extension");
    suppress_extension = cpl_parameter_get_bool(par);
    kmos_band_pars_load(parlist, "kmos.kmos_std_star");
    par = cpl_parameterlist_find_const(parlist,
            "kmos.kmos_std_star.mask_method");
    mask_method = cpl_parameter_get_string(par) ;
    if (!strcmp(mask_method, "integrated")) {
        par = cpl_parameterlist_find_const(parlist,"kmos.kmos_std_star.centre");
        centre_txt = cpl_parameter_get_string(par) ;
        centre = kmo_identify_ranges(centre_txt);
        if (cpl_vector_get_size(centre) != 2) {
            cpl_msg_error(__func__, "centre must have 2 values like a,b") ;
            cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
            return -1 ;
        }
        cen_x = cpl_vector_get(centre, 0);
        cen_y = cpl_vector_get(centre, 1);
        cpl_vector_delete(centre);
        par = cpl_parameterlist_find_const(parlist,"kmos.kmos_std_star.radius");
        radius = cpl_parameter_get_double(par) ;
        if (radius < 0.0) {
            cpl_msg_error(__func__, "radius must be greater than 0.0") ;
            cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
            return -1 ;
        }
    } else if (strcmp(mask_method, "optimal")) {
        cpl_msg_error(__func__, "Unsupported mask method: %s", mask_method) ;
        cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
        return -1 ;
    }
    kmos_combine_pars_load(parlist, "kmos.kmos_std_star", &cmethod,
            &cpos_rej, &cneg_rej, &citer, &cmin, &cmax, FALSE);

    /* Check Parameters */
    if (strcmp(cmethod, "average") && strcmp(cmethod, "median") &&
            strcmp(cmethod, "sum") && strcmp(cmethod, "min_max") &&
            strcmp(cmethod, "ksigma")) {
        cpl_msg_error(__func__, 
                "cmethod must be average median sum min_max or ksigma") ;
        cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
        return -1 ;
    }
    if (strcmp(imethod, "NN") && strcmp(imethod, "lwNN") &&
            strcmp(imethod, "swNN") && strcmp(imethod, "MS") &&
            strcmp(imethod, "CS")) {
        cpl_msg_error(__func__, "imethod must be NN,lwNN,swNN,MS or CS") ;
        cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
        return -1 ;
    }
    if (strcmp(fmethod, "gauss") && strcmp(fmethod, "moffat")) {
        cpl_msg_error(__func__, "fmethod must be gauss or moffat") ;
        cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
        return -1 ;
    }
    if (neighborhoodRange <= 0.0) {
        cpl_msg_error(__func__, "neighborhoodRange must be greater than 0.0") ;
        cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
        return -1 ;
    }

    /* Identify the RAW and CALIB frames in the input frameset */
    if (kmo_dfs_set_groups(frameset) != 1) {
        cpl_msg_error(__func__, "Cannot identify RAW and CALIB frames") ;
        cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
        return -1 ;
    }

    /* Check the inputs consistency */
    if (kmos_std_star_check_inputs(frameset, magnitude_txt, &is_stdstarscipatt,
                &compute_qcs, &magnitude1, &magnitude2) != 1) {
        cpl_msg_error(__func__, "Input frameset is not consistent") ;
        cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
        return -1 ;
    }
    
    /* Instrument setup */
    suffix = kmo_dfs_get_suffix(kmo_dfs_get_frame(frameset, STD), TRUE, FALSE);
    cpl_msg_info("", "Detected instrument setup:   %s", suffix+1);

    /* Check which IFUs are active for all frames */
    unused_ifus_before = kmo_get_unused_ifus(frameset, 0, 0);
    unused_ifus_after = kmo_duplicate_unused_ifus(unused_ifus_before);
    kmo_print_unused_ifus(unused_ifus_before, FALSE);
    kmo_free_unused_ifus(unused_ifus_before);

    /* Setup grid definition, wavelength start and end are set later */
    kmclipm_setup_grid(&gd, imethod, neighborhoodRange, KMOS_PIX_RESOLUTION,0.);

    /* Get left and right bounds of IFUs from XCAL */
    tmp_header = kmo_dfs_load_primary_header(frameset, XCAL);
    bounds = kmclipm_extract_bounds(tmp_header);
    cpl_propertylist_delete(tmp_header);

    /* Extract STD frames */
    frameset_std = kmos_std_star_extract_same_grat_stds(frameset, &i) ;

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

    /* Check if there is at least 1 object  */
    if (obj_sky_struct->size == 0) {
        cpl_free(suffix); 
        cpl_free(bounds); 
        kmo_free_unused_ifus(unused_ifus_after);
        cpl_frameset_delete(frameset_std) ;
        cpl_msg_error(cpl_func, "No Object found");
        cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
        return -1 ;
    }

    /* Identify sky-sky-pairs for NOISE_SPEC calculation */
    sky_sky_struct = kmo_create_skySkyStruct(frameset_std);

    /* Get frame */
    illum_frame = kmo_dfs_get_frame(frameset, ILLUM_CORR);
    flat_frame = kmo_dfs_get_frame(frameset, MASTER_FLAT);
    xcal_frame = kmo_dfs_get_frame(frameset, XCAL) ;
    ycal_frame = kmo_dfs_get_frame(frameset, YCAL) ;
    lcal_frame = kmo_dfs_get_frame(frameset, LCAL) ;
    atmos_frame = kmo_dfs_get_frame(frameset, ATMOS_MODEL);
    solar_frame = kmo_dfs_get_frame(frameset, SOLAR_SPEC);

    /* Allocate intermediate memory */
    nifus = KMOS_NR_DETECTORS * KMOS_IFUS_PER_DETECTOR ;
    stored_telluric_data = (cpl_vector**)cpl_calloc(nifus, sizeof(cpl_vector*));
    stored_telluric_noise = (cpl_vector**)cpl_calloc(nifus,sizeof(cpl_vector*));
    stored_starspec_data = (cpl_vector**)cpl_calloc(nifus, sizeof(cpl_vector*));
    stored_starspec_noise = (cpl_vector**)cpl_calloc(nifus,sizeof(cpl_vector*));
    stored_psf_data = (cpl_image**)cpl_calloc(nifus, sizeof(cpl_image*));
    stored_mask = (cpl_image**)cpl_calloc(nifus, sizeof(cpl_image*));
    stored_data_cube =(cpl_imagelist**)cpl_calloc(nifus,sizeof(cpl_imagelist*));
    stored_noise_cube=(cpl_imagelist**)cpl_calloc(nifus,sizeof(cpl_imagelist*));
    stored_qc_throughput = (double*)cpl_calloc(nifus, sizeof(double));
    stored_sub_psf_headers = (cpl_propertylist**)cpl_calloc(nifus, 
            sizeof(cpl_propertylist*));
    stored_sub_tel_data_headers = (cpl_propertylist**)cpl_calloc(nifus, 
            sizeof(cpl_propertylist*));
    stored_sub_tel_noise_headers = (cpl_propertylist**)cpl_calloc(nifus, 
            sizeof(cpl_propertylist*));
    stored_sub_cube_data_headers = (cpl_propertylist**)cpl_calloc(nifus, 
            sizeof(cpl_propertylist*));
    stored_sub_cube_noise_headers=(cpl_propertylist**)cpl_calloc(nifus, 
            sizeof(cpl_propertylist*));
    stored_noisespec = (cpl_vector**)cpl_calloc(nifus, sizeof(cpl_vector*));

    strcpy(filename_telluric, TELLURIC);
    strcpy(filename_starspec, STAR_SPEC);
    strcpy(filename_psf, STD_IMAGE);
    strcpy(filename_mask, STD_MASK);
    strcpy(filename_cubes, STD_CUBE);
    strcpy(filename_noise, NOISE_SPEC);

    /*
    cpl_free(stored_telluric_data);
    cpl_free(stored_telluric_noise);
    cpl_free(stored_starspec_data);
    cpl_free(stored_starspec_noise); 
    cpl_free(stored_psf_data);
    cpl_free(stored_sub_tel_data_headers);
    cpl_free(stored_sub_tel_noise_headers);
    cpl_free(stored_noisespec);
    cpl_free(stored_sub_cube_data_headers);
    cpl_free(stored_sub_cube_noise_headers);
    cpl_free(stored_sub_psf_headers);
    cpl_free(stored_mask);
    cpl_free(stored_data_cube);
    cpl_free(stored_noise_cube);
    cpl_free(stored_qc_throughput) ;
    kmo_delete_objSkyStruct(obj_sky_struct);
    kmo_delete_skySkyStruct(sky_sky_struct);
    cpl_free(suffix); 
    cpl_free(bounds); 
    kmo_free_unused_ifus(unused_ifus_after);
    cpl_frameset_delete(frameset_std) ;
    return 0 ;
    */

    /* Get the first frame containing object */
    obj_frame = obj_sky_struct->table[0].objFrame;
    main_header_tel = kmclipm_propertylist_load(
            cpl_frame_get_filename(obj_frame), 0);
    exptime = cpl_propertylist_get_double(main_header_tel, EXPTIME);

    /* Get the star temperature */
    star_temp = kmos_get_temperature(frameset, spec_type, &star_type) ; 

    /* Loop on detectors */
    for (i = 1; i <= KMOS_NR_DETECTORS ; i++) {
        print_cal_angle_msg_once = FALSE;
        print_xcal_angle_msg_once = FALSE;
        if (i==1) {
            print_cal_angle_msg_once = TRUE;
            print_xcal_angle_msg_once = TRUE;
        }

        /* Get filter for this detector ESO INS FILTi ID */
        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);

        band_table = kmo_dfs_load_table(frameset, WAVE_BAND, 1, 0);
        kmclipm_setup_grid_band_lcal(&gd, filter_id, band_table);
        cpl_table_delete(band_table); 

        /* Load extension header */
        sub_header_orig = kmclipm_propertylist_load(
                cpl_frame_get_filename(obj_frame), i);

        /* Loop on IFUs */
        for (j = 0; j < KMOS_IFUS_PER_DETECTOR; j++) {
            ifu_nr = (i-1)*KMOS_IFUS_PER_DETECTOR + j + 1;

            /* Check if IFU valid  */

            /* Check if there is a sky frame available for this IFU */
            kmo_collapse_objSkyStruct(obj_sky_struct, ifu_nr, &obj_frame,
                    &sky_frame);
            punused_ifus = cpl_array_get_data_int_const(unused_ifus_after[i-1]);
            /* Search for keyword ESO OCS ARMi NOTUSED */
            /* If CPL_ERROR_DATA_NOT_FOUND, process standard star */
            keyword = cpl_sprintf("%s%d%s", IFU_VALID_PREFIX, ifu_nr, 
                    IFU_VALID_POSTFIX);
            cpl_propertylist_get_string(main_header_tel, keyword);
            cpl_free(keyword);

            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 */

                /* Process IFU */
                if (kmos_std_star_compute_ifu(sub_header_orig, obj_frame, 
                            sky_frame, flat_frame, xcal_frame, ycal_frame, 
                            lcal_frame, illum_frame, atmos_frame, solar_frame, 
                            ifu_nr, main_header_tel, gd, bounds[2*(ifu_nr-1)], 
                            bounds[2*(ifu_nr-1)+1], fmethod, flux, 
                            xcal_interpolation, mask_method, cmethod, cpos_rej,
                            cneg_rej, citer, cmax, cmin, cen_x, cen_y, radius, 
                            filter_id, star_type, no_noise, is_stdstarscipatt, 
                            sky_sky_struct[ifu_nr-1], star_temp, 
                            &spec_qc,
                            &stored_sub_tel_data_headers[ifu_nr-1],
                            &stored_sub_psf_headers[ifu_nr-1],
                            &stored_sub_cube_data_headers[ifu_nr-1],
                            &stored_data_cube[ifu_nr-1], 
                            &stored_noise_cube[ifu_nr-1],
                            &stored_psf_data[ifu_nr-1],
                            &stored_mask[ifu_nr-1],
                            &stored_starspec_data[ifu_nr-1],
                            &stored_starspec_noise[ifu_nr-1],
                            &stored_noisespec[ifu_nr-1],
                            &stored_telluric_data[ifu_nr-1],
                            &stored_telluric_noise[ifu_nr-1]) == -1) {
                    stored_telluric_data[ifu_nr-1] = NULL ;
                    stored_telluric_noise[ifu_nr-1] = NULL ;
                    stored_starspec_data[ifu_nr-1] = NULL ;
                    stored_starspec_noise[ifu_nr-1] = NULL ;
                    stored_psf_data[ifu_nr-1] = NULL ;
                    stored_mask[ifu_nr-1] = NULL ;
                    stored_data_cube[ifu_nr-1] = NULL ;
                    stored_noise_cube[ifu_nr-1] = NULL ;
                    stored_noisespec[ifu_nr-1] = NULL ;

                    stored_sub_tel_data_headers[ifu_nr-1] =
                        cpl_propertylist_duplicate(sub_header_orig);
                    stored_sub_tel_noise_headers[ifu_nr-1] =
                        cpl_propertylist_duplicate(sub_header_orig);
                    stored_sub_psf_headers[ifu_nr-1] =
                        cpl_propertylist_duplicate(sub_header_orig);
                    stored_sub_cube_data_headers[ifu_nr-1] =
                        cpl_propertylist_duplicate(sub_header_orig);
                    stored_sub_cube_noise_headers[ifu_nr-1] =
                        cpl_propertylist_duplicate(sub_header_orig);
                } else {
                    /* If magnitude is provided, get zeropoint and throughput */
                    if (compute_qcs) {
                        /* QC THROUGHPUT */
                        gain = cpl_propertylist_get_double(
                                stored_sub_tel_data_headers[ifu_nr-1], GAIN);
                        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_calc_counts(spec_qc, filter_id, crpix1, crval1, 
                                cdelt1, &counts1, &counts2);
                        counts1 /= exptime;
                        counts2 /= exptime;
                        stored_qc_throughput[ifu_nr-1] = 
                            kmo_calc_throughput(magnitude1, magnitude2,
                                    counts1, counts2, gain, filter_id);
                        if (kmclipm_is_nan_or_inf(
                                    stored_qc_throughput[ifu_nr-1])) 
                            stored_qc_throughput[ifu_nr-1] = -1;
                        kmclipm_update_property_double(
                                stored_sub_tel_data_headers[ifu_nr-1], 
                                QC_THROUGHPUT, 
                                stored_qc_throughput[ifu_nr-1], 
                                "[] IFU throughput");

                        /* QC ZEROPOINT */
                        zeropoint = kmo_calc_zeropoint(magnitude1, magnitude2, 
                                counts1, counts2, cdelt1, filter_id);
                        if (kmclipm_is_nan_or_inf(zeropoint)) zeropoint = -1;
                        kmclipm_update_property_double(
                                stored_sub_tel_data_headers[ifu_nr-1], 
                                QC_ZEROPOINT, zeropoint, "[mag] IFU zeropoint");
                    }
                    /* Update number of standard stars */
                    nr_std_stars++;
                    cpl_vector_delete(spec_qc) ;
                }
            } else {
                cpl_error_reset();
                /* IFU is invalid */
                stored_telluric_data[ifu_nr-1] = NULL ;
                stored_telluric_noise[ifu_nr-1] = NULL ;
                stored_starspec_data[ifu_nr-1] = NULL ;
                stored_starspec_noise[ifu_nr-1] = NULL ;
                stored_psf_data[ifu_nr-1] = NULL ;
                stored_mask[ifu_nr-1] = NULL ;
                stored_data_cube[ifu_nr-1] = NULL ;
                stored_noise_cube[ifu_nr-1] = NULL ;
                stored_noisespec[ifu_nr-1] = NULL ;

                stored_sub_tel_data_headers[ifu_nr-1] =
                    cpl_propertylist_duplicate(sub_header_orig);
                stored_sub_tel_noise_headers[ifu_nr-1] =
                    cpl_propertylist_duplicate(sub_header_orig);
                stored_sub_psf_headers[ifu_nr-1] =
                    cpl_propertylist_duplicate(sub_header_orig);
                stored_sub_cube_data_headers[ifu_nr-1] =
                    cpl_propertylist_duplicate(sub_header_orig);
                stored_sub_cube_noise_headers[ifu_nr-1] =
                    cpl_propertylist_duplicate(sub_header_orig);
            }

            /* EXTNAME for DATA */
            extname = kmo_extname_creator(ifu_frame, ifu_nr, EXT_DATA);
            kmclipm_update_property_string(
                    stored_sub_tel_data_headers[ifu_nr-1], EXTNAME, extname,
                    "FITS extension name");
            kmclipm_update_property_string(
                    stored_sub_psf_headers[ifu_nr-1], EXTNAME, extname, 
                    "FITS extension name");
            kmclipm_update_property_string(
                    stored_sub_cube_data_headers[ifu_nr-1], EXTNAME, 
                    extname, "FITS extension name");
            cpl_free(extname);

            /* EXTNAME for NOISE */
            if (stored_sub_tel_noise_headers[ifu_nr-1] == NULL) {
                stored_sub_tel_noise_headers[ifu_nr-1] =
                    cpl_propertylist_duplicate(
                            stored_sub_tel_data_headers[ifu_nr-1]);
            }
            extname = kmo_extname_creator(ifu_frame, ifu_nr, EXT_NOISE);
            kmclipm_update_property_string(
                    stored_sub_tel_noise_headers[ifu_nr-1], EXTNAME, 
                    extname, "FITS extension name");
            if (stored_sub_cube_noise_headers[ifu_nr-1] == NULL)
                stored_sub_cube_noise_headers[ifu_nr-1] =
                    cpl_propertylist_duplicate(
                            stored_sub_cube_data_headers[ifu_nr-1]);
            kmclipm_update_property_string(
                    stored_sub_cube_noise_headers[ifu_nr-1], EXTNAME,
                        extname, "FITS extension name");
            cpl_free(extname);
        } 
        cpl_propertylist_delete(sub_header_orig);
    } 
    cpl_free(bounds);

    /* Write QC parameter: nr of std stars */
    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_set_unused_ifus(unused_ifus_after, main_header_tel,"kmos_std_star");
    kmo_free_unused_ifus(unused_ifus_after);

    main_header_psf = cpl_propertylist_duplicate(main_header_tel);

    if (compute_qcs) {
        /* QC THROUGHPUT MEAN and QC THROUGHPUT SDV */
        kmo_calc_mean_throughput(stored_qc_throughput, nifus, &throughput_mean,
                &throughput_sdv);
        kmclipm_update_property_double(main_header_tel, QC_THROUGHPUT_MEAN,
                throughput_mean, "[] mean throughput for all detectors");
        kmclipm_update_property_double(main_header_tel, QC_THROUGHPUT_SDV,
                throughput_sdv, "[] stdev throughput for all detectors");
    }
    cpl_free(stored_qc_throughput);
    
    /* Save output data */
    if (!suppress_extension)    fn_suffix = cpl_sprintf("%s", suffix);
    else                        fn_suffix = cpl_sprintf("%s", "");
    cpl_free(suffix); 

    /* Save primary extension */
    kmo_dfs_save_main_header(frameset, filename_telluric, fn_suffix,
            obj_frame, main_header_tel, parlist, cpl_func);
    kmo_dfs_save_main_header(frameset, filename_starspec, fn_suffix,
            obj_frame, main_header_tel, parlist, cpl_func);
    kmo_dfs_save_main_header(frameset, filename_mask, fn_suffix,
            obj_frame, main_header_psf, parlist, cpl_func);
    kmo_dfs_save_main_header(frameset, filename_psf, fn_suffix,
            obj_frame, main_header_psf, parlist, cpl_func) ;
    if (!no_noise && is_stdstarscipatt) {
        kmo_dfs_save_main_header(frameset, filename_noise, fn_suffix,
                obj_frame, main_header_tel, parlist, cpl_func);
    }
    cpl_propertylist_delete(main_header_tel);
    
    if (save_cubes) {
        kmo_dfs_save_main_header(frameset, filename_cubes, fn_suffix,
                obj_frame, main_header_psf, parlist, cpl_func);
    }
    cpl_propertylist_delete(main_header_psf);

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

            /* Save telluric-vector */
            if (stored_telluric_data[ifu_nr-1] != NULL) {
                ddd = kmclipm_vector_create(cpl_vector_duplicate(
                            stored_telluric_data[ifu_nr-1]));
                kmo_dfs_save_vector(ddd, filename_telluric, fn_suffix,
                        stored_sub_tel_data_headers[ifu_nr-1], 0./0.);
                kmclipm_vector_delete(ddd) ;
            } else {
                kmo_dfs_save_vector(NULL, filename_telluric, fn_suffix,
                        stored_sub_tel_data_headers[ifu_nr-1], 0./0.);
            }
            if (stored_telluric_noise[ifu_nr-1] != NULL) {
                ddd = kmclipm_vector_create(cpl_vector_duplicate(
                            stored_telluric_noise[ifu_nr-1]));
                kmo_dfs_save_vector(ddd, filename_telluric, fn_suffix,
                        stored_sub_tel_noise_headers[ifu_nr-1], 0./0.);
                kmclipm_vector_delete(ddd) ;
            } else {
                kmo_dfs_save_vector(NULL, filename_telluric, fn_suffix,
                        stored_sub_tel_noise_headers[ifu_nr-1], 0./0.);
            }

            /* 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_dfs_save_vector(ddd, filename_starspec, fn_suffix,
                        stored_sub_tel_data_headers[ifu_nr-1], 0./0.);
                kmclipm_vector_delete(ddd); 
            } else {
                kmo_dfs_save_vector(NULL, filename_starspec, fn_suffix,
                        stored_sub_tel_data_headers[ifu_nr-1], 0./0.);
            }
            if (stored_starspec_noise[ifu_nr-1] != NULL) {
                ddd = kmclipm_vector_create(cpl_vector_duplicate(
                            stored_starspec_noise[ifu_nr-1]));
                kmo_dfs_save_vector(ddd, filename_starspec, fn_suffix,
                        stored_sub_tel_noise_headers[ifu_nr-1], 0./0.);
                kmclipm_vector_delete(ddd); 
            } else {
                kmo_dfs_save_vector(NULL, filename_starspec, fn_suffix,
                        stored_sub_tel_noise_headers[ifu_nr-1], 0./0.);
            }

            /* Save psf-image */
            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_dfs_save_image(stored_mask[ifu_nr-1], filename_mask, 
                    fn_suffix, stored_sub_psf_headers[ifu_nr-1], 0./0.);

            /* Save noise_spec-vector */
            if (!no_noise && is_stdstarscipatt && stored_noisespec != NULL && 
                    stored_noisespec[ifu_nr-1] != NULL && 
                    stored_starspec_data[ifu_nr-1] != NULL) {
                /* QC SNR */
                kmo_calc_band_mean(stored_sub_tel_data_headers[ifu_nr-1],
                        filter_id, stored_starspec_data[ifu_nr-1], NULL,
                        &tmp_data, NULL);
                kmo_calc_band_mean(stored_sub_tel_data_headers[ifu_nr-1],
                        filter_id, stored_noisespec[ifu_nr-1], NULL,
                        &tmp_noise, NULL);
                kmclipm_update_property_double(
                        stored_sub_tel_data_headers[ifu_nr-1], QC_SNR,
                        tmp_data/tmp_noise, "[] SNR");
            }

            if (!no_noise && is_stdstarscipatt) {
                if ((stored_noisespec != NULL) && 
                        stored_noisespec[ifu_nr-1] != NULL) {
                    ddd = kmclipm_vector_create(cpl_vector_duplicate(
                                stored_noisespec[ifu_nr-1]));
                    kmo_dfs_save_vector(ddd, filename_noise, fn_suffix,
                            stored_sub_tel_data_headers[ifu_nr-1], 0./0.);
                    kmclipm_vector_delete(ddd); 
                } else {
                    kmo_dfs_save_vector(NULL, filename_noise, fn_suffix,
                            stored_sub_tel_data_headers[ifu_nr-1], 0./0.);
                }
            }

            /* Save reonstructed cubes */
            if (save_cubes) {
                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_dfs_save_cube(stored_noise_cube[ifu_nr-1],
                        filename_cubes, fn_suffix,
                        stored_sub_cube_noise_headers[ifu_nr-1], 0./0.);
            }
        } 
    } 
    cpl_free(fn_suffix);

    /* DE-Allocate Warning ---  Frames (e.g obj_frame) point to  */
    /*                          obj_..._struct which point to frameset_ѕtd */
    kmo_delete_objSkyStruct(obj_sky_struct);
    kmo_delete_skySkyStruct(sky_sky_struct);
    cpl_frameset_delete(frameset_std);
    
    for (i = 0; i < nifus ; i++) {
        cpl_vector_delete(stored_telluric_data[i]);
        cpl_vector_delete(stored_telluric_noise[i]);
        cpl_vector_delete(stored_starspec_data[i]); 
        cpl_vector_delete(stored_starspec_noise[i]);
        cpl_image_delete(stored_psf_data[i]);
        cpl_propertylist_delete(stored_sub_tel_data_headers[i]); 
        cpl_propertylist_delete(stored_sub_tel_noise_headers[i]);
        cpl_vector_delete(stored_noisespec[i]); 
        cpl_propertylist_delete(stored_sub_cube_data_headers[i]); 
        cpl_propertylist_delete(stored_sub_cube_noise_headers[i]);
        cpl_propertylist_delete(stored_sub_psf_headers[i]); 
        cpl_image_delete(stored_mask[i]);
        cpl_imagelist_delete(stored_data_cube[i]);
        cpl_imagelist_delete(stored_noise_cube[i]);
    }
    cpl_free(stored_telluric_data);
    cpl_free(stored_telluric_noise);
    cpl_free(stored_starspec_data);
    cpl_free(stored_starspec_noise); 
    cpl_free(stored_psf_data);
    cpl_free(stored_sub_tel_data_headers);
    cpl_free(stored_sub_tel_noise_headers);
    cpl_free(stored_noisespec);
    cpl_free(stored_sub_cube_data_headers);
    cpl_free(stored_sub_cube_noise_headers);
    cpl_free(stored_sub_psf_headers);
    cpl_free(stored_mask);
    cpl_free(stored_data_cube);
    cpl_free(stored_noise_cube);

    return 0;
}

/**@}*/

/*----------------------------------------------------------------------------*/
/**
  @brief    Process IFU
  @return   0 if ok, -1 in error case
 */
/*----------------------------------------------------------------------------*/
static int kmos_std_star_compute_ifu(
        cpl_propertylist    *   sub_header_orig,
        cpl_frame           *   obj_frame,
        cpl_frame           *   sky_frame,
        cpl_frame           *   flat_frame,
        cpl_frame           *   xcal_frame,
        cpl_frame           *   ycal_frame,
        cpl_frame           *   lcal_frame,
        cpl_frame           *   illum_frame,
        cpl_frame           *   atmos_frame,
        cpl_frame           *   solar_frame,
        int                     ifu_nr,
        cpl_propertylist    *   main_header_tel,
        gridDefinition          gd,
        int                     low_bound,
        int                     high_bound,
        const char          *   fmethod,
        int                     flux,
        int                     xcal_interpolation,
        const char          *   mask_method,
        const char          *   cmethod,
        double                  cpos_rej,
        double                  cneg_rej,
        int                     citer,
        int                     cmax,
        int                     cmin,
        double                  cen_x,
        double                  cen_y,
        double                  radius,
        const char          *   filter_id,
        char                    star_type,
        int                     no_noise,
        int                     is_stdstarscipatt,
        skySkyStruct            sky_sky_struct,
        double                  star_temp,
        cpl_vector          **  spec_qc,
        cpl_propertylist    **  out_sub_tel_data_header,
        cpl_propertylist    **  out_sub_psf_header,
        cpl_propertylist    **  out_sub_cube_data_header,
        cpl_imagelist       **  out_data_cube,
        cpl_imagelist       **  out_noise_cube,
        cpl_image           **  out_psf_data,
        cpl_image           **  out_mask,
        cpl_vector          **  out_starspec_data,
        cpl_vector          **  out_starspec_noise,
        cpl_vector          **  out_noisespec,
        cpl_vector          **  out_telluric_data,
        cpl_vector          **  out_telluric_noise)
{
    char                *   keyword ;
    cpl_propertylist    *   tmp_head ;
    cpl_image           *   illum_corr ;
    cpl_propertylist    *   pl_psf ;
    double                  std_trace, factor_fwhm, spat_res, x_lo, y_lo, x_hi,
                            y_hi, loc_cen_x, loc_cen_y, r ;
    cpl_size                auto_cen_x, auto_cen_y, nx, ny ;
    cpl_vector          *   tmp_vec ;
    cpl_vector          *   tmp_spec_data ;
    cpl_vector          *   tmp_spec_noise ;
    cpl_vector          *   shot_noise ;
    cpl_vector          *   solar_spec ;
    double              *   ppp ;
    double                  gain, mean_data, mean_data2, flux_scale_factor ;
    double              **  pvec_array ;
    double              *   ptmp_vec ;
    double              *   pstored_noisespec ;
    cpl_vector          **  vec_array ;
    const double        *   ptmp_spec_data ;
    double              *   ptmp_spec_noise ;
    float               *   pmask ;
    cpl_imagelist       *   tmp_cube ;
    cpl_vector          *   lambda_x ;
    cpl_vector          *   atmos_model ;
    int                     i, j, k, nr_sky_pairs, npix, x, y ;
 
    /* Check inputs */
    
    /* Initialise */
    std_trace = -1.0 ;

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

    keyword = cpl_sprintf("%s%d", PRO_STD, ifu_nr);
    cpl_propertylist_update_int(main_header_tel, keyword, 1);
    cpl_free(keyword);

    /* Compute WCS and make copies of sub_header */
    tmp_head=cpl_propertylist_duplicate(sub_header_orig);
    kmo_calc_wcs_gd(main_header_tel, tmp_head, ifu_nr,gd);
    *out_sub_tel_data_header = cpl_propertylist_duplicate(tmp_head);
    *out_sub_psf_header = cpl_propertylist_duplicate(tmp_head);
    *out_sub_cube_data_header = cpl_propertylist_duplicate(tmp_head);
    cpl_propertylist_delete(tmp_head);
    
    /* Adjust telluric-headers: copy key3 to key1 - rm key2 key3*/
    kmos_std_star_adjust_double(*out_sub_tel_data_header, CRVAL1,CRVAL2,CRVAL3);
    kmos_std_star_adjust_double(*out_sub_tel_data_header, CRPIX1,CRPIX2,CRPIX3);
    kmos_std_star_adjust_double(*out_sub_tel_data_header, CDELT1,CDELT2,CDELT3);
    kmos_std_star_adjust_string(*out_sub_tel_data_header, CTYPE1,CTYPE2,CTYPE3);
    kmos_std_star_adjust_string(*out_sub_tel_data_header, CUNIT1,CUNIT2,CUNIT3);

    /* CDx_x */
    cpl_propertylist_erase(*out_sub_tel_data_header, CD1_1);
    cpl_propertylist_erase(*out_sub_tel_data_header, CD1_2);
    cpl_propertylist_erase(*out_sub_tel_data_header, CD1_3);
    cpl_propertylist_erase(*out_sub_tel_data_header, CD2_1);
    cpl_propertylist_erase(*out_sub_tel_data_header, CD2_2);
    cpl_propertylist_erase(*out_sub_tel_data_header, CD2_3);
    cpl_propertylist_erase(*out_sub_tel_data_header, CD3_1);
    cpl_propertylist_erase(*out_sub_tel_data_header, CD3_2);
    cpl_propertylist_erase(*out_sub_tel_data_header, CD3_3);

    /* Adjust psf-headers: delete CRPIX3 etc. */
    cpl_propertylist_erase(*out_sub_psf_header, CRPIX3);
    cpl_propertylist_erase(*out_sub_psf_header, CRPIX3);
    cpl_propertylist_erase(*out_sub_psf_header, CDELT3);
    cpl_propertylist_erase(*out_sub_psf_header, CRVAL3);
    cpl_propertylist_erase(*out_sub_psf_header, CTYPE3);
    cpl_propertylist_erase(*out_sub_psf_header, CUNIT3);
    cpl_propertylist_erase(*out_sub_psf_header, CD1_3);
    cpl_propertylist_erase(*out_sub_psf_header, CD2_3);
    cpl_propertylist_erase(*out_sub_psf_header, CD3_1);
    cpl_propertylist_erase(*out_sub_psf_header, CD3_2);
    cpl_propertylist_erase(*out_sub_psf_header, CD3_3);
        
    /* Reconstruct */
    kmo_reconstruct_sci(ifu_nr, low_bound, high_bound, obj_frame, STD,
            sky_frame, STD, flat_frame, xcal_frame, ycal_frame, lcal_frame,
            NULL, NULL, &gd, out_data_cube, out_noise_cube, flux, FALSE,
            xcal_interpolation, FALSE);

    /* Illumination correction */
    /* Illumination noise small versus noise - skipped */
    if (illum_frame != NULL) {
        illum_corr = kmo_dfs_load_image_frame(illum_frame, 
                ifu_nr, FALSE, FALSE, NULL);
        cpl_imagelist_divide_image(*out_data_cube, illum_corr);
        cpl_image_delete(illum_corr);
    }

    /* QC_STD_TRACE (distance of the PSF to the centre) */
    kmo_calculate_std_trace(*out_data_cube, fmethod, &std_trace);
    kmclipm_update_property_double(*out_sub_psf_header, QC_STD_TRACE, std_trace,
            "[pix] distance PSF - IFU center");

    /* Collapse cube and get PSF image */
    kmclipm_make_image(*out_data_cube, NULL, out_psf_data, NULL, NULL,
            cmethod, cpos_rej, cneg_rej, citer, cmax, cmin);

    /* Fit a 2D profile to get a mask and fwhm in x and y */
    tmp_vec = kmo_fit_profile_2D(*out_psf_data, NULL, fmethod,out_mask,&pl_psf);

    if (!strcmp(mask_method, "integrated")) {
        if (cen_x < 1.0 || cen_y < 1.0) {
            cpl_image_get_maxpos(*out_psf_data,&auto_cen_x,&auto_cen_y);
            loc_cen_x = (double)auto_cen_x - 1.0 ;
            loc_cen_y = (double)auto_cen_y - 1.0 ;
        } else {
            loc_cen_x = cen_x - 1.0 ;
            loc_cen_y = cen_y - 1.0 ;
        }
        kmo_image_fill(*out_mask,0.0);
        pmask = cpl_image_get_data_float(*out_mask);
        nx = cpl_image_get_size_x(*out_mask);
        ny = cpl_image_get_size_y(*out_mask);

        /* draw circle */
        x_lo = floor(loc_cen_x - radius);
        if (x_lo < 0) x_lo = 0;
        y_lo = floor(loc_cen_y - radius);
        if (y_lo < 0) y_lo = 0;
        x_hi = ceil(loc_cen_x + radius);
        if (x_hi > nx) x_hi = nx;
        y_hi = ceil(loc_cen_y + radius);
        if (y_hi > ny) y_hi = ny;
        for (x = x_lo; x < x_hi; x++) {
            for (y = y_lo; y < y_hi; y++) {
                r = sqrt(pow(x - loc_cen_x,2) + pow(y - loc_cen_y,2));
                if (r <= radius) pmask[x + y * nx] = 1.0;
            }
        }
    } else {
        /* Normalise mask to 1 and clip values below 0.5 */
        cpl_image_divide_scalar(*out_mask, cpl_image_get_max(*out_mask));
        for (i = 1; i <= cpl_image_get_size_x(*out_mask); i++) {
            for (j = 1; j <= cpl_image_get_size_y(*out_mask); j++) {
                if (cpl_image_get(*out_mask, i, j, &k) < 0.5) 
                    cpl_image_set(*out_mask, i, j, 0.);
                else   
                    cpl_image_set(*out_mask, i, j, 1.);
            }
        }
    }

    /* Update subheader with fit parameters */
    cpl_propertylist_append(*out_sub_tel_data_header, pl_psf);
    cpl_propertylist_delete(pl_psf);

    /* QC_SPAT_RES (RMS of fwhm_x and fwhm_y) */
    factor_fwhm = 2*sqrt(2*log(2));
    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;
    kmclipm_update_property_double(*out_sub_psf_header, QC_SPAT_RES,
            sqrt(spat_res)*KMOS_PIX_RESOLUTION,
            "[arcsec] mean fwhm resolution of PSF");
    cpl_vector_delete(tmp_vec);
    
    /* Extract spectrum with mask  */
    /* Convert Mean to Sum (* mask area) */
    tmp_spec_data = tmp_spec_noise = NULL ;
    kmo_priv_extract_spec(*out_data_cube, *out_noise_cube, *out_mask,
            &tmp_spec_data, &tmp_spec_noise);
    cpl_vector_multiply_scalar(tmp_spec_data, cpl_image_get_flux(*out_mask));
    if (tmp_spec_noise != NULL) {
        cpl_vector_multiply_scalar(tmp_spec_noise,
                cpl_image_get_flux(*out_mask));
    }
   
    /* Extract spectrum of whole area for QCs */
    /* Convert mean to sum (* 196, IFU area) */
    tmp_vec = *spec_qc = NULL ;
    kmo_priv_extract_spec(*out_data_cube, *out_noise_cube, NULL, spec_qc,
            &tmp_vec);
    npix = cpl_image_get_size_x(cpl_imagelist_get(*out_data_cube, 0)) * 
        cpl_image_get_size_y(cpl_imagelist_get(*out_data_cube, 0)) ;
    cpl_vector_multiply_scalar(*spec_qc, npix);
    if (tmp_vec != NULL) cpl_vector_multiply_scalar(tmp_vec, npix);

    /* Shot noise */
    gain = cpl_propertylist_get_double(*out_sub_tel_data_header, GAIN);

    /* Shot_noise = sqrt(tmp_spec_data*gain)/gain */
    /* set negative values and NaN's to zero before sqrt */
    shot_noise = cpl_vector_duplicate(tmp_spec_data);
    cpl_vector_multiply_scalar(shot_noise, gain);
    ppp = cpl_vector_get_data(shot_noise);
    for (i = 0; i < cpl_vector_get_size(shot_noise); i++) {
        if ((ppp[i] < 0.0) || kmclipm_is_nan_or_inf(ppp[i])) 
            ppp[i] = 0.0;
    }
    cpl_vector_sqrt(shot_noise);
    cpl_vector_divide_scalar(shot_noise, gain);

    /* Scale extracted spectrum to match the one  */
    /* calculated over the whole area (band specific) */
    kmo_calc_band_mean(*out_sub_tel_data_header, filter_id, tmp_spec_data, 
            tmp_spec_noise, &mean_data, NULL);
    kmo_calc_band_mean(*out_sub_tel_data_header, filter_id, *spec_qc, tmp_vec, 
            &mean_data2, NULL);
    cpl_vector_delete(tmp_vec) ;

    flux_scale_factor = mean_data2/mean_data;

    cpl_vector_multiply_scalar(shot_noise, flux_scale_factor);
    cpl_vector_multiply_scalar(tmp_spec_data,flux_scale_factor);
    if ((tmp_spec_noise != NULL) && (fabs(mean_data) > 1e-8))
        cpl_vector_multiply_scalar(tmp_spec_noise, flux_scale_factor);

    /* Store to save to disk later on */
    *out_starspec_data = cpl_vector_duplicate(tmp_spec_data);
    if (tmp_spec_noise != NULL) 
        *out_starspec_noise = cpl_vector_duplicate(tmp_spec_noise);
    else
        *out_starspec_noise = NULL ;

    /* Noise spectra */
    if (!no_noise && is_stdstarscipatt) {
        nr_sky_pairs = sky_sky_struct.nrSkyPairs;
        if (nr_sky_pairs > 2) {
            cpl_msg_info(__func__, "Get noise spec on sky for IFU %d", ifu_nr);
            vec_array = cpl_calloc(nr_sky_pairs,sizeof(cpl_vector*));
            pvec_array = cpl_calloc(nr_sky_pairs, sizeof(double*));
            /* Reconstruct all sky-Pairs, extract spectra  */
            for (i = 0; i < nr_sky_pairs; i++) {
                // reconstruct (sky1-sky2)/flatfield
                kmo_reconstruct_sci(ifu_nr, low_bound, high_bound,
                        sky_sky_struct.skyPairs[i].skyFrame1, STD, 
                        sky_sky_struct.skyPairs[i].skyFrame2, STD, flat_frame,
                        xcal_frame, ycal_frame, lcal_frame, NULL, NULL, &gd, 
                        &tmp_cube, NULL, FALSE, FALSE,
                        xcal_interpolation, FALSE);

                /* Extract spectrum using masked   */
                /* convert mean to sum (* mask aperture) */
                kmo_priv_extract_spec(tmp_cube, NULL, *out_mask, 
                        &(vec_array[i]), NULL);
                cpl_vector_multiply_scalar(vec_array[i], 
                        cpl_image_get_flux(*out_mask));

                /* Scale extracted spectrum to match the one  */
                /* calculated over the whole area (band spec) */
                cpl_vector_multiply_scalar(vec_array[i], flux_scale_factor);
                pvec_array[i] = cpl_vector_get_data(vec_array[i]);
                cpl_imagelist_delete(tmp_cube);
            }

            /* stddev on each wavelength of all extrac spec */
            *out_noisespec = cpl_vector_new(gd.l.dim);
            pstored_noisespec = cpl_vector_get_data(*out_noisespec);
            tmp_vec = cpl_vector_new(nr_sky_pairs);
            ptmp_vec = cpl_vector_get_data(tmp_vec);
            for (i = 0; i < gd.l.dim; i++) {
                for (j = 0; j < nr_sky_pairs; j++) 
                    ptmp_vec[j] = pvec_array[j][i];
                pstored_noisespec[i] = cpl_vector_get_stdev(tmp_vec);
            }
            for (i = 0; i < nr_sky_pairs; i++) cpl_vector_delete(vec_array[i]);
            cpl_free(vec_array);
            cpl_free(pvec_array);
            cpl_vector_delete(tmp_vec);
            
            /* total noise = sqrt (shot_noise^2+sky_noise^2) */
            // and set negative values and NaN's to zero
            cpl_vector_power(*out_noisespec, 2.);
            cpl_vector_power(shot_noise, 2.);
            cpl_vector_add(*out_noisespec, shot_noise);
            ppp = cpl_vector_get_data(*out_noisespec);
            for (i = 0; i < cpl_vector_get_size(*out_noisespec); i++) {
                if ((ppp[i] < 0.0) || kmclipm_is_nan_or_inf(ppp[i])) ppp[i]=0.0;
            }
            cpl_vector_sqrt(*out_noisespec);
            cpl_vector_delete(shot_noise); 
        } else {
            cpl_msg_warning(__func__, "Omit noise-spectra (<2 sky pairs)");
            *out_noisespec = shot_noise;
        }
    } else {
        *out_noisespec = shot_noise;
    } 

    /* Spectrum correction */
    /* Abscissa of output spectrum */
    lambda_x = kmo_create_lambda_vec(gd.l.dim, 1, gd.l.start, gd.l.delta);

    if (star_type=='O' || star_type=='B' || star_type=='A' || star_type=='F') {
        /* OBAF star */

        /* Remove lines if ATMOS_MODEL is provided */
        if (atmos_frame != NULL) {
            /* Interpolate ATMOS_MODEL to same scale as data */
            atmos_model = kmo_interpolate_vector_wcs(atmos_frame, lambda_x);

            /* Remove band-specific lines */
            if (!strcmp(filter_id, "H")) 
                for (i = 0; i < nr_lines_h; i++) 
                    kmo_remove_line(tmp_spec_data, lambda_x, atmos_model, 
                            lines_center_h[i], lines_width_h[i]);
            else if (!strcmp(filter_id, "HK"))
                for (i = 0; i < nr_lines_hk; i++) 
                    kmo_remove_line(tmp_spec_data, lambda_x, atmos_model, 
                            lines_center_hk[i], lines_width_hk[i]);
            else if (!strcmp(filter_id, "K")) 
                for (i = 0; i < nr_lines_k; i++) 
                    kmo_remove_line(tmp_spec_data, lambda_x, atmos_model, 
                            lines_center_k[i], lines_width_k[i]);
            else if (!strcmp(filter_id, "IZ")) 
                for (i = 0; i < nr_lines_iz; i++) 
                    kmo_remove_line(tmp_spec_data, lambda_x, atmos_model, 
                            lines_center_iz[i], lines_width_iz[i]);
            else if (strcmp(filter_id, "YJ") == 0)
                for (i = 0; i < nr_lines_yj; i++) 
                    kmo_remove_line(tmp_spec_data, lambda_x, atmos_model, 
                            lines_center_yj[i], lines_width_yj[i]);
            
            if (0) kmos_std_star_plot() ;
            cpl_vector_delete(atmos_model);
        } else {
            cpl_msg_warning(__func__, "Missing ATMOS_MODEL");
        }
    } else if (star_type == 'G') {
        /* G star */
        if (solar_frame != NULL) {
            /* Interpolate SOLAR_SPEC to same scale and divide it */
            solar_spec = kmo_interpolate_vector_wcs(solar_frame, 
                    lambda_x);
            /* Set to zero if solar_spec isn't overlapping  */
            /* wavelength range of star apectrum completely */
            cpl_vector_divide(tmp_spec_data, solar_spec);
            cpl_vector_delete(solar_spec); 
        } else {
            cpl_msg_warning(__func__, "Missing SOLAR_SPEC");
        }
    }

    if (star_temp > 0.0) {
        /* Divide blackbody from tmp_spec_data */
        kmo_divide_blackbody(tmp_spec_data, lambda_x, star_temp);
    }
    cpl_vector_delete(lambda_x);

    /* Normalise telluric and its noise */
    /* mean is taken in lambda defined range */
    kmo_calc_band_mean(*out_sub_tel_data_header, filter_id, tmp_spec_data, 
            tmp_spec_noise, &mean_data, NULL);
    cpl_vector_divide_scalar(tmp_spec_data, mean_data);

    if (tmp_spec_noise != NULL) {
        /* Scale noise with the same factor as data */
        cpl_vector_divide_scalar(tmp_spec_noise, mean_data);

        /* Set noise spectrum to zero when solar_spec is short*/
        ptmp_spec_data = cpl_vector_get_data_const(tmp_spec_data);
        ptmp_spec_noise = cpl_vector_get_data(tmp_spec_noise);
        for (i = 0; i < cpl_vector_get_size(tmp_spec_data);i++)
            if (ptmp_spec_data[i]==0.0) ptmp_spec_noise[i]=0.0;
    }

    /* Store telluric & error spectrum */
    *out_telluric_data = tmp_spec_data;
    *out_telluric_noise = tmp_spec_noise;
    return 0 ;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Make consistency checks and return some quantities
  @param    frameset          
  @param    is_stdstarscipatt   
  @param    compute_qcs 
  @param    magnitude1
  @param    magnitude2
  @return   1 if consistent, 0 if not, -1 in error case
 */
/*----------------------------------------------------------------------------*/
static int kmos_std_star_check_inputs(
        cpl_frameset            *   frameset,
        const char              *   magnitude_txt,
        int                     *   is_stdstarscipatt,
        int                     *   compute_qcs,
        double                  *   magnitude1,
        double                  *   magnitude2)
{
    int                     nb_std, nb_illum, nb_xcal, nb_ycal, nb_lcal,
                            nb_flat, nb_wave ;
    cpl_frame           *   tmp_frame ;
    cpl_frameset        *   frameset_std ;
    cpl_propertylist    *   tmp_head ;
    const char          *   my_mag_txt ;
    int                     nr_split_mag ;
    char                **  split_mag ;
    char                *   grat_id ;
    int                     same_gratings ;
    const char          *   tmp_str ;

    /* Check Entries */
    if (frameset == NULL || is_stdstarscipatt == NULL || compute_qcs == NULL ||
            magnitude1 == NULL || magnitude2 == NULL) return -1;

    /* Count frames */
    nb_std = cpl_frameset_count_tags(frameset, STD) ;
    nb_illum = cpl_frameset_count_tags(frameset, ILLUM_CORR) ;
    nb_xcal = cpl_frameset_count_tags(frameset, XCAL) ;
    nb_ycal = cpl_frameset_count_tags(frameset, YCAL) ;
    nb_lcal = cpl_frameset_count_tags(frameset, LCAL) ;
    nb_flat = cpl_frameset_count_tags(frameset, MASTER_FLAT) ;
    nb_wave = cpl_frameset_count_tags(frameset, WAVE_BAND) ;

    /* Check numbers */
    if (nb_std < 1) {
        cpl_msg_error(__func__, "At least one STD frame is required") ;
        cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
        return 0 ;
    }
    if (nb_std == 1) 
        cpl_msg_warning(__func__, "2 STD frames needed for sky subtraction") ;

    if (nb_illum < 0 || nb_illum > 1) {
        cpl_msg_error(__func__, "0 or 1 ILLUM frame expected") ;
        cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
        return 0 ;
    }
    if (nb_xcal != 1 || nb_ycal != 1 || nb_lcal != 1) {
        cpl_msg_error(__func__, "1 X/Y/LCAL required") ;
        cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
        return 0 ;
    }
    if (nb_flat != 1) {
        cpl_msg_error(__func__, "1 MASTER_FLAT required") ;
        cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
        return 0 ;
    }
    if (nb_wave != 1) {
        cpl_msg_error(__func__, "1 WAVE_BAND required") ;
        cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
        return 0 ;
    }
    
    /* Extract STD frames with same gratings as the first STD frame */
    frameset_std=kmos_std_star_extract_same_grat_stds(frameset,&same_gratings) ;
    
    /* Get infos from the first STD frame */
    tmp_frame = kmo_dfs_get_frame(frameset_std, STD);
    tmp_head=kmclipm_propertylist_load(cpl_frame_get_filename(tmp_frame),0);
    grat_id = cpl_sprintf("%s", cpl_propertylist_get_string(tmp_head,
                "ESO INS GRAT1 ID"));
    if (!strcmp(cpl_propertylist_get_string(tmp_head, TPL_ID),
                "KMOS_spec_cal_stdstarscipatt"))    *is_stdstarscipatt = TRUE ;
    else                                            *is_stdstarscipatt = FALSE ;
    cpl_propertylist_delete(tmp_head);
    cpl_frameset_delete(frameset_std) ;

    /* Check if QC are computed */
    if (same_gratings) {
        *compute_qcs = TRUE ;
        // now check source of magnitude (user or keyword)
        tmp_frame = kmo_dfs_get_frame(frameset, STD);
        tmp_head=kmclipm_propertylist_load(cpl_frame_get_filename(tmp_frame),0);

        if (!strcmp(magnitude_txt, "")) {
            /* No user defined magnitude */
            if ((cpl_propertylist_has(tmp_head, STDSTAR_MAG)) &&
                (cpl_propertylist_get_type(tmp_head, STDSTAR_MAG) == 
                 CPL_TYPE_STRING)) {
                my_mag_txt = cpl_propertylist_get_string(tmp_head, STDSTAR_MAG);
                split_mag = kmo_strsplit(my_mag_txt, ",", &nr_split_mag);

                /* Check if band and number of magnitudes matches */
                if (nr_split_mag == 2 && !strcmp(grat_id, "HK")) {
                    *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") ||
                            !strcmp(grat_id, "H") || !strcmp(grat_id, "IZ") ||
                            !strcmp(grat_id, "YJ"))) {
                    *magnitude1 = atof(split_mag[0]);
                    cpl_msg_info("", "Magnitude in %s: %g",grat_id,*magnitude1);
                } else {
                    // keyword STDSTAR_MAG doesn't match filter
                    *compute_qcs = FALSE;
                    cpl_msg_warning(cpl_func, "Wrong Mag, QCs not computed") ;
                }
                kmo_strfreev(split_mag);
            } else {
                /* STDSTAR_MAG unavailable or wrong type */
                *compute_qcs = FALSE;
                cpl_msg_warning(cpl_func, "%s is not set, QCs not computed", 
                        STDSTAR_MAG);
            }
        } else {
            // magnitude is user specified
            cpl_msg_info(cpl_func, "Magnitude entered by user, ignore header");

            split_mag = kmo_strsplit(magnitude_txt, ",", &nr_split_mag);
            switch (nr_split_mag) {
                case 1:
                    *magnitude1 = atof(split_mag[0]);
                    cpl_msg_info(cpl_func, "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_strfreev(split_mag);
                    cpl_propertylist_delete(tmp_head);
                    cpl_msg_error(__func__, "Wrong Magnitude Specified") ;
                    cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
                    return 0 ;
            }
            kmo_strfreev(split_mag);
        }
        cpl_propertylist_delete(tmp_head);
    } else {
        *compute_qcs = FALSE ;
    }

    /* Check SOLAR_SPEC grating */
    tmp_frame = kmo_dfs_get_frame(frameset, SOLAR_SPEC);
    if (tmp_frame != NULL) {
        tmp_head=kmclipm_propertylist_load(cpl_frame_get_filename(tmp_frame),0);
        tmp_str = cpl_propertylist_get_string(tmp_head, FILT_ID);
        if (strcmp(tmp_str, grat_id)) {
            cpl_propertylist_delete(tmp_head) ;
            cpl_msg_error(__func__, "Wrong SOLAR_SPEC grating") ;
            cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
            return 0 ;
        }
        cpl_propertylist_delete(tmp_head) ;
    }

    /* Check ATMOS_MODEL grating */
    tmp_frame = kmo_dfs_get_frame(frameset, ATMOS_MODEL);
    if (tmp_frame != NULL) {
        tmp_head=kmclipm_propertylist_load(cpl_frame_get_filename(tmp_frame),0);
        tmp_str = cpl_propertylist_get_string(tmp_head, FILT_ID);
        if (strcmp(tmp_str, grat_id)) {
            cpl_propertylist_delete(tmp_head) ;
            cpl_msg_error(__func__, "Wrong ATMOS_MODEL grating") ;
            cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT) ;
            return 0 ;
        }
        cpl_propertylist_delete(tmp_head) ;
    }
    cpl_free(grat_id) ;

    /* Check if filter_id and grating_id match for all detectors */
    kmo_check_frameset_setup(frameset, XCAL, FALSE, FALSE, TRUE);
    kmo_check_frame_setup(frameset, XCAL, YCAL, TRUE, FALSE, TRUE);
    kmo_check_frame_setup(frameset, XCAL, LCAL, TRUE, FALSE, TRUE);
    kmo_check_frame_setup(frameset, XCAL, MASTER_FLAT, TRUE, FALSE, TRUE);
    kmo_check_frame_setup(frameset, XCAL, STD, FALSE, FALSE, TRUE);
    if (nb_illum == 1) 
        kmo_check_frame_setup(frameset, XCAL, ILLUM_CORR, TRUE, FALSE, FALSE);
    kmo_check_frame_setup_md5_xycal(frameset);
    kmo_check_frame_setup_md5(frameset);
    return 1 ;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Plot 
  @param    
  @return   0 if ok, -1 in error case
 */
/*----------------------------------------------------------------------------*/
static int kmos_std_star_plot(void)
{

    /*
    cpl_vector *tmp_spec_data_atmo = NULL;
    cpl_vector *tmp_spec_data_new = NULL;
                        tmp_spec_data_orig=cpl_vector_duplicate(tmp_spec_data);
    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_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;

*/
    return 0 ;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Adjust header
  @param    
  @return   0 if ok, -1 in error case
 */
/*----------------------------------------------------------------------------*/
static int kmos_std_star_adjust_double(
        cpl_propertylist    *   header,
        const char          *   key1,
        const char          *   key2,
        const char          *   key3)
{
    if (header==NULL || key1==NULL || key2==NULL || key3==NULL) return -1 ;
    cpl_propertylist_update_double(header, key1, 
            cpl_propertylist_get_double(header, key3));
    cpl_propertylist_erase(header, key2) ;
    cpl_propertylist_erase(header, key3) ;
    return 0 ;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Adjust header
  @param    
  @return   0 if ok, -1 in error case
 */
/*----------------------------------------------------------------------------*/
static int kmos_std_star_adjust_string(
        cpl_propertylist    *   header,
        const char          *   key1,
        const char          *   key2,
        const char          *   key3)
{
    if (header==NULL || key1==NULL || key2==NULL || key3==NULL) return -1 ;
    cpl_propertylist_update_string(header, key1, 
            cpl_propertylist_get_string(header, key3));
    cpl_propertylist_erase(header, key2) ;
    cpl_propertylist_erase(header, key3) ;
    return 0 ;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Extract the STD frames that have the same grating as the 1st one
  @param    in              Input frameset
  @param    same_gratings   Flag to indicate if the gratings of STD are
                              identical
  @return   the new frameset or NULL in error case 
 */
/*----------------------------------------------------------------------------*/
static cpl_frameset * kmos_std_star_extract_same_grat_stds(
        cpl_frameset        *   in,
        int                 *   same_gratings)
{
    cpl_frameset        *   frameset_std ;
    cpl_frame           *   tmp_frame ;
    cpl_propertylist    *   tmp_header ;
    char                *   grat_id ;

    /* Check entries */
    if (in == NULL ||  same_gratings== NULL) return NULL ;

    /* Create new frameset */
    frameset_std = cpl_frameset_new();

    tmp_frame = kmo_dfs_get_frame(in, STD);
    tmp_header = kmclipm_propertylist_load(cpl_frame_get_filename(tmp_frame),0);
    grat_id = cpl_sprintf("%s", cpl_propertylist_get_string(tmp_header, 
                "ESO INS GRAT1 ID"));
    cpl_propertylist_delete(tmp_header); 
    cpl_frameset_insert(frameset_std, cpl_frame_duplicate(tmp_frame));

    tmp_frame = kmo_dfs_get_frame(in, NULL);
    *same_gratings = TRUE ;
    while (tmp_frame != 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"))) {
            cpl_frameset_insert(frameset_std, cpl_frame_duplicate(tmp_frame));
        } else {
            *same_gratings = FALSE;
        }
        cpl_propertylist_delete(tmp_header);

        tmp_frame = kmo_dfs_get_frame(in, NULL);
    }
    cpl_free(grat_id) ;
    return frameset_std ;
}
