Index: trunk/ppStack/src/Makefile.am
===================================================================
--- trunk/ppStack/src/Makefile.am	(revision 30394)
+++ trunk/ppStack/src/Makefile.am	(revision 30620)
@@ -49,4 +49,7 @@
 	ppStackFinish.c		\
 	ppStackTarget.c		\
+	ppStackUpdateHeader.c	\
+	ppStackJPEGs.c		\
+	ppStackStats.c		\
 	ppStackErrorCodes.c
 
Index: trunk/ppStack/src/ppStack.c
===================================================================
--- trunk/ppStack/src/ppStack.c	(revision 30394)
+++ trunk/ppStack/src/ppStack.c	(revision 30620)
@@ -1,20 +1,8 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <pslib.h>
-#include <psmodules.h>
-#include <psphot.h>
-
 #include "ppStack.h"
-#include "ppStackLoop.h"
-
-#define TIMER_NAME "PPSTACK"            // Name of timer
 
 int main(int argc, char *argv[])
 {
     psLibInit(NULL);
-    psTimerStart(TIMER_NAME);
+    psTimerStart("PPSTACK");
     psTimerStart("PPSTACK_STEPS");
 
@@ -23,8 +11,9 @@
     psphotErrorRegister();
 
+    ppStackOptions *options = NULL;                               // Options for stacking
+
     pmConfig *config = pmConfigRead(&argc, argv, PPSTACK_RECIPE); // Configuration
-    ppStackOptions *options = NULL;                               // Options for stacking
     if (!config) {
-        goto die;
+	ppStackCleanup(config, options);
     }
 
@@ -33,100 +22,30 @@
     if (!pmModelClassInit()) {
         psError(PPSTACK_ERR_PROG, false, "Unable to initialise model classes.");
-        goto die;
+	ppStackCleanup(config, options);
     }
 
     if (!psphotInit()) {
         psError(PPSTACK_ERR_PROG, false, "Error initialising psphot.");
-        goto die;
+	ppStackCleanup(config, options);
     }
 
-    (void)psTraceSetLevel("ppStack", 5);
-
     if (!ppStackArgumentsSetup(argc, argv, config)) {
-        goto die;
+	ppStackCleanup(config, options);
     }
 
     if (!ppStackCamera(config)) {
-        goto die;
+	ppStackCleanup(config, options);
     }
 
     if (!ppStackArgumentsParse(config)) {
-        goto die;
+	ppStackCleanup(config, options);
     }
 
     options = ppStackOptionsAlloc();
     if (!ppStackLoop(config, options)) {
-        goto die;
+	ppStackCleanup(config, options);
     }
 
-
- die:
-    // Common code for the death.
-    {
-        psExit exitValue = ppStackExitCode(PS_EXIT_SUCCESS); // Exit code
-
-        // Ensure everything closes
-        if (config) {
-            ppStackFileActivation(config, PPSTACK_FILES_PREPARE, true);
-            ppStackFileActivation(config, PPSTACK_FILES_CONVOLVE, true);
-            ppStackFileActivation(config, PPSTACK_FILES_STACK, true);
-            ppStackFileActivation(config, PPSTACK_FILES_UNCONV, true);
-            ppStackFileActivation(config, PPSTACK_FILES_PHOT, true);
-            if (!ppStackFilesIterateUp(config)) {
-                psError(psErrorCodeLast(), false, "Unable to close files.");
-                exitValue = ppStackExitCode(exitValue);
-                pmFPAfileFreeSetStrict(false);
-            }
-        }
-
-        // Write out summary statistics
-        if (options && options->stats) {
-
-            psMetadataAddS32(options->stats, PS_LIST_TAIL, "QUALITY", PS_META_REPLACE,
-                             "Bad data quality flag", options->quality);
-            psMetadataAddF32(options->stats, PS_LIST_TAIL, "TIME_STACK", 0,
-                             "Total time", psTimerClear("PPSTACK_TOTAL"));
-
-            const char *statsMDC = psMetadataConfigFormat(options->stats);
-            if (!statsMDC || strlen(statsMDC) == 0) {
-                psError(PS_ERR_IO, false, "Unable to get statistics MDC file.");
-                return false;
-            }
-            if (fprintf(options->statsFile, "%s", statsMDC) != strlen(statsMDC)) {
-                psError(PS_ERR_IO, false, "Unable to write statistics MDC file.");
-                return false;
-            }
-            psFree(statsMDC);
-            if (fclose(options->statsFile) == EOF) {
-                psError(PS_ERR_IO, false, "Unable to close statistics MDC file.");
-                return false;
-            }
-            options->statsFile = NULL;
-            pmConfigRunFilenameAddWrite(config, "STATS",
-                                        psMetadataLookupStr(NULL, config->arguments, "STATS"));
-        }
-        psFree(options);
-
-        // Dump configuration
-        bool mdok;                                                                    // Status of MD lookup
-        psString dump = psMetadataLookupStr(&mdok, config->arguments, "DUMP_CONFIG"); // File for config
-        if (dump && !pmConfigDump(config, dump)) {
-            psError(psErrorCodeLast(), false, "Unable to dump configuration.");
-            exitValue = ppStackExitCode(exitValue);
-        }
-
-        psTrace("ppStack", 1, "Finished at %f sec\n", psTimerMark(TIMER_NAME));
-        psTimerStop();
-
-        psFree(config);
-        pmModelClassCleanup();
-        pmConfigDone();
-        psLibFinalize();
-        pmVisualClose();
-	pmVisualCleanup ();
-
-        exitValue = ppStackExitCode(exitValue);
-        exit(exitValue);
-    }
+    ppStackCleanup(config, options);
 }
 
Index: trunk/ppStack/src/ppStack.h
===================================================================
--- trunk/ppStack/src/ppStack.h	(revision 30394)
+++ trunk/ppStack/src/ppStack.h	(revision 30620)
@@ -1,2 +1,12 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+#include <ppStats.h>
+#include <psphot.h>
+
 #ifndef PPSTACK_H
 #define PPSTACK_H
@@ -7,7 +17,4 @@
 #include <pslib.h>
 #include <psmodules.h>
-
-#include "ppStackOptions.h"
-#include "ppStackErrorCodes.h"
 
 // Mask values for inputs
@@ -25,4 +32,5 @@
 // List of files
 typedef enum {
+    PPSTACK_FILES_NONE,                 // NOP list
     PPSTACK_FILES_PREPARE,              // Files for preparation
     PPSTACK_FILES_TARGET,               // Files for target generation
@@ -33,4 +41,8 @@
 } ppStackFileList;
 
+#include "ppStackOptions.h"
+#include "ppStackThread.h"
+#include "ppStackLoop.h"
+#include "ppStackErrorCodes.h"
 
 // Setup command-line arguments
@@ -180,6 +192,15 @@
     );
 
+bool ppStackWriteVariance(const char *name, // Name of image
+			  psMetadata *header, // Header
+			  const psImage *variance, // Variance
+			  const psImage *covariance, // Variance
+			  pmConfig *config // Configuration
+    );
+
 /// Return an appropriate exit code based on the error code
 psExit ppStackExitCode(psExit exitValue);
 
+bool ppStackCleanup(pmConfig *config, ppStackOptions *options) PS_ATTR_NORETURN;
+
 #endif
Index: trunk/ppStack/src/ppStackArguments.c
===================================================================
--- trunk/ppStack/src/ppStackArguments.c	(revision 30394)
+++ trunk/ppStack/src/ppStackArguments.c	(revision 30620)
@@ -1,11 +1,2 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <string.h>
-#include <pslib.h>
-#include <psmodules.h>
-
 #include "ppStack.h"
 
@@ -189,11 +180,31 @@
     psMetadataAddStr(arguments, PS_LIST_TAIL, "-temp-mask", 0, "Suffix for temporary masks", NULL);
     psMetadataAddStr(arguments, PS_LIST_TAIL, "-temp-variance", 0, "Suffix for temporary variance maps", NULL);
-    psMetadataAddBool(arguments, PS_LIST_TAIL, "-temp-delete", 0,
-                      "Delete temporary files on completion?", false);
+    psMetadataAddBool(arguments, PS_LIST_TAIL, "-temp-delete", 0, "Delete temporary files on completion?", false);
     psMetadataAddS32(arguments, PS_LIST_TAIL, "-threads", 0, "Number of threads to use", 0);
     psMetadataAddBool(arguments, PS_LIST_TAIL, "-visual", 0, "visualisation", false);
 
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-stack_id",   0, "stack ID",        NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-skycell_id", 0, "skycell ID",      NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-tess_id",    0, "tessellation ID", NULL);
+
     if (argc == 1) {
         usage(argv[0], arguments, config);
+    }
+
+    // stack-type : used to define the stack for PSPS
+    if ((argNum = psArgumentGet (argc, argv, "-stack-type"))) {
+	if (argc <= argNum+1) {
+	    psErrorStackPrint(stderr, "Expected to see an argument for -stack-type");
+	    exit(PS_EXIT_CONFIG_ERROR);
+	}
+        psArgumentRemove (argNum, &argc, argv);
+	if (strcasecmp(argv[argNum], "NIGHTLY") && strcasecmp(argv[argNum], "DEEP") && strcasecmp(argv[argNum], "BEST_IQ")) {
+	    psErrorStackPrint(stderr, "Invalid option for -stack-type %s (must be one of NIGHTLY_STACK, DEEP_STACK, IQ_STACK)", argv[argNum]);
+	    exit(PS_EXIT_CONFIG_ERROR);
+	}
+	psMetadataAddStr (arguments, PS_LIST_TAIL, "STACK_TYPE", PS_META_REPLACE, "Stack Type", argv[argNum]);
+        psArgumentRemove (argNum, &argc, argv);
+    } else { 
+	psMetadataAddStr (arguments, PS_LIST_TAIL, "STACK_TYPE", PS_META_REPLACE, "Stack Type", "DEEP_STACK");
     }
 
Index: trunk/ppStack/src/ppStackCamera.c
===================================================================
--- trunk/ppStack/src/ppStackCamera.c	(revision 30394)
+++ trunk/ppStack/src/ppStackCamera.c	(revision 30620)
@@ -1,12 +1,2 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <string.h>
-#include <pslib.h>
-#include <psmodules.h>
-#include <psphot.h>
-
 #include "ppStack.h"
 
Index: trunk/ppStack/src/ppStackCleanup.c
===================================================================
--- trunk/ppStack/src/ppStackCleanup.c	(revision 30394)
+++ trunk/ppStack/src/ppStackCleanup.c	(revision 30620)
@@ -1,16 +1,6 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
+#include "ppStack.h"
 
-#include <stdio.h>
-#include <pslib.h>
-#include <psmodules.h>
-#include <ppStats.h>
-
-#include "ppStack.h"
-#include "ppStackLoop.h"
-
-
-bool ppStackCleanup(ppStackThreadData *stack, ppStackOptions *options, pmConfig *config)
+// ppStackCleanupFiles 
+bool ppStackCleanupFiles(ppStackThreadData *stack, ppStackOptions *options, pmConfig *config, ppStackFileList stackFiles, ppStackFileList photFiles, bool closeJPEGs)
 {
     psAssert(stack, "Require stack");
@@ -18,78 +8,30 @@
     psAssert(config, "Require configuration");
 
-    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
-    psAssert(recipe, "We've thrown an error on this before.");
-
-#if 0
-    // Ensure masked regions really look masked
-    {
-        psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits for bad
-        psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
-        if (!pmReadoutMaskApply(options->outRO, maskBad)) {
-            psWarning("Unable to apply mask");
-        }
-    }
-#endif
-
-    // Generate binned JPEGs
-    {
-        int bin1 = psMetadataLookupS32(NULL, recipe, "BIN1"); // First binning level
-        int bin2 = psMetadataLookupS32(NULL, recipe, "BIN2"); // Second binning level
-
-        // Target cells
-        pmFPAview *view = pmFPAviewAlloc(0); // View to cells of interest
-        view->chip = view->cell = 0;
-        pmCell *cell1 = pmFPAfileThisCell(config->files, view, "PPSTACK.OUTPUT.JPEG1");
-        pmCell *cell2 = pmFPAfileThisCell(config->files, view, "PPSTACK.OUTPUT.JPEG2");
-        psImageMaskType maskValue = pmConfigMaskGet("BLANK", config); // Bits to mask
-        psFree(view);
-
-        pmReadout *ro1 = pmReadoutAlloc(cell1), *ro2 = pmReadoutAlloc(cell2); // Binned readouts
-        if (!pmReadoutRebin(ro1, options->outRO, maskValue, bin1, bin1) ||
-            !pmReadoutRebin(ro2, ro1, 0, bin2, bin2)) {
-            psError(PPSTACK_ERR_DATA, false, "Unable to bin output.");
-            psFree(ro1);
-            psFree(ro2);
-            return false;
-        }
-        psFree(ro1);
-        psFree(ro2);
-    }
-
-    // Statistics on output
-    if (options->stats) {
-        psTrace("ppStack", 1, "Gathering statistics on stacked image....\n");
-        psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits for bad
-        psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
-
-        pmFPAview *view = pmFPAviewAlloc(0); // View to readout
-        view->chip = view->cell = view->readout = 0;
-
-        ppStatsFPA(options->stats, options->outRO->parent->parent->parent, view, maskBad, config);
-
-        psFree(view);
-    }
-
     if (!ppStackFilesIterateUp(config)) {
         psError(psErrorCodeLast(), false, "Unable to close files.");
         return false;
     }
-    ppStackFileActivation(config, PPSTACK_FILES_STACK, false);
-    ppStackFileActivation(config, PPSTACK_FILES_PHOT, false);
+    ppStackFileActivation(config, stackFiles, false);
+    ppStackFileActivation(config, photFiles, false);
 
     // Ensure files are freed
-    {
-        options->outRO->data_exists = false;
-        options->outRO->parent->data_exists = false;
-        options->outRO->parent->parent->data_exists = false;
-        psFree(options->outRO);
-        options->outRO = NULL;
+    options->outRO->data_exists = false;
+    options->outRO->parent->data_exists = false;
+    options->outRO->parent->parent->data_exists = false;
+    psFree(options->outRO);
+    options->outRO = NULL;
 
-        options->expRO->data_exists = false;
-        options->expRO->parent->data_exists = false;
-        options->expRO->parent->parent->data_exists = false;
-        psFree(options->expRO);
-        options->expRO = NULL;
+    options->expRO->data_exists = false;
+    options->expRO->parent->data_exists = false;
+    options->expRO->parent->parent->data_exists = false;
+    psFree(options->expRO);
+    options->expRO = NULL;
 
+    for (int i = 0; i < options->num; i++) {
+        pmCellFreeData(options->cells->data[i]);
+    }
+
+    if (closeJPEGs) {
+	// XXX move these close / free operations to the jpeg creation function
         pmFPAview *view = pmFPAviewAlloc(0);// Pointer into FPA hierarchy
         view->chip = view->cell = 0;        // pmFPAviewFreeData doesn't want to deal with readouts
@@ -118,2 +60,70 @@
     return true;
 }
+
+bool ppStackCleanup (pmConfig *config, ppStackOptions *options) {
+
+    psExit exitValue = ppStackExitCode(PS_EXIT_SUCCESS); // Exit code
+
+    // Ensure everything closes
+    if (config) {
+	ppStackFileActivation(config, PPSTACK_FILES_PREPARE, true);
+	ppStackFileActivation(config, PPSTACK_FILES_CONVOLVE, true);
+	ppStackFileActivation(config, PPSTACK_FILES_STACK, true);
+	ppStackFileActivation(config, PPSTACK_FILES_UNCONV, true);
+	ppStackFileActivation(config, PPSTACK_FILES_PHOT, true);
+	if (!ppStackFilesIterateUp(config)) {
+	    psError(psErrorCodeLast(), false, "Unable to close files.");
+	    exitValue = ppStackExitCode(exitValue);
+	    pmFPAfileFreeSetStrict(false);
+	}
+    }
+
+    // Write out summary statistics
+    if (options && options->stats) {
+
+	psMetadataAddS32(options->stats, PS_LIST_TAIL, "QUALITY", PS_META_REPLACE, "Bad data quality flag", options->quality);
+	psMetadataAddF32(options->stats, PS_LIST_TAIL, "TIME_STACK", 0, "Total time", psTimerClear("PPSTACK_TOTAL"));
+
+	const char *statsMDC = psMetadataConfigFormat(options->stats);
+	if (!statsMDC || strlen(statsMDC) == 0) {
+	    psError(PS_ERR_IO, false, "Unable to get statistics MDC file.");
+	    exitValue = ppStackExitCode(exitValue);
+	    exit(exitValue);
+	}
+	if (fprintf(options->statsFile, "%s", statsMDC) != strlen(statsMDC)) {
+	    psError(PS_ERR_IO, false, "Unable to write statistics MDC file.");
+	    exitValue = ppStackExitCode(exitValue);
+	    exit(exitValue);
+	}
+	psFree(statsMDC);
+	if (fclose(options->statsFile) == EOF) {
+	    psError(PS_ERR_IO, false, "Unable to close statistics MDC file.");
+	    exitValue = ppStackExitCode(exitValue);
+	    exit(exitValue);
+	}
+	options->statsFile = NULL;
+	pmConfigRunFilenameAddWrite(config, "STATS", psMetadataLookupStr(NULL, config->arguments, "STATS"));
+    }
+    psFree(options);
+
+    // Dump configuration
+    bool mdok;                                                                    // Status of MD lookup
+    psString dump = psMetadataLookupStr(&mdok, config->arguments, "DUMP_CONFIG"); // File for config
+    if (dump && !pmConfigDump(config, dump)) {
+	psError(psErrorCodeLast(), false, "Unable to dump configuration.");
+	exitValue = ppStackExitCode(exitValue);
+    }
+
+    psTrace("ppStack", 1, "Finished at %f sec\n", psTimerMark("PPSTACK"));
+    psTimerStop();
+
+    psFree(config);
+    pmModelClassCleanup();
+    pmConfigDone();
+    psLibFinalize();
+    pmVisualClose();
+    pmVisualCleanup ();
+
+    exitValue = ppStackExitCode(exitValue);
+    exit(exitValue);
+}
Index: trunk/ppStack/src/ppStackCombineFinal.c
===================================================================
--- trunk/ppStack/src/ppStackCombineFinal.c	(revision 30394)
+++ trunk/ppStack/src/ppStackCombineFinal.c	(revision 30620)
@@ -1,14 +1,3 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <pslib.h>
-#include <psmodules.h>
-
 #include "ppStack.h"
-#include "ppStackLoop.h"
-
-#define WCS_TOLERANCE 0.001             // Tolerance for WCS
 
 //#define TESTING                         // Enable test output
@@ -107,53 +96,4 @@
     }
 
-    // Propagate WCS
-    bool wcsDone = false;           // Have we done the WCS?
-    for (int i = 0; i < options->num && !wcsDone; i++) {
-        if (options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
-            continue;
-        }
-
-        ppStackThread *thread = stack->threads->data[0]; // Representative stack
-        pmReadout *inRO = thread->readouts->data[i]; // Template readout
-        if (inRO && !wcsDone) {
-            // Copy astrometry over
-            wcsDone = true;
-            pmHDU *inHDU = pmHDUFromCell(inRO->parent); // Template HDU
-            pmHDU *outHDU = pmHDUFromCell(outRO->parent); // Output HDU
-            pmChip *outChip = outRO->parent->parent; // Output chip
-            pmFPA *outFPA = outChip->parent; // Output FPA
-            if (!outHDU || !inHDU) {
-                psWarning("Unable to find HDU at FPA level to copy astrometry.");
-            } else {
-                if (!pmAstromReadWCS(outFPA, outChip, inHDU->header, 1.0)) {
-                    psErrorClear();
-                    psWarning("Unable to read WCS astrometry from input FPA.");
-                    wcsDone = false;
-                } else {
-                    if (!outHDU->header) {
-                        outHDU->header = psMetadataAlloc();
-                    }
-                    if (!pmAstromWriteWCS(outHDU->header, outFPA, outChip, WCS_TOLERANCE)) {
-                        psErrorClear();
-                        psWarning("Unable to write WCS astrometry to output FPA.");
-                        wcsDone = false;
-                    }
-                }
-            }
-        }
-    }
-
-    // Put version information into the header
-    pmHDU *hdu = pmHDUFromCell(outRO->parent);
-    if (!hdu) {
-        psError(PPSTACK_ERR_PROG, false, "Unable to find HDU for output.");
-        return false;
-    }
-    if (!hdu->header) {
-        hdu->header = psMetadataAlloc();
-    }
-    ppStackVersionHeader(hdu->header);
-
-
 #ifdef TESTING
     static int pass = 0;                // Pass through
Index: trunk/ppStack/src/ppStackCombineInitial.c
===================================================================
--- trunk/ppStack/src/ppStackCombineInitial.c	(revision 30394)
+++ trunk/ppStack/src/ppStackCombineInitial.c	(revision 30620)
@@ -1,12 +1,3 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <pslib.h>
-#include <psmodules.h>
-
 #include "ppStack.h"
-#include "ppStackLoop.h"
 
 //#define TESTING                         // Enable test output
Index: trunk/ppStack/src/ppStackCombinePrepare.c
===================================================================
--- trunk/ppStack/src/ppStackCombinePrepare.c	(revision 30394)
+++ trunk/ppStack/src/ppStackCombinePrepare.c	(revision 30620)
@@ -1,12 +1,3 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <pslib.h>
-#include <psmodules.h>
-
 #include "ppStack.h"
-#include "ppStackLoop.h"
 
 bool ppStackCombinePrepare(const char *outName, const char *expName,
Index: trunk/ppStack/src/ppStackConvolve.c
===================================================================
--- trunk/ppStack/src/ppStackConvolve.c	(revision 30394)
+++ trunk/ppStack/src/ppStackConvolve.c	(revision 30620)
@@ -1,15 +1,5 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <pslib.h>
-#include <psmodules.h>
-
 #include "ppStack.h"
-#include "ppStackLoop.h"
 
 //#define TESTING
-
 
 // Update the value of a concept
@@ -20,6 +10,4 @@
     item->data.F32 = VALUE; \
 }
-
-
 
 bool ppStackConvolve(ppStackOptions *options, pmConfig *config)
@@ -69,6 +57,5 @@
         ppStackFileActivationSingle(config, PPSTACK_FILES_CONVOLVE, true, i);
         if (options->convolve) {
-            // XXX PPSTACK.CONV.KERNEL not defined unless convolve
-            // pmFPAfileActivate(config->files, true, "PPSTACK.CONV.KERNEL");
+            // PPSTACK.CONV.KERNEL not defined unless convolve
             pmFPAfileActivateSingle(config->files, true, "PPSTACK.CONV.KERNEL", i); // Activated file
         }
@@ -191,5 +178,5 @@
         }
         psFree(maskHeader);
-        if (!ppStackWriteImage(options->convVariances->data[i], hdu->header, readout->variance, config)) {
+        if (!ppStackWriteVariance(options->convVariances->data[i], hdu->header, readout->variance, readout->covariance->image, config)) {
             psError(PPSTACK_ERR_IO, false, "Unable to write convolved variance %d", i);
             psFree(fpaList);
Index: trunk/ppStack/src/ppStackErrorCodes.c.in
===================================================================
--- trunk/ppStack/src/ppStackErrorCodes.c.in	(revision 30394)
+++ trunk/ppStack/src/ppStackErrorCodes.c.in	(revision 30620)
@@ -1,2 +1,4 @@
+#include "ppStack.h"
+
 /** @file ppStackErrorCodes.c.in
  *
@@ -10,7 +12,4 @@
  *  Copyright 2009 Institute for Astronomy, University of Hawaii
  */
-
-#include "pslib.h"
-#include "ppStackErrorCodes.h"
 
 /*
Index: trunk/ppStack/src/ppStackFiles.c
===================================================================
--- trunk/ppStack/src/ppStackFiles.c	(revision 30394)
+++ trunk/ppStack/src/ppStackFiles.c	(revision 30620)
@@ -1,15 +1,8 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <unistd.h>
-#include <pslib.h>
-#include <psmodules.h>
-
 #include "ppStack.h"
 
-
 // Here follows lists of files for activation/deactivation at various stages.  Each must be NULL-terminated.
+
+/// NOP list
+static char *filesNOP[] = { NULL };
 
 /// Files required in preparation for convolution
@@ -41,4 +34,5 @@
 {
     switch (list) {
+      case PPSTACK_FILES_NONE:     return filesNOP;
       case PPSTACK_FILES_PREPARE:  return filesPrepare;
       case PPSTACK_FILES_TARGET:   return filesTarget;
@@ -206,2 +200,46 @@
     return true;
 }
+
+// Write an image to a FITS file
+bool ppStackWriteVariance(const char *name, // Name of image
+			  psMetadata *header, // Header
+			  const psImage *variance, // Variance
+			  const psImage *covariance, // Variance
+			  pmConfig *config // Configuration
+    )
+{
+    assert(name);
+    assert(variance);
+
+    psString resolved = pmConfigConvertFilename(name, config, true, true); // Resolved file name
+    psFits *fits = psFitsOpen(resolved, "w");
+    if (!fits) {
+        psError(PPSTACK_ERR_IO, false, "Unable to open FITS file %s to write image.", resolved);
+        psFree(resolved);
+        return false;
+    }
+    if (!psFitsWriteImage(fits, header, variance, 0, NULL)) {
+        psError(PPSTACK_ERR_IO, false, "Unable to write FITS image %s.", resolved);
+        psFitsClose(fits);
+        psFree(resolved);
+        return false;
+    }
+    if (covariance) {
+	psMetadata *tmphead = psMetadataAlloc();
+	psMetadataAddS32(tmphead, PS_LIST_TAIL, "COVARIANCE.CENTRE.X", PS_META_REPLACE, "center", (int)(covariance->numCols / 2));
+	psMetadataAddS32(tmphead, PS_LIST_TAIL, "COVARIANCE.CENTRE.Y", PS_META_REPLACE, "center", (int)(covariance->numRows / 2));
+	if (!psFitsWriteImage(fits, tmphead, covariance, 0, "COVAR_SkyChip_SkyCell")) {
+	    psError(PPSTACK_ERR_IO, false, "Unable to write FITS image %s.", resolved);
+	    psFitsClose(fits);
+	    psFree(resolved);
+	    return false;
+	}
+    }
+    if (!psFitsClose(fits)) {
+        psError(PPSTACK_ERR_IO, false, "Unable to close FITS image %s.", resolved);
+        psFree(resolved);
+        return false;
+    }
+    psFree(resolved);
+    return true;
+}
Index: trunk/ppStack/src/ppStackFinish.c
===================================================================
--- trunk/ppStack/src/ppStackFinish.c	(revision 30394)
+++ trunk/ppStack/src/ppStackFinish.c	(revision 30620)
@@ -1,15 +1,5 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
+#include "ppStack.h"
 
-#include <stdio.h>
-#include <unistd.h>
-#include <pslib.h>
-#include <psmodules.h>
-#include <psphot.h>
-
-#include "ppStack.h"
-#include "ppStackLoop.h"
-
+// this function unlinks the temporary files (if desired)
 bool ppStackFinish(ppStackOptions *options, pmConfig *config)
 {
@@ -32,10 +22,8 @@
             }
 
-            psString imageResolved = pmConfigConvertFilename(options->convImages->data[i],
-                                                             config, false, false);
-            psString maskResolved = pmConfigConvertFilename(options->convMasks->data[i],
-                                                            config, false, false);
-            psString varianceResolved = pmConfigConvertFilename(options->convVariances->data[i],
-                                                                config, false, false);
+	    // XXX careful about repeatative resolution of nebulous names (though these are probably not neb names)
+            psString imageResolved = pmConfigConvertFilename(options->convImages->data[i], config, false, false);
+            psString maskResolved = pmConfigConvertFilename(options->convMasks->data[i], config, false, false);
+            psString varianceResolved = pmConfigConvertFilename(options->convVariances->data[i], config, false, false);
             if (unlink(imageResolved) == -1 || unlink(maskResolved) == -1 ||
                 unlink(varianceResolved) == -1) {
@@ -47,6 +35,4 @@
         }
     }
-
-
     return true;
 }
@@ -60,54 +46,56 @@
 
     psErrorCode errorCode = psErrorCodeLast(); // Error code
-    if (errorCode != PS_ERR_NONE) {
-        psErrorStackPrint(stderr, "Unable to perform stack.");
-        pmFPAfileFreeSetStrict(false);
-        switch (errorCode) {
-          case PPSTACK_ERR_UNKNOWN:
-          case PS_ERR_UNKNOWN:
-            psLogMsg("ppStack", PS_LOG_WARN, "Unknown error code: %x", errorCode);
-            exitValue = PS_EXIT_UNKNOWN_ERROR;
-            break;
-          case PS_ERR_IO:
-          case PS_ERR_DB_CLIENT:
-          case PS_ERR_DB_SERVER:
-          case PS_ERR_BAD_FITS:
-          case PS_ERR_OS_CALL_FAILED:
-          case PM_ERR_SYS:
-          case PPSTACK_ERR_IO:
-            psLogMsg("ppStack", PS_LOG_WARN, "I/O error code: %x", errorCode);
-            exitValue = PS_EXIT_SYS_ERROR;
-            break;
-          case PS_ERR_BAD_PARAMETER_VALUE:
-          case PS_ERR_BAD_PARAMETER_TYPE:
-          case PS_ERR_BAD_PARAMETER_NULL:
-          case PS_ERR_BAD_PARAMETER_SIZE:
-          case PPSTACK_ERR_ARGUMENTS:
-          case PPSTACK_ERR_CONFIG:
-            psLogMsg("ppStack", PS_LOG_WARN, "Configuration error code: %x", errorCode);
-            exitValue = PS_EXIT_CONFIG_ERROR;
-            break;
-          case PPSTACK_ERR_PSF:
-          case PSPHOT_ERR_PSF:
-          case PM_ERR_STAMPS:
-          case PM_ERR_SMALL_AREA:
-          case PPSTACK_ERR_REJECTED:
-          case PPSTACK_ERR_DATA:
-            psLogMsg("ppStack", PS_LOG_WARN, "Data error code: %x", errorCode);
-            exitValue = PS_EXIT_DATA_ERROR;
-            break;
-          case PS_ERR_UNEXPECTED_NULL:
-          case PS_ERR_PROGRAMMING:
-          case PPSTACK_ERR_NOT_IMPLEMENTED:
-          case PPSTACK_ERR_PROG:
-            psLogMsg("ppStack", PS_LOG_WARN, "Programming error code: %x", errorCode);
-            exitValue = PS_EXIT_PROG_ERROR;
-            break;
-          default:
-            // It's a programming error if we're not dealing with the error correctly
-            psLogMsg("ppStack", PS_LOG_WARN, "Unrecognised error code: %x", errorCode);
-            exitValue = PS_EXIT_PROG_ERROR;
-            break;
-        }
+    if (errorCode == PS_ERR_NONE) {
+	return exitValue;
+    }
+
+    psErrorStackPrint(stderr, "Unable to perform stack.");
+    pmFPAfileFreeSetStrict(false);
+    switch (errorCode) {
+      case PPSTACK_ERR_UNKNOWN:
+      case PS_ERR_UNKNOWN:
+	psLogMsg("ppStack", PS_LOG_WARN, "Unknown error code: %x", errorCode);
+	exitValue = PS_EXIT_UNKNOWN_ERROR;
+	break;
+      case PS_ERR_IO:
+      case PS_ERR_DB_CLIENT:
+      case PS_ERR_DB_SERVER:
+      case PS_ERR_BAD_FITS:
+      case PS_ERR_OS_CALL_FAILED:
+      case PM_ERR_SYS:
+      case PPSTACK_ERR_IO:
+	psLogMsg("ppStack", PS_LOG_WARN, "I/O error code: %x", errorCode);
+	exitValue = PS_EXIT_SYS_ERROR;
+	break;
+      case PS_ERR_BAD_PARAMETER_VALUE:
+      case PS_ERR_BAD_PARAMETER_TYPE:
+      case PS_ERR_BAD_PARAMETER_NULL:
+      case PS_ERR_BAD_PARAMETER_SIZE:
+      case PPSTACK_ERR_ARGUMENTS:
+      case PPSTACK_ERR_CONFIG:
+	psLogMsg("ppStack", PS_LOG_WARN, "Configuration error code: %x", errorCode);
+	exitValue = PS_EXIT_CONFIG_ERROR;
+	break;
+      case PPSTACK_ERR_PSF:
+      case PSPHOT_ERR_PSF:
+      case PM_ERR_STAMPS:
+      case PM_ERR_SMALL_AREA:
+      case PPSTACK_ERR_REJECTED:
+      case PPSTACK_ERR_DATA:
+	psLogMsg("ppStack", PS_LOG_WARN, "Data error code: %x", errorCode);
+	exitValue = PS_EXIT_DATA_ERROR;
+	break;
+      case PS_ERR_UNEXPECTED_NULL:
+      case PS_ERR_PROGRAMMING:
+      case PPSTACK_ERR_NOT_IMPLEMENTED:
+      case PPSTACK_ERR_PROG:
+	psLogMsg("ppStack", PS_LOG_WARN, "Programming error code: %x", errorCode);
+	exitValue = PS_EXIT_PROG_ERROR;
+	break;
+      default:
+	// It's a programming error if we're not dealing with the error correctly
+	psLogMsg("ppStack", PS_LOG_WARN, "Unrecognised error code: %x", errorCode);
+	exitValue = PS_EXIT_PROG_ERROR;
+	break;
     }
     return exitValue;
Index: trunk/ppStack/src/ppStackJPEGs.c
===================================================================
--- trunk/ppStack/src/ppStackJPEGs.c	(revision 30620)
+++ trunk/ppStack/src/ppStackJPEGs.c	(revision 30620)
@@ -0,0 +1,36 @@
+#include "ppStack.h"
+
+bool ppStackJPEGs(ppStackThreadData *stack, ppStackOptions *options, pmConfig *config)
+{
+    psAssert(stack, "Require stack");
+    psAssert(options, "Require options");
+    psAssert(config, "Require configuration");
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
+    psAssert(recipe, "We've thrown an error on this before.");
+
+    // Generate binned JPEGs
+    int bin1 = psMetadataLookupS32(NULL, recipe, "BIN1"); // First binning level
+    int bin2 = psMetadataLookupS32(NULL, recipe, "BIN2"); // Second binning level
+
+    // Target cells
+    pmFPAview *view = pmFPAviewAlloc(0); // View to cells of interest
+    view->chip = view->cell = 0;
+    pmCell *cell1 = pmFPAfileThisCell(config->files, view, "PPSTACK.OUTPUT.JPEG1");
+    pmCell *cell2 = pmFPAfileThisCell(config->files, view, "PPSTACK.OUTPUT.JPEG2");
+    psImageMaskType maskValue = pmConfigMaskGet("BLANK", config); // Bits to mask
+    psFree(view);
+
+    pmReadout *ro1 = pmReadoutAlloc(cell1), *ro2 = pmReadoutAlloc(cell2); // Binned readouts
+    if (!pmReadoutRebin(ro1, options->outRO, maskValue, bin1, bin1) ||
+	!pmReadoutRebin(ro2, ro1, 0, bin2, bin2)) {
+	psError(PPSTACK_ERR_DATA, false, "Unable to bin output.");
+	psFree(ro1);
+	psFree(ro2);
+	return false;
+    }
+    psFree(ro1);
+    psFree(ro2);
+
+    return true;
+}
Index: trunk/ppStack/src/ppStackLoop.c
===================================================================
--- trunk/ppStack/src/ppStackLoop.c	(revision 30394)
+++ trunk/ppStack/src/ppStackLoop.c	(revision 30620)
@@ -1,17 +1,219 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <pslib.h>
-#include <psmodules.h>
-
 #include "ppStack.h"
-#include "ppStackLoop.h"
+
+// static functions are defined below
+static int stackSummary(const ppStackOptions *options, const char *place);
+
+bool ppStackLoop(pmConfig *config, ppStackOptions *options)
+{
+    assert(config);
+
+    psTimerStart("PPSTACK_TOTAL");
+    psTimerStart("PPSTACK_STEPS");
+
+    // Setup
+    psTrace("ppStack", 1, "Setup....\n");
+    if (!ppStackSetup(options, config)) {
+        psError(psErrorCodeLast(), false, "Unable to setup.");
+        return false;
+    }
+    psLogMsg("ppStack", PS_LOG_INFO, "Stage 0: Setup: %f sec", psTimerClear("PPSTACK_STEPS"));
+    ppStackMemDump("setup");
+
+    // Preparation for stacking
+    psTrace("ppStack", 1, "Preparation for stacking: merging sources, determining target PSF....\n");
+
+    if (!ppStackPrepare(options, config)) {
+        psError(psErrorCodeLast(), false, "Unable to prepare for stacking.");
+        return false;
+    }
+    psLogMsg("ppStack", PS_LOG_INFO, "Stage 1: Load Sources and Generate Target PSF: %f sec", psTimerClear("PPSTACK_STEPS"));
+    ppStackMemDump("prepare");
+    if (options->quality) return true; // Can't do anything else
+
+    // Convolve inputs
+    psTrace("ppStack", 1, "Convolving inputs to target PSF....\n");
+    if (!ppStackConvolve(options, config)) {
+        psError(psErrorCodeLast(), false, "Unable to convolve images.");
+        return false;
+    }
+    psLogMsg("ppStack", PS_LOG_INFO, "Stage 2: Generate Convolutions and Save: %f sec", psTimerClear("PPSTACK_STEPS"));
+    ppStackMemDump("convolve");
+    if (options->quality) return true; // Can't do anything else
+
+    // Ensure sufficient inputs
+    {
+        int numGood = stackSummary(options, "initial combination");
+        psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
+        bool safe = psMetadataLookupBool(NULL, recipe, "SAFE"); // Be safe when combining
+        if (safe && numGood <= 1) {
+            options->quality = PPSTACK_ERR_REJECTED;
+            psErrorStackPrint(stderr, "Insufficient inputs for combination with safety on");
+            psErrorClear();
+            psWarning("Insufficient inputs for combination with safety on");
+            return true;
+        }
+    }
+
+    // Start threading
+    ppStackThreadInit();
+    ppStackThreadData *stack = ppStackThreadDataSetup(options, config, true);
+    if (!stack) {
+        psError(psErrorCodeLast(), false, "Unable to initialise stack threads.");
+        return false;
+    }
+
+    // Prepare for combination
+    if (!ppStackCombinePrepare("PPSTACK.OUTPUT", "PPSTACK.OUTPUT.EXP", PPSTACK_FILES_STACK, stack, options, config)) {
+        psError(psErrorCodeLast(), false, "Unable to prepare for combination.");
+        psFree(stack);
+        return false;
+    }
+
+    // Initial combination
+    psTrace("ppStack", 1, "Initial stack of convolved images....\n");
+    if (!ppStackCombineInitial(stack, options, config)) {
+        psError(psErrorCodeLast(), false, "Unable to perform initial combination.");
+        psFree(stack);
+        return false;
+    }
+    ppStackMemDump("initial");
+    psLogMsg("ppStack", PS_LOG_INFO, "Stage 3: Make Initial Stack: %f sec", psTimerClear("PPSTACK_STEPS"));
+
+    // Done with stack inputs for now
+    // XXX is this where we are leaking??
+    for (int i = 0; i < options->num; i++) {
+        pmCellFreeData(options->cells->data[i]);
+    }
+    psFree(stack);
+
+    // Pixel rejection
+    psTrace("ppStack", 1, "Reject pixels....\n");
+    if (!ppStackReject(options, config)) {
+        psError(psErrorCodeLast(), false, "Unable to reject pixels.");
+        psFree(stack);
+        return false;
+    }
+    psLogMsg("ppStack", PS_LOG_INFO, "Stage 4: Pixel Rejection: %f sec", psTimerClear("PPSTACK_STEPS"));
+
+    // Check inputs
+    {
+        int numGood = stackSummary(options, "final combination");
+        if (numGood <= 0) {
+            options->quality = PPSTACK_ERR_REJECTED;
+            psErrorStackPrint(stderr, "Insufficient inputs survived rejection stage");
+            psErrorClear();
+            psWarning("Insufficient inputs survived rejection stage");
+            return true;
+        }
+    }
+
+    stack = ppStackThreadDataSetup(options, config, true);
+    if (!stack) {
+        psError(psErrorCodeLast(), false, "Unable to initialise stack threads.");
+        return false;
+    }
+
+    // Final combination
+    psTrace("ppStack", 2, "Final stack of convolved images....\n");
+    if (!ppStackCombineFinal(stack, options->convCovars, options, config, false, false, true)) {
+        psError(psErrorCodeLast(), false, "Unable to perform final combination.");
+        psFree(stack);
+        return false;
+    }
+    psLogMsg("ppStack", PS_LOG_INFO, "Stage 5: Final Stack: %f sec", psTimerClear("PPSTACK_STEPS"));
+    ppStackMemDump("final");
+
+    // Photometry
+    psTrace("ppStack", 1, "Photometering stacked image....\n");
+    if (!ppStackPhotometry(options, config)) {
+        psError(psErrorCodeLast(), false, "Unable to perform photometry.");
+        return false;
+    }
+    psLogMsg("ppStack", PS_LOG_INFO, "Stage 6: Photometry Analysis: %f sec", psTimerClear("PPSTACK_STEPS"));
+    ppStackMemDump("photometry");
+
+    // Update Header
+    if (!ppStackUpdateHeader(stack, options, config)) {
+        psError(psErrorCodeLast(), false, "Unable to update header.");
+        psFree(stack);
+        return false;
+    }
+    // Create JPEGS
+    if (!ppStackJPEGs(stack, options, config)) {
+        psError(psErrorCodeLast(), false, "Unable to make jpegs.");
+        psFree(stack);
+        return false;
+    }
+    // Assemble Stats
+    if (!ppStackStats(stack, options, config)) {
+        psError(psErrorCodeLast(), false, "Unable to assemble statistics.");
+        psFree(stack);
+        return false;
+    }
+
+   // Clean up
+    psTrace("ppStack", 2, "Cleaning up after combination....\n");
+    if (!ppStackCleanupFiles(stack, options, config, PPSTACK_FILES_STACK, PPSTACK_FILES_PHOT, true)) {
+        psError(psErrorCodeLast(), false, "Unable to clean up.");
+        psFree(stack);
+        return false;
+    }
+    psFree(stack);
+    psLogMsg("ppStack", PS_LOG_INFO, "Stage 7: Cleanup, WCS & JPEGS: %f sec", psTimerClear("PPSTACK_STEPS"));
+    ppStackMemDump("cleanup");
+
+    // Unconvolved stack --- it's cheap to calculate, compared to everything else!
+    // XXX unconvolved stack is currently using the convolved mask!  oops!
+    if (options->convolve) {
+        // Start threading
+        ppStackThreadData *stack = ppStackThreadDataSetup(options, config, false);
+        if (!stack) {
+            psError(psErrorCodeLast(), false, "Unable to initialise stack threads.");
+            return false;
+        }
+
+        // Prepare for combination
+        if (!ppStackCombinePrepare("PPSTACK.UNCONV", "PPSTACK.UNCONV.EXP", PPSTACK_FILES_UNCONV,
+                                   stack, options, config)) {
+            psError(psErrorCodeLast(), false, "Unable to prepare for combination.");
+            psFree(stack);
+            return false;
+        }
+
+	// generate the unconvolved stack
+        psTrace("ppStack", 2, "Stack of unconvolved images....\n");
+        if (!ppStackCombineFinal(stack, options->origCovars, options, config,
+                                 false, true, false)) {
+            psError(psErrorCodeLast(), false, "Unable to perform unconvolved combination.");
+            psFree(stack);
+            return false;
+        }
+        psLogMsg("ppStack", PS_LOG_INFO, "Stage 8: Unconvolved Stack: %f sec", psTimerClear("PPSTACK_STEPS"));
+        ppStackMemDump("unconv");
+
+	// Clean up unconvolved stack
+	psTrace("ppStack", 2, "Cleaning up after unconvolved stack....\n");
+	if (!ppStackCleanupFiles(stack, options, config, PPSTACK_FILES_UNCONV, PPSTACK_FILES_NONE, false)) {
+	    psError(psErrorCodeLast(), false, "Unable to clean up.");
+	    psFree(stack);
+	    return false;
+	}
+	psFree(stack);
+    }
+    psFree(options->cells); options->cells = NULL;
+
+    // Finish up
+    psTrace("ppStack", 1, "Finishing up....\n");
+    if (!ppStackFinish(options, config)) {
+        psError(psErrorCodeLast(), false, "Unable to finish up.");
+        return false;
+    }
+    ppStackMemDump("finish");
+
+    return true;
+}
 
 /// Print a summary of the inputs, and return the number of good inputs
-static int stackSummary(const ppStackOptions *options, // Stack options, with input mask
-                        const char *place              // Place in code
-    )
+static int stackSummary(const ppStackOptions *options, const char *place)
 {
     int numGood = 0;                // Number of good inputs
@@ -47,221 +249,2 @@
     return numGood;
 }
-
-
-
-bool ppStackLoop(pmConfig *config, ppStackOptions *options)
-{
-    assert(config);
-
-    psTimerStart("PPSTACK_TOTAL");
-    psTimerStart("PPSTACK_STEPS");
-
-    // Setup
-    psTrace("ppStack", 1, "Setup....\n");
-    if (!ppStackSetup(options, config)) {
-        psError(psErrorCodeLast(), false, "Unable to setup.");
-        return false;
-    }
-    psLogMsg("ppStack", PS_LOG_INFO, "Stage 0: Setup: %f sec", psTimerClear("PPSTACK_STEPS"));
-    ppStackMemDump("setup");
-
-
-    // Preparation for stacking
-    psTrace("ppStack", 1, "Preparation for stacking: merging sources, determining target PSF....\n");
-    if (!ppStackPrepare(options, config)) {
-        psError(psErrorCodeLast(), false, "Unable to prepare for stacking.");
-        return false;
-    }
-    psLogMsg("ppStack", PS_LOG_INFO, "Stage 1: Load Sources and Generate Target PSF: %f sec",
-             psTimerClear("PPSTACK_STEPS"));
-    ppStackMemDump("prepare");
-    if (options->quality) {
-        // Can't do anything else
-        return true;
-    }
-
-    // Convolve inputs
-    psTrace("ppStack", 1, "Convolving inputs to target PSF....\n");
-    if (!ppStackConvolve(options, config)) {
-        psError(psErrorCodeLast(), false, "Unable to convolve images.");
-        return false;
-    }
-    psLogMsg("ppStack", PS_LOG_INFO, "Stage 2: Generate Convolutions and Save: %f sec",
-             psTimerClear("PPSTACK_STEPS"));
-    ppStackMemDump("convolve");
-
-    if (options->quality) {
-        // Can't do anything else
-        return true;
-    }
-
-    // Ensure sufficient inputs
-    {
-        int numGood = stackSummary(options, "initial combination");
-        psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
-        bool safe = psMetadataLookupBool(NULL, recipe, "SAFE"); // Be safe when combining
-        if (safe && numGood <= 1) {
-            options->quality = PPSTACK_ERR_REJECTED;
-            psErrorStackPrint(stderr, "Insufficient inputs for combination with safety on");
-            psErrorClear();
-            psWarning("Insufficient inputs for combination with safety on");
-            return true;
-        }
-    }
-
-    // Start threading
-    ppStackThreadInit();
-    ppStackThreadData *stack = ppStackThreadDataSetup(options, config, true);
-    if (!stack) {
-        psError(psErrorCodeLast(), false, "Unable to initialise stack threads.");
-        return false;
-    }
-
-    // Prepare for combination
-    if (!ppStackCombinePrepare("PPSTACK.OUTPUT", "PPSTACK.OUTPUT.EXP", PPSTACK_FILES_STACK,
-                               stack, options, config)) {
-        psError(psErrorCodeLast(), false, "Unable to prepare for combination.");
-        psFree(stack);
-        return false;
-    }
-
-    // Initial combination
-    psTrace("ppStack", 1, "Initial stack of convolved images....\n");
-    if (!ppStackCombineInitial(stack, options, config)) {
-        psError(psErrorCodeLast(), false, "Unable to perform initial combination.");
-        psFree(stack);
-        return false;
-    }
-    ppStackMemDump("initial");
-    psLogMsg("ppStack", PS_LOG_INFO, "Stage 3: Make Initial Stack: %f sec", psTimerClear("PPSTACK_STEPS"));
-
-    // Done with stack inputs for now
-    for (int i = 0; i < options->num; i++) {
-        pmCellFreeData(options->cells->data[i]);
-    }
-    psFree(stack);
-
-    // Pixel rejection
-    psTrace("ppStack", 1, "Reject pixels....\n");
-    if (!ppStackReject(options, config)) {
-        psError(psErrorCodeLast(), false, "Unable to reject pixels.");
-        psFree(stack);
-        return false;
-    }
-    psLogMsg("ppStack", PS_LOG_INFO, "Stage 4: Pixel Rejection: %f sec", psTimerClear("PPSTACK_STEPS"));
-
-    // Check inputs
-    {
-        int numGood = stackSummary(options, "final combination");
-        if (numGood <= 0) {
-            options->quality = PPSTACK_ERR_REJECTED;
-            psErrorStackPrint(stderr, "Insufficient inputs survived rejection stage");
-            psErrorClear();
-            psWarning("Insufficient inputs survived rejection stage");
-            return true;
-        }
-    }
-
-    stack = ppStackThreadDataSetup(options, config, true);
-    if (!stack) {
-        psError(psErrorCodeLast(), false, "Unable to initialise stack threads.");
-        return false;
-    }
-
-    // Final combination
-    psTrace("ppStack", 2, "Final stack of convolved images....\n");
-    if (!ppStackCombineFinal(stack, options->convCovars, options, config, false, false, true)) {
-        psError(psErrorCodeLast(), false, "Unable to perform final combination.");
-        psFree(stack);
-        return false;
-    }
-    psLogMsg("ppStack", PS_LOG_INFO, "Stage 5: Final Stack: %f sec", psTimerClear("PPSTACK_STEPS"));
-    ppStackMemDump("final");
-
-    // Photometry
-    psTrace("ppStack", 1, "Photometering stacked image....\n");
-    if (!ppStackPhotometry(options, config)) {
-        psError(psErrorCodeLast(), false, "Unable to perform photometry.");
-        return false;
-    }
-    psLogMsg("ppStack", PS_LOG_INFO, "Stage 6: Photometry Analysis: %f sec", psTimerClear("PPSTACK_STEPS"));
-    ppStackMemDump("photometry");
-
-    // Clean up
-    psTrace("ppStack", 2, "Cleaning up after combination....\n");
-    if (!ppStackCleanup(stack, options, config)) {
-        psError(psErrorCodeLast(), false, "Unable to clean up.");
-        psFree(stack);
-        return false;
-    }
-    for (int i = 0; i < options->num; i++) {
-        pmCellFreeData(options->cells->data[i]);
-    }
-    psFree(stack);
-    psLogMsg("ppStack", PS_LOG_INFO, "Stage 7: Cleanup, WCS & JPEGS: %f sec", psTimerClear("PPSTACK_STEPS"));
-    ppStackMemDump("cleanup");
-
-#if 1
-    // Unconvolved stack --- it's cheap to calculate, compared to everything else!
-    // XXX unconvolved stack is currently using the convolved mask!  oops!
-    if (options->convolve) {
-        // Start threading
-        ppStackThreadData *stack = ppStackThreadDataSetup(options, config, false);
-        if (!stack) {
-            psError(psErrorCodeLast(), false, "Unable to initialise stack threads.");
-            return false;
-        }
-
-        // Prepare for combination
-        if (!ppStackCombinePrepare("PPSTACK.UNCONV", "PPSTACK.UNCONV.EXP", PPSTACK_FILES_UNCONV,
-                                   stack, options, config)) {
-            psError(psErrorCodeLast(), false, "Unable to prepare for combination.");
-            psFree(stack);
-            return false;
-        }
-
-        psTrace("ppStack", 2, "Stack of unconvolved images....\n");
-        if (!ppStackCombineFinal(stack, options->origCovars, options, config,
-                                 false, true, false)) {
-            psError(psErrorCodeLast(), false, "Unable to perform unconvolved combination.");
-            psFree(stack);
-            return false;
-        }
-        psLogMsg("ppStack", PS_LOG_INFO, "Stage 8: Unconvolved Stack: %f sec", psTimerClear("PPSTACK_STEPS"));
-        ppStackMemDump("unconv");
-
-        if (!ppStackFilesIterateUp(config)) {
-            psError(psErrorCodeLast(), false, "Unable to close files.");
-            psFree(stack);
-            return false;
-        }
-        ppStackFileActivation(config, PPSTACK_FILES_UNCONV, false);
-        options->outRO->data_exists = false;
-        options->outRO->parent->data_exists = false;
-        options->outRO->parent->parent->data_exists = false;
-        psFree(options->outRO);
-        options->outRO = NULL;
-        options->expRO->data_exists = false;
-        options->expRO->parent->data_exists = false;
-        options->expRO->parent->parent->data_exists = false;
-        psFree(options->expRO);
-        options->expRO = NULL;
-
-        for (int i = 0; i < options->num; i++) {
-            pmCellFreeData(options->cells->data[i]);
-        }
-        psFree(stack);
-    }
-    psFree(options->cells); options->cells = NULL;
-#endif
-
-    // Finish up
-    psTrace("ppStack", 1, "Finishing up....\n");
-    if (!ppStackFinish(options, config)) {
-        psError(psErrorCodeLast(), false, "Unable to finish up.");
-        return false;
-    }
-    ppStackMemDump("finish");
-
-    return true;
-}
Index: trunk/ppStack/src/ppStackLoop.h
===================================================================
--- trunk/ppStack/src/ppStackLoop.h	(revision 30394)
+++ trunk/ppStack/src/ppStackLoop.h	(revision 30620)
@@ -1,12 +1,4 @@
 #ifndef PPSTACK_LOOP_H
 #define PPSTACK_LOOP_H
-
-#include <pslib.h>
-#include <psmodules.h>
-
-#include "ppStackOptions.h"
-#include "ppStackThread.h"
-#include "ppStack.h"
-
 
 // Loop over the inputs, doing the combination
@@ -71,9 +63,12 @@
 
 // Cleanup following combination
-bool ppStackCleanup(
+bool ppStackCleanupFiles(
     ppStackThreadData *stack,           // Stack
     ppStackOptions *options,            // Options
-    pmConfig *config                    // Configuration
-    );
+    pmConfig *config,			// Configuration
+    ppStackFileList stackFiles,         // cleanup these stack files
+    ppStackFileList photFiles,          // cleanup these phot files (PHOT or NOP)
+    bool closeJPEGs    			// close the jpeg files?
+);
 
 // Photometry
@@ -89,3 +84,8 @@
     );
 
+
+bool ppStackUpdateHeader(ppStackThreadData *stack, ppStackOptions *options, pmConfig *config);
+bool ppStackJPEGs(ppStackThreadData *stack, ppStackOptions *options, pmConfig *config);
+bool ppStackStats(ppStackThreadData *stack, ppStackOptions *options, pmConfig *config);
+
 #endif
Index: trunk/ppStack/src/ppStackMatch.c
===================================================================
--- trunk/ppStack/src/ppStackMatch.c	(revision 30394)
+++ trunk/ppStack/src/ppStackMatch.c	(revision 30620)
@@ -1,11 +1,2 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <pslib.h>
-#include <psmodules.h>
-#include <psphot.h>
-
 #include "ppStack.h"
 
@@ -325,15 +316,21 @@
                 // Scale the input parameters
                 psVector *widthsCopy = psVectorCopy(NULL, widths, PS_TYPE_F32); // Copy of kernel widths
-                if (scale && !pmSubtractionParamsScale(&size, &footprint, widthsCopy, scaleRef, scaleMin, scaleMax)) {
-                    psError(psErrorCodeLast(), false, "Unable to scale kernel parameters");
-                    psFree(fake);
-                    psFree(optWidths);
-                    psFree(conv);
-                    psFree(widthsCopy);
-                    if (threads > 0) {
-                        pmSubtractionThreadsFinalize();
-                    }
-                    return false;
-                }
+
+		pmSubtractionParamScaleOptions(scale, scaleRef, scaleMin, scaleMax);
+
+		// XXX EAM : the kernel scaling process has changed: the scale is now set
+		// inside pmSubtractionMatch after the normalization window is measured
+
+                // if (scale && !pmSubtractionParamsScale(&size, &footprint, widthsCopy, scaleRef, scaleMin, scaleMax)) {
+                //     psError(psErrorCodeLast(), false, "Unable to scale kernel parameters");
+                //     psFree(fake);
+                //     psFree(optWidths);
+                //     psFree(conv);
+                //     psFree(widthsCopy);
+                //     if (threads > 0) {
+                //         pmSubtractionThreadsFinalize();
+                //     }
+                //     return false;
+                // }
 
                 if (!pmSubtractionMatch(NULL, conv, fake, readout, footprint, stride, regionSize, spacing,
Index: trunk/ppStack/src/ppStackOptions.c
===================================================================
--- trunk/ppStack/src/ppStackOptions.c	(revision 30394)
+++ trunk/ppStack/src/ppStackOptions.c	(revision 30620)
@@ -1,8 +1,3 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <pslib.h>
-#include "ppStackLoop.h"
+#include "ppStack.h"
 
 static void stackOptionsFree(ppStackOptions *options)
@@ -36,4 +31,5 @@
     psFree(options->inspect);
     psFree(options->rejected);
+
     return;
 }
Index: trunk/ppStack/src/ppStackOptions.h
===================================================================
--- trunk/ppStack/src/ppStackOptions.h	(revision 30394)
+++ trunk/ppStack/src/ppStackOptions.h	(revision 30620)
@@ -1,7 +1,4 @@
 #ifndef PPSTACK_OPTIONS_H
 #define PPSTACK_OPTIONS_H
-
-#include <pslib.h>
-#include <psmodules.h>
 
 /// Options for stacking process
Index: trunk/ppStack/src/ppStackPSF.c
===================================================================
--- trunk/ppStack/src/ppStackPSF.c	(revision 30394)
+++ trunk/ppStack/src/ppStackPSF.c	(revision 30620)
@@ -1,10 +1,2 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <pslib.h>
-#include <psmodules.h>
-
 #include "ppStack.h"
 
Index: trunk/ppStack/src/ppStackPhotometry.c
===================================================================
--- trunk/ppStack/src/ppStackPhotometry.c	(revision 30394)
+++ trunk/ppStack/src/ppStackPhotometry.c	(revision 30620)
@@ -1,13 +1,3 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <pslib.h>
-#include <psmodules.h>
-#include <psphot.h>
-
 #include "ppStack.h"
-#include "ppStackLoop.h"
 
 bool ppStackPhotometry(ppStackOptions *options, pmConfig *config)
Index: trunk/ppStack/src/ppStackPrepare.c
===================================================================
--- trunk/ppStack/src/ppStackPrepare.c	(revision 30394)
+++ trunk/ppStack/src/ppStackPrepare.c	(revision 30620)
@@ -1,12 +1,3 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <pslib.h>
-#include <psmodules.h>
-
 #include "ppStack.h"
-#include "ppStackLoop.h"
 
 #define PHOT_SOURCE_MASK (PM_SOURCE_MODE_FAIL | PM_SOURCE_MODE_SATSTAR | PM_SOURCE_MODE_BLEND | \
Index: trunk/ppStack/src/ppStackReadout.c
===================================================================
--- trunk/ppStack/src/ppStackReadout.c	(revision 30394)
+++ trunk/ppStack/src/ppStackReadout.c	(revision 30620)
@@ -1,12 +1,2 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <pslib.h>
-#include <psmodules.h>
-#include <psphot.h>
-
-#include "ppStackThread.h"
 #include "ppStack.h"
 
Index: trunk/ppStack/src/ppStackReject.c
===================================================================
--- trunk/ppStack/src/ppStackReject.c	(revision 30394)
+++ trunk/ppStack/src/ppStackReject.c	(revision 30620)
@@ -1,12 +1,3 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <pslib.h>
-#include <psmodules.h>
-
 #include "ppStack.h"
-#include "ppStackLoop.h"
 
 // #define TESTING
Index: trunk/ppStack/src/ppStackSetup.c
===================================================================
--- trunk/ppStack/src/ppStackSetup.c	(revision 30394)
+++ trunk/ppStack/src/ppStackSetup.c	(revision 30620)
@@ -1,16 +1,5 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <libgen.h>
-#include <pslib.h>
-#include <psmodules.h>
-
 #include "ppStack.h"
-#include "ppStackLoop.h"
 
 #define BUFFER 16                       // Buffer for name array
-
 
 bool ppStackSetup(ppStackOptions *options, pmConfig *config)
Index: trunk/ppStack/src/ppStackSources.c
===================================================================
--- trunk/ppStack/src/ppStackSources.c	(revision 30394)
+++ trunk/ppStack/src/ppStackSources.c	(revision 30620)
@@ -1,8 +1,2 @@
-#include <stdio.h>
-#include <math.h>
-#include <string.h>
-#include <pslib.h>
-#include <psmodules.h>
-
 #include "ppStack.h"
 
Index: trunk/ppStack/src/ppStackStats.c
===================================================================
--- trunk/ppStack/src/ppStackStats.c	(revision 30620)
+++ trunk/ppStack/src/ppStackStats.c	(revision 30620)
@@ -0,0 +1,27 @@
+#include "ppStack.h"
+
+bool ppStackStats(ppStackThreadData *stack, ppStackOptions *options, pmConfig *config)
+{
+    psAssert(stack, "Require stack");
+    psAssert(options, "Require options");
+    psAssert(config, "Require configuration");
+
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSTACK_RECIPE); // ppStack recipe
+    psAssert(recipe, "We've thrown an error on this before.");
+
+    // Statistics on output
+    if (options->stats) {
+        psTrace("ppStack", 1, "Gathering statistics on stacked image....\n");
+        psString maskBadStr = psMetadataLookupStr(NULL, recipe, "MASK.BAD"); // Name of bits for bad
+        psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
+
+        pmFPAview *view = pmFPAviewAlloc(0); // View to readout
+        view->chip = view->cell = view->readout = 0;
+
+        ppStatsFPA(options->stats, options->outRO->parent->parent->parent, view, maskBad, config);
+
+        psFree(view);
+    }
+
+    return true;
+}
Index: trunk/ppStack/src/ppStackTarget.c
===================================================================
--- trunk/ppStack/src/ppStackTarget.c	(revision 30394)
+++ trunk/ppStack/src/ppStackTarget.c	(revision 30620)
@@ -1,6 +1,2 @@
-#include <stdio.h>
-#include <pslib.h>
-#include <psmodules.h>
-
 #include "ppStack.h"
 
@@ -37,4 +33,5 @@
         return psMemIncrRefCounter(sources);
     }
+    exclusion = 2;
 
     int num = sources->n;               // Number of sources
Index: trunk/ppStack/src/ppStackThread.c
===================================================================
--- trunk/ppStack/src/ppStackThread.c	(revision 30394)
+++ trunk/ppStack/src/ppStackThread.c	(revision 30620)
@@ -1,18 +1,5 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <unistd.h>
-#include <pslib.h>
-#include <psmodules.h>
-
 #include "ppStack.h"
-#include "ppStackOptions.h"
-#include "ppStackThread.h"
-
 
 #define THREAD_WAIT 10000               // Microseconds to wait if thread is not available
-
 
 static void stackThreadFree(ppStackThread *thread)
Index: trunk/ppStack/src/ppStackThread.h
===================================================================
--- trunk/ppStack/src/ppStackThread.h	(revision 30394)
+++ trunk/ppStack/src/ppStackThread.h	(revision 30620)
@@ -1,9 +1,4 @@
 #ifndef PPSTACK_THREAD_H
 #define PPSTACK_THREAD_H
-
-#include <pslib.h>
-#include <psmodules.h>
-
-#include "ppStackOptions.h"
 
 // Thread for stacking chunks
Index: trunk/ppStack/src/ppStackUpdateHeader.c
===================================================================
--- trunk/ppStack/src/ppStackUpdateHeader.c	(revision 30620)
+++ trunk/ppStack/src/ppStackUpdateHeader.c	(revision 30620)
@@ -0,0 +1,73 @@
+#include "ppStack.h"
+
+#define WCS_TOLERANCE 0.001             // Tolerance for WCS
+
+bool ppStackUpdateHeader(ppStackThreadData *stack, ppStackOptions *options, pmConfig *config) {
+
+    pmReadout *outRO = options->outRO;                                      // Output readout
+
+    // Propagate WCS
+    bool wcsDone = false;           // Have we done the WCS?
+    for (int i = 0; i < options->num && !wcsDone; i++) {
+        if (options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
+            continue;
+        }
+
+        ppStackThread *thread = stack->threads->data[0]; // Representative stack
+        pmReadout *inRO = thread->readouts->data[i]; // Template readout
+        if (inRO && !wcsDone) {
+            // Copy astrometry over
+            wcsDone = true;
+            pmHDU *inHDU = pmHDUFromCell(inRO->parent); // Template HDU
+            pmHDU *outHDU = pmHDUFromCell(outRO->parent); // Output HDU
+            pmChip *outChip = outRO->parent->parent; // Output chip
+            pmFPA *outFPA = outChip->parent; // Output FPA
+            if (!outHDU || !inHDU) {
+                psWarning("Unable to find HDU at FPA level to copy astrometry.");
+            } else {
+                if (!pmAstromReadWCS(outFPA, outChip, inHDU->header, 1.0)) {
+                    psErrorClear();
+                    psWarning("Unable to read WCS astrometry from input FPA.");
+                    wcsDone = false;
+                } else {
+                    if (!outHDU->header) {
+                        outHDU->header = psMetadataAlloc();
+                    }
+                    if (!pmAstromWriteWCS(outHDU->header, outFPA, outChip, WCS_TOLERANCE)) {
+                        psErrorClear();
+                        psWarning("Unable to write WCS astrometry to output FPA.");
+                        wcsDone = false;
+                    }
+                }
+            }
+        }
+    }
+
+    // Put version information into the header
+    pmHDU *hdu = pmHDUFromCell(outRO->parent);
+    if (!hdu) {
+        psError(PPSTACK_ERR_PROG, false, "Unable to find HDU for output.");
+        return false;
+    }
+    if (!hdu->header) {
+        hdu->header = psMetadataAlloc();
+    }
+    ppStackVersionHeader(hdu->header);
+    
+    // other interesting header info
+    psMetadataAddS32(hdu->header, PS_LIST_TAIL, "NINPUTS", PS_META_REPLACE, "Number of input images", options->num);
+
+    psString stacktype = psMetadataLookupStr(NULL, config->arguments, "STACK_TYPE"); // NIGHTLY, DEEP, BEST_IQ
+    psMetadataAddStr(hdu->header, PS_LIST_TAIL, "STK_TYPE", PS_META_REPLACE, "type of stack", stacktype);
+
+    psString stackID = psMetadataLookupStr(NULL, config->arguments, "-stack_id"); // stack ID (eg, 123222)
+    psMetadataAddStr(hdu->header, PS_LIST_TAIL, "STK_ID", PS_META_REPLACE, "type of stack", stackID);
+
+    psString skycellID = psMetadataLookupStr(NULL, config->arguments, "-skycell_id"); // skycell ID (eg, skycell.101.00)
+    psMetadataAddStr(hdu->header, PS_LIST_TAIL, "SKYCELL", PS_META_REPLACE, "type of stack", skycellID);
+
+    psString tessID = psMetadataLookupStr(NULL, config->arguments, "-tess_id"); // tessellation ID (eg, RINGS.V0)
+    psMetadataAddStr(hdu->header, PS_LIST_TAIL, "TESS_ID", PS_META_REPLACE, "type of stack", tessID);
+
+    return true;
+}
Index: trunk/ppStack/src/ppStackVersion.c
===================================================================
--- trunk/ppStack/src/ppStackVersion.c	(revision 30394)
+++ trunk/ppStack/src/ppStackVersion.c	(revision 30620)
@@ -1,12 +1,2 @@
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <pslib.h>
-#include <psmodules.h>
-#include <ppStats.h>
-#include <psphot.h>
-
 #include "ppStack.h"
 #include "ppStackVersionDefinitions.h"
