Index: branches/meh_branches/ppstack_test/psModules/src/astrom/pmAstrometryWCS.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/astrom/pmAstrometryWCS.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/astrom/pmAstrometryWCS.c	(revision 33415)
@@ -487,4 +487,6 @@
 
     if (fpa->toSky == NULL) {
+	psFree(fpa->toTPA);
+	psFree(fpa->fromTPA);
         fpa->toTPA = psPlaneTransformIdentity (1);
         fpa->fromTPA = psPlaneTransformIdentity (1);
Index: branches/meh_branches/ppstack_test/psModules/src/camera/pmReadoutFake.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/camera/pmReadoutFake.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/camera/pmReadoutFake.c	(revision 33415)
@@ -51,5 +51,5 @@
 
     psF32 *params = model->params->data.F32; // Model parameters
-    psEllipseAxes axes = pmPSF_ModelToAxes(params, MAX_AXIS_RATIO); // Ellipse axes
+    psEllipseAxes axes = pmPSF_ModelToAxes(params, MAX_AXIS_RATIO, model->type); // Ellipse axes
     // Curiously, the minor axis can be larger than the major axis, so need to check.
     if (axes.major >= axes.minor) {
@@ -58,5 +58,5 @@
         axes.major = axes.minor;
     }
-    return pmPSF_AxesToModel(params, axes);
+    return pmPSF_AxesToModel(params, axes, model->type);
 }
 
@@ -314,5 +314,5 @@
                 }
             }
-            if (!psThreadPoolWait(true)) {
+            if (!psThreadPoolWait(true, true)) {
                 psError(PS_ERR_UNKNOWN, false, "Error waiting for threads.");
                 psFree(groups);
Index: branches/meh_branches/ppstack_test/psModules/src/concepts/pmConceptsStandard.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/concepts/pmConceptsStandard.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/concepts/pmConceptsStandard.c	(revision 33415)
@@ -751,13 +751,17 @@
   bool has_video_cell = false;
 
-  if (concept->type != PS_DATA_STRING) {
-    psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Type for %s (%x) is not string\n",
-	    concept->name, concept->type);
-    return NULL;
-  }
-
-  char *Vptr = strchr(concept->data.V,'V');
-  if (Vptr) {
-    has_video_cell = true;
+  if (concept->type == PS_DATA_BOOL) {
+    has_video_cell = concept->data.B;
+  } else { 
+    if (concept->type != PS_DATA_STRING) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Type for %s (%x) is not string\n",
+                concept->name, concept->type);
+        return NULL;
+      }
+
+      char *Vptr = strchr(concept->data.V,'V');
+      if (Vptr) {
+        has_video_cell = true;
+      }
   }
 
Index: branches/meh_branches/ppstack_test/psModules/src/detrend/pmBias.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/detrend/pmBias.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/detrend/pmBias.c	(revision 33415)
@@ -154,5 +154,5 @@
     if (threaded) {
         // wait here for the threaded jobs to finish
-        if (!psThreadPoolWait(true)) {
+        if (!psThreadPoolWait(true, true)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to apply bias correction.");
             return false;
Index: branches/meh_branches/ppstack_test/psModules/src/detrend/pmDark.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/detrend/pmDark.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/detrend/pmDark.c	(revision 33415)
@@ -601,5 +601,5 @@
     if (threaded) {
         // wait here for the threaded jobs to finish
-        if (!psThreadPoolWait(true)) {
+        if (!psThreadPoolWait(true, true)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to apply dark.");
             psFree(orders);
Index: branches/meh_branches/ppstack_test/psModules/src/detrend/pmFlatField.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/detrend/pmFlatField.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/detrend/pmFlatField.c	(revision 33415)
@@ -161,5 +161,5 @@
     if (threaded) {
         // wait here for the threaded jobs to finish
-        if (!psThreadPoolWait(true)) {
+        if (!psThreadPoolWait(true, true)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to flat-field image.");
             return false;
Index: branches/meh_branches/ppstack_test/psModules/src/detrend/pmPattern.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/detrend/pmPattern.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/detrend/pmPattern.c	(revision 33415)
@@ -6,4 +6,6 @@
 
 #include "pmPattern.h"
+
+#define PATTERN_ROW_BKG_FIX 1
 
 
@@ -89,4 +91,22 @@
     psImageInit(corr, NAN);
 
+#ifdef PATTERN_ROW_BKG_FIX
+    // CZW: 2011-11-30
+    // Define the vectors to hold the "x" and "y" slope trends.
+    // Briefly, the slope trend in the y-axis is a due to variations in the 0-th order term
+    // of the PATTERN.ROW fit between individual rows across the cell.  Similarly, the 1-st
+    // order term of the PATTERN.ROW fit defines the trend in the x-axis (as that's what we
+    // are fitting with PATTERN.ROW in the first place).  However, the thing we're trying to
+    // fix with PATTERN.ROW is the detector level bias wiggles.  These should be overlaid on
+    // the true sky level.  Therefore, simply applying the PATTERN.ROW correction will
+    // introduce cell-to-cell sky variations as these two trends are removed.  To avoid this,
+    // We store the 0th and 1st order values used for each row, and then fit a polynomial to
+    // these results.  By re-adding these systematic trends back, we can remove the row-to-row
+    // variations without improperly removing the real sky trend.
+    psVector *yaxisData = psVectorAlloc(numRows, PS_TYPE_F32); // Data to fit to the constant term
+    psVector *yaxisMask = psVectorAlloc(numRows, PS_TYPE_VECTOR_MASK); // Mask for rows with no fit
+    psVector *xaxisData = psVectorAlloc(numRows, PS_TYPE_F32); // Data to fit to the linear term
+    psVectorInit(yaxisMask, 0);
+#endif
     for (int y = 0; y < numRows; y++) {
         psVectorInit(clipMask, 0);
@@ -105,4 +125,8 @@
             // Not enough points to fit
             patternMaskRow(ro, y, maskBad);
+#ifdef PATTERN_ROW_BKG_FIX
+	    // Ignore this row in our subsequent fits, because the fit failed.
+	    yaxisMask->data.PS_TYPE_VECTOR_MASK_DATA[y] = 0xFF;
+#endif
             continue;
         }
@@ -111,8 +135,22 @@
             psErrorClear();
             patternMaskRow(ro, y, maskBad);
-            continue;
-        }
-
-        poly->coeff[0] -= background;
+#ifdef PATTERN_ROW_BKG_FIX
+	    // Ignore this row in our subsequent fits, because the fit failed.
+	    yaxisMask->data.PS_TYPE_VECTOR_MASK_DATA[y] = 0xFF;
+#endif
+            continue;
+        }
+#ifndef PATTERN_ROW_BKG_FIX
+ 	poly->coeff[0] -= background;
+#else
+	// Store the results we found for this row.
+	yaxisData->data.F32[y] = poly->coeff[0];
+	xaxisData->data.F32[y] = poly->coeff[1];
+	psTrace("pattern",1,"%d %g %g\n",y,poly->coeff[0],poly->coeff[1]);
+	
+	//	yaxisData->data.F32[y] = 0.0;
+/* 	xaxisData->data.F32[y] = 0.0; */
+	
+#endif
         memcpy(corr->data.F64[y], poly->coeff, (order + 1) * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
         psVector *solution = psPolynomial1DEvalVector(poly, indices); // Solution vector
@@ -121,4 +159,7 @@
             psErrorClear();
             patternMaskRow(ro, y, maskBad);
+#ifdef PATTERN_ROW_BKG_FIX
+	    yaxisMask->data.PS_TYPE_VECTOR_MASK_DATA[y] = 0xFF;
+#endif
             continue;
         }
@@ -126,8 +167,94 @@
         for (int x = 0; x < numCols; x++) {
             image->data.F32[y][x] -= solution->data.F32[x];
+	    psTrace("pattern",5,"A: %d %d %g\n",x,y,solution->data.F32[x]);
         }
         psFree(solution);
     }
 
+#ifdef PATTERN_ROW_BKG_FIX
+    // Put the global trends back that were removed by the PATTERN.ROW correction.
+    // Set up the indices for the polynomial
+    psVector *yaxisIndices = psVectorAlloc(numRows, PS_TYPE_F32);
+    norm = 2.0 / (float)numRows;
+    for (int y = 0; y < numRows; y++) {
+      yaxisIndices->data.F32[y] = y * norm - 1.0;
+      psTrace("psModules.detrend.pattern",10,"%d %f %f\n",y,yaxisIndices->data.F32[y],yaxisData->data.F32[y]);
+    }
+
+    // Fit the trend of the constant term, producing the y-axis global trend
+    psStatsInit(clip);
+    psPolynomial1D *yaxisPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1); // Polynomial to fit.
+    if (!psVectorClipFitPolynomial1D(yaxisPoly,clip,yaxisMask,0xFF,yaxisData, NULL, yaxisIndices)) {
+      psWarning("Unable to fit polynomial to y-axis trend");
+      psErrorClear();
+      // If we've failed, we need to do something, so add back in the background level, and
+      // expect that the final image will have background mismatches.
+      for (int y = 0; y < numRows; y++) {
+	for (int x = 0; x < numCols; x++) {
+	  image->data.F32[y][x] += background;
+	  corr->data.F64[y][0]  -= background;
+	}
+      }
+    }
+    else {
+      psVector *solution = psPolynomial1DEvalVector(yaxisPoly,yaxisIndices);
+      if (!solution) {
+	psWarning("Unable to evaluate polynomial");
+	psErrorClear();
+	// If we've failed, we need to do something, so add back in the background level, and
+	// expect that the final image will have background mismatches.
+	for (int y = 0; y < numRows; y++) {
+	  for (int x = 0; x < numCols; x++) {
+	    image->data.F32[y][x] += background;
+	    corr->data.F64[y][0]  -= background;
+	  }
+	}
+      }
+      else {
+	for (int y = 0; y < numRows; y++) {
+	  for (int x = 0; x < numCols; x++) {
+	    image->data.F32[y][x] += solution->data.F32[y];
+	    corr->data.F64[y][0]  -= solution->data.F32[y];
+	    psTrace("pattern",5,"B: %d %d %g\n",x,y,solution->data.F32[x]);
+	  }
+	}
+      }
+      psFree(solution);
+    }      
+
+    // Fit the trend of the linear term, producing the x-axis global trend
+    // We can use the same mask vector, as the same rows failed the row-fit earlier.
+    psStatsInit(clip);
+    psPolynomial1D *xaxisPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1); // Polynomial to fit.
+    if (!psVectorClipFitPolynomial1D(xaxisPoly,clip,yaxisMask,0xFF,xaxisData, NULL, yaxisIndices)) {
+      psWarning("Unable to fit polynomial to x-axis trend");
+      psErrorClear();
+    }
+    else {
+      psVector *solution = psPolynomial1DEvalVector(xaxisPoly,yaxisIndices);
+      if (!solution) {
+	psWarning("Unable to evaluate polynomial");
+	psErrorClear();
+      }
+      else {
+	for (int y = 0; y < numRows; y++) {
+	  for (int x = 0; x < numCols; x++) {
+	    image->data.F32[y][x] += solution->data.F32[y] * indices->data.F32[x];
+	    corr->data.F64[y][1]  -= solution->data.F32[y] ;
+	    psTrace("pattern",5,"C: %d %d %g %g\n",x,y,solution->data.F32[x],indices->data.F32[x]);
+	  }
+	}
+      }
+      psFree(solution);
+    }
+    psFree(yaxisPoly);
+    psFree(xaxisPoly);
+    psFree(yaxisIndices);
+    psFree(yaxisMask);
+    psFree(yaxisData);
+    psFree(xaxisData);
+    // End PATTERN_ROW_BKG_FIX global trend replacement
+#endif 
+    
     psMetadataAddImage(ro->analysis, PS_LIST_TAIL, PM_PATTERN_ROW_CORRECTION, PS_META_REPLACE,
                        "Pattern row correction", corr);
@@ -382,2 +509,432 @@
 
 
+
+bool pmPatternContinuity(pmChip *chip, const psVector *tweak, psStatsOptions bgStat, psStatsOptions cellStat,
+			 psImageMaskType maskVal, psImageMaskType maskBad, int edgeWidth)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_VECTOR_NON_NULL(tweak, false);
+    PS_ASSERT_VECTOR_SIZE(tweak, chip->cells->n, false);
+    PS_ASSERT_VECTOR_TYPE(tweak, PS_TYPE_U8, false);
+
+    int numCells = tweak->n;            // Number of cells
+
+    psVector *meanMask = psVectorAlloc(numCells, PS_TYPE_VECTOR_MASK); // Mask for means
+    psVectorInit(meanMask, 0);
+
+    // Mask bits
+    enum {
+        PM_PATTERN_IGNORE = 0x01,       // Ignore this cell
+        PM_PATTERN_TWEAK  = 0x02,       // Tweak this cell
+        PM_PATTERN_ERROR  = 0x04,       // Error in calculating background
+        PM_PATTERN_ALL    = 0xFF,       // All causes
+    };
+
+    // Count number of cells to tweak
+    int numTweak = 0;                   // Number of cells to tweak
+    int numIgnore = 0;                  // Number of cells to ignore
+    for (int i = 0; i < numCells; i++) {
+        pmCell *cell = chip->cells->data[i]; // Cell of interest
+        if (!cell || !cell->data_exists || !cell->process ||
+            cell->readouts->n == 0 || cell->readouts->n > 1 || !cell->readouts->data[0]) {
+            numIgnore++;
+            meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PM_PATTERN_IGNORE;
+            continue;
+        }
+        if (tweak->data.U8[i]) {
+            meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PM_PATTERN_TWEAK;
+            numTweak++;
+        }
+    }
+    if (numTweak == 0) {
+        // Nothing to do
+        psFree(meanMask);
+        return true;
+    }
+
+    // Measure mean of each cell edge, and use that to determine the cell offsets.
+
+    psStats *bgStats = psStatsAlloc(bgStat); // Statistics on background
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
+
+    psRegion region = {0,0,0,0};
+
+    /* These images hold the edge data for the OTA structure.  */
+    psImage *A = psImageAlloc(8,8,PS_TYPE_F64); // Top edge
+    psImage *B = psImageAlloc(8,8,PS_TYPE_F64); // Bottom edge
+    psImage *C = psImageAlloc(8,8,PS_TYPE_F64); // Right edge
+    psImage *D = psImageAlloc(8,8,PS_TYPE_F64); // Left edge
+    psImageInit(A,0.0);
+    psImageInit(B,0.0);
+    psImageInit(C,0.0);
+    psImageInit(D,0.0);
+    
+    for (int i = 0; i < numCells; i++) {
+        if (meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PM_PATTERN_IGNORE) {
+            continue;
+        }
+        pmCell *cell = chip->cells->data[i]; // Cell of interest
+        pmReadout *ro = cell->readouts->data[0]; // Readout of interest
+
+        psStatsInit(bgStats);
+
+	// Convert cell iterator i into an xy coordinate on the grid of cells
+	int y = (i % 8);
+	int x = (i - y) / 8;
+	
+	for (int j = 0; j < 4; j++) {
+	  if (j == 0) {  // Region B
+	    region = psRegionSet(0,ro->image->numCols,
+				 0,edgeWidth);
+	  }
+	  else if (j == 1) { // Region A
+	    region = psRegionSet(0,ro->image->numCols,
+				 ro->image->numRows - edgeWidth,ro->image->numRows);
+	  }
+	  else if (j == 2) { // Region D
+	    region = psRegionSet(0,edgeWidth,
+				 0,ro->image->numRows);
+	  }
+	  else if (j == 3) { // Region C
+	    region = psRegionSet(ro->image->numCols - edgeWidth,ro->image->numCols,
+				 0,ro->image->numRows);
+	  }
+	  psImage *subset  = psImageSubset(ro->image,region);
+	  psImage *submask = psImageSubset(ro->mask,region);
+
+	  if (!psImageBackground(bgStats, NULL, subset, submask, maskVal, rng)) {
+            psWarning("Unable to measure background for cell %d on edge %d\n", i, j);
+            psErrorClear();
+            meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] |= PM_PATTERN_ERROR;
+	    if (j == 0)      { B->data.F64[y][x] = NAN; }
+	    else if (j == 1) { A->data.F64[y][x] = NAN; }
+	    else if (j == 2) { C->data.F64[y][x] = NAN; }
+	    else if (j == 3) { D->data.F64[y][x] = NAN; }
+	    psFree(subset);
+	    psFree(submask);
+            continue; // Move on to next edge, as only part of this cell may be a problem
+	  }
+ 
+	  // If the returned value is zero, assume something is wrong.  Do I still need this?
+	  if (psStatsGetValue(bgStats,bgStat) < 1e-6) {
+	    if (j == 0)      { B->data.F64[y][x] = NAN; }
+	    else if (j == 1) { A->data.F64[y][x] = NAN; }
+	    else if (j == 2) { C->data.F64[y][x] = NAN; }
+	    else if (j == 3) { D->data.F64[y][x] = NAN; }
+	  }
+	  // If we have an error for this cell/edge, make sure we mask the value
+	  if (meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PM_PATTERN_ERROR) {
+	    if (j == 0)      { B->data.F64[y][x] = NAN; }
+	    else if (j == 1) { A->data.F64[y][x] = NAN; }
+	    else if (j == 2) { C->data.F64[y][x] = NAN; }
+	    else if (j == 3) { D->data.F64[y][x] = NAN; }
+	  }
+	  else { // Set the value to match what we got from the edge box.
+	    if (j == 0)      { B->data.F64[y][x] = psStatsGetValue(bgStats,bgStat); }
+	    else if (j == 1) { A->data.F64[y][x] = psStatsGetValue(bgStats,bgStat); }
+	    else if (j == 2) { C->data.F64[y][x] = psStatsGetValue(bgStats,bgStat); }
+	    else if (j == 3) { D->data.F64[y][x] = psStatsGetValue(bgStats,bgStat); }
+	  }
+
+	  for (int u = 0; u < subset->numCols; u++) {
+	    for (int v = 0; v < subset->numRows; v++) {
+	      psTrace("psModules.detrend.cont",10,"BOX: %d %d (%d %d) (%d %d) %f %d",
+		      i,j,x,y,u,v,subset->data.F32[v][u],submask->data.PS_TYPE_IMAGE_MASK_DATA[v][u]);
+	    }
+	  }	  
+	  
+	  psFree(subset);
+	  psFree(submask);
+
+	}
+	psTrace("psModules.detrend.cont",5, "OTA: %d (%d %d) A: %f B: %f C: %f D: %f",
+		i,x,y,
+		A->data.F64[y][x],B->data.F64[y][x],C->data.F64[y][x],D->data.F64[y][x]);		
+    }
+    psFree(bgStats);
+    psFree(rng);
+
+    // We've now allocated all the edge values, so we can now minimize the offsets.
+    // This involves solving the equation A x = b, where
+    // A is the (64x64 for GPC1) matrix containing the edges that match for each cell
+    // x is the solution vector
+    // b is the combination of offsets across each cell boundary for each cell.
+    // Below "XX" is used as the matrix A, and "solution" is used as both b and x
+    //   (due to the way psMatrixLUSolve operates).
+    psVector *solution = psVectorAlloc(64,PS_TYPE_F64);
+    psImage  *XX       = psImageAlloc(64,64,PS_TYPE_F64);
+    psVectorInit(solution,0.0);
+    psImageInit(XX,0.0);
+    
+    for (int i = 0; i < numCells; i++) {
+      // Accumulate all the possible edge differences we can for this cell.
+      // As we do so, make a note of the correlations by incrementing the element of the matrix.
+      int y = (i % 8);
+      int x = (i - y) / 8;
+      int j;
+      double critical_value = 0.0;
+      if (meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PM_PATTERN_IGNORE) {
+	continue;
+      }
+      if (x + 1 < 8) {  // We have a neighbor adjacent in the +x direction
+	j = 8 * (x + 1) + y; // Determine that neighbor's index
+	if (fabs(C->data.F64[y][x]) > fabs(D->data.F64[y][x+1])) {
+	  critical_value = 2.0 * fabs(D->data.F64[y][x+1]);
+	}
+	else {
+	  critical_value = 2.0 * fabs(C->data.F64[y][x]);
+	}
+	if (critical_value < 25) { critical_value = 25; }
+	psTrace("psModules.detrend.cont",5,"CmD %d %d %d %d %g %g %g", // diagnostic
+		i,x,y,j,
+		C->data.F64[y][x],
+		D->data.F64[y][x+1],
+		critical_value
+		);
+	if (!(meanMask->data.PS_TYPE_VECTOR_MASK_DATA[j] & PM_PATTERN_IGNORE)&&  // If there are no errors with the neighbor,
+	    (isfinite(C->data.F64[y][x]))&&(isfinite(D->data.F64[y][x+1]))&&     // and all edges have valid values,
+	    (fabs(C->data.F64[y][x] - D->data.F64[y][x+1]) < critical_value)     // and there are no large discontinuities,
+	    ) {    
+	  solution->data.F64[i] += C->data.F64[y][x] - D->data.F64[y][x+1];     // Take the difference
+	  XX->data.F64[i][i] += 1;                                              // increment our relation with ourself
+	  XX->data.F64[i][j] += -1;                                             // decrement our relation with the neighbor
+	}
+      }
+      if (x - 1 > -1) { // etc.
+	j = 8 * (x - 1) + y;
+	if (fabs(C->data.F64[y][x-1]) > fabs(D->data.F64[y][x])) {
+	  critical_value = 2.0 * fabs(D->data.F64[y][x]);
+	}
+	else {
+	  critical_value = 2.0 * fabs(C->data.F64[y][x-1]);
+	}
+	if (critical_value < 25) { critical_value = 25; }
+	psTrace("psModules.detrend.cont",5,"DmC %d %d %d %d %g %g %g",
+		i,x,y,j,
+		D->data.F64[y][x],
+		C->data.F64[y][x-1],
+		critical_value
+		);
+
+	if (!(meanMask->data.PS_TYPE_VECTOR_MASK_DATA[j] & PM_PATTERN_IGNORE)&&
+	    (isfinite(D->data.F64[y][x]))&&(isfinite(C->data.F64[y][x-1]))&&
+	    (fabs(D->data.F64[y][x] - C->data.F64[y][x-1]) < critical_value)
+	    ) {
+	  solution->data.F64[i] += D->data.F64[y][x] - C->data.F64[y][x-1];
+	  XX->data.F64[i][i] += 1;
+	  XX->data.F64[i][j] += -1;
+	}
+      }
+      if (y + 1 < 8) {
+	j = 8 * x + (y + 1);
+	psTrace("psModules.detrend.cont",5,"AmB %d %d %d %d %g %g",
+		i,x,y,j,
+		A->data.F64[y][x],
+		B->data.F64[y+1][x]
+		);
+	if (fabs(A->data.F64[y][x]) > fabs(B->data.F64[y+1][x])) {
+	  critical_value = 2.0 * fabs(B->data.F64[y+1][x]);
+	}
+	else {
+	  critical_value = 2.0 * fabs(A->data.F64[y][x]);
+	}
+	if (critical_value < 25) { critical_value = 25; }
+	if (!(meanMask->data.PS_TYPE_VECTOR_MASK_DATA[j] & PM_PATTERN_IGNORE)&&
+	    (isfinite(A->data.F64[y][x]))&&(isfinite(B->data.F64[y+1][x]))&&
+	    (fabs(A->data.F64[y][x] - B->data.F64[y+1][x]) < critical_value)
+	    ) {
+	  solution->data.F64[i] += A->data.F64[y][x] - B->data.F64[y+1][x];
+	  XX->data.F64[i][i] += 1;
+	  XX->data.F64[i][j] += -1;
+	}
+      }
+      if (y - 1 > -1) {
+	j = 8 * x +  (y - 1);
+	psTrace("psModules.detrend.cont",5,"BmA %d %d %d %d %g %g",
+		i,x,y,j,
+		B->data.F64[y][x],
+		A->data.F64[y-1][x]
+		);
+	if (fabs(A->data.F64[y-1][x]) > fabs(B->data.F64[y][x])) {
+	  critical_value = 2.0 * fabs(B->data.F64[y][x]);
+	}
+	else {
+	  critical_value = 2.0 * fabs(A->data.F64[y-1][x]);
+	}
+	if (critical_value < 25) { critical_value = 25; }
+	if (!(meanMask->data.PS_TYPE_VECTOR_MASK_DATA[j] & PM_PATTERN_IGNORE)&&
+	    (isfinite(B->data.F64[y][x]))&&(isfinite(A->data.F64[y-1][x]))&&
+	    (fabs(B->data.F64[y][x] - A->data.F64[y-1][x]) < critical_value)
+	    ) {
+	  solution->data.F64[i] += B->data.F64[y][x] - A->data.F64[y-1][x];
+	  XX->data.F64[i][i] += 1;
+	  XX->data.F64[i][j] += -1;
+	}
+      }
+    }
+    double max_XX = 0;
+    double solution_V = 0;
+    int i_peak = -1;
+    for (int i = 0; i < numCells; i++) { // If any cells have no value of themself, set the matrix to 1.0.
+      if (XX->data.F64[i][i] == 0.0) {
+	XX->data.F64[i][i] = 1.0;
+      }
+      if (XX->data.F64[i][i] > max_XX) {
+	max_XX = XX->data.F64[i][i];
+	solution_V = solution->data.F64[i];
+	i_peak = i;
+      }
+    }
+    psTrace("psModules.detrend.cont",5,"fixed point: %d %g\n",
+	    i_peak,solution_V);
+
+    for (int i = 0; i < numCells; i++) {
+/*        if (!((XX->data.F64[i][i] == 1.0)&& */
+/*  	    (solution->data.F64[i] == 0.0))) { */
+	solution->data.F64[i] -= solution_V;
+	if (i != i_peak) {
+	  for (int j = 0; j < numCells; j++) {
+	    XX->data.F64[i][j] -= XX->data.F64[i_peak][j];
+	  }
+	}
+/*        } */
+    }
+    for (int i = 0; i < numCells; i++) {
+      XX->data.F64[i_peak][i] = 0.0;
+    }
+    XX->data.F64[i_peak][i_peak] = 1.0;
+    
+    
+#if (1)
+    for (int i = 0; i < numCells; i++) { // print matrix A
+      psTrace("psModules.detrend.cont",5,"A: %3d % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f",
+	      i,
+	      XX->data.F64[i][0],	      XX->data.F64[i][1],	      XX->data.F64[i][2],	      XX->data.F64[i][3],
+	      XX->data.F64[i][4],	      XX->data.F64[i][5],	      XX->data.F64[i][6],	      XX->data.F64[i][7],
+	      XX->data.F64[i][8],	      XX->data.F64[i][9],	      XX->data.F64[i][10],	      XX->data.F64[i][11],
+	      XX->data.F64[i][12],	      XX->data.F64[i][13],	      XX->data.F64[i][14],	      XX->data.F64[i][15],
+	      XX->data.F64[i][16],	      XX->data.F64[i][17],	      XX->data.F64[i][18],	      XX->data.F64[i][19],
+	      XX->data.F64[i][20],	      XX->data.F64[i][21],	      XX->data.F64[i][22],	      XX->data.F64[i][23],
+	      XX->data.F64[i][24],	      XX->data.F64[i][25],	      XX->data.F64[i][26],	      XX->data.F64[i][27],
+	      XX->data.F64[i][28],	      XX->data.F64[i][29],	      XX->data.F64[i][30],	      XX->data.F64[i][31],
+	      XX->data.F64[i][32],	      XX->data.F64[i][33],	      XX->data.F64[i][34],	      XX->data.F64[i][35],
+	      XX->data.F64[i][36],	      XX->data.F64[i][37],	      XX->data.F64[i][38],	      XX->data.F64[i][39],
+	      XX->data.F64[i][40],	      XX->data.F64[i][41],	      XX->data.F64[i][42],	      XX->data.F64[i][43],
+	      XX->data.F64[i][44],	      XX->data.F64[i][45],	      XX->data.F64[i][46],	      XX->data.F64[i][47],
+	      XX->data.F64[i][48],	      XX->data.F64[i][49],	      XX->data.F64[i][50],	      XX->data.F64[i][51],
+	      XX->data.F64[i][52],	      XX->data.F64[i][53],	      XX->data.F64[i][54],	      XX->data.F64[i][55],
+	      XX->data.F64[i][56],	      XX->data.F64[i][57],	      XX->data.F64[i][58],	      XX->data.F64[i][59],
+	      XX->data.F64[i][60],	      XX->data.F64[i][61],	      XX->data.F64[i][62],	      XX->data.F64[i][63]
+	      );
+    }
+
+    for (int i = 0; i < numCells; i++) { // print vector b
+      psTrace("psModules.detrend.cont",5,"b: %d %f",
+	      i,
+	      solution->data.F64[i]
+	      );
+    }
+#endif    
+    
+    // Solve the Ax=b equation
+    //    psMatrixLUSolve(XX,solution);
+    psMatrixGJSolve(XX,solution);
+#if (1)
+    for (int i = 0; i < numCells; i++) { // print vector b
+      psTrace("psModules.detrend.cont",5,"x: %d %f",
+	      i,
+	      solution->data.F64[i]
+	      );
+    }
+#endif
+    
+    /* old code to remove the minimum solution value from the set, to give a "minimal set of offsets." Mathematically unnecessary. */
+/*     double min = 99e99; */
+/*     for (int i = 0; i < numCells; i++) { */
+/*       if (solution->data.F64[i] < min) { */
+/* 	min = solution->data.F64[i]; */
+/*       } */
+/*       psTrace("psModules.detrend.cont",5,"x: %d %f %f ", */
+/* 	      i, */
+/* 	      solution->data.F64[i],min */
+/* 	      ); */
+/*     } */
+/*     for (int i = 0; i < numCells; i++) { */
+/* 	if (solution->data.F64[i] != 0.0) { */
+/* 	  solution->data.F64[i] -= min; */
+/* 	} */
+/*     } */
+
+    // Cleanup
+    psFree(XX);
+    psFree(A);
+    psFree(B);
+    psFree(C);
+    psFree(D);
+
+    // Correct cells based on the offsets calculated, and store the result in the analysis metadata.
+    for (int i = 0; i < numCells; i++) {
+        if (meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PM_PATTERN_IGNORE) {
+            continue;
+        }
+        if (!(meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PM_PATTERN_TWEAK)) {
+            continue;
+        }
+        pmCell *cell = chip->cells->data[i]; // Cell of interest
+        pmReadout *ro = cell->readouts->data[0]; // Readout of interest
+
+        float correction = solution->data.F64[i];
+        const char *cellName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME"); // Name of cell
+        psLogMsg("psModules.detrend", PS_LOG_DETAIL, "Correcting background of cell %s by %f",
+                 cellName, correction);
+        psBinaryOp(ro->image, ro->image, "-", psScalarAlloc(correction, PS_TYPE_F32));
+        psMetadataAddF32(ro->analysis, PS_LIST_TAIL, PM_PATTERN_CELL_CORRECTION, PS_META_REPLACE,
+                         "Pattern cell correction solution", correction);
+    }
+
+    psFree(solution);
+    psFree(meanMask);
+
+    return true;
+}
+
+bool pmPatternContinuityApply(pmReadout *ro, psImageMaskType maskBad)
+{
+    PM_ASSERT_READOUT_NON_NULL(ro, false);
+    PM_ASSERT_READOUT_IMAGE(ro, false);
+
+    bool mdok;                          // Status of MD lookup
+    float corr = psMetadataLookupF32(&mdok, ro->analysis, PM_PATTERN_CELL_CORRECTION); // Correction to apply
+    if (!mdok) {
+        // No correction to apply
+        return true;
+    }
+
+    psImage *image = ro->image, *mask = ro->mask; // Image and mask of interest
+    int numCols = image->numCols, numRows = image->numRows; // Size of image
+
+    if (!isfinite(corr)) {
+        for (int y = 0; y < numRows; y++) {
+            for (int x = 0; x < numCols; x++) {
+                image->data.F32[y][x] = NAN;
+            }
+        }
+        if (mask) {
+            for (int y = 0; y < numRows; y++) {
+                for (int x = 0; x < numCols; x++) {
+                    mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] |= maskBad;
+                }
+            }
+        }
+    } else {
+        for (int y = 0; y < numRows; y++) {
+            for (int x = 0; x < numCols; x++) {
+                image->data.F32[y][x] += corr;
+            }
+        }
+    }
+
+    return true;
+}
+
+
Index: branches/meh_branches/ppstack_test/psModules/src/detrend/pmPattern.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/detrend/pmPattern.h	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/detrend/pmPattern.h	(revision 33415)
@@ -54,4 +54,21 @@
                         );
 
+/// Fix the background on cells known to be troublesome
+bool pmPatternContinuity(
+    pmChip *chip,                       ///< Chip to correct
+    const psVector *tweak,              ///< U8 vector indicating whether to tweak the corresponding cell
+    psStatsOptions bgStat,              ///< Statistic to use for background measurement
+    psStatsOptions cellStat,            ///< Statistic to use for combination of cell background measurements
+    psImageMaskType maskVal,            ///< Mask value to use
+    psImageMaskType maskBad,            ///< Mask value to give bad pixels
+    int edgeWidth                       ///< Size of box to use
+    );
+
+/// Apply previously measured cell pattern correction
+bool pmPatternContinuityApply(pmReadout *ro,          ///< Readout to correct
+                        psImageMaskType maskBad ///< Mask value to give bad pixels
+                        );
+
+
 
 /// @}
Index: branches/meh_branches/ppstack_test/psModules/src/detrend/pmShutterCorrection.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/detrend/pmShutterCorrection.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/detrend/pmShutterCorrection.c	(revision 33415)
@@ -805,5 +805,5 @@
         if (threaded) {
             // wait here for the threaded jobs to finish
-            if (!psThreadPoolWait(true)) {
+            if (!psThreadPoolWait(true, true)) {
                 psError(PS_ERR_UNKNOWN, false, "Unable to apply shutter correction.");
                 psFree(shutterImage);
Index: branches/meh_branches/ppstack_test/psModules/src/imcombine/pmPSFEnvelope.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/imcombine/pmPSFEnvelope.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/imcombine/pmPSFEnvelope.c	(revision 33415)
@@ -36,5 +36,5 @@
 
 
-//#define TESTING                         // Enable test output
+#define TESTING                         // Enable test output
 // #define PEAK_NORM                       // Normalise peaks?
 #define PEAK_FLUX 1.0e4                 // Peak flux for each source
@@ -380,5 +380,5 @@
 
         // measure the source moments: tophat windowing, no pixel S/N cutoff
-        if (!pmSourceMoments(source, maxRadius, 0.0, 0.0, 0.0, maskVal)) {
+        if (!pmSourceMoments(source, maxRadius, 0.25*maxRadius, 0.0, 0.0, maskVal)) {
             // Can't do anything about it; limp along as best we can
             psErrorClear();
Index: branches/meh_branches/ppstack_test/psModules/src/imcombine/pmStackReject.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/imcombine/pmStackReject.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/imcombine/pmStackReject.c	(revision 33415)
@@ -313,5 +313,5 @@
     }
 
-    if (!psThreadPoolWait(false)) {
+    if (!psThreadPoolWait(false, true)) {
         psError(psErrorCodeLast(), false, "Unable to grow bad pixels.");
         psFree(source);
Index: branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtraction.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtraction.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtraction.c	(revision 33415)
@@ -905,5 +905,5 @@
         }
     }
-    if (!psThreadPoolWait(true)) {
+    if (!psThreadPoolWait(true, true)) {
         psError(psErrorCodeLast(), false, "Error waiting for threads.");
         return false;
@@ -1019,5 +1019,13 @@
 		}
 	    }
-	    pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "red");
+
+	    psString ds9text = NULL;
+
+	    psStringAppend(&ds9text, "flux: %.0f ", match->fluxes->data.F32[i]);
+	    psStringAppend(&ds9text, "chi2: %.0f ", match->chisq->data.F32[i]);
+
+	    pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "red", ds9text);
+
+	    psFree(ds9text);	   
 
 	    // Set stamp for replacement
@@ -1041,5 +1049,15 @@
 	} else {
 	    numGood++;
-	    pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green");
+
+	    //MEH bad way to do this - should also add to rej/red one as well
+	    // -- is match from subQuality what want?
+	    psString ds9text = NULL;
+
+	    psStringAppend(&ds9text, "flux: %.0f ", match->fluxes->data.F32[i]);
+	    psStringAppend(&ds9text, "chi2: %.0f ", match->chisq->data.F32[i]);
+
+	    pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green", ds9text);
+
+	    psFree(ds9text);	   
         }
     }
@@ -1427,5 +1445,5 @@
     }
 
-    if (!psThreadPoolWait(false)) {
+    if (!psThreadPoolWait(false, true)) {
         psError(psErrorCodeLast(), false, "Error waiting for threads.");
         return false;
@@ -1487,4 +1505,5 @@
             }
         }
+	psFree(out1->covariance);
         out1->covariance = psImageCovarianceAverage(covars);
         psFree(covars);
@@ -1527,4 +1546,5 @@
             }
         }
+	psFree(out2->covariance);
         out2->covariance = psImageCovarianceAverage(covars);
         psFree(covars);
Index: branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionEquation.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionEquation.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionEquation.c	(revision 33415)
@@ -796,6 +796,6 @@
     stamp->normSquare1 = normSquare1;
     stamp->normSquare2 = normSquare2;
-
-    // psLogMsg ("psModules.imcombine", PS_LOG_DETAIL, "normValue: %f %f %f  (%f %f)\n", normI1, normI2, stamp->norm, normSquare1, normSquare2);
+    
+    //psLogMsg ("psModules.imcombine", PS_LOG_DETAIL, "normValue: %f %f %f  (%f %f)\n", normI1, normI2, stamp->norm, normSquare1, normSquare2);
 
     return true;
@@ -958,5 +958,5 @@
     }
 
-    if (!psThreadPoolWait(true)) {
+    if (!psThreadPoolWait(true, true)) {
         psError(psErrorCodeLast(), false, "Error waiting for threads.");
         return false;
@@ -1037,8 +1037,8 @@
                 (void)psBinaryOp(sumVector, sumVector, "+", stamp->vector);
 
-                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green");
+                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green", "");
                 numStamps++;
             } else if (stamp->status == PM_SUBTRACTION_STAMP_REJECTED) {
-                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "red");
+                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "red", "");
             }
         }
@@ -1139,8 +1139,8 @@
 		normSquare2 += stamp->normSquare2;
 
-                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green");
+                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green", "");
                 numStamps++;
             } else if (stamp->status == PM_SUBTRACTION_STAMP_REJECTED) {
-                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "red");
+                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "red", "");
             }
         }
@@ -1382,5 +1382,8 @@
 	float Myy = fluxY2 / fluxC1;
 
+        //MEH uncomment and update
 	// fprintf (stderr, "conv1, flux2: %f, Mx: %f, My: %f, Mxx: %f, Myy: %f, chisq: %f, npix: %d\n", flux2, Mx, My, Mxx, Myy, chisq, npix);
+        //fprintf (stderr, "conv1, flux2: %f, fluxC1: %f, Mxx: %f, Myy: %f, chisqR: %f, npix: %d\n", flux2, fluxC1, Mxx, Myy, chisqR, npix);
+
 	moment += Mxx + Myy;
     }
@@ -1554,4 +1557,7 @@
                     residual->kernel[y][x] = difference->kernel[y][x] - convolved1->kernel[y][x];
 		    convolved1->kernel[y][x] += source->kernel[y][x] * norm;
+                    //MEH 
+                    //psLogMsg("psModules.imcombine", PS_LOG_INFO, "xy: %d %d, norm: %6.3f, bg: %6.3f, difference: %6.3f, target: %6.3f, source: %6.3f, residual: %6.3f, convolved1: %6.3f \n",x,y,norm,background,difference->kernel[y][x],target->kernel[y][x],source->kernel[y][x],residual->kernel[y][x],convolved1->kernel[y][x]);
+
                 }
             }
@@ -1652,5 +1658,8 @@
     } else {
 	sumKernel2 = 1.0;
-    }
+	//MEH
+	sumKernel2 = 0.001;
+    }
+    //MEH if sumKernel2 not use essentially, then not need to be 2.0*? but level of orderFactor may need to be adjusted then
 
     // if we modify the chisq value by the (sumKernel1 + sumKernel2), we account for the
@@ -1658,6 +1667,8 @@
     // penalized by increasing the score somewhat.  the 0.01 value is not well-chosen.
     float orderFactor = 0.01 * kernels->spatialOrder;
-    float score = 2.0 * chisqRValue / (sumKernel1 + sumKernel2) + orderFactor;
-    psLogMsg("psModules.imcombine", PS_LOG_INFO, "chisq: %6.3f, chisqD: %6.3f, moment: %6.3f, sumKernel_1: %6.3f, sumKernel_2, score: %6.3f: %6.3f\n", chisqRValue, chisqDValue, momentValue, sumKernel1, sumKernel2, score);
+    //MEH
+    //float score = 2.0 * chisqRValue / (sumKernel1 + sumKernel2) + orderFactor;
+    float score = 2.0 * chisqRValue / (sqrt(sumKernel1) + sqrt(sumKernel2)) + orderFactor;
+    psLogMsg("psModules.imcombine", PS_LOG_INFO, "chisq: %6.3f, chisqD: %6.3f, moment: %6.3f, sumKernel_1: %6.3f, sumKernel_2: %6.3f, score: %6.3f\n", chisqRValue, chisqDValue, momentValue, sumKernel1, sumKernel2, score);
 
     // save this result if it is the first or the best (skip if bestMatch is NULL)
Index: branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionEquation.v0.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionEquation.v0.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionEquation.v0.c	(revision 33415)
@@ -882,5 +882,5 @@
     }
 
-    if (!psThreadPoolWait(true)) {
+    if (!psThreadPoolWait(true, true)) {
         psError(psErrorCodeLast(), false, "Error waiting for threads.");
         return false;
Index: branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionMatch.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionMatch.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionMatch.c	(revision 33415)
@@ -29,6 +29,6 @@
 static bool useFFT = true;              // Do convolutions using FFT
 
-//#define TESTING
-//#define TESTING_MEMORY
+#define TESTING
+#define TESTING_MEMORY
 
 // Output memory usage information
@@ -538,14 +538,21 @@
     // Where does our variance map come from?
     // Getting the variance exactly right is not necessary --- it's just used for weighting.
+    // MEH - helpful to have one used output to log -- at some level want log output for most every step
+
     psImage *variance = NULL;             // Variance image to use
     if (ro1->variance && ro2->variance) {
         variance = (psImage*)psBinaryOp(NULL, ro1->variance, "+", ro2->variance);
+	psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "TESTING:SubMatch variance avail: var1+var2");
     } else if (ro1->variance) {
         variance = psMemIncrRefCounter(ro1->variance);
+	psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "TESTING:SubMatch variance avail: var1");
     } else if (ro2->variance) {
         variance = psMemIncrRefCounter(ro2->variance);
+	psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "TESTING:SubMatch variance avail: var2");
     } else {
         variance = (psImage*)psBinaryOp(NULL, ro1->image, "+", ro2->image);
-    }
+	psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "TESTING:SubMatch variance avail: none ro1+ro2");
+    }
+
 
     // Putting important variable declarations here, since they are freed after a "goto" if there is an error.
@@ -726,4 +733,7 @@
 		float radMoment1 = stamps->normWindow1 / 2.75;
 		float radMoment2 = stamps->normWindow2 / 2.75;
+		//MEH - FWHM vs radialMoment need *2? may mess up other parts
+		//float radMoment1 = stamps->normWindow1 / 2.75*2;
+		//float radMoment2 = stamps->normWindow2 / 2.75*2;
 		pmSubtractionParamsScale(NULL, NULL, isisWidths, radMoment1, radMoment2);
 
@@ -810,5 +820,5 @@
 		    }
 		}
-
+                //MEH probably should comment out but should be ok?
 		// step 0 : calculate the normalizations, pass along to the next steps via stamps->normValue
 		psTrace("psModules.imcombine", 3, "Calculating normalization...\n");
@@ -825,4 +835,12 @@
 		for (int order = 0; order <= N_TEST_ORDER; order++) {
 		    for (int j = 0; j < N_TEST_MODES; j++) {
+                        //MEH: thinking pmSubtractionCalculateNormalization needs to be called here for conv source
+                        // per mode
+                        //psTrace("psModules.imcombine", 3, "Calculating normalization...order: %d  mode: %d\n",order,j);
+			//                        if (!pmSubtractionCalculateNormalization(stamps, TestModes[j])) {
+                        //    psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
+                        //    goto MATCH_ERROR;
+                        //}
+                        //
 			if (!pmSubtractionMatchAttempt(&bestMatch, kernels, stamps, TestModes[j], order, false)) {
 			    goto MATCH_ERROR;
@@ -843,4 +861,11 @@
 	    // apply the best fit so we are ready to roll
 	    psLogMsg("psModules.imcombine", PS_LOG_INFO, "applying order: %d, mode: %d\n", bestMatch->spatialOrder, bestMatch->mode);
+            //MEH need to call normalization one more time for final/best run - 
+            // but what if dual - already checked for in pmSubtractionCalculateNormalization
+            //if (!pmSubtractionCalculateNormalization(stamps, bestMatch->mode)) {
+            //    psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
+            //    goto MATCH_ERROR;
+            //}
+            // 
 	    if (!pmSubtractionMatchAttempt(NULL, kernels, stamps, bestMatch->mode, bestMatch->spatialOrder, true)) {
 		goto MATCH_ERROR;
@@ -1091,5 +1116,5 @@
     }
 
-    if (!psThreadPoolWait(true)) {
+    if (!psThreadPoolWait(true, true)) {
 	psError(psErrorCodeLast(), false, "Error waiting for threads.");
 	psFree(models);
@@ -1331,5 +1356,6 @@
 	*stampSize = *stampSize * scale + 0.5;
     }
-
+    //MEH
+    psLogMsg("psModules.imcombine", PS_LOG_INFO, "TESTING Scaling kernel parameters fwhm1,2,refscale/min/max: %f %f %f %f %f", fwhm1,fwhm2,scaleRefOption,scaleMinOption,scaleMaxOption);
     psLogMsg("psModules.imcombine", PS_LOG_INFO, "Scaling kernel parameters by %f", scale);
     if (kernelSize) psLogMsg("psModules.imcombine", PS_LOG_INFO, " modified kernel size %d", *kernelSize);
Index: branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionStamps.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionStamps.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionStamps.c	(revision 33415)
@@ -127,4 +127,10 @@
                 continue;
             }
+	    //MEH hack to limit bright end test
+//            if ((image1 && image1->data.F32[y][x] > 2000) ||
+//                (image2 && image2->data.F32[y][x] > 2000 )) {
+//		fprintf (stderr, "%f,%f : thresh\n",image1->data.F32[y][x],image2->data.F32[y][x]);
+//                continue;
+//            }
 
             if (subMask && subMask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] &
@@ -188,5 +194,6 @@
 }
 
-void pmSubtractionStampPrint(FILE *ds9, float x, float y, float size, const char *color)
+// MEH adding text to ds9
+void pmSubtractionStampPrint(FILE *ds9, float x, float y, float size, const char *color, const char *ds9text)
 {
     if (!ds9regions || !ds9) {
@@ -196,4 +203,7 @@
     if (color && strlen(color) > 0) {
         fprintf(ds9, " # color=%s", color);
+    }
+    if (ds9text && strlen(ds9text) > 0) {
+        fprintf(ds9, " text={%s}", ds9text);
     }
     fprintf(ds9, "\n");
@@ -486,7 +496,7 @@
                                              normFrac, sysErr, skyErr);
     }
-
+    //MEH - stamps.dat will repeatedly be overwritten - out to trace may be btter -- see end for summary 
     // XXX TEST : dump all stars in the stamps here
-    if (0) {
+    if (1) {
 	FILE *f = fopen ("stamp.dat", "w");
 	for (int i = 0; i < stamps->num; i++) {
@@ -589,6 +599,9 @@
                 stamp->status = PM_SUBTRACTION_STAMP_FOUND;
                 numFound++;
-                psTrace("psModules.imcombine", 5, "Found stamp in subregion %d: %d,%d\n",
-                        i, (int)stamp->x, (int)stamp->y);
+//                psTrace("psModules.imcombine", 5, "Found stamp in subregion %d: %d,%d\n",
+//                        i, (int)stamp->x, (int)stamp->y);
+		//MEH adding int flux to say something about the source 
+                psTrace("psModules.imcombine", 5, "Found stamp in subregion %d: %d,%d  Flux: %d\n",
+                        i, (int)stamp->x, (int)stamp->y, (int)stamp->flux);
             } else {
                 stamp->status = PM_SUBTRACTION_STAMP_NONE;
@@ -666,8 +679,8 @@
             psTrace("psModules.imcombine", 9, "Rejecting input stamp (%d,%d) because outside region",
                     xPix, yPix);
-            pmSubtractionStampPrint(ds9, xPix, yPix, footprint, "red");
+            pmSubtractionStampPrint(ds9, xPix, yPix, footprint, "red", "");
             continue;
         }
-
+	//MEH - are all using -0.5? seen some seem shifted
         // fprintf (stderr, "stamp: %5.1f %5.1f == %d %d\n", xStamp, yStamp, xPix, yPix);
 
@@ -692,5 +705,5 @@
                 psTrace("psModules.imcombine", 9, "Putting input stamp (%d,%d) into subregion %d",
                         xPix, yPix, j);
-                pmSubtractionStampPrint(ds9, xPix, yPix, footprint, "green");
+                pmSubtractionStampPrint(ds9, xPix, yPix, footprint, "green", "");
             }
         }
@@ -699,5 +712,5 @@
             psTrace("psModules.imcombine", 9, "Unable to find subregion for stamp (%d,%d)",
                     xPix, yPix);
-            pmSubtractionStampPrint(ds9, xPix, yPix, footprint, "yellow");
+            pmSubtractionStampPrint(ds9, xPix, yPix, footprint, "yellow", "");
         }
     }
@@ -865,6 +878,6 @@
         }
     }
-
-#if 0
+//MEH
+#if 1
     {
 	psFits *fits = NULL;
@@ -1156,15 +1169,26 @@
                 float sysErr = 0.25 * PS_SQR(stamps->sysErr); // Systematic error
                 float skyErr = stamps->skyErr;
+		//MEH - image2 not used if not adding to additional.. also these are never freed here, but upstream/downstream?
                 psKernel *image1 = stamp->image1, *image2 = stamp->image2; // Input images
+		//psKernel *image2 = stamp->image2; // Input images
                 for (int y = -size; y <= size; y++) {
                     for (int x = -size; x <= size; x++) {
+			//MEH -- if conv to model then what does sys and weight mean for both?
+			// w1+w2 vs 1/var1+1/var2.. depends on what the weight is for?
+			// why additional ^2? 
                         float additional = image1->kernel[y][x] + image2->kernel[y][x];
-                        weight->kernel[y][x] = 1.0 / (skyErr + var->kernel[y][x] + sysErr * PS_SQR(additional));
+                        //float additional = image2->kernel[y][x];
+//                        weight->kernel[y][x] = 1.0 / (skyErr + var->kernel[y][x] + sysErr * PS_SQR(additional));
+                        weight->kernel[y][x] = 1.0 / (skyErr + 1000.0*var->kernel[y][x] + sysErr * PS_SQR(additional));
                     }
                 }
+		psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "TESTING:: Stamp %d (%.1f,%.1f) weight: %f var: %.1f sysErr: %.2f im1: %.0f im2: %.0f \n", 
+			 i, stamp->x, stamp->y, weight->kernel[0][0],var->kernel[0][0],sysErr,image2->kernel[0][0],image1->kernel[0][0]);
+
             } else {
                 for (int y = -size; y <= size; y++) {
                     for (int x = -size; x <= size; x++) {
-                        weight->kernel[y][x] = 1.0 / var->kernel[y][x];
+//                        weight->kernel[y][x] = 1.0 / var->kernel[y][x];
+                        weight->kernel[y][x] = 1.0 / (1000.0*var->kernel[y][x]);
                     }
                 }
Index: branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionStamps.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionStamps.h	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionStamps.h	(revision 33415)
@@ -163,7 +163,8 @@
                              float x, float y, ///< Position of stamp
                              float size,///< Size of circle
-                             const char *color ///< Colour
+                             const char *color, ///< Colour
+			     const char *ds9text ///< info text
     );
-
+// MEH added text to ds9 file
 
 bool pmSubtractionStampsResetStatus (pmSubtractionStampList *stamps);
Index: branches/meh_branches/ppstack_test/psModules/src/objects/Makefile.am
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/Makefile.am	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/Makefile.am	(revision 33415)
@@ -45,4 +45,5 @@
 	pmSourceIO_CMF_PS1_V2.c \
 	pmSourceIO_CMF_PS1_V3.c \
+	pmSourceIO_CMF_PS1_V4.c \
 	pmSourceIO_CMF_PS1_SV1.c \
 	pmSourceIO_CMF_PS1_DV1.c \
@@ -130,14 +131,17 @@
 
 # pmSourceID_CMF_* functions use a common framework
-BUILT_SOURCES = pmSourceIO_CMF_PS1_V1.v1.c pmSourceIO_CMF_PS1_V2.v1.c pmSourceIO_CMF_PS1_V3.v1.c
+BUILT_SOURCES = pmSourceIO_CMF_PS1_V1.c pmSourceIO_CMF_PS1_V2.c pmSourceIO_CMF_PS1_V3.c pmSourceIO_CMF_PS1_V4.c
 
-pmSourceIO_CMF_PS1_V1.v1.c : pmSourceIO_CMF.c.in mksource.pl
-	mksource.pl pmSourceIO_CMF.c.in PS1_V1 pmSourceIO_CMF_PS1_V1.v1.c
+pmSourceIO_CMF_PS1_V1.c : pmSourceIO_CMF.c.in mksource.pl
+	mksource.pl pmSourceIO_CMF.c.in PS1_V1 pmSourceIO_CMF_PS1_V1.c
 
-pmSourceIO_CMF_PS1_V2.v1.c : pmSourceIO_CMF.c.in mksource.pl
-	mksource.pl pmSourceIO_CMF.c.in PS1_V2 pmSourceIO_CMF_PS1_V2.v1.c
+pmSourceIO_CMF_PS1_V2.c : pmSourceIO_CMF.c.in mksource.pl
+	mksource.pl pmSourceIO_CMF.c.in PS1_V2 pmSourceIO_CMF_PS1_V2.c
 
-pmSourceIO_CMF_PS1_V3.v1.c : pmSourceIO_CMF.c.in mksource.pl
-	mksource.pl pmSourceIO_CMF.c.in PS1_V2 pmSourceIO_CMF_PS1_V3.v1.c
+pmSourceIO_CMF_PS1_V3.c : pmSourceIO_CMF.c.in mksource.pl
+	mksource.pl pmSourceIO_CMF.c.in PS1_V3 pmSourceIO_CMF_PS1_V3.c
+
+pmSourceIO_CMF_PS1_V4.c : pmSourceIO_CMF.c.in mksource.pl
+	mksource.pl pmSourceIO_CMF.c.in PS1_V4 pmSourceIO_CMF_PS1_V4.c
 
 # EXTRA_DIST = pmErrorCodes.h.in pmErrorCodes.dat pmErrorCodes.c.in
Index: branches/meh_branches/ppstack_test/psModules/src/objects/mksource.pl
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/mksource.pl	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/mksource.pl	(revision 33415)
@@ -14,7 +14,11 @@
 
 # see if we can add in PS1_DV* and PS1_SV* as well...
-@cmfmodes = ("PS1_V1", 1,
+%cmfmodes = ("PS1_V1", 1,
 	     "PS1_V2", 2,
-	     "PS1_V3", 3);
+	     "PS1_V3", 3,
+	     "PS1_V4", 4);
+
+print "1: $cmfmodes{1}\n";
+print "PS1_V1: $cmfmodes{'PS1_V1'}\n";
 
 open (FILE, "$template") || die "failed to open template $template\n";
@@ -50,15 +54,19 @@
 
     if ($gtMode) {
+	# print "gtMode : $line\n";
 	$thisLevel = $cmfmodes{$gtMode};
 	$myLevel = $cmfmodes{$cmfmode};
-	if ($thisLevel <= $myLevel) { next; }
-	$line =~ s|\@<\S*\@\s*||;
+	print "gtMode : $gtMode vs $cmfmode, $thisLevel, $myLevel\n";
+	if ($myLevel <= $thisLevel) { next; }
+	$line =~ s|\@>\S*\@\s*||;
     }
 
     if ($ltMode) {
+	# print "ltMode : $line\n";
 	$thisLevel = $cmfmodes{$ltMode};
 	$myLevel = $cmfmodes{$cmfmode};
-	if ($thisLevel >= $myLevel) { next; }
-	$line =~ s|\@>\S*\@\s*||;
+	print "ltMode : $ltMode vs $cmfmode, $thisLevel, $myLevel\n";
+	if ($myLevel >= $thisLevel) { next; }
+	$line =~ s|\@<\S*\@\s*||;
     }
 
Index: branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_DEV.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_DEV.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_DEV.c	(revision 33415)
@@ -89,4 +89,6 @@
 static bool limitsApply = true;         // Apply limits?
 
+# include "pmModel_SERSIC.CP.h"
+
 psF32 PM_MODEL_FUNC (psVector *deriv,
                      const psVector *params,
@@ -94,8 +96,4 @@
 {
     psF32 *PAR = params->data.F32;
-
-    float index = 0.5 / ALPHA;
-    float bn = 1.9992*index - 0.3271;
-    float Io = exp(bn);
 
     psF32 X  = pixcoord->data.F32[0] - PAR[PM_PAR_XPOS];
@@ -105,8 +103,89 @@
     psF32 z  = (PS_SQR(px) + PS_SQR(py) + PAR[PM_PAR_SXY]*X*Y);
 
-    assert (z >= 0);
+    // If the elliptical contour is defined in a valid way, we should never trigger this
+    // assert.  Other models (like PGAUSS) don't use fractional powers, and thus do not have
+    // NaN values for negative values of z
+    psAssert (z >= 0, "do not allow negative z values in model");
+
+    float index = 0.5 / ALPHA;
+    float par7 = ALPHA;
+    float bn = 1.9992*index - 0.3271;
+    float Io = exp(bn);
 
     psF32 f2 = bn*pow(z,ALPHA);
     psF32 f1 = Io*exp(-f2);
+
+    psF32 radius = hypot(X, Y);
+    if (radius < 1.0) {
+
+	// ** use bilinear interpolation to the given location from the 4 surrounding pixels centered on the object center
+
+	// first, use Rmajor and index to find the central pixel flux (fraction of total flux)
+	psEllipseShape shape;
+
+	shape.sx  = PAR[PM_PAR_SXX];
+	shape.sy  = PAR[PM_PAR_SYY];
+	shape.sxy = PAR[PM_PAR_SXY];
+
+	// for a non-circular Sersic, the flux of the Rmajor equivalent is scaled by the AspectRatio
+	psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
+
+	// get the central pixel flux from the lookup table
+	float xPix = (axes.major - centralPixelXo) / centralPixeldX;
+	xPix = PS_MIN (PS_MAX(xPix, 0), centralPixelNX - 1);
+	float yPix = (index - centralPixelYo) / centralPixeldY;
+	yPix = PS_MIN (PS_MAX(yPix, 0), centralPixelNY - 1);
+
+	// the integral of a Sersic has an analytical form as follows:
+	float logGamma = lgamma(2.0*index);
+	float bnFactor = pow(bn, 2.0*index);
+	float norm = 2.0 * M_PI * PS_SQR(axes.major) * index * exp(bn) * exp(logGamma) / bnFactor;
+
+	// XXX interpolate to get the value
+	// XXX for the moment, just integerize
+	// XXX I need to multiply by the integrated flux to get the flux in the central pixel
+	float Vcenter = centralPixel[(int)yPix][(int)xPix] * norm;
+	
+	float px1 = 1.0 / PAR[PM_PAR_SXX];
+	float py1 = 1.0 / PAR[PM_PAR_SYY];
+	float z10 = PS_SQR(px1);
+	float z01 = PS_SQR(py1);
+
+	// which pixels do we need for this interpolation?
+	// (I do not keep state information, so I don't know anything about other evaluations of nearby pixels...)
+	if ((X >= 0) && (Y >= 0)) {
+	    float z11 = z10 + z01 + PAR[PM_PAR_SXY]; // X * Y positive
+	    float V00 = Vcenter;
+	    float V10 = Io*exp(-bn*pow(z10,par7));
+	    float V01 = Io*exp(-bn*pow(z01,par7));
+	    float V11 = Io*exp(-bn*pow(z11,par7));
+	    f1 = interpolatePixels(V00, V10, V01, V11, X, Y);
+	}
+	if ((X < 0) && (Y >= 0)) {
+	    float z11 = z10 + z01 - PAR[PM_PAR_SXY]; // X * Y negative
+	    float V00 = Io*exp(-bn*pow(z10,par7));
+	    float V10 = Vcenter;
+	    float V01 = Io*exp(-bn*pow(z11,par7));
+	    float V11 = Io*exp(-bn*pow(z01,par7));
+	    f1 = interpolatePixels(V00, V10, V01, V11, (1.0 + X), Y);
+	}
+	if ((X >= 0) && (Y < 0)) {
+	    float z11 = z10 + z01 - PAR[PM_PAR_SXY]; // X * Y negative
+	    float V00 = Io*exp(-bn*pow(z01,par7));
+	    float V10 = Io*exp(-bn*pow(z11,par7));
+	    float V01 = Vcenter;
+	    float V11 = Io*exp(-bn*pow(z10,par7));
+	    f1 = interpolatePixels(V00, V10, V01, V11, X, (1.0 + Y));
+	}
+	if ((X < 0) && (Y < 0)) {
+	    float z11 = z10 + z01 + PAR[PM_PAR_SXY]; // X * Y positive
+	    float V00 = Io*exp(-bn*pow(z11,par7));
+	    float V10 = Io*exp(-bn*pow(z10,par7));
+	    float V01 = Io*exp(-bn*pow(z01,par7));
+	    float V11 = Vcenter;
+	    f1 = interpolatePixels(V00, V10, V01, V11, (1.0 + X), (1.0 + Y));
+	}
+    }   
+
     psF32 z0 = PAR[PM_PAR_I0]*f1;
     psF32 f0 = PAR[PM_PAR_SKY] + z0;
@@ -120,9 +199,9 @@
         psF32 *dPAR = deriv->data.F32;
 
+        dPAR[PM_PAR_SKY]  = +1.0;
+        dPAR[PM_PAR_I0]   = +2.0*f1; // XXX extra damping..
+
         // gradient is infinite for z = 0; saturate at z = 0.01
         psF32 z1 = (z < 0.01) ? z0*bn*ALPHA*pow(0.01,ALPHA - 1.0) : z0*bn*ALPHA*pow(z,ALPHA - 1.0);
-
-        dPAR[PM_PAR_SKY]  = +1.0;
-        dPAR[PM_PAR_I0]   = +2.0*f1;
 
         assert (isfinite(z1));
@@ -223,4 +302,5 @@
 
     // set the shape parameters
+    // XXX adjust this?
     if (!pmModelSetShape(&PAR[PM_PAR_SXX], &PAR[PM_PAR_SXY], &PAR[PM_PAR_SYY], source->moments)) {
       return false;
@@ -246,36 +326,28 @@
 }
 
+// A DeVaucouleur model is equivalent to a Sersic with index = 4.0
 psF64 PM_MODEL_FLUX (const psVector *params)
 {
-    float z, norm;
     psEllipseShape shape;
 
     psF32 *PAR = params->data.F32;
 
-    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
-    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sx  = PAR[PM_PAR_SXX];
+    shape.sy  = PAR[PM_PAR_SYY];
     shape.sxy = PAR[PM_PAR_SXY];
 
-    // Area is equivalent to 2 pi sigma^2
+    // for a non-circular DeVaucouleur, the flux of the Rmajor equivalent is scaled by the AspectRatio
     psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
-    psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
-
-    // the area needs to be multiplied by the integral of f(z)
-    norm = 0.0;
-
-    # define DZ 0.25
-
-    float f0 = 1.0;
-    float f1, f2;
-    for (z = DZ; z < 150; z += DZ) {
-	f1 = exp(-pow(z,ALPHA));
-        z += DZ;
-	f2 = exp(-pow(z,ALPHA));
-        norm += f0 + 4*f1 + f2;
-        f0 = f2;
-    }
-    norm *= DZ / 3.0;
-
-    psF64 Flux = PAR[PM_PAR_I0] * Area * norm;
+    float AspectRatio = axes.minor / axes.major;
+
+    float index = 4.0;
+    float bn = 1.9992*index - 0.3271;
+
+    // the integral of a Sersic has an analytical form as follows:
+    float logGamma = lgamma(2.0*index);
+    float bnFactor = pow(bn, 2.0*index);
+    float norm = 2.0 * M_PI * PS_SQR(axes.major) * index * exp(bn) * exp(logGamma) / bnFactor;
+    
+    psF64 Flux = PAR[PM_PAR_I0] * norm * AspectRatio;
 
     return(Flux);
@@ -297,6 +369,6 @@
         return (1.0);
 
-    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
-    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sx  = PAR[PM_PAR_SXX];
+    shape.sy  = PAR[PM_PAR_SYY];
     shape.sxy = PAR[PM_PAR_SXY];
 
Index: branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_EXP.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_EXP.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_EXP.c	(revision 33415)
@@ -81,4 +81,6 @@
 static bool limitsApply = true;         // Apply limits?
 
+# include "pmModel_SERSIC.CP.h"
+
 psF32 PM_MODEL_FUNC (psVector *deriv,
                      const psVector *params,
@@ -93,13 +95,89 @@
     psF32 z  = PS_SQR(px) + PS_SQR(py) + PAR[PM_PAR_SXY]*X*Y;
 
-    // XXX if the elliptical contour is defined in valid way, this step should not be required.
-    // other models (like PGAUSS) don't use fractional powers, and thus do not have NaN values
-    // for negative values of z
-    // XXX use an assert here to force the elliptical parameters to be correctly determined
-    // if (z < 0) z = 0;
-    assert (z >= 0);
-
-    psF32 f2 = sqrt(z);
-    psF32 f1 = exp(-f2);
+    // If the elliptical contour is defined in a valid way, we should never trigger this
+    // assert.  Other models (like PGAUSS) don't use fractional powers, and thus do not have
+    // NaN values for negative values of z
+    psAssert (z >= 0, "do not allow negative z values in model");
+
+    float index = 1.0;
+    float par7 = 0.5;
+    float bn = 1.9992*index - 0.3271;
+    float Io = exp(bn);
+
+    psF32 f2 = bn*sqrt(z);
+    psF32 f1 = Io*exp(-f2);
+
+    psF32 radius = hypot(X, Y);
+    if (radius < 1.0) {
+
+	// ** use bilinear interpolation to the given location from the 4 surrounding pixels centered on the object center
+
+	// first, use Rmajor and index to find the central pixel flux (fraction of total flux)
+	psEllipseShape shape;
+
+	shape.sx  = PAR[PM_PAR_SXX];
+	shape.sy  = PAR[PM_PAR_SYY];
+	shape.sxy = PAR[PM_PAR_SXY];
+
+	// for a non-circular Sersic, the flux of the Rmajor equivalent is scaled by the AspectRatio
+	psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
+
+	// get the central pixel flux from the lookup table
+	float xPix = (axes.major - centralPixelXo) / centralPixeldX;
+	xPix = PS_MIN (PS_MAX(xPix, 0), centralPixelNX - 1);
+	float yPix = (index - centralPixelYo) / centralPixeldY;
+	yPix = PS_MIN (PS_MAX(yPix, 0), centralPixelNY - 1);
+
+	// the integral of a Sersic has an analytical form as follows:
+	float logGamma = lgamma(2.0*index);
+	float bnFactor = pow(bn, 2.0*index);
+	float norm = 2.0 * M_PI * PS_SQR(axes.major) * index * exp(bn) * exp(logGamma) / bnFactor;
+
+	// XXX interpolate to get the value
+	// XXX for the moment, just integerize
+	// XXX I need to multiply by the integrated flux to get the flux in the central pixel
+	float Vcenter = centralPixel[(int)yPix][(int)xPix] * norm;
+	
+	float px1 = 1.0 / PAR[PM_PAR_SXX];
+	float py1 = 1.0 / PAR[PM_PAR_SYY];
+	float z10 = PS_SQR(px1);
+	float z01 = PS_SQR(py1);
+
+	// which pixels do we need for this interpolation?
+	// (I do not keep state information, so I don't know anything about other evaluations of nearby pixels...)
+	if ((X >= 0) && (Y >= 0)) {
+	    float z11 = z10 + z01 + PAR[PM_PAR_SXY]; // X * Y positive
+	    float V00 = Vcenter;
+	    float V10 = Io*exp(-bn*pow(z10,par7));
+	    float V01 = Io*exp(-bn*pow(z01,par7));
+	    float V11 = Io*exp(-bn*pow(z11,par7));
+	    f1 = interpolatePixels(V00, V10, V01, V11, X, Y);
+	}
+	if ((X < 0) && (Y >= 0)) {
+	    float z11 = z10 + z01 - PAR[PM_PAR_SXY]; // X * Y negative
+	    float V00 = Io*exp(-bn*pow(z10,par7));
+	    float V10 = Vcenter;
+	    float V01 = Io*exp(-bn*pow(z11,par7));
+	    float V11 = Io*exp(-bn*pow(z01,par7));
+	    f1 = interpolatePixels(V00, V10, V01, V11, (1.0 + X), Y);
+	}
+	if ((X >= 0) && (Y < 0)) {
+	    float z11 = z10 + z01 - PAR[PM_PAR_SXY]; // X * Y negative
+	    float V00 = Io*exp(-bn*pow(z01,par7));
+	    float V10 = Io*exp(-bn*pow(z11,par7));
+	    float V01 = Vcenter;
+	    float V11 = Io*exp(-bn*pow(z10,par7));
+	    f1 = interpolatePixels(V00, V10, V01, V11, X, (1.0 + Y));
+	}
+	if ((X < 0) && (Y < 0)) {
+	    float z11 = z10 + z01 + PAR[PM_PAR_SXY]; // X * Y positive
+	    float V00 = Io*exp(-bn*pow(z11,par7));
+	    float V10 = Io*exp(-bn*pow(z10,par7));
+	    float V01 = Io*exp(-bn*pow(z01,par7));
+	    float V11 = Vcenter;
+	    f1 = interpolatePixels(V00, V10, V01, V11, (1.0 + X), (1.0 + Y));
+	}
+    }   
+
     psF32 z0 = PAR[PM_PAR_I0]*f1;
     psF32 f0 = PAR[PM_PAR_SKY] + z0;
@@ -118,5 +196,5 @@
         // gradient is infinite for z = 0; saturate at z = 0.01
 	// z1 is -df/dz (the negative sign is canceled by most of dz/dPAR[i]
-        psF32 z1 = (z < 0.01) ? 0.5*z0/sqrt(0.01) : 0.5*z0/sqrt(z);
+        psF32 z1 = (z < 0.01) ? 0.5*bn*z0/sqrt(0.01) : 0.5*bn*z0/sqrt(z);
 
 	// XXX dampen SXX and SYY as in GAUSS?
@@ -216,4 +294,5 @@
 
     // set the shape parameters
+    // XXX adjust this?
     if (!pmModelSetShape(&PAR[PM_PAR_SXX], &PAR[PM_PAR_SXY], &PAR[PM_PAR_SYY], source->moments)) {
       return false;
@@ -233,36 +312,28 @@
 }
 
+// An exponential model is equivalent to a Sersic with index = 1.0
 psF64 PM_MODEL_FLUX (const psVector *params)
 {
-    float z, norm;
     psEllipseShape shape;
 
     psF32 *PAR = params->data.F32;
 
-    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
-    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sx  = PAR[PM_PAR_SXX];
+    shape.sy  = PAR[PM_PAR_SYY];
     shape.sxy = PAR[PM_PAR_SXY];
 
-    // Area is equivalent to 2 pi sigma^2
+    // for a non-circular Exponential, the flux of the Rmajor equivalent is scaled by the AspectRatio
     psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
-    psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
-
-    // the area needs to be multiplied by the integral of f(z) = exp(-sqrt(z)) [0 to infinity]
-    norm = 0.0;
-
-    # define DZ 0.25
-
-    float f0 = 1.0;
-    float f1, f2;
-    for (z = DZ; z < 150; z += DZ) {
-        f1 = exp(-sqrt(z));
-        z += DZ;
-        f2 = exp(-sqrt(z));
-        norm += f0 + 4*f1 + f2;
-        f0 = f2;
-    }
-    norm *= DZ / 3.0;
-
-    psF64 Flux = PAR[PM_PAR_I0] * Area * norm;
+    float AspectRatio = axes.minor / axes.major;
+
+    float index = 1.0;
+    float bn = 1.9992*index - 0.3271;
+
+    // the integral of a Sersic has an analytical form as follows:
+    float logGamma = lgamma(2.0*index);
+    float bnFactor = pow(bn, 2.0*index);
+    float norm = 2.0 * M_PI * PS_SQR(axes.major) * index * exp(bn) * exp(logGamma) / bnFactor;
+    
+    psF64 Flux = PAR[PM_PAR_I0] * norm * AspectRatio;
 
     return(Flux);
@@ -284,6 +355,6 @@
         return (1.0);
 
-    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
-    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sx  = PAR[PM_PAR_SXX];
+    shape.sy  = PAR[PM_PAR_SYY];
     shape.sxy = PAR[PM_PAR_SXY];
 
Index: branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_PGAUSS.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_PGAUSS.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_PGAUSS.c	(revision 33415)
@@ -217,7 +217,8 @@
 }
 
-psF64 PM_MODEL_FLUX(const psVector *params)
-{
-    float norm, z;
+// integrate the model to get the full flux
+psF64 PM_MODEL_FLUX (const psVector *params)
+{
+    float z, norm;
     psEllipseShape shape;
 
@@ -228,25 +229,27 @@
     shape.sxy = PAR[PM_PAR_SXY];
 
-    // Area is equivalent to 2 pi sigma^2
     psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
-    psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
-
-    // the area needs to be multiplied by the integral of f(z)
+    float AspectRatio = axes.minor / axes.major;
+
+    // flux = 2 \pi \int f(r) r dr
     norm = 0.0;
 
-    # define DZ 0.25
-
-    float f0 = 1.0;
+    # define DR 0.25
+
+    // f = f(r) * r
+    float f0 = 0.0;
     float f1, f2;
-    for (z = DZ; z < 150; z += DZ) {
-        f1 = 1.0 / (1 + z + z*z/2.0 + z*z*z/6.0);
-        z += DZ;
-        f2 = 1.0 / (1 + z + z*z/2.0 + z*z*z/6.0);
+    for (float r = DR; r < 150; r += DR) {
+	z = 0.5 * PS_SQR(r / axes.major);
+        f1 = r / (1 + z + z*z/2.0 + z*z*z/6.0);
+        r += DR;
+	z = 0.5 * PS_SQR(r / axes.major);
+        f2 = r / (1 + z + z*z/2.0 + z*z*z/6.0);
         norm += f0 + 4*f1 + f2;
         f0 = f2;
     }
-    norm *= DZ / 3.0;
-
-    psF64 Flux = PAR[PM_PAR_I0] * Area * norm;
+    norm *= DR / 3.0;
+
+    psF64 Flux = PAR[PM_PAR_I0] * norm * 2.0 * M_PI * AspectRatio;
 
     return(Flux);
Index: branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_PS1_V1.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_PS1_V1.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_PS1_V1.c	(revision 33415)
@@ -14,6 +14,6 @@
    * PM_PAR_XPOS 2  - X center of object
    * PM_PAR_YPOS 3  - Y center of object
-   * PM_PAR_SXX 4   - X^2 term of elliptical contour (sqrt(2) / SigmaX)
-   * PM_PAR_SYY 5   - Y^2 term of elliptical contour (sqrt(2) / SigmaY)
+   * PM_PAR_SXX 4   - X^2 term of elliptical contour (SigmaX / sqrt(2))
+   * PM_PAR_SYY 5   - Y^2 term of elliptical contour (SigmaY / sqrt(2))
    * PM_PAR_SXY 6   - X*Y term of elliptical contour
    * PM_PAR_7   7   - amplitude of the linear component (k)
@@ -239,4 +239,5 @@
 }
 
+// integrate the model to get the full flux
 psF64 PM_MODEL_FLUX (const psVector *params)
 {
@@ -250,25 +251,27 @@
     shape.sxy = PAR[PM_PAR_SXY];
 
-    // Area is equivalent to 2 pi sigma^2
     psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
-    psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
-
-    // the area needs to be multiplied by the integral of f(z)
+    float AspectRatio = axes.minor / axes.major;
+
+    // flux = 2 \pi \int f(r) r dr
     norm = 0.0;
 
-    # define DZ 0.25
-
-    float f0 = 1.0;
+    # define DR 0.25
+
+    // f = f(r) * r
+    float f0 = 0.0;
     float f1, f2;
-    for (z = DZ; z < 150; z += DZ) {
-        f1 = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, ALPHA));
-        z += DZ;
-        f2 = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, ALPHA));
+    for (float r = DR; r < 150; r += DR) {
+	z = 0.5 * PS_SQR(r / axes.major);
+        f1 = r / (1 + PAR[PM_PAR_7]*z + pow(z, ALPHA));
+        r += DR;
+	z = 0.5 * PS_SQR(r / axes.major);
+        f2 = r / (1 + PAR[PM_PAR_7]*z + pow(z, ALPHA));
         norm += f0 + 4*f1 + f2;
         f0 = f2;
     }
-    norm *= DZ / 3.0;
-
-    psF64 Flux = PAR[PM_PAR_I0] * Area * norm;
+    norm *= DR / 3.0;
+
+    psF64 Flux = PAR[PM_PAR_I0] * norm * 2.0 * M_PI * AspectRatio;
 
     return(Flux);
Index: branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_QGAUSS.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_QGAUSS.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_QGAUSS.c	(revision 33415)
@@ -240,4 +240,5 @@
 }
 
+// integrate the model to get the full flux
 psF64 PM_MODEL_FLUX (const psVector *params)
 {
@@ -251,25 +252,27 @@
     shape.sxy = PAR[PM_PAR_SXY];
 
-    // Area is equivalent to 2 pi sigma^2
     psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
-    psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
-
-    // the area needs to be multiplied by the integral of f(z)
+    float AspectRatio = axes.minor / axes.major;
+
+    // flux = 2 \pi \int f(r) r dr
     norm = 0.0;
 
-    # define DZ 0.25
-
-    float f0 = 1.0;
+    # define DR 0.25
+
+    // f = f(r) * r
+    float f0 = 0.0;
     float f1, f2;
-    for (z = DZ; z < 150; z += DZ) {
-        f1 = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, ALPHA));
-        z += DZ;
-        f2 = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, ALPHA));
+    for (float r = DR; r < 150; r += DR) {
+	z = 0.5 * PS_SQR(r / axes.major);
+        f1 = r / (1 + PAR[PM_PAR_7]*z + pow(z, ALPHA));
+        r += DR;
+	z = 0.5 * PS_SQR(r / axes.major);
+        f2 = r / (1 + PAR[PM_PAR_7]*z + pow(z, ALPHA));
         norm += f0 + 4*f1 + f2;
         f0 = f2;
     }
-    norm *= DZ / 3.0;
-
-    psF64 Flux = PAR[PM_PAR_I0] * Area * norm;
+    norm *= DR / 3.0;
+
+    psF64 Flux = PAR[PM_PAR_I0] * norm * 2.0 * M_PI * AspectRatio;
 
     return(Flux);
Index: branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_RGAUSS.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_RGAUSS.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_RGAUSS.c	(revision 33415)
@@ -229,7 +229,8 @@
 }
 
+// integrate the model to get the full flux
 psF64 PM_MODEL_FLUX (const psVector *params)
 {
-    float norm, z;
+    float z, norm;
     psEllipseShape shape;
 
@@ -240,25 +241,27 @@
     shape.sxy = PAR[PM_PAR_SXY];
 
-    // Area is equivalent to 2 pi sigma^2
     psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
-    psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
-
-    // the area needs to be multiplied by the integral of f(z)
+    float AspectRatio = axes.minor / axes.major;
+
+    // flux = 2 \pi \int f(r) r dr
     norm = 0.0;
 
-    # define DZ 0.25
-
-    float f0 = 1.0;
+    # define DR 0.25
+
+    // f = f(r) * r
+    float f0 = 0.0;
     float f1, f2;
-    for (z = DZ; z < 150; z += DZ) {
-        f1 = 1.0 / (1 + z + pow(z, PAR[PM_PAR_7]));
-        z += DZ;
-        f2 = 1.0 / (1 + z + pow(z, PAR[PM_PAR_7]));
+    for (float r = DR; r < 150; r += DR) {
+	z = 0.5 * PS_SQR(r / axes.major);
+        f1 = r / (1 + z + pow(z, PAR[PM_PAR_7]));
+        r += DR;
+	z = 0.5 * PS_SQR(r / axes.major);
+        f2 = r / (1 + z + pow(z, PAR[PM_PAR_7]));
         norm += f0 + 4*f1 + f2;
         f0 = f2;
     }
-    norm *= DZ / 3.0;
-
-    psF64 Flux = PAR[PM_PAR_I0] * Area * norm;
+    norm *= DR / 3.0;
+
+    psF64 Flux = PAR[PM_PAR_I0] * norm * 2.0 * M_PI * AspectRatio;
 
     return(Flux);
Index: branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_SERSIC.CP.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_SERSIC.CP.h	(revision 33415)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_SERSIC.CP.h	(revision 33415)
@@ -0,0 +1,30 @@
+static int centralPixelNX = 29;
+static int centralPixelNY = 12;
+static float centralPixelXo = 2.0;
+static float centralPixelYo = 0.5;
+static float centralPixeldX = 0.5;
+static float centralPixeldY = 0.5;
+
+static float centralPixel[12][29] = {
+{  0.0521,  0.0336,  0.0235,  0.0173,  0.0133,  0.0105,  0.0085,  0.0070,  0.0059,  0.0051,  0.0044,  0.0038,  0.0033,  0.0030,  0.0026,  0.0024,  0.0021,  0.0019,  0.0018,  0.0016,  0.0015,  0.0014,  0.0013,  0.0012,  0.0011,  0.0010,  0.0010,  0.0009,  0.0008, }, 
+{  0.0817,  0.0556,  0.0402,  0.0304,  0.0238,  0.0191,  0.0157,  0.0131,  0.0111,  0.0096,  0.0083,  0.0073,  0.0064,  0.0057,  0.0051,  0.0046,  0.0042,  0.0038,  0.0035,  0.0032,  0.0029,  0.0027,  0.0025,  0.0023,  0.0022,  0.0020,  0.0019,  0.0018,  0.0017, }, 
+{  0.1096,  0.0785,  0.0591,  0.0462,  0.0371,  0.0305,  0.0255,  0.0217,  0.0187,  0.0162,  0.0143,  0.0126,  0.0113,  0.0101,  0.0091,  0.0083,  0.0076,  0.0069,  0.0064,  0.0059,  0.0054,  0.0050,  0.0047,  0.0044,  0.0041,  0.0038,  0.0036,  0.0034,  0.0032, }, 
+{  0.1339,  0.0998,  0.0777,  0.0624,  0.0513,  0.0431,  0.0367,  0.0317,  0.0277,  0.0244,  0.0217,  0.0194,  0.0175,  0.0158,  0.0144,  0.0132,  0.0121,  0.0112,  0.0103,  0.0096,  0.0089,  0.0083,  0.0078,  0.0073,  0.0069,  0.0065,  0.0061,  0.0058,  0.0055, }, 
+{  0.1547,  0.1190,  0.0951,  0.0781,  0.0655,  0.0559,  0.0484,  0.0423,  0.0374,  0.0334,  0.0300,  0.0271,  0.0246,  0.0225,  0.0206,  0.0190,  0.0176,  0.0163,  0.0152,  0.0142,  0.0133,  0.0125,  0.0117,  0.0110,  0.0104,  0.0099,  0.0093,  0.0089,  0.0084, }, 
+{  0.1726,  0.1361,  0.1110,  0.0928,  0.0791,  0.0685,  0.0600,  0.0531,  0.0475,  0.0427,  0.0387,  0.0353,  0.0323,  0.0297,  0.0275,  0.0255,  0.0237,  0.0221,  0.0207,  0.0195,  0.0183,  0.0172,  0.0163,  0.0154,  0.0146,  0.0139,  0.0132,  0.0126,  0.0120, }, 
+{  0.1881,  0.1513,  0.1255,  0.1065,  0.0919,  0.0805,  0.0713,  0.0637,  0.0574,  0.0521,  0.0476,  0.0437,  0.0403,  0.0373,  0.0347,  0.0323,  0.0303,  0.0284,  0.0267,  0.0252,  0.0238,  0.0225,  0.0214,  0.0203,  0.0193,  0.0184,  0.0176,  0.0168,  0.0161, }, 
+{  0.2017,  0.1649,  0.1387,  0.1191,  0.1040,  0.0919,  0.0821,  0.0740,  0.0672,  0.0614,  0.0564,  0.0521,  0.0483,  0.0450,  0.0420,  0.0394,  0.0370,  0.0349,  0.0329,  0.0312,  0.0296,  0.0281,  0.0268,  0.0255,  0.0244,  0.0233,  0.0223,  0.0214,  0.0205, }, 
+{  0.2138,  0.1771,  0.1507,  0.1308,  0.1152,  0.1027,  0.0924,  0.0839,  0.0767,  0.0705,  0.0651,  0.0604,  0.0563,  0.0527,  0.0494,  0.0465,  0.0439,  0.0415,  0.0393,  0.0374,  0.0356,  0.0339,  0.0324,  0.0310,  0.0296,  0.0284,  0.0273,  0.0262,  0.0252, }, 
+{  0.2246,  0.1883,  0.1618,  0.1416,  0.1257,  0.1128,  0.1022,  0.0933,  0.0857,  0.0792,  0.0735,  0.0686,  0.0642,  0.0603,  0.0568,  0.0536,  0.0508,  0.0482,  0.0458,  0.0436,  0.0416,  0.0398,  0.0381,  0.0365,  0.0351,  0.0337,  0.0324,  0.0313,  0.0301, }, 
+{  0.2347,  0.1986,  0.1721,  0.1517,  0.1355,  0.1224,  0.1115,  0.1023,  0.0945,  0.0877,  0.0817,  0.0765,  0.0719,  0.0677,  0.0640,  0.0606,  0.0576,  0.0548,  0.0522,  0.0499,  0.0477,  0.0458,  0.0439,  0.0422,  0.0406,  0.0391,  0.0377,  0.0364,  0.0352, }, 
+{  0.2448,  0.2086,  0.1819,  0.1614,  0.1450,  0.1316,  0.1204,  0.1110,  0.1029,  0.0958,  0.0897,  0.0842,  0.0793,  0.0750,  0.0711,  0.0675,  0.0643,  0.0613,  0.0586,  0.0561,  0.0538,  0.0517,  0.0497,  0.0479,  0.0462,  0.0445,  0.0430,  0.0416,  0.0403, }, 
+}; 
+
+static float interpolatePixels (float V00, float V10, float V01, float V11, float dx, float dy) {
+    // bilinear interpolation
+    float rx = 1.0 - dx;
+    float ry = 1.0 - dy;
+    float value = V00*rx*ry + V10*dx*ry + V01*rx*dy + V11*dx*dy;
+    return value;
+}
+
Index: branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_SERSIC.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_SERSIC.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_SERSIC.c	(revision 33415)
@@ -13,8 +13,14 @@
    * PM_PAR_XPOS 2  - X center of object
    * PM_PAR_YPOS 3  - Y center of object
-   * PM_PAR_SXX 4   - X^2 term of elliptical contour (sqrt(2) / SigmaX)
-   * PM_PAR_SYY 5   - Y^2 term of elliptical contour (sqrt(2) / SigmaY)
+   * PM_PAR_SXX 4   - X^2 term of elliptical contour (SigmaX / sqrt(2))
+   * PM_PAR_SYY 5   - Y^2 term of elliptical contour (SigmaY / sqrt(2))
    * PM_PAR_SXY 6   - X*Y term of elliptical contour
    * PM_PAR_7   7   - normalized sersic parameter
+
+   * note that a Sersic model is usually defined in terms of R_e, the half-light radius.  This
+     construction does not include a factor of 2 in the X^2 term, etc, like for a Gaussian.
+     Conversion from SXX, SYY, SXY to R_major, R_minor, theta can be done by using setting:
+     shape.sx = SXX / sqrt(2), shape.sy = SYY / sqrt(2), shape.sxy = SXY, then calling
+     psEllipseShapeToAxes, and multiplying the values of axes.major, axes.minor by sqrt(2)
 
    note that a standard sersic model uses exp(-K*(z^(1/2n) - 1).  the additional elements (K,
@@ -85,4 +91,6 @@
 static bool limitsApply = true;         // Apply limits?
 
+# include "pmModel_SERSIC.CP.h"
+
 psF32 PM_MODEL_FUNC (psVector *deriv,
                      const psVector *params,
@@ -97,17 +105,89 @@
     psF32 z  = PS_SQR(px) + PS_SQR(py) + PAR[PM_PAR_SXY]*X*Y;
 
-    // XXX if the elliptical contour is defined in valid way, this step should not be required.
-    // other models (like PGAUSS) don't use fractional powers, and thus do not have NaN values
-    // for negative values of z
-    // XXX use an assert here to force the elliptical parameters to be correctly determined
-    // if (z < 0) z = 0;
-    assert (z >= 0);
+    // If the elliptical contour is defined in a valid way, we should never trigger this
+    // assert.  Other models (like PGAUSS) don't use fractional powers, and thus do not have
+    // NaN values for negative values of z
+    psAssert (z >= 0, "do not allow negative z values in model");
 
     float index = 0.5 / PAR[PM_PAR_7];
+    float par7 = PAR[PM_PAR_7];
     float bn = 1.9992*index - 0.3271;
     float Io = exp(bn);
 
-    psF32 f2 = bn*pow(z,PAR[PM_PAR_7]);
+    psF32 f2 = bn*pow(z,par7);
     psF32 f1 = Io*exp(-f2);
+
+    psF32 radius = hypot(X, Y);
+    if (radius < 1.0) {
+
+	// ** use bilinear interpolation to the given location from the 4 surrounding pixels centered on the object center
+
+	// first, use Rmajor and index to find the central pixel flux (fraction of total flux)
+	psEllipseShape shape;
+
+	shape.sx  = PAR[PM_PAR_SXX];
+	shape.sy  = PAR[PM_PAR_SYY];
+	shape.sxy = PAR[PM_PAR_SXY];
+
+	// for a non-circular Sersic, the flux of the Rmajor equivalent is scaled by the AspectRatio
+	psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
+
+	// get the central pixel flux from the lookup table
+	float xPix = (axes.major - centralPixelXo) / centralPixeldX;
+	xPix = PS_MIN (PS_MAX(xPix, 0), centralPixelNX - 1);
+	float yPix = (index - centralPixelYo) / centralPixeldY;
+	yPix = PS_MIN (PS_MAX(yPix, 0), centralPixelNY - 1);
+
+	// the integral of a Sersic has an analytical form as follows:
+	float logGamma = lgamma(2.0*index);
+	float bnFactor = pow(bn, 2.0*index);
+	float norm = 2.0 * M_PI * PS_SQR(axes.major) * index * exp(bn) * exp(logGamma) / bnFactor;
+
+	// XXX interpolate to get the value
+	// XXX for the moment, just integerize
+	// XXX I need to multiply by the integrated flux to get the flux in the central pixel
+	float Vcenter = centralPixel[(int)yPix][(int)xPix] * norm;
+	
+	float px1 = 1.0 / PAR[PM_PAR_SXX];
+	float py1 = 1.0 / PAR[PM_PAR_SYY];
+	float z10 = PS_SQR(px1);
+	float z01 = PS_SQR(py1);
+
+	// which pixels do we need for this interpolation?
+	// (I do not keep state information, so I don't know anything about other evaluations of nearby pixels...)
+	if ((X >= 0) && (Y >= 0)) {
+	    float z11 = z10 + z01 + PAR[PM_PAR_SXY]; // X * Y positive
+	    float V00 = Vcenter;
+	    float V10 = Io*exp(-bn*pow(z10,par7));
+	    float V01 = Io*exp(-bn*pow(z01,par7));
+	    float V11 = Io*exp(-bn*pow(z11,par7));
+	    f1 = interpolatePixels(V00, V10, V01, V11, X, Y);
+	}
+	if ((X < 0) && (Y >= 0)) {
+	    float z11 = z10 + z01 - PAR[PM_PAR_SXY]; // X * Y negative
+	    float V00 = Io*exp(-bn*pow(z10,par7));
+	    float V10 = Vcenter;
+	    float V01 = Io*exp(-bn*pow(z11,par7));
+	    float V11 = Io*exp(-bn*pow(z01,par7));
+	    f1 = interpolatePixels(V00, V10, V01, V11, (1.0 + X), Y);
+	}
+	if ((X >= 0) && (Y < 0)) {
+	    float z11 = z10 + z01 - PAR[PM_PAR_SXY]; // X * Y negative
+	    float V00 = Io*exp(-bn*pow(z01,par7));
+	    float V10 = Io*exp(-bn*pow(z11,par7));
+	    float V01 = Vcenter;
+	    float V11 = Io*exp(-bn*pow(z10,par7));
+	    f1 = interpolatePixels(V00, V10, V01, V11, X, (1.0 + Y));
+	}
+	if ((X < 0) && (Y < 0)) {
+	    float z11 = z10 + z01 + PAR[PM_PAR_SXY]; // X * Y positive
+	    float V00 = Io*exp(-bn*pow(z11,par7));
+	    float V10 = Io*exp(-bn*pow(z10,par7));
+	    float V01 = Io*exp(-bn*pow(z01,par7));
+	    float V11 = Vcenter;
+	    f1 = interpolatePixels(V00, V10, V01, V11, (1.0 + X), (1.0 + Y));
+	}
+    }   
+
     psF32 z0 = PAR[PM_PAR_I0]*f1;
     psF32 f0 = PAR[PM_PAR_SKY] + z0;
@@ -121,10 +201,11 @@
         psF32 *dPAR = deriv->data.F32;
 
-        // gradient is infinite for z = 0; saturate at z = 0.01
-        psF32 z1 = (z < 0.01) ? z0*bn*PAR[PM_PAR_7]*pow(0.01,PAR[PM_PAR_7] - 1.0) : z0*bn*PAR[PM_PAR_7]*pow(z,PAR[PM_PAR_7] - 1.0);
-
         dPAR[PM_PAR_SKY]  = +1.0;
         dPAR[PM_PAR_I0]   = +f1;
-        dPAR[PM_PAR_7]    = (z < 0.01) ? -z0*pow(0.01,PAR[PM_PAR_7])*log(0.01) : -z0*f2*log(z);
+
+        // gradient is infinite for z = 0; saturate at z = 0.01
+        psF32 z1 = (z < 0.01) ? z0*bn*par7*pow(0.01,par7 - 1.0) : z0*bn*par7*pow(z,par7 - 1.0);
+
+        dPAR[PM_PAR_7]    = (z < 0.01) ? -z0*pow(0.01,par7)*log(0.01) : -z0*f2*log(z);
 	dPAR[PM_PAR_7]   *= 3.0;
 
@@ -269,8 +350,4 @@
     float Io = exp(0.5*bn);
 
-    // XXX do we need this factor of sqrt(2)?
-    // float Sxx = PS_MAX(0.5, M_SQRT2*shape.sx);
-    // float Syy = PS_MAX(0.5, M_SQRT2*shape.sy);
-
     float Sxx = PS_MAX(0.5, shape.sx);
     float Syy = PS_MAX(0.5, shape.sy);
@@ -294,37 +371,28 @@
 }
 
+// A sersic model has an integral flux which can be analytically represented
 psF64 PM_MODEL_FLUX (const psVector *params)
 {
-    float z, norm;
     psEllipseShape shape;
 
     psF32 *PAR = params->data.F32;
 
-    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
-    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sx  = PAR[PM_PAR_SXX];
+    shape.sy  = PAR[PM_PAR_SYY];
     shape.sxy = PAR[PM_PAR_SXY];
 
-    // Area is equivalent to 2 pi sigma^2
+    // for a non-circular Sersic, the flux of the Rmajor equivalent is scaled by the AspectRatio
     psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
-    psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
-
-    // the area needs to be multiplied by the integral of f(z)
-    norm = 0.0;
-
-    # define DZ 0.25
-
-    float f0 = 1.0;
-    float f1, f2;
-    for (z = DZ; z < 150; z += DZ) {
-        // f1 = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, 2.25));
-	f1 = exp(-pow(z,PAR[PM_PAR_7]));
-        z += DZ;
-	f2 = exp(-pow(z,PAR[PM_PAR_7]));
-        norm += f0 + 4*f1 + f2;
-        f0 = f2;
-    }
-    norm *= DZ / 3.0;
-
-    psF64 Flux = PAR[PM_PAR_I0] * Area * norm;
+    float AspectRatio = axes.minor / axes.major;
+
+    float index = 0.5 / PAR[PM_PAR_7];
+    float bn = 1.9992*index - 0.3271;
+
+    // the integral of a Sersic has an analytical form as follows:
+    float logGamma = lgamma(2.0*index);
+    float bnFactor = pow(bn, 2.0*index);
+    float norm = 2.0 * M_PI * PS_SQR(axes.major) * index * exp(bn) * exp(logGamma) / bnFactor;
+    
+    psF64 Flux = PAR[PM_PAR_I0] * norm * AspectRatio;
 
     return(Flux);
@@ -346,6 +414,6 @@
         return (1.0);
 
-    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
-    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sx  = PAR[PM_PAR_SXX];
+    shape.sy  = PAR[PM_PAR_SYY];
     shape.sxy = PAR[PM_PAR_SXY];
 
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmFootprintCullPeaks.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmFootprintCullPeaks.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmFootprintCullPeaks.c	(revision 33415)
@@ -118,6 +118,8 @@
 	    threshbounds->data.F32[i] = 0.25*beta2*PS_SQR(i) + min_threshold;	    
 	}
-	psAssert(threshbounds->data.F32[threshbounds->n-1] > maxFlux, "upper limit does not include max flux");
-
+        if (threshbounds->data.F32[threshbounds->n-1] > maxFlux) {
+            psWarning ("upper limit: %f does not include max flux: %f",
+                    threshbounds->data.F32[threshbounds->n-1], maxFlux);
+        }
 	psHistogram *threshist = psHistogramAllocGeneric(threshbounds);
 
@@ -181,4 +183,5 @@
 	    if (!myFP->n) {
 		psWarning ("empty footprint?");
+		psFree (myFP);
 		continue;
 	    }
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmModelUtils.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmModelUtils.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmModelUtils.c	(revision 33415)
@@ -122,4 +122,9 @@
     if (!isfinite(axes.theta)) return false;
 
+    // Mxx, Mxy, Myy define the elliptical shape, but Mrf defines the width 
+    float scale = (isfinite(moments->Mrf) && (moments->Mrf > 0.0)) ? moments->Mrf / axes.major : 1.0;
+    axes.major *= scale;
+    axes.minor *= scale;
+
     psEllipseShape shape = psEllipseAxesToShape (axes);
 
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmMoments.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmMoments.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmMoments.c	(revision 33415)
@@ -32,4 +32,8 @@
     tmp->Mrf = 0.0;
     tmp->Mrh = 0.0;
+
+    tmp->KronCore = 0.0;
+    tmp->KronCoreErr = 0.0;
+
     tmp->KronFlux = 0.0;
     tmp->KronFluxErr = 0.0;
@@ -37,4 +41,8 @@
     tmp->KronFinner = 0.0;
     tmp->KronFouter = 0.0;
+
+    tmp->KronFluxPSF = 0.0;
+    tmp->KronFluxPSFErr = 0.0;
+    tmp->KronRadiusPSF = 0.0;
 
     tmp->Mx = 0.0;
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmMoments.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmMoments.h	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmMoments.h	(revision 33415)
@@ -51,4 +51,8 @@
     int nPixels;  ///< Number of pixels used.
 
+    float KronFluxPSF; ///< Kron Flux using PSF-optimized window
+    float KronFluxPSFErr; ///< Kron Flux Error using PSF-optimized window
+    float KronRadiusPSF; ///< Kron Radius using PSF-optimized window (Flux in 2.5 Radius)
+
     float KronCore;    ///< flux in r < 1.0*Mrf
     float KronCoreErr;    ///< error on flux in r < 1.0*Mrf
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmPCM_MinimizeChisq.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmPCM_MinimizeChisq.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmPCM_MinimizeChisq.c	(revision 33415)
@@ -45,4 +45,5 @@
 # define USE_FFT 1
 # define PRE_CONVOLVE 1
+# define TESTCOPY 0
 
 bool pmPCM_MinimizeChisq (
@@ -93,5 +94,9 @@
 	psFree (pcm->psfFFT);
     }
-    pcm->psfFFT = psImageConvolveKernelInit(pcm->modelFlux, pcm->psf);
+# if (!TESTCOPY)
+    if (!pcm->use1Dgauss) {
+	pcm->psfFFT = psImageConvolveKernelInit(pcm->modelFlux, pcm->psf);
+    }
+# endif
 # endif    
 
@@ -285,6 +290,6 @@
 
             // Convert i/j to image space:
-            coord->data.F32[0] = (psF32) (j + source->pixels->col0);
-            coord->data.F32[1] = (psF32) (i + source->pixels->row0);
+            coord->data.F32[0] = (psF32) (j + 0.5 + source->pixels->col0);
+            coord->data.F32[1] = (psF32) (i + 0.5 + source->pixels->row0);
 
             pcm->modelFlux->data.F32[i][j] = pcm->modelConv->modelFunc (deriv, params, coord);
@@ -309,5 +314,21 @@
 # if (PRE_CONVOLVE)
     // convolve model image and derivative images with pre-convolved kernel
-    psImageConvolveKernel (pcm->modelConvFlux, pcm->modelFlux, NULL, 0, pcm->psfFFT);
+
+// XXX for a test, just copy, rather than convolve
+# if (TESTCOPY)
+    psImageCopy (pcm->modelConvFlux, pcm->modelFlux, pcm->modelFlux->type.type);
+# else
+    if (pcm->use1Dgauss) {
+	// do not use the threaded, mask-aware version of this code (psImageSmoothMaskPixelsThread):
+	// * the model flux is not masked
+	// * threading takes place above this level
+	pcm->modelConvFlux = psImageCopy (pcm->modelConvFlux, pcm->modelFlux, pcm->modelFlux->type.type);
+	psImageSmooth_PreAlloc_F32 (pcm->modelConvFlux, pcm->smdata);
+	// psImageSmooth (pcm->modelConvFlux, pcm->sigma, pcm->nsigma);
+    } else {
+	psImageConvolveKernel (pcm->modelConvFlux, pcm->modelFlux, NULL, 0, pcm->psfFFT);
+    }
+# endif
+
     for (int n = 0; n < pcm->dmodelsFlux->n; n++) {
         if (pcm->dmodelsFlux->data[n] == NULL) continue;
@@ -315,5 +336,18 @@
         psImage *dmodel = pcm->dmodelsFlux->data[n];
         psImage *dmodelConv = pcm->dmodelsConvFlux->data[n];
-        psImageConvolveKernel (dmodelConv, dmodel, NULL, 0, pcm->psfFFT);
+# if (TESTCOPY)
+	psImageCopy (dmodelConv, dmodel, dmodel->type.type);
+# else
+	if (pcm->use1Dgauss) {
+	    // do not use the threaded, mask-aware version of this code (psImageSmoothMaskPixelsThread):
+	    // * the model flux is not masked
+	    // * threading takes place above this level
+	    dmodelConv = psImageCopy (dmodelConv, dmodel, dmodel->type.type);
+	    psImageSmooth_PreAlloc_F32 (dmodelConv, pcm->smdata);
+	    // psImageSmooth (dmodelConv, pcm->sigma, pcm->nsigma);
+	} else {
+	    psImageConvolveKernel (dmodelConv, dmodel, NULL, 0, pcm->psfFFT);
+	}
+# endif
     }
 # else
@@ -325,5 +359,15 @@
         psImage *dmodel = pcm->dmodelsFlux->data[n];
         psImage *dmodelConv = pcm->dmodelsConvFlux->data[n];
-        psImageConvolveFFT (dmodelConv, dmodel, NULL, 0, pcm->psf);
+
+	if (pcm->use1Dgauss) {
+	    // do not use the threaded, mask-aware version of this code (psImageSmoothMaskPixelsThread):
+	    // * the model flux is not masked
+	    // * threading takes place above this level
+	    dmodelConv = psImageCopy (dmodelConv, dmodel, dmodel->type.type);
+	    psImageSmooth_PreAlloc_F32 (dmodelConv, pcm->smdata);
+	    // psImageSmooth (dmodelConv, pcm->sigma, pcm->nsigma);
+	} else {
+	    psImageConvolveFFT (dmodelConv, dmodel, NULL, 0, pcm->psf);
+	}
     }
 # endif // PRE-CONVOLVE
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmPCMdata.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmPCMdata.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmPCMdata.c	(revision 33415)
@@ -41,4 +41,7 @@
 #include "pmPCMdata.h"
 
+# define USE_DELTA_PSF 0
+# define USE_1D_GAUSS 1
+
 static void pmPCMdataFree (pmPCMdata *pcm) {
 
@@ -54,4 +57,5 @@
     psFree (pcm->psfFFT);
     psFree (pcm->constraint);
+    psFree (pcm->smdata); // pre-allocated data for psImageSmooth_PreAlloc
     return;
 }
@@ -88,4 +92,10 @@
     pcm->constraint = NULL;
     pcm->nDOF = 0;
+
+    // full convolution with the PSF is expensive.  if we have to save time, we can do a 1D
+    // convolution with a Gaussian approximation to the kernel
+    pcm->use1Dgauss = false;
+    pcm->nsigma = 3.0; 
+    pcm->sigma = 1.0; // this should be set to something sensible when the psf is known
 
     return pcm;
@@ -257,4 +267,28 @@
     pcm->nDOF = nPix - nParams;
 
+# if (USE_1D_GAUSS)
+    pmModel *modelPSF = source->modelPSF;
+    psAssert (modelPSF, "psf model must be defined");
+    
+    psEllipseShape shape;
+    psEllipseAxes axes;
+
+    shape.sx  = modelPSF->params->data.F32[PM_PAR_SXX];
+    shape.sy  = modelPSF->params->data.F32[PM_PAR_SYY];
+    shape.sxy = modelPSF->params->data.F32[PM_PAR_SXY];
+    axes = psEllipseShapeToAxes (shape, 20.0);
+    
+    float FWHM_MAJOR = 2*modelPSF->modelRadius (modelPSF->params, 0.5*modelPSF->params->data.F32[PM_PAR_I0]);
+    float FWHM_MINOR = FWHM_MAJOR * (axes.minor / axes.major);
+
+    pcm->use1Dgauss = true;
+    pcm->sigma = 0.5 * (FWHM_MAJOR + FWHM_MINOR) / 2.35;
+    pcm->nsigma = 2.0;
+
+    pcm->smdata = psImageSmooth_PreAlloc_DataAlloc (source->pixels, pcm->sigma, pcm->nsigma);
+# else
+    pcm->smdata = NULL;
+# endif
+
     return pcm;
 }
@@ -364,6 +398,68 @@
 	    pcm->dmodelsConvFlux->data[n] = psImageCopy (pcm->dmodelsConvFlux->data[n], source->pixels, PS_TYPE_F32);
 	}
+	psFree(pcm->smdata);
+	pcm->smdata = psImageSmooth_PreAlloc_DataAlloc (source->pixels, pcm->sigma, pcm->nsigma);
     }
 
     return true;
 }
+
+// construct a realization of the source model
+bool pmPCMCacheModel (pmSource *source, psImageMaskType maskVal, int psfSize) {
+
+    PS_ASSERT_PTR_NON_NULL(source, false);
+
+    // select appropriate model
+    pmModel *model = pmSourceGetModel (NULL, source);
+    if (model == NULL) return false;  // model must be defined
+
+    // if we already have a cached image, re-use that memory
+    source->modelFlux = psImageCopy (source->modelFlux, source->pixels, PS_TYPE_F32);
+    psImageInit (source->modelFlux, 0.0);
+
+    // modelFlux always has unity normalization (I0 = 1.0)
+    pmModelAdd (source->modelFlux, source->maskObj, model, PM_MODEL_OP_FULL | PM_MODEL_OP_NORM, maskVal);
+
+    // convolve the model image with the PSF
+    if (USE_1D_GAUSS) {
+	// do not use the threaded, mask-aware version of this code (psImageSmoothMaskPixelsThread):
+	// * the model flux is not masked
+	// * threading takes place above this level
+	
+	// define the Gauss parameters from the psf
+	pmModel *modelPSF = source->modelPSF;
+	psAssert (modelPSF, "psf model must be defined");
+    
+	psEllipseShape shape;
+	psEllipseAxes axes;
+
+	shape.sx  = modelPSF->params->data.F32[PM_PAR_SXX];
+	shape.sy  = modelPSF->params->data.F32[PM_PAR_SYY];
+	shape.sxy = modelPSF->params->data.F32[PM_PAR_SXY];
+	axes = psEllipseShapeToAxes (shape, 20.0);
+    
+	float FWHM_MAJOR = 2*modelPSF->modelRadius (modelPSF->params, 0.5*modelPSF->params->data.F32[PM_PAR_I0]);
+	float FWHM_MINOR = FWHM_MAJOR * (axes.minor / axes.major);
+
+	float sigma = 0.5 * (FWHM_MAJOR + FWHM_MINOR) / 2.35;
+	float nsigma = 2.0;
+
+	psImageSmooth (source->modelFlux, sigma, nsigma);
+    } else {
+	// make sure we save a cached copy of the psf flux
+	pmSourceCachePSF (source, maskVal);
+
+	// convert the cached cached psf model for this source to a psKernel
+	psKernel *psf = pmPCMkernelFromPSF (source, psfSize);
+	if (!psf) {
+	    // NOTE: this only happens if the source is too close to an edge
+	    model->flags |= PM_MODEL_STATUS_BADARGS;
+	    return NULL;
+	}
+
+	// XXX not sure if I can place the output on top of the input
+	psImageConvolveFFT (source->modelFlux, source->modelFlux, NULL, 0, psf);
+    }
+    return true;
+}
+
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmPCMdata.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmPCMdata.h	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmPCMdata.h	(revision 33415)
@@ -35,4 +35,10 @@
     int nPar;
     int nDOF;
+
+    bool use1Dgauss;
+    float sigma;
+    float nsigma;
+
+    psImageSmooth_PreAlloc_Data *smdata;
 } pmPCMdata;
 
@@ -90,4 +96,5 @@
 bool pmSourceFitPCM (pmPCMdata *pcm, pmSource *source, pmSourceFitOptions *fitOptions, psImageMaskType maskVal, psImageMaskType markVal, int psfSize);
 
+bool pmPCMCacheModel (pmSource *source, psImageMaskType maskVal, int psfSize);
 
 /// @}
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmPSF.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmPSF.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmPSF.c	(revision 33415)
@@ -281,4 +281,5 @@
 // convert the parameters used in the fitted source model
 // to the parameters used in the 2D PSF model
+// XXX this function may be invalid for SERSIC, DEV, EXP models (SQRT2 not used?)
 bool pmPSF_FitToModel (psF32 *fittedPar, float minMinorAxis)
 {
@@ -307,4 +308,5 @@
 // convert the PSF parameters used in the 2D PSF model fit into the
 // parameters used in the source model
+// XXX this function may be invalid for SERSIC, DEV, EXP models (SQRT2 not used?)
 psEllipsePol pmPSF_ModelToFit (psF32 *modelPar)
 {
@@ -329,5 +331,5 @@
 // convert the parameters used in the fitted source model to the psEllipseAxes representation
 // (major,minor,theta)
-psEllipseAxes pmPSF_ModelToAxes (psF32 *modelPar, double maxAR)
+psEllipseAxes pmPSF_ModelToAxes (psF32 *modelPar, double maxAR, pmModelType type)
 {
     psEllipseShape shape;
@@ -336,10 +338,21 @@
     axes.minor = NAN;
     axes.theta = NAN;
-//   XXX: must assert non-NULL input parameter
+    //   XXX: must assert non-NULL input parameter
     PS_ASSERT_PTR_NON_NULL(modelPar, axes);
 
-    shape.sx  = modelPar[PM_PAR_SXX] / M_SQRT2;
-    shape.sy  = modelPar[PM_PAR_SYY] / M_SQRT2;
-    shape.sxy = modelPar[PM_PAR_SXY];
+    bool useReff = true;
+    useReff |= (type == pmModelClassGetType ("PS_MODEL_SERSIC"));
+    useReff |= (type == pmModelClassGetType ("PS_MODEL_DEV"));
+    useReff |= (type == pmModelClassGetType ("PS_MODEL_EXP"));
+
+    if (useReff) {
+	shape.sx  = modelPar[PM_PAR_SXX];
+	shape.sy  = modelPar[PM_PAR_SYY];
+	shape.sxy = modelPar[PM_PAR_SXY];
+    } else {
+	shape.sx  = modelPar[PM_PAR_SXX] / M_SQRT2;
+	shape.sy  = modelPar[PM_PAR_SYY] / M_SQRT2;
+	shape.sxy = modelPar[PM_PAR_SXY];
+    }
 
     if ((shape.sx == 0) || (shape.sy == 0)) {
@@ -357,5 +370,5 @@
 // convert the psEllipseAxes representation (major,minor,theta) to the parameters used in the
 // fitted source model
-bool pmPSF_AxesToModel (psF32 *modelPar, psEllipseAxes axes)
+bool pmPSF_AxesToModel (psF32 *modelPar, psEllipseAxes axes, pmModelType type)
 {
     PS_ASSERT_PTR_NON_NULL(modelPar, false);
@@ -370,8 +383,18 @@
     psEllipseShape shape = psEllipseAxesToShape (axes);
 
-    modelPar[PM_PAR_SXX] = shape.sx * M_SQRT2;
-    modelPar[PM_PAR_SYY] = shape.sy * M_SQRT2;
-    modelPar[PM_PAR_SXY] = shape.sxy;
-
+    bool useReff = true;
+    useReff |= pmModelClassGetType ("PS_MODEL_SERSIC");
+    useReff |= pmModelClassGetType ("PS_MODEL_DEV");
+    useReff |= pmModelClassGetType ("PS_MODEL_EXP");
+
+    if (useReff) {
+	modelPar[PM_PAR_SXX] = shape.sx;
+	modelPar[PM_PAR_SYY] = shape.sy;
+	modelPar[PM_PAR_SXY] = shape.sxy;
+    } else {
+	modelPar[PM_PAR_SXX] = shape.sx * M_SQRT2;
+	modelPar[PM_PAR_SYY] = shape.sy * M_SQRT2;
+	modelPar[PM_PAR_SXY] = shape.sxy;
+    }
     return true;
 }
@@ -438,5 +461,5 @@
 # if (0)
     psF32 *params = model->params->data.F32; // Model parameters
-    psEllipseAxes axes = pmPSF_ModelToAxes(params, MAX_AXIS_RATIO); // Ellipse axes
+    psEllipseAxes axes = pmPSF_ModelToAxes(params, MAX_AXIS_RATIO, model->type); // Ellipse axes
 
     // Curiously, the minor axis can be larger than the major axis, so need to check.
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmPSF.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmPSF.h	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmPSF.h	(revision 33415)
@@ -106,9 +106,9 @@
 pmPSF *pmPSFBuildSimple (char *typeName, float sxx, float syy, float sxy, ...);
 
-bool pmPSF_AxesToModel (psF32 *modelPar, psEllipseAxes axes);
+bool pmPSF_AxesToModel (psF32 *modelPar, psEllipseAxes axes, pmModelType type);
 bool pmPSF_FitToModel (psF32 *fittedPar, float minMinorAxis);
 
 psEllipsePol pmPSF_ModelToFit (psF32 *modelPar);
-psEllipseAxes pmPSF_ModelToAxes (psF32 *modelPar, double maxAR);
+psEllipseAxes pmPSF_ModelToAxes (psF32 *modelPar, double maxAR, pmModelType type);
 
 /// Calculate FWHM value from a PSF
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmPeaks.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmPeaks.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmPeaks.c	(revision 33415)
@@ -137,5 +137,9 @@
 *****************************************************************************/
 static void peakFree(pmPeak *tmp)
-{} //
+{
+    if (!tmp) return;
+    psFree (tmp->saddlePoints);
+    return;
+}
 
 pmPeak *pmPeakAlloc(psS32 x,
@@ -162,4 +166,5 @@
     tmp->type = type;
     tmp->footprint = NULL;
+    tmp->saddlePoints = NULL;
 
     psMemSetDeallocator(tmp, (psFreeFunc) peakFree);
@@ -185,4 +190,5 @@
     out->assigned        = in->assigned;
     out->type      	 = in->type;
+    out->footprint       = in->footprint;
 
     return true;
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmPeaks.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmPeaks.h	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmPeaks.h	(revision 33415)
@@ -71,5 +71,6 @@
     bool assigned;                      ///< is peak assigned to a source?
     pmPeakType type;                    ///< Description of peak.
-    pmFootprint *footprint;		///< reference to containing footprint
+    pmFootprint *footprint;		///< reference to containing footprint (just a view, not a memcopy)
+    psArray *saddlePoints;		///< set of saddle points between this peak and near neighbors
 }
 pmPeak;
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSource.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSource.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSource.c	(revision 33415)
@@ -58,4 +58,5 @@
     psFree(tmp->modelEXT);
     psFree(tmp->modelFits);
+    psFree(tmp->extFitPars);
     psFree(tmp->extpars);
     psFree(tmp->moments);
@@ -119,4 +120,6 @@
     source->modelEXT = NULL;
     source->modelFits = NULL;
+    source->extFitPars = NULL;
+
     source->type = PM_SOURCE_TYPE_UNKNOWN;
     source->mode = PM_SOURCE_MODE_DEFAULT;
@@ -136,4 +139,9 @@
     source->apFluxErr 	     = NAN; 
 
+    source->windowRadius     = NAN;
+    source->skyRadius  	     = NAN;
+    source->skyFlux    	     = NAN;
+    source->skySlope   	     = NAN;
+
     source->pixWeightNotBad  = NAN;
     source->pixWeightNotPoor = NAN;
@@ -196,8 +204,11 @@
     // NOTE : because of the const id element, we cannot just assign *source = *in
 
+    source->imageID          = in->imageID;
+
     source->type     	     = in->type;
     source->mode     	     = in->mode;
     source->mode2    	     = in->mode2;
     source->tmpFlags 	     = in->tmpFlags;
+
     source->psfMag     	     = in->psfMag;
     source->psfMagErr 	     = in->psfMagErr;
@@ -210,6 +221,13 @@
     source->apFlux    	     = in->apFlux;
     source->apFluxErr 	     = in->apFluxErr;
+
+    source->windowRadius     = in->windowRadius;
+    source->skyRadius  	     = in->skyRadius;  	
+    source->skyFlux    	     = in->skyFlux;    	
+    source->skySlope   	     = in->skySlope;   	
+
     source->pixWeightNotBad  = in->pixWeightNotBad;
     source->pixWeightNotPoor = in->pixWeightNotPoor;
+
     source->psfChisq         = in->psfChisq;
     source->crNsigma         = in->crNsigma;
@@ -252,4 +270,5 @@
     }
     mySource->region   = srcRegion;
+    mySource->windowRadius = Radius;
 
     return true;
@@ -321,4 +340,5 @@
         mySource->psfImage = NULL;
     }
+    mySource->windowRadius = Radius;
     return extend;
 }
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSource.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSource.h	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSource.h	(revision 33415)
@@ -36,4 +36,8 @@
     PM_SOURCE_TMPF_MOMENTS_MEASURED  = 0x0010,
     PM_SOURCE_TMPF_CANDIDATE_PSFSTAR = 0x0020,
+    PM_SOURCE_TMPF_RADIAL_KEEP       = 0x0040,
+    PM_SOURCE_TMPF_RADIAL_SKIP       = 0x0080,
+    PM_SOURCE_TMPF_PETRO_KEEP        = 0x0100,
+    PM_SOURCE_TMPF_PETRO_SKIP        = 0x0200,
 } pmSourceTmpF;
 
@@ -76,4 +80,5 @@
     pmModel *modelEXT;                  ///< EXT Model fit used for subtraction (parameters and type)
     psArray *modelFits;                 ///< collection of extended source models (best == modelEXT)
+    psArray *extFitPars;		///< extra extended fit parameters
     pmSourceType type;                  ///< Best identification of object.
     pmSourceMode mode;                  ///< analysis flags set for object.
@@ -91,4 +96,9 @@
     float apFlux;                       ///< apFlux corresponding to psfMag or extMag (depending on type)
     float apFluxErr;                    ///< apFluxErr corresponding to psfMag or extMag (depending on type)
+
+    float windowRadius;			///< size of box used for full analysis
+    float skyRadius;			///< radius at which profile hits local sky (or goes flat)
+    float skyFlux;			///< mean flux per pixel in aperture at which profile hits local sky (or goes flat)
+    float skySlope;			///< mean flux slope at which profile hits local sky (or goes flat)
 
     float pixWeightNotBad;              ///< PSF-weighted coverage of unmasked (not BAD) pixels
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceExtendedPars.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceExtendedPars.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceExtendedPars.c	(revision 33415)
@@ -258,2 +258,27 @@
     return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmSourceExtendedFluxFree);
 }
+
+// *** pmSourceExtFitPars describes extra metadata related to an extended fit
+static void pmSourceExtFitParsFree (pmSourceExtFitPars *pars) {
+    return;
+}
+
+pmSourceExtFitPars *pmSourceExtFitParsAlloc (void) {
+
+    pmSourceExtFitPars *pars = (pmSourceExtFitPars *) psAlloc(sizeof(pmSourceExtFitPars));
+    psMemSetDeallocator(pars, (psFreeFunc) pmSourceExtFitParsFree);
+
+    pars->Mxx = NAN;
+    pars->Mxy = NAN;
+    pars->Myy = NAN;
+
+    pars->Mrf    = NAN;
+    pars->Mrh    = NAN;
+
+    pars->apMag  = NAN;
+    pars->krMag  = NAN;
+    pars->psfMag = NAN;
+    pars->peakMag = NAN;
+
+    return pars;
+}
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceExtendedPars.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceExtendedPars.h	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceExtendedPars.h	(revision 33415)
@@ -67,4 +67,19 @@
 } pmSourceExtendedPars;
 
+// additional measurements related to the model fits
+typedef struct {
+    float Mxx;
+    float Mxy;
+    float Myy;
+    
+    float Mrf;
+    float Mrh;
+
+    float apMag;
+    float krMag;
+    float psfMag;
+    float peakMag;
+} pmSourceExtFitPars;
+
 pmSourceRadialFlux *pmSourceRadialFluxAlloc();
 bool psMemCheckSourceRadialFlux(psPtr ptr);
@@ -92,5 +107,5 @@
 bool pmSourceRadialProfileSortPair(psVector *index, psVector *extra);
 
-
+pmSourceExtFitPars *pmSourceExtFitParsAlloc (void);
 
 /// @}
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceFitModel.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceFitModel.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceFitModel.c	(revision 33415)
@@ -176,7 +176,9 @@
         break;
       case PM_SOURCE_FIT_EXT:
-        // EXT model fits all params (except sky)
-        nParams = params->n - 1;
+        // EXT model fits all shape params and Io (not Xo, Yo, sky)
+        nParams = params->n - 3;
         psVectorInit (constraint->paramMask, 0);
+        constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_XPOS] = 1;
+        constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_YPOS] = 1;
         constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_SKY] = 1;
         break;
@@ -193,11 +195,13 @@
 	break;
       case PM_SOURCE_FIT_NO_INDEX:
-        // PSF model only fits Io, index (PAR7) -- only Io for models with < 8 params
+        // PSF model only fits Io, Sxx, Sxy, Syy
 	psVectorInit (constraint->paramMask, 0);
+        constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_XPOS] = 1;
+        constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_YPOS] = 1;
 	constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_SKY] = 1;
         if (params->n == 7) {
-	    nParams = params->n - 1;
+	    nParams = params->n - 3;
 	} else {
-	    nParams = params->n - 2;
+	    nParams = params->n - 4;
 	    constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_7] = 1;
 	}
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceFitPCM.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceFitPCM.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceFitPCM.c	(revision 33415)
@@ -48,6 +48,10 @@
 // convolved model image.
 
+# define TIMING 0
+
 bool pmSourceFitPCM (pmPCMdata *pcm, pmSource *source, pmSourceFitOptions *fitOptions, psImageMaskType maskVal, psImageMaskType markVal, int psfSize) {
     
+    if (TIMING) { psTimerStart ("pmSourceFitPCM"); }
+
     psVector *params  = pcm->modelConv->params;
     psVector *dparams  = pcm->modelConv->dparams;
@@ -63,6 +67,13 @@
 
     psImage *covar = psImageAlloc (params->n, params->n, PS_TYPE_F32);
+    // NOTE : 4 allocs to here
 
+    float t1, t2, t3, t4, t5;
+    if (TIMING) { t1 = psTimerMark ("pmSourceFitPCM"); }
+
+    // NOTE : 996 allocs in here
     bool fitStatus = pmPCM_MinimizeChisq (myMin, covar, params, source, pcm);
+    if (TIMING) { t2 = psTimerMark ("pmSourceFitPCM"); }
+
     for (int i = 0; i < dparams->n; i++) {
         if ((pcm->constraint->paramMask != NULL) && pcm->constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[i])
@@ -76,4 +87,5 @@
     }
     psTrace ("psphot", 4, "niter: %d, chisq: %f", myMin->iter, myMin->value);
+    if (TIMING) { t3 = psTimerMark ("pmSourceFitPCM"); }
 
     // renormalize output model image (generated by fitting process)
@@ -97,4 +109,5 @@
 	pmSourceChisqUnsubtracted (source, pcm->modelConv, maskVal);
     }
+    if (TIMING) { t4 = psTimerMark ("pmSourceFitPCM"); }
 
     // set the model success or failure status
@@ -114,4 +127,9 @@
 
     source->mode |= PM_SOURCE_MODE_FITTED; // XXX is this needed?
+    if (TIMING) { t5 = psTimerMark ("pmSourceFitPCM"); }
+
+     if (TIMING) {
+ 	fprintf (stderr, "nIter: %2d, npix: %5d, t1: %6.4f, t2: %6.4f, t3: %6.4f, t4: %6.4f, t5: %6.4f\n", myMin->iter, pcm->nPix, t1, t2, t3, t4, t5);
+     }
 
     psFree(myMin);
@@ -121,4 +139,5 @@
 }
 
+// XXX deprecate this function or merge with the empirical model
 bool pmSourceModelGuessPCM (pmPCMdata *pcm, pmSource *source, psImageMaskType maskVal, psImageMaskType markVal) {
 
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO.c	(revision 33415)
@@ -57,4 +57,7 @@
 #define BLANK_HEADERS "BLANK.HEADERS"   // Name of metadata in camera configuration containing header names
                                         // for putting values into a blank PHU
+static bool pmReadoutReadXSRC(pmFPAfile *file, char * exttype, psMetadata *hduHeader, psString xsrcname, psArray *sources, long *sourceIndex);
+static bool pmReadoutReadXFIT(pmFPAfile *file, char * exttype, psMetadata *hduHeader, psString xfitname, psArray *sources, long *sourceIndex);
+static bool pmReadoutReadXRAD(pmFPAfile *file, pmReadout *readout, char * exttype, psMetadata *hduHeader, psString xfitname, psArray *sources, long *sourceIndex);
 
 // lookup the EXTNAME values used for table data and image header segments
@@ -569,4 +572,5 @@
 	    PM_SOURCES_WRITE("PS1_V2",    CMF_PS1_V2);
 	    PM_SOURCES_WRITE("PS1_V3",    CMF_PS1_V3);
+	    PM_SOURCES_WRITE("PS1_V4",    CMF_PS1_V4);
 	    PM_SOURCES_WRITE("PS1_SV1",   CMF_PS1_SV1);
 	    PM_SOURCES_WRITE("PS1_DV1",   CMF_PS1_DV1);
@@ -960,5 +964,27 @@
         psString dataname = NULL;
         psString deteffname = NULL;
-        if (!pmSourceIOextnames(&headname, &dataname, &deteffname, NULL, NULL, NULL, file, view)) {
+        psString xsrcname = NULL;
+        psString xfitname = NULL;
+        psString xradname = NULL;
+
+        // determine the output table format. Assume if we need to output extendend source
+        // parameters that they may exist in the input. 
+        // XXX: Perhaps we should use different recipe values.
+        // I.E. EXTENDED_SOURCE_ANALYSIS_READ or something like that
+        psMetadata *recipe = psMetadataLookupMetadata(&status, config->recipes, "PSPHOT");
+        if (!status) {
+	    psError(PS_ERR_UNKNOWN, true, "missing recipe PSPHOT in config data");
+	    return false;
+        }
+        // if this is not TRUE, the output files only contain the psf measurements.
+        bool XSRC_OUTPUT = psMetadataLookupBool(&status, recipe, "EXTENDED_SOURCE_ANALYSIS");
+        bool XFIT_OUTPUT = psMetadataLookupBool(&status, recipe, "EXTENDED_SOURCE_FITS");
+        bool XRAD_OUTPUT = psMetadataLookupBool(&status, recipe, "RADIAL_APERTURES");
+
+        if (!pmSourceIOextnames(&headname, &dataname, &deteffname, 
+                XSRC_OUTPUT ? &xsrcname : NULL, 
+                XFIT_OUTPUT ? &xfitname : NULL, 
+                XRAD_OUTPUT ? &xradname : NULL,
+                file, view)) {
             return false;
         }
@@ -1025,4 +1051,7 @@
                 sources = pmSourcesRead_CMF_PS1_V3 (file->fits, hdu->header);
             }
+            if (!strcmp (exttype, "PS1_V4")) {
+                sources = pmSourcesRead_CMF_PS1_V4 (file->fits, hdu->header);
+            }
             if (!strcmp (exttype, "PS1_SV1")) {
                 sources = pmSourcesRead_CMF_PS1_SV1 (file->fits, hdu->header);
@@ -1034,4 +1063,45 @@
                 sources = pmSourcesRead_CMF_PS1_DV2 (file->fits, hdu->header);
             }
+
+            long *sourceIndex = NULL;
+            if (XSRC_OUTPUT || XFIT_OUTPUT || XRAD_OUTPUT) {
+                long seq_max = -1;
+                for (long i = sources->n -1; i >= 0; i--) {
+                    pmSource *source = sources->data[i];
+                    if (source->seq > seq_max) {
+                        seq_max = source->seq;
+                    }
+                }
+                sourceIndex = psAlloc((seq_max + 1) * sizeof(long));
+                for (long i = 0; i < seq_max; i++) {
+                    sourceIndex[i] = -1;
+                }
+                for (long i = 0; i < sources->n; i++) {
+                    pmSource *source = sources->data[i];
+                    sourceIndex[source->seq] = i;
+                }
+            }
+            if (XSRC_OUTPUT && xsrcname) {
+                if (!pmReadoutReadXSRC(file, exttype, hdu->header, xsrcname, sources, sourceIndex)) {
+                    // XXX: is this an error?
+                    psErrorClear();
+                }
+                psFree(xsrcname);
+            }
+            if (XFIT_OUTPUT && xfitname) {
+                if (!pmReadoutReadXFIT(file, exttype, hdu->header, xfitname, sources, sourceIndex)) {
+                    // XXX: is this an error?
+                    psErrorClear();
+                }
+                psFree(xfitname);
+            }
+            if (XRAD_OUTPUT && xradname) {
+                if (!pmReadoutReadXRAD(file, readout, exttype, hdu->header, xradname, sources, sourceIndex)) {
+                    // XXX: is this an error?
+                    psErrorClear();
+                }
+                psFree(xradname);
+            }
+            psFree(sourceIndex);
 
             if (!pmReadoutReadDetEff(file->fits, readout, deteffname)) {
@@ -1161,3 +1231,86 @@
 }
 
-
+// XXX: We might be able to macroize this and reuse for the other types
+
+static bool pmReadoutReadXSRC(pmFPAfile *file, char *exttype, psMetadata *hduHeader, psString xsrcname, psArray *sources, long *sourceIndex) 
+{
+    if (!psFitsMoveExtName (file->fits, xsrcname)) {
+        psError(PS_ERR_UNKNOWN, false, "cannot find xsrc extension %s in %s", xsrcname, file->filename);
+        return false;
+    }
+
+    psMetadata *tableHeader = psFitsReadHeader(NULL, file->fits); // The FITS header
+    if (!tableHeader) psAbort("cannot read table header");
+
+    char *xtension = psMetadataLookupStr (NULL, tableHeader, "XTENSION");
+    if (!xtension) psAbort("cannot read table type");
+    if (strcmp (xtension, "BINTABLE")) {
+        psWarning ("no binary table in extension %s, skipping\n", xsrcname);
+        return false;
+    }
+
+    // XXX these are case-sensitive since the EXTYPE is case-sensitive
+    bool status = false;
+    if (file->type == PM_FPA_FILE_CMF) {
+        if (!strcmp (exttype, "PS1_SV1")) {
+            status  = pmSourcesRead_CMF_PS1_SV1_XSRC (file->fits, hduHeader, sources, sourceIndex);
+        }
+    }
+    psFree(tableHeader);
+    return status;
+}
+
+static bool pmReadoutReadXFIT(pmFPAfile *file, char *exttype, psMetadata *hduHeader, psString extname, psArray *sources, long *sourceIndex) 
+{
+    if (!psFitsMoveExtName (file->fits, extname)) {
+        psError(PS_ERR_UNKNOWN, false, "cannot find extension %s in %s", extname, file->filename);
+        return false;
+    }
+
+    psMetadata *tableHeader = psFitsReadHeader(NULL, file->fits); // The FITS header
+    if (!tableHeader) psAbort("cannot read table header");
+
+    char *xtension = psMetadataLookupStr (NULL, tableHeader, "XTENSION");
+    if (!xtension) psAbort("cannot read table type");
+    if (strcmp (xtension, "BINTABLE")) {
+        psWarning ("no binary table in extension %s, skipping\n", extname);
+        return false;
+    }
+
+    // XXX these are case-sensitive since the EXTYPE is case-sensitive
+    bool status = false;
+    if (file->type == PM_FPA_FILE_CMF) {
+        if (!strcmp (exttype, "PS1_SV1")) {
+            status  = pmSourcesRead_CMF_PS1_SV1_XFIT (file->fits, hduHeader, sources, sourceIndex);
+        }
+    }
+    psFree(tableHeader);
+    return status;
+}
+static bool pmReadoutReadXRAD(pmFPAfile *file, pmReadout *readout, char *exttype, psMetadata *hduHeader, psString extname, psArray *sources, long *sourceIndex) 
+{
+    if (!psFitsMoveExtName (file->fits, extname)) {
+        psError(PS_ERR_UNKNOWN, false, "cannot find extension %s in %s", extname, file->filename);
+        return false;
+    }
+
+    psMetadata *tableHeader = psFitsReadHeader(NULL, file->fits); // The FITS header
+    if (!tableHeader) psAbort("cannot read table header");
+
+    char *xtension = psMetadataLookupStr (NULL, tableHeader, "XTENSION");
+    if (!xtension) psAbort("cannot read table type");
+    if (strcmp (xtension, "BINTABLE")) {
+        psWarning ("no binary table in extension %s, skipping\n", extname);
+        return false;
+    }
+
+    // XXX these are case-sensitive since the EXTYPE is case-sensitive
+    bool status = false;
+    if (file->type == PM_FPA_FILE_CMF) {
+        if (!strcmp (exttype, "PS1_SV1")) {
+            status  = pmSourcesRead_CMF_PS1_SV1_XRAD (file->fits, readout, hduHeader, sources, sourceIndex);
+        }
+    }
+    psFree(tableHeader);
+    return status;
+}
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO.h	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO.h	(revision 33415)
@@ -62,4 +62,9 @@
 bool pmSourcesWrite_CMF_PS1_V3_XRAD(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
 
+bool pmSourcesWrite_CMF_PS1_V4(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_CMF_PS1_V4_XSRC(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_CMF_PS1_V4_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname);
+bool pmSourcesWrite_CMF_PS1_V4_XRAD(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
+
 bool pmSourcesWrite_CMF_PS1_SV1(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe);
 bool pmSourcesWrite_CMF_PS1_SV1_XSRC(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
@@ -86,5 +91,9 @@
 psArray *pmSourcesRead_CMF_PS1_V2 (psFits *fits, psMetadata *header);
 psArray *pmSourcesRead_CMF_PS1_V3 (psFits *fits, psMetadata *header);
+psArray *pmSourcesRead_CMF_PS1_V4 (psFits *fits, psMetadata *header);
 psArray *pmSourcesRead_CMF_PS1_SV1 (psFits *fits, psMetadata *header);
+bool pmSourcesRead_CMF_PS1_SV1_XSRC (psFits *fits, psMetadata *header, psArray *sources, long *);
+bool pmSourcesRead_CMF_PS1_SV1_XFIT (psFits *fits, psMetadata *header, psArray *sources, long *);
+bool pmSourcesRead_CMF_PS1_SV1_XRAD (psFits *fits, pmReadout *readout, psMetadata *header, psArray *sources, long *);
 psArray *pmSourcesRead_CMF_PS1_DV1 (psFits *fits, psMetadata *header);
 psArray *pmSourcesRead_CMF_PS1_DV2 (psFits *fits, psMetadata *header);
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF.c.in
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF.c.in	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF.c.in	(revision 33415)
@@ -55,5 +55,4 @@
 // followed by a zero-size matrix, followed by the table data
 
-// # define MODE @CMFMODE@
 bool pmSourcesWrite_CMF_@CMFMODE@ (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe)
 {
@@ -128,5 +127,5 @@
 
         @ALL@     psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG",           PS_DATA_F32, "magnitude in standard aperture",             source->apMag);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RAW",       PS_DATA_F32, "magnitude in reported aperture",             source->apMagRaw);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RAW",       PS_DATA_F32, "magnitude in reported aperture",             source->apMagRaw);
         @ALL@     psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RADIUS",    PS_DATA_F32, "radius used for aperture mags",              outputs.apRadius);
         @<PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "PEAK_FLUX_AS_MAG", PS_DATA_F32, "Peak flux expressed as magnitude",           outputs.peakMag);
@@ -139,5 +138,5 @@
         @>PS1_V1@ psMetadataAdd (row, PS_LIST_TAIL, "DEC_PSF",          PS_DATA_F64, "PSF DEC coordinate (degrees)",               outputs.dec);
 
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "PEAK_FLUX_AS_MAG", PS_DATA_F32, "Peak flux expressed as magnitude",           outputs.peakMag);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "PEAK_FLUX_AS_MAG", PS_DATA_F32, "Peak flux expressed as magnitude",           outputs.peakMag);
         @ALL@     psMetadataAdd (row, PS_LIST_TAIL, "SKY",              PS_DATA_F32, "Sky level",                                  source->sky);
         @ALL@     psMetadataAdd (row, PS_LIST_TAIL, "SKY_SIGMA",        PS_DATA_F32, "Sigma of sky level",                         source->skyErr);
@@ -151,5 +150,5 @@
         @ALL@     psMetadataAdd (row, PS_LIST_TAIL, "PSF_THETA",        PS_DATA_F32, "PSF orientation angle",                      outputs.psfTheta);
         @ALL@     psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor (bad)",          source->pixWeightNotBad);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF_PERFECT",   PS_DATA_F32, "PSF coverage/quality factor (poor)",         source->pixWeightNotPoor);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF_PERFECT",   PS_DATA_F32, "PSF coverage/quality factor (poor)",         source->pixWeightNotPoor);
         @ALL@     psMetadataAdd (row, PS_LIST_TAIL, "PSF_NDOF",         PS_DATA_S32, "degrees of freedom",                         outputs.nDOF);
         @ALL@     psMetadataAdd (row, PS_LIST_TAIL, "PSF_NPIX",         PS_DATA_S32, "number of pixels in fit",                    outputs.nPix);
@@ -159,22 +158,23 @@
         @ALL@     psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_YY",       PS_DATA_F32, "second moments (Y*Y)",                       moments.Myy);
 
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M3C",      PS_DATA_F32, "third momemt cos theta",                     moments.M_c3);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M3S",      PS_DATA_F32, "third momemt sin theta",                     moments.M_s3);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M4C",      PS_DATA_F32, "fourth momemt cos theta",                    moments.M_c4);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M4S",      PS_DATA_F32, "fourth momemt sin theta",                    moments.M_s4);
-
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_R1",       PS_DATA_F32, "first radial moment",                        moments.Mrf);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_RH",       PS_DATA_F32, "half radial moment",                         moments.Mrh);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX",        PS_DATA_F32, "Kron Flux (in 2.5 R1)",                      moments.Krf);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_ERR",    PS_DATA_F32, "Kron Flux Error",                            moments.dKrf);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_INNER",  PS_DATA_F32, "Kron Flux (in 2.5 R1)",                      moments.Kinner);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_OUTER",  PS_DATA_F32, "Kron Flux (in 2.5 R1)",                      moments.Kouter);
-
-	// Do NOT write these : not consistent with the definition of PS1_V3 in Ohana/src/libautocode/dev/cmf-ps1-v3.d
-        // psMetadataAdd (row, PS_LIST_TAIL, "KRON_CORE_FLUX",   PS_DATA_F32, "Kron Flux (in 1.0 R1)",                      moments.KronCore);
-	// psMetadataAdd (row, PS_LIST_TAIL, "KRON_CORE_ERROR",  PS_DATA_F32, "Kron Error (in 1.0 R1)",                     moments.KronCoreErr);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M3C",      PS_DATA_F32, "third momemt cos theta",                     moments.M_c3);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M3S",      PS_DATA_F32, "third momemt sin theta",                     moments.M_s3);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M4C",      PS_DATA_F32, "fourth momemt cos theta",                    moments.M_c4);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M4S",      PS_DATA_F32, "fourth momemt sin theta",                    moments.M_s4);
+
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_R1",       PS_DATA_F32, "first radial moment",                        moments.Mrf);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_RH",       PS_DATA_F32, "half radial moment",                         moments.Mrh);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX",        PS_DATA_F32, "Kron Flux (in 2.5 R1)",                      moments.Krf);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_ERR",    PS_DATA_F32, "Kron Flux Error",                            moments.dKrf);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_INNER",  PS_DATA_F32, "Kron Flux (in 2.5 R1)",                      moments.Kinner);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_OUTER",  PS_DATA_F32, "Kron Flux (in 2.5 R1)",                      moments.Kouter);
+
+        @>PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "SKY_LIMIT_RAD",    PS_DATA_F32, "Radius where object hits sky",               source->skyRadius);
+        @>PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "SKY_LIMIT_FLUX",   PS_DATA_F32, "Flux / pix where object hits sky",           source->skyFlux);
+        @>PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "SKY_LIMIT_SLOPE",  PS_DATA_F32, "d(Flux/pix)/dRadius where object hits sky",  source->skySlope);
+
         @ALL@     psMetadataAdd (row, PS_LIST_TAIL, "FLAGS",            PS_DATA_U32, "psphot analysis flags",                      source->mode);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "FLAGS2",           PS_DATA_U32, "psphot analysis flags",                      source->mode2);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "PADDING2",         PS_DATA_S32, "more padding", 0);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "FLAGS2",           PS_DATA_U32, "psphot analysis flags",                      source->mode2);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "PADDING2",         PS_DATA_S32, "more padding", 0);
 
         // XXX not sure how to get this : need to load Nimages with weight?
@@ -222,5 +222,5 @@
 
 // read in a readout from the fits file
-psArray *pmSourcesRead_CMF_PS1_V3 (psFits *fits, psMetadata *header)
+psArray *pmSourcesRead_CMF_@CMFMODE@ (psFits *fits, psMetadata *header)
 {
     PS_ASSERT_PTR_NON_NULL(fits, false);
@@ -281,6 +281,11 @@
         // XXX use these to determine PAR[PM_PAR_I0]?
         @ALL@     source->psfMag    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG");
-        @ALL@     source->psfMagErr    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG_SIG");
+        @ALL@     source->psfMagErr = psMetadataLookupF32 (&status, row, "PSF_INST_MAG_SIG");
         @ALL@     source->apMag     = psMetadataLookupF32 (&status, row, "AP_MAG");
+        @>PS1_V2@ source->apMagRaw  = psMetadataLookupF32 (&status, row, "AP_MAG_RAW");
+
+        // XXX use these to determine PAR[PM_PAR_I0] if they exist?
+        @>PS1_V2@ source->psfFlux   = psMetadataLookupF32 (&status, row, "PSF_INST_FLUX");
+        @>PS1_V2@ source->psfFluxErr= psMetadataLookupF32 (&status, row, "PSF_INST_FLUX_SIG");
 
         // XXX this scaling is incorrect: does not include the 2 \pi AREA factor
@@ -288,5 +293,5 @@
         @ALL@     dPAR[PM_PAR_I0]   = (isfinite(source->psfMag)) ? PAR[PM_PAR_I0] * source->psfMagErr : NAN;
 
-        pmPSF_AxesToModel (PAR, axes);
+        pmPSF_AxesToModel (PAR, axes, modelType);
 
         @ALL@     float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
@@ -303,5 +308,5 @@
 
         @ALL@     source->pixWeightNotBad = psMetadataLookupF32 (&status, row, "PSF_QF");
-        @=PS1_V3@ source->pixWeightNotPoor = psMetadataLookupF32 (&status, row, "PSF_QF_PERFECT");
+        @>PS1_V2@ source->pixWeightNotPoor = psMetadataLookupF32 (&status, row, "PSF_QF_PERFECT");
         @ALL@     source->crNsigma  = psMetadataLookupF32 (&status, row, "CR_NSIGMA");
         @ALL@     source->extNsigma = psMetadataLookupF32 (&status, row, "EXT_NSIGMA");
@@ -321,27 +326,31 @@
         @ALL@     source->moments->Myy = psMetadataLookupF32 (&status, row, "MOMENTS_YY");
 
-        @=PS1_V3@ source->moments->Mrf         = psMetadataLookupF32 (&status, row, "MOMENTS_R1");
-        @=PS1_V3@ source->moments->Mrh         = psMetadataLookupF32 (&status, row, "MOMENTS_RH");
-        @=PS1_V3@ source->moments->KronFlux    = psMetadataLookupF32 (&status, row, "KRON_FLUX");
-        @=PS1_V3@ source->moments->KronFluxErr = psMetadataLookupF32 (&status, row, "KRON_FLUX_ERR");
-
-        @=PS1_V3@ source->moments->KronFinner  = psMetadataLookupF32 (&status, row, "KRON_FLUX_INNER");
-        @=PS1_V3@ source->moments->KronFouter  = psMetadataLookupF32 (&status, row, "KRON_FLUX_OUTER");
+        @>PS1_V2@ source->moments->Mrf         = psMetadataLookupF32 (&status, row, "MOMENTS_R1");
+        @>PS1_V2@ source->moments->Mrh         = psMetadataLookupF32 (&status, row, "MOMENTS_RH");
+        @>PS1_V2@ source->moments->KronFlux    = psMetadataLookupF32 (&status, row, "KRON_FLUX");
+        @>PS1_V2@ source->moments->KronFluxErr = psMetadataLookupF32 (&status, row, "KRON_FLUX_ERR");
+
+        @>PS1_V2@ source->moments->KronFinner  = psMetadataLookupF32 (&status, row, "KRON_FLUX_INNER");
+        @>PS1_V2@ source->moments->KronFouter  = psMetadataLookupF32 (&status, row, "KRON_FLUX_OUTER");
+
+        @>PS1_V3@ source->skyRadius            = psMetadataLookupF32 (&status, row, "SKY_LIMIT_RAD");
+        @>PS1_V3@ source->skyFlux              = psMetadataLookupF32 (&status, row, "SKY_LIMIT_FLUX");
+        @>PS1_V3@ source->skySlope             = psMetadataLookupF32 (&status, row, "SKY_LIMIT_SLOPE");
 
 	// XXX we do not save all of the 3rd and 4th moment parameters. when we load in data,
 	// we are storing enough information so the output will be consistent with the input
-        @=PS1_V3@ source->moments->Mxxx = +1.0 * psMetadataLookupF32 (&status, row, "MOMENTS_M3C");
-        @=PS1_V3@ source->moments->Mxxy = 0.0;
-        @=PS1_V3@ source->moments->Mxyy = 0.0;
-        @=PS1_V3@ source->moments->Myyy = -1.0 * psMetadataLookupF32 (&status, row, "MOMENTS_M3S");
-
-        @=PS1_V3@ source->moments->Mxxxx = +1.00 * psMetadataLookupF32 (&status, row, "MOMENTS_M4C");
-        @=PS1_V3@ source->moments->Mxxxy = 0.0;
-        @=PS1_V3@ source->moments->Mxxyy = 0.0;
-        @=PS1_V3@ source->moments->Mxyyy = -0.25 * psMetadataLookupF32 (&status, row, "MOMENTS_M4S");
-        @=PS1_V3@ source->moments->Myyyy = 0.0;
+        @>PS1_V2@ source->moments->Mxxx = +1.0 * psMetadataLookupF32 (&status, row, "MOMENTS_M3C");
+        @>PS1_V2@ source->moments->Mxxy = 0.0;
+        @>PS1_V2@ source->moments->Mxyy = 0.0;
+        @>PS1_V2@ source->moments->Myyy = -1.0 * psMetadataLookupF32 (&status, row, "MOMENTS_M3S");
+
+        @>PS1_V2@ source->moments->Mxxxx = +1.00 * psMetadataLookupF32 (&status, row, "MOMENTS_M4C");
+        @>PS1_V2@ source->moments->Mxxxy = 0.0;
+        @>PS1_V2@ source->moments->Mxxyy = 0.0;
+        @>PS1_V2@ source->moments->Mxyyy = -0.25 * psMetadataLookupF32 (&status, row, "MOMENTS_M4S");
+        @>PS1_V2@ source->moments->Myyyy = 0.0;
 
         @ALL@     source->mode = psMetadataLookupU32 (&status, row, "FLAGS");
-        @=PS1_V3@ source->mode2 = psMetadataLookupU32 (&status, row, "FLAGS2");
+        @>PS1_V2@ source->mode2 = psMetadataLookupU32 (&status, row, "FLAGS2");
         assert (status);
 
@@ -353,5 +362,5 @@
 }
 
-bool pmSourcesWrite_CMF_PS1_V3_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
+bool pmSourcesWrite_CMF_@CMFMODE@_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
 {
     bool status;
@@ -541,5 +550,5 @@
 
 // XXX this layout is still the same as PS1_DEV_1
-bool pmSourcesWrite_CMF_PS1_V3_XFIT (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname)
+bool pmSourcesWrite_CMF_@CMFMODE@_XFIT (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname)
 {
 
@@ -590,4 +599,7 @@
             assert (model);
 
+            // pmSourceExtFitPars *extPars = source->extFitPars->data[j];
+	    // assert (extPars);
+
 	    // skip models which were not actually fitted
 	    if (model->flags & PM_MODEL_STATUS_BADARGS) continue;
@@ -600,5 +612,8 @@
             yErr = dPAR[PM_PAR_YPOS];
 
-            axes = pmPSF_ModelToAxes (PAR, 20.0);
+            axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
+
+	    float kronFlux = source->moments ? source->moments->KronFlux : NAN;
+	    float kronMag = isfinite(kronFlux) ? -2.5*log10(kronFlux) : NAN;
 
             row = psMetadataAlloc ();
@@ -612,4 +627,14 @@
             psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_INST_MAG",     0, "EXT fit instrumental magnitude",             model->mag);
             psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_INST_MAG_SIG", 0, "Sigma of PSF instrumental magnitude",        model->magErr);
+
+            // psMetadataAddF32 (row, PS_LIST_TAIL, "MOMENTS_XX",       0, "second moment in x",                      extPars->Mxx);
+            // psMetadataAddF32 (row, PS_LIST_TAIL, "MOMENTS_XY",       0, "second moment in x,y",                    extPars->Mxy);
+            // psMetadataAddF32 (row, PS_LIST_TAIL, "MOMENTS_YY",       0, "second moment in y",                      extPars->Myy);
+            // psMetadataAddF32 (row, PS_LIST_TAIL, "MOMENTS_R1",       0, "first radial moment",                     extPars->Mrf);
+            // psMetadataAddF32 (row, PS_LIST_TAIL, "MOMENTS_RH",       0, "half radial moment",                      extPars->Mrh);
+
+            psMetadataAddF32 (row, PS_LIST_TAIL, "PSF_INST_MAG",     0, "PSF fit instrumental magnitude",             source->psfMag);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "AP_MAG",           0, "PSF-sized aperture magnitude",               source->apMag);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "KRON_MAG",         0, "Kron Mag",                                   kronMag);
 
             psMetadataAddF32 (row, PS_LIST_TAIL, "NPARAMS",          0, "number of model parameters",                 model->params->n);
@@ -673,5 +698,5 @@
 }
 
-bool pmSourcesWrite_CMF_PS1_V3_XRAD(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
+bool pmSourcesWrite_CMF_@CMFMODE@_XRAD(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
 {
     return true;
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_DV1.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_DV1.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_DV1.c	(revision 33415)
@@ -263,5 +263,5 @@
         dPAR[PM_PAR_I0]   = (isfinite(source->psfMag)) ? PAR[PM_PAR_I0] * source->psfMagErr : NAN;
 
-        pmPSF_AxesToModel (PAR, axes);
+        pmPSF_AxesToModel (PAR, axes, modelType);
 
         float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
@@ -547,5 +547,5 @@
             yErr = dPAR[PM_PAR_YPOS];
 
-            axes = pmPSF_ModelToAxes (PAR, 20.0);
+            axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 
             row = psMetadataAlloc ();
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_DV2.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_DV2.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_DV2.c	(revision 33415)
@@ -281,5 +281,5 @@
         dPAR[PM_PAR_I0]   = (isfinite(source->psfMag)) ? PAR[PM_PAR_I0] * source->psfMagErr : NAN;
 
-        pmPSF_AxesToModel (PAR, axes);
+        pmPSF_AxesToModel (PAR, axes, modelType);
 
         float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
@@ -572,5 +572,5 @@
             yErr = dPAR[PM_PAR_YPOS];
 
-            axes = pmPSF_ModelToAxes (PAR, 20.0);
+            axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 
             row = psMetadataAlloc ();
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_SV1.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_SV1.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_SV1.c	(revision 33415)
@@ -280,5 +280,5 @@
         dPAR[PM_PAR_I0]   = (isfinite(source->psfMag)) ? PAR[PM_PAR_I0] * source->psfMagErr : NAN;
 
-        pmPSF_AxesToModel (PAR, axes);
+        pmPSF_AxesToModel (PAR, axes, modelType);
 
         float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
@@ -539,4 +539,98 @@
 }
 
+bool pmSourcesRead_CMF_PS1_SV1_XSRC(psFits *fits, psMetadata *hduHeader, psArray *sources, long *sourceIndex)
+{
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+
+    bool status;
+    long numSources = psFitsTableSize(fits); // Number of sources in table
+    if (numSources == 0) {
+        psError(psErrorCodeLast(), false, "XSRC Table contains no entries\n");
+        return false;
+    }
+
+    // petrosian mags are not saved, we need to calculate fluxes. For this we need exptime and zero point
+    float zeropt = psMetadataLookupF32(&status, hduHeader, "FPA.ZP");
+    float exptime = psMetadataLookupF32(&status, hduHeader, "EXPTIME");
+    float magOffset = zeropt + 2.5*log10(exptime);
+
+    for (long i = 0; i < numSources; i++) {
+        psMetadata *row = psFitsReadTableRow(fits, i); // Table row
+        if (!row) {
+            psError(psErrorCodeLast(), false, "Unable to read row %ld of sources", i);
+            psFree(row);
+            return false;
+        }
+        // Find the source with this sequence number. 
+        // XXX: I am assuming that sources is sorted in order of seq
+        long seq = psMetadataLookupU32 (&status, row, "IPP_IDET");
+        pmSource *source = NULL;
+#ifndef ASSUME_SORTED
+        long j = seq < sources->n ? seq : sources->n - 1;
+        for (; j >= 0; j--) {
+            source = sources->data[j];
+            if (source->seq == seq) {
+                break;
+            }
+        }
+#else
+        long j = sourceIndex[seq];
+        psAssert(j >= 0 && j < sources->n, "invalid sourceIndex");
+        source = sources->data[j];
+#endif
+        if (!source) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to find source for row %ld sequence number %ld\n", i, seq);
+            psFree(row);
+            return false;
+        }
+
+        if (!source->extpars) {
+            source->extpars = pmSourceExtendedParsAlloc ();
+        }
+        pmSourceExtendedPars *extpars = source->extpars;
+
+        // Assume that X_EXT Y_EXT and sigmas match the psf src so skip
+
+        // We don't have enough information to calculate the major and minor axis. Set major to 1. Should we scale this by
+        // psf size or something?
+        extpars->axes.major = 1.0;
+        extpars->axes.minor = extpars->axes.major * psMetadataLookupF32(&status, row, "F25_ARATIO");
+        extpars->axes.theta = psMetadataLookupF32(&status, row, "F25_THETA");
+
+        float mag = psMetadataLookupF32(&status, row, "PETRO_MAG");
+        float magErr = psMetadataLookupF32(&status, row, "PETRO_MAG_ERR");
+        if (isfinite(mag)) {
+            extpars->petrosianFlux    = pow(10., (magOffset - mag) / 2.5);
+            if (isfinite(magErr)) {
+                extpars->petrosianFluxErr = extpars->petrosianFlux / magErr;
+            }
+        }
+
+        extpars->petrosianRadius   = psMetadataLookupF32(&status, row, "PETRO_RADIUS");
+        extpars->petrosianRadiusErr= psMetadataLookupF32(&status, row, "PETRO_RADIUS_ERR");
+        extpars->petrosianR50      = psMetadataLookupF32(&status, row, "PETRO_RADIUS_50");
+        extpars->petrosianR50Err   = psMetadataLookupF32(&status, row, "PETRO_RADIUS_50_ERR");
+        extpars->petrosianR90      = psMetadataLookupF32(&status, row, "PETRO_RADIUS_90");
+        extpars->petrosianR90Err   = psMetadataLookupF32(&status, row, "PETRO_RADIUS_90_ERR");
+        extpars->petrosianFill     = psMetadataLookupF32(&status, row, "PETRO_FILL");
+
+        psVector *radSB   = psMetadataLookupVector(&status, row, "PROF_SB");
+        psVector *radFlux = psMetadataLookupVector(&status, row, "PROF_FLUX");
+        psVector *radFill = psMetadataLookupVector(&status, row, "PROF_FILL");
+
+        if (radSB && radSB->n > 0) {
+            extpars->radProfile = pmSourceRadialProfileAlloc();
+            extpars->radProfile->binSB   = psMemIncrRefCounter(radSB);
+            extpars->radProfile->binSum   = psMemIncrRefCounter(radFlux);
+            extpars->radProfile->binFill = psMemIncrRefCounter(radFill);
+        }
+
+        psFree(row);
+    }
+
+    return true;
+}
+
 // XXX this layout is still the same as PS1_DEV_1
 bool pmSourcesWrite_CMF_PS1_SV1_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname)
@@ -607,5 +701,5 @@
             yErr = dPAR[PM_PAR_YPOS];
 
-            axes = pmPSF_ModelToAxes (PAR, 20.0);
+            axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 
             row = psMetadataAlloc ();
@@ -684,4 +778,104 @@
     psFree (outhead);
     psFree (table);
+    return true;
+}
+
+bool pmSourcesRead_CMF_PS1_SV1_XFIT(psFits *fits, psMetadata *hduHeader, psArray *sources, long *sourceIndex)
+{
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+
+    bool status;
+    long numSources = psFitsTableSize(fits); // Number of sources in table
+    if (numSources == 0) {
+        psError(psErrorCodeLast(), false, "XFIT Table contains no entries\n");
+        return false;
+    }
+
+    for (long i = 0; i < numSources; i++) {
+        psMetadata *row = psFitsReadTableRow(fits, i); // Table row
+        if (!row) {
+            psError(psErrorCodeLast(), false, "Unable to read row %ld of sources", i);
+            psFree(row);
+            return false;
+        }
+        // Find the source with this sequence number. 
+        // XXX: I am assuming that sources is sorted in order of seq.
+        long seq = psMetadataLookupU32 (&status, row, "IPP_IDET");
+        long j = seq < sources->n ? seq : sources->n - 1;
+        pmSource *source = NULL;
+        for (; j >= 0; j--) {
+            source = sources->data[j];
+            if (source->seq == seq) {
+                break;
+            }
+        }
+        if (!source) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to find source for row %ld sequence number %ld\n", i, seq);
+            psFree(row);
+            return false;
+        }
+        if (!source->modelFits) {
+            // XXX: where to find the number of models to expect?
+            source->modelFits = psArrayAllocEmpty(5);
+        }
+        psString modelName = psMetadataLookupStr(&status, row, "MODEL_TYPE");
+        if (!modelName) {
+            psError(PS_ERR_UNKNOWN, true, "Failed to find model name for row %ld\n", i);
+            psFree(row);
+            return false;
+        }
+        pmModelType modelType = pmModelClassGetType(modelName);
+        if (modelType < 0) {
+            psError(PS_ERR_UNKNOWN, true, "Failed to find model type for %s\n", modelName);
+            psFree(row);
+            return false;
+        }
+        pmModel *model = pmModelAlloc(modelType);
+
+        psF32 *PAR = model->params->data.F32;
+        psF32 *dPAR = model->dparams->data.F32;
+
+        PAR[PM_PAR_XPOS] = psMetadataLookupF32(&status, row, "X_EXT");
+        PAR[PM_PAR_YPOS] = psMetadataLookupF32(&status, row, "Y_EXT");
+        dPAR[PM_PAR_XPOS] = psMetadataLookupF32(&status, row, "X_EXT_SIG");
+        dPAR[PM_PAR_YPOS] = psMetadataLookupF32(&status, row, "Y_EXT_SIG");
+
+        model->mag = psMetadataLookupF32(&status, row, "EXT_INST_MAG");
+        model->magErr = psMetadataLookupF32(&status, row, "EXT_INST_MAG_SIG");
+
+        psEllipseAxes axes;
+        axes.major = psMetadataLookupF32(&status, row, "EXT_WIDTH_MAJ");
+        axes.minor = psMetadataLookupF32(&status, row, "EXT_WIDTH_MIN");
+        axes.theta = psMetadataLookupF32(&status, row, "EXT_THETA");
+        if (!pmPSF_AxesToModel(PAR, axes, modelType)) {
+            // Do we need to fail here or can this happen?
+            psError(PS_ERR_UNKNOWN, false, "Failed to convert psf axes to model");
+            psFree(model);
+            psFree(row);
+            return false;
+        }
+        // XXX: clean this up
+        if (model->params->n > 7) {
+            PAR[7] = psMetadataLookupF32(&status, row, "EXT_PAR_07");
+        }
+        // read the covariance matrix
+        int nparams = model->params->n;
+        psImage *covar = psImageAlloc(nparams, nparams, PS_TYPE_F32);
+        for (int y = 0; y < nparams; y++) {
+            for (int x = 0; x < nparams; x++) {
+                char name[64];
+                snprintf(name, 64, "EXT_COVAR_%02d_%02d", y, x);
+                covar->data.F32[y][x] = psMetadataLookupF32(&status, row, name);
+            }
+        }
+        model->covar = covar;
+
+        psArrayAdd(source->modelFits, 1, model);
+        psFree(model);
+
+        psFree(row);
+    }
+
     return true;
 }
@@ -831,2 +1025,94 @@
     return true;
 }
+
+bool pmSourcesRead_CMF_PS1_SV1_XRAD(psFits *fits, pmReadout *readout, psMetadata *hduHeader, psArray *sources, long *sourceIndex)
+{
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+
+    bool status;
+    long numSources = psFitsTableSize(fits); // Number of sources in table
+    if (numSources == 0) {
+        psError(psErrorCodeLast(), false, "XRAD Table contains no entries\n");
+        return false;
+    }
+
+    long       seq_first = -1;
+    long       seq_last = -1;
+    psVector   *fwhmValues = psVectorAllocEmpty(10, PS_TYPE_F32);
+    long       max_entries = -1;
+    long       num_entries = -1;
+
+    for (long i = 0; i < numSources; i++) {
+        psMetadata *row = psFitsReadTableRow(fits, i); // Table row
+        if (!row) {
+            psError(psErrorCodeLast(), false, "Unable to read row %ld of sources", i);
+            psFree(row);
+            return false;
+        }
+        // Find the source with this sequence number. 
+        // XXX: I am assuming that sources is sorted in order of seq.
+        long seq = psMetadataLookupU32 (&status, row, "IPP_IDET");
+        long j = seq < sources->n ? seq : sources->n - 1;
+        pmSource *source = NULL;
+        for (; j >= 0; j--) {
+            source = sources->data[j];
+            if (source->seq == seq) {
+                break;
+            }
+        }
+        if (!source) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to find source for row %ld sequence number %ld\n", i, seq);
+            psFree(row);
+            return false;
+        }
+        if (seq_first == -1) {
+            seq_first = seq;
+        }
+        if (seq == seq_first) {
+            psF32 value = psMetadataLookupF32(&status, row, "PSF_FWHM");
+            psVectorAppend(fwhmValues, value);
+        }
+        if (seq == seq_last) {
+            num_entries++;
+        } else {
+            num_entries = 1;
+            seq_last = seq;
+        }
+        if (num_entries > max_entries) {
+            max_entries = num_entries;
+        }
+
+        if (!source->radialAper) {
+            // XXX: where to find the number of models to expect?
+            source->radialAper = psArrayAllocEmpty(5);
+        }
+        pmSourceRadialApertures *radialAper = pmSourceRadialAperturesAlloc();
+
+        radialAper->flux = psMemIncrRefCounter(psMetadataLookupVector(&status, row, "APER_FLUX"));
+        radialAper->fluxStdev = psMemIncrRefCounter(psMetadataLookupVector(&status, row, "APER_FLUX_STDEV"));
+        radialAper->fluxErr = psMemIncrRefCounter(psMetadataLookupVector(&status, row, "APER_FLUX_ERR"));
+        radialAper->fill = psMemIncrRefCounter(psMetadataLookupVector(&status, row, "APER_FILL"));
+
+        psArrayAdd(source->radialAper, 1, radialAper);
+
+        psFree(radialAper);
+        psFree(row);
+    }
+
+    // check for consistency between the length of fwhmValues and the maximum number of entries for each row
+    if (fwhmValues->n != max_entries) {
+        psError(PS_ERR_PROGRAMMING, true, "number of PSF_FWHM values found %ld does not match expected number: %ld\n",
+            fwhmValues->n, max_entries);
+        psAssert(0, "fixme");
+    }
+
+    if (!readout->analysis) {
+        readout->analysis = psMetadataAlloc();
+    }
+
+    psMetadataAddVector(readout->analysis, PS_LIST_TAIL, "STACK.PSF.FWHM.VALUES", PS_META_REPLACE, "PSF sizes", fwhmValues);
+    psFree(fwhmValues);
+
+    return true;
+}
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_V1.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_V1.c	(revision 32133)
+++ 	(revision )
@@ -1,664 +1,0 @@
-/** @file  pmSourceIO.c
- *
- *  @author EAM, IfA
- *
- *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2009-02-18 02:44:19 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <math.h>
-#include <string.h>
-#include <pslib.h>
-
-#include "pmConfig.h"
-#include "pmDetrendDB.h"
-
-#include "pmHDU.h"
-#include "pmFPA.h"
-#include "pmFPALevel.h"
-#include "pmFPAview.h"
-#include "pmFPAfile.h"
-
-#include "pmTrend2D.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmSpan.h"
-#include "pmFootprintSpans.h"
-#include "pmFootprint.h"
-#include "pmPeaks.h"
-#include "pmMoments.h"
-#include "pmModelFuncs.h"
-#include "pmModel.h"
-#include "pmModelUtils.h"
-#include "pmModelClass.h"
-#include "pmSourceMasks.h"
-#include "pmSourceExtendedPars.h"
-#include "pmSourceDiffStats.h"
-#include "pmSource.h"
-#include "pmSourceFitModel.h"
-#include "pmPSF.h"
-#include "pmPSFtry.h"
-
-#include "pmSourceIO.h"
-#include "pmSourceOutputs.h"
-
-// panstarrs-style FITS table output (header + table in 1st extension)
-// this format consists of a header derived from the image header
-// followed by a zero-size matrix, followed by the table data
-
-bool pmSourcesWrite_CMF_PS1_V1 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe)
-{
-    PS_ASSERT_PTR_NON_NULL(fits, false);
-    PS_ASSERT_PTR_NON_NULL(sources, false);
-    PS_ASSERT_PTR_NON_NULL(extname, false);
-
-    psArray *table;
-    psMetadata *row;
-
-    pmChip *chip = readout->parent->parent;
-
-    // if the sequence is defined, write these in seq order; otherwise
-    // write them in S/N order:
-    if (sources->n > 0) {
-        pmSource *source = (pmSource *) sources->data[0];
-        if (source->seq == -1) {
-          // let's write these out in S/N order
-          sources = psArraySort (sources, pmSourceSortByFlux);
-        } else {
-          sources = psArraySort (sources, pmSourceSortBySeq);
-        }
-    }
-
-    table = psArrayAllocEmpty (sources->n);
-
-    short nImageOverlap; 
-    float magOffset; 
-    float zeroptErr; 
-    float fwhmMajor; 
-    float fwhmMinor;
-    pmSourceOutputsCommonValues (&nImageOverlap, &magOffset, &zeroptErr, &fwhmMajor, &fwhmMinor, readout, imageHeader);
-
-    // we write out PSF-fits for all sources, regardless of quality.  the source flags tell us the state
-    // by the time we call this function, all values should be assigned.  let's use asserts to be sure in some cases.
-    for (int i = 0; i < sources->n; i++) {
-        pmSource *source = (pmSource *) sources->data[i];
-
-        // If source->seq is -1, source was generated in this analysis.  If source->seq is
-        // not -1, source was read from elsewhere: in the latter case, preserve the source
-        // ID.  source.seq is used instead of source.id since the latter is a const
-        // generated on Alloc, and would thus be wrong for read in sources.
-        if (source->seq == -1) {
-          source->seq = i;
-        }
-
-	// set the 'best' values for various output fields:
-	pmSourceOutputs outputs;
-	pmSourceOutputsSetValues (&outputs, source, chip, fwhmMajor, fwhmMinor, magOffset);
-
-	pmSourceOutputsMoments moments;
-	pmSourceOutputsSetMoments (&moments, source);
-
-        row = psMetadataAlloc ();
-        psMetadataAdd (row, PS_LIST_TAIL, "IPP_IDET",         PS_DATA_U32, "IPP detection identifier index",             source->seq);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_PSF",            PS_DATA_F32, "PSF x coordinate",                           outputs.xPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_PSF",            PS_DATA_F32, "PSF y coordinate",                           outputs.yPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_PSF_SIG",        PS_DATA_F32, "Sigma in PSF x coordinate",                  outputs.xErr); // XXX this is only measured for non-linear fits
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_PSF_SIG",        PS_DATA_F32, "Sigma in PSF y coordinate",                  outputs.yErr); // XXX this is only measured for non-linear fits
-        psMetadataAdd (row, PS_LIST_TAIL, "RA_PSF",           PS_DATA_F32, "PSF RA coordinate (degrees)",                outputs.ra);
-        psMetadataAdd (row, PS_LIST_TAIL, "DEC_PSF",          PS_DATA_F32, "PSF DEC coordinate (degrees)",               outputs.dec);
-        psMetadataAdd (row, PS_LIST_TAIL, "POSANGLE",         PS_DATA_F32, "position angle at source (degrees)",         outputs.posAngle);
-        psMetadataAdd (row, PS_LIST_TAIL, "PLTSCALE",         PS_DATA_F32, "plate scale at source (arcsec/pixel)",       outputs.pltScale);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_MAG",     PS_DATA_F32, "PSF fit instrumental magnitude",             source->psfMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_MAG_SIG", PS_DATA_F32, "Sigma of PSF instrumental magnitude",        source->psfMagErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG",           PS_DATA_F32, "magnitude in standard aperture",             source->apMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RADIUS",    PS_DATA_F32, "radius used for aperture mags",              outputs.apRadius);
-        psMetadataAdd (row, PS_LIST_TAIL, "PEAK_FLUX_AS_MAG", PS_DATA_F32, "Peak flux expressed as magnitude",           outputs.peakMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "CAL_PSF_MAG",      PS_DATA_F32, "PSF Magnitude using supplied calibration",   outputs.calMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "CAL_PSF_MAG_SIG",  PS_DATA_F32, "measured scatter of zero point calibration", zeroptErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "SKY",              PS_DATA_F32, "Sky level",                                  source->sky);
-        psMetadataAdd (row, PS_LIST_TAIL, "SKY_SIGMA",        PS_DATA_F32, "Sigma of sky level",                         source->skyErr);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_CHISQ",        PS_DATA_F32, "Chisq of PSF-fit",                           outputs.chisq);
-        psMetadataAdd (row, PS_LIST_TAIL, "CR_NSIGMA",        PS_DATA_F32, "Nsigma deviations from PSF to CF",           source->crNsigma);
-        psMetadataAdd (row, PS_LIST_TAIL, "EXT_NSIGMA",       PS_DATA_F32, "Nsigma deviations from PSF to EXT",          source->extNsigma);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_MAJOR",        PS_DATA_F32, "PSF width (major axis)",                     outputs.psfMajor);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_MINOR",        PS_DATA_F32, "PSF width (minor axis)",                     outputs.psfMinor);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_THETA",        PS_DATA_F32, "PSF orientation angle",                      outputs.psfTheta);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor",                source->pixWeightNotBad);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_NDOF",         PS_DATA_S32, "degrees of freedom",                         outputs.nDOF);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_NPIX",         PS_DATA_S32, "number of pixels in fit",                    outputs.nPix);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_XX",       PS_DATA_F32, "second moments (X^2)",                       moments.Mxx);
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_XY",       PS_DATA_F32, "second moments (X*Y)",                       moments.Mxy);
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_YY",       PS_DATA_F32, "second moments (Y*Y)",                       moments.Myy);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "FLAGS",            PS_DATA_U32, "psphot analysis flags",                      source->mode);
-
-        // XXX not sure how to get this : need to load Nimages with weight?
-        psMetadataAdd (row, PS_LIST_TAIL, "N_FRAMES",         PS_DATA_U16, "Number of frames overlapping source center", nImageOverlap);
-        psMetadataAdd (row, PS_LIST_TAIL, "PADDING",          PS_DATA_S16, "padding", 0);
-
-        psArrayAdd (table, 100, row);
-        psFree (row);
-
-        // EXT_NSIGMA will be NAN if: 1) contour ellipse is imaginary; 2) source is not
-        // subtracted
-
-        // CR_NSIGMA will be NAN if: 1) source is not subtracted; 2) source is on the image
-        // edge; 3) any pixels in the 3x3 peak region are masked;
-    }
-
-    psMetadata *header = psMetadataCopy(NULL, tableHeader);
-    pmSourceMasksHeader(header);
-
-    if (table->n == 0) {
-        if (!psFitsWriteBlank(fits, header, extname)) {
-            psError(psErrorCodeLast(), false, "Unable to write empty sources file.");
-            psFree(table);
-            psFree(header);
-            return false;
-        }
-        psFree(table);
-        psFree(header);
-        return true;
-    }
-
-    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
-    if (!psFitsWriteTable(fits, header, table, extname)) {
-        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
-        psFree(table);
-        psFree(header);
-        return false;
-    }
-    psFree(table);
-    psFree(header);
-
-    return true;
-}
-
-// read in a readout from the fits file
-psArray *pmSourcesRead_CMF_PS1_V1 (psFits *fits, psMetadata *header)
-{
-    PS_ASSERT_PTR_NON_NULL(fits, false);
-    PS_ASSERT_PTR_NON_NULL(header, false);
-
-    bool status;
-    psF32 *PAR, *dPAR;
-    psEllipseAxes axes;
-
-    // define PSF model type
-    // XXX need to carry the extra model parameters
-    int modelType = pmModelClassGetType ("PS_MODEL_GAUSS");
-
-    char *PSF_NAME = psMetadataLookupStr (&status, header, "PSF_NAME");
-    if (PSF_NAME != NULL) {
-        modelType = pmModelClassGetType (PSF_NAME);
-    }
-    assert (modelType > -1);
-
-    // We get the size of the table, and allocate the array of sources first because the table
-    // is large and ephemeral --- when the table gets blown away, whatever is allocated after
-    // the table is read blocks the free.  In fact, it's better to read the table row by row.
-    long numSources = psFitsTableSize(fits); // Number of sources in table
-    psArray *sources = psArrayAlloc(numSources); // Array of sources, to return
-
-    // convert the table to the pmSource entriesa
-    for (int i = 0; i < numSources; i++) {
-        psMetadata *row = psFitsReadTableRow(fits, i); // Table row
-
-        pmSource *source = pmSourceAlloc ();
-        pmModel *model = pmModelAlloc (modelType);
-        source->modelPSF  = model;
-        source->type = PM_SOURCE_TYPE_STAR; // XXX this should be added to the flags
-
-        // NOTE: A SEGV here because "model" is NULL is probably caused by not initialising the models.
-        PAR = model->params->data.F32;
-        dPAR = model->dparams->data.F32;
-
-        source->seq       = psMetadataLookupU32 (&status, row, "IPP_IDET");
-        PAR[PM_PAR_XPOS]  = psMetadataLookupF32 (&status, row, "X_PSF");
-        PAR[PM_PAR_YPOS]  = psMetadataLookupF32 (&status, row, "Y_PSF");
-        dPAR[PM_PAR_XPOS] = psMetadataLookupF32 (&status, row, "X_PSF_SIG");
-        dPAR[PM_PAR_YPOS] = psMetadataLookupF32 (&status, row, "Y_PSF_SIG");
-        axes.major        = psMetadataLookupF32 (&status, row, "PSF_MAJOR");
-        axes.minor        = psMetadataLookupF32 (&status, row, "PSF_MINOR");
-        axes.theta        = psMetadataLookupF32 (&status, row, "PSF_THETA");
-
-        PAR[PM_PAR_SKY]   = psMetadataLookupF32 (&status, row, "SKY");
-        dPAR[PM_PAR_SKY]  = psMetadataLookupF32 (&status, row, "SKY_SIGMA");
-        source->sky       = PAR[PM_PAR_SKY];
-        source->skyErr    = dPAR[PM_PAR_SKY];
-
-        // XXX use these to determine PAR[PM_PAR_I0]?
-        source->psfMag    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG");
-        source->psfMagErr    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG_SIG");
-        source->apMag     = psMetadataLookupF32 (&status, row, "AP_MAG");
-
-        // XXX this scaling is incorrect: does not include the 2 \pi AREA factor
-        PAR[PM_PAR_I0]    = (isfinite(source->psfMag)) ? pow(10.0, -0.4*source->psfMag) : NAN;
-        dPAR[PM_PAR_I0]   = (isfinite(source->psfMag)) ? PAR[PM_PAR_I0] * source->psfMagErr : NAN;
-
-        pmPSF_AxesToModel (PAR, axes);
-
-        float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
-        float peakFlux    = (isfinite(peakMag)) ? pow(10.0, -0.4*peakMag) : NAN;
-
-        // recreate the peak to match (xPos, yPos) +/- (xErr, yErr)
-        source->peak = pmPeakAlloc(PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], peakFlux, PM_PEAK_LONE);
-        source->peak->rawFlux = peakFlux;
-        source->peak->smoothFlux = peakFlux;
-        source->peak->xf   = PAR[PM_PAR_XPOS]; // more accurate position
-        source->peak->yf   = PAR[PM_PAR_YPOS]; // more accurate position
-        source->peak->dx   = dPAR[PM_PAR_XPOS];
-        source->peak->dy   = dPAR[PM_PAR_YPOS];
-
-        source->pixWeightNotBad = psMetadataLookupF32 (&status, row, "PSF_QF");
-        source->crNsigma  = psMetadataLookupF32 (&status, row, "CR_NSIGMA");
-        source->extNsigma = psMetadataLookupF32 (&status, row, "EXT_NSIGMA");
-        source->apRadius  = psMetadataLookupS32 (&status, row, "AP_MAG_RADIUS");
-
-        // note that some older versions used PSF_PROBABILITY: this was not well defined.
-        model->chisq      = psMetadataLookupF32 (&status, row, "PSF_CHISQ");
-        model->nDOF       = psMetadataLookupS32 (&status, row, "PSF_NDOF");
-        model->nPix       = psMetadataLookupS32 (&status, row, "PSF_NPIX");
-
-        source->moments = pmMomentsAlloc ();
-        source->moments->Mx = source->peak->xf; // we don't have both Mx,My and xf,yf in the cmf
-        source->moments->My = source->peak->yf; // we don't have both Mx,My and xf,yf in the cmf
-
-        source->moments->Mxx = psMetadataLookupF32 (&status, row, "MOMENTS_XX");
-        source->moments->Mxy = psMetadataLookupF32 (&status, row, "MOMENTS_XY");
-        source->moments->Myy = psMetadataLookupF32 (&status, row, "MOMENTS_YY");
-
-        source->mode = psMetadataLookupU32 (&status, row, "FLAGS");
-        assert (status);
-
-        sources->data[i] = source;
-        psFree(row);
-    }
-
-    return sources;
-}
-
-// XXX this layout is still the same as PS1_DEV_1
-bool pmSourcesWrite_CMF_PS1_V1_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
-{
-
-    bool status;
-    psArray *table;
-    psMetadata *row;
-    psF32 *PAR, *dPAR;
-    psF32 xPos, yPos;
-    psF32 xErr, yErr;
-
-    // create a header to hold the output data
-    psMetadata *outhead = psMetadataAlloc ();
-
-    // write the links to the image header
-    psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTNAME", PS_META_REPLACE, "xsrc table extension", extname);
-
-    // let's write these out in S/N order
-    sources = psArraySort (sources, pmSourceSortByFlux);
-
-    table = psArrayAllocEmpty (sources->n);
-
-    // which extended source analyses should we perform?
-    // bool doPetrosian    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
-    // bool doIsophotal    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ISOPHOTAL");
-    // bool doAnnuli       = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
-    // bool doKron         = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_KRON");
-
-    psVector *radialBinsLower = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.LOWER");
-    psVector *radialBinsUpper = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.UPPER");
-    assert (radialBinsLower->n == radialBinsUpper->n);
-
-    // we write out all sources, regardless of quality.  the source flags tell us the state
-    for (int i = 0; i < sources->n; i++) {
-        // skip source if it is not a ext sourc
-        // XXX we have two places that extended source parameters are measured:
-        // psphotExtendedSources, which measures the aperture-like parameters and (potentially) the psf-convolved extended source models,
-        // psphotFitEXT, which does the simple extended source model fit (not psf-convolved)
-        // should we require both?
-
-        pmSource *source = sources->data[i];
-
-        // skip sources without measurements
-        if (source->extpars == NULL) continue;
-
-        // we require a PSF model fit (ignore the real crud)
-        pmModel *model = source->modelPSF;
-        if (model == NULL) continue;
-
-        // XXX I need to split the extended models from the extended aperture measurements
-        PAR = model->params->data.F32;
-        dPAR = model->dparams->data.F32;
-        xPos = PAR[PM_PAR_XPOS];
-        yPos = PAR[PM_PAR_YPOS];
-        xErr = dPAR[PM_PAR_XPOS];
-        yErr = dPAR[PM_PAR_YPOS];
-
-        row = psMetadataAlloc ();
-
-        // XXX we are not writing out the mode (flags) or the type (psf, ext, etc)
-        psMetadataAdd (row, PS_LIST_TAIL, "IPP_IDET",         PS_DATA_U32, "IPP detection identifier index",             source->seq);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_EXT",            PS_DATA_F32, "EXT model x coordinate",                     xPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_EXT",            PS_DATA_F32, "EXT model y coordinate",                     yPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_EXT_SIG",        PS_DATA_F32, "Sigma in EXT x coordinate",                  xErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_EXT_SIG",        PS_DATA_F32, "Sigma in EXT y coordinate",                  yErr);
-
-# if (0)
-        // Petrosian measurements
-        // XXX insert header data: petrosian ref radius, flux ratio
-        if (doPetrosian) {
-            pmSourcePetrosianValues *petrosian = source->extpars->petrosian;
-            if (petrosian) {
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG",        PS_DATA_F32, "Petrosian Magnitude",       petrosian->mag);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG_ERR",    PS_DATA_F32, "Petrosian Magnitude Error", petrosian->magErr);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS",     PS_DATA_F32, "Petrosian Radius",          petrosian->rad);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_ERR", PS_DATA_F32, "Petrosian Radius Error",    petrosian->radErr);
-            } else {
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG",        PS_DATA_F32, "Petrosian Magnitude",       NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG_ERR",    PS_DATA_F32, "Petrosian Magnitude Error", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS",     PS_DATA_F32, "Petrosian Radius",          NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_ERR", PS_DATA_F32, "Petrosian Radius Error",    NAN);
-            }
-        }
-
-        // Kron measurements
-        if (doKron) {
-            pmSourceKronValues *kron = source->extpars->kron;
-            if (kron) {
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG",        PS_DATA_F32, "Kron Magnitude",       kron->mag);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG_ERR",    PS_DATA_F32, "Kron Magnitude Error", kron->magErr);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS",     PS_DATA_F32, "Kron Radius",          kron->rad);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS_ERR", PS_DATA_F32, "Kron Radius Error",    kron->radErr);
-            } else {
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG",        PS_DATA_F32, "Kron Magnitude",       NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG_ERR",    PS_DATA_F32, "Kron Magnitude Error", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS",     PS_DATA_F32, "Kron Radius",          NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS_ERR", PS_DATA_F32, "Kron Radius Error",    NAN);
-            }
-        }
-
-        // Isophot measurements
-        // XXX insert header data: isophotal level
-        if (doIsophotal) {
-            pmSourceIsophotalValues *isophot = source->extpars->isophot;
-            if (isophot) {
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG",        PS_DATA_F32, "Isophot Magnitude",       isophot->mag);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG_ERR",    PS_DATA_F32, "Isophot Magnitude Error", isophot->magErr);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS",     PS_DATA_F32, "Isophot Radius",          isophot->rad);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS_ERR", PS_DATA_F32, "Isophot Radius Error",    isophot->radErr);
-            } else {
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG",        PS_DATA_F32, "Isophot Magnitude",       NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG_ERR",    PS_DATA_F32, "Isophot Magnitude Error", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS",     PS_DATA_F32, "Isophot Radius",          NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS_ERR", PS_DATA_F32, "Isophot Radius Error",    NAN);
-            }
-        }
-
-        // Flux Annuli
-        if (doAnnuli) {
-            pmSourceAnnuli *annuli = source->extpars->annuli;
-            if (annuli) {
-                psVector *fluxVal = annuli->flux;
-                psVector *fluxErr = annuli->fluxErr;
-                psVector *fluxVar = annuli->fluxVar;
-
-                for (int j = 0; j < fluxVal->n; j++) {
-                    char name[32];
-                    sprintf (name, "FLUX_VAL_R_%02d", j);
-                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux value in annulus", fluxVal->data.F32[j]);
-                    sprintf (name, "FLUX_ERR_R_%02d", j);
-                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux error in annulus", fluxErr->data.F32[j]);
-                    sprintf (name, "FLUX_VAR_R_%02d", j);
-                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux stdev in annulus", fluxVar->data.F32[j]);
-                }
-            } else {
-                for (int j = 0; j < radialBinsLower->n; j++) {
-                    char name[32];
-                    sprintf (name, "FLUX_VAL_R_%02d", j);
-                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux value in annulus", NAN);
-                    sprintf (name, "FLUX_ERR_R_%02d", j);
-                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux error in annulus", NAN);
-                    sprintf (name, "FLUX_VAR_R_%02d", j);
-                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux stdev in annulus", NAN);
-                }
-            }
-        }
-
-# endif
-        psArrayAdd (table, 100, row);
-        psFree (row);
-    }
-
-    if (table->n == 0) {
-        if (!psFitsWriteBlank (fits, outhead, extname)) {
-            psError(psErrorCodeLast(), false, "Unable to write empty sources.");
-            psFree(outhead);
-            psFree(table);
-            return false;
-        }
-        psFree (outhead);
-        psFree (table);
-        return true;
-    }
-
-    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
-    if (!psFitsWriteTable (fits, outhead, table, extname)) {
-        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
-        psFree (outhead);
-        psFree(table);
-        return false;
-    }
-    psFree (outhead);
-    psFree (table);
-
-    return true;
-}
-
-// XXX this layout is still the same as PS1_DEV_1
-bool pmSourcesWrite_CMF_PS1_V1_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname)
-{
-
-    psArray *table;
-    psMetadata *row;
-    psF32 *PAR, *dPAR;
-    psEllipseAxes axes;
-    psF32 xPos, yPos;
-    psF32 xErr, yErr;
-    char name[64];
-
-    // create a header to hold the output data
-    psMetadata *outhead = psMetadataAlloc ();
-
-    // write the links to the image header
-    psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTNAME", PS_META_REPLACE, "xsrc table extension", extname);
-
-    // let's write these out in S/N order
-    sources = psArraySort (sources, pmSourceSortByFlux);
-
-    // we are writing one row per model; we need to write out same number of columns for each row: find the max Nparams
-    int nParamMax = 0;
-    for (int i = 0; i < sources->n; i++) {
-        pmSource *source = sources->data[i];
-        if (source->modelFits == NULL) continue;
-        for (int j = 0; j < source->modelFits->n; j++) {
-            pmModel *model = source->modelFits->data[j];
-            assert (model);
-            nParamMax = PS_MAX (nParamMax, model->params->n);
-        }
-    }
-
-    table = psArrayAllocEmpty (sources->n);
-
-    // we write out all sources, regardless of quality.  the source flags tell us the state
-    for (int i = 0; i < sources->n; i++) {
-
-        pmSource *source = sources->data[i];
-
-        // XXX if no model fits are saved, write out modelEXT?
-        if (source->modelFits == NULL) continue;
-
-        // We have multiple sources : need to flag the one used to subtract the light (the 'best' model)
-        for (int j = 0; j < source->modelFits->n; j++) {
-
-            // choose the convolved EXT model, if available, otherwise the simple one
-            pmModel *model = source->modelFits->data[j];
-            assert (model);
-
-	    // skip models which were not actually fitted
-	    if (model->flags & PM_MODEL_STATUS_BADARGS) continue;
-
-            PAR = model->params->data.F32;
-            dPAR = model->dparams->data.F32;
-            xPos = PAR[PM_PAR_XPOS];
-            yPos = PAR[PM_PAR_YPOS];
-            xErr = dPAR[PM_PAR_XPOS];
-            yErr = dPAR[PM_PAR_YPOS];
-
-            axes = pmPSF_ModelToAxes (PAR, 20.0);
-
-            row = psMetadataAlloc ();
-
-            // XXX we are not writing out the mode (flags) or the type (psf, ext, etc)
-            psMetadataAddU32 (row, PS_LIST_TAIL, "IPP_IDET",         0, "IPP detection identifier index",             source->seq);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "X_EXT",            0, "EXT model x coordinate",                     xPos);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "Y_EXT",            0, "EXT model y coordinate",                     yPos);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "X_EXT_SIG",        0, "Sigma in EXT x coordinate",                  xErr);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "Y_EXT_SIG",        0, "Sigma in EXT y coordinate",                  yErr);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_INST_MAG",     0, "EXT fit instrumental magnitude",             model->mag);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_INST_MAG_SIG", 0, "Sigma of PSF instrumental magnitude",        model->magErr);
-
-            psMetadataAddF32 (row, PS_LIST_TAIL, "NPARAMS",          0, "number of model parameters",                 model->params->n);
-            psMetadataAddStr (row, PS_LIST_TAIL, "MODEL_TYPE",       0, "name of model",                              pmModelClassGetName (model->type));
-
-            // XXX these should be major and minor, not 'x' and 'y'
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_WIDTH_MAJ",    0, "EXT width in x coordinate",                  axes.major);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_WIDTH_MIN",    0, "EXT width in y coordinate",                  axes.minor);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_THETA",        0, "EXT orientation angle",                      axes.theta);
-
-            // write out the other generic parameters
-            for (int k = 0; k < nParamMax; k++) {
-                if (k == PM_PAR_I0) continue;
-                if (k == PM_PAR_SKY) continue;
-                if (k == PM_PAR_XPOS) continue;
-                if (k == PM_PAR_YPOS) continue;
-                if (k == PM_PAR_SXX) continue;
-                if (k == PM_PAR_SXY) continue;
-                if (k == PM_PAR_SYY) continue;
-
-                snprintf (name, 64, "EXT_PAR_%02d", k);
-
-                if (k < model->params->n) {
-                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "", model->params->data.F32[k]);
-                } else {
-                    psMetadataAddF32 (row, PS_LIST_TAIL, name, PS_DATA_F32, "", NAN);
-                }
-            }
-
-            // XXX other parameters which may be set.
-            // XXX flag / value to define the model
-            // XXX write out the model type, fit status flags
-
-            psArrayAdd (table, 100, row);
-            psFree (row);
-        }
-    }
-
-    if (table->n == 0) {
-        if (!psFitsWriteBlank (fits, outhead, extname)) {
-            psError(psErrorCodeLast(), false, "Unable to write empty sources.");
-            psFree(outhead);
-            psFree(table);
-            return false;
-        }
-        psFree (outhead);
-        psFree (table);
-        return true;
-    }
-
-    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
-    if (!psFitsWriteTable (fits, outhead, table, extname)) {
-        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
-        psFree (outhead);
-        psFree(table);
-        return false;
-    }
-    psFree (outhead);
-    psFree (table);
-    return true;
-}
-
-bool pmSourceLocalAstrometry (psSphere *ptSky, float *posAngle, float *pltScale, pmChip *chip, float xPos, float yPos) {
-
-    pmFPA *fpa = chip->parent;
-
-    if (!chip->toFPA) goto escape;
-    if (!fpa->toTPA) goto escape;
-    if (!fpa->toSky) goto escape;
-
-    // generate RA,DEC
-    psPlane ptCH, ptFP, ptTP_o, ptTP_x, ptTP_y;
-
-    // calculate the astrometry for the coordinate of interest
-    ptCH.x = xPos;
-    ptCH.y = yPos;
-    psPlaneTransformApply (&ptFP, chip->toFPA, &ptCH);
-    psPlaneTransformApply (&ptTP_o, fpa->toTPA, &ptFP);
-    psDeproject (ptSky, &ptTP_o, fpa->toSky);
-
-    // calculate the astrometry for the coordinate + 1pix in X
-    ptCH.x = xPos + 1.0;
-    ptCH.y = yPos;
-    psPlaneTransformApply (&ptFP, chip->toFPA, &ptCH);
-    psPlaneTransformApply (&ptTP_x, fpa->toTPA, &ptFP);
-
-    // calculate the astrometry for the coordinate + 1pix in Y
-    ptCH.x = xPos;
-    ptCH.y = yPos + 1.0;
-    psPlaneTransformApply (&ptFP, chip->toFPA, &ptCH);
-    psPlaneTransformApply (&ptTP_y, fpa->toTPA, &ptFP);
-
-    // the resulting Tangent Plane coordinates are in TP pixels; convert to local Tangent Plane
-    // degrees
-
-    float dTPx_dCHx = fpa->toSky->Xs * (ptTP_x.x - ptTP_o.x);
-    float dTPy_dCHx = fpa->toSky->Ys * (ptTP_x.y - ptTP_o.y);
-
-    float dTPx_dCHy = fpa->toSky->Xs * (ptTP_y.x - ptTP_o.x);
-    float dTPy_dCHy = fpa->toSky->Ys * (ptTP_y.y - ptTP_o.y);
-
-    float pltScale_x = hypot(dTPx_dCHx, dTPy_dCHx);
-    float pltScale_y = hypot(dTPx_dCHy, dTPy_dCHy);
-    *pltScale = 0.5*(pltScale_x + pltScale_y);
-
-    float posAngle_x = atan2 (+dTPy_dCHx, +dTPx_dCHx);
-    float posAngle_y = atan2 (-dTPy_dCHy, +dTPx_dCHy);
-    *posAngle = 0.5*(posAngle_x + posAngle_y);
-
-    return true;
-
-escape:
-    // no astrometry calibration, give up
-    ptSky->r = NAN;
-    ptSky->d = NAN;
-    *posAngle = NAN;
-    *pltScale = NAN;
-
-    return false;
-}
-
-bool pmSourcesWrite_CMF_PS1_V1_XRAD(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
-{
-    return true;
-}
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_V2.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_V2.c	(revision 32133)
+++ 	(revision )
@@ -1,669 +1,0 @@
-/** @file  pmSourceIO.c
- *
- *  @author EAM, IfA
- *
- *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2009-02-18 02:44:19 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <math.h>
-#include <string.h>
-#include <pslib.h>
-
-#include "pmConfig.h"
-#include "pmDetrendDB.h"
-
-#include "pmHDU.h"
-#include "pmFPA.h"
-#include "pmFPALevel.h"
-#include "pmFPAview.h"
-#include "pmFPAfile.h"
-
-#include "pmTrend2D.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmSpan.h"
-#include "pmFootprintSpans.h"
-#include "pmFootprint.h"
-#include "pmPeaks.h"
-#include "pmMoments.h"
-#include "pmModelFuncs.h"
-#include "pmModel.h"
-#include "pmModelUtils.h"
-#include "pmModelClass.h"
-#include "pmSourceMasks.h"
-#include "pmSourceExtendedPars.h"
-#include "pmSourceDiffStats.h"
-#include "pmSource.h"
-#include "pmSourceFitModel.h"
-#include "pmPSF.h"
-#include "pmPSFtry.h"
-
-#include "pmSourceIO.h"
-#include "pmSourceOutputs.h"
-
-// panstarrs-style FITS table output (header + table in 1st extension)
-// this format consists of a header derived from the image header
-// followed by a zero-size matrix, followed by the table data
-
-bool pmSourcesWrite_CMF_PS1_V2 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe)
-{
-    PS_ASSERT_PTR_NON_NULL(fits, false);
-    PS_ASSERT_PTR_NON_NULL(sources, false);
-    PS_ASSERT_PTR_NON_NULL(extname, false);
-
-    psArray *table;
-    psMetadata *row;
-
-    pmChip *chip = readout->parent->parent;
-
-    // if the sequence is defined, write these in seq order; otherwise
-    // write them in S/N order:
-    if (sources->n > 0) {
-        pmSource *source = (pmSource *) sources->data[0];
-        if (source->seq == -1) {
-	    // let's write these out in S/N order
-	    sources = psArraySort (sources, pmSourceSortByFlux);
-        } else {
-	    sources = psArraySort (sources, pmSourceSortBySeq);
-        }
-    }
-
-    table = psArrayAllocEmpty (sources->n);
-
-    short nImageOverlap; 
-    float magOffset; 
-    float zeroptErr; 
-    float fwhmMajor; 
-    float fwhmMinor;
-    pmSourceOutputsCommonValues (&nImageOverlap, &magOffset, &zeroptErr, &fwhmMajor, &fwhmMinor, readout, imageHeader);
-
-    // we write out PSF-fits for all sources, regardless of quality.  the source flags tell us the state
-    // by the time we call this function, all values should be assigned.  let's use asserts to be sure in some cases.
-    for (int i = 0; i < sources->n; i++) {
-        pmSource *source = (pmSource *) sources->data[i];
-
-        // If source->seq is -1, source was generated in this analysis.  If source->seq is
-        // not -1, source was read from elsewhere: in the latter case, preserve the source
-        // ID.  source.seq is used instead of source.id since the latter is a const
-        // generated on Alloc, and would thus be wrong for read in sources.
-        if (source->seq == -1) {
-	    source->seq = i;
-        }
-
-	// set the 'best' values for various output fields:
-	pmSourceOutputs outputs;
-	pmSourceOutputsSetValues (&outputs, source, chip, fwhmMajor, fwhmMinor, magOffset);
-
-	pmSourceOutputsMoments moments;
-	pmSourceOutputsSetMoments (&moments, source);
-
-        row = psMetadataAlloc ();
-        psMetadataAdd (row, PS_LIST_TAIL, "IPP_IDET",         PS_DATA_U32, "IPP detection identifier index",             source->seq);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_PSF",            PS_DATA_F32, "PSF x coordinate",                           outputs.xPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_PSF",            PS_DATA_F32, "PSF y coordinate",                           outputs.yPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_PSF_SIG",        PS_DATA_F32, "Sigma in PSF x coordinate",                  outputs.xErr); // XXX this is only measured for non-linear fits
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_PSF_SIG",        PS_DATA_F32, "Sigma in PSF y coordinate",                  outputs.yErr); // XXX this is only measured for non-linear fits
-
-        psMetadataAdd (row, PS_LIST_TAIL, "POSANGLE",         PS_DATA_F32, "position angle at source (degrees)",         outputs.posAngle);
-        psMetadataAdd (row, PS_LIST_TAIL, "PLTSCALE",         PS_DATA_F32, "plate scale at source (arcsec/pixel)",       outputs.pltScale);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_MAG",     PS_DATA_F32, "PSF fit instrumental magnitude",             source->psfMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_MAG_SIG", PS_DATA_F32, "Sigma of PSF instrumental magnitude",        source->psfMagErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG",           PS_DATA_F32, "magnitude in standard aperture",             source->apMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RADIUS",    PS_DATA_F32, "radius used for aperture mags",              outputs.apRadius);
-        psMetadataAdd (row, PS_LIST_TAIL, "PEAK_FLUX_AS_MAG", PS_DATA_F32, "Peak flux expressed as magnitude",           outputs.peakMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "CAL_PSF_MAG",      PS_DATA_F32, "PSF Magnitude using supplied calibration",   outputs.calMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "CAL_PSF_MAG_SIG",  PS_DATA_F32, "measured scatter of zero point calibration", zeroptErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "RA_PSF",           PS_DATA_F64, "PSF RA coordinate (degrees)",                outputs.ra);
-        psMetadataAdd (row, PS_LIST_TAIL, "DEC_PSF",          PS_DATA_F64, "PSF DEC coordinate (degrees)",               outputs.dec);
-        psMetadataAdd (row, PS_LIST_TAIL, "SKY",              PS_DATA_F32, "Sky level",                                  source->sky);
-        psMetadataAdd (row, PS_LIST_TAIL, "SKY_SIGMA",        PS_DATA_F32, "Sigma of sky level",                         source->skyErr);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_CHISQ",        PS_DATA_F32, "Chisq of PSF-fit",                           outputs.chisq);
-        psMetadataAdd (row, PS_LIST_TAIL, "CR_NSIGMA",        PS_DATA_F32, "Nsigma deviations from PSF to CF",           source->crNsigma);
-        psMetadataAdd (row, PS_LIST_TAIL, "EXT_NSIGMA",       PS_DATA_F32, "Nsigma deviations from PSF to EXT",          source->extNsigma);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_MAJOR",        PS_DATA_F32, "PSF width (major axis)",                     outputs.psfMajor);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_MINOR",        PS_DATA_F32, "PSF width (minor axis)",                     outputs.psfMinor);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_THETA",        PS_DATA_F32, "PSF orientation angle",                      outputs.psfTheta);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor",                source->pixWeightNotBad);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_NDOF",         PS_DATA_S32, "degrees of freedom",                         outputs.nDOF);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_NPIX",         PS_DATA_S32, "number of pixels in fit",                    outputs.nPix);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_XX",       PS_DATA_F32, "second moments (X^2)",                       moments.Mxx);
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_XY",       PS_DATA_F32, "second moments (X*Y)",                       moments.Mxy);
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_YY",       PS_DATA_F32, "second moments (Y*Y)",                       moments.Myy);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "FLAGS",            PS_DATA_U32, "psphot analysis flags",                      source->mode);
-
-        // XXX not sure how to get this : need to load Nimages with weight?
-        psMetadataAdd (row, PS_LIST_TAIL, "N_FRAMES",         PS_DATA_U16, "Number of frames overlapping source center", nImageOverlap);
-        psMetadataAdd (row, PS_LIST_TAIL, "PADDING",          PS_DATA_S16, "padding", 0);
-
-        psArrayAdd (table, 100, row);
-        psFree (row);
-
-        // EXT_NSIGMA will be NAN if: 1) contour ellipse is imaginary; 2) source is not
-        // subtracted
-
-        // CR_NSIGMA will be NAN if: 1) source is not subtracted; 2) source is on the image
-        // edge; 3) any pixels in the 3x3 peak region are masked;
-    }
-
-    // XXX why do we make a copy here to be supplemented with the masks?  why not do this in the calling function?
-    psMetadata *header = psMetadataCopy(NULL, tableHeader);
-    pmSourceMasksHeader(header);
-
-    if (table->n == 0) {
-        if (!psFitsWriteBlank(fits, header, extname)) {
-            psError(psErrorCodeLast(), false, "Unable to write blank sources file.");
-            psFree(table);
-            psFree(header);
-            return false;
-        }
-        psFree(table);
-        psFree(header);
-        return true;
-    }
-
-    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
-    if (!psFitsWriteTable(fits, header, table, extname)) {
-        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
-        psFree(table);
-        psFree(header);
-        return false;
-    }
-    psFree(table);
-    psFree(header);
-
-    return true;
-}
-
-// read in a readout from the fits file
-psArray *pmSourcesRead_CMF_PS1_V2 (psFits *fits, psMetadata *header)
-{
-    PS_ASSERT_PTR_NON_NULL(fits, false);
-    PS_ASSERT_PTR_NON_NULL(header, false);
-
-    bool status;
-    psF32 *PAR, *dPAR;
-    psEllipseAxes axes;
-
-    // define PSF model type
-    // XXX need to carry the extra model parameters
-    int modelType = pmModelClassGetType ("PS_MODEL_GAUSS");
-
-    char *PSF_NAME = psMetadataLookupStr (&status, header, "PSF_NAME");
-    if (PSF_NAME != NULL) {
-        modelType = pmModelClassGetType (PSF_NAME);
-    }
-    assert (modelType > -1);
-
-    // We get the size of the table, and allocate the array of sources first because the table
-    // is large and ephemeral --- when the table gets blown away, whatever is allocated after
-    // the table is read blocks the free.  In fact, it's better to read the table row by row.
-    long numSources = psFitsTableSize(fits); // Number of sources in table
-    psArray *sources = psArrayAlloc(numSources); // Array of sources, to return
-
-    // convert the table to the pmSource entriesa
-    for (int i = 0; i < numSources; i++) {
-        psMetadata *row = psFitsReadTableRow(fits, i); // Table row
-        if (!row) {
-            psError(psErrorCodeLast(), false, "Unable to read row %d of sources", i);
-            psFree(sources);
-            return NULL;
-        }
-
-        pmSource *source = pmSourceAlloc ();
-        pmModel *model = pmModelAlloc (modelType);
-        source->modelPSF  = model;
-        source->type = PM_SOURCE_TYPE_STAR; // XXX this should be added to the flags
-
-        // NOTE: A SEGV here because "model" is NULL is probably caused by not initialising the models.
-        PAR = model->params->data.F32;
-        dPAR = model->dparams->data.F32;
-
-        source->seq       = psMetadataLookupU32 (&status, row, "IPP_IDET");
-        PAR[PM_PAR_XPOS]  = psMetadataLookupF32 (&status, row, "X_PSF");
-        PAR[PM_PAR_YPOS]  = psMetadataLookupF32 (&status, row, "Y_PSF");
-        dPAR[PM_PAR_XPOS] = psMetadataLookupF32 (&status, row, "X_PSF_SIG");
-        dPAR[PM_PAR_YPOS] = psMetadataLookupF32 (&status, row, "Y_PSF_SIG");
-        axes.major        = psMetadataLookupF32 (&status, row, "PSF_MAJOR");
-        axes.minor        = psMetadataLookupF32 (&status, row, "PSF_MINOR");
-        axes.theta        = psMetadataLookupF32 (&status, row, "PSF_THETA");
-
-        PAR[PM_PAR_SKY]   = psMetadataLookupF32 (&status, row, "SKY");
-        dPAR[PM_PAR_SKY]  = psMetadataLookupF32 (&status, row, "SKY_SIGMA");
-        source->sky       = PAR[PM_PAR_SKY];
-        source->skyErr    = dPAR[PM_PAR_SKY];
-
-        // XXX use these to determine PAR[PM_PAR_I0]?
-        source->psfMag    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG");
-        source->psfMagErr    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG_SIG");
-        source->apMag     = psMetadataLookupF32 (&status, row, "AP_MAG");
-
-        // XXX this scaling is incorrect: does not include the 2 \pi AREA factor
-        PAR[PM_PAR_I0]    = (isfinite(source->psfMag)) ? pow(10.0, -0.4*source->psfMag) : NAN;
-        dPAR[PM_PAR_I0]   = (isfinite(source->psfMag)) ? PAR[PM_PAR_I0] * source->psfMagErr : NAN;
-
-        pmPSF_AxesToModel (PAR, axes);
-
-        float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
-        float peakFlux    = (isfinite(peakMag)) ? pow(10.0, -0.4*peakMag) : NAN;
-
-        // recreate the peak to match (xPos, yPos) +/- (xErr, yErr)
-        source->peak = pmPeakAlloc(PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], peakFlux, PM_PEAK_LONE);
-        source->peak->rawFlux = peakFlux;
-        source->peak->smoothFlux = peakFlux;
-        source->peak->xf   = PAR[PM_PAR_XPOS]; // more accurate position
-        source->peak->yf   = PAR[PM_PAR_YPOS]; // more accurate position
-        source->peak->dx   = dPAR[PM_PAR_XPOS];
-        source->peak->dy   = dPAR[PM_PAR_YPOS];
-
-        source->pixWeightNotBad = psMetadataLookupF32 (&status, row, "PSF_QF");
-        source->crNsigma  = psMetadataLookupF32 (&status, row, "CR_NSIGMA");
-        source->extNsigma = psMetadataLookupF32 (&status, row, "EXT_NSIGMA");
-        source->apRadius  = psMetadataLookupS32 (&status, row, "AP_MAG_RADIUS");
-
-        // note that some older versions used PSF_PROBABILITY: this was not well defined.
-        model->chisq      = psMetadataLookupF32 (&status, row, "PSF_CHISQ");
-        model->nDOF       = psMetadataLookupS32 (&status, row, "PSF_NDOF");
-        model->nPix       = psMetadataLookupS32 (&status, row, "PSF_NPIX");
-
-        source->moments = pmMomentsAlloc ();
-        source->moments->Mx = source->peak->xf; // we don't have both Mx,My and xf,yf in the cmf
-        source->moments->My = source->peak->yf; // we don't have both Mx,My and xf,yf in the cmf
-
-        source->moments->Mxx = psMetadataLookupF32 (&status, row, "MOMENTS_XX");
-        source->moments->Mxy = psMetadataLookupF32 (&status, row, "MOMENTS_XY");
-        source->moments->Myy = psMetadataLookupF32 (&status, row, "MOMENTS_YY");
-
-        source->mode = psMetadataLookupU32 (&status, row, "FLAGS");
-        assert (status);
-
-        sources->data[i] = source;
-        psFree(row);
-    }
-
-    return sources;
-}
-
-bool pmSourcesWrite_CMF_PS1_V2_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
-{
-
-    bool status;
-    psArray *table;
-    psMetadata *row;
-    psF32 *PAR, *dPAR;
-    psF32 xPos, yPos;
-    psF32 xErr, yErr;
-    int nRow = -1;
-
-    // create a header to hold the output data
-    psMetadata *outhead = psMetadataAlloc ();
-
-    // write the links to the image header
-    psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTNAME", PS_META_REPLACE, "xsrc table extension", extname);
-
-    pmChip *chip = readout->parent->parent;
-    pmFPA  *fpa  = chip->parent;
-
-    // zero point corrections
-    bool status1 = false;
-    bool status2 = false;
-    float magOffset = 0.0;
-    float exptime   = psMetadataLookupF32(&status1, fpa->concepts, "FPA.EXPOSURE");
-    float zeropt    = psMetadataLookupF32(&status2, fpa->concepts, "FPA.ZP");
-    if (!isfinite(zeropt)) {
-        zeropt    = psMetadataLookupF32 (&status2, imageHeader, "ZPT_OBS");
-    }
-    if (status1 && status2 && (exptime > 0.0)) {
-        magOffset = zeropt + 2.5*log10(exptime);
-    }
-
-    // let's write these out in S/N order
-    sources = psArraySort (sources, pmSourceSortByFlux);
-
-    table = psArrayAllocEmpty (sources->n);
-
-    // which extended source analyses should we perform?
-    bool doAnnuli       = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
-    bool doPetrosian    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
-    // bool doIsophotal    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ISOPHOTAL");
-    // bool doKron         = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_KRON");
-
-    psVector *radMin = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.LOWER");
-    psVector *radMax = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.UPPER");
-    psAssert (radMin->n == radMax->n, "inconsistent annular bins");
-
-    // int nRadialBins = 0;
-    // if (doAnnuli) {
-    // 	// get the max count of radial bins
-    // 	for (int i = 0; i < sources->n; i++) {
-    // 	    pmSource *source = sources->data[i];
-    // 	    if (!source->extpars) continue;
-    // 	    if (!source->extpars->radProfile ) continue;
-    //         if (!source->extpars->radProfile->binSB) continue;
-    // 	    nRadialBins = PS_MAX(nRadialBins, source->extpars->radProfile->binSB->n);
-    // 	}
-    // }
-
-    // we write out all sources, regardless of quality.  the source flags tell us the state
-    for (int i = 0; i < sources->n; i++) {
-        // skip source if it is not a ext sourc
-        // XXX we have two places that extended source parameters are measured:
-        // psphotExtendedSources, which measures the aperture-like parameters and (potentially) the psf-convolved extended source models,
-        // psphotFitEXT, which does the simple extended source model fit (not psf-convolved)
-        // should we require both?
-
-        pmSource *source = sources->data[i];
-
-        // skip sources without measurements
-        if (source->extpars == NULL) continue;
-
-        // we require a PSF model fit (ignore the real crud)
-        pmModel *model = source->modelPSF;
-        if (model == NULL) continue;
-
-        // XXX I need to split the extended models from the extended aperture measurements
-        PAR = model->params->data.F32;
-        dPAR = model->dparams->data.F32;
-        xPos = PAR[PM_PAR_XPOS];
-        yPos = PAR[PM_PAR_YPOS];
-        xErr = dPAR[PM_PAR_XPOS];
-        yErr = dPAR[PM_PAR_YPOS];
-
-        row = psMetadataAlloc ();
-
-        // XXX we are not writing out the mode (flags) or the type (psf, ext, etc)
-        psMetadataAdd (row, PS_LIST_TAIL, "IPP_IDET",         PS_DATA_U32, "IPP detection identifier index",             source->seq);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_EXT",            PS_DATA_F32, "EXT model x coordinate",                     xPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_EXT",            PS_DATA_F32, "EXT model y coordinate",                     yPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_EXT_SIG",        PS_DATA_F32, "Sigma in EXT x coordinate",                  xErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_EXT_SIG",        PS_DATA_F32, "Sigma in EXT y coordinate",                  yErr);
-
-	float AxialRatio = NAN;
-	float AxialTheta = NAN;
-	pmSourceExtendedPars *extpars = source->extpars;
-	if (extpars) {
-	    AxialRatio = extpars->axes.minor / extpars->axes.major;
-	    AxialTheta = extpars->axes.theta;
-	}
-        psMetadataAdd (row, PS_LIST_TAIL, "F25_ARATIO",       PS_DATA_F32, "Axial Ratio of radial profile",              AxialRatio);
-        psMetadataAdd (row, PS_LIST_TAIL, "F25_THETA",        PS_DATA_F32, "Angle of radial profile ellipse",                  AxialTheta);
-
-        // Petrosian measurements
-        // XXX insert header data: petrosian ref radius, flux ratio
-	// XXX check flags to see if Pet was measured
-        if (doPetrosian) {
-	    pmSourceExtendedPars *extpars = source->extpars;
-            if (extpars) {
-		// XXX note that this mag is either calibrated or instrumental depending on existence of zero point 
-		float mag = (extpars->petrosianFlux > 0.0) ? -2.5*log10(extpars->petrosianFlux) + magOffset : NAN; // XXX zero point
-		float magErr = (extpars->petrosianFlux > 0.0) ? extpars->petrosianFlux / extpars->petrosianFluxErr : NAN; // XXX zero point
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG",        PS_DATA_F32, "Petrosian Magnitude", mag);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG_ERR",    PS_DATA_F32, "Petrosian Magnitude Error", magErr);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS",     PS_DATA_F32, "Petrosian Radius (pix)", extpars->petrosianRadius);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_ERR", PS_DATA_F32, "Petrosian Radius Error (pix)", extpars->petrosianRadiusErr);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50",     PS_DATA_F32, "Petrosian R50 (pix)", extpars->petrosianR50);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50_ERR", PS_DATA_F32, "Petrosian R50 Error (pix)", extpars->petrosianR50Err);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90",     PS_DATA_F32, "Petrosian R90 (pix)", extpars->petrosianR90);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90_ERR", PS_DATA_F32, "Petrosian R90 Error (pix)", extpars->petrosianR90Err);
-            } else {
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG",        PS_DATA_F32, "Petrosian Magnitude",       NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG_ERR",    PS_DATA_F32, "Petrosian Magnitude Error", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS",     PS_DATA_F32, "Petrosian Radius",          NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_ERR", PS_DATA_F32, "Petrosian Radius Error",    NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50",     PS_DATA_F32, "Petrosian R50 (pix)", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50_ERR", PS_DATA_F32, "Petrosian R50 Error (pix)",NAN); 
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90",     PS_DATA_F32, "Petrosian R90 (pix)", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90_ERR", PS_DATA_F32, "Petrosian R90 Error (pix)",NAN); 
-            }
-        }
-
-# if (0)
-        // Kron measurements
-        if (doKron) {
-            pmSourceKronValues *kron = source->extpars->kron;
-            if (kron) {
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG",        PS_DATA_F32, "Kron Magnitude",       kron->mag);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG_ERR",    PS_DATA_F32, "Kron Magnitude Error", kron->magErr);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS",     PS_DATA_F32, "Kron Radius",          kron->rad);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS_ERR", PS_DATA_F32, "Kron Radius Error",    kron->radErr);
-            } else {
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG",        PS_DATA_F32, "Kron Magnitude",       NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG_ERR",    PS_DATA_F32, "Kron Magnitude Error", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS",     PS_DATA_F32, "Kron Radius",          NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS_ERR", PS_DATA_F32, "Kron Radius Error",    NAN);
-            }
-        }
-
-        // Isophot measurements
-        // XXX insert header data: isophotal level
-        if (doIsophotal) {
-            pmSourceIsophotalValues *isophot = source->extpars->isophot;
-            if (isophot) {
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG",        PS_DATA_F32, "Isophot Magnitude",       isophot->mag);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG_ERR",    PS_DATA_F32, "Isophot Magnitude Error", isophot->magErr);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS",     PS_DATA_F32, "Isophot Radius",          isophot->rad);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS_ERR", PS_DATA_F32, "Isophot Radius Error",    isophot->radErr);
-            } else {
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG",        PS_DATA_F32, "Isophot Magnitude",       NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG_ERR",    PS_DATA_F32, "Isophot Magnitude Error", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS",     PS_DATA_F32, "Isophot Radius",          NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS_ERR", PS_DATA_F32, "Isophot Radius Error",    NAN);
-            }
-        }
-# endif
-
-        // Flux Annuli (if we have extended source measurements, we have these.  only optionally save them)
-        if (doAnnuli) {
-	    psVector *radSB   = psVectorAlloc(radMin->n, PS_TYPE_F32);
-	    psVector *radFlux = psVectorAlloc(radMin->n, PS_TYPE_F32);
-	    psVector *radFill = psVectorAlloc(radMin->n, PS_TYPE_F32);
-	    psVectorInit (radSB, NAN);
-	    psVectorInit (radFlux, NAN);
-	    psVectorInit (radFill, NAN);
-	    if (!source->extpars) goto empty_annuli;
-	    if (!source->extpars->radProfile) goto empty_annuli;
-	    if (!source->extpars->radProfile->binSB) goto empty_annuli;
-	    psAssert (source->extpars->radProfile->binSum, "programming error");
-	    psAssert (source->extpars->radProfile->binFill, "programming error");
-	    psAssert (source->extpars->radProfile->binSB->n <= radFlux->n, "inconsistent vector lengths");
-	    psAssert (source->extpars->radProfile->binSum->n <= radFlux->n, "inconsistent vector lengths");
-	    psAssert (source->extpars->radProfile->binFill->n <= radFlux->n, "inconsistent vector lengths");
-
-	    // copy the data from fluxVal (which is not guaranteed to be the full length) to radFlux
-	    for (int j = 0; j < source->extpars->radProfile->binSB->n; j++) {
-		radSB->data.F32[j]   = source->extpars->radProfile->binSB->data.F32[j];
-		radFlux->data.F32[j] = source->extpars->radProfile->binSum->data.F32[j];
-		radFill->data.F32[j] = source->extpars->radProfile->binFill->data.F32[j];
-	    }
-
-	empty_annuli:
-	    psMetadataAdd (row, PS_LIST_TAIL, "PROF_SB", PS_DATA_VECTOR, "mean surface brightness annuli", radSB);
-	    psMetadataAdd (row, PS_LIST_TAIL, "PROF_FLUX", PS_DATA_VECTOR, "flux within annuli", radFlux);
-	    psMetadataAdd (row, PS_LIST_TAIL, "PROF_FILL", PS_DATA_VECTOR, "fill factor of annuli", radFill);
-	    psFree (radSB);
-	    psFree (radFlux);
-	    psFree (radFill);
-	}
-	if (nRow < 0) {
-	    nRow = row->list->n;
-	} else {
-	    psAssert (nRow == row->list->n, "inconsistent row lengths");
-	}
-	psArrayAdd (table, 100, row);
-	psFree (row);
-    }
-    
-    if (table->n == 0) {
-	if (!psFitsWriteBlank (fits, outhead, extname)) {
-	    psError(psErrorCodeLast(), false, "Unable to write empty sources file.");
-	    psFree(outhead);
-	    psFree(table);
-	    return false;
-	}
-	psFree (outhead);
-	psFree (table);
-	return true;
-    }
-    
-    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
-    if (!psFitsWriteTable (fits, outhead, table, extname)) {
-	psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
-	psFree (outhead);
-    psFree(table);
-    return false;
-    }
-    psFree (outhead);
-    psFree (table);
-    
-    return true;
-}
-
-// XXX this layout is still the same as PS1_DEV_1
-bool pmSourcesWrite_CMF_PS1_V2_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname)
-{
-
-    psArray *table;
-    psMetadata *row;
-    psF32 *PAR, *dPAR;
-    psEllipseAxes axes;
-    psF32 xPos, yPos;
-    psF32 xErr, yErr;
-    char name[64];
-
-    // create a header to hold the output data
-    psMetadata *outhead = psMetadataAlloc ();
-
-    // write the links to the image header
-    psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTNAME", PS_META_REPLACE, "xsrc table extension", extname);
-
-    // let's write these out in S/N order
-    sources = psArraySort (sources, pmSourceSortByFlux);
-
-    // we are writing one row per model; we need to write out same number of columns for each row: find the max Nparams
-    int nParamMax = 0;
-    for (int i = 0; i < sources->n; i++) {
-        pmSource *source = sources->data[i];
-        if (source->modelFits == NULL) continue;
-        for (int j = 0; j < source->modelFits->n; j++) {
-            pmModel *model = source->modelFits->data[j];
-            assert (model);
-            nParamMax = PS_MAX (nParamMax, model->params->n);
-        }
-    }
-
-    table = psArrayAllocEmpty (sources->n);
-
-    // we write out all sources, regardless of quality.  the source flags tell us the state
-    for (int i = 0; i < sources->n; i++) {
-
-        pmSource *source = sources->data[i];
-
-        // XXX if no model fits are saved, write out modelEXT?
-        if (source->modelFits == NULL) continue;
-
-        // We have multiple sources : need to flag the one used to subtract the light (the 'best' model)
-        for (int j = 0; j < source->modelFits->n; j++) {
-
-            // choose the convolved EXT model, if available, otherwise the simple one
-            pmModel *model = source->modelFits->data[j];
-            assert (model);
-
-	    // skip models which were not actually fitted
-	    if (model->flags & PM_MODEL_STATUS_BADARGS) continue;
-
-            PAR = model->params->data.F32;
-            dPAR = model->dparams->data.F32;
-            xPos = PAR[PM_PAR_XPOS];
-            yPos = PAR[PM_PAR_YPOS];
-            xErr = dPAR[PM_PAR_XPOS];
-            yErr = dPAR[PM_PAR_YPOS];
-
-            axes = pmPSF_ModelToAxes (PAR, 20.0);
-
-            row = psMetadataAlloc ();
-
-            // XXX we are not writing out the mode (flags) or the type (psf, ext, etc)
-            psMetadataAddU32 (row, PS_LIST_TAIL, "IPP_IDET",         0, "IPP detection identifier index",             source->seq);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "X_EXT",            0, "EXT model x coordinate",                     xPos);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "Y_EXT",            0, "EXT model y coordinate",                     yPos);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "X_EXT_SIG",        0, "Sigma in EXT x coordinate",                  xErr);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "Y_EXT_SIG",        0, "Sigma in EXT y coordinate",                  yErr);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_INST_MAG",     0, "EXT fit instrumental magnitude",             model->mag);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_INST_MAG_SIG", 0, "Sigma of PSF instrumental magnitude",        model->magErr);
-
-            psMetadataAddF32 (row, PS_LIST_TAIL, "NPARAMS",          0, "number of model parameters",                 model->params->n);
-            psMetadataAddStr (row, PS_LIST_TAIL, "MODEL_TYPE",       0, "name of model",                              pmModelClassGetName (model->type));
-
-            // XXX these should be major and minor, not 'x' and 'y'
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_WIDTH_MAJ",    0, "EXT width in x coordinate",                  axes.major);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_WIDTH_MIN",    0, "EXT width in y coordinate",                  axes.minor);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_THETA",        0, "EXT orientation angle",                      axes.theta);
-
-            // write out the other generic parameters
-            for (int k = 0; k < nParamMax; k++) {
-                if (k == PM_PAR_I0) continue;
-                if (k == PM_PAR_SKY) continue;
-                if (k == PM_PAR_XPOS) continue;
-                if (k == PM_PAR_YPOS) continue;
-                if (k == PM_PAR_SXX) continue;
-                if (k == PM_PAR_SXY) continue;
-                if (k == PM_PAR_SYY) continue;
-
-                snprintf (name, 64, "EXT_PAR_%02d", k);
-
-                if (k < model->params->n) {
-                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "", model->params->data.F32[k]);
-                } else {
-                    psMetadataAddF32 (row, PS_LIST_TAIL, name, PS_DATA_F32, "", NAN);
-                }
-            }
-
-            // XXX other parameters which may be set.
-            // XXX flag / value to define the model
-            // XXX write out the model type, fit status flags
-
-            psArrayAdd (table, 100, row);
-            psFree (row);
-        }
-    }
-
-    if (table->n == 0) {
-        if (!psFitsWriteBlank (fits, outhead, extname)) {
-            psError(psErrorCodeLast(), false, "Unable to write empty sources file.");
-            psFree(outhead);
-            psFree(table);
-            return false;
-        }
-        psFree (outhead);
-        psFree (table);
-        return true;
-    }
-
-    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
-    if (!psFitsWriteTable (fits, outhead, table, extname)) {
-        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
-        psFree (outhead);
-        psFree(table);
-        return false;
-    }
-    psFree (outhead);
-    psFree (table);
-    return true;
-}
-
-bool pmSourcesWrite_CMF_PS1_V2_XRAD(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
-{
-    return true;
-}
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_V3.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_V3.c	(revision 32133)
+++ 	(revision )
@@ -1,680 +1,0 @@
-/** @file  pmSourceIO.c
- *
- *  @author EAM, IfA
- *
- *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2009-02-18 02:44:19 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <math.h>
-#include <string.h>
-#include <pslib.h>
-
-#include "pmConfig.h"
-#include "pmDetrendDB.h"
-
-#include "pmHDU.h"
-#include "pmFPA.h"
-#include "pmFPALevel.h"
-#include "pmFPAview.h"
-#include "pmFPAfile.h"
-
-#include "pmTrend2D.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmSpan.h"
-#include "pmFootprintSpans.h"
-#include "pmFootprint.h"
-#include "pmPeaks.h"
-#include "pmMoments.h"
-#include "pmModelFuncs.h"
-#include "pmModel.h"
-#include "pmModelUtils.h"
-#include "pmModelClass.h"
-#include "pmSourceMasks.h"
-#include "pmSourceExtendedPars.h"
-#include "pmSourceDiffStats.h"
-#include "pmSource.h"
-#include "pmSourceFitModel.h"
-#include "pmPSF.h"
-#include "pmPSFtry.h"
-
-#include "pmSourceIO.h"
-#include "pmSourceOutputs.h"
-
-// panstarrs-style FITS table output (header + table in 1st extension)
-// this format consists of a header derived from the image header
-// followed by a zero-size matrix, followed by the table data
-
-bool pmSourcesWrite_CMF_PS1_V3 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe)
-{
-    PS_ASSERT_PTR_NON_NULL(fits, false);
-    PS_ASSERT_PTR_NON_NULL(sources, false);
-    PS_ASSERT_PTR_NON_NULL(extname, false);
-
-    psArray *table;
-    psMetadata *row;
-
-    pmChip *chip = readout->parent->parent;
-
-    // if the sequence is defined, write these in seq order; otherwise
-    // write them in S/N order:
-    if (sources->n > 0) {
-        pmSource *source = (pmSource *) sources->data[0];
-        if (source->seq == -1) {
-	    // let's write these out in S/N order
-	    sources = psArraySort (sources, pmSourceSortByFlux);
-        } else {
-	    sources = psArraySort (sources, pmSourceSortBySeq);
-        }
-    }
-
-    table = psArrayAllocEmpty (sources->n);
-
-    short nImageOverlap; 
-    float magOffset; 
-    float zeroptErr; 
-    float fwhmMajor; 
-    float fwhmMinor;
-    pmSourceOutputsCommonValues (&nImageOverlap, &magOffset, &zeroptErr, &fwhmMajor, &fwhmMinor, readout, imageHeader);
-
-    // we write out PSF-fits for all sources, regardless of quality.  the source flags tell us the state
-    // by the time we call this function, all values should be assigned.  let's use asserts to be sure in some cases.
-    for (int i = 0; i < sources->n; i++) {
-        pmSource *source = (pmSource *) sources->data[i];
-
-        // If source->seq is -1, source was generated in this analysis.  If source->seq is
-        // not -1, source was read from elsewhere: in the latter case, preserve the source
-        // ID.  source.seq is used instead of source.id since the latter is a const
-        // generated on Alloc, and would thus be wrong for read in sources.
-        if (source->seq == -1) {
-	    source->seq = i;
-        }
-
-	// set the 'best' values for various output fields:
-	pmSourceOutputs outputs;
-	pmSourceOutputsSetValues (&outputs, source, chip, fwhmMajor, fwhmMinor, magOffset);
-
-	pmSourceOutputsMoments moments;
-	pmSourceOutputsSetMoments (&moments, source);
-
-        row = psMetadataAlloc ();
-        psMetadataAdd (row, PS_LIST_TAIL, "IPP_IDET",         PS_DATA_U32, "IPP detection identifier index",             source->seq);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_PSF",            PS_DATA_F32, "PSF x coordinate",                           outputs.xPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_PSF",            PS_DATA_F32, "PSF y coordinate",                           outputs.yPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_PSF_SIG",        PS_DATA_F32, "Sigma in PSF x coordinate",                  outputs.xErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_PSF_SIG",        PS_DATA_F32, "Sigma in PSF y coordinate",                  outputs.yErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "POSANGLE",         PS_DATA_F32, "position angle at source (degrees)",         outputs.posAngle);
-        psMetadataAdd (row, PS_LIST_TAIL, "PLTSCALE",         PS_DATA_F32, "plate scale at source (arcsec/pixel)",       outputs.pltScale);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_MAG",     PS_DATA_F32, "PSF fit instrumental magnitude",             source->psfMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_MAG_SIG", PS_DATA_F32, "Sigma of PSF instrumental magnitude",        source->psfMagErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_FLUX",    PS_DATA_F32, "PSF fit instrumental flux (counts)",         source->psfFlux);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_FLUX_SIG",PS_DATA_F32, "Sigma of PSF instrumental flux",             source->psfFluxErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG",           PS_DATA_F32, "magnitude in standard aperture",             source->apMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RAW",       PS_DATA_F32, "magnitude in reported aperture",             source->apMagRaw);
-        psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RADIUS",    PS_DATA_F32, "radius used for aperture mags",              outputs.apRadius);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "CAL_PSF_MAG",      PS_DATA_F32, "PSF Magnitude using supplied calibration",   outputs.calMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "CAL_PSF_MAG_SIG",  PS_DATA_F32, "measured scatter of zero point calibration", zeroptErr);
-	
-	// NOTE: RA & DEC (both double) need to be on an 8-byte boundary...
-        psMetadataAdd (row, PS_LIST_TAIL, "RA_PSF",           PS_DATA_F64, "PSF RA coordinate (degrees)",                outputs.ra);
-        psMetadataAdd (row, PS_LIST_TAIL, "DEC_PSF",          PS_DATA_F64, "PSF DEC coordinate (degrees)",               outputs.dec);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "PEAK_FLUX_AS_MAG", PS_DATA_F32, "Peak flux expressed as magnitude",           outputs.peakMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "SKY",              PS_DATA_F32, "Sky level",                                  source->sky);
-        psMetadataAdd (row, PS_LIST_TAIL, "SKY_SIGMA",        PS_DATA_F32, "Sigma of sky level",                         source->skyErr);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_CHISQ",        PS_DATA_F32, "Chisq of PSF-fit",                           outputs.chisq);
-        psMetadataAdd (row, PS_LIST_TAIL, "CR_NSIGMA",        PS_DATA_F32, "Nsigma deviations from PSF to CF",           source->crNsigma);
-        psMetadataAdd (row, PS_LIST_TAIL, "EXT_NSIGMA",       PS_DATA_F32, "Nsigma deviations from PSF to EXT",          source->extNsigma);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_MAJOR",        PS_DATA_F32, "PSF width (major axis)",                     outputs.psfMajor);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_MINOR",        PS_DATA_F32, "PSF width (minor axis)",                     outputs.psfMinor);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_THETA",        PS_DATA_F32, "PSF orientation angle",                      outputs.psfTheta);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor (bad)",          source->pixWeightNotBad);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF_PERFECT",   PS_DATA_F32, "PSF coverage/quality factor (poor)",         source->pixWeightNotPoor);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_NDOF",         PS_DATA_S32, "degrees of freedom",                         outputs.nDOF);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_NPIX",         PS_DATA_S32, "number of pixels in fit",                    outputs.nPix);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_XX",       PS_DATA_F32, "second moments (X^2)",                       moments.Mxx);
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_XY",       PS_DATA_F32, "second moments (X*Y)",                       moments.Mxy);
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_YY",       PS_DATA_F32, "second moments (Y*Y)",                       moments.Myy);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M3C",      PS_DATA_F32, "third momemt cos theta",                     moments.M_c3);
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M3S",      PS_DATA_F32, "third momemt sin theta",                     moments.M_s3);
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M4C",      PS_DATA_F32, "fourth momemt cos theta",                    moments.M_c4);
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M4S",      PS_DATA_F32, "fourth momemt sin theta",                    moments.M_s4);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_R1",       PS_DATA_F32, "first radial moment",                        moments.Mrf);
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_RH",       PS_DATA_F32, "half radial moment",                         moments.Mrh);
-        psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX",        PS_DATA_F32, "Kron Flux (in 2.5 R1)",                      moments.Krf);
-        psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_ERR",    PS_DATA_F32, "Kron Flux Error",                            moments.dKrf);
-        psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_INNER",  PS_DATA_F32, "Kron Flux (in 2.5 R1)",                      moments.Kinner);
-        psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_OUTER",  PS_DATA_F32, "Kron Flux (in 2.5 R1)",                      moments.Kouter);
-
-	// Do NOT write these : not consistent with the definition of PS1_V3 in Ohana/src/libautocode/dev/cmf-ps1-v3.d
-        // psMetadataAdd (row, PS_LIST_TAIL, "KRON_CORE_FLUX",   PS_DATA_F32, "Kron Flux (in 1.0 R1)",                      moments.KronCore);
-	// psMetadataAdd (row, PS_LIST_TAIL, "KRON_CORE_ERROR",  PS_DATA_F32, "Kron Error (in 1.0 R1)",                     moments.KronCoreErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "FLAGS",            PS_DATA_U32, "psphot analysis flags",                      source->mode);
-        psMetadataAdd (row, PS_LIST_TAIL, "FLAGS2",           PS_DATA_U32, "psphot analysis flags",                      source->mode2);
-        psMetadataAdd (row, PS_LIST_TAIL, "PADDING2",         PS_DATA_S32, "more padding", 0);
-
-        // XXX not sure how to get this : need to load Nimages with weight?
-        psMetadataAdd (row, PS_LIST_TAIL, "N_FRAMES",         PS_DATA_U16, "Number of frames overlapping source center", nImageOverlap);
-        psMetadataAdd (row, PS_LIST_TAIL, "PADDING",          PS_DATA_S16, "padding", 0);
-
-        psArrayAdd (table, 100, row);
-        psFree (row);
-
-        // EXT_NSIGMA will be NAN if: 1) contour ellipse is imaginary; 2) source is not
-        // subtracted
-
-        // CR_NSIGMA will be NAN if: 1) source is not subtracted; 2) source is on the image
-        // edge; 3) any pixels in the 3x3 peak region are masked;
-    }
-
-    // XXX why do we make a copy here to be supplemented with the masks?  why not do this in the calling function?
-    psMetadata *header = psMetadataCopy(NULL, tableHeader);
-    pmSourceMasksHeader(header);
-
-    if (table->n == 0) {
-        if (!psFitsWriteBlank(fits, header, extname)) {
-            psError(psErrorCodeLast(), false, "Unable to write blank sources file.");
-            psFree(table);
-            psFree(header);
-            return false;
-        }
-        psFree(table);
-        psFree(header);
-        return true;
-    }
-
-    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
-    if (!psFitsWriteTable(fits, header, table, extname)) {
-        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
-        psFree(table);
-        psFree(header);
-        return false;
-    }
-    psFree(table);
-    psFree(header);
-
-    return true;
-}
-
-// read in a readout from the fits file
-psArray *pmSourcesRead_CMF_PS1_V3 (psFits *fits, psMetadata *header)
-{
-    PS_ASSERT_PTR_NON_NULL(fits, false);
-    PS_ASSERT_PTR_NON_NULL(header, false);
-
-    bool status;
-    psF32 *PAR, *dPAR;
-    psEllipseAxes axes;
-
-    // define PSF model type
-    // XXX need to carry the extra model parameters
-    int modelType = pmModelClassGetType ("PS_MODEL_GAUSS");
-
-    char *PSF_NAME = psMetadataLookupStr (&status, header, "PSF_NAME");
-    if (PSF_NAME != NULL) {
-        modelType = pmModelClassGetType (PSF_NAME);
-    }
-    assert (modelType > -1);
-
-    // We get the size of the table, and allocate the array of sources first because the table
-    // is large and ephemeral --- when the table gets blown away, whatever is allocated after
-    // the table is read blocks the free.  In fact, it's better to read the table row by row.
-    long numSources = psFitsTableSize(fits); // Number of sources in table
-    psArray *sources = psArrayAlloc(numSources); // Array of sources, to return
-
-    // convert the table to the pmSource entriesa
-    for (int i = 0; i < numSources; i++) {
-        psMetadata *row = psFitsReadTableRow(fits, i); // Table row
-        if (!row) {
-            psError(psErrorCodeLast(), false, "Unable to read row %d of sources", i);
-            psFree(sources);
-            return NULL;
-        }
-
-        pmSource *source = pmSourceAlloc ();
-        pmModel *model = pmModelAlloc (modelType);
-        source->modelPSF  = model;
-        source->type = PM_SOURCE_TYPE_STAR; // XXX this should be added to the flags
-
-        // NOTE: A SEGV here because "model" is NULL is probably caused by not initialising the models.
-        PAR = model->params->data.F32;
-        dPAR = model->dparams->data.F32;
-
-        source->seq       = psMetadataLookupU32 (&status, row, "IPP_IDET");
-        PAR[PM_PAR_XPOS]  = psMetadataLookupF32 (&status, row, "X_PSF");
-        PAR[PM_PAR_YPOS]  = psMetadataLookupF32 (&status, row, "Y_PSF");
-        dPAR[PM_PAR_XPOS] = psMetadataLookupF32 (&status, row, "X_PSF_SIG");
-        dPAR[PM_PAR_YPOS] = psMetadataLookupF32 (&status, row, "Y_PSF_SIG");
-        axes.major        = psMetadataLookupF32 (&status, row, "PSF_MAJOR");
-        axes.minor        = psMetadataLookupF32 (&status, row, "PSF_MINOR");
-        axes.theta        = psMetadataLookupF32 (&status, row, "PSF_THETA");
-
-        PAR[PM_PAR_SKY]   = psMetadataLookupF32 (&status, row, "SKY");
-        dPAR[PM_PAR_SKY]  = psMetadataLookupF32 (&status, row, "SKY_SIGMA");
-        source->sky       = PAR[PM_PAR_SKY];
-        source->skyErr    = dPAR[PM_PAR_SKY];
-
-        source->psfMag    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG");
-        source->psfMagErr = psMetadataLookupF32 (&status, row, "PSF_INST_MAG_SIG");
-        source->apMag     = psMetadataLookupF32 (&status, row, "AP_MAG");
-        source->apMagRaw  = psMetadataLookupF32 (&status, row, "AP_MAG_RAW");
-
-        // XXX use these to determine PAR[PM_PAR_I0] if they exist?
-        source->psfFlux   = psMetadataLookupF32 (&status, row, "PSF_INST_FLUX");
-        source->psfFluxErr = psMetadataLookupF32 (&status, row, "PSF_INST_FLUX_SIG");
-
-        // XXX this scaling is incorrect: does not include the 2 \pi AREA factor
-        PAR[PM_PAR_I0]    = (isfinite(source->psfMag)) ? pow(10.0, -0.4*source->psfMag) : NAN;
-        dPAR[PM_PAR_I0]   = (isfinite(source->psfMag)) ? PAR[PM_PAR_I0] * source->psfMagErr : NAN;
-
-        pmPSF_AxesToModel (PAR, axes);
-
-        float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
-        float peakFlux    = (isfinite(peakMag)) ? pow(10.0, -0.4*peakMag) : NAN;
-
-        // recreate the peak to match (xPos, yPos) +/- (xErr, yErr)
-        source->peak = pmPeakAlloc(PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], peakFlux, PM_PEAK_LONE);
-        source->peak->rawFlux = peakFlux;
-        source->peak->smoothFlux = peakFlux;
-        source->peak->xf   = PAR[PM_PAR_XPOS]; // more accurate position
-        source->peak->yf   = PAR[PM_PAR_YPOS]; // more accurate position
-        source->peak->dx   = dPAR[PM_PAR_XPOS];
-        source->peak->dy   = dPAR[PM_PAR_YPOS];
-
-	// we no longer sort by S/N, only flux
-        // if (isfinite (source->psfMagErr) && (source->psfMagErr > 0.0)) {
-        //   source->peak->SN = 1.0 / source->psfMagErr;
-        // } else {
-        //   source->peak->SN = sqrt(source->peak->flux); // an alternate proxy: various functions sort by peak S/N
-        // }
-
-        source->pixWeightNotBad = psMetadataLookupF32 (&status, row, "PSF_QF");
-        source->pixWeightNotPoor = psMetadataLookupF32 (&status, row, "PSF_QF_PERFECT");
-        source->crNsigma  = psMetadataLookupF32 (&status, row, "CR_NSIGMA");
-        source->extNsigma = psMetadataLookupF32 (&status, row, "EXT_NSIGMA");
-        source->apRadius  = psMetadataLookupS32 (&status, row, "AP_MAG_RADIUS");
-
-        // note that some older versions used PSF_PROBABILITY: this was not well defined.
-        model->chisq      = psMetadataLookupF32 (&status, row, "PSF_CHISQ");
-        model->nDOF       = psMetadataLookupS32 (&status, row, "PSF_NDOF");
-        model->nPix       = psMetadataLookupS32 (&status, row, "PSF_NPIX");
-
-        source->moments = pmMomentsAlloc ();
-        source->moments->Mx = source->peak->xf; // we don't have both Mx,My and xf,yf in the cmf
-        source->moments->My = source->peak->yf; // we don't have both Mx,My and xf,yf in the cmf
-
-        source->moments->Mxx = psMetadataLookupF32 (&status, row, "MOMENTS_XX");
-        source->moments->Mxy = psMetadataLookupF32 (&status, row, "MOMENTS_XY");
-        source->moments->Myy = psMetadataLookupF32 (&status, row, "MOMENTS_YY");
-
-        source->moments->Mrf         = psMetadataLookupF32 (&status, row, "MOMENTS_R1");
-        source->moments->Mrh         = psMetadataLookupF32 (&status, row, "MOMENTS_RH");
-        source->moments->KronFlux    = psMetadataLookupF32 (&status, row, "KRON_FLUX");
-        source->moments->KronFluxErr = psMetadataLookupF32 (&status, row, "KRON_FLUX_ERR");
-
-        source->moments->KronFinner  = psMetadataLookupF32 (&status, row, "KRON_FLUX_INNER");
-        source->moments->KronFouter  = psMetadataLookupF32 (&status, row, "KRON_FLUX_OUTER");
-
-	// XXX we do not save all of the 3rd and 4th moment parameters. when we load in data,
-	// we are storing enough information so the output will be consistent with the input
-        source->moments->Mxxx = +1.0 * psMetadataLookupF32 (&status, row, "MOMENTS_M3C");
-        source->moments->Mxxy = 0.0;
-        source->moments->Mxyy = 0.0;
-        source->moments->Myyy = -1.0 * psMetadataLookupF32 (&status, row, "MOMENTS_M3S");
-
-        source->moments->Mxxxx = +1.00 * psMetadataLookupF32 (&status, row, "MOMENTS_M4C");
-        source->moments->Mxxxy = 0.0;
-        source->moments->Mxxyy = 0.0;
-        source->moments->Mxyyy = -0.25 * psMetadataLookupF32 (&status, row, "MOMENTS_M4S");
-        source->moments->Myyyy = 0.0;
-
-        source->mode = psMetadataLookupU32 (&status, row, "FLAGS");
-        source->mode2 = psMetadataLookupU32 (&status, row, "FLAGS2");
-        assert (status);
-
-        sources->data[i] = source;
-        psFree(row);
-    }
-
-    return sources;
-}
-
-bool pmSourcesWrite_CMF_PS1_V3_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
-{
-    bool status;
-    psArray *table;
-    psMetadata *row;
-    psF32 *PAR, *dPAR;
-    psF32 xPos, yPos;
-    psF32 xErr, yErr;
-    int nRow = -1;
-    char keyword1[80], keyword2[80];
-
-    // create a header to hold the output data
-    psMetadata *outhead = psMetadataAlloc ();
-
-    // write the links to the image header
-    psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTNAME", PS_META_REPLACE, "xsrc table extension", extname);
-
-    pmChip *chip = readout->parent->parent;
-    pmFPA  *fpa  = chip->parent;
-
-    // zero point corrections
-    bool status1 = false;
-    bool status2 = false;
-    float magOffset = 0.0;
-    float exptime   = psMetadataLookupF32(&status1, fpa->concepts, "FPA.EXPOSURE");
-    float zeropt    = psMetadataLookupF32(&status2, fpa->concepts, "FPA.ZP");
-    if (!isfinite(zeropt)) {
-        zeropt    = psMetadataLookupF32 (&status2, imageHeader, "ZPT_OBS");
-    }
-    if (status1 && status2 && (exptime > 0.0)) {
-        magOffset = zeropt + 2.5*log10(exptime);
-    }
-
-    // let's write these out in S/N order
-    sources = psArraySort (sources, pmSourceSortByFlux);
-
-    table = psArrayAllocEmpty (sources->n);
-
-    // which extended source analyses should we perform?
-    bool doAnnuli       = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
-    bool doPetrosian    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
-
-    psVector *radMin = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.LOWER");
-    psVector *radMax = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.UPPER");
-    psAssert (radMin->n == radMax->n, "inconsistent annular bins");
-
-    // write the radial profile apertures to header
-    for (int i = 0; i < radMax->n; i++) {
-      sprintf (keyword1, "RMIN_%02d", i);
-      sprintf (keyword2, "RMAX_%02d", i);
-      psMetadataAddF32 (outhead, PS_LIST_TAIL, keyword1, PS_META_REPLACE, "min radius for SB profile", radMin->data.F32[i]);
-      psMetadataAddF32 (outhead, PS_LIST_TAIL, keyword2, PS_META_REPLACE, "min radius for SB profile", radMax->data.F32[i]);
-    }
-
-    // we write out all sources, regardless of quality.  the source flags tell us the state
-    for (int i = 0; i < sources->n; i++) {
-        pmSource *source = sources->data[i];
-
-        // skip sources without measurements
-        if (source->extpars == NULL) continue;
-
-        // we require a PSF model fit (ignore the real crud)
-        pmModel *model = source->modelPSF;
-        if (model == NULL) continue;
-
-        // XXX I need to split the extended models from the extended aperture measurements
-        PAR = model->params->data.F32;
-        dPAR = model->dparams->data.F32;
-        xPos = PAR[PM_PAR_XPOS];
-        yPos = PAR[PM_PAR_YPOS];
-        xErr = dPAR[PM_PAR_XPOS];
-        yErr = dPAR[PM_PAR_YPOS];
-
-        row = psMetadataAlloc ();
-
-        // XXX we are not writing out the mode (flags) or the type (psf, ext, etc)
-        psMetadataAdd (row, PS_LIST_TAIL, "IPP_IDET",         PS_DATA_U32, "IPP detection identifier index",             source->seq);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_EXT",            PS_DATA_F32, "EXT model x coordinate",                     xPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_EXT",            PS_DATA_F32, "EXT model y coordinate",                     yPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_EXT_SIG",        PS_DATA_F32, "Sigma in EXT x coordinate",                  xErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_EXT_SIG",        PS_DATA_F32, "Sigma in EXT y coordinate",                  yErr);
-
-	float AxialRatio = NAN;
-	float AxialTheta = NAN;
-	pmSourceExtendedPars *extpars = source->extpars;
-	if (extpars) {
-	    AxialRatio = extpars->axes.minor / extpars->axes.major;
-	    AxialTheta = extpars->axes.theta;
-	}
-        psMetadataAdd (row, PS_LIST_TAIL, "F25_ARATIO",       PS_DATA_F32, "Axial Ratio of radial profile",              AxialRatio);
-        psMetadataAdd (row, PS_LIST_TAIL, "F25_THETA",        PS_DATA_F32, "Angle of radial profile ellipse",                  AxialTheta);
-
-        // Petrosian measurements
-        // XXX insert header data: petrosian ref radius, flux ratio
-	// XXX check flags to see if Pet was measured
-        if (doPetrosian) {
-	    pmSourceExtendedPars *extpars = source->extpars;
-            if (extpars) {
-		// XXX note that this mag is either calibrated or instrumental depending on existence of zero point 
-		float mag = (extpars->petrosianFlux > 0.0) ? -2.5*log10(extpars->petrosianFlux) + magOffset : NAN; // XXX zero point
-		float magErr = (extpars->petrosianFlux > 0.0) ? extpars->petrosianFlux / extpars->petrosianFluxErr : NAN; // XXX zero point
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG",        PS_DATA_F32, "Petrosian Magnitude", mag);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG_ERR",    PS_DATA_F32, "Petrosian Magnitude Error", magErr);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS",     PS_DATA_F32, "Petrosian Radius (pix)", extpars->petrosianRadius);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_ERR", PS_DATA_F32, "Petrosian Radius Error (pix)", extpars->petrosianRadiusErr);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50",     PS_DATA_F32, "Petrosian R50 (pix)", extpars->petrosianR50);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50_ERR", PS_DATA_F32, "Petrosian R50 Error (pix)", extpars->petrosianR50Err);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90",     PS_DATA_F32, "Petrosian R90 (pix)", extpars->petrosianR90);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90_ERR", PS_DATA_F32, "Petrosian R90 Error (pix)", extpars->petrosianR90Err);
-            } else {
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG",        PS_DATA_F32, "Petrosian Magnitude",       NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG_ERR",    PS_DATA_F32, "Petrosian Magnitude Error", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS",     PS_DATA_F32, "Petrosian Radius",          NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_ERR", PS_DATA_F32, "Petrosian Radius Error",    NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50",     PS_DATA_F32, "Petrosian R50 (pix)", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50_ERR", PS_DATA_F32, "Petrosian R50 Error (pix)",NAN); 
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90",     PS_DATA_F32, "Petrosian R90 (pix)", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90_ERR", PS_DATA_F32, "Petrosian R90 Error (pix)",NAN); 
-            }
-        }
-
-        // Flux Annuli (if we have extended source measurements, we have these.  only optionally save them)
-        if (doAnnuli) {
-	    psVector *radSB   = psVectorAlloc(radMin->n, PS_TYPE_F32);
-	    psVector *radFlux = psVectorAlloc(radMin->n, PS_TYPE_F32);
-	    psVector *radFill = psVectorAlloc(radMin->n, PS_TYPE_F32);
-	    psVectorInit (radSB, NAN);
-	    psVectorInit (radFlux, NAN);
-	    psVectorInit (radFill, NAN);
-	    if (!source->extpars) goto empty_annuli;
-	    if (!source->extpars->radProfile) goto empty_annuli;
-	    if (!source->extpars->radProfile->binSB) goto empty_annuli;
-	    psAssert (source->extpars->radProfile->binSum, "programming error");
-	    psAssert (source->extpars->radProfile->binFill, "programming error");
-	    psAssert (source->extpars->radProfile->binSB->n <= radFlux->n, "inconsistent vector lengths");
-	    psAssert (source->extpars->radProfile->binSum->n <= radFlux->n, "inconsistent vector lengths");
-	    psAssert (source->extpars->radProfile->binFill->n <= radFlux->n, "inconsistent vector lengths");
-
-	    // copy the data from fluxVal (which is not guaranteed to be the full length) to radFlux
-	    for (int j = 0; j < source->extpars->radProfile->binSB->n; j++) {
-		radSB->data.F32[j]   = source->extpars->radProfile->binSB->data.F32[j];
-		radFlux->data.F32[j] = source->extpars->radProfile->binSum->data.F32[j];
-		radFill->data.F32[j] = source->extpars->radProfile->binFill->data.F32[j];
-	    }
-
-	empty_annuli:
-	    psMetadataAdd (row, PS_LIST_TAIL, "PROF_SB", PS_DATA_VECTOR, "mean surface brightness annuli", radSB);
-	    psMetadataAdd (row, PS_LIST_TAIL, "PROF_FLUX", PS_DATA_VECTOR, "flux within annuli", radFlux);
-	    psMetadataAdd (row, PS_LIST_TAIL, "PROF_FILL", PS_DATA_VECTOR, "fill factor of annuli", radFill);
-	    psFree (radSB);
-	    psFree (radFlux);
-	    psFree (radFill);
-	}
-	if (nRow < 0) {
-	    nRow = row->list->n;
-	} else {
-	    psAssert (nRow == row->list->n, "inconsistent row lengths");
-	}
-	psArrayAdd (table, 100, row);
-	psFree (row);
-    }
-    
-    if (table->n == 0) {
-	if (!psFitsWriteBlank (fits, outhead, extname)) {
-	    psError(psErrorCodeLast(), false, "Unable to write empty sources file.");
-	    psFree(outhead);
-	    psFree(table);
-	    return false;
-	}
-	psFree (outhead);
-	psFree (table);
-	return true;
-    }
-    
-    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
-    if (!psFitsWriteTable (fits, outhead, table, extname)) {
-	psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
-	psFree (outhead);
-    psFree(table);
-    return false;
-    }
-    psFree (outhead);
-    psFree (table);
-    
-    return true;
-}
-
-// XXX this layout is still the same as PS1_DEV_1
-bool pmSourcesWrite_CMF_PS1_V3_XFIT (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname)
-{
-
-    psArray *table;
-    psMetadata *row;
-    psF32 *PAR, *dPAR;
-    psEllipseAxes axes;
-    psF32 xPos, yPos;
-    psF32 xErr, yErr;
-    char name[64];
-
-    // create a header to hold the output data
-    psMetadata *outhead = psMetadataAlloc ();
-
-    // write the links to the image header
-    psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTNAME", PS_META_REPLACE, "xsrc table extension", extname);
-
-    // let's write these out in S/N order
-    sources = psArraySort (sources, pmSourceSortByFlux);
-
-    // we are writing one row per model; we need to write out same number of columns for each row: find the max Nparams
-    int nParamMax = 0;
-    for (int i = 0; i < sources->n; i++) {
-        pmSource *source = sources->data[i];
-        if (source->modelFits == NULL) continue;
-        for (int j = 0; j < source->modelFits->n; j++) {
-            pmModel *model = source->modelFits->data[j];
-            assert (model);
-            nParamMax = PS_MAX (nParamMax, model->params->n);
-        }
-    }
-
-    table = psArrayAllocEmpty (sources->n);
-
-    // we write out all sources, regardless of quality.  the source flags tell us the state
-    for (int i = 0; i < sources->n; i++) {
-
-        pmSource *source = sources->data[i];
-
-        // XXX if no model fits are saved, write out modelEXT?
-        if (source->modelFits == NULL) continue;
-
-        // We have multiple sources : need to flag the one used to subtract the light (the 'best' model)
-        for (int j = 0; j < source->modelFits->n; j++) {
-
-            // choose the convolved EXT model, if available, otherwise the simple one
-            pmModel *model = source->modelFits->data[j];
-            assert (model);
-
-	    // skip models which were not actually fitted
-	    if (model->flags & PM_MODEL_STATUS_BADARGS) continue;
-
-            PAR = model->params->data.F32;
-            dPAR = model->dparams->data.F32;
-            xPos = PAR[PM_PAR_XPOS];
-            yPos = PAR[PM_PAR_YPOS];
-            xErr = dPAR[PM_PAR_XPOS];
-            yErr = dPAR[PM_PAR_YPOS];
-
-            axes = pmPSF_ModelToAxes (PAR, 20.0);
-
-            row = psMetadataAlloc ();
-
-            // XXX we are not writing out the mode (flags) or the type (psf, ext, etc)
-            psMetadataAddU32 (row, PS_LIST_TAIL, "IPP_IDET",         0, "IPP detection identifier index",             source->seq);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "X_EXT",            0, "EXT model x coordinate",                     xPos);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "Y_EXT",            0, "EXT model y coordinate",                     yPos);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "X_EXT_SIG",        0, "Sigma in EXT x coordinate",                  xErr);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "Y_EXT_SIG",        0, "Sigma in EXT y coordinate",                  yErr);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_INST_MAG",     0, "EXT fit instrumental magnitude",             model->mag);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_INST_MAG_SIG", 0, "Sigma of PSF instrumental magnitude",        model->magErr);
-
-            psMetadataAddF32 (row, PS_LIST_TAIL, "NPARAMS",          0, "number of model parameters",                 model->params->n);
-            psMetadataAddStr (row, PS_LIST_TAIL, "MODEL_TYPE",       0, "name of model",                              pmModelClassGetName (model->type));
-
-            // XXX these should be major and minor, not 'x' and 'y'
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_WIDTH_MAJ",    0, "EXT width in x coordinate",                  axes.major);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_WIDTH_MIN",    0, "EXT width in y coordinate",                  axes.minor);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_THETA",        0, "EXT orientation angle",                      axes.theta);
-
-            // write out the other generic parameters
-            for (int k = 0; k < nParamMax; k++) {
-                if (k == PM_PAR_I0) continue;
-                if (k == PM_PAR_SKY) continue;
-                if (k == PM_PAR_XPOS) continue;
-                if (k == PM_PAR_YPOS) continue;
-                if (k == PM_PAR_SXX) continue;
-                if (k == PM_PAR_SXY) continue;
-                if (k == PM_PAR_SYY) continue;
-
-                snprintf (name, 64, "EXT_PAR_%02d", k);
-
-                if (k < model->params->n) {
-                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "", model->params->data.F32[k]);
-                } else {
-                    psMetadataAddF32 (row, PS_LIST_TAIL, name, PS_DATA_F32, "", NAN);
-                }
-            }
-
-            // XXX other parameters which may be set.
-            // XXX flag / value to define the model
-            // XXX write out the model type, fit status flags
-
-            psArrayAdd (table, 100, row);
-            psFree (row);
-        }
-    }
-
-    if (table->n == 0) {
-        if (!psFitsWriteBlank (fits, outhead, extname)) {
-            psError(psErrorCodeLast(), false, "Unable to write empty sources file.");
-            psFree(outhead);
-            psFree(table);
-            return false;
-        }
-        psFree (outhead);
-        psFree (table);
-        return true;
-    }
-
-    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
-    if (!psFitsWriteTable (fits, outhead, table, extname)) {
-        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
-        psFree (outhead);
-        psFree(table);
-        return false;
-    }
-    psFree (outhead);
-    psFree (table);
-    return true;
-}
-
-bool pmSourcesWrite_CMF_PS1_V3_XRAD(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
-{
-    return true;
-}
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMP.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMP.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMP.c	(revision 33415)
@@ -135,5 +135,5 @@
         lsky = (source->sky < 1.0) ? 0.0 : log10(source->sky);
 
-        axes = pmPSF_ModelToAxes (PAR, 20.0);
+        axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 
         float psfMagErr = isfinite(source->psfMagErr) ? source->psfMagErr : 999;
@@ -293,5 +293,5 @@
                 goto skip_source;
 
-            pmPSF_AxesToModel (PAR, axes);
+            pmPSF_AxesToModel (PAR, axes, modelType);
 
             psArrayAdd (sources, 100, source);
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_OBJ.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_OBJ.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_OBJ.c	(revision 33415)
@@ -91,5 +91,5 @@
         }
 
-        axes = pmPSF_ModelToAxes (PAR, 20.0);
+        axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 
         psLineInit (line);
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_PS1_CAL_0.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_PS1_CAL_0.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_PS1_CAL_0.c	(revision 33415)
@@ -113,5 +113,5 @@
             yErr = dPAR[PM_PAR_YPOS];
 	    if (isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SXX])) {
-		axes = pmPSF_ModelToAxes (PAR, 20.0);
+		axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 	    } else {
 		axes.major = NAN;
@@ -288,5 +288,5 @@
 	dPAR[PM_PAR_I0]   = (isfinite(source->psfMag)) ? PAR[PM_PAR_I0] * source->psfMagErr : NAN;
 
-        pmPSF_AxesToModel (PAR, axes);
+        pmPSF_AxesToModel (PAR, axes, modelType);
 
         float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
@@ -618,5 +618,5 @@
 	    yErr = dPAR[PM_PAR_YPOS];
 
-	    axes = pmPSF_ModelToAxes (PAR, 20.0);
+	    axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 
 	    // generate RA,DEC
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_PS1_DEV_0.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_PS1_DEV_0.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_PS1_DEV_0.c	(revision 33415)
@@ -89,5 +89,5 @@
             yErr = dPAR[PM_PAR_YPOS];
 
-            axes = pmPSF_ModelToAxes (PAR, 20.0);
+            axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
         } else {
             // XXX: This code seg faults if source->peak is NULL.
@@ -214,5 +214,5 @@
         source->psfMagErr    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG_SIG");
 
-        pmPSF_AxesToModel (PAR, axes);
+        pmPSF_AxesToModel (PAR, axes, modelType);
 
         float peakMag = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_PS1_DEV_1.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_PS1_DEV_1.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_PS1_DEV_1.c	(revision 33415)
@@ -95,5 +95,5 @@
             yErr = dPAR[PM_PAR_YPOS];
 	    if (isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SXX])) {
-		axes = pmPSF_ModelToAxes (PAR, 20.0);
+		axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 	    } else {
 		axes.major = NAN;
@@ -257,5 +257,5 @@
 	dPAR[PM_PAR_I0]   = (isfinite(source->psfMag)) ? PAR[PM_PAR_I0] * source->psfMagErr : NAN;
 
-        pmPSF_AxesToModel (PAR, axes);
+        pmPSF_AxesToModel (PAR, axes, modelType);
 
         float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
@@ -522,5 +522,5 @@
 	    yErr = dPAR[PM_PAR_YPOS];
 
-	    axes = pmPSF_ModelToAxes (PAR, 20.0);
+	    axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 
 	    row = psMetadataAlloc ();
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_SMPDATA.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_SMPDATA.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_SMPDATA.c	(revision 33415)
@@ -92,5 +92,5 @@
 	    lsky = (source->sky < 1.0) ? 0.0 : log10(source->sky);
 
-	    axes = pmPSF_ModelToAxes (PAR, 20.0);
+	    axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 
 	} else {
@@ -190,5 +190,5 @@
         axes.theta       = psMetadataLookupF32 (&status, row, "THETA");
 
-	pmPSF_AxesToModel (PAR, axes);
+	pmPSF_AxesToModel (PAR, axes, modelType);
 
         source->psfMag = psMetadataLookupF32 (&status, row, "MAG_RAW") - ZERO_POINT;
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_SX.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_SX.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_SX.c	(revision 33415)
@@ -81,5 +81,5 @@
         // pmSourceSextractType (source, &type, &flags);
 
-        axes = pmPSF_ModelToAxes (PAR, 20.0);
+        axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 
         psLineInit (line);
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceMasks.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceMasks.h	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceMasks.h	(revision 33415)
@@ -46,4 +46,5 @@
     PM_SOURCE_MODE2_DIFF_WITH_DOUBLE = 0x00000002, ///< diff source matched to positive detections in both images
     PM_SOURCE_MODE2_MATCHED          = 0x00000004, ///< diff source matched to positive detections in both images
+    PM_SOURCE_MODE2_DIFF_SELF_MATCH  = 0x00000800, ///< positive detection match is probably this source 
 
     PM_SOURCE_MODE2_ON_SPIKE         = 0x00000008, ///< > 25% of (PSF-weighted) pixels land on diffraction spike
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceMoments.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceMoments.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceMoments.c	(revision 33415)
@@ -96,11 +96,18 @@
     // (int) so they can be used in the image index below.
 
-    // do 2 passes : the first pass should use a somewhat smaller radius and no sigma window to 
-    // get an unbiased (but probably noisy) centroid
-    if (!pmSourceMomentsGetCentroid (source, 0.75*radius, 0.0, minSN, maskVal, source->peak->xf, source->peak->yf)) {
-	return false;
-    }
-    // second pass applies the Gaussian window and uses the centroid from the first pass
-    if (!pmSourceMomentsGetCentroid (source, radius, sigma, minSN, maskVal, source->moments->Mx, source->moments->My)) {
+    // XXX // do 2 passes : the first pass should use a somewhat smaller radius and no sigma window to 
+    // XXX // get an unbiased (but probably noisy) centroid
+    // XXX if (!pmSourceMomentsGetCentroid (source, 0.75*radius, 0.0, minSN, maskVal, source->peak->xf, source->peak->yf)) {
+    // XXX 	return false;
+    // XXX }
+    // XXX // second pass applies the Gaussian window and uses the centroid from the first pass
+    // XXX if (!pmSourceMomentsGetCentroid (source, radius, sigma, minSN, maskVal, source->moments->Mx, source->moments->My)) {
+    // XXX 	return false;
+    // XXX }
+
+    // If we use a large radius for the centroid, it will be biased by any neighbors.  The flux
+    // of any object drops pretty quickly outside 1-2 sigmas.  (The exception is bright
+    // saturated stars, for which we need to use a very large radius here)
+    if (!pmSourceMomentsGetCentroid (source, 1.5*sigma, 0.0, minSN, maskVal, source->peak->xf, source->peak->yf)) {
 	return false;
     }
@@ -108,11 +115,4 @@
     // Now calculate higher-order moments, using the above-calculated first moments to adjust coordinates
     // Xn  = SUM (x - xc)^n * (z - sky)
-
-    float RFW = 0.0;
-    float RHW = 0.0;
-
-    float RF = 0.0;
-    float RH = 0.0;
-    float RS = 0.0;
     float XX = 0.0;
     float XY = 0.0;
@@ -143,4 +143,5 @@
     float yCM = Yo - 0.5 - source->pixels->row0; // coord of peak in subimage
 
+    // calculate the higher-order moments using Xo,Yo
     for (psS32 row = 0; row < source->pixels->numRows ; row++) {
 
@@ -194,12 +195,5 @@
 	    Sum += pDiff;
 
-	    // Kron Flux uses the 1st radial moment (NOT Gaussian windowed?)
 	    float r = sqrt(r2);
-	    float rf = r * fDiff;
-	    float rh = sqrt(r) * fDiff;
-	    float rs = fDiff;
-
-	    float rfw = r * pDiff;
-	    float rhw = sqrt(r) * pDiff;
 
 	    float x = xDiff * pDiff;
@@ -221,11 +215,4 @@
 	    float yyyy = yDiff * yyy / r2;
 
-	    RF  += rf;
-	    RH  += rh;
-	    RS  += rs;
-
-	    RFW  += rfw;
-	    RHW  += rhw;
-
 	    XX  += xx;
 	    XY  += xy;
@@ -242,10 +229,22 @@
 	    XYYY  += xyyy;
 	    YYYY  += yyyy;
+
+	    // Kron Flux uses the 1st radial moment (NOT Gaussian windowed?)
+	    // XXX float r = sqrt(r2);
+	    // XXX float rf = r * fDiff;
+	    // XXX float rh = sqrt(r) * fDiff;
+	    // XXX float rs = fDiff;
+	    // XXX 
+	    // XXX float rfw = r * pDiff;
+	    // XXX float rhw = sqrt(r) * pDiff;
+	    // XXX 
+	    // XXX RF  += rf;
+	    // XXX RH  += rh;
+	    // XXX RS  += rs;
+	    // XXX 
+	    // XXX RFW  += rfw;
+	    // XXX RHW  += rhw;
 	}
     }
-
-    source->moments->Mrf = RF/RS;
-    source->moments->Mrh = RH/RS;
-
     source->moments->Mxx = XX/Sum;
     source->moments->Mxy = XY/Sum;
@@ -262,4 +261,64 @@
     source->moments->Mxyyy = XYYY/Sum;
     source->moments->Myyyy = YYYY/Sum;
+
+    // *** now calculate the 1st radial moment (for kron flux) -- symmetrical averaging
+
+    float **vPix = source->pixels->data.F32;
+    float **vWgt = source->variance->data.F32;
+    psImageMaskType  **vMsk = (source->maskObj == NULL) ? NULL : source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA;
+
+    float RF = 0.0;
+    float RH = 0.0;
+    float RS = 0.0;
+
+    for (psS32 row = 0; row < source->pixels->numRows ; row++) {
+
+	float yDiff = row - yCM;
+	if (fabs(yDiff) > radius) continue;
+
+	// coordinate of mirror pixel
+	int yFlip = yCM - yDiff;
+	if (yFlip < 0) continue;
+	if (yFlip >= source->pixels->numRows) continue;
+
+	for (psS32 col = 0; col < source->pixels->numCols ; col++) {
+	    // check mask and value for this pixel
+	    if (vMsk && (vMsk[row][col] & maskVal)) continue;
+	    if (isnan(vPix[row][col])) continue;
+
+	    float xDiff = col - xCM;
+	    if (fabs(xDiff) > radius) continue;
+
+	    // coordinate of mirror pixel
+	    int xFlip = xCM - xDiff;
+	    if (xFlip < 0) continue;
+	    if (xFlip >= source->pixels->numCols) continue;
+
+	    // check mask and value for mirror pixel
+	    if (vMsk && (vMsk[yFlip][xFlip] & maskVal)) continue;
+	    if (isnan(vPix[yFlip][xFlip])) continue;
+
+	    // radius is just a function of (xDiff, yDiff)
+	    float r2  = PS_SQR(xDiff) + PS_SQR(yDiff);
+	    if (r2 > R2) continue;
+
+	    float fDiff1 = vPix[row][col] - sky;
+	    float fDiff2 = vPix[yFlip][xFlip] - sky;
+	    float pDiff = (fDiff1 > 0.0) ? sqrt(fabs(fDiff1*fDiff2)) : -sqrt(fabs(fDiff1*fDiff2));
+
+	    // Kron Flux uses the 1st radial moment (NOT Gaussian windowed?)
+	    float r = sqrt(r2);
+	    float rf = r * pDiff;
+	    float rh = sqrt(r) * pDiff;
+	    float rs = 0.5 * (fDiff1 + fDiff2);
+
+	    RF  += rf;
+	    RH  += rh;
+	    RS  += rs;
+	}
+    }
+
+    source->moments->Mrf = RF/RS;
+    source->moments->Mrh = RH/RS;
 
     // if Mrf (first radial moment) is very small, we are getting into low-significance
@@ -270,4 +329,6 @@
 	kronRefRadius = MIN(radius, kronRefRadius);
     }
+
+    // *** now calculate the kron flux values using the 1st radial moment
 
     float radKinner = 1.0*kronRefRadius;
@@ -283,14 +344,130 @@
     float SumOuter = 0.0;
 
+    // calculate the Kron flux, and related fluxes (NO symmetrical averaging)
     for (psS32 row = 0; row < source->pixels->numRows ; row++) {
-
+	
 	float yDiff = row - yCM;
 	if (fabs(yDiff) > radKouter) continue;
+	
+	for (psS32 col = 0; col < source->pixels->numCols ; col++) {
+	    // check mask and value for this pixel
+	    if (vMsk && (vMsk[row][col] & maskVal)) continue;
+	    if (isnan(vPix[row][col])) continue;
+	    
+	    float xDiff = col - xCM;
+	    if (fabs(xDiff) > radKouter) continue;
+	    
+	    // radKron is just a function of (xDiff, yDiff)
+	    float r2  = PS_SQR(xDiff) + PS_SQR(yDiff);
+
+	    float fDiff1 = vPix[row][col] - sky;
+	    float pDiff = fDiff1;
+	    float wDiff = vWgt[row][col];
+				    
+	    // skip pixels below specified significance level.  this is allowed, but should be
+	    // avoided -- the over-weights the wings of bright stars compared to those of faint
+	    // stars.
+	    if (PS_SQR(pDiff) < minSN2*wDiff) continue;
+	    
+	    float r  = sqrt(r2);
+	    if (r < radKron) {
+		Sum += pDiff;
+		Var += wDiff;
+		nKronPix ++;
+		// if (beVerbose) fprintf (stderr, "mome: %d %d  %f  %f  %f\n", col, row, sky, *vPix, Sum);
+	    }
+
+	    // use sigma (fixed by psf) not a radKron based value
+	    if (r < sigma) {
+		SumCore += pDiff;
+		VarCore += wDiff;
+		nCorePix ++;
+	    }
+
+	    if ((r > radKinner) && (r < radKron)) {
+		SumInner += pDiff;
+		nInner ++;
+	    }
+	    if ((r > radKron)  && (r < radKouter)) {
+		SumOuter += pDiff;
+		nOuter ++;
+	    }
+	}
+    }
+    // *** should I rescale these fluxes by pi R^2 / nNpix?
+    // XXX source->moments->KronCore    = SumCore       * M_PI * PS_SQR(sigma) / nCorePix;
+    // XXX source->moments->KronCoreErr = sqrt(VarCore) * M_PI * PS_SQR(sigma) / nCorePix;
+    // XXX source->moments->KronFlux    = Sum       * M_PI * PS_SQR(radKron) / nKronPix;
+    // XXX source->moments->KronFluxErr = sqrt(Var) * M_PI * PS_SQR(radKron) / nKronPix;
+    // XXX source->moments->KronFinner = SumInner * M_PI * (PS_SQR(radKron)   - PS_SQR(radKinner)) / nInner;
+    // XXX source->moments->KronFouter = SumOuter * M_PI * (PS_SQR(radKouter) -   PS_SQR(radKron)) / nOuter;
+
+    source->moments->KronCore    = SumCore;
+    source->moments->KronCoreErr = sqrt(VarCore);
+    source->moments->KronFlux    = Sum;
+    source->moments->KronFluxErr = sqrt(Var);
+    source->moments->KronFinner = SumInner;
+    source->moments->KronFouter = SumOuter;
+
+    // XXX not sure I should save this here...
+    source->moments->KronFluxPSF    = source->moments->KronFlux;
+    source->moments->KronFluxPSFErr = source->moments->KronFluxErr;
+    source->moments->KronRadiusPSF  = source->moments->Mrf;
+
+    psTrace ("psModules.objects", 4, "Mrf: %f  KronFlux: %f  Mxx: %f  Mxy: %f  Myy: %f  Mxxx: %f  Mxxy: %f  Mxyy: %f  Myyy: %f  Mxxxx: %f  Mxxxy: %f  Mxxyy: %f  Mxyyy: %f  Mxyyy: %f\n",
+	     source->moments->Mrf,   source->moments->KronFlux, 
+	     source->moments->Mxx,   source->moments->Mxy,   source->moments->Myy,
+	     source->moments->Mxxx,  source->moments->Mxxy,  source->moments->Mxyy,  source->moments->Myyy,
+	     source->moments->Mxxxx, source->moments->Mxxxy, source->moments->Mxxyy, source->moments->Mxyyy, source->moments->Myyyy);
+
+    psTrace ("psModules.objects", 3, "peak %f %f (%f = %f) Mx: %f  My: %f  Sum: %f  Mxx: %f  Mxy: %f  Myy: %f  sky: %f  Npix: %d\n",
+	     source->peak->xf, source->peak->yf, source->peak->rawFlux, sqrt(source->peak->detValue), source->moments->Mx,   source->moments->My, Sum, source->moments->Mxx,   source->moments->Mxy,   source->moments->Myy, sky, source->moments->nPixels);
+
+    return(true);
+}
+
+bool pmSourceMomentsGetCentroid(pmSource *source, float radius, float sigma, float minSN, psImageMaskType maskVal, float xGuess, float yGuess) { 
+
+    // First Pass: calculate the first moments (these are subtracted from the coordinates below)
+    // Sum = SUM (z - sky)
+    // X1  = SUM (x - xc)*(z - sky)
+    // .. etc
+
+    float sky = 0.0;
+
+    float peakPixel = -PS_MAX_F32;
+    psS32 numPixels = 0;
+    float Sum = 0.0;
+    float Var = 0.0;
+    float X1 = 0.0;
+    float Y1 = 0.0;
+    float R2 = PS_SQR(radius);
+    float minSN2 = PS_SQR(minSN);
+    float rsigma2 = 0.5 / PS_SQR(sigma);
+
+    float xPeak = xGuess - source->pixels->col0; // coord of peak in subimage
+    float yPeak = yGuess - source->pixels->row0; // coord of peak in subimage
+
+    // we are guaranteed to have a valid pixel and variance at this location (right? right?)
+    // float weightNorm = source->pixels->data.F32[yPeak][xPeak] / sqrt (source->variance->data.F32[yPeak][xPeak]);
+    // psAssert (isfinite(source->pixels->data.F32[yPeak][xPeak]), "peak must be on valid pixel");
+    // psAssert (isfinite(source->variance->data.F32[yPeak][xPeak]), "peak must be on valid pixel");
+    // psAssert (source->variance->data.F32[yPeak][xPeak] > 0, "peak must be on valid pixel");
+
+    // the moments [Sum(x*f) / Sum(f)] are calculated in pixel index values, and should
+    // not depend on the fractional pixel location of the source.  However, the aperture
+    // (radius) and the Gaussian window (sigma) depend subtly on the fractional pixel
+    // position of the expected centroid
+
+    for (psS32 row = 0; row < source->pixels->numRows ; row++) {
+
+	float yDiff = row + 0.5 - yPeak;
+	if (fabs(yDiff) > radius) continue;
 
 	float *vPix = source->pixels->data.F32[row];
 	float *vWgt = source->variance->data.F32[row];
 
-	psImageMaskType  *vMsk = (source->maskObj == NULL) ? NULL : source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[row];
-	// psImageMaskType  *vMsk = (source->maskView == NULL) ? NULL : source->maskView->data.PS_TYPE_IMAGE_MASK_DATA[row];
+	psImageMaskType *vMsk = (source->maskObj == NULL) ? NULL : source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[row];
+	// psImageMaskType *vMsk = (source->maskView == NULL) ? NULL : source->maskView->data.PS_TYPE_IMAGE_MASK_DATA[row];
 
 	for (psS32 col = 0; col < source->pixels->numCols ; col++, vPix++, vWgt++) {
@@ -304,117 +481,4 @@
 	    if (isnan(*vPix)) continue;
 
-	    float xDiff = col - xCM;
-	    if (fabs(xDiff) > radKouter) continue;
-
-	    // radKron is just a function of (xDiff, yDiff)
-	    float r2  = PS_SQR(xDiff) + PS_SQR(yDiff);
-
-	    float pDiff = *vPix - sky;
-	    float wDiff = *vWgt;
-
-	    // skip pixels below specified significance level.  this is allowed, but should be
-	    // avoided -- the over-weights the wings of bright stars compared to those of faint
-	    // stars.
-	    if (PS_SQR(pDiff) < minSN2*wDiff) continue;
-
-	    float r  = sqrt(r2);
-	    if (r < radKron) {
-		Sum += pDiff;
-		Var += wDiff;
-		nKronPix ++;
-		// if (beVerbose) fprintf (stderr, "mome: %d %d  %f  %f  %f\n", col, row, sky, *vPix, Sum);
-	    }
-
-	    // use sigma (fixed by psf) not a radKron based value
-	    if (r < sigma) {
-		SumCore += pDiff;
-		VarCore += wDiff;
-		nCorePix ++;
-	    }
-
-	    if ((r > radKinner) && (r < radKron)) {
-		SumInner += pDiff;
-		nInner ++;
-	    }
-	    if ((r > radKron)  && (r < radKouter)) {
-		SumOuter += pDiff;
-		nOuter ++;
-	    }
-	}
-    }
-    // *** should I rescale these fluxes by pi R^2 / nNpix?
-    source->moments->KronCore    = SumCore       * M_PI * PS_SQR(sigma) / nCorePix;
-    source->moments->KronCoreErr = sqrt(VarCore) * M_PI * PS_SQR(sigma) / nCorePix;
-    source->moments->KronFlux    = Sum       * M_PI * PS_SQR(radKron) / nKronPix;
-    source->moments->KronFluxErr = sqrt(Var) * M_PI * PS_SQR(radKron) / nKronPix;
-    source->moments->KronFinner = SumInner * M_PI * (PS_SQR(radKron)   - PS_SQR(radKinner)) / nInner;
-    source->moments->KronFouter = SumOuter * M_PI * (PS_SQR(radKouter) -   PS_SQR(radKron)) / nOuter;
-
-    psTrace ("psModules.objects", 4, "Mrf: %f  KronFlux: %f  Mxx: %f  Mxy: %f  Myy: %f  Mxxx: %f  Mxxy: %f  Mxyy: %f  Myyy: %f  Mxxxx: %f  Mxxxy: %f  Mxxyy: %f  Mxyyy: %f  Mxyyy: %f\n",
-	     source->moments->Mrf,   source->moments->KronFlux, 
-	     source->moments->Mxx,   source->moments->Mxy,   source->moments->Myy,
-	     source->moments->Mxxx,  source->moments->Mxxy,  source->moments->Mxyy,  source->moments->Myyy,
-	     source->moments->Mxxxx, source->moments->Mxxxy, source->moments->Mxxyy, source->moments->Mxyyy, source->moments->Myyyy);
-
-    psTrace ("psModules.objects", 3, "peak %f %f (%f = %f) Mx: %f  My: %f  Sum: %f  Mxx: %f  Mxy: %f  Myy: %f  sky: %f  Npix: %d\n",
-	     source->peak->xf, source->peak->yf, source->peak->rawFlux, sqrt(source->peak->detValue), source->moments->Mx,   source->moments->My, Sum, source->moments->Mxx,   source->moments->Mxy,   source->moments->Myy, sky, source->moments->nPixels);
-
-    return(true);
-}
-
-bool pmSourceMomentsGetCentroid(pmSource *source, float radius, float sigma, float minSN, psImageMaskType maskVal, float xGuess, float yGuess) { 
-
-    // First Pass: calculate the first moments (these are subtracted from the coordinates below)
-    // Sum = SUM (z - sky)
-    // X1  = SUM (x - xc)*(z - sky)
-    // .. etc
-
-    float sky = 0.0;
-
-    float peakPixel = -PS_MAX_F32;
-    psS32 numPixels = 0;
-    float Sum = 0.0;
-    float Var = 0.0;
-    float X1 = 0.0;
-    float Y1 = 0.0;
-    float R2 = PS_SQR(radius);
-    float minSN2 = PS_SQR(minSN);
-    float rsigma2 = 0.5 / PS_SQR(sigma);
-
-    float xPeak = xGuess - source->pixels->col0; // coord of peak in subimage
-    float yPeak = yGuess - source->pixels->row0; // coord of peak in subimage
-
-    // we are guaranteed to have a valid pixel and variance at this location (right? right?)
-    // float weightNorm = source->pixels->data.F32[yPeak][xPeak] / sqrt (source->variance->data.F32[yPeak][xPeak]);
-    // psAssert (isfinite(source->pixels->data.F32[yPeak][xPeak]), "peak must be on valid pixel");
-    // psAssert (isfinite(source->variance->data.F32[yPeak][xPeak]), "peak must be on valid pixel");
-    // psAssert (source->variance->data.F32[yPeak][xPeak] > 0, "peak must be on valid pixel");
-
-    // the moments [Sum(x*f) / Sum(f)] are calculated in pixel index values, and should
-    // not depend on the fractional pixel location of the source.  However, the aperture
-    // (radius) and the Gaussian window (sigma) depend subtly on the fractional pixel
-    // position of the expected centroid
-
-    for (psS32 row = 0; row < source->pixels->numRows ; row++) {
-
-	float yDiff = row + 0.5 - yPeak;
-	if (fabs(yDiff) > radius) continue;
-
-	float *vPix = source->pixels->data.F32[row];
-	float *vWgt = source->variance->data.F32[row];
-
-	psImageMaskType *vMsk = (source->maskObj == NULL) ? NULL : source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[row];
-	// psImageMaskType *vMsk = (source->maskView == NULL) ? NULL : source->maskView->data.PS_TYPE_IMAGE_MASK_DATA[row];
-
-	for (psS32 col = 0; col < source->pixels->numCols ; col++, vPix++, vWgt++) {
-	    if (vMsk) {
-		if (*vMsk & maskVal) {
-		    vMsk++;
-		    continue;
-		}
-		vMsk++;
-	    }
-	    if (isnan(*vPix)) continue;
-
 	    float xDiff = col + 0.5 - xPeak;
 	    if (fabs(xDiff) > radius) continue;
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceOutputs.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceOutputs.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceOutputs.c	(revision 33415)
@@ -107,5 +107,5 @@
 	}
 	if (isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SXY]) && isfinite(PAR[PM_PAR_SYY])) {
-	    axes = pmPSF_ModelToAxes (PAR, 20.0);
+	    axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 	    outputs->psfMajor = axes.major;
 	    outputs->psfMinor = axes.minor;
@@ -178,5 +178,67 @@
     moments->KronCore    = source->moments ? source->moments->KronCore : NAN;
     moments->KronCoreErr = source->moments ? source->moments->KronCoreErr : NAN;
-
-    return true;
-}
+    moments->KronPSF    = source->moments ? source->moments->KronFluxPSF : NAN;
+    moments->KronPSFErr = source->moments ? source->moments->KronFluxPSFErr : NAN;
+
+    return true;
+}
+
+bool pmSourceLocalAstrometry (psSphere *ptSky, float *posAngle, float *pltScale, pmChip *chip, float xPos, float yPos) {
+
+    pmFPA *fpa = chip->parent;
+
+    if (!chip->toFPA) goto escape;
+    if (!fpa->toTPA) goto escape;
+    if (!fpa->toSky) goto escape;
+
+    // generate RA,DEC
+    psPlane ptCH, ptFP, ptTP_o, ptTP_x, ptTP_y;
+
+    // calculate the astrometry for the coordinate of interest
+    ptCH.x = xPos;
+    ptCH.y = yPos;
+    psPlaneTransformApply (&ptFP, chip->toFPA, &ptCH);
+    psPlaneTransformApply (&ptTP_o, fpa->toTPA, &ptFP);
+    psDeproject (ptSky, &ptTP_o, fpa->toSky);
+
+    // calculate the astrometry for the coordinate + 1pix in X
+    ptCH.x = xPos + 1.0;
+    ptCH.y = yPos;
+    psPlaneTransformApply (&ptFP, chip->toFPA, &ptCH);
+    psPlaneTransformApply (&ptTP_x, fpa->toTPA, &ptFP);
+
+    // calculate the astrometry for the coordinate + 1pix in Y
+    ptCH.x = xPos;
+    ptCH.y = yPos + 1.0;
+    psPlaneTransformApply (&ptFP, chip->toFPA, &ptCH);
+    psPlaneTransformApply (&ptTP_y, fpa->toTPA, &ptFP);
+
+    // the resulting Tangent Plane coordinates are in TP pixels; convert to local Tangent Plane
+    // degrees
+
+    float dTPx_dCHx = fpa->toSky->Xs * (ptTP_x.x - ptTP_o.x);
+    float dTPy_dCHx = fpa->toSky->Ys * (ptTP_x.y - ptTP_o.y);
+
+    float dTPx_dCHy = fpa->toSky->Xs * (ptTP_y.x - ptTP_o.x);
+    float dTPy_dCHy = fpa->toSky->Ys * (ptTP_y.y - ptTP_o.y);
+
+    float pltScale_x = hypot(dTPx_dCHx, dTPy_dCHx);
+    float pltScale_y = hypot(dTPx_dCHy, dTPy_dCHy);
+    *pltScale = 0.5*(pltScale_x + pltScale_y);
+
+    float posAngle_x = atan2 (+dTPy_dCHx, +dTPx_dCHx);
+    float posAngle_y = atan2 (-dTPy_dCHy, +dTPx_dCHy);
+    *posAngle = 0.5*(posAngle_x + posAngle_y);
+
+    return true;
+
+escape:
+    // no astrometry calibration, give up
+    ptSky->r = NAN;
+    ptSky->d = NAN;
+    *posAngle = NAN;
+    *pltScale = NAN;
+
+    return false;
+}
+
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceOutputs.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceOutputs.h	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceOutputs.h	(revision 33415)
@@ -52,4 +52,6 @@
     float KronCore;
     float KronCoreErr;
+    float KronPSF;
+    float KronPSFErr;
 } pmSourceOutputsMoments;
 
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourcePhotometry.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourcePhotometry.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourcePhotometry.c	(revision 33415)
@@ -56,4 +56,6 @@
 static psImageMaskType maskBurntool = 0;
 static psImageMaskType maskConvPoor = 0;
+static psImageMaskType maskGhost    = 0;
+static psImageMaskType maskGlint    = 0;
 
 bool pmSourceMagnitudesInit (pmConfig *config, psMetadata *recipe)
@@ -68,4 +70,6 @@
 	maskBurntool = pmConfigMaskGet("BURNTOOL", config);
 	maskConvPoor = pmConfigMaskGet("CONV.POOR", config);
+	maskGhost    = pmConfigMaskGet("GHOST", config);
+	maskGlint    = pmConfigMaskGet("GHOST", config);
 	maskSuspect  = maskSpike | maskStarCore | maskBurntool | maskConvPoor;
     }
@@ -94,5 +98,5 @@
 {
     PS_ASSERT_PTR_NON_NULL(source, false);
-    PS_ASSERT_PTR_NON_NULL(psf, false);
+    // PS_ASSERT_PTR_NON_NULL(psf, false);
 
     int status = false;
@@ -533,4 +537,36 @@
     }
 
+    // Check that if the peak is on/off a ghost, glint, or diffraction spike.  In regular IPP
+    // processing, these values are only set in the image mask after the 'camera' stage
+
+    int xChip = source->peak->x;
+    int yChip = source->peak->y;
+
+    // need to access the parent if we are looking at a subimage (likely)
+    psImage *chipImage = (source->pixels == NULL) ? source->pixels : (psImage *) source->pixels->parent;
+
+    bool onChip = true;
+    onChip &= (xChip >= 0);
+    onChip &= (xChip < chipImage->numCols);
+    onChip &= (yChip >= 0);
+    onChip &= (yChip < chipImage->numRows);
+    if (!onChip) {
+	// if the source is off the edge of the chip, raise a different bit?
+	source->mode |= PM_SOURCE_MODE_OFF_CHIP;
+    } else {
+	int xMask = xChip - mask->col0;
+	int yMask = yChip - mask->row0;
+	psImageMaskType maskValue = mask->data.PS_TYPE_IMAGE_MASK_DATA[yMask][xMask];
+	if (maskValue & maskGhost) {
+	    source->mode |= PM_SOURCE_MODE_ON_GHOST;
+	}
+	pmSourceMode PM_SOURCE_MODE_ON_GLINT = PM_SOURCE_MODE_ON_GHOST;
+	if (maskValue & maskGlint) {
+	    source->mode |= PM_SOURCE_MODE_ON_GLINT;
+	}
+	if (maskValue & maskSpike) {
+	    source->mode |= PM_SOURCE_MODE_ON_SPIKE;
+	}
+    }
     return (true);
 }
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourcePlotPSFModel.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourcePlotPSFModel.c	(revision 32133)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourcePlotPSFModel.c	(revision 33415)
@@ -145,5 +145,5 @@
         // force the axis ratio to be < 20.0
         psEllipseAxes axes_mnt = psEllipseMomentsToAxes (moments, 20.0);
-        psEllipseAxes axes_psf = pmPSF_ModelToAxes (PAR, 20.0);
+        psEllipseAxes axes_psf = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 
         // moments major axis
