Index: trunk/psphot/src/psphotSourceStats.c
===================================================================
--- trunk/psphot/src/psphotSourceStats.c	(revision 21183)
+++ trunk/psphot/src/psphotSourceStats.c	(revision 21359)
@@ -1,3 +1,5 @@
 # include "psphotInternal.h"
+
+bool psphotSourceStats_Unthreaded (int *nfail, int *nmoments, psArray *sources, psMetadata *recipe);
 
 psArray *psphotSourceStats (pmConfig *config, pmReadout *readout, pmDetections *detections) {
@@ -78,40 +80,52 @@
 	for (int j = 0; j < cells->n; j++) {
 
-	    // allocate a job -- if threads are not defined, this just runs the job
-	    psThreadJob *job = psThreadJobAlloc ("PSPHOT_SOURCE_STATS");
-
-	    psArrayAdd(job->args, 1, cells->data[j]); // sources
-	    psArrayAdd(job->args, 1, recipe);
-	    PS_ARRAY_ADD_SCALAR(job->args, 0, PS_TYPE_S32); // this is used as a return value for Nmoments
-	    PS_ARRAY_ADD_SCALAR(job->args, 0, PS_TYPE_S32); // this is used as a return value for Nfail
-
-	    if (!psThreadJobAddPending(job)) {
+	    if (nThreads) {
+		// allocate a job -- if threads are not defined, this just runs the job
+		psThreadJob *job = psThreadJobAlloc ("PSPHOT_SOURCE_STATS");
+
+		psArrayAdd(job->args, 1, cells->data[j]); // sources
+		psArrayAdd(job->args, 1, recipe);
+		PS_ARRAY_ADD_SCALAR(job->args, 0, PS_TYPE_S32); // this is used as a return value for Nmoments
+		PS_ARRAY_ADD_SCALAR(job->args, 0, PS_TYPE_S32); // this is used as a return value for Nfail
+
+		if (!psThreadJobAddPending(job)) {
+		    psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
+		    psFree (job);
+		    return NULL;
+		}
+		psFree(job);
+	    } else {
+		int nfail = 0;
+		int nmoments = 0;
+		if (!psphotSourceStats_Unthreaded (&nfail, &nmoments, cells->data[j], recipe)) {
+		    psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
+		    return NULL;
+		}
+		Nfail += nfail;
+		Nmoments += nmoments;
+	    }
+	}
+
+	if (nThreads) {
+	    // wait for the threads to finish and manage results
+	    if (!psThreadPoolWait (false)) {
 		psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
-		psFree (job);
 		return NULL;
 	    }
-	    psFree(job);
-
-	}
-
-	// wait for the threads to finish and manage results
-	if (!psThreadPoolWait (false)) {
-	    psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
-	    return NULL;
-	}
-
-	// we have only supplied one type of job, so we can assume the types here
-	psThreadJob *job = NULL;
-	while ((job = psThreadJobGetDone()) != NULL) {
-	    if (job->args->n < 1) {
-		fprintf (stderr, "error with job\n");
-	    } else {
-		psScalar *scalar = NULL;
-		scalar = job->args->data[2];
-		Nmoments += scalar->data.S32;
-		scalar = job->args->data[3];
-		Nfail += scalar->data.S32;
+
+	    // we have only supplied one type of job, so we can assume the types here
+	    psThreadJob *job = NULL;
+	    while ((job = psThreadJobGetDone()) != NULL) {
+		if (job->args->n < 1) {
+		    fprintf (stderr, "error with job\n");
+		} else {
+		    psScalar *scalar = NULL;
+		    scalar = job->args->data[2];
+		    Nmoments += scalar->data.S32;
+		    scalar = job->args->data[3];
+		    Nfail += scalar->data.S32;
+		}
+		psFree(job);
 	    }
-	    psFree(job);
 	}
     }
@@ -213,2 +227,83 @@
     return true;
 }
+
+bool psphotSourceStats_Unthreaded (int *nfail, int *nmoments, psArray *sources, psMetadata *recipe) {
+
+    bool status = false;
+    float BIG_RADIUS;
+
+    float INNER    = psMetadataLookupF32 (&status, recipe, "SKY_INNER_RADIUS");
+    if (!status) return false;
+    float MIN_SN   = psMetadataLookupF32 (&status, recipe, "MOMENTS_SN_MIN");
+    if (!status) return false;
+    float RADIUS   = psMetadataLookupF32 (&status, recipe, "PSF_MOMENTS_RADIUS");
+    if (!status) return false;
+
+    // bit-masks to test for good/bad pixels
+    psImageMaskType maskVal = psMetadataLookupImageMask(&status, recipe, "MASK.PSPHOT");
+    assert (maskVal);
+
+    // bit-mask to mark pixels not used in analysis
+    psImageMaskType markVal = psMetadataLookupImageMask(&status, recipe, "MARK.PSPHOT");
+    assert (markVal);
+
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    // threaded measurement of the sources moments
+    int Nfail = 0;
+    int Nmoments = 0;
+    for (int i = 0; i < sources->n; i++) {
+        pmSource *source = sources->data[i];
+
+        // skip faint sources for moments measurement
+        if (source->peak->SN < MIN_SN) {
+            continue;
+        }
+
+        // measure a local sky value
+        // the local sky is now ignored; kept here for reference only
+        status = pmSourceLocalSky (source, PS_STAT_SAMPLE_MEDIAN, INNER, maskVal, markVal);
+        if (!status) {
+	    psErrorClear(); // XXX re-consider the errors raised here
+	    Nfail ++;
+	    continue;
+        }
+
+        // measure the local sky variance (needed if noise is not sqrt(signal))
+        // XXX EAM : this should use ROBUST not SAMPLE median, but it is broken
+        status = pmSourceLocalSkyVariance (source, PS_STAT_SAMPLE_MEDIAN, INNER, maskVal, markVal);
+        if (!status) {
+	    Nfail ++;
+	    psErrorClear();
+	    continue;
+        }
+
+        // measure basic source moments
+        status = pmSourceMoments (source, RADIUS);
+        if (status) {
+            Nmoments ++;
+            continue;
+        }
+
+        // if no valid pixels, or massive swing, likely saturated source,
+        // try a much larger box
+        BIG_RADIUS = PS_MIN (INNER, 3*RADIUS);
+        psTrace ("psphot", 4, "retrying moments for %d, %d\n", source->peak->x, source->peak->y);
+        status = pmSourceMoments (source, BIG_RADIUS);
+        if (status) {
+            Nmoments ++;
+            continue;
+        }
+
+        Nfail ++;
+        psErrorClear();
+        continue;
+    }
+
+    // change the value of a scalar on the array (wrap this and put it in psArray.h)
+    *nmoments = Nmoments;
+    *nfail = Nfail;
+    
+    return true;
+}
