Index: /trunk/dvoTools/src/dvoMakeCorrUnbin.c
===================================================================
--- /trunk/dvoTools/src/dvoMakeCorrUnbin.c	(revision 12591)
+++ /trunk/dvoTools/src/dvoMakeCorrUnbin.c	(revision 12592)
@@ -26,21 +26,14 @@
     assert (trimsec);
 
-    // dimensions of input image:
-    int nx = inData->image->numCols;
-    int ny = inData->image->numRows;
+    // I have the fine and ruff image sizes, determine the binning factor
+    psImageBinning *binning = psImageBinningAlloc();
+    binning->nXruff = inData->numCols;
+    binning->nYruff = inData->numRows;
+    binning->nXfine = trimsec->x1 - trimsec->x0;
+    binning->nYfine = trimsec->y1 - trimsec->y0;
+    psImageBinningSetBinning (binning, PS_IMAGE_BINNING_CENTER);
+    psImageBinningSetSkip(binning, inData);
 
-    // dimensions of output image:
-    int Nx = trimsec->x1 - trimsec->x0;
-    int Ny = trimsec->y1 - trimsec->y0;
-
-    // choose the binning factor which would yield nx,ny pixels from Nx,Ny
-    int DX = Nx / nx;			// 36 / 3 = 12, 35 / 3 = 11, 34 / 3 = 11
-    if (Nx % nx) DX ++;			// 36, 35, 34 -> DX = 12
-    int xOffset = (Nx % nx) / 2;	// for nx = 3, xOffset = 0 or 1
-    
-    int DY = Ny / ny;
-    if (Ny % ny) DY ++;
-    int yOffset = (Ny % ny) / 2;
-
+    // construct the supporing pmFPA/pmChip/pmCell structures
     pmReadout *outData = pmFPAviewThisReadout (view, outFile->fpa);
     if (outData == NULL) {
@@ -48,14 +41,16 @@
 	pmChip *outChip = pmFPAviewThisChip (view, outFile->fpa);
 
-	pmChipCopyStructure (outChip, inChip, DX, DY);
+	pmChipCopyStructure (outChip, inChip, binning->nXbin, binning->nYbin);
 	outData = pmFPAviewThisReadout (view, outFile->fpa);
 	assert (outData != NULL);
     }
 
-    psImageRecycle (outData->image, Nx, Ny, PS_TYPE_F32);
+    // generate the output (fine-scale) image array
+    psImageRecycle (outData->image, binning->nXfine, binning->nYfine, PS_TYPE_F32);
 
-    // linear interpolation to full-scale
-    if (!psImageUnbin (outData->image, inData->image, DX, DY, xOffset, yOffset)) {
+    // linear interpolation to full fine scale
+    if (!psImageUnbin (outData->image, inData->image, binning)) {
 	psError (PS_ERR_UNKNOWN, true, "failed to unbin image");
+	psFree (binning);
 	return false;
     }
@@ -74,4 +69,5 @@
     // exit (0);
 
+    psFree (binning);
     return true;
 }
Index: /trunk/ppImage/src/ppImageRebinReadout.c
===================================================================
--- /trunk/ppImage/src/ppImageRebinReadout.c	(revision 12591)
+++ /trunk/ppImage/src/ppImageRebinReadout.c	(revision 12592)
@@ -63,4 +63,5 @@
 }
 
+// XXX this should be made consistent with psImageBinning
 bool ppImageRebinReadout (pmReadout *output, pmReadout *input, pmFPAfile *outFile)
 {
Index: /trunk/psphot/src/psphotImageLoop.c
===================================================================
--- /trunk/psphot/src/psphotImageLoop.c	(revision 12591)
+++ /trunk/psphot/src/psphotImageLoop.c	(revision 12592)
@@ -63,6 +63,7 @@
 	while ((cell = pmFPAviewNextCell (view, input->fpa, 1)) != NULL) {
             psLogMsg ("psphot", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+	    // XXX who should be setting cell->process?
             // if (! cell->process || ! cell->file_exists) { continue; }
-            if (! cell->process) { continue; }
+            // if (! cell->process) { continue; }
 	    
 	    // process each of the readouts
Index: /trunk/psphot/src/psphotImageMedian.c
===================================================================
--- /trunk/psphot/src/psphotImageMedian.c	(revision 12591)
+++ /trunk/psphot/src/psphotImageMedian.c	(revision 12592)
@@ -1,3 +1,4 @@
 # include "psphot.h"
+// # define TESTSAVE
 
 // generate the median in NxN boxes, clipping heavily
@@ -8,5 +9,4 @@
     pmFPA *inFPA;
     pmFPAfile *file;
-    psRegion region;
     static char *defaultStatsName = "FITTED_MEAN";
 
@@ -85,32 +85,20 @@
     psImage *mask  = readout->mask;
 
-    // dimensions of input & output image
-    int Nx = image->numCols;
-    int Ny = image->numRows;
-
-    // scaling factor
-    int DX = psMetadataLookupS32 (&status, recipe, "BACKGROUND.XBIN");
-    int DY = psMetadataLookupS32 (&status, recipe, "BACKGROUND.YBIN");
-
-    // overhang : we will balance this evenly
-    int xExtra = (Nx % DX) / 2;
-    int yExtra = (Ny % DY) / 2;
-    int xOffset = (xExtra > 0) ? DX - xExtra : 0;
-    int yOffset = (yExtra > 0) ? DY - yExtra : 0;
-    xOffset -= image->col0;
-    yOffset -= image->row0;
-
-    // dimensions of binned image
-    int nx, ny;
-    if (Nx % DX == 0) {
-	nx = Nx / DX;
-    } else {
-	nx = (xExtra) ? (Nx / DX) + 2 : (Nx / DX) + 1;
-    }
-    if (Ny % DY == 0) {
-	ny = Ny / DY;
-    } else {
-	ny = (yExtra) ? (Ny / DY) + 2 : (Ny / DY) + 1;
-    }
+    // I have the fine image size, I know the binning factor, determine the ruff image size
+    psImageBinning *binning = psImageBinningAlloc();
+    binning->nXfine = image->numCols;
+    binning->nYfine = image->numRows;
+    binning->nXbin  = psMetadataLookupS32 (&status, recipe, "BACKGROUND.XBIN");
+    binning->nYbin  = psMetadataLookupS32 (&status, recipe, "BACKGROUND.YBIN");
+
+    psImageBinningSetRuffSize(binning, PS_IMAGE_BINNING_CENTER);
+    psImageBinningSetSkip(binning, image);
+    status = psMetadataAddPtr(recipe, PS_LIST_TAIL, "PSPHOT.BACKGROUND.BINNING", PS_DATA_UNKNOWN, "Background binning", binning);
+    if (!status) {
+	psError (PSPHOT_ERR_PROG, false, "failed to save bininnng");
+	return false;
+    }
+
+    // we save the binning structure for use in psphotMagnitudes
 
     // we have 4 possibilities: (INTERNAL or I/O file) and (exists or not) 
@@ -119,5 +107,5 @@
     if (file == NULL) {
 	// we are not using PSPHOT.BACKMDL as an I/O file: define an internal version
-        model = pmFPAfileDefineInternal (config->files, "PSPHOT.BACKMDL", nx, ny, PS_TYPE_F32);
+        model = pmFPAfileDefineInternal (config->files, "PSPHOT.BACKMDL", binning->nXruff, binning->nYruff, PS_TYPE_F32);
     } else {
 	if (file->mode == PM_FPA_MODE_INTERNAL) {
@@ -130,10 +118,9 @@
 		// readout does not yet exist: create from input
 		// XXX we have an inconsistency in this calculation here and in pmFPACopy
-		pmFPAfileCopyStructureView (file->fpa, inFPA, DX, DY, view);
+		// XXX use the psImageBinning functions to set the output image size
+		pmFPAfileCopyStructureView (file->fpa, inFPA, binning->nXbin, binning->nYbin, view);
 		model = pmFPAviewThisReadout (view, file->fpa);
-		if ((nx != model->image->numCols) || (ny != model->image->numRows)) {
-		    psError (PSPHOT_ERR_PROG, true, "inconsistent sizes for model dimensions");
-		    return false;
-		}
+		assert (binning->nXruff == model->image->numCols);
+		assert (binning->nYruff == model->image->numRows);
 	    }
 	}
@@ -141,34 +128,22 @@
     psF32 **modelData = model->image->data.F32;
 
-    assert(model->analysis != NULL);
-    psMetadataAdd(model->analysis, PS_LIST_TAIL, "PSPHOT.BACKGROUND.XBIN", PS_DATA_S32 | PS_META_REPLACE,
-		  "Background x-binsize", DX);
-    psMetadataAdd(model->analysis, PS_LIST_TAIL, "PSPHOT.BACKGROUND.YBIN", PS_DATA_S32 | PS_META_REPLACE,
-		  "Background x-binsize", DY);
-    psMetadataAdd(model->analysis, PS_LIST_TAIL, "PSPHOT.BACKGROUND.XOFF", PS_DATA_S32 | PS_META_REPLACE,
-		  "Background x-overhang", xOffset);
-    psMetadataAdd(model->analysis, PS_LIST_TAIL, "PSPHOT.BACKGROUND.YOFF", PS_DATA_S32 | PS_META_REPLACE,
-		  "Background y-overhang", yOffset);
-
-
     // measure clipped median for subimages
-    for (int iy = 0; iy < ny; iy++) {
-        for (int ix = 0; ix < nx; ix++) {
-            // sx, sy are in parent coords
-            int sx = ix*DX - xOffset;
-            int sy = iy*DY - yOffset;
-
-	    // XXX test override
-	    // sx = 1955;
-	    // sy = 4;
-
-            region = psRegionSet (sx, sx + 2*DX, sy, sy + 2*DY);
-            region = psRegionForImage (image, region);
-            psImage *subset  = psImageSubset (image, region);
+    psRegion ruffRegion = {0,0,0,0};
+    psRegion fineRegion = {0,0,0,0};
+    for (int iy = 0; iy < model->image->numRows; iy++) {
+        for (int ix = 0; ix < model->image->numCols; ix++) {
+	    
+	    // convert the ruff grid cell to the equivalent fine grid cell
+	    // XXX we need to watch out for row0,col0
+	    ruffRegion = psRegionSet (ix, ix + 2, iy, iy + 2);
+	    fineRegion = psImageBinningSetFineRegion (binning, ruffRegion);
+            fineRegion = psRegionForImage (image, fineRegion);
+
+            psImage *subset  = psImageSubset (image, fineRegion);
 	    if (!subset->numCols || !subset->numRows) {
 		psFree (subset);
 		continue;
 	    }
-            psImage *submask = psImageSubset (mask, region);
+            psImage *submask = psImageSubset (mask, fineRegion);
 
 	    // reset the default values
@@ -210,9 +185,9 @@
 
     // patch over bad regions (use average of 8 possible neighbor pixels)
-    // XXX consider testing pixels against the 8 neighbors and replacing outliers...
+    // XXX consider testing all pixels against the 8 neighbors and replacing outliers...
     float Count = 0;
     float Value = 0;
-    for (int iy = 0; iy < ny; iy++) {
-        for (int ix = 0; ix < nx; ix++) {
+    for (int iy = 0; iy < model->image->numRows; iy++) {
+        for (int ix = 0; ix < model->image->numCols; ix++) {
 	    if (!isnan(modelData[iy][ix])) {
 		Value += modelData[iy][ix];
@@ -224,9 +199,9 @@
 	    for (int jy = iy - 1; jy <= iy + 1; jy++) {
 		if (jy <   0) continue;
-		if (jy >= ny) continue;
+		if (jy >= model->image->numRows) continue;
 		for (int jx = ix - 1; jx <= ix + 1; jx++) {
 		    if (!jx && !jy) continue;
 		    if (jx   <   0) continue;
-		    if (jx   >= nx) continue;
+		    if (jx   >= model->image->numCols) continue;
 		    value += modelData[jy][jx];
 		    count += 1.0;
@@ -240,6 +215,6 @@
 
     // patch over remaining bad regions (use global average)
-    for (int iy = 0; iy < ny; iy++) {
-        for (int ix = 0; ix < nx; ix++) {
+    for (int iy = 0; iy < model->image->numRows; iy++) {
+        for (int ix = 0; ix < model->image->numCols; ix++) {
 	    if (!isnan(modelData[iy][ix])) continue;
 	    modelData[iy][ix] = Value;
@@ -253,9 +228,9 @@
     psImageStats (statsBck, model->image, NULL, 0);
     psMetadataAdd (recipe, PS_LIST_TAIL, "SKY_MEAN", PS_DATA_F32 | PS_META_REPLACE, "sky model mean",          statsBck->sampleMean);
-    psMetadataAdd (recipe, PS_LIST_TAIL, "SKYSTDEV", PS_DATA_F32 | PS_META_REPLACE, "sky model stdev",        statsBck->sampleStdev);
+    psMetadataAdd (recipe, PS_LIST_TAIL, "SKYSTDEV", PS_DATA_F32 | PS_META_REPLACE, "sky model stdev",         statsBck->sampleStdev);
     psMetadataAdd (recipe, PS_LIST_TAIL, "SKY_MAX",  PS_DATA_F32 | PS_META_REPLACE, "sky model maximum value", statsBck->max);
     psMetadataAdd (recipe, PS_LIST_TAIL, "SKY_MIN",  PS_DATA_F32 | PS_META_REPLACE, "sky model minimum value", statsBck->min);
-    psMetadataAdd (recipe, PS_LIST_TAIL, "SKY_NX",   PS_DATA_S32 | PS_META_REPLACE, "sky model size (x)",      nx);
-    psMetadataAdd (recipe, PS_LIST_TAIL, "SKY_NY",   PS_DATA_S32 | PS_META_REPLACE, "sky model size (y)",      ny);
+    psMetadataAdd (recipe, PS_LIST_TAIL, "SKY_NX",   PS_DATA_S32 | PS_META_REPLACE, "sky model size (x)",      model->image->numCols);
+    psMetadataAdd (recipe, PS_LIST_TAIL, "SKY_NY",   PS_DATA_S32 | PS_META_REPLACE, "sky model size (y)",      model->image->numRows);
     psLogMsg ("psphot", PS_LOG_INFO, "background sky : min %f mean %f max %f stdev %f", 
 	      statsBck->min, statsBck->sampleMean, statsBck->max, statsBck->sampleStdev);
@@ -275,5 +250,5 @@
 	    pmFPAfileCopyStructureView (file->fpa, inFPA, 1, 1, view);
 	    background = pmFPAviewThisReadout (view, file->fpa);
-	    if ((Nx != background->image->numCols) || (Ny != background->image->numRows)) {
+	    if ((image->numCols != background->image->numCols) || (image->numRows != background->image->numRows)) {
 		psError (PSPHOT_ERR_PROG, true, "inconsistent sizes for background dimensions");
 		return false;
@@ -281,11 +256,11 @@
 	}
     } else {
-        background = pmFPAfileDefineInternal (config->files, "PSPHOT.BACKGND", Nx, Ny, PS_TYPE_F32);
+        background = pmFPAfileDefineInternal (config->files, "PSPHOT.BACKGND", image->numCols, image->numRows, PS_TYPE_F32);
     }
     psF32 **backData = background->image->data.F32;
 
     // linear interpolation to full-scale
-    if (!psImageUnbin (background->image, model->image, DX, DY, xOffset, yOffset)) {
-	psError (PSPHOT_ERR_PROG, true, "failed to build background iamge");
+    if (!psImageUnbin (background->image, model->image, binning)) {
+	psError (PSPHOT_ERR_PROG, true, "failed to build background image");
 	return false;
     }
@@ -330,4 +305,5 @@
     psFree(stats);
     psFree(statsDefaults);
+    psFree(binning);
     psFree(rng);
 
@@ -335,2 +311,70 @@
     return true;
 }
+
+
+# if (0)
+
+    // dimensions of input & output image
+    int Nx = image->numCols;
+    int Ny = image->numRows;
+
+    // scaling factor
+    int DX = psMetadataLookupS32 (&status, recipe, "BACKGROUND.XBIN");
+    int DY = psMetadataLookupS32 (&status, recipe, "BACKGROUND.YBIN");
+
+    // overhang : we will balance this evenly
+    int xExtra = (Nx % DX) / 2;
+    int yExtra = (Ny % DY) / 2;
+    int xOffset = (xExtra > 0) ? DX - xExtra : 0;
+    int yOffset = (yExtra > 0) ? DY - yExtra : 0;
+    xOffset -= image->col0;
+    yOffset -= image->row0;
+
+    // dimensions of binned image
+    int nx, ny;
+    if (Nx % DX == 0) {
+	nx = Nx / DX;
+    } else {
+	nx = (xExtra) ? (Nx / DX) + 2 : (Nx / DX) + 1;
+    }
+    if (Ny % DY == 0) {
+	ny = Ny / DY;
+    } else {
+	ny = (yExtra) ? (Ny / DY) + 2 : (Ny / DY) + 1;
+    }
+
+    // we have 4 possibilities: (INTERNAL or I/O file) and (exists or not) 
+    // select model pixels (from output background model file, or create internal file)
+    file = psMetadataLookupPtr (&status, config->files, "PSPHOT.BACKMDL");
+    if (file == NULL) {
+	// we are not using PSPHOT.BACKMDL as an I/O file: define an internal version
+        model = pmFPAfileDefineInternal (config->files, "PSPHOT.BACKMDL", nx, ny, PS_TYPE_F32);
+    } else {
+	if (file->mode == PM_FPA_MODE_INTERNAL) {
+	    // we are not using PSPHOT.BACKMDL as an I/O file: already defined above
+	    model = file->readout;
+	} else {
+	    // we are using PSPHOT.BACKMDL as an I/O file: select readout or create
+	    model = pmFPAviewThisReadout (view, file->fpa);
+	    if (model == NULL) {
+		// readout does not yet exist: create from input
+		// XXX we have an inconsistency in this calculation here and in pmFPACopy
+		pmFPAfileCopyStructureView (file->fpa, inFPA, DX, DY, view);
+		model = pmFPAviewThisReadout (view, file->fpa);
+		if ((nx != model->image->numCols) || (ny != model->image->numRows)) {
+		    psError (PSPHOT_ERR_PROG, true, "inconsistent sizes for model dimensions");
+		    return false;
+		}
+	    }
+	}
+    }
+    psF32 **modelData = model->image->data.F32;
+
+
+
+            // sx, sy are in parent coords
+            int sx = ix*DX - xOffset;
+            int sy = iy*DY - yOffset;
+            region = psRegionSet (sx, sx + 2*DX, sy, sy + 2*DY);
+
+# endif
Index: /trunk/psphot/src/psphotMagnitudes.c
===================================================================
--- /trunk/psphot/src/psphotMagnitudes.c	(revision 12591)
+++ /trunk/psphot/src/psphotMagnitudes.c	(revision 12592)
@@ -18,12 +18,7 @@
     // Get enough information to return sky level
     assert(background != NULL);
-    assert(background->analysis != NULL);
-    int DX = psMetadataLookupS32(&status, background->analysis, "PSPHOT.BACKGROUND.XBIN");
-    assert (status == true);
-    int DY = psMetadataLookupS32(&status, background->analysis, "PSPHOT.BACKGROUND.YBIN");
-    assert (status == true);
-    int dx = psMetadataLookupS32(&status, background->analysis, "PSPHOT.BACKGROUND.XOFF");
-    assert (status == true);
-    int dy = psMetadataLookupS32(&status, background->analysis, "PSPHOT.BACKGROUND.YOFF");
+
+    // the binning details are saved on the analysis metadata 
+    psImageBinning *binning = psMetadataLookupPtr(&status, recipe, "PSPHOT.BACKGROUND.BINNING");
     assert (status == true);
 
@@ -40,5 +35,5 @@
 	if (status) Nap ++;
 
-	source->sky = psImageUnbinPixel(source->peak->x, source->peak->y, background->image, DX, DY, dx, dy);
+	source->sky = psImageUnbinPixel(source->peak->x, source->peak->y, background->image, binning);
 	if (isnan(source->sky) && false) {
 	  psError(PSPHOT_ERR_SKY, false, "Setting pmSource.sky");
