/*
 * This file is part of the ESO X-Shooter Pipeline
 * Copyright (C) 2004-2009 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
 */
/*
 * $Author: amodigli $
 * $Date: 2013-04-22 08:41:32 $
 * $Revision: 1.59 $
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <math.h>
#include <cpl.h>
/* irplib */
#include <irplib_utils.h>  /* portable isinf check */


#include <xsh_pfits.h>
#include <xsh_msg.h>
#include <xsh_dfs.h>
#include <xsh_error.h>
#include <xsh_utils.h>
#include <xsh_utils_table.h>
#include <xsh_utils_wrappers.h>
#include <xsh_utils_efficiency.h>
#include <xsh_efficiency_response.h>
#include <xsh_star_index.h>
#include <xsh_data_atmos_ext.h>

#define PRO_STD_STAR_SPECTRA "STD_STAR_SPECTRA"
static const char COL_NAME_HIGH_ABS[] = "HIGH_ABS";
static const char COL_NAME_WAVELENGTH[] = "WAVELENGTH";
//static const char COL_NAME_WAVELENGTH_C[] = "WAVELENGTH";
static const char COL_NAME_WAVE_ATMDISP[] = "LAMBDA";
static const char COL_NAME_ABS_ATMDISP[] = XSH_ATMOS_EXT_LIST_COLNAME_K;

static const char COL_NAME_REF[]		= "REF";
static const char COL_NAME_COR[]		= "COR";
static const char COL_NAME_SRC_COR[]	= "SRC_COR";

static const char COL_NAME_WAVE_OBJ[] = "WAVELENGTH";
static const char COL_NAME_INT_OBJ[]	= "INT_OBJ";//"counts_tot";
static const char COL_NAME_ORD_OBJ[]	= "ORD";//"counts_tot";
static const char COL_NAME_WAVE_REF[]		= "LAMBDA";
static const char COL_NAME_FLUX_REF[]		= "FLUX";
static const char COL_NAME_BINWIDTH_REF[]	= "BIN_WIDTH";
static const char COL_NAME_EPHOT[]		= "EPHOT";
static const char COL_NAME_EXT[]		= "EXT";
static const char COL_NAME_SRC_EFF[]	= "EFF";
//static const char PAR_NAME_DIT[]		= "ESO DET DIT";
//static const double UVES_flux_factor =1.e16;

static char FRM_EXTCOEFF_TAB[]	= XSH_EXTCOEFF_TAB;

static int 
xsh_column_to_double(cpl_table* ptable, const char* column);


static double* 
xsh_create_column_double(cpl_table* tbl, const char* col_name, int nrow);



static cpl_error_code
xsh_get_std_obs_values(cpl_propertylist* plist,
                       double* exptime,
                       double* airmass,
                       double* dRA,
                       double* dDEC);

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

/*---------------------------------------------------------------------------*/
/**
  @brief    load reference table

  @param    frames    input frames list
  @param    dRA       Right Ascension
  @param    dDEC      Declination
  @param    EPSILON   tolerance to find ref spectra on catalog on (ra,dec)
  @param    instrument     xsh instrument
  @param    pptable    pointer to new table
  @return   Interpolated data points
 */
/*---------------------------------------------------------------------------*/

void 
xsh_load_ref_table(cpl_frameset* frames, 
                   double dRA, 
                   double dDEC, 
                   double EPSILON, 
                   xsh_instrument* instrument, 
                   cpl_table** pptable)
{
  const char* name = NULL;
  cpl_frame* frm_ref = NULL;

  /* REF STD frame */
  check(frm_ref=xsh_find_frame_with_tag(frames,XSH_FLUX_STD_TAB,instrument));
  if (!frm_ref)
    {
      xsh_msg("REF frame is not found, trying to get REF from the catalog");
     /* REF STD catalog frame */
      // get from catalog
      check(frm_ref=xsh_find_frame_with_tag(frames,XSH_FLUX_STD_CAT,instrument));
      if (frm_ref)
	{
	  check(name=cpl_frame_get_filename(frm_ref));
	  if (name)
	    {
	      star_index* pstarindex = star_index_load(name);
	      if (pstarindex)
		{
		  const char* star_name = NULL;
		  xsh_msg("Searching std RA[%f] DEC[%f] with tolerance[%f] in star catalog", dRA, dDEC, EPSILON);
		  *pptable = star_index_get(pstarindex, dRA, dDEC, EPSILON, EPSILON, &star_name);

		  if (*pptable && star_name)
		    {
		      xsh_msg("Found STD star: %s", star_name);
		    }
		  else
		    {
                       xsh_msg("ERROR - REF star %s could not be found in the catalog",star_name);
		    }
		}
	      else
		{
		  xsh_msg("ERROR - could not load the catalog");
		}
	    }
	}
    }
  else
    {
      xsh_msg("REF frame is found");
      check(name=cpl_frame_get_filename(frm_ref));
      check(*pptable=cpl_table_load(name,1,0));
    }
  return;
 cleanup:
  return;
}


cpl_error_code
xsh_rv_ref_wave_init(xsh_std_star_id std_star_id ,XSH_ARM arm, xsh_rv_ref_wave_param* w){

  switch (arm) {
  case XSH_ARM_AGC:
  case XSH_ARM_UVB:
    w->wguess = 486.1322;
    w->range_wmin=450;
    w->range_wmax=550;
    w->ipol_wmin_max=470;
    w->ipol_wmax_min=500;
    w->ipol_hbox=0.5;

    break;
  case XSH_ARM_VIS:
    w->wguess= 656.2793;
    w->range_wmin=640;
    w->range_wmax=670;
    w->ipol_wmin_max=653;
    w->ipol_wmax_min=661;
    w->ipol_hbox=1.5;

    break;
  case XSH_ARM_NIR:
    w->wguess= 1094.12;
    w->range_wmin=1000;
    w->range_wmax=1200;
    w->ipol_wmin_max=1090;
    w->ipol_wmax_min=1100;
    w->ipol_hbox=1.5;

    break;
  case XSH_ARM_UNDEFINED:

    w->wguess= 999;
    w->range_wmin=999;
    w->range_wmax=999;
    w->ipol_wmin_max=999;
    w->ipol_wmax_min=999;
    w->ipol_hbox=1.5;
    break;
  }

  return cpl_error_get_code();
}

xsh_rv_ref_wave_param * xsh_rv_ref_wave_param_create(void){

  xsh_rv_ref_wave_param * self = cpl_malloc(sizeof(xsh_rv_ref_wave_param));
  self->wguess=999.;    /* Reference line wavelength position */
  self->range_wmin=999.; /* minimum of wavelength box for line fit */
  self->range_wmax=999.; /* maximum of wavelength box for line fit */
  self->ipol_wmin_max=999.; /* maximum lower sub-range wavelength value used to fit line slope */
  self->ipol_wmax_min=999.; /* minimum upper sub-range wavelength value used to fit line slope */

  return self;
}

void xsh_rv_ref_wave_param_destroy(xsh_rv_ref_wave_param * p){

  cpl_free(p);

  return ;
}

/*---------------------------------------------------------------------------*/
/**
  @brief    parse referece std stars catalog 

  @param    cat        input frame catalog
  @param    dRA        Right Ascension
  @param    dDEC       Declination
  @param    EPSILON    tolerance to find ref spectra on catalog on (ra,dec)
  @param    pptable     pointer to new table
  @return   cpl error code. The table will contain interpolated data points
 */
/*---------------------------------------------------------------------------*/

cpl_error_code xsh_parse_catalog_std_stars(cpl_frame* cat, double dRA,
    double dDEC, double EPSILON, cpl_table** pptable,xsh_std_star_id* std_star_id) {
  const char* name = NULL;
  XSH_ASSURE_NOT_NULL_MSG(cat, "Provide input catalog");
  if (cat) {
    check(name=cpl_frame_get_filename(cat));
    if (name) {
      star_index* pstarindex = star_index_load(name);
      if (pstarindex) {
        const char* star_name = NULL;
        xsh_msg(
            "Searching std RA[%f] DEC[%f] with tolerance[%f] in star catalog", dRA, dDEC, EPSILON);
        *pptable = star_index_get(pstarindex, dRA, dDEC, EPSILON, EPSILON,
            &star_name);
        if(star_name != NULL) {
             if ( strcmp(star_name,"GD71")     == 0 ) *std_star_id = XSH_GD71;
        else if ( strcmp(star_name,"Feige110") == 0 ) *std_star_id = XSH_Feige110;
        else if ( strcmp(star_name,"GD153")    == 0 ) *std_star_id = XSH_GD153;
        else if ( strcmp(star_name,"LTT3218")  == 0 ) *std_star_id = XSH_LTT3218;
        else if ( strcmp(star_name,"LTT7987")  == 0 ) *std_star_id = XSH_LTT7987;
        else if ( strcmp(star_name,"BD17")     == 0 ) *std_star_id = XSH_BD17;
        else if ( strcmp(star_name,"EG274")    == 0 ) *std_star_id = XSH_EG274;
        }
        xsh_msg("star index=%d",*std_star_id);
        if (*pptable && star_name != NULL) {
          xsh_msg("Found STD star: %s", star_name);

        } else {
          xsh_msg(
              "ERROR - REF star %s could not be found in the catalog", star_name);
        }
      } else {
        xsh_msg("ERROR - could not load the catalog");
      }
      star_index_delete(pstarindex);
    }
  }
  cleanup: return cpl_error_get_code();
}





static double* 
xsh_create_column_double(cpl_table* tbl, const char* col_name, int nrow)
{
  double* retval = 0;
  check(cpl_table_new_column(tbl, col_name, CPL_TYPE_DOUBLE));
  check(cpl_table_fill_column_window_double(tbl, col_name, 0, nrow, -1));
  check(retval = cpl_table_get_data_double(tbl,col_name));
  return retval;
 cleanup:
  return retval;
}


/*---------------------------------------------------------------------------*/
/**
  @brief    get STD star observation exptime, airmass, RA, DEC
  @param    plist    (input) property list
  @param    exptime  (output) exposure time
  @param    airmass  (output) airmass (average of start and end)
  @param    dRA      (output) Right Ascension
  @param    dDEC     (output) Declination
  @return   CPL error code
 */
/*---------------------------------------------------------------------------*/

cpl_error_code
xsh_get_std_obs_values(cpl_propertylist* plist,
                       double* exptime,
                       double* airmass,
                       double* dRA,
                       double* dDEC)
{

  *airmass=xsh_pfits_get_airm_mean(plist) ;
  *dDEC=xsh_pfits_get_dec(plist);
  *dRA=xsh_pfits_get_ra(plist);
  *exptime=xsh_pfits_get_exptime(plist);

   return cpl_error_get_code();

}

cpl_error_code
xsh_efficiency_add_high_abs_regions(cpl_table** eff,HIGH_ABS_REGION * phigh)
{
  int nrow=cpl_table_get_nrow(*eff);
  double* pwav=NULL;  
  int* phigh_abs=NULL;
  int k=0;
  int i=0;
  cpl_table_new_column(*eff,COL_NAME_HIGH_ABS,CPL_TYPE_INT);
  cpl_table_fill_column_window_int(*eff,COL_NAME_HIGH_ABS,0,nrow,0);
  pwav=cpl_table_get_data_double(*eff,COL_NAME_WAVELENGTH);
  phigh_abs=cpl_table_get_data_int(*eff,COL_NAME_HIGH_ABS);
  if ( phigh != NULL ) {
    for( k = 0 ; phigh->lambda_min != 0. ; k++, phigh++ ) {
      for(i=0;i<nrow;i++) {
        if(pwav[i]>=phigh->lambda_min && pwav[i]<=phigh->lambda_max) {
	  phigh_abs[i]=1;
	}
      }
    }
  }

  return cpl_error_get_code();

}

/*---------------------------------------------------------------------------*/
/**
  @brief Compute efficiency
  @param frames     input frameset
  @param dGain      detector's gain value
  @param dEpsilon   tolerance to find ref spectra on catalog on (ra,dec)
  @param aimprim    airmass
  @param inst       instrument (arm) setting
  @param col_name_atm_wave atmospheric extinction table wave column name
  @param col_name_atm_abs  atmospheric extinction table absorption column name
  @param col_name_ref_wave reference flux std table wave column name
  @param col_name_ref_flux reference flux std table flux column name
  @param col_name_ref_bin reference flux std table sampling bin value
  @param col_name_obj_wave observed std table wave column name
  @param col_name_obj_flux observed std table flux column name

  @return  frame with computed efficiency
 */
/*---------------------------------------------------------------------------*/

cpl_frame*
xsh_utils_efficiency(
		     cpl_frameset * frames,
		     double dGain,
		     double dEpsilon,
		     double aimprim,
                     xsh_instrument* inst,
                     const char* col_name_atm_wave,
                     const char* col_name_atm_abs,
                     const char* col_name_ref_wave,
                     const char* col_name_ref_flux,
                     const char* col_name_ref_bin,
                     const char* col_name_obj_wave,
                     const char* col_name_obj_flux
		     )
{
  cpl_frame* frm_sci = NULL;
  cpl_frame* frm_atmext = NULL;
  cpl_table* tbl_obj_spectrum = NULL; // input table with spectrum
  cpl_table* tbl_atmext = NULL;
  double exptime = 600;

  cpl_propertylist* plist = NULL;
  cpl_table* tbl_ref = NULL;
  cpl_frame* result=NULL;

  const char* name=NULL;
  double dRA = 0;
  double dDEC = 0;
  char prod_name[256];
  char prod_tag[256];
  double airmass=0;
  const double mk2AA=1E4; /* mkm/AA */
  int nclip=0;
  int ntot=0;
  cpl_table* tbl_result = NULL;


  // read all input data and call the internal function
  // read the input tables
  // 1. observation
  check(frm_sci=xsh_find_frame_with_tag(frames,XSH_STD_FLUX_SLIT_STARE_ORDER1D, inst));
  check(name=cpl_frame_get_filename(frm_sci));
  //xsh_msg("name=%s",name);
  check(tbl_obj_spectrum=cpl_table_load(name,1,0));
  check(plist=cpl_propertylist_load(name,0));

  xsh_get_std_obs_values(plist,&exptime,&airmass,&dRA,&dDEC);

  // 2. reference table
  xsh_load_ref_table(frames, dRA, dDEC, dEpsilon, inst, &tbl_ref);
  if (tbl_ref)
    {
      // 3. atmext
      check(frm_atmext=cpl_frameset_find(frames,FRM_EXTCOEFF_TAB));
      check(name=cpl_frame_get_filename(frm_atmext));
      check(tbl_atmext=cpl_table_load(name,1,0));

      tbl_result = xsh_utils_efficiency_internal(
						 tbl_obj_spectrum,
						 tbl_atmext,
						 tbl_ref,
						 exptime,
						 airmass,
						 aimprim,
						 dGain,
                                                 1,
                                                 mk2AA,//valid only for SINFONI
                                                 col_name_atm_wave,
                                                 col_name_atm_abs,
                                                 col_name_ref_wave,
                                                 col_name_ref_flux,
                                                 col_name_ref_bin,
                                                 col_name_obj_wave,
                                                 col_name_obj_flux,
                                                 &ntot,&nclip);
      if (tbl_result)
	{
	  HIGH_ABS_REGION * phigh=NULL;
	  check(xsh_efficiency_add_high_abs_regions(&tbl_result,phigh));
	  sprintf(prod_tag,"EFFICIENCY_%s",xsh_instrument_arm_tostring(inst));
	  sprintf(prod_name,"%s.fits",prod_tag);

          result=xsh_frame_product(prod_name,prod_tag,CPL_FRAME_TYPE_TABLE, 
                                   CPL_FRAME_GROUP_CALIB,CPL_FRAME_LEVEL_FINAL);
          cpl_table_save(tbl_result, plist, NULL,prod_name, CPL_IO_DEFAULT);
      
	  xsh_free_table(&tbl_result);
	}

    }

 cleanup:
  xsh_free_propertylist(&plist);
  xsh_free_table(&tbl_atmext);
  xsh_free_table(&tbl_obj_spectrum);
  xsh_free_table(&tbl_ref);
  return result;
}

static int 
xsh_column_to_double(cpl_table* ptable, const char* column)
{
  const char* TEMP = "_temp_";
  check(cpl_table_duplicate_column(ptable, TEMP, ptable, column));
  check(cpl_table_erase_column(ptable, column));
  check(cpl_table_cast_column(ptable, TEMP, column, CPL_TYPE_DOUBLE));
  check(cpl_table_erase_column(ptable, TEMP ));
  return 0;
 cleanup:
  xsh_msg(" error column to double [%s]", column);
  return -1;
}


/*---------------------------------------------------------------------------*/
/**
  @brief Compute efficiency
  @param tbl_obj_spectrum     input object spectrum
  @param tbl_atmext           input atmospheric extinction table
  @param tbl_ref              input reference flux STD table
  @param exptime              input exposure time
  @param airmass              input observed frame airmass
  @param aimprim              input airmass corrective factor
  @param gain                 input gain
  @param biny                 bin on Y (spatial) direction
  @param src2ref_wave_sampling input cnversion factor to pass from src 2 ref units
  @param col_name_atm_wave atmospheric extinction table wave column name
  @param col_name_atm_abs  atmospheric extinction table absorption column name
  @param col_name_ref_wave reference flux std table wave column name
  @param col_name_ref_flux reference flux std table flux column name
  @param col_name_ref_bin  reference flux std table bin value
  @param col_name_obj_wave observed std table wave column name
  @param col_name_obj_flux observed std table flux column name

  @return   table with computed efficiency
 */
/*---------------------------------------------------------------------------*/

cpl_table* 
xsh_utils_efficiency_internal(cpl_table* tbl_obj_spectrum,
			      cpl_table* tbl_atmext,
			      cpl_table* tbl_ref,
			      double exptime,
			      double airmass,
			      double aimprim,
			      double gain,
			      int    biny,
                              double src2ref_wave_sampling,
                              const char* col_name_atm_wave,
                              const char* col_name_atm_abs,
                              const char* col_name_ref_wave,
                              const char* col_name_ref_flux,
                              const char* col_name_ref_bin,
                              const char* col_name_obj_wave,
                              const char* col_name_obj_flux,
                              int* ntot,
                              int* nclip
			      )
{

  const double TEL_AREA		= 51.2e4; /* collecting area in cm2 */
  double cdelta1 = 0;
  int i = 0;
  cpl_table* tbl_sel = NULL;

  /* structure of the input table
   * col			type	description
   * wavelength	double
   * flux			double
   * */
  cpl_table* tbl_result = NULL; // output table

  /*
   * structure of the output table
   * col			type	description
   * wavelength	double
   * EFF			double	efficiency in range 0-1
   * */

  double* pref = NULL;
  double* pext = NULL;
  double* pcor = NULL;
  double* peph = NULL;

  double* pw = NULL; // wavelength of the input table
  int nrow = 0;
  double ref_bin_size=0;
  double um2nm=0.001;
  double eff_med=0.;
  double eff_rms=0.;
  double eff_thresh=0.;

  double kappa=5;
  double nm2AA=10.;
  int nsel=0;
  double eff=0;
  nrow = cpl_table_get_nrow(tbl_obj_spectrum);
  xsh_msg_dbg_medium("Starting efficiency calculation: exptime[%f] airmass[%f] nrow[%d]", 
	  exptime, airmass, nrow);

  //cpl_table_dump(tbl_obj_spectrum,0,3,stdout);
  //xsh_msg("col wave=%s col_flux=%s",col_name_obj_wave,col_name_obj_flux);

  /* convert to double */
  xsh_column_to_double(tbl_obj_spectrum,col_name_obj_wave);
  xsh_column_to_double(tbl_obj_spectrum,col_name_obj_flux);

  check(xsh_column_to_double(tbl_atmext,col_name_atm_wave ));
  check(xsh_column_to_double(tbl_atmext,col_name_atm_abs ));
  check(xsh_column_to_double(tbl_ref,col_name_ref_wave ));
  check(xsh_column_to_double(tbl_ref,col_name_ref_flux ));
  check(xsh_column_to_double(tbl_ref,col_name_ref_bin ));

  /* as the reference spectra are erg cm-2 s-1 AA-1 and the wave scale
     have been converted from AA to nm to be uniform to XSH scale
     we apply a corresponding factor to the flux */
  //check(cpl_table_multiply_scalar(tbl_ref,col_name_ref_flux,nm2AA));

  /* get bin size of ref STD star spectrum */
  ref_bin_size=cpl_table_get_double(tbl_ref,col_name_ref_bin,0,NULL);
  xsh_msg_dbg_medium("ref_bin_size[nm]=%g",ref_bin_size);

  /*
  xsh_msg("Unit conversion factor src/ref STD spectrum=%g",
          src2ref_wave_sampling);
  */

  /* convert obj spectrum wave unit to the same of the reference one
     This is required to be able to obtain the interpolated value of
     the extinction and reference std star spectrum at the sampled
     object wavelength when we compute the correction term due to 
     atmospheric extinction 
    */
  check(cpl_table_multiply_scalar(tbl_obj_spectrum,col_name_obj_wave,
                                 src2ref_wave_sampling));


  /* Computes the atmospheric distorsion correction:
     COL_NAME_REF: reference std star flux value
     COL_NAME_EXT: atm extinction
     COL_NAME_EPHOT: photon energy
     COL_NAME_COR: corrective factor (due to atmospheric extinction)
  */
  xsh_msg_dbg_medium("object 2 src std  wave factor %g",src2ref_wave_sampling);
  check(pw=cpl_table_get_data_double(tbl_obj_spectrum,col_name_obj_wave));

  // prepare columns for the output data
  check(tbl_result=cpl_table_new(nrow));
  check(pref=xsh_create_column_double(tbl_result, COL_NAME_REF, nrow));
  check(pext=xsh_create_column_double(tbl_result, COL_NAME_EXT, nrow));
  check(pcor=xsh_create_column_double(tbl_result, COL_NAME_COR, nrow));
  check(peph=xsh_create_column_double(tbl_result, COL_NAME_EPHOT, nrow));
  xsh_msg_dbg_medium("wave range: [%g,%g] nm",pw[0],pw[nrow-1]); 
  xsh_msg_dbg_medium("src_bin_size[nm]=%g",pw[1] - pw[0]);
  
  cdelta1 = (pw[1] - pw[0]) / src2ref_wave_sampling ; /* we want the delta in original units. As we rescaled to AA we need to correct for this */
  xsh_msg_dbg_medium("nrow=%d cdelta1=%g",nrow,cdelta1);
  for (i = 0; i < nrow; i++)
    {
      check(pext[i] = xsh_table_interpolate(tbl_atmext, pw[i],col_name_atm_wave, col_name_atm_abs));
      check(pref[i] = xsh_table_interpolate(tbl_ref, pw[i], col_name_ref_wave,col_name_ref_flux));
      pcor[i] = pow(10,(0.4*pext[i] * (aimprim - airmass)));
      eff = 1.e7*1.986e-19/(pw[i]*um2nm);
      if(!isnan(eff)) {
         peph[i]=eff;
      } else {
         /*
         xsh_msg("pw=%g pext=%g pref=%g pcor=%g pef=%g",
                      pw[i],pext[i],pref[i],pcor[i],peph[i]);
         */
         cpl_table_set_invalid(tbl_result,COL_NAME_EPHOT,i);
      }
      /* ph energy: 1.986*10^-19(J.ph^-1)/lam(um) ==> 
         in as pw is expressed in nm units we need to multiply by um2nm: 10-3 
       */
      /*
      if(i< 2) {
         xsh_msg("pw[i]=%g,pcor=%g peph=%g",pw[i],pcor[i],peph[i]);
      }
      */
    }
  cpl_table_erase_invalid(tbl_result);

  /*
  xsh_msg("atm: %s, %s ref: %s, %s obj: %s, %s",
          col_name_atm_wave,col_name_atm_abs,
          col_name_ref_wave,col_name_ref_flux,
          col_name_obj_wave,col_name_obj_flux);
  */
  /* add in result table also ORDER, OBJ(flux), WAVELENGTH columns */
  check(cpl_table_duplicate_column(tbl_result,"ORDER",
				   tbl_obj_spectrum,COL_NAME_ORD_OBJ ));
  check(cpl_table_duplicate_column(tbl_result,COL_NAME_SRC_COR,
				   tbl_obj_spectrum, col_name_obj_flux));
  check(cpl_table_duplicate_column(tbl_result,col_name_obj_wave,
				   tbl_obj_spectrum,col_name_obj_wave));

  /* correct for atmospheric extintion: 
     bring the observed STD star out of the atmosphere */
  check(cpl_table_multiply_columns(tbl_result,COL_NAME_SRC_COR,COL_NAME_COR));

  /* correct object flux by binning: 
     As the reference std star spectra flux values are in units of 
     erg/cm2/s/A we have to obtain the bin size in Angstrom units.
     Because the sampling step is in nm units, we need to apply a nm2AA factor: 
     cdelt1[src_sampling]*src2ref_wave_sampling*nm2AA */
  cpl_table_divide_scalar(tbl_result, COL_NAME_SRC_COR, src2ref_wave_sampling);
  cpl_table_divide_scalar(tbl_result, COL_NAME_SRC_COR, cdelta1);
  cpl_table_divide_scalar(tbl_result, COL_NAME_SRC_COR, nm2AA);
  /* correct for spatial bin size: Why?? */
  cpl_table_divide_scalar(tbl_result,COL_NAME_SRC_COR,biny); 


  /*correct ref std star flux by binning: 
    divides by the bin size in ref_wave_sampling (usually AA) */
  check(cpl_table_divide_scalar(tbl_result,COL_NAME_REF,ref_bin_size));

  /* create efficiency column, initialized with the intensity value (ADU) 
     corrected for extinction and sampling step */
  check(cpl_table_duplicate_column(tbl_result,COL_NAME_SRC_EFF,
				   tbl_result,COL_NAME_SRC_COR));

  /* We need now to convert ADU to erg/cm2/s :
   correct for detector gain, exposure time, telescope area: we are now
   in units of electrons/s/cm2 */
  check(cpl_table_multiply_scalar(tbl_result,COL_NAME_SRC_EFF,
				  gain / (exptime * TEL_AREA)));

  /* To pass from electron to ergs we correct for photon energy */
  check(cpl_table_multiply_columns(tbl_result,COL_NAME_SRC_EFF,
				   COL_NAME_EPHOT));

  /* our observed STD star value is now finally in the same units 
     as the one of the reference STD star: the efficiency is the ratio
     observed_flux/reference_flux */
 
  check(cpl_table_divide_columns(tbl_result,COL_NAME_SRC_EFF,COL_NAME_REF));
  /* apply factor UVES_flux_factor (1.e16) as reference catalog has fluxes 
     in units of UVES_flux_factor (1e-16) */
  //check(cpl_table_multiply_scalar(tbl_result,COL_NAME_SRC_EFF,UVES_flux_factor));
  //check(cpl_table_save(tbl_result,NULL,NULL,"pippo.fits", CPL_IO_DEFAULT));
  /* To have cleaner plots we clean from outliers */
  eff_med=cpl_table_get_column_median(tbl_result,COL_NAME_SRC_EFF);
  eff_rms=cpl_table_get_column_stdev(tbl_result,COL_NAME_SRC_EFF);

  eff_thresh=(eff_med+kappa*eff_rms<10.) ? eff_med+kappa*eff_rms:10.;
  if(irplib_isinf(eff_thresh)) eff_thresh=10.;
/*

  cpl_table_and_selected_double(tbl_result,COL_NAME_SRC_EFF,
				CPL_GREATER_THAN,1.e-5);
*/
/*
  xsh_msg("1eff_med=%g eff_rms=%g thresh=%g",
          eff_med,eff_rms,eff_thresh);
*/
  *ntot=cpl_table_get_nrow(tbl_result);
  cpl_table_and_selected_double(tbl_result,COL_NAME_SRC_EFF,
				CPL_GREATER_THAN,1.e-5);

  cpl_table_and_selected_double(tbl_result,COL_NAME_SRC_EFF,
				CPL_LESS_THAN,eff_thresh);

  eff_med=cpl_table_get_column_median(tbl_result,COL_NAME_SRC_EFF);
  eff_rms=cpl_table_get_column_stdev(tbl_result,COL_NAME_SRC_EFF);

  eff_thresh=(eff_med+kappa*eff_rms<10.) ? eff_med+kappa*eff_rms:10.;
  if(irplib_isinf(eff_thresh)) eff_thresh=10.;
/*
  xsh_msg("2eff_med=%g eff_rms=%g thresh=%g",
          eff_med,eff_rms,eff_thresh);
*/
  cpl_table_and_selected_double(tbl_result,COL_NAME_SRC_EFF,
				CPL_LESS_THAN,eff_thresh);

  nsel=cpl_table_and_selected_double(tbl_result,COL_NAME_SRC_EFF,
                                      CPL_LESS_THAN,1);

  *nclip=(*ntot)-nsel;
  tbl_sel=cpl_table_extract_selected(tbl_result);


 cleanup:
  xsh_free_table(&tbl_result);
  return tbl_sel;
}

/**
@brief get RA, DEC, airmass (mean) of a frame
@param frm_sci input frame
@param ra right ascension
@param dec declination
@param airmass airmass (mean)
@return void
 */
void
xsh_frame_sci_get_ra_dec_airmass(cpl_frame* frm_sci,
                                 double* ra, 
                                 double* dec, 
                                 double* airmass)
{

   const char* name_sci=NULL;
   cpl_propertylist* plist=NULL;
   
   name_sci=cpl_frame_get_filename(frm_sci);
   check(plist=cpl_propertylist_load(name_sci,0));
   *ra=xsh_pfits_get_ra(plist);
   *dec=xsh_pfits_get_dec(plist);
   *airmass=xsh_pfits_get_airm_mean(plist);

cleanup:

   xsh_free_propertylist(&plist);
   return;
}


// TO BE FIXED (gain is wrong in NIR)
static void
xsh_frame_sci_get_gain_airmass_exptime_naxis1_biny(cpl_frame* frm_sci,
                                           xsh_instrument* instrument,
                                           double* gain,
                                           double* airmass, 
                                           double* exptime, 
                                           int* naxis1, 
                                           int* biny)
{

   const char* name_sci=NULL;
   cpl_propertylist* plist=NULL;
  
   name_sci=cpl_frame_get_filename(frm_sci);
   check(plist=cpl_propertylist_load(name_sci,0));
   check(*naxis1=xsh_pfits_get_naxis1(plist));
   *airmass=xsh_pfits_get_airm_mean(plist);
 
  if( xsh_instrument_get_arm(instrument) == XSH_ARM_NIR){
    *gain=2.12;
    *biny=1;
    check(*exptime=xsh_pfits_get_dit(plist));
  } else {
    check(*gain=xsh_pfits_get_conad(plist));
    check(*biny=xsh_pfits_get_biny(plist)); 
    check(*exptime=xsh_pfits_get_win1_dit1(plist));
  }

cleanup:
   xsh_free_propertylist(&plist);

   return;
}

/**
@brief extract spectrum
@param frm_cat catalog frame
@param frm_sci science frame
 */
cpl_frame*
xsh_catalog_extract_spectrum_frame(cpl_frame* frm_cat,
                                   cpl_frame* frm_sci)
{
   cpl_frame* result=NULL;
   double dRA=0;
   double dDEC=0;
   //double nm2AA=10.;
   double nm2nm=1.;
   cpl_table* tbl_ref=NULL;
   char fname[256];
   char ftag[256];
   double airmass=0;
   xsh_std_star_id std_star_id=0;

   XSH_ASSURE_NOT_NULL_MSG(frm_sci,"Null input sci frame set!Exit");
   XSH_ASSURE_NOT_NULL_MSG(frm_cat,"Null input std star cat frame set!Exit");
   xsh_frame_sci_get_ra_dec_airmass(frm_sci,&dRA,&dDEC,&airmass);
   check(xsh_parse_catalog_std_stars(frm_cat,dRA,dDEC,STAR_MATCH_DEPSILON,&tbl_ref,&std_star_id));

   //cpl_table_divide_scalar(tbl_ref,COL_NAME_FLUX_REF,UVES_flux_factor);
   cpl_table_divide_scalar(tbl_ref,COL_NAME_WAVE_REF,nm2nm);
   cpl_table_multiply_scalar(tbl_ref,COL_NAME_FLUX_REF,nm2nm);
   check(cpl_table_divide_columns(tbl_ref,COL_NAME_FLUX_REF,COL_NAME_BINWIDTH_REF));

   sprintf(fname,"ref_std_star_spectrum.fits");
   sprintf(ftag,"STD_STAR_FLUX");

   check(cpl_table_save(tbl_ref,NULL,NULL,fname,CPL_IO_DEFAULT));
   result=xsh_frame_product(fname,ftag,CPL_FRAME_TYPE_TABLE,
                            CPL_FRAME_GROUP_CALIB,CPL_FRAME_LEVEL_INTERMEDIATE);
  cleanup:
   return result;
}

/**
@brief computes efficiency
@param frm_sci science frame
@param frm_cat catalog frame
@param frm_atmext atmos[heric extinction frame
@param instrument instrument arm setting
 */
cpl_frame*
xsh_efficiency_compute(cpl_frame* frm_sci, 
                       cpl_frame* frm_cat,
                       cpl_frame* frm_atmext, 
                       cpl_frame* high_abs_win, 
                       xsh_instrument* instrument)

{

  cpl_image* ima_sci=NULL;
  cpl_vector* vec_ord=NULL;
  cpl_image* ima_obj=NULL;
  cpl_table* obj_tab=NULL;

  const char* name_sci=NULL;
  const char* name_atm=NULL;
  cpl_propertylist* plist=NULL;
  cpl_propertylist* x_plist=NULL;


  cpl_table* tbl_ord=NULL;
  cpl_table* tot_eff=NULL;
  cpl_table* tbl_eff=NULL;
  cpl_table* tbl_ref=NULL;
  cpl_table* tbl_atmext=NULL;

  double * pobj=NULL;
  double * pw=NULL;
  double * pf=NULL;
  int * po=NULL;

  cpl_frame* frm_eff=NULL;

  //char name_eff[256];

  double crval1=0;
  double cdelt1=0;
  int naxis1=0;
  int nrow=0;
 

  double exptime=600;
  cpl_vector* rec_profile=NULL;
  int i=0;
  double airmass=0;
  double dRA=0;
  double dDEC=0;
  char key_name[40];
  int next=0;
  int nord=0;
   


  int j=0;
  double wav=0;
  double gain=0;
  int biny=1;
  double aimprim=0;
  int ord=0;
  //double nm2AA=10.;
  double nm2nm=1.;

  char fname[256];
  char tag[256];
  double emax=0;
  double emed=0;
  int nclip=0;
  int ntot=0;

  int nclip_tot=0;
  int neff_tot=0;
  double fclip=0;
  xsh_std_star_id std_star_id=0;
  int vec_size=0;
  XSH_ASSURE_NOT_NULL_MSG(frm_sci,"Null input sci frame set!Exit");
  XSH_ASSURE_NOT_NULL_MSG(frm_cat,"Null input std star cat frame set!Exit");
  XSH_ASSURE_NOT_NULL_MSG(frm_atmext,"Null input atmospheric ext frame set!Exit");


  check(next = cpl_frame_get_nextensions(frm_sci));
  //xsh_msg("next=%d",next);
  nord=(next+1)/3;

  xsh_frame_sci_get_ra_dec_airmass(frm_sci,&dRA,&dDEC,&airmass);
  name_sci=cpl_frame_get_filename(frm_sci);
  plist=cpl_propertylist_load(name_sci,0);
  xsh_frame_sci_get_gain_airmass_exptime_naxis1_biny(frm_sci,instrument,
                                                     &gain,&airmass,&exptime, 
                                                     &naxis1,&biny);



  check(xsh_parse_catalog_std_stars(frm_cat,dRA,dDEC,STAR_MATCH_DEPSILON,
                                    &tbl_ref,&std_star_id));



  xsh_msg_dbg_medium("gain=%g airm=%g exptime=%g airmass=%g ra=%g dec=%g",
          gain,airmass,exptime,airmass,dRA,dDEC);

  xsh_msg_dbg_medium("name_sci=%s",name_sci);
  nrow=naxis1*nord;
  /* note that this table is overdimensioned: naxis1 is the size of the multi
     extension image holding the 2D spectrum (usually order by order)
     but orders may have different length==> different naxis1
  */
  obj_tab=cpl_table_new(nrow);


  cpl_table_new_column(obj_tab,COL_NAME_ORD_OBJ,CPL_TYPE_INT);
  cpl_table_new_column(obj_tab,COL_NAME_WAVE_OBJ,CPL_TYPE_DOUBLE);
  cpl_table_new_column(obj_tab,COL_NAME_INT_OBJ,CPL_TYPE_DOUBLE);

  check(cpl_table_fill_column_window_int(obj_tab,COL_NAME_ORD_OBJ,0,nrow,-1));
  check(cpl_table_fill_column_window_double(obj_tab,COL_NAME_WAVE_OBJ,0,nrow,-1));
  check(cpl_table_fill_column_window_double(obj_tab,COL_NAME_INT_OBJ,0,nrow,-1));



  /* extract the object signal on a given slit
     extract the sky signal on 2 given slits
     computes the extracted sky per pixel on each sky window
     average the two extracted sky
     rescale the averaged sky to the object windows and 
     subtract it from the object
  */

  check(name_atm=cpl_frame_get_filename(frm_atmext));
  xsh_msg_dbg_medium("name_atm=%s",name_atm);
  check(tbl_atmext=cpl_table_load(name_atm,1,0));

  if(!cpl_table_has_column(tbl_atmext,XSH_ATMOS_EXT_LIST_COLNAME_K)){
     xsh_msg_warning("You are using an obsolete atm extinction line table");
     cpl_table_duplicate_column(tbl_atmext,XSH_ATMOS_EXT_LIST_COLNAME_K,
                                tbl_atmext,XSH_ATMOS_EXT_LIST_COLNAME_OLD);
  }

 

  if(tbl_ref == NULL) {
    xsh_msg_error("Provide std sar catalog frame");
    return NULL;
  //cpl_table_dump(tbl_ref,0,3,stdout);
  }

  for(i=0;i<next;i+=3) {

 
     //xsh_msg("Processing extracted spectrum extension %d",i);
     xsh_free_vector(&vec_ord);
     pobj=NULL;
     xsh_free_propertylist(&x_plist);
     check(vec_ord=cpl_vector_load(name_sci,i));
     check(x_plist=cpl_propertylist_load(name_sci,i));
     vec_size=cpl_vector_get_size(vec_ord);
 
     //xsh_msg("naxis1=%d vec_ord nx=%d",naxis1,vec_size);
     check(pobj=cpl_vector_get_data(vec_ord));
     check(crval1=xsh_pfits_get_crval1(x_plist));
     check(cdelt1=xsh_pfits_get_cdelt1(x_plist));
     xsh_free_table(&tbl_ord);
     tbl_ord=cpl_table_new(vec_size);
     check(cpl_table_copy_structure(tbl_ord,obj_tab));
     check(cpl_table_fill_column_window_int(tbl_ord,COL_NAME_ORD_OBJ,0,vec_size,-1));
     check(cpl_table_fill_column_window_double(tbl_ord,COL_NAME_WAVE_OBJ,0,vec_size,-1));
     check(cpl_table_fill_column_window_double(tbl_ord,COL_NAME_INT_OBJ,0,vec_size,-1));
  
     check(po=cpl_table_get_data_int(tbl_ord,COL_NAME_ORD_OBJ));
     check(pw=cpl_table_get_data_double(tbl_ord,COL_NAME_WAVE_OBJ));
     check(pf=cpl_table_get_data_double(tbl_ord,COL_NAME_INT_OBJ));
 

     //xsh_msg("crval1=%g cdelt1=%g",crval1,cdelt1);
     ord=i/3;
     for(j=0;j<vec_size;j++) {
        po[j]=ord;
        wav=crval1+cdelt1*j;
        pw[j]=wav;
        pf[j]=pobj[j];   
        //xsh_msg("j=%d Flux[%g]=%g",j,pw[j],pobj[j]);
     }
 
     //cpl_table_dump(tbl_ord,0,3,stdout);
     //sprintf(name_eff,"obj_ord%d.fits",ord);
     //check(cpl_table_save(tbl_ord,plist, x_plist,name_eff, CPL_IO_DEFAULT));

     //sprintf(name_eff,"ref_ord%d.fits",ord);
     //check(cpl_table_save(tbl_ref,plist, x_plist,name_eff, CPL_IO_DEFAULT));
    
     //check(cpl_table_save(tbl_atmext,plist, x_plist,"table_atm.fits", CPL_IO_DEFAULT));
    
     xsh_free_table(&tbl_eff);
     check(tbl_eff=xsh_utils_efficiency_internal(tbl_ord,tbl_atmext,tbl_ref,
                                                 exptime,airmass,aimprim,gain,
                                                 biny,nm2nm,
                                                 COL_NAME_WAVE_ATMDISP,
                                                 COL_NAME_ABS_ATMDISP,
                                                 COL_NAME_WAVE_REF,
                                                 COL_NAME_FLUX_REF,
                                                 COL_NAME_BINWIDTH_REF,
                                                 COL_NAME_WAVE_OBJ,
                                                 COL_NAME_INT_OBJ,
                                                 &ntot,&nclip));

 
     xsh_free_table(&tbl_ord);
     //cpl_table_dump(tbl_eff,1,2,stdout);
     nrow=cpl_table_get_nrow(tbl_eff);
     if(nrow>0) {
        check(emax=cpl_table_get_column_max(tbl_eff,"EFF"));
     } else {
        emax=-999;
     }
     sprintf(key_name,"%s%2d", XSH_QC_EFF_PEAK_ORD,ord);
     cpl_propertylist_append_double(plist,key_name,emax);
     cpl_propertylist_set_comment(plist,key_name,"Peak efficiency");
     if(nrow>0) {
       check(emed=cpl_table_get_column_median(tbl_eff,"EFF"));
     } else {
        emed=-999;
     }
     sprintf(key_name,"%s%2d", XSH_QC_EFF_MED_ORD,ord);
     cpl_propertylist_append_double(plist,key_name,emed);
     cpl_propertylist_set_comment(plist,key_name,"Median efficiency");

 
     neff_tot+=ntot;
     //xsh_msg("neff_tot=%d vec_size=%d",neff_tot,vec_size);
     if(ord==0) {
        tot_eff=cpl_table_duplicate(tbl_eff);
     } else {
        cpl_table_insert(tot_eff,tbl_eff,neff_tot);
     }
     nclip_tot+=nclip;
     
     //sprintf(name_eff,"eff_ord%d.fits",ord);
     //check(cpl_table_save(tbl_eff,plist, x_plist,name_eff, CPL_IO_DEFAULT));
  }
 
  //abort();

  xsh_msg("nclip_tot=%d",nclip_tot);
  HIGH_ABS_REGION * phigh=NULL;
  phigh=xsh_fill_high_abs_regions(instrument,high_abs_win);
 
  check(xsh_efficiency_add_high_abs_regions(&tot_eff,phigh));

  sprintf(tag,"EFFICIENCY_%s_%s",xsh_instrument_mode_tostring(instrument),
          xsh_instrument_arm_tostring(instrument));
  sprintf(fname,"%s.fits",tag);
 
  nrow=cpl_table_get_nrow(tot_eff);
  fclip=nclip_tot;
 
  if(neff_tot > 0) {
     fclip/=neff_tot;
  } else {
     fclip=-999;
  }
 
  xsh_msg("nclip_tot =%d neff_tot=%d fclip=%g",nclip_tot,neff_tot,fclip);
  xsh_pfits_set_qc_eff_fclip(plist,fclip);
  xsh_pfits_set_qc_eff_nclip(plist,nclip_tot);

  //cpl_propertylist_dump(plist,stdout);
  check(cpl_table_save(tot_eff,plist, x_plist,fname, CPL_IO_DEFAULT));

  xsh_free_table(&obj_tab);
 
  check(frm_eff=xsh_frame_product(fname,tag,CPL_FRAME_TYPE_TABLE, 
                                  CPL_FRAME_GROUP_CALIB,
                                  CPL_FRAME_LEVEL_FINAL));
 

 
  cleanup:
 
 //if(phigh!=NULL) cpl_free(phigh);


  xsh_free_table(&tot_eff);
  xsh_free_table(&tbl_ref);
  xsh_free_table(&tbl_atmext);
  xsh_free_table(&obj_tab);
  xsh_free_table(&tbl_ord);
  xsh_free_table(&tbl_eff);

  xsh_free_image(&ima_sci);
  xsh_free_vector(&rec_profile); 
  xsh_free_vector(&vec_ord);
  xsh_free_image(&ima_obj);
  xsh_free_propertylist(&plist);
  xsh_free_propertylist(&x_plist);

  return frm_eff;

}



static double slaAirmas ( double zd )
/*
**  - - - - - - - - - -
**   s l a A i r m a s
**  - - - - - - - - - -
**
**  Air mass at given zenith distance.
**
**  (double precision)
**
**  Given:
**     zd     d     observed zenith distance (radians)
**
**  The result is an estimate of the air mass, in units of that
**  at the zenith.
**
**  Notes:
**
**  1)  The "observed" zenith distance referred to above means "as
**      affected by refraction".
**
**  2)  Uses Hardie's (1962) polynomial fit to Bemporad's data for
**      the relative air mass, X, in units of thickness at the zenith
**      as tabulated by Schoenberg (1929). This is adequate for all
**      normal needs as it is accurate to better than 0.1% up to X =
**      6.8 and better than 1% up to X = 10. Bemporad's tabulated
**      values are unlikely to be trustworthy to such accuracy
**      because of variations in density, pressure and other
**      conditions in the atmosphere from those assumed in his work.
**
**  3)  The sign of the ZD is ignored.
**
**  4)  At zenith distances greater than about ZD = 87 degrees the
**      air mass is held constant to avoid arithmetic overflows.
**
**  References:
**     Hardie, R.H., 1962, in "Astronomical Techniques"
**        ed. W.A. Hiltner, University of Chicago Press, p180.
**     Schoenberg, E., 1929, Hdb. d. Ap.,
**        Berlin, Julius Springer, 2, 268.
**
**  Adapted from original Fortran code by P.W.Hill, St Andrews.
**
**  Last revision:   5 October 1994
**
**  Copyright P.T.Wallace.  All rights reserved.
*/
{
   double w, seczm1;
      
   w = fabs ( zd );
   /* replace   
   seczm1 = 1.0 / ( cos ( gmin ( 1.52,w ) ) ) - 1.0;
   by :
   w = (1.52 > w) ? w : 1.52;
   seczm1 = 1.0 / ( cos ( w ) ) - 1.0;
   */

   w = (1.52 > w) ? w : 1.52;
   seczm1 = 1.0 / ( cos ( w ) ) - 1.0;
   return 1.0 + seczm1 * ( 0.9981833
                           - seczm1 * ( 0.002875 + 0.0008083 * seczm1 ) );
}
      
/*
  LST 
  =========================
  * LST (sidereal time) keyword is retrieved from the TCS when the  
  EXPSTRT is executed on the instrument side. Such commands is executed 
  whenever and exposure is started on the instrument.
  * The keyword is added by the tif module, in methods 
  tifFitsLib::tifGetFitsStart()
  * The value of the keyword, is the current (instantaneous) value found 
  in telescope's DB:
  "trk:data:times.lst". This value is computed given the 
  UTC from the time server:
*/   



void slaAoppat ( double date, double aoprms[14] )
/*
**  - - - - - - - - - -
**   s l a A o p p a t
**  - - - - - - - - - -
**
**  Recompute the sidereal time in the apparent to observed place
**  star-independent parameter block.
**
**  Given:
**     date   double      UTC date/time (Modified Julian Date, JD-2400000.5)
**                        (see slaAoppa source for comments on leap seconds)
**     aoprms double[14]  star-independent apparent-to-observed parameters
**
**       (0-11)   not required
**       (12)     longitude + eqn of equinoxes + sidereal dut
**       (13)     not required
**
**  Returned:
**     aoprms double[14]  star-independent apparent-to-observed parameters:
**
**       (0-12)   not changed
**       (13)     local apparent sidereal time (radians)
**
**  For more information, see slaAoppa.
**
**  Called:  slaGmst
**
**  Last revision:   31 October 1993
**
**  Copyright P.T.Wallace.  All rights reserved.
*/
{
   /* Temporarily commented out 
      aoprms[13] = slaGmst ( date ) + aoprms[12];
   */
}

static double
xsh_utils_get_airm(cpl_propertylist* plist)
{

   double airmass=0;
  /* an unknown */
   double azimuth=0;
   /* an unknown */
   double altitude=0;
   /* From FITS header */
   double declination=0;
   double RA=0;

   double hour_angle=0;

   /* to get wich UT is used, that affects the latitude value */
   const char* telescope_id=NULL; 
   int tel_id=0; 
   double latitude=0;
   double lat_sig=-1;
   double lat_deg=24;
   double lat_min=37;
   double lat_sec=30; /* approximative value refined later according to UT# */
   int len=0;
   //double exptime_sec=0;
   //double exptime_hms=0;
   //double exptime_deg=0;
   double LST=0; /*Local Sidereal Time */

   declination=xsh_pfits_get_dec(plist);
   RA=xsh_pfits_get_ra(plist);
   LST=xsh_pfits_get_lst(plist);
   hour_angle=LST-RA;

   //xsh_msg("declination=%g",declination);
   //xsh_msg("right ascension=%g",RA);
   //xsh_msg("LST=%g",LST);

   //xsh_msg("hour_angle=%g [hms]",hour_angle);
   //xsh_msg("hour_angle=%g [deg]",xsh_hms2deg(hour_angle));

   telescope_id=xsh_pfits_get_telescop(plist);
   //exptime_sec=xsh_pfits_get_exptime(plist);
   //exptime_hms=exptime_sec/3600;
   //xsh_msg("exptime=%g [sec]",exptime_sec);
   //xsh_msg("exptime=%g [hms]",exptime_hms);
   //exptime_deg=xsh_hms2deg(exptime_hms);
   //xsh_msg("exptime=%g [deg]",exptime_deg);
   
   //hour_angle+=exptime_deg;

   //xsh_msg("telescope_id=%s",telescope_id);
   len=strlen(telescope_id);
   /* 48 is bitcode of 0 */
   tel_id=telescope_id[len-1]-48;
  
   //xsh_msg("tel_id=%d",tel_id);

   switch ( tel_id ) {
      case 1: lat_sec=33.117;
         break;
      case 2: lat_sec=31.465;
         break;
      case 3: lat_sec=30.300;
         break;
      case 4: lat_sec=31.000;
         break;
   }
   //xsh_msg("lat_sec=%g",lat_sec);

   latitude=lat_sig*(lat_deg+lat_min/60+lat_sec/3600);
   //xsh_msg("latitude=%18.15g",latitude);

   double c1=0;
   double c2=0;
   double c3=0;

   c1=sin(latitude)*sin(declination)+cos(latitude)*cos(declination)*cos(hour_angle);
   c2=cos(latitude)*sin(declination)-sin(latitude)*cos(declination)*cos(hour_angle);
   c3=-cos(latitude)*sin(hour_angle);

   azimuth=atan(c3/c2);
   altitude=atan(c1/c2*cos(azimuth));
   //xsh_msg("azimuth=%g",azimuth);
   xsh_msg("altitude=%g",altitude);
   //double zenith_distance=90-altitude;
   //xsh_msg("zenit_distance=%g",zenith_distance);
   /* Pickering 2002: X=1/sin(h+2444/(165+47h^1.1))
     see Wikipedia Air mass, interpolation formulae
    */
   airmass=1/sin(altitude+244/(165+47*pow(altitude,1.1)));
   //xsh_msg("airmass med=%g",airmass);

   return airmass;


}


double
xsh_utils_compute_airm_eff(cpl_frameset* raws)
{

  int i=0;
  int size=0;
  cpl_frame* frm=NULL;
  cpl_propertylist* plist=NULL;
  const char* name=NULL;
  double airm_start=0;
  double airm_end=0;
  double airm_med=0;
  double airm_eff=0;

  double exptime=0;
  double area=0;
  double sum_area=0;
  double sum_time=0;

  XSH_ASSURE_NOT_NULL_MSG(raws,"raws frameset null pointer");

  size=cpl_frameset_get_size(raws);
  for(i=0;i<size;i++){

     frm=cpl_frameset_get_frame(raws,i);
     name=cpl_frame_get_filename(frm);
     plist=cpl_propertylist_load(name,0);

     airm_med=xsh_utils_get_airm(plist);
     airm_start=xsh_pfits_get_airm_start(plist);
     airm_end=xsh_pfits_get_airm_end(plist);
     exptime=xsh_pfits_get_exptime(plist);

     area=(exptime/6)*(airm_start+4*airm_med+airm_end);

     //xsh_msg("area=%g",area);
     //xsh_msg("exptime=%g",exptime);
     sum_area+=area;
     sum_time+=exptime;
     xsh_free_propertylist(&plist);

  } 
  //xsh_msg("sum exptime=%g",sum_time);
  airm_eff=sum_area/sum_time;

  cleanup:
  xsh_free_propertylist(&plist);
  xsh_msg("airmass eff=%g",airm_eff);

  return airm_eff;

}

double
xsh_utils_compute_airm(cpl_frameset* raws)
{
 
  cpl_frame* frm=NULL;
  cpl_propertylist* plist=NULL;
  const char* name=NULL;
  int i=0;
  int size=0;
  double airm=0;
  double airm_eff=0;
  double exptime=0;
  double time_tot=0;
  double* pairm=NULL;
  double* pprod=NULL;
  double* ptime=NULL;

  cpl_array* time_array=NULL;
  cpl_array* airm_array=NULL;
  //cpl_array* prod_array=NULL;

  XSH_ASSURE_NOT_NULL_MSG(raws,"raws frameset null pointer");

  size=cpl_frameset_get_size(raws);
  airm_array=cpl_array_new(size,CPL_TYPE_DOUBLE);
  time_array=cpl_array_new(size,CPL_TYPE_DOUBLE);
  //prod_array=cpl_array_new(size,CPL_TYPE_DOUBLE);
  pairm=cpl_array_get_data_double(airm_array);
  ptime=cpl_array_get_data_double(time_array);
  pprod=cpl_array_get_data_double(time_array);

  if(size>2) {
    for(i=0;i<size;i++){

      frm=cpl_frameset_get_frame(raws,i);
      name=cpl_frame_get_filename(frm);
      plist=cpl_propertylist_load(name,0);
      airm=xsh_pfits_get_airm_mean(plist);
      exptime=xsh_pfits_get_exptime(plist);
      ptime[i]=exptime;
      pairm[i]=airm;
      pprod[i]=airm*exptime;

    }
    airm_eff=(pprod[0]+pprod[size-1])/(ptime[0]+ptime[size-1]);

  } else if (size == 2) {
    frm=cpl_frameset_get_frame(raws,0);
    name=cpl_frame_get_filename(frm);
    plist=cpl_propertylist_load(name,0);

    airm=xsh_pfits_get_airm_mean(plist);
    exptime=xsh_pfits_get_exptime(plist);

    airm_eff=airm*exptime;
    time_tot=exptime;

    frm=cpl_frameset_get_frame(raws,1);
    name=cpl_frame_get_filename(frm);
    plist=cpl_propertylist_load(name,0);


    airm_eff+=airm*exptime;
    time_tot+=exptime;

    airm_eff/=time_tot;
  } else {

    frm=cpl_frameset_get_frame(raws,0);
    name=cpl_frame_get_filename(frm);
    plist=cpl_propertylist_load(name,0);

    airm=xsh_pfits_get_airm_mean(plist);
    exptime=xsh_pfits_get_exptime(plist);

    airm_eff=airm;
  }

  cleanup:
  return airm_eff;
  
}


/**@}*/
