Index: trunk/ppImage/src/Makefile.am
===================================================================
--- trunk/ppImage/src/Makefile.am	(revision 5977)
+++ trunk/ppImage/src/Makefile.am	(revision 6064)
@@ -17,5 +17,7 @@
 	ppImageParseCamera.c \
 	ppImageParseDetrend.c \
-	ppImageWeights.c
+	ppImageWeights.c \
+	ppImageOutput.c \
+	ppImagePhot.c
 #	ppDetrendFlat.c
 #	ppImageOutput.c
Index: trunk/ppImage/src/ppImage.c
===================================================================
--- trunk/ppImage/src/ppImage.c	(revision 5977)
+++ trunk/ppImage/src/ppImage.c	(revision 6064)
@@ -27,6 +27,9 @@
     ppImageLoop(&data, &options, &config);
 
-    // output options
-    // ppImageOutput(&data, &options, &config);
+    // Output image
+    ppImageOutput(&data, &config);
+
+    // Do photometry
+    ppImagePhot(&data, &options, &config);
 
     exit(EXIT_SUCCESS);
Index: trunk/ppImage/src/ppImage.h
===================================================================
--- trunk/ppImage/src/ppImage.h	(revision 5977)
+++ trunk/ppImage/src/ppImage.h	(revision 6064)
@@ -9,11 +9,10 @@
 #include "psAdditionals.h"
 
-#include "pmAstrometry.h"
+#include "pmFPA.h"
+#include "pmFPAConstruct.h"
+#include "pmFPARead.h"
+#include "pmFPAWrite.h"
 #include "pmReadout.h"
 #include "pmConfig.h"
-#include "pmFPAConstruct.h"
-#include "pmFPARead.h"
-#include "pmFPAConceptsGet.h"
-#include "pmFPAWrite.h"
 
 #include "pmFlatField.h"
@@ -23,4 +22,5 @@
 #include "pmChipMosaic.h"
 //#include "pmFPAMorph.h"
+#include "pmSubtractBias.h"
 
 #define RECIPE "PHASE2"                 // Name of the recipe to use
@@ -53,17 +53,10 @@
     bool doSource;                      // Source identification and photometry
     bool doAstrom;                      // Astrometry
-
     bool doOverscan;                    // Overscan subtraction
-    int overscanBins;                   // Number of pixels per bin for overscan
-    psStats *overscanStats;             // Statistics for overscan
-    void *overscanFit;                  // Overscan fit (polynomial or spline)
-    pmFit overscanFitType;              // Fit type for overscan
-    pmOverscanAxis overscanMode;        // Axis for overscan
-
+    pmOverscanOptions *overscan;        // Overscan options
     bool doNonLin;                      // Non-linearity correction
     psDataType nonLinearType;
     psMetadataItem *nonLinearData;
     void *nonLinearSource;
-
     ppImageLoadDepth imageLoadDepth;    // How much of the FPA to load at once
 } ppOptions;
@@ -142,6 +135,8 @@
 bool ppDetrendNonLinearLookup(pmReadout *input, psMetadataItem *dataItem);
 bool ppDetrendNonLinearPolynomial(pmReadout *input, psMetadataItem *dataItem);
-bool ppDetrendBias(pmCell *inputCell, pmReadout *inputReadout, pmReadout *pedestal, ppOptions *options);
+bool ppDetrendBias(pmReadout *inputReadout, pmReadout *bias, pmReadout *dark, ppOptions *options);
+#if 0
 pmReadout* ppDetrendPedestal(pmReadout *pedestal, pmCell *input, pmReadout *bias, pmReadout *dark, float darkTime, ppOptions *options);
+#endif
 pmReadout* ppDetrendSelectFirst(pmCell *cell, char *name, bool doThis);
 
@@ -150,4 +145,6 @@
 #endif
 
+bool ppImageOutput(ppData *data, ppConfig *config);
+bool ppImagePhot(ppData *data, ppOptions *options, ppConfig *config);
 
 #endif // Pau.
Index: trunk/ppImage/src/ppImageDetrendBias.c
===================================================================
--- trunk/ppImage/src/ppImageDetrendBias.c	(revision 5977)
+++ trunk/ppImage/src/ppImageDetrendBias.c	(revision 6064)
@@ -1,84 +1,56 @@
-# include "ppImage.h"
+#include "ppImage.h"
 
-bool ppDetrendBias (pmCell *inputCell, pmReadout *inputReadout, pmReadout *pedestal, ppOptions *options) {
+bool ppDetrendBias(pmReadout *inputReadout, pmReadout *bias, pmReadout *dark, ppOptions *options) {
 
-    psPolynomial1D *poly;
-    psSpline1D *spline;
-    psString coeffs;
 
-    // XXX EAM : renamed the general edge overscan concept
-    if (options->overscanMode == PM_OVERSCAN_EDGE) {
-	// Need to get the read direction
-	int readdir = psMetadataLookupS32(NULL, inputCell->concepts, "CELL.READDIR");
-	if (readdir == 1) {
-	    options->overscanMode = PM_OVERSCAN_ROWS;
-	} else if (readdir == 2) {
-	    options->overscanMode = PM_OVERSCAN_COLUMNS;
-	} else {
-	    psAbort("phase2", "CELL.READDIR (%d) is not 1 or 2", readdir);
-	}
+//    psMetadataPrint(inputCell->concepts, 7);
+
+    pmSubtractBias(inputReadout, options->overscan, bias, dark);
+
+    // Output overscan fit results, if required
+    if (! options->doOverscan || ! options->overscan) {
+        return true;
     }
 
-    // XXX : should this be a psArray?
-    psList *inputOverscans = NULL;
-    if (options->doOverscan) {
-	inputOverscans = pmReadoutGetBias(inputReadout); // List of overscan bias regions
+    if (!options->overscan->poly && !options->overscan->spline) {
+        psLogMsg("ppImage", PS_LOG_WARN, "No fit generated!\n");
+        return true;
     }
 
-    // supplies the fit result in 'overscanFit'
-    pmSubtractBias(inputReadout, 
-		   options->overscanFit, 
-		   inputOverscans, 
-		   options->overscanMode, 
-		   options->overscanStats, 
-		   options->overscanBins, 
-		   options->overscanFitType, 
-		   pedestal);
-    psFree(inputOverscans);
-    
-    // Output overscan fit results, if required
-    if (! options->doOverscan) { 
-	return true;
+    switch (options->overscan->fitType) {
+      case PM_FIT_POLY_ORD:
+      case PM_FIT_POLY_CHEBY:
+        {
+            psPolynomial1D *poly = options->overscan->poly; // The polynomial
+            psString coeffs = NULL;     // String containing the coefficients
+            for (int i = 0; i < poly->nX; i++) {
+                psStringAppend(&coeffs, "%e ", poly->coeff[i]);
+            }
+            psLogMsg("phase2", PS_LOG_INFO, "Overscan polynomial coefficients:\n%s\n", coeffs);
+            psFree(coeffs);
+            break;
+        }
+      case PM_FIT_SPLINE:
+        {
+            psSpline1D *spline = options->overscan->spline; // The spline
+            psString coeffs = NULL;     // String containing the coefficients
+            for (int i = 0; i < spline->n; i++) {
+                psPolynomial1D *poly = spline->spline[i]; // i-th polynomial
+                psStringAppend(&coeffs, "%d: ", i);
+                for (int j = 0; j < poly->nX; j++) {
+                    psStringAppend(&coeffs, "%e ", poly->coeff[i]);
+                }
+                psStringAppend(&coeffs, "\n");
+            }
+            psLogMsg("phase2", PS_LOG_INFO, "Overscan spline coefficients:\n%s\n", coeffs);
+            psFree(coeffs);
+            break;
+        }
+      case PM_FIT_NONE:
+        break;
+      default:
+        psAbort(__func__, "Should never get here!!!\n");
     }
 
-    if (! options->overscanFit) {
-	psLogMsg("phase2", PS_LOG_WARN, "No fit generated!\n");
-	return true;
-    } 
-
-    switch (options->overscanFitType) {
-      case PM_FIT_POLYNOMIAL:
-	poly = (psPolynomial1D *) options->overscanFit; // The polynomial
-	coeffs = NULL;     // String containing the coefficients
-	for (int i = 0; i < poly->nX; i++) {
-	    psStringAppend(&coeffs, "%e ", poly->coeff[i]);
-	}
-	psLogMsg("phase2", PS_LOG_INFO, "Overscan polynomial coefficients:\n%s\n",
-		 coeffs);
-	psFree(coeffs);
-	break;
-
-      case PM_FIT_SPLINE:
-	spline = (psSpline1D *) options->overscanFit; // The spline
-	coeffs = NULL;     // String containing the coefficients
-	for (int i = 0; i < spline->n; i++) {
-	    psPolynomial1D *poly = spline->spline[i]; // i-th polynomial
-	    psStringAppend(&coeffs, "%d: ", i);
-	    for (int j = 0; j < poly->nX; j++) {
-		psStringAppend(&coeffs, "%e ", poly->coeff[i]);
-	    }
-	    psStringAppend(&coeffs, "\n");
-	}
-	psLogMsg("phase2", PS_LOG_INFO, "Overscan spline coefficients:\n%s\n",
-		 coeffs);
-	psFree(coeffs);
-	break;
-
-      case PM_FIT_NONE:
-	break;
-
-      default:
-	psAbort(__func__, "Should never get here!!!\n");
-    }
     return true;
 }
Index: trunk/ppImage/src/ppImageDetrendCell.c
===================================================================
--- trunk/ppImage/src/ppImageDetrendCell.c	(revision 5977)
+++ trunk/ppImage/src/ppImageDetrendCell.c	(revision 6064)
@@ -1,15 +1,17 @@
 # include "ppImage.h"
+#include "pmFPA.h"
 
 // mask, bias, dark, flat are defined per Cell
 // pedestal is constructed for each readout, which may have different exposure times
 
-pmReadout *ppDetrendSelectFirst (pmCell *cell, char *name, bool doThis) {
+pmReadout *ppDetrendSelectFirst(pmCell *cell, char *name, bool doThis)
+{
 
     if (!doThis) {
-	return NULL;
+        return NULL;
     }
 
     if (cell->readouts->n > 1) {
-	psLogMsg("phase2", PS_LOG_WARN, "%s contains multiple readouts: only the first will be used.", name);
+        psLogMsg("phase2", PS_LOG_WARN, "%s contains multiple readouts: only the first will be used.", name);
     }
 
@@ -19,49 +21,48 @@
 }
 
-bool ppDetrendCell (ppDetrend *detrend, ppOptions *options, ppConfig *config) {
+bool ppDetrendCell(ppDetrend *detrend, ppOptions *options, ppConfig *config)
+{
 
-    pmReadout *mask = ppDetrendSelectFirst (detrend->mask, "mask", options->doMask);
-    pmReadout *bias = ppDetrendSelectFirst (detrend->bias, "bias", options->doBias);
-    pmReadout *dark = ppDetrendSelectFirst (detrend->dark, "dark", options->doDark);
-    // pmReadout *flat = ppDetrendSelectFirst (detrend->flat, "flat", options->doFlat);
+    pmCellSetWeights(detrend->input);
+
+    pmReadout *mask = ppDetrendSelectFirst(detrend->mask, "mask", options->doMask);
+    pmReadout *bias = ppDetrendSelectFirst(detrend->bias, "bias", options->doBias);
+    pmReadout *dark = ppDetrendSelectFirst(detrend->dark, "dark", options->doDark);
+    pmReadout *flat = ppDetrendSelectFirst(detrend->flat, "flat", options->doFlat);
 
     // Dark time for dark image
-    float darkTime = 1.0;       
+    float darkTime = 1.0;
     if (options->doDark) {
-	darkTime = psMetadataLookupF32(NULL, detrend->dark->concepts, "CELL.DARKTIME");
-	if (darkTime <= 0.0) {
-	    psErrorStackPrint(stderr, "DARKTIME for dark image (%f) is non-positive.\n", darkTime);
-	    exit(EXIT_FAILURE);
-	}
+        darkTime = psMetadataLookupF32(NULL, detrend->dark->concepts, "CELL.DARKTIME");
+        if (darkTime <= 0.0) {
+            psErrorStackPrint(stderr, "DARKTIME for dark image (%f) is non-positive.\n", darkTime);
+            exit(EXIT_FAILURE);
+        }
     }
-
-    // the pedestal combines the bias and dark contributions for a single readout
-    pmReadout *pedestal = NULL;
 
     for (int k = 0; k < detrend->input->readouts->n; k++) {
 
-	pmReadout *input = detrend->input->readouts->data[k]; // Readout of interest in input image
+        pmReadout *input = detrend->input->readouts->data[k]; // Readout of interest in input image
 
-	// Mask bad pixels
-	if (options->doMask) {
-	    ppDetrendMask (detrend->input, input, mask);
-	}
+        // Mask bad pixels
+        if (options->doMask) {
+            ppDetrendMask (detrend->input, input, mask);
+        }
 
-	// Non-linearity correction
-	if (options->doNonLin) {
-	    ppDetrendNonLinear (detrend->input, input, options);
-	}
+        // Non-linearity correction
+        if (options->doNonLin) {
+            ppDetrendNonLinear (detrend->input, input, options);
+        }
 
-	// generate the pedestal image (passes through the old pedestal if current exptime == last exptime)
-	pedestal = ppDetrendPedestal (pedestal, detrend->input, bias, dark, darkTime, options);
+        // Bias, dark and overscan subtraction are all merged.
+        ppDetrendBias(input, bias, dark, options);
 
-	// Bias, dark and overscan subtraction are all merged.
-	ppDetrendBias (detrend->input, input, pedestal, options);
-
-	// Flat-field correction (no options used?)
-	if (options->doFlat) {
-	    psLogMsg("phase2", PS_LOG_INFO, "flat field disabled.\n");
-	    // pmFlatField (input, flat);
-	}
+        // Flat-field correction (no options used?)
+        if (options->doFlat) {
+#if 0
+            psLogMsg("phase2", PS_LOG_INFO, "flat field disabled.\n");
+#endif
+            pmFlatField(input, flat);
+        }
     }
     return true;
Index: trunk/ppImage/src/ppImageOptions.c
===================================================================
--- trunk/ppImage/src/ppImageOptions.c	(revision 5977)
+++ trunk/ppImage/src/ppImageOptions.c	(revision 6064)
@@ -17,9 +17,9 @@
     options->doAstrom = false;          // Astrometry
     // Overscan options
-    options->overscanBins = 1;          // Number of pixels per bin for overscan
-    options->overscanStats = NULL;      // Statistics for overscan
-    options->overscanFit = NULL;        // Overscan fit (polynomial or spline)
-    options->overscanFitType = PM_FIT_NONE; // Fit type for overscan
-    options->overscanMode = PM_OVERSCAN_NONE; // Axis for overscan
+    options->overscan = NULL;           // Overscan options
+    bool overscanSingle = false;        // A single value for entire overscan?
+    pmFit overscanFit = PM_FIT_NONE;    // Fit type for overscan
+    int overscanOrder = 0;              // Order for overscan fit
+    psStats *overscanStats = NULL;      // Statistics for overscan
     // Non-linearity options
     options->nonLinearType = 0;         // Type of non-linearity data (vector, string or metadata)
@@ -28,4 +28,5 @@
     // Various others
     options->imageLoadDepth = PP_LOAD_NONE; // No load depth specified yet
+
 
     bool mdStatus = false;              // Result of MD lookup
@@ -122,43 +123,36 @@
     // XXX EAM : we should abort on invalid options. default options?
     if (psMetadataLookupBool(NULL, config->recipe, "OVERSCAN")) {
-        // XXX EAM : does 'overscanMode = NONE' mean doOverscan = false?
         options->doOverscan = true;
-        psString mode = psMetadataLookupStr(NULL, config->recipe, "OVERSCAN.MODE");
-        if (! strcasecmp(mode, "INDIVIDUAL")) {
-            options->overscanMode = PM_OVERSCAN_EDGE;
-        } else if (! strcasecmp(mode, "ALL")) {
-            options->overscanMode = PM_OVERSCAN_ALL;
-        } else if (strcasecmp(mode, "NONE")) {
-            psLogMsg(__func__, PS_LOG_WARN, "OVERSCAN.MODE (%s) is not one of NONE, INDIVIDUAL, or ALL:"
-                     " assuming NONE.\n", mode);
-        }
+
+        // Do the overscan as a single value?
+        overscanSingle = psMetadataLookupBool(NULL, config->recipe, "OVERSCAN.SINGLE");
+
+        // How do we fit it?
         psString fit = psMetadataLookupStr(NULL, config->recipe, "OVERSCAN.FIT");
         if (! strcasecmp(fit, "POLYNOMIAL")) {
-            options->overscanFitType = PM_FIT_POLYNOMIAL;
-            int order = psMetadataLookupS32(NULL, config->recipe, "OVERSCAN.ORDER"); // Order of polynomial fit
-            options->overscanFit = psPolynomial1DAlloc(order, PS_POLYNOMIAL_ORD);
+            overscanFit = PM_FIT_POLY_ORD;
+            overscanOrder = psMetadataLookupS32(NULL, config->recipe, "OVERSCAN.ORDER");
+        } else if (! strcasecmp(fit, "CHEBYSHEV")) {
+            overscanFit = PM_FIT_POLY_CHEBY;
+            overscanOrder = psMetadataLookupS32(NULL, config->recipe, "OVERSCAN.ORDER");
         } else if (! strcasecmp(fit, "SPLINE")) {
-            options->overscanFit = NULL;
-            options->overscanFitType = PM_FIT_SPLINE;
-            // int order = psMetadataLookupS32(NULL, config->recipe, "OVERSCAN.ORDER"); // Order of polynomial fit
-            // XXX : not in psLib yet : options->overscanFit = psSpline1DAlloc();
+            overscanFit = PM_FIT_SPLINE;
         } else if (strcasecmp(fit, "NONE")) {
             psLogMsg(__func__, PS_LOG_WARN, "OVERSCAN.FIT (%s) is not one of NONE, POLYNOMIAL, or SPLINE:"
                      " assuming NONE.\n", fit);
         }
-        options->overscanBins = psMetadataLookupS32(NULL, config->recipe, "OVERSCAN.BIN");
-        if (options->overscanBins <= 0) {
-            psErrorStackPrint(stderr, "OVERSCAN.BIN (%d) is non-positive --- assuming 1.\n", options->overscanBins);
-            options->overscanBins = 1;
-        }
+
         psString stat = psMetadataLookupStr(NULL, config->recipe, "OVERSCAN.STAT");
         if (! strcasecmp(stat, "MEAN")) {
-            options->overscanStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+            overscanStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
         } else if (! strcasecmp(stat, "MEDIAN")) {
-            options->overscanStats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
+            overscanStats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
         } else {
             psErrorStackPrint(stderr, "OVERSCAN.STAT (%s) is not one of MEAN, MEDIAN: assuming MEAN\n", stat);
-            options->overscanStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+            overscanStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
         }
+
+        // Fill in the options
+        options->overscan = pmOverscanOptionsAlloc(overscanSingle, overscanFit, overscanOrder, overscanStats);
     }
 
Index: trunk/ppImage/src/ppImageOutput.c
===================================================================
--- trunk/ppImage/src/ppImageOutput.c	(revision 5977)
+++ trunk/ppImage/src/ppImageOutput.c	(revision 6064)
@@ -1,29 +1,19 @@
 # include "ppImage.h"
 
-bool ppImageLoop (ppData *data, ppRecipe *options, ppConfig *config) {
+bool ppImageOutput(ppData *data, ppConfig *config)
+{
+    const char *outname = psMetadataLookupStr(NULL, config->arguments, "-output"); // Name of output file
+    psFits *outFile = psFitsOpen(outname, "w"); // File for writing
 
     // Write the output
-    pmFPAWrite(outputFile, input, database);
+    pmFPAWrite(outFile, data->input->fpa, config->database);
+#if 0
     pmFPAWriteMask(input, outputFile);
     pmFPAWriteWeight(input, outputFile);
+#endif
 
-    psLogMsg("phase2", PS_LOG_INFO, "Output completed after %f sec.\n", psTimerMark("phase2"));
+    psFitsClose(outFile);
+    psLogMsg("ppImage", PS_LOG_INFO, "Output completed after %f sec.\n", psTimerMark("ppImage"));
 
-    psFitsClose(outputFile);
-
-    psFree(arguments);
-    psFree(site);
-    psFree(header);
-    psFree(camera);
-    psFree(recipe);
-    psFree(input);
-    psFree(mask);
-    psFree(bias);
-    psFree(dark);
-    psFree(flat);
-    psFree(overscanFit);
-    psFree(overscanStats);
-
-    psLogMsg("phase2", PS_LOG_INFO, "Output completed after %f sec.\n", psTimerMark("phase2"));
     return true;
 }
Index: trunk/ppImage/src/ppImagePhot.c
===================================================================
--- trunk/ppImage/src/ppImagePhot.c	(revision 6064)
+++ trunk/ppImage/src/ppImagePhot.c	(revision 6064)
@@ -0,0 +1,32 @@
+#include "ppImage.h"
+
+bool ppImagePhot(ppData *data, ppOptions *options, ppConfig *config)
+{
+    ppFile *input = data->input;        // The input file information
+    pmFPA *fpa = input->fpa;       // The input FPA
+
+    int numMosaicked = pmFPAMosaicChips(fpa, 1, 1); // Number of chips mosaicked together
+    psLogMsg(__func__, PS_LOG_INFO, "%d chips mosaicked.\n", numMosaicked);
+
+#if 1
+    // Write out the mosaicked chip, just to see; this wouldn't normally happen
+    psFits *mosaicFile = psFitsOpen("mosaic.fits", "w");
+    psArray *chips = fpa->chips;
+    for (int i = 0; i < chips->n; i++) {
+        pmChip *chip = chips->data[i];
+        if (! chip || ! chip->exists || ! chip->process) {
+            continue;
+        }
+        psArray *cells = chip->cells;
+        pmCell *cell = cells->data[0];
+        psArray *readouts = cell->readouts;
+        pmReadout *readout = readouts->data[0];
+        psImage *image = readout->image;
+        psFitsWriteImage(mosaicFile, NULL, image, 0);
+    }
+#endif
+
+    // XXX EAM: Insert psphot stuff here
+
+    return true;
+}
