/*                                                                              *
 *   This file is part of the ESO UVES Pipeline                                 *
 *   Copyright (C) 2004,2005 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: 2013-08-08 13:36:46 $
 * $Revision: 1.52 $
 * $Name: not supported by cvs2svn $
 * $Log: not supported by cvs2svn $
 * Revision 1.51  2013/07/01 15:36:52  amodigli
 * Rename DEBUG to debug_mode to remove compiler error on some platforms (that name is reserved to special compiler options)
 *
 * Revision 1.50  2012/11/28 09:45:26  amodigli
 * changed default minthresh for FIBER mode back to its original value: 0.2 that shows to be more robust
 *
 * Revision 1.49  2011/08/25 08:18:04  amodigli
 * changed default minthresh to 0.01 only in flames_cal_orderpos, and flames_obs_redchain uves_orderpos_body.c
 *
 * Revision 1.48  2010/12/13 08:21:36  amodigli
 * fixed mem leak
 *
 * Revision 1.47  2010/09/27 15:22:34  amodigli
 * removed 'norders' parameter
 *
 * Revision 1.46  2010/09/24 09:32:04  amodigli
 * put back QFITS dependency to fix problem spot by NRI on FIBER mode (with MIDAS calibs) data
 *
 * Revision 1.44  2010/06/09 08:50:23  amodigli
 * In case USE_GUESS_TAB==2, make check if temporary order table start order numbering from 1. If not correct it appropriately before calling uves_locate_orders. Then put back orders numbering as it was. This to prevent a problem possibly found in case USE_GUESS_TAB==2 and an input guess table is provided with order numbering not starting at 1
 *
 * Revision 1.43  2010/05/11 10:49:09  amodigli
 * Fixed typo on param description
 *
 * Revision 1.42  2010/05/06 14:55:00  amodigli
 * increased min allowed val of backsubgrid to 10 and changed default of kappa to 4
 *
 * Revision 1.41  2009/10/29 17:16:54  amodigli
 * added param to specify if red cdd is new/old in call to uves_get_badpix
 *
 * Revision 1.40  2009/02/18 12:06:06  amodigli
 * fixed mem leak
 *
 * Revision 1.39  2008/09/29 06:57:52  amodigli
 * add #include <string.h>
 *
 * Revision 1.38  2008/05/01 09:51:42  amodigli
 * fixed compiler warnings
 *
 * Revision 1.37  2008/02/15 12:43:49  amodigli
 * allow lower/upper chip for parameter process_chip
 *
 * Revision 1.36  2007/12/17 07:41:41  amodigli
 * added some descriptors to pipe products
 *
 * Revision 1.35  2007/12/03 08:00:19  amodigli
 * added HIERARCH keys to 'debug' product
 *
 * Revision 1.34  2007/11/13 16:19:17  amodigli
 * product order table in case of FIBER mode is now FIB_ORD_TAB_x
 *
 * Revision 1.33  2007/10/23 06:48:57  amodigli
 * Master bias is subtracted if provided
 *
 * Revision 1.32  2007/10/05 16:01:45  amodigli
 * using proces_chip parameter to process or not a given RED chip
 *
 * Revision 1.31  2007/09/19 14:13:02  amodigli
 * Fibre-Order-Definition-Results-->Single-Fibre-Order-Definition-Results
 *
 * Revision 1.30  2007/08/23 15:14:14  amodigli
 * fixed DFS04255: the value of ymax in the search of orders was improperly set
 *
 * Revision 1.29  2007/08/21 13:08:26  jmlarsen
 * Removed irplib_access module, largely deprecated by CPL-4
 *
 * Revision 1.28  2007/08/02 15:21:06  amodigli
 * added parameter --use_guess_tab and possibility to use the input guess table as it was in  MIDAS (default option). Kept optional also current behaviour
 *
 * Revision 1.27  2007/07/17 12:40:04  jmlarsen
 * Fixed bug in update of DEFPOL variables
 *
 * Revision 1.26  2007/06/28 09:17:40  jmlarsen
 * Write polynomial in MIDAS format if FLAMES
 *
 * Revision 1.25  2007/06/22 09:28:51  jmlarsen
 * Changed interface of uves_save_image
 *
 * Revision 1.24  2007/06/06 08:17:33  amodigli
 * replace tab with 4 spaces
 *
 * Revision 1.23  2007/05/22 14:34:32  jmlarsen
 * Removed unnecessary includes
 *
 * Revision 1.22  2007/04/24 16:45:17  amodigli
 * changed interface of calls to uves_load_ordertable to match new interface
 *
 * Revision 1.21  2007/04/24 12:50:29  jmlarsen
 * Replaced cpl_propertylist -> uves_propertylist which is much faster
 *
 * Revision 1.20  2007/04/17 09:34:38  jmlarsen
 * Parametrize the assumption about consecutive orders (for FLAMES support)
 *
 * Revision 1.19  2007/04/12 12:01:44  jmlarsen
 * Skip Hough transform if guess table is provided
 *
 * Revision 1.18  2007/04/03 06:29:21  amodigli
 * changed interface to uves_load_ordertable
 *
 * Revision 1.17  2007/03/28 14:02:18  jmlarsen
 * Removed unused parameter
 *
 * Revision 1.16  2007/03/28 11:38:55  jmlarsen
 * Killed MIDAS flag, removed dead code
 *
 * Revision 1.15  2006/12/11 12:34:58  jmlarsen
 * Fixed QC bugs
 *
 * Revision 1.14  2006/12/11 11:06:44  jmlarsen
 * Read QC chip name from input header
 *
 * Revision 1.13  2006/12/07 08:24:21  jmlarsen
 * Factored some common QC parameters
 *
 * Revision 1.12  2006/12/01 12:26:51  jmlarsen
 * Factored out FLAMES plate-id code
 *
 * Revision 1.11  2006/11/16 14:12:21  jmlarsen
 * Changed undefined trace number from 0 to -1, to support zero as an actual trace number
 *
 * Revision 1.10  2006/11/16 09:49:25  jmlarsen
 * Fixed doxygen bug
 *
 * Revision 1.9  2006/11/15 15:02:14  jmlarsen
 * Implemented const safe workarounds for CPL functions
 *
 * Revision 1.7  2006/11/15 14:04:08  jmlarsen
 * Removed non-const version of parameterlist_get_first/last/next which is 
 * already in CPL, added const-safe wrapper, unwrapper and deallocator functions
 *
 * Revision 1.6  2006/11/13 12:46:26  jmlarsen
 * Added doc.
 *
 * Revision 1.5  2006/11/06 15:19:41  jmlarsen
 * Removed unused include directives
 *
 * Revision 1.4  2006/10/26 14:03:33  jmlarsen
 * Fixed position of const modifier
 *
 * Revision 1.3  2006/10/25 07:22:59  jmlarsen
 * Fixed wrong parameter context: hough -> trace
 *
 * Revision 1.2  2006/10/24 14:42:26  jmlarsen
 * Added plate number logging
 *
 * Revision 1.34  2006/10/17 12:33:02  jmlarsen
 * Added semicolon at UVES_RECIPE_DEFINE invocation
 *
 * Revision 1.33  2006/10/09 13:01:13  jmlarsen
 * Use macro to define recipe interface functions
 *
 * Revision 1.32  2006/09/20 12:53:57  jmlarsen
 * Replaced stringcat functions with uves_sprintf()
 *
 * Revision 1.31  2006/09/19 14:31:10  jmlarsen
 * uves_insert_frame(): use bitmap to specify which image statistics keywords must be computed
 *
 * Revision 1.30  2006/09/19 06:55:11  jmlarsen
 * Changed interface of uves_frameset to optionally write image statistics kewwords
 *
 * Revision 1.29  2006/08/24 11:36:37  jmlarsen
 * Write recipe start/stop time to header
 *
 * Revision 1.28  2006/08/18 13:35:42  jmlarsen
 * Fixed/changed QC parameter formats
 *
 * Revision 1.27  2006/08/11 14:56:05  amodigli
 * removed Doxygen warnings
 *
 * Revision 1.26  2006/08/07 11:35:35  jmlarsen
 * Disabled parameter environment variable mode
 *
 * Revision 1.25  2006/07/14 12:19:28  jmlarsen
 * Support multiple QC tests per product
 *
 * Revision 1.24  2006/07/03 14:20:39  jmlarsen
 * Exclude bad pixels from order tracing
 *
 * Revision 1.23  2006/07/03 13:09:24  amodigli
 * adjusted description display layout
 *
 * Revision 1.22  2006/07/03 12:46:34  amodigli
 * updated description
 *
 * Revision 1.21  2006/06/22 12:13:10  amodigli
 * removed ESO prefix
 *
 * Revision 1.20  2006/06/22 06:44:06  amodigli
 * added some QC param
 *
 * Revision 1.19  2006/06/16 08:25:34  jmlarsen
 * Do the order tracing on non-median filtered frame
 *
 * Revision 1.18  2006/06/13 11:57:02  jmlarsen
 * Check that calibration frames are from the same chip ID
 *
 * Revision 1.17  2006/06/08 11:40:50  amodigli
 * added check to have output order table as input guess, if provided
 *
 * Revision 1.16  2006/06/08 08:42:53  jmlarsen
 * Added support for computing Hough transform on image subwindow
 *
 * Revision 1.15  2006/06/07 13:06:28  jmlarsen
 * Changed doxygen tag addtogroup -> defgroup
 *
 * Revision 1.14  2006/06/07 09:01:28  amodigli
 * added some doc
 *
 * Revision 1.13  2006/06/06 08:40:10  jmlarsen
 * Shortened max line length
 *
 * Revision 1.12  2006/05/09 15:42:00  amodigli
 * added QC log
 *
 * Revision 1.11  2006/05/08 15:41:32  amodigli
 * added order table chopping (commented out)
 *
 * Revision 1.10  2006/05/05 13:55:17  jmlarsen
 * Minor doc update
 *
 * Revision 1.9  2006/04/20 10:47:39  amodigli
 * added qclog
 *
 * Revision 1.8  2006/04/06 09:48:15  amodigli
 * changed uves_frameset_insert interface to have QC log
 *
 * Revision 1.7  2006/04/06 08:46:40  jmlarsen
 * Changed default polynomial degrees to auto
 *
 * Revision 1.6  2006/03/24 14:04:14  jmlarsen
 * Changed background subtraction sample density default parameter value
 *
 * Revision 1.5  2006/03/09 10:53:41  jmlarsen
 * Changed default bivariate degrees to MIDAS values
 *
 * Revision 1.4  2006/03/03 13:54:11  jmlarsen
 * Changed syntax of check macro
 *
 * Revision 1.3  2006/02/28 09:15:22  jmlarsen
 * Minor update
 *
 * Revision 1.2  2006/02/21 14:26:54  jmlarsen
 * Minor changes
 *
 * Revision 1.1  2006/02/03 07:46:30  jmlarsen
 * Moved recipe implementations to ./uves directory
 *
 * Revision 1.63  2006/01/19 08:47:24  jmlarsen
 * Inserted missing doxygen end tag
 *
 * Revision 1.62  2006/01/05 14:31:31  jmlarsen
 * Checking for guess DRS table before guess order table
 *
 * Revision 1.61  2005/12/20 16:10:32  jmlarsen
 * Added some documentation
 *
 * Revision 1.60  2005/12/19 16:17:55  jmlarsen
 * Replaced bool -> int
 *
 */
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

/*----------------------------------------------------------------------------*/
/**
 * @addtogroup uves_orderpos
 */
/*----------------------------------------------------------------------------*/

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

#include <uves_orderpos_body.h>

#include <uves_orderpos_hough.h>
#include <uves_orderpos_follow.h>

#include <uves_physmod_chop_otab.h>
#include <uves_corrbadpix.h>
#include <uves_utils.h>
#include <uves_recipe.h>
#include <uves_parameters.h>
#include <uves_backsub.h>
#include <uves_pfits.h>
#include <uves_dfs.h>
#include <uves_qclog.h>
#include <uves_utils_wrappers.h>
#include <uves_utils_cpl.h>
#include <uves_error.h>
#include <uves_msg.h>

#include <cpl.h>

#include <float.h>
#include <string.h>
/*-----------------------------------------------------------------------------
                            Functions prototypes
 -----------------------------------------------------------------------------*/
/* compute qclog */
static void uves_orderpos_qclog(cpl_table* table,
                bool flames,
                int pord, 
                int dord,
                int samples_per_order,
                uves_propertylist* rhead, 
                enum uves_chip chip,
                cpl_table* qclog);

/**@{*/

/*-----------------------------------------------------------------------------
                            Exported variables
 -----------------------------------------------------------------------------*/

const char * const uves_orderpos_desc_short = "Defines echelle order positions";
const char * const uves_orderpos_desc =
"The recipe defines the order positions in an echelle image. The orders are\n"
"initially detected by means of a Hough transformation, the orders are then \n"
"traced, and the positions are finally fitted with a global polynomial.\n"
"\n"
"Expected input frames are narrow flat fields, ORDER_FLAT_xxx, or standard \n"
"stars, STANDARD_xxx, where xxx is 'BLUE' or 'RED', and optionally for each \n"
"chip a DRS setup table (DRS_TABLE_BLUE, DRS_TABLE_REDL, DRS_TABLE_REDU) or \n"
"guess order table (ORDER_GUESS_TAB_BLUE, ORDER_GUESS_TAB_REDL, \n"
"ORDER_GUESS_TAB_REDU, \n"
"or, for backward compatibility, ORDER_TABLE_BLUE, ORDER_TABLE_REDL, \n"
"ORDER_TABLE_REDU). The recipe processes only the first raw frame found.\n"
"\n"
"Output is one (or two if input is a red frame) order table(s) \n"
"(UVES: ORDER_TABLE_(BLUE|REDL|REDU); FLAMES: FIB_ORDEF_(REDL|REDU) contaning\n"
"the columns:\n"
"X                : Position along x\n"
"Order            : Relative order number\n"
"Y                : Order line centroid location\n"
"Yfit             : The fitted order location\n"
"dY               : Uncertainty of Y\n"
"dYfit_Square     : Variance of Yfit\n"
"Residual         : Y - Yfit\n"
"Residual_Square  : Residual^2\n"
"OrderRMS         : Root mean squared residual of initial\n"
"                   one-dimensional linear fit of order\n"
"\n"
"The bivariate fit polynomial itself is stored in table extension no. 2.\n"
"The 3rd table extension contains a table that defines the active fibre traces\n"
"and their positions (for support of FLAMES/UVES)\n";


/*-----------------------------------------------------------------------------
                              Functions code
 -----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
/**
  @brief    Setup the recipe options    
  @param    parameters   the parameterlist to fill
  @param    recipe_id    name of calling recipe
  @return   0 if everything is ok
 */
/*----------------------------------------------------------------------------*/
int
uves_orderpos_define_parameters_body(cpl_parameterlist *parameters,
                     const char *recipe_id)
{
    const char *subcontext;
    double min_thresh=0.2;

    /*****************
     *    General    *
     *****************/
    
    if (uves_define_global_parameters(parameters) != CPL_ERROR_NONE)
    {
        return -1;
    }
    
    /*****************
     * Preprocessing *
     *****************/

    subcontext = "preproc";

    /* Use of Guess sol */
    uves_par_new_enum("use_guess_tab", 
		      CPL_TYPE_INT,
               "If a Guess order table is provided this parameter set how it is used:"
               "0: No usage, "
               "1: use it to set lower/upper Y raws where order are searched "
		      "2: the order table try to fully match the guess",
		      1, 3, 0, 1, 2);
    

    
    /* Radx, Rady */
    uves_par_new_range("radx", 
               CPL_TYPE_INT,
               "Half X size of median filtering window",
               2, 0, INT_MAX);
    
    uves_par_new_range("rady",
               CPL_TYPE_INT,
               "Half Y size of median filtering window",
               1, 0, INT_MAX);

    /* Mmethod */
    uves_par_new_enum("mmethod",
              CPL_TYPE_STRING,
              "Background subtraction method. If equal "
              "to 'median' the background is sampled using "
              "the median of a sub-window. If 'minimum', "
              "the minimum sub-window value is used. If "
              "'no', no background subtraction is done.",
              "median",                        /* Default */
              3,                               /* Number of options */
              "median", "minimum", "no");      /* List of options */
  
    /* Backsubgrid */
    uves_par_new_range("backsubgrid",
               CPL_TYPE_INT,
               "Number of grid points (in x- and y-direction) "
               "used to estimate "
               "the background (mode=poly).",
               50, 10, INT_MAX);
    
    /* Backsubradiusy */
    uves_par_new_range("backsubradiusy",
               CPL_TYPE_INT,
               "The height (in pixels) of the background "
               "sampling window is (2*radiusy + 1). "
               "This parameter is not corrected for binning.",
               2, 0, INT_MAX);
    
    /* Backsubkappa */
    uves_par_new_range("backsubkappa",
               CPL_TYPE_DOUBLE,
               "The value of kappa in the one-sided kappa-sigma "
               "clipping used to "
               "estimate the background (mode=poly).",
               4.0, 0.0, DBL_MAX);
    
    /* Backsubdegx, backsubdegy */
    uves_par_new_range("backsubdegx",
               CPL_TYPE_INT,
               "Degree (in x) of polynomial used "
               "to estimate the background (mode=poly).",
               2, 1, INT_MAX);
    
    uves_par_new_range("backsubdegy",
               CPL_TYPE_INT,
               "Degree (in y) of polynomial used "
               "to estimate the background (mode=poly).",
               2, 1, INT_MAX);
    
    /*******************
     * Hough detection *
     *******************/
    subcontext = "hough";  

    /* Samplewidth */
    uves_par_new_range("samplewidth",
               CPL_TYPE_INT,
               "Separation of sample traces "
               "(used by Hough transform) in input image",
               50, 1, INT_MAX);
    
    /* Minslope, Maxslope */
    uves_par_new_range("minslope",
               CPL_TYPE_DOUBLE,
               "Minimum possible line slope. This should "
               "be the 'physical' slope on the chip, "
               "i.e. not taking binning factors into "
               "account, which is handled by the recipe",
               0.0, 0.0, DBL_MAX);
    
    uves_par_new_range("maxslope",
               CPL_TYPE_DOUBLE,
               "Maximum possible line slope",
               0.2, 0.0, DBL_MAX);
    
    /* Sloperes */
    uves_par_new_range("sloperes",
               CPL_TYPE_INT,
               "Resolution (width in pixels) of Hough space",
               120, 1, INT_MAX);
    
    /* Norders */
    /* decided to temporally remove this as only option 0 works
       later one need to fix the behaviour for this parameter
 
    uves_par_new_range("norders",
               CPL_TYPE_INT,
               "Number of echelle orders to detect. If "
               "set to 0 the predicted number of orders will "
               "be read from the guess order table. If no "
               "guess order table is given, the recipe will "
               "try to autodetect the number of orders. If "
               "the raw frame is a red chip and this parameter "
               "is specified, the given value will be used "
               "for both red chips",
               0, 0, INT_MAX);
 
    */   
    /* Pthres */
    uves_par_new_range("pthres",
               CPL_TYPE_DOUBLE,
               "In automatic mode, or if the number of orders "
               "to detect is read from a guess table, the detection "
               "of new lines stops when the intensity of a candidate "
               "line drops to less than 'pthres' times the intensity "
               "of the previous detection. "
/* Text applicable only if 'norders' parameter is present 
               "Otherwise - i.e. if the "
               "number of orders to detect was specified by setting "
               "the 'norders' parameters - this parameter is "
               "ignored."
*/
               ,
               0.2, 0.0, 1.0);
    
    /*******************
     * Order tracing   *
     *******************/
    subcontext = "trace";

    /* Tracestep */
    uves_par_new_range("tracestep",
               CPL_TYPE_INT,
               "The step size used when tracing the orders",
               10, 1, INT_MAX);
    
    /* Minthres */
    min_thresh=0.2;
    uves_par_new_range("minthresh",
                       CPL_TYPE_DOUBLE,
                       "The minimum threshold value is (min + "
                       "minthres*(max - min)). Here 'min' "
               "and 'max' are the lowest and highest pixel "
               "values in the central bin of the order",
                       min_thresh, 0.0, 1.0);
    
    /* Maxgap */
    uves_par_new_range("maxgap",
                       CPL_TYPE_DOUBLE,
                       "If the order line drops below detection "
                       "threshold, the order tracing algorithm "
                       "will try to jump a gap of maximum size 'maxgap' "
                       "multiplied by the image width",
                       .2, 0.0, 1.0);

    /***************
     *  Rejection  *
     ***************/

    subcontext = "reject";

    /* Maxrms */   /* In uves/midas default is 3.5 */
    uves_par_new_range("maxrms",
                 CPL_TYPE_DOUBLE,
                 "When fitting the orders with straight lines, "
                 "this is the maximum allowed RMS relative to "
                 "the median RMS of all orders",
                 100.0, 0.0, DBL_MAX);

    /* In MIDAS,  defpol=2,3 is used which is not optimal
       but faster than (-1,-1)               */
    /* Defpol1 */
    uves_par_new_range("defpol1",
                 CPL_TYPE_INT,
                 "The degree of the bivarite fit (cross "
                 "dispersion direction). If negative, "
                 "the degree is optimized to give the best fit",
                 -1,
                 -1, INT_MAX);
    
    /* Defpol2 */
    uves_par_new_range("defpol2",
                 CPL_TYPE_INT,
                 "The degree of the bivarite fit (order number). "
                 "If negative, "
                 "the degree is optimized to give the best fit",
                 -1,
                 -1, INT_MAX);
    
    /* Kappa */
    uves_par_new_range("kappa",
                 CPL_TYPE_DOUBLE,
                 "Used for kappa-sigma clipping of the final "
                 "polynomial fit. If negative, no clipping is done",
                 4.0, -2.0, DBL_MAX);

    return (cpl_error_get_code() != CPL_ERROR_NONE);
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Define order lines of one chip
  @param    raw_image      The image to process
  @param    raw_header     FITS header of the raw image
  @param    rotated_header Header containing keywords to describe
                           the rotated input image
  @param    chip           CCD chip
  @param    debug_mode          If set to true, intermediate results are saved 
                           to the current directory
  @param    RADX           See man-page or @c uves_orderpos_create()
  @param    RADY           See man-page or @c uves_orderpos_create()
  @param    BM_METHOD      See man-page or @c uves_orderpos_create()
  @param    BACKSUBGRID    See man-page or @c uves_orderpos_create()
  @param    BACKSUBRADIUSY See man-page or @c uves_orderpos_create()
  @param    BACKSUBKAPPA   See man-page or @c uves_orderpos_create()
  @param    BACKSUBDEGX    See man-page or @c uves_orderpos_create()
  @param    BACKSUBDEGY    See man-page or @c uves_orderpos_create()
  @param    SAMPLEWIDTH    See man-page or @c uves_orderpos_create()
  @param    MINSLOPE       See man-page or @c uves_orderpos_create()
  @param    MAXSLOPE       See man-page or @c uves_orderpos_create()
  @param    SLOPERES       See man-page or @c uves_orderpos_create()
  @param    NORDERS        See man-page or @c uves_orderpos_create()
  @param    norders_is_guess   If true (i.e. if the parameter @em NORDERS is 
                               just a guess)
                               the number of orders actually detected might
                               differ from @em NORDERS
  @param    PTHRES         See man-page or @c uves_orderpos_create()
  @param    TRACESTEP      See man-page or @c uves_orderpos_create()
  @param    MINTHRESH      See man-page or @c uves_orderpos_create()
  @param    MAXGAP         See man-page or @c uves_orderpos_create()
  @param    MAXRMS         See man-page or @c uves_orderpos_create()
  @param    DEFPOL1        See man-page or @c uves_orderpos_create()
                           If negative, will be set to the actual degree used
  @param    DEFPOL2        See man-page or @c uves_orderpos_create()
                           If negative, will be set to the actual degree used
  @param    KAPPA          See man-page or @c uves_orderpos_create()
  @param    bivariate_fit  (output) The global polynomial defining the
                           orders' locations as function of @em x and
               @em order number
  @param    norders        (output) Number of order lines detected
  @return   The order table for this chip

  This function executes the macro-steps involved in processing a single chip:
  - Applies a median filter.
  - Subtracts the background. (See @c uves_backsub_poly().)
  - Detects order lines using a Hough transformation. (See @c uves_hough() .)
  - Traces the order lines. (See @c uves_locate_orders() .)

 */
/*----------------------------------------------------------------------------*/
static cpl_table *
uves_orderpos_process_chip(const cpl_image *raw_image, 
                           uves_propertylist *raw_header,
                           const uves_propertylist *rotated_header, 
                           enum uves_chip chip, 
                           int binx, int biny,
                          /* General */
			   bool      debug_mode,
			   /* Preprocessing */
			   int      USE_GUESS_TAB,
			   int      RADX,
			   int      RADY,
			   background_measure_method BM_METHOD,
			   int      BACKSUBGRID,
			   int      BACKSUBRADIUSY,
			   double   BACKSUBKAPPA,
			   int      BACKSUBDEGX,
			   int      BACKSUBDEGY,
			   /* Hough transform */
			   int      SAMPLEWIDTH,
			   double   MINSLOPE,
			   double   MAXSLOPE,
			   int      SLOPERES,
			   int      NORDERS,
			   bool      norders_is_guess,
			   double   PTHRES,
			   /* Order following */
			   int      TRACESTEP,
			   double   MINTHRESH,
			   double   MAXGAP,
			   /* Rejection */
			   double   MAXRMS,
			   int      *DEFPOL1,
			   int      *DEFPOL2,
			   double   KAPPA,
			   /* Output */
			   polynomial **bivariate_fit,
			   int *norders,
                           cpl_table* guess_table)
{
    /* Result */
    cpl_table   *tracetable     = NULL;

    cpl_image   *noise          = NULL;    /* Image defining the noise
                           of the current image */
    cpl_image   *back_subbed    = NULL;
    cpl_image   *hough_trans    = NULL;
    polynomial  *guess_locations = NULL;
    
    /* Debug objects */
    cpl_image   *inputlines     = NULL; /* Hough solution drawn
                        on top of input image */
    cpl_table   *ordertable     = NULL; /* A preliminary order table
                        containing one row per order */
    cpl_image   *hough_original = NULL;
    int abs_ord_min=0;
    int abs_ord_max=0;
    int badpixels_marked = 0;
    int ymin = 0;
    int ymax = 0;
    int ord_min=0;
    bool red_ccd_is_new=0;
    check_nomsg(red_ccd_is_new=uves_ccd_is_new(raw_header));

    check( back_subbed = cpl_image_duplicate(raw_image), "Error duplicating image");
    ymin = 1;
    ymax = cpl_image_get_size_y(back_subbed);
    uves_msg("===============");
    /* uves_msg("guess order tab=%p",guess_table); */
    /* Calculate the basic order table */
    if(guess_table != NULL) 
    {
      if(USE_GUESS_TAB == 1) {
	int ymin_guess=ymin;
	int ymax_guess=ymax;
        int omin=0;
        int omax=0;
	cpl_table* tmp_tbl=NULL;

        uves_msg("Use input guess order table to define the detector area");
        uves_msg("where to locate orders");
 

        tmp_tbl=cpl_table_duplicate(guess_table);
        uves_physmod_chop_otab(raw_header,chip,&tmp_tbl,"Order",&omin,&omax);
 
	uves_msg("On Guess Found %d orders.",omax-omin+1);
	if(omax < cpl_table_get_column_max(guess_table,"Order")) {
	  uves_free_table(&tmp_tbl);
          check(tmp_tbl=uves_extract_table_rows(guess_table,"Order",
						CPL_EQUAL_TO,omax+1),
		"Error selecting Order");

	  check(ymax_guess=(int)cpl_table_get_column_min(tmp_tbl,"Yfit")-1,
		"error getting ymax_guess");
	  uves_free_table(&tmp_tbl);
	}

	if(omin > cpl_table_get_column_min(guess_table,"Order")) {
	  uves_free_table(&tmp_tbl);
	  check(tmp_tbl=uves_extract_table_rows(guess_table,"Order",
						CPL_EQUAL_TO,omin-1),
		"Error selecting Order");

	  check(ymin_guess=(int)cpl_table_get_column_max(tmp_tbl,"Yfit")+1,
		"error getting ymin_guess");

	  uves_free_table(&tmp_tbl);
	}
        ymin = (ymin_guess>ymin) ? ymin_guess : ymin;
        ymax = (ymax_guess<ymax) ? ymax_guess : ymax;

        uves_msg("Serching them in the region [ymin,ymax]=[%d,%d]",ymin,ymax);
        uves_free_table(&tmp_tbl);


      } else if (USE_GUESS_TAB == 2) {

      // Create a table with order lines in the same format as the
      // Hough transform would do it, i.e. intersept + slope for each order.
            
            int minorder = uves_round_double(
                cpl_table_get_column_min(guess_table, "Order"));
            int maxorder = uves_round_double(
                cpl_table_get_column_max(guess_table, "Order"));
            int nx = cpl_image_get_size_x(back_subbed);
            int order;


        uves_msg("Create a table with order lines in the same format as the");
        uves_msg("Hough transform would do it, ");
	uves_msg("i.e. intersept + slope for each order.");

            {
                double kappa = 4;
                int max_degree = 6;
                double min_rms = 0.1; /* pixels */
                double mse;
                check( guess_locations = uves_polynomial_regression_2d_autodegree(
                           guess_table,
                           "X", "Order", "Yfit", NULL,
                           NULL, NULL, NULL,
                           &mse, NULL, NULL,
                           kappa,
                           max_degree, max_degree, min_rms, -1,
                           false,
                           NULL, NULL, -1, NULL),
                   "Could not fit polynomial to provided table");

            uves_msg("Provided table contains orders %d - %d. RMS = %.3f pixels",
                     minorder, maxorder, sqrt(mse));
            }


            
            ordertable = cpl_table_new(maxorder - minorder + 1);
            cpl_table_new_column(ordertable, "Order", CPL_TYPE_INT);
            cpl_table_new_column(ordertable, "Intersept", CPL_TYPE_DOUBLE);
            cpl_table_new_column(ordertable, "Slope", CPL_TYPE_DOUBLE);
            cpl_table_new_column(ordertable, "Spacing", CPL_TYPE_INT);

            for (order = minorder; order <= maxorder; order++)
                {
                    int row = order - minorder;
                    double slope = 
                        uves_polynomial_derivative_2d(guess_locations, nx/2, order, 1);
                    double intersept = uves_polynomial_evaluate_2d(guess_locations, nx/2, order)
                        - slope*(nx/2);
                    int spacing = 
                        uves_round_double(uves_polynomial_derivative_2d(
                                              guess_locations, nx/2, order, 2));
                    
                    cpl_table_set_int   (ordertable, "Order", row, order);
                    cpl_table_set_double(ordertable, "Slope", row, slope);
                    cpl_table_set_int   (ordertable, "Spacing", row, spacing);
                    cpl_table_set_double(ordertable, "Intersept", row, intersept);
                }

      } 

    }
    if( (guess_table == NULL) || (USE_GUESS_TAB != 2) )
        {
  
            /* Detect orders from scratch */
	  uves_msg("Detect orders from scratch ");



          /* Remove bad/hot pixels with a median filter.
             * This is needed for the Hough transform, but
             * we trace the orders on the raw image (gives better fit).
             */
            {
                bool extrapolate_border = true;     /* This is needed to avoid a sudden
                                                       intensity increase near the image
                                                       borders (which will confuse the Hough
                                                       transform) */
                
                uves_msg("Applying %dx%d median filter", RADX*2+1, RADY*2+1);
                check( uves_filter_image_median(&back_subbed, RADX, RADY, extrapolate_border), 
                       "Could not filter image");
            }

            /* Subtract background */
            uves_msg("Subtracting background (grid sampling)");
            
            check( uves_backsub_poly(back_subbed,
                                     NULL, NULL,            /* Order locations are unknown */
                                     BM_METHOD,
                                     BACKSUBGRID,
                                     BACKSUBRADIUSY,
                                     BACKSUBDEGX,
                                     BACKSUBDEGY,
                                     BACKSUBKAPPA),
                   "Could not subtract background");

            check( ordertable = uves_hough(back_subbed,
                                           ymin, ymax,
                                           NORDERS, norders_is_guess,
                                           SAMPLEWIDTH,
                                           PTHRES,
                                           MINSLOPE,
                                           MAXSLOPE,
                                           SLOPERES,
                                           true,  /* Consecutive orders? */
                                           &hough_trans,
                                           &hough_original), 
                   "Could not locate echelle orders");
            
            if (debug_mode)
                {

                   check(uves_propertylist_copy_property_regexp((uves_propertylist*)rotated_header,                                                raw_header,
    						"^ESO ", 0),
			  "Error copying hieararch keys");

                    check( uves_save_image_local("Hough transform", "hough", 
                                                 hough_original, chip, -1, -1, 
                                                 rotated_header, true),
                           "Error saving hough image");
                    
                    check( uves_save_image_local("Hough transform (peaks deleted)", 
                                                 "hough_delete", hough_trans, 
                                                 chip, -1, -1, rotated_header, 
                                                 true),
                           "Error saving hough image");
                    
                    /* For debugging, draw Hough detected orders
                       (straight lines) on top of the input image */
                    check( inputlines = cpl_image_duplicate(raw_image), 
                           "Could not duplicate image");
                    check( uves_draw_orders(ordertable, inputlines), 
                           "Could not draw hough orders on image");
                    
                    check( uves_save_image_local("Lines detected by Hough transform",
                                                 "inputlines", inputlines, chip, -1, -1, rotated_header, true),
                           "Error saving hough image");
                    
                    uves_free_image(&inputlines);
                }

            /* Clean up */
            uves_free_image(&hough_trans);
            uves_free_image(&hough_original);
        }
    /* Initial order detection done */

    /* Subtract background, but this time sample the interorder space */
    check(( uves_free_image(&back_subbed),
            back_subbed = cpl_image_duplicate(raw_image)),
          "Error duplicating image");
    
    uves_msg("Subtracting background (inter-order sampling)");
    check( uves_backsub_poly(back_subbed,
                             ordertable, NULL,
                             BM_METHOD,
                             BACKSUBGRID,
                             BACKSUBRADIUSY,
                             BACKSUBDEGX,
                             BACKSUBDEGY,
                             BACKSUBKAPPA),
           "Could not subtract background");
    
    /* Create noise image (readout + photonic noise) 
     * We need to do this *after* background subtraction,
     * because we cannot distinguish bias from scattered light
     * (if master bias was explicitly subtracted, we should
     *  define the noise model before background subtraction)
     */
    check( noise = uves_define_noise(back_subbed, raw_header, 1, chip),
       "Error creating noise image");
    
    if (debug_mode)
    {
        /* Save pre-processed (cropped, rotated, median filtered, 
           backgr. subtracted) input image  */
        check( uves_save_image_local("Pre-processed raw frame", "preproc", 
                     back_subbed, chip, -1, -1, rotated_header, true), 
           "Error saving image");
        
        /* Save noise of pre-processed input image */
        check( uves_save_image_local("Noise of pre-processed image", 
                     "preproc_noise", 
                     noise, chip, -1, -1, rotated_header, true),
           "Error saving image");
    }
    
    /* Mark bad pixels */
    check( badpixels_marked = 
       uves_correct_badpix_all(back_subbed,   /* MIDAS uses raw_image here */
                   raw_header, 
                   chip, 
                   binx, biny, 
                   true,           /* Mark, don't interpolate bad pixels */
                               red_ccd_is_new
           ),
       "Error marking bad pixels");
    
    uves_msg("%d pixels marked as bad", badpixels_marked);

    /* Trace the orders
       (using the background subtracted image or not if MIDAS) */
    uves_msg("norders=%d",*norders);
    if(USE_GUESS_TAB==2) {
       ord_min=cpl_table_get_column_min(ordertable,"Order");
       if (ord_min>1) {
          cpl_table_subtract_scalar(ordertable,"Order",ord_min-1);
       }
    }

    check( tracetable = uves_locate_orders(back_subbed,    /* MIDAS uses raw_image here */
                       noise,
                       ordertable,
                       TRACESTEP,
                       MINTHRESH,
                       MAXGAP,
                       MAXRMS,
                       DEFPOL1, DEFPOL2,
                       KAPPA,
                       bivariate_fit,
                       norders), "Could not trace orders");
    
    if(USE_GUESS_TAB==2) {
       if (ord_min>1) {
          cpl_table_add_scalar(ordertable,"Order",ord_min-1);
       }
    }

    if (false)
        /* This is a workaround for a broken wavecal recipe in the MIDAS
           pipeline. There is no reason to do this in the CPL pipeline,
           where the wavecal recipe is able to deal with partial orders */
        {
            check(uves_physmod_chop_otab(raw_header,chip,&tracetable,"Order",
                                         &abs_ord_min,&abs_ord_max),
                  "Could not run uves_physmod_chop_otab on trace order table");
        }


    /* add QC log */


    /* Save basic info about orders */
    if (debug_mode) check( uves_save_table_local("Basic order table", "basic",
                        ordertable, chip, -1, -1, raw_header, NULL),
              "Error saving table");
    
  cleanup:
    uves_free_image(&back_subbed);
    uves_free_image(&noise);
    uves_free_image(&hough_trans);
    uves_polynomial_delete(&guess_locations);

    /* Debug objects */
    uves_free_image(&hough_original);
    uves_free_image(&inputlines);
    uves_free_table(&ordertable);
    
    return tracetable;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Get the command line options and execute the data reduction
  @param    frames      the frames list
  @param    flames      FLAMES mode?
  @param    parameters  the parameters list
  @return   CPL_ERROR_NONE if everything is ok

  This function is the executor function for the order position recipe. It
  - loads the first raw frame of the frame set,
  - corrects recipe parameters for binning,
  - processes each chip in the input image
    (see @c uves_orderpos_process_chip()), and
  - saves the resulting order table(s).
 */
/*----------------------------------------------------------------------------*/
void
uves_orderpos_exe_body(cpl_frameset *frames,
               bool flames,
               const char *recipe_id,
               const cpl_parameterlist *parameters,
               const char *starttime)
{
    /*
     * Variables that will contain the values of the recipe parameters 
     */

    /* General */
    bool      debug_mode;
    /* Preprocessing */
    int      USE_GUESS_TAB, RADX, RADY;
    background_measure_method BM_METHOD;
    int      BACKSUBGRID;
    int      BACKSUBRADIUSY;
    double   BACKSUBKAPPA;
    int      BACKSUBDEGX;
    int      BACKSUBDEGY;
    /* Hough transform */
    int      SAMPLEWIDTH;
    double   MINSLOPE, MAXSLOPE;
    int      SLOPERES;
    int      NORDERS=0;
    double   PTHRES;
    /* Order following */
    int      TRACESTEP;
    double   MINTHRESH;
    double   MAXGAP;
    /* Rejection */
    double   MAXRMS;
    int      DEFPOL1_par; /* Recipe parameter */
    int      DEFPOL2_par;
    double   KAPPA;
    const char* PROCESS_CHIP=NULL;

    /* Master bias */
    cpl_image *master_bias               = NULL;
    uves_propertylist *master_bias_header = NULL;


    /* Input image */
    cpl_image        *raw_image[2]      = {NULL, NULL};
    uves_propertylist *raw_header[2]     = {NULL, NULL};
    uves_propertylist *rotated_header[2] = {NULL, NULL};
    cpl_image        *raw_image_int     = NULL;

    /* Input guess table */
    uves_propertylist *guess_header      = NULL; 
    cpl_table        *guess_table       = NULL; 

    /* Output table */
    cpl_table        *tracetable        = NULL;
    polynomial       *bivariate_fit     = NULL;
    cpl_table        *traces            = NULL;  /* FLAMES/UVES compatibility */
    uves_propertylist *product_header    = NULL;
    cpl_table* qclog[2] = {NULL, NULL};

    /* Local variables */
    const char *raw_filename = "";
    char *product_filename = NULL;
    int ord_predict = 0;
    bool norders_is_guess = false;               /* Was norders read from guess table? */
    bool blue;
    enum uves_chip chip;
    int binx = 0;
    int biny = 0;


    const char *guess_filename = "";
    const char *chip_name = "";
    const char *master_bias_filename = "";

    int raw_index =0;
    int norders = 0;                           /* Number of orders detected */
        
    int DEFPOL1 = 0;
    int DEFPOL2 = 0;

    bool load_guess = false;

    int plate_no;

    int samples_per_order =0;
    char values[80];

    int i=0;
    int j=0;
    char extname[80];
    uves_propertylist* table_header = NULL;


    /* Read recipe parameters */
    {
    /* General */
    check( uves_get_parameter(parameters, NULL, "uves", "debug", CPL_TYPE_BOOL, &debug_mode), 
           "Could not read parameter");
    check( uves_get_parameter(parameters, NULL, "uves", "process_chip", CPL_TYPE_STRING, &PROCESS_CHIP),
               "Could not read parameter");
    uves_string_toupper((char*)PROCESS_CHIP);

    /* Preprocessing */
    check( uves_get_parameter(parameters, NULL, recipe_id, "preproc.use_guess_tab",
                  CPL_TYPE_INT   , &USE_GUESS_TAB), "Could not read parameter");
    check( uves_get_parameter(parameters, NULL, recipe_id, "preproc.radx",
                  CPL_TYPE_INT   , &RADX), "Could not read parameter");
    check( uves_get_parameter(parameters, NULL, recipe_id, "preproc.rady", 
                  CPL_TYPE_INT   , &RADY), "Could not read parameter");

    check( BM_METHOD = uves_get_bm_method(parameters, recipe_id, "preproc"),
           "Could not read background measuring method");

    check( uves_get_parameter(parameters, NULL, recipe_id, "preproc.backsubgrid", 
                  CPL_TYPE_INT  , &BACKSUBGRID), 
           "Could not read parameter");
    check( uves_get_parameter(parameters, NULL, recipe_id, "preproc.backsubradiusy",
                  CPL_TYPE_INT, &BACKSUBRADIUSY), "Could not read parameter");
    check( uves_get_parameter(parameters, NULL, recipe_id, "preproc.backsubkappa", 
                  CPL_TYPE_DOUBLE,&BACKSUBKAPPA), "Could not read parameter");
    check( uves_get_parameter(parameters, NULL, recipe_id, "preproc.backsubdegx", 
                  CPL_TYPE_INT  , &BACKSUBDEGX), "Could not read parameter");
    check( uves_get_parameter(parameters, NULL, recipe_id, "preproc.backsubdegy", 
                  CPL_TYPE_INT  , &BACKSUBDEGY), "Could not read parameter");
    /* Hough */
    check( uves_get_parameter(parameters, NULL, recipe_id, "hough.samplewidth"  , 
                  CPL_TYPE_INT   , &SAMPLEWIDTH), "Could not read parameter");
    check( uves_get_parameter(parameters, NULL, recipe_id, "hough.minslope"     , 
                  CPL_TYPE_DOUBLE, &MINSLOPE   ), "Could not read parameter");
    check( uves_get_parameter(parameters, NULL, recipe_id, "hough.maxslope"     , 
                  CPL_TYPE_DOUBLE, &MAXSLOPE   ), "Could not read parameter");
    check( uves_get_parameter(parameters, NULL, recipe_id, "hough.sloperes"     , 
                  CPL_TYPE_INT   , &SLOPERES   ), "Could not read parameter");
/* hough.norders parameter has been temporally removed 
    check( uves_get_parameter(parameters, NULL, recipe_id, "hough.norders"      , 
                  CPL_TYPE_INT   , &NORDERS    ), "Could not read parameter");
*/
    check( uves_get_parameter(parameters, NULL, recipe_id, "hough.pthres"       , 
                  CPL_TYPE_DOUBLE, &PTHRES     ), "Could not read parameter");
    /* Tracing */
    check( uves_get_parameter(parameters, NULL, recipe_id, "trace.tracestep"    , 
                  CPL_TYPE_INT   , &TRACESTEP  ), "Could not read parameter");
    check( uves_get_parameter(parameters, NULL, recipe_id, "trace.minthresh"    , 
                  CPL_TYPE_DOUBLE, &MINTHRESH  ), "Could not read parameter");
    check( uves_get_parameter(parameters, NULL, recipe_id, "trace.maxgap"       , 
                  CPL_TYPE_DOUBLE, &MAXGAP     ), "Could not read parameter");
    /* Reject */
    check( uves_get_parameter(parameters, NULL, recipe_id, "reject.maxrms"      , 
                  CPL_TYPE_DOUBLE, &MAXRMS     ), "Could not read parameter");
    check( uves_get_parameter(parameters, NULL, recipe_id, "reject.defpol1"     , 
                  CPL_TYPE_INT   , &DEFPOL1_par ), "Could not read parameter");
    check( uves_get_parameter(parameters, NULL, recipe_id, "reject.defpol2"     , 
                  CPL_TYPE_INT   , &DEFPOL2_par), "Could not read parameter");
    check( uves_get_parameter(parameters, NULL, recipe_id, "reject.kappa"       , 
                  CPL_TYPE_DOUBLE, &KAPPA      ), "Could not read parameter");
    
    /* The range of parameters have already been checked by the caller */
    /* Do some additional checking */
    assure( MINSLOPE  < MAXSLOPE , CPL_ERROR_ILLEGAL_INPUT, 
        "Minimum slope must be smaller than maximum slope (min = %f; max = %f)",
        MINSLOPE, MAXSLOPE);
    if (MAXSLOPE > 0.5){
        uves_msg_warning("Hough transformation might fail when searching for "
                 "lines with slope larger than 0.5 (maxslope = %f)", MAXSLOPE);
    }

    if (DEFPOL1_par >= 6 || DEFPOL2_par >= 6)
        {
        uves_msg_warning("Polynomial fitting might be unstable with "
                 "polynomial degrees higher than 5");
        }
    
    }
    
    /* Load raw image and header, and identify input frame as red or blue */
    check( uves_load_orderpos(frames, 
                              flames, 
                              &raw_filename, raw_image,
                  raw_header, rotated_header, &blue), "Error loading raw frame");
    
    /* Normalize the range of slopes to match the binning of the input image */
    check (binx = uves_pfits_get_binx(raw_header[0]),
       "Could not read x binning factor from input header");
    check (biny = uves_pfits_get_biny(raw_header[0]),
       "Could not read y binning factor from input header");
    /* If, for instance, BINX = 2, the slope of a line in the input frame is
       twice the slope of the line on the (unbinned) chip, and generally we need
       to change SLOPE := BINX/BINY * SLOPE, when going from unbinned to binned
       data.
       Additionally, when rotating a UVES frame into standard orientation, x- and y-
       directions are swapped, so the parameters MINSLOPE and MAXSLOPE must be
       multiplied by BINY/BINX to correct for binning. */
    MINSLOPE = (MINSLOPE*biny)/binx;
    MAXSLOPE = (MAXSLOPE*biny)/binx;
    
    ord_predict = NORDERS;
    
    /* Loop over one or two chips */
    for (chip = uves_chip_get_first(blue); 
     chip != UVES_CHIP_INVALID; 
     chip = uves_chip_get_next(chip))
    {

      if(strcmp(PROCESS_CHIP,"REDU") == 0) {
	chip = uves_chip_get_next(chip);
      }

        table_header = uves_propertylist_new();

        raw_index = uves_chip_get_index(chip);
        norders = 0;                           /* Number of orders detected */
        
        DEFPOL1 = DEFPOL1_par;
        DEFPOL2 = DEFPOL2_par;
        
        uves_msg("Processing %s chip in '%s'",
             uves_chip_tostring_upper(chip), raw_filename);

        check_nomsg( chip_name = uves_pfits_get_chipid(raw_header[raw_index], chip));

        uves_msg_debug("Binning = %dx%d", binx, biny);

        /* If user didn't specify number of orders, then
         *   Load the DRS-table (MIDAS), or if not present,
         *   load the guess order table, or if not present,
         *   auto-detect number of orders
         */
        if (NORDERS == 0)
        {
            /* The number of orders to detect will 
               be read from input guess table (if available),
               and it is just a guess: */
            norders_is_guess = true;

            uves_free_propertylist(&guess_header);
            
            if (cpl_frameset_find(frames, UVES_DRS_SETUP(flames, chip)) != NULL)
            {
                uves_msg_low("No guess order table found");
                
                check( uves_load_drs(frames, flames, chip_name, &guess_filename, 
                         &guess_header, chip),
                   "Error loading setup table");
                
                uves_msg("Using setup table in '%s'", guess_filename);

                check( ord_predict = uves_pfits_get_ordpred(guess_header), 
                   "Could not read predicted number "
                   "of orders from DRS table header");
            }
            else if (cpl_frameset_find(frames,
                           UVES_ORDER_TABLE(flames, chip)) != NULL ||
                 cpl_frameset_find(frames,
                           UVES_GUESS_ORDER_TABLE(flames, chip)) != NULL)
            {
                load_guess = (
                cpl_frameset_find(frames,
                          UVES_GUESS_ORDER_TABLE(flames, chip))
                != NULL);
                
                uves_free_table(&guess_table);
                
                check( uves_load_ordertable(
                       frames,
                       flames,
                       chip_name,
                       &guess_filename, 
                       &guess_table,
                       &guess_header,
                                       NULL,
                                       NULL,  /* Don't read order polynomial */
                       NULL,  /* Don't read fibre traces */
                       NULL, NULL,  /* oshift, yshift */
                                       NULL, NULL, /* fibre_pos,fibre_mask */
                       chip,
                       load_guess),
                   "Error loading guess order table");
                
                uves_msg("Using guess order table in '%s'", guess_filename);
                
                check( ord_predict = uves_pfits_get_ordpred(guess_header), 
                   "Could not read predicted number of orders from "
                   "guess order table header");
                        }
            else
            { 
                uves_msg("No guess table found");
            }
        }
        else
        {
            /* The user has specified the number of orders to detect.
             * The user is always right, so don't allow the
             * detection algorithm to detect fewer orders. 
             */
            norders_is_guess = false;
        }

        /* Load master bias, set pointer to NULL if not present */
        uves_free_image(&master_bias);
        uves_free_propertylist(&master_bias_header);
        if (cpl_frameset_find(frames, UVES_MASTER_BIAS(chip)) != NULL)
        {
            uves_free_image(&master_bias);
            uves_free_propertylist(&master_bias_header);
            check( uves_load_mbias(frames,
                       chip_name,
                       &master_bias_filename, &master_bias,
                       &master_bias_header, chip), 
               "Error loading master bias");
            
            uves_msg_low("Using master bias in '%s'", master_bias_filename);
	    cpl_image_subtract(raw_image[raw_index],master_bias);        

        }
        else
        {
            uves_msg_low("No master bias in SOF. Bias subtraction not done");
        }

        /* Execute macro steps */
        check((uves_free_table       (&tracetable),
           uves_polynomial_delete(&bivariate_fit),
           tracetable = uves_orderpos_process_chip(
               raw_image[raw_index], 
               raw_header[raw_index], 
               rotated_header[raw_index],
               chip, binx, biny,
               debug_mode,
               USE_GUESS_TAB,
               RADX, RADY,
               BM_METHOD,
               BACKSUBGRID,
               BACKSUBRADIUSY,
               BACKSUBKAPPA,
               BACKSUBDEGX, BACKSUBDEGY,
               SAMPLEWIDTH,
               MINSLOPE, MAXSLOPE,
               SLOPERES,
               ord_predict,
               norders_is_guess,
               PTHRES,
               TRACESTEP,
               MINTHRESH,
               MAXGAP,
               MAXRMS,
               &DEFPOL1,
               &DEFPOL2,
               KAPPA,
               &bivariate_fit,
               &norders,
                       guess_table)),
          "Error processing chip");
        
        /* Finished. Save the products */
        uves_msg("Saving products...");
        
        /* QC parameters should go here.
           Other mandatory keywords (FITS + dfs) are
           automatically added. */
        uves_free_propertylist(&product_header);
        product_header = uves_propertylist_new();

        /* Write number of detected orders */
        check( uves_pfits_set_ordpred( product_header, norders),
           "Error writing number of detected orders");

        if (flames)
        {

            check( plate_no = uves_flames_pfits_get_plateid(raw_header[raw_index]),
               "Error reading plate id");

            uves_flames_pfits_set_newplateid(product_header, plate_no);
        }

        /* Save order trace table */
        {
        samples_per_order = 
            cpl_image_get_size_x(raw_image[raw_index]) / TRACESTEP;
            
        uves_qclog_delete(&qclog[0]);
        qclog[0] = uves_qclog_init(raw_header[raw_index], chip);
        check_nomsg( uves_orderpos_qclog(tracetable,
                         flames,
                         ord_predict,
                         norders,
                         samples_per_order,
                         raw_header[raw_index],chip,qclog[0]));
        }

        if (flames) {
            /* Write polynomial in MIDAS format, as expected 
               by flames_cal_prep_sff_ofpos */

            /* Only COEFFI(6) and COEFFI(7) are used */
            snprintf(values, 80, "-1 -1 -1 -1 -1 %d %d", DEFPOL1, DEFPOL2);
            
            uves_propertylist_append_string(product_header, 
                                            "HISTORY", "'COEFFI','I*4'");
            uves_propertylist_append_string(product_header,
                                            "HISTORY", values);
            uves_propertylist_append_string(product_header,
                                            "HISTORY", " ");

            /* Polynomial coeffients */
            uves_propertylist_append_string(product_header, 
                                            "HISTORY", "'COEFFD','R*8'");
            {

                for (j = 0; j <= DEFPOL2; j++) {
                    for (i = 0; i <= DEFPOL1; i++) {
                        snprintf(values, 80, "%g",
                                 uves_polynomial_get_coeff_2d(bivariate_fit, i, j));
                        uves_propertylist_append_string(product_header,
                                                        "HISTORY", values);
                    }
                }
            }
            uves_propertylist_append_string(product_header,
                                            "HISTORY", " ");

            /* Min - max values */
            uves_propertylist_append_string(product_header, 
                                            "HISTORY", "'COEFFR','R*4'");
            snprintf(values, 80, "%g %g", 
                     cpl_table_get_column_min(tracetable, "X"),
                    cpl_table_get_column_max(tracetable, "X"));            
            uves_propertylist_append_string(product_header,
                                            "HISTORY", values);

            snprintf(values, 80, "%g %g", 
                     cpl_table_get_column_min(tracetable, "Order"),
                     cpl_table_get_column_max(tracetable, "Order"));
            uves_propertylist_append_string(product_header,
                                            "HISTORY", values);

            uves_propertylist_append_string(product_header,
                                            "HISTORY", " ");
        }
        //uves_pfits_set_extname(product_header,"Order table");
        //uves_pfits_set_extname(product_header,"Guess order table");
        sprintf(extname,"ORDER_TAB");
        uves_pfits_set_extname(table_header,extname);
        check(( cpl_free(product_filename),
            product_filename = uves_order_table_filename(chip),
            uves_frameset_insert(frames,
                     tracetable,
                     CPL_FRAME_GROUP_PRODUCT,
                     CPL_FRAME_TYPE_TABLE,
                     CPL_FRAME_LEVEL_INTERMEDIATE,
                     product_filename,
                     UVES_ORD_TAB(flames, chip),
                     raw_header[raw_index],
                     product_header,
                     table_header,
                     parameters,
                     recipe_id,
                     PACKAGE "/" PACKAGE_VERSION,
                     qclog,
                     starttime, true, 0)),
           "Could not add trace table %s to frameset", product_filename);
            uves_qclog_delete(&qclog[0]);
        uves_msg("Trace table %s added to frameset", product_filename);
        sprintf(extname,"ORDER_POL");
        uves_pfits_set_extname(table_header,extname);

        /* Save polynomial in next extension (don't add header keywords) */
        check( uves_save_polynomial(bivariate_fit, product_filename, table_header),
           "Could not write polynomial to file %s", product_filename);
        
        /* Original comment:
           For compatibility with FLAMES/UVES, create the extension defining
           that there is only one trace which has zero offset, and that 
           this trace is enabled.

           Update after the implementation of FLAMES support:
           This extension is not used by FLAMES and is therefore redundant.
           But for simplicity, keep it as it was
        */
        {

        uves_free_table(&traces);
        traces = uves_ordertable_traces_new();
        uves_ordertable_traces_add(traces, 0, 0.0, 1);
        sprintf(extname,"ORDER_INFO");
        uves_pfits_set_extname(table_header,extname);

        check( uves_table_save(traces,
                      NULL,            /* Primary header, 
                              ignored when mode is IO_EXTEND */
                      table_header,            /* Extension header               */
                      product_filename,/* This file already exists
                              (or an error occurs)           */
                      CPL_IO_EXTEND),  /* Append to existing file        */
               "Error appending table to file '%s'", product_filename);
        }
        /* Finished saving order table */
        
        if (flames) {
            /* Make two calibration frames out of the input frame */

            /* Save as integer */
            uves_free_image(&raw_image_int);
            raw_image_int = cpl_image_cast(raw_image[raw_index],
                           CPL_TYPE_INT);

            check(( cpl_free(product_filename),
                product_filename = uves_ordef_filename(chip),
                uves_frameset_insert(frames,
                         raw_image_int,
                         CPL_FRAME_GROUP_PRODUCT,
                         CPL_FRAME_TYPE_IMAGE,
                         CPL_FRAME_LEVEL_INTERMEDIATE,
                         product_filename,
                         FLAMES_ORDEF(flames, chip),
                         raw_header[raw_index],     /* raw header    */
                         rotated_header[raw_index], /* output header */
                         NULL,
                         parameters,
                         recipe_id,
                         PACKAGE "/" PACKAGE_VERSION,
                         NULL, /* No QC */
                         starttime, false,
                         CPL_STATS_MIN | CPL_STATS_MAX)),
              "Could not add raw frame %s to frameset", product_filename);

            uves_msg("Raw frame %s added to frameset", product_filename);
            uves_free_image(&raw_image_int);
        }

      if(strcmp(PROCESS_CHIP,"REDL") == 0) {
	chip = uves_chip_get_next(chip);
      }

        uves_free_propertylist(&table_header);
    } /* For each chip */

  cleanup:
     /* Raw */
     uves_free_image(&(raw_image[0]));
     uves_free_image(&(raw_image[1]));
     uves_free_image(&raw_image_int);
     uves_free_propertylist(&(raw_header[0]));
     uves_free_propertylist(&(raw_header[1]));
     uves_free_propertylist(&(rotated_header[0]));
     uves_free_propertylist(&(rotated_header[1]));

    /* Master bias */
    uves_free_image(&master_bias);
    uves_free_propertylist(&master_bias_header);

     /* Calibration */
     uves_free_propertylist(&guess_header);
     uves_free_table       (&guess_table);
     
     /* Product */
     uves_qclog_delete(&qclog[0]);
     uves_free_table       (&tracetable);
     uves_polynomial_delete(&bivariate_fit);
     uves_free_table       (&traces);
     uves_free_propertylist(&product_header);
     cpl_free(product_filename);
     
     return;
}
/**
@brief computes QC log
@param table              reference input table on which QC log is computed
@param flames             FLAMES mode?
@param pord               predicted orders
@param dord               detected orders
@param samples_per_order  number of measure points per order
@param raw_header         input raw fits header
@param qclog              ouput QC log table
*/
static void uves_orderpos_qclog(cpl_table* table,
                bool flames,
                int pord, 
                int dord,
                int samples_per_order,
                uves_propertylist* raw_header, 
                enum uves_chip chip,
                cpl_table* qclog)
{
    const char* chip_name;
    const char* grat_name;
    const char* ins_mode;
    double grat_wlen=0;

    uves_qclog_add_string(qclog,
              "QC TEST1 ID",
              flames ? 
              "Single-Fibre-Order-Definition-Results" :
              "Order-Definition-Results",
              "Name of QC test",
              "%s");

    check_nomsg( chip_name=uves_pfits_get_chip_name(raw_header,chip) );
    /* chip_name = UVES_QC_CHIP_VAL(chip); */

  uves_qclog_add_string(qclog,
                        uves_remove_string_prefix(UVES_CHIP_NAME(chip),"ESO "),
                        chip_name,
                        "Grating unique ID",
                        "%s");


  check_nomsg(grat_name=uves_pfits_get_gratname(raw_header,chip));



  uves_qclog_add_string(qclog,
                        uves_remove_string_prefix(UVES_GRATNAME(chip),"ESO "),
                        grat_name,
                        "Cross disperser ID",
                        "%s");


  check_nomsg(ins_mode=uves_pfits_get_insmode(raw_header));
  uves_qclog_add_string(qclog,
                        uves_remove_string_prefix(UVES_INSMODE,"ESO "),
                        ins_mode,
                        "Instrument mode used.",
                        "%s");


  check_nomsg(grat_wlen=uves_pfits_get_gratwlen(raw_header,chip));
  uves_qclog_add_double(qclog,
                        uves_remove_string_prefix(UVES_GRATWLEN(chip),"ESO "),
                        grat_wlen,
                        "Grating central wavelength [nm]",
                        "%.1f");

  uves_msg_debug("chip_name=%s grat_name=%s ins_mode=%s grat_wlen=%f",
         chip_name,grat_name,ins_mode,grat_wlen);

  uves_qclog_add_double(qclog,
                        "QC ORD RESIDMIN",
                        cpl_table_get_column_min(table,"Residual"),
                        "min resid in ord def",
                        "%8.4f");

  uves_qclog_add_double(qclog,
                        "QC ORD RESIDMAX",
                        cpl_table_get_column_max(table,"Residual"),
                        "max resid in ord def",
                        "%8.4f");

  uves_qclog_add_double(qclog,
                        "QC ORD RESIDAVG",
                        cpl_table_get_column_mean(table,"Residual"),
                        "mean resid in ord def",
                        "%8.4f");

  uves_qclog_add_double(qclog,
                        "QC ORD RESIDRMS",
                        cpl_table_get_column_stdev(table,"Residual"),
                        "rms resid in ord def",
                        "%8.4f");

  uves_qclog_add_int(qclog,
                        "QC ORD NPRED",
                        pord,
                        "predicted number of orders",
                        "%d");

  uves_qclog_add_int(qclog,
                        "QC ORD NDET",
                        dord,
                        "detected number of orders",
                        "%d");

  uves_qclog_add_int(qclog,
             "QC ORD NPOSALL",
             dord * samples_per_order,
             "Number of position found",
             "%d");
  
  uves_qclog_add_int(qclog,
             "QC ORD NPOSSEL",
             cpl_table_get_nrow(table),
             "Number of position selected",
             "%d");

  uves_qclog_add_int(qclog,
                        "QC ORDMIN",
                        cpl_table_get_column_min(table,"Order"),
                        "Number of position selected",
                        "%d");

  uves_qclog_add_int(qclog,
                        "QC ORDMAX",
                        cpl_table_get_column_max(table,"Order"),
                        "Number of position selected",
                        "%d");



  /* In later MIDAS versions, these were added: */

  check_nomsg(uves_qclog_add_string(qclog,
                    uves_remove_string_prefix(UVES_READ_SPEED,"ESO "),
                    uves_pfits_get_readspeed(raw_header),
                    "Readout speed",
                    "%s"));
  
  check_nomsg(uves_qclog_add_int(qclog,
                 uves_remove_string_prefix(UVES_BINX, "ESO "),
                 uves_pfits_get_binx(raw_header),
                 "Binning factor along X",
                 "%d"));
  
  check_nomsg(uves_qclog_add_int(qclog,
                 uves_remove_string_prefix(UVES_BINY, "ESO "),
                 uves_pfits_get_biny(raw_header),
                 "Binning factor along Y",
                 "%d"));
  
  check_nomsg(uves_qclog_add_string(qclog,
                    uves_remove_string_prefix(UVES_INSPATH,"ESO "),
                    uves_pfits_get_inspath(raw_header),
                    "Optical path used (h).",
                    "%s"));
    
 cleanup:
  return;

}
/**@}*/


