Index: trunk/ppImage/src/ppImageLoop.c
===================================================================
--- trunk/ppImage/src/ppImageLoop.c	(revision 13924)
+++ trunk/ppImage/src/ppImageLoop.c	(revision 13970)
@@ -3,5 +3,4 @@
 #endif
 
-#include <ppStats.h>
 #include "ppImage.h"
 #include "ppImageDetrendFringe.h"
@@ -10,24 +9,8 @@
 bool ppImageLoop (pmConfig *config, ppImageOptions *options) {
 
-    bool mdok;                      // Status of MD lookup
     bool status;
     pmChip *chip;
     pmCell *cell;
     pmReadout *readout;
-    psMetadata *stats = NULL;
-
-    const char *statsName = psMetadataLookupStr(&mdok, config->arguments, "STATS"); // Filename for statistics
-    FILE *statsFile = NULL;             // File stream for statistics
-    if (mdok && statsName && strlen(statsName) > 0) {
-        psString resolved = pmConfigConvertFilename(statsName, config, true); // Resolved filename
-        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;
-        }
-        stats = psMetadataAlloc();
-        psFree(resolved);
-    }
 
     pmFPAfile *input = psMetadataLookupPtr(&status, config->files, "PPIMAGE.INPUT");
@@ -96,75 +79,18 @@
         }
 
-        // Solve the fringe system
+        // Apply the fringe correction
         if (options->doFringe) {
-            pmChip *fringe = pmFPAfileThisChip(config->files, view, "PPIMAGE.FRINGE");
-            if (!ppImageDetrendFringeSolve(chip, fringe, options)) {
+	    if (!ppImageDetrendFringeApply (config, chip, view, options)) {
                 psFree (view);
                 return false;
             }
-        }
-
-        // Go back over the cells to apply the fringe and do statistics
-        view->cell = view->readout = -1;
-        while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) {
-            if (!cell->process || !cell->file_exists) {
-                continue;
-            }
-
-            // Apply the fringe correction
-            if (options->doFringe) {
-                psTrace("ppImage", 3, "Applying fringe correction...\n");
-                pmCell *fringeCell = pmFPAfileThisCell(config->files, view, "PPIMAGE.FRINGE");
-                if (!ppImageDetrendFringeGenerate(cell, fringeCell)) {
-                    psFree (view);
-                    return false;
-                }
-            }
-
-            // Perform statistics on the detrended cell
-            if (stats) {
-                bool mdok;              // Status of MD lookup
-                pmFPAfile *output = psMetadataLookupPtr(&mdok, config->files, "PPIMAGE.OUTPUT");
-                if (!mdok || !output) {
-                    psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find file PPIMAGE.OUTPUT.");
-                    psFree (view);
-                    return false;
-                }
-
-                if (!ppStats(stats, output->fpa, view,
-                             options->satMask | options->badMask | options->maskValue,
-                             config)) {
-                    psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate stats for image.");
-                    psFree(stats);
-                    psFree(view);
-                    return false;
-                }
-
-                if (options->doFringe && !ppStatsFringe(stats, chip, "FRINGE.SOLUTION")) {
-                    psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to extract fringe solution for image.");
-                    psFree(stats);
-                    psFree(view);
-                    return false;
-                }
-            }
-
-            // Add MD5 information for cell
-            pmHDU *hdu = pmHDUFromCell(cell); // HDU that owns the cell
-            while ((readout = pmFPAviewNextReadout(view, input->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);
-            }
-        }
+	}
+	
+	// measure various statistics for this image
+	if (!ppImageStats (config, chip, view, options)) {
+	    psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to measures stats for image");
+	    psFree (view);
+	    return false;
+	}
 
         if (!ppImageMosaicChip(config, options, view, "PPIMAGE.CHIP", "PPIMAGE.OUTPUT")) {
@@ -214,14 +140,8 @@
 
     // Write out summary statistics
-    if (stats) {
-        char *statsMDC = psMetadataConfigFormat(stats);
-        if (!statsMDC || strlen(statsMDC) == 0) {
-            psError(PS_ERR_IO, false, "Unable to get statistics MDC file.\n");
-        } else {
-            fprintf(statsFile, "%s", statsMDC);
-        }
-        psFree(statsMDC);
-        fclose(statsFile);
-        psFree(stats);
+    if (!ppImageStatsOutput (config, input->fpa, options)) {
+	psError(PS_ERR_UNKNOWN, false, "Unable to write statistics file.\n");
+	psFree (view);
+	return false;
     }
 
