/*
 * 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") ;
        return NULL ;
    }
    if ( NULL == (temp_fit_mem = (float *) cpl_calloc( n_params*MAXPAR, 
                                     sizeof (float) ) ) )
    {
        sinfo_msg_error (" could not allocate memory\n") ;
        return NULL ;
    }


    if ( NULL == (temp_derv_mem   = 
             (float *) cpl_calloc( n_params*MAXPAR, sizeof (float) ) ) )
    {
        sinfo_msg_error (" could not allocate memory\n") ;
        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 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 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
*/

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 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 position ;
    int gmax, gmin ;
    int col ;
    int * row_mem;
    float sum ;
    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 ;
        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 ; 

            /* ---------------------------------------------------------------
             * find the nearest line position for each wavelength in the list 
             * and set this position to the given line intensity as weight 
             */
            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] ;
                    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 */
        position = INT32_MAX ;
        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 reads the line data of the calibration lamps
@name sinfo_new_read_list()
@param listname name of the list file, arrays to store the wavelength 
                and the intensities of the emission lines
@param  lineCenter 
@param lineIntensity
*/

int 
sinfo_new_read_list( char * listname, 
                     float * lineCenter, 
                     float * lineIntensity )
{
    FILE * fp ;
    int i, n_lines ;
    
    if ( NULL == lineCenter )
    {
        sinfo_msg_error(" lineCenter array is not allocated\n") ;
        return -1 ;
    }

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

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

    i = 0 ;
    while ( fscanf( fp, "%f %f", &lineCenter[i], &lineIntensity[i] ) != EOF )
    {
        i ++ ;
    }
    n_lines = i ;
    fclose(fp) ;
           
    return n_lines ;
}


/**
@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) ;
                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, result ;
    float offset ;
    int num, found ;
    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 ++ )
    {
        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);
        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++ )
    {
        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 col_index;
    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 */
        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") ;

        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 determines a wavelength calibration map
@name sinfo_new_wave_map()
@param lineImage:    image from a calibration emission lamp,
@param bcoefs:       transformed fit coefficients
@param n_a_fitcoefs: number of fit coefficients for the single  
                     column fits lambda-position
@param n_b_fitcoefs: number of fit coefficients for the fits of
                     the single a coefficients across the columns
@param wavelength:   wavelength list from lamp file
@param intensity:    corresponding line intensity from line list
@param n_lines:      number of lines in the list
@param magFactor:    magnifying factor of the image for FFT
@return wavelength calibration map image.
@doc this routine determines a wavelength calibration map frame 
                   associating a wavelength value to 
     each pixel by using the fit coefficients determined before.
 */

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)
{
    cpl_image * retImage ;
    float cenpos, cenpix ;
    float centreval, centrepix, wavelag ;
    float  pixvalue ;
    float a_initial ;
    int i, j, k, l/*, m*/, line, col, row, found, sign ;
    int var, maxlag, cmin, cmax, offset ;
    double * result ;
    float col_off ;
    float angst ;
    double xcorr_max ;
    int delta ;

    double* z=NULL ;
    double* a=NULL ;
    double*  wave=NULL ;
    float* emline=NULL ;
    float* spec=NULL ;
    int ilx=0;
    int ily=0;
    int olx=0;
    int oly=0;
    float* pidata=NULL;
    float* podata=NULL;


    gsl_poly_complex_workspace * w ;
    
    if ( NULL == lineImage )
    {
        sinfo_msg_error("no 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 ( NULL == wavelength || n_lines <= 0 )
    {
        sinfo_msg_error("no wavelength list given\n") ;
        return NULL ;
    }

    if ( NULL == intensity )
    {
        sinfo_msg_error("no intensity list given\n") ;
        return NULL ;
    }

    if ( NULL == bcoefs )
    {
        sinfo_msg_error("no coefficients given\n") ;
        return NULL ;
    }

    if ( magFactor <= 1 )
    {
        sinfo_msg_error("wrong magnifying factor given\n") ;
        return NULL ;
    }
    
    /* allocate memory */
    if ( NULL == ( retImage = 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(retImage);
    oly=cpl_image_get_size_y(retImage);
    podata=cpl_image_get_data_float(retImage);


    var    = (magFactor - 1)*(magFactor - 1) ;
    offset = ily * (magFactor/4 + 1) ;

    /* find out if Angstroem or microns are used */
    if ( wavelength[0] > 10000. )
    {
    /* Angstroem */
        angst = 10000. ;
    }
    else if ( wavelength[0] > 1000. && wavelength[0] < 10000. )
    {
    /* nanometers */
    angst = 1000. ;
    }
    else
    {
    /* microns */
    angst = 1. ;
    }

    z=cpl_calloc(2*(n_a_fitcoefs - 1),sizeof(double)) ;
    a=cpl_calloc(n_a_fitcoefs,sizeof(double));
    wave=cpl_calloc(n_lines,sizeof(double)) ;
    emline=cpl_calloc(2*magFactor*ily,sizeof(float));
    spec=cpl_calloc(2*magFactor*ily,sizeof(float)) ;

    /* go through the image columns */
    for ( col = 0 ; col < ilx ; col++ )
    {
        /* initialize the emline array for each column */
        for ( i = 0 ; i < 2*magFactor*ily ; i++ )
        {
            emline[i] = 0. ;
        }
        col_off = (float)col - (float)(ilx-1)/2. ;

        /* determine the coefficients by using the given bcoefs */
        for ( i = 0 ; i < n_a_fitcoefs ; i++ ) 
        {
            /* initialize coefficients and solution */
            a[i] = 0. ;
            if (i < n_a_fitcoefs-1)
            {
                z[2*i] = 0. ;
                z[2*i+1] = 0. ;
            }
            for ( j = 0 ; j < n_b_fitcoefs ; j++ )
            {
                a[i] += bcoefs[i][j] * pow(col_off, j) ;
            }
        }
        a_initial = a[0] ;
        
        /* go through the lines and generate an artificial spectrum */
        for ( line = 0 ; line < n_lines ; line++ )
        {
            /* go from Angstroem to micron */
            wave[line] = wavelength[line]/angst ;

            /* ---------------------------------------------------------------
             * solve the polynomial for the exact offset of the line that means
             * find the root of the polynomial of order n_fitcoefs - 1
             */
            a[0] = a_initial - wave[line] ;

            if(NULL==(w=sinfo_gsl_poly_complex_workspace_alloc(n_a_fitcoefs)))
            {
                sinfo_msg_error("could not allocate complex workspace!") ;
                cpl_image_delete(retImage) ;
                return NULL ;
            }
            if (-1 == sinfo_gsl_poly_complex_solve(a, n_a_fitcoefs, w, z))
            {
                sinfo_msg_error("sinfo_gsl_poly_complex_solve did not work!") ;
                cpl_image_delete(retImage) ;
                return NULL ;
            }
            sinfo_gsl_poly_complex_workspace_free(w) ;

            
            j = 0 ;
            found = -1 ;
            for ( i = 0 ; i < n_a_fitcoefs - 1  ; i++ )
            {
                /* test for appropriate solution */
                if( z[2*i] > (-1.)*(float) ily/2. && 
                    z[2*i] < (float)ily/2. && z[2*i+1] == 0. )
                {
                    found = 2*i ;
                    j ++ ;
                }
                else
                {
                    continue ;
                } 
            }
            if ( j == 0 )
            {
                sinfo_msg_warning("no offset solution found "
                                  "for line %d in column %d\n", line, col) ;
                continue ;
            } 
            else if ( j == 1 )
            {
                cenpos = z[found] + (float) ily /2. ;
            }
            else
            {
                sinfo_msg_warning("two or more offset solutions found "
                                  "for line %d in column %d\n", line, col) ;
                continue ;
            }
             
            /*---------------------------------------------------------------
             * magnify image by the given factor add an additional offset 
             */
            cenpix = cenpos * (float) magFactor + (float) offset ;  
            
            /* determine max and min pixel limits over 
               which line should be convolved */
            cmin = (sinfo_new_nint(cenpix) - (var-1)) > 0 ? 
                    sinfo_new_nint(cenpix) - (var-1) : 0 ;
            cmax = (sinfo_new_nint(cenpix) + (var-1)) < 2*magFactor * ily ? 
                    sinfo_new_nint(cenpix) + (var-1) :  2*magFactor * ily ;

            /* convolve neon lines with Gaussian function */
            for ( j = cmin ; j < cmax ; j++ )
            {
                emline[j] += intensity[line] * 
                exp((double)(-0.5*(j-cenpix)*(j-cenpix))/(double)var) ;
            }
        }
                
        /*--------------------------------------------------------------------- 
         * for each column, map the image data points onto an magFactor times 
           bigger element grid for FFT in the cross sinfo_new_correlation, 
           first initialize the two helping arrays for each new column.
         */
        for ( k = 0 ; k < 2*magFactor * ily ; k++ )
        {
            spec[k] =  0. ;
        }
   
        /* now take the image data points of the column and put them 
           into the spec array */
        for ( row = 0 ; row < ily ; row++ ) /* go through the column */
        {
            /* insert 8 values for each image row (magnification) and 
               add same offset as for emline array */
            for ( l = 0 ; l < magFactor ; l++ )   
        {
            /* set bad pixels or negative values to zero */
                if (!isnan(pidata[col + row * ilx]) &&
                                (pidata[col + row * ilx] > 0.))
                {
                    spec[offset + l + (row * magFactor)] = 
                pidata[col + row * ilx] ;     
                }
                else
                {
                    spec[offset + l + (row * magFactor)] = 0. ;
                }
            }
    }
 
        /* now call the cross sinfo_new_correlation routine */
        if (NULL == (result = sinfo_new_xcorrel(spec, 2*magFactor * ily, 
                                                emline, 2*magFactor * ily, 
                                                magFactor * ily, &delta, 
                                                &maxlag, &xcorr_max)) ) 
        {
        sinfo_msg_warning ("cross sinfo_new_correlation did not work,"
                               " col: %d is set ZERO\n", col) ;
            for ( row = 0 ; row < ily ; row++ )
            {
                podata[col + row * ilx] = ZERO ;
            }
            continue ;
        }
    
        if ( xcorr_max <= 0. )
        {
            sinfo_msg_warning ("cross sinfo_new_correlation sum is negative,"
                               " col: %d is set ZERO\n", col) ;
            for ( row = 0 ; row < ily ; row++ )
            {
                podata[col + row * ilx] = ZERO ;
            }
            cpl_free(result) ;
            continue ;
        }

        wavelag = (float) -delta / (float) magFactor ;
        if ( fabs(wavelag) > (float)ily/20. )
        {
            sinfo_msg_warning("wave lag too big, col: %d is set ZERO\n", col) ;
            for ( row = 0 ; row < ily ; row++ )
            {
                podata[col + row * ilx] = ZERO ;
            }
            cpl_free(result) ;
        continue ;
        }

        /*-------------------------------------------------------------------- 
         * determine new zero order coefficient centreval, of which the 
           formula is determined by setting equal a polynomial shifted by 
           wavelag with the same higher order coefficients and set the new 
           zero order coefficient to get both sides of the equation 
           approximately equal.
         */ 
        centreval = a_initial ;
        for ( i = 1 ; i < n_a_fitcoefs ; i++ )
        {
            if ( i%2 == 0 )
            {
                sign = -1 ;
            }
            else
            {
                sign = 1 ;
            }
            centreval += (float)sign * a[i]*pow(wavelag, i) ;
        }

        /* prepare to write out wavelength as pixel values */
        for ( row = 0 ; row < ily ; row++ )
        {
            centrepix = (float)row - ((float)ily - 1.)/2. ;
            pixvalue = 0. ;
            for ( i = 1 ; i < n_a_fitcoefs ; i++ )
            {
                pixvalue += a[i]*pow(centrepix, i) ; 
            }
            podata[col + row * ilx] = centreval + pixvalue ; 
        }
        cpl_free(result) ;   
    }



    cpl_free(z) ;
    cpl_free(a) ;
    cpl_free(wave) ;
    cpl_free(emline) ;
    cpl_free(spec) ;

    return retImage ;
}                    

/**
@brief
@name sinfo_new_wavelength_calibration()
@param image:        merged image from a calibration emission lamp,
@param wave:         wavelength array read from the wavelength list
@param n_lines:      number of lines in the wavelength list
@param row_clean: resulting list of the row indices but without the
                     lines that are too close to each other for the fit
                     output of sinfo_findLines()
@param wavelength_clean: corrected wavelength list corresponding to
                     the row_clean array
                     output of sinfo_findLines()
@param n_found_lines: output of sinfo_findLines(): total number of 
                      found emission lines
@param dispersion:   dispersion of spectrum: micron per pixel
@param halfWidth:    half width of the box where the line must sit
@param minAmplitude: minimum amplitude of the Gaussian to do the fit
@param max_residual: maximum residual value, beyond that value
                     the polynomial lambda-position fit is rejected.
@param fwhm:         first guess for the full width of half maximum
                     of the sinfo_gaussian line fit
@param n_a_fitcoefs: number of fit coefficients for the single
                     column fits: lambda-position
@param n_b_fitcoefs: number of fit coefficients for the fits of
                     the single a coefficients across the columns
@param sigmaFactor:  factor of the standard deviation of the determined
                     polynomial coefficients of the columns beyond
                     which these coefficients are not used to carry out
                     the polynomial fit across the columns.
@param pixel_tolerance: maximum tolerated difference between estimated
                     and fitted line positions.
@return 0 if all went o.k., -1 if something went wrong.
        # bcoefs: array of cooefficients of the polynomial fit 
                  across the columns.
        # par: array of the resulting FitParams data structure
@doc 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)
 */

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,
                           float        pixel_tolerance )

{
    int          i, j, k ;
    int          n_fit ;
    int          n_reject ;
    float     *  acoefs ;
    float     *  dacoefs ;
    float     ** abuf ;
    float     ** dabuf ;
    float        chisq_poly, chisq_cross ;
    int          zeroind ;
    /*float     *  mem ;*/
    int lx=0;
    int ly=0;
    float* pdata=NULL;

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

    if ( par == NULL )
    {
        sinfo_msg_error("no fit parameter data structure given\n") ;
        return -1 ;
    }
    if ( wave == NULL )
    {
        sinfo_msg_error("no wavelength list given\n") ;
        return -1 ;
    }
    if ( n_lines <= 0 )
    {
        sinfo_msg_error("impossible number of lines in line list given\n") ;
        return -1 ;
    }
    if ( row_clean == NULL )
    {
        sinfo_msg_error("no row_clean array given\n") ;
        return -1 ;
    }
    if ( wavelength_clean == NULL )
    {
        sinfo_msg_error("no wavelength_clean array given\n") ;
        return -1 ;
    }

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

    if ( halfWidth <= 0 || halfWidth > ly/2 )
    {
        sinfo_msg_error("impossible half width of the fitting box given\n") ;
        return -1 ;
    }
    if ( minAmplitude < 1. )
    {
        sinfo_msg_error("impossible minimal amplitude\n") ;
        return -1 ;
    }

    if ( max_residual <= 0. || max_residual > 1. )
    {
        sinfo_msg_error("impossible max_residual given\n") ;
        return -1 ;
    }

    if ( fwhm <= 0. || fwhm > 10. )
    {
        sinfo_msg_error("impossible fwhm given\n") ;

        return -1 ;
    }

    if ( n_a_fitcoefs <= 0 || n_a_fitcoefs > 9 )
    {
        sinfo_msg_error("unrealistic n_a_fitcoefs given\n") ;
        return -1 ;
    }

    if ( n_b_fitcoefs <= 0 || n_b_fitcoefs > 9 )
    {
        sinfo_msg_error(" unrealistic n_b_fitcoefs given\n") ;
        return -1 ;
    }
    if ( sigmaFactor <= 0. )
    {
        sinfo_msg_error(" impossible sigmaFactor given\n") ;
        return -1 ;
    }

    /* initialize the variables */
    n_reject = 0 ;
    n_fit = 0 ;

    /* fit each found line by using a Gaussian function and determine the 
       exact position */
    if ( 0 > (n_fit = sinfo_new_fit_lines( image , par, fwhm, 
                                           n_found_lines, row_clean, 
                                           wavelength_clean,
                                halfWidth, minAmplitude )) )
    {
        sinfo_msg_error(" cannot fit the lines, "
                        "error code of sinfo_fitLines: %d\n", n_fit) ;
        return -1 ;
    }

    /* first check for faked lines like bad pixels */
    if ( -1 == sinfo_new_check_for_fake_lines (par, dispersion, 
                                               wavelength_clean, row_clean, 
                                               n_found_lines,
                                               lx, pixel_tolerance) )
    {
        sinfo_msg_error("cannot fit the lines, "
                        "error code of sinfo_fitLines: %d", n_fit) ;
        return -1 ;
    }

    /* allocate memory */
    if (NULL == (acoefs = (float*) cpl_calloc (n_a_fitcoefs, sizeof(float*))) ||
        NULL == (dacoefs = (float*)cpl_calloc (n_a_fitcoefs, sizeof(float*))) ||
        NULL == (abuf = (float**) cpl_calloc (n_a_fitcoefs, sizeof(float*))) ||
        NULL == (dabuf = (float**) cpl_calloc (n_a_fitcoefs, sizeof(float*))) )
    {
        sinfo_msg_error(" cannot allocate memory\n") ;
        return -1 ;
    }

    for ( i = 0 ; i < n_a_fitcoefs ; i++ )
    {
        if ( NULL == (abuf[i] = (float*) cpl_calloc(lx, sizeof(float))) ||
             NULL == (dabuf[i] = (float*) cpl_calloc(lx, sizeof(float))) )
        {
            sinfo_msg_error(" cannot allocate memory\n") ;
            cpl_free(abuf) ;
            cpl_free(dabuf) ;
            return -1 ;
        }
    }

    /* fit wavelengths to the corresponding found positions for each column */
    k = 0 ;
    
    for ( i = 0 ; i < lx ; i++ )
    {
        zeroind = 0 ;
        if ( FLT_MAX == (chisq_poly = sinfo_new_polyfit( par, i, 
                                                         n_found_lines[i], 
                                                         ly, dispersion,
                                                         max_residual, acoefs, 
                                                         dacoefs, &n_reject, 
                                                         n_a_fitcoefs)) )
        {
      /* 
           sinfo_msg_warning (" error in polyfitt in column: %d\n", i) ;
       */
            for ( j = 0 ; j < n_a_fitcoefs ; j++ )
            {
                acoefs[j] = ZERO ;
                dacoefs[j] = ZERO ;
            }
        }

        for ( j = 0 ; j < n_a_fitcoefs ; j++ )
        {
            if ( acoefs[0] <= 0. || acoefs[1] ==0. ||
                 dacoefs[j] == 0. || isnan(acoefs[j]) )
            {
                zeroind = 1 ;

            }
        }
        for ( j = 0 ; j < n_a_fitcoefs ; j++ )
        {
            if ( zeroind == 0 )
            {
                abuf[j][i]  = acoefs[j] ;
                dabuf[j][i] = dacoefs[j] ;
            }
            else
            {
                abuf[j][i]  = ZERO ;
                dabuf[j][i] = ZERO ;
            }
        }
    }

    /* fit each acoefs across the columns to smooth the result */
    for ( i = 0 ; i < n_a_fitcoefs ; i++ )
    {
        if ( FLT_MAX == (chisq_cross = sinfo_new_coefs_cross_fit(lx, 
                                                                 abuf[i], 
                                                                 dabuf[i],
                                                                 bcoefs[i],
                                                                 n_b_fitcoefs,
                                                                 sigmaFactor)))
        {
            sinfo_msg_error (" cannot carry out the fitting of coefficients"
                             " across the columns, for the coefficient with"
                             " index: %d\n", i) ;
            for ( i = 0 ; i < n_a_fitcoefs ; i++ )
            {
                cpl_free (abuf[i]) ;
                cpl_free (dabuf[i]) ;
            }
            cpl_free ( acoefs ) ;
            cpl_free ( dacoefs ) ;
            cpl_free ( abuf ) ;
            cpl_free ( dabuf ) ;
            return -1 ;
        }
    }

    /* free all allocated memory */
    for ( i = 0 ; i < n_a_fitcoefs ; i++ )
    {
        cpl_free (abuf[i]) ;
        cpl_free (dabuf[i]) ;
    }
    cpl_free ( acoefs ) ;
    cpl_free ( dacoefs ) ;
    cpl_free ( abuf ) ;
    cpl_free ( dabuf ) ;

    return 0 ;   
}

                           
/**
@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 ;
    cpl_image * tempCalImage ;
    cpl_image * tempImage ;
    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 sum, new_sum ;
    float disp, mindisp ;
    int *calcolpos=NULL;//[2560];
    int i/*, j*/, col, row, testrow ;
    int half_width, firstpos ;
    int dispInd ;
    int n ;
    int flag;
    float temprow;
    float minLambda = 0. ;
    /*dpoint list[n_params] ;*/
    /*double * polycoeffs ;*/
    double poly ;
    /*float error;*/
    int zeroind ;
    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");
        return NULL ;
    }
   
    /* mirror the wavelength map and the raw image if 
       the dispersion is negative */
    if ( dispInd < 0 )
    {

        /* allocate a temp image */
        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);
        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++ )
        {
            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 */
    retimagecol = cpl_malloc(*n_rows * sizeof(retimagecol[0]));
    calcolpos = cpl_malloc(*n_rows * 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
         */
        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;
    }
        
    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 ( 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___*/
