
/*********************************************************************
 * E.S.O. - VLT project
 *
 * "@(#) $Id: kmclipm_test_priv_functions.c,v 1.9 2013-10-08 11:12:02 aagudo Exp $"
 *
 * Tests for functions in src/kmclipm_priv_functions.c/.h
 *
 * who       when        what
 * --------  ----------  ----------------------------------------------
 * aagudo     2007-10-19  created
 * aagudo     2007-12-14  added test for new function:
 *                        kmclipm_test_priv_paint_ifu_rectangle()
 *                        (Makes boundary around IFU-images in patrol-view)
 * aagudo     2007-12-17  fixed memory leak in
                          kmclipm_test_priv_create_patrol_view()
 * aagudo     2008-06-06  updated calls to test functions because of new testlib
 * aagudo     2008-06-24  added test for kmclipm_priv_paint_ifu_rectangle_rtd()
 */

/**
    @defgroup kmclipm_test_priv_functions Unit tests for the private functions in kmclipm_priv_functions

    Unit test for the functions in the module @ref kmclipm_priv_functions

    @{
*/

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

#include <float.h>
#include <math.h>
#include <sys/wait.h>
#include <sys/stat.h>

#include "kmclipm_constants.h"
#include "kmclipm_priv_constants.h"
#include "kmclipm_priv_reconstruct.h"
#include "kmclipm_priv_functions.h"
#include "kmclipm_priv_error.h"

cpl_msg_severity my_level= CPL_MSG_WARNING;

/*------------------------------------------------------------------------------
 *              Implementation
 *----------------------------------------------------------------------------*/

/**
    @brief  Routine to test kmclipm_priv_paste_ifu_images()
 */
void test_kmclipm_priv_paste_ifu_images()
{
    double        ret_val = 0.0;
    cpl_image     *img = NULL,
                  *img2 = NULL,
                  *img3 = NULL;

    /* ----- test with wrong values ----- */
    /* 2 NULL pointers & 2 negative values*/
    img = NULL;

    ret_val = kmclipm_priv_paste_ifu_images(NULL, &img, -1, -1);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    /* 1 NULL pointers  & 2 negative values*/
    img2 = cpl_image_new(2, 2, CPL_TYPE_FLOAT);
    cpl_image_set(img2, 1, 1, 9.9);
    cpl_image_set(img2, 1, 2, 9.9);
    cpl_image_set(img2, 2, 1, 9.9);
    cpl_image_set(img2, 2, 2, 9.9);

    ret_val = kmclipm_priv_paste_ifu_images(img2, &img, -1, -1);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    ret_val += ret_val; /* against warnings [-Wunused-but-set-variable] */

    /* ----- test with correct values ----- */

    /* 2 negative values*/
    img = cpl_image_new(4, 4, CPL_TYPE_FLOAT);

    cpl_test_abs(kmclipm_priv_paste_ifu_images(img2, &img, -1, -1), 9.9, 1e-6);

    /* 1 negative value*/
    cpl_test_abs(kmclipm_priv_paste_ifu_images(img2, &img, -3, -1), 9.9, 1e-6);

    /* to big value*/
    cpl_test_abs(kmclipm_priv_paste_ifu_images(img2, &img, 4, -1), 9.9, 1e-6);

    /* to big value*/
    cpl_test_abs(kmclipm_priv_paste_ifu_images(img2, &img, 3, 4), 9.9, 1e-6);

    /* ok value */
    cpl_test_abs(kmclipm_priv_paste_ifu_images(img2, &img, 3, 3), 9.9, 1e-6);

    /* --- check result (subtract generated image from img3) */
    img3 = cpl_image_new(4, 4, CPL_TYPE_FLOAT);
    cpl_image_set(img3, 3, 3, 9.9);
    cpl_image_set(img3, 3, 4, 9.9);
    cpl_image_set(img3, 4, 3, 9.9);
    cpl_image_set(img3, 4, 4, 9.9);

    /* Assert that images are equal */
    cpl_image_subtract(img, img3);
    cpl_test_abs(cpl_image_get_max(img), 0.0, 0.001);

    cpl_image_delete(img);
    cpl_image_delete(img2);
    cpl_image_delete(img3);

    cpl_test_error(CPL_ERROR_NONE);
}

/**
    @brief  Routine to test kmclipm_priv_create_patrol_view()
 */
void test_kmclipm_priv_create_patrol_view()
{
    float        *img_data  = NULL;

    cpl_image    *img       = NULL,
                 *img2      = NULL,
                 *ret_ptr   = NULL;

    cpl_imagelist *img_list = NULL;

    int           i         = 0,
                  j         = 0;

    const int     ifu_id_zero[] = {-1,
                                   0,0,0,0,0,0,0,0,0,0,0,0,
                                   0,0,0,0,0,0,0,0,0,0,0,0},
                  ifu_id_mixed[] = {-1,
                                   1,0,1,0,1,0,1,0,1,0,1,1,
                                   0,1,0,0,0,0,0,0,0,0,0,0};

    const double  nominal_pos[] = {-1.0, -1.0,
                                   -111.66474, 31.4259,   /* IFU_1_x, IFU_1_y */
                                   -71.01666, 46.6983,    /* IFU_2*/
                                   -28.72386, 13.56894,   /* IFU_3 */
                                   -15.33114, 0.64614,    /* IFU_4 */
                                   111.54726, 16.85838,   /* IFU_5 */
                                   34.83282, 93.1029,     /* IFU_6 */
                                   -10.39698, -113.42694, /* IFU_7 */
                                   50.10522, -112.25214,  /* IFU_8 */
                                   -50, -30,              /* IFU_9 */
                                   -10, -80,              /* IFU_10 */
                                   -50, 15,               /* IFU_11 */
                                   10, -100,              /* IFU_12 */
                                   15, 15,                /* IFU_13 */
                                   126, 0,                /* IFU_14 */
                                   -10, -10,              /* IFU_15 */
                                   89.716577, 89.716577,  /* IFU_16 */
                                   50, -40,               /* IFU_17 */
                                   40, -20,               /* IFU_18 */
                                   70, -10,               /* IFU_19 */
                                   -10, 50,               /* IFU_20 */
                                   -40, 80,               /* IFU_21 */
                                   -70, 0,                /* IFU_22 */
                                   60, 10,                /* IFU_23 */
                                   70, 50 };              /* IFU_24 */    

    /* ----- test with wrong values ----- */
    /* 3 NULL pointers */
    ret_ptr = kmclipm_priv_create_patrol_view(NULL, NULL, NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    /* empty image list and 2 NULL pointers */
    img_list = cpl_imagelist_new();
    ret_ptr = kmclipm_priv_create_patrol_view(img_list, NULL, NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    /* empty image list, nominal_pos and 1 NULL pointer */
    ret_ptr = kmclipm_priv_create_patrol_view(img_list, nominal_pos, NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    /* too large img_list ( greater than KMOS_NR_IFUS) */
    for (j = 0; j <= KMOS_NR_IFUS; j++) {
        img2 = cpl_image_new(KMOS_SLITLET_X, KMOS_SLITLET_Y, CPL_TYPE_FLOAT);
        cpl_imagelist_set(img_list, img2, j);
    }
    ret_ptr = kmclipm_priv_create_patrol_view(img_list, nominal_pos,
                                              ifu_id_zero);
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);

    cpl_imagelist_delete(img_list);

    /* empty image list, nominal_pos is ok and ifu_id is mixed*/
    img_list = cpl_imagelist_new();

    ret_ptr = kmclipm_priv_create_patrol_view(img_list, nominal_pos,
                                              ifu_id_mixed);
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);
    cpl_imagelist_delete(img_list);

    /* empty image list, nominal_pos and ifu_id is zero */
    img_list = cpl_imagelist_new();
    ret_ptr = kmclipm_priv_create_patrol_view(img_list, nominal_pos,
                                              ifu_id_zero);
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);

    /* ----- test with correct values ----- */

    /* non-empty image list, nominal_pos is ok and ifu_id is zero*/

    for (j = 0; j < KMOS_NR_DETECTORS * KMOS_IFUS_PER_DETECTOR; j++) {
        img = cpl_image_new(KMOS_SLITLET_X, KMOS_SLITLET_Y, CPL_TYPE_FLOAT);
        img_data = cpl_image_get_data_float(img);
        for (i = 0; i < KMOS_SLITLET_X * KMOS_SLITLET_Y; i++) {
            img_data[i] = 66.0;
        }
        cpl_imagelist_set(img_list, img, j);
    }

    ret_ptr = kmclipm_priv_create_patrol_view(img_list, nominal_pos,
                                              ifu_id_zero);
    cpl_test_nonnull(ret_ptr);

    cpl_image_delete(ret_ptr); ret_ptr = NULL;
    cpl_imagelist_delete(img_list); img_list = NULL;

    cpl_test_error(CPL_ERROR_NONE);
}

/**
    @brief  Routine to test kmclipm_priv_paint_ifu_rectangle_patrol()
 */
void test_kmclipm_priv_paint_ifu_rectangle_patrol()
{
    cpl_image     *img = NULL;

    /* ----- test with wrong values ----- */
    /* NULL pointers & 2 negative values*/
    kmclipm_priv_paint_ifu_rectangle_patrol(&img, -1, -1, 99.0);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    /* ----- test with correct values ----- */
    img = cpl_image_new(PATROL_FIELD_SIZE, PATROL_FIELD_SIZE, CPL_TYPE_FLOAT);
    kmclipm_priv_paint_ifu_rectangle_patrol(&img, -1, -1, 99.0);
    kmclipm_priv_paint_ifu_rectangle_patrol(&img, 2, -1, 99.0);
    kmclipm_priv_paint_ifu_rectangle_patrol(&img, 2, 10, 99.0);
    kmclipm_priv_paint_ifu_rectangle_patrol(&img, 10, 2, 99.0);

    cpl_image_delete(img);

    cpl_test_error(CPL_ERROR_NONE);
}

/**
    @brief  Routine to test kmclipm_priv_paint_ifu_rectangle_rtd()
 */
void test_kmclipm_priv_paint_ifu_rectangle_rtd()
{
    cpl_image     *img = NULL;

    int ids[] = {  -1,
                    1,  1,  2,  1,  1,  1,  1,  1,
                    1,  1,  1,  1,  1,  1,  1,  1,
                    0,  1,  1,  1,  1,  1,  1,  1};

    /* ----- test with wrong values ----- */
    /* 2 NULL pointers */
    kmclipm_priv_paint_ifu_rectangle_rtd(&img, NULL, 99.0);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    /* 1 NULL pointers */
    img = cpl_image_new(2, 2, CPL_TYPE_FLOAT);
    kmclipm_priv_paint_ifu_rectangle_rtd(&img, NULL, 99.0);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    /* to small image*/
    kmclipm_priv_paint_ifu_rectangle_rtd(&img, ids, 99.0);
    cpl_test_error(CPL_ERROR_ILLEGAL_INPUT);
    cpl_image_delete(img);

    /* ----- test with correct values ----- */
    img = cpl_image_new(kmclipm_priv_get_rtd_width(),
                        kmclipm_priv_get_rtd_height(),
                        CPL_TYPE_FLOAT);
    kmclipm_priv_paint_ifu_rectangle_rtd(&img, ids, 99.0);
    cpl_test_error(CPL_ERROR_NONE);

    cpl_image_delete(img);

    cpl_test_error(CPL_ERROR_NONE);
}

/**
    @brief  Routine to test kmclipm_priv_gauss2d_fnc()
 */
void test_kmclipm_priv_gauss2d_fnc()
{
    double  x[]     = {100, 130},
            a[]     = {1, 100, 130, 5, 0};

    double  result  = 0.0,
            tol     = 0.001;

    int     r = 0;

    /* ----- invalid test ----- */

    /* ----- valid test  ----- */
    r = kmclipm_priv_gauss2d_fnc(x, a, &result);
    cpl_test_eq(r, 0);
    cpl_test_abs(result, 1.0, tol);

    x[0] = 98, x[1] = 128;
    r = kmclipm_priv_gauss2d_fnc(x, a, &result);
    cpl_test_eq(r, 0);
    cpl_test_abs(result, 0.411796, tol);

    x[0] = 100, x[1] = 130;
    a[0] = 3; a[4] = 2;
    r = kmclipm_priv_gauss2d_fnc(x, a, &result);
    cpl_test_eq(r, 0);
    cpl_test_abs(result, 5.0, tol);

    cpl_test_error(CPL_ERROR_NONE);
}

/**
    @brief  Routine to test kmclipm_priv_gauss2d_fncd()
 */
void test_kmclipm_priv_gauss2d_fncd()
{
    double  x[]     = {100, 130},
            a[]     = {1, 100, 130, 5},
            res[]   = {0, 0, 0, 0, 0};

    double  tol     = 0.001;

    int     r = 0;

    /* ----- invalid test ----- */

    /* ----- valid test  ----- */
    r = kmclipm_priv_gauss2d_fncd(x, a, res);
    cpl_test_eq(r, 0);
    cpl_test_abs(res[0], 1.0, tol);
    cpl_test_abs(res[1], 0.0, tol);
    cpl_test_abs(res[2], 0.0, tol);
    cpl_test_abs(res[3], 0.0, tol);
    cpl_test_abs(res[4], 1.0, tol);

    x[0] = 98, x[1] = 128;
    r = kmclipm_priv_gauss2d_fncd(x, a, res);
    cpl_test_eq(r, 0);
    cpl_test_abs(res[0], 0.411796, tol);
    cpl_test_abs(res[1], -0.182678, tol);
    cpl_test_abs(res[2], -0.182678, tol);
    cpl_test_abs(res[3], 0.146143, tol);
    cpl_test_abs(res[4], 1.0, tol);

    cpl_test_error(CPL_ERROR_NONE);
}

/**
    @brief  Routine to test kmclipm_priv_debug_vector()
 */
void test_kmclipm_priv_debug_vector()
{
    cpl_vector *x = NULL;

    /* ----- invalid test ----- */
    kmclipm_priv_debug_vector(x);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    /* ----- valid test  ----- */
    x = cpl_vector_new(3);
    cpl_vector_set(x, 0, 5.5);
    cpl_vector_set(x, 1, 6.6);
    cpl_vector_set(x, 2, 7.7);
    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_priv_debug_vector(x));
    cpl_vector_delete(x);

    cpl_test_error(CPL_ERROR_NONE);
}

/**
    @brief  Routine to test kmclipm_priv_debug_image()
 */
void test_kmclipm_priv_debug_image()
{
    cpl_image *x = NULL;

    /* ----- invalid test ----- */
    kmclipm_priv_debug_image(x);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    /* ----- valid test  ----- */
    x = cpl_image_new(2, 2, CPL_TYPE_FLOAT);
    cpl_image_set(x, 1, 1, 1);
    cpl_image_set(x, 1, 2, 2);
    cpl_image_set(x, 2, 1, 3);
    cpl_image_set(x, 2, 2, 4);
    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_priv_debug_image(x));
    cpl_image_delete(x);

    cpl_test_error(CPL_ERROR_NONE);
}

/**
    @brief  Routine to test kmclipm_priv_debug_cube()
 */
void test_kmclipm_priv_debug_cube()
{
    cpl_imagelist   *x = NULL;
    cpl_image       *img = NULL;

    /* ----- invalid test ----- */
    kmclipm_priv_debug_cube(x);
    cpl_test_error(CPL_ERROR_NULL_INPUT);

    /* ----- valid test  ----- */
    x = cpl_imagelist_new();
    img = cpl_image_new(2, 2, CPL_TYPE_FLOAT);
    cpl_image_set(img, 1, 1, 1);
    cpl_image_set(img, 1, 2, 2);
    cpl_image_set(img, 2, 1, 3);
    cpl_image_set(img, 2, 2, 4);
    cpl_imagelist_set(x, img, 0);
    img = cpl_image_new(2, 2, CPL_TYPE_FLOAT);
    cpl_image_set(img, 1, 1, 5);
    cpl_image_set(img, 1, 2, 6);
    cpl_image_set(img, 2, 1, 7);
    cpl_image_set(img, 2, 2, 8);
    cpl_imagelist_set(x, img, 1);
    cpl_test_eq(CPL_ERROR_NONE,
                kmclipm_priv_debug_cube(x));
    cpl_imagelist_delete(x);

    cpl_test_error(CPL_ERROR_NONE);
}

void test_kmclipm_priv_find_angle()
{
    char    *filt           = NULL,
            *grat           = NULL,
            *fn_xcal        = NULL,
            *fn_ycal        = NULL,
            *fn_lcal        = NULL,
            *fn_lut         = NULL,
            *my_path        = NULL;

    cpl_propertylist *pl    = cpl_propertylist_new();
    filt = cpl_sprintf("%s", "K");
    grat = cpl_sprintf("%s", "K");

    /* --- invalid tests --- */
    cpl_msg_set_level(CPL_MSG_OFF);
    kmclipm_priv_find_angle(0, NULL, NULL, NULL, NULL, NULL, NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);
    kmclipm_priv_find_angle(0, &filt, NULL, NULL, NULL, NULL, NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);
    kmclipm_priv_find_angle(0, &filt, &grat, NULL, NULL, NULL, NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);
    kmclipm_priv_find_angle(0, &filt, &grat, &fn_xcal, NULL, NULL, NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);
    kmclipm_priv_find_angle(0, &filt, &grat, &fn_xcal, &fn_ycal, NULL, NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);
    kmclipm_priv_find_angle(0, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, NULL);
    cpl_test_error(CPL_ERROR_NULL_INPUT);
    kmclipm_priv_find_angle(0, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    cpl_test_error(CPL_ERROR_FILE_IO);

    my_path = cpl_sprintf("%s/test_data/bbb/", ".");
    kmclipm_set_cal_path(my_path, FALSE);
    cpl_free(my_path);
    my_path = cpl_sprintf("mkdir -p %s/test_data/bbb/", ".");
    system(my_path);
    cpl_free(my_path);
    kmclipm_priv_find_angle(0, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    cpl_free(fn_xcal);
    cpl_free(fn_ycal);
    cpl_free(fn_lcal);
    cpl_free(fn_lut);
    cpl_test_error(CPL_ERROR_ILLEGAL_OUTPUT);

    cpl_propertylist_update_double(pl, ROTANGLE, 0);
    cpl_propertylist_update_string(pl, "ESO INS FILT1 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS FILT2 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS FILT3 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS GRAT1 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS GRAT2 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS GRAT3 ID", "K");
    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "XCAL");
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK0.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);
    kmclipm_priv_find_angle(0, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    cpl_free(fn_xcal);
    cpl_free(fn_ycal);
    cpl_free(fn_lcal);
    cpl_free(fn_lut);
    cpl_test_error(CPL_ERROR_ILLEGAL_OUTPUT);

    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "YCAL");
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK0.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);
    kmclipm_priv_find_angle(0, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    cpl_free(fn_xcal);
    cpl_free(fn_ycal);
    cpl_free(fn_lcal);
    cpl_free(fn_lut);
    cpl_test_error(CPL_ERROR_ILLEGAL_OUTPUT);

    cpl_msg_set_level(my_level);

    /* --- valid tests --- */

    /* create cal0 */
    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "LCAL");
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK0.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);
    kmclipm_priv_find_angle(0, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK0.fits", ".");
    cpl_test_eq_string(fn_xcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK0.fits", ".");
    cpl_test_eq_string(fn_ycal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK0.fits", ".");
    cpl_test_eq_string(fn_lcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lut_KKK_0", ".");
    cpl_test_eq_string(fn_lut, my_path);
    cpl_free(my_path);
    cpl_free(fn_xcal); fn_xcal = NULL;
    cpl_free(fn_ycal); fn_ycal = NULL;
    cpl_free(fn_lcal); fn_lcal = NULL;
    cpl_free(fn_lut);  fn_lut  = NULL;
    cpl_test_error(CPL_ERROR_NONE);

    /* create cal60 */
    cpl_propertylist_update_double(pl, ROTANGLE, 60);
    cpl_propertylist_update_string(pl, "ESO INS FILT1 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS FILT2 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS FILT3 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS GRAT1 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS GRAT2 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS GRAT3 ID", "K");
    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "XCAL");
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK60.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);
    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "YCAL");
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK60.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);
    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "LCAL");
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK60.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);
    kmclipm_priv_find_angle(0, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK0.fits", ".");
    cpl_test_eq_string(fn_xcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK0.fits", ".");
    cpl_test_eq_string(fn_ycal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK0.fits", ".");
    cpl_test_eq_string(fn_lcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lut_KKK_0", ".");
    cpl_test_eq_string(fn_lut, my_path);
    cpl_free(my_path);
    cpl_free(fn_xcal); fn_xcal = NULL;
    cpl_free(fn_ycal); fn_ycal = NULL;
    cpl_free(fn_lcal); fn_lcal = NULL;
    cpl_free(fn_lut);  fn_lut  = NULL;
    cpl_test_error(CPL_ERROR_NONE);

    kmclipm_priv_find_angle(60, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK60.fits", ".");
    cpl_test_eq_string(fn_xcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK60.fits", ".");
    cpl_test_eq_string(fn_ycal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK60.fits", ".");
    cpl_test_eq_string(fn_lcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lut_KKK_60", ".");
    cpl_test_eq_string(fn_lut, my_path);
    cpl_free(my_path);
    cpl_free(fn_xcal); fn_xcal = NULL;
    cpl_free(fn_ycal); fn_ycal = NULL;
    cpl_free(fn_lcal); fn_lcal = NULL;
    cpl_free(fn_lut);  fn_lut  = NULL;
    cpl_test_error(CPL_ERROR_NONE);

    kmclipm_priv_find_angle(45, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK60.fits", ".");
    cpl_test_eq_string(fn_xcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK60.fits", ".");
    cpl_test_eq_string(fn_ycal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK60.fits", ".");
    cpl_test_eq_string(fn_lcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lut_KKK_60", ".");
    cpl_test_eq_string(fn_lut, my_path);
    cpl_free(my_path);
    cpl_free(fn_xcal); fn_xcal = NULL;
    cpl_free(fn_ycal); fn_ycal = NULL;
    cpl_free(fn_lcal); fn_lcal = NULL;
    cpl_free(fn_lut);  fn_lut  = NULL;
    cpl_test_error(CPL_ERROR_NONE);

    kmclipm_priv_find_angle(-1, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK0.fits", ".");
    cpl_test_eq_string(fn_xcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK0.fits", ".");
    cpl_test_eq_string(fn_ycal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK0.fits", ".");
    cpl_test_eq_string(fn_lcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lut_KKK_0", ".");
    cpl_test_eq_string(fn_lut, my_path);
    cpl_free(my_path);
    cpl_free(fn_xcal); fn_xcal = NULL;
    cpl_free(fn_ycal); fn_ycal = NULL;
    cpl_free(fn_lcal); fn_lcal = NULL;
    cpl_free(fn_lut);  fn_lut  = NULL;
    cpl_test_error(CPL_ERROR_NONE);

    kmclipm_priv_find_angle(29.9, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK0.fits", ".");
    cpl_test_eq_string(fn_xcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK0.fits", ".");
    cpl_test_eq_string(fn_ycal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK0.fits", ".");
    cpl_test_eq_string(fn_lcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lut_KKK_0", ".");
    cpl_test_eq_string(fn_lut, my_path);
    cpl_free(my_path);
    cpl_free(fn_xcal); fn_xcal = NULL;
    cpl_free(fn_ycal); fn_ycal = NULL;
    cpl_free(fn_lcal); fn_lcal = NULL;
    cpl_free(fn_lut);  fn_lut  = NULL;
    cpl_test_error(CPL_ERROR_NONE);

    kmclipm_priv_find_angle(30, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK0.fits", ".");
    cpl_test_eq_string(fn_xcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK0.fits", ".");
    cpl_test_eq_string(fn_ycal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK0.fits", ".");
    cpl_test_eq_string(fn_lcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lut_KKK_0", ".");
    cpl_test_eq_string(fn_lut, my_path);
    cpl_free(my_path);
    cpl_free(fn_xcal); fn_xcal = NULL;
    cpl_free(fn_ycal); fn_ycal = NULL;
    cpl_free(fn_lcal); fn_lcal = NULL;
    cpl_free(fn_lut);  fn_lut  = NULL;
    cpl_test_error(CPL_ERROR_NONE);

    kmclipm_priv_find_angle(30.1, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK60.fits", ".");
    cpl_test_eq_string(fn_xcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK60.fits", ".");
    cpl_test_eq_string(fn_ycal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK60.fits", ".");
    cpl_test_eq_string(fn_lcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lut_KKK_60", ".");
    cpl_test_eq_string(fn_lut, my_path);
    cpl_free(my_path);
    cpl_free(fn_xcal); fn_xcal = NULL;
    cpl_free(fn_ycal); fn_ycal = NULL;
    cpl_free(fn_lcal); fn_lcal = NULL;
    cpl_free(fn_lut);  fn_lut  = NULL;
    cpl_test_error(CPL_ERROR_NONE);

    /* create cal120 */
    cpl_propertylist_update_double(pl, ROTANGLE, 120);
    cpl_propertylist_update_string(pl, "ESO INS FILT1 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS FILT2 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS FILT3 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS GRAT1 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS GRAT2 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS GRAT3 ID", "K");
    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "XCAL");
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK120.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);
    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "YCAL");
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK120.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);
    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "LCAL");
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK120.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);

    /* create cal180 */
    cpl_propertylist_update_double(pl, ROTANGLE, 180);
    cpl_propertylist_update_string(pl, "ESO INS FILT1 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS FILT2 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS FILT3 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS GRAT1 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS GRAT2 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS GRAT3 ID", "K");
    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "XCAL");
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK180.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);
    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "YCAL");
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK180.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);
    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "LCAL");
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK180.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);

    /* create cal240 */
    cpl_propertylist_update_double(pl, ROTANGLE, 240);
    cpl_propertylist_update_string(pl, "ESO INS FILT1 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS FILT2 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS FILT3 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS GRAT1 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS GRAT2 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS GRAT3 ID", "K");
    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "XCAL");
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK240.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);
    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "YCAL");
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK240.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);
    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "LCAL");
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK240.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);

    /* create cal300 */
    cpl_propertylist_update_double(pl, ROTANGLE, 300);
    cpl_propertylist_update_string(pl, "ESO INS FILT1 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS FILT2 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS FILT3 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS GRAT1 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS GRAT2 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS GRAT3 ID", "K");
    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "XCAL");
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK300.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);
    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "YCAL");
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK300.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);
    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "LCAL");
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK300.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);

    kmclipm_priv_find_angle(-1, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK0.fits", ".");
    cpl_test_eq_string(fn_xcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK0.fits", ".");
    cpl_test_eq_string(fn_ycal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK0.fits", ".");
    cpl_test_eq_string(fn_lcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lut_KKK_0", ".");
    cpl_test_eq_string(fn_lut, my_path);
    cpl_free(my_path);
    cpl_free(fn_xcal); fn_xcal = NULL;
    cpl_free(fn_ycal); fn_ycal = NULL;
    cpl_free(fn_lcal); fn_lcal = NULL;
    cpl_free(fn_lut);  fn_lut  = NULL;
    cpl_test_error(CPL_ERROR_NONE);

    kmclipm_priv_find_angle(299.9, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK300.fits", ".");
    cpl_test_eq_string(fn_xcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK300.fits", ".");
    cpl_test_eq_string(fn_ycal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK300.fits", ".");
    cpl_test_eq_string(fn_lcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lut_KKK_300", ".");
    cpl_test_eq_string(fn_lut, my_path);
    cpl_free(my_path);
    cpl_free(fn_xcal); fn_xcal = NULL;
    cpl_free(fn_ycal); fn_ycal = NULL;
    cpl_free(fn_lcal); fn_lcal = NULL;
    cpl_free(fn_lut);  fn_lut  = NULL;
    cpl_test_error(CPL_ERROR_NONE);

    kmclipm_priv_find_angle(330.1, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK0.fits", ".");
    cpl_test_eq_string(fn_xcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK0.fits", ".");
    cpl_test_eq_string(fn_ycal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK0.fits", ".");
    cpl_test_eq_string(fn_lcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lut_KKK_0", ".");
    cpl_test_eq_string(fn_lut, my_path);
    cpl_free(my_path);
    cpl_free(fn_xcal); fn_xcal = NULL;
    cpl_free(fn_ycal); fn_ycal = NULL;
    cpl_free(fn_lcal); fn_lcal = NULL;
    cpl_free(fn_lut);  fn_lut  = NULL;
    cpl_test_error(CPL_ERROR_NONE);

    kmclipm_priv_find_angle(330, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK300.fits", ".");
    cpl_test_eq_string(fn_xcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK300.fits", ".");
    cpl_test_eq_string(fn_ycal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK300.fits", ".");
    cpl_test_eq_string(fn_lcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lut_KKK_300", ".");
    cpl_test_eq_string(fn_lut, my_path);
    cpl_free(my_path);
    cpl_free(fn_xcal); fn_xcal = NULL;
    cpl_free(fn_ycal); fn_ycal = NULL;
    cpl_free(fn_lcal); fn_lcal = NULL;
    cpl_free(fn_lut);  fn_lut  = NULL;
    cpl_test_error(CPL_ERROR_NONE);

    kmclipm_priv_find_angle(329.9, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK300.fits", ".");
    cpl_test_eq_string(fn_xcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK300.fits", ".");
    cpl_test_eq_string(fn_ycal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK300.fits", ".");
    cpl_test_eq_string(fn_lcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lut_KKK_300", ".");
    cpl_test_eq_string(fn_lut, my_path);
    cpl_free(my_path);
    cpl_free(fn_xcal); fn_xcal = NULL;
    cpl_free(fn_ycal); fn_ycal = NULL;
    cpl_free(fn_lcal); fn_lcal = NULL;
    cpl_free(fn_lut);  fn_lut  = NULL;
    cpl_test_error(CPL_ERROR_NONE);

    kmclipm_priv_find_angle(-29.9, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK0.fits", ".");
    cpl_test_eq_string(fn_xcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK0.fits", ".");
    cpl_test_eq_string(fn_ycal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK0.fits", ".");
    cpl_test_eq_string(fn_lcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lut_KKK_0", ".");
    cpl_test_eq_string(fn_lut, my_path);
    cpl_free(my_path);
    cpl_free(fn_xcal); fn_xcal = NULL;
    cpl_free(fn_ycal); fn_ycal = NULL;
    cpl_free(fn_lcal); fn_lcal = NULL;
    cpl_free(fn_lut);  fn_lut  = NULL;
    cpl_test_error(CPL_ERROR_NONE);

    kmclipm_priv_find_angle(-30, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK300.fits", ".");
    cpl_test_eq_string(fn_xcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK300.fits", ".");
    cpl_test_eq_string(fn_ycal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK300.fits", ".");
    cpl_test_eq_string(fn_lcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lut_KKK_300", ".");
    cpl_test_eq_string(fn_lut, my_path);
    cpl_free(my_path);
    cpl_free(fn_xcal); fn_xcal = NULL;
    cpl_free(fn_ycal); fn_ycal = NULL;
    cpl_free(fn_lcal); fn_lcal = NULL;
    cpl_free(fn_lut);  fn_lut  = NULL;
    cpl_test_error(CPL_ERROR_NONE);

    kmclipm_priv_find_angle(-30.1, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK300.fits", ".");
    cpl_test_eq_string(fn_xcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK300.fits", ".");
    cpl_test_eq_string(fn_ycal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK300.fits", ".");
    cpl_test_eq_string(fn_lcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lut_KKK_300", ".");
    cpl_test_eq_string(fn_lut, my_path);
    cpl_free(my_path);
    cpl_free(fn_xcal); fn_xcal = NULL;
    cpl_free(fn_ycal); fn_ycal = NULL;
    cpl_free(fn_lcal); fn_lcal = NULL;
    cpl_free(fn_lut);  fn_lut  = NULL;
    cpl_test_error(CPL_ERROR_NONE);

    kmclipm_priv_find_angle(-362, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK0.fits", ".");
    cpl_test_eq_string(fn_xcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK0.fits", ".");
    cpl_test_eq_string(fn_ycal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK0.fits", ".");
    cpl_test_eq_string(fn_lcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lut_KKK_0", ".");
    cpl_test_eq_string(fn_lut, my_path);
    cpl_free(my_path);
    cpl_free(fn_xcal); fn_xcal = NULL;
    cpl_free(fn_ycal); fn_ycal = NULL;
    cpl_free(fn_lcal); fn_lcal = NULL;
    cpl_free(fn_lut);  fn_lut  = NULL;
    cpl_test_error(CPL_ERROR_NONE);

    /* create cal0 H-band */
    cpl_propertylist_update_double(pl, ROTANGLE, 0);
    cpl_propertylist_update_string(pl, "ESO INS FILT1 ID", "H");
    cpl_propertylist_update_string(pl, "ESO INS FILT2 ID", "H");
    cpl_propertylist_update_string(pl, "ESO INS FILT3 ID", "H");
    cpl_propertylist_update_string(pl, "ESO INS GRAT1 ID", "H");
    cpl_propertylist_update_string(pl, "ESO INS GRAT2 ID", "H");
    cpl_propertylist_update_string(pl, "ESO INS GRAT3 ID", "H");
    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "XCAL");
    my_path = cpl_sprintf("%s/test_data/bbb/xcalH0.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);
    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "YCAL");
    my_path = cpl_sprintf("%s/test_data/bbb/ycalH0.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);
    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "LCAL");
    my_path = cpl_sprintf("%s/test_data/bbb/lcalH0.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);
    kmclipm_priv_find_angle(0, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    my_path = cpl_sprintf("%s/test_data/bbb/xcalK0.fits", ".");
    cpl_test_eq_string(fn_xcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/ycalK0.fits", ".");
    cpl_test_eq_string(fn_ycal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lcalK0.fits", ".");
    cpl_test_eq_string(fn_lcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lut_KKK_0", ".");
    cpl_test_eq_string(fn_lut, my_path);
    cpl_free(my_path);
    cpl_free(fn_xcal); fn_xcal = NULL;
    cpl_free(fn_ycal); fn_ycal = NULL;
    cpl_free(fn_lcal); fn_lcal = NULL;
    cpl_free(fn_lut);  fn_lut  = NULL;
    cpl_test_error(CPL_ERROR_NONE);

    cpl_free(filt); filt = cpl_sprintf("H");
    cpl_free(grat); grat = cpl_sprintf("H");
    kmclipm_priv_find_angle(2, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    my_path = cpl_sprintf("%s/test_data/bbb/xcalH0.fits", ".");
    cpl_test_eq_string(fn_xcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/ycalH0.fits", ".");
    cpl_test_eq_string(fn_ycal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lcalH0.fits", ".");
    cpl_test_eq_string(fn_lcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/bbb/lut_HHH_0", ".");
    cpl_test_eq_string(fn_lut, my_path);
    cpl_free(my_path);
    cpl_free(fn_xcal); fn_xcal = NULL;
    cpl_free(fn_ycal); fn_ycal = NULL;
    cpl_free(fn_lcal); fn_lcal = NULL;
    cpl_free(fn_lut);  fn_lut  = NULL;
    cpl_test_error(CPL_ERROR_NONE);

    /* create cal-test */
    my_path = cpl_sprintf("%s/test_data/ccc/", ".");
    kmclipm_set_cal_path(my_path, FALSE);
    cpl_free(my_path);
    my_path = cpl_sprintf("mkdir -p %s/test_data/ccc/", ".");
    system(my_path);
    cpl_free(my_path);

    cpl_propertylist_update_double(pl, ROTANGLE, 0);
    cpl_propertylist_update_string(pl, "ESO INS FILT1 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS FILT2 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS FILT3 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS GRAT1 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS GRAT2 ID", "K");
    cpl_propertylist_update_string(pl, "ESO INS GRAT3 ID", "K");
    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "XCAL");
    my_path = cpl_sprintf("%s/test_data/ccc/xcal_test.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);
    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "YCAL");
    my_path = cpl_sprintf("%s/test_data/ccc/ycal_test.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);
    cpl_propertylist_update_string(pl, CPL_DFS_PRO_CATG, "LCAL");
    my_path = cpl_sprintf("%s/test_data/ccc/lcal_test.fits", ".");
    cpl_propertylist_save(pl, my_path, CPL_IO_CREATE);
    cpl_free(my_path);
    cpl_free(filt); filt = cpl_sprintf("K");
    cpl_free(grat); grat = cpl_sprintf("K");
    kmclipm_priv_find_angle(0, &filt, &grat, &fn_xcal, &fn_ycal, &fn_lcal, &fn_lut);
    my_path = cpl_sprintf("%s/test_data/ccc/xcal_test.fits", ".");
    cpl_test_eq_string(fn_xcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/ccc/ycal_test.fits", ".");
    cpl_test_eq_string(fn_ycal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/ccc/lcal_test.fits", ".");
    cpl_test_eq_string(fn_lcal, my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("%s/test_data/ccc/lut_KKK_0", ".");
    cpl_test_eq_string(fn_lut,  my_path);
    cpl_free(my_path);
    cpl_free(fn_xcal); fn_xcal = NULL;
    cpl_free(fn_ycal); fn_ycal = NULL;
    cpl_free(fn_lcal); fn_lcal = NULL;
    cpl_free(fn_lut);  fn_lut  = NULL;
    cpl_test_error(CPL_ERROR_NONE);

    /* test additionally if filt & grat have been changed from H to K */
    cpl_test_eq_string(filt, "K");
    cpl_test_eq_string(grat, "K");

    cpl_free(filt); filt = NULL;
    cpl_free(grat); grat = NULL;
    cpl_propertylist_delete(pl); pl = NULL;

    my_path = cpl_sprintf("rm -rf %s/test_data/bbb", ".");
    system(my_path);
    cpl_free(my_path);
    my_path = cpl_sprintf("rm -rf %s/test_data/ccc", ".");
    system(my_path);
    cpl_free(my_path);
}

/**
    @brief  Executes the unit tests for all functions in the module
            @ref kmclipm_priv_functions
    @param  argc    ...
    @param  argv    ...
    @return Error code, 0 for NONE.
 */
int main()
{
    /* For DEVELOPERS:
     * to omit all CPL_MSG_INFO output, apply following statement in the shell
     * before executing "make check":
     *      setenv CPL_MSG_LEVEL warning
     *  (or export CPL_MSG_LEVEL=warning)
     */

    cpl_test_init("<usd-help@eso.org>", my_level);

    char *my_path = cpl_sprintf("%s/test_data", ".");
    mkdir(my_path, 0777);
    cpl_free(my_path);

    cpl_msg_set_level_from_env();
    my_level = cpl_msg_get_level();

    test_kmclipm_priv_paste_ifu_images();
    test_kmclipm_priv_create_patrol_view();
    test_kmclipm_priv_paint_ifu_rectangle_patrol();
    test_kmclipm_priv_paint_ifu_rectangle_rtd();
    test_kmclipm_priv_gauss2d_fnc();
    test_kmclipm_priv_gauss2d_fncd();
    test_kmclipm_priv_debug_vector();
    test_kmclipm_priv_debug_image();
    test_kmclipm_priv_debug_cube();
    test_kmclipm_priv_find_angle();

    cpl_msg_info("","All tests done.");
    cpl_msg_set_level(CPL_MSG_WARNING);
    return cpl_test_end(0);
}

/** @} */
