Index: /trunk/ppImage/src/Makefile.am
===================================================================
--- /trunk/ppImage/src/Makefile.am	(revision 13999)
+++ /trunk/ppImage/src/Makefile.am	(revision 14000)
@@ -4,8 +4,5 @@
 
 noinst_HEADERS = \
-	ppImage.h \
-	ppImageOptions.h \
-	ppImageDetrendFringe.h \
-	ppImageVersion.h
+	ppImage.h 
 
 ppImage_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PSPHOT_CFLAGS) $(PSASTRO_CFLAGS) $(PPSTATS_CFLAGS) $(ppImage_CFLAGS)
@@ -27,5 +24,9 @@
 	ppImageAddstar.c \
 	ppImageStats.c \
+	ppImageStatsOutput.c \
+	ppImagePixelStats.c \
+	ppImageMetadataStats.c \
 	ppImageDefineFile.c \
+	ppImageFileCheck.c \
 	ppImageVersion.c
 
@@ -50,5 +51,9 @@
 	ppImageAddstar.c \
 	ppImageStats.c \
+	ppImageStatsOutput.c \
+	ppImagePixelStats.c \
+	ppImageMetadataStats.c \
 	ppImageDefineFile.c \
+	ppImageFileCheck.c \
 	ppImageVersion.c
 
Index: /trunk/ppImage/src/ppImage.c
===================================================================
--- /trunk/ppImage/src/ppImage.c	(revision 13999)
+++ /trunk/ppImage/src/ppImage.c	(revision 14000)
@@ -45,57 +45,2 @@
     return PS_EXIT_SUCCESS;
 }
-
-void ppImageFileCheck (pmConfig *config) {
-
-    // add the output names to the output-type files
-    psMetadataItem *item = NULL;
-    psMetadataIterator *iter = psMetadataIteratorAlloc (config->files, PS_LIST_HEAD, NULL);
-    while ((item = psMetadataGetAndIncrement (iter)) != NULL) {
-        pmFPAfile *file = item->data.V;
-        pmFPA *fpa = file->fpa;
-        fprintf (stderr, "file %s\n", file->name);
-        if (!fpa) {
-            fprintf (stderr, "  has no FPA\n");
-            continue;
-        }
-        if (fpa->hdu) {
-            if (fpa->hdu->images) fprintf (stderr, "  (%d,%d) images\n", -1, -1);
-            if (fpa->hdu->weights) fprintf (stderr, "  (%d,%d) weights\n", -1, -1);
-            if (fpa->hdu->masks) fprintf (stderr, "  (%d,%d) masks\n", -1, -1);
-            if (fpa->hdu->header) fprintf (stderr, "  (%d,%d) header\n", -1, -1);
-        } else {
-            // fprintf (stderr, "  has no fpa data (%d,%d)\n", -1, -1);
-        }
-        for (int i = 0; i < fpa->chips->n; i++) {
-            pmChip *chip = fpa->chips->data[i];
-            if (chip->hdu) {
-                if (chip->hdu->images) fprintf (stderr, "  (%d,%d) images\n", i, -1);
-                if (chip->hdu->weights) fprintf (stderr, "  (%d,%d) weights\n", i, -1);
-                if (chip->hdu->masks) fprintf (stderr, "  (%d,%d) masks\n", i, -1);
-                if (chip->hdu->header) fprintf (stderr, "  (%d,%d) header\n", i, -1);
-            } else {
-                // fprintf (stderr, "  has no chip data (%d,%d)\n", i, -1);
-            }
-            for (int j = 0; j < chip->cells->n; j++) {
-                pmCell *cell = chip->cells->data[j];
-                if (cell->hdu) {
-                    if (cell->hdu->images) fprintf (stderr, "  (%d,%d) images\n", i, j);
-                    if (cell->hdu->weights) fprintf (stderr, "  (%d,%d) weights\n", i, j);
-                    if (cell->hdu->masks) fprintf (stderr, "  (%d,%d) masks\n", i, j);
-                    if (cell->hdu->header) fprintf (stderr, "  (%d,%d) header\n", i, j);
-                } else {
-                    // fprintf (stderr, "  has no cell data (%d,%d)\n", i, j);
-                }
-                for (int k = 0; k < cell->readouts->n; k++) {
-                    pmReadout *readout = cell->readouts->data[k];
-                    if (readout) {
-                        if (readout->image) fprintf (stderr, "  (%d,%d,%d) image\n", i, j, k);
-                        if (readout->weight) fprintf (stderr, "  (%d,%d,%d) weight\n", i, j, k);
-                        if (readout->mask) fprintf (stderr, "  (%d,%d,%d) masks\n", i, j, k);
-                    }
-                }
-            }
-        }
-    }
-    psFree (iter);
-}
Index: /trunk/ppImage/src/ppImage.h
===================================================================
--- /trunk/ppImage/src/ppImage.h	(revision 13999)
+++ /trunk/ppImage/src/ppImage.h	(revision 14000)
@@ -14,8 +14,69 @@
 #include "psphot.h"
 #include "psastro.h"
-#include "ppImageOptions.h"
+#include "ppStats.h"
 
 #define RECIPE_NAME "PPIMAGE"           // Name of the recipe to use
 #define TIMERNAME "ppImage"             // Name of timer
+
+// Options for ppImage processing
+typedef struct {
+    bool doMask;                        // Mask bad pixels
+    psMaskType maskValue;               // apply this bit-mask to choose masked bits
+    psMaskType satMask;                 // Mask value to give saturated pixels
+    psMaskType badMask;                 // Mask value to give bad pixels
+    psMaskType flatMask;                // Mask value to give bad flat pixels
+    psMaskType blankMask;               // Mask value to give blank pixels
+
+    bool doBias;                        // Bias subtraction
+    bool doDark;                        // Dark subtraction
+    bool doShutter;                     // Shutter correction
+    bool doFlat;                        // Flat-field normalisation
+    bool doFringe;                      // Fringe subtraction
+    bool doPhotom;                      // Source identification and photometry
+    bool doAstromChip;                  // per-chip Astrometry
+    bool doAstromMosaic;                // full-mosaic Astrometry
+    bool doAddstar;                     // add results to object database?
+
+    bool doOverscan;                    // Overscan subtraction
+    pmOverscanOptions *overscan;        // Overscan options
+
+    bool doNonLin;                      // Non-linearity correction
+    psDataType nonLinearType;
+    psMetadataItem *nonLinearData;
+    void *nonLinearSource;
+
+    bool doStats;
+
+    bool BaseFITS;
+    bool ChipFITS;
+    bool FPA1FITS;
+    bool FPA2FITS;
+
+    bool Bin1FITS;
+    bool Bin1JPEG;
+    int xBin1, yBin1;
+
+    bool Bin2FITS;
+    bool Bin2JPEG;
+    int xBin2, yBin2;
+
+    float fringeRej;                    // Fringe rejection limit
+    int fringeIter;                     // Fringe iterations
+    float fringeKeep;                   // Fringe keep fraction
+} ppImageOptions;
+
+// Cells to be used in the detrend
+typedef struct {
+    pmCell *input;                      // The input cell, to be operated upon
+    pmCell *mask;                       // The bad pixel mask
+    pmCell *bias;                       // The bias correction
+    pmCell *dark;                       // The dark correction
+    pmCell *flat;                       // The flat-field correction
+} ppImageDetrend;
+
+ppImageOptions *ppImageOptionsAlloc(void);
+
+// Determine the processing options
+ppImageOptions *ppImageOptionsParse(pmConfig *config);
 
 // Get the configuration
@@ -35,9 +96,12 @@
 
 bool ppImageDetrendBias(pmReadout *inputReadout, pmReadout *bias, pmReadout *dark, ppImageOptions *options);
-bool ppImageDetrendMask(pmReadout *input, pmReadout *mask);
 
 bool ppImageDetrendNonLinear(pmReadout *input, ppImageOptions *options);
 bool ppImageDetrendNonLinearLookup(pmReadout *input, psMetadataItem *dataItem);
 bool ppImageDetrendNonLinearPolynomial(pmReadout *input, psMetadataItem *dataItem);
+
+bool ppImageDetrendCell(ppImageDetrend *detrend, ppImageOptions *options, pmConfig *config);
+bool ppImageDetrendBias(pmReadout *inputReadout, pmReadout *bias, pmReadout *dark, ppImageOptions *options);
+pmReadout* ppImageDetrendSelectFirst(pmCell *cell, char *name, bool doThis);
 
 bool ppImageRebinChip (pmConfig *config, pmFPAview *view, char *outName);
@@ -69,5 +133,52 @@
 
 // write stats to output file 
-bool ppImageStatsOutput (pmConfig *config, pmFPA *fpa, const ppImageOptions *options);
+bool ppImageStatsOutput (pmConfig *config, const ppImageOptions *options);
+
+
+// Measure fringes
+bool ppImageDetrendFringeMeasure(pmReadout *readout, // Readout to measure
+                                 pmCell *fringe, // Fringe cell (each readout is a different component)
+				 const bool isResidual,
+                                 const ppImageOptions *options // Options
+    );
+
+// Solve the fringe system
+bool ppImageDetrendFringeSolve(pmChip *scienceChip, // Chip with science
+                               const pmChip *refChip, // Chip with reference fringes
+			       const bool isResidual,
+                               const ppImageOptions *options // Options
+    );
+
+// Generate fringe frame
+bool ppImageDetrendFringeGenerate(pmCell *science, // Science cell
+                                  pmCell *fringes, // Fringe cell, one readout per fringe component
+				  const ppImageOptions *options // Options
+    );
+
+
+bool ppImageDetrendFringeApply (pmConfig *config, // config
+				pmChip *chip, // science chip
+				const pmFPAview *inputView, // current view
+				const ppImageOptions *options // options
+    );
+
+/// Return short version information
+psString ppImageVersion(void);
+
+/// Return long version information
+psString ppImageVersionLong(void);
+
+/// Update the metadata with version information for all dependencies
+void ppImageVersionMetadata(psMetadata *metadata ///< Metadata to update with version information
+    );
+
+
+// calculate stats, including MD5
+bool ppImagePixelStats (pmConfig *config, const ppImageOptions *options, const pmFPAview *inputView);
+
+// calculate stats from headers and concepts
+bool ppImageMetadataStats (pmConfig *config, const ppImageOptions *options);
+
+void ppImageFileCheck (pmConfig *config);
 
 #endif
Index: /trunk/ppImage/src/ppImageArguments.c
===================================================================
--- /trunk/ppImage/src/ppImageArguments.c	(revision 13999)
+++ /trunk/ppImage/src/ppImageArguments.c	(revision 14000)
@@ -4,6 +4,4 @@
 
 #include "ppImage.h"
-#include "ppImageVersion.h"
-#include "ppStatsVersion.h"
 
 static void usage (void) {
Index: unk/ppImage/src/ppImageDetrend.h
===================================================================
--- /trunk/ppImage/src/ppImageDetrend.h	(revision 13999)
+++ 	(revision )
@@ -1,27 +1,0 @@
-#ifndef PP_IMAGE_DETREND_H
-#define PP_IMAGE_DETREND_H
-
-#include "pslib.h"
-#include "psmodules.h"
-#include "ppImageOptions.h"
-
-// Cells to be used in the detrend
-typedef struct {
-    pmCell *input;                      // The input cell, to be operated upon
-    pmCell *mask;                       // The bad pixel mask
-    pmCell *bias;                       // The bias correction
-    pmCell *dark;                       // The dark correction
-    pmCell *flat;                       // The flat-field correction
-} ppImageDetrend;
-
-
-bool ppImageDetrendCell(ppImageDetrend *detrend, ppImageOptions *options, pmConfig *config);
-bool ppImageDetrendMask(pmCell *cell, pmReadout *input, pmReadout *mask);
-bool ppImageDetrendNonLinear(pmCell *cell, pmReadout *input, ppImageOptions *options);
-bool ppImageDetrendNonLinearLookup(pmReadout *input, psMetadataItem *dataItem);
-bool ppImageDetrendNonLinearPolynomial(pmReadout *input, psMetadataItem *dataItem);
-bool ppImageDetrendBias(pmReadout *inputReadout, pmReadout *bias, pmReadout *dark, ppImageOptions *options);
-pmReadout* ppImageDetrendSelectFirst(pmCell *cell, char *name, bool doThis);
-
-
-#endif
Index: /trunk/ppImage/src/ppImageDetrendFringe.c
===================================================================
--- /trunk/ppImage/src/ppImageDetrendFringe.c	(revision 13999)
+++ /trunk/ppImage/src/ppImageDetrendFringe.c	(revision 14000)
@@ -3,10 +3,5 @@
 #endif
 
-#include <stdio.h>
-#include <pslib.h>
-#include <psmodules.h>
-
-#include "ppImageOptions.h"
-#include "ppImageDetrendFringe.h"
+#include "ppImage.h"
 
 bool ppImageDetrendFringeMeasure(pmReadout *readout, pmCell *fringe, const bool isResidual, const ppImageOptions *options)
Index: unk/ppImage/src/ppImageDetrendFringe.h
===================================================================
--- /trunk/ppImage/src/ppImageDetrendFringe.h	(revision 13999)
+++ 	(revision )
@@ -1,35 +1,0 @@
-#ifndef PP_IMAGE_DETREND_FRINGE_H
-#define PP_IMAGE_DETREND_FRINGE_H
-
-#include <pslib.h>
-#include <psmodules.h>
-#include "ppImageOptions.h"
-
-// Measure fringes
-bool ppImageDetrendFringeMeasure(pmReadout *readout, // Readout to measure
-                                 pmCell *fringe, // Fringe cell (each readout is a different component)
-				 const bool isResidual,
-                                 const ppImageOptions *options // Options
-    );
-
-// Solve the fringe system
-bool ppImageDetrendFringeSolve(pmChip *scienceChip, // Chip with science
-                               const pmChip *refChip, // Chip with reference fringes
-			       const bool isResidual,
-                               const ppImageOptions *options // Options
-    );
-
-// Generate fringe frame
-bool ppImageDetrendFringeGenerate(pmCell *science, // Science cell
-                                  pmCell *fringes, // Fringe cell, one readout per fringe component
-				  const ppImageOptions *options // Options
-    );
-
-
-bool ppImageDetrendFringeApply (pmConfig *config, // config
-				pmChip *chip, // science chip
-				const pmFPAview *inputView, // current view
-				const ppImageOptions *options // options
-);
-
-#endif
Index: /trunk/ppImage/src/ppImageDetrendReadout.c
===================================================================
--- /trunk/ppImage/src/ppImageDetrendReadout.c	(revision 13999)
+++ /trunk/ppImage/src/ppImageDetrendReadout.c	(revision 14000)
@@ -4,5 +4,4 @@
 
 #include "ppImage.h"
-#include "ppImageDetrendFringe.h"
 
 bool ppImageDetrendReadout(pmConfig *config, ppImageOptions *options, pmFPAview *view)
Index: /trunk/ppImage/src/ppImageLoop.c
===================================================================
--- /trunk/ppImage/src/ppImageLoop.c	(revision 13999)
+++ /trunk/ppImage/src/ppImageLoop.c	(revision 14000)
@@ -4,6 +4,4 @@
 
 #include "ppImage.h"
-#include "ppImageDetrendFringe.h"
-#include "ppImageVersion.h"
 
 bool ppImageLoop (pmConfig *config, ppImageOptions *options) {
@@ -88,5 +86,5 @@
 	
 	// measure various statistics for this image
-	if (!ppImageStats (config, chip, view, options)) {
+	if (!ppImagePixelStats (config, options, view)) {
 	    psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to measures stats for image");
 	    psFree (view);
@@ -119,5 +117,5 @@
         }
 
-        // Close cells
+        // Close cells (XXX shouldn't pmFPAfileClose iterate down as needed?)
         view->cell = -1;
         while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) {
@@ -139,11 +137,4 @@
     }
 
-    // Write out summary statistics
-    if (!ppImageStatsOutput (config, input->fpa, options)) {
-	psError(PS_ERR_UNKNOWN, false, "Unable to write statistics file.\n");
-	psFree (view);
-	return false;
-    }
-
     // XXX an optional full-scale FPA mosaic could happen here
     // it would require us to NOT free PPIMAGE.CHIP until here
@@ -161,4 +152,21 @@
     }
 
+    ppImageFileCheck (config);
+
+    // Write out summary statistics
+    if (!ppImageMetadataStats (config, options)) {
+	psError(PS_ERR_UNKNOWN, false, "Unable to write statistics file.\n");
+	psFree (view);
+	return false;
+    }
+
+
+    // Write out summary statistics
+    if (!ppImageStatsOutput (config, options)) {
+	psError(PS_ERR_UNKNOWN, false, "Unable to write statistics file.\n");
+	psFree (view);
+	return false;
+    }
+
     // Close FPA
     if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
Index: /trunk/ppImage/src/ppImageMetadataStats.c
===================================================================
--- /trunk/ppImage/src/ppImageMetadataStats.c	(revision 14000)
+++ /trunk/ppImage/src/ppImageMetadataStats.c	(revision 14000)
@@ -0,0 +1,62 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+// calculate stats from headers and concepts
+bool ppImageMetadataStats (pmConfig *config, const ppImageOptions *options) {
+
+    bool mdok;              // Status of MD lookup
+    
+    if (!options->doStats) {
+	return true;
+    }
+
+    psMaskType maskVal = options->satMask | options->badMask | options->maskValue;
+
+    // Extract statistics from the last output fpa
+    pmFPAfile *outImage = psMetadataLookupPtr(&mdok, config->files, "PPIMAGE.OUTPUT");
+    pmFPAfile *outPhotom = psMetadataLookupPtr(&mdok, config->files, "PSPHOT.OUTPUT");
+    pmFPAfile *outAstrom = psMetadataLookupPtr(&mdok, config->files, "PSASTRO.OUTPUT");
+
+    if (!outImage && !outPhotom && !outAstrom) {
+	psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find any output file (PPIMAGE.OUTPUT, PSPHOT.OUTPUT, PSASTRO.OUTPUT).");
+	return false;
+    }
+
+    // get the latest output product available
+    pmFPAfile *output = outAstrom;
+    if (!output) {
+	output = outPhotom;
+    }
+    if (!output) {
+	output = outImage;
+    }
+
+    // select or create the fpa-level analysis stats metadata for PPIMAGE.OUTPUT:
+    psMetadata *stats = psMetadataLookupPtr (&mdok, outImage->fpa->analysis, "PPIMAGE.STATS");
+    psMemIncrRefCounter (stats);
+    if (!stats) {
+	stats = psMetadataAlloc ();
+	if (!psMetadataAdd (outImage->fpa->analysis, PS_LIST_TAIL, "PPIMAGE.STATS", PS_DATA_METADATA, "stats container", stats)) {
+	    psError(PS_ERR_UNKNOWN, false, "Unable to push stats on fpa.analysis.");
+	    psFree (stats);
+	    return false;
+	}
+    }
+
+    // extract stats for the complete fpa 
+    pmFPAview *view = pmFPAviewAlloc(0);
+
+    if (!ppStatsMetadata(stats, output->fpa, view, maskVal, config)) {
+	psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate stats for image.");
+	psFree(view);
+	psFree(stats);
+	return false;
+    }
+
+    psFree(view);
+    psFree(stats);
+    return true;
+}
Index: unk/ppImage/src/ppImageOptions.h
===================================================================
--- /trunk/ppImage/src/ppImageOptions.h	(revision 13999)
+++ 	(revision )
@@ -1,57 +1,0 @@
-#ifndef PP_IMAGE_OPTIONS_H
-#define PP_IMAGE_OPTIONS_H
-
-// Options for ppImage processing
-typedef struct {
-    bool doMask;                        // Mask bad pixels
-    psMaskType maskValue;               // apply this bit-mask to choose masked bits
-    psMaskType satMask;                 // Mask value to give saturated pixels
-    psMaskType badMask;                 // Mask value to give bad pixels
-    psMaskType flatMask;                // Mask value to give bad flat pixels
-    psMaskType blankMask;               // Mask value to give blank pixels
-
-    bool doBias;                        // Bias subtraction
-    bool doDark;                        // Dark subtraction
-    bool doShutter;                     // Shutter correction
-    bool doFlat;                        // Flat-field normalisation
-    bool doFringe;                      // Fringe subtraction
-    bool doPhotom;                      // Source identification and photometry
-    bool doAstromChip;                  // per-chip Astrometry
-    bool doAstromMosaic;                // full-mosaic Astrometry
-    bool doAddstar;                     // add results to object database?
-
-    bool doOverscan;                    // Overscan subtraction
-    pmOverscanOptions *overscan;        // Overscan options
-
-    bool doNonLin;                      // Non-linearity correction
-    psDataType nonLinearType;
-    psMetadataItem *nonLinearData;
-    void *nonLinearSource;
-
-    bool doStats;
-
-    bool BaseFITS;
-    bool ChipFITS;
-    bool FPA1FITS;
-    bool FPA2FITS;
-
-    bool Bin1FITS;
-    bool Bin1JPEG;
-    int xBin1, yBin1;
-
-    bool Bin2FITS;
-    bool Bin2JPEG;
-    int xBin2, yBin2;
-
-    float fringeRej;                    // Fringe rejection limit
-    int fringeIter;                     // Fringe iterations
-    float fringeKeep;                   // Fringe keep fraction
-
-} ppImageOptions;
-
-ppImageOptions *ppImageOptionsAlloc(void);
-
-// Determine the processing options
-ppImageOptions *ppImageOptionsParse(pmConfig *config);
-
-#endif
Index: /trunk/ppImage/src/ppImagePixelStats.c
===================================================================
--- /trunk/ppImage/src/ppImagePixelStats.c	(revision 14000)
+++ /trunk/ppImage/src/ppImagePixelStats.c	(revision 14000)
@@ -0,0 +1,106 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+// calculate stats, including MD5
+bool ppImagePixelStats (pmConfig *config, const ppImageOptions *options, const pmFPAview *inputView) {
+
+    bool mdok;              // Status of MD lookup
+
+    psMaskType maskVal = options->satMask | options->badMask | options->maskValue;
+
+    // loop over the cells/readouts for this chip
+    pmFPAview *view = pmFPAviewAlloc(0); // View for local processing
+    *view = *inputView;
+    
+    // perform the analysis of for this FPA
+    pmFPAfile *output = psMetadataLookupPtr(&mdok, config->files, "PPIMAGE.OUTPUT");
+    if (!output) {
+	psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find output file (PPIMAGE.OUTPUT).");
+	psFree (view);
+	return false;
+    }
+
+    // select the corresponding chip
+    pmChip *chip = pmFPAviewThisChip (view, output->fpa);
+    if (!chip) {
+	psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find requested chip.");
+	psFree (view);
+	return false;
+    }
+    
+    // Perform statistics for this chip
+    if (options->doStats) {
+
+	// select or create the fpa-level analysis stats metadata (save on PPIMAGE.OUPUT:PPIMAGE.STATS)
+	psMetadata *stats = psMetadataLookupPtr (&mdok, output->fpa->analysis, "PPIMAGE.STATS");
+	psMemIncrRefCounter (stats);
+	if (!stats) {
+	    stats = psMetadataAlloc ();
+	    if (!psMetadataAdd (output->fpa->analysis, PS_LIST_TAIL, "PPIMAGE.STATS", PS_DATA_METADATA, "stats container", stats)) {
+		psError(PS_ERR_UNKNOWN, false, "Unable to push stats on fpa.analysis.");
+		psFree (view);
+		psFree (stats);
+		return false;
+	    }
+	}
+
+	if (!ppStatsPixels(stats, output->fpa, view, maskVal, config)) {
+	    psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate stats for image.");
+	    psFree (view);
+	    psFree (stats);
+	    return false;
+	}
+
+	// extract the fringe amplitude from the analysis
+	if (options->doFringe && !ppStatsFringe(stats, chip, "FRINGE", "FRINGE.SOLUTION")) {
+	    psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to extract fringe solution for image.");
+	    psFree (view);
+	    psFree (stats);
+	    return false;
+	}
+
+	// extract the fringe residual amplitude from the analysis
+	if (options->doFringe && !ppStatsFringe(stats, chip, "FRINGE_RESID", "FRINGE.RESIDUAL.SOLUTION")) {
+	    psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to extract fringe solution for image.");
+	    psFree (view);
+	    psFree (stats);
+	    return false;
+	}
+	
+	psFree (stats);
+    }
+
+    pmCell *cell = NULL;
+    pmReadout *readout = NULL;
+
+    view->cell = view->readout = -1;
+    while ((cell = pmFPAviewNextCell(view, output->fpa, 1)) != NULL) {
+	if (!cell->process || !cell->file_exists) {
+	    continue;
+	}
+
+	// Add MD5 information for cell
+	pmHDU *hdu = pmHDUFromCell(cell); // HDU that owns the cell
+	while ((readout = pmFPAviewNextReadout(view, output->fpa, 1)) != NULL) {
+	    const char *chipName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME");
+	    const char *cellName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME");
+
+	    psString headerName = NULL; // Header name for MD5
+	    psStringAppend(&headerName, "MD5_%s_%s_%d", chipName, cellName, view->readout);
+
+	    psVector *md5 = psImageMD5(readout->image); // md5 hash
+	    psString md5string = psMD5toString(md5); // String
+	    psFree (md5);
+	    psMetadataAddStr(hdu->header, PS_LIST_TAIL, headerName, PS_META_REPLACE,
+			     "Image MD5", md5string);
+	    psFree (md5string);
+	    psFree (headerName);
+	}
+    }
+
+    psFree (view);
+    return true;
+}
Index: /trunk/ppImage/src/ppImageStats.c
===================================================================
--- /trunk/ppImage/src/ppImageStats.c	(revision 13999)
+++ /trunk/ppImage/src/ppImageStats.c	(revision 14000)
@@ -3,10 +3,5 @@
 #endif
 
-#include <stdio.h>
-#include <pslib.h>
-#include <psmodules.h>
-
 #include "ppImage.h"
-#include "ppStats.h"
 
 // calculate stats, including MD5
@@ -42,7 +37,10 @@
 	if (options->doStats) {
 
-	    pmFPAfile *output = psMetadataLookupPtr(&mdok, config->files, "PPIMAGE.OUTPUT");
-	    if (!mdok || !output) {
-		psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find file PPIMAGE.OUTPUT.");
+	    pmFPAfile *outImage = psMetadataLookupPtr(&mdok, config->files, "PPIMAGE.OUTPUT");
+	    pmFPAfile *outPhotom = psMetadataLookupPtr(&mdok, config->files, "PSPHOT.OUTPUT");
+	    pmFPAfile *outAstrom = psMetadataLookupPtr(&mdok, config->files, "PSASTRO.OUTPUT");
+
+	    if (!outImage && !outPhotom && !outAstrom) {
+		psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find any output file (PPIMAGE.OUTPUT, PSPHOT.OUTPUT, PSASTRO.OUTPUT).");
 		psFree (view);
 		psFree(stats);
@@ -50,5 +48,14 @@
 	    }
 
-	    if (!ppStats(stats, output->fpa, view,
+	    // get the latest output product available
+	    pmFPAfile *output = outAstrom;
+	    if (!output) {
+		output = outPhotom;
+	    }
+	    if (!output) {
+		output = outImage;
+	    }
+
+	    if (!ppStatsFPA(stats, output->fpa, view,
 			 options->satMask | options->badMask | options->maskValue,
 			 config)) {
@@ -98,50 +105,2 @@
     return true;
 }
-
-// write stats to output file 
-bool ppImageStatsOutput (pmConfig *config, pmFPA *fpa, const ppImageOptions *options) {
-
-    bool mdok;
-
-    // measure statistics, or ignore?
-    if (!options->doStats) return true;
-	
-    // get the output stats filename
-    const char *statsName = psMetadataLookupStr(&mdok, config->arguments, "STATS"); // Filename for statistics
-    if (!statsName && !strlen(statsName)) {
-	psError (PS_ERR_UNEXPECTED_NULL, false, "missing STATS entry in arguments list.");
-	return false;
-    }
-
-    // convert to a real UNIX filename
-    psString resolved = pmConfigConvertFilename(statsName, config, true); // Resolved filename
-    FILE *statsFile = fopen (resolved, "w");
-    if (!statsFile) {
-	psError(PS_ERR_IO, true, "Unable to open statistics file %s for writing.\n", resolved);
-	psFree(resolved);
-	return false;
-    }
-    psFree(resolved);
-
-    // select the fpa-level analysis stats metadata:
-    psMetadata *stats = psMetadataLookupPtr (&mdok, fpa->analysis, "PPIMAGE.STATS");
-    if (!stats) {
-	psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find PPIMAGE.STATS entry in fpa analysis.\n");
-	return false;
-    }
-
-    // convert the stats MDC to a string
-    char *statsMDC = psMetadataConfigFormat(stats);
-    if (!statsMDC || strlen(statsMDC) == 0) {
-	psError(PS_ERR_IO, false, "Unable to serialize stats metadata.\n");
-	return false;
-    } 
-
-    // write the stats MDC to a file
-    // XXX why does this not call psMetadataConfigPrint?
-    fprintf(statsFile, "%s", statsMDC);
-
-    psFree(statsMDC);
-    fclose(statsFile);
-    return true;
-}
Index: /trunk/ppImage/src/ppImageStatsOutput.c
===================================================================
--- /trunk/ppImage/src/ppImageStatsOutput.c	(revision 14000)
+++ /trunk/ppImage/src/ppImageStatsOutput.c	(revision 14000)
@@ -0,0 +1,62 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+// write stats to output file 
+bool ppImageStatsOutput (pmConfig *config, const ppImageOptions *options) {
+
+    bool mdok;
+
+    // measure statistics, or ignore?
+    if (!options->doStats) return true;
+	
+    // PPIMAGE.STATS is stored on PPIMAGE.OUTPUT
+    pmFPAfile *output = psMetadataLookupPtr(&mdok, config->files, "PPIMAGE.OUTPUT");
+    if (!output) {
+	psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find PPIMAGE.OUTPUT entry in config file.\n");
+	return false;
+    }
+
+    // select the fpa-level analysis stats metadata:
+    psMetadata *stats = psMetadataLookupPtr (&mdok, output->fpa->analysis, "PPIMAGE.STATS");
+    if (!stats) {
+	psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find PPIMAGE.STATS entry in output fpa analysis.\n");
+	return false;
+    }
+
+    // convert the stats MDC to a string
+    char *statsMDC = psMetadataConfigFormat(stats);
+    if (!statsMDC || strlen(statsMDC) == 0) {
+	psError(PS_ERR_IO, false, "Unable to serialize stats metadata.\n");
+	return false;
+    } 
+
+    // get the output stats filename
+    const char *statsName = psMetadataLookupStr(&mdok, config->arguments, "STATS"); // Filename for statistics
+    if (!statsName && !strlen(statsName)) {
+	psError (PS_ERR_UNEXPECTED_NULL, false, "missing STATS entry in arguments list.");
+	psFree(statsMDC);
+	return false;
+    }
+
+    // convert to a real UNIX filename
+    psString resolved = pmConfigConvertFilename(statsName, config, true); // Resolved filename
+    FILE *statsFile = fopen (resolved, "w");
+    if (!statsFile) {
+	psError(PS_ERR_IO, true, "Unable to open statistics file %s for writing.\n", resolved);
+	psFree(statsMDC);
+	psFree(resolved);
+	return false;
+    }
+    psFree(resolved);
+
+    // write the stats MDC to a file
+    // XXX why does this not call psMetadataConfigPrint?
+    fprintf(statsFile, "%s", statsMDC);
+
+    psFree(statsMDC);
+    fclose(statsFile);
+    return true;
+}
Index: /trunk/ppImage/src/ppImageVersion.c
===================================================================
--- /trunk/ppImage/src/ppImageVersion.c	(revision 13999)
+++ /trunk/ppImage/src/ppImageVersion.c	(revision 14000)
@@ -3,12 +3,5 @@
 #endif
 
-#include <stdio.h>
-#include <pslib.h>
-#include <psmodules.h>
-#include <psphot.h>
-#include <psastro.h>
-#include <ppStats.h>
-
-#include "ppImageVersion.h"
+#include "ppImage.h"
 
 static const char *cvsTag = "$Name: not supported by cvs2svn $";// CVS tag name
Index: unk/ppImage/src/ppImageVersion.h
===================================================================
--- /trunk/ppImage/src/ppImageVersion.h	(revision 13999)
+++ 	(revision )
@@ -1,14 +1,0 @@
-#ifndef PP_IMAGE_VERSION_H
-#define PP_IMAGE_VERSION_H
-
-/// Return short version information
-psString ppImageVersion(void);
-
-/// Return long version information
-psString ppImageVersionLong(void);
-
-/// Update the metadata with version information for all dependencies
-void ppImageVersionMetadata(psMetadata *metadata ///< Metadata to update with version information
-    );
-
-#endif
