/* $Id: hawki_standard_process.c,v 1.38 2015/11/27 12:22:21 jim Exp $
 *
 * This file is part of the HAWKI Pipeline
 * Copyright (C) 2015 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, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * $Author: jim $
 * $Date: 2015/11/27 12:22:21 $
 * $Revision: 1.38 $
 * $Name:  $
 */

/* Includes */

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

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <libgen.h>
#include <cpl.h>
#include <math.h>

#include "hawki_utils.h"
#include "hawki_pfits.h"
#include "hawki_dfs.h"
#include "hawki_var.h"
#include "casu_utils.h"
#include "casu_mask.h"
#include "casu_mods.h"
#include "casu_fits.h"
#include "casu_tfits.h"
#include "casu_sky.h"
#include "casu_stats.h"
#include "casu_wcsutils.h"

/* Data structures */

typedef struct {
    cpl_frameset   *contrib;
    cpl_frameset   *contrib_var;
    cpl_frame      *skyframe;
    cpl_frame      *skyframe_var;
    cpl_frame      *template;
    char           fname[BUFSIZ];
    char           fname_var[BUFSIZ];
} skystruct;

typedef struct {
    cpl_frameset *current;
    cpl_frameset *current_var;
    cpl_frameset *orig;
} pawprint;

/* Structures for user supplied command line options */

/* General */

typedef struct {
    
    /* General parameters */

    int         preview_only;
    int         minphotom;
    int         prettynames;
    int         cdssearch_astrom;
    int         cdssearch_photom;
    int         savemstd;
    char        *cacheloc;
    float       magerrcut;

    /* Pawsky_mask parameters */

    int         psm_niter;
    int         psm_ipix;
    float       psm_thresh;
    int         psm_nbsize;
    float       psm_smkern;

    /* Source catalogue extraction parameters */

    int         src_cat_ipix;
    float       src_cat_thresh;
    int         src_cat_icrowd;
    float       src_cat_rcore;
    int         src_cat_nbsize;
} configstruct;

/* A structure with long term allocated memory. Very useful for 
   garbage collection */

typedef struct {

    /* Level 0 stuff */

    cpl_size         *labels;
    cpl_frame        *master_dark;
    cpl_frame        *master_twilight_flat;
    cpl_frame        *master_conf;
    casu_mask        *mask;
    cpl_frame        *phottab;
    cpl_table        *tphottab;
    cpl_frameset     *science_frames;
    cpl_frame        **product_frames_simple;
    cpl_frame        **product_frames_simple_var;
    float            *gaincors;
    char             *catpath_a;
    char             *catname_a;
    char             *catpath_p;
    char             *catname_p;
    pawprint         *scipaw;
    skystruct        *sky;
    cpl_frame        *schlf_n;
    cpl_frame        *schlf_s;
    cpl_frame        *readgain;

    /* Level 1 stuff */

    casu_fits        *fdark;
    casu_fits        *fdark_var;
    casu_fits        *fflat;
    casu_fits        *fconf;
    casu_fits        *fsky;

    int              nscience;
    casu_fits        **sci_fits;
  
} memstruct;


/* List of data products types */

enum {SIMPLE_FILE,
      SIMPLE_VAR,
      SIMPLE_CAT,
      SKY_FILE,
      SKY_FILE_VAR,
      MSTDS_ASTROM,
      MSTDS_PHOTOM
};

/* Recipe name for product headers */

#define HAWKI_RECIPENAME "hawki_standard_process"

/* Required routines for CPL/esorex */

static int hawki_standard_process_create(cpl_plugin *plugin);
static int hawki_standard_process_exec(cpl_plugin *plugin);
static int hawki_standard_process_destroy(cpl_plugin *plugin);
static int hawki_standard_process(cpl_parameterlist *parlist,
                                  cpl_frameset *framelist);

/* Pawprint manipulation routines */

static void hawki_std_paw_init(pawprint **paw, cpl_frameset *frms,
                               configstruct *cs);
static void hawki_std_paw_delete(pawprint **paws);

/* Sky related routines */

static int hawki_std_pawsky_mask(cpl_frameset *framelist, 
                                 cpl_parameterlist *parlist, configstruct *cs,
                                 cpl_frameset *contrib, 
                                 cpl_frameset *contrib_var, cpl_frame *template,
                                 char *fname, char *fname_var, 
                                 cpl_frame *master_conf,
                                 casu_mask *mask, cpl_frame **product_frame,
                                 cpl_frame **product_frame_var);

/* Save routines */

static int hawki_std_save_simple(casu_fits *obj, cpl_frameset *framelist,
                                 cpl_parameterlist *parlist, int isprod,
                                 cpl_frame *template, int isfirst,
                                 const char *tag, char *fname, char *assoc,
                                 char *photosys, cpl_frame **product_frame);
static int hawki_std_save_sky(casu_fits *outsky, cpl_frameset *framelist,
                              cpl_parameterlist *parlist, 
                              char *fname, cpl_frame *template, int isvar,
                              int isfirst, cpl_frame **product_frame);
static int hawki_std_save_cat(casu_tfits *stack, cpl_frameset *framelist,
                              cpl_parameterlist *parlist, 
                              cpl_frame *template, int fnametype, int ptype,
                              int fnumber, char *photosys,
                              char *prov, cpl_frame **product_frame);

/* Utility routines */

static cpl_frameset *hawki_std_update_frameset(cpl_frameset *frms, 
                                               configstruct *cs, int ftype);
static void hawki_std_product_name(char *template, int producttype, 
                                   int nametype, int fnumber, char *outfname);

/* Garbage collection routines */

static void hawki_std_init(memstruct *ps);
static void hawki_std_tidy(memstruct *ps, int level);

static char hawki_standard_process_description[] =
"hawki_standard_process -- HAWKI standard field recipe.\n\n"
"Process a pawprint for HAWKI standard data. Remove instrumental\n"
"signature, remove sky background, photometrically and astrometrically\n"
"calibrate each image in the pawprint individually\n\n"
"The program accepts the following files in the SOF:\n\n"
"    Tag                   Description\n"
"    -----------------------------------------------------------------------\n"
"    %-21s A list of raw standard field images \n"
"    %-21s A master dark frame\n"
"    %-21s A master twilight flat frame\n"
"    %-21s A photometric calibration table\n"
"    %-21s A master confidence map\n"
"    %-21s A master readgain table\n"
"    %-21s A master 2MASS index for astrometry or\n"
"    %-21s A master PPMXL index for astrometry or\n"
"    %-21s A master local astrometric FITS file\n"
"    %-21s A master 2MASS index for photometry or\n"
"    %-21s A master PPMXL index for photometry or\n"
"    %-21s A master local photometric FITS file\n"
"    %-21s Northern Schlegel Map\n"
"    %-21s Southern Schlegel Map\n"
"All of the above are required unless specifically tagged as optional. The"
"astrometric and photometric files are not required if these can be obtained"
"from the CDS using the --cdssearch options\n"
"\n";

/**@{*/

/**
    \ingroup recipelist
    \defgroup hawki_standard_process hawki_standard_process
    \brief Reduce a HAWKI standard sequence

    \par Name: 
        hawki_standard_process
    \par Purpose: 
        Reduce a sequence of HAWKI standard star data
    \par Description: 
        A single sequence of HAWKI standard star data in a single filter 
        is corrected for instrumental signature. The sky is removed using 
        the pawsky_mask algorithm and the objects are extracted from each
        image. The images are astrometrically and photometrically calibrated 
        using either locally held standard catalogues or information queried
        from the CDS.
    \par Language:
        C
    \par Parameters:
        General reduction parameters:
        - \b preview_only (bool): If set we only get a preview of the reduction
        - \b minphotom (int): The minimum number of standards for photometry
        - \b prettynames (bool): True if we're using nice file names
        - \b cdssearch_astrom (string): Use CDS for astrometric standards?
            - none: Use no CDS catalogues. Use local catalogues instead
            - 2mass: 2MASS PSC
            - usnob: USNOB catalogue
            - ppmxl: PPMXL catalogue
            - wise: WISE catalogue
        - \b cdssearch_photom (string): Use CDS for photometric standards?
            - none: Use no CDS catalogues. Use local catalogues instead
            - 2mass: 2MASS PSC
            - ppmxl: PPMXL catalogue
        - \b cacheloc (string): A directory where we can put the standard
             star cache
         - \b magerrcut (float): A cut in the magnitude error of the 
              photometric standards
       
        Parameters for source detection on stacks:
        - \b src_cat_ipix (int): The minimum allowable size of an object
        - \b src_cat_thresh (float): The detection threshold in sigma above sky
        - \b src_cat_icrowd (int): If set then the deblending software will be used
        - \b src_cat_rcore (float): The core radius in pixels
        - \b src_cat_nbsize (int): The smoothing box size for background map estimation
        
        Parameters for pawsky_mask algorithm:
        - \b psm_ipix (int): The minimum allowable size of an object
        - \b psm_niter (int): The number of iterations in the algorithm
        - \b psm_thresh (float): The detection threshold in sigma above sky
        - \b psm_nbsize (int): The smoothing box size for background map estimation
        - \b psm_smkern (float): The smoothing kernel in the detection
        
    \par Input File Types:
        The following list gives the file types that can appear in the SOF
        file. The word in bold is the DO category value.
        - \b STD (required): The list of input standard images.
        - \b MASTER_DARK (required): A master dark frame.
        - \b MASTER_TWILIGHT_FLAT (required): A master twilight flat frame.
        - \b PHOTCAL_TAB (required): A photometric calibration table
        - \b MASTER_READGAIN (required): A master read/gain table
        - \b MASTER_CONF (required): A master confidence map 
             for the filter used in the science images.
        - \b MASTER_2MASS_CATLAOGUE_ASTROM or \b MASTER_PPMXL_CATALOGUE_ASTROM
             or \b MASTER_LOCAL_CATALOGUE_ASTROM (required): A master standard 
             star catalogue for astrometry
        - \b MASTER_2MASS_CATLAOGUE_PHOTOM or \b MASTER_PPMXL_CATALOGUE_PHOTOM
             or \b MASTER_LOCAL_CATALOGUE_PHOTOM (required): A master standard 
             star catalogue for astrometry
        - \b SCHLEGEL_MAP_NORTH (required): The Northern Schlegel dust map
        - \b SCHLEGEL_MAP_SOUTH (required): The Southern Schlegel dust map
    \par Output Products:
        The following list gives the output data products that are generated
        by this recipe. The word in bold gives the DPR CATG keyword value for
        each product:
        - Copies of the input standard frames that have been corrected for
          instrumetal signature and sky (\b BASIC_CALIBRATED_STD)
        - Variance map for each standard frame (\b BASIC_VAR_MAP)
        - Source catalogues from the instrumentally corrected images
          (\b BASIC_CAT_STD)
        - A mean sky frame (\b MEAN_SKY) and its variance map (\b MEAN_SKY_VAR)
        - A matched standards catalogue for astrometry, if desired
          (\b MATCHSTD_ASTROM)
        - A matched standards catalogue for photometry, if desired
          (\b MATCHSTD_PHOTOM)
    \par Output QC Parameters:
        - \b SATURATION
             The saturation level in ADUs.
        - \b MEAN_SKY
             The mean level of the background sky over the image in ADUs.
        - \b SKY_NOISE
             The RMS of the background sky over the image in ADUs
        - \b IMAGE_SIZE
             The average size of stellar images on the image in pixels
        - \b ELLIPTICITY
             The average ellipticity of stellar images on the image
        - \b POSANG
             The average position angle in degrees from North towards East.
             NB: this value only makes sense if the ellipticity is significant
        - \b APERTURE_CORR
             The aperture correction for an aperture of radius rcore.
        - \b NOISE_OBJ
             The number of noise objects found in the image
        - \b MAGZPT
             The photometric zero point
        - \b MAGZERR
             The internal error in the photometric zero point
        - \b MAGNZPT
             The number of stars used to determine the magnitude zero point
        - \b MAGNCUT
             The number of stars cut from magnitude zero point calculation
        - \b SKYBRIGHT
             The sky brightness in mag/arcsec**2
        - \b LIMITING_MAG
             The limiting magnitude for this image for a 5 sigma detection.
        - \b WCS_DCRVAL1
             The offset of the equatorial coordinate represented by CRVAL1 
             from the raw frame to the reduced one (degrees).
        - \b WCS_DCRVAL2
             The offset of the equatorial coordinate represented by CRVAL2
             from the raw frame to the reduced one (degrees).
        - \b WCS_DTHETA
             The change in the WCS coordinate system rotation from the raw
             to the reduced frame (degrees)
        - \b WCS_SCALE
             The scale of the pixels in the reduced frames in arcseconds per
             pixel
        - \b WCS_SHEAR
             The shear of the astrometric solution in the form of the 
             difference between the rotation of the x solution and the rotation
             of the y solution (abs(xrot) - abs(yrot) in degrees)
        - \b WCS_RMS
             The average error in the WCS fit (arcsec)
    \par Notes
        None.
    \par Fatal Error Conditions:
        - NULL input frameset
        - Input frameset headers incorrect meaning that RAW and CALIB frame
          can't be distinguished
        - No standard frames in the input frameset
        - Required master calibration images and tables are missing
        - Unable to save data products
    \par Non-Fatal Error Conditions:
        - None
    \par Conditions Leading To Dummy Products:
        - Master calibration images either won't load or are flagged as dummy
        - The detector for the current image extension is flagged dead in
          all science frames
        - Various processing routines fail.
    \par Author:
        Jim Lewis, CASU
    \par Code Reference: 
        hawki_standard_process.c
*/

/* Function code */

/*---------------------------------------------------------------------------*/
/**
  @brief    Build the list of available plugins, for this module.
  @param    list    the plugin list
  @return   0 if everything is ok

  This function is exported.
 */
/*---------------------------------------------------------------------------*/

int cpl_plugin_get_info(cpl_pluginlist *list) {
    cpl_recipe  *recipe = cpl_calloc(1,sizeof(*recipe));
    cpl_plugin  *plugin = &recipe->interface;
    char alldesc[SZ_ALLDESC];
    (void)snprintf(alldesc,SZ_ALLDESC,
                   hawki_standard_process_description,
                   HAWKI_STD_OBJECT_RAW,HAWKI_CAL_DARK,HAWKI_CAL_TWILIGHT_FLAT,
                   HAWKI_CAL_PHOTTAB,HAWKI_CAL_CONF,HAWKI_CAL_READGAIN,
                   HAWKI_CAL_2MASS_A,HAWKI_CAL_PPMXL_A,HAWKI_CAL_LOCCAT_A,
                   HAWKI_CAL_2MASS_P,HAWKI_CAL_PPMXL_P,HAWKI_CAL_LOCCAT_P,
                   HAWKI_CAL_SCHL_N,HAWKI_CAL_SCHL_S);

    cpl_plugin_init(plugin,
                    CPL_PLUGIN_API,
                    HAWKI_BINARY_VERSION,
                    CPL_PLUGIN_TYPE_RECIPE,
                    "hawki_standard_process",
                    "HAWKI standard field recipe",
                    alldesc,
                    "Jim Lewis",
                    "jrl@ast.cam.ac.uk",
                    hawki_get_license(),
                    hawki_standard_process_create,
                    hawki_standard_process_exec,
                    hawki_standard_process_destroy);

    cpl_pluginlist_append(list,plugin);

    return(0);
}

/*---------------------------------------------------------------------------*/
/**
  @brief    Setup the recipe options
  @param    plugin  the plugin
  @return   0 if everything is ok

  Create the recipe instance and make it available to the application using the
  interface.
 */
/*---------------------------------------------------------------------------*/

static int hawki_standard_process_create(cpl_plugin *plugin) {
    cpl_recipe      *recipe;
    cpl_parameter   *p;

    /* Get the recipe out of the plugin */

    if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
        recipe = (cpl_recipe *)plugin;
    else 
        return(-1);

    /* Create the parameters list in the cpl_recipe object */

    recipe->parameters = cpl_parameterlist_new();

    /* Fill in flag to just print out how the data will be processed and 
       then exit */

    p = cpl_parameter_new_value("hawki.hawki_standard_process.preview_only",
                                CPL_TYPE_BOOL,"Preview only?",
                                "hawki.hawki_standard_process",FALSE);
    cpl_parameter_set_alias(p,CPL_PARAMETER_MODE_CLI,"preview_only");
    cpl_parameterlist_append(recipe->parameters,p);

    /* Fill in flag to just print out how the data will be processed and 
       then exit */

    p = cpl_parameter_new_range("hawki.hawki_standard_process.minphotom",
                                CPL_TYPE_INT,
                                "Minimum stars for photometry solution",
                                "hawki.hawki_standard_process",1,1,
                                100000);
    cpl_parameter_set_alias(p,CPL_PARAMETER_MODE_CLI,"minphotom");
    cpl_parameterlist_append(recipe->parameters,p);

    /* Fill in flag to decide whether use predictable names or nice
       names based on input file names */

    p = cpl_parameter_new_value("hawki.hawki_standard_process.prettynames",
                                CPL_TYPE_BOOL,"Use pretty product names?",
                                "hawki.hawki_standard_process",FALSE);
    cpl_parameter_set_alias(p,CPL_PARAMETER_MODE_CLI,"prettynames");
    cpl_parameterlist_append(recipe->parameters,p);

    /* Fill in flag to use save the matched standard catalogues */

    p = cpl_parameter_new_value("hawki.hawki_standard_process.savemstd",
                                CPL_TYPE_BOOL,
                                "Save matched standard catalogues?",
                                "hawki.hawki_standard_process",FALSE);
    cpl_parameter_set_alias(p,CPL_PARAMETER_MODE_CLI,"savemstd");
    cpl_parameterlist_append(recipe->parameters,p);

    /* Flag to decide how we get the astrometric  standard star information. 
       If zero, then use the local catalogues specified in the sof. If not 
       zero, then use one of the selection of catalogues available from CDS */

    p = cpl_parameter_new_enum("hawki.hawki_standard_process.cdssearch_astrom",
                               CPL_TYPE_STRING,
                               "CDS astrometric catalogue",
                               "hawki.hawki_standard_process",
                               "none",5,"none","2mass","usnob","ppmxl","wise");
    cpl_parameter_set_alias(p,CPL_PARAMETER_MODE_CLI,"cdssearch_astrom");
    cpl_parameterlist_append(recipe->parameters,p);

    /* Same for photometric standards */

    p = cpl_parameter_new_enum("hawki.hawki_standard_process.cdssearch_photom",
                               CPL_TYPE_STRING,
                               "CDS photometric catalogue",
                               "hawki.hawki_standard_process",
                               "none",3,"none","2mass","ppmxl");
    cpl_parameter_set_alias(p,CPL_PARAMETER_MODE_CLI,"cdssearch_photom");
    cpl_parameterlist_append(recipe->parameters,p);

    /* ***Special parameters for object detection *** */

    /* Fill in the minimum object size */

    p = cpl_parameter_new_range("hawki.hawki_standard_process.src_cat_ipix",
                                CPL_TYPE_INT,
                                "Minimum pixel area for each detected object",
                                "hawki.hawki_standard_process",10,1,100000);
    cpl_parameter_set_alias(p,CPL_PARAMETER_MODE_CLI,"cat_ipix");
    cpl_parameterlist_append(recipe->parameters,p);

    /* Fill in the detection threshold parameter */

    p = cpl_parameter_new_range("hawki.hawki_standard_process.src_cat_thresh",
                                CPL_TYPE_DOUBLE,
                                "Detection threshold in sigma above sky",
                                "hawki.hawki_standard_process",1.5,1.0e-6,
                                1.0e10);
    cpl_parameter_set_alias(p,CPL_PARAMETER_MODE_CLI,"cat_thresh");
    cpl_parameterlist_append(recipe->parameters,p);

    /* Fill in flag to use deblending software or not */

    p = cpl_parameter_new_value("hawki.hawki_standard_process.src_cat_icrowd",
                                CPL_TYPE_BOOL,"Use deblending?",
                                "hawki.hawki_standard_process",TRUE);
    cpl_parameter_set_alias(p,CPL_PARAMETER_MODE_CLI,"cat_icrowd");
    cpl_parameterlist_append(recipe->parameters,p);

    /* Fill in core radius */

    p = cpl_parameter_new_range("hawki.hawki_standard_process.src_cat_rcore",
                                CPL_TYPE_DOUBLE,"Value of Rcore in pixels",
                                "hawki.hawki_standard_process",10.0,1.0e-6,
                                1024.0);
    cpl_parameter_set_alias(p,CPL_PARAMETER_MODE_CLI,"cat_rcore");
    cpl_parameterlist_append(recipe->parameters,p);

    /* Fill in background smoothing box size */

    p = cpl_parameter_new_range("hawki.hawki_standard_process.src_cat_nbsize",
                                CPL_TYPE_INT,"Background smoothing box size",
                                "hawki.hawki_standard_process",128,1,2048);
    cpl_parameter_set_alias(p,CPL_PARAMETER_MODE_CLI,"cat_nbsize");
    cpl_parameterlist_append(recipe->parameters,p);

    /* ***Special parameters for pawsky_mask algorithm*** */

    /* The pixel area for detected objects */

    p = cpl_parameter_new_range("hawki.hawki_standard_process.psm_ipix",
                                CPL_TYPE_INT,
                                "Minimum pixel area for each detected object",
                                "hawki.hawki_standard_process",10,1,100000);
    cpl_parameter_set_alias(p,CPL_PARAMETER_MODE_CLI,"psm_ipix");
    cpl_parameterlist_append(recipe->parameters,p);

    /* Number of iterations */

    p = cpl_parameter_new_range("hawki.hawki_standard_process.psm_niter",
                                CPL_TYPE_INT,
                                "Number of iterations in pawsky mask",
                                "hawki.hawki_standard_process",5,1,10);
    cpl_parameter_set_alias(p,CPL_PARAMETER_MODE_CLI,"psm_niter");
    cpl_parameterlist_append(recipe->parameters,p);

    /* Detection threshold */

    p = cpl_parameter_new_range("hawki.hawki_standard_process.psm_thresh",
                                CPL_TYPE_DOUBLE,
                                "Detection threshold in sigma above sky",
                                "hawki.hawki_standard_process",1.5,1.0e-6,
                                1.0e10);
    cpl_parameter_set_alias(p,CPL_PARAMETER_MODE_CLI,"psm_thresh");
    cpl_parameterlist_append(recipe->parameters,p);
        
    /* Background smoothing box size */

    p = cpl_parameter_new_range("hawki.hawki_standard_process.psm_nbsize",
                                CPL_TYPE_INT,"Background smoothing box size",
                                "hawki.hawki_standard_process",256,1,2048);
    cpl_parameter_set_alias(p,CPL_PARAMETER_MODE_CLI,"psm_nbsize");
    cpl_parameterlist_append(recipe->parameters,p);

    /* Smoothing kernel size */

    p = cpl_parameter_new_range("hawki.hawki_standard_process.psm_smkern",
                                CPL_TYPE_DOUBLE,
                                "Smoothing kernel size (pixels)",
                                "hawki.hawki_standard_process",2.0,1.0e-6,5.0);
    cpl_parameter_set_alias(p,CPL_PARAMETER_MODE_CLI,"psm_smkern");
    cpl_parameterlist_append(recipe->parameters,p);

    /* Location for standard star cache */

    p = cpl_parameter_new_value("hawki.hawki_standard_process.cacheloc",
                                CPL_TYPE_STRING,
                                "Location for standard star cache",
                                "hawki.hawki_standard_process",".");
    cpl_parameter_set_alias(p,CPL_PARAMETER_MODE_CLI,"cacheloc");
    cpl_parameterlist_append(recipe->parameters,p);
        
    /* Magnitude errror cut */

    p = cpl_parameter_new_value("hawki.hawki_standard_process.magerrcut",
                                CPL_TYPE_DOUBLE,
                                "Magnitude error cut",
                                "hawki.hawki_standard_process",100.0);
    cpl_parameter_set_alias(p,CPL_PARAMETER_MODE_CLI,"magerrcut");
    cpl_parameterlist_append(recipe->parameters,p);        

    /* Get out of here */

    return(0);
}
    
/*---------------------------------------------------------------------------*/
/**
  @brief    Execute the plugin instance given by the interface
  @param    plugin  the plugin
  @return   0 if everything is ok
 */
/*---------------------------------------------------------------------------*/

static int hawki_standard_process_exec(cpl_plugin *plugin) {
    cpl_recipe  *recipe;

    /* Get the recipe out of the plugin */

    if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
        recipe = (cpl_recipe *)plugin;
    else 
        return(-1);

    return(hawki_standard_process(recipe->parameters,recipe->frames));
}
                                
/*---------------------------------------------------------------------------*/
/**
  @brief    Destroy what has been created by the 'create' function
  @param    plugin  the plugin
  @return   0 if everything is ok
 */
/*---------------------------------------------------------------------------*/

static int hawki_standard_process_destroy(cpl_plugin *plugin) {
    cpl_recipe *recipe ;

    /* Get the recipe out of the plugin */

    if (cpl_plugin_get_type(plugin) == CPL_PLUGIN_TYPE_RECIPE)
        recipe = (cpl_recipe *)plugin;
    else 
        return(-1);

    cpl_parameterlist_delete(recipe->parameters);
    return(0);
}

/*---------------------------------------------------------------------------*/
/**
  @brief    The recipe data reduction part is implemented here 
  @param    parlist     the parameters list
  @param    framelist   the frames list
  @return   0 if everything is ok
 */
/*---------------------------------------------------------------------------*/

static int hawki_standard_process(cpl_parameterlist *parlist,
                                  cpl_frameset *framelist) {
    const char *fctid="hawki_standard_process";
    cpl_parameter *p;
    int nfail,status,ndit,j,live,isfirst,nsci,njsteps,n,npts,k,i;
    int match,ngood;
    cpl_size nlab;
    float gaincor_fac,dit,*sdata,med,sig,gain,readnoise;
    cpl_frame *catindex_a,*template,*frm,*curframe,*product_frame,*product_cat;
    cpl_frame *curvar,*product_frame_var,*catindex_p,*product_mstd_a;
    cpl_frame *product_mstd_p;
    cpl_propertylist *pp;
    cpl_table *stdscat,*tab;
    casu_fits *ff,*ffv,*sky_extns[HAWKI_NEXTN],*fconf[HAWKI_NEXTN];
    casu_fits *curfits[HAWKI_NEXTN],*sky_extns_var[HAWKI_NEXTN];
    casu_fits *curfits_var[HAWKI_NEXTN];
    casu_tfits *outtab[HAWKI_NEXTN],*matchstd_a[HAWKI_NEXTN];
    casu_tfits *mstds[HAWKI_NEXTN];
    cpl_image *img,*simg;
    char filt[16],projid[16],*fname,tmpname[BUFSIZ],tmpname_var[BUFSIZ];
    char *junk1,*junk2,*pcat,*assoc,photosys[8];
    unsigned char *bpm;
    configstruct cs;
    memstruct ps;
    double abmagsat[HAWKI_NEXTN],abmaglim[HAWKI_NEXTN],sum1,sum2;

    /* Check validity of the input frameset */

    if (framelist == NULL || cpl_frameset_get_size(framelist) <= 0) {
        cpl_msg_error(fctid,"Input framelist NULL or has no input data");
        return(-1);
    }

    /* Initialise some things */

    hawki_std_init(&ps);

    /* Get the parameters */

    p = cpl_parameterlist_find(parlist,
                               "hawki.hawki_standard_process.preview_only");
    cs.preview_only = cpl_parameter_get_bool(p);
    p = cpl_parameterlist_find(parlist,
                               "hawki.hawki_standard_process.minphotom");
    cs.minphotom = cpl_parameter_get_int(p);
    p = cpl_parameterlist_find(parlist,
                               "hawki.hawki_standard_process.prettynames");
    cs.prettynames = (cpl_parameter_get_bool(p) ? 1 : 0);
    p = cpl_parameterlist_find(parlist,
                               "hawki.hawki_standard_process.savemstd");
    cs.savemstd = cpl_parameter_get_bool(p);
    p = cpl_parameterlist_find(parlist,
                               "hawki.hawki_standard_process.cdssearch_astrom");
    if (! strcmp(cpl_parameter_get_string(p),"none")) 
        cs.cdssearch_astrom = 0;
    else if (! strcmp(cpl_parameter_get_string(p),"2mass")) 
        cs.cdssearch_astrom = 1;
    else if (! strcmp(cpl_parameter_get_string(p),"usnob")) 
        cs.cdssearch_astrom = 2;
    else if (! strcmp(cpl_parameter_get_string(p),"ppmxl")) 
        cs.cdssearch_astrom = 3;
    else if (! strcmp(cpl_parameter_get_string(p),"wise")) 
        cs.cdssearch_astrom = 5;
    else
        cs.cdssearch_astrom = 0; /* Fallback is to use none */

    p = cpl_parameterlist_find(parlist,
                               "hawki.hawki_standard_process.cdssearch_photom");
    if (! strcmp(cpl_parameter_get_string(p),"none")) 
        cs.cdssearch_photom = 0;
    else if (! strcmp(cpl_parameter_get_string(p),"2mass")) 
        cs.cdssearch_photom = 1;
    else if (! strcmp(cpl_parameter_get_string(p),"ppmxl")) 
        cs.cdssearch_photom = 3;
    else
        cs.cdssearch_photom = 0; /* Fallback is to use none */


    /* Object detection special parameters */

    p = cpl_parameterlist_find(parlist,
                               "hawki.hawki_standard_process.src_cat_ipix");
    cs.src_cat_ipix = cpl_parameter_get_int(p);
    p = cpl_parameterlist_find(parlist,
                               "hawki.hawki_standard_process.src_cat_thresh");
    cs.src_cat_thresh = (float)cpl_parameter_get_double(p);
    p = cpl_parameterlist_find(parlist,
                               "hawki.hawki_standard_process.src_cat_icrowd");
    cs.src_cat_icrowd = cpl_parameter_get_bool(p);
    p = cpl_parameterlist_find(parlist,
                               "hawki.hawki_standard_process.src_cat_rcore");
    cs.src_cat_rcore = (float)cpl_parameter_get_double(p);
    p = cpl_parameterlist_find(parlist,
                               "hawki.hawki_standard_process.src_cat_nbsize");
    cs.src_cat_nbsize = cpl_parameter_get_int(p);

    /* Pawsky_mask special parameters */

    p = cpl_parameterlist_find(parlist,
                               "hawki.hawki_standard_process.psm_ipix");
    cs.psm_ipix = cpl_parameter_get_int(p);
    p = cpl_parameterlist_find(parlist,
                               "hawki.hawki_standard_process.psm_niter");
    cs.psm_niter = cpl_parameter_get_int(p);
    p = cpl_parameterlist_find(parlist,
                               "hawki.hawki_standard_process.psm_thresh");
    cs.psm_thresh = cpl_parameter_get_double(p);
    p = cpl_parameterlist_find(parlist,
                               "hawki.hawki_standard_process.psm_nbsize");
    cs.psm_nbsize = cpl_parameter_get_int(p);
    p = cpl_parameterlist_find(parlist,
                               "hawki.hawki_standard_process.psm_smkern");
    cs.psm_smkern = cpl_parameter_get_double(p);

    /* Cache location */

    p = cpl_parameterlist_find(parlist,
                               "hawki.hawki_standard_process.cacheloc");
    cs.cacheloc = (char *)cpl_parameter_get_string(p);

    /* Magnitude error cut */

    p = cpl_parameterlist_find(parlist,
                               "hawki.hawki_standard_process.magerrcut");
    cs.magerrcut = (float)cpl_parameter_get_double(p);

    /* Sort out raw from calib frames */

    if (hawki_dfs_set_groups(framelist) != CASU_OK) {
        cpl_msg_error(fctid,"Cannot identify RAW and CALIB frames");
        hawki_std_tidy(&ps,0);
        return(-1);
    }

    /* Label the input frames */

    if ((ps.labels = cpl_frameset_labelise(framelist,casu_compare_tags,
                                           &nlab)) == NULL) {
        cpl_msg_error(fctid,"Cannot labelise the input frames");
        hawki_std_tidy(&ps,0);
        return(-1);
    }

    /* Get the input science frames */

    nfail = 0;
    if ((ps.science_frames = 
         casu_frameset_subgroup(framelist,ps.labels,nlab,
                                HAWKI_STD_OBJECT_RAW)) == NULL) {
        cpl_msg_error(fctid,"No standard images to process!");
        hawki_std_tidy(&ps,0);
        return(-1);
    }
    nfail += hawki_testfrms(ps.science_frames,HAWKI_NEXTN,1,1);

    /* Check to see if there is a master dark frame */

    if ((ps.master_dark = 
         casu_frameset_subgroup_1(framelist,ps.labels,nlab,
                                  HAWKI_CAL_DARK)) == NULL) {
        cpl_msg_error(fctid,"No master dark found");
        hawki_std_tidy(&ps,0);
        return(-1);
    }
    nfail += hawki_testfrm_1(ps.master_dark,HAWKI_NEXTN,1,0);
        
    /* Check to see if there is a master twilight flat frame */

    if ((ps.master_twilight_flat = 
         casu_frameset_subgroup_1(framelist,ps.labels,nlab,
                                  HAWKI_CAL_TWILIGHT_FLAT)) == NULL) {
        cpl_msg_error(fctid,"No master twilight flat found");
        hawki_std_tidy(&ps,0);
        return(-1);
    }
    nfail += hawki_testfrm_1(ps.master_twilight_flat,HAWKI_NEXTN,1,0);

    /* Get the gain corrections */

    status = CASU_OK;
    if (casu_gaincor_calc(ps.master_twilight_flat,&i,&(ps.gaincors),
                          &status) != CASU_OK) {
        cpl_msg_error(fctid,"Error calculating gain corrections");
        hawki_std_tidy(&ps,0);
        return(-1);
    }

    /* Check to see if there is a master confidence map. */

    if ((ps.master_conf = 
        casu_frameset_subgroup_1(framelist,ps.labels,nlab,
                                 HAWKI_CAL_CONF)) == NULL) {
        cpl_msg_error(fctid,"No master confidence map file found");
        hawki_std_tidy(&ps,0);
        return(-1);
    }
    nfail += hawki_testfrm_1(ps.master_conf,HAWKI_NEXTN,1,0);
    ps.mask = casu_mask_define(framelist,ps.labels,nlab,HAWKI_CAL_CONF,
                               "NONE");
        
    /* Check to see if there is a master read/gain table */

    if ((ps.readgain = 
        casu_frameset_subgroup_1(framelist,ps.labels,nlab,
                                 HAWKI_CAL_READGAIN)) == NULL) {
        cpl_msg_error(fctid,"No master readgain table file found");
        hawki_std_tidy(&ps,0);
        return(-1);
    }
    nfail += hawki_testrdgn(ps.master_conf,ps.readgain);

    /* Is the 2mass/ppmxl index file specified for astrom? */

    catindex_a = NULL;
    if (cs.cdssearch_astrom == 0) {
        if ((catindex_a = casu_frameset_subgroup_1(framelist,ps.labels,nlab,
                                                   HAWKI_CAL_2MASS_A)) == NULL) {
            if ((catindex_a = casu_frameset_subgroup_1(framelist,ps.labels,nlab,
                                                       HAWKI_CAL_PPMXL_A)) == NULL) {
                if ((catindex_a = casu_frameset_subgroup_1(framelist,ps.labels,
                                                           nlab,HAWKI_CAL_LOCCAT_A)) == NULL) {
        
                    cpl_msg_error(fctid,
                                  "No astrometric standard catalogue found -- cannot continue");
                    hawki_std_tidy(&ps,0);
                    return(-1);
                }
            }
        }
        nfail += hawki_testfrm_1(catindex_a,1,0,0);
    } else {
        (void)casu_getstds_cdslist(cs.cdssearch_astrom,&junk1,&junk2,&status);
        freespace(junk1);
        freespace(junk2);
        if (status != CASU_OK) {
            status = CASU_OK;
            nfail++;
        }
    }

    /* Is the 2mass/ppmxl index file specified for photom? */

    catindex_p = NULL;
    if (cs.cdssearch_photom == 0) {
        if ((catindex_p = casu_frameset_subgroup_1(framelist,ps.labels,nlab,
                                                   HAWKI_CAL_2MASS_P)) == NULL) {
            if ((catindex_p = casu_frameset_subgroup_1(framelist,ps.labels,nlab,
                                                       HAWKI_CAL_PPMXL_P)) == NULL) {
                if ((catindex_p = casu_frameset_subgroup_1(framelist,ps.labels,
                                                           nlab,HAWKI_CAL_LOCCAT_P)) == NULL) {
        
                    cpl_msg_error(fctid,
                                  "No photometric standard catalogue found -- cannot continue");
                    hawki_std_tidy(&ps,0);
                    return(-1);
                }
            }
        }
        nfail += hawki_testfrm_1(catindex_p,1,0,0);
        pp = cpl_propertylist_load(cpl_frame_get_filename(catindex_p),1);
        if (cpl_propertylist_has(pp,"EXTNAME")) {
            pcat = cpl_strdup(cpl_propertylist_get_string(pp,"EXTNAME"));
        } else {
            cpl_msg_error(fctid,"No extension name in %s",
                          cpl_frame_get_filename(catindex_p));
            nfail++;
        }
        cpl_propertylist_delete(pp);
    } else {
        (void)casu_getstds_cdslist(cs.cdssearch_photom,&pcat,&junk2,&status);
        freespace(junk2);
        if (status != CASU_OK) {
            status = CASU_OK;
            nfail++;
        }
    }

    /* Check to see if there is a photometric table */

    if ((ps.phottab = casu_frameset_subgroup_1(framelist,ps.labels,nlab,
                                               HAWKI_CAL_PHOTTAB)) == NULL) {
        cpl_msg_error(fctid,"No photometric table found");
        hawki_std_tidy(&ps,0);
        return(-1);
    }
    n = cpl_frame_get_nextensions(ps.phottab);
    nfail += hawki_testfrm_1(ps.phottab,n,0,0);

    /* Check the photometric table to see if it contains information about the
       photometric source we want to use */

    match = 0;
    for (i = 1; i <= n; i++) {
        pp = cpl_propertylist_load(cpl_frame_get_filename(ps.phottab),i);
        if (cpl_propertylist_has(pp,"EXTNAME") &&
            strcmp(pcat,cpl_propertylist_get_string(pp,"EXTNAME")) == 0) {
            if (cpl_propertylist_has(pp,"PHOTOSYS")) {
                strncpy(photosys,cpl_propertylist_get_string(pp,"PHOTOSYS"),8-1);
                photosys[8-1] = '\0';
            } else {
                cpl_msg_error(fctid,
                              "Photcal table missing PHOTOSYS information in extension %d",i);
                cpl_propertylist_delete(pp);
                break;
            }
            match = 1;
            ps.tphottab = cpl_table_load(cpl_frame_get_filename(ps.phottab),
                                         i,0);
            cpl_propertylist_delete(pp);
            break;
        }
        cpl_propertylist_delete(pp);
    }
    if (! match) {
        cpl_msg_error(fctid,"Photcal table has no information on %s",pcat);
        nfail++;
    }        
    freespace(pcat);

    /* Is the Northern Schlegel file specified? */

    if ((ps.schlf_n = casu_frameset_subgroup_1(framelist,ps.labels,nlab,
                                               HAWKI_CAL_SCHL_N)) == NULL) {
        cpl_msg_error(fctid,"Schlegel North map not found -- cannot continue");
        hawki_std_tidy(&ps,0);
        return(-1);
    }
    nfail += hawki_testfrm_1(ps.schlf_n,0,1,0);

    /* Is the Southern Schlegel file specified? */

    if ((ps.schlf_s = casu_frameset_subgroup_1(framelist,ps.labels,nlab,
                                               HAWKI_CAL_SCHL_S)) == NULL) {
        cpl_msg_error(fctid,"Schlegel South map not found -- cannot continue");
        hawki_std_tidy(&ps,0);
        return(-1);
    }
    nfail += hawki_testfrm_1(ps.schlf_s,0,1,0);

    /* Check the cache location is writable */

    if (access(cs.cacheloc,R_OK+W_OK+X_OK) != 0) {
        cpl_msg_error(fctid,"Cache location %s inacessible",cs.cacheloc);
        nfail++;
    }

    /* Ok if any of this failed, then get out of here. This makes it a bit 
       simpler later on since we now know that all of the specified files 
       have the correct number of extensions and will load properly */

    if (nfail > 0) {
        cpl_msg_error(fctid,
                      "There are %" CPL_SIZE_FORMAT " input file errors -- cannot continue",
                      (cpl_size)nfail);
        hawki_std_tidy(&ps,0);
        return(-1);
    }

    /* Get the number of DITs. This assumes it is the same for all the science
       images (and it damn well better be!) */

    pp = cpl_propertylist_load(cpl_frame_get_filename(cpl_frameset_get_position(ps.science_frames,0)),0);
    if (hawki_pfits_get_ndit(pp,&ndit) != CASU_OK) {
        cpl_msg_error(fctid,"No value for NDIT available");
        freepropertylist(pp);
        hawki_std_tidy(&ps,0);
        return(-1);
    }
    cpl_propertylist_delete(pp);

    /* Get catalogue parameters */

    if (cs.cdssearch_astrom == 0) {
        if (casu_catpars(catindex_a,&(ps.catpath_a),
                         &(ps.catname_a)) == CASU_FATAL) {
            hawki_std_tidy(&ps,0);
            cpl_frame_delete(catindex_a);
            return(-1);
        }
        cpl_frame_delete(catindex_a);
    } else {
        ps.catpath_a = NULL;
        ps.catname_a = NULL;
    }
    if (cs.cdssearch_photom == 0) {
        if (casu_catpars(catindex_p,&(ps.catpath_p),
                         &(ps.catname_p)) == CASU_FATAL) {
            hawki_std_tidy(&ps,0);
            cpl_frame_delete(catindex_p);
            return(-1);
        }
        cpl_frame_delete(catindex_p);
    } else {
        ps.catpath_p = NULL;
        ps.catname_p = NULL;
    }

    /* Define the pawprint now */

    hawki_std_paw_init(&(ps.scipaw),ps.science_frames,&cs);

    /* Define the sky frame */

    ps.sky = cpl_malloc(sizeof(skystruct));
    ps.sky->contrib = cpl_frameset_duplicate(ps.scipaw->current);
    ps.sky->contrib_var = cpl_frameset_duplicate(ps.scipaw->current_var);
    ps.sky->template = cpl_frame_duplicate(cpl_frameset_get_position(ps.scipaw->orig,
                                                                  0));
    ps.sky->skyframe = NULL;
    ps.sky->skyframe_var = NULL;
    hawki_std_product_name((char *)cpl_frame_get_filename(ps.sky->template),
                           SKY_FILE,cs.prettynames,1,ps.sky->fname);
    hawki_std_product_name((char *)cpl_frame_get_filename(ps.sky->template),
                           SKY_FILE_VAR,cs.prettynames,1,ps.sky->fname_var);

    /* Print out some stuff if you want a preview */

    if (cs.preview_only) {
        fprintf(stdout,"This is a paw group with %" CPL_SIZE_FORMAT " files\n",
                cpl_frameset_get_size(ps.science_frames));
        frm = cpl_frameset_get_position(ps.science_frames,0);
        pp = cpl_propertylist_load(cpl_frame_get_filename(frm),0);
        (void)hawki_pfits_get_filter(pp,filt);
        (void)hawki_pfits_get_ndit(pp,&ndit);
        (void)hawki_pfits_get_dit(pp,&dit);
        (void)hawki_pfits_get_projid(pp,projid);
        cpl_propertylist_delete(pp);
        njsteps = cpl_frameset_get_size(ps.science_frames);

        fprintf(stdout,"Filter: %8s, DIT: %8.1f, NDIT: %" CPL_SIZE_FORMAT ", Project: %s\n",
                filt,dit,(cpl_size)ndit,projid);
        fprintf(stdout,"Dark: %s\n",cpl_frame_get_filename(ps.master_dark));
        fprintf(stdout,"Flat: %s\n",
                cpl_frame_get_filename(ps.master_twilight_flat));
        fprintf(stdout,"CPM: %s\n",cpl_frame_get_filename(ps.master_conf));
        fprintf(stdout,"The %" CPL_SIZE_FORMAT " files are:\n",
                (cpl_size)njsteps);
        n = cpl_frameset_get_size(ps.scipaw->orig);
        for (j = 0; j < n; j++) {
            frm = cpl_frameset_get_position(ps.scipaw->orig,j);
            fprintf(stdout,"%s\n",cpl_frame_get_filename(frm));
        }
        if (cs.cdssearch_astrom != 0) {
            p = cpl_parameterlist_find(parlist,
                                       "hawki.hawki_standard_process.cdssearch_astrom");
            fprintf(stdout,"Astrometry will be done using CDS catalogue: %s\n",
                    cpl_parameter_get_string(p));
        } else {
            fprintf(stdout,"Astrometry will be done using local catalogue %s in %s\n",
                    ps.catname_a,ps.catpath_a);
        }
        if (cs.cdssearch_photom != 0) {
            p = cpl_parameterlist_find(parlist,
                                       "hawki.hawki_standard_process.cdssearch_photom");
            fprintf(stdout,"Photometry will be done using CDS catalogue: %s\n",
                    cpl_parameter_get_string(p));
        } else {
            fprintf(stdout,"Photometry will be done using local catalogue %s in %s\n",
                    ps.catname_p,ps.catpath_p);
        }
        hawki_std_tidy(&ps,0);
        return(0);
    }

    /* Get some workspace for the product frames */

    nsci = cpl_frameset_get_size(ps.science_frames);
    ps.product_frames_simple = cpl_malloc(nsci*sizeof(cpl_frame *));
    ps.product_frames_simple_var = cpl_malloc(nsci*sizeof(cpl_frame *));
    for (i = 0; i < nsci; i++) {
        ps.product_frames_simple[i] = NULL;
        ps.product_frames_simple_var[i] = NULL;
    }

    /* Do the stage1 processing. Loop for all extensions */

    cpl_msg_info(fctid,"Beginning stage1 reduction");
    cpl_msg_indent_more();
    for (j = 1; j <= HAWKI_NEXTN; j++) {
        isfirst = (j == 1);
        gaincor_fac = (ps.gaincors)[j-1];
        cpl_msg_info(fctid,"Extension [%" CPL_SIZE_FORMAT "]",(cpl_size)j);
        
        /* Load up the calibration frames. We know these won't fail now
           so we can skip all the boring testing... */
        
        ps.fdark = casu_fits_load(ps.master_dark,CPL_TYPE_FLOAT,j);
        ps.fflat = casu_fits_load(ps.master_twilight_flat,CPL_TYPE_FLOAT,j);
        ps.fconf = casu_fits_load(ps.master_conf,CPL_TYPE_INT,j);

        /* Load the mask */

        casu_mask_load(ps.mask,j,
                       (int)cpl_image_get_size_x(casu_fits_get_image(ps.fconf)),
                       (int)cpl_image_get_size_y(casu_fits_get_image(ps.fconf)));

        /* Get the readnoise and gain */

        hawki_getrdgn(ps.readgain,casu_fits_get_extname(ps.fflat),
                      &readnoise,&gain);
        ps.fdark_var = hawki_var_create(ps.fdark,ps.mask,readnoise,gain);

        /* Now load up the science frames */
        
        ps.nscience = cpl_frameset_get_size(ps.science_frames);
        ps.sci_fits = casu_fits_load_list(ps.science_frames,CPL_TYPE_FLOAT,j);
           
        /* Loop through and mark the frames where the header says the detector
           wasn't live.  */

        for (i = 0; i < ps.nscience; i++) {
            ff = ps.sci_fits[i];
            hawki_pfits_get_detlive(casu_fits_get_ehu(ff),&live);
            if (! live) 
                casu_fits_set_error(ff,CASU_FATAL);
        }

        /* Do the corrections now. First for the object frames. Write
           out intermediate versions as we go */
       
        assoc = NULL;
        for (i = 0; i < ps.nscience; i++) {
            ff = ps.sci_fits[i];
            ffv = hawki_var_create(ff,ps.mask,readnoise,gain);
            if (casu_fits_get_status(ff) == CASU_FATAL) {
                cpl_msg_info(fctid,"Detector is flagged dead in %s",
                             casu_fits_get_fullname(ff));
            } else {
                hawki_updatewcs(casu_fits_get_ehu(ff));
                status = CASU_OK;
                (void)casu_darkcor(ff,ps.fdark,1.0,&status);
                hawki_var_add(ffv,ps.fdark_var);
                (void)casu_flatcor(ff,ps.fflat,&status);
                hawki_var_div_im(ffv,ps.fflat);
                (void)casu_gaincor(ff,gaincor_fac,&status);
                hawki_var_divk(ffv,gaincor_fac);                
                casu_fits_set_error(ff,status);
            }
            cpl_propertylist_update_float(casu_fits_get_ehu(ff),"READNOIS",
                                          readnoise);
            cpl_propertylist_set_comment(casu_fits_get_ehu(ff),"READNOIS",
                                         "[ADU] Read noise used in reduction");
            cpl_propertylist_update_float(casu_fits_get_ehu(ff),"GAIN",
                                          gain);
            cpl_propertylist_set_comment(casu_fits_get_ehu(ff),"GAIN",
                                         "[e-/ADU] gain used in reduction");
            frm = cpl_frameset_get_position(ps.scipaw->current,i);
            fname = (char *)cpl_frame_get_filename(frm);
            hawki_std_save_simple(ff,framelist,parlist,0,
                                  cpl_frameset_get_position(ps.science_frames,i),
                                  isfirst,HAWKI_PRO_SIMPLE_STD,fname,assoc,
                                  photosys,ps.product_frames_simple+i);

            /* Save the variance array */

            frm = cpl_frameset_get_position(ps.scipaw->current_var,i);
            fname = (char *)cpl_frame_get_filename(frm);
            hawki_std_save_simple(ffv,framelist,parlist,0,
                                  cpl_frameset_get_position(ps.science_frames,i),
                                  isfirst,HAWKI_PRO_VAR_SCI,fname,assoc,
                                  photosys,ps.product_frames_simple_var+i);
            freefits(ffv);
        }

        /* Intermediate tidy */

        hawki_std_tidy(&ps,1);
    }
    
    /* Only delete the product frames for the science images because what
       we've just saved isn't a product yet. */

    for (i = 0; i < nsci; i++) 
        freeframe(ps.product_frames_simple[i]);
    for (i = 0; i < nsci; i++) 
        freeframe(ps.product_frames_simple_var[i]);
    cpl_msg_indent_less();

    /* Form the sky frames. These are saved and registered as products */

    cpl_msg_info(fctid,"Making sky");
    if (hawki_std_pawsky_mask(framelist,parlist,&cs,ps.sky->contrib,
                              ps.sky->contrib_var,ps.sky->template,
                              ps.sky->fname,ps.sky->fname_var,ps.master_conf,
                              ps.mask,&(ps.sky->skyframe),
                              &(ps.sky->skyframe_var)) != CASU_OK) {
        cpl_msg_error(fctid,"Error making sky");
        hawki_std_tidy(&ps,0);
        return(-1);
    }

    /* Do sky subtraction, wcsfit and write them out */

    cpl_msg_info(fctid,"Beginning stage2 reduction");
    
    /* Load up the sky extensions and normalise the data arrays
       to a zero median. Also load up the confidence map */

    npts = HAWKI_NX*HAWKI_NY;
    bpm = cpl_calloc(npts,sizeof(unsigned char));
    for (i = 1; i <= HAWKI_NEXTN; i++) {
        casu_mask_load(ps.mask,i,0,0);
        memmove(bpm,casu_mask_get_data(ps.mask),npts*sizeof(unsigned char));
        sky_extns[i-1] = casu_fits_load(ps.sky->skyframe,CPL_TYPE_FLOAT,i);
        sky_extns_var[i-1] = casu_fits_load(ps.sky->skyframe_var,CPL_TYPE_FLOAT,
                                            i);
        if (casu_is_dummy(casu_fits_get_ehu(sky_extns[i-1]))) {
            casu_fits_set_status(sky_extns[i-1],CASU_FATAL);
            cpl_msg_warning(fctid,"Extension %" CPL_SIZE_FORMAT " sky is a dummy",
                              (cpl_size)i);
        } else {
            img = casu_fits_get_image(sky_extns[i-1]);
            sdata = cpl_image_get_data_float(img);
            casu_qmedsig(sdata,bpm,npts,5.0,3,-1000.0,65535.0,&med,&sig);
            for (k = 0; k < npts; k++)
                sdata[k] -= med;
        }
    }
    freespace(bpm);

    /* Loop for each image */

    for (j = 0; j < nsci; j++) {
        curframe = cpl_frameset_get_position(ps.scipaw->current,j);
        curvar = cpl_frameset_get_position(ps.scipaw->current_var,j);
        cpl_msg_info(fctid,"Beginning work on %s",
                     cpl_frame_get_filename(curframe));
        template = cpl_frameset_get_position(ps.scipaw->orig,j);

        /* Loop for all the extensions. First do the sky subtraction,
           then generate a catalogue. Get a photometric standard catalogue
           and match it to the source catalogue */

        for (i = 1; i <= HAWKI_NEXTN; i++) {
            curfits[i-1] = casu_fits_load(curframe,CPL_TYPE_FLOAT,i);
            curfits_var[i-1] = casu_fits_load(curvar,CPL_TYPE_FLOAT,i);
            fconf[i-1] = casu_fits_load(ps.master_conf,CPL_TYPE_INT,i);
            if (casu_is_dummy(casu_fits_get_ehu(curfits[i-1]))) {
                casu_fits_set_status(curfits[i-1],CASU_FATAL);
                cpl_msg_warning(fctid,"Extension %" CPL_SIZE_FORMAT " is a dummy",
                                (cpl_size)i);
                mstds[i-1] = NULL;
                tab = casu_dummy_catalogue(6);
                outtab[i-1] = casu_tfits_wrap(tab,NULL,
                                              cpl_propertylist_duplicate(casu_fits_get_phu(curfits[i-1])),
                                              cpl_propertylist_duplicate(casu_fits_get_ehu(curfits[i-1])));
                casu_tfits_set_status(outtab[i-1],CASU_FATAL);
            } else if (casu_fits_get_status(sky_extns[i-1]) != CASU_OK) {
                casu_fits_set_status(curfits[i-1],CASU_FATAL);
                cpl_msg_warning(fctid,"Extension %" CPL_SIZE_FORMAT " has a dummy sky",
                                (cpl_size)i);
                mstds[i-1] = NULL;
                tab = casu_dummy_catalogue(6);
                outtab[i-1] = casu_tfits_wrap(tab,NULL,
                                              cpl_propertylist_duplicate(casu_fits_get_phu(curfits[i-1])),
                                              cpl_propertylist_duplicate(casu_fits_get_ehu(curfits[i-1])));
                casu_tfits_set_status(outtab[i-1],CASU_FATAL);
            } else {
                img = casu_fits_get_image(curfits[i-1]);
                simg = casu_fits_get_image(sky_extns[i-1]);
                cpl_propertylist_update_string(casu_fits_get_ehu(curfits[i-1]),
                                               "ESO DRS SKYCOR",
                                               casu_fits_get_fullname(sky_extns[i-1]));
                                               
                cpl_image_subtract(img,(const cpl_image *)simg);
                hawki_var_add(curfits_var[i-1],sky_extns_var[i-1]);
                status = CASU_OK;
                hawki_pfits_get_gain(casu_fits_get_ehu(curfits[i-1]),&gain);
                casu_imcore(curfits[i-1],fconf[i-1],cs.src_cat_ipix,
                            cs.src_cat_thresh,cs.src_cat_icrowd,
                            cs.src_cat_rcore,cs.src_cat_nbsize,6,3.0,
                            &(outtab[i-1]),gain,&status);
                (void)casu_getstds(casu_fits_get_ehu(curfits[i-1]),
                                   1,ps.catpath_p,ps.catname_p,
                                   cs.cdssearch_photom,cs.cacheloc,&stdscat,
                                   &status);
                (void)casu_matchstds(casu_tfits_get_table(outtab[i-1]),
                                     stdscat,300.0,&tab,&status);
                mstds[i-1] = casu_tfits_wrap(tab,outtab[i-1],NULL,NULL);
                freetable(stdscat);
            }
            matchstd_a[i-1] = NULL;
        }

        /* Now fit the WCS */

        hawki_wcsfit_multi(curfits,outtab,ps.catname_a,ps.catpath_a,
                           cs.cdssearch_astrom,cs.cacheloc,cs.savemstd,
                           matchstd_a);
            
        /* Now do the photometric calibration */

        hawki_pfits_get_filter(casu_fits_get_phu(curfits[0]),filt);
        casu_photcal_extinct(curfits,mstds,outtab,HAWKI_NEXTN,filt,ps.tphottab,
                             cs.minphotom,ps.schlf_n,ps.schlf_s,
                             "EXPTIME","ESO TEL AIRM START",cs.magerrcut,
                             &status);

        /* Fill in some NULL values */

        ngood = 0;
        sum1 = 0.0;
        sum2 = 0.0;
        for (i = 1; i <= HAWKI_NEXTN; i++) {
            pp = casu_fits_get_ehu(curfits[i-1]);
            if (cpl_propertylist_has(pp,"ABMAGSAT")) 
                abmagsat[i-1] = cpl_propertylist_get_double(pp,"ABMAGSAT");
            else
                abmagsat[i-1] = 0.0;
            sum1 += abmagsat[i-1];
            if (abmagsat[i-1] != 0.0)
                ngood++;
            if (cpl_propertylist_has(pp,"ABMAGLIM")) 
                abmaglim[i-1] = cpl_propertylist_get_double(pp,"ABMAGLIM");
            else
                abmaglim[i-1] = 0.0;
            sum2 += abmaglim[i-1];
        }
        if (ngood > 0) {
            sum1 /= (double)ngood;
            sum2 /= (double)ngood;
            for (i = 1; i <= HAWKI_NEXTN; i++) {
                pp = casu_fits_get_ehu(curfits[i-1]);
                if (abmagsat[i-1] == 0.0)
                    cpl_propertylist_update_double(pp,"ABMAGSAT",sum1);
                if (abmaglim[i-1] == 0.0)
                    cpl_propertylist_update_double(pp,"ABMAGLIM",sum2);
            }
        }
        
        /* Save the image */

        (void)snprintf(tmpname,BUFSIZ,"tmp_%s",
                       cpl_frame_get_filename(curframe));
        for (i = 1; i <= HAWKI_NEXTN; i++) {
            hawki_copywcs(casu_fits_get_ehu(curfits[i-1]),
                          casu_fits_get_ehu(curfits_var[i-1]));
            isfirst = (i == 1);
            assoc = cpl_strdup(casu_fits_get_filename(curfits_var[i-1]));
            hawki_std_save_simple(curfits[i-1],framelist,parlist,1,template,
                                  isfirst,HAWKI_PRO_SIMPLE_STD,tmpname,assoc,
                                  photosys,&product_frame);
            freefits(curfits[i-1]);
            freefits(fconf[i-1]);
            freespace(assoc);
        }
        remove(cpl_frame_get_filename(curframe));
        cpl_frame_set_filename(product_frame,cpl_frame_get_filename(curframe));
        rename(tmpname,cpl_frame_get_filename(curframe));

        /* Save the variance array */

        (void)snprintf(tmpname_var,BUFSIZ,"tmp_%s",
                       cpl_frame_get_filename(curvar));
        for (i = 1; i <= HAWKI_NEXTN; i++) {
            isfirst = (i == 1);
            hawki_std_save_simple(curfits_var[i-1],framelist,parlist,1,template,
                                  isfirst,HAWKI_PRO_VAR_SCI,tmpname_var,assoc,
                                  photosys,&product_frame_var);
            freefits(curfits_var[i-1]);
        }
        remove(cpl_frame_get_filename(curvar));
        cpl_frame_set_filename(product_frame_var,
                               cpl_frame_get_filename(curvar));
        rename(tmpname_var,cpl_frame_get_filename(curvar));

        /* Now the source catalogue */
        
        product_cat = NULL;
        for (i = 1; i <= HAWKI_NEXTN; i++) { 
            hawki_std_save_cat(outtab[i-1],framelist,parlist,template,
                               cs.prettynames,SIMPLE_CAT,j+1,photosys,
                               (char *)cpl_frame_get_filename(curframe),
                               &product_cat);
            freetfits(outtab[i-1]);
        }                               

        /* Now the matched standards catalogues, if requested */

        if (cs.savemstd) {
            product_mstd_a = NULL;
            for (i = 1; i <= HAWKI_NEXTN; i++) 
                hawki_std_save_cat(matchstd_a[i-1],framelist,parlist,template,
                                   cs.prettynames,MSTDS_ASTROM,j+1,
                                   photosys,NULL,&product_mstd_a);
            product_mstd_p = NULL;
            for (i = 1; i <= HAWKI_NEXTN; i++)  
                hawki_std_save_cat(mstds[i-1],framelist,parlist,template,
                                   cs.prettynames,MSTDS_PHOTOM,j+1,
                                   photosys,NULL,&product_mstd_p);
        }                       
     
        /* Tidy up */

        for (i = 0; i < HAWKI_NEXTN; i++) {
            freetfits(mstds[i]);
            freetfits(matchstd_a[i]);
        }
    }

    /* Now get rid of the sky and confidence maps and do a general tidy*/

    for (i = 0; i < HAWKI_NEXTN; i++) {
        freefits(sky_extns[i]);
        freefits(sky_extns_var[i]);
    }
    hawki_std_paw_delete(&(ps.scipaw));
    cpl_msg_indent_less();

    /* Final cleanup */

    hawki_std_tidy(&ps,0);
    return(0);
}

/*=========================== Support Routines ==============================*/

/*=========================== Pawprint Structure Routines ===================*/

/*---------------------------------------------------------------------------*/
/**
    \par Name:
        hawki_std_paw_delete
    \par Purpose:
        Routine to free up workspace in a pawprint structure
    \par Description:
        Free up workspace in a pawprint structure.
    \par Language:
        C
    \param paw
        The input pawprint structure.
    \return
        Nothing
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

static void hawki_std_paw_delete(pawprint **paw) {

    if (*paw == NULL)
        return;
    freeframeset((*paw)->orig);
    freeframeset((*paw)->current);
    freeframeset((*paw)->current_var);
    freespace(*paw);
}

/*---------------------------------------------------------------------------*/
/**
    \par Name:
        hawki_std_paw_init
    \par Purpose:
        Routine to set up a pawprint structure 
    \par Description:
        A pawprint structure is set up to contain the information of the
        input data pawprint
    \par Language:
        C
    \param paw
        The input pawprint structure.
    \param frms
        The frameset containing the input pawprint
    \param cs
        The configuration structure defined in the main routine
    \return
        Nothing
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

static void hawki_std_paw_init(pawprint **paw, cpl_frameset *frms, 
                               configstruct *cs) {

    /* Initialise everything. Start with all the frames and framesets */

    *paw = cpl_malloc(sizeof(pawprint));
    (*paw)->orig = cpl_frameset_duplicate(frms);
    (*paw)->current = hawki_std_update_frameset((*paw)->orig,cs,SIMPLE_FILE);
    (*paw)->current_var = hawki_std_update_frameset((*paw)->orig,cs,
                                                    SIMPLE_VAR);

}

/*=========================== Sky Routines ==================================*/

/*---------------------------------------------------------------------------*/
/**
    \par Name:
        hawki_std_pawsky_mask
    \par Purpose:
        Driver routine for the pawsky_mask and pawsky_mask_pre algorithms
    \par Description:
        For each extension a set of images is combined using either the
        pawksy_mask or pawsky_mask_pre algorithm. Each extension is saved 
        in turn.
    \par Language:
        C
    \param framelist
        The frameset with the SOF input frames
    \param parlist
        The parameter list with the input command line parameters
    \param cs
        The configuration structure defined in the main routine
    \param contrib
        The frameset that contains all the frames that will contribute to 
        the sky image
    \param contrib_var
        The frameset that contains all the variances that will contribute to 
        the sky variance
    \param template
        A template frame used in the save routine
    \param fname
        A name for the output sky file
    \param fname_var
        A name for the output sky variance file
    \param master_conf
        The master confidence map for the contributing frameset
    \param mask
        The bad pixel mask derived from the master confidence map
    \param product_frame
        The output frame for the sky data product
    \param product_frame_var
        The output frame for the sky variance product
    \retval CASU_OK if all went well
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/
    
static int hawki_std_pawsky_mask(cpl_frameset *framelist, 
                                 cpl_parameterlist *parlist, configstruct *cs,
                                 cpl_frameset *contrib, 
                                 cpl_frameset *contrib_var, cpl_frame *template,
                                 char *fname, char *fname_var, 
                                 cpl_frame *master_conf, casu_mask *mask, 
                                 cpl_frame **product_frame,
                                 cpl_frame **product_frame_var) {
    int j,nfiles,status,isfirst,k;
    const char *fctid="hawki_std_pawsky_mask";
    casu_fits **infits,*conf,*skyout,**infits_var,*skyout_var;

    /* Output product frame */

    *product_frame = NULL;

    /* Loop for each extension */

    nfiles = cpl_frameset_get_size(contrib);
    cpl_msg_info(fctid,"Creating sky %s",fname);
    cpl_msg_indent_more();
    for (j = 1; j <= HAWKI_NEXTN; j++) {
        cpl_msg_info(fctid,"Extension [%" CPL_SIZE_FORMAT "]",(cpl_size)j);
        isfirst = (j == 1);
        
        /* Load up input images for this extension + confidence map
           and associated bad pixel mask... */

        infits = casu_fits_load_list(contrib,CPL_TYPE_FLOAT,j);
        for (k = 0; k < nfiles; k++) {
            if (casu_is_dummy(casu_fits_get_ehu(infits[k]))) 
                casu_fits_set_status(infits[k],CASU_FATAL);
        }
        conf = casu_fits_load(master_conf,CPL_TYPE_INT,j);
        casu_mask_load(mask,j,
                       (int)cpl_image_get_size_x(casu_fits_get_image(conf)),
                       (int)cpl_image_get_size_y(casu_fits_get_image(conf)));
        infits_var = casu_fits_load_list(contrib_var,CPL_TYPE_FLOAT,j);

        /* Form the sky for this extension */

        status = CASU_OK;
        skyout = NULL;
        skyout_var = NULL;
        (void)casu_pawsky_mask(infits,infits_var,nfiles,conf,mask,&skyout,
                               &skyout_var,cs->psm_niter,cs->psm_ipix,
                               cs->psm_thresh,cs->psm_nbsize,cs->psm_smkern,
                               &status);

        /* Save the sky frame */

        if (hawki_std_save_sky(skyout,framelist,parlist,fname,template,
                               0,isfirst,product_frame) != CASU_OK) {
            freefitslist(infits,nfiles); 
            freefitslist(infits_var,nfiles); 
            freefits(conf);
            freefits(skyout);
            freefits(skyout_var);
            return(CASU_FATAL);
        }

        /* Save the sky variance frame */

        if (hawki_std_save_sky(skyout_var,framelist,parlist,fname_var,template,
                               1,isfirst,product_frame_var) != CASU_OK) {
            freefitslist(infits,nfiles); 
            freefitslist(infits_var,nfiles); 
            freefits(conf);
            freefits(skyout);
            freefits(skyout_var);
            return(CASU_FATAL);
        }

        /* Tidy up and move on to the next one */

        freefitslist(infits,nfiles);
        freefitslist(infits_var,nfiles);
        freefits(conf);
        freefits(skyout);
        freefits(skyout_var);
    }
    cpl_msg_indent_less();
    return(CASU_OK);
}                

/*=========================== Save Routines =================================*/

/*---------------------------------------------------------------------------*/
/**
    \par Name:
        hawki_std_save_simple
    \par Purpose:
        Save the simple image products
    \par Description:
        An extension of a simple image product is saved here
    \par Language:
        C
    \param obj
        The casu_fits structure for the image to be saved
    \param framelist
        The input recipe frameset
    \param parlist
        The input recipe parameter list
    \param isprod
        Set of this frame is to be an output data product
    \param template
        A template to be used in the cpl saving routine
    \param isfirst
        Set of this is the first extension to be written to the file
    \param tag
        The product tag for the saved file
    \param fname
        The output file name
    \param photosys
        The photometric system
    \param assoc
        A list of associated file names for Phase III headers
    \param product_frame
        The output product frame
    \retval CASU_OK if everything is ok
    \retval CASU_FATAL if something failed
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

static int hawki_std_save_simple(casu_fits *obj, cpl_frameset *framelist,
                                 cpl_parameterlist *parlist, int isprod,
                                 cpl_frame *template, int isfirst,
                                 const char *tag, char *fname, char *assoc,
                                 char *photosys, cpl_frame **product_frame) {
    cpl_propertylist *plist;
    int isdummy;
    char filt[16];
    int ndit = 1;
    float dit,texp,fwhm;
    double val,ext;
    const char *fctid = "hawki_std_save_simple";

    /* Get some information about this guy */

    isdummy = (casu_fits_get_status(obj) != CASU_OK);

    /*Get ndit as it is also used in the secondary headers */
    plist = casu_fits_get_phu(obj);
    hawki_pfits_get_ndit(plist,&ndit); /*Get ndit as it is also used in the
    secondary headers for zeropint correction of the telescope */

    /* If we need to make a PHU then do that now based on the first frame
       in the input frame list. Get rid of the file if it already exists */

    if (isfirst) {
        if (access(fname,F_OK)) 
            remove(fname);

        /* Create a new product frame object and define some tags */

        *product_frame = cpl_frame_new();
        cpl_frame_set_filename(*product_frame,fname);

        /* Tag the product */

        cpl_frame_set_tag(*product_frame,tag);
        cpl_frame_set_type(*product_frame,CPL_FRAME_TYPE_IMAGE);
        cpl_frame_set_group(*product_frame,CPL_FRAME_GROUP_PRODUCT);
        cpl_frame_set_level(*product_frame,CPL_FRAME_LEVEL_FINAL);

        /* Set up the PHU header */

        plist = casu_fits_get_phu(obj);
        hawki_dfs_set_product_primary_header(plist,*product_frame,framelist,
                                             parlist,HAWKI_RECIPENAME,
                                             "PRO-1.15",template,1);

        /* Set some PhaseIII parameters */

        cpl_propertylist_update_string(plist,"RADECSYS","ICRS");
        cpl_propertylist_update_string(plist,"ORIGIN","ESO-PARANAL");
        cpl_propertylist_set_comment(plist,"ORIGIN",
                                     "European Southern Observatory");
        cpl_propertylist_update_string(plist,"TELESCOP","ESO-VLT-U4");
        cpl_propertylist_set_comment(plist,"TELESCOP","ESO telescope name");
        cpl_propertylist_update_string(plist,"INSTRUME","HAWKI");
        cpl_propertylist_set_comment(plist,"INSTRUME","Instrument used");
        cpl_propertylist_update_string(plist,"PRODCATG","SCIENCE.MEFIMAGE");
        cpl_propertylist_set_comment(plist,"PRODCATG","Data product category");
        cpl_propertylist_update_string(plist,"OBSTECH","IMAGE");
        cpl_propertylist_set_comment(plist,"OBSTECH","Observation Technique");
        cpl_propertylist_update_string(plist,"IMATYPE","PAWPRINT");
        cpl_propertylist_update_bool(plist,"ISAMP",1);
        cpl_propertylist_set_comment(plist,"ISAMP",
                                     "TRUE if image represents partially sampled sky");
        cpl_propertylist_update_bool(plist,"SINGLEXP",1);
        cpl_propertylist_set_comment(plist,"SINGLEXP",
                                     "TRUE if resulting from a single exposure");
        cpl_propertylist_update_string(plist,"PROV1",
                                       cpl_propertylist_get_string(plist,"ARCFILE"));
        cpl_propertylist_set_comment(plist,"PROV1","Originating raw science file");     
        cpl_propertylist_update_int(plist,"NCOMBINE",1);
        cpl_propertylist_set_comment(plist,"NCOMBINE","Number of input images");
        if (isprod && strcmp(tag,HAWKI_PRO_VAR_SCI) != 0) {
            cpl_propertylist_update_string(plist,"ASSON1",assoc);
            cpl_propertylist_set_comment(plist,"ASSON1","Associated file");
            cpl_propertylist_update_string(plist,"ASSOC1","ANCILLARY.VARMAP");
            cpl_propertylist_set_comment(plist,"ASSOC1","Associated file category");
        } else {
            cpl_propertylist_erase(plist,"PRODCATG");
            cpl_propertylist_erase(plist,"ASSON1");
            cpl_propertylist_erase(plist,"ASSOC1");
        }
        hawki_pfits_get_filter(plist,filt);
        cpl_propertylist_update_string(plist,"FILTER",filt);
        cpl_propertylist_set_comment(plist,"FILTER","Filter used in observation"); 
        if (cpl_propertylist_has(plist,"FILTER1")) 
            cpl_propertylist_erase(plist,"FILTER1");
        if (cpl_propertylist_has(plist,"FILTER2")) 
            cpl_propertylist_erase(plist,"FILTER2");
        hawki_pfits_get_dit(plist,&dit);
        texp = (float)ndit*dit;
        cpl_propertylist_update_double(plist,"EFF_EXPT",(double)texp);
        cpl_propertylist_update_double(plist,"EXPTIME",(double)texp);
        cpl_propertylist_update_double(plist,"TEXPTIME",texp);
        cpl_propertylist_update_double(plist,"MJD-END",
                                       cpl_propertylist_get_double(plist,"MJD-OBS") +
                                       texp/86400.0);
        cpl_propertylist_set_comment(plist,"MJD-END","End of observations");
        cpl_propertylist_update_string(plist,"PROG_ID",
                                       cpl_propertylist_get_string(plist,"ESO OBS PROG ID"));
        cpl_propertylist_set_comment(plist,"PROG_ID","ESO programme identification");
        cpl_propertylist_update_int(plist,"OBID1",
                                    cpl_propertylist_get_int(plist,"ESO OBS ID"));
        cpl_propertylist_set_comment(plist,"OBID1","Observation block ID");
        cpl_propertylist_update_bool(plist,"M_EPOCH",0);
        cpl_propertylist_set_comment(plist,"M_EPOCH",
                                     "TRUE if resulting from multiple epochs");
        cpl_propertylist_update_string(plist,"REFERENC","");
        cpl_propertylist_set_comment(plist,"REFERENC","Bibliographic Reference");
        if (cpl_propertylist_has(casu_fits_get_ehu(obj),"ZPFUDGED")) {
            if (cpl_propertylist_get_bool(casu_fits_get_ehu(obj),"ZPFUDGED")) {
                cpl_propertylist_update_string(plist,"FLUXCAL","UNCALIBRATED");
            } else {
                cpl_propertylist_update_string(plist,"FLUXCAL","ABSOLUTE");
            }
        } else {
            cpl_propertylist_update_string(plist,"FLUXCAL","UNCALIBRATED");
        }
        cpl_propertylist_set_comment(plist,"FLUXCAL","Certifies the validity of PHOTZP");
        cpl_propertylist_update_double(plist,"DIT",
                                       cpl_propertylist_get_double(plist,"ESO DET DIT"));
        cpl_propertylist_set_comment(plist,"DIT","Detector integration time");

        /* 'Save' the PHU image */                         

        if (cpl_image_save(NULL,fname,CPL_TYPE_UCHAR,plist,CPL_IO_DEFAULT) != 
            CPL_ERROR_NONE) {
            cpl_msg_error(fctid,"Cannot save product PHU");
            cpl_frame_delete(*product_frame);
            return(CASU_FATAL);
        }
        if (isprod) 
            cpl_frameset_insert(framelist,*product_frame);
    }

    /* Get the extension property list */

    plist = casu_fits_get_ehu(obj);
    if (isdummy)
        casu_dummy_property(plist);

    /* Add some PhaseIII stuff */

    cpl_propertylist_update_string(plist,"BUNIT","ADU");
    cpl_propertylist_set_comment(plist,"BUNIT","Physical unit of array values");
    cpl_propertylist_update_string(plist,"PHOTSYS",photosys);
    cpl_propertylist_set_comment(plist,"PHOTSYS","Photometric System");
    if (isprod && strcmp(tag,HAWKI_PRO_VAR_SCI) != 0) {
        val = cpl_propertylist_get_double(plist,"ESO DRS ZPIM1");
        cpl_propertylist_update_double(plist,"ESO QC AXT0",val);
        cpl_propertylist_set_comment(plist,"ESO QC AXT0",
                                     "[mag] Ext corrected zeropoint");
        if (val > 0 && ndit > 0) {
            cpl_propertylist_update_double(plist,"ESO QC AXT0 TEL",
                            val+ 2.5 * log10((double)ndit));
            cpl_propertylist_set_comment(plist,"ESO QC AXT0 TEL",
                                         "[mag] Ext corrected tel zeropoint");
        } else {
            cpl_propertylist_update_double(plist,"ESO QC AXT0 TEL", 0.);
            cpl_propertylist_set_comment(plist,"ESO QC AXT0 TEL",
                                         "[mag] Ext corrected tel zeropoint");
        }
        ext = cpl_propertylist_get_double(plist,"ESO DRS EXTINCT");
        val -= ext;
        cpl_propertylist_update_double(plist,"ESO QC ZPOINT",val);
        cpl_propertylist_set_comment(plist,"ESO QC ZPOINT",
                                     "[mag] Measured zeropoint without ext");
        if (val > 0 && ndit > 0) {
            cpl_propertylist_update_double(plist,"ESO QC ZPOINT TEL",
                            val + 2.5 * log10((double)ndit));
            cpl_propertylist_set_comment(plist,"ESO QC ZPOINT TEL",
                                         "[mag] Measured Telescope ZP without ext");
        } else {
            cpl_propertylist_update_double(plist,"ESO QC ZPOINT TEL",0.);
            cpl_propertylist_set_comment(plist,"ESO QC ZPOINT TEL",
                                         "[mag] Measured Telescope ZP without ext");
        }
        fwhm = cpl_propertylist_get_float(plist,"ESO DRS IMAGE_SIZE");
        cpl_propertylist_update_double(plist,"ESO QC ZPOINT FWHM_AS",fwhm);
        cpl_propertylist_set_comment(plist,"ESO QC ZPOINT FWHM_AS",
                                     "[arcsec] Median FWHM of stars");
    }

    /* Fiddle with the header now */

    hawki_dfs_set_product_exten_header(plist,*product_frame,framelist,parlist,
                                       HAWKI_RECIPENAME,"PRO-1.15",template);
    if (cpl_image_save(casu_fits_get_image(obj),fname,CPL_TYPE_FLOAT,plist,
                       CPL_IO_EXTEND) != CPL_ERROR_NONE) {
        cpl_msg_error(fctid,"Cannot save product image extension -- %s",
                      cpl_error_get_message());
        return(CASU_FATAL);
    }

    /* Get out of here */

    return(CASU_OK);
}

/*---------------------------------------------------------------------------*/
/**
    \par Name:
        hawki_std_save_cat
    \par Purpose:
        Save an extension of source catalogue data product
    \par Description:
        An extension of a stacked image source catalogue data product is 
        saved here. 
    \par Language:
        C
    \param stack
        The casu_tfits structure for the  source catalogue to be saved
    \param framelist
        The input recipe frameset
    \param parlist
        The input recipe parameter list
    \param template
        A template to be used in the cpl saving routine
    \param fnametype
        The type of file name. This could either be 0 -> ESO 'predictable'
        names, 1 -> pretty names based on input file names, 2 -> temporary
        file names.   
    \param fnumber
        The number of the product to be used if the ESO name convention is
        chosen.
    \param photosys
        The photometric system
    \param prov
        The filename for the provenance
    \param product_frame
        The output product frame
    \retval CASU_OK if everything is ok
    \retval CASU_FATAL if something failed
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

static int hawki_std_save_cat(casu_tfits *stack, cpl_frameset *framelist,
                              cpl_parameterlist *parlist, 
                              cpl_frame *template, int fnametype, int ptype,
                              int fnumber, char *photosys, char *prov,
                              cpl_frame **product_frame) {
    cpl_propertylist *plist;
    int isfirst,isdummy;
    char bname[BUFSIZ],*base,*tname;
    const char *fctid = "hawki_std_save_cat";

    /* Get some information about this guy */

    isdummy = (casu_tfits_get_status(stack) != CASU_OK);
    isfirst = (*product_frame == NULL);
    tname = cpl_strdup(cpl_frame_get_filename(template));
    base = basename(tname);
    hawki_std_product_name(base,ptype,fnametype,fnumber,bname);
    freespace(tname);

    /* If we need to make a PHU then do that now based on the first frame
       in the input frame list. Get rid of the file if it already exists */

    if (isfirst) {
        if (access(bname,F_OK)) 
            remove(bname);

        /* Create a new product frame object and define some tags */

        *product_frame = cpl_frame_new();
        cpl_frame_set_filename(*product_frame,bname);

        /* Tag the product */

        switch (ptype) {
        case SIMPLE_CAT:
            cpl_frame_set_tag(*product_frame,HAWKI_PRO_SIMPLE_CAT);
            break;
        case MSTDS_ASTROM:
            cpl_frame_set_tag(*product_frame,HAWKI_PRO_MATCHSTD_ASTROM);
            break;
        case MSTDS_PHOTOM:
            cpl_frame_set_tag(*product_frame,HAWKI_PRO_MATCHSTD_PHOTOM);
            break;
        }
        cpl_frame_set_type(*product_frame,CPL_FRAME_TYPE_TABLE);
        cpl_frame_set_group(*product_frame,CPL_FRAME_GROUP_PRODUCT);
        cpl_frame_set_level(*product_frame,CPL_FRAME_LEVEL_FINAL);

        /* Set up the PHU header */

        plist = casu_tfits_get_phu(stack);
        hawki_dfs_set_product_primary_header(plist,*product_frame,framelist,
                                             parlist,HAWKI_RECIPENAME,
                                             "PRO-1.15",NULL,0);

        cpl_propertylist_update_string(plist,"RADECSYS","ICRS");
        cpl_propertylist_update_string(plist,"ORIGIN","ESO-PARANAL");
        cpl_propertylist_set_comment(plist,"ORIGIN",
                                     "European Southern Observatory");
        cpl_propertylist_update_string(plist,"TELESCOP","ESO-VLT-U4");
        cpl_propertylist_set_comment(plist,"TELESCOP","ESO telescope name");
        cpl_propertylist_update_string(plist,"INSTRUME","HAWKI");
        cpl_propertylist_set_comment(plist,"INSTRUME","Instrument used");
        cpl_propertylist_update_string(plist,"PRODCATG","SCIENCE.SRCTBL");
        cpl_propertylist_set_comment(plist,"PRODCATG","Data product category");
        cpl_propertylist_update_string(plist,"OBSTECH","IMAGE");
        cpl_propertylist_set_comment(plist,"OBSTECH","Observation Technique");
         cpl_propertylist_update_string(plist,"PROG_ID",
                                       cpl_propertylist_get_string(plist,"ESO OBS PROG ID"));
        cpl_propertylist_set_comment(plist,"PROG_ID","ESO programme identification");
        cpl_propertylist_update_int(plist,"OBID1",
                                    cpl_propertylist_get_int(plist,"ESO OBS ID"));
        cpl_propertylist_set_comment(plist,"OBID1","Observation block ID");
        cpl_propertylist_set_bool(plist,"M_EPOCH",0);
        cpl_propertylist_set_comment(plist,"M_EPOCH",
                                     "TRUE if resulting from multiple epochs");
        cpl_propertylist_update_string(plist,"REFERENC","");
        cpl_propertylist_set_comment(plist,"REFERENC","Bibliographic Reference");
        cpl_propertylist_erase(plist,"ASSON1");
        cpl_propertylist_erase(plist,"ASSOC1");
        if (prov != NULL) 
            cpl_propertylist_update_string(plist,"PROV1",prov);

        /* 'Save' the PHU image */                         

        if (cpl_image_save(NULL,bname,CPL_TYPE_UCHAR,plist,
                           CPL_IO_DEFAULT) != CPL_ERROR_NONE) {
            cpl_msg_error(fctid,"Cannot save product PHU");
            cpl_frame_delete(*product_frame);
            return(CASU_FATAL);
        }
        cpl_frameset_insert(framelist,*product_frame);
    }

    /* Get the extension property list */

    plist = casu_tfits_get_ehu(stack);
    if (isdummy)
        casu_dummy_property(plist);

    /* Fiddle with the header now */

    cpl_propertylist_update_string(plist,"PHOTSYS",photosys);
    cpl_propertylist_set_comment(plist,"PHOTSYS","Photometric System");
    cpl_propertylist_erase(plist,"BUNIT");
    hawki_dfs_set_product_exten_header(plist,*product_frame,framelist,
                                       parlist,HAWKI_RECIPENAME,
                                       "PRO-1.15",NULL);
    if (cpl_table_save(casu_tfits_get_table(stack),NULL,
                       plist,bname,CPL_IO_EXTEND) != CPL_ERROR_NONE) {
        cpl_msg_error(fctid,"Cannot save product table extension -- %s",
                      cpl_error_get_message());
        return(CASU_FATAL);
    }

    /* Get out of here */

    return(CASU_OK);
}

/*---------------------------------------------------------------------------*/
/**
    \par Name:
        hawki_std_save_sky
    \par Purpose:
        Save the mean sky image products
    \par Description:
        Extensions of sky frame image products are saved here.
    \par Language:
        C
    \param outsky
        The casu_fits structure for the sky frame to be saved
    \param framelist
        The input recipe frameset
    \param parlist
        The input recipe parameter list
    \param template
        A template to be used in the cpl saving routine
    \param isvar
        Set if this a variance map
    \param isfirst
        Set if this is the first extension to be saved.
    \param fnametype
        The type of file name. This could either be 0 -> ESO 'predictable'
        names, 1 -> pretty names based on input file names, 2 -> temporary
        file names.   
    \param product_frame
        The output product frame
    \retval CASU_OK if everything is ok
    \retval CASU_FATAL if something failed
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

static int hawki_std_save_sky(casu_fits *outsky, cpl_frameset *framelist,
                              cpl_parameterlist *parlist, 
                              char *fname, cpl_frame *template, int isvar,
                              int isfirst, cpl_frame **product_frame) {
    cpl_propertylist *p;
    int isdummy;
    cpl_image *fim;
    const char *fctid = "hawki_std_save_sky";

    /* Work out which frame to base the output on. If this particular
       sequence failed for whatever reason, there will be a dummy sky frame. */

    fim = casu_fits_get_image(outsky);
    isdummy = (casu_fits_get_status(outsky) != CASU_OK);

    /* If we need to make a PHU then do that now based on the first frame
       in the input frame list */

    if (isfirst) {

        /* Create a new product frame object and define some tags */

        *product_frame = cpl_frame_new();
        cpl_frame_set_filename(*product_frame,fname);
        if (isvar) 
            cpl_frame_set_tag(*product_frame,HAWKI_PRO_MEAN_SKY_VAR);
        else
            cpl_frame_set_tag(*product_frame,HAWKI_PRO_MEAN_SKY);
        cpl_frame_set_type(*product_frame,CPL_FRAME_TYPE_IMAGE);
        cpl_frame_set_group(*product_frame,CPL_FRAME_GROUP_PRODUCT);
        cpl_frame_set_level(*product_frame,CPL_FRAME_LEVEL_FINAL);

        /* Set up the PHU header. */

        p = casu_fits_get_phu(outsky);
        hawki_dfs_set_product_primary_header(p,*product_frame,framelist,
                                             parlist,HAWKI_RECIPENAME,
                                             "PRO-1.15",template,0);

        /* 'Save' the PHU image */                         

        if (cpl_image_save(NULL,fname,CPL_TYPE_UCHAR,p,
                           CPL_IO_DEFAULT) != CPL_ERROR_NONE) {
            cpl_msg_error(fctid,"Cannot save product PHU");
            cpl_frame_delete(*product_frame);
            return(-1);
        }
        cpl_frameset_insert(framelist,*product_frame);
    }

    /* Get the extension property list */

    p = cpl_propertylist_duplicate(casu_fits_get_ehu(outsky));
    if (isdummy)
        casu_dummy_property(p);

    /* Fiddle with the header now */

    hawki_dfs_set_product_exten_header(p,*product_frame,framelist,parlist,
                                       HAWKI_RECIPENAME,"PRO-1.15",template);
    if (cpl_image_save(fim,fname,CPL_TYPE_FLOAT,p,CPL_IO_EXTEND) != 
        CPL_ERROR_NONE) {
        cpl_msg_error(fctid,"Cannot save product image extension");
        return(-1);
    }

    /* Quick tidy */

    cpl_propertylist_delete(p);

    /* Get out of here */

    return(0);
}

/*---------------------------------------------------------------------------*/
/**
    \par Name:
        hawki_std_update_frameset
    \par Purpose:
        Create a new frameset from an old one and update the file names
    \par Description:
        A new frameset is created from an old one. The file names in the
        new frameset are modified so that they are only the basenames of
        the filenames in the old frameset (i.e. directory info is stripped)
    \par Language:
        C
    \param frms
        The input frameset
    \param cs
        The configuration structure defined in the main routine
    \param ftype
        The file type for the naming convention
    \return
        The output frameset
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

static cpl_frameset *hawki_std_update_frameset(cpl_frameset *frms, 
                                               configstruct *cs, int ftype) {
    cpl_frameset *copy;
    cpl_size i,n;
    cpl_frame *fr;
    char *fname,bname[BUFSIZ];

    /* NULL input... */

    if (frms == NULL)
        return(NULL);

    /* First make a copy of the input frameset */

    copy = cpl_frameset_duplicate(frms);

    /* Now go through and change the file names */

    n = cpl_frameset_get_size(frms);
    for (i = 0; i < n; i++) {
        fr = cpl_frameset_get_position(copy,i);
        fname = cpl_strdup(cpl_frame_get_filename(fr));
        hawki_std_product_name(fname,ftype,cs->prettynames,i+1,bname);
        cpl_frame_set_filename(fr,bname);
        cpl_free(fname);
    }

    /* Get out of here */

    return(copy);
}

/*---------------------------------------------------------------------------*/
/**
    \par Name:
        hawki_std_product_name
    \par Purpose:
        Set up an output product file name
    \par Description:
        An output file name is defined based on a template name,  a
        'predictable' name or a temporary file name.
    \par Language:
        C
    \param template
        If the pretty file name option is used or if this is a temporary
        file name, then the this will be used as a reference name.
    \param producttype
        The type of product you are writing out. These are enumerated above.
    \param nametype
        This is: 0 -> predictable names, 1 -> pretty names based on input
        file names or 2 -> temporary file names.
    \param fnumber
        If the predictable file names are going to be used, then this is 
        a number that is appended to a file name root.
    \param outfname
        The output file name
    \return
        Nothing
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

static void hawki_std_product_name(char *template, int producttype, 
                                   int nametype, int fnumber, char *outfname) {
    const char *esonames[] = {"exp_","exp_var_","exp_cat","sky_","sky_var_",
                              "mstd_a","mstd_p"};
    const char *suffix[] = {"_ex","_ex_var","_ex_cat","sky_","sky_var_",
                            "_ex_mstd_a","_ex_mstd_p"};
    char *fname,*bname,*dot;

    /* If the name type is the ESO predictable sort, then it's easy. Just
       use the esonames defined above and append the correct number */

    switch (nametype) {
    case 0:
        (void)sprintf(outfname,"%s%d.fits",esonames[producttype],fnumber);
        break;

    /* If this is a temporary file, then just append tmp_ to the template
       name and return that */

    case 2:
        fname = cpl_strdup(template);
        bname = basename(fname);
        (void)sprintf(outfname,"tmp_%s",bname);
        freespace(fname);
        break;

    /* Ok, we want a pretty name... */

    case 1:
        fname = cpl_strdup(template);
        bname = basename(fname);
        if (producttype != SKY_FILE && producttype != SKY_FILE_VAR) {
            (void)sprintf(outfname,"%s",bname);
            dot = strrchr(outfname,'.');
            (void)sprintf(dot,"%s.fits",suffix[producttype]);
        } else {
            sprintf(outfname,"%s%s",suffix[producttype],bname);
        }
        freespace(fname);
        break;

    /* something else ?? */

    default:
        (void)strcpy(outfname,"");
        break;
    }
    return;
}   

/**@}*/

/*---------------------------------------------------------------------------*/
/**
    \par Name:
        hawki_std_init
    \par Purpose:
        Initialise pointers in the memory structure.
    \par Description:
        Initialise pointers in the memory structure.
    \par Language:
        C
    \param ps
        The memory structure defined in the main routine
    \return
        Nothing
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

static void hawki_std_init(memstruct *ps) {

    /* Level 0 stuff */

    ps->labels = NULL;
    ps->master_dark = NULL;
    ps->master_twilight_flat = NULL;
    ps->master_conf = NULL;
    ps->mask = NULL;
    ps->phottab = NULL;
    ps->tphottab = NULL;
    ps->science_frames = NULL;
    ps->product_frames_simple = NULL;
    ps->product_frames_simple_var = NULL;
    ps->gaincors = NULL;
    ps->catpath_a = NULL;
    ps->catname_a = NULL;
    ps->catpath_p = NULL;
    ps->catname_p = NULL;
    ps->scipaw = NULL;
    ps->sky = NULL;
    ps->schlf_n = NULL;
    ps->schlf_s = NULL;
    ps->readgain = NULL;

    /* Level 1 stuff */

    ps->fdark = NULL;
    ps->fdark_var = NULL;
    ps->fflat = NULL;
    ps->fconf = NULL;
    ps->fsky = NULL;
    ps->nscience = 0;
    ps->sci_fits = NULL;
}

/*---------------------------------------------------------------------------*/
/**
    \par Name:
        hawki_std_tidy
    \par Purpose:
        Free allocated workspace in the global memory structure
    \par Description:
        Free allocated workspace in the global memory structure. The tidy works
        on two levels. Level 1 is for items that are usually cleared up after
        each extension is processed. Level 0 is for cleaning up the whole
        recipe
    \par Language:
        C
    \param ps
        The memory structure defined in the main routine
    \param level
        The level of the tidy to be done. 1: Tidy up after finishing an 
        extension, 0: Tidy up after finishing the recipe.
    \return
        Nothing
    \author
        Jim Lewis, CASU
 */
/*---------------------------------------------------------------------------*/

static void hawki_std_tidy(memstruct *ps, int level) {

    /* Level 1 stuff */

    freefits(ps->fdark); 
    freefits(ps->fdark_var); 
    freefits(ps->fflat); 
    freefits(ps->fconf);
    freefits(ps->fsky);
    freefitslist(ps->sci_fits,ps->nscience);
    ps->nscience = 0;

    if (level == 1)
        return;
    
    /* Level 0 stuff */

    freespace(ps->labels);
    freeframe(ps->master_dark);
    freeframe(ps->master_twilight_flat);
    freeframe(ps->master_conf);
    freemask(ps->mask);
    freeframe(ps->phottab);
    freetable(ps->tphottab);
    freeframeset(ps->science_frames);
    freespace(ps->product_frames_simple);     /* NB: We only have to delete */
    freespace(ps->product_frames_simple_var); /* the arrays and not the */
                                              /* frames as these get passed */
                                              /* back to esorex */
    freespace(ps->gaincors);
    freespace(ps->catpath_a);
    freespace(ps->catpath_p);
    freespace(ps->catname_a);
    freespace(ps->catname_p);
    if (ps->sky != NULL) {
        freeframeset(ps->sky->contrib);
        freeframeset(ps->sky->contrib_var);
        freeframe(ps->sky->template);    
        freespace(ps->sky);
    }
    hawki_std_paw_delete(&(ps->scipaw));
    freeframe(ps->schlf_n);
    freeframe(ps->schlf_s);
    freeframe(ps->readgain);
}



/*

$Log: hawki_standard_process.c,v $
Revision 1.38  2015/11/27 12:22:21  jim
Added ability to define location of standard star location

Revision 1.37  2015/11/22 15:45:15  jim
superficial changes

Revision 1.36  2015/11/06 10:22:36  jim
Fixed cast

Revision 1.35  2015/11/06 10:21:11  jim
Fixed typo

Revision 1.34  2015/10/24 09:32:30  jim
Fixed _save_cat to fix provenance info

Revision 1.33  2015/10/15 11:23:34  jim
modified qc

Revision 1.32  2015/09/15 11:10:07  jim
Fixed so that WCS is the same in variance arrays as science images

Revision 1.31  2015/09/11 09:31:20  jim
Changed parameter declarations from _value to _range where appropriate

Revision 1.30  2015/08/07 13:07:06  jim
Fixed copyright to ESO

Revision 1.29  2015/07/29 09:16:37  jim
Fixed bug with file numbering in the ugly name mode

Revision 1.28  2015/07/07 09:49:40  jim
removed diagnostic prints

Revision 1.27  2015/07/07 09:39:38  jim
Fixed bug in save routine

Revision 1.26  2015/07/02 12:30:00  jim
Fixed so that we get the correct value of FLUXCAL now

Revision 1.25  2015/06/29 10:35:59  jim
photosys comes from photcal table header now

Revision 1.24  2015/06/08 09:34:56  jim
Modified to add more phase3 stuff to headers

Revision 1.23  2015/05/21 07:48:48  jim
Fixed bug where error was thrown if more than one extension was found
in the photcal tab

Revision 1.22  2015/04/30 12:12:14  jim
fixed bug where predictable catalogue names are not unique. Also restored
missing code block where the search parameters for the local standards
are set.

Revision 1.21  2015/03/12 09:16:51  jim
Modified to remove some compiler moans

Revision 1.20  2015/03/04 13:18:40  jim
Fixed some more memory leaks

Revision 1.19  2015/02/14 12:34:53  jim
It is now possible to write out matched standards catalogues

Revision 1.18  2015/01/29 12:03:01  jim
Modified comments. Modified command line arguments to use strings for
enums where that makes sense. Also removed some support routines to
hawki_utils.c

Revision 1.17  2014/12/12 21:44:31  jim
Fixed to remove inverse variance

Revision 1.16  2014/12/11 12:24:09  jim
lots of upgrades

Revision 1.15  2014/05/06 12:01:43  jim
Fixed bug in declaration of enum cdssearch

Revision 1.14  2014/04/27 10:10:33  jim
Modified to allow for local FITS file standard star catalogues

Revision 1.13  2014/04/09 11:08:21  jim
Get rid of a couple of compiler moans

Revision 1.12  2014/04/09 09:09:51  jim
Detabbed

Revision 1.11  2014/04/04 05:11:09  jim
Fixed some memory leaks

Revision 1.10  2014/04/02 16:56:11  jim
fixed typo

Revision 1.9  2014/04/02 09:49:26  jim
Modified to add the readnoise and gain information

Revision 1.8  2014/03/26 16:09:40  jim
Removed calls to deprecated cpl routine. Uses floating point confidence
maps

Revision 1.7  2013/11/21 09:38:14  jim
detabbed

Revision 1.6  2013-10-24 09:33:22  jim
Upgraded so that if a detector is flagged as dead things progress smoothly

Revision 1.5  2013-09-30 18:03:59  jim
Changed some parameter defaults

Revision 1.4  2013-08-30 09:10:15  jim
fixed a few memory leaks

Revision 1.3  2013-08-29 17:30:26  jim
Altered to remove remaining globals

Revision 1.2  2013-08-28 05:44:36  jim
Fixed tabs and fixed some memory leaks

Revision 1.1.1.1  2013-08-27 12:07:48  jim
Imported


*/
