Index: /branches/eam_branches/20091201/ppSub/src/ppSub.h
===================================================================
--- /branches/eam_branches/20091201/ppSub/src/ppSub.h	(revision 26596)
+++ /branches/eam_branches/20091201/ppSub/src/ppSub.h	(revision 26597)
@@ -152,4 +152,7 @@
     );
 
+/// Generate JPEG images
+bool ppSubResidualSampleJpeg(pmConfig *config);
+
 /// Generate inverse subtraction
 bool ppSubReadoutInverse(pmConfig *config // Configuration
Index: /branches/eam_branches/20091201/ppSub/src/ppSubCamera.c
===================================================================
--- /branches/eam_branches/20091201/ppSub/src/ppSubCamera.c	(revision 26596)
+++ /branches/eam_branches/20091201/ppSub/src/ppSubCamera.c	(revision 26597)
@@ -297,4 +297,16 @@
     jpeg2->save = true;
 
+    // Output residual JPEG
+    pmFPAfile *jpeg3 = pmFPAfileDefineOutput(config, NULL, "PPSUB.OUTPUT.RESID.JPEG");
+    if (!jpeg3) {
+        psError(PS_ERR_IO, false, _("Unable to generate output file from PPSUB.OUTPUT.RESID.JPEG"));
+        return false;
+    }
+    if (jpeg3->type != PM_FPA_FILE_JPEG) {
+        psError(PS_ERR_IO, true, "PPSUB.OUTPUT.RESID.JPEG is not of type JPEG");
+        return false;
+    }
+    jpeg3->save = true;
+
     // Output subtraction kernel
     pmFPAfile *kernel = defineCalcFile(config, output, "PPSUB.OUTPUT.KERNELS", PM_FPA_FILE_SUBKERNEL);
Index: /branches/eam_branches/20091201/ppSub/src/ppSubFiles.c
===================================================================
--- /branches/eam_branches/20091201/ppSub/src/ppSubFiles.c	(revision 26596)
+++ /branches/eam_branches/20091201/ppSub/src/ppSubFiles.c	(revision 26597)
@@ -23,5 +23,5 @@
 // Subtraction files
 static const char *subFiles[] = { "PPSUB.OUTPUT", "PPSUB.OUTPUT.MASK", "PPSUB.OUTPUT.VARIANCE",
-                                  "PPSUB.OUTPUT.JPEG1", "PPSUB.OUTPUT.JPEG2",
+                                  "PPSUB.OUTPUT.JPEG1", "PPSUB.OUTPUT.JPEG2", "PPSUB.OUTPUT.RESID.JPEG",
                                   NULL };
 
Index: /branches/eam_branches/20091201/ppSub/src/ppSubLoop.c
===================================================================
--- /branches/eam_branches/20091201/ppSub/src/ppSubLoop.c	(revision 26596)
+++ /branches/eam_branches/20091201/ppSub/src/ppSubLoop.c	(revision 26597)
@@ -54,4 +54,9 @@
         // Can't do anything at all
         return true;
+    }
+    // generate the residual stamp grid for visualization
+    if (!ppSubResidualSampleJpeg(config)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to update.");
+        return false;
     }
 
@@ -130,4 +135,5 @@
     }
 
+    // generate the binned image used to write the jpeg
     if (!ppSubReadoutJpeg(config)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to update.");
Index: /branches/eam_branches/20091201/ppSub/src/ppSubReadoutJpeg.c
===================================================================
--- /branches/eam_branches/20091201/ppSub/src/ppSubReadoutJpeg.c	(revision 26596)
+++ /branches/eam_branches/20091201/ppSub/src/ppSubReadoutJpeg.c	(revision 26597)
@@ -49,2 +49,92 @@
     return true;
 }
+
+bool ppSubResidualSampleJpeg(pmConfig *config) {
+
+    pmFPAview *view = ppSubViewReadout(); // View to readout
+
+    // we save sample difference stamps on the convolved image readout->analysis metadata
+    pmReadout *inConv = pmFPAfileThisReadout(config->files, view, "PPSUB.INPUT.CONV"); // Input convolved
+
+    psArray *samples = psArrayAllocEmpty(16); // Array of sample stamp images
+
+    // we may have multiple entries with the same name; pull them off into a separate array:
+    psString regex = NULL;          // Regular expression
+    psStringAppend(&regex, "^%s$", "SUBTRACTION.SAMPLE.STAMP.SET");
+    psMetadataIterator *iter = psMetadataIteratorAlloc(inConv->analysis, PS_LIST_HEAD, regex); // Iterator
+    psFree(regex);
+
+    psMetadataItem *item = NULL;// Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+	assert(item->type == PS_DATA_ARRAY);
+	psArray *sampleStamps = item->data.V; 
+	samples = psArrayAdd(samples, 16, sampleStamps);
+    }
+    psFree(iter);
+    psAssert (samples, "no sample stamps?");
+    psAssert (samples->n, "no sample stamps?");
+
+    // get the kernel sizes
+    psArray *kernels = samples->data[0];
+    psAssert (kernels, "no valid kernel?");
+    psAssert (kernels->n, "no valid kernel?");
+
+    psImage *kernel = kernels->data[0];
+    psAssert (kernel, "missing valid kernel?");
+
+    int DX = kernel->numCols;
+    int DY = kernel->numRows;
+
+    // each array contains up to 9 sample stamps.  generate an image mosaicking these together in 3x3 blocks.
+    int innerBorder = 1;
+    int outerBorder = 2;
+    int NXblock = sqrt(samples->n);
+    int NYblock = samples->n / NXblock;
+    if (samples->n % NXblock) NYblock ++;
+
+    int NXpix = NXblock * (3 * (DX + innerBorder) + outerBorder);
+    int NYpix = NYblock * (3 * (DY + innerBorder) + outerBorder);
+
+    // output cell
+    pmCell *cell = pmFPAfileThisCell(config->files, view, "PPSUB.OUTPUT.RESID.JPEG");
+    pmReadout *readout = pmReadoutAlloc(cell);
+    readout->image = psImageAlloc(NXpix, NYpix, PS_TYPE_F32);
+
+    cell->data_exists = true;
+    cell->parent->data_exists = true;
+
+    psImageInit (readout->image, 0.0);
+
+    for (int i = 0; i < samples->n; i++) {
+
+	int xBlock = i % NXblock;
+	int yBlock = i / NYblock;
+	     
+	psArray *kernels = samples->data[i];
+
+	for (int j = 0; j < kernels->n; j++) {
+
+	    psImage *kernel = kernels->data[j];
+
+	    int xSub = j % 3;
+	    int ySub = j / 3;
+
+	    int xPix = xBlock * (3 * (DX + innerBorder) + outerBorder) + xSub * (DX + innerBorder);
+	    int yPix = yBlock * (3 * (DX + innerBorder) + outerBorder) + ySub * (DY + innerBorder);
+
+	    for (int y = 0; y < kernel->numRows; y++) {
+		for (int x = 0; x < kernel->numCols; x++) {
+		    readout->image->data.F32[y + yPix][x + xPix] = kernel->data.F32[y][x];
+		}
+	    }
+	}
+    }
+
+    {
+	psFits *fits = psFitsOpen ("resid.stamps.fits", "w");
+	psFitsWriteImage (fits, NULL, readout->image, 0, NULL);
+	psFitsClose (fits);
+    }
+
+    return true;
+}
