/* $Id: naco_img_strehl.c,v 1.67 2011-12-22 11:09:36 llundin Exp $
 *
 * This file is part of the NACO 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 St, Fifth Floor, Boston, MA  02111-1307  USA
 */

/*
 * $Author: llundin $
 * $Date: 2011-12-22 11:09:36 $
 * $Revision: 1.67 $
 * $Name: not supported by cvs2svn $
 */

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

/*-----------------------------------------------------------------------------
                                Includes
 -----------------------------------------------------------------------------*/
#include "naco_recipe.h"
#include "naco_strehl.h"

#include <string.h>

/*-----------------------------------------------------------------------------
                                Define
 -----------------------------------------------------------------------------*/

#define STREHL_MINIMUM_FLUX             100000
#define STREHL_MAXIMUM_PEAK             4000
#define STREHL_DEF_LOCATE_SX            512
#define STREHL_DEF_LOCATE_SY            512

#define RECIPE_STRING "naco_img_strehl"

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

static cpl_image * naco_img_strehl_reduce(const cpl_parameterlist *,
                                          const irplib_framelist *);

static cpl_error_code naco_img_strehl_qc(cpl_propertylist *,
                                         cpl_propertylist *,
                                         const irplib_framelist *);

static cpl_error_code naco_img_strehl_save(cpl_frameset *,
                                           const cpl_parameterlist *,
                                           const cpl_propertylist *,
                                           const cpl_propertylist *,
                                           const cpl_image *, 
                                           const irplib_framelist *,
                                           int);

NACO_RECIPE_DEFINE(naco_img_strehl,
                   NACO_PARAM_STAR_R  |                        
                   NACO_PARAM_BG_RINT |                        
                   NACO_PARAM_BG_REXT,
                   "Strehl computation recipe",
                   RECIPE_STRING " -- NACO Strehl Ratio recipe.\n"          
                   "This recipe estimates the strehl ratio and its error "
                   "bound.\nThe Set Of Frames (sof-file) must specify at "
                   "least one pair of files and they must be tagged either\n"
                   "NACO-raw-file.fits " NACO_IMG_STREHL_CAL " or\n"
                   "NACO-raw-file.fits " NACO_IMG_STREHL_TECH "\n");

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

static struct {
    /* Inputs */
    int         mode;
    /* Outputs */
    double      pscale;
    const char * filter;
    double      pos_x;
    double      pos_y;
    double      strehl;
    double      strehl_err;
    double      star_bg;
    double      star_peak;
    double      star_flux;
    double      psf_peak;
    double      psf_flux;
    double      bg_noise;
} naco_img_strehl_config;

/*----------------------------------------------------------------------------*/
/**
 * @defgroup naco_img_strehl   Strehl Ratio
 */
/*----------------------------------------------------------------------------*/

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


/*----------------------------------------------------------------------------*/
/**
  @brief    The recipe data reduction part is implemented here 
  @param    framelist   the frames list
  @param    parlist     the parameters list
  @return   0 iff everything is ok
 */
/*----------------------------------------------------------------------------*/
static int naco_img_strehl(cpl_frameset            * framelist,
                           const cpl_parameterlist * parlist)
{
    cpl_errorstate cleanstate = cpl_errorstate_get();
    irplib_framelist * allframes = NULL;
    irplib_framelist * rawframes = NULL;
    irplib_framelist * framepair = NULL;
    cpl_frame        * frame1    = NULL;
    cpl_frame        * frame2    = NULL;
    cpl_image        * img       = NULL;
    cpl_propertylist * qclist    = cpl_propertylist_new();
    cpl_propertylist * paflist   = cpl_propertylist_new();
    int                nframes, npairs;
    int                nb_good = 0;
    int                i;
    
    /* Identify the RAW and CALIB frames in the input frameset */
    irplib_check(naco_dfs_set_groups(framelist),
                 "Could not group the input frames");

    allframes = irplib_framelist_cast(framelist);
    skip_if(allframes == NULL);
    irplib_check(rawframes
                 = irplib_framelist_extract_regexp(allframes,
                                                   "^(" NACO_IMG_STREHL_CAL
                                                   "|"  NACO_IMG_STREHL_TECH
                                                   ")$", CPL_FALSE),
                 "Could not load the raw frames");
    irplib_framelist_empty(allframes);

    naco_img_strehl_config.mode = 0;
    if (cpl_frameset_find(framelist, NACO_IMG_STREHL_CAL))
        naco_img_strehl_config.mode = 1;

    if (cpl_frameset_find(framelist, NACO_IMG_STREHL_TECH)) {
        irplib_ensure(!naco_img_strehl_config.mode, CPL_ERROR_INCOMPATIBLE_INPUT,
                       "Mixing of DO categories "  NACO_IMG_STREHL_CAL " and "
                        NACO_IMG_STREHL_TECH "is not supported");
        naco_img_strehl_config.mode = 2;
    }

    /* Check if there is an even number of frames */
    nframes = irplib_framelist_get_size(rawframes);
    skip_if_lt(nframes, 2, "frames");

    if (nframes % 2) {
        cpl_msg_warning(cpl_func, "The SOF comprises an odd number (%d) of raw "
                        "frames, ignoring the last", nframes);
        skip_if(irplib_framelist_erase(rawframes, nframes - 1));
    }
    npairs = nframes/2;

    framepair = irplib_framelist_new();
    
    /* Reduce each pair */
    for (i=0; i < npairs; i++) {
        cpl_error_code error;

        /* Reduce data pair i */
        cpl_msg_info(cpl_func, "Reducing pair %d out of %d", i+1, npairs);

        frame1 = irplib_framelist_unset(rawframes, 0, NULL);
        error = irplib_framelist_set(framepair, frame1, 0);
        bug_if(error);

        skip_if(irplib_framelist_load_propertylist(framepair, 0, 0, "^("
                                                   IRPLIB_PFITS_REGEXP_RECAL "|"
                                                   NACO_PFITS_REGEXP_STREHL "|"
                                                   NACO_PFITS_REGEXP_STREHL_PAF
                                                   ")$", CPL_FALSE));

        frame2 = irplib_framelist_unset(rawframes, 0, NULL);
        error = irplib_framelist_set(framepair, frame2, 1);
        bug_if(error);

        skip_if(irplib_framelist_load_propertylist_all(framepair, 0, "^("
                                                       NACO_PFITS_REGEXP_STREHL
                                                       ")$", CPL_FALSE));

        img = naco_img_strehl_reduce(parlist, framepair);

        if (img == NULL) {
            if (npairs > 1)
                irplib_error_recover(cleanstate, "Could not reduce pair number "
                                     "%d:", i+1);
        } else {

            skip_if(naco_img_strehl_qc(qclist, paflist, framepair));

            /* PRO.CATG */
            bug_if (cpl_propertylist_append_string(paflist, CPL_DFS_PRO_CATG,
                                                   naco_img_strehl_config.mode
                                                   == 1 ? NACO_IMG_STREHL_RESCAL
                                                   : NACO_IMG_STREHL_RESTECH));

            irplib_ensure(!naco_img_strehl_save(framelist, parlist, qclist,
                                                paflist, img, framepair, i+1),
                          CPL_ERROR_FILE_IO,
                          "Could not save the products of pair %d", i+1);

            cpl_image_delete(img);
            img = NULL;
            cpl_propertylist_empty(qclist);
            cpl_propertylist_empty(paflist);

            nb_good++;
        }

        /* Empty the list - and deallocate the propertylists */
        irplib_framelist_empty(framepair);

        bug_if(irplib_framelist_get_size(framepair) != 0);

    }

    bug_if(irplib_framelist_get_size(rawframes) != 0);

    irplib_ensure(nb_good > 0, CPL_ERROR_DATA_NOT_FOUND,
                  "None of the %d pairs of frames could be reduced", npairs);

    end_skip;
    
    cpl_image_delete(img);
    irplib_framelist_delete(allframes);
    irplib_framelist_delete(rawframes);
    irplib_framelist_delete(framepair);
    cpl_propertylist_delete(qclist);
    cpl_propertylist_delete(paflist);

    return cpl_error_get_code();
}

/*----------------------------------------------------------------------------*/
/**
  @brief    The recipe data reduction part is implemented here 
  @param    parlist   The list of recipe parameters
  @param    framepair The list of two frames to reduce
  @return   the diff image or NULL in error case
 */
/*----------------------------------------------------------------------------*/
static cpl_image * naco_img_strehl_reduce(const cpl_parameterlist * parlist,
                                          const irplib_framelist * framepair)
{
 
    const cpl_frame * frame1 = irplib_framelist_get_const(framepair, 0);
    const cpl_frame * frame2 = irplib_framelist_get_const(framepair, 1);
    const cpl_propertylist * plist1
        = irplib_framelist_get_propertylist_const(framepair, 0);
    const cpl_propertylist * plist2
        = irplib_framelist_get_propertylist_const(framepair, 1);
    const char    * sval1;
    const char    * sval2;
    cpl_image     * im1 = NULL;
    cpl_image     * im2 = NULL;
     /* Sigma-levels for detection of a bright star.
        NACO-3.3.1 mysteriously has: 5, 2, 2, 0.5  */
    double          psigmas[] = {5.0, 2.0, 1.0, 0.5};
    const int       nsigmas = (int)(sizeof(psigmas)/sizeof(double));
    cpl_size        isigma;
    cpl_vector    * sigmas = NULL;
    cpl_apertures * apert = NULL;
    double          dval;
    double          pixscale;
    double          lam, dlam;
    int             iflux;
    int ndit;
                        

    bug_if(cpl_error_get_code());

    naco_img_strehl_config.pscale = -1.0;
    naco_img_strehl_config.pos_x = -1.0;
    naco_img_strehl_config.pos_y = -1.0;
    naco_img_strehl_config.strehl = -1.0;
    naco_img_strehl_config.strehl_err = -1.0;
    naco_img_strehl_config.star_bg = -1.0;
    naco_img_strehl_config.star_peak = -1.0;
    naco_img_strehl_config.star_flux = -1.0;
    naco_img_strehl_config.psf_peak = -1.0;
    naco_img_strehl_config.psf_flux = -1.0;
    naco_img_strehl_config.bg_noise = -1.0;

    /* Print out the filter and the pixel scale */
    irplib_check(sval1 = naco_pfits_get_filter(plist1);
                 sval2 = naco_pfits_get_filter(plist2);
                 pixscale = naco_pfits_get_pixscale(plist1);
                 dval = naco_pfits_get_pixscale(plist2),
                 "Could not load the filter names and Pixel-scales");

    irplib_ensure(!strcmp(sval1, sval2), CPL_ERROR_INCOMPATIBLE_INPUT,
                  "The filters in the pair are different: %s <=> %s", sval1, sval2);

    irplib_ensure(fabs(pixscale-dval) <= 1e-3, CPL_ERROR_INCOMPATIBLE_INPUT,
                  "The pixel-scales in the pair are different: %g <=> %g",
                   pixscale, dval);

    naco_img_strehl_config.pscale = pixscale;
    naco_img_strehl_config.filter = sval1;

    cpl_msg_info(cpl_func, "Filter:        [%s]", naco_img_strehl_config.filter);
    cpl_msg_info(cpl_func, "Pixel scale:   [%g]", naco_img_strehl_config.pscale);

    /* Load input images */
    cpl_msg_info(cpl_func, "---> Loading the pair of input images");
    im1 = cpl_image_load(cpl_frame_get_filename(frame1), CPL_TYPE_FLOAT, 0, 0);
    skip_if(0);
    im2 = cpl_image_load(cpl_frame_get_filename(frame2), CPL_TYPE_FLOAT, 0, 0);
    skip_if(0);
    
    /* Subtract input images */
    cpl_msg_info(cpl_func, "---> Subtracting input images");
    skip_if(cpl_image_subtract(im1, im2));
    cpl_image_delete(im2);
    im2 = NULL;

    /* Get lam and dlam from the filter name for the Strehl computation */
    irplib_check(naco_get_filter_infos(naco_img_strehl_config.filter, &lam, &dlam),
                 "Cannot get filter infos [%s]", naco_img_strehl_config.filter);

    cpl_msg_warning(cpl_func, "LAM=%g, DLAM=%g", lam, dlam);
    /* Detect a bright star around the center */
    cpl_msg_info(cpl_func, "---> Detecting a bright star using %d sigma-levels "
                 "ranging from %g down to %g", nsigmas, psigmas[0],
                 psigmas[nsigmas-1]);
    sigmas = cpl_vector_wrap(nsigmas, psigmas);
    irplib_check(apert = cpl_apertures_extract_window(im1, sigmas, 
            (int)(cpl_image_get_size_x(im1)-STREHL_DEF_LOCATE_SX)/2,
            (int)(cpl_image_get_size_y(im1)-STREHL_DEF_LOCATE_SY)/2,
            (int)(cpl_image_get_size_x(im1)+STREHL_DEF_LOCATE_SX)/2,
            (int)(cpl_image_get_size_y(im1)+STREHL_DEF_LOCATE_SY)/2,
                                                      &isigma),
                 "Could not detect the star");

    skip_if(irplib_apertures_find_max_flux(apert, &iflux, 1));

    naco_img_strehl_config.pos_x = cpl_apertures_get_centroid_x(apert, iflux);
    naco_img_strehl_config.pos_y = cpl_apertures_get_centroid_y(apert, iflux);
    cpl_apertures_delete(apert);
    apert = NULL;
    cpl_msg_info(cpl_func, "Star detected at sigma=%g, at position %g %g",
                 psigmas[isigma],
                 naco_img_strehl_config.pos_x,
                 naco_img_strehl_config.pos_y);
    skip_if(0);

    /* Get lam and dlam from the filter name for the Strehl computation */
    irplib_check(naco_get_filter_infos(naco_img_strehl_config.filter, &lam, &dlam),
                 "Cannot get filter infos [%s]", naco_img_strehl_config.filter);

    /* Compute the strehl */
    cpl_msg_info(cpl_func, "---> Computing the strehl ratio");
    irplib_check(naco_strehl_compute(im1, parlist, RECIPE_STRING, lam, dlam,
                                     naco_img_strehl_config.pos_x,
                                     naco_img_strehl_config.pos_y,
                                     naco_img_strehl_config.pscale,
                                  &(naco_img_strehl_config.strehl),
                                  &(naco_img_strehl_config.strehl_err),
                                  &(naco_img_strehl_config.star_bg),
                                  &(naco_img_strehl_config.star_peak),
                                  &(naco_img_strehl_config.star_flux),
                                  &(naco_img_strehl_config.psf_peak),
                                  &(naco_img_strehl_config.psf_flux),
                                  &(naco_img_strehl_config.bg_noise)),
                 "Could not compute the Strehl ratio");

    ndit = naco_pfits_get_ndit(plist1); 
    skip_if(cpl_error_get_code());

    cpl_msg_info(cpl_func, "Strehl:        %g", naco_img_strehl_config.strehl);
    cpl_msg_info(cpl_func, "Strehl error:  %g", naco_img_strehl_config.strehl_err);
    cpl_msg_info(cpl_func, "Star bg:       %g", naco_img_strehl_config.star_bg);
    cpl_msg_info(cpl_func, "Star peak:     %g", naco_img_strehl_config.star_peak);
    cpl_msg_info(cpl_func, "Star flux:     %g", naco_img_strehl_config.star_flux);
    cpl_msg_info(cpl_func, "PSF peak:      %g", naco_img_strehl_config.psf_peak);
    cpl_msg_info(cpl_func, "PSF flux:      %g", naco_img_strehl_config.psf_flux);
    cpl_msg_info(cpl_func, "Bg noise:      %g", naco_img_strehl_config.bg_noise);

    if (ndit*naco_img_strehl_config.star_flux < STREHL_MINIMUM_FLUX)
        cpl_msg_warning(cpl_func, "The Strehl may be unreliable, due to a too "
                        "low star flux: %g < %g/%d",
                        naco_img_strehl_config.star_flux,
                        (double)STREHL_MINIMUM_FLUX, ndit);

    if (naco_img_strehl_config.star_peak > STREHL_MAXIMUM_PEAK)
        cpl_msg_warning(cpl_func, "The Strehl may be unreliable, due to a too "
                        "high star peak: %g > %g",
                        naco_img_strehl_config.star_peak,
                        (double)STREHL_MAXIMUM_PEAK);

    end_skip;

    cpl_image_delete(im2);
    cpl_apertures_delete(apert);
    cpl_vector_unwrap(sigmas);

    if (cpl_error_get_code()) {
        cpl_image_delete(im1);
        im1 = NULL;
    }

    return im1;
}


/*----------------------------------------------------------------------------*/
/**
  @brief    Append the QC parameters to the propertylist
  @param    qclist    List of QC parameters
  @param    paflist   List of PAF parameters
  @param    framepair List of rawframes and their propertylists
  @return   0 iff everything is ok
 */
/*----------------------------------------------------------------------------*/
static cpl_error_code naco_img_strehl_qc(cpl_propertylist       * qclist,
                                         cpl_propertylist       * paflist,
                                         const irplib_framelist * framepair)
{

    cpl_errorstate cleanstate = cpl_errorstate_get();
    const cpl_propertylist * plist1
        = irplib_framelist_get_propertylist_const(framepair, 0);
    const cpl_propertylist * plist2
        = irplib_framelist_get_propertylist_const(framepair, 1);
    const char * optiname;
    double                  dval1, dval2;
    double                  mean;
    const double            almost_zero = 1e-3; /* FIXME: Arbitrary constant */



    bug_if (0);

    /* Get the keywords for the paf file */
    skip_if (cpl_propertylist_copy_property_regexp(paflist, plist1, "^("
                                                   NACO_PFITS_REGEXP_STREHL_PAF
                                                   ")$", 0));

    /* AOS.RTC.DET.DST.L0MEAN */
    dval1 = naco_pfits_get_l0mean(plist1);
    dval2 = naco_pfits_get_l0mean(plist2);
    mean = 0.0;
    if (cpl_error_get_code())
        naco_error_reset("Could not get FITS key:");
    else {
        if (fabs(dval1) <= almost_zero) mean = dval2;
        else if (fabs(dval2) <= almost_zero) mean = dval1;
        else mean = (dval1+dval2)/2.0;
    }
    skip_if(cpl_propertylist_append_double(paflist,
                                           "ESO AOS RTC DET DST L0MEAN", mean));

    /* AOS.RTC.DET.DST.T0MEAN */
    dval1 = naco_pfits_get_t0mean(plist1);
    dval2 = naco_pfits_get_t0mean(plist2);
    mean = 0.0;
    if (cpl_error_get_code())
        naco_error_reset("Could not get FITS key:");
    else {
        if (fabs(dval1) <= almost_zero) mean = dval2;
        else if (fabs(dval2) <= almost_zero) mean = dval1;
        else mean = (dval1+dval2)/2.0;
    }
    skip_if(cpl_propertylist_append_double(paflist, "ESO AOS RTC DET DST T0MEAN",
                                           mean));

    /* AOS.RTC.DET.DST.R0MEAN */
    dval1 = naco_pfits_get_r0mean(plist1);
    dval2 = naco_pfits_get_r0mean(plist2);
    mean = 0.0;
    if (cpl_error_get_code())
        naco_error_reset("Could not get FITS key:");
    else {
        if (fabs(dval1) <= almost_zero) mean = dval2;
        else if (fabs(dval2) <= almost_zero) mean = dval1;
        else mean = (dval1+dval2)/2.0;
    }
    skip_if(cpl_propertylist_append_double(paflist, "ESO AOS RTC DET DST R0MEAN",
                                           mean));

    /* AOS.RTC.DET.DST.ECMEAN */
    dval1 = naco_pfits_get_ecmean(plist1);
    dval2 = naco_pfits_get_ecmean(plist2);
    mean = 0.0;
    if (cpl_error_get_code())
        naco_error_reset("Could not get FITS key:");
    else {
        if (fabs(dval1) <= almost_zero) mean = dval2;
        else if (fabs(dval2) <= almost_zero) mean = dval1;
        else mean = (dval1+dval2)/2.0;
    }
    skip_if(cpl_propertylist_append_double(paflist, "ESO AOS RTC DET DST ECMEAN",
                                           mean));

    /* AOS.RTC.DET.DST.FLUXMEAN */
    dval1 = naco_pfits_get_fluxmean(plist1);
    dval2 = naco_pfits_get_fluxmean(plist2);
    mean = 0.0;
    if (cpl_error_get_code())
        naco_error_reset("Could not get FITS key:");
    else {
        if (fabs(dval1) <= almost_zero) mean = dval2;
        else if (fabs(dval2) <= almost_zero) mean = dval1;
        else mean = (dval1+dval2)/2.0;
    }
    skip_if(cpl_propertylist_append_double(paflist,"ESO AOS RTC DET DST FLUXMEAN",
                                           mean));

    /* Add QC parameters */
    skip_if(cpl_propertylist_append_string(qclist, "ESO QC FILTER OBS", 
                                           naco_img_strehl_config.filter));

    optiname = naco_pfits_get_opti3_name(plist1);
    if (cpl_error_get_code())
        naco_error_reset("Could not get FITS key:");
    else
        skip_if(cpl_propertylist_append_string(qclist, "ESO QC FILTER NDENS",
                                               optiname));
    optiname = naco_pfits_get_opti4_name(plist1);
    if (cpl_error_get_code())
        naco_error_reset("Could not get FITS key:");
    else
        skip_if(cpl_propertylist_append_string(qclist, "ESO QC FILTER POL",
                                               optiname));

    /* QC.AIRMASS */
    mean = 0.5*(naco_pfits_get_airmass_start(plist1) +
                naco_pfits_get_airmass_end(plist2));
    if (cpl_error_get_code()) {
        mean = 0.0;
        naco_error_reset("Could not get FITS key:");
    }
    skip_if(cpl_propertylist_append_double(qclist, "ESO QC AIRMASS", mean));

    /* STREHL */
    cpl_propertylist_append_double(qclist, "ESO QC STREHL", 
            naco_img_strehl_config.strehl);
    cpl_propertylist_append_double(qclist, "ESO QC STREHL FLUX", 
            naco_img_strehl_config.star_flux);
    cpl_propertylist_append_double(qclist, "ESO QC STREHL PEAK", 
            naco_img_strehl_config.star_peak);
    cpl_propertylist_append_double(qclist, "ESO QC STREHL ERROR", 
            naco_img_strehl_config.strehl_err);
    cpl_propertylist_append_double(qclist, "ESO QC STREHL RMS", 
            naco_img_strehl_config.bg_noise);
    cpl_propertylist_append_double(qclist, "ESO QC STREHL POSX", 
            naco_img_strehl_config.pos_x);
    cpl_propertylist_append_double(qclist, "ESO QC STREHL POSY", 
            naco_img_strehl_config.pos_y);

    bug_if(0);

    bug_if (cpl_propertylist_append(paflist, qclist));

    bug_if (cpl_propertylist_copy_property_regexp(qclist, plist1, "^("
                                                  IRPLIB_PFITS_REGEXP_RECAL 
                                                  ")$", 0));

    bug_if (irplib_pfits_set_airmass(qclist, framepair));

    end_skip;

    return cpl_error_get_code();
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Save the recipe product on disk
  @param    set_tot     the input framelist
  @param    parlist     the parameterlist
  @param    qclist      List of QC parameters
  @param    paflist     List of PAF parameters
  @param    ima         the image to save
  @param    framepair   The list of two reduced frames
  @param    pair_id     the pair id
  @return   0 iff everything is ok
 */
/*----------------------------------------------------------------------------*/
static cpl_error_code naco_img_strehl_save(cpl_frameset            * set_tot,
                                           const cpl_parameterlist * parlist,
                                           const cpl_propertylist  * qclist,
                                           const cpl_propertylist  * paflist,
                                           const cpl_image         * ima,
                                           const irplib_framelist  * framepair,
                                           int                       pair_id)
{
    cpl_frameset * rawframes = irplib_frameset_cast(framepair);
    char         * filename  = NULL;


    bug_if(0);

    /* Write the FITS file */
    filename = cpl_sprintf(RECIPE_STRING "%02d" CPL_DFS_FITS, pair_id);
    skip_if (irplib_dfs_save_image(set_tot, parlist, rawframes, ima,
                               CPL_BPP_IEEE_FLOAT, RECIPE_STRING,
                               naco_img_strehl_config.mode == 1
                               ? NACO_IMG_STREHL_RESCAL
                               : NACO_IMG_STREHL_RESTECH, qclist, NULL,
                               naco_pipe_id, filename));


    cpl_free(filename);
    filename = cpl_sprintf(RECIPE_STRING "%02d" CPL_DFS_PAF, pair_id);
    skip_if (cpl_dfs_save_paf("NACO", RECIPE_STRING, paflist, filename));

    end_skip;

    cpl_free(filename);
    cpl_frameset_delete(rawframes);

    return cpl_error_get_code();
}


