/* $Id: kmo_priv_wave_cal.c,v 1.48 2013-10-08 14:55:01 erw Exp $
 *
 * This file is part of the KMOS 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * $Author: erw $
 * $Date: 2013-10-08 14:55:01 $
 * $Revision: 1.48 $
 * $Name: not supported by cvs2svn $
 */

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

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

#include <sys/stat.h>
#include <math.h>
#include <stdio.h>
#include <string.h>

#ifdef __USE_XOPEN2K
#include <stdlib.h>
#define GGG
#else
#define __USE_XOPEN2K /* to get the definition for setenv in stdlib.h */
#include <stdlib.h>
#undef __USE_XOPEN2K
#endif

#include <cpl.h>

#include "irplib_wlxcorr.h"

#include "kmo_utils.h"
#include "kmo_error.h"
#include "kmo_dfs.h"
#include "kmo_functions.h"
#include "kmo_constants.h"
#include "kmo_priv_wave_cal.h"
#include "kmo_priv_noise_map.h"
#include "kmo_priv_flat.h"
#include "kmo_cpl_extensions.h"
#include "kmo_priv_functions.h"
#include "kmo_debug.h"

/*----------------------------------------------------------------------------*/
/**
    @defgroup kmos_priv_wave_cal     Helper functions for kmo_wave_cal

    @{
 */
/*----------------------------------------------------------------------------*/

int dbgplot = FALSE;
//int dbgplot = TRUE;

void kmo_wave_write_data_vector(const char *filename, const cpl_vector *vector, const char *extname) {

    cpl_propertylist *pl = cpl_propertylist_new();
    cpl_propertylist_update_string(pl, "EXTNAME", extname);
    cpl_error_code error = cpl_vector_save(vector,
            filename,CPL_BPP_IEEE_DOUBLE,pl,CPL_IO_EXTEND);
    if (error != CPL_ERROR_NONE) {
        cpl_msg_error(__func__, "%d: %s",error, cpl_error_get_message());
    }
}

void plot_estimated_lines(cpl_vector *spectrum, const cpl_bivector *catalog,
        cpl_vector *positions, cpl_vector *lambdas) {

    int catalog_size = cpl_bivector_get_size(catalog);
    cpl_bivector *catalog_lines = cpl_bivector_new(3 * catalog_size);
    double *xcl = cpl_bivector_get_x_data(catalog_lines);
    double *ycl = cpl_bivector_get_y_data(catalog_lines);
    const double *xc = cpl_bivector_get_x_data_const(catalog);
    const double *yc = cpl_bivector_get_y_data_const(catalog);
    const double l_off = 0.000001;
    double ratio = cpl_vector_get_max(spectrum) /
            cpl_vector_get_max(cpl_bivector_get_y_const(catalog));
    int ix = 0;
    for (ix = 0; ix < catalog_size; ix++) {
        xcl[ix*3+0] = xc[ix] - l_off;
        xcl[ix*3+1] = xc[ix];
        xcl[ix*3+2] = xc[ix] + l_off;
        ycl[ix*3+0] = 0.;
        ycl[ix*3+1] = yc[ix] * ratio;
        ycl[ix*3+2] = 0.;
    }

    int degree = 6;
    int line_list_size = cpl_vector_get_size(positions);
    if (line_list_size < (degree+1)) {
        degree = line_list_size - 1;
    }

    cpl_polynomial *poly = cpl_polynomial_new(1);
    double *px = cpl_vector_get_data((cpl_vector*)positions);
    cpl_matrix *x_matrix = cpl_matrix_wrap(1, cpl_vector_get_size(positions), px);
    const cpl_boolean   sampsym     = CPL_FALSE;
    cpl_size    mindeg1d    = 0,    //1,
                maxdeg1d    = degree;
    cpl_polynomial_fit(poly,
                       x_matrix,
                       &sampsym,
                       lambdas,
                       NULL,
                       CPL_FALSE,
                       &mindeg1d,
                       &maxdeg1d);

    cpl_vector *xwave_l = cpl_vector_duplicate(spectrum);
    cpl_vector *xwave_p = cpl_vector_duplicate(spectrum);
    for (ix = 0; ix < cpl_vector_get_size(spectrum); ix++) {
        cpl_vector_set(xwave_p, ix, ix);
    }
    cpl_vector_fill_polynomial(xwave_l,poly,0.,1.);
    cpl_bivector *spec_l = cpl_bivector_wrap_vectors(xwave_l, spectrum);
    cpl_bivector *spec_p = cpl_bivector_wrap_vectors(xwave_p, spectrum);

    cpl_bivector *line_list = cpl_bivector_new(3 * line_list_size);
    double *xll = cpl_bivector_get_x_data(line_list);
    double *yll = cpl_bivector_get_y_data(line_list);
    for (ix = 0; ix < line_list_size; ix++) {
        double pos = cpl_vector_get(positions,ix);
        double lambda = cpl_vector_get(lambdas,ix);
        int lx;
        for (lx=0; lx<catalog_size; lx++) {
            if (fabs(xc[lx] - lambda) < 0.00001) {
                break;
            }
        }
        if (lx == catalog_size) {lx = catalog_size-1;}
        xll[ix*3+0] = pos - 1;
        xll[ix*3+1] = pos;
        xll[ix*3+2] = pos + 1;
        yll[ix*3+0] = 0.;
        yll[ix*3+1] = yc[lx] * ratio;
        yll[ix*3+2] = 0.;

    }


#define NBIV1 2
    const char *options1[NBIV1] = {
            "w l t 'catalog'",
            "w l t 'spectrum'"};
    cpl_bivector *bivectors1[NBIV1];
    bivectors1[0] = catalog_lines;
    bivectors1[1] = spec_l;
    cpl_plot_bivectors("",options1,"",(const cpl_bivector**)bivectors1, NBIV1);

#define NBIV2 2
    const char *options2[NBIV2] = {
            "w l t 'estimated line list'",
            "w l t 'spectrum'"};
    cpl_bivector *bivectors2[NBIV2];
    bivectors2[0] = line_list;
    bivectors2[1] = spec_p;
    cpl_plot_bivectors("",options2,"",(const cpl_bivector**)bivectors2, NBIV2);

}

void plot_best_fit(const cpl_vector *spectrum, const cpl_bivector *catalog,
        cpl_polynomial *guess_poly, cpl_polynomial *best_poly, const cpl_polynomial *init_poly) {

    int catalog_size = cpl_bivector_get_size(catalog);
    cpl_bivector *catalog_lines = cpl_bivector_new(3 * catalog_size);
    double *xcl = cpl_bivector_get_x_data(catalog_lines);
    double *ycl = cpl_bivector_get_y_data(catalog_lines);
    const double *xc = cpl_bivector_get_x_data_const(catalog);
    const double *yc = cpl_bivector_get_y_data_const(catalog);
    const double l_off = 0.000001;
    double ratio = cpl_vector_get_max(spectrum) /
            cpl_vector_get_max(cpl_bivector_get_y_const(catalog));
    int ix = 0;
    for (ix = 0; ix < catalog_size; ix++) {
        xcl[ix*3+0] = xc[ix] - l_off;
        ycl[ix*3+0] = 0.;
        xcl[ix*3+1] = xc[ix];
        ycl[ix*3+1] = yc[ix] * ratio;
        xcl[ix*3+2] = xc[ix] + l_off;
        ycl[ix*3+2] = 0.;
    }

    cpl_vector *guess_xwave = cpl_vector_duplicate(spectrum);
    cpl_vector *best_xwave = cpl_vector_duplicate(spectrum);
    cpl_vector *init_xwave = cpl_vector_duplicate(spectrum);

    cpl_vector_fill_polynomial(guess_xwave,guess_poly,0.,1.);
    cpl_bivector *guess = cpl_bivector_wrap_vectors(guess_xwave, (cpl_vector*)spectrum);

    cpl_vector_fill_polynomial(best_xwave,best_poly,0.,1.);
    cpl_bivector *best = cpl_bivector_wrap_vectors(best_xwave, (cpl_vector*)spectrum);

    cpl_vector_fill_polynomial(init_xwave,init_poly,0.,1.);
    cpl_bivector *init = cpl_bivector_wrap_vectors(init_xwave, (cpl_vector*)spectrum);


#define NBIV 4
    const char *options[NBIV] = {
            "w l t 'catalog'",
            "w l t 'guess'",
            "w l t 'best'",
            "w l t 'initial'"};
    cpl_bivector *bivectors[NBIV];
    bivectors[0] = catalog_lines;
    bivectors[1] = guess;
    bivectors[2] = best;
    bivectors[3] = init;
    cpl_plot_bivectors("",options,"",(const cpl_bivector**)bivectors, NBIV);

}

void kmo_find_lines_test (cpl_vector *spectrum, cpl_bivector * catalog/*,
        cpl_vector **positions,
        cpl_vector **lambdas*/) {

    int loop = 1;
    int plot = 1;
    int degree = 2;
    int nsamples = 20;
    double slitw = 0.3;
    double fwhm = 3.0;
    int i = 0, sx = 0, ssx = 0, dx = 0;

    double xc;
    cpl_table *wlres;
    cpl_vector *xcorrs;
    double wl_error_fill = .1;
//    double coeffs[4] = {1.95090384365, 0.000264104234528,
//            -1.3379515120037174E-8, 3.081911523618767E-12};
//    double coeffs[4] = {1.94479301822645,0.000248990234102953,-1.23913455301759E-08,2.76262973149598E-12};
//    double coeffs[4] = {1.43475, 0.000209167, -3.11131e-09, 2.85747e-13};
    double coeffs[4] = {1.41497, 0.000267623, -2.43218e-09, -1.90498e-13};
     cpl_polynomial *best_poly = NULL;
    cpl_polynomial *guess_poly;
    cpl_polynomial *init_poly;

    // cut off invalid ranges at the begin and end of spectrum
    const int slide_range = 10;
    const int required_valid_values = 5;
    int start_index;
    int end_index;
    int spectrum_size = cpl_vector_get_size(spectrum);
    double *ps = cpl_vector_get_data(spectrum);
    double invald_value = 0.0;
    for (sx = 0; sx < spectrum_size-slide_range; sx++) {
        int scnt = 0;
        for (ssx = sx; ssx < sx+slide_range; ssx++) {
            if (ps[ssx] != invald_value) {
                scnt++;
            }
        }
        if (scnt >= required_valid_values) {
            start_index = sx;
            break;
        }
    }
    for (sx = spectrum_size-1; sx>0+slide_range; sx--) {
        int scnt = 0;
        for (ssx = sx; ssx > sx-slide_range; ssx--) {
            if (ps[ssx] != invald_value) {
                scnt++;
            }
        }
        if (scnt >= required_valid_values) {
            end_index = sx+slide_range;
            break;
        }
    }
//    cpl_vector *sub_spectrum = cpl_vector_extract(spectrum, start_index, end_index, 1);

    // trim catalog according sub_spectrum size
    double start_wavelength = start_index * coeffs[1] + coeffs[0];
    double end_wave_length = end_index * coeffs[1] + coeffs[0];
    double *catalog_wavelengths = cpl_bivector_get_x_data(catalog);
    int start_ix;
    int end_ix;
    for (sx = 0; sx < cpl_bivector_get_size(catalog); sx++) {
        if (catalog_wavelengths[sx] < start_wavelength) {
            start_ix = sx;
        }
        if (catalog_wavelengths[sx] < end_wave_length) {
            end_ix = sx;
        }
    }
    start_ix++;
    cpl_vector *sub_catalog_x = cpl_vector_extract(cpl_bivector_get_x(catalog),start_ix,end_ix,1);
    cpl_vector *sub_catalog_y = cpl_vector_extract(cpl_bivector_get_y(catalog),start_ix,end_ix,1);
    cpl_bivector *sub_catalog = cpl_bivector_wrap_vectors(sub_catalog_x, sub_catalog_y);


    while (loop) {
        int no_coeffs = sizeof(coeffs)/sizeof(coeffs[0]);
        cpl_size pow[1];
        if (best_poly == NULL) {
            guess_poly = cpl_polynomial_new(1);
            for (i = 0; i < no_coeffs; i++) {
                pow[0]=i;
                cpl_polynomial_set_coeff(guess_poly, pow, coeffs[i]);
            }
            init_poly = cpl_polynomial_duplicate(guess_poly);
        } else {
            guess_poly = cpl_polynomial_duplicate(best_poly);
        }
        for (dx = cpl_polynomial_get_degree(guess_poly)+1; dx < degree+1; dx++ ) {
            pow[0] = dx;
            if (dx < no_coeffs) {
                cpl_polynomial_set_coeff(guess_poly, pow, coeffs[dx]);
            } else {
                cpl_polynomial_set_coeff(guess_poly, pow, 0.0);
            }
        }
        cpl_vector *wl_error = cpl_vector_new(degree+1);
        cpl_vector_fill (wl_error, wl_error_fill);
        best_poly = irplib_wlxcorr_best_poly(
                spectrum, sub_catalog, degree, guess_poly, wl_error,
                nsamples, slitw, fwhm, &xc, &wlres, &xcorrs);
        printf("Cross correlation: %g\n",xc);
        cpl_polynomial_dump(guess_poly,stdout);
        cpl_polynomial_dump(best_poly,stdout);
        //        if (xcorrs != NULL) {
        //            cpl_vector_dump(xcorrs,stdout);
        //        }


        // support plotting

        if (plot) {
            plot_best_fit(spectrum, catalog, guess_poly, best_poly, init_poly);
//            int dummy=1;
        }
    }

    return;
}

int kmo_best_wave_cal_poly (const cpl_vector *spectrum, const cpl_bivector *catalog,
        const cpl_polynomial *init_poly,
        cpl_polynomial **best_poly_ptr,
        int *final_degree,
        double *final_xc) {

    int converged = 0;
    int plot = 0; // bit 0 -> plot initial fit, bit 1 -> plot final fit, bit 2 -> plot interm. fits
    int degree;
    int nsamples;
    int max_loops;
    int nr_trials;
    int nr_loops;
//    int pow[1];
    double c_wl_error;
    double fwhm = 2;
    double slitw = 2.;
    double xc;
    double previous_xc;

    cpl_table *wlres;
    cpl_vector *xcorrs;
//    cpl_vector *wl_error;
    cpl_polynomial *guess_poly;
    cpl_polynomial *best_poly;

    struct best_fit_pars_struct {
        int degree;
        int nsamples;
        int max_loops;
        double wl_error;
    };
    struct best_fit_pars_struct best_fit_pars[] = {
//                {2, 10, 1, 0.2},
//                {2, 41, 2, 0.1},
//                {1, 101, 1, 0.05},
//                {2, 21, 1, 0.01},
//                {2, 21, 1, 0.001},
//                {2, 11, 3, 0.01},
//            {3, 11, 1, 0.02},
//            {3, 11, 1, 0.005},
            {3, 10, 1 , 0.02},
            {3, 10, 1 , 0.004},
            {4, 4, 1 , 0.004},
    };
    nr_loops = sizeof(best_fit_pars) / sizeof(best_fit_pars[0]);

    best_poly = cpl_polynomial_duplicate(init_poly);

    int lx = 0;
    for (lx = 0; lx < nr_loops; lx++) {
        degree = best_fit_pars[lx].degree;
        nsamples = best_fit_pars[lx].nsamples;
        max_loops = best_fit_pars[lx].max_loops;
        c_wl_error = best_fit_pars[lx].wl_error;
        cpl_vector *wl_error = cpl_vector_new(degree+1);
        cpl_vector_fill (wl_error, c_wl_error);

//            int no_coeffs = sizeof(coeffs)/sizeof(coeffs[0]);
        guess_poly = cpl_polynomial_duplicate(best_poly);
        cpl_polynomial_delete(best_poly);

//            printf("Cross correlation: IFU %d, degree %d, nsamples %d, wl_error %5f -->",
//                    global_ifu_nr, degree, nsamples, best_fit_pars[lx].wl_error);

        best_poly = irplib_wlxcorr_best_poly(
                spectrum, catalog, degree, guess_poly, wl_error,
                nsamples, slitw, fwhm, &xc, &wlres, &xcorrs);

//            printf(" %5f", xc);
        if ((plot & 1) && (lx == 0)) {
            plot_best_fit(spectrum, catalog, guess_poly, best_poly, init_poly);
        }
        if (best_poly == NULL) {
            converged = 0;
            break;
        } else {
            converged = 1;
        }

        previous_xc = xc;
        for (nr_trials=1; nr_trials<max_loops; nr_trials++) { //try it not more than x times
            previous_xc = xc;
            cpl_polynomial_delete(guess_poly);
            guess_poly = best_poly;

            best_poly = irplib_wlxcorr_best_poly(
                    spectrum, catalog, degree, guess_poly, wl_error,
                    nsamples, slitw, fwhm, &xc, &wlres, &xcorrs);
//                printf(" %5f",xc);
            if (plot & 4) {
                plot_best_fit(spectrum, catalog, guess_poly, best_poly, init_poly);
            }

            if (best_poly == NULL) {
                converged = 0;
                break;
            } else {
                converged = 1;
            }

            if (fabs((xc-previous_xc)/previous_xc) < 0.005) { //break if improvement is less
                break;                                      // than .5%
            }
        }

        if (!converged) {
            break;
        }
    }

    if (plot & 2) {
        plot_best_fit(spectrum, catalog, guess_poly, best_poly, init_poly);
    }


    if (converged) {
        *best_poly_ptr = best_poly;
        cpl_polynomial_delete(guess_poly);
    } else {
        *best_poly_ptr = guess_poly;
        cpl_polynomial_delete(best_poly);
   }

    *final_degree = degree;
    *final_xc = xc;
    return converged;
}

/*
 * Extract part of array and return it wrapped in a cpl_vector
 * Part is specified by an offset (start index) and a range (number of elements
 * Offset must be smaller than array size, range must be larger than 0
 * The functions truncates the range if it is partly outside the arry.
 */
cpl_vector *kmo_wave_wrap_array_part (
        double *array,
        int size,
        int offset,
        int range)
{
    if ((range <= 0) || (offset >= size)) {
        range = 0;
    } else {
        if (offset < 0) {
                range = range + offset;
                offset = 0;
        }
        if ((offset + range) > size) {
            range = size - offset + 1;
        }
    }

    if (range <= 0) {
        return NULL;
    } else {
        return cpl_vector_wrap(range, &array[offset]);
    }
}

void kmo_find_lines (const char *filter_id,
        int global_ifu_nr,
        int slitlet_nr,
        cpl_vector *spectrum,
        const cpl_bivector *catalog,
        const cpl_table *reflines,
        cpl_vector **positions,
        cpl_vector **lambdas)
{
    int         debug       = 0,
                n_rl        = 0,
                n_trace     = 0,
                n_catalog   = 0,
                n_valid     = 0,
                i           = 0;
    const int   *rl_ref     = NULL,
                *rl_offset  = NULL,
                *rl_range   = NULL,
                *rl_cut     = NULL;
    double      *rl_wl      = NULL,
                *rl_pos     = NULL,
                *trace      = NULL,
                *xtrace     = NULL,
                *ll_pos     = NULL,
                *ll_lambda  = NULL;
    cpl_table   *subTable   = NULL;
    char        filter_regex[5] = {0,0,0,0,0};
    cpl_vector *fit_pars    = NULL;

    KMO_TRY {
        int detector;
        if (global_ifu_nr < 9) {
            detector = 1;
        } else if (global_ifu_nr < 17) {
            detector = 2;
        } else {
            detector = 3;
        }
        KMO_TRY_EXIT_IF_ERROR(
                cpl_table_select_all((cpl_table*)reflines));
        filter_regex[0] = 0;
        strncat(filter_regex, "^",1);
        strncat(filter_regex,filter_id,2);
        strncat(filter_regex, "$",1);
        int row_cnt = cpl_table_and_selected_string((cpl_table*)reflines, "FILTER", CPL_EQUAL_TO, filter_regex );
        row_cnt = cpl_table_and_selected_int((cpl_table*)reflines, "DETECTOR", CPL_EQUAL_TO, detector);
        if (row_cnt <= 0) {
            cpl_msg_debug(__func__,"found no entries in reference line table for band %s and detector %d",
                    filter_id, global_ifu_nr);
            KMO_TRY_SET_ERROR(CPL_ERROR_DATA_NOT_FOUND);
            KMO_TRY_EXIT();
        }
        subTable = cpl_table_extract_selected(reflines);
        n_rl = row_cnt;
        KMO_TRY_EXIT_IF_NULL(
                rl_wl = cpl_table_get_data_double(subTable, "WAVELENGTH"));
        KMO_TRY_EXIT_IF_NULL(
                rl_ref = cpl_table_get_data_int_const(subTable, "REFERENCE"));
        KMO_TRY_EXIT_IF_NULL(
                rl_offset = cpl_table_get_data_int_const(subTable, "OFFSET"));
        KMO_TRY_EXIT_IF_NULL(
                rl_range = cpl_table_get_data_int_const(subTable, "RANGE"));
        KMO_TRY_EXIT_IF_NULL(
                rl_cut = cpl_table_get_data_int_const(subTable, "CUT"));

        KMO_TRY_EXIT_IF_NULL(
                trace = cpl_vector_get_data(spectrum));
        n_trace = cpl_vector_get_size(spectrum);
        KMO_TRY_EXIT_IF_NULL(
                xtrace = cpl_malloc(sizeof(double) * n_trace));
        for (i = 0; i < n_trace; i++) {
            xtrace[i] = i;
        }
        KMO_TRY_EXIT_IF_NULL(
                rl_pos = cpl_malloc(sizeof(double) * n_rl));

        //
        // first match reference line with absolute positions
        //
        if (debug) printf("RP: %2d: %2d: (%d)  ", global_ifu_nr, slitlet_nr, n_rl );
        for (i = 0; i < n_rl; i++) {
            if (rl_ref[i] == -1) {
                double x0, sigma, area, goffset;
                cpl_vector *xv;
                cpl_vector *yv1;
                cpl_vector *yv2;
                int offset = rl_offset[i];
                int range = rl_range[i];

                yv1 = kmo_wave_wrap_array_part(trace, n_trace, offset, range);
                if (yv1 == NULL) {
                    rl_pos[i] = -1;
                    cpl_msg_warning(__func__,
                            "Offset/range (%d/%d) spec is outside trace dimension for reference line %f for IFU %d slitlet %d in band %s",
                            offset, range, rl_wl[i], global_ifu_nr, slitlet_nr, filter_id);
                    continue;
                }

                double max = cpl_vector_get_max(yv1);
                if (max < rl_cut[i]) {
                    cpl_msg_debug(__func__,
                            "peak value %f less than cut level %d for reference line %f for IFU %d slitlet %d in band %s",
                            max, rl_cut[i], rl_wl[i], global_ifu_nr, slitlet_nr, filter_id);
                }
                cpl_vector *mx = kmo_idl_where(yv1, max-1.0, ge);
                range = 30;
                offset = offset + ((int) cpl_vector_get(mx, 0)) - range/2;

                xv = kmo_wave_wrap_array_part(xtrace, n_trace, offset, range);
                yv2 = kmo_wave_wrap_array_part(trace, n_trace, offset, range);
                if (yv2 == NULL) {
                    cpl_vector_unwrap(yv1);
                    cpl_vector_delete(mx);
                    rl_pos[i] = -1;
                    cpl_msg_warning(__func__,
                            "Offset/range (%d/%d) spec is outside trace dimension for reference line %f for IFU %d slitlet %d in band %s",
                            offset, range, rl_wl[i], global_ifu_nr, slitlet_nr, filter_id);
                    continue;
                }

                KMO_TRY_EXIT_IF_ERROR(
                        kmo_easy_gaussfit(xv, yv2, &x0, &sigma, &area, &goffset));
                if ((x0 > 0.0) && (sigma < 5.0) && (area > 20.)) {
                    rl_pos[i] = x0;
                } else {
                    rl_pos[i] = -1;
                    cpl_msg_debug(__func__,
                            "could not match reference line %f for IFU %d slitlet %d in band %s",
                            rl_wl[i], global_ifu_nr, slitlet_nr, filter_id);
                }
                if (debug) printf("  %f, %f,  ", rl_wl[i], rl_pos[i]);
                cpl_vector_unwrap(xv);
                cpl_vector_unwrap(yv1);
                cpl_vector_unwrap(yv2);
                cpl_vector_delete(mx);
           }
        }
        //
        // next match reference line with relative positions
        //
        for (i = 0; i < n_rl; i++) {
            if (rl_ref[i] != -1) {
                double x0, sigma, area, goffset;
                cpl_vector *xv;
                cpl_vector *yv1;
                cpl_vector *yv2;
                int offset;
                int range = rl_range[i];

                if (rl_pos[rl_ref[i]] != -1) {
                    offset = rl_pos[rl_ref[i]] + rl_offset[i];
                    yv1 = kmo_wave_wrap_array_part(trace, n_trace, offset, range);
                    if (yv1 == NULL) {
                        rl_pos[i] = -1;
                        cpl_msg_debug(__func__,
                                "Offset/range (%d/%d) spec is outside trace dimension for reference line %f for IFU %d slitlet %d in band %s",
                                offset, range, rl_wl[i], global_ifu_nr, slitlet_nr, filter_id);
                        continue;
                    }

                    double max = cpl_vector_get_max(yv1);
                    if (max < rl_cut[i]) {
                        cpl_msg_debug(__func__,
                                "peak value %f less than cut level %d for reference line %f for IFU %d slitlet %d in band %s",
                                max, rl_cut[i], rl_wl[i], global_ifu_nr, slitlet_nr, filter_id);
                    }
                    cpl_vector *mx = kmo_idl_where(yv1, max-1.0, ge);
                    range = 16;
                    offset = offset + ((int) cpl_vector_get(mx, 0)) - range/2;

                    xv = kmo_wave_wrap_array_part(xtrace, n_trace, offset, range);
                    yv2 = kmo_wave_wrap_array_part(trace, n_trace, offset, range);
                    if (yv2 == NULL) {
                        cpl_vector_unwrap(yv1);
                        cpl_vector_delete(mx);
                        rl_pos[i] = -1;
                        cpl_msg_debug(__func__,
                                "Offset/range (%d/%d) spec is outside trace dimension for reference line %f for IFU %d slitlet %d in band %s",
                                offset, range, rl_wl[i], global_ifu_nr, slitlet_nr, filter_id);
                        continue;
                    }

                    KMO_TRY_EXIT_IF_ERROR(
                            kmo_easy_gaussfit(xv, yv2, &x0, &sigma, &area, &goffset));
//                    sigma = 1.;
//                    cpl_error_code ce = cpl_vector_fit_gaussian(xv, NULL, yv, NULL,
//                            CPL_FIT_CENTROID | CPL_FIT_AREA | CPL_FIT_OFFSET,
//                            &x0, &sigma, &area, &goffset, NULL, NULL, NULL);
                    if ((x0 > 0.0) && (sigma < 5.0) && (area > 20.)) {
                        rl_pos[i] = x0;
                    } else {
                        rl_pos[i] = -1;
                        cpl_msg_debug(__func__,
                                "could not match reference line %f for IFU %d slitlet %d in band %s",
                                rl_wl[i], global_ifu_nr, slitlet_nr, filter_id);
                    }
                    cpl_vector_unwrap(xv);
                    cpl_vector_unwrap(yv1);
                    cpl_vector_unwrap(yv2);
                    cpl_vector_delete(mx);
                } else {
                    rl_pos[i] = -1;
                    cpl_msg_debug(__func__,
                            "corresponding reference line not found for wavelength %f for IFU %d slitlet %d in band %s",
                            rl_wl[i], global_ifu_nr, slitlet_nr, filter_id);
                }
                if (debug) printf("  %f, %f,  ", rl_wl[i], rl_pos[i]);
            }
        }
        if (debug) printf("\n");
        //
        // fit a polynomial through the reference line
        //
        cpl_vector *wl1;
        cpl_vector *rp1;
        cpl_vector *wl2;
        cpl_vector *rp2;
        cpl_vector *idx;
        const int degree = 3;
        KMO_TRY_EXIT_IF_NULL(
                wl1 = cpl_vector_wrap(n_rl, rl_wl));
        KMO_TRY_EXIT_IF_NULL(
                rp1 = cpl_vector_wrap(n_rl, rl_pos));
        idx = kmo_idl_where(rp1, -0.1, gt);
        if (idx == NULL) {
            cpl_msg_debug(__func__,"no reference lines found for IFU %d slitlet %d in band %s",
                    global_ifu_nr, slitlet_nr, filter_id);
            KMO_TRY_SET_ERROR(CPL_ERROR_DATA_NOT_FOUND);
            KMO_TRY_EXIT();
        } else if (cpl_vector_get_size(idx) <= degree) {
            cpl_msg_debug(__func__,"too few reference lines (%lld) found for IFU %d slitlet %d in band %s",
                    cpl_vector_get_size(idx), global_ifu_nr, slitlet_nr, filter_id);
            KMO_TRY_SET_ERROR(CPL_ERROR_DATA_NOT_FOUND);
            KMO_TRY_EXIT();
        } else {
            rp2 = kmo_idl_values_at_indices(rp1, idx);
            wl2 = kmo_idl_values_at_indices(wl1, idx);
            KMO_TRY_EXIT_IF_ERROR(
                    cpl_vector_sort(wl2, CPL_SORT_ASCENDING));
            KMO_TRY_EXIT_IF_ERROR(
                    cpl_vector_sort(rp2, CPL_SORT_ASCENDING));
            KMO_TRY_EXIT_IF_NULL(
                    fit_pars = kmo_polyfit_1d(wl2, rp2, degree));
            cpl_vector_delete(wl2);
            cpl_vector_delete(rp2);
        }
        if (idx != NULL) cpl_vector_delete(idx);
        cpl_vector_unwrap(wl1);
        cpl_vector_unwrap(rp1);
        if (debug) {
            printf("FP: %2d: %2d: %d  ", global_ifu_nr, slitlet_nr, degree+1 );
            int fp = 0;
            for (fp = 0; fp <= degree; fp++) {
                printf(", %f", cpl_vector_get(fit_pars, fp));
            }
            printf("\n");
        }


        //
        // calculate all line positions
        //
//        const int hrange = 6; //same search range as in kmo_fit_arcline
        const int hrange = 4;
        double *c;
        cpl_vector *catalog_lambdas;
        KMO_TRY_EXIT_IF_NULL(
                c = cpl_vector_get_data(fit_pars));
        KMO_TRY_EXIT_IF_NULL(
                catalog_lambdas = cpl_bivector_get_x((cpl_bivector*)catalog));
        n_catalog = cpl_vector_get_size(catalog_lambdas);
        KMO_TRY_EXIT_IF_NULL(
                ll_pos = cpl_malloc(sizeof(double) * n_catalog));
        KMO_TRY_EXIT_IF_NULL(
                ll_lambda = cpl_malloc(sizeof(double) * n_catalog));
        n_valid = 0;
        for (i = 0; i < n_catalog; i++) {
            double x = cpl_vector_get(catalog_lambdas, i);
            double tmp;
            switch (degree) {
            case 1 :
                tmp = c[0] + x * c[1];
                break;
            case 2 :
                tmp = c[0] + x * (c[1] + x * c[2]);
                break;
            case 3 :
                tmp = c[0] + x * (c[1] + x * (c[2] + x * c[3]));
                break;
            case 4:
                tmp = c[0] + x * (c[1] + x * (c[2] + x * (c[3] + x * c[4])));
                break;
            }
            if (((tmp-hrange) >= 0) && ((tmp+hrange) < n_trace)) {
                double x0, sigma, area, goffset;
                int range = 2 * hrange; //same search range as in kmo_fit_arcline
                cpl_vector *xv = NULL;
                cpl_vector *yv = NULL;
                int offset = ((int) tmp) - hrange;
                KMO_TRY_EXIT_IF_NULL(
                        xv = cpl_vector_wrap(range, &xtrace[offset]));
                KMO_TRY_EXIT_IF_NULL(
                        yv = cpl_vector_wrap(range, &trace[offset]));
                KMO_TRY_EXIT_IF_ERROR(
                        kmo_easy_gaussfit(xv, yv, &x0, &sigma, &area, &goffset));
                cpl_vector_unwrap(xv); xv = NULL;
                cpl_vector_unwrap(yv); yv = NULL;
                if (debug) printf("LF: %2d: %2d: %2d:  %f  %6.1f   %6.1f  %4.1f  %7.1f   %5.1f    %f",
                        global_ifu_nr, slitlet_nr, i,
                        x, tmp,
                        x0, sigma, area, goffset, tmp-x0);
                if ((x0 > 0.0) && (sigma < 5.0) && (area > 1.01)) {
                    if (debug) printf("\n");
//                    ll_pos[n_valid] = x0;
                    ll_pos[n_valid] = tmp;
                    ll_lambda[n_valid] = x;
                    n_valid++;
                } else {
                    if (debug) printf("     rejected\n");
                }
            }
        }
        //
        // create and fill output vectors
        //
//        n_valid = n_valid -2;
        KMO_TRY_EXIT_IF_NULL(
                *positions = cpl_vector_new(n_valid));
        KMO_TRY_EXIT_IF_NULL(
                *lambdas = cpl_vector_new(n_valid));
        for (i = 0; i < n_valid; i++) {
            KMO_TRY_EXIT_IF_ERROR(
                    cpl_vector_set(*positions, i, ll_pos[i]));
            KMO_TRY_EXIT_IF_ERROR(
                    cpl_vector_set(*lambdas, i, ll_lambda[i]));
        }
        cpl_free(ll_pos); ll_pos = NULL;
        cpl_free(ll_lambda); ll_lambda = NULL;
        cpl_free(xtrace); xtrace = NULL;
        cpl_free(rl_pos); rl_pos = NULL;
        if (subTable != NULL) cpl_table_delete(subTable);
    } KMO_CATCH {
        KMO_CATCH_MSG();
    }

    cpl_vector_delete(fit_pars); fit_pars = NULL;

    return;

}

void kmo_find_lines_2 (const char * table_filename,
        const char *filter_id,
        int global_ifu_nr,
        int slitlet_nr,
        cpl_vector *spectrum,
        const cpl_bivector *catalog,
        cpl_vector **positions,
        cpl_vector **lambdas) {

//    int loop = 0;
    int converged;
    int degree;
//    int nsamples = 10;
//    int max_loops;
    int nr_matching_rows;
    int valid;
    cpl_size pow[1];
//    double slitw = 2.0;
//    double slitw = 0.01;
//    double fwhm = 0.0008;
//    double slitw = 3.;

    double x;
    double xc;
//    double previous_xc;
//    cpl_table *wlres;
//    cpl_vector *xcorrs;
//    double wl_error_fill = .01;
    double coeffs[NR_COEFFS_COLS];
//    = {1.95090384365, 0.000264104234528,
//            -1.3379515120037174E-8, 3.081911523618767E-12};
    cpl_polynomial *best_poly = NULL;
    cpl_polynomial *init_poly = NULL;
    char *coeff_cols[NR_COEFFS_COLS] = {COEFF_COLS};
    int i = 0, ix = 0, sx = 0, ssx = 0;

    KMO_TRY {

        // load polynom coefficients table to read first guess polynom coefficients
        cpl_table *coeffs_table = cpl_table_load(table_filename, 1, 1);
        if (coeffs_table == NULL) {
            cpl_msg_error(__func__, "Cannot load polynom coefficients table %s", table_filename);
            KMO_TRY_EXIT();
        }

        KMO_TRY_EXIT_IF_ERROR(
                cpl_table_select_all(coeffs_table));

        char filter_id_regex[5] = {'\0', '\0', '\0', '\0', '\0'};
        if (strlen(filter_id) < 2) {
            strcat(filter_id_regex, "^");
            strcat(filter_id_regex, filter_id);
            strcat(filter_id_regex, "$");
            nr_matching_rows = cpl_table_and_selected_string(coeffs_table, BAND_COL, CPL_EQUAL_TO,
                    filter_id_regex);
            if (nr_matching_rows != KMOS_NR_IFUS) {
                KMO_TRY_EXIT_WITH_ERROR(CPL_ERROR_ILLEGAL_INPUT);
            }
        } else {
            KMO_TRY_EXIT_WITH_ERROR(CPL_ERROR_ILLEGAL_INPUT);
        }
        nr_matching_rows = cpl_table_and_selected_int(coeffs_table, IFU_COL, CPL_EQUAL_TO, global_ifu_nr);
        if (nr_matching_rows != 1) {
            KMO_TRY_EXIT_WITH_ERROR(CPL_ERROR_ILLEGAL_INPUT);
        }
        cpl_array *selected_rows = cpl_table_where_selected(coeffs_table);
        int selected_row = cpl_array_get_int(selected_rows, 0, &valid);
       if (valid !=0 ) {
            KMO_TRY_EXIT();
       }
//       printf("Selected row %d, band %s (shall be %s), ifu %d (shall be %d), %f\n",
//               selected_row,
//               cpl_table_get_string(coeffs_table, BAND_COL, selected_row), filter_id,
//               cpl_table_get_int(coeffs_table, IFU_COL, selected_row, &valid), global_ifu_nr,
//               cpl_table_get_double(coeffs_table, A0_COL, selected_row, &valid));
        cpl_array_delete(selected_rows);

        init_poly = cpl_polynomial_new(1);
        for (i = 0; i < NR_COEFFS_COLS; i++) {
            coeffs[i] = cpl_table_get_double(coeffs_table, coeff_cols[i], selected_row, &valid);
            if (valid == -1) {
                cpl_msg_error(__func__,
                        "Error reading polynom coefficients table %s, column %s, row %d",
                        table_filename, coeff_cols[i], selected_row);
                KMO_TRY_EXIT();
            }
            if (valid == 1) {
                cpl_msg_error(__func__,
                        "Invalid value in polynom coefficients table %s, column %s, row %d",
                        table_filename, coeff_cols[i], selected_row);
                KMO_TRY_EXIT();
            }
            pow[0]=i;
            KMO_TRY_EXIT_IF_ERROR(
                    cpl_polynomial_set_coeff(init_poly, pow, coeffs[i]));
        }
        cpl_table_delete(coeffs_table);

        // cut off invalid ranges at the begin and end of spectrum
        const int slide_range = 10;
        const int required_valid_values = 5;
        int start_index;
        int end_index;
        int spectrum_size = cpl_vector_get_size(spectrum);
        double *ps = cpl_vector_get_data(spectrum);
        double invald_value = 0.0;
        for (sx = 0; sx < spectrum_size-slide_range; sx++) {
            int scnt = 0;
            for (ssx = sx; ssx < sx+slide_range; ssx++) {
                if (ps[ssx] != invald_value) {
                    scnt++;
                }
            }
            if (scnt >= required_valid_values) {
                start_index = sx;
                break;
            }
        }
        for (sx = spectrum_size-1; sx > 0+slide_range; sx--) {
            int scnt = 0;
            for (ssx = sx; ssx > sx-slide_range; ssx--) {
                if (ps[ssx] != invald_value) {
                    scnt++;
                }
            }
            if (scnt >= required_valid_values) {
                end_index = sx+slide_range;
                break;
            }
        }
//        cpl_vector *sub_spectrum = cpl_vector_extract(spectrum, start_index, end_index, 1);

        // trim catalog according sub_spectrum size
        x = start_index;
        double start_wavelength = coeffs[0] + coeffs[1]*x + coeffs[2]*x*x + coeffs[3]*x*x*x;
        x = end_index;
        double end_wave_length = coeffs[0] + coeffs[1]*x + coeffs[2]*x*x + coeffs[3]*x*x*x;
        const double *catalog_wavelengths = cpl_bivector_get_x_data_const(catalog);
        int start_ix = -1;
        int end_ix = cpl_bivector_get_size(catalog) - 1;
        for (sx = 0; sx < cpl_bivector_get_size(catalog); sx++) {
            if (catalog_wavelengths[sx] < start_wavelength) {
                start_ix = sx;
            }
            if (catalog_wavelengths[sx] < end_wave_length) {
                end_ix = sx;
            }
        }
        start_ix++;
        cpl_vector *sub_catalog_x = cpl_vector_extract(cpl_bivector_get_x_const(catalog),start_ix,end_ix,1);
        cpl_vector *sub_catalog_y = cpl_vector_extract(cpl_bivector_get_y_const(catalog),start_ix,end_ix,1);
        cpl_bivector *sub_catalog = cpl_bivector_wrap_vectors(sub_catalog_x, sub_catalog_y);
//        printf("Number of sub_catalog lines: %d\n",cpl_vector_get_size(sub_catalog_x));

        cpl_size c0[1]= {0},
                 c1[1]= {1},
                 c2[1]= {2},
                 c3[1]= {3};
        printf("Band %s  IFU %d  slitlet %d  initial fit: a0 %g a1 %g a2 %g a3 %g",
                filter_id, global_ifu_nr, slitlet_nr,
                cpl_polynomial_get_coeff(init_poly, c0),
                cpl_polynomial_get_coeff(init_poly, c1),
                cpl_polynomial_get_coeff(init_poly, c2),
                cpl_polynomial_get_coeff(init_poly, c3));

        // find best polynom
//        int test_loop=1;
//        while (test_loop) {
        converged =  kmo_best_wave_cal_poly (spectrum, sub_catalog, init_poly,
                &best_poly, &degree, &xc);
//        }
//        cpl_polynomial_dump(best_poly,stdout);


        printf(" final fit: converge:  %d  degree %d  a0 %g a1 %g a2 %g a3 %g  final xc: %g\n",
                converged, degree,
                cpl_polynomial_get_coeff(best_poly, c0),
                cpl_polynomial_get_coeff(best_poly, c1),
                cpl_polynomial_get_coeff(best_poly, c2),
                cpl_polynomial_get_coeff(best_poly, c3),
                xc);

        cpl_bivector *fref;
        cpl_bivector *fout;
        KMO_TRY_EXIT_IF_NULL (
                fref = cpl_bivector_new(KMOS_DETECTOR_SIZE));
        KMO_TRY_EXIT_IF_ERROR(
                cpl_vector_fill_polynomial(cpl_bivector_get_x(fref),best_poly,0.,1.));
        double *fref_y = cpl_bivector_get_y_data(fref);
        for (ix = 0; ix < KMOS_DETECTOR_SIZE; ix++) {
            fref_y[ix] = ix;
        }

        double *scx =  cpl_bivector_get_x_data(sub_catalog);
        int fout_size = 0;
        double min = cpl_vector_get(cpl_bivector_get_x(fref), 0);
        double max = cpl_vector_get(cpl_bivector_get_x(fref), KMOS_DETECTOR_SIZE - 1);
        for (ix = 0; ix < cpl_bivector_get_size(sub_catalog); ix++) {
            if (scx[ix]>=min && scx[ix]<=max) {
                fout_size++;
            }
        }
        KMO_TRY_EXIT_IF_NULL (
                fout = cpl_bivector_new(fout_size));
        double *fout_x = cpl_bivector_get_x_data(fout);
        int tmp_ix = 0;
        for (ix = 0; ix < cpl_bivector_get_size(sub_catalog); ix++) {
            if (scx[ix]>=min && scx[ix]<=max) {
                fout_x[tmp_ix] = scx[ix];
                tmp_ix++;
            }
        }
//        KMO_TRY_EXIT_IF_ERROR(
//                cpl_vector_copy(cpl_bivector_get_x(fout), cpl_bivector_get_x(sub_catalog)));

        KMO_TRY_EXIT_IF_ERROR(
                cpl_bivector_interpolate_linear(fout, fref));
        KMO_TRY_EXIT_IF_NULL(
                *positions = cpl_vector_new(fout_size));
        KMO_TRY_EXIT_IF_ERROR(
                cpl_vector_copy(*positions,cpl_bivector_get_y(fout)));
        KMO_TRY_EXIT_IF_NULL(
                *lambdas = cpl_vector_new(fout_size));
         KMO_TRY_EXIT_IF_ERROR(
                cpl_vector_copy(*lambdas, cpl_bivector_get_x(fout)));


        cpl_bivector_delete(fref);
        cpl_bivector_delete(fout);
        cpl_polynomial_delete(best_poly);


    } KMO_CATCH {
        KMO_CATCH_MSG();
    }

    return;
}

cpl_table*  kmo_wave_guess_table_open (const char *filename, int create) {

    cpl_table *table = NULL;
    char *bands[NR_BANDS] = {BANDS};

    KMO_TRY
    {
        table = cpl_table_load(filename, 1, 1);
        cpl_error_reset();
        if (table == NULL && create) { // table read error, create new table
            KMO_TRY_EXIT_IF_NULL(
                    table = cpl_table_new(NR_BANDS * KMOS_NR_IFUS));  //five bands times 24 IFUs
            KMO_TRY_EXIT_IF_ERROR(
                    cpl_table_new_column(table, BAND_COL, CPL_TYPE_STRING));
            KMO_TRY_EXIT_IF_ERROR(
                    cpl_table_new_column(table, IFU_COL, CPL_TYPE_INT));
            KMO_TRY_EXIT_IF_ERROR(
                    cpl_table_new_column(table, A0_COL, CPL_TYPE_DOUBLE));
            KMO_TRY_EXIT_IF_ERROR(
                    cpl_table_new_column(table, A1_COL, CPL_TYPE_DOUBLE));
            KMO_TRY_EXIT_IF_ERROR(
                    cpl_table_new_column(table, A2_COL, CPL_TYPE_DOUBLE));
            KMO_TRY_EXIT_IF_ERROR(
                    cpl_table_new_column(table, A3_COL, CPL_TYPE_DOUBLE));

            int c = 0, b = 0, i = 0;
            for (b = 0; b < NR_BANDS; b++) {
                for (i = 0; i < (KMOS_NR_IFUS); i++) {
                    KMO_TRY_EXIT_IF_ERROR(
                            cpl_table_set_string(table, BAND_COL, c, bands[b]));
                    KMO_TRY_EXIT_IF_ERROR(
                            cpl_table_set_int(table, IFU_COL, c, i+1));
                    c++;
                }
            }
            KMO_TRY_EXIT_IF_ERROR(
                    cpl_table_fill_invalid_int(table, "IFU", -1));
            KMO_TRY_EXIT_IF_ERROR(
                    cpl_table_save(table, NULL, NULL, filename, CPL_IO_CREATE));

        }
    } KMO_CATCH {
        KMO_CATCH_MSG();
    }

    return table;

}

int kmo_wave_guess_table_get_column (cpl_table *table,
        const char *filter_id, const int global_ifu_nr) {

    int selected_row = -1;
    int nr_matching_rows;
    cpl_array*  selected_rows;


    KMO_TRY
    {
        KMO_TRY_EXIT_IF_ERROR(
                cpl_table_select_all(table));

        char filter_id_regex[5] = {'\0', '\0', '\0', '\0', '\0'};
        if (strlen(filter_id) < 2) {
            strcat(filter_id_regex, "^");
            strcat(filter_id_regex, filter_id);
            strcat(filter_id_regex, "$");
            nr_matching_rows = cpl_table_and_selected_string(table, BAND_COL, CPL_EQUAL_TO,
                    filter_id_regex);
            if (nr_matching_rows != KMOS_NR_IFUS) {
                KMO_TRY_EXIT_WITH_ERROR(CPL_ERROR_ILLEGAL_INPUT);
            }
        } else {
            KMO_TRY_EXIT_WITH_ERROR(CPL_ERROR_ILLEGAL_INPUT);
        }
        nr_matching_rows = cpl_table_and_selected_int(table, IFU_COL, CPL_EQUAL_TO, global_ifu_nr);
        if (nr_matching_rows != 1) {
            KMO_TRY_EXIT_WITH_ERROR(CPL_ERROR_ILLEGAL_INPUT);
        }
        selected_rows = cpl_table_where_selected(table);
        int valid;
        selected_row = cpl_array_get_int(selected_rows, 0, &valid);
        if (valid !=0 ) {
            KMO_TRY_EXIT();
        }
        cpl_array_delete(selected_rows);

    } KMO_CATCH {
        KMO_CATCH_MSG();
    }

    return selected_row;

}
void kmo_wave_guess_table_update_1 (const char *filename,
        int global_ifu_nr,  /*int slitlet_nr, */const char *filter_id,
        cpl_vector *positions, cpl_vector *lambdas) {

    cpl_table* table = NULL;
    char *coeff_cols[NR_COEFFS_COLS] = {COEFF_COLS};
    int selected_row;
//    double mse;
    cpl_size pows[1];

    KMO_TRY
    {
// deprecated
//        cpl_polynomial *poly  = cpl_polynomial_fit_1d_create(positions, lambdas, 3, &mse);

// replaced with this, ok?
cpl_polynomial *poly = cpl_polynomial_new(1);
double *px = cpl_vector_get_data((cpl_vector*)positions);
cpl_matrix *x_matrix = cpl_matrix_wrap(1, cpl_vector_get_size(positions), px);
const cpl_boolean   sampsym     = CPL_FALSE;
cpl_size mindeg1d    = 0,    //1,
         maxdeg1d    = 3;
cpl_polynomial_fit(poly,
                   x_matrix,
                   &sampsym,
                   lambdas,
                   NULL,
                   CPL_FALSE,
                   &mindeg1d,
                   &maxdeg1d);

//        cpl_polynomial_dump(poly, stdout);

        table = kmo_wave_guess_table_open(filename, 1);

        selected_row = kmo_wave_guess_table_get_column(table, filter_id, global_ifu_nr);

        int ix = 0;
        for (ix = 0; ix < NR_COEFFS_COLS; ix++) {
            pows[0] = ix;
            KMO_TRY_EXIT_IF_ERROR(
                    cpl_table_set_double(table, coeff_cols[ix], selected_row,
                            cpl_polynomial_get_coeff(poly, pows)));
        }
        KMO_TRY_EXIT_IF_ERROR(
                cpl_table_fill_invalid_int(table, "IFU", -1));
        KMO_TRY_EXIT_IF_ERROR(
                cpl_table_save(table, NULL, NULL, filename, CPL_IO_CREATE));

    } KMO_CATCH {
        KMO_CATCH_MSG();
    }
    cpl_table_delete(table);

}

void kmo_wave_guess_table_update_2 (const char *filename,
        int ifu_nr, int global_ifu_nr,  const char *filter_id,
        int flip_trace,
        cpl_matrix **fitpars) {

//    cpl_error_code error = CPL_ERROR_NONE;
    cpl_table* table = NULL;
    int selected_row = 0, i = 0;
    char *coeff_cols[NR_COEFFS_COLS] = {COEFF_COLS};
    double coeffs[NR_COEFFS_COLS];

    KMO_TRY
    {
        table = kmo_wave_guess_table_open(filename, 1);

        selected_row = kmo_wave_guess_table_get_column(table, filter_id, global_ifu_nr);

        for (i = 0; i < NR_COEFFS_COLS; i++) {
            cpl_matrix *col = cpl_matrix_extract_column(fitpars[ifu_nr-1], i);
            coeffs[i] = cpl_matrix_get_median(col);
            printf("coeffs distribution: %d %d:  %g %g %g\n", ifu_nr, i,
                    cpl_matrix_get_min(col),cpl_matrix_get_median(col),cpl_matrix_get_max(col));
        }

        if (!flip_trace) {
            double x = 2047.;
            KMO_TRY_EXIT_IF_ERROR(
                    cpl_table_set_double(table, coeff_cols[0], selected_row,
                            coeffs[0] + coeffs[1]*x + coeffs[2]*x*x + coeffs[3]*x*x*x ));
            KMO_TRY_EXIT_IF_ERROR(
                    cpl_table_set_double(table, coeff_cols[1], selected_row,
                            -coeffs[1] - 2*coeffs[2]*x - 3*coeffs[3]*x*x ));
            KMO_TRY_EXIT_IF_ERROR(
                    cpl_table_set_double(table, coeff_cols[2], selected_row,
                            coeffs[2] + 3*coeffs[3]*x ));
            KMO_TRY_EXIT_IF_ERROR(
                    cpl_table_set_double(table, coeff_cols[3], selected_row,
                            -coeffs[3]));
        } else {
            for (i = 0; i < NR_COEFFS_COLS; i++) {
                KMO_TRY_EXIT_IF_ERROR(
                        cpl_table_set_double(table, coeff_cols[i], selected_row, coeffs[i]));
            }
        }

        int dummy;
        printf("coeffs to table:                               %g %g %g %g\n",
                cpl_table_get_double(table, coeff_cols[0], selected_row, &dummy),
                cpl_table_get_double(table, coeff_cols[1], selected_row, &dummy),
                cpl_table_get_double(table, coeff_cols[2], selected_row, &dummy),
                cpl_table_get_double(table, coeff_cols[3], selected_row, &dummy)
        );

        KMO_TRY_EXIT_IF_ERROR(
                cpl_table_fill_invalid_int(table, "IFU", -1));
        KMO_TRY_EXIT_IF_ERROR(
                cpl_table_save(table, NULL, NULL, filename, CPL_IO_CREATE));

    } KMO_CATCH {
        KMO_CATCH_MSG();
    }
    cpl_table_delete(table);
}

/**
    @brief
        Calculates the wavelength calibration data for a detector.

    @param data             The arcframe.
    @param bad_pix          The badpixel frame.
    @param xcal             The x-calibration frame from kmo_flat.
    @param ycal             The y-calibration frame from kmo_flat.
    @param filter_id        The filtzer ID (e.g. "K", "HK", etc)
    @param lamp_config      Either ARGON, NEON or ARGON_NEON
    @param detector_nr      The detector index (from 1 to 3)
    @param ifu_inactive     Array containing flags if IFUs are (in)active.
    @param edge_table       An array of size KMOS_IFUS_PER_DETECTOR. Each
                            holding a table with 4 columns of edge fit parameters
    @param lines            Vector with the defined arclines. This vector has
                            been generated with kmo_get_lines().
    @param disp             The expected dispersion in wavelength.
    @param lcal             (Output) The wavelength calibration frame
    @param qc_ar_eff        (Output) The argon lamp efficency QC parameter.
    @param qc_ne_eff        (Output) The neon lamp efficency QC parameter.
    @param flip_trace       FOR SIMULATION DATA only. Set to TRUE in this
                            case (wavelengths are ascending from bottom of
                            detector to top in this case).
    @param fit_order        Polynomial order of the wavelength solution.

    @return
        The function returns CPL_ERROR_NONE on success or a CPL error code
        otherwise.

    First a line profile across the badpix frame is created, the positions of the
    edges and corresponding IFUs are detected.
    Then all slitlets are looped: The wavelength profile is calculated, then the
    arclines defined in a text file are matched to get approximate estimates of
    the line positions. Then the exact line positions are calculated using
    function fitting. Then a polynomial is fitted along the wavelength direction
    to get the wavelength calibration data. At the end some QC parameters are
    collected and returned.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT    if any of the inputs is zero.
    @li CPL_ERROR_ILLEGAL_INPUT if @c detector_nr < 0 or if the dimensions of
                                any of the input frames aren't equal.
*/
cpl_error_code  kmo_calc_wave_calib(const cpl_image *data,
                                    const cpl_image *bad_pix,
                                    const cpl_image *xcal,
                                    const cpl_image *ycal,
                                    const char *filter_id,
                                    enum lampConfiguration lamp_config,
                                    const int detector_nr,
                                    cpl_array *ifu_inactive,
                                    cpl_table **edge_table,
                                    const cpl_bivector *lines,
                                    const cpl_table *reflines,
                                    double disp,
                                    cpl_image **lcal,
                                    double *qc_ar_eff,
                                    double *qc_ne_eff,
                                    int flip_trace,
                                    const int fit_order,
                                    const int line_estimate_method)
{
    cpl_error_code  ret_error           = CPL_ERROR_NONE;

    const char *coeffs_table_filename = "kmo_wave_guess_polynom_table.fits";

    char *data_fits_prefix = NULL;
    char *data_fits_name = NULL;
    if (getenv("KMO_WAVE_CAL_DATA_PREFIX") != NULL) {
        data_fits_prefix = getenv("KMO_WAVE_CAL_DATA_PREFIX");
    }

    const float     *pdata              = NULL;

    cpl_vector      *midline            = NULL,
                    *trace              = NULL,
                    *trace_median       = NULL,
                    *lambdas            = NULL,
                    *positions          = NULL,
                    *qc_vec             = NULL,
//                    *qc_disp_vec        = NULL,
                    ***xarray             = NULL,
                    ***larray             = NULL,
                    *qc_ar_flux         = NULL,
                    *qc_ne_flux         = NULL,
                    *tmp_qc1            = NULL,
                    *tmp_qc2            = NULL,
                    *left_edge          = NULL,
                    *right_edge         = NULL;

    cpl_image       ***yarray           = NULL;

    cpl_matrix      **fitpars           = NULL;
    cpl_matrix      **cfitpars           = NULL;
    cpl_array       **valid_columns     = NULL;

    double          *pqc_ar_flux        = NULL,
                    *pqc_ne_flux        = NULL,
                    *ppositions         = NULL,
                    *pleft_edge         = NULL,
                    *pright_edge        = NULL,
                    *plarray            = NULL,
                    min_lambda          = 0.0,
                    max_lambda          = 0.0,
                    tmp_min_lambda      = 0.0,
                    tmp_max_lambda      = 0.0,
                    *pfitpars           = NULL;

    int             nx                  = 0,
                    ny                  = 0,
                    nr_edges            = 0,
                    nr_extrapolated_lines = 0,
                    slitlet_cnt         = 0,    // across all IFUs
                    slitlet_nr          = 0,
                    nr_valid_slitlets   = 0,
                    fit_arc_size_x      = 0,
                    fit_arc_size_y      = 0,
                    no_err_msg          = FALSE,
                    ifu_nr              = 0,
                    global_ifu_nr       = 0,
                    i                   = 0,
                    j                   = 0,
                    sx                  = 0,
                    k                   = 0,
                    g                   = 0,
                    z                   = 0,
                    edge_offset = 4;    // from left_edge we shift to the right
                                        // from the right edge we shift to the
                                        // left to be sure that rotation doesn't
                                        // affect the line to trace
    int             line_check_ok[KMOS_IFUS_PER_DETECTOR][KMOS_SLITLET_Y];
    int             max_nr_x_positions;
    int             *pvalid_columns;

    KMO_TRY
    {
        KMO_TRY_ASSURE((data != NULL) &&
                       (bad_pix != NULL) &&
                       (xcal != NULL) &&
                       (ycal != NULL) &&
                       (filter_id != NULL) &&
                       (ifu_inactive != NULL) &&
                       (edge_table != NULL) &&
                       (lines != NULL) &&
                       (lcal != NULL) &&
                       (qc_ar_eff != NULL) &&
                       (qc_ne_eff != NULL),
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        KMO_TRY_ASSURE(detector_nr > 0,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "detector_nr has to be positive!");

        KMO_TRY_ASSURE(cpl_array_get_size(ifu_inactive)
                                                    == KMOS_IFUS_PER_DETECTOR,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "Array 'ifu_inactive'' must be of size 8!");

        nx = cpl_image_get_size_x(data);
        ny = cpl_image_get_size_y(data);

        KMO_TRY_ASSURE((nx == cpl_image_get_size_x(bad_pix)) &&
                       (ny == cpl_image_get_size_y(bad_pix)) &&
                       (nx == cpl_image_get_size_x(xcal)) &&
                       (ny == cpl_image_get_size_y(xcal)) &&
                       (nx == cpl_image_get_size_x(ycal)) &&
                       (ny == cpl_image_get_size_y(ycal)),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "Data- and bad pix-frame haven't the same size!");

        KMO_TRY_EXIT_IF_NULL(
            pdata = cpl_image_get_data_float_const(data));

        KMO_TRY_EXIT_IF_NULL(
            *lcal = cpl_image_new(nx, ny, CPL_TYPE_FLOAT));
        KMO_TRY_EXIT_IF_ERROR(
            kmo_image_fill(*lcal, 0.0));

        KMO_TRY_EXIT_IF_NULL(
            xarray = cpl_calloc(KMOS_IFUS_PER_DETECTOR,sizeof(cpl_vector **)));
        KMO_TRY_EXIT_IF_NULL(
            larray = cpl_calloc(KMOS_IFUS_PER_DETECTOR,sizeof(cpl_vector **)));
        KMO_TRY_EXIT_IF_NULL(
            yarray = cpl_calloc(KMOS_IFUS_PER_DETECTOR,sizeof(cpl_image **)));
        for (i = 0; i < KMOS_IFUS_PER_DETECTOR; i++) {
            if ((edge_table[i] != NULL) &&
                (cpl_array_get_int(ifu_inactive, i, NULL) == 0))
            {
                int nr_slitlets = cpl_table_get_nrow(edge_table[i])/2;
                KMO_TRY_EXIT_IF_NULL(
                    xarray[i] = cpl_calloc(nr_slitlets, sizeof (cpl_vector *)));
                KMO_TRY_EXIT_IF_NULL(
                    larray[i] = cpl_calloc(nr_slitlets, sizeof (cpl_vector *)));
                KMO_TRY_EXIT_IF_NULL(
                    yarray[i] = cpl_calloc(nr_slitlets, sizeof (cpl_image *)));
            } else {
                xarray[i] = NULL;
                larray[i] = NULL;
                yarray[i] = NULL;

            }
        }

        KMO_TRY_EXIT_IF_NULL(
            fitpars = (cpl_matrix**)cpl_malloc(KMOS_IFUS_PER_DETECTOR *
                                         sizeof(cpl_matrix*)));
        KMO_TRY_EXIT_IF_NULL(
            cfitpars = (cpl_matrix**)cpl_malloc(KMOS_IFUS_PER_DETECTOR *
                                         sizeof(cpl_matrix*)));
        KMO_TRY_EXIT_IF_NULL(
            valid_columns = (cpl_array**)cpl_malloc(KMOS_IFUS_PER_DETECTOR *
                                     sizeof(cpl_array*)));
        for (i = 0; i < KMOS_IFUS_PER_DETECTOR; i++) {
            fitpars[i] = NULL;
            cfitpars[i] = NULL;
            valid_columns[i] = NULL;
        }

        //
        // get the number of valid edges
        //
        for (i = 0; i < KMOS_IFUS_PER_DETECTOR; i++) {
            if ((edge_table[i] != NULL) &&
                (cpl_array_get_int(ifu_inactive, i, NULL) == 0))
            {
                nr_edges += cpl_table_get_nrow(edge_table[i]);
            }
        }

        // no valid edge, all IFUs must be deactivated, quit function
        if (nr_edges == 0) {
            cpl_error_set(cpl_func, CPL_ERROR_UNSPECIFIED);
            no_err_msg = TRUE;
            KMO_TRY_CHECK_ERROR_STATE();
        }

        //
        // allocate data structures
        //
        KMO_TRY_EXIT_IF_NULL(
            qc_vec = cpl_vector_new(4));
//        KMO_TRY_EXIT_IF_NULL(
//            qc_disp_vec = cpl_vector_new(9));

        KMO_TRY_EXIT_IF_NULL(
            qc_ar_flux = cpl_vector_new(nr_edges/2));
        cpl_vector_fill(qc_ar_flux, -1);
        KMO_TRY_EXIT_IF_NULL(
            qc_ne_flux = cpl_vector_new(nr_edges/2));
        cpl_vector_fill(qc_ne_flux, -1);

        KMO_TRY_EXIT_IF_NULL(
            pqc_ar_flux = cpl_vector_get_data(qc_ar_flux));
        KMO_TRY_EXIT_IF_NULL(
            pqc_ne_flux = cpl_vector_get_data(qc_ne_flux));

        KMO_TRY_EXIT_IF_NULL(
            left_edge = cpl_vector_new(ny-2*KMOS_BADPIX_BORDER));
        KMO_TRY_EXIT_IF_NULL(
            right_edge = cpl_vector_new(ny-2*KMOS_BADPIX_BORDER));
        KMO_TRY_EXIT_IF_NULL(
            pleft_edge = cpl_vector_get_data(left_edge));
        KMO_TRY_EXIT_IF_NULL(
            pright_edge = cpl_vector_get_data(right_edge));
        cpl_vector_fill(left_edge, -1);
        cpl_vector_fill(right_edge, -1);

//        // allocate here an image (used as array) to keep track of the lcal
//        // values set in kmo_fit_spectrum. If this function fails these values
//        // can be set to 0.0 again -> only this slitlet is set invalid,
//        // pipeline can continue
//        KMO_TRY_EXIT_IF_NULL(
//            unroll_lcal = cpl_image_new(KMOS_DETECTOR_SIZE*70,
//                                        2, CPL_TYPE_FLOAT));

        //
        // loop all the slitlets
        //
        min_lambda = DBL_MAX;
        max_lambda = -DBL_MAX;

        KMO_TRY_CHECK_ERROR_STATE();
        max_nr_x_positions = 0;
        for (i = 0; i < KMOS_IFUS_PER_DETECTOR; i++)
// use loop below for line identification
//            for (int i = 4; i <= 4; i++)
        {
            for (sx = 0; sx < KMOS_SLITLET_Y; sx ++) {
                line_check_ok[i][sx] = FALSE;
            }

            ifu_nr = i+1;
            global_ifu_nr = (detector_nr-1)*KMOS_IFUS_PER_DETECTOR + ifu_nr;
//cpl_msg_debug(cpl_func,"IFU: %d", i+1);
            if ((edge_table[i] != NULL) &&
                (cpl_array_get_int(ifu_inactive, i, NULL) == 0))
            {
                // IFU has valid edges

                // loop all slitlets of this IFU
//cpl_msg_debug(cpl_func,"det: %d, ifu: %d, slit: %d", detector_nr, ifu_nr, slitlet_nr);
                for (k = 0; k < cpl_table_get_nrow(edge_table[i]); k+=2)
// use loop below for line identification
// (this will be last edge of IFU 4 --> middle of the detector)
//                     int t = cpl_table_get_nrow(edge_table[i])/2;
//                    for (int k = t; k <= t; k++)
                {
                    int sx = k/2;  //slitlet index
                    slitlet_nr = cpl_table_get_int(edge_table[i], "ID",
                                                   k, NULL);
                    KMO_TRY_CHECK_ERROR_STATE();
                    //
                    // calculate left and right edge
                    //
                    for (j = KMOS_BADPIX_BORDER; j < ny-KMOS_BADPIX_BORDER; j++)
                    {
                        pleft_edge[j-KMOS_BADPIX_BORDER]  = (int)(kmo_calc_fitted_slitlet_edge(edge_table[i], k, j)+0.5);
                        pright_edge[j-KMOS_BADPIX_BORDER] = (int)(kmo_calc_fitted_slitlet_edge(edge_table[i], k+1, j)+0.5);
                    }
//cpl_vector_save(left_edge, "left_edge.fits", CPL_BPP_IEEE_FLOAT, NULL,CPL_IO_DEFAULT);
//cpl_vector_save(right_edge, "right_edge.fits", CPL_BPP_IEEE_FLOAT, NULL,CPL_IO_DEFAULT);

//cpl_image_save(data, "img_data.fits", CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_CREATE);
//cpl_image_save(bad_pix, "img_badpix.fits", CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_CREATE);
//cpl_image* ddd = cpl_image_duplicate(data);
//for (int ggg = 5; ggg<2045; ggg++) {
//    int gggx = cpl_vector_get(left_edge, ggg-5);
//    int gggy = ggg;
//    cpl_image_set(ddd, gggx, gggy, 999999999);

//    gggx = cpl_vector_get(right_edge, ggg-5);
//    cpl_image_set(ddd, gggx, gggy, 888888888);
//}
//cpl_image_save(ddd, "img_data2.fits", CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_CREATE);
//cpl_image_delete(ddd);

                    //
                    // extract initial spectral trace
                    //
                    trace = kmo_extract_initial_trace(data,
                                                      bad_pix,
                                                      left_edge,
                                                      right_edge,
                                                      edge_offset);

//cpl_vector_save(trace, "trace.fits", CPL_BPP_IEEE_FLOAT, NULL,CPL_IO_DEFAULT);
                    if (trace == NULL) {
                        cpl_msg_warning("", "Couldn't get the wavelength "
                                        "trace for slitlet %d on IFU %d",
                                        slitlet_nr, global_ifu_nr);
                        cpl_error_reset();
                    } else {
                        // apply median-filter to trace and subtract it to
                        // remove any slope
                        KMO_TRY_EXIT_IF_NULL(
                            trace_median = cpl_vector_filter_median_create(
                                                                  trace, 20));

                        cpl_vector_subtract(trace, trace_median);
                        cpl_vector_delete(trace_median); trace_median = NULL;

                        // change orientation (real data only)
                        // In simulated data wavelengths go from bottom to top,
                        // in real data they go from top to bottom.
                        if (flip_trace == FALSE) {
                            kmo_vector_flip_old(trace);
                        }
//kmo_plot_vector("set title \"trace\";", "w l;", trace);

                        //
                        // identify initial lines,
                        // fit a vertical curve to get good initial estimates of
                        // all arcline positions
                        //

//                        kmo_find_lines_test (trace, lines/*, &positions, &lambdas*/);
                        if (data_fits_name != NULL) {
                            cpl_free(data_fits_name);
                        }
                        if (data_fits_prefix != NULL) {
                            data_fits_name = cpl_sprintf(
                                "%s_%s_ifu_%2.2d_slitlet_%2.2d.fits",
                                data_fits_prefix, filter_id, i+1 + 8 * (detector_nr-1), slitlet_nr);
                        }
                        if (data_fits_name != NULL) {
                            cpl_vector_save(NULL,data_fits_name,
                                    CPL_BPP_IEEE_DOUBLE,NULL,CPL_IO_CREATE);
                            kmo_wave_write_data_vector(data_fits_name,
                                    trace,"trace");
                            kmo_wave_write_data_vector(data_fits_name,
                                    cpl_bivector_get_x_const(lines), "line_list_lambda");
                            kmo_wave_write_data_vector(data_fits_name,
                                    cpl_bivector_get_y_const(lines), "line_list_strength");
                        }

                        if ((line_estimate_method & 3) == 0) {
                            ret_error = kmo_estimate_lines(trace,
                                                           cpl_bivector_get_x_const(lines),
                                                           filter_id, disp,
                                                           &positions, &lambdas,
                                                           detector_nr,
                                                           ifu_nr,
                                                           slitlet_nr,
                                                           data_fits_name);

                            if (data_fits_name != NULL) {
                                kmo_wave_write_data_vector(data_fits_name,
                                        positions, "estimate_positions");
                                kmo_wave_write_data_vector(data_fits_name,
                                        lambdas, "estimate_lambdas");
                                KMO_TRY_CHECK_ERROR_STATE();
                            }
                            int plotIt = 0;
                            if (plotIt) {
                                plot_estimated_lines(trace, lines, positions, lambdas);
                            }

                            if (line_estimate_method & 16) {
                                kmo_wave_guess_table_update_1(coeffs_table_filename,
                                        i+1 + (detector_nr-1) * KMOS_IFUS_PER_DETECTOR, /*slitlet_nr,*/
                                        filter_id, positions, lambdas);
                            }
                        }
                        if ((line_estimate_method & 3) == 1){
                            kmo_find_lines_2 (coeffs_table_filename, filter_id,
                                    global_ifu_nr, slitlet_nr,
                                    trace, lines,
                                    &positions, &lambdas);
                            ret_error = CPL_ERROR_NONE;
                            if (data_fits_name != NULL) {
                                kmo_wave_write_data_vector(data_fits_name,
                                                   positions, "find_positions");
                                kmo_wave_write_data_vector(data_fits_name,
                                                   lambdas, "find_lambdas");
                            }
                        }
                        if ((line_estimate_method & 3) == 2){
                            kmo_find_lines(filter_id,
                                           global_ifu_nr, slitlet_nr,
                                           trace, lines, reflines,
                                           &positions, &lambdas);
                            ret_error = CPL_ERROR_NONE;
                            if (data_fits_name != NULL) {
                                kmo_wave_write_data_vector(data_fits_name,
                                                   positions, "find_positions");
                                kmo_wave_write_data_vector(data_fits_name,
                                                   lambdas, "find_lambdas");
                            }
                        }

                        if (ret_error != CPL_ERROR_NONE) {
                            cpl_msg_warning("", "Couldn't estimate any line "
                                            "positions for slitlet %d on IFU "
                                            "%d", slitlet_nr, global_ifu_nr);
                            cpl_vector_delete(trace); trace = NULL;
                            cpl_error_reset();
                        } else {
                            // change orientation back (real data only)
                            if (flip_trace == FALSE) {
                                ppositions = cpl_vector_get_data(positions);
                                for (g = 0;
                                     g < cpl_vector_get_size(positions);
                                     g++)
                                {
                                    // correction of wave shift error of 1 pix
                                    //ppositions[g] = ny-1-ppositions[g];
                                    ppositions[g] = ny-ppositions[g];
                                }
                            }
                            cpl_vector_delete(trace); trace = NULL;

                            //
                            // calculate exact y-positions of arclines,
                            // fit a polynomial to every arcline
                            // extrapolate every fitted arcline to the
                            // minimum &maximum edges of the slitlet
                            //

                            // size in x for extrapolation for this slitlet
                            fit_arc_size_x = cpl_vector_get_max(right_edge) -
                                             cpl_vector_get_min(left_edge) +1;
                            fit_arc_size_y = cpl_vector_get_size(positions);
                            if (fit_arc_size_x > max_nr_x_positions) {
                                max_nr_x_positions = fit_arc_size_x;
                            }

                            // allocate enough memory to hold extrapolated
                            // arclines
                            cpl_vector_delete(xarray[i][sx]); xarray[i][sx] = NULL;
                            KMO_TRY_EXIT_IF_NULL(
                                xarray[i][sx] = cpl_vector_new(fit_arc_size_x));

                            cpl_vector_delete(larray[i][sx]); larray[i][sx] = NULL;

                            cpl_image_delete(yarray[i][sx]); yarray[i][sx] = NULL;
                            KMO_TRY_EXIT_IF_NULL(
                                yarray[i][sx] = cpl_image_new(fit_arc_size_x,
                                                       fit_arc_size_y,
                                                       CPL_TYPE_FLOAT));

                            //
                            // calculate exact y-positions of arclines &
                            // extrapolate
                            //
kmclipm_vector *lll = kmclipm_vector_new(fit_arc_size_y);
                            ret_error = kmo_extrapolate_arclines(
                                            data, bad_pix,
                                            positions, lambdas,
                                            left_edge, right_edge,
                                            &xarray[i][sx], &yarray[i][sx], &lll,
                                            &qc_vec,
                                            detector_nr, ifu_nr, slitlet_nr,
                                            filter_id,
                                            lamp_config);
larray[i][sx] = cpl_vector_duplicate(lll->data);
kmclipm_vector_delete(lll); lll = NULL;

                            if (data_fits_name != NULL) {
                                kmo_wave_write_data_vector(data_fits_name,
                                    larray[i][sx], "extrapolated_lambdas");
                            }

                            // count how many arclines could be extrapolated
                            nr_extrapolated_lines = 0;
                            KMO_TRY_EXIT_IF_NULL(
                                plarray = cpl_vector_get_data(larray[i][sx]));
                            for (z = 0;
                                 z < cpl_vector_get_size(larray[i][sx]); z++) {
                                if (plarray[z] > 0.0) {
                                    nr_extrapolated_lines++;
                                }
                            }

                            if (ret_error != CPL_ERROR_NONE) {
                                cpl_msg_warning("", "Couldn't extrapolate "
                                                "arclines in order to remove "
                                                "rotation for slitlet %d on "
                                                "IFU %d",
                                                slitlet_nr, global_ifu_nr);
                                cpl_error_reset();
                            } else if (nr_extrapolated_lines < 3) {
                                cpl_msg_warning("", "Less than 3 arclines "
                                                "could be extrapolated in "
                                                "order to remove rotation "
                                                "for slitlet %d on IFU %d",
                                                slitlet_nr, global_ifu_nr);
                            } else {
//cpl_vector_save(xarray[i][sx], "xarray.fits", CPL_BPP_IEEE_FLOAT, NULL,CPL_IO_DEFAULT);
//cpl_vector_save(larray[i][sx], "larray.fits", CPL_BPP_IEEE_DOUBLE, NULL,CPL_IO_DEFAULT);
//cpl_image_save(yarray[i][sx], "yarray.fits", CPL_BPP_IEEE_FLOAT, NULL,CPL_IO_DEFAULT);

                                line_check_ok[i][sx] = TRUE;

                                // set qc_parameters
                                pqc_ar_flux[slitlet_cnt] =
                                                     cpl_vector_get(qc_vec,1);
                                pqc_ne_flux[slitlet_cnt] =
                                                     cpl_vector_get(qc_vec,3);
                                slitlet_cnt++;
                            } // end if: no arclines extrapolated
                        } // end if: no lines estimated
                        cpl_vector_delete(positions); positions = NULL;
                        cpl_vector_delete(lambdas); lambdas = NULL;
                    } // end if: trace was NULL
                    KMO_TRY_CHECK_ERROR_STATE();
                } // end for k: loop slitlets
            } else {
                // IFU doesn't have any valid edges
                if (cpl_array_get_int(ifu_inactive, i, NULL) != 1) {
                    KMO_TRY_EXIT_IF_ERROR(
                        cpl_array_set_int(ifu_inactive, i, 2));
                }
            } // end if: IFU has edges (edge_table[i] != NULL)
            KMO_TRY_CHECK_ERROR_STATE();
        } // end for i: loop IFUs

        slitlet_cnt = 0;

        KMO_TRY_CHECK_ERROR_STATE();
        for (i = 0; i < KMOS_IFUS_PER_DETECTOR; i++)
        {
            ifu_nr = i+1;
            global_ifu_nr = (detector_nr-1)*KMOS_IFUS_PER_DETECTOR + ifu_nr;
            nr_valid_slitlets = 0;
            if ((edge_table[i] != NULL) &&
                (cpl_array_get_int(ifu_inactive, i, NULL) == 0))
            {
                KMO_TRY_EXIT_IF_NULL(
                        fitpars[i] = cpl_matrix_new(KMOS_SLITLET_Y *
                                                    max_nr_x_positions,
                                                    fit_order+1));
                KMO_TRY_EXIT_IF_NULL(
                        pfitpars = cpl_matrix_get_data(fitpars[i]));
                KMO_TRY_EXIT_IF_NULL(
                        valid_columns[i] = cpl_array_new(
//                                KMOS_SLITLET_Y * max_nr_x_positions *(fit_order+1),
                                KMOS_SLITLET_Y * max_nr_x_positions,
                                CPL_TYPE_INT));
                KMO_TRY_EXIT_IF_NULL(
                        pvalid_columns = cpl_array_get_data_int(valid_columns[i]));

                // loop thorugh all slitlets with detected lines of this IFU
                for (k = 0; k < cpl_table_get_nrow(edge_table[i]); k+=2)
                {
                    int sx = k/2;  //slitlet index
                    if ( ! line_check_ok[i][sx] ) {
                        continue;
                    }
                    if (data_fits_name != NULL) {
                        cpl_free(data_fits_name);
                    }
                    if (data_fits_prefix != NULL) {
                        data_fits_name = cpl_sprintf(
                            "%s_%s_ifu_%2.2d_slitlet_%2.2d.fits",
                            data_fits_prefix, filter_id, i+1 + 8 * (detector_nr-1), slitlet_nr);
                    }

                    slitlet_nr = cpl_table_get_int(edge_table[i], "ID",
                            k, NULL);
                    KMO_TRY_CHECK_ERROR_STATE();
                    //
                    // calculate left and right edge
                    //
                    for (j = 0; j < ny-2*KMOS_BADPIX_BORDER; j++)
                    {
                        pleft_edge[j]  = (int)(kmo_calc_fitted_slitlet_edge(edge_table[i], k, j+KMOS_BADPIX_BORDER)+0.5);
                        pright_edge[j] = (int)(kmo_calc_fitted_slitlet_edge(edge_table[i], k+1, j+KMOS_BADPIX_BORDER)+0.5);
                    }

                    //
                    // fit spectrum to the extrapolated arclines
                    //
                    int tmp_int =
                            kmo_fit_spectrum_1(bad_pix,
                                    xarray[i][sx], yarray[i][sx], larray[i][sx],
                                    left_edge, right_edge,
                                    lcal,
                                    pfitpars,
                                    fit_order,
                                    pvalid_columns,
                                    sx * max_nr_x_positions * (fit_order+1),
                                    detector_nr,
                                    ifu_nr,
                                    slitlet_nr,
                                    data_fits_name);
                    nr_valid_slitlets += tmp_int;
                    //               if ((ret_error != CPL_ERROR_NONE) || (tmp_int == FALSE)) {
                    if (tmp_int == FALSE) {
                        cpl_msg_warning("", "Couldn't fit a "
                                "spectrum for slitlet %d "
                                "on IFU %d",
                                slitlet_nr,
                                global_ifu_nr);
                        cpl_error_reset();
                    }
                }
            }
        }

        KMO_TRY_CHECK_ERROR_STATE();

//printf("skip fitting of wavelength fit coefficients\n");
        goto skip;
//
// compress fit parameter matrices by removing invalid slots
//

        for (i = 0; i < KMOS_IFUS_PER_DETECTOR; i++) {
            int lix = 0;  //local IFU index
            int nr_cols;
            int nr_elements;
            if ((edge_table[i] != NULL) &&
                (cpl_array_get_int(ifu_inactive, i, NULL) == 0))
            {
                nr_cols = cpl_matrix_get_nrow(fitpars[i]);
                nr_elements = cpl_array_get_size(valid_columns[i]);
                if (nr_cols != nr_elements) {
                    cpl_msg_debug(__func__,
                            "trace point: nr_cols: %d      nr_elements: %d\n",
                            nr_cols,nr_elements);
                }
                if (nr_cols <= nr_elements) {
                    const int *cols = cpl_array_get_data_int_const(valid_columns[i]);
                    lix = 0;
                    for (k = 0; k < nr_cols; k++) {
                        if (cols[k] == 1) {
                            lix++;
                        }
                    }

                    KMO_TRY_EXIT_IF_NULL(
                            cfitpars[i] = cpl_matrix_new(lix, fit_order+1));

                    lix = 0;
                    for (k = 0; k < nr_cols; k++) {
                        if (cols[k] == 1) {
                            KMO_TRY_EXIT_IF_ERROR(
                                    cpl_matrix_copy(cfitpars[i],
                                            cpl_matrix_extract_row(fitpars[i],k),
                                            lix,0));
                            lix++;
                        }
                    }
                }
            }
        }

        char *value;
        int if_then_all_pars = 1;
        int use_chebyshev = 0;
        double threshold_factor = 3.0;  // n times stdev
        int left_right_pairs = 0;
        if ((value = getenv("LC_ALLPARS")) != NULL) {
            if_then_all_pars = atoi(value);
        }
        if ((value = getenv("LC_CH")) != NULL) {
            use_chebyshev = atoi(value);
        }
        if ((value = getenv("LC_SIDES")) != NULL) { // number of different sizes
                                                    // 1 -> all are equal, 2 -> left / right edges
            left_right_pairs = atoi(value);
        }
        if ((value = getenv("LC_FACTOR")) != NULL) { // n times stdev
            threshold_factor = atof(value);
        }

        KMO_TRY_EXIT_IF_ERROR(
                kmo_flat_interpolate_edge_parameters(cfitpars,
                        threshold_factor, use_chebyshev, if_then_all_pars, left_right_pairs));

//
// "decompress" fit parameter matrices by adding invalid slots again
//
        for (i = 0; i < KMOS_IFUS_PER_DETECTOR; i++) {
            int lix = 0;  //local IFU index
            int nr_cols;
            int nr_elements;
            if ((edge_table[i] != NULL) &&
                (cpl_array_get_int(ifu_inactive, i, NULL) == 0))
            {
                cpl_matrix_fill(fitpars[i],0.0);
                nr_cols = cpl_matrix_get_nrow(fitpars[i]);
                nr_elements = cpl_array_get_size(valid_columns[i]);
                if (nr_cols <= nr_elements) {
                    const int *cols = cpl_array_get_data_int_const(valid_columns[i]);
                    lix = 0;
                    for (k = 0; k < nr_cols; k++) {
                        if (cols[k] == 1) {
                            KMO_TRY_EXIT_IF_ERROR(
                                    cpl_matrix_copy(fitpars[i],
                                            cpl_matrix_extract_row(cfitpars[i],lix),
                                            k,0));
                            lix++;
                        }
                    }
                    if (line_estimate_method & 32) {
                        kmo_wave_guess_table_update_2(coeffs_table_filename,
                                i+1, i+1 + (detector_nr-1) * KMOS_IFUS_PER_DETECTOR,
                                filter_id, flip_trace, cfitpars);
                    }
                }
            }
        }

skip:
        for (i = 0; i < KMOS_IFUS_PER_DETECTOR; i++)
        {
            ifu_nr = i+1;
            global_ifu_nr = (detector_nr-1)*KMOS_IFUS_PER_DETECTOR + ifu_nr;
            nr_valid_slitlets = 0;
            if ((edge_table[i] != NULL) &&
                (cpl_array_get_int(ifu_inactive, i, NULL) == 0))
            {
                // IFU has valid edges

               KMO_TRY_EXIT_IF_NULL(
                        pfitpars = cpl_matrix_get_data(fitpars[i]));
               KMO_TRY_EXIT_IF_NULL(
                        pvalid_columns = cpl_array_get_data_int(valid_columns[i]));

                // loop all slitlets of this IFU
                for ( k = 0; k < cpl_table_get_nrow(edge_table[i]); k+=2)
                {
                    int sx = k/2;  //slitlet index
                    if ( ! line_check_ok[i][sx] ) {
                        continue;
                    }
                    slitlet_nr = cpl_table_get_int(edge_table[i], "ID",
                                                   k, NULL);
                    KMO_TRY_CHECK_ERROR_STATE();
                    //
                    // calculate left and right edge
                    //
                    for (j = 0; j < ny-2*KMOS_BADPIX_BORDER; j++)
                    {
                        pleft_edge[j]  = (int)(kmo_calc_fitted_slitlet_edge(edge_table[i], k, j+KMOS_BADPIX_BORDER)+0.5);
                        pright_edge[j] = (int)(kmo_calc_fitted_slitlet_edge(edge_table[i], k+1, j+KMOS_BADPIX_BORDER)+0.5);
                    }

                    //
                    // fit spectrum to the extrapolated arclines
                    //

                    int tmp_int =
                            kmo_fit_spectrum_2(bad_pix,
                                        xarray[i][sx], yarray[i][sx], larray[i][sx],
                                        left_edge, right_edge,
                                        lcal,
                                        &tmp_min_lambda,
                                        &tmp_max_lambda,
                                        pfitpars,
                                        fit_order,
                                        pvalid_columns,
                                        sx * max_nr_x_positions * (fit_order+1),
                                        detector_nr,
                                        ifu_nr,
                                        slitlet_nr);

//                    int tmp_int =
//                            kmo_fit_spectrum(bad_pix,
//                                        xarray[i][sx], yarray[i][sx], larray[i][sx],
//                                        left_edge, right_edge,
//                                        lcal,
//                                        &tmp_min_lambda,
//                                        &tmp_max_lambda,
//                                        fit_order,
//                                        detector_nr,
//                                        ifu_nr,
//                                        slitlet_nr);

                    nr_valid_slitlets += tmp_int;
                    if ((ret_error != CPL_ERROR_NONE) ||
                         (tmp_int == FALSE))
                    {
                        cpl_msg_warning("", "Couldn't fit a "
                                        "spectrum for slitlet %d "
                                        "on IFU %d",
                                        slitlet_nr,
                                        global_ifu_nr);
                        cpl_error_reset();
                    } else {
                        if (tmp_min_lambda < min_lambda) {
                            min_lambda = tmp_min_lambda;
                        }
                        if (tmp_max_lambda > max_lambda) {
                            max_lambda = tmp_max_lambda;
                        }

                        slitlet_cnt++; // increase edge counter
                                       // (for this detector)
                    } // end if: no spectrum fitted
                } // end of slitlet loop
                if (nr_valid_slitlets == 0) {
                    cpl_msg_warning("", "No wavelength calibration performed "
                                    "for IFU %d. Set it invalid now.",
                                    global_ifu_nr);
                    // set IFU invalid
                    if (cpl_array_get_int(ifu_inactive, i, NULL) != 1) {
                        KMO_TRY_EXIT_IF_ERROR(
                            cpl_array_set_int(ifu_inactive, i, 2));
                    }
                }
           } // end if: IFU has edges (edge_table[i] != NULL)
        } //end of IFU loop

        //
        // check if all IFUs are deactivated, if yes: quit function
        //
        int cnt_invalid = 0;
        for (i = 0; i < KMOS_IFUS_PER_DETECTOR; i++) {
            if ((cpl_array_get_int(ifu_inactive, i, NULL) == 1) ||
                (cpl_array_get_int(ifu_inactive, i, NULL) == 2))
            {
                cnt_invalid++;
            }
        }

        if (cnt_invalid == KMOS_IFUS_PER_DETECTOR) {
            cpl_error_set(cpl_func, CPL_ERROR_UNSPECIFIED);
            no_err_msg = TRUE;
            KMO_TRY_CHECK_ERROR_STATE();
        }

        KMO_TRY_EXIT_IF_ERROR(
            cpl_image_multiply(*lcal, bad_pix));

        //
        // calculate global qc parameters on the whole detector frame
        //
        tmp_qc1 = kmo_idl_where(qc_ar_flux, -1, ne);
        if (tmp_qc1 != NULL) {
            KMO_TRY_EXIT_IF_NULL(
                tmp_qc2 = kmo_idl_values_at_indices(qc_ar_flux, tmp_qc1));
            *qc_ar_eff = kmo_vector_get_mean_old(tmp_qc2);
            cpl_vector_delete(tmp_qc2); tmp_qc2 = NULL;
        } else {
            *qc_ar_eff = -1.0;
        }
        cpl_vector_delete(tmp_qc1); tmp_qc1 = NULL;


        tmp_qc1 = kmo_idl_where(qc_ne_flux, -1, ne);
        if (tmp_qc1 != NULL) {
            KMO_TRY_EXIT_IF_NULL(
                tmp_qc2 = kmo_idl_values_at_indices(qc_ne_flux, tmp_qc1));
            *qc_ne_eff = kmo_vector_get_mean_old(qc_ne_flux);
            cpl_vector_delete(tmp_qc2); tmp_qc2 = NULL;
        } else {
            *qc_ne_eff = -1.0;
        }
        cpl_vector_delete(tmp_qc1); tmp_qc1 = NULL;
    }
    KMO_CATCH
    {
        if (!no_err_msg) {
            KMO_CATCH_MSG();
        }
        ret_error = cpl_error_get_code();

        cpl_image_delete(*lcal); *lcal = NULL;
    }

    for (i = 0; i < KMOS_IFUS_PER_DETECTOR; i++) {
        if ((edge_table[i] != NULL) &&
            (cpl_array_get_int(ifu_inactive, i, NULL) == 0))
        {
            int nr_slitlets = cpl_table_get_nrow(edge_table[i])/2;
            for (j = 0; j<nr_slitlets; j++) {
                cpl_vector_delete(xarray[i][j]); xarray[i][j] = NULL;
                cpl_image_delete(yarray[i][j]); yarray[i][j] = NULL;
                cpl_vector_delete(larray[i][j]); larray[i][j] = NULL;
            }
        }
        cpl_free(xarray[i]); xarray[i] = NULL;
        cpl_free(yarray[i]); yarray[i] = NULL;
        cpl_free(larray[i]); larray[i] = NULL;
        cpl_matrix_delete(fitpars[i]); fitpars[i] = NULL;
        cpl_matrix_delete(cfitpars[i]); cfitpars[i] = NULL;
        cpl_array_delete(valid_columns[i]); valid_columns[i] = NULL;
    }
    cpl_free(xarray); xarray = NULL;
    cpl_free(yarray); yarray = NULL;
    cpl_free(larray); larray = NULL;
    cpl_free(fitpars); fitpars = NULL;
    cpl_free(cfitpars); cfitpars = NULL;
    cpl_free(valid_columns); valid_columns = NULL;

    cpl_vector_delete(midline); midline = NULL;
    cpl_vector_delete(trace); trace = NULL;
    cpl_vector_delete(positions); positions = NULL;
    cpl_vector_delete(lambdas); lambdas = NULL;
    cpl_vector_delete(qc_vec); qc_vec = NULL;
    cpl_vector_delete(qc_ar_flux); qc_ar_flux = NULL;
    cpl_vector_delete(qc_ne_flux); qc_ne_flux = NULL;
    cpl_vector_delete(trace_median); trace_median = NULL;
    cpl_vector_delete(left_edge); left_edge = NULL;
    cpl_vector_delete(right_edge); right_edge = NULL;

    return ret_error;
}

/**
    @brief
        Extracts a wavelength profile of a slitlet from an arcframe.

    @param data         The arc frame.
    @param bad_pix      The associated bad pixel frame.
    @param left_edge    The left edge of a slitlet along he y axis of the
                        detector frame.
    @param right_edge   The right edge of a slitlet along he y axis of the
                        detector frame.
    @param edge_offset  Offset to apply to the defined edge positions, to
                        compensate the rotation of the detector frame.

    @return
        The extracted wavelength profile.

    For each wavelength (y-direction) the median of all pixels between
    left_edge + edge_offset and right_edge - edge_offset is calculated.

    (Used in kmo_calc_wave_calib())

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT    if any of the inputs is zero.
*/
cpl_vector*     kmo_extract_initial_trace(const cpl_image *data,
                                          const cpl_image *bad_pix,
                                          const cpl_vector *left_edge,
                                          const cpl_vector *right_edge,
                                          const int edge_offset)
{
    cpl_vector      *trace          = NULL,
                    *my_tmp         = NULL,
                    **tmp_array     = NULL;

    double          *ptrace         = NULL,
                    *pmy_tmp        = NULL;

    const double    *pleft_edge     = NULL,
                    *pright_edge    = NULL;

    const float     *pdata          = NULL,
                    *pbad_pix       = NULL;

    int             k               = 0,
                    i               = 0,
                    j               = 0,
                    nx              = 0,
                    ny              = 0,
                    my_right        = 0,
                    my_left         = 0,
                    offset          = edge_offset,
                    nr_tmp          = 25;   // nr. of preallocated vectors

    KMO_TRY
    {
        KMO_TRY_ASSURE((data != NULL) &&
                       (bad_pix != NULL) &&
                       (left_edge != NULL) &&
                       (right_edge != NULL),
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        nx = cpl_image_get_size_x(data);
        ny = cpl_image_get_size_y(data);

        KMO_TRY_ASSURE((nx == cpl_image_get_size_x(bad_pix)) &&
                       (ny == cpl_image_get_size_y(bad_pix)) &&
                       (ny == cpl_vector_get_size(left_edge)+2*KMOS_BADPIX_BORDER) &&
                       (ny == cpl_vector_get_size(right_edge)+2*KMOS_BADPIX_BORDER),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "Sizes of inputs don't match!");

        KMO_TRY_EXIT_IF_NULL(
            pdata = cpl_image_get_data_float_const(data));
        KMO_TRY_EXIT_IF_NULL(
            pbad_pix = cpl_image_get_data_float_const(bad_pix));
        KMO_TRY_EXIT_IF_NULL(
            pleft_edge = cpl_vector_get_data_const(left_edge));
        KMO_TRY_EXIT_IF_NULL(
            pright_edge = cpl_vector_get_data_const(right_edge));

        KMO_TRY_EXIT_IF_NULL(
            trace = cpl_vector_new(ny));
        cpl_vector_fill(trace, 0.0);
        KMO_TRY_EXIT_IF_NULL(
            ptrace = cpl_vector_get_data(trace));
//cpl_vector_save(left_edge, "left_edge.fits", CPL_BPP_IEEE_FLOAT, NULL,CPL_IO_DEFAULT);
//cpl_vector_save(right_edge, "right_edge.fits", CPL_BPP_IEEE_FLOAT, NULL,CPL_IO_DEFAULT);
        // preallocatarray of 25 vectors of length 1 to 26
        // in order to calculate median in for-loop, is faster that way
        KMO_TRY_EXIT_IF_NULL(
            tmp_array = (cpl_vector**)cpl_malloc(nr_tmp * sizeof(cpl_vector*)));
        for (i = 0; i < nr_tmp; i++) {
            tmp_array[i] = cpl_vector_new(i+1);
        }

        // loop along y axis of detector
        for (i = KMOS_BADPIX_BORDER; i < ny-KMOS_BADPIX_BORDER; i++) {
            offset = edge_offset;
            // take middle part of slitlet to get a trace value
            my_left = pleft_edge[i-KMOS_BADPIX_BORDER] + offset;
            my_right = pright_edge[i-KMOS_BADPIX_BORDER] - offset;

            // check if it is at least 4 pixels wide, if no: make it larger
            while ((my_right-my_left < 4) && (offset > 0)) {
                offset--;
                my_left = pleft_edge[i-KMOS_BADPIX_BORDER] + offset;
                my_right = pright_edge[i-KMOS_BADPIX_BORDER] - offset;
            }

            if ((my_left<my_right) && (my_left != -1) && (my_right != -1)) {
                // detemine which preallocated vector to use
                if (my_right-my_left+1 > nr_tmp) {
                    // huh, vector is so much tilted or bent that I need a larger vector
                    my_tmp = cpl_vector_new(my_right-my_left+1);
                } else {
                    my_tmp = tmp_array[my_right-my_left];
                }

                cpl_vector_fill(my_tmp, 0.0);
                KMO_TRY_EXIT_IF_NULL(
                    pmy_tmp = cpl_vector_get_data(my_tmp));

                // loop from left to right of slitlet, omit bad pixels
                k = 0;
                for (j = my_left; j <= my_right; j++) {
                    if (pbad_pix[j+i*nx] >= 0.5) {
                        pmy_tmp[k++] = pdata[j+i*nx];
                    }
                }
                ptrace[i] = cpl_vector_get_median(my_tmp);

                if (my_right-my_left+1 > nr_tmp) {
                    // delete the large vecor again
                    cpl_vector_delete(my_tmp); my_tmp = NULL;
                }
            }
        }
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        cpl_vector_delete(trace); trace = NULL;
    }

    for (i = 0; i < nr_tmp; i++) {
        cpl_vector_delete(tmp_array[i]); tmp_array[i] = NULL;
    }
    cpl_free(tmp_array); tmp_array = NULL;

    return trace;
}

/**
    @brief
         Generate output to estimate line detection quality

    Creates a plot, console output and files

    @param trace_low    The low-pass filtered trace along the wavelength axis.
    @param peak_pos     The identified candidate positions of lines.
    @param peak_lambda  The candidate wavelengths of the linelist.
    @param positions    The positions of the matched lines.
    @param lambdas      The wavelengths of the matched lines.
    @param dbg_detector_nr (Needed fort debugging only)
    @param dbg_ifu_nr      (Needed fort debugging only)
    @param dbg_slitlet_nr  (Needed fort debugging only)


    @return
        The function returns CPL_ERROR_NONE on success or a CPL error code
        otherwise.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT    if any of the inputs is zero or any of the
                                outputs isn't allocated.
*/
cpl_error_code  debug_fitted_lines(const cpl_vector *trace_low,
                                   const cpl_vector *peak_pos,
                                   const cpl_vector *peak_lambda,
                                   const cpl_vector *positions,
                                   const cpl_vector *lambdas,
                                   int dbg_detector_nr,
                                   int dbg_ifu_nr,
                                   int dbg_slitlet_nr)
{
    int             nr_plots            = 4,
                    speak_pos           = 0,
                    speak_lambda        = 0,
                    spositions          = 0,
                    slambdas            = 0,
                    strace_low          = 0,
                    offset              = 500,
                    all_matched         = FALSE,
                    all_list            = FALSE,
                    k                   = 0;
    cpl_bivector    *plots[nr_plots];

    cpl_vector      *xx                 = NULL,
                    *in_peaky           = NULL,
                    *out_peaky          = NULL,
                    *non_matched        = NULL,
                    *non_matchedy       = NULL,
                    *non_list           = NULL,
                    *non_listy          = NULL;

    double          *pxx                = NULL,
                    *pin_peaky          = NULL,
                    *pout_peaky         = NULL,
                    *pnon_matched       = NULL,
                    *pnon_matchedy      = NULL,
                    *pnon_list          = NULL,
                    *pnon_listy         = NULL;

    const double    *ppeak_pos          = NULL,
                    *ppeak_lambda       = NULL,
                    *ptrace_low         = NULL,
                    *ppositions         = NULL,
                    *plambdas           = NULL;

    char            *title              = NULL;

    cpl_error_code  ret_error           = CPL_ERROR_NONE;

    KMO_TRY
    {
        KMO_TRY_ASSURE((trace_low != NULL) &&
                       (peak_pos != NULL) &&
                       (peak_lambda != NULL) &&
                       (positions != NULL) &&
                       (lambdas != NULL),
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        // get sizes of input vectors
        speak_pos    = cpl_vector_get_size(peak_pos);
        speak_lambda = cpl_vector_get_size(peak_lambda);
        spositions   = cpl_vector_get_size(positions);
        slambdas     = cpl_vector_get_size(lambdas);
        strace_low   = cpl_vector_get_size(trace_low);
        KMO_TRY_CHECK_ERROR_STATE();

        KMO_TRY_ASSURE(spositions == slambdas,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "positions and lambdas must have same size!");

        if (dbgplot)
        {
//    kmo_plot_vector("set title \"trace\";", "w l;", trace);
//    kmo_plot_vector("set title \"trace low-pass\";", "w l;", trace_low);

            //
            // plot data with gnuplot
            //
            KMO_TRY_EXIT_IF_NULL(
                xx  = cpl_vector_duplicate(trace_low));
            KMO_TRY_EXIT_IF_NULL(
                in_peaky  = cpl_vector_duplicate(xx));
            KMO_TRY_EXIT_IF_NULL(
                out_peaky = cpl_vector_duplicate(xx));
            if (speak_pos-spositions == 0) {
                all_matched = TRUE;
            } else {
                KMO_TRY_EXIT_IF_NULL(
                    non_matched = cpl_vector_new(speak_pos-spositions));
                KMO_TRY_EXIT_IF_NULL(
                    non_matchedy = cpl_vector_duplicate(xx));
                KMO_TRY_EXIT_IF_ERROR(
                    cpl_vector_fill(non_matchedy, -1000.0));
                KMO_TRY_EXIT_IF_NULL(
                    pnon_matched  = cpl_vector_get_data(non_matched));
                KMO_TRY_EXIT_IF_NULL(
                    pnon_matchedy  = cpl_vector_get_data(non_matchedy));
            }
            if (speak_lambda-slambdas == 0) {
                all_list = TRUE;
            } else {
                KMO_TRY_EXIT_IF_NULL(
                    non_list = cpl_vector_new(speak_lambda-slambdas));
                KMO_TRY_EXIT_IF_NULL(
                    non_listy = cpl_vector_duplicate(xx));
                KMO_TRY_EXIT_IF_ERROR(
                    cpl_vector_fill(non_listy, -1000.0));
                KMO_TRY_EXIT_IF_NULL(
                    pnon_list  = cpl_vector_get_data(non_list));
                KMO_TRY_EXIT_IF_NULL(
                    pnon_listy  = cpl_vector_get_data(non_listy));
            }
            KMO_TRY_EXIT_IF_ERROR(
                cpl_vector_fill(in_peaky, -1000.0));
            KMO_TRY_EXIT_IF_ERROR(
                cpl_vector_fill(out_peaky, -1000.0));
            KMO_TRY_EXIT_IF_NULL(
                pxx = cpl_vector_get_data(xx));
            KMO_TRY_EXIT_IF_NULL(
                pin_peaky = cpl_vector_get_data(in_peaky));
            KMO_TRY_EXIT_IF_NULL(
                pout_peaky = cpl_vector_get_data(out_peaky));
            KMO_TRY_EXIT_IF_NULL(
                ppeak_pos = cpl_vector_get_data_const(peak_pos));
            KMO_TRY_EXIT_IF_NULL(
                ppeak_lambda = cpl_vector_get_data_const(peak_lambda));
            KMO_TRY_EXIT_IF_NULL(
                ptrace_low = cpl_vector_get_data_const(trace_low));
            KMO_TRY_EXIT_IF_NULL(
                ppositions = cpl_vector_get_data_const(positions));
            KMO_TRY_EXIT_IF_NULL(
                plambdas = cpl_vector_get_data_const(lambdas));

            // create x vector
            for (k = 0; k < strace_low; k++) {
                pxx[k] = k;
            }
            KMO_TRY_CHECK_ERROR_STATE();

            // create y vector: in peak positions (in_peaky)
            int ind = 0;
            for (k = 0; k < strace_low; k++) {
                if (k > ppeak_pos[ind]) {
                    pin_peaky[k] = ptrace_low[k];
                    ind++;
                    if (ind == speak_pos) {
                        break;
                    }
                }
            }
            KMO_TRY_CHECK_ERROR_STATE();

            // create y vector: matched peak positions (out_peaky)
            ind = 0;
            for (k = 0; k < strace_low; k++) {
                if (k > ppositions[ind]) {
                    pout_peaky[k] = ptrace_low[k] + offset;
                    ind++;
                    if (ind == spositions) {
                        break;
                    }
                }
            }
            KMO_TRY_CHECK_ERROR_STATE();

            // find unmatched positions & non-existing lines in linelist
            int max = speak_pos;
            if (max < speak_lambda) max = speak_lambda;
            if (max < spositions) max = spositions;
            int ipeak_pos = 0,
                ipositions = 0,
                ipeak_lambda = 0,
                n = 0, l = 0;
            for (k = 0; k < max; k++) {
                if ((ppeak_pos[ipeak_pos] == ppositions[ipositions]) &&
                   (ppeak_lambda[ipeak_lambda] == plambdas[ipositions]))
                {
                    ipeak_pos++;
                    ipositions++;
                    ipeak_lambda++;
                } else {
                    if ((ppeak_pos[ipeak_pos] < ppositions[ipositions]) &&
                        (ppeak_lambda[ipeak_lambda] == plambdas[ipositions]))
                    {
                        // position missed
                        if (!all_matched) {
                            pnon_matched[n] = ppeak_pos[ipeak_pos++];
//                            cpl_msg_error("", ">>>match>>>%g", pnon_matched[n]);
                            n++;
                        }
                    } else if ((ppeak_pos[ipeak_pos] < ppositions[ipositions]) &&
                               (ppeak_lambda[ipeak_lambda] < plambdas[ipositions]))
                    {
                        // position AND line missed
                        if (!all_matched) {
                            pnon_matched[n] = ppeak_pos[ipeak_pos++];
//                            cpl_msg_error("", ">>>match>>>%g", pnon_matched[n]);
                            n++;
                        }
                        if (!all_list) {
                            pnon_list[l] = ppeak_lambda[ipeak_lambda++];
                            cpl_msg_warning("", "line non matched: %g", pnon_list[l]);
                            l++;
                        }
                    } else {
                        // line missed
                        if (!all_list) {
                            pnon_list[l] = ppeak_lambda[ipeak_lambda++];
                            cpl_msg_warning("", "line non matched: %g", pnon_list[l]);
                            l++;
                        }
                    }
                }
            }
            KMO_TRY_CHECK_ERROR_STATE();
            // adding some eventually non-recognised at the end
            if (ipeak_lambda < speak_lambda) {
                for (k = ipeak_lambda; k < speak_lambda; k++) {
                    if (!all_list) {
                        pnon_list[l] = ppeak_lambda[ipeak_lambda++];
//                        cpl_msg_error("", "a>>>list>>>%g", pnon_list[l]);
                        l++;
                    }
                }
            }
            if (ipeak_pos < speak_pos) {
                for (k = ipeak_pos; k < speak_pos; k++) {
                    if (!all_matched) {
                        pnon_matched[n] = ppeak_pos[ipeak_pos++];
//                        cpl_msg_error("", ">>>amatch>>>%g", pnon_matched[n]);
                        n++;
                    }
                }
            }
            KMO_TRY_CHECK_ERROR_STATE();

            // create non_matched vector
            if (!all_matched) {
                ind = 0;
                for (k = 0; k < strace_low; k++) {
                    if (k > pnon_matched[ind]) {
                        pnon_matchedy[k] = ptrace_low[k] + offset+50;
                        ind++;
                        if (ind == cpl_vector_get_size(non_matched)) {
                            break;
                        }
                    }
                }
                KMO_TRY_CHECK_ERROR_STATE();
            }

            KMO_TRY_EXIT_IF_NULL(
                plots[0] = cpl_bivector_wrap_vectors(xx, (cpl_vector*)trace_low));
            KMO_TRY_EXIT_IF_NULL(
                plots[1] = cpl_bivector_wrap_vectors(xx, in_peaky));
            KMO_TRY_EXIT_IF_NULL(
                plots[2] = cpl_bivector_wrap_vectors(xx, out_peaky));
            if (!all_matched) {
                KMO_TRY_EXIT_IF_NULL(
                    plots[3] = cpl_bivector_wrap_vectors(xx, non_matchedy));
            }

            char *options[4] = {
                    "w l t 'trace'",
                    "w p lc rgbcolor \"blue\" t 'candidate peaks'",
                    "w p lc rgbcolor \"green\" t 'matched peaks'",
                    "w p lc rgbcolor \"red\" t 'non-matched peaks'"};
            if (!all_matched) {
                KMO_TRY_EXIT_IF_NULL(
                    title = cpl_sprintf("set term x11; set title 'low_pass trace  "
                                    "(# lines: list: %d, candidate: %d, matched: "
                                    "%d) Det: %d, IFU: %d, slitlet: %d';",
                                    speak_lambda, speak_pos, spositions,
                                    dbg_detector_nr, dbg_ifu_nr, dbg_slitlet_nr));
            } else {
                nr_plots = 3;
                KMO_TRY_EXIT_IF_NULL(
                    title = cpl_sprintf("set term x11; set title 'low_pass trace  "
                                    "(# lines: list: %d, candidate: %d) Det: %d, "
                                    "IFU: %d, slitlet: %d';", speak_lambda,
                                    speak_pos, dbg_detector_nr, dbg_ifu_nr,
                                    dbg_slitlet_nr));
            }

            KMO_TRY_EXIT_IF_ERROR(
                cpl_plot_bivectors(title,
                               (const char**)options,
                               "",
                               (const cpl_bivector**)plots,
                               nr_plots));
            for (k = 0; k < nr_plots; k++) {
                cpl_bivector_unwrap_vectors(plots[k]);
            }
            KMO_TRY_CHECK_ERROR_STATE();

            cpl_vector_delete(xx); xx = NULL;
            cpl_vector_delete(in_peaky); in_peaky = NULL;
            cpl_vector_delete(out_peaky); out_peaky = NULL;
            cpl_vector_delete(non_matchedy); non_matchedy = NULL;
            cpl_vector_delete(non_matched); non_matched = NULL;
            KMO_TRY_CHECK_ERROR_STATE();

            //
            // save data to disk
            //
            cpl_vector_save(trace_low, "dbg_trace_low.fits",
                            CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_DEFAULT);
            cpl_vector_save(peak_pos, "dbg_in_pos.fits",
                            CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_DEFAULT);
            cpl_vector_save(peak_lambda, "dbg_in_lambda.fits",
                            CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_DEFAULT);
            cpl_vector_save(positions, "dbg_matched_pos.fits",
                            CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_DEFAULT);
            cpl_vector_save(lambdas, "dbg_matched_lambda.fits",
                            CPL_BPP_IEEE_FLOAT, NULL, CPL_IO_DEFAULT);
            KMO_TRY_CHECK_ERROR_STATE();

            //
            // print data to console
            //
            char cin_pos[256], cin_lam[256], cout_pos[256], cout_lam[256];
            max = speak_pos;
            int k_out = 0,
                same = FALSE;
            if (max < speak_lambda) max = speak_lambda;
            if (max < spositions) max = spositions;
            if (max < slambdas) max = slambdas;

            cpl_msg_debug(cpl_func,"---------------------------------------"
                             "---------------------------------------");
            cpl_msg_debug(cpl_func,"Det: %d, IFU: %d, slitlet: %d",
                          dbg_detector_nr, dbg_ifu_nr, dbg_slitlet_nr);
            cpl_msg_debug(cpl_func,"lines list: %d, candidate: %d, matched: %d",
                          speak_lambda, speak_pos, spositions);
            cpl_msg_debug(cpl_func,"|in:\tindex\tpos,\tlambda\t"
                             "|out:\tindex\tpos,\tlambda");
            for (k = 0; k < max; k++) {
                if (fabs(ppeak_pos[k] - ppositions[k_out]) < 0.001)
                    same = TRUE;
                else same = FALSE;

                if (k < speak_pos) sprintf(cin_pos, "%g", ppeak_pos[k]);
                else strcpy(cin_pos, "-");

                if (k < speak_lambda) sprintf(cin_lam, "%g", ppeak_lambda[k]);
                else strcpy(cin_lam, "-");

                if (same) {
                    if (k_out < spositions) sprintf(cout_pos, "%g", ppositions[k_out]);
                    else strcpy(cout_pos, "-");

                    if (k_out < slambdas) sprintf(cout_lam, "%g", plambdas[k_out]);
                    else strcpy(cout_lam, "-");

                    k_out++;
                } else {
                    strcpy(cout_pos, "-");
                    strcpy(cout_lam, "-");
                }
                cpl_msg_debug(cpl_func,"|\t%d\t%s\t%s\t|\t%d\t%s\t%s",
                              k+1, cin_pos, cin_lam,
                              k+1, cout_pos, cout_lam);
            }
            cpl_msg_debug(cpl_func,"---------------------------------------"
                             "---------------------------------------");
            KMO_TRY_CHECK_ERROR_STATE();

            // uncomment this line if all this should be printed just once
            //dbgplot = FALSE;
        }
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        ret_error = cpl_error_get_code();
    }

    return ret_error;
}

/**
    @brief
        Matches given arclines to a wavelength profile.

    @param trace           The given wavelength profile.
    @param lines           Vector with the defined arclines. This vector has
                           been generated with kmo_get_lines().
    @param filter_id       The filter ID of this detector.
    @param disp            The expected dispersion in wavelength.
    @param positions       (Output) Matched positions.
    @param lambdas         (Output) Matched wavelengths.
    @param dbg_detector_nr (Needed fort debugging only)
    @param dbg_ifu_nr      (Needed fort debugging only)
    @param dbg_slitlet_nr  (Needed fort debugging only)

    @return
        The function returns CPL_ERROR_NONE on success or a CPL error code
        otherwise.

    First the positions of the peaks in @c trace are detected. Therefore the
    trace is low-pass filtered and thresholded (peaks are only recognised if the
    values between to peaks go below the threshold!) and the maximum values are
    detected. To each of these preliminary positions a gaussfit is applied to
    get the exact position.
    Then a line pattern matching using cpl_ppm_match_positions() is performed.

    (Used in kmo_calc_wave_calib())

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT    if any of the inputs is zero.
*/
cpl_error_code  kmo_estimate_lines(const cpl_vector *trace,
                                   const cpl_vector *lines,
                                   const char *filter_id,
                                   double disp,
                                   cpl_vector **positions,
                                   cpl_vector **lambdas,
                                   int dbg_detector_nr,
                                   int dbg_ifu_nr,
                                   int dbg_slitlet_nr,
                                   const char *data_fits_name)
{
    cpl_error_code  ret_error   = CPL_ERROR_NONE;

    cpl_bivector    *matched_positions  = NULL;


    cpl_vector  *trace_low              = NULL,
                *tmp_vec                = NULL,
                *peak_pos               = NULL,
                *dx                     = NULL,
                *dy                     = NULL;

    double      tolerance               = 0.02,     // 2 %
                thresh                  = 0.0,
                min_disp                = 0.0,
                max_disp                = 0.0,
                x0                      = 0.0,
                sigma                   = 0.0,
                area                    = 0.0,
                offset                  = 0.0,
                *ptrace_low             = NULL,
                *ptmp_vec               = NULL,

                *ppeak_pos              = NULL,
                *pdx                    = NULL,
                *pdy                    = NULL;

    const double *ptrace                = NULL;

    int         tmp_max_pos             = -1,
                i                       = 0,
                k                       = 0,
                j                       = 0,
                max_lines               = 200;

    KMO_TRY
    {
        KMO_TRY_ASSURE((trace != NULL) &&
                       (filter_id != NULL) &&
                       (lines != NULL) &&
                       (positions != NULL) &&
                       (lambdas != NULL),
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        if (disp < 0.0) {
            if (strcmp(filter_id, "H") == 0) {
                disp = SPEC_RES_H;
                tolerance = 0.01;
            } else if (strcmp(filter_id, "K") == 0) {
                disp = SPEC_RES_K;
            } else if (strcmp(filter_id, "HK") == 0) {
                disp = SPEC_RES_HK;
            } else if (strcmp(filter_id, "YJ") == 0) {
                disp = SPEC_RES_YJ;
            } else if (strcmp(filter_id, "IZ") == 0) {
                disp = SPEC_RES_IZ;
            } else {
                KMO_TRY_ASSURE(1==0,
                               CPL_ERROR_ILLEGAL_INPUT,
                               "Filter ID is wrong (%s)!", filter_id);
            }
        }

        min_disp = disp - disp * 0.5;  // add/subtract 50%
        max_disp = disp + disp * 0.5;

        // detect peaks in initial trace
        //----------------------------------------------

        // signal will be low-pass filtered, values below stdev/3 are ignored
        KMO_TRY_EXIT_IF_NULL(
            trace_low = cpl_vector_filter_lowpass_create(trace,
                                                      CPL_LOWPASS_GAUSSIAN, 27));
        if (data_fits_name != NULL) {
            kmo_wave_write_data_vector(data_fits_name, trace_low, "low_pass_filtered_trace");
        }
        KMO_TRY_EXIT_IF_NULL(
            ptrace_low = cpl_vector_get_data(trace_low));

        thresh = cpl_vector_get_stdev(trace_low)/3;

        KMO_TRY_EXIT_IF_NULL(
            tmp_vec = cpl_vector_new(max_lines));
        KMO_TRY_EXIT_IF_NULL(
            ptmp_vec = cpl_vector_get_data(tmp_vec));
        cpl_vector_fill(tmp_vec, -1);

        // this for-loop recognises only peaks when values
        // between peaks go below thresh

        // correction of wave shift error of 1 pix
        //for (i = 1; i < cpl_vector_get_size(trace); i++) {
        for (i = 0; i < cpl_vector_get_size(trace); i++) {
            if ((ptrace_low[i] > thresh) &&
                (ptrace_low[i] > ptrace_low[i-1]) &&
                (ptrace_low[i] > ptrace_low[i+1])) {
                ptmp_vec[k] = i;
                k++;
                if (k >= max_lines) {
                    cpl_msg_error(cpl_func, "Lines read in exceed 200.");
                    KMO_TRY_SET_ERROR(CPL_ERROR_ILLEGAL_INPUT);
                    KMO_TRY_CHECK_ERROR_STATE();
                    break;
                }
            }
        }

        // get rid of trailing -1
        tmp_max_pos = 0;
        for (i = 0; i < max_lines; i++) {
            if (ptmp_vec[i] != -1) {
                tmp_max_pos = i;
            } else {
                break;
            }
        }

        KMO_TRY_EXIT_IF_NULL(
            peak_pos = cpl_vector_extract(tmp_vec, 0, tmp_max_pos, 1));

        if (data_fits_name != NULL) {
            kmo_wave_write_data_vector(data_fits_name, peak_pos, "peak_pos");
        }

        // get center of gauss at determined positions
        //----------------------------------------------------
        dx = cpl_vector_new(9);
        dy = cpl_vector_new(9);

        KMO_TRY_EXIT_IF_NULL(
            pdx=cpl_vector_get_data(dx));
        KMO_TRY_EXIT_IF_NULL(
            pdy=cpl_vector_get_data(dy));
        KMO_TRY_EXIT_IF_NULL(
            ppeak_pos = cpl_vector_get_data(peak_pos));
        KMO_TRY_EXIT_IF_NULL(
            ptrace = cpl_vector_get_data_const(trace));

        for (i = 0; i < cpl_vector_get_size(peak_pos); i++) {
            for (j = 0; j < 9; j++) {
                pdx[j] = ppeak_pos[i]-4+j;
                pdy[j] = ptrace[(int)pdx[j]];
            }

            KMO_TRY_EXIT_IF_ERROR(
                kmo_easy_gaussfit(dx, dy, &x0, &sigma, &area, &offset));

            ppeak_pos[i] = x0;
        }

        if (data_fits_name != NULL) {
            kmo_wave_write_data_vector(data_fits_name, peak_pos, "peak_pos_gauss_corrected");
        }

        cpl_vector_delete(tmp_vec); tmp_vec = NULL;
        cpl_vector_delete(dx); dx = NULL;
        cpl_vector_delete(dy); dy = NULL;

        // pattern matching
        //----------------------------------------------------
        // somehow the macro doesn't work with cpl_ppm_match_positions() ?!?
        // KMO_TRY_EXIT_IF_NULL(
        matched_positions = cpl_ppm_match_positions(peak_pos, lines,
                                                    min_disp, max_disp,
                                                    tolerance, NULL, NULL);

        if (matched_positions == NULL) {
            cpl_msg_warning(cpl_func,"Pattern matching failed at detector: %d,"
                           "IFU: %d, slitlet: %d!!",
                            dbg_detector_nr, dbg_ifu_nr, dbg_slitlet_nr);
            KMO_TRY_SET_ERROR(CPL_ERROR_UNSPECIFIED);
            return cpl_error_get_code();
        }

        KMO_TRY_EXIT_IF_NULL(
            *positions = cpl_bivector_get_x(matched_positions));
        KMO_TRY_EXIT_IF_NULL(
            *lambdas = cpl_bivector_get_y(matched_positions));

        cpl_bivector_unwrap_vectors(matched_positions);
        matched_positions = NULL;

        // use code below for line identification
//        if (1==0) {
        if (dbg_ifu_nr==4)
        if (dbgplot) {
            debug_fitted_lines(trace_low, peak_pos, lines, *positions,
                               *lambdas, dbg_detector_nr, dbg_ifu_nr,
                               dbg_slitlet_nr);
            KMO_TRY_CHECK_ERROR_STATE();
        }
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        ret_error = cpl_error_get_code();

        cpl_bivector_delete(matched_positions); matched_positions = NULL;
        cpl_vector_delete(*positions); *positions= NULL;
        cpl_vector_delete(*lambdas); *lambdas= NULL;
        cpl_bivector_delete(matched_positions); matched_positions = NULL;
    }

    cpl_vector_delete(tmp_vec); tmp_vec = NULL;
    cpl_vector_delete(trace_low); trace_low = NULL;
    cpl_vector_delete(peak_pos); peak_pos = NULL;
    cpl_vector_delete(dx); dx = NULL;
    cpl_vector_delete(dy); dy = NULL;

    return ret_error;
}

/**
    @brief
        Extracts the applicable lines from the line list table.

    @param arclines        Table with the defined arclines. The table must
                           consist of three columns of data: The 1st column
                           contains the wavelengths of the arclines, the 2nd
                           column contains the intensities of the arclines and
                           the 3rd contains a string, either 'Ar' or 'Ne'
                           designating the lamp the line belongs to. (Currently
                           the intensities aren't used)
    @param lamp_config     The Lamp configuration: Either ARGON, NEON or
                           ARGON_NEON.

    @return
        The vector with the applicable lines.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT    if any of the inputs is zero or any of the
                                outputs isn't allocated.
*/
cpl_bivector*  kmo_get_lines(const cpl_table *arclines,
                           enum lampConfiguration lamp_config)
{
    double      *plines                 = NULL;
    double      *pstrength              = NULL;
    cpl_bivector  *lines                = NULL;
    const float *pwave_line             = NULL;
    const float *pstrength_line         = NULL;
    const char  **pgas_line             = NULL;
    int         size                    = 0,
                i                       = 0;

    KMO_TRY
    {
        KMO_TRY_ASSURE(arclines != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        // get column "gas"
        KMO_TRY_EXIT_IF_NULL(
            pgas_line = cpl_table_get_data_string_const(arclines, GAS_COL));

        // count nr of lines matching "lamp_config"
        switch (lamp_config) {
        case ARGON:
            for (i = 0; i < cpl_table_get_nrow(arclines); i++) {
                if (strcmp(pgas_line[i], AR_LINE) == 0) {
                    size++;
                }
            }
            break;
        case NEON:
            for (i = 0; i < cpl_table_get_nrow(arclines); i++) {
                if (strcmp(pgas_line[i], NE_LINE) == 0) {
                    size++;
                }
            }
            break;
        case ARGON_NEON:
            size = cpl_table_get_nrow(arclines);
            break;
        default:
            KMO_TRY_ASSURE(1==0,
                           CPL_ERROR_ILLEGAL_INPUT,
                           "Unknown lamp configuration! (Ar, Ne or Ar+Ne expected)");
        }

        KMO_TRY_ASSURE(size > 0,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "No lines have been found in the list for the detected "
                       "arc lamp configuration! Did you provide the correct arc"
                       " line list?");

        // create & fill vector
        KMO_TRY_EXIT_IF_NULL(
             lines = cpl_bivector_new(size));
        KMO_TRY_EXIT_IF_NULL(
             plines = cpl_bivector_get_x_data(lines));
        KMO_TRY_EXIT_IF_NULL(
             pstrength = cpl_bivector_get_y_data(lines));
       KMO_TRY_EXIT_IF_NULL(
             pwave_line = cpl_table_get_data_float_const(arclines, WAVE_COL));
        KMO_TRY_EXIT_IF_NULL(
             pstrength_line = cpl_table_get_data_float_const(arclines, STRE_COL));
        size = 0;
        switch (lamp_config) {
        case ARGON:
            for (i = 0; i < cpl_table_get_nrow(arclines); i++) {
                if (strcmp(pgas_line[i], AR_LINE) == 0) {
                    plines[size] = pwave_line[i];
                    pstrength[size] = pstrength_line[i];
                    size++;
               }
            }
            break;
        case NEON:
            for (i = 0; i < cpl_table_get_nrow(arclines); i++) {
                if (strcmp(pgas_line[i], NE_LINE) == 0) {
                    plines[size] = pwave_line[i];
                    pstrength[size] = pstrength_line[i];
                    size++;
                }
            }
            break;
        case ARGON_NEON:
            for (i = 0; i < cpl_table_get_nrow(arclines); i++) {
                plines[i] = pwave_line[i];
                pstrength[i] = pstrength_line[i];
         }
            break;
        default:
            KMO_TRY_ASSURE(1==0,
                           CPL_ERROR_ILLEGAL_INPUT,
                           "Unknown lamp configuration! (Ar, Ne or Ar+Ne expected)");
        }
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        cpl_bivector_delete(lines); lines = NULL;
    }

    return lines;
}

/**
    @brief
        Calculates the exact positions of arclines based on estimates.

    @param data       The image data.
    @param bad_pix    The bad pixel mask.
    @param positions  Estimated positions of the lines.
    @param lambdas    Wavelengths of the lines.
    @param left_edge  The left edge of a slitlet along he y axis of the
                      detector frame.
    @param right_edge The right edge of a slitlet along he y axis of the
                      detector frame.
    @param xarray     (Output) Vector containing absolute indices to the
                      detector frame
    @param yarray     (Output) Array containing the exact positions for all
                      matched lines (in @c kmo_estimate_lines ) at each of the
                      x-positions defined in @c xarray
    @param larray     (Output) Vector containing the corresponding wavelengths
    @param qc_vec     (Output) Vector containing qc parameters calculated for
                      the processed slitlet:
                      qc_vec[0] = argon fwhm
                      qc_vec[1] = argon flux
                      qc_vec[2] = neon fwhm
                      qc_vec[3] = neon flux
    @param dbg_detector_nr (Needed fort debugging only)
    @param dbg_ifu_nr      (Needed fort debugging only)
    @param dbg_slitlet_nr  (Needed fort debugging only)

    @return
        The function returns CPL_ERROR_NONE on success or a CPL error code
        otherwise.

    The exact position of the arclines is calculated based on estimated
    positions.
    - First a gauss is fitted to every arcline at each x-position to get the
      effective position in the visible part of the slitlet. At this stage
      already some QC parameters are collected regarding the quality of the
      fits (sigma & flux) at the reference argon and neon line.
    Since the slitlet can be tilted and we want to fit a polynomial in
    wavelength direction later on, we would have only few data points at one
    edge at the bottom and the top. For this we extrapolate the exact arcline
    positions in x-direction to get a rectangular grid.
    - This is done using rejection on the exact data points, fitting a
      polynomial in x-direction, subtracting the fitted values from the
      original ones, rejecting again, fitting again a polynomial, subtracting
      again. If the standard deviation of this operation is small enough, we
      apply the fitted polynomial to our rectangular grid otherwise the values
      are set to -1. At this stage again some QC parameters are calculated
      regarding the FWHM and flux of our reference argon and neon lines.

    (Used in kmo_calc_wave_calib())

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT    if any of the inputs is zero or any of the
                                outputs isn't allocated.
    @li CPL_ERROR_ILLEGAL_INPUT if @c positions and @c lambdas haven't the same
                                size or if @c left_edge is greater than
                                @c right_edge
*/
cpl_error_code  kmo_extrapolate_arclines(const cpl_image *data,
                                         const cpl_image *bad_pix,
                                         const cpl_vector *positions,
                                         const cpl_vector *lambdas,
                                         const cpl_vector *left_edge,
                                         const cpl_vector *right_edge,
                                         cpl_vector **xarray,
                                         cpl_image **yarray,
                                         kmclipm_vector **larray,
                                         cpl_vector **qc_vec,
                                         int dbg_detector_nr,
                                         int dbg_ifu_nr,
                                         int dbg_slitlet_nr,
                                         const char *filter_id,
                                         enum lampConfiguration lamp_config)
{
    cpl_error_code      ret_error           = CPL_ERROR_NONE;
    cpl_vector          *good               = NULL,
                        *fit_par            = NULL,
                        *tmp_yarr2          = NULL,
                        *tmp_xarr2          = NULL;
    kmclipm_vector      *yline              = NULL,
                        *sline              = NULL,
                        *fline              = NULL,
                        *ne_sline           = NULL,
                        *ne_fline           = NULL,
                        *ar_sline           = NULL,
                        *ar_fline           = NULL,
                        *tmp_xarr           = NULL,
                        *tmp_yarr           = NULL,
                        *yfit               = NULL;
    const double        *ppositions         = NULL,
                        *plambdas           = NULL,
                        *pleft_edge         = NULL,
                        *pright_edge        = NULL;
    double              *pxarray            = NULL,
                        *pfit_par           = NULL,
                        stddev              = 0.0,
                        mean                = 0.0,
                        fwhm                = 0.0,
                        ref_wavelength_ar   = 0.0,
                        ref_wavelength_ne   = 0.0,
                        tmp_dbl             = 0.0;
    int                 nx_yarray           = 0,
                        ny_yarray           = 0,
                        tmp_int             = 0,
                        i                   = 0,
                        ii                  = 0,
                        j                   = 0,
                        k                   = 0,
                        i_arc               = 0,
                        poly_degree         = 1;//2;
    float               *pyarray            = NULL;

    KMO_TRY
    {
        KMO_TRY_ASSURE((data != NULL) &&
                       (bad_pix != NULL) &&
                       (positions != NULL) &&
                       (lambdas != NULL) &&
                       (xarray != NULL) &&
                       (yarray != NULL) &&
                       (larray != NULL) &&
                       (qc_vec != NULL),
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        KMO_TRY_ASSURE((*xarray != NULL) &&
                       (*yarray != NULL) &&
                       (*larray != NULL) &&
                       (*qc_vec != NULL),
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is allocated!");

        KMO_TRY_ASSURE(cpl_vector_get_size(positions) ==
                                                 cpl_vector_get_size(lambdas),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "vectors of positions and lambdas don't have same size!");

        // get band dependen reference arc lines
        if ((lamp_config == ARGON) ||
            (lamp_config == ARGON_NEON))
        {
            if (strcmp(filter_id, "H") == 0) {
                ref_wavelength_ar = REF_LINE_AR_H;
            } else if (strcmp(filter_id, "K") == 0) {
                ref_wavelength_ar = REF_LINE_AR_K;
            } else if (strcmp(filter_id, "YJ") == 0) {
                ref_wavelength_ar = REF_LINE_AR_YJ;
            } else if (strcmp(filter_id, "IZ") == 0) {
                ref_wavelength_ar = REF_LINE_AR_IZ;
            } else if (strcmp(filter_id, "HK") == 0) {
                ref_wavelength_ar = REF_LINE_AR_HK;
            }
        }

        if ((lamp_config == NEON) ||
            (lamp_config == ARGON_NEON))
        {
            if (strcmp(filter_id, "H") == 0) {
                ref_wavelength_ne = REF_LINE_NE_H;
            } else if (strcmp(filter_id, "K") == 0) {
                ref_wavelength_ne = REF_LINE_NE_K;
            } else if (strcmp(filter_id, "YJ") == 0) {
                ref_wavelength_ne = REF_LINE_NE_YJ;
            } else if (strcmp(filter_id, "IZ") == 0) {
                ref_wavelength_ne = REF_LINE_NE_IZ;
            } else if (strcmp(filter_id, "HK") == 0) {
                ref_wavelength_ne = REF_LINE_NE_HK;
            }
        }

        KMO_TRY_EXIT_IF_NULL(
            ppositions = cpl_vector_get_data_const(positions));
        KMO_TRY_EXIT_IF_NULL(
            plambdas = cpl_vector_get_data_const(lambdas));
        KMO_TRY_EXIT_IF_NULL(
            pleft_edge = cpl_vector_get_data_const(left_edge));
        KMO_TRY_EXIT_IF_NULL(
            pright_edge = cpl_vector_get_data_const(right_edge));
        KMO_TRY_EXIT_IF_NULL(
            pxarray = cpl_vector_get_data(*xarray));
        KMO_TRY_EXIT_IF_NULL(
            pyarray = cpl_image_get_data(*yarray));

        nx_yarray = cpl_image_get_size_x(*yarray);
        ny_yarray = cpl_image_get_size_y(*yarray);

        // fill arrays
        tmp_int = cpl_vector_get_min(left_edge);
        for (j = 0; j < nx_yarray; j++) {
            pxarray[j] = tmp_int + j;
        }
        KMO_TRY_EXIT_IF_ERROR(
            kmo_image_fill(*yarray, -1.0));
        KMO_TRY_EXIT_IF_ERROR(
            cpl_vector_fill(*qc_vec, -1.0));

        // each arcline is traced across the slitlet:
        // fit a vertical gauss curve for each arcline at each x-pos of the
        // slitlet to get the exact y-pos of the line
        // --> fills xarray and yarray
        for (i_arc = 0; i_arc < ny_yarray; i_arc++) {
            KMO_TRY_EXIT_IF_NULL(
                yline = kmclipm_vector_new(nx_yarray));
            KMO_TRY_EXIT_IF_NULL(
                sline = kmclipm_vector_new(nx_yarray));
            KMO_TRY_EXIT_IF_NULL(
                fline = kmclipm_vector_new(nx_yarray));

            tmp_int = (int)(ppositions[i_arc] + 0.5);

            KMO_TRY_EXIT_IF_ERROR(
                kmo_fit_arcline((int)(pleft_edge[tmp_int] + 0.5),
                                (int)(pright_edge[tmp_int] + 0.5),
                                tmp_int,
                                cpl_vector_get_min(left_edge),
                                data, bad_pix,
                                &yline, &sline, &fline));

            // paste yline into yarray
            for (i = 0; i < nx_yarray; i++) {
                if (kmclipm_vector_is_rejected(yline, i)) {
                    cpl_image_reject(*yarray, i+1, i_arc+1) ;
                } else {
                    cpl_image_set(*yarray, i+1, i_arc+1, kmclipm_vector_get(yline, i, NULL));
                }
            }

            // save temporarily sline & fline if we are at either the neon or
            // argon reference line to monitor the corresponding lamp efficiency
            if (fabs(plambdas[i_arc]-ref_wavelength_ar) <  0.001) {
                ar_sline = kmclipm_vector_duplicate(sline);
                ar_fline = kmclipm_vector_duplicate(fline);
            }
            if (fabs(plambdas[i_arc]-ref_wavelength_ne) <  0.001) {
                ne_sline = kmclipm_vector_duplicate(sline);
                ne_fline = kmclipm_vector_duplicate(fline);
            }

            kmclipm_vector_delete(yline); yline = NULL;
            kmclipm_vector_delete(sline); sline = NULL;
            kmclipm_vector_delete(fline); fline = NULL;
        } // for i_arc
        KMO_TRY_CHECK_ERROR_STATE();

        // extrapolate yarray to the boundaries given by xarray:
        // - take a line, crop it to its effective length, reject deviant
        //   values, fit a polynomial to it
        // - subtract the fitted values from the original values, reject and fit
        //   again
        // - if the fit is good, then extrapolate the arcline to the whole width
        //   of yarray and calculate qc -parameters
        for (i_arc = 0; i_arc < ny_yarray; i_arc++) {
            // copy a line from yarray to a vector & get its effective start
            // and end points
            KMO_TRY_EXIT_IF_NULL(
                tmp_yarr = kmclipm_vector_new(nx_yarray));

            if (tmp_yarr == NULL) {
                goto ooops; // sets this arcline invalid
            }

            for (i = 0; i < nx_yarray; i++) {
                if (cpl_image_is_rejected(*yarray, i+1, i_arc+1)) {
                    kmclipm_vector_reject(tmp_yarr, i) ;
                } else {
                    kmclipm_vector_set(tmp_yarr, i, pyarray[i+i_arc*nx_yarray]);
                }
            }
            KMO_TRY_CHECK_ERROR_STATE();

            // reject deviant values
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_reject_deviant(tmp_yarr, 3, 3, &stddev, &mean));
            KMO_TRY_EXIT_IF_NULL(
                tmp_yarr2 = kmclipm_vector_create_non_rejected(tmp_yarr));

            // crop same range of xarray, reject same values as in tmp_yarr
            KMO_TRY_EXIT_IF_NULL(
                tmp_xarr = kmclipm_vector_create(cpl_vector_duplicate(*xarray)));
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_vector_reject_from_mask(tmp_xarr,
                                                kmclipm_vector_get_bpm(tmp_yarr),
                                                TRUE));
            KMO_TRY_EXIT_IF_NULL(
                tmp_xarr2 = kmclipm_vector_create_non_rejected(tmp_xarr));

            KMO_TRY_CHECK_ERROR_STATE();
            KMO_TRY_ASSURE((kmclipm_vector_get_min(tmp_xarr, NULL) >= KMOS_BADPIX_BORDER) &&
                           (kmclipm_vector_get_max(tmp_xarr, NULL) < KMOS_DETECTOR_SIZE-KMOS_BADPIX_BORDER+1),
                           CPL_ERROR_NULL_INPUT,
                           "xarray: indices outside frame! (min: %g, max: %g)",
                           kmclipm_vector_get_min(tmp_xarr, NULL),
                           kmclipm_vector_get_max(tmp_xarr, NULL));

            // fit a polynomial to the line of yarray
            KMO_TRY_EXIT_IF_NULL(
                fit_par = kmo_polyfit_1d(tmp_xarr2, tmp_yarr2, poly_degree));

            KMO_TRY_EXIT_IF_NULL(
                pfit_par = cpl_vector_get_data(fit_par));

            cpl_vector_delete(tmp_xarr2); tmp_xarr2 = NULL;
            cpl_vector_delete(tmp_yarr2); tmp_yarr2 = NULL;

            // calculate the fitted polynomial
            KMO_TRY_EXIT_IF_NULL(
                yfit = kmclipm_vector_new(kmclipm_vector_get_size(tmp_yarr)));
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_vector_reject_from_mask(yfit,
                                                kmclipm_vector_get_bpm(tmp_yarr),
                                                TRUE));

            for (i = 0; i < kmclipm_vector_get_size(tmp_yarr); i++) {
                if (!kmclipm_vector_is_rejected(yfit, i)) {
                    tmp_dbl = 0.0;
                    for(k = 0; k < cpl_vector_get_size(fit_par); k++) {
                        tmp_dbl += pfit_par[k] * pow(pxarray[i], k);
                    }

                    KMO_TRY_EXIT_IF_ERROR(
                        kmclipm_vector_set(yfit, i, tmp_dbl));
                }
            }
            cpl_vector_delete(fit_par); fit_par = NULL;

            // subtract fitted polynomial from original values
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_vector_subtract(yfit, tmp_yarr));
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_vector_multiply_scalar(yfit, -1.0));

            // reject again
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_reject_deviant(yfit, 3, 3, &stddev, &mean));

            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_vector_reject_from_mask(tmp_yarr,
                                                kmclipm_vector_get_bpm(yfit),
                                                TRUE));
            KMO_TRY_EXIT_IF_NULL(
                tmp_yarr2 = kmclipm_vector_create_non_rejected(tmp_yarr));

            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_vector_reject_from_mask(tmp_xarr,
                                                kmclipm_vector_get_bpm(yfit),
                                                TRUE));
            KMO_TRY_EXIT_IF_NULL(
                tmp_xarr2 = kmclipm_vector_create_non_rejected(tmp_xarr));

            // fit again a polynomial
cpl_vector *dbg_ggg = cpl_vector_duplicate(tmp_yarr2);
            KMO_TRY_EXIT_IF_NULL(
                fit_par = kmo_polyfit_1d(tmp_xarr2, tmp_yarr2, poly_degree));

            KMO_TRY_EXIT_IF_NULL(
                pfit_par = cpl_vector_get_data(fit_par));

            cpl_vector_delete(tmp_xarr2); tmp_xarr2 = NULL;

            // calculate again the fitted polynomial
            for (i = 0; i < kmclipm_vector_get_size(tmp_yarr); i++) {
                if (!kmclipm_vector_is_rejected(yfit, i)) {
                    tmp_dbl = 0.0;
                    for(k = 0; k < cpl_vector_get_size(fit_par); k++) {
                        tmp_dbl += pfit_par[k] * pow(pxarray[i], k);
                    }
                    KMO_TRY_EXIT_IF_ERROR(
                        kmclipm_vector_set(yfit, i, tmp_dbl));
                }
            }

            kmclipm_vector_subtract(tmp_yarr, yfit);

            stddev = kmclipm_vector_get_stdev(tmp_yarr);
            KMO_TRY_CHECK_ERROR_STATE();

            //if (stddev < 0.1) {  // was too low for real lab data on edges of detector
            if (stddev < 0.5) {
//cpl_msg_debug(">>>","OK     Det: %d, IFU: %d, slitlet: %d, arcline: %d (stdev: %g)",
//              dbg_detector_nr, dbg_ifu_nr, dbg_slitlet_nr, i_arc+1, stddev);
                // now calculate fitted values over whole, tilted slitlet width
                // overwrite yarray with fitted yfit-values
                // --> write yarray with extrapolated (in x-direction),fitted values
                for (i = 0; i < nx_yarray; i++) {
                    pyarray[i+i_arc*nx_yarray] = 0.0;
                    for(k = 0; k < cpl_vector_get_size(fit_par); k++) {
                        pyarray[i+i_arc*nx_yarray] += pfit_par[k] * pow(pxarray[i], k);
                    }
                }

                // write larray (according lambda-value)
                kmclipm_vector_set(*larray, i_arc, plambdas[i_arc]);

                // calc QC-parameters with good-array from rejection and
                // ne_sline, ne_fline, ar_sline & ar_fline
                if (fabs(plambdas[i_arc]-ref_wavelength_ar) <  0.001)
                {
                    // calc fwhm of reference argon line
                    KMO_TRY_EXIT_IF_ERROR(
                        kmclipm_vector_reject_from_mask(ar_sline,
                                                        kmclipm_vector_get_bpm(yfit),
                                                        TRUE));

                    fwhm = CPL_MATH_FWHM_SIG * kmclipm_vector_get_mean(ar_sline);

                    KMO_TRY_EXIT_IF_ERROR(
                        cpl_vector_set(*qc_vec, 0, fwhm));

                    // calc flux of reference argon line
                    KMO_TRY_EXIT_IF_ERROR(
                        kmclipm_vector_reject_from_mask(ar_fline,
                                                        kmclipm_vector_get_bpm(yfit),
                                                        TRUE));

                    KMO_TRY_EXIT_IF_ERROR(
                        cpl_vector_set(*qc_vec, 1,
                                       kmclipm_vector_get_mean(ar_fline) * fwhm));

                    kmclipm_vector_delete(ar_sline); ar_sline = NULL;
                    kmclipm_vector_delete(ar_fline); ar_fline = NULL;
                }

                if (fabs(plambdas[i_arc]-ref_wavelength_ne) <  0.001)
                {
                    // calc fwhm of reference neon line
                    KMO_TRY_EXIT_IF_ERROR(
                        kmclipm_vector_reject_from_mask(ne_sline,
                                                        kmclipm_vector_get_bpm(yfit),
                                                        TRUE));

                    fwhm = CPL_MATH_FWHM_SIG * kmclipm_vector_get_mean(ne_sline);

                    KMO_TRY_EXIT_IF_ERROR(
                        cpl_vector_set(*qc_vec, 2, fwhm));

                    // calc flux of reference neon line
                    KMO_TRY_EXIT_IF_ERROR(
                        kmclipm_vector_reject_from_mask(ne_fline,
                                                        kmclipm_vector_get_bpm(yfit),
                                                        TRUE));

                    KMO_TRY_EXIT_IF_ERROR(
                        cpl_vector_set(*qc_vec, 3,
                                       kmclipm_vector_get_mean(ne_fline) * fwhm));

                    kmclipm_vector_delete(ne_sline); ne_sline = NULL;
                    kmclipm_vector_delete(ne_fline); ne_fline = NULL;
                }
            } else {
ooops:
if (dbgplot) {
cpl_msg_debug(">>>","FAILED Det: %d, IFU: %d, slitlet: %d, arcline: %d (stdev: %g)",
              dbg_detector_nr, dbg_ifu_nr, dbg_slitlet_nr, i_arc+1, stddev);
}
                // bad fit, set yarray and larray to -1 for this line
                for (i = 0; i < nx_yarray; i++) {
                    pyarray[i+i_arc*nx_yarray] = -1.0;
                    cpl_image_reject(*yarray, i+1, i_arc+1);
                }
                kmclipm_vector_set(*larray, i_arc, -1.0);
                kmclipm_vector_reject(*larray, i_arc);

//                if (dbgplot)
                if (1==0)
                {
                // debug output: when real & fitted data don't fit well enough
                cpl_bivector    *plots[2];
                cpl_vector *xx = cpl_vector_duplicate(tmp_xarr2);
                for (ii = 0; ii < cpl_vector_get_size(xx); ii++) cpl_vector_set(xx, ii, ii);
                KMO_TRY_EXIT_IF_NULL(
                    plots[0] = cpl_bivector_wrap_vectors(xx, tmp_xarr2));
                KMO_TRY_EXIT_IF_NULL(
                    plots[1] = cpl_bivector_wrap_vectors(xx, dbg_ggg));
                char *options[2] = {
                        "w l t 'yfit'",
                        "w l lc rgbcolor \"blue\" t 'data'"};
                char            title[1024], nr[256];
                strcpy(title, "set term x11; "
                              "set title 'Det: ");
                sprintf(nr, "%d", dbg_detector_nr);
                strcat(title, nr);
                strcat(title, ", IFU: ");
                sprintf(nr, "%d", dbg_ifu_nr);
                strcat(title, nr);
                strcat(title, ", slitlet: ");
                sprintf(nr, "%d", dbg_slitlet_nr);
                strcat(title, nr);
                strcat(title, ", arcline: ");
                sprintf(nr, "%d", i_arc+1);
                strcat(title, nr);
                strcat(title, ", stddev(data-fit): ");
                sprintf(nr, "%g", stddev);
                strcat(title, nr);
                strcat(title, "';");
                KMO_TRY_EXIT_IF_ERROR(
                    cpl_plot_bivectors(title,
                                   (const char**)options,
                                   "",
                                   (const cpl_bivector**)plots,
                                   2));
cpl_vector_delete(dbg_ggg); dbg_ggg=NULL;
                cpl_vector_delete(xx); xx=NULL;
                }
                KMO_TRY_CHECK_ERROR_STATE();
            }

            cpl_vector_delete(fit_par); fit_par = NULL;
            kmclipm_vector_delete(tmp_xarr); tmp_xarr = NULL;
            kmclipm_vector_delete(tmp_yarr); tmp_yarr = NULL;
            cpl_vector_delete(tmp_xarr2); tmp_xarr2 = NULL;
            cpl_vector_delete(tmp_yarr2); tmp_yarr2 = NULL;
            cpl_vector_delete(good); good = NULL;
            cpl_vector_delete(dbg_ggg); dbg_ggg = NULL;
            kmclipm_vector_delete(yfit); yfit = NULL;
        } // end for: i_arc < ny_yarray
        KMO_TRY_CHECK_ERROR_STATE();
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        ret_error = cpl_error_get_code();

        cpl_vector_fill(*xarray, -1.0);
        kmo_image_fill(*yarray, -1.0);
        kmclipm_vector_fill(*larray, -1.0);
        cpl_vector_fill(*qc_vec, -1.0);
    }

    kmclipm_vector_delete(ne_sline); ne_sline = NULL;
    kmclipm_vector_delete(ne_fline); ne_fline = NULL;
    kmclipm_vector_delete(ar_sline); ar_sline = NULL;
    kmclipm_vector_delete(ar_fline); ar_fline = NULL;

    return ret_error;
}

/**
    @brief
        Fits a gauss to each valid pixel of an arcline, starting from the middle
        of the slitlet.

    @param left_edge  The left edge of the slitlet at @c ypos .
    @param right_edge The right edge of the slitlet at @c ypos .
    @param ypos       y-position of the arcline we are examining.
    @param min_left_edge The left-most poition of the overall left edge. Needed
                      to get formatting of ylines correctly.
    @param data       The image data.
    @param badpix     The bad pixel mask.
    @param yline      (Output) The fitted y-positions of the valid pixels
                      according to the bad pixel mask.
    @param sline      (Output) The sigmas of the fitted values.
    @param fline      (Output) The fluxes of the fitted values.

    @return
        The function returns CPL_ERROR_NONE on success or a CPL error code
        otherwise.

    First all output vectors are filled with -1. A -1 in the final result will
    denote an invalid pixel.
    Then we move from the left edge to the right edge and fit a gauss
    function to every x-position, as long as there are enough good pixels.
    The gauss is fitted in a range of +/- 6 pixels in y-direction.
    For each fit the calculated sigma and flux is recorded.
    The resulting output vectors will contain -1 at the left and/or right edges
    according to the rotation of the slitlet. These missing values will be
    extrapolated in @c kmo_extrapolate_arclines .

    (Used in kmo_extrapolate_arclines())

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT    if any of the inputs is zero or any of the
                                outputs isn't allocated.
*/
cpl_error_code  kmo_fit_arcline(const int left_edge,
                                const int right_edge,
                                const int ypos,
                                const int min_left_edge,
                                const cpl_image *data,
                                const cpl_image *badpix,
                                kmclipm_vector **yline,
                                kmclipm_vector **sline,
                                kmclipm_vector **fline)
{
    cpl_error_code  ret_error   = CPL_ERROR_NONE;

    cpl_vector      *yvec       = NULL,
                    *bvec       = NULL,
                    *fvec       = NULL,
                    *good       = NULL,
                    *yvec_good  = NULL,
                    *fvec_good  = NULL;

    double          *pyvec      = NULL,
                    *pbvec      = NULL,
                    *pfvec      = NULL,
                    x0          = 0.0,
                    sigma       = 0.0,
                    area        = 0.0,
                    offset      = 0.0;

    const float     *pdata      = NULL,
                    *pbadpix    = NULL;

//    int             len         = 6,
    int             len         = 4,
                    nx          = 0,
//                    doitonce    = FALSE,
                    tmp_int     = 0,
                    i = 0, ix = 0, iy = 0;

    KMO_TRY
    {
        KMO_TRY_ASSURE((data != NULL) &&
                       (badpix != NULL) &&
                       (yline != NULL) &&
                       (sline != NULL) &&
                       (fline != NULL),
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        KMO_TRY_ASSURE((*yline != NULL) &&
                       (*sline != NULL) &&
                       (*fline != NULL),
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is allocated!");

        KMO_TRY_EXIT_IF_ERROR(
            kmclipm_vector_fill(*yline, -1.0));
        KMO_TRY_EXIT_IF_ERROR(
            kmclipm_vector_fill(*sline, -1.0));
        KMO_TRY_EXIT_IF_ERROR(
            kmclipm_vector_fill(*fline, -1.0));

        // setup small vector in y-direction
        KMO_TRY_EXIT_IF_NULL(
            yvec = cpl_vector_new(2*len+1));
        KMO_TRY_EXIT_IF_NULL(
            pyvec = cpl_vector_get_data(yvec));

        for (i = 0; i < 2*len+1; i++) {
            pyvec[i] = -len + i + ypos;
        }

        // setup same vector with image-data
        KMO_TRY_EXIT_IF_NULL(
            fvec = cpl_vector_new(2*len+1));
        KMO_TRY_EXIT_IF_NULL(
            pfvec = cpl_vector_get_data(fvec));
        KMO_TRY_EXIT_IF_NULL(
            pdata = cpl_image_get_data_float_const(data));
        nx = cpl_image_get_size_x(data);

        // setup same vector with badpix-data
        KMO_TRY_EXIT_IF_NULL(
            bvec = cpl_vector_new(2*len+1));
        KMO_TRY_EXIT_IF_NULL(
            pbvec = cpl_vector_get_data(bvec));
        KMO_TRY_EXIT_IF_NULL(
            pbadpix = cpl_image_get_data_float_const(badpix));

        // move from the left of the slit to the right border
        for (ix = left_edge; ix <= right_edge; ix++) {
            for (iy = 0; iy < 2*len+1; iy++) {
                pfvec[iy] = pdata[ix+(int)pyvec[iy]*nx];
                pbvec[iy] = pbadpix[ix+(int)pyvec[iy]*nx];
            }

            good = kmo_idl_where(bvec, 0.5, gt);
            KMO_TRY_CHECK_ERROR_STATE();

//            // override: For science grade detectors the arcline can't be fitted
//            // because there are so many pixels flagged as bad. Anyway on the arc
//            // frame the lines can be seen visually perfectly, so fit anyway and
//            // give a warning
//            if ((good != NULL) & (cpl_vector_get_size(good) <= len + 1)) {
//                doitonce=TRUE;
//                cpl_msg_warning(cpl_func, "Too much bad pixels! "
//                                          "Is this a science grade detector?");
//            }

//            if (doitonce ||
//               ((good != NULL) && (cpl_vector_get_size(good) > len + 1)))
//            {
            if (good != NULL)
            {
                yvec_good = kmo_idl_values_at_indices(yvec, good);
                fvec_good = kmo_idl_values_at_indices(fvec, good);

                cpl_vector_delete(good); good = NULL;

                ret_error = kmo_easy_gaussfit(yvec_good, fvec_good,
                                              &x0, &sigma, &area, &offset);

                tmp_int = ix-min_left_edge;
                if (ret_error == CPL_ERROR_NONE) {
                    if ((tmp_int >= 0) && (tmp_int < kmclipm_vector_get_size(*yline))) {
                        kmclipm_vector_set(*yline, tmp_int,
                                           x0);
                        kmclipm_vector_set(*sline, tmp_int,
                                           sigma);
                        kmclipm_vector_set(*fline, tmp_int,
                                           area/(sqrt(CPL_MATH_2PI)*sigma)+offset);
                    }
                } else {
                    // gauss couldn't be calculated at this position,
                    // ihnore it (i.e. yline, sline, fline stay -1)
                    ret_error = CPL_ERROR_NONE;
                    cpl_error_reset();

                    kmclipm_vector_reject(*yline, tmp_int);
                    kmclipm_vector_reject(*sline, tmp_int);
                    kmclipm_vector_reject(*fline, tmp_int);
                }
                cpl_vector_delete(yvec_good); yvec_good = NULL;
                cpl_vector_delete(fvec_good); fvec_good = NULL;
            }
        } // for (ix = left_edge to right_edge)

        for (i = 0; i < kmclipm_vector_get_size(*yline); i++) {
            if (kmclipm_vector_get(*yline, i, NULL) == -1) {
                kmclipm_vector_reject(*yline, i);
                kmclipm_vector_reject(*sline, i);
                kmclipm_vector_reject(*fline, i);
            }
        }
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        ret_error = cpl_error_get_code();

        kmclipm_vector_delete(*yline); *yline= NULL;
        kmclipm_vector_delete(*sline); *sline= NULL;
        kmclipm_vector_delete(*fline); *fline= NULL;

    }

    cpl_vector_delete(fvec); fvec = NULL;
    cpl_vector_delete(bvec); bvec = NULL;
    cpl_vector_delete(yvec); yvec = NULL;

    return ret_error;
}

/**
    @brief
        Fits polynomials in wavelength direction to a whole slitlet.

    @param bad_pix      The bad pixel mask.
    @param xarray       Vector containing absolute indices to the detector
                        frame.
    @param yarray       Array containing the exact positions for all matched
                        lines at each of the x-positions defined in @c xarray
    @param larray       Vector containing the corresponding wavelengths.
    @param left_edge    The left edge of a slitlet along he y axis of the
                        detector frame.
    @param right_edge   The right edge of a slitlet along he y axis of the
                        detector frame.
    @param lcal         (Input/Output) Contains a pointer to the final
                        wavelength calibration frame. It will be updated slitlet
                        by slitlet.
    @param min_lambda   (Input/Output) Keeps track of the minimum wavelength in
                        the whole processed frame.
    @param max_lambda   (Input/Output) Keeps track of the maximum wavelength in
                        the whole processed frame.
    @param fit_order    Polynomial order of the wavelength solution.
    @param fit_order    Polynomial order of the wavelength solution.
    @param dbg_detector_nr (Needed fort debugging only)
    @param dbg_ifu_nr      (Needed fort debugging only)
    @param dbg_slitlet_nr  (Needed fort debugging only)

    @return
        The function returns TRUE if the slitlet has at least one valid pixel
        which could have been wavelength calibrated, FALSE otherwise.

    For each x-position between @c side1 and @c side2, a polynomial will be
    fitted in wavelength direction.
    For all valid pixels according to @c bad_pix, the resulting values will be
    written into @c lcal. (To be precise: the value is only written if it really
    belongs to the actual slitlet, since the rotation can be so strong, we could
    accidentally write values at positions belonging to a neighbouring slitlet.
    To avois this, we comare @c ifu_id with the values in @c xcal and @c ycal at
    currently processed positions.)
    The fitting procedure is as follows we fit the values, subtrat the fitted
    values from the original ones, reject deviant values and fit again.
    For all valid columns (at least one pixel has been written to @c lcal after
    checking @c bad_pix, @c xcal and @c ycal) we calculate some QC parameters
    (see description of @c qc_vec ).

    (Used in kmo_calc_wave_calib())

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT    if any of the inputs is zero or any of the
                                outputs isn't allocated.
    @li CPL_ERROR_ILLEGAL_INPUT if tghe dimensions of @c xarray, @c yarray and
                                @c larray don't match.
*/
int  kmo_fit_spectrum(const cpl_image *bad_pix,
                      const cpl_vector *xarray,
                      const cpl_image *yarray,
                      const cpl_vector *larray,
                      const cpl_vector *left_edge,
                      const cpl_vector *right_edge,
                      cpl_image **lcal,
                      double *min_lambda,
                      double *max_lambda,
//                      cpl_image *unroll_lcal,
//                      int unroll_lcal_cnt,
                      const int fit_order,
                      int dbg_detector_nr,
                      int dbg_ifu_nr,
                      int dbg_slitlet_nr)
{
    int             l               = 0,
                    lsize           = 0,
                    xsize           = 0,
                    nx              = 0,
                    ny              = 0,
                    valid_column    = FALSE,
                    valid_slitlet   = FALSE, // TRUE for at least one valid column
                    order_first_try = 0,
                    ix = 0, iy = 0, k = 0, j= 0;
    cpl_vector      *yvec           = NULL,
                    *lvec           = NULL,
                    *good           = NULL,
                    *yvec2          = NULL,
                    *lvec2          = NULL,
                    *good2          = NULL,
                    *tmp_vec        = NULL,
                    *fit_par        = NULL,
                    *fit_par2       = NULL,
                    *lfit           = NULL;

    double          *pyvec          = NULL,
                    *ptmp_vec       = NULL,
                    *pfit_par       = NULL,
                    *plfit          = NULL,
                    stddev          = 0.0,
                    mean            = 0.0,
                    tmp_dbl         = 0.0,
                    *pyvec2         = NULL,
                    *plvec2         = NULL;

    const double    *pxarray        = NULL,
                    *pleft_edge     = NULL,
                    *pright_edge    = NULL;

    const float     *pyarray        = NULL,
                    *pbad_pix       = NULL;

    float           *plcal          = NULL/*,
                    *punroll_lcal   = NULL*/;

    KMO_TRY
    {
        KMO_TRY_ASSURE((xarray != NULL) &&
                       (yarray != NULL) &&
                       (larray != NULL) &&
                       (lcal != NULL),
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        KMO_TRY_ASSURE(*lcal != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is allocated!");

        KMO_TRY_ASSURE((cpl_vector_get_size(xarray) ==
                                              cpl_image_get_size_x(yarray)) &&
                       (cpl_vector_get_size(larray) ==
                                              cpl_image_get_size_y(yarray)),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "Size of inputs doesn't match!");

        KMO_TRY_ASSURE((dbg_detector_nr > 0) &&
                       (dbg_ifu_nr > 0) &&
                       (dbg_slitlet_nr > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "dbg values must be > 0!");

        // get some dimensions
        xsize = cpl_vector_get_size(xarray);
        nx = cpl_image_get_size_x(*lcal);
        ny = cpl_image_get_size_y(*lcal);
        KMO_TRY_CHECK_ERROR_STATE();

        // get some pointers
        KMO_TRY_EXIT_IF_NULL(
            pbad_pix = cpl_image_get_data_const(bad_pix));
        KMO_TRY_EXIT_IF_NULL(
            plcal = cpl_image_get_data(*lcal));
        KMO_TRY_EXIT_IF_NULL(
            pxarray = cpl_vector_get_data_const(xarray));
        KMO_TRY_EXIT_IF_NULL(
            pyarray = cpl_image_get_data_const(yarray));
        KMO_TRY_EXIT_IF_NULL(
            pleft_edge = cpl_vector_get_data_const(left_edge));
        KMO_TRY_EXIT_IF_NULL(
            pright_edge = cpl_vector_get_data_const(right_edge));
//        KMO_TRY_EXIT_IF_NULL(
//            punroll_lcal = cpl_image_get_data(unroll_lcal));

        // extract valid arclines
        KMO_TRY_EXIT_IF_NULL(
            good = kmo_idl_where(larray, -1.0, ne));

        KMO_TRY_EXIT_IF_NULL(
            lvec = kmo_idl_values_at_indices(larray, good));

        lsize = cpl_vector_get_size(lvec);
        KMO_TRY_CHECK_ERROR_STATE();

        KMO_TRY_EXIT_IF_NULL(
            lfit = cpl_vector_new(lsize));
        KMO_TRY_EXIT_IF_NULL(
            plfit = cpl_vector_get_data(lfit));

        // set up tmp vector
        KMO_TRY_EXIT_IF_NULL(
            tmp_vec = cpl_vector_new(cpl_vector_get_size(larray)));
        KMO_TRY_EXIT_IF_NULL(
            ptmp_vec = cpl_vector_get_data(tmp_vec));

        // for each x-pos fit a polynomial to spectrum and fill fitted values
        // into lcal taking into account the slitlet_mask

        for (ix = 0; ix < xsize; ix++) {
            if ((pxarray[ix] >= KMOS_BADPIX_BORDER) &&
                (pxarray[ix] < KMOS_DETECTOR_SIZE-KMOS_BADPIX_BORDER))
            {
                // extract positions for valid arclines
                for (j = 0; j < cpl_vector_get_size(larray); j++) {
                    // correction of wave shift error of 1 pix
                    ptmp_vec[j] = pyarray[ix+j*xsize]+1;
                }

                KMO_TRY_EXIT_IF_NULL(
                    yvec = kmo_idl_values_at_indices(tmp_vec, good));
                KMO_TRY_EXIT_IF_NULL(
                    pyvec = cpl_vector_get_data(yvec));

                order_first_try = fit_order;
fit_again:
                // fit the polynomial (1st fit)
                fit_par = kmo_polyfit_1d(yvec, lvec, order_first_try);
                if (cpl_error_get_code() != CPL_ERROR_NONE) {
                    // fit failed
                    cpl_error_reset();
                    cpl_vector_delete(fit_par); fit_par = NULL;
                    if (order_first_try > 2) {
                        // try again with lower order (down to 2)
                        order_first_try--;
                        goto fit_again;
                    } else {
                        // fit failed definitely, proceed with next ix
                        valid_column = FALSE;
                    }
                } else {
                    // fit succeeded: calc polynomial, subtract it, reject
                    // and fit again
                    valid_column = TRUE;
                    KMO_TRY_EXIT_IF_NULL(
                        pfit_par = cpl_vector_get_data(fit_par));

                    // calculate the fitted polynomial
                    l = 0;
                    for (j = 0; j < lsize; j++) {
                        plfit[l] = 0.0;
                        for(k = 0; k < cpl_vector_get_size(fit_par); k++) {
                            plfit[l] += pfit_par[k] * pow(pyvec[j], k);
                        }
                        l++;
                    }
                    cpl_vector_delete(fit_par); fit_par = NULL;

//if ((dbg_detector_nr==1) && (dbg_ifu_nr==2) && (dbg_slitlet_nr==8)) {
//    cpl_vector_save(lfit, "lfit.fits", CPL_BPP_IEEE_FLOAT, NULL,CPL_IO_DEFAULT);
//    cpl_vector_save(lvec, "lvec.fits", CPL_BPP_IEEE_FLOAT, NULL,CPL_IO_DEFAULT);
//    cpl_vector_save(yvec, "yvec.fits", CPL_BPP_IEEE_FLOAT, NULL,CPL_IO_DEFAULT);

//    char tit[1024], nr[12];
//    const char *opt[2] = { "w p lc rgbcolor \"red\" t 'real'",
//                           "w p lc rgbcolor \"blue\" t 'fit'"};
//    strcpy(tit, "set title '1st fit (det: ");
//    sprintf(nr, "%d", dbg_detector_nr); strcat(tit, nr);
//    strcat(tit, ", ifu: ");
//    sprintf(nr, "%d", dbg_ifu_nr); strcat(tit, nr);
//    strcat(tit, ", slitlet: ");
//    sprintf(nr, "%d", dbg_slitlet_nr); strcat(tit, nr);
//    strcat(tit, ", #vals: ");
//    sprintf(nr, "%d", lsize); strcat(tit, nr);
//    strcat(tit, ")';");
//    kmo_plot_vectors2(tit, opt, yvec, lvec, lfit);
//}
                    // subtract fitted from original values
                    KMO_TRY_EXIT_IF_ERROR(
                        cpl_vector_subtract(lfit, lvec));

                    KMO_TRY_EXIT_IF_ERROR(
                        cpl_vector_multiply_scalar(lfit, -1.0));

//if ((dbg_detector_nr==1) && (dbg_ifu_nr==2) && (dbg_slitlet_nr==8)) {
//    kmo_plot_vector("", "", lfit);
//}
                    // reject deviant values
kmclipm_vector *ddd1 = kmclipm_vector_create(cpl_vector_duplicate(lfit));
                    KMO_TRY_EXIT_IF_ERROR(
                        kmclipm_reject_deviant(ddd1, 3, 3, &stddev, &mean));
good2 = kmclipm_vector_get_mask(ddd1);
kmclipm_vector_delete(ddd1); ddd1 = NULL;
//if ((dbg_detector_nr==1) && (dbg_ifu_nr==2) && (dbg_slitlet_nr==8)) {
//    kmo_debug_vector( good2);
//}
                    KMO_TRY_EXIT_IF_NULL(
                        yvec2 = kmo_idl_values_at_indices(yvec, good2));
                    KMO_TRY_EXIT_IF_NULL(
                        pyvec2 = cpl_vector_get_data(yvec2));

                    cpl_vector_delete(yvec); yvec = NULL;

                    KMO_TRY_EXIT_IF_NULL(
                        lvec2 = kmo_idl_values_at_indices(lvec, good2));
                    KMO_TRY_EXIT_IF_NULL(
                        plvec2 = cpl_vector_get_data(lvec2));
//if ((dbg_detector_nr==1) && (dbg_ifu_nr==2) && (dbg_slitlet_nr==8)) {
//    cpl_vector_save(lvec2, "lvec2.fits", CPL_BPP_IEEE_FLOAT, NULL,CPL_IO_DEFAULT);
//    cpl_vector_save(yvec2, "yvec2.fits", CPL_BPP_IEEE_FLOAT, NULL,CPL_IO_DEFAULT);
//}
                    // refit the polynomial (2nd fit)
                    fit_par2 = kmo_polyfit_1d(yvec2, lvec2, fit_order);

                    if (cpl_error_get_code() != CPL_ERROR_NONE) {
                        // 2nd fit failed: take first fit instead
                        valid_column = FALSE;
                        cpl_error_reset();
                        cpl_vector_delete(fit_par2); fit_par2 = NULL;
                    } else {
                        // 2nd fit succeeded: take this one!
                        valid_column = TRUE;
                        cpl_vector_delete(fit_par);
                        fit_par = fit_par2;
                    }

//if ((dbg_detector_nr==1) && (dbg_ifu_nr==2) && (dbg_slitlet_nr==8)) {
//    pfit_par = cpl_vector_get_data(fit_par);
//    if (pfit_par !=NULL) {
//        char tit[1024], nr[12];
//        // calculate the fitted polynomial
//        KMO_TRY_EXIT_IF_NULL(
//            pyvec2 = cpl_vector_get_data(yvec2));
//        cpl_vector *lfit2 = NULL;
//        double *plfit2 = NULL;
//        KMO_TRY_EXIT_IF_NULL(
//            lfit2 = cpl_vector_new(cpl_vector_get_size(lvec2)));
//        KMO_TRY_EXIT_IF_NULL(
//            plfit2 = cpl_vector_get_data(lfit2));
//        l = 0;
//        for (int j = 0; j < cpl_vector_get_size(lvec2); j++) {
//            plfit2[l] = 0.0;
//            for(int k = 0; k < cpl_vector_get_size(fit_par); k++) {
//                plfit2[l] += pfit_par[k] * pow(pyvec2[j], k);
//            }
//            l++;
//        }
//        cpl_vector_save(lfit2, "lfit2.fits", CPL_BPP_IEEE_FLOAT, NULL,CPL_IO_DEFAULT);
//        const char*opt2[2] = { "w p lc rgbcolor \"red\" t 'real'",
//                              "w p lc rgbcolor \"chartreuse\" t 'fit'"};
//        strcpy(tit, "set title '2nd fit (det: ");
//        sprintf(nr, "%d", dbg_detector_nr); strcat(tit, nr);
//        strcat(tit, ", ifu: ");
//        sprintf(nr, "%d", dbg_ifu_nr); strcat(tit, nr);
//        strcat(tit, ", slitlet: ");
//        sprintf(nr, "%d", dbg_slitlet_nr); strcat(tit, nr);
//        strcat(tit, ", #vals: ");
//        sprintf(nr, "%d", cpl_vector_get_size(lvec2)); strcat(tit, nr);
//        strcat(tit, ")';");
//        kmo_plot_vectors2(tit, opt2, yvec2, lvec2, lfit2);
//    } else {
//        cpl_error_reset();
//    }
//}
                }

                if (valid_column) {
                    // recalculate the fitted polynomial and write value only into lcal
                    // if it corresponds to the actual slitlet
                    KMO_TRY_EXIT_IF_NULL(
                        pfit_par = cpl_vector_get_data(fit_par));

                    valid_column = FALSE;
                    for (iy = KMOS_BADPIX_BORDER; iy < ny-KMOS_BADPIX_BORDER; iy++) {
                        if ((pxarray[ix] >= pleft_edge[iy-KMOS_BADPIX_BORDER]) &&
                            (pxarray[ix] <= pright_edge[iy-KMOS_BADPIX_BORDER]))
                        {
                            tmp_dbl = 0.0;
                            for(k = 0; k < cpl_vector_get_size(fit_par); k++) {
                                tmp_dbl += pfit_par[k] * pow(iy+1, k);
                            }

                            if ((iy == KMOS_BADPIX_BORDER) && (tmp_dbl < *min_lambda)) {
                                *min_lambda = tmp_dbl;
                            }
                            if ((iy == ny-KMOS_BADPIX_BORDER-1) && (tmp_dbl > *max_lambda)) {
                                *max_lambda = tmp_dbl;
                            }

                            if (pbad_pix[(int)(pxarray[ix])+iy*nx] >= 0.5) {
                                // good pix
                                valid_column = TRUE;
                                plcal[(int)(pxarray[ix])+iy*nx] = tmp_dbl;
//                                punroll_lcal[unroll_lcal_cnt] = (int)(pxarray[ix]);
//                                punroll_lcal[unroll_lcal_cnt+1] = iy;
//                                unroll_lcal_cnt +=2;
                            } else {
                                // bad pix
                                // is already set to 0
                            }
                        }
                    }  // for iy=0:ny-1

                    if (valid_column) {
                        // at least one pixel in this column of this slitlet
                        // was valid, so set this slitlet as valid
                        valid_slitlet = TRUE;
                    } // end if (valid_column)
                } // end if (valid_column)

                cpl_vector_delete(yvec2); yvec2 = NULL;
                cpl_vector_delete(lvec2); lvec2 = NULL;
                cpl_vector_delete(fit_par); fit_par = NULL;
            }
        }  // for ix = 0:xsize-1
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();

        valid_slitlet = FALSE;
//        ret_error = cpl_error_get_code();
//        for (int i = 0; i < unroll_lcal_cnt; i+=2) {
//            plcal[(int)(punroll_lcal[i]) +
//                  (int)(punroll_lcal[i+1])*nx] = 0.0;
//        }

        *min_lambda = -1.0;
        *max_lambda = -1.0;
    }

    cpl_vector_delete(good); good = NULL;
    cpl_vector_delete(good2); good2 = NULL;
    cpl_vector_delete(lvec); lvec = NULL;
    cpl_vector_delete(lvec2); lvec2 = NULL;
    cpl_vector_delete(tmp_vec); tmp_vec = NULL;
    cpl_vector_delete(yvec); yvec = NULL;
    cpl_vector_delete(yvec2); yvec2 = NULL;
    cpl_vector_delete(lfit); lfit = NULL;

    return valid_slitlet;
}

int  kmo_fit_spectrum_1 (const cpl_image *bad_pix,
                      const cpl_vector *xarray,
                      const cpl_image *yarray,
                      const cpl_vector *larray,
                      const cpl_vector *left_edge,
                      const cpl_vector *right_edge,
                      cpl_image **lcal,
                      double *pfitpar,
                      const int fit_order,
                      int *valid_columns,
                      const int fitpar_offset,
                      int dbg_detector_nr,
                      int dbg_ifu_nr,
                      int dbg_slitlet_nr,
                      const char* data_fits_name)
{
    int             l               = 0,
                    lsize           = 0,
                    xsize           = 0,
                    valid_slitlet   = FALSE, // TRUE for at least one valid column
                    order_first_try = 0,
                    valid_col_offset = fitpar_offset/(fit_order+1),
                    px = 0, ix = 0, j = 0, k = 0;
    cpl_vector      *yvec           = NULL,
                    *lvec           = NULL,
                    *good           = NULL,
                    *yvec2          = NULL,
                    *lvec2          = NULL,
                    *good2          = NULL,
                    *tmp_vec        = NULL,
                    *fit_par        = NULL,
                    *fit_par2       = NULL,
                    *lfit           = NULL;

    double          *pyvec          = NULL,
                    *ptmp_vec       = NULL,
                    *pfit_par       = NULL,
                    *plfit          = NULL,
                    stddev          = 0.0,
                    mean            = 0.0,
                    *pyvec2         = NULL,
                    *plvec2         = NULL;

    const double    *pxarray        = NULL,
                    *pleft_edge     = NULL,
                    *pright_edge    = NULL;

    const float     *pyarray        = NULL,
                    *pbad_pix       = NULL;

    float           *plcal          = NULL/*,
                    *punroll_lcal   = NULL*/;

    KMO_TRY
    {
        KMO_TRY_ASSURE((xarray != NULL) &&
                       (yarray != NULL) &&
                       (larray != NULL) &&
                       (lcal != NULL),
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        KMO_TRY_ASSURE((cpl_vector_get_size(xarray) ==
                                              cpl_image_get_size_x(yarray)) &&
                       (cpl_vector_get_size(larray) ==
                                              cpl_image_get_size_y(yarray)),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "Size of inputs doesn't match!");

        KMO_TRY_ASSURE((dbg_detector_nr > 0) &&
                       (dbg_ifu_nr > 0) &&
                       (dbg_slitlet_nr > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "dbg values must be > 0!");

        // get some dimensions
        xsize = cpl_vector_get_size(xarray);
        KMO_TRY_CHECK_ERROR_STATE();

        // get some pointers
        KMO_TRY_EXIT_IF_NULL(
            pbad_pix = cpl_image_get_data_const(bad_pix));
        KMO_TRY_EXIT_IF_NULL(
            plcal = cpl_image_get_data(*lcal));
        KMO_TRY_EXIT_IF_NULL(
            pxarray = cpl_vector_get_data_const(xarray));
        KMO_TRY_EXIT_IF_NULL(
            pyarray = cpl_image_get_data_const(yarray));
        KMO_TRY_EXIT_IF_NULL(
            pleft_edge = cpl_vector_get_data_const(left_edge));
        KMO_TRY_EXIT_IF_NULL(
            pright_edge = cpl_vector_get_data_const(right_edge));

        // extract valid arclines
        KMO_TRY_EXIT_IF_NULL(
            good = kmo_idl_where(larray, -1.0, ne));

        KMO_TRY_EXIT_IF_NULL(
            lvec = kmo_idl_values_at_indices(larray, good));

        lsize = cpl_vector_get_size(lvec);
        KMO_TRY_CHECK_ERROR_STATE();

        KMO_TRY_EXIT_IF_NULL(
            lfit = cpl_vector_new(lsize));
        KMO_TRY_EXIT_IF_NULL(
            plfit = cpl_vector_get_data(lfit));

        // set up tmp vector
        KMO_TRY_EXIT_IF_NULL(
            tmp_vec = cpl_vector_new(cpl_vector_get_size(larray)));
        KMO_TRY_EXIT_IF_NULL(
            ptmp_vec = cpl_vector_get_data(tmp_vec));

        // for each x-pos fit a polynomial to spectrum and fill fitted values
        // into lcal taking into account the slitlet_mask

        for (ix = 0; ix < xsize; ix++) {
            if ((pxarray[ix] >= KMOS_BADPIX_BORDER) &&
                (pxarray[ix] < KMOS_DETECTOR_SIZE-KMOS_BADPIX_BORDER))
            {
                KMO_TRY_CHECK_ERROR_STATE();
                // extract positions for valid arclines
                for (j = 0; j < cpl_vector_get_size(larray); j++) {
                    // correction of wave shift error of 1 pix
                    ptmp_vec[j] = pyarray[ix+j*xsize]+1;
                }

                KMO_TRY_EXIT_IF_NULL(
                    yvec = kmo_idl_values_at_indices(tmp_vec, good));
                KMO_TRY_EXIT_IF_NULL(
                    pyvec = cpl_vector_get_data(yvec));

                order_first_try = fit_order;
fit_again:
                // fit the polynomial (1st fit)
                fit_par = kmo_polyfit_1d(yvec, lvec, order_first_try);
                if (cpl_error_get_code() != CPL_ERROR_NONE) {
                    // fit failed
                    cpl_error_reset();
                    cpl_vector_delete(fit_par); fit_par = NULL;
                    if (order_first_try > 2) {
                        // try again with lower order (down to 2)
                        order_first_try--;
                        goto fit_again;
                    } else {
                        // fit failed definitely, proceed with next ix
                        valid_columns[valid_col_offset+ix] = FALSE;
                    }
                } else {
                    // fit succeeded: calc polynomial, subtract it, reject
                    // and fit again
                    valid_columns[valid_col_offset+ix] = TRUE;
                    KMO_TRY_EXIT_IF_NULL(
                        pfit_par = cpl_vector_get_data(fit_par));

                    // calculate the fitted polynomial
                    l = 0;
                    for (j = 0; j < lsize; j++) {
                        plfit[l] = 0.0;
                        for(k = 0; k < cpl_vector_get_size(fit_par); k++) {
                            plfit[l] += pfit_par[k] * pow(pyvec[j], k);
                        }
                        l++;
                    }
// keep fit_par in case second fit fails
//erw
//                    cpl_vector_delete(fit_par); fit_par = NULL;

                    // subtract fitted from original values
                    KMO_TRY_EXIT_IF_ERROR(
                        cpl_vector_subtract(lfit, lvec));

                    KMO_TRY_EXIT_IF_ERROR(
                        cpl_vector_multiply_scalar(lfit, -1.0));

                    // reject deviant values
kmclipm_vector *ddd2 = kmclipm_vector_create(cpl_vector_duplicate(lfit));
                    KMO_TRY_EXIT_IF_ERROR(
                        kmclipm_reject_deviant(ddd2, 3, 3, &stddev, &mean));
cpl_vector *mmask = kmclipm_vector_get_mask(ddd2);
good2 = kmo_idl_where(mmask,1.0,eq);
cpl_vector_delete(mmask); mmask = NULL;
kmclipm_vector_delete(ddd2); ddd2 = NULL;
                    KMO_TRY_EXIT_IF_NULL(
                        yvec2 = kmo_idl_values_at_indices(yvec, good2));
                    KMO_TRY_EXIT_IF_NULL(
                        pyvec2 = cpl_vector_get_data(yvec2));

                    cpl_vector_delete(yvec); yvec = NULL;

                    KMO_TRY_EXIT_IF_NULL(
                        lvec2 = kmo_idl_values_at_indices(lvec, good2));
                    KMO_TRY_EXIT_IF_NULL(
                        plvec2 = cpl_vector_get_data(lvec2));

                    // refit the polynomial (2nd fit)
                    fit_par2 = kmo_polyfit_1d(yvec2, lvec2, fit_order);
                    cpl_vector_delete(yvec2); yvec2 = NULL;
                    cpl_vector_delete(lvec2); lvec2 = NULL;

                    if (cpl_error_get_code() != CPL_ERROR_NONE) {
                        // 2nd fit failed: take first fit instead
                        //    but only if fitted with requested order
                        cpl_msg_debug(__func__,
                                "second fit failed: size of first fit: %lld, requested size is %d",
                                cpl_vector_get_size(fit_par), fit_order+1);
                        if (cpl_vector_get_size(fit_par) == (fit_order+1)) {
                            cpl_msg_debug(__func__, "first fit is taken");
                            valid_columns[valid_col_offset+ix] = TRUE;
                        } else {
                            valid_columns[valid_col_offset+ix] = FALSE;
                        }
                        cpl_error_reset();
                        cpl_vector_delete(fit_par2); fit_par2 = NULL;
                    } else {
                        // 2nd fit succeeded: take this one!
                        valid_columns[valid_col_offset+ix] = TRUE;
                        cpl_vector_delete(fit_par);
                        fit_par = fit_par2;
                    }

                    if (data_fits_name != NULL) {
                        char *fit_ext_name = cpl_sprintf("first_fit_parameter_%d",ix);
                        kmo_wave_write_data_vector(data_fits_name, fit_par, fit_ext_name);
                        cpl_free(fit_ext_name);
                        KMO_TRY_RECOVER();
                    }

                    if (valid_columns[valid_col_offset+ix]) {
                        for (px = 0; px < fit_order+1; px++) {
//                            pfitpar[fitpar_offset+px] = cpl_vector_get(fit_par, px);
                            pfitpar[fitpar_offset+ix*(fit_order+1)+px] = cpl_vector_get(fit_par, px);
                            KMO_TRY_CHECK_ERROR_STATE();
                        }
                        valid_slitlet = TRUE;
                    }
                    cpl_vector_delete(fit_par);
                    KMO_TRY_CHECK_ERROR_STATE();
                }
            }
            cpl_vector_delete(good2); good2 = NULL;
        } // end for-loop
    }
    KMO_CATCH {
        KMO_CATCH_MSG();
    }

    cpl_vector_delete(good); good = NULL;
    cpl_vector_delete(lvec); lvec = NULL;
    cpl_vector_delete(lfit); lfit = NULL;
    cpl_vector_delete(tmp_vec); tmp_vec = NULL;

    return valid_slitlet;
}

int  kmo_fit_spectrum_2(const cpl_image *bad_pix,
                      const cpl_vector *xarray,
                      const cpl_image *yarray,
                      const cpl_vector *larray,
                      const cpl_vector *left_edge,
                      const cpl_vector *right_edge,
                      cpl_image **lcal,
                      double *min_lambda,
                      double *max_lambda,
                      double *pfitpar,
                      const int fit_order,
                      int *valid_columns,
                      const int fitpar_offset,
                      int dbg_detector_nr,
                      int dbg_ifu_nr,
                      int dbg_slitlet_nr)
{
    int             lsize           = 0,
                    xsize           = 0,
                    nx              = 0,
                    ny              = 0,
                    valid_col_offset = fitpar_offset/(fit_order+1),
                    ix = 0, px = 0, k = 0, iy = 0;
    cpl_vector      *yvec           = NULL,
                    *lvec           = NULL,
                    *good           = NULL,
                    *yvec2          = NULL,
                    *lvec2          = NULL,
                    *good2          = NULL,
                    *tmp_vec        = NULL,
                    *fit_par        = NULL,
                    *lfit           = NULL;

    double          *ptmp_vec       = NULL,
                    *pfit_par       = NULL,
                    *plfit          = NULL,
                    tmp_dbl         = 0.0;

    const double    *pxarray        = NULL,
                    *pleft_edge     = NULL,
                    *pright_edge    = NULL;

    const float     *pyarray        = NULL,
                    *pbad_pix       = NULL;

    float           *plcal          = NULL/*,
                    *punroll_lcal   = NULL*/;

    KMO_TRY
    {
        KMO_TRY_ASSURE((xarray != NULL) &&
                       (yarray != NULL) &&
                       (larray != NULL) &&
                       (lcal != NULL),
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        KMO_TRY_ASSURE(*lcal != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is allocated!");

        KMO_TRY_ASSURE((cpl_vector_get_size(xarray) ==
                                              cpl_image_get_size_x(yarray)) &&
                       (cpl_vector_get_size(larray) ==
                                              cpl_image_get_size_y(yarray)),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "Size of inputs doesn't match!");

        KMO_TRY_ASSURE((dbg_detector_nr > 0) &&
                       (dbg_ifu_nr > 0) &&
                       (dbg_slitlet_nr > 0),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "dbg values must be > 0!");

        // get some dimensions
        xsize = cpl_vector_get_size(xarray);
        nx = cpl_image_get_size_x(*lcal);
        ny = cpl_image_get_size_y(*lcal);
        KMO_TRY_CHECK_ERROR_STATE();

        // get some pointers
        KMO_TRY_EXIT_IF_NULL(
            pbad_pix = cpl_image_get_data_const(bad_pix));
        KMO_TRY_EXIT_IF_NULL(
            plcal = cpl_image_get_data(*lcal));
        KMO_TRY_EXIT_IF_NULL(
            pxarray = cpl_vector_get_data_const(xarray));
        KMO_TRY_EXIT_IF_NULL(
            pyarray = cpl_image_get_data_const(yarray));
        KMO_TRY_EXIT_IF_NULL(
            pleft_edge = cpl_vector_get_data_const(left_edge));
        KMO_TRY_EXIT_IF_NULL(
            pright_edge = cpl_vector_get_data_const(right_edge));

        // extract valid arclines
        KMO_TRY_EXIT_IF_NULL(
            good = kmo_idl_where(larray, -1.0, ne));

        KMO_TRY_EXIT_IF_NULL(
            lvec = kmo_idl_values_at_indices(larray, good));

        lsize = cpl_vector_get_size(lvec);
        KMO_TRY_CHECK_ERROR_STATE();

        KMO_TRY_EXIT_IF_NULL(
            lfit = cpl_vector_new(lsize));
        KMO_TRY_EXIT_IF_NULL(
            plfit = cpl_vector_get_data(lfit));

        // set up tmp vector
        KMO_TRY_EXIT_IF_NULL(
            tmp_vec = cpl_vector_new(cpl_vector_get_size(larray)));
        KMO_TRY_EXIT_IF_NULL(
            ptmp_vec = cpl_vector_get_data(tmp_vec));

        // for each x-pos fit a polynomial to spectrum and fill fitted values
        // into lcal taking into account the slitlet_mask

        for (ix = 0; ix < xsize; ix++) {
            if ((pxarray[ix] >= KMOS_BADPIX_BORDER) &&
                (pxarray[ix] < KMOS_DETECTOR_SIZE-KMOS_BADPIX_BORDER))
            {

//                // extract positions for valid arclines
//                for (int j = 0; j < cpl_vector_get_size(larray); j++) {
//                    // correction of wave shift error of 1 pix
//                    ptmp_vec[j] = pyarray[ix+j*xsize]+1;
//                }
//
//                KMO_TRY_EXIT_IF_NULL(
//                    yvec = kmo_idl_values_at_indices(tmp_vec, good));
//                KMO_TRY_EXIT_IF_NULL(
//                    pyvec = cpl_vector_get_data(yvec));
//
//                order_first_try = fit_order;
//fit_again:
//                // fit the polynomial (1st fit)
//                fit_par = kmo_polyfit_1d(yvec, lvec, order_first_try);
//                if (cpl_error_get_code() != CPL_ERROR_NONE) {
//                    // fit failed
//                    cpl_error_reset();
//                    cpl_vector_delete(fit_par); fit_par = NULL;
//                    if (order_first_try > 2) {
//                        // try again with lower order (down to 2)
//                        order_first_try--;
//                        goto fit_again;
//                    } else {
//                        // fit failed definitely, proceed with next ix
//                        valid_column = FALSE;
//                    }
//                } else {
//                    // fit succeeded: calc polynomial, subtract it, reject
//                    // and fit again
//                    valid_column = TRUE;
//                    KMO_TRY_EXIT_IF_NULL(
//                        pfit_par = cpl_vector_get_data(fit_par));
//
//                    // calculate the fitted polynomial
//                    l = 0;
//                    for (int j = 0; j < lsize; j++) {
//                        plfit[l] = 0.0;
//                        for(int k = 0; k < cpl_vector_get_size(fit_par); k++) {
//                            plfit[l] += pfit_par[k] * pow(pyvec[j], k);
//                        }
//                        l++;
//                    }
//                    cpl_vector_delete(fit_par); fit_par = NULL;
//
//                    // subtract fitted from original values
//                    KMO_TRY_EXIT_IF_ERROR(
//                        cpl_vector_subtract(lfit, lvec));
//
//                    KMO_TRY_EXIT_IF_ERROR(
//                        cpl_vector_multiply_scalar(lfit, -1.0));
//
//                   // reject deviant values
//                    KMO_TRY_EXIT_IF_NULL(
//                        good2 = kmclipm_reject_deviant(lfit, 3, 3, &stddev, &mean));
//
//                    KMO_TRY_EXIT_IF_NULL(
//                        yvec2 = kmo_idl_values_at_indices(yvec, good2));
//                    KMO_TRY_EXIT_IF_NULL(
//                        pyvec2 = cpl_vector_get_data(yvec2));
//
//                    cpl_vector_delete(yvec); yvec = NULL;
//
//                    KMO_TRY_EXIT_IF_NULL(
//                        lvec2 = kmo_idl_values_at_indices(lvec, good2));
//                    KMO_TRY_EXIT_IF_NULL(
//                        plvec2 = cpl_vector_get_data(lvec2));
//
//                    // refit the polynomial (2nd fit)
//                    fit_par2 = kmo_polyfit_1d(yvec2, lvec2, fit_order);
//
//                    if (cpl_error_get_code() != CPL_ERROR_NONE) {
//                        // 2nd fit failed: take first fit instead
//                        printf("erw trace point 2 in kmo_fit_spectrum\n");
//                        valid_column = FALSE;
//                        cpl_error_reset();
//                        cpl_vector_delete(fit_par2); fit_par2 = NULL;
//                    } else {
//                        // 2nd fit succeeded: take this one!
//                        valid_column = TRUE;
//                        cpl_vector_delete(fit_par);
//                        fit_par = fit_par2;
//                    }
//                }

                KMO_TRY_EXIT_IF_NULL(
                        fit_par = cpl_vector_new(fit_order+1));
                KMO_TRY_EXIT_IF_NULL(
                        pfit_par = cpl_vector_get_data(fit_par));

                if (valid_columns[valid_col_offset+ix]) {
                    // recalculate the fitted polynomial and write value only into lcal
                    // if it corresponds to the actual slitlet

                    for (px = 0; px < fit_order+1; px++) {
                        KMO_TRY_EXIT_IF_ERROR(
                                cpl_vector_set(fit_par, px,
                                        pfitpar[fitpar_offset+ix*(fit_order+1)+px]));
                    }

                    for (iy = KMOS_BADPIX_BORDER; iy < ny-KMOS_BADPIX_BORDER; iy++) {
                        if ((pxarray[ix] >= pleft_edge[iy-KMOS_BADPIX_BORDER]) &&
                            (pxarray[ix] <= pright_edge[iy-KMOS_BADPIX_BORDER]))
                        {
                            tmp_dbl = 0.0;
                            for(k = 0; k < cpl_vector_get_size(fit_par); k++) {
                                tmp_dbl += pfit_par[k] * pow(iy+1, k);
                            }

                            if ((iy == KMOS_BADPIX_BORDER) && (tmp_dbl < *min_lambda)) {
                                *min_lambda = tmp_dbl;
                            }
                            if ((iy == ny-KMOS_BADPIX_BORDER-1) && (tmp_dbl > *max_lambda)) {
                                *max_lambda = tmp_dbl;
                            }

                            if (pbad_pix[(int)(pxarray[ix])+iy*nx] >= 0.5) {
                                // good pix
                                plcal[(int)(pxarray[ix])+iy*nx] = tmp_dbl;
                            } else {
                                // bad pix
                                // is already set to 0
                            }
                        }
                    }  // for iy=0:ny-1
                } // end if (valid_columns)

                cpl_vector_delete(yvec2); yvec2 = NULL;
                cpl_vector_delete(lvec2); lvec2 = NULL;
                cpl_vector_delete(fit_par); fit_par = NULL;
            }
        }  // for ix = 0:xsize-1
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();

        *min_lambda = -1.0;
        *max_lambda = -1.0;
    }

    cpl_vector_delete(good); good = NULL;
    cpl_vector_delete(good2); good2 = NULL;
    cpl_vector_delete(lvec); lvec = NULL;
    cpl_vector_delete(lvec2); lvec2 = NULL;
    cpl_vector_delete(tmp_vec); tmp_vec = NULL;
    cpl_vector_delete(yvec); yvec = NULL;
    cpl_vector_delete(yvec2); yvec2 = NULL;
    cpl_vector_delete(lfit); lfit = NULL;

    return 1;
}

/**
}
    @brief
        Reconstruct detector arc images using current calibration and writes them to a fits file

    @param frameset         the input frameset
    @param arcDetImg_data   the arc frame
    @param darkDetImg_data  the dark frame
    @param xcalDetImg       the xcal frame
    @param ycalDetImg       the ycal frame
    @param lcalDetImg       the lcal frame
    @param ifu_inactive     Array containing flags if IFUs are (in)active.
    @param flip             FALSE if detector image should be flipped (i.e.
                            real data must be flipped)
    @param device_nr        the device number
    @param suffix           the filter/frating suffix
    @param filter_id        the filter ID
    @param lamp_config      the lamp configuration
    @param qc_headers    (Input/Output) Input: Allocated structure for 3 cpl_propertylists
                         Output: Calculated qc parameters for all 3 detector frames stored
                         each in a cpl_propertylist. These have to be deleted afterwards!

    @return
        CPL_ERROR_NONE or any encountered CPL error

*/
cpl_image* kmo_reconstructed_arc_image(cpl_frameset *frameset,
                                       cpl_image *arcDetImg_data,
                                       cpl_image *darkDetImg_data,
                                       cpl_image *xcalDetImg,
                                       cpl_image *ycalDetImg,
                                       cpl_image *lcalDetImg,
                                       cpl_array *ifu_inactive,
                                       int flip,
                                       int device_nr,
                                       const char *suffix,
                                       const char *filter_id,
                                       enum lampConfiguration lamp_config,
                                       cpl_propertylist **qc_header)
{
    cpl_propertylist    *tmp_header                 = NULL;
    cpl_imagelist       *data_cube                  = NULL;
    gridDefinition      gd;
    cpl_image           *det_img                    = NULL,
                        *flatDetImg_data            = NULL;
    int                 *bounds                     = NULL,
                        pos_maxdiff                 = 0,
                        pos_mindiff                 = 0,
                        crpix                       = 0,
                        ifu = 0, lx = 0, ly = 0, lz = 0;
    double              neighborhoodRange           = 1.001,
                        mindiff                     = 0.,
                        maxdiff                     = 0.,
                        pos_mean_offset             = 0.,
                        pos_stdev                   = 0.,
                        pos_95ile                   = 0.,
                        fwhm_mean                   = 0.,
                        fwhm_stdev                  = 0.,
                        fwhm_95ile                  = 0.,
                        wavelength                  = 0.,
                        cdelt                       = 0.;
    char                *imethod                    = "CS",
                        lutFilename[1024],
                        *last_env           = NULL;
    float               *pdet_img                   = NULL;
    cpl_frame           *xcalFrame                  = NULL,
                        *ycalFrame                  = NULL;
    cpl_array           *calTimestamp               = NULL;
    kmclipm_vector      *stored_ar_pos_mean_offset  = NULL,
                        *stored_ar_pos_stdev        = NULL,
                        *stored_ar_pos_95ile        = NULL,
                        *stored_ar_fwhm_mean        = NULL,
                        *stored_ar_fwhm_stdev       = NULL,
                        *stored_ar_fwhm_95ile       = NULL,
                        *stored_ne_pos_mean_offset  = NULL,
                        *stored_ne_pos_stdev        = NULL,
                        *stored_ne_pos_95ile        = NULL,
                        *stored_ne_fwhm_mean        = NULL,
                        *stored_ne_fwhm_stdev       = NULL,
                        *stored_ne_fwhm_95ile       = NULL;
    cpl_table           *band_table                 = NULL;
    cpl_vector          *calAngles                   = NULL;

    KMO_TRY
    {
        KMO_TRY_ASSURE((ifu_inactive != NULL),
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        KMO_TRY_ASSURE(cpl_array_get_size(ifu_inactive)
                                                      == KMOS_IFUS_PER_DETECTOR,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "Array 'ifu_inactive'' must be of size 8!");

        // create filename for LUT
        strcpy(lutFilename,  "lut");
        strcat(lutFilename,  suffix);

        // get bounds from XCAL
        KMO_TRY_EXIT_IF_NULL(
            xcalFrame = kmo_dfs_get_frame(frameset, XCAL));
        KMO_TRY_EXIT_IF_NULL(
            tmp_header = kmo_dfs_load_primary_header(frameset, XCAL));
        KMO_TRY_EXIT_IF_NULL(
            bounds = kmclipm_extract_bounds(tmp_header));
        cpl_propertylist_delete(tmp_header); tmp_header = NULL;

        //
        // get timestamps of xcal, ycal & lcal
        //
        KMO_TRY_EXIT_IF_NULL(
            ycalFrame = kmo_dfs_get_frame(frameset, YCAL));
        KMO_TRY_EXIT_IF_NULL(
            calTimestamp = kmo_get_timestamps(xcalFrame, ycalFrame, NULL));
        calAngles = cpl_vector_new(3); // is not not used because no LUT will be used

       // make sure no reconstruction lookup table (LUT) is used
        if (getenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE") != NULL) {
            last_env = getenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE");
        }
        setenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE","NONE",1);

        //
        // setup grid definition, wavelength start and end points will be set
        // in the detector looplamp_config
        //
        char *reconstr_method = getenv("KMO_WAVE_RECONSTRUCT_METHOD");
        if (reconstr_method != NULL) {
            imethod = reconstr_method;
        }
        KMO_TRY_EXIT_IF_ERROR(
            kmclipm_setup_grid(&gd, imethod, neighborhoodRange, KMOS_PIX_RESOLUTION, 0.));

        char *tmp_band_method = getenv("KMO_BAND_METHOD");
        int band_method = 0;
        if (tmp_band_method != NULL) {
            band_method = atoi(tmp_band_method);
        }
        // this overrides gd.l.dim in kmclipm_setup_grid()
        KMO_TRY_EXIT_IF_NULL(
            band_table = kmo_dfs_load_table(frameset, WAVE_BAND, 1, 0));
        KMO_TRY_EXIT_IF_ERROR(
            kmclipm_setup_grid_band_lcal(&gd, lcalDetImg, filter_id,
                                         band_method, band_table));
        cpl_table_delete(band_table); band_table = NULL;

        KMO_TRY_EXIT_IF_NULL(
            det_img = cpl_image_new(gd.x.dim * gd.y.dim * KMOS_IFUS_PER_DETECTOR,
                                    gd.l.dim,CPL_TYPE_FLOAT));
        KMO_TRY_EXIT_IF_NULL(
            pdet_img = cpl_image_get_data_float(det_img));

        //
        // setup det_img-header
        //
        KMO_TRY_EXIT_IF_NULL(
            *qc_header = cpl_propertylist_new());

        KMO_TRY_EXIT_IF_ERROR(
            kmclipm_update_property_string(*qc_header, CTYPE1, "PIXEL", ""));
        KMO_TRY_EXIT_IF_ERROR(
            kmclipm_update_property_string(*qc_header, CTYPE2, "WAVELEN", ""));
        KMO_TRY_EXIT_IF_ERROR(
            kmclipm_update_property_double(*qc_header, CRPIX1, 0.0, ""));
        crpix = 1;
        if (flip == FALSE) {
            crpix = gd.l.dim;
        }
        KMO_TRY_EXIT_IF_ERROR(
            kmclipm_update_property_double(*qc_header, CRPIX2, crpix, ""));
        KMO_TRY_EXIT_IF_ERROR(
            kmclipm_update_property_double(*qc_header, CRVAL1, 0.0, ""));
        KMO_TRY_EXIT_IF_ERROR(
            kmclipm_update_property_double(*qc_header, CRVAL2, gd.l.start, ""));
        KMO_TRY_EXIT_IF_ERROR(
            kmclipm_update_property_double(*qc_header, CDELT1, 1.0, ""));

        cdelt = gd.l.delta;
        if (flip == FALSE) {
            cdelt = -cdelt;
        }
        KMO_TRY_EXIT_IF_ERROR(
            kmclipm_update_property_double(*qc_header, CDELT2, cdelt, ""));

        //
        // load master flat & its noise
        //
        KMO_TRY_EXIT_IF_NULL(
            flatDetImg_data = cpl_image_duplicate(arcDetImg_data));
        KMO_TRY_EXIT_IF_ERROR(
            kmo_image_fill(flatDetImg_data, 1.0));

        // loop through all IFUs on this detector
        stored_ar_pos_mean_offset = kmclipm_vector_new(KMOS_IFUS_PER_DETECTOR);
        stored_ar_pos_stdev       = kmclipm_vector_new(KMOS_IFUS_PER_DETECTOR);
        stored_ar_pos_95ile       = kmclipm_vector_new(KMOS_IFUS_PER_DETECTOR);
        stored_ar_fwhm_mean       = kmclipm_vector_new(KMOS_IFUS_PER_DETECTOR);
        stored_ar_fwhm_stdev      = kmclipm_vector_new(KMOS_IFUS_PER_DETECTOR);
        stored_ar_fwhm_95ile      = kmclipm_vector_new(KMOS_IFUS_PER_DETECTOR);
        stored_ne_pos_mean_offset = kmclipm_vector_new(KMOS_IFUS_PER_DETECTOR);
        stored_ne_pos_stdev       = kmclipm_vector_new(KMOS_IFUS_PER_DETECTOR);
        stored_ne_pos_95ile       = kmclipm_vector_new(KMOS_IFUS_PER_DETECTOR);
        stored_ne_fwhm_mean       = kmclipm_vector_new(KMOS_IFUS_PER_DETECTOR);
        stored_ne_fwhm_stdev      = kmclipm_vector_new(KMOS_IFUS_PER_DETECTOR);
        stored_ne_fwhm_95ile      = kmclipm_vector_new(KMOS_IFUS_PER_DETECTOR);

        kmclipm_vector_fill(stored_ar_pos_mean_offset, -1.);
        kmclipm_vector_fill(stored_ar_pos_stdev, -1.);
        kmclipm_vector_fill(stored_ar_pos_95ile, -1.);
        kmclipm_vector_fill(stored_ar_fwhm_mean, -1.);
        kmclipm_vector_fill(stored_ar_fwhm_stdev, -1.);
        kmclipm_vector_fill(stored_ar_fwhm_95ile, -1.);
        kmclipm_vector_fill(stored_ne_pos_mean_offset, -1.);
        kmclipm_vector_fill(stored_ne_pos_stdev, -1.);
        kmclipm_vector_fill(stored_ne_pos_95ile, -1.);
        kmclipm_vector_fill(stored_ne_fwhm_mean, -1.);
        kmclipm_vector_fill(stored_ne_fwhm_stdev, -1.);
        kmclipm_vector_fill(stored_ne_fwhm_95ile, -1.);

        for (ifu = 0; ifu < KMOS_IFUS_PER_DETECTOR; ifu++) {
            int ifu_nr = ifu + 1 + (device_nr - 1) * KMOS_IFUS_PER_DETECTOR;
            if ((bounds[2*(ifu_nr-1)] != -1) &&
                (bounds[2*(ifu_nr-1)+1] != -1) &&
                (cpl_array_get_int(ifu_inactive, ifu, NULL) == 0))
            {
                // reconstruct cube
                KMO_TRY_EXIT_IF_ERROR(
                    kmo_reconstruct_sci_image(ifu_nr,
                                            bounds[2*(ifu_nr-1)],
                                            bounds[2*(ifu_nr-1)+1],
                                            arcDetImg_data,
                                            NULL,
                                            darkDetImg_data,
                                            NULL,
                                            flatDetImg_data,
                                            NULL,
                                            xcalDetImg,
                                            ycalDetImg,
                                            lcalDetImg,
                                            &gd,
                                            calTimestamp,
                                            calAngles,
                                            lutFilename,
                                            &data_cube,
                                            NULL,
                                            FALSE,
                                            FALSE,
                                            NULL,
                                            NULL,
                                            NULL));

                // create detector image
                for (lz = 0; lz < gd.l.dim; lz++) {
                    float *slice;
                    KMO_TRY_EXIT_IF_NULL(
                        slice = cpl_image_get_data_float(
                                             cpl_imagelist_get(data_cube, lz)));
                    for (ly = 0; ly < gd.y.dim; ly++) {
                        for (lx = 0; lx < gd.x.dim; lx++) {
                            int ix = lx +
                                     ly * gd.x.dim +
                                     ifu * gd.x.dim*gd.y.dim +
                                     lz * gd.x.dim*gd.y.dim*KMOS_IFUS_PER_DETECTOR;
                            pdet_img[ix] = slice[lx + ly*gd.x.dim];
                        }
                    }
                }

                // calculate temporary QC parameters
                if ((lamp_config == ARGON) ||
                    (lamp_config == ARGON_NEON))
                {
                    if (strcmp(filter_id, "H") == 0) {
                        wavelength = REF_LINE_AR_H;
                    } else if (strcmp(filter_id, "K") == 0) {
                        wavelength = REF_LINE_AR_K;
                    } else if (strcmp(filter_id, "YJ") == 0) {
                        wavelength = REF_LINE_AR_YJ;
                    } else if (strcmp(filter_id, "IZ") == 0) {
                        wavelength = REF_LINE_AR_IZ;
                    } else if (strcmp(filter_id, "HK") == 0) {
                        wavelength = REF_LINE_AR_HK;
                    }

                    KMO_TRY_EXIT_IF_ERROR(
                        kmo_calc_qc_wave_cal(data_cube, 1.0,
                                             gd.l.start, gd.l.delta, wavelength,
                                             &pos_mean_offset, &pos_stdev,
                                             &pos_95ile,
                                             &fwhm_mean, &fwhm_stdev,
                                             &fwhm_95ile));

                    // set these QC values
                    kmclipm_vector_set(stored_ar_pos_mean_offset, ifu,
                                       pos_mean_offset);
                    kmclipm_vector_set(stored_ar_pos_stdev, ifu, pos_stdev);
                    kmclipm_vector_set(stored_ar_pos_95ile, ifu, pos_95ile);
                    kmclipm_vector_set(stored_ar_fwhm_mean, ifu, fwhm_mean);
                    kmclipm_vector_set(stored_ar_fwhm_stdev, ifu, fwhm_stdev);
                    kmclipm_vector_set(stored_ar_fwhm_95ile, ifu, fwhm_95ile);
                }
                KMO_TRY_CHECK_ERROR_STATE();

                if ((lamp_config == NEON) ||
                    (lamp_config == ARGON_NEON))
                {
                    if (strcmp(filter_id, "H") == 0) {
                        wavelength = REF_LINE_NE_H;
                    } else if (strcmp(filter_id, "K") == 0) {
                        wavelength = REF_LINE_NE_K;
                    } else if (strcmp(filter_id, "YJ") == 0) {
                        wavelength = REF_LINE_NE_YJ;
                    } else if (strcmp(filter_id, "IZ") == 0) {
                        wavelength = REF_LINE_NE_IZ;
                    } else if (strcmp(filter_id, "HK") == 0) {
                        wavelength = REF_LINE_NE_HK;
                    }

                    KMO_TRY_EXIT_IF_ERROR(
                        kmo_calc_qc_wave_cal(data_cube, 1.0,
                                             gd.l.start, gd.l.delta, wavelength,
                                             &pos_mean_offset, &pos_stdev,
                                             &pos_95ile,
                                             &fwhm_mean, &fwhm_stdev,
                                             &fwhm_95ile));

                    // set these QC values
                    kmclipm_vector_set(stored_ne_pos_mean_offset, ifu,
                                       pos_mean_offset);
                    kmclipm_vector_set(stored_ne_pos_stdev, ifu, pos_stdev);
                    kmclipm_vector_set(stored_ne_pos_95ile, ifu, pos_95ile);
                    kmclipm_vector_set(stored_ne_fwhm_mean, ifu, fwhm_mean);
                    kmclipm_vector_set(stored_ne_fwhm_stdev, ifu, fwhm_stdev);
                    kmclipm_vector_set(stored_ne_fwhm_95ile, ifu, fwhm_95ile);
                }
                KMO_TRY_CHECK_ERROR_STATE();
                cpl_imagelist_delete(data_cube); data_cube = NULL;
            } else {
                // IFU not valid,  fill det_img with NaNs
                double nan = 0./0.;
                for (lz = 0; lz < gd.l.dim; lz++) {
                    for (ly = 0; ly < gd.y.dim; ly++) {
                        for (lx = 0; lx < gd.x.dim; lx++) {
                            int ix = lx +
                                     ly * gd.x.dim +
                                     ifu * gd.x.dim*gd.y.dim +
                                     lz * gd.x.dim*gd.y.dim*KMOS_IFUS_PER_DETECTOR;
                            pdet_img[ix] = nan;
                        }
                    }
                }

                // reject these QC values
                kmclipm_vector_reject(stored_ar_pos_mean_offset, ifu);
                kmclipm_vector_reject(stored_ar_pos_stdev, ifu);
                kmclipm_vector_reject(stored_ar_pos_95ile, ifu);
                kmclipm_vector_reject(stored_ar_fwhm_mean, ifu);
                kmclipm_vector_reject(stored_ar_fwhm_stdev, ifu);
                kmclipm_vector_reject(stored_ar_fwhm_95ile, ifu);

                kmclipm_vector_reject(stored_ne_pos_mean_offset, ifu);
                kmclipm_vector_reject(stored_ne_pos_stdev, ifu);
                kmclipm_vector_reject(stored_ne_pos_95ile, ifu);
                kmclipm_vector_reject(stored_ne_fwhm_mean, ifu);
                kmclipm_vector_reject(stored_ne_fwhm_stdev, ifu);
                kmclipm_vector_reject(stored_ne_fwhm_95ile, ifu);
            } //if bounds[]
            KMO_TRY_CHECK_ERROR_STATE();
        } // for: ifu=KMOS_IFUS_PER_DETECTOR
        KMO_TRY_CHECK_ERROR_STATE();

//cpl_msg_info("","     | pos Ar\t\t\t| fwhm Ar\t\t\t\t| pos Ne\t\t\t| fwhm Ne\t\t\t |");
//cpl_msg_info("","ifu: | avg/off.\tstdev\t95%%ile\t| avg\t\tstdev\t95%%ile\t\t| avg/off.\tstdev\t95%%ile\t| avg\t\tstdev\t95%%ile\t |");
//cpl_msg_info("","------------------------------------------------------------------------------------------------------------------------------------------");
//for (int ifu = 0; ifu < KMOS_IFUS_PER_DETECTOR; ifu++) {
//cpl_msg_info("","%i   | %.4f\t%.4f\t%.4f\t| %.4f\t%.4f\t%.4f\t| %.4f\t%.4f\t%.4f\t| %.4f\t%.4f\t%.4f |", ifu,
//             kmclipm_vector_get(stored_ar_pos_mean_offset, ifu, NULL),
//             kmclipm_vector_get(stored_ar_pos_stdev, ifu, NULL),
//             kmclipm_vector_get(stored_ar_pos_95ile, ifu, NULL),
//             kmclipm_vector_get(stored_ar_fwhm_mean, ifu, NULL),
//             kmclipm_vector_get(stored_ar_fwhm_stdev, ifu, NULL),
//             kmclipm_vector_get(stored_ar_fwhm_95ile, ifu, NULL),
//             kmclipm_vector_get(stored_ne_pos_mean_offset, ifu, NULL),
//             kmclipm_vector_get(stored_ne_pos_stdev, ifu, NULL),
//             kmclipm_vector_get(stored_ne_pos_95ile, ifu, NULL),
//             kmclipm_vector_get(stored_ne_fwhm_mean, ifu, NULL),
//             kmclipm_vector_get(stored_ne_fwhm_stdev, ifu, NULL),
//             kmclipm_vector_get(stored_ne_fwhm_95ile, ifu, NULL));
//}

        if (flip == FALSE) {
            KMO_TRY_EXIT_IF_ERROR(
                cpl_image_flip(det_img, 0));
        }

        // calculate QC parameters for whole detector
        if ((lamp_config == ARGON) ||
            (lamp_config == ARGON_NEON))
        {
            maxdiff = kmclipm_vector_get_max(stored_ar_pos_mean_offset,
                                             &pos_maxdiff);
            mindiff = kmclipm_vector_get_min(stored_ar_pos_mean_offset,
                                             &pos_mindiff);
            if (fabs(mindiff) > fabs(maxdiff)) {
                maxdiff = mindiff;
                pos_maxdiff = pos_mindiff;
            }

            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_update_property_double(*qc_header, QC_ARC_AR_POS_MEAN,
                               kmclipm_vector_get_mean(stored_ar_pos_mean_offset),
                               "[km/s] mean of pos. offset for Ar ref. line"));
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_update_property_double(*qc_header, QC_ARC_AR_POS_MAXDIFF,
                               maxdiff,
                               "[km/s] max diff of pos. offset for Ar ref. line"));
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_update_property_int(*qc_header, QC_ARC_AR_POS_MAXDIFF_ID,
                            pos_maxdiff+1,
                            "[] IFU ID with max diff in Ar pos. offset"));
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_update_property_double(*qc_header, QC_ARC_AR_POS_STDEV,
                            kmclipm_vector_get_mean(stored_ar_pos_stdev),
                            "[km/s] mean stdev of pos. offset for Ar ref. line"));
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_update_property_double(*qc_header, QC_ARC_AR_POS_95ILE,
                            kmclipm_vector_get_mean(stored_ar_pos_95ile),
                            "[km/s] mean 95%ile of pos. offset for Ar ref. line"));
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_update_property_double(*qc_header, QC_ARC_AR_FWHM_MEAN,
                            kmclipm_vector_get_mean(stored_ar_fwhm_mean),
                            "[km/s] mean of fwhm for Ar ref. line"));
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_update_property_double(*qc_header, QC_ARC_AR_FWHM_STDEV,
                            kmclipm_vector_get_mean(stored_ar_fwhm_stdev),
                            "[km/s] mean stdev of fwhm for Ar ref. line"));
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_update_property_double(*qc_header, QC_ARC_AR_FWHM_95ILE,
                            kmclipm_vector_get_mean(stored_ar_fwhm_95ile),
                            "[km/s] mean 95%ile of fwhm for Ar ref. line"));
        }

        if ((lamp_config == NEON) ||
            (lamp_config == ARGON_NEON))
        {
            maxdiff = kmclipm_vector_get_max(stored_ne_pos_mean_offset,
                                             &pos_maxdiff);
            mindiff = kmclipm_vector_get_min(stored_ne_pos_mean_offset,
                                             &pos_mindiff);
            if (fabs(mindiff) > fabs(maxdiff)) {
                maxdiff = mindiff;
                pos_maxdiff = pos_mindiff;
            }

            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_update_property_double(*qc_header, QC_ARC_NE_POS_MEAN,
                            kmclipm_vector_get_mean(stored_ne_pos_mean_offset),
                            "[km/s] mean of pos. offset for Ne ref. line"));
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_update_property_double(*qc_header, QC_ARC_NE_POS_MAXDIFF,
                            maxdiff,
                            "[km/s] max diff of pos. offset for Ne ref. line"));
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_update_property_int(*qc_header, QC_ARC_NE_POS_MAXDIFF_ID,
                            pos_maxdiff+1,
                            "[] IFU ID with max diff in Ne pos. offset"));
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_update_property_double(*qc_header, QC_ARC_NE_POS_STDEV,
                            kmclipm_vector_get_mean(stored_ne_pos_stdev),
                            "[km/s] mean stdev of pos. offset for Ne ref. line"));
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_update_property_double(*qc_header, QC_ARC_NE_POS_95ILE,
                            kmclipm_vector_get_mean(stored_ne_pos_95ile),
                            "[km/s] mean 95%ile of pos. offset for Ne ref. line"));
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_update_property_double(*qc_header, QC_ARC_NE_FWHM_MEAN,
                            kmclipm_vector_get_mean(stored_ne_fwhm_mean),
                            "[km/s] mean of fwhm for Ne ref. line"));
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_update_property_double(*qc_header, QC_ARC_NE_FWHM_STDEV,
                            kmclipm_vector_get_mean(stored_ne_fwhm_stdev),
                            "[km/s] mean stdev of fwhm for Ne ref. line"));
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_update_property_double(*qc_header, QC_ARC_NE_FWHM_95ILE,
                            kmclipm_vector_get_mean(stored_ne_fwhm_95ile),
                            "[km/s] mean 95%ile of fwhm for Ne ref. line"));
        }
    } KMO_CATCH {
        cpl_msg_error(cpl_func,
                      "Encountered error: %s in %s",
                      cpl_error_get_message(),
                      cpl_error_get_where());
        cpl_propertylist_delete(*qc_header); *qc_header = NULL;
        cpl_image_delete(det_img); det_img = NULL;
    }
    cpl_image_delete(flatDetImg_data); flatDetImg_data = NULL;
    cpl_array_delete(calTimestamp); calTimestamp = NULL;
    if (calAngles != NULL) {cpl_vector_delete(calAngles);}

    kmclipm_vector_delete(stored_ar_pos_mean_offset);
    stored_ar_pos_mean_offset = NULL;
    kmclipm_vector_delete(stored_ar_pos_stdev); stored_ar_pos_stdev = NULL;
    kmclipm_vector_delete(stored_ar_pos_95ile); stored_ar_pos_95ile = NULL;
    kmclipm_vector_delete(stored_ar_fwhm_mean); stored_ar_fwhm_mean = NULL;
    kmclipm_vector_delete(stored_ar_fwhm_stdev); stored_ar_fwhm_stdev = NULL;
    kmclipm_vector_delete(stored_ar_fwhm_95ile); stored_ar_fwhm_95ile = NULL;
    kmclipm_vector_delete(stored_ne_pos_mean_offset);
    stored_ne_pos_mean_offset = NULL;
    kmclipm_vector_delete(stored_ne_pos_stdev); stored_ne_pos_stdev = NULL;
    kmclipm_vector_delete(stored_ne_pos_95ile); stored_ne_pos_95ile = NULL;
    kmclipm_vector_delete(stored_ne_fwhm_mean); stored_ne_fwhm_mean = NULL;
    kmclipm_vector_delete(stored_ne_fwhm_stdev); stored_ne_fwhm_stdev = NULL;
    kmclipm_vector_delete(stored_ne_fwhm_95ile); stored_ne_fwhm_95ile = NULL;
    cpl_free(bounds); bounds = NULL;

    if (last_env != NULL) {
        setenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE",last_env,1);
    } else {
        unsetenv("KMCLIPM_PRIV_RECONSTRUCT_LUT_MODE");
    }

    return det_img;
}

cpl_error_code kmo_calc_qc_wave_cal(const cpl_imagelist *cube,
                                    double crpix,
                                    double crval,
                                    double cdelt,
                                    double ref_wavelength,
                                    double *pos_mean_offset,
                                    double *pos_stdev,
                                    double *pos_95ile,
                                    double *fwhm_mean_offset,
                                    double *fwhm_stdev,
                                    double *fwhm_95ile)
{
    cpl_error_code  err             = CPL_ERROR_NONE;
    double          vscale          = 0.,
                    fitted_centroid = 0.,
                    fitted_sigma    = 0.,
                    fitted_area     = 0.,
                    offset          = 0.,
                    gpeakm          = 0.,
                    gpeaks          = 0.,
                    gposm           = 0.,
                    gposs           = 0.,
                    gfwhmm          = 0.,
                    gfwhms          = 0.,
                    gposml          = 0.,
                    gpmax           = 0.,
                    gpmin           = 0.,
                    q95             = 0.,
                    f95             = 0.;
    int             nx              = 0,
                    ny              = 0,
                    nz              = 0,
                    ref_pixpos      = 0,
                    len             = 19,
                    len2            = len/2,    // 9
                    tmp_int         = 0,
                    index           = 0,
                    i = 0, ix = 0, iy = 0, iz = 0;
    const cpl_image *img            = NULL;
    cpl_vector      *x              = NULL,
                    *y              = NULL,
                    *vecl           = NULL,
                    *vgposm         = NULL,
                    *vgposml        = NULL;
    cpl_bivector    *bivec_ref      = NULL,
                    *bivec_out      = NULL;
    kmclipm_vector  *lambda         = NULL,
                    *tmp_vec        = NULL,
                    *gpos           = NULL,
                    *gfwhm          = NULL,
                    *gpeak          = NULL;

    KMO_TRY
    {
        KMO_TRY_ASSURE((cube != NULL) ||
                       (pos_mean_offset != NULL) ||
                       (pos_stdev != NULL) ||
                       (pos_95ile != NULL) ||
                       (fwhm_mean_offset != NULL) ||
                       (fwhm_stdev != NULL) ||
                       (fwhm_95ile != NULL),
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        KMO_TRY_EXIT_IF_NULL(
            img = cpl_imagelist_get_const(cube, 0));
        nx = cpl_image_get_size_x(img);
        ny = cpl_image_get_size_y(img);
        nz = cpl_imagelist_get_size(cube);

        // setup & initialise lambda vector
        KMO_TRY_EXIT_IF_NULL(
            lambda = kmclipm_vector_new(nz));
        for (i = 0; i < nz; i++) {
            KMO_TRY_EXIT_IF_ERROR(
                kmclipm_vector_set(lambda, i, (i+1-crpix)*cdelt+crval));
        }

        // find approximate position of ref_wavelength in lambda
        // (pixel position of min(abs(lambda-ref_wavelength)) )
        KMO_TRY_EXIT_IF_NULL(
            tmp_vec = kmclipm_vector_duplicate(lambda));

        KMO_TRY_EXIT_IF_ERROR(
            kmclipm_vector_subtract_scalar(tmp_vec, ref_wavelength));

        KMO_TRY_EXIT_IF_ERROR(
            kmclipm_vector_abs(tmp_vec));

        kmclipm_vector_get_min(tmp_vec, &ref_pixpos);
        KMO_TRY_CHECK_ERROR_STATE();

        kmclipm_vector_delete(tmp_vec); tmp_vec = NULL;

        KMO_TRY_EXIT_IF_NULL(
            x = cpl_vector_new(len));
        for (i = 0; i < len; i++) {
            cpl_vector_set(x, i, i);
        }
        KMO_TRY_EXIT_IF_NULL(
            y = cpl_vector_new(len));
        KMO_TRY_EXIT_IF_NULL(
            gpos = kmclipm_vector_new(nx*ny));
        KMO_TRY_EXIT_IF_NULL(
            gfwhm = kmclipm_vector_new(nx*ny));
        KMO_TRY_EXIT_IF_NULL(
            gpeak = kmclipm_vector_new(nx*ny));

        for (iy = 0; iy < ny; iy++) {
            for (ix = 0; ix < nx; ix++) {
                int iv = 0;
                for (iz = ref_pixpos-len2; iz <= ref_pixpos+len2; iz++) {
                    KMO_TRY_EXIT_IF_NULL(
                        img = cpl_imagelist_get_const(cube, iz));
                    cpl_vector_set(y, iv++, cpl_image_get(img, ix+1, iy+1, &tmp_int));
                    KMO_TRY_CHECK_ERROR_STATE();
                }

//                KMO_TRY_EXIT_IF_ERROR(
//                    err = cpl_vector_fit_gaussian(x, NULL,
//                                                  y, NULL,
//                                                  CPL_FIT_CENTROID |
//                                                      CPL_FIT_STDEV |
//                                                      CPL_FIT_AREA,
//                                                  &fitted_centroid,
//                                                  &fitted_sigma,
//                                                  &fitted_area,
//                                                  &offset,      // not fitted here
//                                                  NULL, NULL, NULL));
                err = cpl_vector_fit_gaussian(x, NULL,
                                              y, NULL,
                                              CPL_FIT_CENTROID |
                                                  CPL_FIT_STDEV |
                                                  CPL_FIT_AREA,
                                              &fitted_centroid,
                                              &fitted_sigma,
                                              &fitted_area,
                                              &offset,      // not fitted here
                                              NULL, NULL, NULL);
                if (err == CPL_ERROR_NONE) {
                    // found the reference line and fitted it
                    kmclipm_vector_set(gpos,  ix+iy*nx, fitted_centroid);
                    kmclipm_vector_set(gfwhm, ix+iy*nx, fitted_sigma*CPL_MATH_FWHM_SIG);
                    kmclipm_vector_set(gpeak, ix+iy*nx, fitted_area/(fitted_sigma*CPL_MATH_SQRT2PI));
                } else {
                    // didn't identify the reference line
                    kmclipm_vector_reject(gpos, ix+iy*nx);
                    kmclipm_vector_reject(gfwhm, ix+iy*nx);
                    kmclipm_vector_reject(gpeak, ix+iy*nx);
                    err = CPL_ERROR_NONE;
                }
                cpl_error_reset();// error status must be reset anyway also if err==CPL_ERROR_NONE
            }
        }
        cpl_vector_delete(y); y = NULL;
        KMO_TRY_CHECK_ERROR_STATE();
//kmclipm_vector_dump(gpos);
//kmclipm_vector_dump(gfwhm);
//kmclipm_vector_dump(gpeak);
        // calculate robust means on gpeaks, reject same values on gpos and gfwhm
//cpl_msg_debug(cpl_func,"min before: %g", kmclipm_vector_get_min(gpeak, NULL));
//cpl_msg_debug(cpl_func,"max before: %g", kmclipm_vector_get_max(gpeak, NULL));
        KMO_TRY_EXIT_IF_ERROR(
            kmo_priv_reject_qc(gpeak, &gpeakm, &gpeaks));
        gpmax = gpeakm + 5*gpeaks;
        gpmin = gpeakm - 5*gpeaks;
        if (gpmin < 0.) {
            gpmin = 0.;
        }
        KMO_TRY_CHECK_ERROR_STATE();

        for (i = 0; i < kmclipm_vector_get_size(gpos); i++) {
            int isrej = 0;
            double val = kmclipm_vector_get(gpeak, i, &isrej);
            if (!kmclipm_vector_is_rejected(gpos, i) &&
                (isrej || ((val < gpmin) || (val > gpmax)))
               )
            {
                kmclipm_vector_reject(gpos, i);
            }
        }
        KMO_TRY_CHECK_ERROR_STATE();
int n_rej = kmclipm_vector_count_rejected(gpos);    // sollte 15 sein... ist 1
        for (i = 0; i < kmclipm_vector_get_size(gfwhm); i++) {
            int isrej = 0;
            double val = kmclipm_vector_get(gpeak, i, &isrej);
            if (!kmclipm_vector_is_rejected(gfwhm, i) &&
                (isrej || ((val < gpmin) || (val > gpmax)))
               )
            {
                kmclipm_vector_reject(gfwhm, i);
            }
        }
        KMO_TRY_CHECK_ERROR_STATE();

        KMO_TRY_EXIT_IF_ERROR(
            kmo_priv_reject_qc(gpos, &gposm, &gposs));
n_rej = kmclipm_vector_count_rejected(gpos);    // sollte 15 sein... ist 1
        KMO_TRY_EXIT_IF_ERROR(
            kmo_priv_reject_qc(gfwhm, &gfwhmm, &gfwhms));

        // interpolate gposml out from gposm (gposm: position of mean line pix pos
        // gposml: correspinding lambda value)
        KMO_TRY_EXIT_IF_NULL(
            tmp_vec = kmclipm_vector_extract(lambda, ref_pixpos-9, ref_pixpos+9));
        KMO_TRY_EXIT_IF_NULL(
            vecl = kmclipm_vector_create_non_rejected(tmp_vec));
        kmclipm_vector_delete(tmp_vec); tmp_vec = NULL;

        KMO_TRY_EXIT_IF_NULL(
            bivec_ref = cpl_bivector_wrap_vectors(x, vecl));
        KMO_TRY_EXIT_IF_NULL(
            vgposm = cpl_vector_new(1));
        cpl_vector_set(vgposm, 0, gposm);
        KMO_TRY_EXIT_IF_NULL(
            vgposml = cpl_vector_new(1));
        KMO_TRY_EXIT_IF_NULL(
            bivec_out = cpl_bivector_wrap_vectors(vgposm, vgposml));

        KMO_TRY_EXIT_IF_ERROR(
            cpl_bivector_interpolate_linear(bivec_out, bivec_ref));

        cpl_bivector_unwrap_vectors(bivec_ref);
        cpl_bivector_unwrap_vectors(bivec_out);
        cpl_vector_delete(vgposm); vgposm = NULL;
        gposml = cpl_vector_get(vgposml, 0);
        cpl_vector_delete(x); x = NULL;
        cpl_vector_delete(vecl); vecl = NULL;
        cpl_vector_delete(vgposml); vgposml = NULL;

        // get 95%ile position offset
        KMO_TRY_EXIT_IF_ERROR(
            kmclipm_vector_subtract_scalar(gpos, gposm));
        KMO_TRY_EXIT_IF_ERROR(
            kmclipm_vector_abs(gpos));

        KMO_TRY_EXIT_IF_NULL(
            x = kmclipm_vector_create_non_rejected(gpos));
        KMO_TRY_EXIT_IF_ERROR(
            cpl_vector_sort(x, CPL_SORT_ASCENDING));
        index = (int)ceil(0.95 * cpl_vector_get_size(x));
        if (index != 0) {
            index -= 1;  //vector index is running 0..n-1
        }
        q95 = cpl_vector_get(x, index);
        cpl_vector_delete(x); x = NULL;
        KMO_TRY_CHECK_ERROR_STATE();

        // get 95%ile fwhm
        KMO_TRY_EXIT_IF_NULL(
            x = kmclipm_vector_create_non_rejected(gfwhm));
        KMO_TRY_EXIT_IF_ERROR(
            cpl_vector_sort(x, CPL_SORT_ASCENDING));
        index = (int)ceil(0.95 * cpl_vector_get_size(x));
        if (index != 0) {
            index -= 1;  //vector index is running 0..n-1
        }
        f95 = cpl_vector_get(x, index);
        cpl_vector_delete(x); x = NULL;
        KMO_TRY_CHECK_ERROR_STATE();

        vscale= cdelt / ref_wavelength * CPL_PHYS_C/1000.; // speed of light in [km/s]
cpl_msg_debug(cpl_func,"1 ref_pixpos = %g [km/s]",vscale);
cpl_msg_debug(cpl_func,"number rejected = %d (%g %%)",n_rej, 1.*n_rej/(nx*ny)*100.);
cpl_msg_debug(cpl_func,"Line position mean=%g, stddev=%g [um]",gposml-ref_wavelength, gposs*cdelt);
cpl_msg_debug(cpl_func,"Line position mean=%g, stddev=%g [km/s]",(gposml-ref_wavelength)/ref_wavelength*CPL_PHYS_C/1000., gposs*vscale);
cpl_msg_debug(cpl_func,"95%%ile = %g pixels, %g km/s", q95, q95*vscale);
cpl_msg_debug(cpl_func," ");
cpl_msg_debug(cpl_func,"Line width mean=%g, stddev=%g [km/s]",gfwhmm*vscale, gfwhms*vscale);
cpl_msg_debug(cpl_func,"95%%ile = %g pixels, %g km/s", f95, f95*vscale);

        *pos_mean_offset = (gposml-ref_wavelength)/ref_wavelength*CPL_PHYS_C/1000.;
        *pos_stdev = gposs*vscale;
        *pos_95ile = q95*vscale;
        *fwhm_mean_offset = gfwhmm*vscale;
        *fwhm_stdev = gfwhms*vscale;
        *fwhm_95ile = f95*vscale;
        KMO_TRY_CHECK_ERROR_STATE();
    } KMO_CATCH {
        KMO_CATCH_MSG();
        err = cpl_error_get_code();
        *pos_mean_offset = 0./0.;
        *pos_stdev = 0./0.;
        *pos_95ile = 0./0.;
        *fwhm_mean_offset = 0./0.;
        *fwhm_stdev = 0./0.;
        *fwhm_95ile = 0./0.;
    }

    kmclipm_vector_delete(lambda); lambda = NULL;
    kmclipm_vector_delete(gpos); gpos = NULL;
    kmclipm_vector_delete(gfwhm); gfwhm = NULL;
    kmclipm_vector_delete(gpeak); gpeak = NULL;

    return err;
}

/**
    @brief
        Calculate mean and stdev with rejection for QC parameters.

    @param data     The data values.
    @param stddev   (Output) The standard deviation after rejection.
    @param mean     (Output) The mean after rejection.

    @return
        A possibly shorter vector than @c data . It contains the indices of the
        valid pixels to consider for further calculations.

    Deviant values will be removed using a two-step rejection.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT    if @c data is NULL.
*/
cpl_error_code kmo_priv_reject_qc(const kmclipm_vector *cube,
                                  double *mean,
                                  double *stddev)
{
    cpl_error_code  err                 = CPL_ERROR_NONE;
    cpl_vector      *tmp_vec_sort       = NULL;
    kmclipm_vector  *secf               = NULL,
                    *abssecf            = NULL;
    double          median_val          = 0.0,
                    clip_val            = 0.0;
    int             size                = 0,
                    i                   = 0,
                    index               = 0,
                    nz                  = 0;

    KMO_TRY
    {
        KMO_TRY_ASSURE((cube != NULL) &&
                       (mean != NULL) &&
                       (stddev != NULL),
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        nz = cpl_vector_get_size(cube->data);

        /*
         * 1st rejection iteration (80% clipping)
         */

        /* get absolute values from data-median */
        KMO_TRY_EXIT_IF_NULL(
            secf = kmclipm_vector_duplicate(cube));

        median_val = kmclipm_vector_get_median(secf, KMCLIPM_ARITHMETIC);
        KMO_TRY_CHECK_ERROR_STATE();

        KMO_TRY_EXIT_IF_NULL(
            abssecf = kmclipm_vector_duplicate(secf));

        KMO_TRY_EXIT_IF_ERROR(
            kmclipm_vector_subtract_scalar(abssecf, median_val));
        KMO_TRY_EXIT_IF_ERROR(
            kmclipm_vector_abs(abssecf));

        // get 80% clip value,
        // reject all above 5*clip_val in abssecf
        KMO_TRY_EXIT_IF_NULL(
            tmp_vec_sort = kmclipm_vector_create_non_rejected(abssecf));
        KMO_TRY_EXIT_IF_ERROR(
            cpl_vector_sort(tmp_vec_sort, CPL_SORT_ASCENDING));
        size = cpl_vector_get_size(tmp_vec_sort);
        index = (int)ceil(0.79 * size) -1;
        clip_val = cpl_vector_get(tmp_vec_sort, index);
        cpl_vector_delete(tmp_vec_sort); tmp_vec_sort = NULL;
        KMO_TRY_CHECK_ERROR_STATE();

        for (i = 0; i < nz; i++) {
            if (kmclipm_vector_is_rejected(abssecf, i) ||
                kmclipm_vector_get(abssecf, i, NULL) > clip_val*5)
            {
                kmclipm_vector_reject(secf, i);
            }
        }
        KMO_TRY_CHECK_ERROR_STATE();

        *mean = kmclipm_vector_get_median(secf, KMCLIPM_ARITHMETIC);
        *stddev = kmclipm_vector_get_stdev(secf);
        KMO_TRY_CHECK_ERROR_STATE();

        for (i = 0; i < nz; i++) {
            if (!kmclipm_vector_is_rejected(secf, i))  {
                if (fabs(kmclipm_vector_get(secf, i, NULL)-*mean) > 3*(*stddev))  {
                    kmclipm_vector_reject(secf, i);
                }
            }
        }
        KMO_TRY_CHECK_ERROR_STATE();
//cpl_msg_debug(cpl_func,"min after: %g", kmclipm_vector_get_min(secf, NULL));
//cpl_msg_debug(cpl_func,"max after: %g", kmclipm_vector_get_max(secf, NULL));
//cpl_msg_debug(cpl_func,"rejected: %d", kmclipm_vector_count_rejected(secf));
        *mean = kmclipm_vector_get_mean(secf);
        *stddev = kmclipm_vector_get_stdev(secf);
        KMO_TRY_CHECK_ERROR_STATE();
    }
    KMO_CATCH
    {
        err = cpl_error_get_code();
        *stddev = -1.0;
        *mean = -1.0;
    }

    kmclipm_vector_delete(secf); secf = NULL;
    kmclipm_vector_delete(abssecf); abssecf = NULL;

    return err;
}

/**
    @brief
        Calculates the pixel position of the fitted edge.

    The KMOS_BADPIX_BORDER is respected:
      - minimum edge position can be KMOS_BADPIX_BORDER
      - maximum edge border can be KMOS_DETECTOR_SIZE-KMOS_BADPIX_BORDER-1

    @param  edge_table  The table containing the fitted edge parameters.
                        In the columns are the edge parameters, in the
                        rows are the edges.
    @param  row         The number of the edge to calculate.
    @param  y           The position in y at this edge where the x value
                        should be calculated.

    @return An int value (x) denoting the edge of the slitlet. This pixel is part
            of the edge.

    Possible _cpl_error_code_ set in this function:
    @li CPL_ERROR_NULL_INPUT if @c edge_table is NULL.
 */
double kmo_calc_fitted_slitlet_edge(cpl_table *edge_table, int row, int y)
{
    int     nr_cols     = 0;
    char    *name       = NULL;
    double  x           = 0.;

    KMO_TRY
    {
        // check inputs
        KMO_TRY_ASSURE(edge_table != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "Any of the inputs is NULL!");
        KMO_TRY_ASSURE(((row >= 0) && (row < cpl_table_get_nrow(edge_table))),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "row must >= 0 and smaller than size of table (%d)!", (int)cpl_table_get_nrow(edge_table));
        KMO_TRY_ASSURE(((y >= KMOS_BADPIX_BORDER) && (y < KMOS_DETECTOR_SIZE-KMOS_BADPIX_BORDER+1)),
                       CPL_ERROR_ILLEGAL_INPUT,
                       "y must be >= %d and < %d! (y=%d)", KMOS_BADPIX_BORDER, KMOS_DETECTOR_SIZE-KMOS_BADPIX_BORDER+1, y);

        // subtract one because the first column contains the slitlet IDs
        nr_cols = cpl_table_get_ncol(edge_table)-1;

        int i = 0;
        for (i = 0; i < nr_cols; i++) {
            // construct column name
            KMO_TRY_EXIT_IF_NULL(
                name = cpl_sprintf("A%d", i));

            // x = A0 + A1*y + A2*y*y + A3*y*y*y + ...
            x += pow(y, i) *
                   cpl_table_get_double(edge_table, name, row, NULL);

            cpl_free(name); name = NULL;
        }

        // check min/max value of x (must be from KMOS_BADPIX_BORDER
        // to KMOS_DETECTOR_SIZE-KMOS_BADPIX_BORDER-1 )
        if (x < KMOS_BADPIX_BORDER)  {
            x = KMOS_BADPIX_BORDER;
        }
        if  (x > KMOS_DETECTOR_SIZE-KMOS_BADPIX_BORDER-1) {
            x = KMOS_DETECTOR_SIZE-KMOS_BADPIX_BORDER-1;
        }
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        x = -1;
    }
    return x;
}

/**
    @brief
        Gets the number of pixels above a given threshold.

    @param data            Input Image.
    @param threshold       The threshold level.

    @return
        The number of pixels which are saturated. Returns -1 on error.

    Possible cpl_error_code set in this function:

    @li CPL_ERROR_NULL_INPUT    if @c data is NULL.
    @li CPL_ERROR_ILLEGAL_INPUT if @c threshold is <= zero.
*/
int kmo_image_get_saturated(const cpl_image *data, float threshold)
{
    int         saturated_pixels    = 0,
                i                   = 0,
                j                   = 0,
                nx                  = 0,
                ny                  = 0;

    const float *pdata              = NULL;

    KMO_TRY
    {
        KMO_TRY_ASSURE(data != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "No input data is provided!");

        KMO_TRY_ASSURE(threshold > 0.0,
                       CPL_ERROR_ILLEGAL_INPUT,
                       "threshold must be greater than zero!");

        nx = cpl_image_get_size_x(data);
        ny = cpl_image_get_size_y(data);
        KMO_TRY_CHECK_ERROR_STATE();

        KMO_TRY_EXIT_IF_NULL(
            pdata = cpl_image_get_data_float_const(data));

        /* Loop on the pixels of the data-image */
        for (j = 0; j < ny; j++) {
            for (i = 0; i < nx; i++) {
                if (pdata[i+j*nx] > threshold) {
                    saturated_pixels++;
                }
            }
        }
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        saturated_pixels = -1;
    }

    return saturated_pixels;
}

/**
    @brief
        Checks if grating and filter matches for each detector.

    This function checks if a value at a specified position in @c ifu_lambda_in
    lies inbetween a specified range.

    @param pl           The propertylist to examine.
    @param nr_devices   The number of detectors available (in general 3 for KMOS :-).
    @param check_return The index to the value in @c ifu_lambda_in to examine.
                         Starts at zero.

    @return
        An array of filter-strings for each device (only if @c check_return is
        TRUE), NULL otherwise.

    Possible cpl_error_code set in this function:

    @c CPL_ERROR_NULL_INPUT    Not enough inputs defined
*/
char**  kmo_get_filter_setup(const cpl_propertylist *pl,
                             int nr_devices,
                             int check_return)
{

    const char       *tmp_str1              = NULL,
                     *tmp_str2              = NULL;

    char             *keyword               = NULL,
                     **ret                  = NULL;
    int             i                       = 0;
    KMO_TRY
    {
        KMO_TRY_ASSURE(pl != NULL,
                       CPL_ERROR_NULL_INPUT,
                       "Not all input data is provided!");

        if (check_return == 1) {
            KMO_TRY_EXIT_IF_NULL(
                ret = (char**)cpl_malloc(sizeof(char*)*nr_devices));
        }


        for (i = 0; i < nr_devices; i++) {
            if (check_return == 1) {
                // allocate string to return
                KMO_TRY_EXIT_IF_NULL(
                    ret[i] = (char*)cpl_malloc(sizeof(char)*32));
            }

            // get grating
            KMO_TRY_EXIT_IF_NULL(
                keyword = cpl_sprintf("%s%d%s", IFU_GRATID_PREFIX, 1, IFU_GRATID_POSTFIX));
            tmp_str1 = cpl_propertylist_get_string(pl, keyword);
            cpl_free(keyword); keyword = NULL;

            // get filter
            KMO_TRY_EXIT_IF_NULL(
                keyword = cpl_sprintf("%s%d%s", IFU_FILTID_PREFIX, 1, IFU_FILTID_POSTFIX));
            tmp_str2 = cpl_propertylist_get_string(pl, keyword);
            cpl_free(keyword); keyword = NULL;

            KMO_TRY_ASSURE(strcmp(tmp_str1, tmp_str2) == 0,
                           CPL_ERROR_ILLEGAL_INPUT,
                           "Grating (%s) and filter (%s) for detector %d"
                           "don't match!", tmp_str1, tmp_str2, i+1);

            if (check_return == 1) {
                strcpy(ret[i], tmp_str1);
            }
        }
    }
    KMO_CATCH
    {
        KMO_CATCH_MSG();
        if (check_return == 1) {
            for (i = 0; i < nr_devices; i++) {
                cpl_free(ret[i]); ret[i] = NULL;
            }
            cpl_free(ret); ret = NULL;
        }
        ret = NULL;
    }

    return ret;
}

/** @} */
