/* $Id: hawki_cal_zpoint.c,v 1.39 2013-09-02 14:26:22 cgarcia Exp $
 *
 * This file is part of the HAWKI 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/*
 * $Author: cgarcia $
 * $Date: 2013-09-02 14:26:22 $
 * $Revision: 1.39 $
 * $Name: not supported by cvs2svn $
 */

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

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

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

#include "irplib_utils.h"
#include "irplib_calib.h"
#include "irplib_strehl.h"
#include "irplib_stdstar.h"
#include "irplib_cat.h"
#include "irplib_wcs.h"

#include "hawki_image_stats.h"
#include "hawki_utils_legacy.h"
#include "hawki_calib.h"
#include "hawki_load.h"
#include "hawki_save.h"
#include "hawki_pfits_legacy.h"
#include "hawki_dfs_legacy.h"
#include "hawki_alloc.h"

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

static int hawki_cal_zpoint_create(cpl_plugin *) ;
static int hawki_cal_zpoint_exec(cpl_plugin *) ;
static int hawki_cal_zpoint_destroy(cpl_plugin *) ;
static int hawki_cal_zpoint(cpl_parameterlist *, cpl_frameset *) ;

static void hawki_cal_zpoint_output_init(void);
static int hawki_cal_zpoint_retrieve_input_param
(cpl_parameterlist  *  parlist);
static cpl_table ** hawki_cal_zpoint_reduce
(cpl_frameset    *   set,
 const char      *   stdstars,
 const char      *   bpm,
 const char      *   flat,
 cpl_table       **  raw_zpoint_stats,
 int             *   labels,
 cpl_imagelist   **  images);
static int hawki_cal_zpoint_save
(cpl_table           **  zpoint_tables,
 int                 *   labels,
 cpl_imagelist       *   images,
 cpl_table           **  raw_zpoint_stats,
 cpl_frameset        *   zpoint_frames,
 cpl_frameset        *   calib_frames,
 const cpl_frame     *   stars_frame,
 cpl_parameterlist   *   parlist,
 cpl_frameset        *   set);
static int hawki_cal_zpoint_compute_qc
(cpl_propertylist *   qcmainparams, 
 cpl_propertylist **  qcextparams,
 cpl_frameset     *   set);
static cpl_table ** hawki_cal_zpoint_photom
(cpl_imagelist       *   ilist,
 cpl_bivector        *   pos,
 int                 *   labels);
static int hawki_cal_zpoint_get_mag(const char *, double, double, hawki_band) ;
static int hawki_cal_zpoint_compute_keywords
(cpl_frameset * set, 
 int          * labels);
static cpl_error_code  hawki_cal_zpoint_get_expected_pos
(cpl_frameset * set,
 int          * labels);
int hawki_cal_zpoint_check_epoch_equinox(cpl_propertylist * plist);

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

static struct {
    /* Inputs */
    double      xcoord[HAWKI_NB_DETECTORS] ;
    double      ycoord[HAWKI_NB_DETECTORS] ;
    double      target_ra;
    double      target_dec;
    double      stdstar_given_magnitude;
    double      detect_sigma ;
    int         sx ;
    int         sy ;
    double      phot_star_radius ;
    double      phot_bg_r1 ;
    double      phot_bg_r2 ;
} hawki_cal_zpoint_config;

static struct {
    /* Outputs */
    double      dit;
    double      pixscale;
    char        filter[512];
    hawki_band  band;
    char        starname[512];
    char        sptype[512];
    char        catalog[512];
    double      humidity;
    double      airmass[HAWKI_NB_DETECTORS];
    double      zpoint[HAWKI_NB_DETECTORS];
    double      atx0[HAWKI_NB_DETECTORS];
    double      posx[HAWKI_NB_DETECTORS];
    double      posy[HAWKI_NB_DETECTORS];
    double      flux[HAWKI_NB_DETECTORS];
    double      instrmag[HAWKI_NB_DETECTORS];
    double      peak[HAWKI_NB_DETECTORS];
    double      bgd[HAWKI_NB_DETECTORS];
    double      fwhmx[HAWKI_NB_DETECTORS];
    double      fwhmy[HAWKI_NB_DETECTORS];
    double      fwhm[HAWKI_NB_DETECTORS];
    double      fwhmx_as[HAWKI_NB_DETECTORS];
    double      fwhmy_as[HAWKI_NB_DETECTORS];
    double      fwhm_as[HAWKI_NB_DETECTORS];
    double      mean_zpoint;
    double      mean_atx0;
    double      mean_airmass;
    double      ext_coeff;
    double      stdstar_ra;
    double      stdstar_dec;
    double      stdstar_mag_filter;
    double      stdstar_mag_H;
    double      stdstar_mag_J;
    double      stdstar_mag_K;
    double      stdstar_mag_Y;
    int         stdstar_incat_found;
    int         stdstar_mag_available;
    int         stdstar_image_detected[HAWKI_NB_DETECTORS];
    int         zpoint_computable[HAWKI_NB_DETECTORS];
    int         zpoint_mean_computable;
} hawki_cal_zpoint_outputs;

static char hawki_cal_zpoint_description[] =
"(OBSOLETE) hawki_cal_zpoint -- Zero point recipe\n"
"The input of the recipe files listed in the Set Of Frames (sof-file)\n"
"must be tagged as:\n"
"raw-file.fits "HAWKI_CAL_ZPOINT_RAW" or\n"
"stdstars-file.fits "HAWKI_CALPRO_STDSTARS" or\n"
"flat-file.fits "HAWKI_CALPRO_FLAT" or\n"
"bpm-file.fits "HAWKI_CALPRO_BPM"\n"
"The recipe creates as an output:\n"
"hawki_cal_zpoint.fits ("HAWKI_CALPRO_ZPOINT_TAB"): Zero point solution table\n"
"hawki_cal_zpoint_check.fits ("HAWKI_CALPRO_ZPOINT_IMA"): Standard star images corrected (for checking purposes)\n"
"hawki_cal_zpoint_stats.fits ("HAWKI_CALPRO_ZPOINT_STATS"): Statistics of the raw standard star images\n"
"Return code:\n"
"esorex exits with an error code of 0 if the recipe completes successfully\n"
"or 1 otherwise";

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

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

  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,
                    HAWKI_BINARY_VERSION,
                    CPL_PLUGIN_TYPE_RECIPE,
                    "hawki_cal_zpoint",
                    "(OBSOLETE) Zero point computation recipe",
                    hawki_cal_zpoint_description,
                    "ESO Pipeline Group",
                    PACKAGE_BUGREPORT,
                    hawki_get_license_legacy(),
                    hawki_cal_zpoint_create,
                    hawki_cal_zpoint_exec,
                    hawki_cal_zpoint_destroy) ;

    cpl_pluginlist_append(list, plugin) ;

    return 0;
}

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

  Create the recipe instance and make it available to the application using the
  interface.
 */
/*----------------------------------------------------------------------------*/
static int hawki_cal_zpoint_create(cpl_plugin * plugin)
{
    cpl_recipe      * recipe ;
    cpl_parameter   * p ;

    /* Get the recipe out of the plugin */
    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();
    if (recipe->parameters == NULL)
        return 1;

    /* Fill the parameters list */
    /* --detect_sigma */
    p = cpl_parameter_new_value("hawki.hawki_cal_zpoint.detect_sigma",
            CPL_TYPE_DOUBLE, "the sigma value for object detection",
            "hawki.hawki_cal_zpoint", 7.0);
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "detect_sigma") ;
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV) ;
    cpl_parameterlist_append(recipe->parameters, p) ;
    /* --star_r */
    p = cpl_parameter_new_value("hawki.hawki_cal_zpoint.star_r",CPL_TYPE_DOUBLE,
            "the star radius", "hawki.hawki_cal_zpoint", -1.0) ;
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "star_r") ;
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV) ;
    cpl_parameterlist_append(recipe->parameters, p) ;
    /* --bg_r1 */
    p = cpl_parameter_new_value("hawki.hawki_cal_zpoint.bg_r1", CPL_TYPE_DOUBLE,
            "the internal background radius", "hawki.hawki_cal_zpoint", -1.0) ;
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "bg_r1") ;
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV) ;
    cpl_parameterlist_append(recipe->parameters, p) ;
    /* --bg_r2 */
    p = cpl_parameter_new_value("hawki.hawki_cal_zpoint.bg_r2", CPL_TYPE_DOUBLE,
            "the external background radius", "hawki.hawki_cal_zpoint", -1.0) ;
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "bg_r2") ;
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV) ;
    cpl_parameterlist_append(recipe->parameters, p) ;
    /* --ra */
    p = cpl_parameter_new_value("hawki.hawki_cal_zpoint.ra", CPL_TYPE_DOUBLE,
            "RA in degrees", "hawki.hawki_cal_zpoint", 999.0) ;
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ra") ;
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV) ;
    cpl_parameterlist_append(recipe->parameters, p) ;
    /* --dec */
    p = cpl_parameter_new_value("hawki.hawki_cal_zpoint.dec", CPL_TYPE_DOUBLE,
            "DEC in degrees", "hawki.hawki_cal_zpoint", 999.0) ;
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "dec") ;
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV) ;
    cpl_parameterlist_append(recipe->parameters, p) ;
    /* --mag */
    p = cpl_parameter_new_value("hawki.hawki_cal_zpoint.mag", CPL_TYPE_DOUBLE,
            "magnitude", "hawki.hawki_cal_zpoint", 99.0) ;
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "mag") ;
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV) ;
    cpl_parameterlist_append(recipe->parameters, p) ;
    /* --sx */
    p = cpl_parameter_new_value("hawki.hawki_cal_zpoint.sx", CPL_TYPE_INT,
            "x half-size of the search box", "hawki.hawki_cal_zpoint", 100) ;
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sx") ;
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV) ;
    cpl_parameterlist_append(recipe->parameters, p) ;
    /* --sy */
    p = cpl_parameter_new_value("hawki.hawki_cal_zpoint.sy", CPL_TYPE_INT,
            "y half-size of the search box", "hawki.hawki_cal_zpoint", 100) ;
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "sy") ;
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV) ;
    cpl_parameterlist_append(recipe->parameters, p) ;
    /* --xcoord */
    p = cpl_parameter_new_value("hawki.hawki_cal_zpoint.xcoord", CPL_TYPE_STRING,
            "Coordinates in X where the standard star is located. If -1 use WCS",
            "hawki.hawki_cal_zpoint", "-1., -1., -1., -1.");
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "xcoord") ;
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV) ;
    cpl_parameterlist_append(recipe->parameters, p) ;
    /* --ycoord */
    p = cpl_parameter_new_value("hawki.hawki_cal_zpoint.ycoord", CPL_TYPE_STRING,
            "Coordinates in Y where the standard star is located. If -1 use WCS",
            "hawki.hawki_cal_zpoint", "-1., -1., -1., -1.") ;
    cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI, "ycoord") ;
    cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV) ;
    cpl_parameterlist_append(recipe->parameters, p) ;

    /* Return */
    return 0;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Execute the plugin instance given by the interface
  @param    plugin  the plugin
  @return   0 if everything is ok
 */
/*----------------------------------------------------------------------------*/
static int hawki_cal_zpoint_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 ;

    /* Issue a banner */
    hawki_print_banner();

    return hawki_cal_zpoint(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 hawki_cal_zpoint_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    The recipe data reduction part is implemented here
  @param    parlist     the parameters list
  @param    framelist   the frames list
  @return   0 if everything is ok
 */
/*----------------------------------------------------------------------------*/
static int hawki_cal_zpoint(
        cpl_parameterlist   *   parlist,
        cpl_frameset        *   framelist)
{
    const char      *   flat ;
    const char      *   bpm ;
    const char      *   stdstars ;
    cpl_frameset    *   zpoint_frames ;
    cpl_frameset    *   calib_frames ;
    const cpl_frame *   stars_frame;
    cpl_table       **  raw_zpoint_stats;
    cpl_table       **  zpoint_tables;
    cpl_imagelist   *   std_star_images ;
    int             *   labels;
    int                 idet;

    /* Initialise Output */
    hawki_cal_zpoint_output_init();
    zpoint_frames = NULL ;

    /* Retrieve input parameters */
    if(hawki_cal_zpoint_retrieve_input_param(parlist))
    {
        cpl_msg_error(__func__, "Wrong parameters");
        return -1;
    }

    /* Identify the RAW and CALIB frames in the input frameset */
    if (hawki_dfs_set_groups_legacy(framelist)) {
        cpl_msg_error(__func__, "Cannot identify RAW and CALIB frames") ;
        return -1 ;
    }

    /* Retrieve calibration data */
    calib_frames = cpl_frameset_new();
    flat = hawki_extract_first_filename(framelist, HAWKI_CALPRO_FLAT) ;
    if(flat)
        cpl_frameset_insert(calib_frames, cpl_frame_duplicate(
                cpl_frameset_find_const(framelist, HAWKI_CALPRO_FLAT)));
    bpm = hawki_extract_first_filename(framelist, HAWKI_CALPRO_BPM) ;
    if(bpm)
        cpl_frameset_insert(calib_frames, cpl_frame_duplicate(
                cpl_frameset_find_const(framelist, HAWKI_CALPRO_BPM)));

    /* STD stars catalog requested */
    stars_frame = cpl_frameset_find_const(framelist, HAWKI_CALPRO_STDSTARS);
    if (stars_frame  == NULL) 
    {
        cpl_msg_error(__func__,"Cannot find the catalog in the input list (%s)",
                      HAWKI_CALPRO_STDSTARS);
        cpl_frameset_delete(calib_frames);
        return -1 ;
    }
    stdstars = cpl_frame_get_filename(stars_frame);

    /* Retrieve raw frames */
    if ((zpoint_frames = hawki_extract_frameset(framelist,
                    HAWKI_CAL_ZPOINT_RAW)) != NULL) {
    } else {
        cpl_msg_error(__func__, "Cannot find raw frames in the input list (%s)",
                      HAWKI_CAL_ZPOINT_RAW);
        cpl_frameset_delete(calib_frames);
        return -1 ;
    }

    /* Exactly 4 images are expected */
    if (cpl_frameset_get_size(zpoint_frames) != 4) {
        cpl_msg_error(__func__, 
                      "4 input raw frames are expected, not %"CPL_SIZE_FORMAT,
                      cpl_frameset_get_size(zpoint_frames)) ;
        cpl_frameset_delete(zpoint_frames) ;
        cpl_frameset_delete(calib_frames);
        return -1 ;
    }

    /* Create the statistics table */
    raw_zpoint_stats = cpl_malloc(HAWKI_NB_DETECTORS * sizeof(cpl_table *));
    for(idet=0 ; idet<HAWKI_NB_DETECTORS ; idet++)
    {
        raw_zpoint_stats[idet] = cpl_table_new(
            cpl_frameset_get_size(zpoint_frames));
    }
    hawki_image_stats_initialize(raw_zpoint_stats);

    /* Compute the zpoint values */
    cpl_msg_info(__func__, "Reduce the data") ;
    cpl_msg_indent_more() ;
    labels = cpl_calloc(cpl_frameset_get_size(zpoint_frames), sizeof(int)) ;
    if ((zpoint_tables = hawki_cal_zpoint_reduce(zpoint_frames, stdstars, 
             bpm, flat, raw_zpoint_stats, labels, &std_star_images))==NULL)
    {
        cpl_msg_error(__func__, "Cannot reduce the data") ;
        cpl_frameset_delete(zpoint_frames) ;
        cpl_frameset_delete(calib_frames);
        hawki_table_delete(raw_zpoint_stats);
        cpl_free(labels);
        cpl_msg_indent_less() ;
        return -1 ;
    }
    cpl_msg_indent_less() ;

    /* Save the products */
    cpl_msg_info(__func__, "Save the products") ;
    cpl_msg_indent_more() ;
    if (hawki_cal_zpoint_save
            (zpoint_tables, labels, std_star_images, raw_zpoint_stats, 
             zpoint_frames, calib_frames, stars_frame,
             parlist, framelist) == -1)
    {
        cpl_msg_warning(__func__, "Data could not be saved. "
                        "Check permisions or disk space") ;
        cpl_frameset_delete(zpoint_frames);
        hawki_table_delete(zpoint_tables) ;
        cpl_imagelist_delete(std_star_images) ;
        cpl_frameset_delete(calib_frames);
        hawki_table_delete(raw_zpoint_stats);
        cpl_free(labels);
        cpl_msg_indent_less() ;
        return -1 ;
    }
    cpl_msg_indent_less() ;

    /* Free and return */
    cpl_frameset_delete(zpoint_frames);
    cpl_frameset_delete(calib_frames);
    cpl_imagelist_delete(std_star_images);
    hawki_table_delete(zpoint_tables);
    hawki_table_delete(raw_zpoint_stats);
    cpl_free(labels);

    /* Return */
    if (cpl_error_get_code())
    {
        cpl_msg_error(__func__,
                      "HAWK-I pipeline could not recover from previous errors");
        return -1 ;
    }
    else return 0 ;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    The recipe data reduction part is implemented here
  @param    set         the set of frames
  @param    stdstars    the stad stars
  @param    bpm         the bad pixel map name or NULL
  @param    flat        the flat field name or NULL
  @param    images      the images where the photometry is computed
  @return   a cpl_table with the results or NULL
 */
/*----------------------------------------------------------------------------*/
static cpl_table ** hawki_cal_zpoint_reduce
(cpl_frameset    *   set,
 const char      *   stdstars,
 const char      *   bpm,
 const char      *   flat,
 cpl_table       **  raw_zpoint_stats,
 int             *   labels,
 cpl_imagelist   **  star_images)
{
    cpl_frame           *   cur_frame ;
    cpl_propertylist    *   plist ;
    const char          *   sval ;
    cpl_imagelist       *   star_images_frame_order ;
    int                     nima ;
    cpl_bivector        *   positions ;
    cpl_image           *   filt_ima ;
    cpl_mask            *   kernel;
    int                     size_x, size_y ;
    double                  pos_x, pos_y, pos_x_cen, pos_y_cen, dist, min_dist ;
    cpl_apertures       *   aperts ;
    cpl_table           **  zpoint_tables;
    cpl_image           *   tmp_ima ;
    int                     iaper;
    int                     idet;
    int                     iframe;
    int                     iframe_star = -1;
    int                     nframes;
    char                    rastr[32];
    char                    decstr[32];
    cpl_errorstate          error_prevstate;
    int                     return_code;

    /* Check inputs */
    if (set == NULL) return NULL ;
    if (stdstars == NULL) return NULL ;
    if (star_images == NULL) return NULL ;

    /* Get the filter name, DIT, Target RA and DEC  */
    error_prevstate = cpl_errorstate_get();
    cur_frame = cpl_frameset_get_position(set, 0) ;
    plist=cpl_propertylist_load(cpl_frame_get_filename(cur_frame), 0) ;
    if ((sval = hawki_pfits_get_filter_legacy(plist)) == NULL) return NULL ;
    else sprintf(hawki_cal_zpoint_outputs.filter, "%s", sval) ;
    if (hawki_cal_zpoint_config.target_ra > 998.0)
    {
        hawki_cal_zpoint_config.target_ra = hawki_pfits_get_targ_alpha(plist);
        //hawki_cal_zpoint_config.target_ra = hawki_pfits_get_ra(plist) -
        //hawki_pfits_get_cumoffseta(plist) / 3600.;// Not valid before Nov 2008
        if(hawki_cal_zpoint_check_epoch_equinox(plist) == -1)
        {
            cpl_propertylist_delete(plist);
            return NULL;
        }
    }
    if (hawki_cal_zpoint_config.target_dec > 998.0)
    {
        hawki_cal_zpoint_config.target_dec = hawki_pfits_get_targ_delta(plist);
        //hawki_cal_zpoint_config.target_dec = hawki_pfits_get_dec(plist) -
        //hawki_pfits_get_cumoffsetd(plist) / 3600.;// Not valid before Nov 2008
        if(hawki_cal_zpoint_check_epoch_equinox(plist) == -1)
        {
            cpl_propertylist_delete(plist);
            return NULL;
        }
    }
    hawki_cal_zpoint_outputs.dit = hawki_pfits_get_dit_legacy(plist) ;
    hawki_cal_zpoint_outputs.pixscale = hawki_pfits_get_pixscale(plist) ;
    cpl_propertylist_delete(plist) ;
    if(!cpl_errorstate_is_equal(error_prevstate))
    {
        cpl_msg_error(__func__, "Cannot get keywords from main header:");
        cpl_msg_indent_more();
        cpl_msg_error(__func__, "%s",cpl_error_get_message());
        cpl_msg_indent_less();
        return NULL ;
    }
    cpl_msg_info(__func__,"Searching catalog stars closest to target:");
    cpl_msg_indent_more();
    hawki_utils_ra2str(rastr, 32, hawki_cal_zpoint_config.target_ra);
    hawki_utils_dec2str(decstr, 32, hawki_cal_zpoint_config.target_dec);
    cpl_msg_info(__func__,"RA = %g (%s); DEC = %g (%s)",
                 hawki_cal_zpoint_config.target_ra, rastr,
                 hawki_cal_zpoint_config.target_dec, decstr);
    cpl_msg_info(__func__,"HAWK-I Filter: %s", hawki_cal_zpoint_outputs.filter);
    cpl_msg_indent_less();

    /* Get the band */
    if ((hawki_cal_zpoint_outputs.band =
                hawki_get_band(hawki_cal_zpoint_outputs.filter)) ==
            HAWKI_BAND_UNKNOWN) {
        cpl_msg_error(__func__, "Cannot associate the filter %s to a band",
                hawki_cal_zpoint_outputs.filter) ;
        return NULL ;
    }

    /* Get the standard star information from database */
    cpl_msg_indent_more();
    return_code = hawki_cal_zpoint_get_mag(stdstars,
            hawki_cal_zpoint_config.target_ra,
            hawki_cal_zpoint_config.target_dec,
            hawki_cal_zpoint_outputs.band);
    if (return_code == -1)
    {
        cpl_msg_error(__func__, "Could not open star database");
        return NULL ;
    }   
    else if(return_code == 1)
    {
        cpl_msg_warning(__func__,"No suitable star found in catalog");
        cpl_msg_warning(__func__,"Using the target coordinates "
                        "as the standard star coordinates: ");
        hawki_cal_zpoint_outputs.stdstar_ra = 
                hawki_cal_zpoint_config.target_ra;
        hawki_cal_zpoint_outputs.stdstar_dec = 
                hawki_cal_zpoint_config.target_dec;
        hawki_utils_ra2str(rastr, 32, hawki_cal_zpoint_outputs.stdstar_ra);
        hawki_utils_dec2str(decstr, 32, hawki_cal_zpoint_outputs.stdstar_dec);
        cpl_msg_info(__func__, " RA = %g (%s) ; DEC = %g (%s)",
                     hawki_cal_zpoint_outputs.stdstar_ra, rastr,
                     hawki_cal_zpoint_outputs.stdstar_dec, decstr);
    }
    else
    {    
        cpl_msg_info(__func__, "Catalog where the star was found: %s",
                     hawki_cal_zpoint_outputs.catalog);
        cpl_msg_info(__func__, "Star name: %s",
                     hawki_cal_zpoint_outputs.starname);
        if(hawki_cal_zpoint_outputs.stdstar_mag_available == 1)
        {
            cpl_msg_info(__func__, "Star magnitude in filter %s : %g [mag]",
                    hawki_cal_zpoint_outputs.filter,
                    hawki_cal_zpoint_outputs.stdstar_mag_filter);
        }
        hawki_utils_ra2str(rastr, 32, hawki_cal_zpoint_outputs.stdstar_ra);
        hawki_utils_dec2str(decstr, 32, hawki_cal_zpoint_outputs.stdstar_dec);
        cpl_msg_info(__func__, "Star coordinates: RA = %g (%s) ; DEC = %g (%s)",
                     hawki_cal_zpoint_outputs.stdstar_ra, rastr,
                     hawki_cal_zpoint_outputs.stdstar_dec, decstr);
    }
    if (hawki_cal_zpoint_config.stdstar_given_magnitude < 98.0) 
    {
        hawki_cal_zpoint_outputs.stdstar_mag_available = 1;
        hawki_cal_zpoint_outputs.stdstar_mag_filter = 
                hawki_cal_zpoint_config.stdstar_given_magnitude;
        cpl_msg_info(__func__, "Using user defined "
                     "star magnitude in filter %s : %g [mag]",
                     hawki_cal_zpoint_outputs.filter,
                     hawki_cal_zpoint_outputs.stdstar_mag_filter);
    }
    cpl_msg_indent_less();

    /* Labelise frames */
    cpl_msg_info(__func__, "Guessing which frame the STD is in for each chip");
    if(hawki_detectors_locate_star
        (set, hawki_cal_zpoint_outputs.stdstar_ra,
         hawki_cal_zpoint_outputs.stdstar_dec, labels) != 0)
    {
        cpl_msg_error(__func__, "Cannot determine which frame the STD is on") ;
        return NULL ;
    }

    /* Check all the detectors have one std star */
    cpl_msg_info(__func__, "Check that all the detectors have one STD star");
    nframes = cpl_frameset_get_size(set) ;
    if(hawki_check_stdstar_alldetectors(labels, nframes) != 0)
    {
        cpl_msg_error(__func__, "Not all the detectors have STD observations");
        return NULL ;
    }

    /* Compute the expected position of the star in pixels */
    /* This is stored in hawki_cal_zpoint_config.xcoord, ycoord */
    cpl_msg_indent_more();
    if(hawki_cal_zpoint_get_expected_pos(set, labels) != CPL_ERROR_NONE)
    {
        cpl_msg_error(__func__,"Could not determine where the star is located");
        cpl_msg_indent_less();
        return NULL;
    }
    cpl_msg_indent_less();
    
    /* Fetch the airmass and humidity */
    hawki_cal_zpoint_compute_keywords(set, labels);

    /* Create the positions vector */
    nima = cpl_frameset_get_size(set) ;
    positions = cpl_bivector_new(nima) ;

    /* Initialize */
    *star_images = cpl_imagelist_new();

    /* Loop on the detectors */
    cpl_msg_info(__func__,"Loop on the chips");
    cpl_msg_indent_more() ;
    for(idet = 0; idet < HAWKI_NB_DETECTORS; ++idet)
    {
        cpl_imagelist * sky_images;
        cpl_image     * star_ima = NULL;
        cpl_image     * sky;
        cpl_image     * flat_im;
        int             ext_nb;

        cpl_msg_info(__func__, "Loading the chip %d", idet+1);
        cpl_msg_indent_more() ;

        /* Allocate */
        sky_images = cpl_imagelist_new();

        cpl_msg_indent_more() ;
        for (iframe=0 ; iframe<nframes ; iframe++)
        {
            cpl_image * ima_cur;

            /* Load the image */
            ima_cur = hawki_load_image(set, iframe, idet+1, CPL_TYPE_FLOAT) ;
            if(ima_cur == NULL)
            {
                cpl_bivector_delete(positions) ;
                cpl_imagelist_delete(*star_images);
                cpl_imagelist_delete(sky_images) ;
                cpl_msg_error(__func__, "Error reading image");
                return NULL;
            }
            /* Get image statistics */
            size_x = cpl_image_get_size_x(ima_cur) ;
            size_y = cpl_image_get_size_y(ima_cur) ;
            if(hawki_image_stats_fill_from_image
                (raw_zpoint_stats,
                 ima_cur,
                 1,
                 1,
                 size_x,
                 size_y,
                 idet,
                 iframe) !=0 )
            {
                cpl_msg_error(__func__,"Cannot compute stats on ima %d det %d",
                              iframe+1, idet+1);
                cpl_bivector_delete(positions) ;
                cpl_imagelist_delete(*star_images);
                cpl_imagelist_delete(sky_images) ;
                cpl_image_delete(ima_cur);
                cpl_msg_indent_less() ;
                return NULL ;
            }

            /* Add the image to either the sky images or the star image */
            if(labels[iframe] == idet + 1)
            {
                star_ima = ima_cur;
                iframe_star = iframe;
            }
            else
                cpl_imagelist_set(sky_images, ima_cur,
                                  cpl_imagelist_get_size(sky_images));
        }
        cpl_msg_indent_less();

        /* Create the sky */
        cpl_msg_info(__func__, "Correct for the sky");
        sky = cpl_imagelist_collapse_median_create(sky_images);
        cpl_imagelist_delete(sky_images) ;

        /* Subtract the sky */
        cpl_image_subtract(star_ima, sky) ;
        cpl_image_delete(sky) ;

        /* Divide by the flatfield if one is provided */
        if (flat) {
            cpl_msg_info(__func__, "Correct for the flat") ;

            /* Get the extension with the current chip */
            if ((ext_nb = hawki_get_ext_from_detector(flat, idet + 1)) == -1)
            {
                cpl_msg_error(__func__, "Cannot get the extension with chip %d",
                              idet + 1);
                cpl_imagelist_delete(*star_images) ;
                cpl_bivector_delete(positions) ;
                return NULL ;
            }
            /* Load */
            flat_im = cpl_image_load(flat, CPL_TYPE_FLOAT, 0, ext_nb) ;
            cpl_image_divide(star_ima, flat_im) ;
            cpl_image_delete(flat_im) ;
        }

        /* Correct the bad pixels */
        if (bpm) {
            cpl_msg_info(__func__, "Correct for the bad pixels") ;
            if (hawki_bpm_calib(star_ima, bpm, idet + 1) == -1)
            {
                cpl_msg_error(__func__, "Cannot correct the BPM for chip %d",
                              idet + 1);
                cpl_imagelist_delete(*star_images) ;
                cpl_bivector_delete(positions) ;
                return NULL ;
            }
        }

        /* Put the result in the image list */
        cpl_imagelist_set(*star_images, star_ima,
                          cpl_imagelist_get_size(*star_images)) ;

        /* Object detection */
        cpl_msg_info(__func__,"For chip %d the STD should be on frame %d",
                     idet + 1, iframe_star+1);
        pos_x_cen = pos_y_cen = -1.0 ;
        size_x = cpl_image_get_size_x(star_ima) ;
        size_y = cpl_image_get_size_y(star_ima) ;

        /* Filtering the image*/
        kernel = cpl_mask_new(3, 3);
        cpl_mask_not(kernel);
        filt_ima = cpl_image_new(cpl_image_get_size_x(star_ima),
                                 cpl_image_get_size_y(star_ima),
                                 cpl_image_get_type(star_ima));
        cpl_image_filter_mask(filt_ima, star_ima, kernel, CPL_FILTER_MEDIAN, 
                              CPL_BORDER_FILTER);
        cpl_mask_delete(kernel);

        /* Looking for apertures */
        aperts = cpl_apertures_extract_sigma(filt_ima,
                hawki_cal_zpoint_config.detect_sigma) ;
        cpl_image_delete(filt_ima) ;
        if (aperts == NULL)
        {
            cpl_msg_error(__func__, "Cannot find the central object") ;
            cpl_imagelist_delete(*star_images) ;
            cpl_bivector_delete(positions) ;
            return NULL ;
        }
        min_dist = size_x * size_x + size_y * size_y ;
        for (iaper=0 ; iaper<cpl_apertures_get_size(aperts) ; iaper++) {
            pos_x = cpl_apertures_get_centroid_x(aperts, iaper+1) ;
            pos_y = cpl_apertures_get_centroid_y(aperts, iaper+1) ;
            dist = (pos_x-hawki_cal_zpoint_config.xcoord[idet])*
                    (pos_x-hawki_cal_zpoint_config.xcoord[idet]) +
                   (pos_y-hawki_cal_zpoint_config.ycoord[idet])*
                    (pos_y-hawki_cal_zpoint_config.ycoord[idet]);
            if (dist<min_dist) {
                min_dist = dist ;
                pos_x_cen = pos_x ;
                pos_y_cen = pos_y ;
            }
        }
        cpl_apertures_delete(aperts) ;

        cpl_vector_set(cpl_bivector_get_x(positions), iframe_star, pos_x_cen) ;
        cpl_vector_set(cpl_bivector_get_y(positions), iframe_star, pos_y_cen) ;
        cpl_msg_info(__func__, "Expected star position: %g %g",
                hawki_cal_zpoint_config.xcoord[idet],
                hawki_cal_zpoint_config.ycoord[idet]);
        cpl_msg_info(__func__, "Bright object position: %g %g",
                pos_x_cen, pos_y_cen) ;

        /* Check that the star is within the search window */
        if(fabs(pos_x_cen - hawki_cal_zpoint_config.xcoord[idet]) >
                hawki_cal_zpoint_config.sx                        ||
           fabs(pos_y_cen - hawki_cal_zpoint_config.ycoord[idet]) >
                hawki_cal_zpoint_config.sy)
        {
            hawki_cal_zpoint_outputs.stdstar_image_detected[idet] = 0;
            cpl_msg_warning(cpl_func,"No object has been found within the box"
                            "limits [%d, %d] around the expected position",
                            hawki_cal_zpoint_config.sx,
                            hawki_cal_zpoint_config.sy);
        }
        else
        {
            hawki_cal_zpoint_outputs.stdstar_image_detected[idet] = 1;
        }

        /* Free */
        cpl_msg_indent_less() ;
    }
    cpl_msg_indent_less() ;

    /* Reorder the images (frame order) */
    star_images_frame_order = cpl_imagelist_new() ;
    for (iframe=0 ; iframe< nframes; iframe++)
    {
        tmp_ima = cpl_image_duplicate
            (cpl_imagelist_get(*star_images, labels[iframe] -1 ));
        cpl_imagelist_set(star_images_frame_order, tmp_ima, iframe);
    }

    /* Compute the photometry */
    cpl_msg_info(__func__, "Compute the photometry") ;
    cpl_msg_indent_more() ;
    if ((zpoint_tables = hawki_cal_zpoint_photom
            (star_images_frame_order, positions, labels))==NULL) {
        cpl_msg_error(__func__, "Cannot reduce") ;
        cpl_bivector_delete(positions) ;
        cpl_imagelist_delete(star_images_frame_order) ;
        cpl_msg_indent_less() ;
        return NULL ;
    }


    /* Free and exit */
    cpl_msg_indent_less() ;
    cpl_bivector_delete(positions) ;
    cpl_imagelist_delete(star_images_frame_order) ;

    return zpoint_tables;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Reduce one image
  @param    ilist   the input image list
  @param    pos     the objects positions
  @param    labels  the chip numbers
  @return   the newly allocated table with the following columns:
            POSX POSY ZPOINT FLUX BGD PEAK FWHMX FWHMY
            or NULL in error case
 */
/*----------------------------------------------------------------------------*/
static cpl_table ** hawki_cal_zpoint_photom(
        cpl_imagelist       *   ilist,
        cpl_bivector        *   pos,
        int                 *   labels)
{
    cpl_table **        zpoint;
    int                 nframes;
    double              r, r1, r2;
    double              stdstar_mag;
    double              dit;
    double              extinction;
    double              pixscale;
    cpl_image       *   ima ;
    double          *   pos_x ;
    double          *   pos_y ;
    double              bgd, fl, zp, peak, fwhm_x, fwhm_y ;
    cpl_bivector    *   iqe_res ;
    int                 iframe;
    int                 idet;

    /* Test entries */
    if (ilist == NULL) return NULL ;
    if (pos == NULL) return NULL ;

    /* Initialise */
    nframes = cpl_imagelist_get_size(ilist) ;
    stdstar_mag = hawki_cal_zpoint_outputs.stdstar_mag_filter;
    dit = hawki_cal_zpoint_outputs.dit ;
    pixscale = hawki_cal_zpoint_outputs.pixscale ;

    /* Get extinction */
    switch (hawki_cal_zpoint_outputs.band) {
        case HAWKI_BAND_J:      extinction = 0.098 ; break ;
        case HAWKI_BAND_H:      extinction = 0.039 ; break ;
        case HAWKI_BAND_K:      extinction = 0.065 ; break ;
        case HAWKI_BAND_Y:      extinction = 0.00 ; break ;
        default:                extinction = 0.00 ; break ;
    }
    hawki_cal_zpoint_outputs.ext_coeff = extinction;
    cpl_msg_info(__func__,"Using tabulated extinction for band %s: %f",
                 hawki_cal_zpoint_outputs.filter, extinction);

    /* Loop on the images */
    for (iframe=0 ; iframe<nframes ; iframe++) {
        idet = labels[iframe]-1;
        if(hawki_cal_zpoint_outputs.stdstar_image_detected[idet] == 1)
        {
            /* Get the current image */
            ima = cpl_imagelist_get(ilist, iframe) ;

            /* Get the current position */
            pos_x = cpl_bivector_get_x_data(pos) ;
            pos_y = cpl_bivector_get_y_data(pos) ;

            /* FWHM_X / FWHM_Y */
            iqe_res = cpl_image_iqe
                    (ima, (int)(pos_x[iframe]-10.0), (int)(pos_y[iframe]-10.0),
                          (int)(pos_x[iframe]+10.0), (int)(pos_y[iframe]+10.0));
            if (iqe_res == NULL)
            {
                cpl_msg_debug(__func__,"Cannot compute FWHM for chip %d",
                        idet + 1);
                fwhm_x = fwhm_y = -1.0 ;
                cpl_error_reset() ;
            } else {
                fwhm_x = cpl_vector_get(cpl_bivector_get_x(iqe_res), 2) ;
                fwhm_y = cpl_vector_get(cpl_bivector_get_x(iqe_res), 3) ;
                cpl_bivector_delete(iqe_res) ;
            }

            /* Determine the radii */
            r = hawki_cal_zpoint_config.phot_star_radius ;
            if (r < 0) {
                if (fwhm_x>0 && fwhm_y>0)   r = 5*(fwhm_x+fwhm_y)/2.0 ;
                else                        r = HAWKI_PHOT_STAR_RADIUS ;
            }
            r1 = hawki_cal_zpoint_config.phot_bg_r1 ;
            r2 = hawki_cal_zpoint_config.phot_bg_r2 ;
            if (r1 < 0) r1 = r + 10.0 ;
            if (r2 < 0) r2 = r1 + 20.0 ;
            //cpl_msg_info(__func__, "Use radii for star: %g and background: %g, %g",
            //        r, r1, r2) ;

            /* Compute the photometry */
            /* Background */
            bgd = irplib_strehl_ring_background(ima, (int)(pos_x[iframe]),
               (int)(pos_y[iframe]), (int)r1, (int)r2, IRPLIB_BG_METHOD_MEDIAN);
            /* Flux */
            fl = irplib_strehl_disk_flux(ima,
                    (int)(pos_x[iframe]), (int)(pos_y[iframe]), (int)r, bgd);
            
            //cpl_msg_info(__func__, "Zero point in chip %d:   %g", labels[iframe], zp) ;
            /* Peak */
            peak = cpl_image_get_max_window(ima,
                    (int)(pos_x[iframe]-5), (int)(pos_y[iframe]-5),
                    (int)(pos_x[iframe]+5), (int)(pos_y[iframe]+5));

            /* Zero Point */
            if (hawki_cal_zpoint_outputs.stdstar_mag_available == 1)
            {
                if (fl > 0 && dit > 0)
                {
                    hawki_cal_zpoint_outputs.zpoint_computable[idet] = 1;
                    zp = stdstar_mag + 2.5 * log10(fl) - 2.5 * log10(dit);

                    hawki_cal_zpoint_outputs.zpoint[idet] = zp;
                    hawki_cal_zpoint_outputs.atx0[idet] = zp + 
                            extinction * hawki_cal_zpoint_outputs.airmass[idet];
                }
                else
                    hawki_cal_zpoint_outputs.zpoint_computable[idet] = 0;
            }
            hawki_cal_zpoint_outputs.posx[idet] = pos_x[iframe];
            hawki_cal_zpoint_outputs.posy[idet] = pos_y[iframe];
            hawki_cal_zpoint_outputs.flux[idet] = fl;
            hawki_cal_zpoint_outputs.instrmag[idet] = 2.5 * log10(fl/dit);
            hawki_cal_zpoint_outputs.peak[idet] = peak;
            hawki_cal_zpoint_outputs.bgd[idet] = bgd;
            hawki_cal_zpoint_outputs.fwhmx[idet] = fwhm_x;
            hawki_cal_zpoint_outputs.fwhmy[idet] = fwhm_y;
            if (fwhm_x > 0 && fwhm_y > 0)
                hawki_cal_zpoint_outputs.fwhm[idet] = sqrt(fwhm_x*fwhm_y);
            else
                hawki_cal_zpoint_outputs.fwhm[idet] = -1.0;
            hawki_cal_zpoint_outputs.fwhmx_as[idet] = fwhm_x * pixscale;
            hawki_cal_zpoint_outputs.fwhmy_as[idet] = fwhm_y * pixscale;
            if (fwhm_x > 0 && fwhm_y > 0)
                hawki_cal_zpoint_outputs.fwhm_as[idet] =
                        sqrt(fwhm_x*fwhm_y*pixscale*pixscale);
            else
                hawki_cal_zpoint_outputs.fwhm_as[labels[iframe]-1] = -1.0;
        }
        else
        {
            cpl_msg_warning(cpl_func,"Standard star not detected in chip %d. "
                    "No zeropoint computed.", idet + 1);
        }

    }

    /* Create the table */
    zpoint = hawki_table_new(1);
    //tab = cpl_table_new(nframes) ;
    for (idet=0 ; idet<HAWKI_NB_DETECTORS ; idet++) {
        cpl_table_new_column(zpoint[idet], 
                             HAWKI_COL_ZPOINT_CHIP, CPL_TYPE_INT);
        cpl_table_new_column(zpoint[idet], 
                             HAWKI_COL_ZPOINT_STARNAME, CPL_TYPE_STRING);
        cpl_table_new_column(zpoint[idet], 
                             HAWKI_COL_ZPOINT_POSX, CPL_TYPE_DOUBLE);
        cpl_table_set_column_unit(zpoint[idet],
                                  HAWKI_COL_ZPOINT_POSX,"pix");
        cpl_table_new_column(zpoint[idet], 
                             HAWKI_COL_ZPOINT_POSY, CPL_TYPE_DOUBLE);
        cpl_table_set_column_unit(zpoint[idet],HAWKI_COL_ZPOINT_POSY,"pix");
        cpl_table_new_column(zpoint[idet], 
                             HAWKI_COL_ZPOINT_ZPOINT, CPL_TYPE_DOUBLE);
        cpl_table_set_column_unit(zpoint[idet],HAWKI_COL_ZPOINT_ZPOINT,"mag");
        cpl_table_new_column(zpoint[idet], 
                             HAWKI_COL_ZPOINT_ATX0, CPL_TYPE_DOUBLE);
        cpl_table_set_column_unit(zpoint[idet],HAWKI_COL_ZPOINT_ATX0,"mag");
        cpl_table_new_column(zpoint[idet], 
                             HAWKI_COL_ZPOINT_AIRMASS, CPL_TYPE_DOUBLE);
        cpl_table_new_column(zpoint[idet], 
                             HAWKI_COL_ZPOINT_FLUX, CPL_TYPE_DOUBLE);
        cpl_table_set_column_unit(zpoint[idet],HAWKI_COL_ZPOINT_FLUX,"ADU");
        cpl_table_new_column(zpoint[idet], 
                             HAWKI_COL_ZPOINT_INSTRMAG, CPL_TYPE_DOUBLE);
        cpl_table_set_column_unit(zpoint[idet],
                                  HAWKI_COL_ZPOINT_INSTRMAG,"log(ADU/s)");
        cpl_table_new_column(zpoint[idet], 
                             HAWKI_COL_ZPOINT_FILTER, CPL_TYPE_STRING);
        cpl_table_new_column(zpoint[idet], 
                             HAWKI_COL_ZPOINT_PEAK, CPL_TYPE_DOUBLE);
        cpl_table_set_column_unit(zpoint[idet],HAWKI_COL_ZPOINT_PEAK,"ADU");
        cpl_table_new_column(zpoint[idet], 
                             HAWKI_COL_ZPOINT_BGD, CPL_TYPE_DOUBLE);
        cpl_table_set_column_unit(zpoint[idet],HAWKI_COL_ZPOINT_BGD,"ADU");
        cpl_table_new_column(zpoint[idet], 
                             HAWKI_COL_ZPOINT_FWHMX, CPL_TYPE_DOUBLE);
        cpl_table_set_column_unit(zpoint[idet],HAWKI_COL_ZPOINT_FWHMX,"pix");
        cpl_table_new_column(zpoint[idet], 
                             HAWKI_COL_ZPOINT_FWHMY, CPL_TYPE_DOUBLE);
        cpl_table_set_column_unit(zpoint[idet],HAWKI_COL_ZPOINT_FWHMY,"pix");
        cpl_table_new_column(zpoint[idet], 
                             HAWKI_COL_ZPOINT_FWHM, CPL_TYPE_DOUBLE);
        cpl_table_set_column_unit(zpoint[idet],HAWKI_COL_ZPOINT_FWHM,"pix");
        cpl_table_new_column(zpoint[idet], 
                             HAWKI_COL_ZPOINT_FWHMX_AS, CPL_TYPE_DOUBLE);
        cpl_table_set_column_unit(zpoint[idet],HAWKI_COL_ZPOINT_FWHMX_AS,"arcsec");
        cpl_table_new_column(zpoint[idet], 
                             HAWKI_COL_ZPOINT_FWHMY_AS, CPL_TYPE_DOUBLE);
        cpl_table_set_column_unit(zpoint[idet],HAWKI_COL_ZPOINT_FWHMY_AS,"arcsec");
        cpl_table_new_column(zpoint[idet], 
                             HAWKI_COL_ZPOINT_FWHM_AS, CPL_TYPE_DOUBLE);
        cpl_table_set_column_unit(zpoint[idet],HAWKI_COL_ZPOINT_FWHM_AS,"arcsec");
        cpl_table_new_column(zpoint[idet], 
                             HAWKI_COL_ZPOINT_STARMAG, CPL_TYPE_DOUBLE);
        cpl_table_set_column_unit(zpoint[idet],HAWKI_COL_ZPOINT_STARMAG,"mag");
        
        cpl_table_set_int(zpoint[idet], HAWKI_COL_ZPOINT_CHIP, 0, idet+1) ;
        cpl_table_set_string(zpoint[idet], HAWKI_COL_ZPOINT_FILTER, 0,
                hawki_cal_zpoint_outputs.filter);
        cpl_table_set_double(zpoint[idet], HAWKI_COL_ZPOINT_AIRMASS, 0,
                hawki_cal_zpoint_outputs.airmass[idet]) ;
        if(hawki_cal_zpoint_outputs.stdstar_incat_found== 1)
        {
            cpl_table_set_string(zpoint[idet], HAWKI_COL_ZPOINT_STARNAME, 0,
                    hawki_cal_zpoint_outputs.starname) ;
            if(hawki_cal_zpoint_outputs.stdstar_mag_available== 1)
            {
                cpl_table_set_double(zpoint[idet], HAWKI_COL_ZPOINT_STARMAG, 0,
                        hawki_cal_zpoint_outputs.stdstar_mag_filter);
            }
        }
        if(hawki_cal_zpoint_outputs.stdstar_image_detected[idet] == 1)
        {
            cpl_table_set_double(zpoint[idet], HAWKI_COL_ZPOINT_POSX, 0,
                    hawki_cal_zpoint_outputs.posx[idet]) ;
            cpl_table_set_double(zpoint[idet], HAWKI_COL_ZPOINT_POSY, 0,
                                 hawki_cal_zpoint_outputs.posy[idet]) ;
            cpl_table_set_double(zpoint[idet], HAWKI_COL_ZPOINT_FLUX, 0,
                                 hawki_cal_zpoint_outputs.flux[idet]) ;
            cpl_table_set_double(zpoint[idet], HAWKI_COL_ZPOINT_INSTRMAG, 0,
                                 hawki_cal_zpoint_outputs.instrmag[idet]) ;
            cpl_table_set_double(zpoint[idet], HAWKI_COL_ZPOINT_PEAK, 0,
                                 hawki_cal_zpoint_outputs.peak[idet]) ;
            cpl_table_set_double(zpoint[idet], HAWKI_COL_ZPOINT_BGD, 0,
                                 hawki_cal_zpoint_outputs.bgd[idet]) ;
            cpl_table_set_double(zpoint[idet], HAWKI_COL_ZPOINT_FWHMX, 0,
                                 hawki_cal_zpoint_outputs.fwhmx[idet]) ;
            cpl_table_set_double(zpoint[idet], HAWKI_COL_ZPOINT_FWHMY, 0,
                                 hawki_cal_zpoint_outputs.fwhmy[idet]) ;
            cpl_table_set_double(zpoint[idet], HAWKI_COL_ZPOINT_FWHM, 0,
                                 hawki_cal_zpoint_outputs.fwhm[idet]) ;
            cpl_table_set_double(zpoint[idet], HAWKI_COL_ZPOINT_FWHMX_AS, 0,
                                 hawki_cal_zpoint_outputs.fwhmx_as[idet]) ;
            cpl_table_set_double(zpoint[idet], HAWKI_COL_ZPOINT_FWHMY_AS, 0,
                                 hawki_cal_zpoint_outputs.fwhmy_as[idet]) ;
            cpl_table_set_double(zpoint[idet], HAWKI_COL_ZPOINT_FWHM_AS, 0,
                                 hawki_cal_zpoint_outputs.fwhm_as[idet]) ;
            if(hawki_cal_zpoint_outputs.zpoint_computable[idet] == 1)
            {
                cpl_table_set_double(zpoint[idet], HAWKI_COL_ZPOINT_ZPOINT, 0,
                        hawki_cal_zpoint_outputs.zpoint[idet]);
                cpl_table_set_double(zpoint[idet], HAWKI_COL_ZPOINT_ATX0, 0,
                        hawki_cal_zpoint_outputs.atx0[idet]);
            }
        }
    }

    /* Mean values */
    if(hawki_cal_zpoint_outputs.stdstar_mag_available == 1)
    {
        int nzpoint = 0;
        hawki_cal_zpoint_outputs.mean_zpoint = 0.0 ;
        hawki_cal_zpoint_outputs.mean_atx0 = 0.0 ;
        for (idet=0 ; idet<HAWKI_NB_DETECTORS ; idet++) {
            if(hawki_cal_zpoint_outputs.zpoint_computable[idet] == 1)
            {

                hawki_cal_zpoint_outputs.mean_zpoint +=
                        hawki_cal_zpoint_outputs.zpoint[idet] ;
                hawki_cal_zpoint_outputs.mean_atx0  +=
                        hawki_cal_zpoint_outputs.atx0[idet] ;
                nzpoint++;
            }
        }
        if(nzpoint > 0)
        {
            hawki_cal_zpoint_outputs.zpoint_mean_computable = 1;
            hawki_cal_zpoint_outputs.mean_zpoint /= nzpoint ;
            hawki_cal_zpoint_outputs.mean_atx0 /= nzpoint ;
        }
    }
    
    /* Output results */
    for (idet=0 ; idet<HAWKI_NB_DETECTORS ; idet++)
    {
        if(hawki_cal_zpoint_outputs.zpoint_computable[idet] == 1)
        {
            cpl_msg_info(__func__, "Zero point [at airmass=1] in chip %d:",
                    idet + 1);
            cpl_msg_indent_more();
            cpl_msg_info(__func__,"   ZP: %g [%g]",
                         hawki_cal_zpoint_outputs.zpoint[idet],
                         hawki_cal_zpoint_outputs.atx0[idet]);
            cpl_msg_info(__func__,"   Flux of star: %f",
                         hawki_cal_zpoint_outputs.flux[idet]);
            cpl_msg_indent_less();
        }
        else
            cpl_msg_info(__func__, "Zero point not available for chip %d",
                    idet + 1);

    }

    return zpoint;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Get the standard star magnitude
  @param    std_file    the standard stars catalog
  @param    frame       the input frame
  @param    mag         the computed magnitude
  @return   0 if ok, -1 in error case
 */
/*----------------------------------------------------------------------------*/
static int hawki_cal_zpoint_get_mag(
        const char  *       std_file,
        double              pointing_ra,
        double              pointing_dec,
        hawki_band          band)
{
    cpl_table      * catalogue;
    char           * star_name;
    char           * used_catname;
    char           * star_type;
    double           stdstar_ra;
    double           stdstar_dec;
    double           mag_filter;
    double           mag_J;
    double           mag_H;
    double           mag_K;
    double           mag_Y;
    int              mag_invalid;
    int              star_ind;

    hawki_cal_zpoint_outputs.stdstar_incat_found = 0;    
    
    /* Load the catalog */
    if ((catalogue = irplib_stdstar_load_catalog(std_file, "all")) == NULL) {
        cpl_msg_error(cpl_func,"Cannot read catalogue file %s",std_file);
        return -1;
    }

    /* Check that the columns are there */
    if (irplib_stdstar_check_columns_exist(catalogue) != CPL_ERROR_NONE) {
        cpl_msg_error(cpl_func,"Note all the columns are present"
                      " in the  catalogue file %s",std_file);
        return -1;
    }    
    
    /* Select stars within a given distance */
    if (irplib_stdstar_select_stars_dist(catalogue, 
            pointing_ra, pointing_dec, 5.0) == -1) {
        cpl_table_delete(catalogue) ;
        return 1;
    }

    /* Take the closest */
    if ((star_ind=irplib_stdstar_find_closest(catalogue, 
            pointing_ra, pointing_dec)) < 0) {
        cpl_table_delete(catalogue) ;
        return 1;
    }

    /* Retrieve the star information */
    hawki_cal_zpoint_outputs.stdstar_incat_found = 1;    
    star_name = cpl_strdup(cpl_table_get_string(catalogue,
                                            IRPLIB_STDSTAR_STAR_COL, star_ind));
    used_catname = cpl_strdup(cpl_table_get_string
                              (catalogue, IRPLIB_STDSTAR_CAT_COL, star_ind));
    star_type = cpl_strdup(cpl_table_get_string(catalogue, 
            IRPLIB_STDSTAR_TYPE_COL, star_ind));
    stdstar_ra  = cpl_table_get_double(catalogue, 
            IRPLIB_STDSTAR_RA_COL, star_ind, NULL);
    stdstar_dec = cpl_table_get_double(catalogue, 
            IRPLIB_STDSTAR_DEC_COL, star_ind, NULL);
    mag_filter = cpl_table_get_double(catalogue, hawki_std_band_name(band), 
            star_ind, &mag_invalid);
    mag_H = cpl_table_get_double(catalogue, hawki_std_band_name(HAWKI_BAND_H),
            star_ind, NULL);
    mag_J = cpl_table_get_double(catalogue, hawki_std_band_name(HAWKI_BAND_J), 
            star_ind, NULL);
    mag_K = cpl_table_get_double(catalogue, hawki_std_band_name(HAWKI_BAND_K), 
            star_ind, NULL);
    mag_Y = cpl_table_get_double(catalogue, hawki_std_band_name(HAWKI_BAND_Y), 
            star_ind, NULL);
    
    /* Store results */
    strncpy(hawki_cal_zpoint_outputs.starname, star_name, 510);
    strncpy(hawki_cal_zpoint_outputs.sptype, star_type, 510);
    strncpy(hawki_cal_zpoint_outputs.catalog, used_catname, 510);
    hawki_cal_zpoint_outputs.stdstar_ra = stdstar_ra;
    hawki_cal_zpoint_outputs.stdstar_dec = stdstar_dec;
    if(mag_invalid == 0)
    {
        hawki_cal_zpoint_outputs.stdstar_mag_available = 1;
        hawki_cal_zpoint_outputs.stdstar_mag_filter = mag_filter;
    }
    else
        hawki_cal_zpoint_outputs.stdstar_mag_available = 0;
    hawki_cal_zpoint_outputs.stdstar_mag_H = mag_H;
    hawki_cal_zpoint_outputs.stdstar_mag_J = mag_J;
    hawki_cal_zpoint_outputs.stdstar_mag_K = mag_K;
    hawki_cal_zpoint_outputs.stdstar_mag_Y = mag_Y;
    cpl_free(star_name);
    cpl_free(star_type);
    cpl_free(used_catname);
    cpl_table_delete(catalogue);
    
    return 0;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Save the recipe product on disk
  @param    tab         the table to save
  @param    images      the images where the photometry is computed
  @param    parlist     the parameter list
  @param    set         the input frame set
  @return   0 if everything is ok, -1 otherwise
 */
/*----------------------------------------------------------------------------*/
static int hawki_cal_zpoint_save
(cpl_table           **  zpoint_tables,
 int                 *   labels,
 cpl_imagelist       *   images,
 cpl_table           **  raw_zpoint_stats,
 cpl_frameset        *   zpoint_frames,
 cpl_frameset        *   calib_frames,
 const cpl_frame     *   stars_frame,
 cpl_parameterlist   *   parlist,
 cpl_frameset        *   set)
{
    cpl_propertylist    *   qcmainparams;
    cpl_propertylist    **  qcextparams;
    cpl_propertylist    *   mainheader;
    cpl_propertylist    **  extheaders;
    cpl_propertylist    **  statsqcextparams;
    cpl_propertylist    **  statsextheaders;
    cpl_frameset        *   used_frames;
    const char          *   ref_filename;
    const char          *   recipe_name = "hawki_cal_zpoint" ;
    int                     idet;
    int                     iframe;
    cpl_errorstate          error_prevstate = cpl_errorstate_get();


    /* Get the reference frame */
    ref_filename = hawki_get_extref_file(set);

    /* Create QC parameters for the zpoint table */
    qcmainparams = cpl_propertylist_new();
    qcextparams = cpl_malloc(HAWKI_NB_DETECTORS * sizeof(cpl_propertylist*)) ;
    for (idet=0 ; idet<HAWKI_NB_DETECTORS ; idet++)
        qcextparams[idet] = cpl_propertylist_new();
    /* Write QC parameters */
    hawki_cal_zpoint_compute_qc(qcmainparams, qcextparams, set);
    
    /* Create QC parameters for the stats table: Statistics of the raw images */
    statsqcextparams = cpl_malloc(HAWKI_NB_DETECTORS * sizeof(cpl_propertylist*)) ;
    for (idet=0 ; idet<HAWKI_NB_DETECTORS ; idet++)
        statsqcextparams[idet] = cpl_propertylist_new();
    hawki_image_stats_stats(raw_zpoint_stats, statsqcextparams);    
    
    /* Create the main and extension headers for the zpoint table*/
    mainheader = cpl_propertylist_new();
    extheaders = cpl_malloc(HAWKI_NB_DETECTORS * sizeof(cpl_propertylist*)) ;
    for (idet=0 ; idet<HAWKI_NB_DETECTORS ; idet++)
        extheaders[idet] = cpl_propertylist_new();
    /* Copy QC params to the headers */
    cpl_propertylist_append(mainheader, qcmainparams);
    for (idet=0 ; idet<HAWKI_NB_DETECTORS ; idet++)
        cpl_propertylist_append(extheaders[idet], qcextparams[idet]) ;
    
    /* Create the  extension headers for the stats table*/
    statsextheaders = cpl_malloc(HAWKI_NB_DETECTORS * sizeof(cpl_propertylist*)) ;
    for (idet=0 ; idet<HAWKI_NB_DETECTORS ; idet++)
        statsextheaders[idet] = cpl_propertylist_duplicate(statsqcextparams[idet]);
    
    /* Write additional keywords to the headers */
    for (idet=0 ; idet<HAWKI_NB_DETECTORS ; idet++)
    {
        cpl_propertylist    *   inputlist;
        cpl_propertylist    *   wcslist;
        int                     this_iframe = -1;
        int                     ext_nb;

        /* Propagate some keywords from input raw frame extensions */
        ext_nb=hawki_get_ext_from_detector(ref_filename, idet+1);
        inputlist = cpl_propertylist_load_regexp(ref_filename, ext_nb,
                HAWKI_HEADER_EXT_FORWARD, 0);
        cpl_propertylist_append(extheaders[idet], inputlist);
        cpl_propertylist_append(statsextheaders[idet], inputlist);
        cpl_propertylist_delete(inputlist);

        /* Propagate WCS keywords */
        for(iframe=0; iframe<cpl_frameset_get_size(zpoint_frames); iframe++)
            if(labels[iframe] == idet + 1)
                this_iframe = iframe;
        wcslist = cpl_propertylist_load_regexp
            (cpl_frame_get_filename(cpl_frameset_get_position(zpoint_frames, this_iframe)),
                                    ext_nb, HAWKI_HEADER_WCS, 0);
        cpl_propertylist_copy_property_regexp
            (extheaders[idet], wcslist, HAWKI_HEADER_WCS, 0);
        cpl_propertylist_delete(wcslist);
    }
    
    /* Write the zpoint table  */
    used_frames = cpl_frameset_duplicate(zpoint_frames);
    for(iframe = 0; iframe< cpl_frameset_get_size(calib_frames); ++iframe)
        cpl_frameset_insert(used_frames, cpl_frame_duplicate(
                cpl_frameset_get_position(calib_frames, iframe)));
    cpl_frameset_insert(used_frames, cpl_frame_duplicate(stars_frame));
    hawki_tables_save(set,
                      parlist,
                      used_frames,
                      (const cpl_table **)zpoint_tables,
                      recipe_name,
                      HAWKI_CALPRO_ZPOINT_TAB,
                      HAWKI_PROTYPE_ZPOINT_TAB,
                      mainheader,
                      (const cpl_propertylist **)extheaders,
                      "hawki_cal_zpoint.fits");
    cpl_frameset_delete(used_frames);
    
    /* Write the table with the raw zpoint objects statistics */
    used_frames = cpl_frameset_duplicate(zpoint_frames);
    hawki_tables_save(set,
                      parlist,
                      used_frames,
                      (const cpl_table **)raw_zpoint_stats,
                      recipe_name,
                      HAWKI_CALPRO_ZPOINT_STATS,
                      HAWKI_PROTYPE_ZPOINT_STATS,
                      NULL,
                      (const cpl_propertylist **)statsextheaders,
                      "hawki_cal_zpoint_stats.fits");

    /* Write the images  */
    for(iframe = 0; iframe< cpl_frameset_get_size(calib_frames); ++iframe)
        cpl_frameset_insert(used_frames, cpl_frame_duplicate(
                cpl_frameset_get_position(calib_frames, iframe)));
    hawki_imagelist_save(set,
                         parlist,
                         used_frames,
                         images,
                         recipe_name,
                         HAWKI_CALPRO_ZPOINT_IMA,
                         HAWKI_PROTYPE_ZPOINT_IMA,
                         NULL,
                         NULL,
                         "hawki_cal_zpoint_check.fits") ;
    cpl_frameset_delete(used_frames);

    /* Free */
    for (idet=0 ; idet<HAWKI_NB_DETECTORS ; idet++) {
        cpl_propertylist_delete(extheaders[idet]) ;
        cpl_propertylist_delete(qcextparams[idet]) ;
        cpl_propertylist_delete(statsqcextparams[idet]);
        cpl_propertylist_delete(statsextheaders[idet]);
    }
    cpl_propertylist_delete(mainheader);
    cpl_propertylist_delete(qcmainparams);
    cpl_free(extheaders);
    cpl_free(qcextparams);
    cpl_free(statsqcextparams);
    cpl_free(statsextheaders);

    /* Free */
    if(!cpl_errorstate_is_equal(error_prevstate))
    {
        cpl_error_reset();
        return -1;
    }
    return  0;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Compute the QC parameters
  @param    qcmainparams  The main header
  @param    qcextparams   The extensions headers
  @return   0 if everything is ok, -1 otherwise
 */
/*----------------------------------------------------------------------------*/
static int hawki_cal_zpoint_compute_qc
(cpl_propertylist *   qcmainparams, 
 cpl_propertylist **  qcextparams,
 cpl_frameset     *   set)
{
    int  idet;
    int  nframes;

    
    nframes = cpl_frameset_get_size(set) ;

    /* Check inputs */
    if (qcmainparams == NULL) return -1;
    for (idet=0 ; idet<HAWKI_NB_DETECTORS ; idet++)
        if (qcextparams[idet] == NULL) return -1;
    
    /* Write the QC params common to all extensions in the main header */
    cpl_propertylist_append_string(qcmainparams, "ESO QC FILTER OBS",
            hawki_cal_zpoint_outputs.filter);
    cpl_propertylist_set_comment(qcmainparams, "ESO QC FILTER OBS",
                                 "Observation filter");
    cpl_propertylist_append_string(qcmainparams, "ESO QC FILTER REF",
            hawki_std_band_name(hawki_cal_zpoint_outputs.band)) ;
    cpl_propertylist_set_comment(qcmainparams, "ESO QC FILTER REF",
                                 "Reference filter");
    cpl_propertylist_append_double(qcmainparams, "ESO QC AMBI RHUM AVG",
            hawki_cal_zpoint_outputs.humidity);
    cpl_propertylist_set_comment(qcmainparams, "ESO QC AMBI RHUM AVG",
                     "(percent) ambient relative humidity @ 30/2 m");
    cpl_propertylist_append_double(qcmainparams, "ESO QC AIRMASS MEAN",
            hawki_cal_zpoint_outputs.mean_airmass) ;
    cpl_propertylist_set_comment(qcmainparams, "ESO QC AIRMASS MEAN",
                                 "Average airmass");
    cpl_propertylist_append_double(qcmainparams, "ESO QC DATANCOM",
                                   nframes);
    cpl_propertylist_set_comment(qcmainparams, "ESO QC DATANCOM",
                                 "Number of files used for the reduction");
    if(hawki_cal_zpoint_outputs.stdstar_incat_found == 1)
    {
        cpl_propertylist_append_string(qcmainparams, "ESO QC STDNAME",
                hawki_cal_zpoint_outputs.starname) ;
        cpl_propertylist_set_comment(qcmainparams, "ESO QC STDNAME",
                                     "Standard star name");
        cpl_propertylist_append_string(qcmainparams, "ESO QC SPECTYPE",
                                       hawki_cal_zpoint_outputs.sptype) ;
        cpl_propertylist_set_comment(qcmainparams, "ESO QC SPECTYPE",
                                     "Standard star spectral type");
        cpl_propertylist_append_double(qcmainparams, "ESO QC STARMAG H",
                                       hawki_cal_zpoint_outputs.stdstar_mag_H) ;
        cpl_propertylist_set_comment(qcmainparams, "ESO QC STARMAG H",
                                     "Standard star magnitude in H");
        cpl_propertylist_append_double(qcmainparams, "ESO QC STARMAG J",
                                       hawki_cal_zpoint_outputs.stdstar_mag_J) ;
        cpl_propertylist_set_comment(qcmainparams, "ESO QC STARMAG J",
                                     "Standard star magnitude in J");
        cpl_propertylist_append_double(qcmainparams, "ESO QC STARMAG K",
                                       hawki_cal_zpoint_outputs.stdstar_mag_K) ;
        cpl_propertylist_set_comment(qcmainparams, "ESO QC STARMAG K",
                                     "Standard star magnitude in K");
        cpl_propertylist_append_double(qcmainparams, "ESO QC STARMAG Y",
                                       hawki_cal_zpoint_outputs.stdstar_mag_Y) ;
        cpl_propertylist_set_comment(qcmainparams, "ESO QC STARMAG Y",
                                     "Standard star magnitude in Y");
        cpl_propertylist_append_string(qcmainparams, "ESO QC CATNAME",
                                       hawki_cal_zpoint_outputs.catalog) ;
        cpl_propertylist_set_comment(qcmainparams, "ESO QC CATNAME",
                                     "Standard star catalogue name");
    }
    if(hawki_cal_zpoint_outputs.stdstar_mag_available == 1)
    {
        cpl_propertylist_append_double(qcmainparams, "ESO QC STARMAG",
                hawki_cal_zpoint_outputs.stdstar_mag_filter) ;
        cpl_propertylist_set_comment(qcmainparams, "ESO QC STARMAG",
                                 "Standard star magnitude in observed filter");
    }
    if(hawki_cal_zpoint_outputs.zpoint_mean_computable == 1)
    {
        cpl_propertylist_append_double(qcmainparams, "ESO QC ZPOINT MEAN",
                hawki_cal_zpoint_outputs.mean_zpoint) ;
        cpl_propertylist_set_comment(qcmainparams, "ESO QC ZPOINT MEAN",
                            "Mean measured zero-point for all the chips [mag]");
        cpl_propertylist_append_double(qcmainparams, "ESO QC ATX0 MEAN",
                                       hawki_cal_zpoint_outputs.mean_atx0);
        cpl_propertylist_set_comment(qcmainparams, "ESO QC ATX0 MEAN",
               "Mean extinction corrected zero-point for all the chips [mag]");
        cpl_propertylist_append_double(qcmainparams, "ESO QC ZPOINT EXT COEFF",
                                       hawki_cal_zpoint_outputs.ext_coeff);
        cpl_propertylist_set_comment(qcmainparams, "ESO QC ZPOINT EXT COEFF",
               "Extinction coefficient used in the computation of ATX0");
    }
        
    /* Write QC params that are specific of the extension */
    for (idet=0 ; idet<HAWKI_NB_DETECTORS ; idet++)
    {
        if(hawki_cal_zpoint_outputs.zpoint_computable[idet] == 1)
        {
            cpl_propertylist_append_double(qcextparams[idet], "ESO QC ZPOINT",
                    hawki_cal_zpoint_outputs.zpoint[idet]) ;
            cpl_propertylist_set_comment(qcextparams[idet], "ESO QC ZPOINT",
                                 "Measured zero-point for a given chip [mag]");
            cpl_propertylist_append_double(qcextparams[idet], "ESO QC ATX0",
                                           hawki_cal_zpoint_outputs.atx0[idet]);
            cpl_propertylist_set_comment(qcextparams[idet], "ESO QC ATX0",
                     "Extinction corrected zero-point for a given chip [mag]");
        }
        if(hawki_cal_zpoint_outputs.stdstar_image_detected[idet] == 1)
        {
            cpl_propertylist_append_double(qcextparams[idet], "ESO QC ZPOINT POSX",
                                           hawki_cal_zpoint_outputs.posx[idet]);
            cpl_propertylist_set_comment(qcextparams[idet], "ESO QC ZPOINT POSX",
                                    "X position of the standard star [pixel]");
            cpl_propertylist_append_double(qcextparams[idet], "ESO QC ZPOINT POSY",
                                           hawki_cal_zpoint_outputs.posy[idet]);
            cpl_propertylist_set_comment(qcextparams[idet], "ESO QC ZPOINT POSY",
                                    "Y position of the standard star [pixel]");
            cpl_propertylist_append_double(qcextparams[idet], "ESO QC ZPOINT FLUX",
                                        hawki_cal_zpoint_outputs.flux[idet]);
            cpl_propertylist_set_comment(qcextparams[idet], "ESO QC ZPOINT FLUX",
                    "Flux of the standard star [ADU]");
            cpl_propertylist_append_double(qcextparams[idet], "ESO QC ZPOINT PEAK",
                                           hawki_cal_zpoint_outputs.peak[idet]) ;
            cpl_propertylist_set_comment(qcextparams[idet], "ESO QC ZPOINT PEAK",
                                         "Peak of the standard star [ADU]");
            cpl_propertylist_append_double(qcextparams[idet], "ESO QC ZPOINT BGD",
                                           hawki_cal_zpoint_outputs.bgd[idet]) ;
            cpl_propertylist_set_comment(qcextparams[idet], "ESO QC ZPOINT BGD",
                                   "Background around the standard star [ADU]");
            cpl_propertylist_append_double(qcextparams[idet], "ESO QC ZPOINT FWHMX",
                                        hawki_cal_zpoint_outputs.fwhmx[idet]);
            cpl_propertylist_set_comment(qcextparams[idet], "ESO QC ZPOINT FWHMX",
                                         "X FWHM of the standard star [pixel]");
            cpl_propertylist_append_double(qcextparams[idet], "ESO QC ZPOINT FWHMY",
                                         hawki_cal_zpoint_outputs.fwhmy[idet]);
            cpl_propertylist_set_comment(qcextparams[idet], "ESO QC ZPOINT FWHMY",
                                         "Y FWHM of the standard star [pixel]");
            cpl_propertylist_append_double(qcextparams[idet], "ESO QC ZPOINT FWHM",
                                           hawki_cal_zpoint_outputs.fwhm[idet]);
            cpl_propertylist_set_comment(qcextparams[idet], "ESO QC ZPOINT FWHM",
                                         "FWHM of the standard star [pixel]");
            cpl_propertylist_append_double(qcextparams[idet], "ESO QC ZPOINT FWHMX_AS",
                                      hawki_cal_zpoint_outputs.fwhmx_as[idet]);
            cpl_propertylist_set_comment(qcextparams[idet], "ESO QC ZPOINT FWHMX_AS",
                                       "X FWHM of the standard star [arcsec]");
            cpl_propertylist_append_double(qcextparams[idet], "ESO QC ZPOINT FWHMY_AS",
                                    hawki_cal_zpoint_outputs.fwhmy_as[idet]);
            cpl_propertylist_set_comment(qcextparams[idet], "ESO QC ZPOINT FWHMY_AS",
                                      "Y FWHM of the standard star [arcsec]");
            cpl_propertylist_append_double(qcextparams[idet], "ESO QC ZPOINT FWHM_AS",
                                       hawki_cal_zpoint_outputs.fwhm_as[idet]);
            cpl_propertylist_set_comment(qcextparams[idet], "ESO QC ZPOINT FWHM_AS",
                                         "FWHM of the standard star [arcsec]");
        }
    }
    return 0;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Compute the median of the values of a key in several frames
  @param    set     the input frame set
  @return   0 if everything is ok, -1 otherwise
 */
/*----------------------------------------------------------------------------*/
static int hawki_cal_zpoint_compute_keywords(
        cpl_frameset    *   set,
        int             *   labels)
{
    int                     nframes ;
    cpl_vector          *   hum_vec ;
    cpl_frame           *   cur_frame ;
    cpl_propertylist    *   plist ;
    int                     iframe;

    /* Test inputs  */
    if (set == NULL) return -1 ;

    /* Initialize */
    nframes = cpl_frameset_get_size(set) ;
    hawki_cal_zpoint_outputs.mean_airmass   = 0.0 ;

    hum_vec = cpl_vector_new(nframes) ;

    for (iframe=0 ; iframe<nframes ; iframe++) {
        if (cpl_error_get_code()) {
            cpl_vector_delete(hum_vec) ;
            return -1 ;
        }
        cur_frame = cpl_frameset_get_position(set, iframe) ;
        plist = cpl_propertylist_load(cpl_frame_get_filename(cur_frame), 0) ;
        if (iframe==0) 
            hawki_cal_zpoint_outputs.mean_airmass += 
                hawki_pfits_get_airmass_start(plist) ; 
        if (iframe==nframes-1) 
            hawki_cal_zpoint_outputs.mean_airmass += 
                hawki_pfits_get_airmass_end(plist);
        hawki_cal_zpoint_outputs.airmass[labels[iframe] - 1] = 
            (hawki_pfits_get_airmass_start(plist) + 
             hawki_pfits_get_airmass_end(plist)) / 2.;
        cpl_vector_set(hum_vec,  iframe, hawki_pfits_get_humidity_level(plist));
        cpl_propertylist_delete(plist) ;
        if (cpl_error_get_code()) {
            cpl_vector_delete(hum_vec) ;
            cpl_error_reset() ;
            return -1 ;
        }
    }
    hawki_cal_zpoint_outputs.humidity  = cpl_vector_get_mean(hum_vec) ;
    hawki_cal_zpoint_outputs.mean_airmass /= 2 ;

    /* Free and return */
    cpl_vector_delete(hum_vec) ;
    if (cpl_error_get_code()) return -1 ;
    return 0 ;
}

static cpl_error_code hawki_cal_zpoint_get_expected_pos
(cpl_frameset * set,
 int          * labels)
{
    const char *  filename;
    int           iframe;

    for(iframe=0 ; iframe<HAWKI_NB_DETECTORS ; iframe++)
    {
        cpl_propertylist * wcs_plist;
        cpl_wcs          * wcs;
        int                idet;

        idet = labels[iframe];
        if(hawki_cal_zpoint_config.xcoord[idet - 1] == -1 ||
                hawki_cal_zpoint_config.ycoord[idet - 1] == -1)
        {
            filename = cpl_frame_get_filename
                (cpl_frameset_get_position_const(set, iframe));
            wcs_plist = cpl_propertylist_load
                (filename, hawki_get_ext_from_detector(filename, idet));
            wcs = cpl_wcs_new_from_propertylist(wcs_plist);
            cpl_propertylist_delete(wcs_plist);
            if(wcs == NULL)
            {
                cpl_msg_error(__func__, "Could not get WCS info");
                cpl_wcs_delete(wcs);
                return CPL_ERROR_ILLEGAL_INPUT;
            }
            if(irplib_wcs_radectoxy(wcs,
                                    hawki_cal_zpoint_outputs.stdstar_ra,
                                    hawki_cal_zpoint_outputs.stdstar_dec,
                                    &(hawki_cal_zpoint_config.xcoord[idet - 1]),
                                    &(hawki_cal_zpoint_config.ycoord[idet - 1]))
                    != CPL_ERROR_NONE)
            {
                cpl_msg_error(__func__,"Could not get the expected position of star");
                cpl_wcs_delete(wcs);
                return CPL_ERROR_UNSPECIFIED;
            }
            cpl_msg_info(cpl_func,
                         "Star expected position in detector %d is X=%f Y=%f",
                         idet, hawki_cal_zpoint_config.xcoord[idet - 1],
                         hawki_cal_zpoint_config.ycoord[idet - 1]);

            /* Free */
            cpl_wcs_delete(wcs);
        }
        else
        {
            cpl_msg_info(cpl_func,
                        "Using given star position in detector %d: X=%f Y=%f",
                         idet,
                         hawki_cal_zpoint_config.xcoord[idet - 1],
                         hawki_cal_zpoint_config.ycoord[idet - 1]);

        }
    }

    return 0;
}


static void hawki_cal_zpoint_output_init(void)
{
    int idet;

    hawki_cal_zpoint_outputs.starname[0] = (char)0 ;
    hawki_cal_zpoint_outputs.sptype[0] = (char)0 ;
    hawki_cal_zpoint_outputs.filter[0] = (char)0 ;
    hawki_cal_zpoint_outputs.catalog[0] = (char)0 ;
    hawki_cal_zpoint_outputs.pixscale = -1.0 ;
    hawki_cal_zpoint_outputs.dit = -1.0 ;
    hawki_cal_zpoint_outputs.humidity = -1.0 ;
    hawki_cal_zpoint_outputs.mean_airmass = -1.0 ;
    for (idet=0 ; idet<HAWKI_NB_DETECTORS ; idet++)
    {
        hawki_cal_zpoint_outputs.airmass[idet] = -1.0 ;
        hawki_cal_zpoint_outputs.zpoint[idet] = -1.0 ;
        hawki_cal_zpoint_outputs.atx0[idet] = -1.0 ;
        hawki_cal_zpoint_outputs.posx[idet] = -1.0 ;
        hawki_cal_zpoint_outputs.posy[idet] = -1.0 ;
        hawki_cal_zpoint_outputs.flux[idet] = -1.0 ;
        hawki_cal_zpoint_outputs.peak[idet] = -1.0 ;
        hawki_cal_zpoint_outputs.bgd[idet] = -1.0 ;
        hawki_cal_zpoint_outputs.fwhmx[idet] = -1.0 ;
        hawki_cal_zpoint_outputs.fwhmy[idet] = -1.0 ;
        hawki_cal_zpoint_outputs.fwhm[idet] = -1.0 ;
        hawki_cal_zpoint_outputs.fwhmx_as[idet] = -1.0 ;
        hawki_cal_zpoint_outputs.fwhmy_as[idet] = -1.0 ;
        hawki_cal_zpoint_outputs .fwhm_as[idet] = -1.0 ;
    }
}

int hawki_cal_zpoint_retrieve_input_param
(cpl_parameterlist  *  parlist)
{
    cpl_parameter   *   par ;
    const char      *   sval ;

    par = NULL ;

    /* --ra */
    par = cpl_parameterlist_find(parlist, "hawki.hawki_cal_zpoint.ra") ;
    hawki_cal_zpoint_config.target_ra = cpl_parameter_get_double(par) ;
    /* --dec */
    par = cpl_parameterlist_find(parlist, "hawki.hawki_cal_zpoint.dec") ;
    hawki_cal_zpoint_config.target_dec = cpl_parameter_get_double(par) ;
    /* --mag */
    par = cpl_parameterlist_find(parlist, "hawki.hawki_cal_zpoint.mag") ;
    hawki_cal_zpoint_config.stdstar_given_magnitude = 
            cpl_parameter_get_double(par) ;
    /* --detect_sigma */
    par = cpl_parameterlist_find(parlist,"hawki.hawki_cal_zpoint.detect_sigma");
    hawki_cal_zpoint_config.detect_sigma = cpl_parameter_get_double(par) ;
    /* --sx */
    par = cpl_parameterlist_find(parlist, "hawki.hawki_cal_zpoint.sx") ;
    hawki_cal_zpoint_config.sx = cpl_parameter_get_int(par) ;
    /* --sy */
    par = cpl_parameterlist_find(parlist, "hawki.hawki_cal_zpoint.sy") ;
    hawki_cal_zpoint_config.sy = cpl_parameter_get_int(par) ;
    /* --star_r */
    par = cpl_parameterlist_find(parlist, "hawki.hawki_cal_zpoint.star_r") ;
    hawki_cal_zpoint_config.phot_star_radius = cpl_parameter_get_double(par) ;
    /* --bg_r1 */
    par = cpl_parameterlist_find(parlist, "hawki.hawki_cal_zpoint.bg_r1") ;
    hawki_cal_zpoint_config.phot_bg_r1 = cpl_parameter_get_double(par) ;
    /* --bg_r2 */
    par = cpl_parameterlist_find(parlist, "hawki.hawki_cal_zpoint.bg_r2") ;
    hawki_cal_zpoint_config.phot_bg_r2 = cpl_parameter_get_double(par) ;
    /* --xcoord */
    par = cpl_parameterlist_find(parlist, "hawki.hawki_cal_zpoint.xcoord");
    sval = cpl_parameter_get_string(par);
    setlocale(LC_NUMERIC, "C"); // Behaviour of sscanf depends on the locale
    if (sscanf(sval, "%lf,%lf,%lf,%lf",
               hawki_cal_zpoint_config.xcoord,
               hawki_cal_zpoint_config.xcoord+1,
               hawki_cal_zpoint_config.xcoord+2,
               hawki_cal_zpoint_config.xcoord+3)!=4)
    {
        return -1;
    }
    /* --ycoord */
    par = cpl_parameterlist_find(parlist, "hawki.hawki_cal_zpoint.ycoord");
    sval = cpl_parameter_get_string(par);
    if (sscanf(sval, "%lf,%lf,%lf,%lf",
               hawki_cal_zpoint_config.ycoord,
               hawki_cal_zpoint_config.ycoord+1,
               hawki_cal_zpoint_config.ycoord+2,
               hawki_cal_zpoint_config.ycoord+3)!=4)
    {
        return -1;
    }

    return 0;
}

int hawki_cal_zpoint_check_epoch_equinox(cpl_propertylist * plist)
{
    if(hawki_pfits_get_targ_epoch(plist) != 2000. ||
       hawki_pfits_get_targ_equinox(plist) != 2000.)
    {
        cpl_msg_error(__func__,"Epoch and equinox must be 2000.");
        return -1;
    }
    else
        return 0;
}
