Index: trunk/ppNoiseMap/src/Makefile.am
===================================================================
--- trunk/ppNoiseMap/src/Makefile.am	(revision 24458)
+++ trunk/ppNoiseMap/src/Makefile.am	(revision 24470)
@@ -36,4 +36,6 @@
 	ppNoiseMapCleanup.c \
 	ppNoiseMapVersion.c \
+	ppNoiseMapReadout.c \
+	ppNoiseMapStats.c \
 	ppNoiseMapDefineFile.c
 
Index: trunk/ppNoiseMap/src/ppNoiseMap.h
===================================================================
--- trunk/ppNoiseMap/src/ppNoiseMap.h	(revision 24458)
+++ trunk/ppNoiseMap/src/ppNoiseMap.h	(revision 24470)
@@ -46,3 +46,8 @@
 void ppNoiseMapVersionPrint(void);
 
+/// perform the noise measurement
+bool ppNoiseMapReadout (pmConfig *config, pmFPAview *view);
+
+/// measure the noise for the readout
+bool ppNoiseMapStats(pmReadout *out, const pmReadout *in, psImageMaskType maskVal, int xBin, int yBin);
 #endif
Index: trunk/ppNoiseMap/src/ppNoiseMapLoop.c
===================================================================
--- trunk/ppNoiseMap/src/ppNoiseMapLoop.c	(revision 24458)
+++ trunk/ppNoiseMap/src/ppNoiseMapLoop.c	(revision 24470)
@@ -38,4 +38,5 @@
         }
 
+	// load all cells for this chip before generating output file fpa:
         pmCell *cell;                   // Cell from chip
         while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) {
@@ -70,13 +71,16 @@
                     continue;
                 }
+	    }
+	}
 
-                // perform the noise analysis
-                // if (!ppNoiseMapReadout(config, view)) {
-                //     ESCAPE("Unable to detrend readout");
-                // }
-
+	if (!ppNoiseMapReadout(config, view)) {
+	    ESCAPE("Unable to measure noise map for readout");
+	}
+	
+	// close the cells
+        while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) {
+            if (!cell->process || !cell->file_exists) {
+                continue;
             }
-
-	    // Close cell
             if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
                 ESCAPE("save failure for Cell");
@@ -90,8 +94,4 @@
     }
 
-    // if (psTraceGetLevel("ppNoiseMap") >= 3) {
-    //     ppNoiseMapFileCheck(config);
-    // }
-
     // Output and Close FPA
     if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
Index: trunk/ppNoiseMap/src/ppNoiseMapParseCamera.c
===================================================================
--- trunk/ppNoiseMap/src/ppNoiseMapParseCamera.c	(revision 24458)
+++ trunk/ppNoiseMap/src/ppNoiseMapParseCamera.c	(revision 24470)
@@ -2,4 +2,6 @@
 
 bool ppNoiseMapParseCamera(pmConfig *config) {
+
+    bool status;
 
     if (!ppNoiseMapDefineFile(config, NULL, "PPNOISEMAP.INPUT", "INPUT", PM_FPA_FILE_IMAGE)) {
@@ -10,10 +12,16 @@
     psAssert(input, "We just put it there!");
 
-    // add recipe options supplied on command line
-    // bool status = false;
-    // psMetadata *recipe  = psMetadataLookupPtr(&status, config->recipes, RECIPE_NAME);
+    // the recipe is only fully parsed after the camera is first determined
+    psMetadata *recipe  = psMetadataLookupPtr(&status, config->recipes, RECIPE_NAME);
 
-    // the following files are output targets
-    pmFPAfile *outImage = pmFPAfileDefineOutput(config, input->fpa, "PPNOISEMAP.OUTPUT");
+    int xBin1 = psMetadataLookupS32(&status, recipe, "XBIN");
+    int yBin1 = psMetadataLookupS32(&status, recipe, "YBIN");
+
+    // this generates an output that maps to the same input pixels:
+    // pmFPAfile *outImage = pmFPAfileDefineOutput(config, input->fpa, "PPNOISEMAP.OUTPUT");
+
+    // this output results in new pixels with binning:
+    pmFPAfile *outImage = pmFPAfileDefineFromFPA(config, input->fpa, xBin1, yBin1, "PPNOISEMAP.OUTPUT");
+
     if (!outImage) {
         psError(PS_ERR_IO, false, _("Unable to generate output file from PPNOISEMAP.OUTPUT"));
Index: trunk/ppNoiseMap/src/ppNoiseMapReadout.c
===================================================================
--- trunk/ppNoiseMap/src/ppNoiseMapReadout.c	(revision 24470)
+++ trunk/ppNoiseMap/src/ppNoiseMapReadout.c	(revision 24470)
@@ -0,0 +1,37 @@
+#include "ppNoiseMap.h"
+
+bool ppNoiseMapReadout (pmConfig *config, pmFPAview *view) {
+
+    pmFPAfile *outFile = psMetadataLookupPtr(NULL, config->files, "PPNOISEMAP.OUTPUT");
+    if (outFile == NULL) return false;
+
+    pmChip *inChip  = pmFPAviewThisChip (view, outFile->src);
+    pmChip *outChip = pmFPAviewThisChip (view, outFile->fpa);
+    if (!pmChipCopyStructure (outChip, inChip, outFile->xBin, outFile->yBin)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to copy chip structure.");
+        return false;
+    }
+
+    pmCell *cell;
+    pmReadout *inReadout, *outReadout;
+
+    while ((cell = pmFPAviewNextCell (view, outFile->src, 1)) != NULL) {
+        psLogMsg ("ppNoiseMapReadout", 5, "rebin: Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+        if (! cell->process || ! cell->file_exists) { continue; }
+
+        // process each of the readouts
+        while ((inReadout = pmFPAviewNextReadout (view, outFile->src, 1)) != NULL) {
+            if (! inReadout->data_exists) { continue; }
+
+            outReadout = pmFPAviewThisReadout (view, outFile->fpa);
+
+            // run the rebin code
+            if (!ppNoiseMapStats(outReadout, inReadout, 0, outFile->xBin, outFile->yBin)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to rebin readout.");
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
Index: trunk/ppNoiseMap/src/ppNoiseMapStats.c
===================================================================
--- trunk/ppNoiseMap/src/ppNoiseMapStats.c	(revision 24470)
+++ trunk/ppNoiseMap/src/ppNoiseMapStats.c	(revision 24470)
@@ -0,0 +1,99 @@
+#include "ppNoiseMap.h"
+
+bool ppNoiseMapStats(pmReadout *out, const pmReadout *in, psImageMaskType maskVal, int xBin, int yBin)
+{
+    PM_ASSERT_READOUT_NON_NULL(out, false);
+    PM_ASSERT_READOUT_NON_NULL(in, false);
+
+    psImage *inImage = in->image, *inMask = in->mask; // Input image
+    int numColsIn = inImage->numCols, numRowsIn = inImage->numRows; // Size of input image
+
+    psImageBinning *binning = psImageBinningAlloc(); // Binning instructions
+    binning->nXbin = xBin;
+    binning->nYbin = yBin;
+    binning->nXfine = numColsIn;
+    binning->nYfine = numRowsIn;
+    binning->nXskip = 0;
+    binning->nYskip = 0;
+    psImageBinningSetRuffSize(binning, PS_IMAGE_BINNING_CENTER);
+
+    int numColsOut = binning->nXruff, numRowsOut = binning->nYruff; // Size of output image
+
+    // re-use or re-generate the image
+    psImage *outImage;                  // Output image
+    if (out->image && out->image->numCols >= numColsOut && out->image->numRows >= numRowsOut) {
+        outImage = out->image;
+    } else {
+        outImage = out->image = psImageRecycle(out->image,  numColsOut, numRowsOut, PS_TYPE_F32);
+    }
+
+    psImage *outMask;                   // Output mask
+    if (out->mask && out->mask->numCols >= numColsOut && out->mask->numRows >= numRowsOut) {
+        outMask = out->mask;
+    } else {
+        outMask = out->mask = psImageRecycle(out->mask,  numColsOut, numRowsOut, PS_TYPE_IMAGE_MASK);
+    }
+
+    int nPixels = binning->nXbin * binning->nYbin;
+    psVector *values = psVectorAlloc (nPixels, PS_TYPE_F32);
+    psStats *stats = psStatsAlloc (PS_STAT_ROBUST_STDEV);
+    
+    int xLast = numColsIn - 1, yLast = numRowsIn - 1; // Last index
+    int yStart = psImageBinningGetFineY(binning, 0); // Starting input y for binning
+    for (int yOut = 0; yOut < numRowsOut; yOut++) {
+        int yStop = psImageBinningGetFineY(binning, yOut + 1); // Stopping input y for binning
+        yStop = PS_MIN(yStop, yLast);
+        int xStart = psImageBinningGetFineX(binning, 0); // Starting input x for binning
+        for (int xOut = 0; xOut < numColsOut; xOut++) {
+            int xStop = psImageBinningGetFineX(binning, xOut + 1); // Stopping input x for binning
+            xStop = PS_MIN(xStop, xLast);
+
+	    // save the pixels for this subcell into the vector, 
+            int numPix = 0;             // Number of pixels
+            for (int y = yStart; y < yStop; y++) {
+                for (int x = xStart; x < xStop; x++) {
+                    if (inMask && (inMask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] & maskVal)) {
+                        continue;
+                    }
+                    values->data.F32[numPix] = inImage->data.F32[y][x];
+                    numPix++;
+                }
+            }
+	    values->n = numPix;
+
+	    // measure the stats for this subcell
+	    // Values to set
+            float imageValue;
+	    psImageMaskType maskValue;
+	    psStatsInit (stats);
+	    if (!psVectorStats (stats, values, NULL, NULL, 0)) {
+		psWarning ("failure to measure stats for subcell %d,%d\n", xOut, yOut);
+                imageValue = NAN;
+                maskValue = maskVal;
+	    } else {
+                imageValue = stats->robustStdev;
+                maskValue = 0;
+            } 
+            outImage->data.F32[yOut][xOut] = imageValue;
+            outMask->data.PS_TYPE_IMAGE_MASK_DATA[yOut][xOut] = maskValue;
+            xStart = xStop;
+        }
+        yStart = yStop;
+    }
+
+    psFree(binning);
+    psFree(values);
+    psFree(stats);
+
+    out->data_exists = true;
+    if (out->parent) {
+        pmCell *outCell = out->parent;  // Output cell
+        outCell->data_exists = outCell->parent->data_exists = true;
+
+        // We would copy the concepts from the input cell, except that is done by pmFPACopy,
+        // pmChipCopyStructure, etc.  This function just does the mechanics of binning.
+        // We don't even update the CELL.XBIN, CELL.YBIN because that would apply the correction twice.
+    }
+
+    return true;
+}
