/* $Id: irplib_strehl-test.c,v 1.10 2013-01-29 08:43:33 jtaylor Exp $
 *
 * This file is part of the ESO Common Pipeline Library
 * Copyright (C) 2001-2008 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: jtaylor $
 * $Date: 2013-01-29 08:43:33 $
 * $Revision: 1.10 $
 * $Name: not supported by cvs2svn $
 */

/*-----------------------------------------------------------------------------
                                   Includes
 -----------------------------------------------------------------------------*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <cpl_test.h>

#include "irplib_strehl.h"
#include "irplib_utils.h"

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

#ifndef IMAGESZ
#define IMAGESZ 1024
#endif

/*-----------------------------------------------------------------------------
                                   Static functions
 -----------------------------------------------------------------------------*/
static void irplib_strehl_test_one(void);
static cpl_image * irplib_strehl_create(cpl_size, cpl_size,
                                       double, double, double);
static void irplib_strehl_test(const cpl_image *, double, double,
                               double, double, double);

void irplib_strehl_test_fits(const char *, double, double, double);


/*-----------------------------------------------------------------------------
                                  Main
 -----------------------------------------------------------------------------*/
int main (void)
{

    cpl_test_init(PACKAGE_BUGREPORT, CPL_MSG_WARNING);

    irplib_strehl_test_one();

#ifdef IRPLIB_STREHL_FITSDIR
    irplib_strehl_test_fits(IRPLIB_STREHL_FITSDIR
                            "strehl11pc_CWL1p6micron_BW0p05micron_PS12p25mas"
                            ".fits",  12.25e-3, 1.6, 0.05);

    irplib_strehl_test_fits(IRPLIB_STREHL_FITSDIR
                            "strehl24pc_CWL1p6micron_BW0p05micron_PS12p25mas"
                            ".fits",  12.25e-3, 1.6, 0.05);

    irplib_strehl_test_fits(IRPLIB_STREHL_FITSDIR
                            "strehl60pc_CWL1p6micron_BW0p05micron_PS12p25mas"
                            ".fits",  12.25e-3, 1.6, 0.05);

    irplib_strehl_test_fits(IRPLIB_STREHL_FITSDIR
                            "strehl73pc_CWL1p6micron_BW0p05micron_PS12p25mas"
                            ".fits",  12.25e-3, 1.6, 0.05);

    irplib_strehl_test_fits(IRPLIB_STREHL_FITSDIR
                            "strehl74pc_CWL1p6micron_BW0p05micron_PS12p25mas"
                            ".fits",  12.25e-3, 1.6, 0.05);

    irplib_strehl_test_fits(IRPLIB_STREHL_FITSDIR
                            "strehl82pc_CWL1p6micron_BW0p05micron_PS12p25mas"
                            ".fits",  12.25e-3, 1.6, 0.05);
#endif

    return cpl_test_end(0);
}


void irplib_strehl_test_fits(const char * file, double pixscale,
                             double lam, double dlam)
{
    cpl_image * img = cpl_image_load(file, CPL_TYPE_FLOAT, 0, 0);

    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_nonnull(img);

    cpl_msg_info(cpl_func, "Testing %s", file);
    irplib_strehl_test(img, 0.0, 10.0, pixscale, lam, dlam);

    cpl_image_delete(img);
}


/*----------------------------------------------------------------------------*/
/**
  @brief    Test the strehl with specific settings

 */
/*----------------------------------------------------------------------------*/
static void irplib_strehl_test_one(void)
{
    cpl_image * img = irplib_strehl_create(IMAGESZ, IMAGESZ, 1000.0, 1.0, 1.0);

    irplib_strehl_test(img, 1000.0, 1.0, 0.03,
                       2.18, 0.35); /* NACO values */

    cpl_image_delete(img);
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Fill image for strehl testing
  @param    nx          Image X-size
  @param    ny          Image Y-size
  @param    norm        norm of the gaussian.
  @param    sig_x       Sigma in x for the gaussian distribution.
  @param    sig_y       Sigma in y for the gaussian distribution.
  @return   The created image

 */
/*----------------------------------------------------------------------------*/
static cpl_image * irplib_strehl_create(cpl_size nx, cpl_size ny,
                                       double norm,
                                       double sig_x,
                                       double sig_y)
{

    const double noise = FLT_EPSILON;
    cpl_size     mx, my;
    cpl_image * im1 = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
    cpl_error_code code;

    code = cpl_image_fill_gaussian(im1, nx/2, ny/2, norm, sig_x, sig_y);
    cpl_test_eq_error(code, CPL_ERROR_NONE);

    if (noise != 0.0) {
        cpl_image * im0 = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
        code = cpl_image_fill_noise_uniform(im0, -noise, noise);
        cpl_test_eq_error(code, CPL_ERROR_NONE);
        code = cpl_image_subtract(im1, im0);
        cpl_test_eq_error(code, CPL_ERROR_NONE);
        cpl_image_delete(im0);
    }

    code = cpl_image_get_maxpos(im1, &mx, &my);
    cpl_test_eq_error(code, CPL_ERROR_NONE);
    cpl_test_eq(mx, nx/2);
    cpl_test_eq(my, ny/2);

    return im1;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Test Strehl computation
  @param    norm        norm of the gaussian, or zero
  @param    sigma       The detection level
  @param    pixscale    The pixel scale (arcsecs/pixel)
  @param    lam         Central wavelength [micron]
  @param    dlam        Filter bandwidth [micron]
  @return   void

 */
/*----------------------------------------------------------------------------*/
static void irplib_strehl_test(const cpl_image * im1, double norm,
                               double sigma,
                               double pixscale, double lam, double dlam)
{

    const cpl_size nx = cpl_image_get_size_x(im1);
    const cpl_size ny = cpl_image_get_size_y(im1);
    cpl_error_code code;

     /* Sigma-levels for detection of a bright star,  copied from NACO  */
    const double    psigmas[] = {sigma, sigma/2.0, sigma/4.0};
    const size_t    nsigmas = sizeof(psigmas)/sizeof(*psigmas);
    cpl_size        isigma;
    cpl_vector    * sigmas = cpl_vector_wrap(nsigmas, (double*)psigmas);
    cpl_apertures * apert = NULL;
    double          fwhm_x, fwhm_y;
    cpl_size        mx, my;

    double strehl = 0.0, strehl_err = 0.0;
    double star_bg = 0.0, star_peak = 0.0, star_flux = 0.0;
    double psf_peak = 0.0, psf_flux = 0.0, bg_noise = 0.0;

    const double star_radius  = IRPLIB_STREHL_STAR_RADIUS;
    const double background_1 = IRPLIB_STREHL_BACKGROUND_R1;
    const double background_2 = IRPLIB_STREHL_BACKGROUND_R2;

    code = cpl_image_get_maxpos(im1, &mx, &my);
    cpl_test_eq_error(code, CPL_ERROR_NONE);
    cpl_test_leq(mx - nx/4, mx);
    cpl_test_leq(my - ny/4, my);
    cpl_test_leq(mx, mx + nx/4);
    cpl_test_leq(my, my + ny/4);

    apert = cpl_apertures_extract_window(im1, sigmas,
                                         mx - nx/4, my - ny/4,
                                         mx + nx/4, my + ny/4,
                                         &isigma);
    cpl_test_error(CPL_ERROR_NONE);
    cpl_test_nonnull(apert);
    cpl_test_zero(isigma);

    cpl_apertures_delete(apert);
    cpl_test_eq_ptr(cpl_vector_unwrap(sigmas), psigmas);

    cpl_test_lt(0.0, pixscale);

    code = cpl_image_get_fwhm(im1, mx, my, &fwhm_x, &fwhm_y);
    cpl_test_eq_error(code, CPL_ERROR_NONE);

    cpl_msg_info(cpl_func, "Expected star-radius vs. actual FWHM [pixel]: "
                 "%g <=> (%g, %g)", star_radius / pixscale,
                 fwhm_x, fwhm_y);

    cpl_msg_debug(cpl_func, "Inner and outer radius of ring-zone for noise "
                  "estimate [pixel]: %g < %g", background_1 / pixscale,
                  background_2 / pixscale);

    code = irplib_strehl_compute(im1, IRPLIB_STREHL_M1, IRPLIB_STREHL_M2,
                                 lam, dlam, pixscale,
                                 IRPLIB_STREHL_BOX_SIZE, 
                                 mx, my,
                                 star_radius,
                                 background_1,
                                 background_2,
                                 -1, -1,
                                 &strehl,
                                 &strehl_err,
                                 &star_bg,
                                 &star_peak,
                                 &star_flux,
                                 &psf_peak,
                                 &psf_flux,
                                 &bg_noise);
    cpl_test_eq_error(code, CPL_ERROR_NONE);
    cpl_test_leq(0.0, strehl);
    cpl_test_leq(strehl, 1.0);
    cpl_test_leq(0.0, strehl_err);
    if (norm > 0.0) {
        cpl_test_rel(norm, star_flux, 0.01);
    }

    cpl_msg_info(cpl_func, "Strehl: ratio=%g, error=%g; Background: flux=%g, "
                 "noise=%g; Star: peak=%g, flux=%g; PSF: peak=%g, flux=%g",
                 strehl, strehl_err, star_bg, bg_noise, star_peak, star_flux,
                 psf_peak, psf_flux);

}
