/*
 * This file is part of the ESO SINFONI Pipeline
 * Copyright (C) 2004,2005 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, 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA
 */
/*************************************************************************
 * E.S.O. - VLT project
 *
 *
 *
 * who       when      what
 * --------  --------  ----------------------------------------------
 * schreib  13/07/00  created
 */

/************************************************************************
 *   NAME
 *     sinfo_wave_calibration.c -
 *     routines needed for wavelength calibration
 *
 *   SYNOPSIS
 *
 *   1) FitParams ** sinfo_new_fit_params( int n_params )
 *
 *   2) void sinfo_new_destroy_fit_params ( FitParams ** params )
 *
 *   3) void sinfo_new_dump_fit_params_to_ascii(FitParams ** params,
                                               const char * filename )
 *
 *   4) void sinfo_new_dump_ascii_to_fit_params ( FitParams ** params,
                                                 char * filename )
 *
 *   5) int sinfo_new_find_lines(cpl_image * lineImage,
 *                     float    * wave_position,
 *                     float    * wave_intensity,
 *                     int        n_lines,
 *                     int     ** row_clean,
 *                     float   ** wavelength_clean,
 *                     float      beginWave,
 *                     float      dispersion,
 *                     float      mindiff,
 *                     int        halfWidth,
 *                     int      * n_found_lines,
 *                     float      sigma,
 *                     int      * sum_lines )
 *
 *   6) int sinfo_new_read_list( char * listname,
                                float * lineCenter, 
                                float * lineIntensity )
 *
 *
 *   7) int sinfo_new_line_fit ( cpl_image  *  mergedImage,
 *                    FitParams *  par,
 *                    float        fwhm,
 *                    int          lineInd,
 *                    int          column,
 *                    int          halfWidth,
 *                    int          lineRow,
 *                    float        min_amplitude )
 *
 *   8) int sinfo_new_fit_lines ( cpl_image  *  line_image,
 *                     FitParams ** allParams,
 *                     float        fwhm,
 *                     int       *  n_lines,
 *                     int       ** row,
 *                     float     ** wavelength,
 *                     int          width,
 *                     float        min_amplitude )
 *
 *   9) float sinfo_new_polyfit( FitParams ** par,
 *                     int          column,
 *                     int          n_lines,
 *                     int          n_rows,
 *                     float        dispersion,
 *                     float        max_residual,
 *                     float *      acoefs,
 *                     float *      dacoefs,
 *                     int   *      n_reject,
 *                     int          n_fitcoefs )
 *
 *   10) float sinfo_new_coefs_cross_fit ( int      n_columns,
 *                            float *  acoefs,
 *                            float *  dacoefs,
 *                            float *  bcoefs,
 *                            int      n_fitcoefs,
 *                            float    sigma_factor )
 *
 *
 *  11) cpl_image * sinfo_new_wave_map( cpl_image * lineImage,
 *                          float   ** bcoefs,
 *                          int        n_a_fitcoefs,
 *                          int        n_b_fitcoefs,
 *                          float    * wavelength,
 *                          float    * intensity,
 *                          int        n_lines,
 *                          int        magFactor,
 *                          int      * bad_column_mask,
 *                          int        n_bad_columns )
 *
 *  12) int sinfo_new_wavelength_calibration( cpl_image   * image,
 *                                 FitParams ** par ,
 *                                 float     ** bcoefs,
 *                                 float      * wave,
 *                                 int          n_lines,
 *                                 int       ** row_clean,
 *                                 float     ** wavelength_clean,
 *                                 int        * n_found_lines,
 *                                 float        dispersion,
 *                                 int          halfWidth,
 *                                 float        minAmplitude,
 *                                 float        max_residual,
 *                                 float        fwhm,
 *                                 int          n_a_fitcoefs,
 *                                 int          n_b_fitcoefs,
 *                                 float        sigmaFactor )
 *
 *  13) cpl_image * sinfo_new_convolve_image_by_gauss( cpl_image * lineImage,
 *                                       int        hw )
 *
 *  14) cpl_image * sinfo_new_defined_resampling( cpl_image * image,
 *                                    cpl_image * calimage,
 *                                    int        n_params,
 *                                    int        n_rows,
 *                                    double   * dispersion,
 *                                    float    * minval,
 *                                    float    * maxval,
 *                                    double   * centralLambda,
 *                                    int      * centralpix )
 *
 *   DESCRIPTION
 *
 *   1) allocates memory for a new sinfo_vector of
 *      FitParams data structures
 *   2) frees memory of a sinfo_vector of FitParams data structures
 *   3) dumps the fit parameters to an ASCII file
 *   4) dumps ASCII information to an allocated FitParams data structure
 *   5) determines the pixel shift between the line list
 *      and the real image by using the beginning wavelength
 *      on the detector and the dispersion estimate.
 *   6) reads the line data of the calibration lamps
 *   7) fits a sinfo_gaussian to a 1-dimensional slice of an image,
 *      this routine uses the routine sinfo_new_lsqfit_c as a non-linear
 *      least square fit method (Levenberg-Marquardt).
 *   8) calculates and stores the fit parameters of the neon
 *      emission lines of a neon frame by using the sinfo_linefit
 *      routine.
 *   9) fits a second order polynom
 *      lambda[i] = a1 + a2*pos[i] + a3*pos[i]^2
 *      to determine the connection between the listed wave-
 *      length values and the gauss-fitted positions for each
 *      image column using the singular value decomposition
 *      method.
 *  10) Fits the each single parameter of the three fit parameters
 *      acoefs from sinfo_polyfit through the image columns
 *  11) this routine determines a wavelength calibration map
 *      frame associating a wavelength value to each pixel
 *      by using the fit coefficients determined before.
 *  12) this routine takes an image from a calibration
 *      emission lamp and delivers the fit coefficients of
 *      a polynomial fit across the columns
 *      of the coefficients of the polynomial line position
 *      fits as output. Furthermore it delivers an array of the fit parameters
 *      as output. This routine expects Nyquist sampled spectra
 *     (either an interleaved image or an image convolved with an
 *      appropriate function in spectral direction)
 *  13) convolves an emission line image with a sinfo_gaussian
 *      with user given integer half width by using the eclipse
 *      routine sinfo_function1d_filter_lowpass().
 *  14) Given a source image and a corresponding wavelength
 *      calibration file this routine produces an image
 *      in which elements in a given row are associated
 *      with a single wavelength. It thus corrects for
 *      the wavelength shifts between adjacent elements
 *      in the rows of the input image. The output image
 *      is larger in the wavelength domain than the input
 *      image with pixels in each column corresponding to
 *      undefined (blank, ZERO) values. The distribution
 *      of these undefined values varies from column to
 *      column. The input image is resampled at discrete
 *      wavelength intervals using a polynomial interpolation
 *      routine.
 *      The wavelength intervals (dispersion) and the
 *      central wavelength are defined and stable for each
 *      used grating. Thus, each row has a defined wavelength
 *      for each grating. Only the number of rows can be
 *      changed by the user.
 *
 *   FILES
 *
 *   ENVIRONMENT
 *
 *   RETURN VALUES
 *
 *   CAUTIONS
 *
 *   EXAMPLES
 *
 *   SEE ALSO
 *
 *   BUGS
 *
 *------------------------------------------------------------------------
 */

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

/* 
 * System Headers
 */

/* 
 * Local Headers
 */

#include "sinfo_function_1d.h"
#include "sinfo_wave_calibration.h"
#include "sinfo_solve_poly_root.h"
#include "sinfo_recipes.h"
#include "sinfo_globals.h"
#include "sinfo_svd.h"
#include "sinfo_msg.h"


/**@{*/
/**
 * @addtogroup sinfo_rec_wavecal wavelength calibration functions
 *
 * TBD
 */


/**
@brief allocates memory for a new array of FitParams data structures
@name sinfo_new_fit_params()
@param n_params number of spectral lines that will be fitted
@return allocated array of FitParams data structures 
 */

FitParams ** sinfo_new_fit_params( int n_params )
{
    FitParams ** new_params =NULL;
    FitParams  * temp_params =NULL;
    float * temp_fit_mem =NULL;
    float * temp_derv_mem=NULL;
    int i ;


    if ( n_params <= 0 )
    {
        sinfo_msg_error (" wrong number of lines to fit\n") ;
        return NULL ;
    }

    if (NULL==(new_params=(FitParams **) cpl_calloc ( n_params , 
                    sizeof (FitParams*) ) ) )
    {
        sinfo_msg_error (" could not allocate memory\n") ;
        return NULL ;
    }
    if ( NULL == (temp_params = cpl_calloc ( n_params , sizeof (FitParams) ) ) )
    {
        sinfo_msg_error (" could not allocate memory\n") ;
        cpl_free(new_params);
        return NULL ;
    }
    if ( NULL == (temp_fit_mem = (float *) cpl_calloc( n_params*MAXPAR, 
                    sizeof (float) ) ) )
    {
        sinfo_msg_error (" could not allocate memory\n") ;
        cpl_free(temp_params);
        cpl_free(new_params);
        return NULL ;
    }


    if ( NULL == (temp_derv_mem   = 
                    (float *) cpl_calloc( n_params*MAXPAR, sizeof (float) ) ) )
    {
        sinfo_msg_error (" could not allocate memory\n") ;
        cpl_free(temp_params);
        cpl_free(new_params);
        cpl_free(temp_fit_mem);
        return NULL ;
    }

    for ( i = 0 ; i < n_params ; i ++ )
    {
        new_params[i] = temp_params+i;
        new_params[i] -> fit_par    = temp_fit_mem+i*MAXPAR;
        new_params[i] -> derv_par   = temp_derv_mem+i*MAXPAR;
        new_params[i] -> column     = 0 ;
        new_params[i] -> line       = 0 ;
        new_params[i] -> wavelength = 0. ;
        new_params[i] -> n_params   = n_params ;
    }

    return new_params ;
}

/**
@brief frees memory of an array of FitParams data structures
@name sinfo_new_destroy_fit_params()
@param params fit params to destroy
@return nothing
 */

void sinfo_new_destroy_fit_params ( FitParams *** params )
{ 

    if ( *params == NULL )
    {
        return ;
    }

    cpl_free ( (*params)[0] -> fit_par ) ;
    (*params)[0] -> fit_par=NULL;
    cpl_free ( (*params)[0] -> derv_par ) ;
    (*params)[0] -> derv_par=NULL;
    cpl_free ( (*params)[0] ) ;
    (*params)[0]=NULL;
    cpl_free ( (*params) ) ;
    (*params)=NULL;
}




/**
@brief dumps ASCII information to an allocated FitParams data structure
@name dump_ascii_to_fit_params()
@param params allocated dummy for the fit params
@param filename
@return params: filled FitParams object
 */
/* TODO: not used */
void
sinfo_new_dump_ascii_to_fit_params ( FitParams ** params, char * filename )
{
    FILE * fp ;
    int i ;

    if ( NULL == params )
    {
        sinfo_msg_error (" no fit parameters available!\n") ;
        return ;
    }

    if ( NULL == filename )
    {
        sinfo_msg_error (" no filename available!\n") ;
        return ;
    }

    if ( NULL == (fp = fopen ( filename, "r" ) ) )
    {
        sinfo_msg_error(" cannot open %s\n", filename) ;
        return ;
    }

    for ( i = 0 ; i < params[0]->n_params ; i++ )
    {
        fscanf(fp, "%d %d %d %f %f %f %f %f %f %f %f %f\n",
                        &params[i]->n_params,
                        &params[i]->column,
                        &params[i]->line,
                        &params[i]->wavelength,
                        &params[i]->fit_par[0],
                        &params[i]->fit_par[1],
                        &params[i]->fit_par[2],
                        &params[i]->fit_par[3],
                        &params[i]->derv_par[0],
                        &params[i]->derv_par[1],
                        &params[i]->derv_par[2],
                        &params[i]->derv_par[3] ) ;
    }
    fclose(fp) ;
}





/**
@brief dumps the fit parameters to an ASCII file
@name sinfo_new_dump_fit_params_to_ascii()
@param params: fit params to dump
@param filename
@return filled ASCII file 
 */

void sinfo_new_dump_fit_params_to_ascii ( FitParams ** params, const char * filename )
{
    FILE * fp ;
    int    i ;

    if ( NULL == params )
    {
        sinfo_msg_error (" no fit parameters available!\n") ;
        return ;
    }

    if ( NULL == filename )
    {
        sinfo_msg_error (" no filename available!\n") ;
        return ;
    }

    if ( NULL == (fp = fopen ( filename, "w" ) ) )
    {
        sinfo_msg_error(" cannot open %s\n", filename) ;
        return ;
    }

    for ( i = 0 ; i < params[0] -> n_params ; i++ )
    {
        fprintf(fp, "%d %d %d %f %f %f %f %f %f %f %f %f\n", 
                        params[i]->n_params,
                        params[i]->column,
                        params[i]->line,
                        params[i]->wavelength,
                        params[i]->fit_par[0],
                        params[i]->fit_par[1],
                        params[i]->fit_par[2],
                        params[i]->fit_par[3],
                        params[i]->derv_par[0],
                        params[i]->derv_par[1],
                        params[i]->derv_par[2],
                        params[i]->derv_par[3] ) ;
    }
    fclose(fp) ;
}

/**
@brief determines the pixel shift between the line list and the real image
@name sinfo_new_find_lines()
@param lineImage: merged emission line image,
@param wave_position: wavelength list in Angstroems
@param wave_intensity: corresponding intensity list 
@param n_lines: number of lines in list
@param row_clean: resulting list of the row indices but without 
                  the lines that are too close to each other for the fit
@param wavelength_clean: corrected wavelength list corresponding 
                         to the row_clean array
@param beginWave: beginning wavelength on detector in microns 
@param dispersion: dispersion of the grating on the detector
                   (microns per pixel, attention: merged image).
@param mindiff: minimal difference of mean and median column
                intensity to do the correlation. 
                This is done to avoid correlations in columns 
                without emission line intensity.
@param halfWidth:   half width of the box where the line must sit,
@param n_found_lines: number of found and correlated emission lines in a column.
@param sigma: sigma of Gaussian that is convolved with the artificial spectrum
@return 0 if all went o.k.
        # row: resulting list of the row indices of the line positions
        # row_clean: resulting list of the row indices but without the 
                     lines that are too close to each other for the fit
        # wavelength: wavelength from the list corresponding to 
                      the found row positions
        # wavelength_clean: corrected wavelength list corresponding 
                            to the row_clean array
        # n_found_lines: number of found and correlated 
                         emission lines in a column.
        # sum_lines: total sum of found and correlated emission lines
                     -1 if something has gone wrong 
@doc determines the pixel shift between the line list and the 
     real image by using the 
     beginning wavelength on the detector and the dispersion estimate.
 */

int sinfo_new_find_lines(cpl_image * lineImage, 
                         float    * wave_position,
                         float    * wave_intensity,
                         int        n_lines,
                         int     ** row_clean,
                         float   ** wavelength_clean,
                         float      beginWave,
                         float      dispersion1,
                         float      dispersion2,
                         float      mindiff,
                         int        halfWidth,
                         int      * n_found_lines,
                         float      sigma,
                         int      * sum_lines )
{
    int     ** row ;
    float   ** wavelength ;
    float buf1, buf2 ;
    float meanval ;
    float colmedian ;
    float * column, * tempcol ;
    float * lines ;
    float * conv_lines ;
    float * wave_buffer ;
    float * wave_mem;
    int   * dummy_row ;
    int i, j, k, m ;

    int gmax, gmin ;
    int col ;
    int * row_mem;

    float angst ;

    int lx=0;
    int ly=0;
    float* pdata=NULL;

    if ( NULL == lineImage )
    {
        sinfo_msg_error (" no image given\n") ;
        return -1 ;
    }

    lx=cpl_image_get_size_x(lineImage);
    ly=cpl_image_get_size_y(lineImage);
    pdata=cpl_image_get_data_float(lineImage);

    if ( n_lines <= 0 || NULL == wave_position ) 
    {
        sinfo_msg_error(" no line list given\n") ;
        return -1 ;
    }
    if ( NULL == wave_intensity ) 
    {
        sinfo_msg_error(" no intensity list given\n") ;
        return -1 ;
    }

    if ( dispersion1 == 0. )
    {
        sinfo_msg_error(" wrong dispersion given\n") ;
        return -1 ;
    }

    if ( row_clean == NULL )
    {
        sinfo_msg_error(" row_clean array is not allocated\n") ;
        return -1 ;
    }

    if ( wavelength_clean == NULL )
    {
        sinfo_msg_error(" wavelength_clean array is not allocated\n") ;
        return -1 ;
    }

    if ( beginWave <= 0. )
    {
        sinfo_msg_error (" impossible beginWave given\n") ;
        return -1 ;
    }
    if ( mindiff < -100. )
    {
        sinfo_msg_error (" wrong mindiff value\n") ;
        return -1 ;
    }

    if ( halfWidth <= 0 )
    {
        sinfo_msg_error(" wrong half width of fit box given\n") ;
        return -1 ;
    }

    if ( n_found_lines == NULL )
    {
        sinfo_msg_error(" n_found_lines not allocated\n") ;
        return -1 ;
    }

    if ( sigma <= 0. || sigma >= ly / 2)
    {
        sinfo_msg_error(" wrong sigma given\n") ;
        return -1 ;
    }

    /* allocate memory */
    row        = (int**) cpl_calloc( lx, sizeof(int*)) ;
    wavelength = (float**) cpl_calloc( lx, sizeof(float*)) ;
    row_mem = cpl_calloc( n_lines*lx, sizeof(int) ) ;
    wave_mem = cpl_calloc( n_lines*lx, sizeof(float) ) ;
    for ( i = 0 ; i <lx ; i++ )
    {
        row[i] = row_mem + i*n_lines;
        wavelength[i] = wave_mem + i*n_lines;
    }

    /* find if the wavelength is given in microns, nanometers or Angstroem */
    if ( wave_position[0] > 10000. )
    {
        /* Angstroem */
        angst = 10000. ;
    }
    else if ( wave_position[0] > 1000. && wave_position[0] < 10000. )
    {
        /* nanometers */
        angst = 1000. ;
    }
    else
    {
        /* microns */
        angst = 1. ;
    }

    /*----------------------------------------------------------------------
     * compute the mean and median intensity value in the given 
       column and determine if there is enough intensity in the column to 
       do the correlation
     */
    tempcol = (float*) cpl_calloc(ly, sizeof(float)) ;
    *sum_lines = 0 ;
    buf1 = 0. ;
    buf2 = 0. ;
    /* allocate memory */
    column      = (float*) cpl_calloc(ly, sizeof (float)) ;
    lines       = (float*) cpl_calloc(ly, sizeof (float)) ;
    conv_lines  = (float*) cpl_calloc(ly, sizeof (float)) ;
    wave_buffer = (float*) cpl_calloc(ly, sizeof (float)) ;
    dummy_row   = (int*)   cpl_calloc(n_lines, sizeof(int)) ;

    for ( col = 0 ; col < lx ; col++ )
    {
        n_found_lines[col] = 0 ;
        float sum = 0. ;
        for ( i = 0 ; i < ly ; i++ )
        {
            if (isnan(pdata[col + i*lx]) )
            {
                tempcol[i] = 0. ;
                continue ;
            }

            sum = sum + pdata[col + i*lx] ;
            tempcol[i] = pdata[col + i*lx];
        }
        meanval = sum / ly ;
        /* lets assume the sinfo_new_median is the background */
        colmedian =  sinfo_new_median ( tempcol, ly);

        if ( meanval - colmedian < mindiff )
        {
            sinfo_msg_warning(" sorry, not enough intensity "
                            "(mean: %f, diff: %f) in image column: "
                            "%d to correlate emission lines\n",
                            meanval, meanval - colmedian,col) ;
            continue ;
        }

        for ( i = 0 ; i < ly ; i++ )
        {
            conv_lines[i]=0;
            wave_buffer[i]=0;
        }
        for ( i = 0 ; i < n_lines ; i++ )
        {
            dummy_row[i] = 0;
        }

        /* go through the column with index col */
        for ( i = 0 ; i < ly ; i++ )
        {
            if ( isnan(pdata[col+i*lx]) )
            {
                column[i] = 0. ;
            }
            else
            {
                column[i] = pdata[col + i*lx] ;
            }

            /* determine the line position on the pixels */
            /*lines[i] = (dispersion * (float) i + beginWave) * angst ;*/ 
            lines[i] = (dispersion1 * (float) (i-ly/2) + 
                            dispersion2 * (float) (i-ly/2) *
                            (float) (i-ly/2) +
                            beginWave) * angst ;
            /*
            sinfo_msg("wo=%g d1=%g d2=%g angst=%g lines[i]=%g",
                      beginWave,dispersion1,dispersion2,angst,lines[i]);
                      */
            /* ---------------------------------------------------------------
             * find the nearest line position for each wavelength in the list 
             * and set this position to the given line intensity as weight 
             */
            /*
            sinfo_msg("dispersion1 =%g dispersion2=%g dispersion0=%g ly/2=%d",
                      dispersion1,dispersion2,beginWave,ly/2);
            */
            for ( j = 0 ; j < n_lines ; j ++ )
            {
                buf1 = 0. ;
                buf2 = 0. ;
                if ( (wave_position[j] >= (lines[i] - 
                                fabs(dispersion1)/2.*angst)) &&
                                (wave_position[j] <= (lines[i] +
                                                fabs(dispersion1)/2.*angst)) )
                {
                    buf1 = wave_intensity[j] ; /* set the given line intensity 
                                                  as weight */
                    buf2 = wave_position[j] ;
                    //sinfo_msg("line posx=%d posy=%d wave=%g",col,i,buf2);
                    break ;
                }
            }

            lines[i] = buf1 ;

            wave_buffer[i] = buf2 ; /* get the wavelength associated 
                                       with the corresponding 
                                           found emission line */

            /* convolve the artificial spectrum by a Gaussian 
               with given sigma value */
            if ( lines[i] != 0. )
            {
                /* consider only +- 2 sigma */
                gmin = sinfo_new_nint((float) i - 2. * sigma) ;
                gmax = sinfo_new_nint((float) i + 2. * sigma) ;

                /* be aware of image margins */
                if ( gmin < 0 )
                {
                    gmin = 0 ;
                }
                if ( gmax >= ly )
                {
                    gmax = ly - 1 ;
                } 
                for ( j = gmin ; j <= gmax ; j++ )
                {
                    conv_lines[j] += 
                                    lines[i] * exp( (double)( -0.5*((j - i)*(j - i)))/
                                                    (double) (sigma*sigma) ) ;
                }
            }
        }

        /* do the cross sinfo_new_correlitioation */
        //int position = INT32_MAX ;
        int position = sinfo_new_correlation(column+5, conv_lines+5, ly-10 ) ;
        if ( abs (position) > ly / 4 )
        {
            sinfo_msg_warning(" sorry, shift of artificial data relative to"
                            " image (%d) seems to be too high in column: %d",
                            position, col) ;
            continue ;
        }

        //AMO we initialize this to -999 and later check that it is not
        //-999 to prevent an invalid read out due to the fact that not
        //all elements of row are filled by the the loop below
        for ( j = 0 ; j < n_lines ; j ++ ) {
            row[col][j] = -999;
        }

        //The following loop does not fill all elements
        j = 0 ;
        for ( i = 0 ; i < ly ; i ++ )
        {
            if ( lines[i] != 0.0 )
            {
                if ( (i - position) >= 0 && (i - position) < ly )
                {
                    row[col][j] = i - position ;
                    /* get the wavelength corresponding to 
                       found line row index */
                    wavelength[col][j] = wave_buffer[i] / angst ;
                    j++ ;
                }
            }
        }


        /* ------------------------------------------------------------------
         *  determine the row_clean array, that means, take only the row 
            values if the distance between adjacent lines is large enough 
            for the fit
         */
        //sinfo_msg("lx=%d",lx);
        for ( k = 1 ; k <= j && k<(lx-1); k ++ )
        {
            if (dummy_row[k-1] != -1)
            {
                dummy_row[k-1] = row[col][k-1] ;
            }
            if ( (row[col][k] - row[col][k-1]) <= 2*halfWidth )
            {
                dummy_row[k-1] = -1 ;
                if (k<n_lines) {
                    dummy_row[k]   = -1 ;
                }
            }
            /* the following gives invalid read size 4: check that k+1<lx */

            //sinfo_msg("col=%d k=%d row1=%d row2=%d",
            //           col,k,row[col][k+1],row[col][k]);
            if ( (row[col][j] != -999) && 
                            (row[col][k+1] - row[col][k]) <= 2*halfWidth)
            {
                if (k<n_lines) {
                    dummy_row[k]   = -1 ;
                }
                if (k+1<n_lines) {
                    dummy_row[k+1] = -1 ;
                }
            }
        }

        m = 0 ;
        for ( k = 0 ; k < j ; k ++ )
        {
            if ( dummy_row[k] != -1 && dummy_row[k] != 0 )
            {
                row_clean[col][m] = dummy_row[k] ;
                wavelength_clean[col][m] = wavelength[col][k] ;
                m ++ ;
            }
        }

        n_found_lines[col] = m ;

        *sum_lines += n_found_lines[col] ;
    }
    cpl_free (column) ;
    cpl_free (lines) ;
    cpl_free (conv_lines) ;
    cpl_free (dummy_row) ;
    cpl_free (wave_buffer) ;
    cpl_free (row_mem) ;
    cpl_free (wave_mem) ;
    cpl_free (tempcol) ;
    cpl_free (row) ;
    cpl_free (wavelength) ;

    return 0 ;
}

/**
@brief fits a Gaussian to a 1-dimensional slice of an image
@name sinfo_new_line_fit()
@param mergedImage: image of a calibration emission lamp,
@param par:         dummys for the resulting fitting parameters,
@param fwhm:        guess value for full width of half maximum of Gaussian
@param lineInd:     index of the emission line,
@param column:      present index of the image column,
@param halfWidth:   half width of the box where the line must sit,
@param lineRow:     row index where the line is expected,
@param min_amplitude: minimum line amplitude with respect to the 
                      background to do the fit
@return the fitting parameter data structure containing the resulting 
        parameters.
        # integers: number of iterations if all was ok,
        #  -8   if no input image was given,
        #  -9   if no dummy for the fit parameters is given,
        # -10  if the wrong column index was given,
        # -11  if the wrong box width was given,
        # -12  if the wrong row index was given,
        # -13  if a wrong minimum amplitude factor was given
        # -14  if the spectral sinfo_vector data structure memory 
               could not be allocated          
        # -15  wrong row index or box width was given,
        # -16  signal too low to fit,         
        # -17  least squares fit failed
@doc fits a Gaussian to a 1-dimensional slice of an image, this routine 
     uses the routine sinfo_new_lsqfit_c as a non-linear least square fit 
     method (Levenberg-Marquardt).               
 */

int sinfo_new_line_fit ( cpl_image  *  mergedImage, 
                         FitParams *  par,
                         float        fwhm,
                         int          lineInd,
                         int          column,
                         int          halfWidth,
                         int          lineRow,
                         float        min_amplitude,
                         Vector    *  line,
                         int       *  mpar,
                         float     *  xdat,
                         float     *  wdat )
{
    int i, j ;
    int iters, xdim, ndat ;
    int numpar, its ;
    int position ;
    float maxval, tol, lab ;
    int lx=0;
    int ly=0;
    float* pdata=NULL;

    if ( mergedImage == NULL )
    {
        sinfo_msg_error (" no image given as input\n") ;
        return -8 ;
    }
    lx=cpl_image_get_size_x(mergedImage);
    ly=cpl_image_get_size_y(mergedImage);
    pdata=cpl_image_get_data_float(mergedImage);


    if ( par == NULL )
    {
        sinfo_msg_error(" fit parameters not given\n") ;
        return -9 ;
    }
    if ( column < 0 || column > lx )
    {
        sinfo_msg_error (" wrong column number\n") ;
        return -10 ;
    }
    if ( halfWidth < 0 || halfWidth > ly )
    {
        sinfo_msg_error (" wrong width given\n") ;
        return -11 ;
    }
    if ( lineRow < 0 || lineRow > ly )
    {
        sinfo_msg_error (" wrong number of row of the line given\n") ;
        return -12 ;
    }
    if ( min_amplitude < 1. )
    {
        sinfo_msg_error (" wrong minimum amplitude\n") ;
        return -13 ;
    }

    /* initialise the Vector */
    for ( i = 0 ; i < line -> n_elements ; i++) 
    {
        line->data[i] = 0;
    }

    par -> column = column  ;
    par -> line   = lineInd ;

    /* determine the values of the spectral sinfo_vector given as input */
    /* go through the chosen column */

    j = 0 ;
    for ( i = lineRow-halfWidth ; i <= lineRow+halfWidth ; i++ ) 
    {
        if ( i < 0 || i >= ly )
        {
            sinfo_msg_error (" wrong line position or width given\n") ;
            return -15 ;
        }
        else
        {
            line -> data[j] = pdata[column + i*lx] ;
            j ++ ;
        }
    } 

    /*-------------------------------------------------------------------- 
     * go through the spectral sinfo_vector 
     * determine the maximum pixel value in the spectral sinfo_vector 
     */
    maxval = -FLT_MAX ;
    position = -INT32_MAX ;
    for ( i = 0 ; i < line -> n_elements ; i++ )
    {
        xdat[i] = i ;
        wdat[i] = 1.0 ;
        if ( line -> data[i] >= maxval )
        {
            maxval = line -> data[i] ;
            position = i ;
        }
    }

    /* set initial values for the fitting routine */
    xdim     = XDIM ;
    ndat     = line -> n_elements ;
    numpar   = MAXPAR ;
    tol      = TOL ;
    lab      = LAB ;
    its      = ITS ;
    par -> fit_par[1] = fwhm ;
    par -> fit_par[2] = (float) position ;
    par -> fit_par[3] = (float) (line -> data[0] + 
                    line -> data[line->n_elements - 1]) / 2.0 ;
    par -> fit_par[0]  = maxval - (par -> fit_par[3]) ;

    /* exclude low signal cases */
    if ( par->fit_par[0] < min_amplitude )
    {
        cpl_msg_debug ("sinfo_linefit:",
                        " sorry, amplitude of line too low to fit: %f",
                        par->fit_par[0] ) ;
        return -16 ;
    }

    for ( i = 0 ; i < MAXPAR ; i++ )
    {
        par -> derv_par[i] = 0.0 ;
        mpar[i] = 1 ;
    }

    /* finally, do the least square fit using a sinfo_gaussian */
    if ( 0 > ( iters = sinfo_new_lsqfit_c( xdat, &xdim, 
                    line -> data, wdat,
                    &ndat, par -> fit_par,
                    par -> derv_par, mpar,
                    &numpar, &tol, &its, &lab )) )
    {
        cpl_msg_debug ("sinfo_linefit:",
                        " sinfo_new_lsqfit_c: least squares fit failed,"
                        " error no.: %d\n", iters) ;
        return -17 ;
    }

    /* correct the fitted position for the given row of the 
       line in image coordinates */
    par -> fit_par[2] =  (float) (lineRow - halfWidth) + par -> fit_par[2] ;

    /* all was o.k. */
    return iters ;
}

/**
@brief calculates and stores the fit parameters of the neon emission 
       lines of a neon frame by using the linefit routine.
@name sinfo_new_fit_lines
@param line_image: merged image of a calibration lamp ,
@param allParams:  allocated sinfo_vector of FitParams data structures,
@param fwhm:       guess value for full width of half maximum of Gaussian
@param n_lines:    number of neon lines that will be fitted in one column ,
@param row:        list of the rows of the fitted lines
@param wavelength: list of wavelength corresponding to the found line rows
@param width:      half width of a box around the found rows within the line 
                   is fitted
@param min_amplitude: minimum line amplitude with respect to the background 
                   to do the fit
@return filled FitParams data structure sinfo_vector, number of successfully 
                  fitted lines,
      # errors: negative integers resulting from the linefit routine and:
      # -18: no image given,
      # -19: number of emission lines or number of slitlets is wrong,
      # -20: sinfo_vector of the slitlet boundaries or of the line rows 
             or of the half width are empty.
      # -21: no wavelength array given.
 */

int sinfo_new_fit_lines ( cpl_image  *  line_image, 
                          FitParams ** allParams,
                          float        fwhm,
                          int       *  n_lines,
                          int       ** row,
                          float     ** wavelength,
                          int          width,
                          float        min_amplitude )
{
    int i, k, l ;
    int result ;
    Vector * line;
    int    * mpar;
    float  * xdat, * wdat;
    int lx=0;
    /* int ly=0; */
    /* float* pdata=NULL; */

    if ( line_image == NULL )
    {
        sinfo_msg_error (" no image given\n") ;
        return -18 ;
    }
    lx=cpl_image_get_size_x(line_image);
    /* ly=cpl_image_get_size_y(line_image); */
    /* pdata=cpl_image_get_data_float(line_image); */

    if ( n_lines == NULL )
    {
        sinfo_msg_error (" no counter of emission lines\n") ;
        return -19 ;
    } 
    if ( row == NULL || width <= 0 )
    {
        sinfo_msg_error (" row or width vectors are empty\n") ;
        return -20 ;
    }
    if ( wavelength == NULL )
    {
        sinfo_msg_error (" no wavelength array given\n") ;
        return -21 ;
    }

    k = 0 ;

    /* allocate memory for the spectral sinfo_vector */
    line = sinfo_new_vector (2*width + 1) ;
    /* allocate memory */
    xdat = (float *) cpl_calloc( line -> n_elements, sizeof (float) ) ;
    wdat = (float *) cpl_calloc( line -> n_elements, sizeof (float) ) ;
    mpar = (int *)   cpl_calloc( MAXPAR, sizeof (int) ) ;

    /* go through the columns */
    for ( i = 0 ; i < lx ; i++ )
    {
        if ( n_lines[i] == 0 )
        {
            continue ;
        }
        /* go through the emission lines in a column */
        for ( l = 0 ; l < n_lines[i] ; l++ )
        {
            if ( row[i][l] <= 0 )
            {
                continue ;
            }

            /* --------------------------------------------------------------
             * fit the single lines using sinfo_linefit and store the 
               parameters in
             * an array of the FitParams data structure allParams[].
             */ 
            if ( (result = sinfo_new_line_fit ( line_image, 
                            allParams[k], fwhm, l, i,
                            width, row[i][l],
                            min_amplitude,line,mpar,
                            xdat,wdat ) ) < 0 )
            {
                cpl_msg_debug ("sinfo_fitLines:",
                                " sinfo_linefit failed, error no.: %d,"
                                " column: %d, row: %d, line: %d\n",
                                result, i, row[i][l], l) ;
                continue ;
            }
            if ( (allParams[k] -> fit_par[0] <= 0.) || 
                            (allParams[k] -> fit_par[1] <= 0.)
                            || (allParams[k] -> fit_par[2] <= 0.) )
            {
                sinfo_msg_warning (" negative fit parameters in column: %d,"
                                " line: %d\n", i, l) ;
                sinfo_msg_warning("f0=%g f1=%g f2=%g",
                                   allParams[k] -> fit_par[0],allParams[k] -> fit_par[1],allParams[k] -> fit_par[2]);
                continue ;
            }
            allParams[k] -> wavelength = wavelength[i][l] ;
            k++ ;
        }
    }

    /* free memory */
    sinfo_new_destroy_vector(line);
    cpl_free(xdat);
    cpl_free(wdat);
    cpl_free(mpar);

    /* all is o.k. */
    return k ;
}    

/**
@brief fits a polynomial to determine the connection between the 
       listed wavelength values and the gauss-fitted positions for 
       each image column using the singular value decomposition method.
@name sinfo_new_polyfit()
@param par:          filled array of fit parameter structure
@param column:       image column index
@param n_lines:      number of found lines in column
@param n_rows:       number of image rows
@param dispersion:   microns per pixel
@param max_residual: maximum residual value, beyond that value the fit is 
                     rejected.
@param acoefs:       array of the 3 coefficients of the fitted parabola
@param dacoefs:      array of standard deviations of the 3 coefficients
@param n_reject:     rejected number of fits due to high residuals
@param n_fitcoefs:   number of polynomial coefficients to fit
@return # chisq, the three fit coefficients acoefs[i] and their standard 
                     deviations dacoefs[i], 
               the rejected number of fits due to too high residuals: n_reject
        # FLT_MAX in case of error
@doc fits a polynomial
     lambda[i] = a1 + a2*pos[i] + a3*pos[i]^2 +...
     to determine the connection between the listed wavelength values 
     and the gauss-fitted positions for each image column using the 
     singular value decomposition method.
 */

float sinfo_new_polyfit( FitParams ** par,
                         int          column,
                         int          n_lines,
                         int          n_rows,
                         float        dispersion,
                         float        max_residual,
                         float *      acoefs,
                         float *      dacoefs,
                         int   *      n_reject,
                         int          n_fitcoefs )
{
    float ** ucoefs, ** vcoefs, ** covar ;
    float *mem;
    float * lambda, * posit ;
    float * weight, * resid ;
    float * newlam, * newpos, * newwet ;
    float * wcoefs=NULL ;
    float chisq;
    float offset ;
    int num;
    int i, j, k, n ;

    /* reset the fit coefficients and their errors */
    for ( i = 0 ; i < n_fitcoefs ; i++ )
    {
        acoefs[i]  = 0. ;
        dacoefs[i] = 0. ;
    }
    if ( NULL == par )
    {
        sinfo_msg_error(" no fit params given\n");
        return FLT_MAX ;
    }

    if ( 0 >= n_lines )
    {
        /*
        sinfo_msg_warning (" sorry, number of lines is wrong") ;
         */
        return FLT_MAX ;
    }
    if ( 0 >= n_rows )
    {
        sinfo_msg_error (" sorry, number of rows is wrong") ;
        return FLT_MAX ;
    }
    if ( dispersion == 0. )
    {
        sinfo_msg_error (" sorry, wrong dispersion given") ;
        return FLT_MAX ;
    }

    offset = (float)(n_rows - 1)/2. ;

    /* allocate memory */

    mem = (float*) cpl_calloc( n_lines*7, sizeof (float) ) ;
    lambda = mem;
    posit  = mem + n_lines;
    weight = mem + n_lines*2;
    resid  = mem + n_lines*3;
    newlam = mem + n_lines*4;
    newpos = mem + n_lines*5;
    newwet = mem + n_lines*6;

    /*lambda = (float*) cpl_calloc( n_lines, sizeof (float) ) ;
    posit  = (float*) cpl_calloc( n_lines, sizeof (float) ) ;
    weight = (float*) cpl_calloc( n_lines, sizeof (float) ) ;
    resid  = (float*) cpl_calloc( n_lines, sizeof (float) ) ;
    newlam = (float*) cpl_calloc( n_lines, sizeof (float) ) ;
    newpos = (float*) cpl_calloc( n_lines, sizeof (float) ) ;
    newwet = (float*) cpl_calloc( n_lines, sizeof (float) ) ;*/

    /* allocate coefficient matrices*/
    ucoefs = sinfo_matrix ( 1, n_lines, 1, n_fitcoefs ) ;
    vcoefs = sinfo_matrix ( 1, n_lines, 1, n_fitcoefs ) ;
    covar  = sinfo_matrix ( 1, n_fitcoefs, 1, n_fitcoefs ) ;
    wcoefs=cpl_calloc(n_fitcoefs,sizeof(float)) ;

    /* go through all fit parameters */
    n = 0 ;
    for ( i = 0 ; i < (par[0] -> n_params) ; i ++ )
    {
        int found = -1 ;
        /* find the given column and go through the lines in that column */
        for ( j = 0 ; j < n_lines ; j ++ )
        {
            if ( (par[i] -> column == column) && (par[i] -> line == j) )
            {
                found = i ;
            }
            else
            {
                continue ;
            }

            /* store only fit params with reasonable values */
            if ( par[found] -> derv_par[2] != 0. && 
                            par[found] -> fit_par[2] > 0. &&
                            par[found] -> wavelength > 0. &&
                            par[found] -> fit_par[1] > 0. &&
                            par[found] -> fit_par[0] > 0. )
            {
                /* ----------------------------------------------------------
                 * store the found position, error of the position as 
                   weight and the associated
                 * wavelength values of the fitted lines
                 */
                posit[n]  = par[found] -> fit_par[2] ;
                weight[n] = par[found] -> derv_par[2] ;
                lambda[n] = par[found] -> wavelength ;
                n ++ ;
            }
            else
            {
                continue ;
            }
        }

    }

    num = n ;
    if ( num < n_fitcoefs )
    {
        sinfo_msg_warning("not enough lines found in column %d to "
                        "determine the three coefficients.\n", column) ;
        for ( i = 0 ; i < n_fitcoefs ; i++ )
        {
            acoefs[i]  = ZERO ;
            dacoefs[i] = ZERO ;
        }
        sinfo_free_matrix ( ucoefs, 1/*, n_lines*/,    1/*, n_fitcoefs*/ ) ;
        sinfo_free_matrix ( vcoefs, 1/*, n_lines*/,    1/*, n_fitcoefs*/ ) ;
        sinfo_free_matrix ( covar,  1/*, n_fitcoefs*/, 1/*, n_fitcoefs*/ ) ;
        /*cpl_free (lambda) ;
        cpl_free (posit) ;
        cpl_free (weight) ;
        cpl_free (resid) ;
        cpl_free (newlam) ;
        cpl_free (newpos) ;
        cpl_free (newwet) ;*/
        cpl_free (mem);
        cpl_free(wcoefs) ;
        return FLT_MAX ;
    }

    /*-------------------------------------------------------------------------
     * scale the pixel position values to smaller than 1 and transform 
       the weights to wavelength units 
     */

    for ( i = 0 ; i < num ; i ++ )
    {
        posit[i] = (posit[i] - offset)/offset ;
        weight[i] *= fabs(dispersion) ;
    }

    /* do the fit using the singular value decomposition method */
    sinfo_svd_fitting( posit - 1, lambda - 1, 
                       weight - 1, num, acoefs-1, n_fitcoefs,
                       ucoefs, vcoefs, wcoefs-1, covar, &chisq, sinfo_fpol ) ;

    /* scale the linear and the quadratic coefficient */
    for ( i = 1 ; i < n_fitcoefs ; i++ )
    {
        acoefs[i] /= pow(offset, i) ;
    }

    /* now that we have determined the fit coefficients, find the residuals */
    *n_reject = 0 ;

    j = 0 ;
    for ( i = 0 ; i < num ; i++ )
    {
        float result = 0. ;
        for ( k = 0 ; k < n_fitcoefs ; k++ )
        {
            result += acoefs[k] * pow(posit[i], k) ;
        }

        resid[i] = lambda[i] - result ;

        if ( fabs( resid[i] ) > max_residual)
        {
            (*n_reject) ++ ;
        }
        else
        {
            newlam[j] = lambda[i] ;
            newpos[j] = posit[i] ;
            newwet[j] = weight[i] ;
            j++ ;
        }
    }

    num = j ;
    if ( num >= n_fitcoefs )
    {
        sinfo_svd_fitting( newpos - 1, newlam - 1, 
                        newwet - 1, num, acoefs-1, n_fitcoefs, ucoefs,
                        vcoefs, wcoefs-1, covar, &chisq, sinfo_fpol ) ;

        /* scale the resulting coefficients */
        for ( i = 0 ; i < n_fitcoefs ; i++ )
        {
            acoefs[i] /= pow(offset, i) ;
            dacoefs[i] = sqrt( (double) covar[i+1][i+1] ) / pow(offset, i) ;
        }
    }
    else
    {
        sinfo_msg_warning (" too many lines rejected (number: %d) "
                        "due to high residuals, fit coefficients are set "
                        "zero, in column: %d\n", *n_reject, column) ;
        for ( i = 0 ; i < n_fitcoefs ; i++ )
        {
            acoefs[i]  = ZERO ;
            dacoefs[i] = ZERO ;
        }
    }

    sinfo_free_matrix ( ucoefs, 1/*, n_lines*/,    1/*, n_fitcoefs*/ ) ;
    sinfo_free_matrix ( vcoefs, 1/*, n_lines*/,    1/*, n_fitcoefs*/ ) ;
    sinfo_free_matrix ( covar,  1/*, n_fitcoefs*/, 1/*, n_fitcoefs*/ ) ;
    /*cpl_free (lambda) ;
    cpl_free (posit) ;
    cpl_free (weight) ;
    cpl_free (resid) ;
    cpl_free (newlam) ;
    cpl_free (newpos) ;
    cpl_free (newwet) ;*/
    cpl_free (mem);
    cpl_free(wcoefs) ;

    return chisq ;
}

/**
@brief Fits each single polnomial coefficient acoefs resulting 
       from sinfo_polyfit across the image columns
@name sinfo_new_coefs_cross_fit()
@param n_columns:    number of image columns
@param acoefs:       coeffs fitted in sinfo_polyfit
@param note: this is a sinfo_vector of coefficients with the same 
             index for all columns
@param dacoefs:      fit errors of the corresponding acoefs
@param bcoefs:       the fitted coefs
@param n_fitcoefs:   number of fit coefficients
@param sigma_factor: factor of sigma beyond which the column 
                     coefficients are discarded for the fit
@return chisq, the found fit coefficients 
 */

float sinfo_new_coefs_cross_fit ( int      n_columns,
                                  float *  acoefs,
                                  float *  dacoefs,
                                  float *  bcoefs,
                                  int      n_fitcoefs,
                                  float    sigma_factor )
{

    float* sub_col_index=NULL ;
    float* sub_acoefs=NULL ;
    float* sub_dacoefs=NULL ;
    float* wcoefs=NULL ;
    float ** ucoefs, **vcoefs, **covar ;
    float chisq ;
    float * acoefsclean ;
    double sum, sumq, mean ;
    double sigma ;
    double cliphi, cliplo ;
    float offset ;
    int i, n, num, ndata ;
    int nc ;


    if ( n_columns < 1 )
    {
        sinfo_msg_error(" wrong number of image columns given\n") ;
        return FLT_MAX ;
    }
    if ( acoefs == NULL || dacoefs == NULL )
    {
        sinfo_msg_error(" coeffs or errors of coefficients are not given\n") ;
        return FLT_MAX ;
    }
    if ( bcoefs == NULL )
    {
        sinfo_msg_error(" coeffs are not allocated\n") ;
        return FLT_MAX ;
    }

    if ( n_fitcoefs < 1 )
    {
        sinfo_msg_error(" wrong number of fit coefficients\n") ;
        return FLT_MAX ;
    }
    if ( sigma_factor <= 0. )
    {
        sinfo_msg_error(" impossible sigma_factor given!\n") ;
        return FLT_MAX ;
    }

    offset = (float)(n_columns - 1) / 2. ;

    /* ----------------------------------------------------------
     * determine the clean mean and sigma value of the coefficients,
     * that means reject 10 % of the extreme low and high values
     */

    wcoefs=cpl_calloc(n_fitcoefs,sizeof(float)) ;

    nc = 0 ;
    for ( i = 0 ; i < n_columns ; i++ )
    {
        if ( isnan(acoefs[i]) || acoefs[i] == 0. || dacoefs[i] == 0. )
        {
            continue ;
        }
        else
        {
            nc++ ;
        }
    }
    acoefsclean = (float*) cpl_calloc(nc , sizeof(float)) ;
    nc = 0 ;
    for ( i = 0 ; i < n_columns ; i++ )
    {
        if ( isnan(acoefs[i]) || acoefs[i] == 0. || dacoefs[i] == 0. )
        {
            continue ;
        }
        else
        {
            acoefsclean[nc] = acoefs[i] ;
            nc++ ;
        }
    }
    sinfo_pixel_qsort(acoefsclean, nc) ;
    sum   = 0. ;
    sumq  = 0. ;
    mean  = 0. ;
    sigma = 0. ;
    n     = 0 ;
    for ( i = (int)((float)nc*LOW_REJECT) ; 
                    i < (int)((float)nc*HIGH_REJECT) ; i++ )
    {
        sum  += (double)acoefsclean[i] ;
        sumq += ((double)acoefsclean[i] * (double)acoefsclean[i]) ;
        n ++ ;
    }
    mean          = sum/(double)n ;
    sigma         = sqrt( sumq/(double)n - (mean * mean) ) ;
    cliphi        = mean + sigma * (double)sigma_factor ;
    cliplo        = mean - sigma * (double)sigma_factor ;

    sub_col_index=cpl_calloc(n_columns,sizeof(float)) ;
    sub_acoefs=cpl_calloc(n_columns,sizeof(float));
    sub_dacoefs=cpl_calloc(n_columns,sizeof(float)) ;

    /* fit only the reasonnable values */
    num = 0 ;
    for ( i = 0 ; i < n_columns ; i++ )
    {
        /* associate the column indices to the corresponding array */
        float col_index = (float) i ;

        /* take only the reasonnable coefficients */
        if ( !isnan(acoefs[i]) && 
                        (acoefs[i] <= cliphi) && (acoefs[i] >= cliplo) &&
                        (dacoefs[i] != 0. ) && (acoefs[i] != 0.) )
        {
            sub_acoefs[num]    = acoefs[i] ;
            sub_dacoefs[num]   = dacoefs[i] ;
            sub_col_index[num] = col_index ;
            num ++ ;
        }
    }
    ndata = num ;

    if ( ndata < n_fitcoefs )
    {
        sinfo_msg_error("not enough data found to determine "
                        "the fit coefficients.\n") ;
        cpl_free(wcoefs) ;
        cpl_free(sub_dacoefs) ;
        cpl_free(sub_col_index) ;
        cpl_free(sub_acoefs) ;
        return FLT_MAX ;
    }

    /* allocate coefficient matrices */
    ucoefs = sinfo_matrix(1, ndata, 1, n_fitcoefs) ;
    vcoefs = sinfo_matrix(1, ndata, 1, n_fitcoefs) ;
    covar  = sinfo_matrix ( 1, n_fitcoefs, 1, n_fitcoefs ) ;

    /* scale the x-values for the fit */
    for ( i = 0 ; i < ndata ; i++ )
    {
        sub_col_index[i] = (sub_col_index[i] - offset) / offset ;
    }

    /* finally, do the singular value decomposition fit */
    sinfo_svd_fitting ( sub_col_index-1, sub_acoefs-1, 
                        sub_dacoefs-1, ndata, bcoefs-1,
                        n_fitcoefs, ucoefs, vcoefs, 
                        wcoefs-1, covar, &chisq, sinfo_fpol ) ;

    /* scale the found coefficients */
    for ( i = 0 ; i < n_fitcoefs ; i ++ )
    {
        bcoefs[i] /= pow(offset, i) ;
    }

    /* free memory */
    cpl_free (acoefsclean) ;
    sinfo_free_matrix( ucoefs, 1/*, ndata*/, 1/*, n_fitcoefs */) ;
    sinfo_free_matrix( vcoefs, 1/*, ndata*/, 1/*, n_fitcoefs */) ;
    sinfo_free_matrix ( covar, 1/*, n_fitcoefs*/, 1/*, n_fitcoefs*/ ) ;

    cpl_free(sub_col_index) ;
    cpl_free(sub_acoefs) ;
    cpl_free(sub_dacoefs) ;
    cpl_free(wcoefs) ;

    return chisq ;
}


/**
@brief
@name sinfo_new_convolve_image_by_gauss()
@param lineImage:  emission line image  
@param hw:         kernel half width of the sinfo_gaussian response function
@return emission line image convolved with a Gaussian
@doc convolves an emission line image with a Gaussian with user given 
       integer half width by 
     using the eclipse routine sinfo_function1d_filter_lowpass().
 */

cpl_image * sinfo_new_convolve_image_by_gauss( cpl_image * lineImage,
                                               int        hw )
{
    cpl_image * returnImage ;
    float* column_buffer=NULL ;
    float * filter ;
    int col, row ;
    int ilx=0;
    int ily=0;
    /*
    int olx=0;
    int oly=0;
     */
    float* pidata=NULL;
    float* podata=NULL;

    if ( lineImage == NULL )
    {
        sinfo_msg_error(" no input image given!\n") ;
        return NULL ;
    }
    ilx=cpl_image_get_size_x(lineImage);
    ily=cpl_image_get_size_y(lineImage);
    pidata=cpl_image_get_data_float(lineImage);

    if ( hw < 1 )
    {
        sinfo_msg_error(" wrong half width given!\n") ;
        return NULL ;
    }

    /* allocate memory for returned image */
    if ( NULL == ( returnImage = cpl_image_new(ilx,ily,CPL_TYPE_FLOAT ) ))
    {
        sinfo_msg_error(" cannot allocate a new image\n");
        return NULL ;
    }
    /*
    olx=cpl_image_get_size_x(returnImage);
    oly=cpl_image_get_size_y(returnImage);
     */
    podata=cpl_image_get_data_float(returnImage);

    column_buffer=cpl_calloc(ily,sizeof(float)) ;

    /* go through the image columns and save them in a buffer */
    for ( col = 0 ; col < ilx ; col++ )
    { 
        for ( row = 0 ; row < ily ; row++ )
        {
            column_buffer[row] = pidata[col + row*ilx] ;
        }

        /*--------------------------------------------------------------------- 
         * now low pass filter the columns by the sinfo_gaussian and fill 
           the return image.
         */  
        filter = sinfo_function1d_filter_lowpass( column_buffer,
                        ily,
                        LOW_PASS_GAUSSIAN,
                        hw ) ;
        for ( row = 0 ; row < ily ; row++ )
        {
            podata[col + row*ilx] = filter[row] ;
        }
        sinfo_function1d_del(filter) ;
    }

    cpl_free(column_buffer);
    return returnImage ;
}

/**
@brief Given a source image and a corresponding wavelength calibration 
       file this routine produces an image in which elements in a given 
       row are associated with a single wavelength.
@name sinfo_new_defined_resampling()
@param image:      source image to be calibrated
@param calimage:   wavelength map image 
@param n_params:   number of fit parameters for the polynomial interpolation 
                   standard should be 3 that means order of polynom + 1
@param n_rows:     desired number of rows for the final image, 
                   this will be the final number 
                   of spectral pixels in the final data cube.
@param dispersion: dummy for the resulting dispersion
@param minval:     dummy for minimal wavelength value,
@param maxval:     dummy for maximal wavelength value 
@param centralLambda: dummy for the final central wavelength
@return # wavelength calibrated source image, 
        # dispersion: resulting spectral dispersion (microns/pixel)
                      is chosen as the minimum dispersion found in 
                      the wavelength map - 2% of this value
        # minval:     minimal wavelength value,
        # maxval:     maximal wavelength value 
        # centralLambda: final central wavelength value
        # centralpix: row of central wavelength (in image coordinates!)
@doc Given a source image and a corresponding wavelength calibration file 
     this routine produces an image in which elements in a given row are 
     associated with a single wavelength. It thus corrects 
     for the wavelength shifts between adjacent elements in the rows of 
     the input image. The output image is larger in the wavelength domain 
     than the input image with pixels in each column 
     corresponding to undefined (blank, ZERO) values. The distribution of
     these undefined values varies from column to column. The input image is
     resampled at discrete wavelength intervals using
     a polynomial interpolation routine. The wavelength intervals (dispersion) 
     and the central wavelength are defined and stable for each used grating.
     Thus, each row has a defined wavelength
     for each grating. Only the number of rows can be changed by the user. 
 */

cpl_image * sinfo_new_defined_resampling( cpl_image * image,
                                          cpl_image * calimage,
                                          int        n_params,
                                          int*       n_rows,
                                          double   * dispersion,
                                          float    * minval,
                                          float    * maxval,
                                          double   * centralLambda,
                                          int    * centralpix )
{
    cpl_image * retImage ;
    float lambda ;
    float dif, lambda_renorm ;
    float * retimagecol = NULL;//[2560] ; /* retimagecol[n_rows] ; */

    float* imagecol=NULL ;
    float* calcol=NULL ;
    float* x_renorm=NULL ;

    float * imageptr ;
    float new_sum ;
    float disp, mindisp ;
    int *calcolpos=NULL;//[2560];
    int i/*, j*/, col, row, testrow ;
    int half_width, firstpos ;
    int dispInd ;

    int flag;
    float temprow;
    float minLambda = 0. ;
    /*dpoint list[n_params] ;*/
    /*double * polycoeffs ;*/
    double poly ;
    /*float error;*/

    int ilx=0;
    int ily=0;
    int clx=0;
    int cly=0;
    int olx=0;
    int oly=0;

    float* podata=NULL;
    float* pidata=NULL;
    float* pcdata=NULL;
    float* ptidata=NULL;
    float* ptcdata=NULL;

    if ( NULL == image )
    {
        sinfo_msg_error(" source image not given\n") ;
        return NULL ;
    }
    ilx=cpl_image_get_size_x(image);
    ily=cpl_image_get_size_y(image);
    pidata=cpl_image_get_data_float(image);


    if ( NULL == calimage )
    {
        sinfo_msg_error(" wavelength map image not given\n") ;
        return NULL ;
    }
    clx=cpl_image_get_size_x(calimage);
    cly=cpl_image_get_size_y(calimage);
    pcdata=cpl_image_get_data_float(calimage);
    if ( ilx != clx ||
                    ily != cly )
    {
        sinfo_msg_error("source image and wavelength map image "
                        "are not compatible in size\n") ;
        return NULL ;
    }                              

    if ( n_params < 1 )
    {
        sinfo_msg_error (" wrong number of fit parameters given\n") ;
        return NULL ;
    }

    if ( n_params > 4 )
    {
        sinfo_msg_warning(" attention: very high number of fit "
                        "parameters given, not tested !!!\n") ;
    }

    imagecol=cpl_calloc(ily,sizeof(float)) ;
    calcol=cpl_calloc(cly,sizeof(float)) ;
    x_renorm=cpl_calloc(n_params,sizeof(float)) ;


    /*if ( n_rows <= cly)
    {
        sinfo_msg_error (" number of rows of resampled image will be "
                         " smaller than in wavelength calibration map,"
                         " information would get lost!") ;
        return NULL ;
    }*/

    dispInd = 0 ;

    /* first determine the dispersion direction */
    for ( col = 0 ; col < clx ; col++ )
    {
        if ( isnan(pcdata[col]) || pcdata[col] <= 0. )
        {
            continue ;
        }
        if ((pcdata[col] - pcdata[col+(clx)*(cly-1)]) > 0. )
        {
            dispInd--  ;
        }
        else if ((pcdata[col] - pcdata[col+(clx)*(cly-1)]) < 0. )
        {
            dispInd++ ;
        }
        else
        {
            continue ;
        }
    }

    if ( dispInd == 0 )
    {
        sinfo_msg_error(" zero dispersion?\n");
        cpl_free(imagecol) ;
        cpl_free(calcol) ;
        cpl_free(x_renorm) ;
        return NULL ;
    }

    /* mirror the wavelength map and the raw image if 
       the dispersion is negative */
    if ( dispInd < 0 )
    {

        /* allocate a temp image */
        cpl_image* tempCalImage=NULL;
        if ( NULL == ( tempCalImage = cpl_image_new(clx,cly,CPL_TYPE_FLOAT)))
        {
            sinfo_msg_error(" cannot allocate a new image\n");
            return NULL ;
        }
        ptcdata=cpl_image_get_data_float(tempCalImage);
        cpl_image* tempImage=NULL;
        if ( NULL == ( tempImage = cpl_image_new( ilx, ily,CPL_TYPE_FLOAT)))
        {
            sinfo_msg_error(" cannot allocate a new image\n");
            cpl_image_delete(tempCalImage) ;
            return NULL ;
        }
        ptidata=cpl_image_get_data_float(tempImage);

        for ( col = 0 ; col < clx ; col++ )
        {
            int n = cly - 1 ;
            for ( row = 0 ; row < cly ; row++ )
            {
                ptcdata[col+row*clx] = pcdata[col+n*clx] ;
                ptidata[col+row*clx] = pidata[col+n*clx] ;
                n-- ;
            }
        }

        for ( i = 0 ; i < (int) ilx*ily ; i++ )
        {
            pidata[i] = ptidata[i] ;
            pcdata[i] = ptcdata[i] ;
        }
        cpl_image_delete(tempCalImage) ;
        cpl_image_delete(tempImage) ;
    }

    /* determine the max and min pixel value in the first and the last row */
    *maxval = -FLT_MAX ;
    *minval =  FLT_MAX ;
    mindisp = FLT_MAX ;
    for ( col = 0 ; col < clx ; col++ )
    {
        if ( isnan(pcdata[col]) || pcdata[col] <= 0. )
        {
            continue ;
        }
        disp = (pcdata[col+(clx)*((cly)-1)]
                       - pcdata[col]) / (float)cly ;
        if ( mindisp > disp )
        {
            mindisp = disp ;
        }
        if ( *minval >= pcdata[col] )
        {
            *minval = pcdata[col] ;
        }
        if ( *maxval <= pcdata[col + (clx)*((cly)-1)] )
        {
            *maxval = pcdata[col + (clx)*((cly)-1)] ;
        }
    }

    /* find the used grating and set the dispersion to the defined value */
    if (*minval > 1.9 )
    {
        if ( cly > 1024 && cly < 3000)
        {
            *dispersion = DISPERSION_K_DITH ;
            *centralLambda = CENTRALLAMBDA_K ;
        }
        else if ( cly < 2000)
        {
            *dispersion = DISPERSION_K ;
            *centralLambda = CENTRALLAMBDA_K ;
        }
        else
        {
            *dispersion = DISPERSION_K_DITH/2 ;
            *centralLambda = CENTRALLAMBDA_K ;
        }
    }
    else if (*minval < 1.2 )
    {
        if ( cly > 1024 )
        {
            *dispersion = DISPERSION_J_DITH ;
            *centralLambda = CENTRALLAMBDA_J ;
        }
        else
        {
            *dispersion = DISPERSION_J ;
            *centralLambda = CENTRALLAMBDA_J ;
        }
    }
    else 
    {
        if ( *maxval > 2.3 )
        {
            if ( cly > 1024 )
            {
                *dispersion = DISPERSION_HK_DITH ;
                *centralLambda = CENTRALLAMBDA_HK ;
            }
            else
            {
                *dispersion = DISPERSION_HK ;
                *centralLambda = CENTRALLAMBDA_HK ;
            }
        }
        else 
        {
            if ( cly > 1024 )
            {
                *dispersion = DISPERSION_H_DITH ;
                *centralLambda = CENTRALLAMBDA_H ;
            }
            else
            {
                *dispersion = DISPERSION_H ;
                *centralLambda = CENTRALLAMBDA_H ;
            }
        }
    }

    /*if ( *minval + (float)n_rows * *dispersion < *maxval ) 
    {
        sinfo_msg_error(" given number of rows too small!\n");
        return NULL ;
    }*/
    if ( (*maxval - *minval) / *dispersion < (float)cly ) 
    {
        sinfo_msg_error(" must be something wrong with the wavelength map!\n");
        return NULL ;
    }

    /* determine the central pixel and the lambda in the first image row */
    *n_rows = floor(floor(0.5+(*maxval - *minval) / *dispersion)/2+0.5)*2;
    *centralpix = *n_rows / 2 ; 
    minLambda  = *centralLambda - *dispersion * (float)*centralpix ;
    /*if ( (minLambda + *dispersion * n_rows) < *maxval ) 
    {
        sinfo_msg_error(" not enough rows defined \n");
        return NULL ;
    }
    if ( minLambda  > *minval ) 
    {
        sinfo_msg_error(" not enough rows defined \n");
        return NULL ;
    }*/

    /* allocate memory */
    if ( NULL == ( retImage = cpl_image_new( ilx, *n_rows,CPL_TYPE_FLOAT ) ))
    {
        sinfo_msg_error(" cannot allocate a new image\n");
        return NULL ;
    }
    podata=cpl_image_get_data_float(retImage);
    olx=cpl_image_get_size_x(retImage);
    oly=cpl_image_get_size_y(retImage);
    /* now go through the columns */
    /* OLD setting: this later on would hit the size of imagecol,
     * calcolpos arrays as raw,testraw can go upt to oly>n_rows
     * for this reason we oversize the arrays retimagecol,calcolpos
    retimagecol = cpl_malloc(*n_rows * sizeof(retimagecol[0]));
    calcolpos = cpl_malloc(*n_rows * sizeof(calcolpos[0]));
    */

    retimagecol = cpl_malloc(oly * sizeof(retimagecol[0]));
    calcolpos = cpl_malloc(oly * sizeof(calcolpos[0]));

    for ( col = 0 ; col < olx ; col++ )
    {
        /*------------------------------------------------------------------ 
         * copy the columns of the source image and the wavemap image into
         * buffer arrays to speed things up
         */
        float sum = 0. ;
        for ( row = 0 ; row < ily ; row++ )
        {
            imagecol[row] = pidata[col + row*ilx] ; 
            if (!isnan(imagecol[row]))
            {
                sum += imagecol[row] ;
            }
            calcol[row]   = pcdata[col + row*clx] ; 
        }

        for ( row = 0 ; row < oly ; row++ )
        {
            retimagecol[row] = 0. ;
            calcolpos[row] = -1;
        }

        for ( row=0 ; row < cly ; row++)
        {
            temprow = (calcol[row]- minLambda)/ *dispersion;
            if (temprow >= 0 && temprow < oly)
                calcolpos[(int) temprow]  = row;
        }

        int zeroind = 0 ;


        for ( row = 0 ; row < oly ; row++ )
        {
            lambda = minLambda + *dispersion * (float) row ;

            /*--------------------------------------------------------------- 
             * lambda must lie between the two available wavelength extremes
             * otherwise the image pixels are set to ZERO 
             */
            if ( row < cly )
            {
                if ( isnan(calcol[row]) )
                {
                    zeroind = 1 ;
                } 
            }

            if ( (lambda < calcol[0]) || 
                            (lambda > calcol[(cly)-1]) || zeroind == 1 )
            {
                retimagecol[row] = ZERO ;
                continue ;
            }
            /*testrow = 0 ; 
            while ( lambda > calcol[testrow] )
            {
                testrow++ ;
            }*/
            if (calcolpos[row]==-1) {
                if(row>= (*n_rows-1)) calcolpos[row] = calcolpos[row-1];
                if(row<  (*n_rows-1)) calcolpos[row] = calcolpos[row+1];
            }
            if(calcolpos[row]>0) {
                if (lambda-calcol[calcolpos[row]-1]==0.) {
                    calcolpos[row]=calcolpos[row]-1;
                }
            }

            testrow = calcolpos[row];
            /*-----------------------------------------------------------------
             * at this point calcol[testrow-1] < lambda <= calcol[testrow] 
             * now determine the box position in which the polint fit is 
               carried through.
             * the half width of the box is half the number of fit parameters.
             * Now we determine the start position of the fitting box and treat
             * the special case of being near the sinfo_edge.
             */

            if ( n_params % 2 == 0 )
            {
                half_width = (int)(n_params/2) - 1 ;
            }
            else
            {
                half_width = (int)(n_params/2) ;
            }

            if ( testrow > -1 && isnan(imagecol[testrow]) )
            {
                for ( i = row-half_width ; i < row-half_width+n_params ; i++ )
                { 
                    if (i < 0) continue ;
                    if ( i >= oly ) continue  ;
                    retimagecol[i] = ZERO ;
                }
                imagecol[testrow] = 0. ;
            }

        }

        /* now loop over the rows and establish the lambda for each row */
        new_sum = 0. ;
        for ( row = 0 ; row < oly ; row++ )
        {
            if ( isnan(retimagecol[row]) )
            {
                continue ;
            }
            lambda = minLambda + *dispersion * (float) row ;

            /*--------------------------------------------------------------- 
             * lambda must lie between the two available wavelength extremes
             * otherwise the image pixels are set to ZERO 
             */
            if ( (lambda < calcol[0]) || (lambda > calcol[(cly)-1]) ) 
            {
                retimagecol[row] = ZERO ;
                continue ;
            }
            /*testrow = 0 ; 
            while ( lambda > calcol[testrow] )
            {
                testrow++ ;
            }*/
            if (calcolpos[row]==-1) {
                if(row >= (*n_rows-1)) calcolpos[row] = calcolpos[row-1];
                if(row <  (*n_rows-1)) calcolpos[row] = calcolpos[row+1];
            }

            testrow = calcolpos[row];

            /*--------------------------------------------------------------
             * at this point calcol[testrow-1] < lambda <= calcol[testrow] 
             * now determine the box position in which the polynomial 
               interpolation is carried through.
             * the half width of the box is half the number of fit parameters.
             * Now we determine the start position of the fitting box and treat
             * the special case of being near the sinfo_edge.
             */

            if ( n_params % 2 == 0 )
            {
                half_width = (int)(n_params/2) - 1 ;
            }
            else
            {
                half_width = (int)(n_params/2) ;
            }

            firstpos   = testrow - half_width ;
            if ( firstpos < 0 )
            {
                firstpos = 0 ;
            }
            else if ( firstpos > ((cly)-n_params) )
            {
                firstpos = cly - n_params ;
            }
            if ( isnan(imagecol[firstpos]) )
            {
                retimagecol[row] = ZERO ;
                continue ;
            }


            /* we must rescale the x-values (smaller than 1) 
               for the fitting routine */
            dif = calcol[firstpos+n_params-1] - calcol[firstpos] ;
            for ( i = 0 ; i < n_params ; i++ )
            {
                x_renorm[i] = (calcol[firstpos + i] - calcol[firstpos]) / dif ;
            }


            lambda_renorm = ( lambda - calcol[firstpos] ) / dif ; 

            imageptr = &imagecol[firstpos] ;

            flag = 0;
            poly=sinfo_new_nev_ille(x_renorm, imageptr,
                            n_params-1, lambda_renorm, &flag);

            new_sum += poly ;
            retimagecol[row] = poly ; 
        }

        /* now renorm the total flux */
        for ( row = 0 ; row < oly ; row++ )
        {
            if ( new_sum == 0. ) new_sum = 1. ;
            if ( isnan(retimagecol[row]) )
            {
                podata[col+row*olx] = ZERO ;
            }
            else
            {
                /* rescaling is commented out because it delivers wrong results
                   in case of appearance of blanks or bad pixels */
                podata[col+row*olx] = retimagecol[row] /* * sum/new_sum*/ ;
            }
        }

    }
    cpl_free(retimagecol);
    cpl_free(calcolpos);
    cpl_free(imagecol) ;
    cpl_free(calcol) ;
    cpl_free(x_renorm) ;

    return retImage ;
}

/**@}*/
/*___oOo___*/
