Index: anches/eam_branches/20091201/psModules/src/objects/pmObjects.c
===================================================================
--- /branches/eam_branches/20091201/psModules/src/objects/pmObjects.c	(revision 26522)
+++ 	(revision )
@@ -1,25 +1,0 @@
-/** @file  pmObjects.c
- *
- *  This file will ...
- *
- *  @author GLG, MHPCC
- *  @author EAM, IfA: significant modifications.
- *
- *  @version $Revision: 1.13 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2006-09-15 09:49:01 $
- *
- *  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 "pmObjects.h"
-#include "pmModelGroup.h"
-
Index: anches/eam_branches/20091201/psModules/src/objects/pmObjects.h
===================================================================
--- /branches/eam_branches/20091201/psModules/src/objects/pmObjects.h	(revision 26522)
+++ 	(revision )
@@ -1,68 +1,0 @@
-/* @file  pmObjects.h
- *
- * The process of finding, measuring, and classifying astronomical sources on
- * images is one of the critical tasks of the IPP or any astronomical software
- * system. This file will define structures and functions related to the task
- * of source detection and measurement. The elements defined in this section
- * are generally low-level components which can be connected together to
- * construct a complete object measurement suite.
- *
- * @author GLG, MHPCC
- *
- * @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
- * @date $Date: 2007-01-24 02:54:15 $
- * Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- */
-
-#ifndef PM_OBJECTS_H
-#define PM_OBJECTS_H
-
-/// @addtogroup Objects Object Detection / Analysis Functions
-/// @{
-
-#include <stdio.h>
-#include <math.h>
-#include <pslib.h>
-
-/**
- *
- * This function converts the source classification into the closest available
- * approximation to the Dophot classification scheme:
- * XXX EAM : fix this to use current source classification scheme
- *
- * PM_SOURCE_DEFECT: 8
- * PM_SOURCE_SATURATED: 8
- * PM_SOURCE_SATSTAR: 10
- * PM_SOURCE_PSFSTAR: 1
- * PM_SOURCE_GOODSTAR: 1
- * PM_SOURCE_POOR_FIT_PSF: 7
- * PM_SOURCE_FAIL_FIT_PSF: 4
- * PM_SOURCE_FAINTSTAR: 4
- * PM_SOURCE_GALAXY: 2
- * PM_SOURCE_FAINT_GALAXY: 2
- * PM_SOURCE_DROP_GALAXY: 2
- * PM_SOURCE_FAIL_FIT_GAL: 2
- * PM_SOURCE_POOR_FIT_GAL: 2
- * PM_SOURCE_OTHER: ?
- *
- */
-int pmSourceDophotType(
-    pmSource *source                    ///< Add comment.
-);
-
-
-/** pmSourceSextractType()
- *
- * This function converts the source classification into the closest available
- * approximation to the Sextractor classification scheme. the correspondence is
- * not yet defined (TBD) .
- *
- * XXX: Must code this.
- *
- */
-int pmSourceSextractType(
-    pmSource *source                    ///< Add comment.
-);
-
-/// @}
-#endif
Index: /branches/eam_branches/20091201/psModules/src/objects/pmPhotObj.c
===================================================================
--- /branches/eam_branches/20091201/psModules/src/objects/pmPhotObj.c	(revision 26523)
+++ /branches/eam_branches/20091201/psModules/src/objects/pmPhotObj.c	(revision 26523)
@@ -0,0 +1,39 @@
+/** @file  pmPhotObj.c
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.13 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-09-15 09:49:01 $
+ * Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <pslib.h>
+#include "pmPhotObj.h"
+
+static void pmPhotObjFree (pmPhotObj *tmp)
+{
+    if (!tmp) return;
+    psFree(tmp->sources);
+}
+
+
+pmPhotObj *pmPhotObjAlloc() 
+{
+    static int id = 1;
+    pmPhotObj *obj = (pmPhotObj *) psAlloc(sizeof(pmPhotObj));
+    psMemSetDeallocator(obj, (psFreeFunc) pmPhotObjFree);
+
+    *(int *)&obj->id = id; // cast away the const to set the id.
+    id++;
+
+    obj->sources = NULL;
+    return obj;
+}
+
Index: /branches/eam_branches/20091201/psModules/src/objects/pmPhotObj.h
===================================================================
--- /branches/eam_branches/20091201/psModules/src/objects/pmPhotObj.h	(revision 26523)
+++ /branches/eam_branches/20091201/psModules/src/objects/pmPhotObj.h	(revision 26523)
@@ -0,0 +1,41 @@
+/* @file  pmPhotObj.h
+ *
+ * @author EAM, IfA
+ *
+ * @version $Revision: $
+ * @date $Date: 2009-02-16 22:30:50 $
+ * Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+# ifndef PM_PHOT_OBJ_H
+# define PM_PHOT_OBJ_H
+
+#include <pslib.h>
+#include "pmPeaks.h"
+#include "pmModel.h"
+#include "pmMoments.h"
+
+/// @addtogroup Objects Object Detection / Analysis Functions
+/// @{
+
+
+/** pmPhotObj data structure
+ *
+ *  A Photometry Object is a connected set of source measurements with a common
+ *  connection.  Each source that comprises the object represents the detection of some
+ *  astronomical object on a single image.  The object represents the related collection
+ *  of measurements.  The fits are coupled in some way.  For example, they may all have
+ *  the same position, but independent fluxes.  Or, they may have a common set of
+ *  positions and shape parameters.  Or the position in each image may be related by a
+ *  function.
+ *
+ *  XXX is thre any info that is neaded for each object beyond that carried by the sources
+ *  (besides ID)?
+ */
+typedef struct {
+  int seq;                            ///< ID for output (generated on write OR set on read)
+  psArray *sources;
+} pmPhotObj;
+
+/// @}
+# endif /* PM_PHOT_OBJ_H */
Index: /branches/eam_branches/20091201/psModules/src/objects/pmSource.c
===================================================================
--- /branches/eam_branches/20091201/psModules/src/objects/pmSource.c	(revision 26522)
+++ /branches/eam_branches/20091201/psModules/src/objects/pmSource.c	(revision 26523)
@@ -3,12 +3,10 @@
  *  Functions to define and manipulate sources on images
  *
- *  @author GLG, MHPCC
- *  @author EAM, IfA: significant modifications.
+ *  @author EAM, IfA
+ *  @author GLG, MHPCC (initial code base)
  *
  *  @version $Revision: 1.70 $ $Name: not supported by cvs2svn $
  *  @date $Date: 2009-02-16 22:29:59 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
+ *  Copyright 2009 Institute for Astronomy, University of Hawaii
  */
 
Index: /branches/eam_branches/20091201/psphot/src/psphotMaskReadout.c
===================================================================
--- /branches/eam_branches/20091201/psphot/src/psphotMaskReadout.c	(revision 26522)
+++ /branches/eam_branches/20091201/psphot/src/psphotMaskReadout.c	(revision 26523)
@@ -75,2 +75,25 @@
     return true;
 }
+
+bool psphotStackSetMaskAndVariance (pmConfig *config, const pmFPAview *view, psMetadata *recipe) {
+
+    int num = psMetadataAddS32 (&status, config->arguments, "INPUTS.NUM");
+    psAbort (!status, "programming error: must define INPUTS.NUM");
+
+    // loop over the available readouts
+    for (int i = 0; i < num; i++) {
+
+        pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PSPHOT.INPUT", i); // File of interest
+
+	// find the currently selected readout
+	pmReadout  *readout = pmFPAviewThisReadout (view, file->fpa);
+	PS_ASSERT_PTR_NON_NULL (readout, false);
+
+	// Generate the mask and weight images, including the user-defined analysis region of interest
+	psphotSetMaskAndVariance (config, readout, recipe);
+
+	// display the image, weight, mask (ch 1,2,3)
+	psphotVisualShowImage (readout);
+    }
+
+}
Index: /branches/eam_branches/20091201/psphot/src/psphotModelBackground.c
===================================================================
--- /branches/eam_branches/20091201/psphot/src/psphotModelBackground.c	(revision 26522)
+++ /branches/eam_branches/20091201/psphot/src/psphotModelBackground.c	(revision 26523)
@@ -328,5 +328,5 @@
 }
 
-
+// XXX these two functions are absurdly-named and very similar -- fix!
 
 psImage *psphotBackgroundModel(pmReadout *ro, const pmConfig *config)
@@ -376,2 +376,33 @@
     return true;
 }
+
+// XXX supply filename or keep PSPHOT.INPUT fixed?
+bool psphotStackModelBackground (pmConfig *config, const pmFPAview *view)
+{
+    bool status = true;
+
+    int num = psMetadataAddS32 (&status, config->arguments, "INPUTS.NUM");
+    psAbort (!status, "programming error: must define INPUTS.NUM");
+
+    // loop over the available readouts
+    for (int i = 0; i < num; i++) {
+
+        pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PSPHOT.INPUT", i); // File of interest
+
+	// find the currently selected readout
+	pmReadout  *readout = pmFPAviewThisReadout (view, file->fpa);
+	PS_ASSERT_PTR_NON_NULL (readout, false);
+
+	psImageBinning *binning = psphotBackgroundBinning(readout->image, config); // Image binning parameters
+	pmReadout *model = pmFPAGenerateReadout(config, view, "PSPHOT.BACKMDL", inFPA, binning); // this needs to be a MULTI like PSPHOT.INPUT
+	pmReadout *modelStdev = pmFPAGenerateReadout(config, view, "PSPHOT.BACKMDL.STDEV", inFPA, binning); // this needs to be a MULTI like PSPHOT.INPUT
+
+	// XXX save results on model->analysis or readout->analysis??
+	if (!backgroundModel(model->image, modelStdev->image, model->analysis, readout, binning, config)) {
+	    psError(PS_ERR_UNKNOWN, false, "Unable to generate background model");
+	    return false;
+	}
+	npass ++;
+    }
+    return true;
+}
Index: /branches/eam_branches/20091201/psphot/src/psphotReadoutStack.c
===================================================================
--- /branches/eam_branches/20091201/psphot/src/psphotReadoutStack.c	(revision 26523)
+++ /branches/eam_branches/20091201/psphot/src/psphotReadoutStack.c	(revision 26523)
@@ -0,0 +1,207 @@
+# include "psphotInternal.h"
+
+bool psphotReadoutStack (pmConfig *config, const pmFPAview *view) {
+
+    psTimerStart ("psphotReadout");
+
+    pmModelClassSetLimits(PM_MODEL_LIMITS_LAX); // allow models to have ugly fits (eg, central cusp)
+
+    // select the current recipe
+    psMetadata *recipe = psMetadataLookupPtr (NULL, config->recipes, PSPHOT_RECIPE);
+    if (!recipe) {
+        psError(PSPHOT_ERR_CONFIG, false, "missing recipe %s", PSPHOT_RECIPE);
+        return false;
+    }
+
+    // set the photcode for this image
+    if (!psphotAddPhotcode (recipe, config, view, "PSPHOT.INPUT")) {
+        psError (PSPHOT_ERR_CONFIG, false, "trouble defining the photcode");
+        return false;
+    }
+
+    // generate a background model (median, smoothed image)
+    if (!psphotStackSetMaskAndVariance (config, view, recipe)) {
+        return psphotReadoutCleanupStack (config, recipe, NULL, NULL, NULL);
+    }
+    if (!strcasecmp (breakPt, "NOTHING")) {
+	return psphotReadoutCleanup(config, readout, recipe, NULL, NULL, NULL);
+    }
+
+    // optional break-point for processing
+    char *breakPt = psMetadataLookupStr (NULL, recipe, "BREAK_POINT");
+    PS_ASSERT_PTR_NON_NULL (breakPt, false);
+
+    // generate a background model (median, smoothed image)
+    if (!psphotStackModelBackground (config, view)) {
+        return psphotReadoutCleanup (config, readout, recipe, NULL, NULL, NULL);
+    }
+    if (!psphotSubtractBackground (config, view, "PSPHOT.INPUT")) {
+        return psphotReadoutCleanup (config, readout, recipe, NULL, NULL, NULL);
+    }
+    if (!strcasecmp (breakPt, "BACKMDL")) {
+        return psphotReadoutCleanup (config, readout, recipe, NULL, NULL, NULL);
+    }
+
+    // find the detections (by peak and/or footprint) in the image.
+    pmDetections *detections = psphotFindDetections (NULL, readout, recipe);
+    if (!detections) {
+        psLogMsg ("psphot", 3, "unable to find detections in this image");
+        return psphotReadoutCleanup (config, readout, recipe, detections, psf, NULL);
+    }
+
+    // construct sources and measure basic stats
+    psArray *sources = psphotSourceStats (config, readout, detections, true);
+    if (!sources) return false;
+    if (!strcasecmp (breakPt, "PEAKS")) {
+        return psphotReadoutCleanup(config, readout, recipe, detections, psf, sources);
+    }
+
+    // find blended neighbors of very saturated stars
+    // XXX merge this with Basic Deblend?
+    psphotDeblendSatstars (readout, sources, recipe);
+
+    // mark blended peaks PS_SOURCE_BLEND
+    if (!psphotBasicDeblend (sources, recipe)) {
+        psLogMsg ("psphot", 3, "failed on deblend analysis");
+        return psphotReadoutCleanup (config, readout, recipe, detections, psf, sources);
+    }
+
+    // classify sources based on moments, brightness
+    if (!psphotRoughClass (readout, sources, recipe, havePSF)) {
+        psLogMsg ("psphot", 3, "failed to find a valid PSF clump for image");
+        return psphotReadoutCleanup (config, readout, recipe, detections, psf, sources);
+    }
+    if (!strcasecmp (breakPt, "MOMENTS")) {
+        return psphotReadoutCleanup(config, readout, recipe, detections, psf, sources);
+    }
+
+    if (!havePSF && !psphotImageQuality (recipe, sources)) {
+        psLogMsg("psphot", 3, "failed to measure image quality");
+        return psphotReadoutCleanup(config, readout, recipe, detections, psf, sources);
+    }
+
+    // if we were not supplied a PSF, choose one here
+    if (psf == NULL) {
+        // use bright stellar objects to measure PSF
+        // XXX if we do not have enough stars to generate the PSF, build one
+        // from the SEEING guess and model class
+        psf = psphotChoosePSF (readout, sources, recipe);
+        if (psf == NULL) {
+            psLogMsg ("psphot", 3, "failure to construct a psf model");
+            return psphotReadoutCleanup (config, readout, recipe, detections, psf, sources);
+        }
+        havePSF = true;
+    }
+    if (!strcasecmp (breakPt, "PSFMODEL")) {
+        return psphotReadoutCleanup (config, readout, recipe, detections, psf, sources);
+    }
+    psphotVisualShowPSFModel (readout, psf);
+
+    // include externally-supplied sources
+    psphotLoadExtSources (config, view, sources);
+
+    // construct an initial model for each object, set the radius to fitRadius, set circular fit mask
+    psphotGuessModels (config, readout, sources, psf);
+
+    // linear PSF fit to source peaks, subtract the models from the image (in PSF mask)
+    psphotFitSourcesLinear (readout, sources, recipe, psf, FALSE);
+
+    // We have to place these visualizations here because the models are not realized until
+    // psphotGuessModels or fitted until psphotFitSourcesLinear.
+    psphotVisualShowPSFStars (recipe, psf, sources);
+
+    // identify CRs and extended sources
+    psphotSourceSize (config, readout, sources, recipe, psf, 0);
+    if (!strcasecmp (breakPt, "ENSEMBLE")) {
+        goto finish;
+    }
+    psphotVisualShowSatStars (recipe, psf, sources);
+
+    // non-linear PSF and EXT fit to brighter sources
+    // replace model flux, adjust mask as needed, fit, subtract the models (full stamp)
+    psphotBlendFit (config, readout, sources, psf);
+
+    // replace all sources
+    psphotReplaceAllSources (sources, recipe);
+
+    // linear fit to include all sources (subtract again)
+    psphotFitSourcesLinear (readout, sources, recipe, psf, TRUE);
+
+    // if we only do one pass, skip to extended source analysis
+    if (!strcasecmp (breakPt, "PASS1")) {
+        goto pass1finish;
+    }
+    // NOTE: possibly re-measure background model here with objects subtracted
+
+    // add noise for subtracted objects
+    psphotAddNoise (readout, sources, recipe);
+
+    // find fainter sources (pass 2)
+    detections = psphotFindDetections (detections, readout, recipe);
+
+    // remove noise for subtracted objects (ie, return to normal noise level)
+    psphotSubNoise (readout, sources, recipe);
+
+    // define new sources based on only the new peaks
+    psArray *newSources = psphotSourceStats (config, readout, detections, false);
+
+    // set source type
+    if (!psphotRoughClass (readout, newSources, recipe, havePSF)) {
+        psLogMsg ("psphot", 3, "failed to find a valid PSF clump for image");
+        return psphotReadoutCleanup (config, readout, recipe, detections, psf, sources);
+    }
+
+    // create full input models, set the radius to fitRadius, set circular fit mask
+    psphotGuessModels (config, readout, newSources, psf);
+
+    // replace all sources so fit below applies to all at once
+    psphotReplaceAllSources (sources, recipe);
+
+    // merge the newly selected sources into the existing list
+    psphotMergeSources (sources, newSources);
+    psFree (newSources);
+
+    // linear fit to all sources
+    psphotFitSourcesLinear (readout, sources, recipe, psf, TRUE);
+
+pass1finish:
+
+    // measure source size for the remaining sources
+    psphotSourceSize (config, readout, sources, recipe, psf, 0);
+
+    psphotExtendedSourceAnalysis (readout, sources, recipe);
+
+    psphotExtendedSourceFits (readout, sources, recipe);
+
+finish:
+
+    // plot positive sources
+    // psphotSourcePlots (readout, sources, recipe);
+
+    // measure aperture photometry corrections
+    if (!psphotApResid (config, readout, sources, psf)) {
+        psLogMsg ("psphot", 3, "failed on psphotApResid");
+        return psphotReadoutCleanup (config, readout, recipe, detections, psf, sources);
+    }
+
+    // calculate source magnitudes
+    psphotMagnitudes(config, readout, view, sources, psf);
+
+    if (!psphotEfficiency(config, readout, view, psf, recipe, sources)) {
+        psErrorStackPrint(stderr, "Unable to determine detection efficiencies from fake sources");
+        psErrorClear();
+    }
+
+    // replace failed sources?
+    // psphotReplaceUnfitSources (sources);
+
+    // replace background in residual image
+    psphotSkyReplace (config, view);
+
+    // drop the references to the image pixels held by each source
+    psphotSourceFreePixels (sources);
+
+    // create the exported-metadata and free local data
+    return psphotReadoutCleanup(config, readout, recipe, detections, psf, sources);
+}
+
Index: /branches/eam_branches/20091201/psphot/src/psphotStack.c
===================================================================
--- /branches/eam_branches/20091201/psphot/src/psphotStack.c	(revision 26523)
+++ /branches/eam_branches/20091201/psphot/src/psphotStack.c	(revision 26523)
@@ -0,0 +1,35 @@
+# include "psphotStandAlone.h"
+
+int main (int argc, char **argv) {
+
+    psTimerStart ("complete");
+    pmErrorRegister();                  // register psModule's error codes/messages
+    psphotInit();
+
+    // load command-line arguments, options, and system config data
+    pmConfig *config = psphotStackArguments (argc, argv);
+    assert(config);
+
+    psphotVersionPrint();
+
+    // load input data (config and images (signal, noise, mask)
+    if (!psphotStackParseCamera (config)) {
+        psErrorStackPrint(stderr, "Error setting up the camera\n");
+        exit (psphotGetExitStatus());
+    }
+
+    // call psphot for each readout
+    if (!psphotStacImageLoop (config)) {
+        psErrorStackPrint(stderr, "Error in the psphot image loop\n");
+        exit (psphotGetExitStatus());
+    }
+
+    psLogMsg ("psphot", 3, "complete psphot run: %f sec\n", psTimerMark ("complete"));
+
+    psErrorCode exit_status = psphotGetExitStatus();
+    psphotCleanup (config);
+    exit (exit_status);
+}
+
+// all functions which return to this level must raise one of the top-level error codes if they
+// exit with an error.  these error codes are used to specify the program exit status
Index: /branches/eam_branches/20091201/psphot/src/psphotStackArguments.c
===================================================================
--- /branches/eam_branches/20091201/psphot/src/psphotStackArguments.c	(revision 26523)
+++ /branches/eam_branches/20091201/psphot/src/psphotStackArguments.c	(revision 26523)
@@ -0,0 +1,159 @@
+# include "psphotStandAlone.h"
+
+static void usage(pmConfig *config, int exitCode);
+static void writeHelpInfo(pmConfig* config, FILE* ofile);
+
+pmConfig *psphotStackArguments(int argc, char **argv) {
+
+    int N;
+    bool status;
+
+    // print help info
+    if (psArgumentGet(argc, argv, "-help")) writeHelpInfo(argv[0], config, stdout);
+    if (psArgumentGet(argc, argv, "-h")) writeHelpInfo(argv[0], config, stdout);
+
+    // load config data from default locations
+    pmConfig *config = pmConfigRead(&argc, argv, PSPHOT_RECIPE);
+    if (config == NULL) {
+      psErrorStackPrint(stderr, "Can't read site configuration");
+	exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    // -version and -dumpconfig arguments
+    PSARGUMENTS_INSTANTIATE_GENERICS( psphot, config, argc, argv );
+
+    // save the following additional recipe values based on command-line options
+    // these options override the PSPHOT recipe values loaded from recipe files
+    psMetadata *options = pmConfigRecipeOptions (config, PSPHOT_RECIPE);
+
+    // Number of threads is handled
+    PSARGUMENTS_INSTANTIATE_THREADSARG( psphot, config, argc, argv )
+
+    // photcode : used in output to supplement header data (argument or recipe?)
+    if ((N = psArgumentGet (argc, argv, "-photcode"))) {
+        if (argc <= N+1) {
+	  psErrorStackPrint(stderr, "Expected to see 1 more argument; saw %d", argc - 1);
+	  exit(PS_EXIT_CONFIG_ERROR);
+	}
+        psArgumentRemove (N, &argc, argv);
+        psMetadataAddStr (options, PS_LIST_TAIL, "PHOTCODE", PS_META_REPLACE, "", argv[N]);
+        psArgumentRemove (N, &argc, argv);
+    }
+
+    // visual : interactive display mode
+    if ((N = psArgumentGet (argc, argv, "-visual"))) {
+        psArgumentRemove (N, &argc, argv);
+        pmVisualSetVisual(true);
+    }
+
+    // break : used from recipe throughout psphotReadout to stop processing early
+    if ((N = psArgumentGet (argc, argv, "-break"))) {
+	if (argc <= N+1) {
+	  psErrorStackPrint(stderr, "Expected to see 1 more argument; saw %d", argc - 1);
+	  exit(PS_EXIT_CONFIG_ERROR);
+	}
+        psArgumentRemove (N, &argc, argv);
+        psMetadataAddStr (options, PS_LIST_TAIL, "BREAK_POINT", PS_META_REPLACE, "", argv[N]);
+        psArgumentRemove (N, &argc, argv);
+    }
+
+    // find the input data file (an mdc file)
+    if ((N = psArgumentGet(argc, argv, "-input"))) {
+	if (argc <= N+1) {
+	  psErrorStackPrint(stderr, "Expected to see 1 more argument; saw %d", argc - 1);
+	  exit(PS_EXIT_CONFIG_ERROR);
+	}
+        psArgumentRemove(N, &argc, argv);
+
+        unsigned int numBad = 0;                     // Number of bad lines
+        psMetadata *inputs = psMetadataConfigRead(NULL, &numBad, argv[N], false); // Input file info
+        if (!inputs || numBad > 0) {
+	    psErrorStackPrint(stderr, "Unable to cleanly read MDC file with inputs.");
+	    exit(PS_EXIT_CONFIG_ERROR);
+        }
+        psMetadataAddMetadata(config->arguments, PS_LIST_TAIL, "INPUTS", 0, "Metadata with input details", inputs);
+        psFree(inputs);
+
+        psArgumentRemove(N, &argc, argv);
+    }
+
+    if (argc != 2) {
+        psError(PSPHOT_ERR_ARGUMENTS, true, "Expected to see one more argument; saw %d", argc - 1);
+	usage(config, PS_EXIT_CONFIG_ERROR);
+    }
+
+    // output position is fixed
+    psMetadataAddStr (config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "", argv[1]);
+
+    psTrace("psphot", 1, "Done with psphotArguments...\n");
+    return (config);
+}
+
+static void writeHelpInfo(pmConfig* config, FILE* ofile)
+{
+  fprintf(ofile,
+	  "Usage: psphotStack -input (input.mdc) outroot\n"
+	  "\n"
+	  "where:\n"
+	  "  FileNameList is a text file containing filenames, one per line\n"
+	  "  MaskFileNameList is a text file of mask filenames, one per line\n"
+	  "  VarFileNameList is a text file of variance filenames, one per line\n"
+	  "  OutFileBaseName is the 'root name' for output files\n"
+	  "\n"
+	  "additional options:\n"
+	  "  -chip nn[,nn,...]\n"
+	  "     select detector chips to process; default is all.\n"
+	  "     Indices correspond to zero-based offset in the FPA metadata table.\n"
+	  "  -photcode PhotoCodeName\n"
+	  "     specify photocode\n"
+	  "  -region RegionString\n"
+	  "     specify analysis region.  String is of form '[x0:x1,y0:y1]'\n"
+	  "     To use this option you must define a default in psphot.config\n"
+	  "  -visual\n"
+	  "     turns on interactive display mode\n"
+	  "  -dumpconfig CfgFileName\n"
+          "     causes config info to be dumped to the named file.\n"
+	  "  -break NOTHING|BACKMDL|PEAKS|MOMENTS|PSFMODEL|ENSEMBLE|PASS1\n"
+	  "     choose a point at which to exit processing early\n"
+	  "  -nthreads n\n"
+	  "     set number of parallel threads of execution\n"
+	  "  -F OldFileRule ReplacementFileRule\n"
+	  "     change file naming rule; e.g. '-F PSPHOT.OUTPUT PSPHOT.OUT.CMF.MEF'\n"
+	  "  -D name stringval\n"
+	  "     set a string-valued config parameter\n"
+	  "  -Di name intval\n"
+	  "     set an integer-valued config parameter\n"
+	  "  -Df name fval\n"
+	  "     set a float-valued config parameter\n"
+	  "  -Db name boolval\n"
+	  "     set a boolean-valued config parameter\n"
+	  "  -v, -vv, -vvv\n"
+	  "     set increasing levels of verbosity\n"
+	  "  -logfmt FormatString\n"
+	  "     set format string used for log messages\n"
+	  "  -trace Fac Lvl\n"
+	  "     set tracing for facility Fac to integer Lvl, e.g. '-trace err 10'\n"
+	  "  -trace-levels\n"
+	  "     print current trace levels\n");
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+    exit(PS_EXIT_SUCCESS);
+}
+
+static void usage(pmConfig *config,      // Configuration
+		  int exitCode
+		  ) 
+{
+    fprintf(stderr, 
+	    "Usage: psphotStack -input input.mdc outroot\n"
+	    "Try 'psphotStack -help' for more options and explanation\n");
+
+    if (exitCode != PS_EXIT_SUCCESS) psErrorStackPrint(stderr, "Error reading arguments\n");
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+    exit(exitCode);
+}
+
Index: /branches/eam_branches/20091201/psphot/src/psphotStackImageLoop.c
===================================================================
--- /branches/eam_branches/20091201/psphot/src/psphotStackImageLoop.c	(revision 26523)
+++ /branches/eam_branches/20091201/psphot/src/psphotStackImageLoop.c	(revision 26523)
@@ -0,0 +1,36 @@
+# include "psphotStandAlone.h"
+
+# define ESCAPE(MESSAGE) { \
+  psError(PSPHOT_ERR_DATA, false, MESSAGE); \
+  psFree (view); \
+  return false; \
+}
+
+bool psphotImageLoop (pmConfig *config) {
+
+    bool status;
+    pmChip *chip;
+    pmCell *cell;
+    pmReadout *readout;
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+
+    // XXX for now, just load the full set of images up front
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE ("failed input for fpa in psphot.");
+
+    // XXX for now, we assume there is only a single chip in the PHU:
+    psphotReadoutStack (config, view);
+
+
+    // fail if we failed to handle an error
+    if (psErrorCodeLast() != PS_ERR_NONE) psAbort ("failed to handle an error!");
+
+    psFree (view);
+    return true;
+}
+
+/* 
+   the easiest way to implement this is to assume we can pre-load the full set of images up front.
+   with 5 filters and 6000^2 (image, mask, var = 10 byte per pixel), we need 1.8GB, which is not too bad.
+ */
+
Index: /branches/eam_branches/20091201/psphot/src/psphotStackParseCamera.c
===================================================================
--- /branches/eam_branches/20091201/psphot/src/psphotStackParseCamera.c	(revision 26523)
+++ /branches/eam_branches/20091201/psphot/src/psphotStackParseCamera.c	(revision 26523)
@@ -0,0 +1,102 @@
+# include "psphotStandAlone.h"
+
+static pmFPAfile *defineFile(pmConfig *config, pmFPAfile *bind, const char *name, const char *filename, pmFPAfileType type);
+
+// define the needed / desired I/O files
+bool psphotStackParseCamera (pmConfig *config) {
+
+    bool status = false;
+
+    // the input images are defined as a set of metadatas in the INPUTS metadata folder
+    psMetadata *inputs = psMetadataLookupMetadata(&status, config->arguments, "INPUTS"); // The inputs info
+    if (!inputs) {
+	psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find inputs.");
+	return false;
+    }
+
+    psMetadata *item == NULL;
+    int nInputs = 0;
+    while ((item = psMetadataGetIndex(inputs, nInputs)) != NULL) {
+	if (item->type != PS_DATA_METADATA) {
+	    psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Component %s of the input metadata is not of type METADATA", item->name);
+	    return false;
+	}
+
+	psMetadata *input = item->data.md; // The input metadata of interest
+
+	// look for 'IMAGE', 'MASK', 'VARIANCE' in folder (only 'IMAGE' is required)
+
+	psString image = psMetadataLookupStr(NULL, input, "IMAGE"); // Name of image
+	if (!image || strlen(image) == 0) {
+	    psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Component %s lacks IMAGE of type STR", item->name);
+	    return false;
+	}
+	pmFPAfile *imageFile = defineFile(config, NULL, "PSPHOT.INPUT", image, PM_FPA_FILE_IMAGE); // File for image
+	if (!imageFile) {
+	    psError(PS_ERR_UNKNOWN, false, "Unable to define file from image %d (%s)", i, image);
+	    return false;
+	}
+
+	psString mask = psMetadataLookupStr(&status, input, "MASK"); // Name of mask
+	if (mask && strlen(mask) > 0) {
+	    if (!defineFile(config, imageFile, "PSPHOT.INPUT.MASK", mask, PM_FPA_FILE_MASK)) {
+		psError(PS_ERR_UNKNOWN, false, "Unable to define file from mask %d (%s)", i, mask);
+		return false;
+	    }
+	}
+
+	psString variance = psMetadataLookupStr(&mdok, input, "VARIANCE"); // Name of variance map
+	if (variance && strlen(variance) > 0) {
+	    if (!defineFile(config, imageFile, "PSPHOT.INPUT.VARIANCE", variance, PM_FPA_FILE_VARIANCE)) {
+		psError(PS_ERR_UNKNOWN, false, "Unable to define file from variance %d (%s)", i, variance);
+		return false;
+	    }
+	}
+	nInputs ++;
+    }
+    psMetadataRemoveKey(config->arguments, "FILENAMES");
+    psMetadataAddS32 (config->arguments, PS_LIST_TAIL, "INPUTS.NUM", PS_META_REPLACE, "number of inputs", nInputs);
+
+    // define the additional input/output files associated with psphot
+    // XXX figure out which files are needed by psphotStack
+    if (false && !psphotDefineFiles (config, input)) {
+        psError(PSPHOT_ERR_CONFIG, false, "Trouble defining the additional input/output files");
+        return false;
+    }
+
+    psTrace("psphot", 1, "Done with psphotStackParseCamera...\n");
+
+    psErrorClear();                     // some metadata lookup may have failed
+    return true;
+}
+
+// Define a file
+static pmFPAfile *defineFile(pmConfig *config, // Configuration
+                             pmFPAfile *bind, // File to which to bind
+                             const char *name, // Name of file rule
+                             const char *filename, // Name of file
+                             pmFPAfileType type // Type of file
+                             )
+{
+
+    psArray *dummy = psArrayAlloc(1);   // Dummy array of filenames for this FPA
+    dummy->data[0] = psStringCopy(filename);
+    psMetadataAddArray(config->arguments, PS_LIST_TAIL, "FILENAMES", PS_META_REPLACE,
+                       "Filenames for file rule definition", dummy);
+    psFree(dummy);
+
+    bool found = false;             // Found the file?
+    pmFPAfile *file = bind ? pmFPAfileBindFromArgs(&found, bind, config, name, "FILENAMES") :
+        pmFPAfileDefineFromArgs(&found, config, name, "FILENAMES");
+    if (!file || !found) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to define file %s from %s", name, filename);
+        return NULL;
+    }
+    if (file->type != type) {
+        psError(PS_ERR_IO, true, "%s is not of type %s", name, pmFPAfileStringFromType(type));
+        return NULL;
+    }
+
+    return file;
+}
+
Index: /branches/eam_branches/20091201/psphot/src/psphotSubtractBackground.c
===================================================================
--- /branches/eam_branches/20091201/psphot/src/psphotSubtractBackground.c	(revision 26522)
+++ /branches/eam_branches/20091201/psphot/src/psphotSubtractBackground.c	(revision 26523)
@@ -112,2 +112,37 @@
     return true;
 }
+
+// XXX supply filename or keep PSPHOT.INPUT fixed?
+bool psphotStackSubtractBackground (pmConfig *config, const pmFPAview *view)
+{
+    bool status = true;
+
+    int num = psMetadataAddS32 (&status, config->arguments, "INPUTS.NUM");
+    psAbort (!status, "programming error: must define INPUTS.NUM");
+
+    // loop over the available readouts
+    for (int i = 0; i < num; i++) {
+
+        pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PSPHOT.INPUT", i); // File of interest
+
+	// find the currently selected readout
+	pmReadout  *readout = pmFPAviewThisReadout (view, file->fpa);
+	PS_ASSERT_PTR_NON_NULL (readout, false);
+
+	psImageBinning *binning = psphotBackgroundBinning(readout->image, config); // Image binning parameters
+	pmReadout *model = pmFPAGenerateReadout(config, view, "PSPHOT.BACKMDL", inFPA, binning); // this needs to be a MULTI like PSPHOT.INPUT
+	pmReadout *modelStdev = pmFPAGenerateReadout(config, view, "PSPHOT.BACKMDL.STDEV", inFPA, binning); // this needs to be a MULTI like PSPHOT.INPUT
+
+	// XXX save results on model->analysis or readout->analysis??
+	// XXX need to create a worker function from the function above...
+	if (!backgroundModel(model->image, modelStdev->image, model->analysis, readout, binning, config)) {
+	    psError(PS_ERR_UNKNOWN, false, "Unable to generate background model");
+	    return false;
+	}
+	// display the backsub and backgnd images
+	psphotVisualShowBackground (config, view, readout);
+
+	npass ++;
+    }
+    return true;
+}
