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

/*
 * $Author: llundin $
 * $Date: 2012/01/11 08:08:57 $
 * $Revision: 1.59 $
 * $Name: visir-3_5_1 $
 */

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

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

#include "visir_recipe.h"
#include <math.h>

/*-----------------------------------------------------------------------------
                            Defines
 -----------------------------------------------------------------------------*/

#define RECIPE_STRING "visir_img_burst"

#ifndef VISIR_IMG_BURST_SHIFT_FILE
#define VISIR_IMG_BURST_SHIFT_FILE "shift.txt"
#endif

#define VISIR_PFITS_STRING_CHOP_START "ESO TEL CHOP START"
#define VISIR_PFITS_STRING_OBS_START  "DATE-OBS"
#define VISIR_PFITS_DOUBLE_CHOP_FREQ  "ESO TEL CHOP FREQ"

#define VISIR_SECS_PER_DAY (24 * 3600)

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

#include <visir_destripe.h>
#include <irplib_wcs.h>

static cpl_error_code visir_destripe_imagelist(cpl_imagelist *, int,
                                               cpl_boolean);

static cpl_image * visir_chop_nod_burst(cpl_frameset *,
                                        const cpl_parameterlist *,
                                        int, int, int, int, int,
                                        double, double, double,
                                        cpl_boolean, cpl_boolean, cpl_boolean);
static int visir_img_burst_get_index(cpl_frame *, cpl_frame *,
                                     const cpl_matrix *,
                                     int, int, int, int, int, double, double,
                                     int, int *, int *);
static cpl_imagelist * visir_img_burst_create_chop_nod(cpl_frame *,
                                                       cpl_frame *, int, int,
                                                       int, int);
static cpl_image * visir_img_burst_create_combined(cpl_frameset *,
                                                   const cpl_parameterlist *,
                                                   cpl_imagelist *,
                                                   const cpl_matrix *, double,
                                                   cpl_boolean);
static int visir_img_burst_get_quadrant(double, double);
static cpl_image * imagelist_combine(cpl_imagelist *);
static int find_starting_index(cpl_frame *,cpl_frame * );
static cpl_image * image_1d_poly_create(const cpl_image *);
static cpl_apertures * visir_img_burst_extract(const cpl_image *, double);
static cpl_image * cpl_image_get_median_choose(const cpl_image *, int);
static cpl_bivector * visir_extract_4_sources_box(cpl_image *,
                                                  const cpl_matrix *,
                                                  double, int, cpl_boolean);

static cpl_image * image_median_conv(const cpl_image *, const cpl_matrix *,
                                     int);
static cpl_matrix * visir_img_burst_psf_create(int);

cpl_recipe_define(visir_img_burst, VISIR_BINARY_VERSION,         
                  "Lars Lundin", PACKAGE_BUGREPORT, "2007",
                  "Images burst combination recipe",
                  RECIPE_STRING " -- VISIR burst mode recipe.\n"
                  "This recipe recombines data observed in chopping "
                  "and/or nodding mode into one combined image "
                  "using optionally cross-correlation methods\n");

/*----------------------------------------------------------------------------*/
/**
 * @defgroup visir_img_burst   Burst mode image combination
 */
/*----------------------------------------------------------------------------*/

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

/*----------------------------------------------------------------------------*/
/**
  @internal
  @brief    Fill the recipe parameterlist
  @param    self  The parameterlist
  @return   0 iff everything is ok

 */
/*----------------------------------------------------------------------------*/
static
cpl_error_code visir_img_burst_fill_parameterlist(cpl_parameterlist * self)
{
    const char * context = PACKAGE "." RECIPE_STRING;
    cpl_error_code err;

    cpl_ensure_code(self, CPL_ERROR_NULL_INPUT);

    /* Fill the parameters list */

    /* --startindex */
    err = irplib_parameterlist_set_int(self, PACKAGE, RECIPE_STRING,
                                       "startindex", 1, "SI", context,
                                       "starting image where the noise level "
                                       "is ok - -1 for auto mode");
    cpl_ensure_code(!err, err);

    /*--indice_pos*/
    err = irplib_parameterlist_set_int(self, PACKAGE, RECIPE_STRING,
                                       "indice_pos", -1, "IPOS", context,
                                       "index where the pos source changes "
                                       "position in the nodded cube");
    cpl_ensure_code(!err, err);

    /*---indice_neg*/
    err = irplib_parameterlist_set_int(self, PACKAGE, RECIPE_STRING,
                                       "indice_neg", -1, "INEG", context,
                                       "index where the neg source changes "
                                       "position in the nodded cube");
    cpl_ensure_code(!err, err);

    /*left_chop:: positive position of the source in the chopping guess:
     * value of 1 if it is true(on the left),0 if it is false*/
    err = irplib_parameterlist_set_int(self, PACKAGE, RECIPE_STRING,
                                       "left_chop", -1, "left_chop", context,
                                       "tell if the source is in the left or "
                                       "right side in the chopped im");
    cpl_ensure_code(!err, err);

    /*left_nod: positive position of the source in the nodding guess:
     * value of 1 if it is true(on the left),0 if it is false*/
    err = irplib_parameterlist_set_int(self, PACKAGE, RECIPE_STRING,
                                       "left_nod", -1, "left_nod", context,
                                       "tell if the source is in the left or "
                                       "right side in the nodded im");
    cpl_ensure_code(!err, err);

    /*sigma_nod-->threshold= sigma * stdev  for the detection in the nodding
     * images*/
    err = irplib_parameterlist_set_double(self, PACKAGE, RECIPE_STRING,
                                          "sigma_nod", 4.0, "sigma_nod",context,
                                          "threshold used for the detection of "
                                          "sources in the images nodded");
    cpl_ensure_code(!err, err);

    /*sigma_chop-->threshold= sigma * stdev  for the detection in the chopping
     * first guess:lots of noise fot the faint source (sigma should be 0.7)*/
    err = irplib_parameterlist_set_double(self, PACKAGE, RECIPE_STRING,
                                          "sigma_chop", 4.0, "sigma_chop",
                                          context, "threshold used for the det."
                                          " of sources in the images chopped");
    cpl_ensure_code(!err, err);

    /*sigma_4sources-->threshold= sigma * stdev  for the detection
     * for the 4 sources in oder to have their position*/
    err = irplib_parameterlist_set_double(self, PACKAGE, RECIPE_STRING,
                                          "sigma_4sources", 4.0,
                                          "sigma_4sources", context,
                                          "threshold used for the detection of "
                                          "the position of 4 sources");
    cpl_ensure_code(!err, err);

    /*destriping correction:put the keyword if correction needed
     * non correction by default*/
    err = irplib_parameterlist_set_bool(self, PACKAGE, RECIPE_STRING,
                                        "destriping", CPL_FALSE,
                                        "destripe", context,
                                        "Flag to use the destriping");
    cpl_ensure_code(!err, err);

    /* Morphological cleaning:put the keyword if cleaning needed
     * non correction by default*/
    err = irplib_parameterlist_set_bool(self, PACKAGE, RECIPE_STRING,
                                        "morpho", CPL_FALSE,
                                        "morpho", context,
                                        "Flag to use the morphological "
                                        "cleaning in the destriping");
    cpl_ensure_code(!err, err);

    /*DEBUG */
    err = irplib_parameterlist_set_bool(self, PACKAGE, RECIPE_STRING,
                                        "debug", CPL_FALSE,
                                        "debug", context,
                                        "Flag to make a debug computation "
                                        "and save files");
    cpl_ensure_code(!err, err);

    /*PATH for the file of the psf used for convolution*/
    /* FIXME: Replace with size param ? */
    err = irplib_parameterlist_set_string(self, PACKAGE, RECIPE_STRING,
                                          "psf", "/dev/null",
                                          "psf", context,
                                          "Ignored");
    cpl_ensure_code(!err, err);

    return CPL_ERROR_NONE;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    The recipe data reduction part is implemented here 
  @param    parlist     the parameters list
  @param    framelist   the frames list
  @return   0 iff everything is ok
 */
/*----------------------------------------------------------------------------*/
static int visir_img_burst(cpl_frameset            * framelist,
                           const cpl_parameterlist * parlist)
{
    cpl_image * combined = NULL;
    int         startindex;
    int         indice_pos;
    int         indice_neg;
    int         left_chop;
    int         left_nod;
    double      sigma_chop;
    double      sigma_nod;
    double      sigma_4sources;
    cpl_boolean destripe;
    cpl_boolean morpho;
    cpl_boolean debug;

    /* Remove the file of shifts if it already exists */
    (void)remove(VISIR_IMG_BURST_SHIFT_FILE);

    bug_if(0);

    startindex = irplib_parameterlist_get_int(parlist, PACKAGE, RECIPE_STRING,
                                              "startindex");

    indice_pos = irplib_parameterlist_get_int(parlist, PACKAGE, RECIPE_STRING,
                                              "indice_pos");

    indice_neg = irplib_parameterlist_get_int(parlist, PACKAGE, RECIPE_STRING,
                                              "indice_neg");

    left_chop = irplib_parameterlist_get_int(parlist, PACKAGE, RECIPE_STRING,
                                             "left_chop");

    left_nod = irplib_parameterlist_get_int(parlist, PACKAGE, RECIPE_STRING,
                                             "left_nod");

    sigma_nod = irplib_parameterlist_get_double(parlist, PACKAGE, RECIPE_STRING,
                                                "sigma_nod");

    sigma_chop = irplib_parameterlist_get_double(parlist, PACKAGE,
                                                 RECIPE_STRING, "sigma_chop");

    sigma_4sources = irplib_parameterlist_get_double(parlist, PACKAGE,
                                                     RECIPE_STRING,
                                                     "sigma_4sources");

    destripe = irplib_parameterlist_get_bool(parlist, PACKAGE, RECIPE_STRING,
                                             "destriping");

    morpho = irplib_parameterlist_get_bool(parlist, PACKAGE, RECIPE_STRING,
                                           "morpho");

    debug = irplib_parameterlist_get_bool(parlist, PACKAGE, RECIPE_STRING,
                                           "debug");

    bug_if(0);

    combined = visir_chop_nod_burst(framelist, parlist, startindex, indice_pos,
                                    indice_neg, left_chop, left_nod, sigma_nod,
                                    sigma_chop, sigma_4sources, destripe,
                                    morpho, debug);
    skip_if (combined == NULL);

    /* Create the product */
    skip_if(irplib_dfs_save_image(framelist, parlist, framelist, combined,
                                  CPL_BPP_IEEE_FLOAT, RECIPE_STRING,
                                  "IMG_BURST_COMBINED", NULL, NULL,
                                  visir_pipe_id,
                                  RECIPE_STRING "_combined" CPL_DFS_FITS));

    end_skip;

    cpl_image_delete(combined);

    return cpl_error_get_code();
}

/*----------------------------------------------------------------------------*/
/**
  @internal
  @brief    Main function for the burst reduction
  @param    starindex   the first image where the noise is not too high
                        respect to the average noise of the cube nodded
  @param    indice_pos  position of the first image in the cube nodded where
                        the positive source is changing its position
  @param    indice_neg  position of the first image in the cube nodded where
                        the negative source is changing its position
  @param    left_chop   positive position of the source in the chopping guess:
                        value of 1 if it is true(on the left),0 if it is false
  @param    left_nod    positive position of the source in the nodding guess:
                        value of 1 if it is true(on the left),0 if it is false
  @param    sigma_nod   threshold= sigma * stdev  for the detection in the
                        nodding images
  @param    sigma_chop  threshold= sigma * stdev  for the detection in the
                        chopping images :noise is quite high
  @param    sigma_4sources  threshold= sigma * stdev  for the detection in a
                            nodding image of the position of the 4 sources.
  @param    destripe    flag to destripe
  @param    morpho      flag to make the morphological cleaning for the
                        destriping correction
  @param    debug       flag to save more files to debug the program.by default
                        just 3 products re saved.
  @return   a combined image

  The function does the following:
    takes 2 files in entry(nodding A- nodding B) select in the cube
        of nodded images where the noise is ok
    detect the position in the cube of each file of the first chopping
        changement
    make a chopping difference in each file
    make a cube chopped and nodded
    detect the 4 sources in this cube by quadrants
    make shift and add on 4 cubes built with the 4 sources in each image of
        the cube chopped and nodded
    make a shift and add final with 4 images of the 4 sources summed on the cube
 */
/*----------------------------------------------------------------------------*/
static cpl_image *
visir_chop_nod_burst(cpl_frameset            * framelist,
                     const cpl_parameterlist * parlist,
                     int                       startindex,
                     int                       indice_pos,
                     int                       indice_neg,
                     int                       left_chop,
                     int                       left_nod,
                     double                    sigma_nod,
                     double                    sigma_chop,
                     double                    sigma_4sources,
                     cpl_boolean               destripe,
                     cpl_boolean               morpho,
                     cpl_boolean               debug)
{
    /*
  @param    frame0      nodding position A
  @param    frame1      nodding position B
    */
    cpl_frame * frame0;
    cpl_frame * frame1;

    cpl_propertylist * plist = NULL;
    double             chop_freq, dit;
    const char       * filename;
    const char       * sval;
    cpl_matrix       * kernel = NULL;
    int                periode, nima;
    int                index_file1, index_file2;
    int                startindex_loc;
    cpl_imagelist    * cube_chop_nod = NULL;
    cpl_image        * final = NULL;


    bug_if(0);

    frame0 = cpl_frameset_get_frame(framelist, 0);
    frame1 = cpl_frameset_get_frame(framelist, 1);

    skip_if(0);

    cpl_frame_set_group(frame0, CPL_FRAME_GROUP_RAW);
    cpl_frame_set_group(frame1, CPL_FRAME_GROUP_RAW);

    bug_if(0);

    /* get the chopping frequency and the DIT of one image in burst mode
        to know how many images correspond to one position chopping */
    filename = cpl_frame_get_filename(frame1);
    skip_if(filename == NULL);
    plist = cpl_propertylist_load(filename, 0);
    irplib_ensure(plist != NULL, cpl_error_get_code(), "Could not load the "
                  "property list from %s", filename);
    dit       = visir_pfits_get_dit(plist);
    sval      = visir_pfits_get_filter(plist);
    chop_freq = cpl_propertylist_get_double(plist,
                                            VISIR_PFITS_DOUBLE_CHOP_FREQ);
    nima      = visir_pfits_get_naxis3(plist);
    skip_if(0);
    cpl_msg_info(cpl_func, "Burst integration time = %g s.", dit);
    cpl_msg_info(cpl_func, "chopping frequency = %g Hz", chop_freq);
    cpl_msg_info(cpl_func, "Filter = %s", sval);
    cpl_msg_info(cpl_func, "RA  = %g", visir_pfits_get_ra(plist));
    sval = cpl_propertylist_get_comment(plist, "RA");
    skip_if(0);
    cpl_msg_info(cpl_func, "RA  = %s", sval);
    cpl_msg_info(cpl_func, "DEC = %g", visir_pfits_get_dec(plist));
    sval = cpl_propertylist_get_comment(plist, "DEC");
    skip_if(0);
    cpl_msg_info(cpl_func, "DEC = %s", sval);
    cpl_msg_info(cpl_func, "AIRMASS = %g",
            cpl_propertylist_get_double(plist, "ESO TEL AIRM START"));
    cpl_msg_info(cpl_func, "SEEING START = %g",
            cpl_propertylist_get_double(plist, "ESO TEL AMBI FWHM START"));
    cpl_msg_info(cpl_func, "SEEING END = %g",
            cpl_propertylist_get_double(plist, "ESO TEL AMBI FWHM END"));
    cpl_msg_info(cpl_func, "HUMIDITY = %g",
            cpl_propertylist_get_double(plist, "ESO TEL AMBI RHUM"));
    cpl_msg_info(cpl_func, "Average Coherence time = %g",
            cpl_propertylist_get_double(plist, "ESO TEL AMBI TAU0"));
    cpl_msg_info(cpl_func, "Wind speed = %g",
            cpl_propertylist_get_double(plist, "ESO TEL AMBI WINDSP"));
    cpl_msg_info(cpl_func,"RA DEC = %g",
            cpl_propertylist_get_double(plist, "ESO ADA GUID RA"));
    sval = cpl_propertylist_get_comment(plist, "ESO ADA GUID RA");
    skip_if(0);
    cpl_msg_info(cpl_func,"RA GUID = %s", sval);
    cpl_msg_info(cpl_func,"DEC GUID = %g",
            cpl_propertylist_get_double(plist, "ESO ADA GUID DEC"));
    sval = cpl_propertylist_get_comment(plist, "ESO ADA GUID DEC");
    skip_if(0);
    cpl_msg_info(cpl_func,"DEC GUID = %s", sval);

    cpl_propertylist_delete(plist);
    plist = NULL;

    /*number of frames in one position chopping*/
    periode=(((1/chop_freq)/dit)/2)+0.5;
    cpl_msg_info(cpl_func, "Periode = %d", periode);

    /*define kernel as a gaussian 2d with sigma=1.*/
    kernel =  visir_img_burst_psf_create(9);

    cpl_ensure(kernel != NULL, cpl_error_get_code(), NULL);

    /* Make a statistic on the cube to measure the noise and
     * skip the first startindex file where the noise is too high
     * -> transient effect of the detector*/
    if (startindex == -1)   startindex_loc=find_starting_index(frame0, frame1);
    else                    startindex_loc=startindex;
    cpl_msg_info(cpl_func, "Start index = %d", startindex_loc);

    /* Get the index in the files */
    cpl_msg_info(cpl_func, "Get the index");
    if (visir_img_burst_get_index(frame0, frame1, kernel, periode,
                startindex_loc, indice_pos, indice_neg, left_nod, sigma_nod,
                sigma_chop, left_chop, &index_file1, &index_file2) == -1)
    {
        cpl_msg_error(cpl_func, "Cannot get the index");
        cpl_matrix_delete(kernel);
        return NULL;
    }
    cpl_msg_info(cpl_func,"Index 1st file = %d", index_file1);
    cpl_msg_info(cpl_func,"Index 2nd file = %d", index_file2);

    /* Create cube_chop_nod */
    cpl_msg_info(cpl_func, "Create cube_chop_nod");
    if ((cube_chop_nod = visir_img_burst_create_chop_nod(frame0, frame1, nima,
                    index_file1, index_file2, periode)) == NULL)
    {
        cpl_msg_error(cpl_func, "Cannot create cube_chop_nod");
        cpl_matrix_delete(kernel);
        return NULL;
    }

    if (debug)
    {
        cpl_imagelist_save(cube_chop_nod,
                           RECIPE_STRING "_cube_chop_nod" CPL_DFS_FITS,
                           CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_DEFAULT);
    }
    /* Destripe if requested */
    if (destripe)
    {
        if (visir_destripe_imagelist(cube_chop_nod, 15, morpho)) {
            cpl_msg_error(cpl_func, "Could not destripe");
            cpl_matrix_delete(kernel);
            cpl_imagelist_delete(cube_chop_nod);
            return NULL;
        }
        cpl_imagelist_save(cube_chop_nod,
                           RECIPE_STRING "_cube_chop_nod_destriped" CPL_DFS_FITS,
                           CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_DEFAULT);

    }

    /* Create the final image */
    cpl_msg_info(cpl_func, "Creating the final image");
    final = visir_img_burst_create_combined(framelist, parlist,
                                            cube_chop_nod, kernel,
                                            sigma_4sources, debug);

    if (final == NULL)
        cpl_msg_error(cpl_func, "Could not create the final image");

    end_skip;

    cpl_imagelist_delete(cube_chop_nod);
    cpl_matrix_delete(kernel);
    cpl_propertylist_delete(plist);

    return final;
}


/*
 * Take the cube of images of each file .fits of the framelist.
 * Subtract the 2 cubes (nod A - nod B) image by image.
 * It is not necessary to take all the 1000 images in account: need
 * to know the start point, when the 2 sources(pos and neg)
 * are moving.(need quantity of images less than the period) */
static int visir_img_burst_get_index(cpl_frame        * frame0,
                                     cpl_frame        * frame1,
                                     const cpl_matrix * kernel,
                                     int             periode,
                                     int             startindex,
                                     int             indice_pos,
                                     int             indice_neg,
                                     int             left_nod,
                                     double          sigma_nod,
                                     double          sigma_chop,
                                     int             left_chop,
                                     int         *   index_file1,
                                     int         *   index_file2)
{
    int                 *   quadrant_pos;
    int                 *   quadrant_neg;
    double              *   position_x_pos;
    double              *   position_x_neg;
    cpl_image           *   image;
    cpl_image           *   image_poly;
    cpl_image           *   tmp_image;
    cpl_image           *   image_conv;
    cpl_apertures       *   aperts;
    char                    position_pos;
    char                    position00 = '?';
    int                     quadrant00;
    int                     indice_pos_loc, indice_neg_loc;
    int                     i;

    /* Initialise */
    indice_pos_loc = indice_pos;
    indice_neg_loc = indice_neg;

    /* Allocate the quadrant array */
    quadrant_pos=cpl_malloc((periode+1)*(sizeof(int)));
    quadrant_neg=cpl_malloc((periode+1)*(sizeof(int)));
    position_x_pos=cpl_malloc((periode+1)*(sizeof(double)));
    position_x_neg=cpl_malloc((periode+1)*(sizeof(double)));

    if (indice_pos_loc == -1 || indice_neg_loc == -1 || left_nod == -1 )
    {
        for (i=startindex; i<startindex+periode+1; i++)
        {
            image = cpl_image_load(cpl_frame_get_filename(frame0),
                    CPL_TYPE_FLOAT,i,0);
            tmp_image = cpl_image_load(cpl_frame_get_filename(frame1),
                    CPL_TYPE_FLOAT,i,0);
            cpl_image_subtract(image, tmp_image);
            cpl_image_delete(tmp_image);
            if (image == NULL)
            {
                cpl_msg_error(cpl_func, "Cannot subtract the images");
                cpl_free(quadrant_pos);
                cpl_free(quadrant_neg);
                cpl_free(position_x_pos);
                cpl_free(position_x_neg);
                return -1;
            }

            /* Subtract a poly_1d fit of the image itself */
            if ((tmp_image = image_1d_poly_create(image)) == NULL)
            {
                cpl_free(quadrant_pos);
                cpl_free(quadrant_neg);
                cpl_free(position_x_pos);
                cpl_free(position_x_neg);
                cpl_image_delete(image);
                return -1;
            }
            cpl_image_subtract(image, tmp_image);
            cpl_image_delete(tmp_image);

            /* Make a median (5,5) and a convolution by gaussian 9x9 */
            if ((image_conv = image_median_conv(image, kernel, 3)) == NULL)
            {
                cpl_msg_error(cpl_func, "Cannot do the convolution");
                cpl_free(quadrant_pos);
                cpl_free(quadrant_neg);
                cpl_free(position_x_pos);
                cpl_free(position_x_neg);
                cpl_image_delete(image);
                return -1;
            }
            cpl_image_delete(image);

            /* Detection of positiv source and put the localisation in a
             * quandrant number */
            if (indice_pos_loc == -1 || left_nod == -1)
            {
                aperts = visir_img_burst_extract(image_conv, sigma_nod);
                if (aperts == NULL)
                {
                    cpl_msg_info(cpl_func, "Cannot detect positive source: i=%d",
                            i);
                    /*it means the chopper is changing position*/
                    quadrant_pos[i-startindex]=0;
                }
                else
                {
                    position_x_pos[i-startindex] =
                        cpl_apertures_get_centroid_x(aperts, 1);
                    quadrant_pos[i-startindex] =
                        visir_img_burst_get_quadrant(
                                cpl_apertures_get_centroid_x(aperts, 1),
                                cpl_apertures_get_centroid_y(aperts, 1));
                }
                cpl_apertures_delete(aperts);

                if (i-startindex != 0)
                {
                    if (((quadrant_pos[i-startindex] !=
                                    quadrant_pos[i-startindex-1]) &&
                                (fabs(position_x_pos[i-startindex]-
                                      position_x_pos[i-startindex-1]) < 10)) ||
                                (quadrant_pos[i-startindex-1] == 0) )
                    {
                        indice_pos_loc = i;
                    }
                }
            }

            /* detection of negativ source and put the localisation in a
             * quandrant number to detect the postion in order to make the
             * chopping substraction */
            if (indice_neg_loc == -1)
            {
                cpl_image_multiply_scalar(image_conv, -1);
                aperts = visir_img_burst_extract(image_conv, sigma_nod);
                cpl_image_multiply_scalar(image_conv, -1);
                if(aperts == NULL)
                {
                    cpl_msg_info(cpl_func, "Cannot detect negative source: i=%d",
                            i);
                    /*it means the chopper is changing position*/
                    quadrant_neg[i-startindex]=0;
                }
                else
                {
                    position_x_neg[i-startindex]=
                        cpl_apertures_get_centroid_x(aperts, 1);
                    quadrant_neg[i-startindex] =
                        visir_img_burst_get_quadrant(
                                cpl_apertures_get_centroid_x(aperts, 1),
                                cpl_apertures_get_centroid_y(aperts, 1));
                }
                cpl_apertures_delete(aperts);

                if (i-startindex != 0)
                {
                    if (((quadrant_neg[i-startindex] !=
                                    quadrant_neg[i-startindex-1]) &&
                                (fabs(position_x_neg[i-startindex]-
                                      position_x_neg[i-startindex-1]) < 10)) ||
                            (quadrant_neg[i-startindex-1] == 0))
                    {
                        indice_neg_loc = i;
                    }
                }
            }
            cpl_image_delete(image_conv);
        }
    }
    cpl_msg_info(cpl_func, "Index pos = %d ", indice_pos_loc);
    cpl_msg_info(cpl_func, "Index neg = %d ", indice_neg_loc);

    /*determination of the side of the positive source in the nodding guess*/
    if (quadrant_pos[0]==1 || quadrant_pos[0]==3)   position_pos='l';
    else                                            position_pos='r';
    if(left_nod == 0)                               position_pos='r';
    if(left_nod == 1)                               position_pos='l';

    cpl_msg_info(cpl_func, "Side of the positive source in nodding -> %c",
            position_pos);

    cpl_free(position_x_pos);
    cpl_free(position_x_neg);
    cpl_free(quadrant_neg);
    cpl_free(quadrant_pos);

    cpl_msg_info(cpl_func, "left_chop = %d", left_chop);
    if (left_chop == -1)
    {
        /* first guess: detection of the sources on the first chopping in the
         * first nodding position (first file) in order to know which file
         * corresponds to the source positiv or negativ; to know if the first
         * file corresponds to the left size or to the right size of the image
         */
        image=cpl_image_load(cpl_frame_get_filename(frame0),CPL_TYPE_FLOAT,
               startindex,0);
        tmp_image=cpl_image_load(cpl_frame_get_filename(frame0),
               CPL_TYPE_FLOAT,startindex+periode,0);
        cpl_image_subtract(image, tmp_image);
        cpl_image_delete(tmp_image);
        if(image == NULL)
        {
            cpl_msg_info(cpl_func,"Cannot subtract the chopping guess");
            return -1;
        }
        image_poly = image_1d_poly_create(image);
        cpl_image_subtract(image,image_poly);
        cpl_image_delete(image_poly);

        image_conv = image_median_conv(image, kernel, 5);
        cpl_image_delete(image);
        /* make the substraction of the file startindex minus
         * the file 'startindex+periode':
         * it is sure to have a position 1-position 2 in the chopping position*/
        aperts = visir_img_burst_extract(image_conv, sigma_chop);
        cpl_image_delete(image_conv);
        if(aperts == NULL)
        {
            cpl_msg_error(cpl_func,
                    "Cannot detect object in the 1st chopping guess");
            cpl_msg_error(cpl_func, "---> reduce sigma_chop");
            return -1;
        }
        else
        {
            quadrant00 = visir_img_burst_get_quadrant(
                    cpl_apertures_get_centroid_x(aperts,1),
                    cpl_apertures_get_centroid_y(aperts,1));
            cpl_apertures_delete(aperts);
        }
        if (quadrant00 == 1 || quadrant00 ==3) {position00='l';}
        if (quadrant00 == 2 || quadrant00 ==4) {position00='r';}
        cpl_msg_info(cpl_func, "Side of the positive source in chopping -> %c",
                position00);
        cpl_msg_info(cpl_func, "Quadrant of the positive source in chopping = %d",
                quadrant00);
    }

    /*determination of the side of the positive source in the chopping guess*/
    if (left_chop == 1) {position00='l';}
    if (left_chop == 0) {position00='r';}
    cpl_msg_info(cpl_func, "Side of the positive source in chopping -> %c",
            position00);

    /*determination if the first file correspond to the left or
     * right of the image*/
    if (position_pos == position00)
    {
        *index_file1 = indice_pos_loc;
        *index_file2 = indice_neg_loc;
    }
    else
    {
        *index_file1 = indice_neg_loc;
        *index_file2 = indice_pos_loc;
    }
    return 0;
}

static cpl_imagelist * visir_img_burst_create_chop_nod(
        cpl_frame   *   frame0,
        cpl_frame   *   frame1,
        int             nima,
        int             index_file1,
        int             index_file2,
        int             periode)
{
    cpl_imagelist       *   cube_chop_pos;
    cpl_imagelist       *   cube_chop_neg;
    int                     max_index;
    int                     nchop_cycles;
    cpl_image           *   image1;
    cpl_image           *   image2;
    int                     i, k;

    /* Reload the raw image to make the chopping substraction in a fits file*/
    cube_chop_pos=cpl_imagelist_new();
    cube_chop_neg=cpl_imagelist_new();

   /* Calculate the maximum number of chopping cycle taking into account
    * the starting file*/
   if (index_file1 > index_file2)   max_index = index_file1;
   else                             max_index = index_file2;

   nchop_cycles = (nima-max_index) / (periode*2+1);
   cpl_msg_info(cpl_func, "Number of chopping cycles = %d", nchop_cycles);

   /*make the chopping subtraction in each fits file*/
   for (k=0; k<nchop_cycles; k++)
    {
        cpl_msg_info(cpl_func, "Chopping cycle number %d", k+1);
        /* for the first file */
        for (i=index_file1+1+(2*periode*k);
                i <index_file1+1+(2*periode*k)+periode; i++)
            /*with the +1,the first file where the chopper stabilizes
             * is taken away*/
        {
            image1 = cpl_image_load(cpl_frame_get_filename(frame0),
                    CPL_TYPE_FLOAT,i+periode,0);
            image2 = cpl_image_load(cpl_frame_get_filename(frame0),
                    CPL_TYPE_FLOAT,i,0);
            cpl_image_subtract(image1,image2);
            cpl_image_delete(image2);
            nima = cpl_imagelist_get_size(cube_chop_pos);
            cpl_imagelist_set(cube_chop_pos, image1, nima);
        }

        /*for the second file*/
        for(i=index_file2+1+(2*periode*k);
                i <index_file2+1+(2*periode*k)+periode; i++)
        {
            image1=cpl_image_load(cpl_frame_get_filename(frame1),
                    CPL_TYPE_FLOAT,i+periode,0);
            image2=cpl_image_load(cpl_frame_get_filename(frame1),
                    CPL_TYPE_FLOAT,i,0);
            cpl_image_subtract(image1,image2);
            cpl_image_delete(image2);
            nima = cpl_imagelist_get_size(cube_chop_neg);
            cpl_imagelist_set(cube_chop_neg,image1, nima);
        }
    }

    /*substraction to have the image chopped and nodded*/
    if (cpl_imagelist_subtract(cube_chop_pos,cube_chop_neg) != CPL_ERROR_NONE)
    {
        cpl_msg_error(cpl_func, "Cannot subtract image lists");
        cpl_imagelist_delete(cube_chop_neg);
        cpl_imagelist_delete(cube_chop_pos);
        return NULL;
    }
    cpl_imagelist_delete(cube_chop_neg);
    return cube_chop_pos;
}


/*---------------- method to sum all the sources-----------------------
 * detect the position of the 4 sources in an image and
 * take a box around them that we keep to take the sources in the
 * other images.
 * we make 4 shift and add with 4 quadrants in each image*/
static cpl_image *
visir_img_burst_create_combined(cpl_frameset            * framelist,
                                const cpl_parameterlist * parlist,
                                cpl_imagelist           * cube_chop_nod,
                                const cpl_matrix        * kernel,
                                double                    sigma_4sources,
                                cpl_boolean               debug)
{
    cpl_image           *   image;
    cpl_bivector        *   positions;
    int                     size;
    double                  x1, x2, x3, x4, y_1, y2, y3, y4;
    cpl_image           *   image_pos1,
                        *   image_pos2,
                        *   image_neg1,
                        *   image_neg2;
    cpl_imagelist       *   cube1,
                        *   cube2,
                        *   cube3,
                        *   cube4;
    cpl_image           *   combined_1,
                        *   combined_2,
                        *   combined_3,
                        *   combined_4;
    cpl_imagelist       *   cube_final;
    cpl_vector          *   taille_x,
                        *   taille_y;
    double                  min_x, min_y;
    cpl_image           *   combined = NULL;
    int                     j;


    /*take the image in the middle of the cube to be sure to have no noise*/
    image=cpl_imagelist_get(cube_chop_nod,cpl_imagelist_get_size
            (cube_chop_nod)/2);
    if (image == NULL)
    {
        cpl_msg_error(cpl_func, "Cannot get the first chopping guess");
        return NULL;
    }

    /*detect the position of the 4 sources in the first image*/
    /*the number after the kernel is the threshold for detection*/
    /*the last number is the size box of the median*/
    positions = visir_extract_4_sources_box(image, kernel, sigma_4sources, 3,
                                            debug);
    if (positions == NULL)
    {
        image=cpl_imagelist_get(cube_chop_nod,cpl_imagelist_get_size
                    (cube_chop_nod)/3);
        positions = visir_extract_4_sources_box(image, kernel, sigma_4sources,
                                                3, debug);
    }
    if (positions == NULL)
    {
        cpl_msg_info(cpl_func, "Cannot detect -> reduce sigma_4sources");
        return NULL;
    }

    /* The box is a square */
    size = cpl_vector_get(cpl_bivector_get_x(positions),4);
    /* because in the extraction function,we start with (1,1) */
    size--;

    /* Get the coordinates of the 4 sources on the image */
    x1=cpl_vector_get(cpl_bivector_get_x(positions),0);
    x2=cpl_vector_get(cpl_bivector_get_x(positions),1);
    x3=cpl_vector_get(cpl_bivector_get_x(positions),2);
    x4=cpl_vector_get(cpl_bivector_get_x(positions),3);

    y_1=cpl_vector_get(cpl_bivector_get_y(positions),0);
    y2=cpl_vector_get(cpl_bivector_get_y(positions),1);
    y3=cpl_vector_get(cpl_bivector_get_y(positions),2);
    y4=cpl_vector_get(cpl_bivector_get_y(positions),3);
    cpl_bivector_delete(positions);

    cpl_msg_info(cpl_func,"Source 1 position : (%g, %g)", x1, y_1);
    cpl_msg_info(cpl_func,"Source 2 position : (%g, %g)", x2, y2);
    cpl_msg_info(cpl_func,"Source 3 position : (%g, %g)", x3, y3);
    cpl_msg_info(cpl_func,"Source 4 position : (%g, %g)", x4, y4);

    cube1=cpl_imagelist_new();
    cube2=cpl_imagelist_new();
    cube3=cpl_imagelist_new();
    cube4=cpl_imagelist_new();
    cpl_msg_info(cpl_func,"Extract the 4 sources in the chopped/nodded cube");
    for (j=0; j<cpl_imagelist_get_size(cube_chop_nod);j++)
    {
        image=cpl_imagelist_get(cube_chop_nod,j);
        /* made 4 box around each of the 4 sources*/
        if (x1 < 500 && y_1 < 500)
        {image_pos1=cpl_image_extract(image,x1-size,y_1-size,x1+size,y_1+size);}
        else {image_pos1=cpl_image_extract(image,1,1,2*size+1,2*size+1); }
        if (x2 < 500 && y2 < 500)
        {image_pos2=cpl_image_extract(image,x2-size,y2-size,x2+size,y2+size);}
        else {image_pos2=cpl_image_extract(image,1,1,2*size+1,2*size+1);}
        if(x3 < 500 && y3 < 500)
        {image_neg1=cpl_image_extract(image,x3-size,y3-size,x3+size,y3+size);}
        else {image_neg1=cpl_image_extract(image,1,1,2*size+1,2*size+1);}
        if(x4 < 500 && y4 < 500)
        {image_neg2=cpl_image_extract(image,x4-size,y4-size,x4+size,y4+size);}
        else {image_neg2=cpl_image_extract(image,1,1,2*size+1,2*size+1);}

        cpl_imagelist_set(cube1,image_pos1,j);
        cpl_imagelist_set(cube2,image_pos2,j);
        cpl_image_multiply_scalar(image_neg1,-1);
        cpl_image_multiply_scalar(image_neg2,-1);
        cpl_imagelist_set(cube3,image_neg1,j);
        cpl_imagelist_set(cube4,image_neg2,j);
    }

    if (cube1 == NULL || cube2 == NULL || cube3 == NULL || cube4 == NULL)
    {
        cpl_msg_error(cpl_func, "Cannot build the 4 cubes");
        if (cube1) cpl_imagelist_delete(cube1);
        if (cube2) cpl_imagelist_delete(cube2);
        if (cube3) cpl_imagelist_delete(cube3);
        if (cube4) cpl_imagelist_delete(cube4);
        return NULL;
    }

    /*combine the 4 cubes of the 4 quadrants of the cube_chop_nod*/
    combined_1=imagelist_combine(cube1);
    combined_2=imagelist_combine(cube2);
    combined_3=imagelist_combine(cube3);
    combined_4=imagelist_combine(cube4);

    cpl_imagelist_delete(cube1);
    cpl_imagelist_delete(cube2);
    cpl_imagelist_delete(cube3);
    cpl_imagelist_delete(cube4);

    if ((combined_1 == NULL) ||  (combined_2 == NULL) ||  (combined_3 == NULL)
            || (combined_4== NULL))
    {
        cpl_msg_error(cpl_func, "Cannot shift and add the 4 cubes");
        if (combined_1) cpl_image_delete(combined_1);
        if (combined_2) cpl_image_delete(combined_2);
        if (combined_3) cpl_image_delete(combined_3);
        if (combined_4) cpl_image_delete(combined_4);
        return NULL;
    }

    cube_final=cpl_imagelist_new();
    /*find the smallest box to make the shift and add on the 4 final images*/
    taille_x=cpl_vector_new(4);
    taille_y=cpl_vector_new(4);

    cpl_vector_set(taille_x,0,cpl_image_get_size_x(combined_1));
    cpl_vector_set(taille_x,1,cpl_image_get_size_x(combined_2));
    cpl_vector_set(taille_x,2,cpl_image_get_size_x(combined_3));
    cpl_vector_set(taille_x,3,cpl_image_get_size_x(combined_4));

    cpl_vector_set(taille_y,0,cpl_image_get_size_y(combined_1));
    cpl_vector_set(taille_y,1,cpl_image_get_size_y(combined_2));
    cpl_vector_set(taille_y,2,cpl_image_get_size_y(combined_3));
    cpl_vector_set(taille_y,3,cpl_image_get_size_y(combined_4));

    min_x=cpl_vector_get_min(taille_x);
    min_y=cpl_vector_get_min(taille_y);
    /*put all the image at the same size,the minimum one */
    cpl_imagelist_set(cube_final,cpl_image_extract(combined_1,
                cpl_vector_get(taille_x,0)/2-(min_x/2)+1,
                cpl_vector_get(taille_y,0)/2-(min_y/2)+1,
                cpl_vector_get(taille_x,0)/2+(min_x/2),
                cpl_vector_get(taille_y,0)/2+(min_y/2)),0);
    cpl_imagelist_set(cube_final,cpl_image_extract(combined_2,
                cpl_vector_get(taille_x,1)/2-(min_x/2)+1,
                cpl_vector_get(taille_y,1)/2-(min_y/2)+1,
                cpl_vector_get(taille_x,1)/2+(min_x/2),
                cpl_vector_get(taille_y,1)/2+(min_y/2)),1);
    cpl_imagelist_set(cube_final,cpl_image_extract(combined_3,
                cpl_vector_get(taille_x,2)/2-(min_x/2)+1,
                cpl_vector_get(taille_y,2)/2-(min_y/2)+1,
                cpl_vector_get(taille_x,2)/2+(min_x/2),
                cpl_vector_get(taille_y,2)/2+(min_y/2)),2);
    cpl_imagelist_set(cube_final,cpl_image_extract(combined_4,
                cpl_vector_get(taille_x,3)/2-(min_x/2)+1,
                cpl_vector_get(taille_y,3)/2-(min_y/2)+1,
                cpl_vector_get(taille_x,3)/2+(min_x/2),
                cpl_vector_get(taille_y,3)/2+(min_y/2)),3);
    cpl_vector_delete(taille_x);
    cpl_vector_delete(taille_y);
    cpl_image_delete(combined_1);
    cpl_image_delete(combined_2);
    cpl_image_delete(combined_3);
    cpl_image_delete(combined_4);

    skip_if(irplib_dfs_save_imagelist(framelist, parlist, framelist, cube_final,
                                      CPL_BPP_IEEE_FLOAT, RECIPE_STRING,
                                      "IMG_BURST_COMBINE_LIST", NULL, NULL,
                                      visir_pipe_id, RECIPE_STRING
                                      "_cube_4sources" CPL_DFS_FITS));

    /*combine the four images of the final cube*/
    if ((combined = imagelist_combine(cube_final)) == NULL)
    {
        cpl_msg_error(cpl_func, "Cannot do the final shift and add");
        cpl_imagelist_delete(cube_final);
        return NULL;
    }

    end_skip;

    cpl_imagelist_delete(cube_final);

    return combined;
}

static int visir_img_burst_get_quadrant(double x, double y)
{
    if (x > 128.0 && y > 128.0) return 2;
    if (x > 128.0 && y < 128.0) return 4;
    if (x < 128.0 && y > 128.0) return 1;
    if (x < 128.0 && y < 128.0) return 3;
    return -1;
}


/*make a function which finds the combine of a cube : *************************
 * we have to put all the images of the cube at the same level.
 * for the moment:no convolution with a kernel:we let the option.*/
static cpl_image * imagelist_combine(cpl_imagelist * cube)
{
    cpl_bivector        *   offset;
    cpl_bivector        *   offset_find;
    cpl_bivector        *   position;
    cpl_vector          *   correl;
    double              *   offs_ref_pur_x;
    double              *   offs_ref_pur_y;
    cpl_bivector        *   offs_ref_purged;
    int                     i,j;
    double              *   correl_data;
    double              *   offset_find_x,    * offset_find_y;
    int                     ngood;
    cpl_image           **  combined;
    cpl_image           *   out_ima;
    double                  center_x,center_y;
    int                     n;
    cpl_image           *   image;
    cpl_imagelist       *   cube_essai;
    double                  mean1,mean2;
    FILE                *   f_out;

    n=cpl_imagelist_get_size(cube);
    correl=cpl_vector_new(n);

    /*define the first estimate of the offset*/
    offset=cpl_bivector_new(n);
    cpl_vector_fill(cpl_bivector_get_x(offset),0.);
    cpl_vector_fill(cpl_bivector_get_y(offset),0.);

    /*define the bivector of the correlation point:sources are centered*/
    position=cpl_bivector_new(1);
    center_x=cpl_image_get_size_x(cpl_imagelist_get(cube,0))/2;
    center_y=cpl_image_get_size_y(cpl_imagelist_get(cube,0))/2;
    cpl_vector_fill(cpl_bivector_get_x(position),center_x);
    cpl_vector_fill(cpl_bivector_get_y(position),center_y);

    cube_essai=cpl_imagelist_new();

    /*method:we put the cube to the same level of noise to calculate the offset
     * that we apply on the real cube of data,not modified*/

    /*put the cube to the same level*/
    image = cpl_image_duplicate(cpl_imagelist_get(cube,0));
    mean1=cpl_image_get_mean(image);
    cpl_imagelist_set(cube_essai,image,0);

    for (i=1; i<n;  i++)
    {
        mean2=cpl_image_get_mean(cpl_imagelist_get(cube,i));
        image=cpl_image_add_scalar_create(cpl_imagelist_get(cube,i),
                mean1-mean2);
        cpl_imagelist_set(cube_essai,image,i);
    }

    /*depends which cube we want to combine*/
    if (n == 4)
    {
        offset_find=cpl_geom_img_offset_fine(cube_essai, offset, position,
                15,15 ,10, 10, correl);
    }
    else
    {
        offset_find=cpl_geom_img_offset_fine(cube_essai, offset, position,
                10,10,10, 10, correl);
    }
    cpl_bivector_delete(offset);
    cpl_bivector_delete(position);
    cpl_imagelist_delete(cube_essai);
    if(offset_find == NULL)
    {
        cpl_msg_error(cpl_func, "Cannot find the offsets");
        cpl_vector_delete(correl);
        return NULL;
    }

    f_out=fopen(VISIR_IMG_BURST_SHIFT_FILE,"a");
    cpl_bivector_dump(offset_find,f_out);
    fclose(f_out);

    /*select the good offset where the correlation has worked*/
    offset_find_x = cpl_bivector_get_x_data(offset_find);
    offset_find_y = cpl_bivector_get_y_data(offset_find);
    correl_data = cpl_vector_get_data(correl);
    ngood = 0;
    for (i=0; i<cpl_imagelist_get_size(cube); i++)
        if (correl_data[i] > -0.5) ngood++;
    cpl_msg_info(cpl_func, "Good frames: %d / %d", ngood,
                 (int)cpl_imagelist_get_size(cube));

    /* Purge the bad correlated planes */
    cpl_imagelist_erase(cube, correl);
    offs_ref_purged = cpl_bivector_new(ngood);
    offs_ref_pur_x = cpl_bivector_get_x_data(offs_ref_purged);
    offs_ref_pur_y = cpl_bivector_get_y_data(offs_ref_purged);
    j = 0;
    for (i=0; i<n; i++)
    {
        if (correl_data[i] > -0.5)
        {
            offs_ref_pur_x[j] = offset_find_x[i];
            offs_ref_pur_y[j] = offset_find_y[i];
            j++;
        }
    }
    cpl_bivector_delete(offset_find);
    cpl_vector_delete(correl);

    /* Apply the shift & add on nodded */
    combined=cpl_geom_img_offset_saa(cube, offs_ref_purged,
            CPL_KERNEL_DEFAULT, 0., 0., CPL_GEOM_UNION, NULL, NULL);
    cpl_bivector_delete(offs_ref_purged);
    if (combined == NULL)
    {
        cpl_msg_error(cpl_func, "Cannot shift and add");
        return NULL;
    }

    /* Free and return */
    out_ima = cpl_image_duplicate(combined[0]);
    cpl_image_delete(combined[0]);
    cpl_image_delete(combined[1]);
    cpl_free(combined);
    return out_ima;
}


/*function which calculates statistic over a cube of images and****************
 * decides when the noise level is ok*/
static int find_starting_index (
        cpl_frame   *   frame0,
        cpl_frame   *   frame1)
{
    cpl_vector          *   stdev,  *   stdev_extract;
    cpl_propertylist    *   plist;
    int                     NAXIS3;
    int                     i;
    cpl_image           *   image,  * image1;
    double                  mean;
    double                  val;
    double                  threshold;

    cpl_msg_info(cpl_func, "Finding startindex");
    plist = cpl_propertylist_load(cpl_frame_get_filename(frame1), 0);
    NAXIS3 = visir_pfits_get_naxis3(plist);
    stdev=cpl_vector_new(NAXIS3);

    for(i=0;   i   <   NAXIS3;   i++)
    {
        image=cpl_image_load(cpl_frame_get_filename(frame0),
                        CPL_TYPE_FLOAT,i,0);
        image1=cpl_image_load(cpl_frame_get_filename(frame1),
                        CPL_TYPE_FLOAT,i,0);
        cpl_image_subtract(image,image1);
        cpl_image_delete(image1);

        cpl_vector_set(stdev,i,cpl_image_get_stdev(image));
        cpl_image_delete(image);
    }

    stdev_extract=cpl_vector_extract(stdev,NAXIS3-1-100,NAXIS3-1,1);
    mean=cpl_vector_get_mean(stdev_extract);
    threshold=1.3   *   mean; /*30% above the mean value*/
    cpl_vector_delete(stdev_extract);

    for(i=NAXIS3-1; i>0; i--)
    {
        val=cpl_vector_get(stdev,i);
        if(val > threshold)  {break;}
    }
    cpl_vector_delete(stdev);

    return i+1;
}

/*make a fit 1d-polynomial to an image*****************************************/
static cpl_image *  image_1d_poly_create(const cpl_image  *   image)
{
    const cpl_size   degree   = 1;
    const int        sizex    = cpl_image_get_size_x(image);
    const int        sizey    = cpl_image_get_size_y(image);
    cpl_matrix     * pixpos   = cpl_matrix_new(2, sizex * sizey);
    cpl_vector     * val      = cpl_vector_new(sizex * sizey);
    double         * pixpos_x = cpl_matrix_get_data(pixpos);
    double         * pixpos_y = pixpos_x + sizex * sizey;
    cpl_polynomial * poly;
    cpl_image      * image_poly;
    int              j;
    int              k = 0;
    cpl_error_code   error;


    for (j = 0; j < sizey; j++) {
        int i;
        for (i = 0; i < sizex; i++) {
            int          is_rejected;
            const double pixval = cpl_image_get(image, i+1, j+1, &is_rejected);

            if (!is_rejected) {
                pixpos_x[k] = i;
                pixpos_y[k] = j;
                (void)cpl_vector_set(val, k, pixval);
                k++;
            }
        }
    }

    cpl_vector_set_size(val, k);
    cpl_matrix_set_size(pixpos, 2, k);

    poly = cpl_polynomial_new(2);
    error = cpl_polynomial_fit(poly, pixpos, NULL, val, NULL, CPL_FALSE, NULL,
                               &degree);

    cpl_matrix_delete(pixpos);
    cpl_vector_delete(val);

    if (error) {
        cpl_msg_error(cpl_func,"Cannot fit the image");
        cpl_polynomial_delete(poly);
        return NULL;
    }
    image_poly = cpl_image_new(sizex, sizey, CPL_TYPE_FLOAT);

    error = cpl_image_fill_polynomial(image_poly, poly, 1.0,1.0,1.0,1.0);
    cpl_polynomial_delete(poly);

    if (error) {
        cpl_msg_error(cpl_func, "Could not fill the polynomial image");
        cpl_image_delete(image_poly);
        return NULL;
    }

    return image_poly;
}

/*----------------------------------------------------------------------------*/
/**
  @internal
  @brief    Simple apertures detection in an image using a provided sigma
  @param    in      Input image
  @param    sigma   detection level
  @return   The list of detected apertures or NULL if nothing detected or in
            error case.

  The threshold used for the detection is the median plus the average distance
  to the median times sigma.

  The apertures are classified by the number of pixels.

  The input image type can be CPL_TYPE_DOUBLE, CPL_TYPE_FLOAT or CPL_TYPE_INT.

  Possible #_cpl_error_code_ set in this function:
  - CPL_ERROR_NULL_INPUT
  - CPL_ERROR_ILLEGAL_INPUT
  - CPL_ERROR_ILLEGAL_OUTPUT
 */
/*----------------------------------------------------------------------------*/
static cpl_apertures * visir_img_burst_extract(
        const cpl_image     *   in,
        double                  sigma)
{
    double              median, med_dist;
    double              threshold;
    cpl_mask        *   selection;
    cpl_mask        *   kernel = NULL;
    cpl_image       *   labels;
    cpl_apertures   *   aperts;
    cpl_size            nlabels;
    int                 i,j;
    int                 sizex,sizey;
    cpl_mask       *    edge;
    cpl_binary     *    data;
    const int           size = 17;

    /* Check entries */
    cpl_ensure(in, CPL_ERROR_NULL_INPUT, NULL);
    cpl_ensure(sigma>0.0, CPL_ERROR_ILLEGAL_INPUT, NULL);

    /* Compute the threshold */
    median = cpl_image_get_median_dev(in, &med_dist);
    threshold = median + sigma * med_dist;
    /* threshold = sigma * cpl_image_get_stdev(in);*/

    /* Binarise the image */
    selection = cpl_mask_threshold_image_create(in, threshold, DBL_MAX);
    cpl_ensure(selection, CPL_ERROR_ILLEGAL_OUTPUT, NULL);

    /*modification on the 27/09/05:
      to avoid the bad detection on the side:
      put the mask to zero on the edge to not take into account
      the strong background*/
    sizex=cpl_image_get_size_x(in);
    sizey=cpl_image_get_size_y(in);
    edge=cpl_mask_new(sizex,sizey);
    data=cpl_mask_get_data(edge);

    for (i=size; i < sizex-size; i++)
    {
        for (j=size; j < sizey-size; j++)
        {
            data[i+j*sizex]=CPL_BINARY_1;
        }
    }

    /*cpl_image_save(selection,"selection.fits",CPL_BPP_IEEE_FLOAT, NULL,
                                           CPL_IO_DEFAULT);
    cpl_image_save(edge,"edge.fits",CPL_BPP_IEEE_FLOAT, NULL,
                                                       CPL_IO_DEFAULT);
    */

    cpl_mask_and(selection,edge);
    if (selection == NULL)

    {
        cpl_msg_error(cpl_func,"problem in the mask reduction because of the edge");
        return NULL;
    }
    cpl_mask_delete(edge);

    /* Apply a morphological closing to remove the single pixel detections */
    kernel = cpl_mask_new(3, 3);
    cpl_mask_not(kernel);

    if (cpl_mask_filter(selection, selection, kernel,
                            CPL_FILTER_CLOSING, CPL_BORDER_ZERO)) {
        cpl_mask_delete(selection);
        cpl_mask_delete(kernel);
        cpl_ensure(0, CPL_ERROR_ILLEGAL_OUTPUT, NULL);
    }

    cpl_mask_delete(kernel);

    /* Labelise the different detected apertures */
    if ((labels = cpl_image_labelise_mask_create(selection, &nlabels))==NULL)
    {
        cpl_mask_delete(selection);
        cpl_ensure(0, CPL_ERROR_ILLEGAL_OUTPUT, NULL);
    }
    cpl_mask_delete(selection);

    /* Nothing detected */
    if (nlabels == 0)
    {
        cpl_image_delete(labels);
        return NULL;
    }

    /* Create the detected apertures list */
    if ((aperts = cpl_apertures_new_from_image(in, labels)) == NULL)
    {
        cpl_image_delete(labels);
        cpl_ensure(0, CPL_ERROR_ILLEGAL_OUTPUT, NULL);
    }
    /*sort apertures by number of pixels*/
    cpl_apertures_sort_by_flux(aperts);

    /* Free and return */
    cpl_image_delete(labels);
    return aperts;
}

/* function which makes a median on  square box*******************************
 * but we don't care about the edge
 return an image with value 0 on the edge: original size minus
 'size' on each edge*/
static cpl_image * cpl_image_get_median_choose(const cpl_image * image,
                                               int               size)
{
    const int   sizex = cpl_image_get_size_x(image);
    const int   sizey = cpl_image_get_size_y(image);
    const int   hsize = size/2;
    cpl_image * self  = cpl_image_new(sizex, sizey, CPL_TYPE_FLOAT);
    cpl_mask  * kernel = cpl_mask_new(1+2*hsize, 1+2*hsize);

    bug_if(image == NULL);

    bug_if(hsize <= 0);
    bug_if(sizex <= hsize);
    bug_if(sizey <= hsize);

    bug_if(cpl_mask_not(kernel));
    bug_if(cpl_image_filter_mask(self, image, kernel, CPL_FILTER_MEDIAN,
                                 CPL_BORDER_NOP));

    end_skip;

    cpl_mask_delete(kernel);

    return self;
}

/*function which returns a bivector of size 5:
 * the 4 positions of the 4 sources in the image and
 * the size (size_x,size_y) of the box addapted for the extraction.
 the kernel is used to make the convolution:in order to detect the sources*/
static cpl_bivector * visir_extract_4_sources_box(cpl_image        * image,
                                                  const cpl_matrix * kernel,
                                                  double             sigma,
                                                  int             taille_median,
                                                  cpl_boolean        debug)
{
    cpl_image       *   image_diff;
    cpl_image       *   image_poly;
    cpl_image       *   image_conv;
    cpl_apertures   *   aper_pos,
                    *   aper_neg;
    int                 quadrant1, quadrant2;
    cpl_vector      *   taille;
    int                 dimx, dimy, size, x1, x2, x3, x4, y_1, y2, y3, y4;
    cpl_bivector    *   result;
    int                 index;

    dimx=cpl_image_get_size_x(image);
    dimy=cpl_image_get_size_y(image);

    image_poly=image_1d_poly_create(image);
    image_diff = cpl_image_subtract_create(image,image_poly);
    cpl_image_delete(image_poly);

    /*size of the median box is choosed*/
    image_conv = image_median_conv(image_diff, kernel, taille_median);
    cpl_image_delete(image_diff);
    if(image_conv == NULL) return NULL;

    /*image_conv=cpl_image_get_median_choose(image,taille_median);*/
    if (debug == 1) {
        cpl_image_save(image_conv, RECIPE_STRING "_image00_median" CPL_DFS_FITS,
                       CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_DEFAULT);
    }

    aper_pos=visir_img_burst_extract(image_conv, sigma);
    if(aper_pos == NULL)
    {
        cpl_msg_error(cpl_func,"cannot detect the positive object");
        cpl_image_delete(image_conv);
        return  NULL;
    }
    cpl_image_multiply_scalar(image_conv, -1);
    aper_neg=visir_img_burst_extract(image_conv, sigma);
    cpl_image_delete(image_conv);

    if (cpl_apertures_get_size(aper_pos) < 2  ||
         cpl_apertures_get_size(aper_neg) < 2)
    {
        cpl_msg_info(cpl_func,"no 2 sources in the detection of the 4 sources");
        if (aper_pos) cpl_apertures_delete(aper_pos);
        if (aper_neg) cpl_apertures_delete(aper_neg);
        return NULL;
    }

    /*detect the 2 sources positiv in the image*/
    quadrant1 = visir_img_burst_get_quadrant(
            cpl_apertures_get_centroid_x(aper_pos,1),
            cpl_apertures_get_centroid_y(aper_pos,1));
    quadrant2 = visir_img_burst_get_quadrant(
            cpl_apertures_get_centroid_x(aper_pos,2),
            cpl_apertures_get_centroid_y(aper_pos,2));

    if ((quadrant1 == 1 && quadrant2 == 3)||(quadrant2 == 1 && quadrant1 == 3))
        cpl_msg_warning(cpl_func,"2 sources detected on the same side");

    if ((quadrant1 == 2 && quadrant2 == 4)||(quadrant2 == 2 && quadrant1 == 4))
        cpl_msg_warning(cpl_func,"2 sources detected on the same side");

    /*coordinates of the 4 sources in the image*/
    x1=cpl_apertures_get_centroid_x(aper_pos,1);
    y_1=cpl_apertures_get_centroid_y(aper_pos,1);
    x2=cpl_apertures_get_centroid_x(aper_pos,2);
    y2=cpl_apertures_get_centroid_y(aper_pos,2);
    x3=cpl_apertures_get_centroid_x(aper_neg,1);
    y3=cpl_apertures_get_centroid_y(aper_neg,1);
    x4=cpl_apertures_get_centroid_x(aper_neg,2);
    y4=cpl_apertures_get_centroid_y(aper_neg,2);
    cpl_apertures_delete(aper_pos);
    cpl_apertures_delete(aper_neg);

    result=cpl_bivector_new(5);

    if ((x1 > 15) && (x1 < dimx-1-15)) /*not near the edge*/
        cpl_vector_set(cpl_bivector_get_x(result),0,x1);
    else
        cpl_vector_set(cpl_bivector_get_x(result),0,1000);
    if ((x2 > 15) && (x2 < dimx-1-15)) /*not near the edge*/
        cpl_vector_set(cpl_bivector_get_x(result),1,x2);
    else
        cpl_vector_set(cpl_bivector_get_x(result),1,1000);
    if ((x3 > 15) && (x3 < dimx-1-15)) /*not near the edge*/
        cpl_vector_set(cpl_bivector_get_x(result),2,x3);
    else
        cpl_vector_set(cpl_bivector_get_x(result),2,1000);
    if ((x4 > 15) && (x4 < dimx-1-15)) /*not near the edge*/
        cpl_vector_set(cpl_bivector_get_x(result),3,x4);
    else
        cpl_vector_set(cpl_bivector_get_x(result),3,1000);
    if ((y_1 > 15) && (y_1 < dimy-1-15)) /*not near the edge*/
        cpl_vector_set(cpl_bivector_get_y(result),0,y_1);
    else
        cpl_vector_set(cpl_bivector_get_y(result),0,1000);
    if ((y2 > 15) && (y2 < dimy-1-15)) /*not near the edge*/
        cpl_vector_set(cpl_bivector_get_y(result),1,y2);
    else
        cpl_vector_set(cpl_bivector_get_y(result),1,1000);
    if ((y3 > 15) && (y3 < dimy-1-15)) /*not near the edge*/
        cpl_vector_set(cpl_bivector_get_y(result),2,y3);
    else
        cpl_vector_set(cpl_bivector_get_y(result),2,1000);
    if ((y4 > 15) && (y4 < dimy-1-15)) /*not near the edge*/
        cpl_vector_set(cpl_bivector_get_y(result),3,y4);
    else
        cpl_vector_set(cpl_bivector_get_y(result),3,1000);

    x1=cpl_vector_get(cpl_bivector_get_x(result),0);
    x2=cpl_vector_get(cpl_bivector_get_x(result),1);
    x3=cpl_vector_get(cpl_bivector_get_x(result),2);
    x4=cpl_vector_get(cpl_bivector_get_x(result),3);
    y_1=cpl_vector_get(cpl_bivector_get_y(result),0);
    y2=cpl_vector_get(cpl_bivector_get_y(result),1);
    y3=cpl_vector_get(cpl_bivector_get_y(result),2);
    y4=cpl_vector_get(cpl_bivector_get_y(result),3);

    /*
    cpl_msg_info(cpl_func,"aper=%d\n",x1);
    cpl_msg_info(cpl_func,"aper=%d\n",x2);
    cpl_msg_info(cpl_func,"aper=%d\n",x3);
    cpl_msg_info(cpl_func,"aper=%d\n",x4);
    cpl_msg_info(cpl_func,"aper=%d\n",y_1);
    cpl_msg_info(cpl_func,"aper=%d\n",y2);
    cpl_msg_info(cpl_func,"aper=%d\n",y3);
    cpl_msg_info(cpl_func,"aper=%d\n",y4);
     */

    /*vector to calculate the size of the box:
     we take the minimum size taking into accompt the edge */
    taille=cpl_vector_new(22);

    if (fabs(x1-x2) > 50)
        cpl_vector_set(taille,0,fabs(x1-x2)/2);
    else
        cpl_vector_set(taille,0,1000);
    if(fabs(x1-x3) >50)
        cpl_vector_set(taille,1,fabs(x1-x3)/2);
    else
        cpl_vector_set(taille,1,1000);
    if (fabs(x1-x4) > 50 && fabs(x1-x4) < dimx)
        cpl_vector_set(taille,2,fabs(x1-x4)/2);
    else
        cpl_vector_set(taille,2,1000);
    if (fabs(y_1-y2) > 50)
        cpl_vector_set(taille,3,fabs(y_1-y2)/2);
    else
        cpl_vector_set(taille,3,1000);
    if (fabs(y_1-y3) > 50)
        cpl_vector_set(taille,4,fabs(y_1-y3)/2);
    else
        cpl_vector_set(taille,4,1000);
    if (fabs(y_1-y4) > 50)
        cpl_vector_set(taille,5,fabs(y_1-y4)/2);
    else
        cpl_vector_set(taille,5,1000);

    /*cpl_msg_info(cpl_func,"taille[0]=%g\n",cpl_vector_get(taille,0));
      cpl_msg_info(cpl_func,"taille[0]=%g\n",cpl_vector_get(taille,1));
      cpl_msg_info(cpl_func,"taille[0]=%g\n",cpl_vector_get(taille,2));
      cpl_msg_info(cpl_func,"taille[0]=%g\n",cpl_vector_get(taille,3));
      cpl_msg_info(cpl_func,"taille[0]=%g\n",cpl_vector_get(taille,4));
      cpl_msg_info(cpl_func,"taille[0]=%g\n",cpl_vector_get(taille,5));
     */

    cpl_vector_set(taille,6,x1);/*necessary if the source is near the edge*/
    cpl_vector_set(taille,7,dimx-1-x1);
    cpl_vector_set(taille,8,x2);
    cpl_vector_set(taille,9,dimx-1-x2);
    cpl_vector_set(taille,10,x3);
    cpl_vector_set(taille,11,dimx-1-x3);
    cpl_vector_set(taille,12,x4);
    cpl_vector_set(taille,13,dimx-1-x4);
    cpl_vector_set(taille,14,y_1);
    cpl_vector_set(taille,15,dimy-1-y_1);
    cpl_vector_set(taille,16,y2);
    cpl_vector_set(taille,17,dimy-1-y2);
    cpl_vector_set(taille,18,y3);
    cpl_vector_set(taille,19,dimy-1-y3);
    cpl_vector_set(taille,20,y4);
    cpl_vector_set(taille,21,dimy-1-y4);

    cpl_vector_sort(taille,1);

    index=cpl_vector_find(taille,0.);
    size=cpl_vector_get(taille,index); /*2*size is the size of the final box*/
    cpl_vector_delete(taille);

    cpl_vector_set(cpl_bivector_get_x(result),4,size);
    cpl_vector_set(cpl_bivector_get_y(result),4,size);

    /*
       cpl_msg_info(cpl_func,"p=%d\n",size);
       cpl_msg_info(cpl_func,"aper=%d\n",x1);
       cpl_msg_info(cpl_func,"aper=%d\n",x2);
       cpl_msg_info(cpl_func,"aper=%d\n",x3);
       cpl_msg_info(cpl_func,"aper=%d\n",x4);
       cpl_msg_info(cpl_func,"aper=%d\n",y_1);
       cpl_msg_info(cpl_func,"aper=%d\n",y2);
       cpl_msg_info(cpl_func,"aper=%d\n",y3);
       cpl_msg_info(cpl_func,"aper=%d\n",y4);
     */

    return result;
}


/*function which makes a median(taille,taille) + ******************************
 * convolution by a kernel of maximum size 9x9*/
static cpl_image * image_median_conv(const cpl_image * image,
                                     const cpl_matrix * kernel,
                                     int taille)
{
    /* make a median (taille,taille)*/
    cpl_image    * image_median = cpl_image_get_median_choose(image, taille);
    cpl_image    * image_conv;
    cpl_error_code error;

    cpl_ensure(image_median != NULL, cpl_error_get_code(), NULL);
    /* make a convolution with a kernel*/
    image_conv = cpl_image_new(cpl_image_get_size_x(image_median),
                               cpl_image_get_size_y(image_median),
                               cpl_image_get_type(image_median));

    error = cpl_image_filter(image_conv, image_median, kernel,
                             CPL_FILTER_LINEAR, CPL_BORDER_FILTER);

    cpl_image_delete(image_median);

    if (error) {
        cpl_image_delete(image_conv);
        cpl_ensure(0, error, NULL);
    }

    return image_conv;
}

/*----------------------------------------------------------------------------*/
/**
  @internal
  @brief    Create a PSF as a matrix
  @param    size      The created matrix will be size X size
  @return   The created matrix or NULL on error
  @note The created matrix must be deallocated with cpl_matrix_delete().

 */
/*----------------------------------------------------------------------------*/
static cpl_matrix * visir_img_burst_psf_create(int size)
{
    cpl_matrix * self  = NULL;
    cpl_image  * iself = cpl_image_wrap_double(size, size,
                                               cpl_malloc(size * size
                                                          * sizeof(double)));
    const double hsize = floor(size / 2.0);
    const double scale = CPL_MATH_SQRT2PI;
    double       flux;
    int          i;


    bug_if(0);
    bug_if(size < 1);

    for (i=0; i < size; i++) {
        int j;
        for (j=0; j <= i; j++) {
            const double value = exp(-0.5*((i-hsize)*(i-hsize)+
                                           (j-hsize)*(j-hsize)));
            (void)cpl_image_set(iself, i+1, j+1, value/scale);
            if (i != j)
                (void)cpl_image_set(iself, j+1, i+1, value/scale);
        }
    }

    flux = cpl_image_get_flux(iself);

    bug_if(flux <= 0.0);
    bug_if(cpl_image_divide_scalar(iself, flux));

    self = cpl_matrix_wrap(size, size, (double*)cpl_image_unwrap(iself));
    iself = NULL;

    end_skip;

    cpl_image_delete(iself);

    return self;
}


/*----------------------------------------------------------------------------*/
/**
   @brief   Destripe a cube of images
   @param   self         The imagelist to destripe
   @param   niter        The number of destripings per image
   @param   morpho       Flag to enable the morphological cleaning
   @return  CPL_ERROR_NONE iff OK.
*/
/*----------------------------------------------------------------------------*/
static
cpl_error_code visir_destripe_imagelist(cpl_imagelist * self,
                                        int             niter,
                                        cpl_boolean     morpho)
{
    const double threshold = 3.5 * 1.64;
    const double threshold_detect = 1.3;
    const int    size = cpl_imagelist_get_size(self);
    int          i;

    bug_if(self == NULL);
    bug_if(niter < 1);

    cpl_msg_info(cpl_func, "Destriping %d images using %d iterations and "
                 "threshold=%g, detection-threshold=%g", size, niter, threshold,
                 threshold_detect);

    /* Loop on images  */
    for (i = 0; i < size; i++) {
        cpl_image * image = cpl_imagelist_get(self, i);

        cpl_msg_info(cpl_func, "Destriping image %d of %d", i+1, size);

        if (visir_destripe_image(image, niter, threshold, threshold_detect,
                                 morpho)) break;
    }

    skip_if(0);

    end_skip;

    return cpl_error_get_code();
}

#include <visir_destripe.c>
