Index: /trunk/ppMerge/src/ppMergeConfig.c
===================================================================
--- /trunk/ppMerge/src/ppMergeConfig.c	(revision 6997)
+++ /trunk/ppMerge/src/ppMergeConfig.c	(revision 6998)
@@ -1,31 +1,52 @@
-# include "ppMerge.h"
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
 
-static void usage (void) {
-    fprintf (stderr, "USAGE: ppMerge [-file INPUT.fits] [-list INPUT.txt] OUTPUT\n");
-    exit (2);
+#include "ppMergeConfig.h"
+
+// Output usage information
+static void usage(const char *programName // Name of the program
+    )
+{
+    printf("Merge multiple calibration frames into a master frame by stacking.\n\n"
+           "Usage:\n"
+           "\t%s OUTPUT.fits [-files FILES] [-list FILE_LIST]\n\n"
+           "where:\n"
+           "FILES       is a glob to be interpreted by the program.\n"
+           "FILE_LIST   is a list of files (including a glob interpreted by the shell).\n"
+           "\n", programName);
+    exit(EXIT_FAILURE);
 }
 
-pmConfig *ppMergeConfig (int argc, char **argv)
+pmConfig *ppMergeConfig(int *argc, char **argv)
 {
-    bool status;
+    if (*argc == 1) {
+        usage(argv[0]);
+    }
 
-    if (argc == 1) usage ();
-
-    // load the site-wide configuration information
-    pmConfig *config = pmConfigRead(&argc, argv);
+    // Load the site-wide configuration information
+    pmConfig *config = pmConfigRead(argc, argv);
     if (! config) {
         psErrorStackPrint(stderr, "Can't find site configuration!\n");
-        exit(EXIT_FAILURE);
+        usage(argv[0]);
     }
 
-    // Parse other command-line arguments
+    // Parse other command-line arguments, save for future use
     config->arguments = psMetadataAlloc(); // The arguments, with default values
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "-type", 0, "Type of calibration frame", "");
+    psMetadataAddBool(config->arguments, PS_LIST_TAIL, "-zero", 0, "Subtract background?", false);
+    psMetadataAddBool(config->arguments, PS_LIST_TAIL, "-scale", 0, "Scale by background?", false);
+    psMetadataAddBool(config->arguments, PS_LIST_TAIL, "-exptime", 0, "Scale by the exposure time?", false);
+    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "-onoff", 0, "Number of on/off pairs", 0);
 
-    // the input file is a required argument; if not found, we will exit
-    status = pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT", "-file", "-list");
-    if (!status) { usage ();}
+    // We require an input file set
+    bool status = pmConfigFileSetsMD(config->arguments, argc, argv, "INPUT", "-files", "-list");
+    if (!status) {
+        usage(argv[0]);
+    }
 
-    if (! psArgumentParse(config->arguments, &argc, argv) || argc != 2) {
-	usage ();
+    // Parse other arguments
+    if (! psArgumentParse(config->arguments, argc, argv) || *argc != 2) {
+        usage(argv[0]);
     }
 
@@ -33,21 +54,17 @@
     psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Name of the output image", argv[1]);
 
-    // the input image(s) are required arguments
-    // the first one defines the camera
+    // The input images are required.  The first one defines the camera.
     status = false;
-    pmFPAfileFromArgs (&status, config, "PPIMAGE.INPUT", "INPUT");
+    pmFPAfileFromArgs(&status, config, "PPMERGE.INPUT", "INPUT");
     if (!status) {
-	psAbort (__func__, "missing INPUT entry");
+        usage(argv[0]);
     }
 
-# if 0
-    // define Database handle, if used
+
+#if 0
+    // Define database handle, if required
     config->database = pmConfigDB(config->site);
-# endif
+#endif
 
-    return true;
-} 
-
-/* we require all information needed to determine the scaling 
-   to be written in the header.  is this reasonable? 
-*/
+    return config;
+}
Index: /trunk/ppMerge/src/ppMergeConfig.h
===================================================================
--- /trunk/ppMerge/src/ppMergeConfig.h	(revision 6998)
+++ /trunk/ppMerge/src/ppMergeConfig.h	(revision 6998)
@@ -0,0 +1,8 @@
+#ifndef PP_MERGE_CONFIG_H
+#define PP_MERGE_CONFIG_H
+
+// Get the configuration information
+pmConfig *ppMergeConfig(int *argc, char **argv // The standard command-line parameters (but pointer to number)
+    );
+
+#endif
Index: /trunk/ppMerge/src/ppMergeOptions.c
===================================================================
--- /trunk/ppMerge/src/ppMergeOptions.c	(revision 6997)
+++ /trunk/ppMerge/src/ppMergeOptions.c	(revision 6998)
@@ -1,78 +1,136 @@
-# include "ppMerge.h"
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
 
-// XXX EAM : optionally choose the mask image based on the detrend database
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// ppMergeOptions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
-bool ppMergeOptions (ppData *data, ppOptions *options, ppConfig *config) {
+// Free function
+static void mergeOptionsFree(ppMergeOptions *options // Options to free
+    )
+{
+    psFree(options->combine);
+}
 
-    bool status;
+// Allocator
+ppMergeOptions *ppMergeOptionsAlloc(void)
+{
+    ppMergeOptions *options = psAlloc(sizeof(ppMergeOptions)); // The options, to return
+    psMemSetDeallocator(options, (psFreeFunc)mergeOptionsFree);
 
-    // at what depth is the image read?
-    options->imageLoadDepth = PP_LOAD_NONE;
-    char *depth = psMetadataLookupPtr(NULL, config->recipe, "LOAD.DEPTH");
-    if (depth == NULL) {
-	psAbort ("merge", "load depth not specified");
+    options->rows = 0;
+    options->minElectrons = NAN;
+    options->zero = false;
+    options->scale = false;
+    options->exptime = false;
+    options->sample = 1;
+    options->background = PS_STAT_SAMPLE_MEDIAN;
+    options->onOff = 0;
+    options->combine = PS_STAT_SAMPLE_MEAN;
+    options->ref = 3.0;
+    options->iter = 1;
+    options->fracHigh = 0.0;
+    options->fracLow = 0.0;
+    options->nKeep = 1;
+    options->maskVal = 0xffff;
+
+    return options;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// ppMergeOptionsParse
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Parse a recipe option according to its type
+#define OPTION_PARSE(OPTION,MD,NAME,TYPE)                                                                    \
+{                                                                                                            \
+    psMetadataItem *item = psMetadataLookup(MD, NAME);                                                       \
+    if (item) {                                                                                              \
+        OPTION = psMetadataItemParse##TYPE(item);                                                            \
+    }                                                                                                        \
+}
+
+// Parse a statistic
+static psStatsOptions parseStat(psMetadata *source, // Source of the statistics option
+                                const char *name // Name of the statistics option
+    )
+{
+    bool mdok = true;                   // Status of MD lookup
+    const char *stat = psMetadataLookupStr(&mdok, source, name);  // The statistic string
+    if (!mdok || !stat || strlen(stat) == 0) {
+        return 0;
     }
-    if (!strcasecmp(depth, "FPA")) {
-	options->imageLoadDepth = PP_LOAD_FPA;
+    if (strcasecmp(stat, "MEAN") == 0 || strcasecmp(stat, "SAMPLE_MEAN") == 0) {
+        return PS_STAT_SAMPLE_MEAN;
     }
-    if (!strcasecmp(depth, "CHIP")) {
-	options->imageLoadDepth = PP_LOAD_CHIP;
+    if (strcasecmp(stat, "MEDIAN") == 0 || strcasecmp(stat, "SAMPLE_MEDIAN") == 0) {
+        return PS_STAT_SAMPLE_MEDIAN;
     }
-    if (!strcasecmp(depth, "CELL")) {
-	options->imageLoadDepth = PP_LOAD_CELL;
+    if (strcasecmp(stat, "ROBUST") == 0 || strcasecmp(stat, "ROBUST_MEDIAN") == 0) {
+        return PS_STAT_ROBUST_MEDIAN;
     }
-    if (options->imageLoadDepth == PP_LOAD_NONE) {
-	psAbort ("merge", "load depth not specified");
+    if (strcasecmp(stat, "FITTED") == 0 || strcasecmp(stat, "FITTED_MEAN") == 0) {
+        return PS_STAT_FITTED_MEAN;
+    }
+    if (strcasecmp(stat, "CLIPPED") == 0 || strcasecmp(stat, "CLIPPED_MEAN") == 0) {
+        return PS_STAT_CLIPPED_MEAN;
     }
 
-    // global pixel mask
-    options->doMask = false;
-    if (psMetadataLookupBool(NULL, config->recipe, "MASK")) {
-	data->mask->filename = psMetadataLookupStr(NULL, config->arguments, "-mask");
-        if (strlen(data->mask->filename) > 0) {
-            options->doMask = true;
-        } else {
-            psLogMsg("merge", PS_LOG_WARN, "Masking is desired, but no mask was supplied"
-		     " --- no masking will be performed.\n");
-        }
+    psError(PS_ERR_IO, true, "Unable to interpret statistic: %s\n", name);
+    return 0;
+}
+
+// Parse the options
+ppMergeOptions *ppMergeOptionsParse(ppConfig *config // Configuration
+    )
+{
+    ppMergeOptions *options = ppMergeOptionsAlloc(); // The merge options
+
+    // First, deal with the recipe.  These are parameters that will typically be constant for a camera.
+    OPTION_PARSE(options->rows,         config->recipe, "ROWS",      U16 );
+    OPTION_PARSE(options->minElectrons, config->recipe, "ELECTRONS", F32 );
+    OPTION_PARSE(options->sample,       config->recipe, "SAMPLE",    S32 );
+    OPTION_PARSE(options->rej,          config->recipe, "REJ",       F32 );
+    OPTION_PARSE(options->iter,         config->recipe, "ITER",      S32 );
+    OPTION_PARSE(options->fracHigh,     config->recipe, "FRACHIGH",  F32 );
+    OPTION_PARSE(options->fracLow,      config->recipe, "FRACLOW",   F32 );
+    OPTION_PARSE(options->nKeep,        config->recipe, "NKEEP",     S32 );
+    OPTION_PARSE(options->maskVal,      config->recipe, "MASKVAL",   U8  );
+    options->combine = parseStat(config->recipe, "COMBINE");
+    options->background = parseStat(config->recipe, "BACKGROUND");
+
+    // Now the command-line options.  These are parameters that depend on what type of frame is being combined
+
+    // Set options based on the type of calibration frame
+    const char *type = psMetadataLookupStr(NULL, config->arguments, "-type"); // The type of calibration frame
+    if (strcasecmp(type, "BIAS") == 0) {
+        options->zero = false;
+        options->scale = false;
+        options->exptime = false;
+    } else if (strcasecmp(type, "DARK") == 0) {
+        options->zero = false;
+        options->scale = false;
+        options->exptime = true;
+    } else if (strcasecmp(type, "FLAT") == 0) {
+        options->zero = false;
+        options->scale = true;
+        options->exptime = false;
+    } else if (strcasecmp(type, "FRINGE") == 0) {
+        options->zero = true;
+        options->scale = true;
+        options->exptime = false;
+    } else {
+        psLogMsg(__func__, PS_LOG_WARN, "Unrecognised image type: %s --- ignored.\n", type);
     }
 
-    // how do we calculate the merge stack?
-    psStatsOptions mergeStats = 0;
-    psString stat = psMetadataLookupStr(NULL, config->recipe, "MERGE.STAT");
-    if (! strcasecmp(stat, "MEAN")) {
-	mergeStats = PS_STAT_SAMPLE_MEAN;
-    } else if (! strcasecmp(stat, "MEDIAN")) {
-	mergeStats = PS_STAT_SAMPLE_MEDIAN;
-    } else {
-	psAbort ("merge", "MERGE.STAT (%s) is not one of MEAN, MEDIAN\n", stat);
-    }
-    options->combineParams = pmCombineParamsAlloc (mergeStats);
+    // Or you can set them individually
+    OPTION_PARSE(options->zero,    config->recipe, "-zero",    Bool);
+    OPTION_PARSE(options->scale,   config->recipe, "-scale",   Bool);
+    OPTION_PARSE(options->exptime, config->recipe, "-exptime", Bool);
 
-    // other merge stack options
-    options->applyZeroScale = psMetadataLookupBool(NULL, config->recipe, "MERGE.RESCALE");
+    // Number of on/off images
+    OPTION_PARSE(options->onoff, config->recipe, "-onoff", S32);
 
-    int nKeep = psMetadataLookupS32(&status, config->recipe, "MERGE.NKEEP");
-    if (status && nKeep > 0) {
-	options->combineParams->nKeep = nKeep;
-    }
-
-    float fracHigh = psMetadataLookupF32(&status, config->recipe, "MERGE.FRAC.HIGH");
-    if (status) {
-	options->combineParams->fracHigh = fracHigh;
-    }
-
-    float fracLow = psMetadataLookupF32(&status, config->recipe, "MERGE.FRAC.LOW");
-    if (status) {
-	options->combineParams->fracLow = fracLow;
-    }
-
-    // XXX need to set the masking value somehow...
-
-    // gain and readnoise come from camera parameters and depend on chip/cell
-    // XXX drop these from the options structure?
-    options->gain = 1.0;
-    options->readnoise = 0.0;
-
-    return true;
+    return options;
 }
Index: /trunk/ppMerge/src/ppMergeOptions.h
===================================================================
--- /trunk/ppMerge/src/ppMergeOptions.h	(revision 6998)
+++ /trunk/ppMerge/src/ppMergeOptions.h	(revision 6998)
@@ -0,0 +1,44 @@
+#ifndef PP_MERGE_OPTIONS_H
+#define PP_MERGE_OPTIONS_H
+
+#include <psModules.h>
+
+// Mode of on/off pairs; the value corresponds to how many images are in each set
+typedef enum {
+    PP_ONOFF_ABBA = -1,                 // On/off pairs in the ABBA mode
+    PP_ONOFF_NONE = 0,                  // No on/off pairs
+    PP_ONOFF_ABAB = 1,                  // On/off pairs in the ABAB mode (one image each)
+    PP_ONOFF_AABB = 2,                  // On/off pairs, two images each
+    // And so on and so forth... just use a number beyond this
+} ppOnOff;
+
+// Options for ppMerge
+typedef struct {
+    unsigned int rows;                  // Number of rows to read at once
+    float minElectrons;                 // Minimum number of electrons for useful signal
+    bool zero;                          // Subtract background before combining?
+    bool scale;                         // Scale by the background before combining?
+    bool exptime;                       // Normalise by the exposure time?
+    unsigned int sample;                // Sampling factor for measuring the background
+    psStatsOptions background;          // Statistic to use to measure the background
+    ppOnOff onOff;                      // On/off pairs?
+    psStatsOptions combine;             // Statistic to use in combination
+    float rej;                          // Rejection level in combination (for sigma clipping)
+    unsigned int iter;                  // Number of iterations for combination (for sigma clipping)
+    float fracHigh;                     // Fraction of high pixels to throw
+    float fracLow;                      // Fraction of low pixels to throw
+    int nKeep;                          // Minimum number of pixels to keep
+    psMaskType maskVal;                 // Mask value
+} ppMergeOptions;
+
+// Allocator
+ppMergeOptions *ppMergeOptionsAlloc(psStatsOptions stat // Statistic to use in the merge
+    );
+
+
+// Parse the options for ppMerge
+ppMergeOptions *ppMergeOptionsParse(pmConfig *config // Configuration
+    );
+
+
+#endif
