Index: trunk/psphot/src/Makefile.am
===================================================================
--- trunk/psphot/src/Makefile.am	(revision 20069)
+++ trunk/psphot/src/Makefile.am	(revision 20070)
@@ -27,4 +27,5 @@
 	psphotDefineFiles.c	       \
 	psphotReadout.c		       \
+	psphotReadoutFindPSF.c	       \
 	psphotModelBackground.c	       \
 	psphotSubtractBackground.c     \
Index: trunk/psphot/src/psphot.h
===================================================================
--- trunk/psphot/src/psphot.h	(revision 20069)
+++ trunk/psphot/src/psphot.h	(revision 20070)
@@ -21,4 +21,5 @@
 bool            psphotModelTest (pmConfig *config, const pmFPAview *view, psMetadata *recipe);
 bool            psphotReadout (pmConfig *config, const pmFPAview *view);
+bool            psphotReadoutFindPSF(pmConfig *config, const pmFPAview *view);
 bool            psphotReadoutCleanup (pmConfig *config, pmReadout *readout, psMetadata *recipe, pmDetections *detections, pmPSF *psf, psArray *sources);
 bool            psphotDefineFiles (pmConfig *config, pmFPAfile *input);
@@ -107,4 +108,7 @@
 bool            psphotMergeSources (psArray *oldSources, psArray *newSources);
 bool            psphotLoadExtSources (pmConfig *config, const pmFPAview *view, psArray *sources);
+
+pmDetections   *psphotLoadPSFSources (pmConfig *config, const pmFPAview *view);
+
 pmPSF          *psphotLoadPSF (pmConfig *config, const pmFPAview *view, psMetadata *recipe);
 bool            psphotSetHeaderNstars (psMetadata *recipe, psArray *sources);
Index: trunk/psphot/src/psphotMergeSources.c
===================================================================
--- trunk/psphot/src/psphotMergeSources.c	(revision 20069)
+++ trunk/psphot/src/psphotMergeSources.c	(revision 20070)
@@ -51,2 +51,42 @@
     return true;
 }
+
+pmDetections *detections = psphotLoadPSFSources (pmConfig *config, const pmFPAview *view) {
+
+    // find the currently selected readout
+    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PSPHOT.INPUT.CMF");
+    if (!readout) {
+        psLogMsg ("psphot", 3, "readout not found");
+        return NULL;
+    }
+
+    pmDetections *detections = pmDetectionsAlloc();
+  
+    psArray *sources = psMetadataLookupPtr (NULL, readout->analysis, "PSPHOT.SOURCES");
+    if (!sources) {
+        psLogMsg ("psphot", 3, "no psf sources for this readout");
+        return detections;
+    }
+
+    detections->peaks = psArrayAllocEmpty(100);
+
+    for (int i = 0; i < sources->n; i++) {
+        pmSource *source = sources->data[i];
+        pmModel *model = source->modelPSF;
+
+        float xpos = model->params->data.F32[PM_PAR_XPOS];
+        float ypos = model->params->data.F32[PM_PAR_YPOS];
+
+        pmPeak *peak = pmPeakAlloc(xpos, ypos, 1.0, PM_PEAK_LONE);
+        peak->xf = xpos;
+        peak->yf = ypos;
+        peak->flux = 1.0;
+
+	psArrayAdd (detections->peaks, 100, peak);
+	psFree (peak);
+    }
+
+    psLogMsg ("psphot", 3, "%ld PSF sources loaded", detections->peaks->nn);
+
+    return detections;
+}
Index: trunk/psphot/src/psphotReadoutFindPSF.c
===================================================================
--- trunk/psphot/src/psphotReadoutFindPSF.c	(revision 20070)
+++ trunk/psphot/src/psphotReadoutFindPSF.c	(revision 20070)
@@ -0,0 +1,65 @@
+# include "psphotInternal.h"
+
+// in this psphotReadout-variant, we are only trying to determine the PSF given an existing set
+// of input source positions to use as initial PSF stars.
+bool psphotReadoutFindPSF(pmConfig *config, const pmFPAview *view) {
+
+    // measure the total elapsed time in psphotReadout.  XXX the current threading plan
+    // for psphot envisions threading within psphotReadout, not multiple threads calling
+    // the same psphotReadout.  In the current plan, this dtime is the elapsed time used
+    // jointly by the multiple threads, not the total time used by all threads.
+    psTimerStart ("psphotReadout");
+
+    // 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;
+    }
+
+    // find the currently selected readout
+    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PSPHOT.INPUT");
+    PS_ASSERT_PTR_NON_NULL (readout, false);
+
+    // Generate the mask and weight images, including the user-defined analysis region of interest
+    psphotSetMaskAndWeight (config, readout, recipe);
+
+    // display the image, weight, mask (ch 1,2,3)
+    psphotVisualShowImage (readout);
+
+    // include externally-supplied sources
+    // XXX this requires sources to be supplied as PSPHOT.INPUT.CMF
+    pmDetections *detections = psphotLoadPSFSources (config, view);
+    if (detections == NULL) {
+      psLogMsg ("psphot", 3, "problem loading psf stars");
+      return psphotReadoutCleanup (config, readout, recipe, detections, NULL, NULL);
+    }
+    if (detections->peaks == NULL) {
+      psLogMsg ("psphot", 3, "no psf stars supplied");
+      return psphotReadoutCleanup (config, readout, recipe, detections, NULL, NULL);
+    }
+
+    // construct sources and measure basic stats (moments, local sky)
+    psArray *sources = psphotSourceStats (readout, recipe, detections);
+    if (!sources) return false;
+
+    // mark all sources as PSFSTAR
+    for (int i = 0; i < sources->n; i++) {
+      pmSource *source = sources->data[i];
+      source->type = PM_SOURCE_TYPE_STAR;
+      source->mode |= PM_SOURCE_MODE_PSFSTAR;
+    }
+
+    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);
+    }
+    psphotVisualShowPSFModel (readout, psf);
+
+    // 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);
+}
