/*                                                                           *
*   This file is part of the ESO X-shooter Pipeline                         *
*   Copyright (C) 2006 European Southern Observatory                        *
*                                                                           *
*   This library 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    *
*                                                                           */

/*
 * $Author: amodigli $
 * $Date $
 * $Revision: 1.166 $
*/

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

/*---------------------------------------------------------------------------*/
/**
* @defgroup xsh_detect_arclines Arc Lines Detection (xsh_detect_arclines)
* @ingroup drl_functions
*
* Functions used to detect the position on the detector of emission lines 
* listed in a catalogue (recipe xsh_2dmap)
*/
/*---------------------------------------------------------------------------*/
/**@{*/

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

#include <math.h>
#include <xsh_drl.h>
#include <xsh_pfits.h>
#include <xsh_utils.h>
#include <xsh_model_utils.h>
#include <xsh_data_order.h>
#include <xsh_error.h>
#include <xsh_utils.h>
#include <xsh_msg.h>
#include <xsh_data_pre.h>
#include <xsh_data_order.h>
#include <xsh_data_the_map.h>
#include <xsh_data_arclist.h>
#include <xsh_data_wavesol.h>
#include <xsh_data_resid_tab.h>
#include <xsh_data_wavemap.h>
#include <xsh_data_spectralformat.h>
#include <xsh_model_io.h>
#include <xsh_model_kernel.h>
#include <xsh_fit.h>
#include <gsl/gsl_multifit.h>
#include <cpl.h>

/*---------------------------------------------------------------------------
                           Typedefs
 ---------------------------------------------------------------------------*/
static void theo_tab_filter( xsh_the_map *the_tab, xsh_arclist *arclist,
 int* size, double **lambda, double **n, double **s, int **s_index, 
 double **xthe, double **ythe, int nb_pinhole); 

static void theo_tab_model( xsh_xs_3* config_model, xsh_arclist *arclist,
 xsh_spectralformat_list *spectralformat_list, int* size, double **lambda,
			    double **n, double **s, double **sn, 
			    int **s_index, double **xthe, double **ythe,
			    xsh_instrument* instr, int nb_pinhole);

static void data_wavesol_fit_with_sigma( xsh_wavesol *wavesol,
 double *A, double *lambda, double *n, double *s, int size,
					 int max_iter, double min_frac, double sigma, int* rejected);

static int lines_filter_by_sn( xsh_pre* pre, double sn_ref, double x,
			       double y, double* sn);
/*---------------------------------------------------------------------------
                           Functions prototypes
 ---------------------------------------------------------------------------*/

static int lines_filter_by_sn( xsh_pre* pre, double sn_ref, double x,
			       double y, double* sn)
{
 int res = 0;
 int xpix, ypix, rej;
 double flux, noise;

 XSH_ASSURE_NOT_NULL( pre);

 /* compute the S/N for the center pixel */
 xpix =(int) rint(x);
 ypix = (int)rint(y);

 check( flux = cpl_image_get( pre->data, xpix, ypix, &rej));
 check( noise = cpl_image_get( pre->errs, xpix, ypix, &rej));
 *sn = flux / noise;
 res = (*sn > sn_ref);
 cleanup:
   return res;
}

static void theo_tab_model( xsh_xs_3* config_model, xsh_arclist *arclist, 
 xsh_spectralformat_list *spectralformat_list, int* size, double **lambda, 
			    double **n, double **s, double** sn, 
			    int **s_index, double **xthe, double **ythe,
			    xsh_instrument* instr, int nb_pinhole)
{
 int i,j;
 cpl_vector** spectral_tab = NULL;
 int global_size = 0;
 int arc_size=0;
 int index_loc = 0;
 int nb_slit = 1; 

 XSH_ASSURE_NOT_NULL( config_model);
 XSH_ASSURE_NOT_NULL( arclist);
 XSH_ASSURE_NOT_NULL( spectralformat_list);

 check( arc_size = xsh_arclist_get_size( arclist));
 XSH_CALLOC( spectral_tab, cpl_vector*, arc_size);

 nb_slit = nb_pinhole;

 XSH_REGDEBUG("nb pinhole %d ", nb_slit);

 for( i=0; i< arc_size; i++){
   cpl_vector* res = NULL;
   float lambdaARC;
   int res_size;

   check( lambdaARC = xsh_arclist_get_wavelength(arclist, i));
   check( res = xsh_spectralformat_list_get_orders(spectralformat_list, 
     lambdaARC));
   if (res != NULL){
     check( res_size = cpl_vector_get_size( res));
     res_size *= nb_slit;
   }
   else{
     res_size = 0;
     check(xsh_arclist_reject(arclist,i));
   }
   global_size += res_size;
   spectral_tab[i] = res;
 }

 XSH_MALLOC( *lambda, double, global_size);
 XSH_MALLOC( *n, double, global_size);
 XSH_MALLOC( *s, double, global_size);
 XSH_MALLOC( *sn, double, global_size);
 XSH_MALLOC( *s_index, int, global_size);
 XSH_MALLOC( *xthe, double, global_size);
 XSH_MALLOC( *ythe, double, global_size);


 for( i=0; i< arc_size; i++){
   double model_x, model_y;
   cpl_vector *spectral_res = NULL; 
   int spectral_res_size;
   float lambdaARC;

   check( lambdaARC = xsh_arclist_get_wavelength(arclist, i));
   check( spectral_res = spectral_tab[i]);

   if (spectral_res != NULL){
     check( spectral_res_size = cpl_vector_get_size( spectral_res));
     for( j=0; j< spectral_res_size; j++){
       int absorder = 0;
       int islit;
       double slit;

       if (nb_slit > 1){

         for(islit=0; islit < nb_slit; islit++){

           slit = config_model->slit[islit];
           check( absorder = (int) cpl_vector_get( spectral_res, j));

/*            check( xsh_model_get_xy( config_model, instr, 1.000274*lambdaARC, absorder, slit, */
/*              &model_x, &model_y)); */
           check( xsh_model_get_xy( config_model, instr, lambdaARC, absorder, slit,
             &model_x, &model_y));
           (*lambda)[index_loc] = lambdaARC;
           (*n)[index_loc] = absorder;
           (*s_index)[index_loc] = islit;
           (*s)[index_loc] = slit;
           (*sn)[index_loc] = 0;
           (*xthe)[index_loc] = model_x;
           (*ythe)[index_loc] = model_y;
           index_loc++;
         }
       }
       else{
         islit = 4;
         slit = config_model->slit[islit];
         check( absorder = (int) cpl_vector_get( spectral_res, j));
         check( xsh_model_get_xy( config_model, instr, lambdaARC, absorder, slit,
           &model_x, &model_y));
         (*lambda)[index_loc] = lambdaARC;
         (*n)[index_loc] = absorder;
         (*s_index)[index_loc] = islit;
         (*s)[index_loc] = slit;
         (*sn)[index_loc] = 0;
         (*xthe)[index_loc] = model_x;
         (*ythe)[index_loc] = model_y;
         index_loc++;
       }
/*       xsh_msg_dbg_medium( "lambda %f order %d slit %f x %f y %f",lambdaARC, absorder, slit,  
          model_x, model_y); */
     }
   }
 }
 *size = global_size;

 cleanup:
   if ( spectral_tab != NULL){
     for(i=0; i< arc_size; i++){
       xsh_free_vector( &spectral_tab[i]);
     }
     XSH_FREE( spectral_tab);
   }
   if ( cpl_error_get_code() != CPL_ERROR_NONE){
     XSH_FREE( *lambda);
     XSH_FREE( *n);
     XSH_FREE( *s);
     XSH_FREE( *s_index);
     XSH_FREE( *xthe);
     XSH_FREE( *ythe);
   }
   return;
}

/*
 @brief 
   Filters data from theoretical tab with arcline list and clean this list
 @param[in] the_tab
   The theoretical tab
 @param[in] arclist
   The arcline list
 @param[out] size
   Size of output data arrays
 @param[out] lambda
   Lambda array
 @param[out] n
   Order array
 @param[out] s
   Slit array
 @param[out] xthe
   Theoretical positions in X array
 @param[out] ythe
   Theoretical positions in Y array
*/
static void theo_tab_filter( xsh_the_map *the_tab, xsh_arclist *arclist,
 int* size, double **lambda, double **n, double **s, int **s_index, 
 double **xthe, double **ythe, int nb_pinhole)
{
  int the_size = 0;
  int arc_size = 0;
  int i=0, j=0;
  int sol_size = 0;
  int nb_slit = 1;

  XSH_ASSURE_NOT_NULL( the_tab);
  XSH_ASSURE_NOT_NULL( arclist);

  check( arc_size = xsh_arclist_get_size( arclist));
  check( the_size = xsh_the_map_get_size( the_tab));

  XSH_MALLOC( *lambda, double, the_size);
  XSH_MALLOC( *n, double, the_size);
  XSH_MALLOC( *s, double, the_size);
  XSH_MALLOC( *s_index, int, the_size);
  XSH_MALLOC( *xthe, double, the_size);
  XSH_MALLOC( *ythe, double, the_size);

  nb_slit = nb_pinhole;
  XSH_REGDEBUG("nb pinhole %d ", nb_slit);

  for(i=0; i< arc_size; i++){
    float lambdaARC, lambdaTHE;
    int nb_match = 0, max_match = 0;

    check(lambdaARC = xsh_arclist_get_wavelength(arclist, i));
    check(lambdaTHE = xsh_the_map_get_wavelength(the_tab, j));

    xsh_msg_dbg_medium("LINETABLE : Line %d / %d Lambda %f",i+1,  arc_size, 
      lambdaARC);

    while ( (j < the_size-1) && 
      ( (lambdaARC-lambdaTHE) > WAVELENGTH_PRECISION) ){
      j++;
      check(lambdaTHE = xsh_the_map_get_wavelength(the_tab, j));
    }
   
    xsh_msg_dbg_medium("THETABLE : Line %d / %d Lambda %f",j+1, the_size, lambdaTHE);

    max_match = the_size-j;

    while (  nb_match < max_match && fabs(lambdaARC-lambdaTHE) <= WAVELENGTH_PRECISION ){
      double order, slit, xtheval, ytheval;
      int islit;

      check( slit = (double) xsh_the_map_get_slit_position( the_tab, j));
      check( islit = xsh_the_map_get_slit_index( the_tab, j));

      if ( (nb_slit > 1) || (islit == 4) ){

       check( order = (double)xsh_the_map_get_order(the_tab, j));
       check( xtheval = xsh_the_map_get_detx(the_tab, j));
       check( ytheval = xsh_the_map_get_dety(the_tab, j));
       (*lambda)[sol_size] = lambdaTHE;
       (*n)[sol_size] = order;
       (*s)[sol_size] = slit;
       (*s_index)[sol_size] = islit;
       (*xthe)[sol_size] = xtheval;
       (*ythe)[sol_size] = ytheval;
        
       XSH_REGDEBUG("sol_size %d order %f slit %f lambda %f", 
         sol_size, (*n)[sol_size], (*s)[sol_size], (*lambda)[sol_size]);
      
       sol_size++;
       nb_match++;
     }
     if ( j < (the_size-1) ){
       j++;
       check( lambdaTHE = (double) xsh_the_map_get_wavelength( the_tab, j));
     }

   }

   if (nb_match == 0) {
     check(xsh_arclist_reject(arclist, i));
   }
 }
 *size = sol_size;

 cleanup:
   if ( cpl_error_get_code() != CPL_ERROR_NONE){
     XSH_FREE( *lambda);
     XSH_FREE( *n);
     XSH_FREE( *s);
     XSH_FREE( *s_index);
     XSH_FREE( *xthe);
     XSH_FREE( *ythe);
   }
   return;
}

/* 
 @brief
   Fit a polynomial like A = f(lambda,n,s) using sigma clipping 
 @param[in] wavesol
   the wave solution structure
 @param[in] A
   The A array data 
 @param[in] lambda
   The lambda array data
 @param[in] n
   The order array data
 @param[in] s
   The slit array data
 @param[in] size
   The size of A, n and s arrays
 @param[in] max_iter
   The maximum number of iterations for sigma clipping
 @param[in] min_frac
   The minimum fraction allowed for sigma clipping
 @param[in] sigma
   The sigma for sigma clipping
 @param[out] rejected
   A pre allocated array which contains rejected lines
*/
static void data_wavesol_fit_with_sigma( xsh_wavesol *wavesol,
 double *A, double *lambda, double *n, double *s, int size,
					 int max_iter, double min_frac, double sigma, int* rejected)
{
 int nbiter = 0;
 float frac = 1;
 int nbrejected = 0;
 int new_rejected = 1;
 cpl_polynomial *fitpoly = NULL;
 cpl_vector *dispy = NULL;
 int * idx = NULL;
 int index_size = 0;
 int i;

 XSH_ASSURE_NOT_NULL( wavesol);
 XSH_ASSURE_NOT_NULL( A);
 XSH_ASSURE_NOT_NULL( lambda);
 XSH_ASSURE_NOT_NULL( n);
 XSH_ASSURE_NOT_NULL( s);
 XSH_ASSURE_NOT_ILLEGAL( size > 0);

 XSH_CALLOC( idx, int, size);
 check(fitpoly = xsh_wavesol_get_poly( wavesol));
 check(xsh_wavesol_compute( wavesol, size, A,
   &(wavesol->min_y), &(wavesol->max_y), lambda, n, s, fitpoly));

 index_size = size;
 for(i=0; i< index_size; i++){
   idx[i] = i;
 }

 xsh_msg( "Fit wavesol with sigma clipping");

 while (nbiter < max_iter && frac > min_frac && new_rejected > 0){
   double sigma_med;

   new_rejected = 0;
   xsh_msg_dbg_high( "  *** NBITER = %d / %d ***", nbiter+1, max_iter);  
   dispy = cpl_vector_new( index_size);

   for(i = 0; i < index_size; i ++){
     double soly;
     double diffy;
       check( soly = xsh_wavesol_eval_poly(wavesol, lambda[i], n[i], s[i]));
       diffy  = A[i]-soly;
       check( cpl_vector_set( dispy, i, diffy));

   }
   check( sigma_med = cpl_vector_get_stdev( dispy));
   xsh_msg_dbg_high("    sigma %f SIGMA MEDIAN = %f", sigma, sigma_med);

   for(i = 0; i < index_size; i++){
       if ( fabs(cpl_vector_get( dispy, i)) > (sigma * sigma_med) ){
	 new_rejected++;
	 rejected[idx[i]] = 1;
       }
       else{
	 idx[i-new_rejected] = idx[i];
	 A[i-new_rejected] = A[i];
	 lambda[i-new_rejected] = lambda[i];
	 n[i-new_rejected] = n[i];
	 s[i-new_rejected] = s[i]; 
       }
   }
   xsh_free_vector(&dispy);
   index_size -= new_rejected;
   nbrejected += new_rejected;

   frac = 1 - ( (float) nbrejected / (float) size);
   xsh_msg_dbg_high("    NB_REJECT = %d", nbrejected);
   xsh_msg_dbg_high("    GOOD PIXEL FRACTION = %f", frac);

   check( xsh_wavesol_compute(wavesol, index_size, A,
     &(wavesol->min_y), &(wavesol->max_y), lambda, n, s, fitpoly));
   nbiter++;
 }

 cleanup:
   XSH_FREE( idx);
   xsh_free_vector(&dispy);
   return;
}

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

/** 
 @brief detect the position on the detector of emission lines listed in a 
   catalogue, from expected position values given by the Physical model.

 @param[in] frame 
   The data frame in which we detect arclines
 @param[in] theo_tab_frame 
   The theoretical map contains expected positions of lines on the detector (POLYNOMIAL mode) or NULL
 @param[in] arc_lines_tab_frame 
   List of emission lines to be used for the calibration
 @param[in] wave_tab_guess_frame 
   Polynomial coefficients for the dispersion equation of the single pin hole image or NULL (POLYNOMIAL ITERATIVE mode) or NULL
 @param[in] order_tab_recov_frame 
   Order table frame indicates X = f(Y) or NULL (POLYNOMIAL RECOVER mode) or NULL
 @param[in] config_model_frame 
   Configuration model file (MODEL mode) or NULL
 @param[in] spectralformat_frame 
   Spectral format file (MODEL mode) or NULL
 @param[out] resid_tab_orders_frame
   Residual of the lines filtering
 @param[out] arc_lines_clean_tab_frame
   List of emission lines effectively used in the calibration. Non reliable 
   lines have been discarded.
 @param[out] wave_tab_frame
   Polynomial coefficients for the dispersion equation
 @param[out] resid_tab_frame
   Residual of the modeled 2D mapping
 @param[in] solwave_type
   Type of product wavelength solution
 @param[in] da
   Detect_arclines recipe parameters
 @param[in] dac
   Detect_arclines clipping parameters
 @param[in] instr
   Instrument containing the arm, mode and lamp in use
 @param[in] rec_id recipe id
*/
void 
xsh_detect_arclines_dan( cpl_frame *frame, 
    cpl_frame *theo_tab_frame,
    cpl_frame *arc_lines_tab_frame,
    cpl_frame* wave_tab_guess_frame,
    cpl_frame *order_tab_recov_frame,
    cpl_frame *config_model_frame,
    cpl_frame *spectralformat_frame,
    cpl_frame **resid_tab_orders_frame,
    cpl_frame **arc_lines_clean_tab_frame,
    cpl_frame **wave_tab_frame,
    cpl_frame **resid_tab_frame,
    xsh_sol_wavelength solwave_type,
    xsh_detect_arclines_param *da,
    xsh_clipping_param *dac,
    xsh_instrument *instr,
    const char* rec_id,
			 const int clean_tmp,
			 const int resid_tab_name_sw)
{

  xsh_the_map *themap = NULL;
  xsh_resid_tab *resid = NULL;
  xsh_resid_tab *resid_orders = NULL;
  xsh_arclist *arclist = NULL;
  xsh_pre *pre = NULL;
  cpl_polynomial *fit2dx = NULL;
  cpl_propertylist* header = NULL;
  int * sort = NULL;
  double *vlambdadata = NULL, *vsdata = NULL, *vorderdata = NULL, *vsndata=NULL;
  int *vsindexdata = NULL;
  double *vxthedata = NULL,  *vythedata = NULL;
  double *corr_x = NULL, *corr_y = NULL;
  double *gaussian_pos_x = NULL, *gaussian_pos_y = NULL,
      *gaussian_sigma_x = NULL, *gaussian_sigma_y = NULL,
      *gaussian_fwhm_x = NULL, *gaussian_fwhm_y = NULL, *gaussian_norm=NULL;
  int* flag=NULL;
  cpl_array *gaussian_sigma_x_arr =NULL;
  cpl_array *gaussian_sigma_y_arr =NULL;
  cpl_array *gaussian_fwhm_x_arr =NULL;
  cpl_array *gaussian_fwhm_y_arr =NULL;


  double *diffx = NULL, *diffy = NULL;
  double *diffxmean = NULL, *diffymean = NULL, *diffxsig = NULL, *diffysig = NULL;
  xsh_order_list *order_tab_recov = NULL;
  /* Others */
  int nlinematched, nlinecat_clean=0, nlinecat;
  int i, sol_size = 0;
  xsh_wavesol* wave_table = NULL;
  char wave_table_name[256];
  const char* wave_table_tag = NULL;
  xsh_wavesol* wave_tab_guess = NULL;
  xsh_xs_3 config_model;
  xsh_xs_3* p_xs_3;
  xsh_spectralformat_list *spectralformat_list = NULL;
  int lines_not_in_image = 0;
  int lines_not_good_sn = 0;
  int lines_not_gauss_fit = 0;
  int lines_not_valid_pixels = 0;
  int lines_too_few_ph=0;

  int* rejected = NULL;
  int nb_rejected = 0;
  int solution_type = 0;
  const char* solution_type_name[2] = { "POLY", "MODEL"};
  int detection_mode = 0;
  const char* detection_mode_name[3] = { "NORMAL", "CORRECTED", "RECOVER"};
  int nb_pinhole;
  char fname[256];
  char rname[256];
  char rtag[256];
  const char* tag=NULL;

  char dpr_type[256];
  char type[256];
  cpl_table* wave_trace=NULL;


  cpl_table* cfg_tab=NULL;
  const char* cfg_name=NULL;
  char new_name[256];
  char basename[256];
  cpl_propertylist* plist=NULL;

  int min_slit_match=4;
  double line_devs=2.5;
  int found_temp=true;
  int starti=0;
  int nbflag=0;
  int newi=0;
  int j,k;


  /* check input parameters */
  XSH_ASSURE_NOT_NULL( frame);
  XSH_ASSURE_NOT_NULL( arc_lines_tab_frame);
  XSH_ASSURE_NOT_NULL( spectralformat_frame);
  XSH_ASSURE_NOT_NULL( instr);
  XSH_ASSURE_NOT_NULL( da);
  XSH_ASSURE_NOT_NULL( dac);

  /* Ouput parameters */
  XSH_ASSURE_NOT_NULL( arc_lines_clean_tab_frame);
  XSH_ASSURE_NOT_NULL( resid_tab_frame);


  /* Debug parameters */
  xsh_msg_dbg_medium("---Detect arclines parameters");
  xsh_msg_dbg_medium("min_sn %f fit-window-half-size %d", da->min_sn, da->fit_window_hsize);
  xsh_msg_dbg_medium("Clipping sigma %f niter %d frac %f", dac->sigma, dac->niter, dac->frac);

  check( pre = xsh_pre_load (frame, instr));
  check_msg( nb_pinhole = xsh_pfits_get_nb_pinhole( pre->data_header),
      "fail to find number of pinholes info in frame %s",
      cpl_frame_get_filename(frame) );
  check(strcpy(dpr_type,xsh_pfits_get_dpr_type(pre->data_header)));

  if(strstr(dpr_type,"FMTCHK") != NULL) {
    strcpy(type,"FMTCHK_");
  } else if(strstr(dpr_type,"WAVE") != NULL) {
    strcpy(type,"WAVE_");
  } else if(strstr(dpr_type,"WAVE") != NULL) {
    strcpy(type,"ARC_");
  } else {
    strcpy(type,"");
  }



  check( arclist = xsh_arclist_load( arc_lines_tab_frame));
  check( spectralformat_list = xsh_spectralformat_list_load(
      spectralformat_frame, instr));
  check(nlinecat = xsh_arclist_get_size( arclist));

  /* sort data by wavelength */
  check(xsh_arclist_lambda_sort( arclist));


  /* First define the solution type of detect_arclines */
  if ( theo_tab_frame != NULL) {
    /* poly mode */
    XSH_ASSURE_NOT_ILLEGAL( config_model_frame == NULL);
    solution_type = XSH_DETECT_ARCLINES_TYPE_POLY;
    check( themap = xsh_the_map_load( theo_tab_frame));
    check( xsh_the_map_lambda_order_slit_sort( themap));
    check( theo_tab_filter( themap, arclist, &sol_size, &vlambdadata,
        &vorderdata, &vsdata,  &vsindexdata, &vxthedata, &vythedata, nb_pinhole));

  }
  else if ( config_model_frame != NULL){
    /* physical model mode */
    solution_type = XSH_DETECT_ARCLINES_TYPE_MODEL;
    p_xs_3=&config_model;


    //Make sure to modify only current copy
    check(cfg_name=cpl_frame_get_filename(config_model_frame));
    check(plist=cpl_propertylist_load(cfg_name,0));
    check(cfg_tab=cpl_table_load(cfg_name,1,0));
    //check(basename=xsh_get_basename(cfg_name));
    tag=XSH_GET_TAG_FROM_ARM(XSH_MOD_CFG_TAB,instr);
    sprintf(basename,"%s.fits",tag);
    sprintf(new_name,"local_%s",basename);
    check( xsh_pfits_set_pcatg(plist,tag));
    check(cpl_table_save(cfg_tab,plist,NULL,new_name,CPL_IO_DEFAULT));
    xsh_add_temporary_file(new_name);
    check(cpl_frame_set_filename(config_model_frame,new_name));
    xsh_free_table(&cfg_tab);
    xsh_free_propertylist(&plist);

    check( xsh_model_config_load_best( config_model_frame, &config_model));
    XSH_REGDEBUG("load config model ok");
    /* update cfg table frame and corresponding model structure for temperature */
    check(xsh_model_temperature_update_frame(&config_model_frame,frame,
        instr,&found_temp));
    check(xsh_model_temperature_update_structure(&config_model,frame,instr));
    /* predict line positions on detector */
    check( theo_tab_model( &config_model, arclist, spectralformat_list,
        &sol_size, &vlambdadata, &vorderdata, &vsdata,
        &vsndata, &vsindexdata, &vxthedata, &vythedata,
        instr, nb_pinhole));

  }
  else{
    XSH_ASSURE_NOT_ILLEGAL_MSG(1==0,
        "Undefined solution type (POLY or MODEL). See your input sof");
  }


  xsh_spectralformat_list_free(&spectralformat_list);
  xsh_msg_dbg_high("Solution type %s", solution_type_name[solution_type]);

  /* Define the mode of use */
  if ( wave_tab_guess_frame == NULL){
    if ( order_tab_recov_frame == NULL){
      detection_mode = XSH_DETECT_ARCLINES_MODE_NORMAL;
    }
    else{
      detection_mode = XSH_DETECT_ARCLINES_MODE_RECOVER;
      check( order_tab_recov = xsh_order_list_load( order_tab_recov_frame,
          instr));
    }
  }
  else {
    if ( order_tab_recov_frame == NULL){
      detection_mode = XSH_DETECT_ARCLINES_MODE_CORRECTED;

      check( wave_tab_guess = xsh_wavesol_load( wave_tab_guess_frame,
          instr ));
      xsh_msg( "BinX,Y: %d, %d", wave_tab_guess->bin_x,
          wave_tab_guess->bin_y ) ;
    }
    else{
      detection_mode = -1;
    }
  }

  XSH_ASSURE_NOT_ILLEGAL( detection_mode >=XSH_DETECT_ARCLINES_MODE_NORMAL
      && detection_mode <= XSH_DETECT_ARCLINES_MODE_RECOVER);

  xsh_msg( "Detection mode : %s", detection_mode_name[detection_mode]);

  /* allocate some memory */
  XSH_MALLOC( corr_x, double, sol_size);
  XSH_MALLOC( corr_y, double, sol_size);

  XSH_MALLOC( gaussian_pos_x, double, sol_size);
  XSH_MALLOC( gaussian_pos_y, double, sol_size);
  XSH_MALLOC( gaussian_sigma_x, double, sol_size);
  XSH_MALLOC( gaussian_sigma_y, double, sol_size);

  XSH_MALLOC( gaussian_fwhm_x, double, sol_size);
  XSH_MALLOC( gaussian_fwhm_y, double, sol_size);
  XSH_MALLOC( gaussian_norm, double, sol_size);

  XSH_MALLOC( diffx, double, sol_size);
  XSH_MALLOC( diffy, double, sol_size);

  XSH_MALLOC( diffxmean, double, sol_size);
  XSH_MALLOC( diffymean, double, sol_size);

  XSH_MALLOC( diffxsig, double, sol_size);
  XSH_MALLOC( diffysig, double, sol_size);
  XSH_MALLOC( flag, int, sol_size);

  /* initialize array value to default not to have garbage on resid tab*/

  memset(corr_x,0,sizeof(double)*sol_size);
  memset(corr_y,0,sizeof(double)*sol_size);

  memset(gaussian_pos_x,0,sizeof(double)*sol_size);
  memset(gaussian_pos_y,0,sizeof(double)*sol_size);
  memset(gaussian_sigma_x,0,sizeof(double)*sol_size);
  memset(gaussian_sigma_y,0,sizeof(double)*sol_size);
  memset(gaussian_fwhm_x,0,sizeof(double)*sol_size);
  memset(gaussian_fwhm_y,0,sizeof(double)*sol_size);
  memset(gaussian_norm,0,sizeof(double)*sol_size);
  memset(diffx,0,sizeof(double)*sol_size);
  memset(diffy,0,sizeof(double)*sol_size);
  memset(diffxmean,0,sizeof(double)*sol_size);
  memset(diffymean,0,sizeof(double)*sol_size);

  memset(diffxsig,0,sizeof(double)*sol_size);
  memset(diffysig,0,sizeof(double)*sol_size);

  memset(flag,0,sizeof(int)*sol_size);


  /* init */
  nlinematched = 0;

  if ( da->find_center_method == XSH_GAUSSIAN_METHOD){
    xsh_msg_dbg_medium( "USE method GAUSSIAN");
  }
  else{
    xsh_msg_dbg_medium( "USE method BARYCENTER");
  }


  /* perform line detection and sn thresholding */
  for(i=0; i< sol_size; i++){
    int xpos = 0, ypos = 0;
    int slit_index;
    double corrxv = 0.0, corryv = 0.0;
    double lambda, order, slit, xthe, ythe,sn=0;
    double xcor, ycor, x, y, sig_x, sig_y, norm, fwhm_x, fwhm_y;
    /* double max_off=1.0; */

    lambda = vlambdadata[i];
    order = vorderdata[i];
    slit = vsdata[i];

    if(vsndata != NULL) {
      sn = vsndata[i];
    }

    slit_index = vsindexdata[i];
    xthe = vxthedata[i];
    ythe = vythedata[i];
    xsh_msg_dbg_high( "THE LAMBDA %f ORDER %f SLIT %f X %f Y %f", lambda,
        order, slit, xthe, ythe);

    /* Theoretical using pixel FITS convention */
    xcor = xthe;
    ycor = ythe;
    xsh_msg_dbg_high("THE x%f y %f", xthe, ythe);

    /* Find X position  from RECOVER ORDER TAB */
    if ( order_tab_recov != NULL){
      int iorder = 0;
      cpl_polynomial *center_poly_recov = NULL;

      check (iorder = xsh_order_list_get_index_by_absorder(
          order_tab_recov, order));
      center_poly_recov = order_tab_recov->list[iorder].cenpoly;
      check( xcor = cpl_polynomial_eval_1d( center_poly_recov, ycor, NULL));
      corrxv = xcor-xthe;
    }
    /* apply x and y translation from predict data to the model */
    if ( wave_tab_guess != NULL){
      double diffxv, diffyv;

      check( diffxv = xsh_wavesol_eval_polx( wave_tab_guess, lambda,
          order, 0));
      check( diffyv = xsh_wavesol_eval_poly( wave_tab_guess, lambda,
          order, 0));

      xcor = xthe+diffxv;
      ycor = ythe+diffyv;
      corrxv = diffxv;
      corryv = diffyv;
    }
    /* Corrected position using pixel FITS convention */
    xsh_msg_dbg_high("x %f y %f CORR_x %f CORR_y %f", xcor, ycor,
        corrxv, corryv);

    if ( xcor >=0.5 && ycor >=0.5 &&
        xcor < (pre->nx+0.5) && ycor < (pre->ny+0.5)){
      int best_med_res = 0;
      check ( best_med_res = xsh_pre_window_best_median_flux_pos( pre,
          (int)(xcor+0.5)-1, (int)(ycor+0.5)-1,
          da->search_window_hsize, da->running_median_hsize, &xpos, &ypos));

      if (best_med_res != 0){
        lines_not_valid_pixels++;
        flag[i]=1;
        xsh_msg_dbg_medium("Not valid pixels for this line %d",i);
        continue;
      }
      /* Put positions in CPL coordinates */
      xpos = xpos+1;
      ypos = ypos+1;
      xsh_msg_dbg_high("ADJ x %d y %d", xpos, ypos);

      if ( da->find_center_method == XSH_GAUSSIAN_METHOD){
        cpl_image_fit_gaussian(pre->data, xpos, ypos, 1+2*da->fit_window_hsize,
            &norm, &x, &y, &sig_x, &sig_y, &fwhm_x, &fwhm_y);
        xsh_msg_dbg_high("GAUS x %f y %f %d %d %d", x, y,da->fit_window_hsize, da->search_window_hsize, da->running_median_hsize);


	/* IN CASE OF usage of cpl_fit_image_gauss we need this code

	cpl_array* parameters=cpl_array_new(7, CPL_TYPE_DOUBLE);
	cpl_array* err_params=cpl_array_new(7, CPL_TYPE_DOUBLE);
	cpl_array* fit_params=cpl_array_new(7, CPL_TYPE_INT);
        int kk=0;
	//All parameter should be fitted
	for (kk = 0; kk < 7; kk++)
	  cpl_array_set(fit_params, kk, 1);
	double rms=0;
	double red_chisq=0;
	cpl_matrix* covariance=NULL;
	cpl_matrix* phys_cov=NULL;
	double major=0;
	double minor=0;
	double angle=0;
        int size=1+2*da->fit_window_hsize;
	cpl_fit_image_gaussian(pre->data, pre->errs, xpos, ypos,size,size,
				     parameters,err_params,fit_params,
				     &rms,&red_chisq,&covariance,
			       &major,&minor,&angle,&phys_cov);

	double rho=0;
	double back=cpl_array_get(parameters,0,NULL);
	norm=cpl_array_get(parameters,1,NULL);
	rho=cpl_array_get(parameters,2,NULL);

	x=cpl_array_get(parameters,3,NULL);
	y=cpl_array_get(parameters,4,NULL);
	sig_x=cpl_array_get(parameters,5,NULL);
	sig_y=cpl_array_get(parameters,6,NULL);
	fwhm_x=sig_x*CPL_MATH_FWHM_SIG;
	fwhm_y=sig_y*CPL_MATH_FWHM_SIG;

        xsh_free_array(&parameters);
        xsh_free_array(&err_params);
        xsh_free_array(&fit_params);
	*/



      }
      else{
        xsh_image_find_barycenter( pre->data, xpos, ypos,
            1+2*da->fit_window_hsize,
            &norm, &x, &y, &sig_x, &sig_y, &fwhm_x, &fwhm_y);
        xsh_msg_dbg_high("BARY x %f y %f", x, y);
      }

      /* (PBR) Adjust 1ph centroids to be consistent with 9ph
	- This is cleaner than adjusting the ph position in the cfg because:
	  a. we do it just once here -> it gets used in the matching, then
	     propogates to the model optimisation
	  b. We don't need to know if the input config was itself optimised
	     to 9ph or 1ph (i.e. do we need the shift or not)
	  c. Potentially this can be used for poly-mode too, though I
	     restrict it to phys mod below (feel free to change it)
	- The NIR value of 0.125pix for x I got by using a config optimised
	  to 9ph data (i.e. 2dmap) in xsh_predict and checking the input
	  offsets. For y the offset was less than the s.d.
	- Same procedure for UVB. Also same for VIS, but value was ~0
        - A correction from 9ph to full slit centre is still required*/
      if (nb_pinhole==1 && solution_type==XSH_DETECT_ARCLINES_TYPE_MODEL) {
        if (p_xs_3->arm==2) {
          x=x+0.125;
        }
        else if (p_xs_3->arm==0) {
          x=x-0.51;
        }
      }

      if( cpl_error_get_code() == CPL_ERROR_NONE ){
         int code_sn=lines_filter_by_sn( pre, da->min_sn, x, y, &sn);
         vlambdadata[i] = lambda;
         vorderdata[i] = order;
         vsdata[i] = slit;
         if(vsndata!=NULL) {
           vsndata[i] = sn;
         }

         vsindexdata[i] = slit_index;
         vxthedata[i] = xthe;
         vythedata[i] = ythe;
         corr_x[i] = corrxv;
         corr_y[i] = corryv;
         gaussian_pos_x[i] = x;
         gaussian_pos_y[i] = y;
         gaussian_sigma_x[i] = sig_x;
         gaussian_sigma_y[i] = sig_y;
         gaussian_fwhm_x[i] = fwhm_x;
         gaussian_fwhm_y[i] = fwhm_y;
         gaussian_norm[i] = norm;
         diffx[i] = x-xthe;
         diffy[i] = y-ythe;


        if ( code_sn ){
          nlinematched++;
        }
        else{
          lines_not_good_sn++;
          flag[i]=2;
        }
      }
      else{
        flag[i]=3;
        lines_not_gauss_fit++;
        xsh_msg_dbg_medium("No Fit Gaussian for this line %d",i);
        xsh_error_reset();
      }
    }
    else{
      flag[i]=4;
      lines_not_in_image++;
      xsh_msg_dbg_medium("Coordinates are not in the image");
    }
  }

  /*In case of multi-pinhole arc lamp frame, remove all matches where there 
    are less than 7 out of 9 slit positions matched
    the following assumes that the vectors are ordered so that, within 
    each order, all matches for a single wavelength are grouped together
   */
  
  if (nb_pinhole>1) {
    for (i=1; i<sol_size; i++) {
        xsh_msg_dbg_high("filter 7 pinhole : line %d : lambda %f order %f slit %f",
            i, vlambdadata[i], vorderdata[i], vsdata[i]);

        if (vlambdadata[i]!=vlambdadata[i-1] ||
            vorderdata[i]!=vorderdata[i-1] ||
            i==sol_size-1) {

          //special case for last line: TODO AMO: Why this???
          if (i==sol_size-1) {
            i++;
          }
          xsh_msg_dbg_high("filter n pinhole : from %d to %d find %d pinholes",
              starti, i, i-starti);

          if (i-starti-nbflag>=min_slit_match) {
            for (k=starti; k<=i-1; k++) {
              diffxmean[k]=0.0;
              diffymean[k]=0.0;
              for (j=starti; j<=i-1; j++) {
                if (j!=k && flag[k]==0 && flag[j]==0) {
                  diffxmean[k]+=diffx[j];
                  diffymean[k]+=diffy[j];
                }
              }
              diffxmean[k]/=(float)(i-starti-1);
              diffymean[k]/=(float)(i-starti-1);
            }
            for (k=starti; k<=i-1; k++) {
              diffxsig[k]=0.0;
              diffysig[k]=0.0;
              for (j=starti; j<=i-1; j++) {
                if (j!=k && flag[k]==0 && flag[j]==0) {
                  diffysig[k]+=(diffy[j]-diffymean[k])*(diffy[j]-diffymean[k]);
                  diffxsig[k]+=(diffx[j]-diffxmean[k])*(diffx[j]-diffxmean[k]);
                }
              }
              diffxsig[k]=sqrt(diffxsig[k]/(float)(i-starti-1));
              diffysig[k]=sqrt(diffysig[k]/(float)(i-starti-1));
            }
            for (j=starti; j<=i-1; j++) {
              //Below removes individual ph with very different resids than the mean for this line
              if (fabs(diffx[j]-diffxmean[j])>line_devs*diffxsig[j] && fabs(diffy[j]-diffymean[j])>line_devs*diffysig[j] && flag[j]==0) {
	      	flag[j]=6;
		nbflag++;
	      }
            }
	    newi+=i-starti-nbflag;
          } // end min_slit_match
	  // check min_slit again (rather than else) because nbflag may have increased
          if (i-starti-nbflag<min_slit_match) {
            lines_too_few_ph+=i-starti;
	    for (j=starti; j<=i-1; j++) {
	      if (flag[j]==0) {
		flag[j]=5;
		xsh_msg_dbg_medium("Too few pin-holes for this line %d",i);
	      }
	    }
          }
          starti=i;
	  nbflag=0;
        }
       
	if (i<sol_size && flag[i]!=0) {
	  nbflag+=1;
	}
    }// endfor
    nlinematched=newi;
  } // if multi-pinhole (xsh_2dmap)



 
  xsh_msg("nlinematched / sol_size = %d / %d", nlinematched, sol_size);
  assure(nlinematched > 0, CPL_ERROR_ILLEGAL_INPUT,
      "No line matched, value of parameter "
      "detectarclines-search-win-hsize may be too large or too small "
      "detectarclines-fit-win-hsize=%d may be too large or too small "
      "detectarclines-running-median-hsize may be too large or too small "
      "detectarclines-min-sn may be too small "
      "or value of parameter detectarclines-min-sn may be too large",
      da->fit_window_hsize);


  xsh_msg_dbg_medium("  %d lines not found in image", lines_not_in_image);
  xsh_msg_dbg_medium("  %d lines not good S/N", lines_not_good_sn);
  xsh_msg_dbg_medium("  %d lines no fit gaussian", lines_not_gauss_fit);
  xsh_msg_dbg_medium("  %d lines no valid pixels", lines_not_valid_pixels);
  xsh_msg_dbg_medium("  %d lines detected in less than %d/9 ph positions", lines_too_few_ph,min_slit_match);

  /* wrap variables into arrays in order to set to invalid flag==0 values 
     and compute proper statistics */
  gaussian_sigma_x_arr = cpl_array_wrap_double(gaussian_sigma_x, nlinematched);
  gaussian_sigma_y_arr = cpl_array_wrap_double(gaussian_sigma_y, nlinematched);
  gaussian_fwhm_x_arr = cpl_array_wrap_double( gaussian_fwhm_x,nlinematched);
  gaussian_fwhm_y_arr = cpl_array_wrap_double( gaussian_fwhm_y,nlinematched);

  /* Because we have initialised variabbles that hold results to 0, and not 
     all elements are later filled by actual values from the Gauss fit, 
     to get proper statistics we need to set to invalid all array elements
     corresponding to flag value 0 
   */

  for(i=0;i<nlinematched;i++){
    if(flag[i] > 0) {
      cpl_array_set_invalid(gaussian_sigma_x_arr,i);
      cpl_array_set_invalid(gaussian_sigma_y_arr,i);
      cpl_array_set_invalid(gaussian_fwhm_x_arr,i);
      cpl_array_set_invalid(gaussian_fwhm_y_arr,i);
    }
  }

  xsh_msg("sigma gaussian median in x %lg",
      cpl_array_get_median( gaussian_sigma_x_arr));
  xsh_msg("sigma gaussian median in y %lg",
      cpl_array_get_median( gaussian_sigma_y_arr));

  xsh_msg("FWHM gaussian median in x %lg",
      cpl_array_get_median( gaussian_fwhm_x_arr));
  xsh_msg("FWHM gaussian median in y %lg",
      cpl_array_get_median( gaussian_fwhm_y_arr));

  xsh_unwrap_array( &gaussian_sigma_x_arr);
  xsh_unwrap_array( &gaussian_sigma_y_arr);

  xsh_unwrap_array( &gaussian_fwhm_x_arr);
  xsh_unwrap_array( &gaussian_fwhm_y_arr);


  /* Produce the residual orders tab */
  /* make sure result is with flag==0 only */
  if ( resid_tab_orders_frame != NULL){
    check(resid_orders=xsh_resid_tab_create_not_flagged(sol_size,
                                                        vlambdadata,
                                                        vorderdata,
							vsdata,vsndata,
                                                        vsindexdata,
							vxthedata, vythedata,
							corr_x, corr_y,
							gaussian_norm,
							gaussian_pos_x, 
                                                        gaussian_pos_y,
							gaussian_sigma_x, 
                                                        gaussian_sigma_y,
							gaussian_fwhm_x, 
                                                        gaussian_fwhm_y,flag,
							wave_table, 
                                                        solution_type));

    if(resid_tab_name_sw) {
      sprintf(rtag,"%s%s%s",type,"RESID_TAB_ORDERS_",
	      xsh_instrument_arm_tostring( instr ));
    } else {
      sprintf(rtag,"%s%s%s",type,"RESID_ALL_TAB_ORDERS_",
	      xsh_instrument_arm_tostring( instr ));
    }
   
    sprintf(rname,"%s%s",rtag,".fits");

    check( *resid_tab_orders_frame = xsh_resid_tab_save( resid_orders,
							 rname, instr,rtag));
    xsh_add_temporary_file(rname);

  }


  xsh_msg_dbg_high("solution_type=%d poly=%d model=%d",solution_type,
      XSH_DETECT_ARCLINES_TYPE_POLY, XSH_DETECT_ARCLINES_TYPE_MODEL);

  /* Produce the wave tab if need */
  /* TODO: CHECK 
     Here poly mode results may have changed due to having stored 
     results also with flag!=0 */
  if ( solution_type == XSH_DETECT_ARCLINES_TYPE_POLY &&
      (wave_tab_frame != NULL)){
    check( wave_table = xsh_wavesol_create( spectralformat_frame, da, instr));
    XSH_CALLOC( rejected, int, nlinematched);
    if ( solwave_type == XSH_SOLUTION_RELATIVE) {
      /* compute dY = f(lambda,n) */
      check( xsh_wavesol_set_type( wave_table, XSH_WAVESOL_GUESS));
      check( data_wavesol_fit_with_sigma( wave_table, diffy, vlambdadata,
          vorderdata, vsdata,  nlinematched, dac->niter, dac->frac,
					  dac->sigma, rejected));
      nb_rejected = 0;
      for(i=0; i< nlinematched; i++){
        if (rejected[i] == 1){
          nb_rejected++;
        }
        else{
          /* reindex data */
          vxthedata[i-nb_rejected] = vxthedata[i];
          vythedata[i-nb_rejected] = vythedata[i];
          vsindexdata[i-nb_rejected] = vsindexdata[i];
          corr_x[i-nb_rejected] = corr_x[i];
          corr_y[i-nb_rejected] = corr_y[i];
          gaussian_pos_x[i-nb_rejected] = gaussian_pos_x[i];
          gaussian_pos_y[i-nb_rejected] = gaussian_pos_y[i];
          gaussian_sigma_x[i-nb_rejected] = gaussian_sigma_x[i];
          gaussian_sigma_y[i-nb_rejected] = gaussian_sigma_y[i];
          gaussian_fwhm_x[i-nb_rejected] = gaussian_fwhm_x[i];
          gaussian_fwhm_y[i-nb_rejected] = gaussian_fwhm_y[i];
          gaussian_norm[i-nb_rejected] = gaussian_norm[i];
          diffx[i-nb_rejected] = diffx[i];
        }
      }
      nlinematched = nlinematched-nb_rejected;
      /* compute dX = f(lambda,n) */
      check( fit2dx = xsh_wavesol_get_polx( wave_table));
      check( xsh_wavesol_compute( wave_table,
          nlinematched, diffx,
          &(wave_table->min_x), &(wave_table->max_x),
          vlambdadata, vorderdata, vsdata, fit2dx));


      sprintf(wave_table_name,"%s%s.fits","WAVE_TAB_GUESS_",
          xsh_instrument_arm_tostring( instr )) ;

      wave_table_tag = XSH_GET_TAG_FROM_ARM( XSH_WAVE_TAB_GUESS, instr);

    }
    else{
      check( xsh_wavesol_set_type( wave_table, XSH_WAVESOL_2D));
      /* Y = f(lambda,n,s) with sigma clipping */
      check( data_wavesol_fit_with_sigma( wave_table, gaussian_pos_y,
          vlambdadata, vorderdata, vsdata,  nlinematched, dac->niter, dac->frac,
					  dac->sigma, rejected));
      nb_rejected = 0;
      for(i=0; i< nlinematched; i++){
        if (rejected[i] == 1){
          nb_rejected++;
        }
        else{
          /* reindex data */
          vxthedata[i-nb_rejected] = vxthedata[i];
          vythedata[i-nb_rejected] = vythedata[i];
          vsindexdata[i-nb_rejected] = vsindexdata[i];
          corr_x[i-nb_rejected] = corr_x[i];
          corr_y[i-nb_rejected] = corr_y[i];
          gaussian_pos_x[i-nb_rejected] = gaussian_pos_x[i];
          gaussian_sigma_x[i-nb_rejected] = gaussian_sigma_x[i];
          gaussian_sigma_y[i-nb_rejected] = gaussian_sigma_y[i];
          gaussian_fwhm_x[i-nb_rejected] = gaussian_fwhm_x[i];
          gaussian_fwhm_y[i-nb_rejected] = gaussian_fwhm_y[i];
        }
      }
      nlinematched = nlinematched-nb_rejected;
      /* X = f(lambda,n,s) */
      check(fit2dx = xsh_wavesol_get_polx(wave_table));
      check(xsh_wavesol_compute(wave_table, nlinematched, gaussian_pos_x,
          &wave_table->min_x, &wave_table->max_x, vlambdadata, vorderdata,
          vsdata, fit2dx));

      sprintf(wave_table_name,"%s%s.fits","WAVE_TAB_2D_",
          xsh_instrument_arm_tostring( instr )) ;


      wave_table_tag = XSH_GET_TAG_FROM_ARM( XSH_WAVE_TAB_2D, instr);
    }



    /* save the wavelength solution */
    check(header = xsh_wavesol_get_header(wave_table));
    check(xsh_pfits_set_qc_nlinecat(header,nlinecat));
    xsh_msg("poly nlinecat=%d",nlinecat);

    check(xsh_pfits_set_qc_nlinefound(header,sol_size));
    //    check(xsh_pfits_set_qc_nlinecat_clean(header,nlineclean));
    /*    check(xsh_pfits_set_qc(header, (void*)model_date, XSH_QC_MODEL_FMTCHK_DATE,
     instr));*/
    check( wave_trace=xsh_wavesol_trace(wave_table,vlambdadata,vorderdata,
        vsdata,nlinematched));

    check( *wave_tab_frame=xsh_wavesol_save(wave_table,wave_trace,
        wave_table_name,wave_table_tag));
    //xsh_add_temporary_file( wave_table_name) ;
    check( cpl_frame_set_tag( *wave_tab_frame, wave_table_tag));
  } /* end of poly mode case (if wavetab is provided in input) */


  /* Produce the clean arcline list using only not flagged points (flag==0) */
  xsh_arclist_clean_from_list_not_flagged( arclist, vlambdadata, flag,sol_size);

  nlinecat_clean = arclist->size-arclist->nbrejected;
  check( header = xsh_arclist_get_header( arclist));
  check( xsh_pfits_set_qc_nlinecat( header,nlinecat));

  check( xsh_pfits_set_qc_nlinefound( header, sol_size));
  check( xsh_pfits_set_qc_nlinecat_clean( header,nlinecat_clean));
  check( xsh_pfits_set_qc_nlinefound_clean( header,nlinematched));
  if(strcmp(rec_id,"xsh_predict") == 0) {
    tag=XSH_GET_TAG_FROM_ARM( XSH_ARC_LINE_LIST_PREDICT, instr);
  } else {
    tag=XSH_GET_TAG_FROM_ARM( XSH_ARC_LINE_LIST_2DMAP, instr);
  }

  sprintf(fname,"%s%s",tag,".fits");
  check( *arc_lines_clean_tab_frame = xsh_arclist_save( arclist,fname,tag));
  xsh_add_temporary_file(fname);
  

  /* Produce the residual tab */
  check( resid = xsh_resid_tab_create( sol_size, vlambdadata, vorderdata,
      vsdata,vsndata,vsindexdata,vxthedata,vythedata,
      corr_x,corr_y,gaussian_norm,
      gaussian_pos_x,gaussian_pos_y,
      gaussian_sigma_x, gaussian_sigma_y,
      gaussian_fwhm_x, gaussian_fwhm_y,flag,
      wave_table,solution_type));

    if(resid_tab_name_sw) {
       sprintf(rtag,"%s%s%s",type,"RESID_TAB_LINES_",
             xsh_instrument_arm_tostring( instr ));
    } else {
       sprintf(rtag,"%s%s%s",type,"RESID_TAB_ALL_LINES_",
             xsh_instrument_arm_tostring( instr ));
    }
  check( tag = cpl_frame_get_tag( frame));

  XSH_REGDEBUG("TAG %s", tag);

  if ( strstr( tag, XSH_AFC_CAL) ){
    sprintf(rname,"AFC_CAL_%s%s",rtag,".fits") ;
  }
  else if ( strstr( tag, XSH_AFC_ATT) ){
    sprintf(rname,"AFC_ATT_%s%s",rtag,".fits") ;
  }
  else {
    sprintf(rname,"%s%s",rtag,".fits") ;
  }
  check( *resid_tab_frame = xsh_resid_tab_save( resid,rname,instr,rtag)); 
  if(clean_tmp) {
    xsh_add_temporary_file(rname);
  }

  cleanup:
  XSH_FREE( vlambdadata);
  XSH_FREE( vorderdata);
  XSH_FREE( vsdata);
  XSH_FREE( vsndata);
  XSH_FREE( vsindexdata);
  XSH_FREE( vxthedata);
  XSH_FREE( vythedata);
  XSH_FREE( corr_x);
  XSH_FREE( corr_y);
  XSH_FREE( gaussian_pos_x);
  XSH_FREE( gaussian_pos_y);
  XSH_FREE( gaussian_sigma_x);
  XSH_FREE( gaussian_sigma_y);
  XSH_FREE( gaussian_fwhm_x);
  XSH_FREE( gaussian_fwhm_y);
  XSH_FREE( gaussian_norm);
  XSH_FREE( sort);
  XSH_FREE( diffx);
  XSH_FREE( diffy);
  XSH_FREE( diffxmean);
  XSH_FREE( diffymean);
  XSH_FREE( diffxsig);
  XSH_FREE( diffysig);
  XSH_FREE( rejected);
  XSH_FREE( flag);

  xsh_free_table( &wave_trace);
  xsh_spectralformat_list_free(&spectralformat_list);
  xsh_pre_free( &pre);
  xsh_the_map_free( &themap);
  xsh_wavesol_free( &wave_table);
  xsh_wavesol_free( &wave_tab_guess);
  xsh_order_list_free( &order_tab_recov);
  xsh_resid_tab_free( &resid_orders);
  xsh_resid_tab_free( &resid);
  xsh_arclist_free( &arclist);
  return;
}
/*---------------------------------------------------------------------------*/
/** 
 @brief detect the position on the detector of emission lines listed in a 
   catalogue, from expected position values given by the Physical model.

 @param[in] frame 
   The data frame in which we detect arclines
 @param[in] theo_tab_frame 
   The theoretical map contains expected positions of lines on the detector (POLYNOMIAL mode) or NULL
 @param[in] arc_lines_tab_frame 
   List of emission lines to be used for the calibration
 @param[in] wave_tab_guess_frame 
   Polynomial coefficients for the dispersion equation of the single pin hole image or NULL (POLYNOMIAL ITERATIVE mode) or NULL
 @param[in] order_tab_recov_frame 
   Order table frame indicates X = f(Y) or NULL (POLYNOMIAL RECOVER mode) or NULL
 @param[in] config_model_frame 
   Configuration model file (MODEL mode) or NULL
 @param[in] spectralformat_frame 
   Spectral format file (MODEL mode) or NULL
 @param[out] resid_tab_orders_frame
   Residual of the lines filtering
 @param[out] arc_lines_clean_tab_frame
   List of emission lines effectively used in the calibration. Non reliable 
   lines have been discarded.
 @param[out] wave_tab_frame
   Polynomial coefficients for the dispersion equation
 @param[out] resid_tab_frame
   Residual of the modeled 2D mapping
 @param[in] solwave_type
   Type of product wavelength solution
 @param[in] da
   Detect_arclines recipe parameters
 @param[in] dac
   Detect_arclines clipping parameters
 @param[in] instr
   Instrument containing the arm, mode and lamp in use
 @param[in] rec_id recipe id
*/

void 
xsh_detect_arclines( cpl_frame *frame, 
                     cpl_frame *theo_tab_frame,
                     cpl_frame *arc_lines_tab_frame, 
                     cpl_frame* wave_tab_guess_frame,
                     cpl_frame *order_tab_recov_frame,
                     cpl_frame *config_model_frame,
                     cpl_frame *spectralformat_frame,
                     cpl_frame **resid_tab_orders_frame,
                     cpl_frame **arc_lines_clean_tab_frame,
                     cpl_frame **wave_tab_frame,  
                     cpl_frame **resid_tab_frame,
                     xsh_sol_wavelength solwave_type,
                     xsh_detect_arclines_param *da, 
                     xsh_clipping_param *dac,
                     xsh_instrument *instr,
                     const char* rec_id,
                     const int clean_tmp,const int resid_tab_name_sw)
{
 /* MUST BE DEALLOCATED in caller */

 /* ALLOCATED locally */
 xsh_the_map *themap = NULL;
 xsh_resid_tab *resid = NULL;
 xsh_resid_tab *resid_orders = NULL;
 xsh_arclist *arclist = NULL;
 xsh_pre *pre = NULL;
 cpl_polynomial *fit2dx = NULL;
 cpl_propertylist* header = NULL;
 int * sort = NULL;
 double *vlambdadata = NULL, *vsdata = NULL, *vorderdata = NULL, *vsndata=NULL;
 int *vsindexdata = NULL;
 double *vxthedata = NULL,  *vythedata = NULL;
 double *corr_x = NULL, *corr_y = NULL;
 double *gaussian_pos_x = NULL, *gaussian_pos_y = NULL, 
    *gaussian_sigma_x = NULL, *gaussian_sigma_y = NULL,
    *gaussian_fwhm_x = NULL, *gaussian_fwhm_y = NULL, *gaussian_norm=NULL;
 cpl_vector *gaussian_sigma_x_vect =NULL;
 cpl_vector *gaussian_sigma_y_vect =NULL; 
 cpl_vector *gaussian_fwhm_x_vect =NULL;
 cpl_vector *gaussian_fwhm_y_vect =NULL; 
 //cpl_vector *gaussian_norm_vect =NULL; 

 double *diffx = NULL, *diffy = NULL;
 double *diffxmean = NULL, *diffymean = NULL, *diffxsig = NULL, *diffysig = NULL;
 xsh_order_list *order_tab_recov = NULL;
 /* Others */
 int nlinematched, nlinecat_clean=0, nlinecat;
 int i, sol_size = 0;
 xsh_wavesol* wave_table = NULL;
 char wave_table_name[256];
 const char* wave_table_tag = NULL;
 xsh_wavesol* wave_tab_guess = NULL;
 xsh_xs_3 config_model;
 xsh_xs_3* p_xs_3;
 xsh_spectralformat_list *spectralformat_list = NULL;
 int lines_not_in_image = 0;
 int lines_not_good_sn = 0;
 int lines_not_gauss_fit = 0;
 int lines_not_valid_pixels = 0;
 int lines_too_few_ph=0;
 //const char* model_date = NULL;
 int* rejected = NULL;
 int nb_rejected = 0;
 int solution_type = 0;
 const char* solution_type_name[2] = { "POLY", "MODEL"};
 int detection_mode = 0;
 const char* detection_mode_name[3] = { "NORMAL", "CORRECTED", "RECOVER"};
 int nb_pinhole;
 char fname[256];
 char rname[256];
 char rtag[256];
 const char* tag=NULL;

 char dpr_type[256];
 char type[256];
 cpl_table* wave_trace=NULL;
 int* flag=NULL;

   cpl_table* cfg_tab=NULL;
   const char* cfg_name=NULL;
   char new_name[256];
   char basename[256];
 

   cpl_propertylist* plist=NULL;
   //double* p_cfg_val=NULL;
   //double zeroK=-273.15;

   int starti=0;
   int newi=0;
   int j,k;
   int min_slit_match=7;
  int found_temp=true;

 /* check input parameters */ 
 XSH_ASSURE_NOT_NULL( frame);
 XSH_ASSURE_NOT_NULL( arc_lines_tab_frame);
 XSH_ASSURE_NOT_NULL( spectralformat_frame);
 XSH_ASSURE_NOT_NULL( instr);
 XSH_ASSURE_NOT_NULL( da);
 XSH_ASSURE_NOT_NULL( dac);

 /* Ouput parameters */
 XSH_ASSURE_NOT_NULL( arc_lines_clean_tab_frame);
 XSH_ASSURE_NOT_NULL( resid_tab_frame);


 /* Debug parameters */
 xsh_msg_dbg_medium("---Detect arclines parameters");
 xsh_msg_dbg_medium("min_sn %f fit-window-half-size %d", da->min_sn, da->fit_window_hsize);
 xsh_msg_dbg_medium("Clipping sigma %f niter %d frac %f", dac->sigma, dac->niter, dac->frac);

 check( pre = xsh_pre_load (frame, instr));
 check_msg( nb_pinhole = xsh_pfits_get_nb_pinhole( pre->data_header),
            "fail to find number of pinholes info in frame %s",
            cpl_frame_get_filename(frame) );
 check(strcpy(dpr_type,xsh_pfits_get_dpr_type(pre->data_header)));

 if(strstr(dpr_type,"FMTCHK") != NULL) {
   strcpy(type,"FMTCHK_");
 } else if(strstr(dpr_type,"WAVE") != NULL) {
   strcpy(type,"WAVE_");
 } else if(strstr(dpr_type,"WAVE") != NULL) {
   strcpy(type,"ARC_");
 } else {
   strcpy(type,"");
 }

 check( arclist = xsh_arclist_load( arc_lines_tab_frame));
 check( spectralformat_list = xsh_spectralformat_list_load( 
   spectralformat_frame, instr));
 check(nlinecat = xsh_arclist_get_size( arclist));
 /* sort data by wavelength */
 check(xsh_arclist_lambda_sort( arclist));


 /* First define the solution type of detect_arclines */
 if ( theo_tab_frame != NULL) {
   XSH_ASSURE_NOT_ILLEGAL( config_model_frame == NULL);
   solution_type = XSH_DETECT_ARCLINES_TYPE_POLY;
   check( themap = xsh_the_map_load( theo_tab_frame));
   check( xsh_the_map_lambda_order_slit_sort( themap));
   check( theo_tab_filter( themap, arclist, &sol_size, &vlambdadata, 
     &vorderdata, &vsdata,  &vsindexdata, &vxthedata, &vythedata, nb_pinhole));

 }
 else if ( config_model_frame != NULL){
   solution_type = XSH_DETECT_ARCLINES_TYPE_MODEL;
   p_xs_3=&config_model;


    //Make sure to modify only current copy
    check(cfg_name=cpl_frame_get_filename(config_model_frame));
    check(plist=cpl_propertylist_load(cfg_name,0));
    check(cfg_tab=cpl_table_load(cfg_name,1,0));
    //check(basename=xsh_get_basename(cfg_name));
    tag=XSH_GET_TAG_FROM_ARM(XSH_MOD_CFG_TAB,instr);
    sprintf(basename,"%s.fits",tag);
    sprintf(new_name,"local_%s",basename);
    check( xsh_pfits_set_pcatg(plist,tag));
    check(cpl_table_save(cfg_tab,plist,NULL,new_name,CPL_IO_DEFAULT));
    xsh_add_temporary_file(new_name);
    check(cpl_frame_set_filename(config_model_frame,new_name));
    xsh_free_table(&cfg_tab);
    xsh_free_propertylist(&plist);

   check( xsh_model_config_load_best( config_model_frame, &config_model));
   XSH_REGDEBUG("load config model ok");
   /* update cfg table frame and corresponding model structure for temperature */
   check(xsh_model_temperature_update_frame(&config_model_frame,frame,
                                            instr,&found_temp));
   check(xsh_model_temperature_update_structure(&config_model,frame,instr));
   check( theo_tab_model( &config_model, arclist, spectralformat_list, 
			  &sol_size, &vlambdadata, &vorderdata, &vsdata,  
			  &vsndata, &vsindexdata, &vxthedata, &vythedata, 
			  instr, nb_pinhole));
 }
 else{
   XSH_ASSURE_NOT_ILLEGAL_MSG(1==0,
     "Undefined solution type (POLY or MODEL). See your input sof");
 }

 xsh_spectralformat_list_free(&spectralformat_list);
 xsh_msg_dbg_high("Solution type %s", solution_type_name[solution_type]);

 /* TODO detection mode checks and correction by order_tab_recov_frame is
  * not needed now that XSH is operational */

 /* Define the mode of use */
 if ( wave_tab_guess_frame == NULL){
   if ( order_tab_recov_frame == NULL){
     detection_mode = XSH_DETECT_ARCLINES_MODE_NORMAL;
   }
   else{
     detection_mode = XSH_DETECT_ARCLINES_MODE_RECOVER;
     check( order_tab_recov = xsh_order_list_load( order_tab_recov_frame, 
       instr));
   }
 }
 else {
   if ( order_tab_recov_frame == NULL){
     detection_mode = XSH_DETECT_ARCLINES_MODE_CORRECTED;

     check( wave_tab_guess = xsh_wavesol_load( wave_tab_guess_frame,
					       instr ));
     xsh_msg( "BinX,Y: %d, %d", wave_tab_guess->bin_x,
	      wave_tab_guess->bin_y ) ;
   }
   else{
     detection_mode = -1;
   }
 }

 XSH_ASSURE_NOT_ILLEGAL( detection_mode >=XSH_DETECT_ARCLINES_MODE_NORMAL 
   && detection_mode <= XSH_DETECT_ARCLINES_MODE_RECOVER);

 xsh_msg( "Detection mode : %s", detection_mode_name[detection_mode]);

 xsh_msg( "Solution size %d", sol_size);

 /* allocate some memory */
 XSH_MALLOC( corr_x, double, sol_size);
 XSH_MALLOC( corr_y, double, sol_size);

 XSH_MALLOC( gaussian_pos_x, double, sol_size);
 XSH_MALLOC( gaussian_pos_y, double, sol_size);
 XSH_MALLOC( gaussian_sigma_x, double, sol_size);
 XSH_MALLOC( gaussian_sigma_y, double, sol_size);

 XSH_MALLOC( gaussian_fwhm_x, double, sol_size);
 XSH_MALLOC( gaussian_fwhm_y, double, sol_size);
 XSH_MALLOC( gaussian_norm, double, sol_size);

 XSH_MALLOC( diffx, double, sol_size);
 XSH_MALLOC( diffy, double, sol_size);

 XSH_MALLOC( diffxmean, double, sol_size);
 XSH_MALLOC( diffymean, double, sol_size);

 XSH_MALLOC( diffxsig, double, sol_size);
 XSH_MALLOC( diffysig, double, sol_size);

 /* init */
 nlinematched = 0;

 if ( da->find_center_method == XSH_GAUSSIAN_METHOD){
   xsh_msg_dbg_medium( "USE method GAUSSIAN");
 }
 else{
   xsh_msg_dbg_medium( "USE method BARYCENTER");
 }

 /* TODO: this ASCII input should be removed */
 if (xsh_debug_level_get() >= XSH_DEBUG_LEVEL_HIGH) {
   FILE* regfile = NULL;

   regfile = fopen( "FIT.reg", "w");
   fprintf( regfile, "# Region file format: DS9 version 4.0\n"\
     "global color=red font=\"helvetica 4 normal\""\
     "select=1 highlite=1 edit=1 move=1 delete=1 include=1 fixed=0 "\
     "source\nimage\n");
   fclose( regfile);
   regfile = fopen( "NOFIT.reg", "w");
   fprintf( regfile, "# Region file format: DS9 version 4.0\n"\
     "global color=red font=\"helvetica 4 normal\""\
     "select=1 highlite=1 edit=1 move=1 delete=1 include=1 fixed=0 "\
     "source\nimage\n");
   fclose( regfile);
 }

 for(i=0; i< sol_size; i++){
   int xpos = 0, ypos = 0;
   int slit_index;
   double corrxv = 0.0, corryv = 0.0;
   double lambda, order, slit, xthe, ythe,sn=0;
   double xcor, ycor, x, y, sig_x, sig_y, norm, fwhm_x, fwhm_y;
   /* double max_off=1.0; */

   lambda = vlambdadata[i];
   order = vorderdata[i];
   slit = vsdata[i];

   if(vsndata != NULL) {
      sn = vsndata[i];
   }

   slit_index = vsindexdata[i];
   xthe = vxthedata[i];
   ythe = vythedata[i];
   xsh_msg_dbg_high( "THE LAMBDA %f ORDER %f SLIT %f X %f Y %f", lambda, 
     order, slit, xthe, ythe);

   /* Theoretical using pixel FITS convention */
   xcor = xthe;
   ycor = ythe;
   xsh_msg_dbg_high("THE x%f y %f", xthe, ythe);

   /* Find X position  from RECOVER ORDER TAB */
   if ( order_tab_recov != NULL){
     int iorder = 0;
     cpl_polynomial *center_poly_recov = NULL; 

     check (iorder = xsh_order_list_get_index_by_absorder( 
       order_tab_recov, order));
     center_poly_recov = order_tab_recov->list[iorder].cenpoly;
     check( xcor = cpl_polynomial_eval_1d( center_poly_recov, ycor, NULL));
       corrxv = xcor-xthe;
   }
   /* apply x and y translation from predict data to the model */
   if ( wave_tab_guess != NULL){
     double diffxv, diffyv;

     check( diffxv = xsh_wavesol_eval_polx( wave_tab_guess, lambda, 
         order, 0));
     check( diffyv = xsh_wavesol_eval_poly( wave_tab_guess, lambda, 
         order, 0));
  
     xcor = xthe+diffxv;
     ycor = ythe+diffyv;
     corrxv = diffxv;
     corryv = diffyv;
   }
   /* Corrected position using pixel FITS convention */
   xsh_msg_dbg_high("x %f y %f CORR_x %f CORR_y %f", xcor, ycor,
     corrxv, corryv);

   if ( xcor >=0.5 && ycor >=0.5 && 
        xcor < (pre->nx+0.5) && ycor < (pre->ny+0.5)){
     int best_med_res = 0;
     check ( best_med_res = xsh_pre_window_best_median_flux_pos( pre, 
       (int)(xcor+0.5)-1, (int)(ycor+0.5)-1, 
       da->search_window_hsize, da->running_median_hsize, &xpos, &ypos));

     if (best_med_res != 0){
       lines_not_valid_pixels++;
       continue;
     }
     /* Put positions in CPL coordinates */
     xpos = xpos+1;
     ypos = ypos+1;
     xsh_msg_dbg_high("ADJ x %d y %d", xpos, ypos);

     if ( da->find_center_method == XSH_GAUSSIAN_METHOD){
       cpl_image_fit_gaussian(pre->data, xpos, ypos, 1+2*da->fit_window_hsize,
         &norm, &x, &y, &sig_x, &sig_y, &fwhm_x, &fwhm_y);
       xsh_msg_dbg_high("GAUS x %f y %f %d %d %d", x, y,da->fit_window_hsize, da->search_window_hsize, da->running_median_hsize);

       
       /*
	cpl_array* parameters=cpl_array_new(7, CPL_TYPE_DOUBLE);
	cpl_array* err_params=cpl_array_new(7, CPL_TYPE_DOUBLE);
	cpl_array* fit_params=cpl_array_new(7, CPL_TYPE_INT);
        int kk=0;
	//All parameter should be fitted
	for (kk = 0; kk < 7; kk++)
	  cpl_array_set(fit_params, kk, 1);
	double rms=0;
	double red_chisq=0;
	cpl_matrix* covariance=NULL;
	cpl_matrix* phys_cov=NULL;
	double major=0;
	double minor=0;
	double angle=0;
        int size=1+2*da->fit_window_hsize;
	cpl_fit_image_gaussian(pre->data, pre->errs, xpos, ypos,size,size,
				     parameters,err_params,fit_params,
				     &rms,&red_chisq,&covariance,
			       &major,&minor,&angle,&phys_cov);
	
	double rho=0;
	double back=cpl_array_get(parameters,0,NULL);
	norm=cpl_array_get(parameters,1,NULL);
	rho=cpl_array_get(parameters,2,NULL);

	x=cpl_array_get(parameters,3,NULL);
	y=cpl_array_get(parameters,4,NULL);
	sig_x=cpl_array_get(parameters,5,NULL);
	sig_y=cpl_array_get(parameters,6,NULL);
	fwhm_x=sig_x*CPL_MATH_FWHM_SIG;
	fwhm_y=sig_y*CPL_MATH_FWHM_SIG;

        xsh_free_array(&parameters);
        xsh_free_array(&err_params);
        xsh_free_array(&fit_params);
       */
       



     }
     else{
       xsh_image_find_barycenter( pre->data, xpos, ypos, 
         1+2*da->fit_window_hsize,
         &norm, &x, &y, &sig_x, &sig_y, &fwhm_x, &fwhm_y);
       xsh_msg_dbg_high("BARY x %f y %f", x, y);
     }

     /* (PBR) Adjust 1ph centroids to be consistent with 9ph
	- This is cleaner than adjusting the ph position in the cfg because:
	  a. we do it just once here -> it gets used in the matching, then
	     propogates to the model optimisation
	  b. We don't need to know if the input config was itself optimised
	     to 9ph or 1ph (i.e. do we need the shift or not)
	  c. Potentially this can be used for poly-mode too, though I
	     restrict it to phys mod below (feel free to change it)
	- The NIR value of 0.125pix for x I got by using a config optimised
	  to 9ph data (i.e. 2dmap) in xsh_predict and checking the input
	  offsets. For y the offset was less than the s.d.
	- Same procedure for UVB. Also same for VIS, but value was ~0
        - A correction from 9ph to full slit centre is still required*/
     if (nb_pinhole==1 && solution_type==XSH_DETECT_ARCLINES_TYPE_MODEL) {
       if (p_xs_3->arm==2) {
	 x=x+0.125;
       }
       else if (p_xs_3->arm==0) {
	 x=x-0.51;
       }
     }

     if (xsh_debug_level_get() >= XSH_DEBUG_LEVEL_HIGH) {
       const char* fit = NULL;
       const char* color = NULL;
       FILE* regfile = NULL;

       if (cpl_error_get_code() == CPL_ERROR_NONE){
         fit = "FIT.reg";
         color = "green";
       }
       else{
         fit = "NOFIT.reg";
         color = "red";
       }
       regfile = fopen( fit, "a");
       fprintf( regfile, "point(%d,%d) #point=cross color=blue\n", (int)(xcor), (int)(ycor));
       fprintf( regfile, "box(%d,%d,%d,%d) #color=blue\n", (int)(xcor), (int)(ycor), 
         da->search_window_hsize*2,da->search_window_hsize*2);
       fprintf( regfile, "point(%d,%d) #point=cross color=%s font=\"helvetica 10 normal\""\
         " text={%.3f}\n", xpos, ypos, color, lambda);
       fprintf( regfile, "box(%d,%d,%d,%d) #color=%s\n", (int)xpos, (int)ypos, 
         1+2*da->fit_window_hsize, 1+2*da->fit_window_hsize, color);
       fclose( regfile);
     }

     if( cpl_error_get_code() == CPL_ERROR_NONE ){
       if ( lines_filter_by_sn( pre, da->min_sn, x, y, &sn) ){
           vlambdadata[nlinematched] = lambda;
           vorderdata[nlinematched] = order;
           vsdata[nlinematched] = slit;
           if(vsndata!=NULL) {
           vsndata[nlinematched] = sn;
           } 

           vsindexdata[nlinematched] = slit_index;
           vxthedata[nlinematched] = xthe;
           vythedata[nlinematched] = ythe;
           corr_x[nlinematched] = corrxv; 
           corr_y[nlinematched] = corryv;
           gaussian_pos_x[nlinematched] = x;
           gaussian_pos_y[nlinematched] = y;
           gaussian_sigma_x[nlinematched] = sig_x;
           gaussian_sigma_y[nlinematched] = sig_y;
           gaussian_fwhm_x[nlinematched] = fwhm_x;
           gaussian_fwhm_y[nlinematched] = fwhm_y;
           gaussian_norm[nlinematched] = norm;
           diffx[nlinematched] = x-xthe;
           diffy[nlinematched] = y-ythe;
/* 	   if ((fabs(diffx[nlinematched])>max_off || */
/* 		fabs(diffy[nlinematched])>max_off) */
/* 	       && nb_pinhole>1) { */
/* 	     xsh_msg_dbg_medium("Offset too large for this line"); */
/* 	   } */
/* 	   else { */
 	     nlinematched++; 
/* 	   } */
       }
       else{
         lines_not_good_sn++;
         xsh_msg_dbg_medium("Not good s/n for this line");
         if (xsh_debug_level_get() >= XSH_DEBUG_LEVEL_HIGH) {
           
           int xpix, ypix, rej;
           float flux, noise, sn;
           FILE* regfile = NULL;
           char sn_name[256];

           xpix =(int) rint(x);
           ypix = (int)rint(y);

           check( flux = cpl_image_get( pre->data, xpix, ypix, &rej));
           check( noise = cpl_image_get( pre->errs, xpix, ypix, &rej));
           sn = flux / noise;
           sprintf( sn_name, "bad_sn_%.3f.reg", da->min_sn);
           regfile = fopen( sn_name, "a");
           fprintf( regfile, "point(%d,%d) #point=cross color=red font=\"helvetica 10 normal\""\
             " text={%.3f [%.3f}\n", xpix, ypix, lambda, sn);
           fclose( regfile);
         }
       }
     }
     else{
       lines_not_gauss_fit++;
       xsh_msg_dbg_medium("No Fit Gaussian for this line");
       xsh_error_reset();
     }
   }
   else{
     lines_not_in_image++;
     xsh_msg_dbg_medium("Coordinates are not in the image");
   }
 }


 /*Remove all matches where there are less than 7 out of 9 slit positions matched*/
 /*The following assumes that the vectors are ordered so that, within each order,
   all matches for a single wavelength are grouped together*/
  if (nb_pinhole>1) {
    for (i=1; i<nlinematched; i++) {
      xsh_msg_dbg_high("filter 7 pinhole : line %d : lambda %f order %f slit %f", 
        i, vlambdadata[i], vorderdata[i], vsdata[i]);

      if (vlambdadata[i]!=vlambdadata[i-1] || vorderdata[i]!=vorderdata[i-1] || i==nlinematched-1) {
        
        /*special case for last line*/
        if (i==nlinematched-1) {
	  i++;
        }
        xsh_msg_dbg_high("filter 7 pinhole : from %d to %d find %d pinholes", 
          starti, i, i-starti);

        if (i-starti>=min_slit_match) {
	  for (k=starti; k<=i-1; k++) {
	    diffxmean[k]=0.0;
	    diffymean[k]=0.0;
	    for (j=starti; j<=i-1; j++) {
	      if (j!=k) {
	        diffxmean[k]+=diffx[j];
	        diffymean[k]+=diffy[j];
	      }
	    }
	    diffxmean[k]/=(float)(i-starti-1);
	    diffymean[k]/=(float)(i-starti-1);
	  }
	  for (k=starti; k<=i-1; k++) {
	    diffxsig[k]=0.0;
	    diffysig[k]=0.0;
	    for (j=starti; j<=i-1; j++) {
	      if (j!=k) {
	        diffysig[k]+=(diffy[j]-diffymean[k])*(diffy[j]-diffymean[k]);
	        diffxsig[k]+=(diffx[j]-diffxmean[k])*(diffx[j]-diffxmean[k]);
	      }
	    }
	    diffxsig[k]=sqrt(diffxsig[k]/(float)(i-starti-1));
	    diffysig[k]=sqrt(diffysig[k]/(float)(i-starti-1));
	  }
	  for (j=starti; j<=i-1; j++) {
	  /*Below would remove individual ph with very different resids than the mean for this line*/
	   //if (fabs(diffx[j]-diffxmean)<1.5*diffxsig && fabs(diffy[j]-diffymean)<1.5*diffysig) {
	     vlambdadata[newi] = vlambdadata[j];
	     vorderdata[newi] = vorderdata[j];
	     vsdata[newi] =vsdata[j];
	     vsindexdata[newi] = vsindexdata[j];
	     vxthedata[newi] =vxthedata[j];
	     vythedata[newi] =vythedata[j];
	     corr_x[newi] = corr_x[j];
	     corr_y[newi] = corr_y[j];
	     gaussian_pos_x[newi] = gaussian_pos_x[j];
	     gaussian_pos_y[newi] = gaussian_pos_y[j];
	     gaussian_sigma_x[newi] = gaussian_sigma_x[j];
	     gaussian_sigma_y[newi] = gaussian_sigma_y[j];
	     gaussian_fwhm_x[newi] = gaussian_fwhm_x[j];
	     gaussian_fwhm_y[newi] = gaussian_fwhm_y[j];
	     gaussian_norm[newi] = gaussian_norm[j];
	     diffx[newi] = diffx[j];
	     diffy[newi] = diffy[j];
	     newi++;
	     //}
/* 	   else { */
/* 	     printf("no %lf %lf %lf %lf %lf\n",vlambdadata[starti], fabs(diffx[j]),diffxmean,fabs(diffx[j]-diffxmean),diffxsig); */
/* 	     printf("no %lf %lf %lf %lf %lf\n",vlambdadata[starti], fabs(diffx[j]),diffxmean,fabs(diffx[j]-diffxmean),diffxsig); */
/* 	   } */
	 }
       } /*end min_slit_match*/
       else {
	 lines_too_few_ph+=i-starti;
       }
       starti=i;
      }
    }/*endfor*/
    nlinematched=newi;
  }

 xsh_msg("nlinematched / sol_size = %d / %d", nlinematched, sol_size);
 assure(nlinematched > 0, CPL_ERROR_ILLEGAL_INPUT,
        "No line matched, "
        "detectarclines-search-win-hsize, "
        "detectarclines-fit-win-hsize=%d, "
        "detectarclines-running-median-hsize too large or too small "
        "or detectarclines-min-sn may be too large", 
        da->fit_window_hsize);

 xsh_msg_dbg_medium("  %d lines not found in image", lines_not_in_image);
 xsh_msg_dbg_medium("  %d lines not good S/N", lines_not_good_sn);
 xsh_msg_dbg_medium("  %d lines no fit gaussian", lines_not_gauss_fit);
 xsh_msg_dbg_medium("  %d lines no valid pixels", lines_not_valid_pixels);
 xsh_msg_dbg_medium("  %d lines detected in less than %d/9 ph positions", lines_too_few_ph,min_slit_match);
 check( gaussian_sigma_x_vect = cpl_vector_wrap( nlinematched, gaussian_sigma_x));
 check( gaussian_sigma_y_vect = cpl_vector_wrap( nlinematched, gaussian_sigma_y)); 
 check( gaussian_fwhm_x_vect = cpl_vector_wrap( nlinematched, gaussian_fwhm_x));
 check( gaussian_fwhm_y_vect = cpl_vector_wrap( nlinematched, gaussian_fwhm_y)); 
 xsh_msg("sigma gaussian median in x %lg",
   cpl_vector_get_median_const( gaussian_sigma_x_vect));  
 xsh_msg("sigma gaussian median in y %lg",
   cpl_vector_get_median_const( gaussian_sigma_y_vect));

 xsh_msg("FWHM gaussian median in x %lg",
   cpl_vector_get_median_const( gaussian_fwhm_x_vect));  
 xsh_msg("FWHM gaussian median in y %lg",
   cpl_vector_get_median_const( gaussian_fwhm_y_vect));


 xsh_unwrap_vector( &gaussian_sigma_x_vect);
 xsh_unwrap_vector( &gaussian_sigma_y_vect);

 xsh_unwrap_vector( &gaussian_fwhm_x_vect);
 xsh_unwrap_vector( &gaussian_fwhm_y_vect);


 /* Produce the residual orders tab */

 if ( resid_tab_orders_frame != NULL){
    check(resid_orders=xsh_resid_tab_create(nlinematched,vlambdadata,vorderdata, 
                                            vsdata, vsndata,vsindexdata,
                                            vxthedata, vythedata,
                                            corr_x, corr_y,
                                            gaussian_norm,
                                            gaussian_pos_x, gaussian_pos_y, 
                                            gaussian_sigma_x, gaussian_sigma_y,
                                            gaussian_fwhm_x, gaussian_fwhm_y,flag, 
                                            wave_table, solution_type));

   
   sprintf(rtag,"%s%s%s",type,"RESID_TAB_ORDERS_",
			 xsh_instrument_arm_tostring( instr ));

   sprintf(rname,"%s%s",rtag,".fits");

   check( *resid_tab_orders_frame = xsh_resid_tab_save( resid_orders, 
							rname, instr,rtag));
   xsh_add_temporary_file(rname);
    
 }
 xsh_msg_dbg_high("solution_type=%d poly=%d model=%d",solution_type,
         XSH_DETECT_ARCLINES_TYPE_POLY, XSH_DETECT_ARCLINES_TYPE_MODEL);


 /* Produce the wave tab if need */
 if ( solution_type == XSH_DETECT_ARCLINES_TYPE_POLY && 
   (wave_tab_frame != NULL)){
   check( wave_table = xsh_wavesol_create( spectralformat_frame, da, instr));
   XSH_CALLOC( rejected, int, nlinematched);
   if ( solwave_type == XSH_SOLUTION_RELATIVE) {
     /* compute dY = f(lambda,n) */
     check( xsh_wavesol_set_type( wave_table, XSH_WAVESOL_GUESS));
     check( data_wavesol_fit_with_sigma( wave_table, diffy, vlambdadata, 
       vorderdata, vsdata,  nlinematched, dac->niter, dac->frac, 
       dac->sigma, rejected));
     nb_rejected = 0;
     for(i=0; i< nlinematched; i++){
       if (rejected[i] == 1){
         nb_rejected++;
       }
       else{
         /* reindex data */
         vxthedata[i-nb_rejected] = vxthedata[i];
         vythedata[i-nb_rejected] = vythedata[i];
         vsindexdata[i-nb_rejected] = vsindexdata[i];
         corr_x[i-nb_rejected] = corr_x[i];
         corr_y[i-nb_rejected] = corr_y[i];
         gaussian_pos_x[i-nb_rejected] = gaussian_pos_x[i];
         gaussian_pos_y[i-nb_rejected] = gaussian_pos_y[i];
         gaussian_sigma_x[i-nb_rejected] = gaussian_sigma_x[i];
         gaussian_sigma_y[i-nb_rejected] = gaussian_sigma_y[i];
         gaussian_fwhm_x[i-nb_rejected] = gaussian_fwhm_x[i];
         gaussian_fwhm_y[i-nb_rejected] = gaussian_fwhm_y[i];
         gaussian_norm[i-nb_rejected] = gaussian_norm[i];
         diffx[i-nb_rejected] = diffx[i];
       }
     }
     nlinematched = nlinematched-nb_rejected;
     /* compute dX = f(lambda,n) */
     check( fit2dx = xsh_wavesol_get_polx( wave_table));
     check( xsh_wavesol_compute( wave_table, 
       nlinematched, diffx,
       &(wave_table->min_x), &(wave_table->max_x), 
     vlambdadata, vorderdata, vsdata, fit2dx));


     sprintf(wave_table_name,"%s%s.fits","WAVE_TAB_GUESS_",
             xsh_instrument_arm_tostring( instr )) ;

     wave_table_tag = XSH_GET_TAG_FROM_ARM( XSH_WAVE_TAB_GUESS, instr);
 
   }
   else{
     check( xsh_wavesol_set_type( wave_table, XSH_WAVESOL_2D));
     /* Y = f(lambda,n,s) with sigma clipping */
     check( data_wavesol_fit_with_sigma( wave_table, gaussian_pos_y, 
       vlambdadata, vorderdata, vsdata,  nlinematched, dac->niter, dac->frac,
       dac->sigma, rejected));
     nb_rejected = 0;
     for(i=0; i< nlinematched; i++){
       if (rejected[i] == 1){
         nb_rejected++;
       }
       else{
         /* reindex data */
         vxthedata[i-nb_rejected] = vxthedata[i];
         vythedata[i-nb_rejected] = vythedata[i];
         vsindexdata[i-nb_rejected] = vsindexdata[i];
         corr_x[i-nb_rejected] = corr_x[i];
         corr_y[i-nb_rejected] = corr_y[i];
         gaussian_pos_x[i-nb_rejected] = gaussian_pos_x[i];
         gaussian_sigma_x[i-nb_rejected] = gaussian_sigma_x[i];
         gaussian_sigma_y[i-nb_rejected] = gaussian_sigma_y[i];
         gaussian_fwhm_x[i-nb_rejected] = gaussian_fwhm_x[i];
         gaussian_fwhm_y[i-nb_rejected] = gaussian_fwhm_y[i];
       }
     }
     nlinematched = nlinematched-nb_rejected;
     /* X = f(lambda,n,s) */
     check(fit2dx = xsh_wavesol_get_polx(wave_table));
     check(xsh_wavesol_compute(wave_table, nlinematched, gaussian_pos_x,
       &wave_table->min_x, &wave_table->max_x, vlambdadata, vorderdata, 
       vsdata, fit2dx));

     sprintf(wave_table_name,"%s%s.fits","WAVE_TAB_2D_",
             xsh_instrument_arm_tostring( instr )) ;


     wave_table_tag = XSH_GET_TAG_FROM_ARM( XSH_WAVE_TAB_2D, instr);
   }

   /* save the wavelength solution */
   check(header = xsh_wavesol_get_header(wave_table));
   check(xsh_pfits_set_qc_nlinecat(header,nlinecat));
   xsh_msg("nlinecat=%d",nlinecat);

   check(xsh_pfits_set_qc_nlinefound(header,sol_size));
//    check(xsh_pfits_set_qc_nlinecat_clean(header,nlineclean));
/*    check(xsh_pfits_set_qc(header, (void*)model_date, XSH_QC_MODEL_FMTCHK_DATE,
     instr));*/
   check( wave_trace=xsh_wavesol_trace(wave_table,vlambdadata,vorderdata,
				       vsdata,nlinematched));

   check( *wave_tab_frame=xsh_wavesol_save(wave_table,wave_trace,
					   wave_table_name,wave_table_tag));
   //xsh_add_temporary_file( wave_table_name) ;
   check( cpl_frame_set_tag( *wave_tab_frame, wave_table_tag)); 
 }

 /* Produce the clean arcline list */
 check( xsh_arclist_clean_from_list( arclist, vlambdadata, nlinematched));
 nlinecat_clean = arclist->size-arclist->nbrejected;
 check( header = xsh_arclist_get_header( arclist));
 check( xsh_pfits_set_qc_nlinecat( header,nlinecat));
  xsh_msg("nlinecat=%d",nlinecat);

 check( xsh_pfits_set_qc_nlinefound( header, sol_size));
 check( xsh_pfits_set_qc_nlinecat_clean( header,nlinecat_clean));
 check( xsh_pfits_set_qc_nlinefound_clean( header,nlinematched));
 if(strcmp(rec_id,"xsh_predict") == 0) {
 tag=XSH_GET_TAG_FROM_ARM( XSH_ARC_LINE_LIST_PREDICT, instr);
 } else {
 tag=XSH_GET_TAG_FROM_ARM( XSH_ARC_LINE_LIST_2DMAP, instr);
 }
 sprintf(fname,"%s%s",tag,".fits");
 check( *arc_lines_clean_tab_frame = xsh_arclist_save( arclist,fname,tag));
 if(clean_tmp) {
    xsh_add_temporary_file(fname);
 }
  /* Produce the residual tab */
  check( resid = xsh_resid_tab_create( nlinematched, vlambdadata, vorderdata, 
                                       vsdata,vsndata,vsindexdata,vxthedata,vythedata,
                                       corr_x,corr_y,gaussian_norm,
                                       gaussian_pos_x,gaussian_pos_y, 
                                       gaussian_sigma_x, gaussian_sigma_y, 
                                       gaussian_fwhm_x, gaussian_fwhm_y,flag, 
                                       wave_table,solution_type));

    if(resid_tab_name_sw) {
      sprintf(rtag,"%s%s%s",type,"RESID_TAB_DRL_LINES_",
	      xsh_instrument_arm_tostring( instr ));
    } else {
      sprintf(rtag,"%s%s%s",type,"RESID_TAB_LINES_",
	      xsh_instrument_arm_tostring( instr ));
    }
  check( tag = cpl_frame_get_tag( frame));

  XSH_REGDEBUG("TAG %s", tag);

  if ( strstr( tag, XSH_AFC_CAL) ){
    sprintf(rname,"AFC_CAL_%s%s",rtag,".fits") ;
  }
  else if ( strstr( tag, XSH_AFC_ATT) ){
    sprintf(rname,"AFC_ATT_%s%s",rtag,".fits") ;
  }
  else {
    sprintf(rname,"%s%s",rtag,".fits") ;
  }
  check( *resid_tab_frame = xsh_resid_tab_save( resid,rname,instr,rtag)); 
  if(clean_tmp || resid_tab_name_sw) {
     xsh_add_temporary_file(rname);
  }

 cleanup:
   XSH_FREE( vlambdadata);
   XSH_FREE( vorderdata);
   XSH_FREE( vsdata);
   XSH_FREE( vsndata);
   XSH_FREE( vsindexdata);
   XSH_FREE( vxthedata);
   XSH_FREE( vythedata);
   XSH_FREE( corr_x);
   XSH_FREE( corr_y);
   XSH_FREE( gaussian_pos_x);
   XSH_FREE( gaussian_pos_y);
   XSH_FREE( gaussian_sigma_x);
   XSH_FREE( gaussian_sigma_y);
   XSH_FREE( gaussian_fwhm_x);
   XSH_FREE( gaussian_fwhm_y);
   XSH_FREE( gaussian_norm);
   XSH_FREE( sort);
   XSH_FREE( diffx);
   XSH_FREE( diffy);
   XSH_FREE( diffxmean);
   XSH_FREE( diffymean);
   XSH_FREE( diffxsig);
   XSH_FREE( diffysig);
   XSH_FREE( rejected);
   
   xsh_free_table( &wave_trace);
   xsh_spectralformat_list_free(&spectralformat_list);
   xsh_pre_free( &pre);
   xsh_the_map_free( &themap); 
   xsh_wavesol_free( &wave_table);
   xsh_wavesol_free( &wave_tab_guess);
   xsh_order_list_free( &order_tab_recov);
   xsh_resid_tab_free( &resid_orders);
   xsh_resid_tab_free( &resid);
   xsh_arclist_free( &arclist);
   return;
}

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

