Index: /trunk/ppImage/src/Makefile.am
===================================================================
--- /trunk/ppImage/src/Makefile.am	(revision 7737)
+++ /trunk/ppImage/src/Makefile.am	(revision 7738)
@@ -1,3 +1,3 @@
-bin_PROGRAMS = ppImage ppTest
+bin_PROGRAMS = ppImage ppFocus ppTest
 
 noinst_HEADERS = \
@@ -23,12 +23,22 @@
 	ppImageAstrom.c
 
-#	ppImageParseCamera.c \
-#	ppDetrendFlat.c \
-#	ppImageWeights.c \
-#	ppImageLoadPixels.c
-#	ppImageOutput.c
-#	ppImageDetrendPedestal.c
-#	ppImageOutput.c
-#	ppImagePhot.c
+ppFocus_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PSPHOT_CFLAGS) $(PSASTRO_CFLAGS) $(ppImage_CFLAGS)
+ppFocus_LDFLAGS = $(PSLIB_LIBS) $(PSMODULE_LIBS) $(PSPHOT_LIBS) $(PSASTRO_LIBS)
+ppFocus_SOURCES = \
+	ppFocus.c \
+	ppFocusArguments.c \
+	ppFocusParseCamera.c \
+	ppFocusGetFWHM.c \
+	ppFocusFitFWHM.c \
+	ppImageLoop.c \
+	ppImageCleanup.c \
+	ppImageOptions.c \
+	ppImageDetrendReadout.c \
+	ppImageDetrendBias.c \
+	ppImageDetrendNonLinear.c \
+	ppImageRebinReadout.c \
+	ppImageMosaic.c \
+	ppImagePhotom.c \
+	ppImageAstrom.c
 
 ppTest_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(ppImage_CFLAGS)
Index: /trunk/ppImage/src/ppFocus.c
===================================================================
--- /trunk/ppImage/src/ppFocus.c	(revision 7737)
+++ /trunk/ppImage/src/ppFocus.c	(revision 7738)
@@ -25,14 +25,34 @@
     }
     
-    
-    
-
-    // Image Arithmetic Loop
-    if (!ppImageLoop(config, options)) {
-        psErrorStackPrint(stderr, "");
+    // we search the argument data for the named fileset (argname)
+    psArray *infiles = psMetadataLookupPtr(NULL, config->arguments, "INPUT");
+    if (!infiles) {
+        psTrace("pmFPAfile", 5, "Failed to find INPUT in argument list");
         exit(1);
     }
 
-    psLogMsg ("ppImage", 3, "complete ppImage run: %f sec\n", psTimerMark (TIMERNAME));
+    // allocate vectors for analysis
+    psVector *focus = psVectorAlloc (infiles->n, PS_TYPE_F32);
+    psVector *fwhm = psVectorAlloc (infiles->n, PS_TYPE_F32);
+
+    for (int i = 0; i < infiles->n; i++) {
+
+	// Image Arithmetic Loop
+	if (!ppImageLoop(config, options)) {
+	    psErrorStackPrint(stderr, "");
+	    exit(1);
+	}
+	
+	// determine FWHM at reference location in image
+	// (also removes PPIMAGE.INPUT from config->files)
+	ppFocusGetFWHM (config, focus, fwhm);
+
+	// silently ignore failure for (i == infiles->n)
+	pmFPAfileDefineSingleFromArgs (NULL, config, "PPIMAGE.INPUT", "INPUT", i+1);
+    }
+
+    ppFocusFitFWHM (config, focus, fwhm);
+
+    psLogMsg ("ppFocus", 3, "complete ppFocus run: %f sec\n", psTimerMark (TIMERNAME));
 
     // Cleaning up
@@ -44,3 +64,3 @@
 // - the input list is a set of independent images (not multiple files for a single image)
 // - each pass to ppImageLoop performs the analysis on a different pmFPAfile
-// - 
+// - after each ppImageLoop, grap the input pmFPAfile and extract the FWHM stats
Index: /trunk/ppImage/src/ppFocusArguments.c
===================================================================
--- /trunk/ppImage/src/ppFocusArguments.c	(revision 7737)
+++ /trunk/ppImage/src/ppFocusArguments.c	(revision 7738)
@@ -2,5 +2,5 @@
 
 static void usage (void) {
-    fprintf (stderr, "USAGE: ppFocus [-file INPUT.fits] [-list INPUT.txt] OUTPUT\n");
+    fprintf (stderr, "USAGE: ppFocus [-file focus.*.fits] [-list INPUT.txt] OUTPUT\n");
     exit (2);
 }
Index: /trunk/ppImage/src/ppFocusFitFWHM.c
===================================================================
--- /trunk/ppImage/src/ppFocusFitFWHM.c	(revision 7738)
+++ /trunk/ppImage/src/ppFocusFitFWHM.c	(revision 7738)
@@ -0,0 +1,22 @@
+# include "ppImage.h"
+
+bool ppFocusFitFWHM (pmConfig *config, psVector *focus, psVector *fwhm) {
+
+    float minFocus;
+
+    psPolynomial1D *poly = psPolynomial1DAlloc (PS_POLYNOMIAL_ORD, 2);
+
+    psVectorFitPolynomial1D (poly, NULL, 0, fwhm, NULL, focus);
+
+    if (poly->coeff[2] <= 0.0) {
+	psLogMsg ("ppFocus", 3, "poor focus fit: zero or negative curvature\n");
+	psLogMsg ("ppFocus", 3, "fit coeffs: %f  %f  %f\n", 
+		  poly->coeff[0], poly->coeff[1], poly->coeff[2]);
+    }
+    
+    minFocus = -0.5 * poly->coeff[1] / poly->coeff[2];
+    psLogMsg ("ppFocus", 3, "best fit focus: %f\n", minFocus);
+    psLogMsg ("ppFocus", 3, "fwhm @ min: %f\n", psPolynomial1DEval (poly, minFocus));
+    
+    return true;
+}
Index: /trunk/ppImage/src/ppFocusGetFWHM.c
===================================================================
--- /trunk/ppImage/src/ppFocusGetFWHM.c	(revision 7738)
+++ /trunk/ppImage/src/ppFocusGetFWHM.c	(revision 7738)
@@ -0,0 +1,72 @@
+# include "ppImage.h"
+
+bool ppFocusGetFWHM (pmConfig *config, psVector *focus, psVector *fwhm) {
+
+    bool status;
+    pmChip *chip;
+    pmCell *cell;
+    pmReadout *readout;
+    psMetadata *header;
+    float FOCUS, FWHM, FWHM_X, FWHM_Y, FWHMsum;
+    int FWHMnum;
+
+    pmFPAfile *input = psMetadataLookupPtr (&status, config->files, "PPIMAGE.INPUT");
+    if (!status) {
+	psErrorStackPrint(stderr, "Can't find input data!\n");
+	exit(EXIT_FAILURE);
+    }
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+
+    // - find readouts with measured PSFs
+    // - measure the average central FWHM for each PSF
+    FWHMsum = 0.0;
+    FWHMnum = 0;
+
+    while ((chip = pmFPAviewNextChip (view, input->fpa, 1)) != NULL) {
+        psLogMsg ("ppImageLoop", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+        if (!chip->process || !chip->file_exists) { continue; }
+
+	while ((cell = pmFPAviewNextCell (view, input->fpa, 1)) != NULL) {
+            psLogMsg ("ppImageLoop", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+            if (!cell->process || !cell->file_exists) { continue; }
+
+	    // process each of the readouts
+	    while ((readout = pmFPAviewNextReadout (view, input->fpa, 1)) != NULL) {
+		if (!readout->data_exists) { continue; }
+
+		// get average FWHM
+		// XXX one option is to use the PSF model to determine the FWHM somewhere
+		# if 0
+		psf = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.PSF");
+		if (psf == NULL) continue;
+		# endif
+
+		// in this example, we use the supplied PSPHOT.HEADER data in which FWHM_X,Y are supplied
+		header = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.HEADER");
+		if (header == NULL) continue;
+
+		FWHM_X = psMetadataLookupF32 (&status, header, "FWHM_X");
+		FWHM_Y = psMetadataLookupF32 (&status, header, "FWHM_Y");
+
+		FWHMsum += 0.5*(FWHM_X + FWHM_Y);
+		FWHMnum ++;
+	    }
+	}
+    }
+
+    FWHM = FWHMsum / FWHMnum;
+
+    // FOCUS = pmConcepts (&status, input->fpa, "FOCUS");
+    FOCUS = 0;
+
+    fwhm->data.F32[fwhm->n] = FWHM;
+    focus->data.F32[focus->n] = FOCUS;
+
+    psVectorExtend (fwhm, 10, 1);
+    psVectorExtend (focus, 10, 1);
+
+    psMetadataRemoveKey (config->files, "PPIMAGE.INPUT");
+    psFree (view);
+    return true;
+}
Index: /trunk/ppImage/src/ppFocusParseCamera.c
===================================================================
--- /trunk/ppImage/src/ppFocusParseCamera.c	(revision 7737)
+++ /trunk/ppImage/src/ppFocusParseCamera.c	(revision 7738)
@@ -8,5 +8,5 @@
     // the first input image defines the camera, and all recipes and options that follow
     // select only the first file from the INPUT array
-    pmFPAfile *input = pmFPAfileDefineFromArgs (&status, config, "PPIMAGE.INPUT", "INPUT");
+    pmFPAfile *input = pmFPAfileDefineSingleFromArgs (&status, config, "PPIMAGE.INPUT", "INPUT", 0);
     if (!status) {
 	psError(PS_ERR_IO, false, "Failed to build FPA from PPIMAGE.INPUT");
Index: /trunk/ppImage/src/ppImage.h
===================================================================
--- /trunk/ppImage/src/ppImage.h	(revision 7737)
+++ /trunk/ppImage/src/ppImage.h	(revision 7738)
@@ -45,3 +45,9 @@
 void ppImageFileCheck (pmConfig *config);
 
+// functions used by ppFocus
+pmConfig *ppFocusArguments(int argc, char **argv);
+ppImageOptions *ppFocusParseCamera (pmConfig *config);
+bool ppFocusGetFWHM (pmConfig *config, psVector *focus, psVector *fwhm);
+bool ppFocusFitFWHM (pmConfig *config, psVector *focus, psVector *fwhm);
+
 #endif
Index: /trunk/ppImage/src/ppImageLoop.c
===================================================================
--- /trunk/ppImage/src/ppImageLoop.c	(revision 7737)
+++ /trunk/ppImage/src/ppImageLoop.c	(revision 7738)
@@ -7,5 +7,4 @@
     pmCell *cell;
     pmReadout *readout;
-    psMemId ID;
 
     pmFPAfile *input = psMetadataLookupPtr (&status, config->files, "PPIMAGE.INPUT");
@@ -24,5 +23,4 @@
         if (!chip->process || !chip->file_exists) { continue; }
 
-	ID = psMemGetId();
 	if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) return false;
 
