Index: trunk/psphot/src/psphotRoughClass.c
===================================================================
--- trunk/psphot/src/psphotRoughClass.c	(revision 19913)
+++ trunk/psphot/src/psphotRoughClass.c	(revision 19914)
@@ -1,53 +1,40 @@
 # include "psphotInternal.h"
 
-# define CHECK_STATUS(S,MSG) { \
-  if (!status) { \
-    psError(PSPHOT_ERR_CONFIG, false, "missing PSF Clump entry: %s\n", MSG); \
-    return false; \
-  } }
+# define CHECK_STATUS(S,MSG) {						\
+	if (!status) {							\
+	    psError(PSPHOT_ERR_CONFIG, false, "missing PSF Clump entry: %s\n", MSG); \
+	    return false;						\
+	} }
+
+bool psphotRoughClassRegion (int nRegion, psRegion *region, psArray *sources, psMetadata *recipe, const bool havePSF);
 
 // 2006.02.02 : no leaks
-bool psphotRoughClass (psArray *sources, psMetadata *recipe, const bool havePSF) {
+bool psphotRoughClass (pmReadout *readout, psArray *sources, psMetadata *recipe, const bool havePSF) {
 
     bool status;
-    static pmPSFClump psfClump;
 
     psTimerStart ("psphot");
 
-    // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
-    psMaskType maskSat = psMetadataLookupU8(&status, recipe, "MASK.SAT"); // Mask value for bad pixels
-    assert (maskSat);
+    // we make this measurement on a NxM grid of regions across the readout
+    int NX  = psMetadataLookupS32 (&status, recipe, "PSF_CLUMP_NX");  CHECK_STATUS (status, "PSF_CLUMP_NX");
+    int NY  = psMetadataLookupS32 (&status, recipe, "PSF_CLUMP_NY");  CHECK_STATUS (status, "PSF_CLUMP_NY");
+    int dX  = readout->image->numCols / NX;
+    int dY  = readout->image->numRows / NY;
 
-    if (!havePSF) {
-        // determine the PSF parameters from the source moment values
-        psfClump = pmSourcePSFClump (sources, recipe);
-	psMetadataAddF32 (recipe, PS_LIST_TAIL, "PSF.CLUMP.X",  PS_META_REPLACE, "psf clump center", psfClump.X);
-	psMetadataAddF32 (recipe, PS_LIST_TAIL, "PSF.CLUMP.Y",  PS_META_REPLACE, "psf clump center", psfClump.Y);
-	psMetadataAddF32 (recipe, PS_LIST_TAIL, "PSF.CLUMP.DX", PS_META_REPLACE, "psf clump center", psfClump.dX);
-	psMetadataAddF32 (recipe, PS_LIST_TAIL, "PSF.CLUMP.DY", PS_META_REPLACE, "psf clump center", psfClump.dY);
-    } else {
-        // pull FWHM_X,Y from the recipe, use to define psfClump.X,Y
-        psfClump.X  = psMetadataLookupF32 (&status, recipe, "PSF.CLUMP.X");  CHECK_STATUS (status, "PSF.CLUMP.X");
-        psfClump.Y  = psMetadataLookupF32 (&status, recipe, "PSF.CLUMP.Y");  CHECK_STATUS (status, "PSF.CLUMP.Y");
-        psfClump.dX = psMetadataLookupF32 (&status, recipe, "PSF.CLUMP.DX");  CHECK_STATUS (status, "PSF.CLUMP.DX");
-        psfClump.dY = psMetadataLookupF32 (&status, recipe, "PSF.CLUMP.DY");  CHECK_STATUS (status, "PSF.CLUMP.DY");
+    int nRegion = 0;
+    for (int ix = 0; ix < NX; ix ++) {
+	for (int iy = 0; iy < NY; iy ++) {
+
+	    psRegion region = psRegionSet (ix*dX, (ix + 1)*dX, iy*dY, (iy + 1)*dY);
+	    if (!psphotRoughClassRegion (nRegion, &region, sources, recipe, havePSF)) {
+		psError (PSPHOT_ERR_UNKNOWN, false, "failed to determine rough classification for region %d (%f - %f : %f - %f)\n", 
+			 nRegion, region.x0, region.x1, region.y0, region.y1);
+		return false;
+	    }
+	    
+	    nRegion ++;
+	}
     }
-
-    if (psfClump.X < 0) {
-        psError(PSPHOT_ERR_PROG, false, "programming error calling pmSourcePSFClump");
-        return false;
-    }
-    if (!psfClump.X || !psfClump.Y) {
-        psError(PSPHOT_ERR_DATA, true, "Failed to find a valid PSF clump");
-        return false;
-    }
-    psLogMsg ("psphot", 3, "psf clump  X,  Y: %f, %f\n", psfClump.X, psfClump.Y);
-    psLogMsg ("psphot", 3, "psf clump DX, DY: %f, %f\n", psfClump.dX, psfClump.dY);
-
-    // group into STAR, COSMIC, EXTENDED, SATURATED, etc.
-    if (!pmSourceRoughClass (sources, recipe, psfClump, maskSat)) {
-        psError(PSPHOT_ERR_PROG, false, "programming error calling pmSourceRoughClass");
-        return false;
-    }
+    psMetadataAddS32 (recipe, PS_LIST_TAIL, "PSF.CLUMP.NREGIONS",  PS_META_REPLACE, "psf clump regions", nRegion);
 
     // optional printout of source moments only
@@ -56,5 +43,61 @@
     psLogMsg ("psphot.roughclass", PS_LOG_INFO, "rough classification: %f sec\n", psTimerMark ("psphot"));
 
+    psphotVisualPlotMoments (recipe, sources);
+    psphotVisualShowRoughClass (sources);
+    psphotVisualShowFlags (sources);
+
     return true;
 }
 
+bool psphotRoughClassRegion (int nRegion, psRegion *region, psArray *sources, psMetadata *recipe, const bool havePSF) {
+
+    bool status;
+    char regionName[64];
+    pmPSFClump psfClump;
+
+    // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
+    psMaskType maskSat = psMetadataLookupU8(&status, recipe, "MASK.SAT"); // Mask value for bad pixels
+    assert (maskSat);
+
+    snprintf (regionName, 64, "PSF.CLUMP.REGION.%03d", nRegion);
+    psMetadata *regionMD = psMetadataLookupPtr (&status, recipe, regionName);
+    if (!regionMD) {
+	regionMD = psMetadataAlloc();
+	psMetadataAddMetadata (recipe, PS_LIST_TAIL, regionName, PS_META_REPLACE, "psf clump region", regionMD);
+	psFree (regionMD);
+    }
+
+    if (!havePSF) {
+	// determine the PSF parameters from the source moment values
+	psfClump = pmSourcePSFClump (region, sources, recipe);
+	psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.X",  PS_META_REPLACE, "psf clump center", psfClump.X);
+	psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.Y",  PS_META_REPLACE, "psf clump center", psfClump.Y);
+	psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.DX", PS_META_REPLACE, "psf clump center", psfClump.dX);
+	psMetadataAddF32 (regionMD, PS_LIST_TAIL, "PSF.CLUMP.DY", PS_META_REPLACE, "psf clump center", psfClump.dY);
+    } else {
+	// pull FWHM_X,Y from the recipe, use to define psfClump.X,Y
+	psfClump.X  = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.X");   CHECK_STATUS (status, "PSF.CLUMP.X");
+	psfClump.Y  = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.Y");   CHECK_STATUS (status, "PSF.CLUMP.Y");
+	psfClump.dX = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.DX");  CHECK_STATUS (status, "PSF.CLUMP.DX");
+	psfClump.dY = psMetadataLookupF32 (&status, regionMD, "PSF.CLUMP.DY");  CHECK_STATUS (status, "PSF.CLUMP.DY");
+    }
+
+    if (psfClump.X < 0) {
+	psError(PSPHOT_ERR_PROG, false, "programming error calling pmSourcePSFClump");
+	return false;
+    }
+    if (!psfClump.X || !psfClump.Y) {
+	psError(PSPHOT_ERR_DATA, true, "Failed to find a valid PSF clump");
+	return false;
+    }
+    psLogMsg ("psphot", 3, "psf clump  X,  Y: %f, %f\n", psfClump.X, psfClump.Y);
+    psLogMsg ("psphot", 3, "psf clump DX, DY: %f, %f\n", psfClump.dX, psfClump.dY);
+
+    // group into STAR, COSMIC, EXTENDED, SATURATED, etc.
+    if (!pmSourceRoughClass (region, sources, recipe, psfClump, maskSat)) {
+	psError(PSPHOT_ERR_PROG, false, "programming error calling pmSourceRoughClass");
+	return false;
+    }
+
+    return true;
+}
