/*
 * 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  30/08/00  created
*/

/************************************************************************
*   NAME
*        sinfo_cube_construct.c -
*------------------------------------------------------------------------
*/

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

/*
 * System Headers
 */

/*
 * Local Headers
 */
#include "sinfo_function_1d.h"
#include "sinfo_cube_construct.h"
#include "sinfo_spectrum_ops.h"
#include "sinfo_wave_calibration.h"
#include "sinfo_utilities.h"
#include "sinfo_utils_wrappers.h"
#include "sinfo_local_types.h"
#include "sinfo_fft_base.h"

static int
sinfo_sort_slitlets(const int kslit);


static int
sinfo_sort_slitlets_array(const int slit, int* row_index);

/**@{*/
/**
 * @defgroup sinfo_cube_construct Cube generation functions
 *
 * some procedures to construct a data cube
 */

/*----------------------------------------------------------------------------
 *                            Function codes
 *--------------------------------------------------------------------------*/
/**
   @name   sinfo_new_convolve_ns_image_by_gauss()
   @memo convolves a north-south-test image with a Gaussian
                        with user given integer half width by using the
                        routine sinfo_function1d_filter_lowpass().

   @param lineImage  North-south-test image
   @param hw          kernel half width of the Gaussian response function

   @return  north-south-test image convolved with a Gaussian


 */

cpl_image * 
sinfo_new_convolve_ns_image_by_gauss( cpl_image * lineImage,
                                   int        hw )
{
    cpl_image * returnImage ;
    float* row_buffer=NULL ;
    float * filter ;
    int col, row ;
    int ilx=0;
    int ily=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 ;
    }
    podata=cpl_image_get_data_float(returnImage);

    /* go through the image rows and save them in a buffer */
    row_buffer=cpl_calloc(ily,sizeof(float)) ;

    for ( row = 0 ; row < ily ; row++ )
    {
        for ( col = 0 ; col < ilx ; col++ )
        {
            if ( isnan(pidata[col+row*ilx]) )
            {
                row_buffer[col] = 0. ;
            }
            else
            {
                row_buffer[col] = pidata[col + row*ilx] ;
            }
        }

        /*--------------------------------------------------------------------
         * now low pass filter the rows by the gaussian and fill the return
         * image.
         */
        filter = sinfo_function1d_filter_lowpass( row_buffer,
                                            ilx,
                                            LOW_PASS_GAUSSIAN,
                                            hw ) ;
        for ( col = 0 ; col < ily ; col++ )
        {
            podata[col + row*ilx] = filter[col] ;
        }
        /* deallocate memory */
        sinfo_function1d_del (filter) ;
    }
    cpl_free(row_buffer);    
    return returnImage ;
}

/**
   @name  sinfo_north_south_test()

   @memo determines the distances of the slitlets

   @param ns_image   north-south image
   @param n_slitlets number of slitlets
   @param halfWidth  half width of the box in which the lines
                                    are fit by a sinfo_gaussian
   @param fwhm       first guess of the full width at half maximum
   @param minDiff    amplitude threshold for Gaussian: 
                    below this intensity the fit will not 
                    be carried through
   @param estimated_dist estimated average distance of spectra
   @param devtol     maximal pixel deviation of slitlet distances
   @param bottom     bottom image row
   @param top        top image row
   @return            array of the distances of the slitlets from each other
*/

float * 
sinfo_north_south_test( cpl_image * ns_image,
                          int        n_slitlets,
                          int        halfWidth,
                          float      fwhm,
                          float      minDiff,
                          float      estimated_dist,
                          float      devtol,
              int         bottom,
              int         top )
{
    int i, j, k, m, row, col, n, ni, na ;
    int position, counter;
    int xdim, ndat, its, numpar ;
    pixelvalue row_buf[cpl_image_get_size_x(ns_image)] ;
    float sum, mean, maxval ;
    float tol, lab ;
    float * distances ;
    float distances_buf[cpl_image_get_size_y(ns_image)][n_slitlets-1] ;
    float x_position[n_slitlets] ;
    float * xdat, * wdat ;
    int * mpar ;
    int found[3*n_slitlets], found_clean[3*n_slitlets] ;
    int found_cleanit[3*n_slitlets] ;
    Vector * line ;
    FitParams ** par ;
    int foundit, begin, end ;
    int ilx=0;
    //int ily=0;
 
    float* pidata=NULL;
 
    if ( ns_image == NULL )
    {
        sinfo_msg_error("sorry, no image given\n") ;
        return NULL ;
    }
    ilx=cpl_image_get_size_x(ns_image);
    //ily=cpl_image_get_size_y(ns_image);
    pidata=cpl_image_get_data_float(ns_image);


    if ( n_slitlets < 1 )
    {
        sinfo_msg_error("wrong number of slitlets given\n") ;
        return NULL ;
    }
    if ( halfWidth < 0 || halfWidth >= estimated_dist )
    {
        sinfo_msg_error("wrong half width given\n") ;
        return NULL ;
    }
    if ( fwhm <= 0. )
    {
        sinfo_msg_error("wrong fwhm given\n") ;
        return NULL ;
    }
    if ( minDiff < 1. )
    {
        sinfo_msg_error("wrong minDiff given\n") ;
        return NULL ;
    }

    /* allocate memory for output array */
    if (NULL == (distances = (float *) cpl_calloc ( n_slitlets - 1 , 
                                                    sizeof (float) ))) 
    {
        sinfo_msg_error("could not allocate memory\n") ;
        return NULL ;
    }

    /* go through the image rows */
    for ( row = bottom ; row < top ; row++ )
    {
        int zeroindicator = 0 ;

        /* initialize the distance buffer */
        for ( i = 0 ; i < n_slitlets-1 ; i++ )
        {
            distances_buf[row][i] = ZERO ;
        }

        /* fill the row buffer array with image data */
        for ( col = 0 ; col < ilx ; col++ )
        {
            row_buf[col] = pidata[col + row*ilx] ;
        }

        /* determine the mean of the row data */
        sum = 0. ;
        n = 0 ;
        for ( i = 0 ; i < ilx ; i++ )
        {
            if ( isnan(row_buf[i]) )
            {
                continue ;
            }
            sum += row_buf[i] ;
            n++ ;
        }
        mean = sum / (float)n ;


        /* store the positions of image values greater than the mean */
        n = 0 ;
        for ( i = 0 ; i < ilx ; i++ )
        {
            if (isnan(row_buf[i]))
            {
                continue ;
            }
            if ( row_buf[i] >  sqrt(mean*mean*9) )
            {
                found[n] = i ;
                n++ ;
            } 
        }
       
        if ( n < n_slitlets )
        {
            sinfo_msg_warning("t1 wrong number of intensity columns found "
                              "in row: %d, found number: %d, mean: %g",
                              row, n, mean) ;
            continue ;
        }
        else
        { 
            /* find the maximum value position around the found columns */
            na = 0 ;
            for ( i = 1 ; i < n ; i ++ )
            {
                if ( found[i] - found[i-1] < halfWidth )
                {
                    begin = found[i] - halfWidth ;
                    if ( begin < 0 )
                    {
                        begin = 0 ;
                    }
                    end = found[i] + halfWidth ;
                    if ( end >= ilx )
                    {
                        end = ilx - 1 ;
                    }
                    /* find the maximum value inside the box 
                       around the found positions*/
                    maxval = -FLT_MAX ;
                    foundit = 0 ;
                    for ( j = begin ; j <= end ; j++ )
                    {
                        /* do not consider boxes that contain bad pixels */
                        if (isnan(row_buf[j]))
                        {
                            continue ;
                        }
                        if (row_buf[j] >= maxval )
                        {
                            maxval = row_buf[j] ;
                            foundit = j ;
                        }
                    }
                    if (maxval == -FLT_MAX)
                    {
                        continue ;
                    }
                    for ( k = 0 ; k < na ; k++ )
                    {
                        if ( found_cleanit[k] >= begin && 
                             found_cleanit[k] < foundit )
                        {
                            na-- ;
                        }
                    }
                    for ( k = 0 ; k < n ; k++ )
                    {
                        if ( found[k] == foundit)
                        {
                 if (na>0){
                            if ( found_cleanit[na-1] != found[k] )
                            {
                                found_cleanit[na] = found[k] ;
                                na++ ;
                            }
             }
             else{
                found_cleanit[na] = found[k] ;  
                            na++ ;
             } 
                      }
                    }
                }
                else
                {
                    if ( i == 1 )
                    {
                        found_cleanit[na] = found[0] ;
                        na++ ;
                        found_cleanit[na] = found[1] ;
                        na++ ;
                    }
                    else
                    {   
                if (na>0){
                            if ( found_cleanit[na-1] != found[i-1])
                            {
                                found_cleanit[na] = found[i-1] ;
                                na++ ;
                            }
                            if ( found_cleanit[na-1] != found[i])
                            {
                                found_cleanit[na] = found[i] ;
                                na++ ;
                            }
                        }
            else
                        {
                            found_cleanit[na] = found[i] ;
                            na++ ;
                        }
            }  
                }
            }
            /* determine only one pixel position for each slitlet intensity */
            j = 1 ;
            for ( i = 1 ; i < na ; i++ )
            {
                if ( (float)(found_cleanit[i] - found_cleanit[i-1]) < 
                            (estimated_dist - devtol) ||
                     (float)(found_cleanit[i] - found_cleanit[i-1]) > 
                            (estimated_dist + devtol) )
                {
                    continue ;
                }
                else
                {
                    found_clean[j-1] = found_cleanit[i-1] ;
                    found_clean[j]   = found_cleanit[i] ;
                    j++ ;
                }
            }
        }
        if ( j > n_slitlets )
        {
            /* check the distance again */
            ni = 1 ;
            for ( i = 1 ; i < j ; i++ )
            {
                if ( (float)(found_clean[i] - found_clean[i-1]) < 
                            (estimated_dist - devtol ) ||
                     (float)(found_clean[i] - found_clean[i-1]) > 
                            (estimated_dist + devtol ) )
                { 
                    continue ;
                }
                else
                {

                    found_clean[ni-1] = found_clean[i-1] ;
                    found_clean[ni]   = found_clean[i] ;
                    ni++ ;
                }
            }
            if ( ni != n_slitlets )
            {
                sinfo_msg_warning("t2 wrong number of intensity columns"
                                  " found in row: %d,  found number: %d",
                                  row, ni) ;
                continue ;
            }
            else 
            {
                j = ni ;
            }
        }
        else if ( j < n_slitlets )
        {
            cpl_msg_debug ("north_south_test3:",
                            "t3 wrong number of intensity columns "
                            "found in row: %d , found number: %d, mean: %g\n", 
                            row, j, mean) ;
            continue ;
        }
        counter = 0 ;
        /* go through the found intensity pixels in one row */
        for ( i = 0 ; i < j ; i++ )
        {
            /* allocate memory for the array where the line is fitted in */
            if ( NULL == (line = sinfo_new_vector (2*halfWidth + 1)) )
            {
                sinfo_msg_error ("cannot allocate new Vector \n") ;
                cpl_free(distances) ;
                return NULL ;
            }

            /* 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) ) ;
            par = sinfo_new_fit_params(1) ;

            m = 0 ;
            for ( k = found_clean[i]-halfWidth ; 
                  k <= found_clean[i]+halfWidth ; k++ )
            {
                if ( k < 0 )
                {
                    k = 0. ;
                }
                else if ( k >= ilx )
                {
                    k = ilx - 1 ;
                }
                else if ( isnan(row_buf[k]) )
                {
                    zeroindicator = 1 ;
                    break ;
                }
                else
                {
                    line -> data[m] = row_buf[k] ;
                    m++ ;
                }
            }
            if ( zeroindicator == 1 )
            {
                sinfo_new_destroy_vector(line) ;
                cpl_free(xdat) ;
                cpl_free(wdat) ;
                cpl_free(mpar) ;
                sinfo_new_destroy_fit_params(&par) ;
                break ;
            }

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

            /* 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 negative peaks and low signal cases */
            if ( (*par) -> fit_par[0] < minDiff )
            {
                sinfo_msg_warning ("sorry, signal of line too low to fit "
                                   "in row: %d in slitlet %d\n", row, i) ;
                sinfo_new_destroy_vector(line) ;
                cpl_free(xdat) ;
                cpl_free(wdat) ;
                cpl_free(mpar) ;
                sinfo_new_destroy_fit_params(&par) ;
                continue ;
            }

            for ( k = 0 ; k < MAXPAR ; k++ )
            {
                (*par) -> derv_par[k] = 0.0 ;
                mpar[k] = 1 ;
            }
            /* finally, do the least square fit using a Gaussian */
            int iters;
            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 ("north_south_test:",
                               "sinfo_lsqfit_c: least squares fit failed,"
                               " error no.: %d in row: %d in slitlet %d\n",
                                iters, row, i) ;
          */
                sinfo_new_destroy_vector(line) ;
                cpl_free(xdat) ;
                cpl_free(wdat) ;
                cpl_free(mpar) ;
                sinfo_new_destroy_fit_params(&par) ;
                continue ;
            }

            /* check for negative fit results */
            if ( (*par) -> fit_par[0] <= 0. || 
                 (*par) -> fit_par[1] <= 0. ||
                 (*par) -> fit_par[2] < 0. )
            {
                sinfo_msg_warning ("negative parameters as fit result, "
                                   "not used! in row %d in slitlet %d", 
                                   row, i) ;
                sinfo_new_destroy_vector(line) ;
                cpl_free(xdat) ;
                cpl_free(wdat) ;
                cpl_free(mpar) ;
                sinfo_new_destroy_fit_params(&par) ;
                continue ;
            }

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

            /* free memory */
            sinfo_new_destroy_fit_params(&par) ;
            sinfo_new_destroy_vector ( line ) ;
            cpl_free ( xdat ) ;
            cpl_free ( wdat ) ;
            cpl_free ( mpar ) ;
        }
        if (zeroindicator == 1)
        {
            sinfo_msg_debug ("bad pixel in fitting box in row: %d\n", row) ;
            continue ;
        }

        if ( counter != n_slitlets )
        {
            continue ;
            sinfo_msg_warning("wrong number of slitlets found in row: %d",row);
        }
        /* store the distances between the sources in a buffer */
        for ( i = 1 ; i < n_slitlets ; i++ )
        {
            distances_buf[row][i-1] = x_position[i] - x_position[i-1] ;
        }
    }

    /* ----------------------------------------------------------------
     * go through the rows again and take the mean of the distances, 
     * throw away the runaways 
     */
    for ( i = 0 ; i < n_slitlets-1 ; i++ )
    {
        n   = 0 ;
        sum = 0. ;
        for ( row = bottom ; row < top ; row++ )
        {
            if ( fabs( distances_buf[row][i] - estimated_dist ) > devtol || 
                 isnan(distances_buf[row][i]) )
            {
            /*
          sinfo_msg("dist=%g devtol=%g isan=%d", 
            distances_buf[row][i],
            devtol,
            isnan(distances_buf[row][i]));
            */
                continue ;
            }
            sum += distances_buf[row][i] ;
            n++ ;
        }
        if ( n < 2 )
        {
            sinfo_msg_error("distances array could not be determined "
                            "completely!, deviations of distances from number "
                            "of slitlets too big\n" ) ;
            cpl_free(distances) ;
            return NULL ;
        }
        else
        {
            distances[i] = sum / (float)n ;
        }
    }
    return distances ; 
}

/**
   @name     sinfo_new_make_cube()

   @memo     makes a data cube out of a re-sampled source image

   @param    calibImage:  re-sampled source image
   @param    distances:   distances of the slitlets from each other
                          output of function ns_test
   @param     correct_diff_dist: dummy array with 32 elements

   @return    resulting source data cube
   @note      correct_diff_dist: differences of the slitlets from
                                           distance 32 given in the correct
                                           Spiffi sequence. The first slitlet
                                           is the reference, therefore element
                                           23 is set 0.

   @doc      makes a data cube out of a re-sampled source image
             this SPIFFI specific routine takes into account the
             Spiffi slitlet order on the detector.
             Also shifts the resulting image rows by one pixel if
             necessary according to the distances array gained from
             the north-south test routine.
             Can do the same with the bad pixel map image to generate a
             bad pixel mask cube.

 */

cpl_imagelist * 
sinfo_new_make_cube ( cpl_image * calibImage,
                     float    * distances,
                     float    * correct_diff_dist )
{
    cpl_imagelist * returnCube ;
    int imsize;
    int z, col;
    int ilx=0;
    int ily=0;

    if ( NULL == calibImage )
    {
        sinfo_msg_error("no resampled image given!\n") ;
        return NULL ;
    }
    ilx=cpl_image_get_size_x(calibImage);
    ily=cpl_image_get_size_y(calibImage);
    float* pidata=cpl_image_get_data_float(calibImage);

    if ( NULL == distances )
    {
        sinfo_msg_error("no distances array from ns_test given!/n") ;
        return NULL ;
    }

    if ( NULL == correct_diff_dist )
    {
        sinfo_msg_error("correct_diff_dist array is not allocated!/n") ;
        return NULL ;
    }
       
    if ( N_SLITLETS != 32 )
    {
        sinfo_msg_error ("wrong number of slitlets given \n" ) ;
        return NULL ;
    }
    imsize = ilx / N_SLITLETS ;

    /* allocate memory */  
    if ( NULL == (returnCube = cpl_imagelist_new()) )
    {
        sinfo_msg_error ("cannot allocate new cube \n" ) ;
        return NULL ;
    }

    /* now build the data cube out of the resampled image */
    for ( z = 0 ; z < ily ; z++ ) /* go through the z-axis */
    {

        cpl_image* o_img=cpl_image_new(imsize,N_SLITLETS,CPL_TYPE_FLOAT);
        float* podata=cpl_image_get_data_float(o_img);
        int kpix       = 0 ;
        int kslit      = 0 ;
        int slit_index = -1 ;
        int recol      = -1 ;
        for ( col = 0 ; col < ilx ; col++ ) /* go through the image columns */
        {
            if ( col % imsize == 0 )
            {
                recol = 0 ;
                kslit = col/imsize ;
                /* sort the slitlets in the right spiffi specific way */
                if((slit_index=sinfo_sort_slitlets(kslit)) == -1) {
                  sinfo_msg_error("wrong slitlet index: couldn't be a "
                               "spiffi image,  there must be 32 slitlets!") ;
                        cpl_imagelist_delete(returnCube) ;
                        return NULL ;
                }

                if ( kslit != 0 )
                {
                    /*-------------------------------------------------------- 
                     * shift the first pixel by an integer if the absolute 
                     * amount of distances[]
                     * is bigger than 0.5 
                     */
                    kpix = sinfo_new_nint(distances[kslit-1]) ;

                    /*----------------------------------------------- 
                     * now sort the distances array according to the row order 
                     * and add a 0 value for the first (reference) slitlet 
                     * that means row 8 
                     */
                    correct_diff_dist[slit_index] = distances[kslit-1] - 
                                                    (float)kpix ;
                }
                /* refer all distances to the first slitlet */
                else
                {
                    correct_diff_dist[slit_index] = 0. ;
                }
            }

            /* fill each cube plane with one image row */
            podata[recol+slit_index*imsize] = pidata[col+kpix+z*ilx];
            recol++ ;

            if ( recol > imsize )
            {
                sinfo_msg_error("wrong column of reconstructed "
                                "image, shouldn't happen!\n") ; 
                cpl_imagelist_delete(returnCube) ;
                return NULL ;
            }
        }
    }
    return returnCube ;
}



/**
   @name sinfo_sort_slitlets()

   @brief sort the slitlets in the right spiffi specific way

   @param kslit slit number

   @return spiffi slit index or -1
*/

static int
sinfo_sort_slitlets(const int kslit)
{
  int slit_index=0;

  switch (kslit)
    {
    case 0:
      slit_index = 8 ;
      break ;
    case 1:
      slit_index = 7 ;
      break ;
    case 2:
      slit_index = 9 ;
      break ;
    case 3:
      slit_index = 6 ;
      break ;
    case 4:
      slit_index = 10 ;
      break ;
    case 5:
      slit_index = 5 ;
      break ;
    case 6:
      slit_index = 11 ;
      break ;
    case 7:
      slit_index = 4 ;
      break ;
    case 8:
      slit_index = 12 ;
      break ;
    case 9:
      slit_index = 3 ;
      break ;
    case 10:
      slit_index = 13 ;
      break ;
    case 11:
      slit_index = 2 ;
      break ;
    case 12:
      slit_index = 14 ;
      break ;
    case 13:
      slit_index = 1 ;
      break ;
    case 14:
      slit_index = 15 ;
      break ;
    case 15:
      slit_index = 0 ;
      break ;
    case 16:
      slit_index = 31 ;
      break ;
    case 17:
      slit_index = 16 ;
      break ;
    case 18:
      slit_index = 30 ;
      break ;
    case 19:
      slit_index = 17 ;
      break ;
    case 20:
      slit_index = 29 ;
      break ;
    case 21:
      slit_index = 18 ;
      break ;
    case 22:
      slit_index = 28 ;
      break ;
    case 23:
      slit_index = 19 ;
      break ;
    case 24:
      slit_index = 27 ;
      break ;
    case 25:
      slit_index = 20 ;
      break ;
    case 26:
      slit_index = 26 ;
      break ;
    case 27:
      slit_index = 21 ;
      break ;
    case 28:
      slit_index = 25 ;
      break ;
    case 29:
      slit_index = 22 ;
      break ;
    case 30:
      slit_index = 24 ;
      break ;
    case 31:
      slit_index = 23 ;
      break ;
    default:
      sinfo_msg_error("wrong slitlet index: couldn't be a "
                      "spiffi image,  there must be 32 slitlets!") ;
      return -1 ;

                
   }
   return slit_index;

}

/**
   @name sinfo_sord_slitlets_array()

   @short sort spiffi slitlets

   @param  slit slit id
   @param  row_index array with sorted indexes

   @doc sort the slitlets in the right spiffi specific way
        the row_index describes the row index of the current slitlet 
        in the resulting cube images.





*/

static int
sinfo_sort_slitlets_array(const int slit, int* row_index)
{

  switch (slit)
    {
    case 0:
      row_index[0] = 8 ;
      break ;
    case 1:
      row_index[1] = 7 ;
      break ;
    case 2:
      row_index[2] = 9 ;
      break ;
    case 3:
      row_index[3] = 6 ;
      break ;
    case 4:
      row_index[4] = 10 ;
      break ;
    case 5:
      row_index[5] = 5 ;
      break ;
    case 6:
      row_index[6] = 11 ;
      break ;
    case 7:
      row_index[7] = 4 ;
      break ;
    case 8:
      row_index[8] = 12 ;
      break ;
    case 9:
      row_index[9] = 3 ;
      break ;
    case 10:
      row_index[10] = 13 ;
      break ;
    case 11:
      row_index[11] = 2 ;
      break ;
    case 12:
      row_index[12] = 14 ;
      break ;
    case 13:
      row_index[13] = 1 ;
      break ;
    case 14:
      row_index[14] = 15 ;
      break ;
    case 15:
      row_index[15] = 0 ;
      break ;
    case 16:
      row_index[16] = 31 ;
      break ;
    case 17:
      row_index[17] = 16 ;
      break ;
    case 18:
      row_index[18] = 30 ;
      break ;
    case 19:
      row_index[19] = 17 ;
      break ;
    case 20:
      row_index[20] = 29 ;
      break ;
    case 21:
      row_index[21] = 18 ;
      break ;
    case 22:
      row_index[22] = 28 ;
      break ;
    case 23:
      row_index[23] = 19 ;
      break ;
    case 24:
      row_index[24] = 27 ;
      break ;
    case 25:
      row_index[25] = 20 ;
      break ;
    case 26:
      row_index[26] = 26 ;
      break ;
    case 27:
      row_index[27] = 21 ;
      break ;
    case 28:
      row_index[28] = 25 ;
      break ;
    case 29:
      row_index[29] = 22 ;
      break ;
    case 30:
      row_index[30] = 24 ;
      break ;
    case 31:
      row_index[31] = 23 ;
      break ;
    default:
      sinfo_msg_error("wrong slitlet index: couldn't be a spiffi "
                      "image,  there must be 32 slitlets!\n") ;
      return -1 ;
    }

  return 0;

}



/**
   @name sinfo_new_make_cube_spi()

   @param  calibImage  re-sampled source image
   @param  slit_edges  absolute beginning and ending positions of
                                     slitlet, output of sinfo_fitSlits().
   @param  shift       sub_pixel shifts referred to the reference slit
                                     sinfo_edge

   @return  resulting source data cube

   @doc     makes a data cube out of a re-sampled source image
            this SPIFFI specific routine takes into account the
            Spiffi slitlet order on the detector.
            This routine takes fitted slitlet positions into account.
            Can do the same with the bad pixel map image to generate a
            bad pixel mask cube.


*/

cpl_imagelist * 
sinfo_new_make_cube_spi ( cpl_image *  calibImage,
                        float    ** slit_edges,
                        float    *  shift )
{
    cpl_imagelist * returnCube ;

    float * center ;
    int * row_index ;
    int slit ;
    int col, z ;
    int imsize ;
    int * beginCol ;
    int col_counter ;
    int ilx=0;
    int ily=0;


    float* pidata=NULL;


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

    if ( NULL == slit_edges )
    {
        sinfo_msg_error("no slit_edges array given from sinfo_fitSlits()!/n") ;
        return NULL ;
    }

    if ( N_SLITLETS != 32 )
    {
        sinfo_msg_error ("wrong number of slitlets given \n" ) ;
        return NULL ;
    }
    imsize = ilx / N_SLITLETS ;

    /* allocate memory */  
    if ( NULL == (row_index = (int*) cpl_calloc(N_SLITLETS, sizeof(int)) ) )
    {
        sinfo_msg_error ("cannot allocate memory \n" ) ;
        return NULL ;
    }
    if ( NULL == (beginCol = (int*) cpl_calloc(N_SLITLETS, sizeof(int)) ) )
    {
        sinfo_msg_error ("cannot allocate memory \n" ) ;
        cpl_free(row_index) ;
        return NULL ;
    }
    if ( NULL == (center = (float*) cpl_calloc(N_SLITLETS, sizeof(float)) ) )
    {
        sinfo_msg_error ("cannot allocate memory \n" ) ;
        cpl_free (row_index) ;
        cpl_free (beginCol) ;
        return NULL ;
    }
    if ( NULL == (returnCube = cpl_imagelist_new()) )
    {
        sinfo_msg_error ("cannot allocate new cube \n" ) ;
        cpl_free (row_index) ;
        cpl_free (beginCol) ;
        cpl_free (center) ;
        return NULL ;
    }
    /* determine the absolute center of the slitlets and the distances 
       inside the image*/
    for ( slit = 0 ; slit < N_SLITLETS ; slit++ ) 
    /* go through the slitlets of each row of the resampled image */
    {
        center[slit] = (slit_edges[slit][1] + slit_edges[slit][0]) / 2. ;
        /* -------------------------------------------------------------
         * sort the slitlets in the right spiffi specific way
         * the row_index describes the row index of the current slitlet 
         * in the resulting cube images.
         */
        if(-1 == sinfo_sort_slitlets_array(slit,row_index)) {
	  cpl_imagelist_delete(returnCube) ;
	  cpl_free (row_index) ;
          cpl_free (beginCol) ;
          cpl_free (center) ;
          return NULL ;
        }
        /* determine the integer column on which the slitlet starts, center the
           slitlet on the image row */
        float start = center[slit] - (float) (imsize - 1)/2. ;
        beginCol[slit] = sinfo_new_nint (start) ;
        /* determine the error of using integer pixels */
        float diff = start - (float)beginCol[slit] ;

        /*-------------------------------------------------------------------- 
         * determine the output shift values by which the rows are finally 
           shifted, consider the integer pixel errors  
         * resort shift array to get the row index 
         */
        shift[row_index[slit]] = diff ;
    }   

    /* now build the data cube out of the resampled image */
    for ( z = 0 ; z < ily ; z++ ) /* go through the z-axis */
    {
      cpl_image* o_img=cpl_image_new(imsize,N_SLITLETS,CPL_TYPE_FLOAT);
      float* podata=cpl_image_get_data_float(o_img);
      for ( slit = 0 ; slit < N_SLITLETS ; slit++ )
        {
      col_counter = beginCol[slit] ;
      /* each slitlet is centered on the final image row */
      for ( col = 0 ; col < imsize ; col++ )
            {
          if ( col_counter > ilx-1 )
                {
          col_counter-- ;
                }
          if ( col_counter + z*ilx < 0 )
                {
          podata[col+row_index[slit]*imsize] = pidata[0] ;
                }
              else
                {   
                  podata[col+row_index[slit]*imsize]=pidata[col_counter+z*ilx];
                }

                col_counter++ ;
            }
        }
      cpl_imagelist_set(returnCube,o_img,z);    
    }
    cpl_free (row_index) ;
    cpl_free (beginCol) ;
    cpl_free (center) ;

    return returnCube ;
}
/**
   @name   sinfo_new_make_cube_dist()

   @memo   makes a data cube out of a re-sampled source image

   @param  calibImage  re-sampled source image
   @param  firstCol    floating point value of the first column of
                                     the first slitlet in the re-sampled image,
                                     determined "by hand"
   @param  distances   distances of the slitlets from each other
                                     output of function ns_test
   @param  shift       dummy array with 32 elements

   @return              resulting source data cube

   @note       shift differences of the slitlets from
                               distance 32 given in the correct
                               Spiffi row sequence. The first slitlet
                               is the reference, therefore element
                               23 is set 0.

   @doc        makes a data cube out of a re-sampled source image
                        this SPIFFI specific routine takes into account the
                        Spiffi slitlet order on the detector.
                        Also shifts the resulting image rows by one pixel if
                        necessary according to the distances array gained from
                        the north-south test routine.
                Can do the same with the bad pixel map image to generate a
                bad pixel mask cube.

*/

cpl_imagelist * 
sinfo_new_make_cube_dist ( cpl_image * calibImage,
                         float      firstCol,
                         float    * distances,
                         float    * shift )
{
    cpl_imagelist * returnCube ;
    float di ;
    float start ;
    int * row_index ;
    int slit ;
    int col, z ;
    int imsize ;
    int * beginCol ;
    int col_counter ;
    int ilx=0;
    int ily=0;

    float* podata=NULL;
    float* pidata=NULL;

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

    if ( NULL == distances )
    {
        sinfo_msg_error("no distances array given from north_south_test()!") ;
        return NULL ;
    }

    if ( N_SLITLETS != 32 )
    {
        sinfo_msg_error ("wrong number of slitlets given \n" ) ;
        return NULL ;
    }
    imsize = ilx / N_SLITLETS ;

    /* allocate memory */  
    if ( NULL == (row_index = (int*) cpl_calloc(N_SLITLETS, sizeof(int)) ) )
    {
        sinfo_msg_error ("cannot allocate memory \n" ) ;
        return NULL ;
    }
    if ( NULL == (beginCol = (int*) cpl_calloc(N_SLITLETS, sizeof(int)) ) )
    {
        sinfo_msg_error ("cannot allocate memory \n" ) ;
        cpl_free(row_index) ;
        return NULL ;
    }
    if ( NULL == (returnCube = cpl_imagelist_new()) )
    {
        sinfo_msg_error ("cannot allocate new cube \n" ) ;
        cpl_free(row_index) ;
        cpl_free(beginCol) ;
        return NULL ;
    }

    di = 0. ;
    /* determine the absolute beginning of the slitlets and the distances 
       inside the image*/
    for ( slit = 0 ; slit < N_SLITLETS ; slit++ ) 
    /* go through the slitlets of each row of the resampled image */
    {

        /* -------------------------------------------------------------
         * sort the slitlets in the right spiffi specific way
         * the row_index describes the row index of the current slitlet 
         * in the resulting cube images.
         */
        if(-1 == sinfo_sort_slitlets_array(slit,row_index)) {
	  cpl_imagelist_delete(returnCube) ;
	  cpl_free(row_index) ;
	  cpl_free(beginCol) ;
	  return NULL ;
        }

        /* determine the integer column on which the slitlet starts */
        if ( slit == 0 )
        {
            start = firstCol ;
        }
        else
        {
            di += distances[slit-1] ;
            start = firstCol + di ;
        }
        beginCol[slit] = sinfo_new_nint(start) ;

        /* determine the error of using integer pixels, its always smaller 
           than 1 */
        float diff = start - (float)beginCol[slit] ;

        /*---------------------------------------------------------------- 
         * determine the output shift values by which the rows are finally 
         * shifted, consider the integer pixel errors and resort shift array 
         * to get the row index 
         */
        shift[row_index[slit]] = diff ;
    }   

    /* now build the data cube out of the resampled image */
    for ( z = 0 ; z < ily ; z++ ) /* go through the z-axis */
    {
      cpl_image* o_img=cpl_image_new(imsize,N_SLITLETS,CPL_TYPE_FLOAT);
      podata=cpl_image_get_data_float(o_img);
      for ( slit = 0 ; slit < N_SLITLETS ; slit++ )
        {
      col_counter = beginCol[slit] ;
      /* each slitlet is centered on the final image row */
      for ( col = 0 ; col < imsize ; col++ )
            {
          if ( col_counter > ilx-1 )
                {
          col_counter-- ;
                }
                if ( col_counter + z*ilx < 0 )
                {
          podata[col+row_index[slit]*imsize] = podata[0] ;
                }
                else
                {   
                  podata[col+row_index[slit]*imsize]=pidata[col_counter+z*ilx];
                }

                col_counter++ ;
            }
        }  
      cpl_imagelist_set(returnCube,o_img,z);  
    }
    cpl_free (row_index) ;
    cpl_free (beginCol) ;

    return returnCube ;
}
/**
   @name   sinfo_new_make_3D_cube_dist()

   @param  calibImage  re-sampled source image
   @param  firstCol    floating point value of the first column of
                                     the first slitlet in the re-sampled image,
                                     determined "by hand"
   @param  distances   distances of the slitlets from each other
                                     output of function ns_test
   @param  shift       dummy array with 32 elements

   @return resulting source data cube

   @note   shift differences of the slitlets from
                               distance 32 given in the correct
                               Spiffi row sequence. The first slitlet
                               is the reference, therefore element
                               23 is set 0.

   @doc       makes a data cube out of a re-sampled source image
                        this 3D specific routine takes into account the
                        3D slitlet order on the detector.
                        Also shifts the resulting image rows by one pixel if
                        necessary according to the distances array gained from
                        the north-south test routine.
              Can do the same with the bad pixel map image to generate a
              bad pixel mask cube.

*/


cpl_imagelist * 
sinfo_new_make_3D_cube_dist ( cpl_image * calibImage,
                           float      firstCol,
                           float    * distances,
                           float    * shift )
{
    cpl_imagelist * returnCube ;
    float di ;
    float start ;
    int * row_index ;
    int slit ;
    int col, z ;
    int imsize ;
    int * beginCol ;
    int col_counter ;
    int ilx=0;
    int ily=0;

    if ( NULL == calibImage )
    {
        sinfo_msg_error(" no resampled image given!\n") ;
        return NULL ;
    }
    ilx=cpl_image_get_size_x(calibImage);
    ily=cpl_image_get_size_y(calibImage);
    float* pidata=cpl_image_get_data_float(calibImage);

    if ( NULL == distances )
    {
        sinfo_msg_error("no distances array given from north_south_test()!") ;
        return NULL ;
    }

    if ( N_SLITLETS != 16 )
    {
        sinfo_msg_error ("wrong number of slitlets given \n" ) ;
        return NULL ;
    }
    imsize = ilx / N_SLITLETS ;

    /* allocate memory */  
    if ( NULL == (row_index = (int*) cpl_calloc(N_SLITLETS, sizeof(int)) ) )
    {
        sinfo_msg_error ("cannot allocate memory \n" ) ;
        return NULL ;
    }
    if ( NULL == (beginCol = (int*) cpl_calloc(N_SLITLETS, sizeof(int)) ) )
    {
        sinfo_msg_error ("cannot allocate memory \n" ) ;
        cpl_free(row_index) ;
        return NULL ;
    }
    if ( NULL == (returnCube = cpl_imagelist_new()) )
    {
        sinfo_msg_error ("cannot allocate new cube \n" ) ;
        cpl_free(row_index) ;
        cpl_free(beginCol) ;
        return NULL ;
    }

    di = 0. ;
    /* determine the absolute beginning of the slitlets and the distances 
       inside the image*/
    for ( slit = 0 ; slit < N_SLITLETS ; slit++ ) 
    /* go through the slitlets of each row of the resampled image */
    {

        /* --------------------------------------------------------------
         * sort the slitlets in the right 3D specific way
         * the row_index describes the row index of the current slitlet 
         * in the resulting cube images.
         */
        row_index[slit] = slit ;

        /* determine the integer column on which the slitlet starts */
        if ( slit == 0 )
        {
            start = firstCol ;
        }
        else
        {
            di += distances[slit-1] ;
            start = firstCol + di ;
        }
        beginCol[slit] = sinfo_new_nint(start) ;

        /* determine the error of using integer pixels, 
           `its always smaller than 1 */
        float diff = start - (float)beginCol[slit] ;

        /*---------------------------------------------------------------- 
         * determine the output shift values by which the rows are finally 
           shifted, consider the integer pixel errors and resort shift array 
           to get the row index 
         */
        shift[row_index[slit]] = diff ;
    }   

    /* now build the data cube out of the resampled image */
    for ( z = 0 ; z < ily ; z++ ) /* go through the z-axis */
    {
        cpl_image* o_img=cpl_image_new(imsize,N_SLITLETS,CPL_TYPE_FLOAT);
        float* podata=cpl_image_get_data_float(o_img);
        for ( slit = 0 ; slit < N_SLITLETS ; slit++ )
        {
            col_counter = beginCol[slit] ;
            /* each slitlet is centered on the final image row */
            for ( col = 0 ; col < imsize ; col++ )
            {
                if ( col_counter > ilx-1 )
                {
                    col_counter-- ;
                }
                podata[col+row_index[slit]*imsize]=pidata[col_counter+z*ilx];
                col_counter++ ;
            }
        } 
        cpl_imagelist_set(returnCube,o_img,z);   
    }
    cpl_free (row_index) ;
    cpl_free (beginCol) ;

    return returnCube ;
}

/**
  @name   sinfo_new_make_3D_cube()

  @param  calibImage  resampled source image
  @param  kpixshift   integer pixel shifts of the resulting image
                                     plane rows.
  @param  kpixfirst   first valid pixel

  @return resulting source data cube

  @doc    makes a data cube out of a re-sampled source image
          this MPE 3D specific routine takes into account the
          3D slitlet order on the detector.
          Also shifts the resulting image row by an integer pixel shift if
          necessary according to the input kpixshift array 
          Can do the same with the bad pixel map image to generate a
          bad pixel mask cube.

*/


cpl_imagelist * 
sinfo_new_make_3D_cube ( cpl_image * calibImage,
                       int      * kpixshift, 
                       int        kpixfirst )
{
    cpl_imagelist * returnCube ;
    int imsize;
    int z, col;
    int ilx=0;
    int ily=0;

    if ( NULL == calibImage )
    {
        sinfo_msg_error("no resampled image given!\n") ;
        return NULL ;
    }
    ilx=cpl_image_get_size_x(calibImage);
    ily=cpl_image_get_size_y(calibImage);
    float* pidata=cpl_image_get_data_float(calibImage);

    if ( NULL == kpixshift )
    {
        sinfo_msg_error("no shift array given!/n") ;
        return NULL ;
    }

    if ( kpixfirst < 0 )
    {
        sinfo_msg_error("wrong first valid pixel given!/n") ;
        return NULL ;
    }

    if ( N_SLITLETS != 16 )
    {
        sinfo_msg_error ("wrong number of slitlets given \n" ) ;
        return NULL ;
    }
    imsize = ilx / N_SLITLETS ;

    if ( NULL == (returnCube = cpl_imagelist_new()) )
    {
        sinfo_msg_error ("cannot allocate new cube \n" ) ;
        return NULL ;
    }

    /* now build the data cube out of the resampled image */
    for ( z = 0 ; z < ily ; z++ ) /* go through the z-axis */
    {
        cpl_image* o_img=cpl_image_new(imsize,N_SLITLETS,CPL_TYPE_FLOAT);
        float* podata=cpl_image_get_data_float(o_img);
        int kpix       = 0 ;
        int kslit      = 0 ;
        int recol      = -1 ;
        for ( col = 0 ; col < ilx ; col++ ) /* go through the image columns */
        {
            if ( col % imsize == 0 ) 
            {
                recol = 0 ;
                kslit = col/imsize ;
                kpix  = kpixfirst + kpixshift[kslit] ;
            }

            /* fill each cube plane with one image row */
            podata[recol+kslit*imsize] = pidata[col+kpix+z*ilx] ;
            recol++ ;
            if ( recol > imsize )
            {
                sinfo_msg_error("wrong column of reconstructed image, i"
                                "shouldn't happen!\n") ; 
                cpl_imagelist_delete(returnCube) ;
                return NULL ;
            }
        }
        cpl_imagelist_set(returnCube,o_img,z);
    }
    return returnCube ;
}

/**
   @name     sinfo_new_determine_mask_cube()
   @memo converts re-sampled bad pixels to real bad pixels in data cubes.

   @param    sourceMaskCube  bad pixel mask cube generated by using
                              the bad pixel mask frame (0: bad, 1: good)
                              and going through the same reduction steps 
                              as with the observation frame.
   @param lowLimit        low limit of pixel value (about -0.7)
   @param highLimit       high limit of bad pixel value (about 0.7)

   @return resulting bad pixel data cube (bad pixels: 0, good pixels: 1).


*/

cpl_imagelist * 
sinfo_new_determine_mask_cube ( cpl_imagelist * sourceMaskCube,
                              float     lowLimit,
                              float     highLimit )
{
    cpl_imagelist * retCube ; 
    int z, n ;
    int ilx=0;
    int ily=0;
    int inp=0;
    int olx=0;
    int oly=0;
    int onp=0;



    if ( sourceMaskCube == NULL )
    {
        sinfo_msg_error("no cube given!\n") ;
        return NULL ;
    }
    ilx=cpl_image_get_size_x(cpl_imagelist_get(sourceMaskCube,0));
    ily=cpl_image_get_size_y(cpl_imagelist_get(sourceMaskCube,0));
    inp=cpl_imagelist_get_size(sourceMaskCube);


    if ( lowLimit > 0. )
    {
        sinfo_msg_error("lowLimit wrong!\n") ;
        return NULL ;
    }
    if ( highLimit >= 1. || highLimit < 0. )
    {
        sinfo_msg_error("highLimit wrong!\n") ;
        return NULL ;
    }

    retCube = cpl_imagelist_duplicate (sourceMaskCube) ;
    onp=inp;
    olx=ilx;
    oly=ily;

    for ( z = 0 ; z < onp ; z++ )
    {
      cpl_image* o_img=cpl_imagelist_get(retCube,0);
      float* podata=cpl_image_get_data_float(o_img);
        for ( n = 0 ; n < (int) olx*oly; n++ )
        {
            if ( podata[n] == 0. )
            {
               continue ;
            }
            if ( podata[n] == 1. )
            {
               continue ;
            }
            if ( podata[n] >= lowLimit && 
                 podata[n] <= highLimit )
            {
                podata[n] = 0. ;
            }
            else 
            {
                podata[n] = 1. ;
            }
        }
    }
    return retCube ;
}
/**
   @name     sinfo_new_interpol_cube()

   @param    sourceCube  reconstructed source cube from sinfo_makeCube
                                     without fine tuning of rows
   @param    maskCube    bad pixel mask cube, bad pixel are marked
                          with 0., good and interpolated pixels with 1.
                          this maskCube is changed within the routine
                          if a bad pixel was interpolated.
   @param    n_neighbors number of neighbors in one spectral direction
                          with which the bad pixel will be interpolated (7)
   @param    max_radius  maximal pixel radius within an image plane
                          inside which valid pixels are searched to
                          be used for interpolation. If there aren't
                          found 9 good neighboring pixels within this
                          radius the loop is left. (5)

   @rturn   resulting interpolated data cube.
             changed maskCube at the positions of the interpolated pixels

   @doc      Bad pixel interpolation 3D like (saturated pixels exist):
             interpolates the bad pixels of the source cube by
             using the nearest neighbours.
             first it is checked if the bad pixel is interpolatable:
             it is only interpolatable if the number of good pixels
             in its spectrum of length 2*n_neighbors+1 exceeds 3 and
             if there is at least one good pixel on either side of the
             central pixel.
             Afterwards good neighbouring pixels are searched within the
             image plane of the bad pixel by using an increasing pixel radius.
             Good pixels mean, the corresponding spectral pixels of the
             bad pixel and its spatial neighbouring pixel must have
             at least 2 valid pixel pairs to be able to be used for
             the interpolation. The search is stopped if 9 valid neighbouring
             pixels are found.
             Now normalise the found spectral values, collect the valid pixels
             (there must be at least 18) and take the sinfo_median of the valid
             pixels with which the bad pixel is replaced.

*/


cpl_imagelist * 
sinfo_new_interpol_cube ( cpl_imagelist * sourceCube,
                         cpl_imagelist * maskCube,
                         int       n_neighbors, /* 7 */
                         int       max_radius ) /* 5 */
{
    cpl_imagelist  * returnCube ;
    float** spec=NULL ;
    float* spec1=NULL ;
    int n_im, n_bad, n_bad1, n_bad2 ;
    int n_planes, specn, nspec1 ;
    int i, m, n, z, ni, kk, p ;
    int dis, dismin, dismax ;
    int agreed ;
    int xcordi, ycordi, xcordm, ycordm ;



    int ilx=0;
    int ily=0;
    int inp=0;
   
    float* pidata=NULL;
    float* pmdata=NULL;


    if ( NULL == sourceCube )
    {
        sinfo_msg_error(" no source cube given!\n") ;
        return NULL ;
    }


    ilx=cpl_image_get_size_x(cpl_imagelist_get(sourceCube,0));
    ily=cpl_image_get_size_y(cpl_imagelist_get(sourceCube,0));
    inp=cpl_imagelist_get_size(sourceCube);

    if ( NULL == maskCube )
    {
        sinfo_msg_error("no bad pixel mask cube given!\n") ;
        return NULL ;
    }

    if ( n_neighbors <= 0 )
    {
        sinfo_msg_error("wrong number of neighbors in the spectral "
                        "direction given!") ;
        return NULL ;
    }
   
    if ( max_radius <= 0 )
    {
        sinfo_msg_error("wrong maximal radius for interpolation inside "
                        "an image plane given!") ;
        return NULL ;
    }

    returnCube = cpl_imagelist_duplicate(sourceCube) ;
    
    n_im     = ilx * ily ;
    n_planes = inp ;

    spec1=cpl_calloc(300,sizeof(float)) ;
    spec=sinfo_new_2Dfloatarray(100,2*n_neighbors+1) ;

    /* loop over the image planes and look for bad pixels and correct them */
    for ( z = 0 ; z < n_planes ; z++ ) /* go through image planes */
    {
      cpl_image* m_img=cpl_imagelist_get(maskCube,z);
      pmdata=cpl_image_get_data_float(m_img);
      cpl_image* o_img=cpl_imagelist_get(returnCube,z);
      float* podata=cpl_image_get_data_float(o_img);

        /*-------------------------------------------------------------------
         * determine n, the length of one wing in one spectrum with which the 
         * bad pixel will be interpolated. The length of one wing is 
           n_neighbors but less at the edges of the cube. 
         */
        if ( z < n_neighbors )
        {
            n = z ;
        }
        else if ( n_planes - z <= n_neighbors)
        {
            n = n_planes - z -1 ;
        }
        else
        { 
            n = n_neighbors ;
        }

        for ( i = 0 ; i < n_im ; i ++ ) /* go through one image */
        {
            /* continue if the pixel is a good one */
            if ( pmdata[i] != 0. )
            {
                continue ;
            }
        
            /*-------------------------------------------------------------
             * exclude pixels with too many bad neighbors in the spectrum.
             * exit if: too few good pixels in the neighboring spectrum or 
             * good pixels are only on one side of the spectrum.
             */
            n_bad  = 0 ;
            n_bad1 = 0 ;
            n_bad2 = 0 ;
            /* go through the neighbor spectral pixels */
            for ( ni = z-n ; ni <= z+n ; ni++ ) 
            {
                if ( pmdata[i] == 0. )
                {
                    n_bad++ ;
                    /* count bad pixels on either spectral side of 
                       the bad pixel to be interpolated */
                    if ( ni < z )
                    {
                        n_bad1++ ;
                    }
                    if ( ni > z )
                    {
                        n_bad2++ ;
                    }
                }
            }
         
            /*--------------------------------------------------------------- 
             * now the criteria are checked which the neighborhood in the 
               spectral dimension has to match if the pixel is interpolatable.
             * The total number of the good pixel in the spectrum must be more 
               than 3 and there must be at least one good pixel on either side 
               of the central pixel.
             */
            if ( (2*n+1 - n_bad) < 3 || (n - n_bad1) < 1 || (n - n_bad2) < 1 )
            {
                continue ;
            }
            
            /* read the master spectrum into the first row of the array spec */
            kk = 0 ;
            cpl_image* i_img;
            for ( ni = z-n ; ni <= z+n ; ni++ )
            {
              i_img=cpl_imagelist_get(sourceCube,ni);
              pidata=cpl_image_get_data_float(i_img);
                spec[1][kk] = pmdata[i] != 0. ? pidata[i] : ZERO ;
                kk++ ; /* length of spectrum */
            }
            
            /* look for appropriate neighbors in the x-y neighborhood */
            agreed = 1 ; /* loop guard */
            specn  = 2 ; /* number of spectra in spec. 
                            First is master spectrum */
            dismin = 0 ; /* x+y minimal distance to bad pixel */
            dismax = 1 ; /* x+y maximal distance to bad pixel */
            do
            {
                for ( m = 0 ; m < n_im ; m++ )
                {
                    if ( pmdata[m] == 0. )
                    {
                        continue ;
                    }

                    /* --------------------------------------------------------
                     * determine the x and y coordinates of the bad pixel (i)
                     * and the pixels used to interpolate (m) 
                     */
                    xcordi = i % ilx ;
                    xcordm = m % ilx ;
                    ycordi = i / ilx ;
                    ycordm = m / ilx ;
                    /*----------------------------------------------------- 
                     * check the distance: take only close pixels
                     * extension 'i' is coordinate of the bad pixel to be 
                       interpolated
                     */
                    dis = abs(xcordi-xcordm) + abs(ycordi-ycordm) ;
                    if ( dis <= dismin || dis > dismax )
                    {
                        continue ;
                    }
                    /*--------------------------------------------------------
                     * check on number of bad pixels in the spectrum of a 
                     * neighbor pixel; reject it if it contains less than 2 
                     * usable pixel pairs. a bit more explanation:
                     * let this be a 15 pixel spectrum with the pixel to be 
                     * interpolated denoted by '0' and other bad pixels marked 
                     * with 'b'. Good pixels are marked with '1'. Below a 
                     * neighbor spectrum is drawn containing bad pixels as 
                     * well. The third line shows the position of the usable 
                     * pixel pairs, spectral
                     * positions, where both spectra have valid pixels.
                     *
                     *   1 1 1 b b 1 1 0 b 1 b b 1 b b
                     *   b 1 1 1 b b 1 1 1 1 1 1 b b 1
                     *     ^ ^       ^     ^             4 good pixel pairs
                     */
      
                    n_bad = 0 ;
                    for ( ni = z-n ; ni <= z+n ; ni++ )
                    {
                        if ( pmdata[i] == 0. || pmdata[m] == 0. )
                        {
                            n_bad++ ;
                        }
                    }
                    if ( n_bad > 2*n-1 ) 
                    /* we need at least 2 usable pixel pairs */
                    {
                        continue ;
                    }
                    
                    /* transfer the spectrum to the next position 
                       of array spec */
                    kk = 0 ;
                    for ( ni = z-n ; ni <= z+n ; ni++ )
                    {
              i_img=cpl_imagelist_get(sourceCube,ni);
              pidata=cpl_image_get_data_float(i_img);
                        spec[specn][kk] = pmdata[m] != 0. ? pidata[m] : ZERO ;
                        kk++ ;
                    }
                    specn++ ;
                    if ( specn > 10 ) /* if we have 9 neighbors then break */
                    {
                        agreed = 0 ;
                        break ;
                    }
                }
                /* if no break, increase search radius and continue */
                dismin++ ;        
                dismax++ ;
                /* if search radius is too big, exit with fewer 
                   good neighbors */
                if ( dismax > max_radius )
                {
                    agreed = 0 ;
                }
            }   while(agreed) ;    
                       
            specn-- ;
            dismax -= 2 ;
            /* TODO: why compute dismax is later this is not used? */
            /* ---------------------------------------------------------------
             * Take the master spectrum with the bad pixel in the middle and 
               divide it by each of the neighbor spectra and normalize the 
               division with the value in  the center position.
             */
            for ( kk = 0 ; kk < 2*n+1 ; kk++ )
            {
                if ( kk == n )    /* do not divide the master bad pixel */
                {
                    continue ;
                }

                /* do not divide bad pixels in the master spectrum */
                if ( isnan(spec[1][kk]) ) 
                {
                    for ( p = 2 ; p <= specn ; p++ )
                    {
                        spec[p][kk] = ZERO ;
                    }
                }    
                else       /* all is well, now divide */
                {
                    for ( p = 2 ; p <= specn ; p++ )
                    {
                        if ( !isnan(spec[p][kk]) && spec[p][kk] != 0. &&
                             !isnan(spec[p][n]) )
                        {
                            spec[p][kk] = spec[1][kk] / 
                                          spec[p][kk] * spec[p][n] ;
                        }
                        else
                        {
                            spec[p][kk] = ZERO ;
                        }
                    }
                }
            }
 
            /*-----------------------------------------------------------------
             * determine the sinfo_median of all values. With 9 good neighbors 
             * and at least 2 good values per neighbor we have between 18 and 
             * 9*14 values for the statistics. If there are not enough good 
             * neighbors available, only continue if we have collected at 
             * least 18 values.
             */
            nspec1 = 0 ;  
            /* collect the good values in the array spec1 */ 
            for ( p = 2 ; p <= specn ; p++ )
            {
                for ( kk = 0 ; kk < 2*n+1 ; kk++ )
                {
                    if ( !isnan(spec[p][kk]) && kk != n )
                    {
                        spec1[nspec1] = spec[p][kk] ;
                        nspec1++ ;
                    }
                }
            }
            
            /* now test if we have at least 18 values */
            if ( nspec1 < 18 )
            {
                continue ;
            }
 
            /* interpolate the bad pixel by the sinfo_median of spec1 */
            podata[i] = sinfo_new_median(spec1, nspec1) ;
            pmdata[i] = 1 ;
        }
    }           
    sinfo_new_destroy_2Dfloatarray(&spec,2*n_neighbors+1) ;
    cpl_free(spec1);
    return returnCube ;
}
/**
   @name     sinfo_new_fine_tune_cube()

   @param    cube  cube, output of sinfo_makeCube
   @param  correct_diff_dist  differences of the slitlets from
                              distance 32 given in the correct
                              Spiffi row sequence. The first slitlet
                              is the reference, therefore element
                              23 is set 0.
                              Output of sinfo_makeCube!
   @param n_order interpolating polynomial order

   @return resulting data cube having the exact row positions

   @doc    fine tunes each row in the right position according
           to the distances of the slitlets to each other
           (output of the north-south test).
           This means that the rows must be realigned by a
           fraction of a pixel to accomodate non-integer slit
           length. The fractional realignment is done by using
           tanh interpolation.
           Each row is rescaled so that the total flux is conserved.


*/
 
cpl_imagelist * 
sinfo_new_fine_tune_cube( cpl_imagelist * cube,
                                     float   * correct_diff_dist,
                                     int       n_order )
{
    cpl_imagelist * returnCube ;
    float* row_data=NULL ;
    float* corrected_row_data=NULL ;
    float* xnum=NULL ;
    float sum, new_sum ;
    float eval/*, dy*/ ;
    float * imageptr ;
    int row, col ;
    int i, z ;
    int imsize, n_points ;
    int firstpos ;
    int  flag;
    int ilx=0;
    int ily=0;
    int inp=0;

    if ( NULL == cube )
    {
        sinfo_msg_error("no input cube given!\n") ;
        return NULL ;
    }
    ilx=cpl_image_get_size_x(cpl_imagelist_get(cube,0));
    ily=cpl_image_get_size_y(cpl_imagelist_get(cube,0));
    inp=cpl_imagelist_get_size(cube);

    if ( NULL == correct_diff_dist )
    {
        sinfo_msg_error("no distances array from ns_test given!n") ;
        return NULL ;
    }
  
    if ( n_order <= 0 )
    {
        sinfo_msg_error("wrong order of interpolation polynom given!") ;
    returnCube = cpl_imagelist_duplicate(cube);
        return returnCube ;
    }

    returnCube = cpl_imagelist_duplicate(cube);
    
    imsize = ily ;
    if ( imsize != N_SLITLETS )
    {
        sinfo_msg_error ("wrong image size\n" ) ;
        sinfo_free_imagelist(&returnCube);
        return NULL ;
    }

    n_points = n_order + 1 ;
    if ( n_points % 2 == 0 )
    {
        firstpos = (int)(n_points/2) - 1 ;
    }
    else
    {
        firstpos = (int)(n_points/2) ;
    }
    xnum=cpl_calloc(n_order+1,sizeof(float)) ;

    for ( i = 0 ; i < n_points ; i++ )
    {
        xnum[i] = i ;
    }    

    row_data=cpl_calloc(ilx,sizeof(float)) ;
    corrected_row_data=cpl_calloc(ilx,sizeof(float)) ;

    for ( z = 0 ; z < inp ; z++ )
    {
      cpl_image* i_img=cpl_imagelist_get(cube,z);
      float* pidata=cpl_image_get_data_float(i_img);
      cpl_image* o_img=cpl_imagelist_get(returnCube,z);
      float* podata=cpl_image_get_data_float(o_img);


        for ( row = 0 ; row < imsize ; row++ )
        {
            for ( col = 0 ; col < ilx ; col++ )
            {
                corrected_row_data[col] = 0. ;
            }
            sum = 0. ; 
            for ( col = 0 ; col < ilx ; col++ )
            {
                row_data[col] = pidata[col+row*ilx] ;
                if ( isnan(row_data[col]) )
                {
                    row_data[col] = 0. ;
                    for ( i = col - firstpos ; 
                          i < col -firstpos+n_points ; i++ )
                    {
                        if ( i < 0 ) continue ;
                        if ( i >= ilx) continue ; 
                        corrected_row_data[i] = ZERO ;
                    }
                }
                if ( col != 0 && col != ilx - 1 )
                {
                    sum += row_data[col] ;
                }
            }

           
            new_sum = 0. ;
            for ( col = 0 ; col < ilx ; col++ )
            {
                
                if ( isnan(corrected_row_data[col]) )
                {
                    continue ;
                }
                if ( col - firstpos < 0 )
                {
                    imageptr = &row_data[0] ;
                    eval     = correct_diff_dist[row] + col ;
                }
                else if ( col - firstpos + n_points >= ilx )
                {
                    imageptr = &row_data[ilx - n_points] ;
                    eval     = correct_diff_dist[row] + col + n_points - ilx ;
                }
                else
                {
                    imageptr = &row_data[col-firstpos] ;
                    eval     = correct_diff_dist[row] + firstpos ;
                }

        
        flag = 0;
        corrected_row_data[col]=sinfo_new_nev_ille(xnum, imageptr, 
                                                       n_order, eval, &flag);

               
                if ( col != 0 && col != ilx - 1 )
                {
                    new_sum += corrected_row_data[col] ;
                }
            }
            for ( col = 0 ; col < ilx ; col++ )
            {
                
                if ( col == 0 )
                {
                    podata[col+row*ilx] = ZERO ;
                }
                else if ( col == ilx - 1 )
                {
                    podata[col+row*ilx] = ZERO ;
                }
                else
                {
                    if ( isnan(corrected_row_data[col]) ) 
                    {
                        podata[col+row*ilx] = ZERO ;
                    }
                    else
                    {
                        if ( new_sum == 0. ) new_sum = 1. ;
                     
                        podata[col+row*ilx] = corrected_row_data[col] ;
                    }
                }
            }
        }
    }       

    cpl_free(xnum) ;
    cpl_free(row_data) ;
    cpl_free(corrected_row_data) ;

    return returnCube ;
}

/**
   @name   sinfo_new_fine_tune_cube_by_FFT()

   @param  cube:  cube, output of sinfo_makeCube
   @param  correct_diff_dist: differences of the slitlets from
                  distance 32 given in the correct
                  Spiffi row sequence. The first slitlet
                  is the reference, therefore element
                  23 is set 0.
                  Output of sinfo_makeCube!

   @return  resulting data cube having the exact row positions

   @doc    fine tunes each row in the right position according
                        to the distances of the slitlets to each other
                        (output of the north-south test).
                        This means that the rows must be realigned by a
                        fraction of a pixel to accomodate non-integer slit
                        length. The fractional realignment is done by using
                        the FFT algorithm four1() of N.R.


*/

cpl_imagelist * 
sinfo_new_fine_tune_cube_by_FFT( cpl_imagelist * cube,
                                           float   * correct_diff_dist )
{
    cpl_imagelist * returnCube ;

    float* row_data=NULL ;
    dcomplex* data=NULL ;
    dcomplex* corrected_data=NULL ;

    unsigned nn[2];
    /*float corrected_row_data[cube->lx] ;*/
    float phi, pphi ;
    float coph, siph ;
    int row, col ;
    int i, z ;
    int imsize ;
    int blank_indicator ;


    int ilx=0;
    int ily=0;
    int inp=0;
   





    if ( NULL == cube )
    {
        sinfo_msg_error(" no input cube given!\n") ;
        return NULL ;
    }
    ilx=cpl_image_get_size_x(cpl_imagelist_get(cube,0));
    ily=cpl_image_get_size_y(cpl_imagelist_get(cube,0));
    inp=cpl_imagelist_get_size(cube);

    nn[1] = ilx ;
    if ( NULL == correct_diff_dist )
    {
        sinfo_msg_error("no distances array from ns_test given!") ;
        return NULL ;
    }

    returnCube = cpl_imagelist_duplicate( cube ) ;

    imsize = ily ;
    if ( imsize != N_SLITLETS )
    {
        sinfo_msg_error ("wrong image size\n" ) ;
        sinfo_free_imagelist(&returnCube);
        return NULL ;
    }

    data=cpl_calloc(ilx,sizeof(dcomplex)) ;
    corrected_data=cpl_calloc(ilx,sizeof(dcomplex)) ;

    row_data=cpl_calloc(ilx,sizeof(float)) ;
    /* loop over the image planes */
    for ( z = 0 ; z < inp ; z++ )
    {
      cpl_image* i_img=cpl_imagelist_get(cube,z);
      float* pidata=cpl_image_get_data_float(i_img);
      cpl_image* o_img=cpl_imagelist_get(returnCube,z);
      float* podata=cpl_image_get_data_float(o_img);
        /* consider one row at a time */
        for ( row = 0 ; row < imsize ; row++ )
        {
            blank_indicator = 1 ;
            for ( col = 0 ; col < ilx ; col++ )
            {
                /* transfer the row data to a double sized array */
                row_data[col] = pidata[col+row*ilx] ;
        data[col].x = row_data[col] ;
        data[col].y = 0. ;
                /* if row contains a blank pixel proceed */
                if ( isnan(row_data[col]) )
                {
                    blank_indicator = 0 ; 
                }
            }

            /* if row contains a blank don't apply FFT but proceed */
            if ( blank_indicator == 0 )
            {
                for ( col = 0 ; col < ilx ; col++ )
                {
                    podata[col+row*ilx] = ZERO ;
                }
                continue ;
            }
           
            /* FFT algorithm of eclipse */
            sinfo_fftn( data, nn, 1, 1 ) ;
 
            /* calculate the corrected phase shift for each frequency */
            phi = 2*PI_NUMB/(float)ilx * correct_diff_dist[row] ;
            for ( i = 0 ; i < ilx ; i++ )
            {
                /* positive frequencies */
                if ( i <= ilx/2 )
                {
                    /* phase shift */
                    pphi = phi * (float)(i) ;
                    /* Euler factor */
                    coph = cos ( pphi ) ;
                    siph = sin ( pphi ) ;
                }
                else /* negative frequencies */
                {
                    /* phase shift */
                    pphi = phi * (float)(i - ilx/2) ;
                    /* Euler factor */
                    coph = cos ( pphi ) ;
                    siph = sin ( pphi ) ;
                }

                /* ------------------------------------------------------------
                 * now calculate the shift in the pixel space by multiplying
                 * the fourier transform by the Euler factor of the phase shift
                 * and inverse fourier transforming.
                 * used Fourier pair: h(x-x0) <==> H(k)*exp(2*pi*i*k*x0) 
                 */
                /* calculate real part */
                corrected_data[i].x   = data[i].x * coph - data[i].y * siph ; 
                /* calculate imaginary part */
                corrected_data[i].y = data[i].x * siph + data[i].y * coph ;
            }
 
            /* transform back: inverse FFT */
            sinfo_fftn( corrected_data, nn, 1, -1 ) ;

            /* normalize */ 
            for ( i = 0 ; i < ilx ; i++ )
            {
                corrected_data[i].x /= ilx ;
        corrected_data[i].y /= ilx ;
            } 

            /* now transfer row to output, leave the left-most 
               and right-most pixel column */
            for ( col = 0 ; col < ilx ; col++ )
            {
                if ( col == 0 )
                {
                    podata[col+row*ilx] = ZERO ;
                }
                else if ( col == ilx - 1 )
                {
                    podata[col+row*ilx] = ZERO ;
                }
                else
                {
                    podata[col+row*ilx] = corrected_data[col].x ; 
                }
            }
        }
    }

    cpl_free(data) ;
    cpl_free(corrected_data) ;


    cpl_free(row_data);
    return returnCube ;
}
/**
   @name    sinfo_new_fine_tune_cube_by_spline()
   @param   cube:  cube, output of sinfo_makeCube
   @param   correct_diff_dist: differences of the slitlets from
                        distance 32 given in the correct
                        Spiffi row sequence. The first slitlet
                        is the reference, therefore element
                        23 is set 0.
                        Output of sinfo_makeCube!
   @return  resulting data cube having the exact row positions


   @doc     fine tunes each row in the right position according
                        to the distances of the slitlets to each other
                        (output of the north-south test).
                        This means that the rows must be realigned by a
                        fraction of a pixel to accomodate non-integer slit
                        length. The fractional realignment is done by using
                        the spline interpolation algorithm splint in connection
                        with the algorithm spline of N.R.
                        This algorithms assume that each row is a tabulated
                        function. The first derivatives of the interpolating
                        function at the first and last point must be given.
                        These are set higher than 1xe^30, so the routine
                        sets the corresponding boundary condition for a natural
                        spline, with zero second derivative on that boundary.
                        Each row is rescaled so that the total flux is
                        conserved.

 */
cpl_imagelist * sinfo_new_fine_tune_cube_by_spline ( cpl_imagelist * cube,
                                               float   * correct_diff_dist )
{
    cpl_imagelist * returnCube ;

    float* row_data=NULL ;
    float* corrected_row_data=NULL ;
    float* xnum=NULL ;
    float* eval=NULL ;

    float sum, new_sum ;
    int row, col ;
    int i, z ;
    int imsize ;
    int ilx=0;
    int ily=0;
    int inp=0;
 
    if ( NULL == cube )
    {
        sinfo_msg_error("no input cube given!\n") ;
        return NULL ;
    }
    ilx=cpl_image_get_size_x(cpl_imagelist_get(cube,0));
    ily=cpl_image_get_size_y(cpl_imagelist_get(cube,0));
    inp=cpl_imagelist_get_size(cube);

    if ( NULL == correct_diff_dist )
    {
        sinfo_msg_error("no distances array from ns_test given!/n") ;
        return NULL ;
    }
  
    imsize = ily ;
    if ( imsize != N_SLITLETS )
    {
        sinfo_msg_error ("wrong image size\n" ) ;
        return NULL ;
    }
  
    returnCube = cpl_imagelist_duplicate( cube ) ;

    row_data=cpl_calloc(ilx,sizeof(float)) ;
    corrected_row_data=cpl_calloc(ilx,sizeof(float)) ;
    xnum=cpl_calloc(ilx,sizeof(float)) ;
    eval=cpl_calloc(ilx,sizeof(float)) ;
  
    /* fill the xa[] array for a polynomial interpolation */
    for ( i = 0 ; i < ilx ; i++ )
    {
        xnum[i] = i ;
    }    

    /* loop over the image planes */
    for ( z = 0 ; z < inp ; z++ )
    {
      cpl_image* i_img=cpl_imagelist_get(cube,z);
      float* pidata=cpl_image_get_data_float(i_img);
      cpl_image* o_img=cpl_imagelist_get(returnCube,z);
      float* podata=cpl_image_get_data_float(o_img);
        /* consider 1 row at a time */
        for ( row = 0 ; row < imsize ; row++ )
        {
            for ( col = 0 ; col < ilx ; col++ )
            {
                corrected_row_data[col] = 0. ;
            }
        sum = 0. ; /* initialize flux for later rescaling */
            /* go through the columns and compute the flux for each 
               row (leave the sinfo_edge points) */
            for ( col = 0 ; col < ilx ; col++ )
            {   
            eval[col] = correct_diff_dist[row] + (float)col ;
                row_data[col] = pidata[col+row*ilx] ;
                if (col != 0 && col != ilx - 1 && !isnan(row_data[col]) )
                {
                    sum += row_data[col] ;
                }
                if (isnan(row_data[col]) )
                {
                    for ( i = col -1 ; i <= col+1 ; i++ ) 
                    {
                        if ( i < 0 ) continue ;
                        if ( i >= ilx ) continue ;
                        corrected_row_data[i] = ZERO ; 
                    }
                    row_data[col] = 0. ;
                }
        }


            /* ---------------------------------------------------------------
             * now we do the cubic spline interpolation to achieve the 
               fractional (see eclipse).
             */
            if ( -1 == sinfo_function1d_natural_spline(xnum,row_data, ilx, 
                                                       eval,corrected_row_data,
                                                       ilx ) )
        {
            sinfo_msg_error("error in spline interpolation\n") ;
        cpl_imagelist_delete(returnCube) ;
        return NULL ;
        }    

            new_sum = 0. ;
            for ( col = 0 ; col < ilx ; col++ )
            {
                if (isnan(corrected_row_data[col])) continue ;
                /* don't take the sinfo_edge points to calculate 
                   the scaling factor */
                if ( col != 0 && col != ilx - 1 )
                {
                    new_sum += corrected_row_data[col] ;
                }
            }
            for ( col = 0 ; col < ilx ; col++ )
            {
                /* ----------------------------------------------------------
                 * rescale the row data and fill the returned cube, 
                 * leave the left-most and right-most
                 * pixel column 
                 */
                if ( col == 0 )
                {
                    podata[col+row*ilx] = ZERO ;
                }
                else if ( col == ilx - 1 )
                {
                    podata[col+row*ilx] = ZERO ;
                }
                else
                {
                    if ( isnan(corrected_row_data[col]) ) 
                    {
                        podata[col+row*ilx] = ZERO ;
                    }
                    else
                    {
                        if (new_sum == 0.) new_sum = 1. ;
                     /* rescaling is commented out because it delivers 
                        wrong results
                        in case of appearance of blanks or bad pixels */
                  /*       corrected_row_data[col] *= sum / new_sum ; */
                        podata[col+row*ilx] = corrected_row_data[col] ;
                    }
                }
            }
        }
    }       

    cpl_free(row_data) ;
    cpl_free(corrected_row_data) ;
    cpl_free(xnum) ;
    cpl_free(eval) ;

    return returnCube ;
}

/**
@name sinfo_new_calibrate_ns_test perform a North South test

   @param ns_image:   north-south image
   @param n_slitlets: number of slitlets
   @param halfWidth:  half width of the box in which the lines
                                    are fit by a sinfo_gaussian
   @param fwhm:       first guess of the full width at half maximum
   @param minDiff:    minimum amplitude below which the fit
                                    will not be carried through
   @param estimated_dist: estimated average distance of spectra
   @param devtol:     maximal pixel deviation of the distances from
                                    slitlet center
   @param bottom  bottom image raw
   @param top     top image raw 

   @return      array of the distances of the slitlets from each other
   @doc determines the distances of the spectra from the center
        of the slitlets to be able to find the correct angle of
        the north-south entrance slit.
*/

float * 
sinfo_new_calibrate_ns_test( cpl_image * ns_image,
                           int        n_slitlets,
                           int        halfWidth,
                           float      fwhm,
                           float      minDiff,
                           float      estimated_dist,
                           float      devtol,
               int        bottom,
               int        top )
{
    int i, j, k, m, row, col, n, ni, na ;
    int position, counter;
    int xdim, ndat, its, numpar ;
    float sum, mean, maxval ;
    float tol, lab ;
    float * distances ;
    float * ret_distances ;

    float * xdat, * wdat ;
    int * mpar ;

    pixelvalue* row_buf=NULL ;
    float** distances_buf=NULL ;
    float* x_position=NULL ;
    int* found=NULL;
    int* found_clean=NULL ;
    int* found_cleanit=NULL ;

    Vector * line ;
    FitParams ** par ;
    int foundit, begin, end ;



    int ilx=0;
    int ily=0;
    float* pidata=NULL;

    if ( ns_image == NULL )
    {
        sinfo_msg_error("sorry, no image given\n") ;
        return NULL ;
    }
    if ( n_slitlets < 1 )
    {
        sinfo_msg_error("wrong number of slitlets given\n") ;
        return NULL ;
    }
    if ( halfWidth < 0 || halfWidth >= estimated_dist )
    {
        sinfo_msg_error("wrong half width given\n") ;
        return NULL ;
    }
    if ( fwhm <= 0. )
    {
        sinfo_msg_error("wrong fwhm given\n") ;
        return NULL ;
    }
    if ( minDiff < 1. )
    {
        sinfo_msg_error("wrong minDiff given\n") ;
        return NULL ;
    }

    /* allocate memory for output array */
    if (NULL==(distances=(float *)cpl_calloc( n_slitlets , sizeof (float) ))) 
    {
        sinfo_msg_error("could not allocate memory\n") ;
        return NULL ;
    }
    /* allocate memory for output array */
    if (NULL == (ret_distances = (float *) cpl_calloc ( n_slitlets , 
                                           sizeof (float) ))) 
    {
        sinfo_msg_error("could not allocate memory\n") ;
        return NULL ;
    }

    ilx=cpl_image_get_size_x(ns_image);
    ily=cpl_image_get_size_y(ns_image);
    pidata=cpl_image_get_data_float(ns_image);

    row_buf=(pixelvalue*)cpl_calloc(ilx,sizeof(pixelvalue)) ;
    x_position=cpl_calloc(n_slitlets,sizeof(float)) ;
    found=cpl_calloc(3*n_slitlets,sizeof(int));
    found_clean=cpl_calloc(3*n_slitlets,sizeof(int)) ;
    found_cleanit=cpl_calloc(3*n_slitlets,sizeof(int)) ;
    distances_buf=sinfo_new_2Dfloatarray(ily,n_slitlets) ;

    /* go through the image rows */
    for ( row = 0 ; row < ily ; row++ )
    {
        int zeroindicator = 0 ;

        /* initialize the distance buffer */
        for ( i = 0 ; i < n_slitlets ; i++ )
        {
            distances_buf[row][i] = ZERO ;
        }

        /* fill the row buffer array with image data */
        for ( col = 0 ; col < ilx ; col++ )
        {
            row_buf[col] = pidata[col + row*ilx] ;
        }

        /* determine the mean of the row data */
        sum = 0. ;
        n = 0 ;
        for ( i = 0 ; i < ilx ; i++ )
        {
            if ( isnan(row_buf[i]) )
            {
                continue ;
            }
            sum += row_buf[i] ;
            n++ ;
        }
        mean = sum / (float)n ;

        /* store the positions of image values greater than the mean */
        n = 0 ;
        for ( i = 0 ; i < ilx ; i++ )
        {
            if (isnan(row_buf[i]))
            {
                continue ;
            }
            if ( row_buf[i] > mean + ESTIMATE )
            {
                found[n] = i ;
                n++ ;
            }
        }
       
        if ( n < n_slitlets )
        {
            sinfo_msg_warning("t4 wrong number of intensity columns "
                              "found in row: %d, found number: %d", row, n) ;
            continue ;
        }
        else
        { 
            /* find the maximum value position around the found columns */
            na = 0 ;
            for ( i = 1 ; i < n ; i ++ )
            {
                if ( found[i] - found[i-1] < halfWidth )
                {
                    begin = found[i] - halfWidth ;
                    if ( begin < 0 )
                    {
                        begin = 0 ;
                    }
                    end = found[i] + halfWidth ;
                    if ( end >= ilx )
                    {
                        end = ilx - 1 ;
                    }
                    /* find the maximum value inside the box around 
                       the found positions*/
                    maxval = -FLT_MAX ;
                    foundit = 0 ;
                    for ( j = begin ; j <= end ; j++ )
                    {
                        /* do not consider boxes that contain bad pixels */
                        if (isnan(row_buf[j]))
                        {
                            continue ;
                        }
                        if (row_buf[j] >= maxval )
                        {
                            maxval = row_buf[j] ;
                            foundit = j ;
                        }
                    }
                    if (maxval == -FLT_MAX)
                    {
                        continue ;
                    }
                    for ( k = 0 ; k < na ; k++ )
                    {
                        if ( found_cleanit[k] >= begin && 
                             found_cleanit[k] < foundit )
                        {
                            na-- ;
                        }
                    }
                    for ( k = 0 ; k < n ; k++ )
                    {
                        if ( found[k] == foundit)
                        {
                            if ( found_cleanit[na-1] != found[k] )
                            {
                                found_cleanit[na] = found[k] ;
                                na++ ;
                            }
                        }
                    }
                }
                else
                {
                    if ( i == 1 )
                    {
                        found_cleanit[na] = found[0] ;
                        na++ ;
                        found_cleanit[na] = found[1] ;
                        na++ ;
                    }
                    else
                    {
                        if ( found_cleanit[na-1] != found[i-1])
                        {
                            found_cleanit[na] = found[i-1] ;
                            na++ ;
                        }
                        if ( found_cleanit[na-1] != found[i])
                        {
                            found_cleanit[na] = found[i] ;
                            na++ ;
                        }
                    }
                }
            }

            /* determine only one pixel position for each slitlet intensity */
            j = 1 ;
            for ( i = 1 ; i < na ; i++ )
            {
                if ( (float)(found_cleanit[i] - found_cleanit[i-1]) < 
                             (estimated_dist - devtol) ||
                     (float)(found_cleanit[i] - found_cleanit[i-1]) > 
                             (estimated_dist + devtol) )
                {
                    continue ;
                }
                else
                {
                    found_clean[j-1] = found_cleanit[i-1] ;
                    found_clean[j]   = found_cleanit[i] ;
                    j++ ;
                }
            }
        }
        if ( j > n_slitlets )
        {
            /* check the distance again */
            ni = 1 ;
            for ( i = 1 ; i < j ; i++ )
            {
                if ( (float)(found_clean[i] - found_clean[i-1]) < 
                            (estimated_dist - devtol ) ||
                     (float)(found_clean[i] - found_clean[i-1]) > 
                            (estimated_dist + devtol ) )
                { 
                    continue ;
                }
                else
                {
                    found_clean[ni-1] = found_clean[i-1] ;
                    found_clean[ni]   = found_clean[i] ;
                    ni++ ;
                }
            }
            if ( ni != n_slitlets )
            {
                sinfo_msg_warning("t5 wrong number of intensity columns "
                                  "found in row: %d, found number: %d",
                                  row,ni) ;
                continue ;
            }
            else 
            {
                j = ni ;
            }
        }
        else if ( j < n_slitlets )
        {
            sinfo_msg_warning("t6 wrong number of intensity columns found "
                              "in row: %d , found number: %d\n", row, j) ;
            continue ;
        }
        counter = 0 ;
        /* go through the found intensity pixels in one row */
        for ( i = 0 ; i < j ; i++ )
        {
            /* allocate memory for the array where the line is fitted in */
            if ( NULL == (line = sinfo_new_vector (2*halfWidth + 1)) )
            {
                sinfo_msg_error ("cannot allocate new Vector \n") ;
                cpl_free(distances) ;
                return NULL ;
            }

            /* 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) ) ;
            par = sinfo_new_fit_params(1) ;

            m = 0 ;
            for ( k = found_clean[i]-halfWidth ; 
                  k <= found_clean[i]+halfWidth ; k++ )
            {
                if ( k < 0 )
                {
                    k = 0. ;
                }
                else if ( k >= ilx )
                {
                    k = ilx - 1 ;
                }
                else if ( isnan(row_buf[k]) )
                {
                    zeroindicator = 1 ;
                    break ;
                }
                else
                {
                    line -> data[m] = row_buf[k] ;
                    m++ ;
                }
            }
            if ( zeroindicator == 1 )
            {
                sinfo_new_destroy_vector(line) ;
                cpl_free(xdat) ;
                cpl_free(wdat) ;
                cpl_free(mpar) ;
                sinfo_new_destroy_fit_params(&par) ;
                break ;
            }

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

            /* 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 negative peaks and low signal cases */
            if ( (*par) -> fit_par[0] < minDiff )
            {
                sinfo_msg_warning ("sorry, signal of line too low to fit "
                                   "in row: %d in slitlet %d\n", row, i) ;
                sinfo_new_destroy_vector(line) ;
                cpl_free(xdat) ;
                cpl_free(wdat) ;
                cpl_free(mpar) ;
                sinfo_new_destroy_fit_params(&par) ;
                continue ;
            }

            for ( k = 0 ; k < MAXPAR ; k++ )
            {
                (*par) -> derv_par[k] = 0.0 ;
                mpar[k] = 1 ;
            }
            /* finally, do the least square fit using a sinfo_gaussian */
            int iters;
            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_calibrate_ns_test:",
                              "sinfo_lsqfit_c: least squares fit failed,"
                              " error no.: %d in row: %d in slitlet %d\n", 
                              iters, row, i) ;
          */
                sinfo_new_destroy_vector(line) ;
                cpl_free(xdat) ;
                cpl_free(wdat) ;
                cpl_free(mpar) ;
                sinfo_new_destroy_fit_params(&par) ;
                continue ;
            }

            /* check for negative fit results */
            if ( (*par) -> fit_par[0] <= 0. || (*par) -> fit_par[1] <= 0. ||
                 (*par) -> fit_par[2] < 0. )
            {
                sinfo_msg_warning ("negative parameters as fit result, not "
                                   "used! in row %d in slitlet %d", row, i) ;
                sinfo_new_destroy_vector(line) ;
                cpl_free(xdat) ;
                cpl_free(wdat) ;
                cpl_free(mpar) ;
                sinfo_new_destroy_fit_params(&par) ;
                continue ;
            }

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

            /* free memory */
            sinfo_new_destroy_fit_params(&par) ;
            sinfo_new_destroy_vector ( line ) ;
            cpl_free ( xdat ) ;
            cpl_free ( wdat ) ;
            cpl_free ( mpar ) ;
        }
        if (zeroindicator == 1)
        {
            sinfo_msg_debug ("bad pixel in fitting box in row: %d\n", row) ;
            continue ;
        }

        if ( counter != n_slitlets )
        {
            sinfo_msg_warning("wrong number of slitlets found "
                              "in row: %d", row) ;
            continue ;
        }
        /* store the distances between the sources and the slitlet centers */
        for ( i = 0 ; i < n_slitlets ; i++ )
        {
            distances_buf[row][i] = x_position[i] - (15.5 + 32.*(float)i) ;
        }
    }

    /* ----------------------------------------------------------------
     * go through the rows again and take the mean of the distances, 
     * throw away the runaways 
     */
    for ( i = 0 ; i < n_slitlets ; i++ )
    {
        n   = 0 ;
        sum = 0. ;
        for ( row = bottom ; row < top ; row++ )
        {
            if ( fabs( distances_buf[row][i] ) > devtol || 
                 isnan(distances_buf[row][i]) )
            {
             /*
          sinfo_msg("dist=%g devtol=%g isan=%d", 
            distances_buf[row][i],
            devtol,
            isnan(distances_buf[row][i]));
              */              
                continue ;
            }
            sum += distances_buf[row][i] ;
            n++ ;
        }
        if ( n < 2 )
        {
            sinfo_msg_error("distances array could not be determined"
                            " completely!, deviations of distances from"
                            " devtol too big" ) ;
            cpl_free(distances) ;
            return NULL ;
        }
        else
        {
            distances[i] = sum / (float)n ;
        }
    }

    /* now sort the result according to the row sequence in the 
       reconstructed image*/

    for ( i = 0 ; i < n_slitlets ; i++ )
    {
        int row_index;
      if( (row_index=sinfo_sort_slitlets(i)) == -1) {
	sinfo_msg_error("wrong number of a slitlet\n") ;
	cpl_free (distances) ;
	return NULL ;
      }
      ret_distances[row_index] = distances[i] ;
    }
    cpl_free(distances) ;

    cpl_free(row_buf) ;
    cpl_free(x_position) ;
    cpl_free(found);
    cpl_free(found_clean) ;
    cpl_free(found_cleanit) ;
    sinfo_new_destroy_2Dfloatarray(&distances_buf,n_slitlets) ;


    return ret_distances ; 
}
/**
   @memo makes a data cube out of a re-sampled source image this SPIFFI
         specific routine takes into account the Spiffi slitlet order on 
         the detector.
   
   @param calibImage:  re-sampled source image
   @param distances:   distances of the slitlets from each other
                                     output of function ns_test
   @param correct_diff_dist: dummy array with 32 elements
   @return  # resulting source data cube
            # correct_diff_dist: differences of the slitlets from
                                 distance 32 given in the correct
                                 Spiffi row sequence. The first slitlet
                                 is the reference, therefore element 
                                 23 is set 0. 

   @doc  makes a data cube out of a re-sampled source image this SPIFFI specific
         routine takes into account the Spiffi slitlet order on the detector.
         Also shifts the resulting image rows by one pixel if necessary 
         according to the distances array gained from the north-south test 
         routine. Can do the same with the bad pixel map image to generate a
         bad pixel mask cube.
 */

cpl_image * 
sinfo_new_make_true_resamp(cpl_image * calibImage, 
                           cpl_image * wavemap)
{
    cpl_image * returnImage ;
    float edges[33] ;
    int imsize,i,j ;
    int z;
    int col;
    //int recol ;
    int wlx=0;
    //int wly=0;
    int clx=0 ;
    int cly=0;
  
    float* pcdata=NULL;
    float* pwdata=NULL;
    float* prdata=NULL;


    wlx=cpl_image_get_size_x(wavemap);
    //wly=cpl_image_get_size_y(wavemap);
    pwdata=cpl_image_get_data_float(wavemap);

    edges[0]=0;
    j=1;
    for(i=0;i<wlx-1;i++)
    {
        if((pwdata[i]-pwdata[i+1])>0.0025 || (pwdata[i]-pwdata[i+1])<-0.0025)
    {
        sinfo_msg_error("wavemap sinfo_edge %d", i+1);
        edges[j]=i+1;
        j++;
    }
    }
    edges[32]=2048;

    clx=cpl_image_get_size_x(calibImage);
    cly=cpl_image_get_size_y(calibImage);
    pcdata=cpl_image_get_data_float(calibImage);
 
    imsize = clx / N_SLITLETS ;

    /* allocate memory */  
    returnImage = cpl_image_new(clx,cly,CPL_TYPE_FLOAT);
    prdata=cpl_image_get_data_float(returnImage);
    for ( z = 0 ; z < cly ; z++ ) /* go through the z-axis */
    {
        for ( col = 0 ; col < clx ; col++ ) /* go through the image columns */
        prdata[col+z*clx]=ZERO;
    }
    

    /* now build the data cube out of the resampled image */
    for ( z = 0 ; z < cly ; z++ ) /* go through the z-axis */
    {
        int kslit      = 0 ;

        //recol      = -1 ;
        for ( col = 0 ; col < clx ; col++ ) /* go through the image columns */
        {
            /*if ( col % imsize == 0 )
            {*/
            //recol = 0 ;
            /*kslit = col/imsize ;*/
            for(i=0;i<32;i++)
            {
                if(col>=sinfo_new_nint(edges[i]) &&
                                col<sinfo_new_nint(edges[i+1]))
                    kslit=i;
            }
            /* sort the slitlets in the right spiffi specific way */
            int slit_index = -1 ;
            if( (slit_index=sinfo_sort_slitlets(kslit)) == -1) {
                sinfo_msg_error("wrong slitlet index: couldn't be a "
                                "spiffi image,  there must be 32 "
                                "slitlets!") ;

            }

            /*}*/

            /* fill each cube plane with one image row */
            if((col-sinfo_new_nint(edges[kslit]))>0 &&
                            (col-sinfo_new_nint(edges[kslit]))<imsize-1 )
                prdata[(col-sinfo_new_nint(edges[kslit]))+
                       slit_index*imsize+z*clx] = 
                                       pcdata[col+z*clx] ;
            else
                prdata[(col-sinfo_new_nint(edges[kslit]))+
                       slit_index*imsize+z*clx] = ZERO;
            /*recol++ ;*/

        }
    }
    return returnImage ;
}

/*The old slitlet order*/
/*switch (kslit)
                {
                    case 0:
                        slit_index = 23 ;
                        break ;
                    case 1:
                        slit_index = 24 ;
                        break ;
                    case 2:
                        slit_index = 22 ;
                        break ;
                    case 3:
                        slit_index = 25 ;
                        break ;
                    case 4:
                        slit_index = 21 ;
                        break ;
                    case 5:
                        slit_index = 26 ;
                        break ;
                    case 6:
                        slit_index = 20 ;
                        break ;
                    case 7:
                        slit_index = 27 ;
                        break ;
                    case 8:
                        slit_index = 19 ;
                        break ;
                    case 9:
                        slit_index = 28 ;
                        break ;
                    case 10:
                        slit_index = 18 ;
                        break ;
                    case 11:
                        slit_index = 29 ;
                        break ;
                    case 12:
                        slit_index = 17 ;
                        break ;
                    case 13:
                        slit_index = 30 ;
                        break ;
                    case 14:
                        slit_index = 16 ;
                        break ;
                    case 15:
                        slit_index = 31 ;
                        break ;
                    case 16:
                        slit_index = 0 ;
                        break ;
                    case 17:
                        slit_index = 15 ;
                        break ;
                    case 18:
                        slit_index = 1 ;
                        break ;
                    case 19:
                        slit_index = 14 ;
                        break ;
                    case 20:
                        slit_index = 2 ;
                        break ;
                    case 21:
                        slit_index = 13 ;
                        break ;
                    case 22:
                        slit_index = 3 ;
                        break ;
                    case 23:
                        slit_index = 12 ;
                        break ;
                    case 24:
                        slit_index = 4 ;
                        break ;
                    case 25:
                        slit_index = 11 ;
                        break ;
                    case 26:
                        slit_index = 5 ;
                        break ;
                    case 27:
                        slit_index = 10 ;
                        break ;
                    case 28:
                        slit_index = 6 ;
                        break ;
                    case 29:
                        slit_index = 9 ;
                        break ;
                    case 30:
                        slit_index = 7 ;
                        break ;
                    case 31:
                        slit_index = 8 ;
                        break ;
                    default:
                        sinfo_msg_error("wrong slitlet index: couldn't "
                                        "be a spiffi image,  \
                                 there must be 32 slitlets!\n") ;
                        cpl_imagelist_delete(returnCube) ;
                        return NULL ;
                        break ;
                }*/


/*--------------------------------------------------------------------------*/
/**@}*/
