Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/astrom/pmAstrometryVisual.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/astrom/pmAstrometryVisual.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/astrom/pmAstrometryVisual.c	(revision 23351)
@@ -23,5 +23,4 @@
 
 //variables to determine when things are plotted
-static bool isVisual             = false;
 static bool plotGridMatch        = true;
 static bool plotTweak            = true;
@@ -48,10 +47,4 @@
 /* Initialization Routines  */
 
-bool pmAstromSetVisual (bool mode) {
-    isVisual = mode;
-    return true;
-}
-
-
 bool pmAstromVisualClose()
 {
@@ -68,9 +61,8 @@
 {
     // make sure we want to plot this
-    if (!plotRawStars || !isVisual) return true;
+    if (!plotRawStars || !pmVisualIsVisual()) return true;
 
     //set up plot region
     if (!pmVisualInitWindow (&kapa, "psastro:plots")){
-        isVisual = false;
         return false;
     }
@@ -222,5 +214,5 @@
 
     // pause and wait for user input:
-    pmVisualAskUser(&plotRawStars, &isVisual);
+    pmVisualAskUser(&plotRawStars);
 
     psFree (xVec);
@@ -234,9 +226,8 @@
 {
     //make sure we want to plot this
-    if (!isVisual || !plotRefStars) return true;
+    if (!pmVisualIsVisual() || !plotRefStars) return true;
 
     //set up plotting variables
     if (!pmVisualInitWindow (&kapa, "psastro:plots")) {
-        isVisual = false;
         return false;
     }
@@ -284,5 +275,5 @@
 
     // pause and wait for user input:
-    pmVisualAskUser(&plotRefStars, &isVisual);
+    pmVisualAskUser(&plotRefStars);
 
     psFree (xVec);
@@ -301,9 +292,8 @@
 
     // make sure we want to plot this
-    if ( !isVisual || !plotLumFunc ) return true;
+    if ( !pmVisualIsVisual() || !plotLumFunc ) return true;
 
     //set up plotting variables
     if ( !pmVisualInitWindow (&kapa, "psastro:plots")){
-        isVisual = false;
         return false;
     }
@@ -377,5 +367,5 @@
 
         // pause and wait for user input:
-        pmVisualAskUser (&plotLumFunc, &isVisual);
+        pmVisualAskUser (&plotLumFunc);
     }
     return true;
@@ -391,9 +381,8 @@
 
     //make sure we want to plot this
-    if (!isVisual || !plotRemoveClumps) return true;
+    if (!pmVisualIsVisual() || !plotRemoveClumps) return true;
 
     //set up plot variables
     if ( !pmVisualInitWindow (&kapa, "psastro:plots")) {
-        isVisual = false;
         return false;
     }
@@ -480,5 +469,5 @@
 
     //ask for user input and finish
-    pmVisualAskUser (&plotRemoveClumps, &isVisual);
+    pmVisualAskUser (&plotRemoveClumps);
     psFree (xVec);
     psFree (yVec);
@@ -496,9 +485,8 @@
 
     //make sure we want to plot this
-    if (!isVisual || !plotOneChipFit)
+    if (!pmVisualIsVisual() || !plotOneChipFit)
         return true;
 
     if(!pmVisualInitWindow(&kapa, "psastro:plots") || !pmVisualInitWindow(&kapa2, "psastro:plots")) {
-        isVisual = false;
         return false;
     }
@@ -511,5 +499,5 @@
 
     //ask for user input and finish
-    pmVisualAskUser(&plotOneChipFit, &isVisual);
+    pmVisualAskUser(&plotOneChipFit);
     return true;
 }
@@ -522,8 +510,7 @@
 {
     //make sure we want to plot this
-    if(!isVisual || !plotFixChips) return true;
+    if(!pmVisualIsVisual() || !plotFixChips) return true;
 
     if(!pmVisualInitWindow(&kapa, "psastro:plots")) {
-        isVisual = false;
         return false;
     }
@@ -588,5 +575,5 @@
     KapaPlotVector (kapa, xNew->n, yNew->data.F32, "y");
 
-    pmVisualAskUser(&plotFixChips, &isVisual);
+    pmVisualAskUser(&plotFixChips);
     psFree(xNew);
     psFree(yNew);
@@ -606,9 +593,8 @@
 
     //make sure we want to plot this
-    if (!isVisual || !plotAstromGuessCheck) return true;
+    if (!pmVisualIsVisual() || !plotAstromGuessCheck) return true;
 
     //set up graph window
     if ( !pmVisualInitWindow (&kapa, "psastro:plots")) {
-        isVisual = false;
         return false;
     }
@@ -686,5 +672,5 @@
     psFree(xResid);
     psFree(yResid);
-    pmVisualAskUser (&plotAstromGuessCheck, &isVisual);
+    pmVisualAskUser (&plotAstromGuessCheck);
     return true;
 }
@@ -696,8 +682,7 @@
 {
     //make sure we want to plot this
-    if (!isVisual || !plotCommonScale) return true;
+    if (!pmVisualIsVisual() || !plotCommonScale) return true;
 
     if (!pmVisualInitWindow(&kapa, "psastro:plots")){
-        isVisual = false;
         return false;
     }
@@ -736,5 +721,5 @@
     KapaSendLabel (kapa, "Old Pixel Scale of FPA Chips (Not to Scale)", KAPA_LABEL_XP);
 
-    pmVisualAskUser (&plotCommonScale, &isVisual);
+    pmVisualAskUser (&plotCommonScale);
 
     psFree(xVec);
@@ -750,8 +735,7 @@
 
     //make sure we want to plot this
-    if (!isVisual || !plotMosaicOneChip) return true;
+    if (!pmVisualIsVisual() || !plotMosaicOneChip) return true;
 
     if(!pmVisualInitWindow(&kapa, "psastro:plots") || !pmVisualInitWindow(&kapa2, "psastro:plots")) {
-        isVisual = false;
         return false;
     }
@@ -759,10 +743,10 @@
     //plot the residuals
     if (!residPlot(rawstars, refstars, match, recipe, "Single Chip Fit Residuals - Mosaic Mode")) {
-        isVisual = false;
+        pmVisualSetVisual(false);
         return false;
     }
 
     //ask for user input and finish
-    pmVisualAskUser(&plotMosaicOneChip, &isVisual);
+    pmVisualAskUser(&plotMosaicOneChip);
 
     return true;
@@ -775,5 +759,5 @@
 {
     //make sure we want to plot this
-    if (!isVisual || !plotMosaicMatches) return true;
+    if (!pmVisualIsVisual() || !plotMosaicMatches) return true;
 
     char title[60];
@@ -782,15 +766,14 @@
 
     if(!pmVisualInitWindow(&kapa, "psastro:plots") || !pmVisualInitWindow(&kapa2, "psastro:plots")) {
-        isVisual = false;
         return false;
     }
 
     if (!residPlot(rawstars, refstars, match, recipe, title)){
-        isVisual = false;
+        pmVisualSetVisual(false);
         return false;
     }
 
     //ask for user input
-    pmVisualAskUser (&plotMosaicMatches, &isVisual);
+    pmVisualAskUser (&plotMosaicMatches);
     return true;
 }
@@ -807,7 +790,6 @@
 {
     //make sure we want to plot this
-    if (!isVisual || !plotGridMatch) return true;
+    if (!pmVisualIsVisual() || !plotGridMatch) return true;
     if (!pmVisualInitWindow(&kapa, "psastro:plots")){
-        isVisual = false;
         return false;
     }
@@ -952,5 +934,5 @@
     KapaPlotVector (kapa, 2, yslice, "y");
 
-    pmVisualAskUser(&plotGridMatch, &isVisual);
+    pmVisualAskUser(&plotGridMatch);
     psFree(dXplot);
     psFree(dYplot);
@@ -966,7 +948,6 @@
 {
     //make sure we want to plot this
-    if (!isVisual || !plotTweak) return true;
+    if (!pmVisualIsVisual() || !plotTweak) return true;
     if (!pmVisualInitWindow(&kapa, "psastro:plots")) {
-        isVisual = false;
         return false;
     }
@@ -1051,5 +1032,5 @@
                    KAPA_LABEL_XP);
 
-    pmVisualAskUser(&plotTweak, &isVisual);
+    pmVisualAskUser(&plotTweak);
 
     psFree(xIndices);
@@ -1341,5 +1322,4 @@
 # else
 
-bool pmAstromSetVisual(bool mode) { return true; }
 bool pmAstromVisualClose() { return true; }
 bool pmAstromVisualPlotGridMatch (const psArray *raw, const psArray *ref, psImage *gridNP, double offsetX, double offsetY, double maxOffpix, double Scale, double Offset) { return true; }
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/astrom/pmAstrometryVisual.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/astrom/pmAstrometryVisual.h	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/astrom/pmAstrometryVisual.h	(revision 23351)
@@ -22,10 +22,4 @@
     int sPeak;                          ///< sum of stars to peak bin
 } pmLumFunc;
-
-
-/** Enable or disable visual plotting for psastro routines
- * @param mode true/false to enable/disable plotting
- * @return true for success */
-bool pmAstromSetVisual(bool mode);
 
 
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPACopy.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPACopy.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPACopy.c	(revision 23351)
@@ -173,4 +173,10 @@
     int xParityTarget = psMetadataLookupS32(&mdokT, target->concepts, "CELL.XPARITY"); // Target x parity
     assert(mdokS && mdokT);
+
+    if (xParityTarget == 0) {
+        psMetadataItem *item = psMetadataLookup(target->concepts, "CELL.XPARITY"); // Item with parity
+        xParityTarget = item->data.S32 = xParitySource;
+    }
+
     psAssert (abs(xParitySource) == 1, "CELL.XPARITY not set for source");
     psAssert (abs(xParityTarget) == 1, "CELL.XPARITY not set for target");
@@ -192,4 +198,10 @@
     int yParitySource = psMetadataLookupS32(&mdokS, source->concepts, "CELL.YPARITY"); // Source parity
     assert(mdokS && mdokT);
+
+    if (yParityTarget == 0) {
+        psMetadataItem *item = psMetadataLookup(target->concepts, "CELL.YPARITY"); // Item with parity
+        yParityTarget = item->data.S32 = yParitySource;
+    }
+
     psAssert (abs(yParitySource) == 1, "CELL.YPARITY not set for source");
     psAssert (abs(yParityTarget) == 1, "CELL.YPARITY not set for target");
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAMaskWeight.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAMaskWeight.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAMaskWeight.c	(revision 23351)
@@ -331,5 +331,5 @@
 
     if (!psMemIncrRefCounter(rng)) {
-        rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        rng = psRandomAlloc(PS_RANDOM_TAUS);
     }
 
@@ -371,5 +371,5 @@
 
     if (!psMemIncrRefCounter(rng)) {
-        rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        rng = psRandomAlloc(PS_RANDOM_TAUS);
     }
 
@@ -532,5 +532,5 @@
 
     if (!psMemIncrRefCounter(rng)) {
-        rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        rng = psRandomAlloc(PS_RANDOM_TAUS);
     }
 
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAWrite.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAWrite.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAWrite.c	(revision 23351)
@@ -692,4 +692,19 @@
                      "Centre of covariance matrix in y", -yMinCovar);
 
+    // Turn off compression
+    int bitpix = fits->options ? fits->options->bitpix : 0; // Desired bits per pixel
+    psFitsCompression *compress = psFitsCompressionGet(fits); // Current compression options
+    if (!psFitsSetCompression(fits, PS_FITS_COMPRESS_NONE, NULL, 0, 0, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to set FITS compression to NONE");
+        psFree(extname);
+        psFree(header);
+        psFree(images);
+        psFree(compress);
+        return false;
+    }
+    if (fits->options) {
+        fits->options->bitpix = 0;
+    }
+
     // Write images
     if (!psFitsWriteImageCube(fits, header, images, extname)) {
@@ -699,4 +714,9 @@
         psFree(header);
         psFree(images);
+        if (fits->options) {
+            fits->options->bitpix = bitpix;
+        }
+        psFitsCompressionApply(fits, compress);
+        psFree(compress);
         return 0;
     }
@@ -704,4 +724,15 @@
     psFree(header);
     psFree(images);
+
+    // Restore compression
+    if (fits->options) {
+        fits->options->bitpix = bitpix;
+    }
+    if (!psFitsCompressionApply(fits, compress)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to set FITS compression to NONE");
+        psFree(compress);
+        return false;
+    }
+    psFree(compress);
 
     return true;
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPA_JPEG.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPA_JPEG.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPA_JPEG.c	(revision 23351)
@@ -154,5 +154,5 @@
 
     // measure the image statistics for scaling
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS);
     psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN);
     stats->nSubsample = 10000;
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfile.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfile.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfile.c	(revision 23351)
@@ -50,4 +50,5 @@
 
     psFree (file->filename);
+    psFree (file->origname);
     psFree (file->extname);
 
@@ -88,4 +89,5 @@
 
     file->filename = NULL;
+    file->origname = NULL;
     file->extname  = NULL;
 
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfile.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfile.h	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfile.h	(revision 23351)
@@ -91,4 +91,5 @@
     char *name;                         // the name of the rule (useful for debugging / tracing)
     char *filename;                     // the current name of an active file
+    char *origname;                     // the original name (before mangling) of an active file
     char *extname;                      // the current name of an active file extension
 
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileDefine.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileDefine.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileDefine.c	(revision 23351)
@@ -11,4 +11,5 @@
 #include "pmConfig.h"
 #include "pmConfigMask.h"
+#include "pmConfigRun.h"
 #include "pmDetrendDB.h"
 
@@ -216,16 +217,4 @@
         file->imageId = config->imageId;
     }
-
-    // XXX this seems a bit of a hack: use the cameraName to determine the mosaic level...
-    # if (0)
-    if (cameraName) {
-        if (!strcmp(cameraName + strlen(cameraName) - 5, "-CHIP")) {
-            file->mosaicLevel = PM_FPA_LEVEL_CHIP;
-        }
-        if (!strcmp(cameraName + strlen(cameraName) - 5, "-FPA")) {
-            file->mosaicLevel = PM_FPA_LEVEL_FPA;
-        }
-    }
-    # endif
 
     // Use the format we were told to, the format specified in the file rule, or default to the default format
@@ -383,13 +372,15 @@
     }
 
-    psTrace ("psModules.camera", 5, "file: %s, format: %s, fileLevel: %s, extLevel: %s, dataLevel: %s, freeLevel: %s\n",
-             file->name, file->formatName, pmFPALevelToName (file->fileLevel), pmFPALevelToName(extLevel), pmFPALevelToName (file->dataLevel), pmFPALevelToName (file->freeLevel));
+    psTrace("psModules.camera", 5,
+            "file: %s, format: %s, fileLevel: %s, extLevel: %s, dataLevel: %s, freeLevel: %s\n",
+            file->name, file->formatName, pmFPALevelToName(file->fileLevel), pmFPALevelToName(extLevel),
+            pmFPALevelToName(file->dataLevel), pmFPALevelToName(file->freeLevel));
 
     // add argument-supplied OUTPUT name to this file
     char *outname = psMetadataLookupStr(&status, config->arguments, "OUTPUT");
-    psMetadataAddStr(file->names, PS_LIST_TAIL, "OUTPUT", PS_META_NO_REPLACE, "", outname);
+    psMetadataAddStr(file->names, PS_LIST_TAIL, "OUTPUT", PS_META_NO_REPLACE, "Output file name", outname);
 
     // place the resulting file in the config system
-    psMetadataAddPtr (config->files, PS_LIST_TAIL, name, PS_DATA_UNKNOWN, "", file);
+    psMetadataAddPtr(config->files, PS_LIST_TAIL, name, PS_DATA_UNKNOWN, "Output file", file);
     psFree(file);                       // we free this copy of file, but 'files' still has a copy
     return file;                        // the returned value is a view into the version on 'files'
@@ -426,11 +417,204 @@
 }
 
-// search for argname on the config->argument list
-// construct an FPA based on the files in this list (must represent a single FPA)
-// built the association between the FPA elements (CHIP/CELL) and the files
-// define the pmFPAfile filename and bind it to this FPA
-// save the pmFPAfile on config->files
-// return the pmFPAfile (a view to the one saved on config->files)
-pmFPAfile *pmFPAfileDefineFromArgs(bool *success, pmConfig *config, const char *filename, const char *argname)
+
+/// Define a file from an array of filenames
+static pmFPAfile *fpaFileDefineFromArray(pmConfig *config, // Configuration
+                                         pmFPAfile *bind, // File to bind to, or NULL
+                                         const char *name, // Name of file
+                                         const psArray *filenames // Array of file names
+    )
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
+
+    pmFPA *fpa = NULL;                  // FPA for file
+    psMetadata *format = NULL;          // Camera format configuration
+    psString formatName = NULL;         // Name of camera format
+    psString cameraName = NULL;         // Name of camera
+    pmFPALevel fileLevel = PM_FPA_LEVEL_NONE; // Level for files
+    psMetadata *phu = NULL;             // Primary header
+    if (bind) {
+        // Use the FPA we're binding to
+        fpa = psMemIncrRefCounter(bind->fpa);
+        fileLevel = bind->fileLevel;
+    } else {
+        // Need to generate an FPA
+        psString realName = pmConfigConvertFilename(filenames->data[0], config, false, false);
+        if (!realName) {
+            psError(PS_ERR_IO, false, "Failed to convert file name %s", (char *)filenames->data[0]);
+            return NULL;
+        }
+
+        // load the header of the first image
+        // EXTWORD (fits->extword) is not relevant to the PHU
+        psFits *fits = psFitsOpen(realName, "r"); // FITS file
+        if (!fits) {
+            psError(PS_ERR_IO, false, "Failed to open file %s", realName);
+            psFree(realName);
+            return NULL;
+        }
+        phu = psFitsReadHeader (NULL, fits); // Primary header
+        if (!phu) {
+            psError(PS_ERR_IO, false, "Failed to read file header %s", realName);
+            psFree(realName);
+            return NULL;
+        }
+        psFitsClose(fits);
+
+        // Determine the current format from the header; determine camera if not specified already.
+        psMetadata *camera = NULL;      // Camera configuration
+        format = pmConfigCameraFormatFromHeader(&camera, &cameraName, &formatName, config, phu, true);
+        if (!format) {
+            psError(PS_ERR_IO, false, "Failed to determine camera format for %s", realName);
+            psFree(camera);
+            psFree(formatName);
+            psFree(realName);
+            psFree(phu);
+            return NULL;
+        }
+
+        fileLevel = pmFPAPHULevel(format);
+        if (fileLevel == PM_FPA_LEVEL_NONE) {
+            psError(PS_ERR_IO, true, "Unable to determine file level for %s", realName);
+            psFree(camera);
+            psFree(formatName);
+            psFree(realName);
+            psFree(phu);
+            return NULL;
+        }
+
+        // build the template fpa, set up the basic view
+        // XXX do we want this to be the baseCamera name or the metaCamera name?
+        fpa = pmFPAConstruct(camera, cameraName);
+        psFree(camera);
+        if (!fpa) {
+            psError(PS_ERR_IO, false, "Failed to construct FPA from %s", realName);
+            psFree(formatName);
+            psFree(realName);
+            psFree(format);
+            psFree(phu);
+            return NULL;
+        }
+        psFree(realName);
+    }
+
+    // load the given filerule (from config->camera) and bind it to the fpa
+    // the returned file is just a view to the entry on config->files
+    pmFPAfile *file = pmFPAfileDefineInput(config, fpa, cameraName, name); // File, to return
+    if (!file) {
+        psError(PS_ERR_IO, false, "File %s not defined", name);
+        psFree(formatName);
+        psFree(format);
+        psFree(fpa);
+        psFree(phu);
+        return NULL;
+    }
+    psFree(cameraName);
+    psFree(fpa);                        // Drop reference
+
+    file->format = format;
+    file->formatName = formatName;
+    file->fileLevel = fileLevel;
+
+    // We use the filerule and filesrc to identify the files in the file->names data
+    psFree(file->filerule); // this is set in pmFPAfileDefineInput
+    file->filerule = psStringCopy("@FILES");
+    file->filesrc = psStringCopy("{CHIP.NAME}.{CELL.NAME}");
+
+    // Examine the list of input files and validate their cameras
+    // Associate each filename with an element of the FPA
+    // Save the association on file->names
+    for (int i = 0; i < filenames->n; i++) {
+        // Check that the file corresponds to the same camera and format
+        if (!phu) {
+            psString realName = pmConfigConvertFilename(filenames->data[i], config, false, false);
+            if (!realName) {
+                psError(PS_ERR_IO, false, "Failed to convert file name %s", (char*)filenames->data[i]);
+                return NULL;
+            }
+            psFits *fits = psFitsOpen(realName, "r"); // FITS file
+            if (!fits) {
+                psError(PS_ERR_IO, false, "Failed to open file %s", realName);
+                psFree(realName);
+                return NULL;
+            }
+            phu = psFitsReadHeader(NULL, fits);
+            psFitsClose(fits);
+            if (!phu) {
+                psError(PS_ERR_IO, false, "Failed to read file header %s", realName);
+                psFree(realName);
+                return NULL;
+            }
+            psFree(realName);
+        }
+
+        if (i == 0 && file->type == PM_FPA_FILE_MASK) {
+            if (!pmConfigMaskReadHeader(config, phu)) {
+                psError(PS_ERR_IO, false, "Error reading mask bits");
+                psFree(phu);
+                return NULL;
+            }
+        }
+
+        if (bind || i > 0) {
+            if (format) {
+                bool valid = false;
+                if (!pmConfigValidateCameraFormat(&valid, format, phu)) {
+                    psError(PS_ERR_UNKNOWN, false, "Error in config scripts\n");
+                    psFree(phu);
+                    return NULL;
+                }
+                if (!valid) {
+                    psError(PS_ERR_IO, false, "File %s is not from the required camera",
+                            (char*)filenames->data[i]);
+                    psFree(phu);
+                    return NULL;
+                }
+            } else {
+                format = pmConfigCameraFormatFromHeader(NULL, NULL, NULL, config, phu, true);
+                if (!format) {
+                    psError(PS_ERR_IO, false, "Failed to determine camera format from %s",
+                            (char*)filenames->data[i]);
+                    psFree(phu);
+                    return NULL;
+                }
+            }
+        }
+
+        // Ensure the format is set
+        if (!file->format) {
+            file->format = format;
+        }
+        if (!file->formatName) {
+            file->formatName = formatName;
+        }
+
+        // Set the view to the corresponding entry for this phu
+        pmFPAview *view = NULL;         // View to PHU
+        if (bind) {
+            view = pmFPAIdentifySourceFromHeader(bind->fpa, phu, format);
+        } else {
+            view = pmFPAAddSourceFromHeader(fpa, phu, format);
+        }
+        psFree(phu);
+        phu = NULL;
+        if (!view) {
+            psError(PS_ERR_IO, false, "Unable to determine source for %s", name);
+            return NULL;
+        }
+
+        // Associate the filename with the FPA element
+        psString location = pmFPAfileNameFromRule(file->filesrc, file, view);
+        psFree(view);
+        psMetadataAddStr(file->names, PS_LIST_TAIL, location, 0, "Location of file", filenames->data[i]);
+        psFree(location);
+    }
+
+    return file;
+}
+
+
+pmFPAfile *pmFPAfileDefineFromArgs(bool *success, pmConfig *config,
+                                   const char *filename, const char *argname)
 {
     PS_ASSERT_PTR_NON_NULL(config, NULL);
@@ -438,20 +622,7 @@
     PS_ASSERT_STRING_NON_EMPTY(argname, NULL);
 
-    bool status;
-    pmFPA *fpa = NULL;
-    psFits *fits = NULL;
-    pmFPAfile *file = NULL;
-    psMetadata *phu = NULL;
-    psMetadata *format = NULL;
-    psMetadata *camera = NULL;
-    psString formatName = NULL;
-
-    // use success to identify valid exit conditions (as opposed to 'argument not supplied')
-    if (success) {
-        *success = false;
-    }
-
-    // we search the argument data for the named fileset (argname)
-    psArray *infiles = psMetadataLookupPtr(&status, config->arguments, argname);
+    // Search the argument data for the named fileset (argname)
+    bool status;                        // Status of MD lookup
+    psArray *filenames = psMetadataLookupPtr(&status, config->arguments, argname); // Filenames for file
     if (!status) {
         if (success) {
@@ -460,182 +631,23 @@
         return NULL;
     }
-    if (infiles->n < 1) {
-        psError(PS_ERR_IO, false, "Found n == %ld files in %s in arguments\n", infiles->n, argname);
-        return NULL;
-    }
-
-    // this function is implicitly an INPUT operation: do not create the file
-    psString realName = pmConfigConvertFilename (infiles->data[0], config, false, false);
-    if (!realName) {
-        psError(PS_ERR_IO, false, "Failed to convert file name %s\n", (char *) infiles->data[0]);
-        return NULL;
-    }
-
-    // load the header of the first image
-    // EXTWORD (fits->extword) is not relevant to the PHU
-    fits = psFitsOpen (realName, "r");
-    if (!fits) {
-        psError(PS_ERR_IO, false, "Failed to open file %s\n", realName);
-        psFree (realName);
-        return NULL;
-    }
-    phu = psFitsReadHeader (NULL, fits);
-
-    if (!phu) {
-        psError(PS_ERR_IO, false, "Failed to read file header %s\n", realName);
-        psFree (realName);
-        return NULL;
-    }
-    psFitsClose(fits);
-
-    // Determine the current format from the header; Determine camera if not specified already.
-    // the returned pointers 'camera' and 'formatName' are allocated here
-    psString cameraName = NULL;
-    format = pmConfigCameraFormatFromHeader(&camera, &cameraName, &formatName, config, phu, true);
-    if (!format) {
-        psError(PS_ERR_IO, false, "Failed to read CCD format from %s\n", realName);
-        psFree(phu);
-        psFree(camera);
-        psFree(formatName);
-        psFree(realName);
-        return NULL;
-    }
-
-    // build the template fpa, set up the basic view
-    // XXX do we want this to be the baseCamera name or the metaCamera name?
-    fpa = pmFPAConstruct(camera, cameraName);
-    if (!fpa) {
-        psError(PS_ERR_IO, false, "Failed to construct FPA from %s", realName);
-        psFree(phu);
-        psFree(camera);
-        psFree(formatName);
-        psFree(realName);
-        psFree(format);
-        return NULL;
-    }
-    psFree (realName);
-    psFree (camera);
-
-    // load the given filerule (from config->camera) and bind it to the fpa
-    // the returned file is just a view to the entry on config->files
-    file = pmFPAfileDefineInput(config, fpa, cameraName, filename);
-    if (!file) {
-        psError(PS_ERR_IO, false, "file %s not defined\n", filename);
-        psFree(phu);
-        psFree(formatName);
-        psFree(format);
-        psFree(fpa);
-        return NULL;
-    }
-    psFree (cameraName);
-    psFree (file->filerule); // this is set in pmFPAfileDefineInput
-
-    file->format = format;
-    file->formatName = formatName;
-    file->filerule = psStringCopy("@FILES");
-    file->filesrc = psStringCopy("{CHIP.NAME}.{CELL.NAME}");
-    // we use the above rules to identify these files in the file->names data
-
-    file->fileLevel = pmFPAPHULevel(format);
-    if (file->fileLevel == PM_FPA_LEVEL_NONE) {
-        psError(PS_ERR_IO, true, "Unable to determine file level for %s\n", file->name);
-        psFree(phu);
-        psFree(fpa);
-        return NULL;
-    }
-
-    if (file->type == PM_FPA_FILE_MASK) {
-        if (!pmConfigMaskReadHeader (config, phu)) {
-            psError(PS_ERR_IO, false, "error in mask bits");
-            return NULL;
-        }
-    }
-
-    // examine the list of input files and validate their cameras
-    // associated each filename with an element of the FPA
-    // save the association on file->names
-    for (int i = 0; i < infiles->n; i++) {
-        if (i > 0) {
-            // this function is implicitly an INPUT operation: do not create the file
-            psString realName = pmConfigConvertFilename (infiles->data[i], config, false, false);
-            if (!realName) {
-                psError(PS_ERR_IO, false, "Failed to convert file name %s", (char *) infiles->data[i]);
-                psFree(phu);
-                psFree(fpa);
-                return NULL;
-            }
-            // EXTWORD (fits->extword) is not relevant to the PHU
-            fits = psFitsOpen (realName, "r");
-            if (!fits) {
-                psError(PS_ERR_IO, false, "Failed to open file %s\n", realName);
-                psFree(realName);
-                psFree(phu);
-                psFree(fpa);
-                return NULL;
-            }
-            phu = psFitsReadHeader (NULL, fits);
-            if (!phu) {
-                psError(PS_ERR_IO, false, "Failed to read file header %s", realName);
-                psFree(realName);
-                psFitsClose(fits);
-                psFree(phu);
-                psFree(fpa);
-                return NULL;
-            }
-            bool valid = false;
-            if (!pmConfigValidateCameraFormat (&valid, format, phu)) {
-                psError(PS_ERR_UNKNOWN, false, "Error in config scripts\n");
-                psFree(realName);
-                psFitsClose(fits);
-                psFree(phu);
-                psFree(fpa);
-                return NULL;
-            }
-            if (!valid) {
-                psError(PS_ERR_IO, false, "file %s is not from the required camera", realName);
-                psFree(realName);
-                psFitsClose(fits);
-                psFree(phu);
-                psFree(fpa);
-                return NULL;
-            }
-            psFree(realName);
-            psFitsClose(fits);
-        }
-
-        // set the view to the corresponding entry for this phu
-        pmFPAview *view = pmFPAAddSourceFromHeader (fpa, phu, format);
-        if (!view) {
-            psError(PS_ERR_IO, false, "Unable to determine source for %s", file->name);
-            psFree(phu);
-            psFree(fpa);
-            return NULL;
-        }
-
-        // associate the filename with the FPA element
-        char *name = pmFPAfileNameFromRule(file->filesrc, file, view);
-
-        // save the name association in the pmFPAfile structure
-        psMetadataAddStr(file->names, PS_LIST_TAIL, name, 0, "", infiles->data[i]);
-
-        psFree(view);
-        psFree(name);
-        psFree(phu);
-    }
-    psFree(fpa);
+    if (filenames->n == 0) {
+        psError(PS_ERR_IO, false, "No files in array in %s in arguments", argname);
+        if (success) {
+            *success = false;
+        }
+        return NULL;
+    }
+
+    pmFPAfile *file = fpaFileDefineFromArray(config, NULL, filename, filenames); // File of interest
+
     if (success) {
-        *success = true;
-    }
-
-    return file;
-}
-
-// search for argname on the config->argument list
-// construct an FPA based on the files in this list (must represent a single FPA)
-// built the association between the FPA elements (CHIP/CELL) and the files
-// define the pmFPAfile filename and bind it to this FPA
-// save the pmFPAfile on config->files
-// return the pmFPAfile (a view to the one saved on config->files)
-pmFPAfile *pmFPAfileBindFromArgs (bool *success, pmFPAfile *input, pmConfig *config, const char *filename, const char *argname)
+        *success = file ? true : false;
+    }
+
+    return file;
+}
+
+pmFPAfile *pmFPAfileBindFromArgs(bool *success, pmFPAfile *input, pmConfig *config,
+                                 const char *filename, const char *argname)
 {
     PS_ASSERT_PTR_NON_NULL(input, NULL);
@@ -644,18 +656,8 @@
     PS_ASSERT_STRING_NON_EMPTY(argname, NULL);
 
-    bool status;
-    psFits *fits = NULL;
-    pmFPAfile *file = NULL;
-    psMetadata *phu = NULL;
-
-    // use success to identify valid exit conditions (as opposed to 'argument not supplied')
-    if (success) {
-        *success = false;
-    }
-
-    // we search the argument data for the named fileset (argname)
-    psArray *infiles = psMetadataLookupPtr(&status, config->arguments, argname);
+    // Search the argument data for the named fileset (argname)
+    bool status;                        // Status of MD lookup
+    psArray *filenames = psMetadataLookupPtr(&status, config->arguments, argname); // Filenames for file
     if (!status) {
-        // this is not an error: this just means no matching argument was supplied
         if (success) {
             *success = true;
@@ -663,125 +665,23 @@
         return NULL;
     }
-    if (infiles->n < 1) {
-        psError(PS_ERR_IO, false, "Found n == %ld files in %s in arguments\n", infiles->n, argname);
-        return NULL;
-    }
-
-    // load the given filerule (from config->camera) and bind it to the fpa
-    // the returned file is just a view to the entry on config->files
-    file = pmFPAfileDefineInput (config, input->fpa, NULL, filename);
-    if (!file) {
-        psError(PS_ERR_IO, false, "file %s not defined\n", filename);
-        psFree(phu);
-        return NULL;
-    }
-
-    // set derived values
-    file->fileLevel = input->fileLevel;
-
-
-    // define the rule to identify these files in the file->names data
-    psFree (file->filerule);
-    psFree (file->filesrc);
-    file->filerule = psStringCopy ("@FILES");
-    file->filesrc = psStringCopy ("{CHIP.NAME}.{CELL.NAME}");
-
-    // examine the list of input files and validate their cameras
-    // associated each filename with an element of the FPA
-    // save the association on file->names
-    psMetadata *format = NULL;
-    for (int i = 0; i < infiles->n; i++) {
-        // this function is implicitly an INPUT operation: do not create the file
-        psString realName = pmConfigConvertFilename (infiles->data[i], config, false, false);
-        if (!realName) {
-            psError(PS_ERR_IO, false, "Failed to convert file name %s", (char *) infiles->data[i]);
-            return NULL;
-        }
-        // EXTWORD (fits->extword) is not relevant to the PHU
-        fits = psFitsOpen (realName, "r");
-        if (!fits) {
-            psError(PS_ERR_IO, false, "Failed to open file %s\n", realName);
-            psFree(realName);
-            return NULL;
-        }
-        phu = psFitsReadHeader (NULL, fits);
-        if (!phu) {
-            psError(PS_ERR_IO, false, "Failed to read file header %s", realName);
-            psFree(realName);
-            psFitsClose(fits);
-            return NULL;
-        }
-
-        if (!format) {
-            format = pmConfigCameraFormatFromHeader(NULL, NULL, NULL, config, phu, true);
-            if (!format) {
-                psError(PS_ERR_IO, false, "Failed to read CCD format from %s\n", realName);
-                psFree(phu);
-                psFree(realName);
-                psFitsClose(fits);
-                return NULL;
-            }
-        } else {
-            bool valid = false;
-            if (!pmConfigValidateCameraFormat(&valid, format, phu)) {
-                psError(PS_ERR_UNKNOWN, false, "Error in config scripts\n");
-                psFree(realName);
-                psFitsClose(fits);
-                return NULL;
-            }
-            if (!valid) {
-                psError(PS_ERR_IO, false, "specified data file %s does not match format of supplied INPUT\n",
-                        realName);
-                psFree(realName);
-                psFitsClose(fits);
-                return NULL;
-            }
-        }
-
-        psFree(realName);
-        psFitsClose(fits);
-
-        // set the view to the corresponding entry for this phu
-        pmFPAview *view = pmFPAIdentifySourceFromHeader (input->fpa, phu, format);
-        if (!view) {
-            psError(PS_ERR_IO, false, "Unable to determine source for %s", file->name);
-            psFree(phu);
-            return NULL;
-        }
-
-        // associate the filename with the FPA element
-        char *name = pmFPAfileNameFromRule(file->filesrc, file, view);
-
-        // save the name association in the pmFPAfile structure
-        psMetadataAddStr (file->names, PS_LIST_TAIL, name, 0, "", infiles->data[i]);
-
-        if ((i == 0) && (file->type == PM_FPA_FILE_MASK)) {
-            if (!pmConfigMaskReadHeader (config, phu)) {
-                psError(PS_ERR_IO, false, "error in mask bits");
-                return NULL;
-            }
-        }
-
-        psFree(view);
-        psFree(name);
-        psFree(phu);
-    }
-    file->format = format;
-    file->formatName = psStringCopy(config->formatName);
+    if (filenames->n == 0) {
+        psError(PS_ERR_IO, false, "No files in array in %s in arguments", argname);
+        if (success) {
+            *success = false;
+        }
+        return NULL;
+    }
+
+    pmFPAfile *file = fpaFileDefineFromArray(config, input, filename, filenames); // File of interest
 
     if (success) {
-        *success = true;
-    }
-    return file;
-}
-
-// search for argname on the config->argument list
-// construct an FPA based on the files in this list (each represents the same FPA)
-// built the association between the FPA elements (CHIP/CELL) and the files
-// define the pmFPAfile filenames and bind them to the FPAs
-// save the pmFPAfiles on config->files
-// return the pmFPAfiles (a view to the one saved on config->files)
-pmFPAfile *pmFPAfileDefineSingleFromArgs (bool *success, pmConfig *config, const char *filename,
-        const char *argname, int entry)
+        *success = file ? true : false;
+    }
+
+    return file;
+}
+
+pmFPAfile *pmFPAfileDefineSingleFromArgs(bool *success, pmConfig *config, const char *filename,
+                                         const char *argname, int entry)
 {
     PS_ASSERT_PTR_NON_NULL(config, NULL);
@@ -789,19 +689,8 @@
     PS_ASSERT_STRING_NON_EMPTY(argname, NULL);
 
-    bool status;
-    pmFPA *fpa = NULL;
-    psFits *fits = NULL;
-    pmFPAfile *file = NULL;
-    psMetadata *phu = NULL;
-    psMetadata *format = NULL;
-
-    if (success) {
-        *success = false;
-    }
-
-    // we search the argument data for the named fileset (argname)
-    psArray *infiles = psMetadataLookupPtr(&status, config->arguments, argname);
+    // Search the argument data for the named fileset (argname)
+    bool status;                        // Status from MD lookup
+    psArray *filenames = psMetadataLookupPtr(&status, config->arguments, argname); // Filenames for file
     if (!status) {
-        psTrace("psModules.camera", 5, "Failed to find %s in argument list", argname);
         if (success) {
             *success = true;
@@ -809,108 +698,52 @@
         return NULL;
     }
-    if (infiles->n <= entry) {
-        psError(PS_ERR_IO, false, "only %ld files in %s in argument, entry %d requested\n",
-                infiles->n, argname, entry);
-        return NULL;
-    }
-
-    // examine the list of input files and validate their cameras
-    // associated each filename with an element of the FPA
-    // save the association on file->names
-    // EXTWORD (fits->extword) is not relevant to the PHU
-    fits = psFitsOpen (infiles->data[entry], "r");
-    phu = psFitsReadHeader (NULL, fits);
-    psFitsClose (fits);
-
-    // on first call to this function, config->camera is not set.
-    // later calls will give an error if the cameras do not match
-    psMetadata *camera = NULL;
-    psString formatName = NULL;
-    psString cameraName = NULL;
-    format = pmConfigCameraFormatFromHeader (&camera, &cameraName, &formatName, config, phu, true);
-    if (!format) {
-        psError(PS_ERR_IO, false, "Failed to read CCD format from %s\n", (char *)infiles->data[0]);
-        psFree(phu);
-        psFree(camera);
-        psFree(formatName);
-        return NULL;
-    }
-
-    // build the template fpa, set up the basic view
-    fpa = pmFPAConstruct (camera, cameraName);
-    if (!fpa) {
-        psError(PS_ERR_IO, false, "Failed to construct FPA from %s", (char *)infiles->data[0]);
-        psFree(phu);
-        psFree(camera);
-        psFree(formatName);
-        psFree(format);
-        return NULL;
-    }
-    psFree(camera);
-
-    // load the given filerule (from config->camera) and bind it to the fpa
-    // the returned file is just a view to the entry on config->files
-    // we need a variable name here... (but in filerule)
-    file = pmFPAfileDefineInput (config, fpa, cameraName, filename);
-    if (!file) {
-        psError(PS_ERR_IO, false, "file %s not defined\n", filename);
-        psFree(phu);
-        psFree(fpa);
-        psFree(format);
-        return NULL;
-    }
-    psFree(cameraName);
-    FPA_TEST_ASSERT (file);
-
-    file->format = format;
-    file->formatName = formatName;
-    file->filerule = psStringCopy ("@FILES");
-    file->filesrc = psStringCopy ("{CHIP.NAME}.{CELL.NAME}");
-    // adjust the above rules to identify these files in the file->names data
-
-    file->fileLevel = pmFPAPHULevel(format);
-    if (file->fileLevel == PM_FPA_LEVEL_NONE) {
-        psError(PS_ERR_IO, true, "Unable to determine file level for %s\n", file->name);
-        psFree(phu);
-        psFree(fpa);
-        return NULL;
-    }
-
-    if (file->type == PM_FPA_FILE_MASK) {
-        if (!pmConfigMaskReadHeader (config, phu)) {
-            psError(PS_ERR_IO, false, "error in mask bits");
-            return NULL;
-        }
-    }
-
-    // set the view to the corresponding entry for this phu
-    pmFPAview *view = pmFPAAddSourceFromHeader (fpa, phu, format);
-    if (!view) {
-        psError(PS_ERR_IO, false, "Unable to determine source for %s", file->name);
-        psFree(phu);
-        psFree(fpa);
-        return NULL;
-    }
-
-    // associate the filename with the FPA element
-    char *name = pmFPAfileNameFromRule (file->filesrc, file, view);
-
-    // save the name association in the pmFPAfile structure
-    psMetadataAddStr (file->names, PS_LIST_TAIL, name, 0, "", infiles->data[entry]);
-
-    psFree(phu);
-    psFree(fpa);
-    psFree(view);
-    psFree(name);
+    if (filenames->n <= entry) {
+        psError(PS_ERR_IO, false, "Insufficient files (%ld) in array in %s in arguments",
+                filenames->n, argname);
+        if (success) {
+            *success = false;
+        }
+        return NULL;
+    }
+
+    psArray *single = psArrayAlloc(1);  // Array of single filename of interest
+    single->data[0] = psMemIncrRefCounter(filenames->data[entry]);
+    pmFPAfile *file = fpaFileDefineFromArray(config, NULL, filename, single); // File of interest
+    psFree(single);
 
     if (success) {
-        *success = true;
-    }
-    return file;
-}
+        *success = file ? true : false;
+    }
+
+    return file;
+}
+
+pmFPAfile *pmFPAfileDefineFromRun(bool *success, pmConfig *config, const char *filename)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
+
+    psArray *filenames = pmConfigRunFileGet(config, filename); // Filenames used, or NULL
+    if (!filenames) {
+        if (success) {
+            *success = true;
+        }
+        return NULL;
+    }
+
+    pmFPAfile *file = fpaFileDefineFromArray(config, NULL, filename, filenames); // File of interest
+    psFree(filenames);
+
+    if (success) {
+        *success = file ? true : false;
+    }
+
+    return file;
+}
+
 
 // define the named pmFPAfile from the camera->config
 // only valid for pmFPAfile->mode = READ
-pmFPAfile *pmFPAfileDefineFromConf (bool *success, const pmConfig *config, const char *filename)
+pmFPAfile *pmFPAfileDefineFromConf(bool *success, const pmConfig *config, const char *filename)
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
@@ -991,4 +824,8 @@
     pmFPA *fpa = NULL;
     pmFPAfile *file = NULL;
+
+    if (type == PM_DETREND_TYPE_NONE) {
+        return NULL;
+    }
 
     if (success) {
@@ -1177,6 +1014,9 @@
     PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
 
-    pmFPAfile *file = pmFPAfileDefineOutputForFormat(config, NULL, filename, src->cameraName,
-                                                     src->formatName);
+    pmFPAfile *file = pmFPAfileDefineOutputForFormat(config, NULL, filename, src->cameraName, src->formatName);
+    if (!file) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "file %s not defined\n", filename);
+        return NULL;
+    }
     file->src = psMemIncrRefCounter(src->fpa); // inherit output elements from this source pmFPA
     file->xBin = xBin;
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileDefine.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileDefine.h	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileDefine.h	(revision 23351)
@@ -53,5 +53,5 @@
 // Note that the returned pmFPAfile is a view only, so it should not be freed by the caller --- the only
 // reference count is held by the config->files metadata.
-pmFPAfile *pmFPAfileDefineFromArgs (bool *found, pmConfig *config, const char *filename, const char *argname);
+pmFPAfile *pmFPAfileDefineFromArgs(bool *found, pmConfig *config, const char *filename, const char *argname);
 
 // look for the given argname on the argument list; bind the associated files to the specified
@@ -59,5 +59,16 @@
 // Note that the returned pmFPAfile is a view only, so it should not be freed by the caller --- the only
 // reference count is held by the config->files metadata.
-pmFPAfile *pmFPAfileBindFromArgs (bool *found, pmFPAfile *input, pmConfig *config, const char *filename, const char *argname);
+pmFPAfile *pmFPAfileBindFromArgs(bool *found, pmFPAfile *input, pmConfig *config, const char *filename, const char *argname);
+
+/// Define a file based on the filenames in the RUN metadata in the configuration
+///
+/// Note that the returned pmFPAfile is a view only, so it should not be freed by the caller --- the only
+/// reference count is held by the config->files metadata.
+pmFPAfile *pmFPAfileDefineFromRun(
+    bool *found,                        ///< Found files?
+    pmConfig *config,                   ///< Configuration
+    const char *filename                ///< Name of file
+    );
+
 
 // look for the given argname on the argument list.  find the give filename from the file rules
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileIO.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileIO.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmFPAfileIO.c	(revision 23351)
@@ -22,5 +22,4 @@
 #include "pmFPARead.h"
 #include "pmFPAWrite.h"
-#include "pmFPAfileIO.h"
 #include "pmFPAfileFitsIO.h"
 #include "pmSpan.h"
@@ -44,4 +43,7 @@
 #include "pmSubtractionIO.h"
 #include "pmConcepts.h"
+#include "pmConfigRun.h"
+
+#include "pmFPAfileIO.h"
 
 // attempt create, read, write, close, or free pmFPAfiles available in files files are
@@ -164,4 +166,9 @@
     }
 
+    if (!pmConfigRunFileAdd(config, file)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to add file to run-time information");
+        return false;
+    }
+
     // select a reading method
     bool status = true;
@@ -412,4 +419,9 @@
             return false;
         }
+    }
+
+    if (!pmConfigRunFileAdd(config, file)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to add file to run-time information");
+        return false;
     }
 
@@ -728,11 +740,12 @@
     // apply filename mangling rules (file://, path://, neb://)
     bool create = file->mode == PM_FPA_MODE_WRITE ? true : false;
-    psString tmpName = pmConfigConvertFilename (file->filename, config, create, false);
-    if (!tmpName) {
+    psString realName = pmConfigConvertFilename(file->filename, config, create, false);
+    if (!realName) {
         psError(PS_ERR_IO, false, "failed to determine real name from template for %s\n", file->filename);
         return false;
     }
-    psFree (file->filename);
-    file->filename = tmpName;
+    psFree(file->origname);
+    file->origname = file->filename;
+    file->filename = realName;
 
     switch (file->type) {
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmHDUGenerate.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmHDUGenerate.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/camera/pmHDUGenerate.c	(revision 23351)
@@ -474,4 +474,9 @@
                 }
 
+                if (!biassecs || !readout->bias) {
+                    // No need to worry about bias section
+                    continue;
+                }
+
                 if (biassecs->n != readout->bias->n) {
                     psWarning("Number of bias sections (%ld) and number of biases (%ld) do not match.\n",
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/concepts/pmConceptsStandard.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/concepts/pmConceptsStandard.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/concepts/pmConceptsStandard.c	(revision 23351)
@@ -888,4 +888,9 @@
       default:
         psError(PS_ERR_UNKNOWN, true, "Unable to parse %s.\n", pattern->name);
+        return NULL;
+    }
+
+    if (!time) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, false, "Unable to parse time for %s", pattern->name);
         return NULL;
     }
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/Makefile.am	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/Makefile.am	(revision 23351)
@@ -1,5 +1,14 @@
 noinst_LTLIBRARIES = libpsmodulesconfig.la
 
-libpsmodulesconfig_la_CPPFLAGS = $(SRCINC) $(PSMODULES_CFLAGS)
+# PSMODULES_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
+# PSMODULES_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
+# PSMODULES_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+
+# Force recompilation of pmVersion.c, since it gets the version information
+# pmVersion.c: FORCE
+# 	touch pmVersion.c
+# FORCE: ;
+
+libpsmodulesconfig_la_CPPFLAGS = $(SRCINC) $(PSMODULES_CFLAGS) -DPSMODULES_VERSION=$(SVN_VERSION) -DPSMODULES_BRANCH=$(SVN_BRANCH) -DPSMODULES_SOURCE=$(SVN_SOURCE)
 libpsmodulesconfig_la_LDFLAGS  = -release $(PACKAGE_VERSION)
 libpsmodulesconfig_la_SOURCES  = \
@@ -10,4 +19,5 @@
 	pmConfigMask.c \
 	pmConfigDump.c \
+	pmConfigRun.c \
 	pmVersion.c \
 	pmErrorCodes.c
@@ -20,4 +30,5 @@
 	pmConfigMask.h \
 	pmConfigDump.h \
+	pmConfigRun.h \
 	pmVersion.h \
 	pmErrorCodes.h
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfig.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfig.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfig.c	(revision 23351)
@@ -13,4 +13,5 @@
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <strings.h>            /* for strn?casecmp */
@@ -24,9 +25,11 @@
 #include <pslib.h>
 
-#include "pmConfig.h"
 #include "pmErrorCodes.h"
 #include "pmFPALevel.h"
 #include "pmConfigRecipes.h"
 #include "pmConfigCamera.h"
+#include "pmConfigRun.h"
+
+#include "pmConfig.h"
 
 #ifdef HAVE_NEBCLIENT
@@ -142,5 +145,5 @@
     config->format = NULL;
     config->formatName = NULL;
-    config->recipes = psMetadataAlloc();
+    config->recipes = NULL;
     config->recipesRead = PM_RECIPE_SOURCE_NONE;
     config->recipesCamera = psMetadataAlloc();
@@ -432,8 +435,7 @@
     // variable will contain the name of the configuration file.
 
-    char *configFile = NULL;
-    //
+    char *configFile = NULL;            // Name of configuration file
+
     // First, try command line
-    //
     psS32 argNum = psArgumentGet(*argc, argv, "-ipprc");
     if (argNum != 0) {
@@ -447,17 +449,14 @@
         }
     }
-    //
+
     // Next, try environment variable
-    //
     if (!configFile) {
         configFile = getenv(IPPRC_ENV);
         if (configFile) {
-            configFile = psStringCopy (configFile);
-        }
-    }
-
-    //
+            configFile = psStringCopy(configFile);
+        }
+    }
+
     // Last chance is ~/.ipprc
-    //
     if (!configFile) {
         char *home = getenv("HOME");
@@ -466,8 +465,6 @@
     }
 
-    // We have the configuration filename; now we read and parse the config
-    // file and store in psMetadata struct user.
+    // Read and parse the config file and store in struct user.
     // XXX move this section to pmConfigReadUser.c ?
-
     if (!pmConfigFileRead(&config->user, configFile, "user")) {
         psFree(config);
@@ -477,11 +474,10 @@
     psFree(configFile);
 
-    // XXX why was this being called here?  Is someone calling pmConfigRead multiple times?
-    // pmConfigDone();
-    assert (configPath == NULL);
+    pmConfigRunCommand(config, *argc, argv);
 
     // define the config-file search path (configPath).
+    psAssert(configPath == NULL, "Configuration path is already defined.");
     psString path = psMetadataLookupStr(NULL, config->user, "PATH");
-    pmConfigSet (path);
+    pmConfigSet(path);
 
     // read the SITE file
@@ -525,5 +521,4 @@
             psLogSetLevel(logLevel);
         }
-
 
         // Set logging format
@@ -648,4 +643,26 @@
     }
 
+    // Set the random number generator seed
+    {
+        psU64 seed = 0;                 // RNG seed
+        int argNum = psArgumentGet(*argc, argv, "-seed"); // Argument number
+        if (argNum > 0) {
+            psArgumentRemove(argNum, argc, argv);
+            if (argNum >= *argc) {
+                psWarning("-seed command-line switch provided without the required seed value --- ignored.");
+            } else {
+                char *end = NULL;       // Pointer to end of consumed string
+                seed = strtoll(argv[argNum], &end, 0);
+                if (strlen(end) > 0) {
+                    psError(PS_ERR_IO, true, "Unable to read random number generator seed: %s", argv[argNum]);
+                    psFree(config);
+                    return NULL;
+                }
+                psArgumentRemove(argNum, argc, argv);
+            }
+        }
+        pmConfigRunSeed(config, seed);
+    }
+
     // Next, we do a similar thing for the camera configuration file.  The
     // file is read and parsed into psMetadata struct "camera".
@@ -1110,6 +1127,6 @@
 // camera.  If we are discovering the camera (config->camera == NULL), then we also load the
 // recipe files for the camera.
-psMetadata *pmConfigCameraFormatFromHeader(psMetadata **camera, psString *cameraName, psString *formatName, 
-					   pmConfig *config, const psMetadata *header, bool readRecipes)
+psMetadata *pmConfigCameraFormatFromHeader(psMetadata **camera, psString *cameraName, psString *formatName,
+                                           pmConfig *config, const psMetadata *header, bool readRecipes)
 {
     PS_ASSERT_PTR_NON_NULL(config, NULL);
@@ -1672,5 +1689,5 @@
                 // if the object does not exist and create isn't set, then we
                 // should puke
-                psError(PM_ERR_SYS, true, "Unable to access file %s", filename);
+                psError(PM_ERR_SYS, true, "Unable to access file %s: %s", filename, nebErr(server));
                 nebServerFree(server);
                 return NULL;
@@ -1685,5 +1702,6 @@
         if (trunc) {
             if(truncate(path, 0) != 0) {
-                psError(PS_ERR_IO, true, "Failed to truncate Nebulous file %s (real name %s)\n", filename, path);
+                psError(PS_ERR_IO, true, "Failed to truncate Nebulous file %s (real name %s)\n",
+                        filename, path);
                 return NULL;
             }
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigCamera.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigCamera.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigCamera.c	(revision 23351)
@@ -213,4 +213,5 @@
     psStringAppend(&newName, "_%s-SKYCELL", name);
     if (psMetadataLookup(oldCameras, newName)) {
+        psFree(newName);
         return true;
     }
@@ -469,4 +470,5 @@
     psStringAppend(&newName, "_%s-%s", name, mosaicLevel == PM_FPA_LEVEL_CHIP ? "CHIP" : "FPA");
     if (psMetadataLookup(oldCameras, newName)) {
+        psFree(newName);
         return true;
     }
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigDump.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigDump.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigDump.c	(revision 23351)
@@ -54,8 +54,40 @@
     psArray *keep = psStringSplitArray(save, " ,;", false); // List of items to keep
 
-    bool result = configCull(config->recipes, keep); // Result of culling
+    if (!configCull(config->recipes, keep)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to cull system recipes.");
+        psFree(keep);
+        return false;
+    }
     psFree(keep);
 
-    return result;
+    // Need to cull recipes from all cameras as well
+    psMetadata *cameras = psMetadataLookupMetadata(NULL, config->system, "CAMERAS"); // Known cameras
+    if (!cameras) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find CAMERAS in the system configuration.\n");
+        return false;
+    }
+
+    psMetadataIterator *iter = psMetadataIteratorAlloc(cameras, PS_LIST_HEAD, NULL); // Iterator for cameras
+    psMetadataItem *item;               // Item from iteration
+    keep = psArrayAllocEmpty(1);
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        psAssert(item->type == PS_DATA_METADATA, "Should only be metadata types on the camera list.");
+        psMetadata *camera = item->data.md; // Camera configuration
+        bool mdok;                      // Status of MD lookup
+        psMetadata *recipes = psMetadataLookupMetadata(&mdok, camera, "RECIPES"); // Recipe overrides
+        if (!recipes) {
+            continue;
+        }
+        if (!configCull(recipes, keep)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to cull recipes for camera %s", item->name);
+            psFree(iter);
+            psFree(keep);
+            return false;
+        }
+    }
+    psFree(iter);
+    psFree(keep);
+
+    return true;
 }
 
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigRecipes.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigRecipes.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigRecipes.c	(revision 23351)
@@ -11,5 +11,5 @@
 #include "pmConfigRecipes.h"
 
-static bool loadRecipeSystem(bool *status, pmConfig *config, psMetadata *source);
+static bool loadRecipeSystem(bool *status, pmConfig *config);
 static bool loadRecipeCamera(bool *status, pmConfig *config, psMetadata *source);
 static bool loadRecipeSymbols(bool *status, pmConfig *config, pmRecipeSource source);
@@ -56,14 +56,10 @@
     PS_ASSERT_PTR_NON_NULL(config, false);
 
-    if (!config->recipes) {
-        config->recipes = psMetadataAlloc();
-    }
-
     // Read the recipe file names from the system configuration and camera configuration
     // It is an error for config->system:recipes not to exist.  all programs install their
     // master recipe files in the system:recipe location when they are built.
     psAssert(config->system, "base config data defined");
-    if (source & PM_RECIPE_SOURCE_SYSTEM) {
-        if (!loadRecipeSystem(&status, config, config->system)) {
+    if (!config->recipes || (source & PM_RECIPE_SOURCE_SYSTEM)) {
+        if (!loadRecipeSystem(&status, config)) {
             psError(PS_ERR_IO, false, "Failed to read recipes from system config");
             return false;
@@ -321,6 +317,5 @@
 // Load the recipe files for SYSTEM : REQUIRED
 static bool loadRecipeSystem(bool *status,
-                           pmConfig *config, // The configuration into which to read the recipes
-                           psMetadata *source // The source configuration, from which to read the filenames
+                           pmConfig *config // The configuration into which to read the recipes
     )
 {
@@ -329,5 +324,5 @@
     *status = false;
 
-    if (!source) {
+    if (!config->system) {
         psError(PS_ERR_IO, true,
                 "The system configuration has not been read --- cannot read recipes from this location.\n");
@@ -336,14 +331,14 @@
     }
 
-    psMetadata *recipes = psMetadataLookupMetadata(NULL, source, "RECIPES"); // The list of recipes
-    if (!recipes) {
-        psError(PS_ERR_IO, false, "RECIPES not found in the system configuration\n");
-        return false;
-    }
-
-    // Copy contents of the filenames to config->recipes from the "RECIPES" metadata in the source.
-    // We could use psMetadataCopy for this, but it's better to check that everything's of the correct type.
-    // If it's not of the correct type, we can tell the user which file it's in, so they can find it easier.
-    psMetadataIterator *recipesIter = psMetadataIteratorAlloc(recipes, PS_LIST_HEAD, NULL); // Iterator
+    if (!config->recipes) {
+        config->recipes = psMemIncrRefCounter(psMetadataLookupMetadata(NULL, config->system, "RECIPES")); // The list of recipes
+        if (!config->recipes) {
+            psError(PS_ERR_IO, false, "RECIPES not found in the system configuration\n");
+            return false;
+        }
+    }
+
+    // Read in the component recipes
+    psMetadataIterator *recipesIter = psMetadataIteratorAlloc(config->recipes, PS_LIST_HEAD, NULL); // Iterator
     psMetadataItem *item = NULL;        // MD item containing the filename, from recipe iteration
     while ((item = psMetadataGetAndIncrement(recipesIter))) {
@@ -353,9 +348,5 @@
             psFree(recipesIter);
             return false;
-        }
-
-        // add the contents of this recipe file to config->recipes
-        psMetadataAdd(config->recipes, PS_LIST_TAIL, item->name, PS_DATA_METADATA | PS_META_REPLACE,
-                      item->comment, item->data.md);
+	}
     }
     psFree(recipesIter);
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigRecipes.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigRecipes.h	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigRecipes.h	(revision 23351)
@@ -1,9 +1,9 @@
 /*  @file pmConfigRecipes.h
  *  @brief Configuration Recipe functions
- * 
+ *
  *  @author ?, MHPCC
  *  @author Paul Price, IfA
  *  @author Eugene Magnier, IfA
- * 
+ *
  *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
  *  @date $Date: 2007-04-19 02:10:12 $
@@ -13,4 +13,6 @@
 #ifndef PM_CONFIG_RECIPES_H
 #define PM_CONFIG_RECIPES_H
+
+#include <pmConfig.h>
 
 /// @addtogroup Config Configuration System
@@ -26,7 +28,7 @@
 
 
-bool pmConfigLoadRecipeArguments (int *argc, char **argv, pmConfig *config);
-bool pmConfigLoadRecipeOptions (int *argc, char **argv, pmConfig *config, char *flag);
-psMetadata *pmConfigRecipeOptions (pmConfig *config, char *recipe);
+bool pmConfigLoadRecipeArguments(int *argc, char **argv, pmConfig *config);
+bool pmConfigLoadRecipeOptions(int *argc, char **argv, pmConfig *config, char *flag);
+psMetadata *pmConfigRecipeOptions(pmConfig *config, char *recipe);
 
 /// @}
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigRun.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigRun.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigRun.c	(revision 23351)
@@ -0,0 +1,148 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+
+#include "pmConfig.h"
+#include "pmFPAfile.h"
+
+#include "pmConfigRun.h"
+
+// Get the nominated element of the configuration
+static psMetadata *configElement(psMetadata *md, // Metadata with element
+                                 const char *name, // Name of element
+                                 const char *comment // Comment for element
+                             )
+{
+    psAssert(md, "Require metadata");
+    psAssert(name, "Require name of element");
+
+    bool mdok;                          // Status of MD lookup
+    psMetadata *elem = psMetadataLookupMetadata(&mdok, md, name); // Element of interest
+    if (!elem) {
+        elem = psMetadataAlloc();
+        psMetadataAddMetadata(md, PS_LIST_HEAD, name, 0, comment, elem);
+        psFree(elem);                   // Drop reference
+    }
+    return elem;
+}
+
+// Get the RUN information from the configuration
+static psMetadata *configRun(pmConfig *config // Configuration
+                             )
+{
+    psAssert(config, "Require configuration");
+    return configElement(config->user, "RUN", "Run-time information");
+}
+
+
+bool pmConfigRunFileAdd(pmConfig *config, const pmFPAfile *file)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    psMetadata *run = configRun(config);// RUN information
+    psAssert(run, "Require run-time information");
+    psMetadata *files = configElement(run, "FILES", "Filerules used during execution");
+    psAssert(files, "Require list of files");
+
+    const char *name = file->name;      // Name of symbolic file
+    const char *value = file->origname ? file->origname : file->filename; // The file (system) name
+
+    psString regex = NULL;              // Regular expression for iteration
+    psStringAppend(&regex, "^%s$", name);
+    psMetadataIterator *iter = psMetadataIteratorAlloc(files, PS_LIST_HEAD, regex);
+    psFree(regex);
+    psMetadataItem *item;               // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        psAssert(item->type == PS_DATA_STRING, "We only put STRING types here.");
+        if (strcmp(item->data.str, value) == 0) {
+            // It's already present
+            psFree(iter);
+            return true;
+        }
+    }
+    psFree(iter);
+
+    return psMetadataAddStr(files, PS_LIST_TAIL, name, PS_META_DUPLICATE_OK, NULL, value);
+}
+
+psArray *pmConfigRunFileGet(pmConfig *config, const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    PS_ASSERT_STRING_NON_EMPTY(name, false);
+
+    psMetadata *run = configRun(config);// RUN information
+    psAssert(run, "Require run-time information");
+    psMetadata *files = configElement(run, "FILES", "Filerules used during execution");
+    psAssert(files, "Require list of files");
+
+    psList *list = psListAlloc(NULL);   // List of file names
+
+    psString regex = NULL;              // Regular expression for iteration
+    psStringAppend(&regex, "^%s$", name);
+    psMetadataIterator *iter = psMetadataIteratorAlloc(files, PS_LIST_HEAD, regex);
+    psFree(regex);
+    psMetadataItem *item;               // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        psAssert(item->type == PS_DATA_STRING, "We only put STRING types here.");
+        psListAdd(list, PS_LIST_TAIL, item->data.str);
+    }
+    psFree(iter);
+
+    if (psListLength(list) == 0) {
+        // Didn't find anything
+        psFree(list);
+        return NULL;
+    }
+
+    psArray *array = psListToArray(list); // Array of file names, to return
+    psFree(list);
+
+    return array;
+}
+
+
+bool pmConfigRunCommand(pmConfig *config, int argc, char **argv)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *run = configRun(config);// Run-time information
+    psAssert(run, "Require run-time information");
+
+    psString command = NULL;
+    for (int i = 0; i < argc; i++) {
+        psStringAppend(&command, "%s ", argv[i]);
+    }
+
+    psMetadataAddStr(run, PS_LIST_TAIL, "COMMAND", PS_META_REPLACE, "Command line", command);
+    psFree(command);
+
+    return true;
+}
+
+
+bool pmConfigRunSeed(pmConfig *config, psU64 seed)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *run = configRun(config);// Run-time information
+    psAssert(run, "Require run-time information");
+
+    if (!seed) {
+        bool mdok;                          // Status of MD lookup
+        seed = psMetadataLookupU64(&mdok, run, "SEED");
+    }
+    if (!seed) {
+        bool mdok;                          // Status of MD lookup
+        seed = psMetadataLookupU64(&mdok, config->user, "SEED");
+    }
+
+    // seed may still be zero by this point
+    seed = psRandomSeed(seed);
+    return psMetadataAddU64(run, PS_LIST_TAIL, "SEED", PS_META_REPLACE, "Random number generator seed", seed);
+}
+
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigRun.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigRun.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmConfigRun.h	(revision 23351)
@@ -0,0 +1,33 @@
+#ifndef PM_CONFIG_RUN_H
+#define PM_CONFIG_RUN_H
+
+#include <pslib.h>
+#include <pmConfig.h>
+#include <pmFPAfile.h>
+
+/// Add a file to the list of files used in the run-time information
+bool pmConfigRunFileAdd(
+    pmConfig *config,                   ///< Configuration
+    const pmFPAfile *file               ///< File to add
+    );
+
+/// Retrieve file names for a symbolic file from the run-time information
+psArray *pmConfigRunFileGet(
+    pmConfig *config,                   ///< Configuration
+    const char *name                    ///< Name of symbolic file (pmFPAfile)
+    );
+
+/// Add the command line to the run-time information
+bool pmConfigRunCommand(
+    pmConfig *config,                   ///< Configuration
+    int argc, char **argv               ///< Command line arguments
+    );
+
+/// Record the random number generator seed in the run-time information
+bool pmConfigRunSeed(
+    pmConfig *config,                   ///< Configuration
+    psU64 seed                          ///< RNG seed
+    );
+
+#endif
+
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmVersion.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmVersion.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmVersion.c	(revision 23351)
@@ -8,21 +8,71 @@
 #include "pmVersion.h"
 
-static const char *cvsTag = "$Name: not supported by cvs2svn $";// CVS tag name
+#ifndef PSMODULES_VERSION
+#error "PSMODULES_VERSION is not set"
+#endif
+#ifndef PSMODULES_BRANCH
+#error "PSMODULES_BRANCH is not set"
+#endif
+#ifndef PSMODULES_SOURCE
+#error "PSMODULES_SOURCE is not set"
+#endif
+
+#define xstr(s) str(s)
+#define str(s) #s
 
 psString psModulesVersion(void)
 {
-    psString version = NULL;            // Version, to return
-    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
-    return version;
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", xstr(PSMODULES_BRANCH), xstr(PSMODULES_VERSION));
+    return value;
 }
+
+psString psModulesSource(void)
+{
+    return psStringCopy(xstr(PSMODULES_SOURCE));
+}
+
 
 psString psModulesVersionLong(void)
 {
-    psString version = psModulesVersion(); // Version, to return
-    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
+    psString version = psModulesVersion();  // Version, to return
+    psString source = psModulesSource();    // Source
 
-    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
+    psStringPrepend(&version, "psModules ");
+    psStringAppend(&version, " from %s, built %s, %s", source, __DATE__, __TIME__);
+    psFree(source);
 
-    psFree(tag);
+#ifdef __OPTIMIZE__
+    psStringAppend(&version, " optimised");
+#else
+    psStringAppend(&version, " unoptimised");
+#endif
+
+#ifdef HAVE_NEBCLIENT
+    psStringAppend(&version, " with nebclient");
+#else
+    psStringAppend(&version, " without nebclient");
+#endif
+
     return version;
+};
+
+
+bool psModulesVersionHeader(psMetadata *header)
+{
+    PS_ASSERT_METADATA_NON_NULL(header, false);
+
+    psString version = psModulesVersion(); // Software version
+    psString source = psModulesSource();   // Software source
+
+    psStringPrepend(&version, "psModules version: ");
+    psStringPrepend(&source, "psModules source: ");
+
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, version);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, source);
+
+    psFree(version);
+    psFree(source);
+
+    return true;
 }
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmVersion.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmVersion.h	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/config/pmVersion.h	(revision 23351)
@@ -1,8 +1,8 @@
 /*  @file pmVersion.h
  *  @brief Version functions
- * 
+ *
  *  @author Paul Price, IfA
  *  @author Eugene Magnier, IfA
- * 
+ *
  *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
  *  @date $Date: 2007-03-30 21:12:56 $
@@ -22,7 +22,13 @@
  *  @return psString: String with version name.
  */
-psString psModulesVersion(
-    void
-);
+psString psModulesVersion(void);
+
+/** Get current psModules source
+ *
+ *  Returns the current psModules source as a string.
+ *
+ *  @return psString: String with source.
+ */
+psString psModulesSource(void);
 
 /** Get current psModules version (full identification)
@@ -34,4 +40,10 @@
 psString psModulesVersionLong(void);
 
+/// Populate a header with version information
+bool psModulesVersionHeader(
+    psMetadata *header                  ///< Header to populate
+    );
+
+
 /// @}
 #endif
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmDetrendDB.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmDetrendDB.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmDetrendDB.c	(revision 23351)
@@ -20,14 +20,15 @@
 
 //
-static void pmDetrendSelectOptionsFree (pmDetrendSelectOptions *options)
-{
-
-    if (!options)
+static void pmDetrendSelectOptionsFree(pmDetrendSelectOptions *options)
+{
+
+    if (!options) {
         return;
-
-    psFree (options->camera);
-    psFree (options->filter);
-    psFree (options->dettype);
-    psFree (options->version);
+    }
+
+    psFree(options->camera);
+    psFree(options->filter);
+    psFree(options->dettype);
+    psFree(options->version);
 
     return;
@@ -37,5 +38,4 @@
 pmDetrendSelectOptions *pmDetrendSelectOptionsAlloc(const char *camera, psTime time, pmDetrendType type)
 {
-
     pmDetrendSelectOptions *options = psAlloc(sizeof(pmDetrendSelectOptions));
     psMemSetDeallocator(options, (psFreeFunc) pmDetrendSelectOptionsFree);
@@ -63,11 +63,12 @@
 }
 
-static void pmDetrendSelectResultsFree (pmDetrendSelectResults *results)
-{
-
-    if (!results)
+static void pmDetrendSelectResultsFree(pmDetrendSelectResults *results)
+{
+
+    if (!results) {
         return;
-
-    psFree (results->detID);
+    }
+
+    psFree(results->detID);
     psFree(results->level);
 
@@ -75,5 +76,5 @@
 }
 
-pmDetrendSelectResults *pmDetrendSelectResultsAlloc ()
+pmDetrendSelectResults *pmDetrendSelectResultsAlloc(void)
 {
 
@@ -87,20 +88,21 @@
 }
 
-psString pmDetrendTypeToString (pmDetrendType type)
-{
-
-    # define DETREND_STRING_CASE(TTT) \
-case PM_DETREND_TYPE_##TTT: \
-    return psStringCopy (#TTT);
+psString pmDetrendTypeToString(pmDetrendType type)
+{
+
+#define DETREND_STRING_CASE(TYPE) \
+  case PM_DETREND_TYPE_##TYPE: \
+    return psStringCopy(#TYPE);
 
     switch (type) {
-        DETREND_STRING_CASE (MASK);
-        DETREND_STRING_CASE (BIAS);
-        DETREND_STRING_CASE (DARK);
-        DETREND_STRING_CASE (FLAT);
-        DETREND_STRING_CASE (FLATCORR);
-        DETREND_STRING_CASE (SHUTTER);
-        DETREND_STRING_CASE (FRINGE);
-        DETREND_STRING_CASE (ASTROM);
+        DETREND_STRING_CASE(NONE);
+        DETREND_STRING_CASE(MASK);
+        DETREND_STRING_CASE(BIAS);
+        DETREND_STRING_CASE(DARK);
+        DETREND_STRING_CASE(FLAT);
+        DETREND_STRING_CASE(FLATCORR);
+        DETREND_STRING_CASE(SHUTTER);
+        DETREND_STRING_CASE(FRINGE);
+        DETREND_STRING_CASE(ASTROM);
     default:
         return NULL;
@@ -111,6 +113,5 @@
 // detselect -camera (camera) -time (time) -type (type) [others]
 // returns: (type) (class) (exp_flag) DONE
-pmDetrendSelectResults *pmDetrendSelect (const pmDetrendSelectOptions *options,
-        const pmConfig *config)
+pmDetrendSelectResults *pmDetrendSelect(const pmDetrendSelectOptions *options, const pmConfig *config)
 {
     PS_ASSERT_PTR_NON_NULL(options, NULL);
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmDetrendDB.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmDetrendDB.h	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmDetrendDB.h	(revision 23351)
@@ -25,4 +25,5 @@
 
 typedef enum {
+    PM_DETREND_TYPE_NONE,
     PM_DETREND_TYPE_MASK,
     PM_DETREND_TYPE_BIAS,
@@ -59,5 +60,5 @@
 } pmDetrendSelectResults;
 
-psString pmDetrendTypeToString (pmDetrendType type);
+psString pmDetrendTypeToString(pmDetrendType type);
 
 pmDetrendSelectOptions *pmDetrendSelectOptionsAlloc(const char *camera, psTime time, pmDetrendType type);
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmFringeStats.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmFringeStats.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmFringeStats.c	(revision 23351)
@@ -65,5 +65,5 @@
         rng = psMemIncrRefCounter(random);
     } else {
-        rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        rng = psRandomAlloc(PS_RANDOM_TAUS);
     }
 
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmRemnance.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmRemnance.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmRemnance.c	(revision 23351)
@@ -28,5 +28,5 @@
     int numCols = image->numCols, numRows = image->numRows; // Size of image
 
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0); // Random number generator
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
 
     psStats *stats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV); // Statistics
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmShutterCorrection.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmShutterCorrection.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/detrend/pmShutterCorrection.c	(revision 23351)
@@ -911,5 +911,5 @@
     psStats *stats = psStatsAlloc(meanStat | stdevStat); // Statistics to apply
     if (!rng) {
-        rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        rng = psRandomAlloc(PS_RANDOM_TAUS);
     } else {
         psMemIncrRefCounter(rng);
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/extras/pmVisual.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/extras/pmVisual.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/extras/pmVisual.c	(revision 23351)
@@ -18,5 +18,8 @@
 #include "pmHDU.h"
 #include "pmFPA.h"
+#include "pmFPAfile.h"
 #include "pmAstrometryObjects.h"
+#include "pmSubtractionStamps.h"
+#include "pmTrend2D.h"
 #include "pmFPAExtent.h"
 
@@ -28,4 +31,31 @@
 # define KAPAY 700
 
+#include "pmAstrometryVisual.h"
+#include "pmSubtractionVisual.h"
+#include "pmStackVisual.h"
+#include "pmSourceVisual.h"
+
+//#define TESTING
+
+static bool isVisual = false;
+
+
+bool pmVisualSetVisual(bool value) {
+    isVisual = value;
+    return true;
+}
+
+
+bool pmVisualIsVisual(void) {return isVisual;}
+
+bool pmVisualClose(void) {
+    pmAstromVisualClose();
+    pmSubtractionVisualClose();
+    pmStackVisualClose();
+    pmSourceVisualClose();
+    //XXX handle psphot
+    //    psphotVisualClose();
+    return true;
+}
 
 bool pmVisualInitWindow (int *kapid, char *name) {
@@ -34,4 +64,5 @@
         if (*kapid == -1) {
             fprintf (stderr, "Failure to open kapa.\n");
+            isVisual = false;
             return false;
         }
@@ -52,5 +83,5 @@
 
 
-bool pmVisualAskUser(bool *plotFlag, bool *packageFlag)
+bool pmVisualAskUser(bool *plotFlag)
 {
     char key[10];
@@ -63,5 +94,5 @@
     }
     if (key[0] == 'a') {
-        *packageFlag = false;
+        isVisual = false;
     }
     return true;
@@ -182,10 +213,14 @@
 
 bool pmVisualScaleImage(int kapaFD, psImage *inImage, const char *name, int channel, bool clip) {
-
     KiiImage image;
     KapaImageData data;
     Coords coords;
 
-    //make sure we have a compatible image type
+    //make sure we have a compatible image
+    if (inImage == NULL) {
+        fprintf(stderr, "Image is NULL, and cannot be displayed\n");
+        return false;
+    }
+
     if(inImage->type.type != PS_TYPE_F32) {
         fprintf(stderr, "Cannot display this image (imcompatible data type)\n");
@@ -194,4 +229,5 @@
 
     strcpy (coords.ctype, "RA---TAN");
+
 
     double min, max, stdev, mean;
@@ -304,9 +340,11 @@
 
 #else
-
+bool pmVisualSetVisual(bool value) {return true;}
+bool pmVisualIsVisual(void) {return false;}
+bool pmVisualClose(void) {return true;}
 bool pmVisualInitWindow(int *kapid, char *name){return true;}
 bool pmVisualInitGraph (int kapa, void *section, void *graphdata){return true;}
 
-bool pmVisualAskUser(bool *plotFlag, bool *packageFlag){return true;}
+bool pmVisualAskUser(bool *plotFlag){return true;}
 bool pmVisualImStats(psImage *image, double *mean,
                      double *stdev, double *min, double *max){return true;}
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/extras/pmVisual.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/extras/pmVisual.h	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/extras/pmVisual.h	(revision 23351)
@@ -10,4 +10,22 @@
 
 #if (HAVE_KAPA)
+
+
+/** Globally enable or disable plotting
+ * @param value - true to enable plotting
+ * @return true
+ */
+bool pmVisualSetVisual(bool value);
+
+
+/** Check whether plotting is enabled
+ * @retrun true if plots should be generated
+ */
+bool pmVisualIsVisual(void);
+
+
+/** Destroy plotting windows at the end of a run
+ * @return true for success */
+bool pmVisualClose(void);
 
 
@@ -32,8 +50,6 @@
  * At the user's request, this will disable diagnostic plotting.
  * @param plotFlag, set to false if this plot should be disabled in the future
- * @param packageFlag, set to false if all plots from this package (e.g. psastro, pmSubtraction)
- *                      should be disabled.
  */
-bool pmVisualAskUser(bool *plotFlag, bool *packageFlag);
+bool pmVisualAskUser(bool *plotFlag);
 
 
@@ -115,7 +131,10 @@
 
 // kapa-specific data types are changed to void
+bool pmVisualSetVisual(bool value);
+bool pmVisualIsVisual(void);
+bool pmVisualClose(void);
 bool pmVisualInitWindow (int *kapid, char *name);
 bool pmVisualInitGraph (int kapa, void *section, void *graphdata);
-bool pmVisualAskUser(bool *plotFlag, bool *packageFlag);
+bool pmVisualAskUser(bool *plotFlag);
 bool pmVisualScaleImage(int kapaFD, psImage *inImage,
                         const char *name, int channel, bool clip);
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/Makefile.am	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/Makefile.am	(revision 23351)
@@ -19,5 +19,6 @@
 	pmSubtractionThreads.c	\
 	pmPSFEnvelope.c         \
-	pmSubtractionVisual.c
+	pmSubtractionVisual.c   \
+	pmStackVisual.c
 
 pkginclude_HEADERS = \
@@ -37,5 +38,6 @@
 	pmSubtractionThreads.h	\
 	pmPSFEnvelope.h         \
-	pmSubtractionVisual.h
+	pmSubtractionVisual.h   \
+	pmStackVisual.h
 
 CLEANFILES = *~
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmPSFEnvelope.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmPSFEnvelope.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmPSFEnvelope.c	(revision 23351)
@@ -30,5 +30,5 @@
 
 #include "pmPSFEnvelope.h"
-
+#include "pmStackVisual.h"
 
 
@@ -115,4 +115,7 @@
     for (int i = 0; i < inputs->n; i++) {
         pmPSF *psf = inputs->data[i];   // PSF of interest
+        if (!psf) {
+            continue;
+        }
         pmResiduals *resid = psf->residuals;// PSF residuals
         psf->residuals = NULL;
@@ -162,4 +165,5 @@
             psStringAppend(&name, "psf_field_%03d.fits", i);
             psFits *fits = psFitsOpen(name, "w");
+            pmStackVisualPlotTestImage(fakeRO->image, name);
             psFitsWriteImage(fits, NULL, fakeRO->image, 0, NULL);
             psFitsClose(fits);
@@ -179,4 +183,5 @@
         // Write out the envelope
         psFits *fits = psFitsOpen("psf_field_envelope.fits", "w");
+        pmStackVisualPlotTestImage(envelope, "psf_field_envelope.fits");
         psFitsWriteImage(fits, NULL, envelope, 0, NULL);
         psFitsClose(fits);
@@ -231,4 +236,5 @@
         // Write out the envelope
         psFits *fits = psFitsOpen("psf_field_full.fits", "w");
+        pmStackVisualPlotTestImage(readout->image, "psf_field_full.fits");
         psFitsWriteImage(fits, NULL, readout->image, 0, NULL);
         psFitsClose(fits);
@@ -312,4 +318,5 @@
         {
             psFits *fits = psFitsOpen("psf_field_model.fits", "w");
+            pmStackVisualPlotTestImage(generated->image, "psf_field_model.fits");
             psFitsWriteImage(fits, NULL, generated->image, 0, NULL);
             psFitsClose(fits);
@@ -318,4 +325,5 @@
         {
             psFits *fits = psFitsOpen("psf_field_resid.fits", "w");
+            pmStackVisualPlotTestImage(generated->image, "psf_field_resid.fits");
             psFitsWriteImage(fits, NULL, generated->image, 0, NULL);
             psFitsClose(fits);
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmStack.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmStack.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmStack.c	(revision 23351)
@@ -8,6 +8,6 @@
  *  @author GLG, MHPCC
  *
- *  @version $Revision: 1.48 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2009-02-13 23:52:14 $
+ *  @version $Revision: 1.48.2.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2009-02-19 17:59:50 $
  *  Copyright 2004-2007 Institute for Astronomy, University of Hawaii
  *
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmStackVisual.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmStackVisual.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmStackVisual.c	(revision 23351)
@@ -0,0 +1,60 @@
+/** Diagnostic plots for pmStack
+ * @author Chris Beaumont, IfA
+ */
+
+/* Include Files   */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <strings.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+#include <pslib.h>
+
+#include "pmKapaPlots.h"
+#include "pmVisual.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmAstrometryObjects.h"
+
+# if (HAVE_KAPA)
+# include <kapa.h>
+
+//variables to determine when things are plotted
+static bool plotTestImage        = true;
+
+// variables to store plotting window indices
+static int kapa  = -1;
+static int kapa2 = -1;
+
+/** destroy windows at the end of a run*/
+bool pmStackVisualClose()
+{
+    if(kapa != -1)
+        KiiClose(kapa);
+    if(kapa2 != -1)
+        KiiClose(kapa2);
+    return true;
+}
+
+/** Display a test image
+ * @param image to plot
+ * @param name for plot. No spaces allowed.
+ */
+bool pmStackVisualPlotTestImage(psImage *image, char *name) {
+    if (!pmVisualIsVisual() || !plotTestImage) return true;
+    if (!pmVisualInitWindow(&kapa, "pmStack:Images")) return false;
+
+    if(!pmVisualScaleImage(kapa, image, (const char*)name, 0, true)) return false;
+    pmVisualAskUser(&plotTestImage);
+    return true;
+}
+
+#else
+bool pmStackVisualClose() {return true;}
+bool pmStackVisualPlotTestImage(psImage *image, char *name) {return true;}
+#endif
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmStackVisual.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmStackVisual.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmStackVisual.h	(revision 23351)
@@ -0,0 +1,7 @@
+#ifndef PM_STACK_VISUAL_H
+#define PM_STACK_VISUAL_H
+
+bool pmStackVisualClose(void);
+bool pmStackVisualPlotTestImage(psImage *image, char *name);
+
+#endif
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionIO.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionIO.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionIO.c	(revision 23351)
@@ -16,4 +16,5 @@
 
 #include "pmSubtractionIO.h"
+#include "pmStackVisual.h"
 
 #define ARRAY_BUFFER 16                 // Number to add to array at a time
@@ -144,6 +145,6 @@
     // CVS tags, used to identify the version of this file (in case incompatibilities are introduced)
     psString cvsFile = psStringCopy("$RCSfile: pmSubtractionIO.c,v $");
-    psString cvsRev  = psStringCopy("$Revision: 1.9 $");
-    psString cvsDate = psStringCopy("$Date: 2008-10-24 21:21:06 $");
+    psString cvsRev  = psStringCopy("$Revision: 1.9.18.1 $");
+    psString cvsDate = psStringCopy("$Date: 2009-02-19 17:59:50 $");
     psStringSubstitute(&cvsFile, NULL, "RCSfile: ");
     psStringSubstitute(&cvsRev,  NULL, "Revision: ");
@@ -167,4 +168,6 @@
 
     psImage *image = psMetadataLookupPtr(NULL, ro->analysis, PM_SUBTRACTION_ANALYSIS_KERNEL_IMAGE); // Image
+    pmStackVisualPlotTestImage(image, "Subtraction_kernels.fits");
+
     if (image && !psFitsWriteImage(fits, header, image, 0, EXTNAME_IMAGE)) {
         psError(PS_ERR_IO, false, "Unable to write subtraction kernel image.");
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionMatch.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionMatch.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionMatch.c	(revision 23351)
@@ -90,4 +90,202 @@
 }
 
+// Check input arguments
+static bool subtractionMatchCheck(pmReadout *conv1, pmReadout *conv2, // Convolved images
+                                  const pmReadout *ro1, const pmReadout *ro2, // Input images
+                                  int stride, // Size for convolution patches
+                                  float sysError, // Relative systematic error
+                                  psImageMaskType maskVal, // Value to mask for input
+                                  psImageMaskType maskBad, // Mask for output bad pixels
+                                  psImageMaskType maskPoor, // Mask for output poor pixels
+                                  float poorFrac, // Fraction for "poor"
+                                  float badFrac,   // Maximum fraction of bad input pixels to accept
+                                  pmSubtractionMode subMode // Mode of subtraction
+                                  )
+{
+    if (subMode != PM_SUBTRACTION_MODE_2) {
+        PM_ASSERT_READOUT_NON_NULL(conv1, false);
+        if (conv1->image) {
+            psFree(conv1->image);
+            conv1->image = NULL;
+        }
+        if (conv1->mask) {
+            psFree(conv1->mask);
+            conv1->mask = NULL;
+        }
+        if (conv1->variance) {
+            psFree(conv1->variance);
+            conv1->variance = NULL;
+        }
+    }
+    if (subMode != PM_SUBTRACTION_MODE_1) {
+        PM_ASSERT_READOUT_NON_NULL(conv2, false);
+        if (conv2->image) {
+            psFree(conv2->image);
+            conv2->image = NULL;
+        }
+        if (conv2->mask) {
+            psFree(conv2->mask);
+            conv2->mask = NULL;
+        }
+        if (conv2->variance) {
+            psFree(conv2->variance);
+            conv2->variance = NULL;
+        }
+    }
+
+    PM_ASSERT_READOUT_NON_NULL(ro1, false);
+    PM_ASSERT_READOUT_NON_NULL(ro2, false);
+    PM_ASSERT_READOUT_IMAGE(ro1, false);
+    PM_ASSERT_READOUT_IMAGE(ro2, false);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(ro1->image, ro2->image, false);
+    PS_ASSERT_INT_NONNEGATIVE(stride, false);
+    if (isfinite(sysError)) {
+        PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(sysError, 0.0, false);
+        PS_ASSERT_FLOAT_LESS_THAN(sysError, 1.0, false);
+    }
+    // Don't care about maskVal
+    // Don't care about maskBad
+    // Don't care about maskPoor
+    PS_ASSERT_FLOAT_LARGER_THAN(poorFrac, 0.0, false);
+    PS_ASSERT_FLOAT_LESS_THAN_OR_EQUAL(poorFrac, 1.0, false);
+    if (isfinite(badFrac)) {
+        PS_ASSERT_FLOAT_LARGER_THAN(badFrac, 0.0, false);
+        PS_ASSERT_FLOAT_LESS_THAN_OR_EQUAL(badFrac, 1.0, false);
+    }
+
+    return true;
+}
+
+
+
+bool pmSubtractionMatchPrecalc(pmReadout *conv1, pmReadout *conv2, const pmReadout *ro1, const pmReadout *ro2,
+                               psMetadata *analysis, int stride, float sysError,
+                               psImageMaskType maskVal, psImageMaskType maskBad, psImageMaskType maskPoor,
+                               float poorFrac, float badFrac)
+{
+    PS_ASSERT_METADATA_NON_NULL(analysis, false);
+
+    // Extract the kernels
+    pmSubtractionMode mode = PM_SUBTRACTION_MODE_UNSURE; // Subtraction mode: which image to convolve
+    int size = 0;                       // Size of kernel
+    psList *kernelList = psListAlloc(NULL); // List of kernels
+    {
+        psMetadataIterator *iter = psMetadataIteratorAlloc(analysis, PS_LIST_HEAD,
+                                                           "^" PM_SUBTRACTION_ANALYSIS_KERNEL "$");
+        psMetadataItem *item;               // Item from iteration
+        while ((item = psMetadataGetAndIncrement(iter))) {
+            if (item->type != PS_DATA_UNKNOWN) {
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Unexpected type for kernel.");
+                psFree(iter);
+                psFree(kernelList);
+                return false;
+            }
+            pmSubtractionKernels *kernel = item->data.V; // Kernel
+            PM_ASSERT_SUBTRACTION_KERNELS_NON_NULL(kernel, false);
+            size = PS_MAX(size, kernel->size);
+            if (mode == PM_SUBTRACTION_MODE_UNSURE) {
+                mode = kernel->mode;
+            } else if (kernel->mode != mode) {
+                // There's some confusion, so let's set the mode to dual convolution.
+                // This is only used for the subtraction mask, so it's not a big deal.
+                mode = PM_SUBTRACTION_MODE_DUAL;
+            }
+            psListAdd(kernelList, PS_LIST_TAIL, kernel);
+        }
+        psFree(iter);
+    }
+    if (psListLength(kernelList) == 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find kernels");
+        psFree(kernelList);
+        return false;
+    }
+    psArray *kernels = psListToArray(kernelList); // Array of kernels
+    psFree(kernelList);
+
+    // Extract the regions
+    psArray *regions = psArrayAllocEmpty(kernels->n); // Array of regions
+    {
+        psMetadataIterator *iter = psMetadataIteratorAlloc(analysis, PS_LIST_HEAD,
+                                                           "^" PM_SUBTRACTION_ANALYSIS_REGION "$");
+        psMetadataItem *item;               // Item from iteration
+        while ((item = psMetadataGetAndIncrement(iter))) {
+            if (item->type != PS_DATA_REGION) {
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Unexpected type for region.");
+                psFree(iter);
+                psFree(kernels);
+                psFree(regions);
+                return false;
+            }
+            psRegion *region = item->data.V; // Region
+            psArrayAdd(regions, regions->n, region);
+        }
+        psFree(iter);
+    }
+    if (regions->n != kernels->n) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Differing number of kernels (%ld) and regions (%ld)",
+                kernels->n, regions->n);
+        psFree(regions);
+        psFree(kernels);
+        return false;
+    }
+
+    if (!subtractionMatchCheck(conv1, conv2, ro1, ro2, stride, sysError, maskVal, maskBad, maskPoor,
+                               poorFrac, badFrac, mode)) {
+        psFree(kernels);
+        psFree(regions);
+        return false;
+    }
+
+    psImage *subMask = pmSubtractionMask(ro1->mask, ro2 ? ro2->mask : NULL, maskVal, size, 0,
+                                         badFrac, useFFT); // Subtraction mask
+    if (!subMask) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to generate subtraction mask.");
+        psFree(kernels);
+        psFree(regions);
+        return false;
+    }
+
+    psMetadata *outAnalysis = psMetadataAlloc(); // Output analysis values
+
+    psTrace("psModules.imcombine", 2, "Convolving...\n");
+    for (int i = 0; i < kernels->n; i++) {
+        pmSubtractionKernels *kernel = kernels->data[i]; // Kernel of interest
+        psRegion *region = regions->data[i]; // Region of interest
+
+        if (!pmSubtractionAnalysis(outAnalysis, kernel, region, ro1->image->numCols, ro1->image->numRows)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to generate QA data");
+            psFree(outAnalysis);
+            psFree(subMask);
+            psFree(kernels);
+            psFree(regions);
+            return false;
+        }
+
+        if (!pmSubtractionConvolve(conv1, conv2, ro1, ro2, subMask, stride, maskBad, maskPoor, poorFrac,
+                                   sysError, region, kernel, true, useFFT)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to convolve image.");
+            psFree(outAnalysis);
+            psFree(subMask);
+            psFree(kernels);
+            psFree(regions);
+            return false;
+        }
+    }
+
+    psFree(subMask);
+    psFree(kernels);
+    psFree(regions);
+
+    if (conv1) {
+        psMetadataCopy(conv1->analysis, outAnalysis);
+    }
+    if (conv2) {
+        psMetadataCopy(conv2->analysis, outAnalysis);
+    }
+    psFree(outAnalysis);
+
+    return true;
+}
+
 
 bool pmSubtractionMatch(pmReadout *conv1, pmReadout *conv2, const pmReadout *ro1, const pmReadout *ro2,
@@ -101,43 +299,10 @@
                         psImageMaskType maskPoor, float poorFrac, float badFrac, pmSubtractionMode subMode)
 {
-    if (subMode != PM_SUBTRACTION_MODE_2) {
-        PM_ASSERT_READOUT_NON_NULL(conv1, false);
-        if (conv1->image) {
-            psFree(conv1->image);
-            conv1->image = NULL;
-        }
-        if (conv1->mask) {
-            psFree(conv1->mask);
-            conv1->mask = NULL;
-        }
-        if (conv1->variance) {
-            psFree(conv1->variance);
-            conv1->variance = NULL;
-        }
-    }
-    if (subMode != PM_SUBTRACTION_MODE_1) {
-        PM_ASSERT_READOUT_NON_NULL(conv2, false);
-        if (conv2->image) {
-            psFree(conv2->image);
-            conv2->image = NULL;
-        }
-        if (conv2->mask) {
-            psFree(conv2->mask);
-            conv2->mask = NULL;
-        }
-        if (conv2->variance) {
-            psFree(conv2->variance);
-            conv2->variance = NULL;
-        }
-    }
-
-    PM_ASSERT_READOUT_NON_NULL(ro1, false);
-    PM_ASSERT_READOUT_NON_NULL(ro2, false);
-    PM_ASSERT_READOUT_IMAGE(ro1, false);
-    PM_ASSERT_READOUT_IMAGE(ro2, false);
-    PS_ASSERT_IMAGES_SIZE_EQUAL(ro1->image, ro2->image, false);
+    if (!subtractionMatchCheck(conv1, conv2, ro1, ro2, stride, sysError, maskVal, maskBad, maskPoor,
+                               poorFrac, badFrac, subMode)) {
+        return false;
+    }
 
     PS_ASSERT_INT_NONNEGATIVE(footprint, false);
-    PS_ASSERT_INT_NONNEGATIVE(stride, false);
     // regionSize can be just about anything (except maybe negative, but it can be NAN)
     PS_ASSERT_FLOAT_LARGER_THAN(stampSpacing, 0.0, false);
@@ -168,17 +333,4 @@
     PS_ASSERT_INT_POSITIVE(iter, false);
     PS_ASSERT_FLOAT_LARGER_THAN(rej, 0.0, false);
-    if (isfinite(sysError)) {
-        PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(sysError, 0.0, NULL);
-        PS_ASSERT_FLOAT_LESS_THAN(sysError, 1.0, NULL);
-    }
-    // Don't care about maskVal
-    // Don't care about maskBad
-    // Don't care about maskPoor
-    PS_ASSERT_FLOAT_LARGER_THAN(poorFrac, 0.0, NULL);
-    PS_ASSERT_FLOAT_LESS_THAN_OR_EQUAL(poorFrac, 1.0, NULL);
-    if (isfinite(badFrac)) {
-        PS_ASSERT_FLOAT_LARGER_THAN(badFrac, 0.0, NULL);
-        PS_ASSERT_FLOAT_LESS_THAN_OR_EQUAL(badFrac, 1.0, NULL);
-    }
 
     // If the stamp footprint is smaller than the kernel size, then we won't get much signal in the outer
@@ -214,5 +366,5 @@
     int numCols = ro1->image->numCols, numRows = ro1->image->numRows; // Image dimensions
 
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0); // Random number generator
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
 
     memCheck("start");
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionMatch.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionMatch.h	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionMatch.h	(revision 23351)
@@ -48,4 +48,19 @@
     );
 
+/// Match two images using precalculated kernel
+bool pmSubtractionMatchPrecalc(pmReadout *conv1, ///< Output convolved data for image 1
+                               pmReadout *conv2, ///< Output convolved data for image 2
+                               const pmReadout *ro1, ///< Image 1
+                               const pmReadout *ro2, ///< Image 2
+                               psMetadata *analysis, ///< Analysis metadata with pre-calculated kernel, region
+                               int stride, ///< Size for convolution patches
+                               float sysError, ///< Relative systematic error
+                               psImageMaskType maskVal, ///< Value to mask for input
+                               psImageMaskType maskBad, ///< Mask for output bad pixels
+                               psImageMaskType maskPoor, ///< Mask for output poor pixels
+                               float poorFrac, ///< Fraction for "poor"
+                               float badFrac ///< Maximum fraction of bad input pixels to accept
+    );
+
 /// Execute a thread job to measure the PSF width ratios
 bool pmSubtractionOrderThread(psThreadJob *job ///< Job to execute
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionVisual.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionVisual.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionVisual.c	(revision 23351)
@@ -28,5 +28,4 @@
 
 //variables to determine when things are plotted
-static bool isVisual             = false;
 static bool plotConvKernels      = true;
 static bool plotStamps           = true;
@@ -42,9 +41,4 @@
 // Initialization Routines
 
-/** start or stop plotting */
-bool pmSubtractionSetVisual (bool mode) {
-    isVisual = mode;
-    return true;
-}
 
 
@@ -65,11 +59,10 @@
  *    @return true for success */
 bool pmSubtractionVisualPlotConvKernels(psImage *convKernels) {
-    if (!isVisual || !plotConvKernels) return true;
+    if (!pmVisualIsVisual() || !plotConvKernels) return true;
     if (!pmVisualInitWindow(&kapa, "ppSub:Images")) {
-        isVisual = false;
         return false;
     }
     pmVisualScaleImage(kapa, convKernels, "Convolution_Kernels", 0, true);
-    pmVisualAskUser(&plotConvKernels, &isVisual);
+    pmVisualAskUser(&plotConvKernels);
     return true;
 }
@@ -80,11 +73,9 @@
     @return true for success */
 bool pmSubtractionVisualPlotStamps(pmSubtractionStampList *stamps, pmReadout *ro) {
-    if (!isVisual || !plotStamps) return true;
+    if (!pmVisualIsVisual() || !plotStamps) return true;
     if (!pmVisualInitWindow (&kapa, "ppSub:Images")) {
-        isVisual = false;
         return false;
     }
     if (!pmVisualInitWindow (&kapa2, "ppSub:StampMasterImage")) {
-        isVisual = false;
         return false;
     }
@@ -145,5 +136,5 @@
     pmVisualScaleImage(kapa, canvas, "Subtraction_Stamps", 0, true);
 
-    pmVisualAskUser(&plotStamps, &isVisual);
+    pmVisualAskUser(&plotStamps);
     return true;
 }
@@ -152,7 +143,6 @@
 bool pmSubtractionVisualPlotLeastSquares (pmSubtractionStampList *stamps, bool dual) {
 
-    if (!isVisual || !plotLeastSquares) return true;
+    if (!pmVisualIsVisual() || !plotLeastSquares) return true;
     if (!pmVisualInitWindow (&kapa, "PPSub:Images")) {
-        isVisual = false;
         return false;
     }
@@ -209,5 +199,5 @@
     pmVisualScaleImage(kapa, canvas32, "Least_Squares", 0, true);
 
-    pmVisualAskUser(&plotLeastSquares, &isVisual);
+    pmVisualAskUser(&plotLeastSquares);
     psFree(canvas);
     psFree(canvas32);
@@ -216,7 +206,6 @@
 
 bool pmSubtractionVisualShowSubtraction(psImage *image, psImage *ref, psImage *sub) {
-    if (!isVisual || !plotImage) return true;
+    if (!pmVisualIsVisual() || !plotImage) return true;
     if (!pmVisualInitWindow (&kapa, "PPSub:Images")) {
-        isVisual = false;
         return false;
     }
@@ -225,5 +214,5 @@
     pmVisualScaleImage(kapa, ref, "Reference", 1, true);
     pmVisualScaleImage(kapa, sub, "Subtraction", 2, true);
-    pmVisualAskUser(&plotImage, &isVisual);
+    pmVisualAskUser(&plotImage);
     return true;
 }
@@ -267,5 +256,4 @@
 
 #else
-bool pmSubtractionSetVisual (bool mode) {return true;}
 bool pmSubtractionVisualClose() {return true;}
 bool pmSubtractionVisualPlotConvKernels(psImage *convKernels) {return true;}
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionVisual.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionVisual.h	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/imcombine/pmSubtractionVisual.h	(revision 23351)
@@ -2,5 +2,4 @@
 #define PM_SUBTRACTION_VISUAL_H
 
-bool pmSubtractionSetVisual (bool mode);
 bool pmSubtractionVisualClose();
 bool pmSubtractionVisualPlotConvKernels(psImage *convKernels);
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/Makefile.am
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/Makefile.am	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/Makefile.am	(revision 23351)
@@ -19,4 +19,5 @@
 	pmModelUtils.c \
 	pmSource.c \
+	pmSourceMasks.c \
 	pmSourceMoments.c \
 	pmSourceExtendedPars.c \
@@ -69,4 +70,5 @@
 	pmModelUtils.h \
 	pmSource.h \
+	pmSourceMasks.h \
 	pmSourceExtendedPars.h \
 	pmSourceUtils.h \
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmFootprint.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmFootprint.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmFootprint.c	(revision 23351)
@@ -28,10 +28,10 @@
    }
 
-   psTrace("psModules.objects", 5, "---- begin ----\n");
+   psTrace("psModules.objects", 10, "---- begin ----\n");
 
    psFree(tmp->spans);
    psFree(tmp->peaks);
 
-   psTrace("psModules.objects", 5, "---- end ----\n");
+   psTrace("psModules.objects", 10, "---- end ----\n");
 }
 
@@ -42,5 +42,5 @@
 			      const psImage *image) // region footprint lives in
 {
-    psTrace("psModules.objects", 5, "---- begin ----\n");
+    psTrace("psModules.objects", 10, "---- begin ----\n");
 
     static int id = 1;
@@ -69,5 +69,5 @@
     }
 
-    psTrace("psModules.objects", 5, "---- end ----\n");
+    psTrace("psModules.objects", 10, "---- end ----\n");
     return(footprint);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmModel.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmModel.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmModel.c	(revision 23351)
@@ -32,8 +32,8 @@
 static void modelFree(pmModel *tmp)
 {
-    psTrace("psModules.objects", 4, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
     psFree(tmp->params);
     psFree(tmp->dparams);
-    psTrace("psModules.objects", 4, "---- %s() end ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
 }
 
@@ -44,5 +44,5 @@
 pmModel *pmModelAlloc(pmModelType type)
 {
-    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
 
     pmModelClass *class = pmModelClassSelect (type);
@@ -87,5 +87,5 @@
     tmp->modelFitStatus     = class->modelFitStatus;
 
-    psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
     return(tmp);
 }
@@ -130,5 +130,5 @@
 psF32 pmModelEval(pmModel *model, psImage *image, psS32 col, psS32 row)
 {
-    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
     PS_ASSERT_PTR_NON_NULL(image, NAN);
     PS_ASSERT_PTR_NON_NULL(model, NAN);
@@ -144,5 +144,5 @@
     tmpF = model->modelFunc (NULL, model->params, x);
     psFree(x);
-    psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
     return(tmpF);
 }
@@ -150,5 +150,5 @@
 psF32 pmModelEvalWithOffset(pmModel *model, psImage *image, psS32 col, psS32 row, int dx, int dy)
 {
-    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
     PS_ASSERT_PTR_NON_NULL(image, false);
     PS_ASSERT_PTR_NON_NULL(model, false);
@@ -164,5 +164,5 @@
     tmpF = model->modelFunc (NULL, model->params, x);
     psFree(x);
-    psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
     return(tmpF);
 }
@@ -180,5 +180,5 @@
     )
 {
-    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
 
     PS_ASSERT_PTR_NON_NULL(model, false);
@@ -344,5 +344,5 @@
 
     psFree(x);
-    psTrace("psModules.objects", 3, "---- %s(true) end ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s(true) end ----\n", __func__);
     return(true);
 }
@@ -356,7 +356,7 @@
                 psImageMaskType maskVal)
 {
-    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
     psBool rc = AddOrSubModel(image, mask, model, mode, true, maskVal, 0.0, 0.0);
-    psTrace("psModules.objects", 3, "---- %s(%d) end ----\n", __func__, rc);
+    psTrace("psModules.objects", 10, "---- %s(%d) end ----\n", __func__, rc);
     return(rc);
 }
@@ -370,7 +370,7 @@
                 psImageMaskType maskVal)
 {
-    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
     psBool rc = AddOrSubModel(image, mask, model, mode, false, maskVal, 0.0, 0.0);
-    psTrace("psModules.objects", 3, "---- %s(%d) end ----\n", __func__, rc);
+    psTrace("psModules.objects", 10, "---- %s(%d) end ----\n", __func__, rc);
     return(rc);
 }
@@ -386,7 +386,7 @@
                           int dy)
 {
-    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
     psBool rc = AddOrSubModel(image, mask, model, mode, true, maskVal, dx, dy);
-    psTrace("psModules.objects", 3, "---- %s(%d) end ----\n", __func__, rc);
+    psTrace("psModules.objects", 10, "---- %s(%d) end ----\n", __func__, rc);
     return(rc);
 }
@@ -402,7 +402,7 @@
                           int dy)
 {
-    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
     psBool rc = AddOrSubModel(image, mask, model, mode, false, maskVal, dx, dy);
-    psTrace("psModules.objects", 3, "---- %s(%d) end ----\n", __func__, rc);
+    psTrace("psModules.objects", 10, "---- %s(%d) end ----\n", __func__, rc);
     return(rc);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmMoments.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmMoments.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmMoments.c	(revision 23351)
@@ -27,5 +27,5 @@
 pmMoments *pmMomentsAlloc()
 {
-    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
     pmMoments *tmp = (pmMoments *) psAlloc(sizeof(pmMoments));
     tmp->Mx = 0.0;
@@ -52,5 +52,5 @@
     tmp->nPixels = 0;
 
-    psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
     return(tmp);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmPeaks.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmPeaks.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmPeaks.c	(revision 23351)
@@ -37,5 +37,5 @@
                         pmPeakType type)
 {
-    psTrace("psModules.objects", 5, "---- begin ----\n");
+    psTrace("psModules.objects", 10, "---- begin ----\n");
 
     if (peaks == NULL) {
@@ -82,5 +82,5 @@
     psFree (peak);
 
-    psTrace("psModules.objects", 5, "---- end ----\n");
+    psTrace("psModules.objects", 10, "---- end ----\n");
     return(peaks);
 }
@@ -96,5 +96,5 @@
                                        psU32 row)
 {
-    psTrace("psModules.objects", 4, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
     PS_ASSERT_IMAGE_NON_NULL(image, NULL);
     PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
@@ -104,5 +104,5 @@
         tmpVector->data.F32[col] = image->data.F32[row][col];
     }
-    psTrace("psModules.objects", 4, "---- %s() end ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
     return(tmpVector);
 }
@@ -117,13 +117,13 @@
                              psS32 y)
 {
-    psTrace("psModules.objects", 4, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
     if ((x >= valid.x0) &&
             (x <= valid.x1) &&
             (y >= valid.y0) &&
             (y <= valid.y1)) {
-        psTrace("psModules.objects", 4, "---- %s(true) end ----\n", __func__);
+        psTrace("psModules.objects", 10, "---- %s(true) end ----\n", __func__);
         return(true);
     }
-    psTrace("psModules.objects", 4, "---- %s(false) end ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s(false) end ----\n", __func__);
     return(false);
 }
@@ -140,5 +140,5 @@
                     pmPeakType type)
 {
-    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
     static int id = 1;
     pmPeak *tmp = (pmPeak *) psAlloc(sizeof(pmPeak));
@@ -157,5 +157,5 @@
     psMemSetDeallocator(tmp, (psFreeFunc) peakFree);
 
-    psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
     return(tmp);
 }
@@ -172,5 +172,5 @@
 int pmPeaksCompareAscend (const void **a, const void **b)
 {
-    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
     pmPeak *A = *(pmPeak **)a;
     pmPeak *B = *(pmPeak **)b;
@@ -180,11 +180,11 @@
     diff = A->value - B->value;
     if (diff < FLT_EPSILON) {
-        psTrace("psModules.objects", 3, "---- %s(-1) end ----\n", __func__);
+        psTrace("psModules.objects", 10, "---- %s(-1) end ----\n", __func__);
         return (-1);
     } else if (diff > FLT_EPSILON) {
-        psTrace("psModules.objects", 3, "---- %s(+1) end ----\n", __func__);
+        psTrace("psModules.objects", 10, "---- %s(+1) end ----\n", __func__);
         return (+1);
     }
-    psTrace("psModules.objects", 3, "---- %s(0) end ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s(0) end ----\n", __func__);
     return (0);
 }
@@ -194,5 +194,5 @@
 int pmPeaksCompareDescend (const void **a, const void **b)
 {
-    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
     pmPeak *A = *(pmPeak **)a;
     pmPeak *B = *(pmPeak **)b;
@@ -202,11 +202,11 @@
     diff = A->value - B->value;
     if (diff < FLT_EPSILON) {
-        psTrace("psModules.objects", 3, "---- %s(+1) end ----\n", __func__);
+        psTrace("psModules.objects", 10, "---- %s(+1) end ----\n", __func__);
         return (+1);
     } else if (diff > FLT_EPSILON) {
-        psTrace("psModules.objects", 3, "---- %s(-1) end ----\n", __func__);
+        psTrace("psModules.objects", 10, "---- %s(-1) end ----\n", __func__);
         return (-1);
     }
-    psTrace("psModules.objects", 3, "---- %s(0) end ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s(0) end ----\n", __func__);
     return (0);
 }
@@ -258,5 +258,5 @@
 			 psF32 threshold)
 {
-    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
     PS_ASSERT_VECTOR_NON_NULL(vector, NULL);
     PS_ASSERT_VECTOR_NON_EMPTY(vector, NULL);
@@ -276,5 +276,5 @@
             tmpVector = psVectorAlloc(0, PS_TYPE_U32);
         }
-        psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+        psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
         return(tmpVector);
     }
@@ -341,5 +341,5 @@
     }
 
-    psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
     return(tmpVector);
 }
@@ -363,10 +363,10 @@
 psArray *pmPeaksInImage(const psImage *image, psF32 threshold)
 {
-    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
     PS_ASSERT_IMAGE_NON_NULL(image, NULL);
     PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
     if ((image->numRows == 1) || (image->numCols == 1)) {
         psError(PS_ERR_UNKNOWN, true, "Currently, input image must have at least 2 rows and 2 columns.");
-        psTrace("psModules.objects", 3, "---- %s(NULL) end ----\n", __func__);
+        psTrace("psModules.objects", 10, "---- %s(NULL) end ----\n", __func__);
         return(NULL);
     }
@@ -425,5 +425,5 @@
     //
     if (image->numRows == 1) {
-        psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+        psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
         return(list);
     }
@@ -549,5 +549,5 @@
     psFree (tmpRow);
     psFree (row1);
-    psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
     return(list);
 }
@@ -560,5 +560,5 @@
     const psRegion valid)
 {
-    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
     PS_ASSERT_PTR_NON_NULL(peaks, NULL);
 
@@ -575,5 +575,5 @@
         psArrayAdd (output, 200, tmpPeak);
     }
-    psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
     return(output);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSource.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSource.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSource.c	(revision 23351)
@@ -41,5 +41,5 @@
         return;
 
-    psTrace("psModules.objects", 5, "---- begin ----\n");
+    psTrace("psModules.objects", 10, "---- begin ----\n");
     psFree(tmp->peak);
     psFree(tmp->pixels);
@@ -55,5 +55,5 @@
     psFree(tmp->extpars);
     psFree(tmp->blends);
-    psTrace("psModules.objects", 5, "---- end ----\n");
+    psTrace("psModules.objects", 10, "---- end ----\n");
 }
 
@@ -94,5 +94,5 @@
 pmSource *pmSourceAlloc()
 {
-    psTrace("psModules.objects", 5, "---- begin ----\n");
+    psTrace("psModules.objects", 10, "---- begin ----\n");
     static int id = 1;
     pmSource *source = (pmSource *) psAlloc(sizeof(pmSource));
@@ -131,5 +131,5 @@
     source->extNsigma = NAN;
 
-    psTrace("psModules.objects", 5, "---- end ----\n");
+    psTrace("psModules.objects", 10, "---- end ----\n");
     return(source);
 }
@@ -280,5 +280,5 @@
 pmPSFClump pmSourcePSFClump(psRegion *region, psArray *sources, psMetadata *recipe)
 {
-    psTrace("psModules.objects", 5, "---- begin ----\n");
+    psTrace("psModules.objects", 10, "---- begin ----\n");
 
     psArray *peaks  = NULL;
@@ -496,5 +496,5 @@
     }
 
-    psTrace("psModules.objects", 5, "---- end ----\n");
+    psTrace("psModules.objects", 10, "---- end ----\n");
     return (psfClump);
 }
@@ -508,5 +508,5 @@
 bool pmSourceRoughClass(psRegion *region, psArray *sources, psMetadata *recipe, pmPSFClump clump, psImageMaskType maskSat)
 {
-    psTrace("psModules.objects", 5, "---- begin ----");
+    psTrace("psModules.objects", 10, "---- begin ----");
 
     PS_ASSERT_PTR_NON_NULL(sources, false);
@@ -664,5 +664,5 @@
     psTrace ("psModules.objects", 2, "Ncr:      %3d\n", Ncr);
 
-    psTrace("psModules.objects", 5, "---- end ----\n");
+    psTrace("psModules.objects", 10, "---- end ----\n");
     return true;
 }
@@ -692,5 +692,5 @@
                      psF32 radius)
 {
-    psTrace("psModules.objects", 5, "---- begin ----\n");
+    psTrace("psModules.objects", 10, "---- begin ----\n");
     PS_ASSERT_PTR_NON_NULL(source, false);
     PS_ASSERT_PTR_NON_NULL(source->peak, false);
@@ -807,5 +807,5 @@
         psTrace ("psModules.objects", 3, "insufficient valid pixels (%d vs %d; %f) for source\n",
                  numPixels, (int)(0.75*R2), Sum);
-        psTrace("psModules.objects", 5, "---- end (false) ----\n");
+        psTrace("psModules.objects", 10, "---- end (false) ----\n");
         return (false);
     }
@@ -824,5 +824,5 @@
         psTrace ("psModules.objects", 3, "large centroid swing; invalid peak %d, %d\n",
                  source->peak->x, source->peak->y);
-        psTrace("psModules.objects", 5, "---- end(false)  ----\n");
+        psTrace("psModules.objects", 10, "---- end(false)  ----\n");
         return (false);
     }
@@ -850,5 +850,5 @@
              source->moments->Mxx, source->moments->Myy, source->moments->Mxy);
 
-    psTrace("psModules.objects", 5, "---- end ----\n");
+    psTrace("psModules.objects", 10, "---- end ----\n");
     return(true);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSource.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSource.h	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSource.h	(revision 23351)
@@ -19,4 +19,6 @@
 /// @addtogroup Objects Object Detection / Analysis Functions
 /// @{
+
+#include <pmSourceMasks.h>
 
 /** pmSourceType enumeration
@@ -34,37 +36,4 @@
     PM_SOURCE_TYPE_EXTENDED,            ///< an extended object (eg, galaxy) (subtracted model is EXT)
 } pmSourceType;
-
-// bit flags to distinguish analysis results
-typedef enum {
-    PM_SOURCE_MODE_DEFAULT    	    = 0x00000000, ///< Initial value: resets all bits
-    PM_SOURCE_MODE_PSFMODEL   	    = 0x00000001, ///< Source fitted with a psf model (linear or non-linear)
-    PM_SOURCE_MODE_EXTMODEL   	    = 0x00000002, ///< Source fitted with an extended-source model
-    PM_SOURCE_MODE_FITTED     	    = 0x00000004, ///< Source fitted with non-linear model (PSF or EXT; good or bad)
-    PM_SOURCE_MODE_FAIL       	    = 0x00000008, ///< Fit (non-linear) failed (non-converge, off-edge, run to zero)
-    PM_SOURCE_MODE_POOR       	    = 0x00000010, ///< Fit succeeds, but low-SN, high-Chisq, or large (for PSF -- drop?)
-    PM_SOURCE_MODE_PAIR       	    = 0x00000020, ///< Source fitted with a double psf
-    PM_SOURCE_MODE_PSFSTAR    	    = 0x00000040, ///< Source used to define PSF model
-    PM_SOURCE_MODE_SATSTAR    	    = 0x00000080, ///< Source model peak is above saturation
-    PM_SOURCE_MODE_BLEND      	    = 0x00000100, ///< Source is a blend with other sourcers
-    PM_SOURCE_MODE_EXTERNAL   	    = 0x00000200, ///< Source based on supplied input position
-    PM_SOURCE_MODE_BADPSF     	    = 0x00000400, ///< Failed to get good estimate of object's PSF
-    PM_SOURCE_MODE_DEFECT     	    = 0x00000800, ///< Source is thought to be a defect
-    PM_SOURCE_MODE_SATURATED  	    = 0x00001000, ///< Source is thought to be saturated pixels (bleed trail)
-    PM_SOURCE_MODE_CR_LIMIT   	    = 0x00002000, ///< Source has crNsigma above limit
-    PM_SOURCE_MODE_EXT_LIMIT  	    = 0x00004000, ///< Source has extNsigma above limit
-    PM_SOURCE_MODE_MOMENTS_FAILURE  = 0x00008000, ///< could not measure the moments
-    PM_SOURCE_MODE_SKY_FAILURE      = 0x00010000, ///< could not measure the local sky
-    PM_SOURCE_MODE_SKYVAR_FAILURE   = 0x00020000, ///< could not measure the local sky variance
-    PM_SOURCE_MODE_BELOW_MOMENTS_SN = 0x00040000, ///< moments not measured due to low S/N
-    PM_SOURCE_MODE_BIG_RADIUS       = 0x00100000, ///< poor moments for small radius, try large radius
-    PM_SOURCE_MODE_AP_MAGS          = 0x00200000, ///< source has an aperture magnitude
-    PM_SOURCE_MODE_BLEND_FIT        = 0x00400000, ///< source was fitted as a blend
-    PM_SOURCE_MODE_EXTENDED_FIT     = 0x00800000, ///< full extended fit was used
-    PM_SOURCE_MODE_EXTENDED_STATS   = 0x01000000, ///< extended aperture stats calculated
-    PM_SOURCE_MODE_LINEAR_FIT       = 0x02000000, ///< source fitted with the linear fit
-    PM_SOURCE_MODE_NONLINEAR_FIT    = 0x04000000, ///< source fitted with the non-linear fit
-    PM_SOURCE_MODE_RADIAL_FLUX      = 0x08000000, ///< radial flux measurements calculated
-    PM_SOURCE_MODE_SIZE_SKIPPED     = 0x10000000, ///< size could not be determined
-} pmSourceMode;
 
 typedef enum {
@@ -101,5 +70,5 @@
     pmSourceType type;                  ///< Best identification of object.
     pmSourceMode mode;                  ///< analysis flags set for object.
-    pmSourceTmpF tmpFlags;		///< internal-only flags
+    pmSourceTmpF tmpFlags;              ///< internal-only flags
     psArray *blends;                    ///< collection of sources thought to be confused with object
     float psfMag;                       ///< calculated from flux in modelPSF
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceContour.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceContour.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceContour.c	(revision 23351)
@@ -59,5 +59,5 @@
 {
 
-    psTrace("psModules.objects", 4, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
 
     // We define variables incr and lastColumn so that we can use the same loop
@@ -80,10 +80,10 @@
         float value = image->data.F32[y][subCol];
         if (value <= threshold) {
-            psTrace("psModules.objects", 4, "---- %s() end ----\n", __func__);
+            psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
             return (subCol);
         }
         subCol += incr;
     }
-    psTrace("psModules.objects", 4, "---- %s() end ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
     return (lastColumn);
 }
@@ -99,5 +99,5 @@
 {
 
-    psTrace("psModules.objects", 4, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
 
     // We define variables incr and lastColumn so that we can use the same loop
@@ -119,5 +119,5 @@
         float value = image->data.F32[y][subCol];
         if (value >= threshold) {
-            psTrace("psModules.objects", 4, "---- %s() end ----\n", __func__);
+            psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
             if (subCol == x) {
                 return (subCol);
@@ -127,5 +127,5 @@
         subCol += incr;
     }
-    psTrace("psModules.objects", 4, "---- %s() end ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
     return (lastColumn);
 }
@@ -149,5 +149,5 @@
                        psU32 dir)
 {
-    psTrace("psModules.objects", 4, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
     //
     // Convert coords to subImage space.
@@ -159,9 +159,9 @@
     if (!((0 <= subCol) && (subCol < source->pixels->numCols))) {
         psError(PS_ERR_UNKNOWN, true, "Starting column outside subImage range");
-        psTrace("psModules.objects", 4, "---- %s(NAN) end ----\n", __func__);
+        psTrace("psModules.objects", 10, "---- %s(NAN) end ----\n", __func__);
         return(NAN);
     }
     if (!((0 <= subRow) && (subRow < source->pixels->numRows))) {
-        psTrace("psModules.objects", 4, "---- %s(NAN) end ----\n", __func__);
+        psTrace("psModules.objects", 10, "---- %s(NAN) end ----\n", __func__);
         psError(PS_ERR_UNKNOWN, true, "Starting row outside subImage range");
         return(NAN);
@@ -172,5 +172,5 @@
     psF32 oldValue = pmModelEval(source->modelEXT, source->pixels, subCol, subRow);
     if (oldValue == level) {
-        psTrace("psModules.objects", 4, "---- %s() end ----\n", __func__);
+        psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
         return(((psF32) (subCol + source->pixels->col0)));
     }
@@ -194,5 +194,5 @@
         psF32 newValue = pmModelEval(source->modelEXT, source->pixels, subCol, subRow);
         if (oldValue == level) {
-            psTrace("psModules.objects", 4, "---- %s() end ----\n", __func__);
+            psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
             return((psF32) (subCol + source->pixels->col0));
         }
@@ -200,5 +200,5 @@
         if ((newValue <= level) && (level <= oldValue)) {
             // This is simple linear interpolation.
-            psTrace("psModules.objects", 4, "---- %s() end ----\n", __func__);
+            psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
             return( ((psF32) (subCol + source->pixels->col0)) + ((psF32) incr) * ((level - newValue) / (oldValue - newValue)) );
         }
@@ -206,5 +206,5 @@
         if ((oldValue <= level) && (level <= newValue)) {
             // This is simple linear interpolation.
-            psTrace("psModules.objects", 4, "---- %s() end ----\n", __func__);
+            psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
             return( ((psF32) (subCol + source->pixels->col0)) + ((psF32) incr) * ((level - oldValue) / (newValue - oldValue)) );
         }
@@ -213,5 +213,5 @@
     }
 
-    psTrace("psModules.objects", 4, "---- %s(NAN) end ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s(NAN) end ----\n", __func__);
     return(NAN);
 }
@@ -222,5 +222,5 @@
 psArray *pmSourceContour (psImage *image, int xc, int yc, float threshold)
 {
-    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
     PS_ASSERT_PTR_NON_NULL(image, NULL);
 
@@ -339,5 +339,5 @@
     tmpArray->data[0] = (psPtr *) xVec;
     tmpArray->data[1] = (psPtr *) yVec;
-    psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
     return(tmpArray);
 }
@@ -360,5 +360,5 @@
                                psF32 level)
 {
-    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
     PS_ASSERT_PTR_NON_NULL(source, false);
     PS_ASSERT_PTR_NON_NULL(image, false);
@@ -390,5 +390,5 @@
             psFree(xVec);
             psFree(yVec);
-            psTrace("psModules.objects", 3, "---- %s(NULL) end ----\n", __func__);
+            psTrace("psModules.objects", 10, "---- %s(NULL) end ----\n", __func__);
             return(NULL);
             //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
@@ -403,5 +403,5 @@
             psFree(xVec);
             psFree(yVec);
-            psTrace("psModules.objects", 3, "---- %s(NULL) end ----\n", __func__);
+            psTrace("psModules.objects", 10, "---- %s(NULL) end ----\n", __func__);
             return(NULL);
             //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
@@ -428,5 +428,5 @@
             psFree(xVec);
             psFree(yVec);
-            psTrace("psModules.objects", 3, "---- %s(NULL) end ----\n", __func__);
+            psTrace("psModules.objects", 10, "---- %s(NULL) end ----\n", __func__);
             return(NULL);
             //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
@@ -440,5 +440,5 @@
             psFree(xVec);
             psFree(yVec);
-            psTrace("psModules.objects", 3, "---- %s(NULL) end ----\n", __func__);
+            psTrace("psModules.objects", 10, "---- %s(NULL) end ----\n", __func__);
             return(NULL);
             //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
@@ -457,5 +457,5 @@
     tmpArray->data[0] = (psPtr *) yVec;
     tmpArray->data[1] = (psPtr *) xVec;
-    psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
     return(tmpArray);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceFitModel.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceFitModel.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceFitModel.c	(revision 23351)
@@ -58,5 +58,5 @@
                        psImageMaskType maskVal)
 {
-    psTrace("psModules.objects", 5, "---- %s begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s begin ----\n", __func__);
     PS_ASSERT_PTR_NON_NULL(source, false);
     PS_ASSERT_PTR_NON_NULL(source->pixels, false);
@@ -231,5 +231,5 @@
 
     rc = (onPic && fitStatus);
-    psTrace("psModules.objects", 5, "---- %s(%d) end ----\n", __func__, rc);
+    psTrace("psModules.objects", 10, "---- %s(%d) end ----\n", __func__, rc);
     return(rc);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceFitSet.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceFitSet.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceFitSet.c	(revision 23351)
@@ -446,5 +446,5 @@
                      psImageMaskType maskVal)
 {
-    psTrace("psModules.objects", 3, "---- %s begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s begin ----\n", __func__);
     PS_ASSERT_PTR_NON_NULL(source, false);
     PS_ASSERT_PTR_NON_NULL(source->pixels, false);
@@ -533,5 +533,5 @@
     if (nPix <  thisSet->nParamSet + 1) {
         psTrace (__func__, 4, "insufficient valid pixels\n");
-        psTrace("psModules.objects", 3, "---- %s() end : fail pixels ----\n", __func__);
+        psTrace("psModules.objects", 10, "---- %s() end : fail pixels ----\n", __func__);
         for (int i = 0; i < modelSet->n; i++) {
             pmModel *model = modelSet->data[i];
@@ -602,5 +602,5 @@
 
     bool rc = (onPic && fitStatus);
-    psTrace("psModules.objects", 5, "---- %s end (%d) ----\n", __func__, rc);
+    psTrace("psModules.objects", 10, "---- %s end (%d) ----\n", __func__, rc);
     return(rc);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceIO_CMF_PS1_V1.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceIO_CMF_PS1_V1.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceIO_CMF_PS1_V1.c	(revision 23351)
@@ -209,17 +209,23 @@
     }
 
+    psMetadata *header = psMetadataCopy(NULL, tableHeader);
+    pmSourceMasksHeader(header);
+
     if (table->n == 0) {
-        psFitsWriteBlank (fits, tableHeader, extname);
-        psFree (table);
+        psFitsWriteBlank(fits, header, extname);
+        psFree(table);
+        psFree(header);
         return true;
     }
 
     psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
-    if (!psFitsWriteTable (fits, tableHeader, table, extname)) {
+    if (!psFitsWriteTable(fits, header, table, extname)) {
         psError(PS_ERR_IO, false, "writing ext data %s\n", extname);
         psFree(table);
+        psFree(header);
         return false;
     }
-    psFree (table);
+    psFree(table);
+    psFree(header);
 
     return true;
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceMasks.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceMasks.c	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceMasks.c	(revision 23351)
@@ -0,0 +1,48 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+
+#include "pmSourceMasks.h"
+
+#define ADD_MASK(HEADER, NAME, COMMENT) \
+    psMetadataAddU32(HEADER, PS_LIST_TAIL, "SOURCE.MASK." #NAME, \
+                     PS_META_REPLACE, COMMENT, (psU32)PM_SOURCE_MODE_##NAME);
+
+bool pmSourceMasksHeader(psMetadata *header)
+{
+    PS_ASSERT_METADATA_NON_NULL(header, false);
+
+    ADD_MASK(header, PSFMODEL        , "Fit with PSF model");
+    ADD_MASK(header, EXTMODEL        , "Fit with extended model");
+    ADD_MASK(header, FITTED          , "Fit with non-linear model");
+    ADD_MASK(header, FAIL            , "Non-linear fit failed");
+    ADD_MASK(header, POOR            , "Non-linear fit poor");
+    ADD_MASK(header, PAIR            , "Fit with double PSF");
+    ADD_MASK(header, PSFSTAR         , "Used to define PSF model");
+    ADD_MASK(header, SATSTAR         , "Model peak is above saturation");
+    ADD_MASK(header, BLEND           , "Blended with other sources");
+    ADD_MASK(header, EXTERNAL        , "Based on supplied input position");
+    ADD_MASK(header, BADPSF          , "Unable to estimate object PSF");
+    ADD_MASK(header, DEFECT          , "Suspected defect");
+    ADD_MASK(header, SATURATED       , "Suspected saturated (bleed trail)");
+    ADD_MASK(header, CR_LIMIT        , "Suspected cosmic ray");
+    ADD_MASK(header, EXT_LIMIT       , "Suspected extended");
+    ADD_MASK(header, MOMENTS_FAILURE , "Failed to measure moments");
+    ADD_MASK(header, SKY_FAILURE     , "Failed to measure local sky");
+    ADD_MASK(header, SKYVAR_FAILURE  , "Failed to measure sky variance");
+    ADD_MASK(header, BELOW_MOMENTS_SN, "Moments not measured due to low S/N");
+    ADD_MASK(header, BIG_RADIUS      , "Small radius has poor moments");
+    ADD_MASK(header, AP_MAGS         , "Measured aperture magnitude");
+    ADD_MASK(header, BLEND_FIT       , "Fit as a blend");
+    ADD_MASK(header, EXTENDED_FIT    , "Fit with full extended fit");
+    ADD_MASK(header, EXTENDED_STATS  , "Calculated extended aperture stats");
+    ADD_MASK(header, LINEAR_FIT      , "Fit with linear fit");
+    ADD_MASK(header, NONLINEAR_FIT   , "Fit with non-linear fit");
+    ADD_MASK(header, RADIAL_FLUX     , "Calculated radial flux measurements");
+    ADD_MASK(header, SIZE_SKIPPED    , "Could not be determine size");
+
+    return true;
+}
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceMasks.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceMasks.h	(revision 23351)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceMasks.h	(revision 23351)
@@ -0,0 +1,46 @@
+#ifndef PM_SOURCE_MASKS_H
+#define PM_SOURCE_MASKS_H
+
+// Bit flags to distinguish analysis results
+// When adding to or subtracting from this list, please also modify pmSourceMaskHeader
+typedef enum {
+    PM_SOURCE_MODE_DEFAULT          = 0x00000000, ///< Initial value: resets all bits
+    PM_SOURCE_MODE_PSFMODEL         = 0x00000001, ///< Source fitted with a psf model (linear or non-linear)
+    PM_SOURCE_MODE_EXTMODEL         = 0x00000002, ///< Source fitted with an extended-source model
+    PM_SOURCE_MODE_FITTED           = 0x00000004, ///< Source fitted with non-linear model (PSF or EXT; good or bad)
+    PM_SOURCE_MODE_FAIL             = 0x00000008, ///< Fit (non-linear) failed (non-converge, off-edge, run to zero)
+    PM_SOURCE_MODE_POOR             = 0x00000010, ///< Fit succeeds, but low-SN, high-Chisq, or large (for PSF -- drop?)
+    PM_SOURCE_MODE_PAIR             = 0x00000020, ///< Source fitted with a double psf
+    PM_SOURCE_MODE_PSFSTAR          = 0x00000040, ///< Source used to define PSF model
+    PM_SOURCE_MODE_SATSTAR          = 0x00000080, ///< Source model peak is above saturation
+    PM_SOURCE_MODE_BLEND            = 0x00000100, ///< Source is a blend with other sourcers
+    PM_SOURCE_MODE_EXTERNAL         = 0x00000200, ///< Source based on supplied input position
+    PM_SOURCE_MODE_BADPSF           = 0x00000400, ///< Failed to get good estimate of object's PSF
+    PM_SOURCE_MODE_DEFECT           = 0x00000800, ///< Source is thought to be a defect
+    PM_SOURCE_MODE_SATURATED        = 0x00001000, ///< Source is thought to be saturated pixels (bleed trail)
+    PM_SOURCE_MODE_CR_LIMIT         = 0x00002000, ///< Source has crNsigma above limit
+    PM_SOURCE_MODE_EXT_LIMIT        = 0x00004000, ///< Source has extNsigma above limit
+    PM_SOURCE_MODE_MOMENTS_FAILURE  = 0x00008000, ///< could not measure the moments
+    PM_SOURCE_MODE_SKY_FAILURE      = 0x00010000, ///< could not measure the local sky
+    PM_SOURCE_MODE_SKYVAR_FAILURE   = 0x00020000, ///< could not measure the local sky variance
+    PM_SOURCE_MODE_BELOW_MOMENTS_SN = 0x00040000, ///< moments not measured due to low S/N
+    PM_SOURCE_MODE_BIG_RADIUS       = 0x00100000, ///< poor moments for small radius, try large radius
+    PM_SOURCE_MODE_AP_MAGS          = 0x00200000, ///< source has an aperture magnitude
+    PM_SOURCE_MODE_BLEND_FIT        = 0x00400000, ///< source was fitted as a blend
+    PM_SOURCE_MODE_EXTENDED_FIT     = 0x00800000, ///< full extended fit was used
+    PM_SOURCE_MODE_EXTENDED_STATS   = 0x01000000, ///< extended aperture stats calculated
+    PM_SOURCE_MODE_LINEAR_FIT       = 0x02000000, ///< source fitted with the linear fit
+    PM_SOURCE_MODE_NONLINEAR_FIT    = 0x04000000, ///< source fitted with the non-linear fit
+    PM_SOURCE_MODE_RADIAL_FLUX      = 0x08000000, ///< radial flux measurements calculated
+    PM_SOURCE_MODE_SIZE_SKIPPED     = 0x10000000, ///< size could not be determined
+} pmSourceMode;
+
+
+/// Populate header with mask values
+bool pmSourceMasksHeader(
+    psMetadata *header                  ///< Header to populate
+    );
+
+
+
+#endif
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceMatch.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceMatch.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceMatch.c	(revision 23351)
@@ -16,5 +16,5 @@
 #define SOURCE_FAINTEST 50.0            // Faintest magnitude to consider
 #define SOURCES_MAX_LEAF 2              // Maximum number of points on a tree leaf
-
+#define ARRAY_BUFFER 16                 // Buffer for array
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -63,8 +63,8 @@
         pmSource *source = sources->data[i]; // Source of interest
         if (!source) continue;
-	if (source->mode & SOURCE_MASK) continue;
-	if (!isfinite(source->psfMag)) continue;
-	if (!isfinite(source->errMag)) continue;
-	if (source->psfMag > SOURCE_FAINTEST) continue;
+        if (source->mode & SOURCE_MASK) continue;
+        if (!isfinite(source->psfMag)) continue;
+        if (!isfinite(source->errMag)) continue;
+        if (source->psfMag > SOURCE_FAINTEST) continue;
 
         float xSrc, ySrc;               // Coordinates of source
@@ -113,6 +113,5 @@
 }
 
-pmSourceMatch *pmSourceMatchAlloc(int num // Maximum number of images
-    )
+pmSourceMatch *pmSourceMatchAlloc(void)
 {
     pmSourceMatch *match = psAlloc(sizeof(pmSourceMatch)); // Match data
@@ -120,9 +119,9 @@
 
     match->num = 0;
-    match->mag = psVectorAllocEmpty(num, PS_TYPE_F32);
-    match->magErr = psVectorAllocEmpty(num, PS_TYPE_F32);
-    match->image = psVectorAllocEmpty(num, PS_TYPE_U32);
-    match->index = psVectorAllocEmpty(num, PS_TYPE_U32);
-    match->mask = psVectorAllocEmpty(num, PS_TYPE_VECTOR_MASK);
+    match->mag = psVectorAllocEmpty(ARRAY_BUFFER, PS_TYPE_F32);
+    match->magErr = psVectorAllocEmpty(ARRAY_BUFFER, PS_TYPE_F32);
+    match->image = psVectorAllocEmpty(ARRAY_BUFFER, PS_TYPE_U32);
+    match->index = psVectorAllocEmpty(ARRAY_BUFFER, PS_TYPE_U32);
+    match->mask = psVectorAllocEmpty(ARRAY_BUFFER, PS_TYPE_VECTOR_MASK);
 
     return match;
@@ -136,8 +135,10 @@
 {
     int num = match->num;               // Number of matches
-    psAssert(num <= match->mag->nalloc, "Too many matches.");
-    psAssert(num <= match->magErr->nalloc, "Too many matches.");
-    psAssert(num <= match->image->nalloc, "Too many matches.");
-    psAssert(num <= match->index->nalloc, "Too many matches.");
+
+    match->mag = psVectorExtend(match->mag, match->mag->nalloc, 1);
+    match->magErr = psVectorExtend(match->magErr, match->magErr->nalloc, 1);
+    match->image = psVectorExtend(match->image, match->image->nalloc, 1);
+    match->index = psVectorExtend(match->index, match->index->nalloc, 1);
+    match->mask = psVectorExtend(match->mask, match->mask->nalloc, 1);
 
     match->mag->data.F32[num] = mag;
@@ -148,9 +149,9 @@
     match->num++;
 
-    match->mag->n = match->magErr->n = match->image->n = match->index->n = match->mask->n = match->num;
-}
-
-
-psArray *pmSourceMatchSources(const psArray *sourceArrays, float radius)
+    return;
+}
+
+
+psArray *pmSourceMatchSources(const psArray *sourceArrays, float radius, bool cullSingles)
 {
     PS_ASSERT_ARRAY_NON_NULL(sourceArrays, NULL);
@@ -185,16 +186,13 @@
             matches = psArrayAlloc(numSources);
             for (int j = 0; j < numSources; j++) {
-                pmSourceMatch *match = pmSourceMatchAlloc(numImages); // Match data
+                pmSourceMatch *match = pmSourceMatchAlloc(); // Match data
                 pmSourceMatchAdd(match, magImage->data.F32[j], magErrImage->data.F32[j], i, j);
                 matches->data[j] = match;
             }
             numMaster += numSources;
-            goto match_image_done;
-        }
-
-        if (boundsImage->x0 > boundsMaster->x1 || boundsImage->x1 < boundsMaster->x0 ||
-            boundsImage->y0 > boundsMaster->y1 || boundsImage->y1 < boundsMaster->y0) {
+        } else if (boundsImage->x0 > boundsMaster->x1 || boundsImage->x1 < boundsMaster->x0 ||
+                   boundsImage->y0 > boundsMaster->y1 || boundsImage->y1 < boundsMaster->y0) {
+            // Bounds don't overlap --- can just add everything in to the master list
             psTrace("psModules.objects", 7, "Bounds don't overlap\n");
-            // Add everything in to the master list
             long size = numMaster + numSources; // New size
             xMaster = psVectorRealloc(xMaster, size);
@@ -207,5 +205,5 @@
                    numSources * PSELEMTYPE_SIZEOF(PS_TYPE_F32));
             for (int j = 0, k = numMaster; j < numSources; j++, k++) {
-                pmSourceMatch *match = pmSourceMatchAlloc(numImages); // Match data
+                pmSourceMatch *match = pmSourceMatchAlloc(); // Match data
                 pmSourceMatchAdd(match, magImage->data.F32[j], magErrImage->data.F32[j], i, j);
                 matches->data[k] = match;
@@ -215,45 +213,40 @@
             yMaster->n = size;
             matches->n = size;
-
-            goto match_image_done;
-        }
-
-        // Match with the master list
-
-        psTree *tree = psTreePlant(2, SOURCES_MAX_LEAF, xMaster, yMaster); // kd Tree with sources
-        long numMatch = 0;              // Number of matches
-
-        long size = numMaster + numSources; // New size
-        xMaster = psVectorRealloc(xMaster, size);
-        yMaster = psVectorRealloc(yMaster, size);
-        matches = psArrayRealloc(matches, size);
-
-        psVector *coords = psVectorAlloc(2, PS_TYPE_F32); // Coordinates for tree lookup
-        for (int j = 0; j < numSources; j++) {
-            coords->data.F32[0] = xImage->data.F32[j];
-            coords->data.F32[1] = yImage->data.F32[j];
-            long index = psTreeNearestWithin(tree, coords, radius); // Match index
-            if (index >= 0) {
-                // Record the match
-                pmSourceMatch *match = matches->data[index]; // Match data
-                pmSourceMatchAdd(match, magImage->data.F32[j], magErrImage->data.F32[j], i, j);
-                numMatch++;
-            } else {
-                // Add to the master list
-                pmSourceMatch *match = pmSourceMatchAlloc(numImages); // Match data
-                pmSourceMatchAdd(match, magImage->data.F32[j], magErrImage->data.F32[j], i, j);
-                xMaster->data.F32[numMaster] = xImage->data.F32[j];
-                yMaster->data.F32[numMaster] = yImage->data.F32[j];
-                matches->data[numMaster] = match;
-                numMaster++;
-                xMaster->n = yMaster->n = matches->n = numMaster;
-            }
-
-        }
-        psFree(coords);
-        psFree(tree);
-
-
-    match_image_done:
+        } else {
+            // Match with the master list
+            psTree *tree = psTreePlant(2, SOURCES_MAX_LEAF, xMaster, yMaster); // kd Tree with sources
+            long numMatch = 0;          // Number of matches
+
+            long size = numMaster + numSources; // New size
+            xMaster = psVectorRealloc(xMaster, size);
+            yMaster = psVectorRealloc(yMaster, size);
+            matches = psArrayRealloc(matches, size);
+
+            psVector *coords = psVectorAlloc(2, PS_TYPE_F32); // Coordinates for tree lookup
+            for (int j = 0; j < numSources; j++) {
+                coords->data.F32[0] = xImage->data.F32[j];
+                coords->data.F32[1] = yImage->data.F32[j];
+                long index = psTreeNearestWithin(tree, coords, radius); // Match index
+                if (index >= 0) {
+                    // Record the match
+                    pmSourceMatch *match = matches->data[index]; // Match data
+                    pmSourceMatchAdd(match, magImage->data.F32[j], magErrImage->data.F32[j], i, j);
+                    numMatch++;
+                } else {
+                    // Add to the master list
+                    pmSourceMatch *match = pmSourceMatchAlloc(); // Match data
+                    pmSourceMatchAdd(match, magImage->data.F32[j], magErrImage->data.F32[j], i, j);
+                    xMaster->data.F32[numMaster] = xImage->data.F32[j];
+                    yMaster->data.F32[numMaster] = yImage->data.F32[j];
+                    matches->data[numMaster] = match;
+                    numMaster++;
+                    xMaster->n = yMaster->n = matches->n = numMaster;
+                }
+
+            }
+            psFree(coords);
+            psFree(tree);
+        }
+
         psFree(boundsImage);
         psFree(xImage);
@@ -263,26 +256,71 @@
     }
 
-    // Now cull the matches that contain only a single star
-    int numGood = 0;                    // Number of good matches
+    if (cullSingles) {
+        // Now cull the matches that contain only a single star
+        int numGood = 0;                    // Number of good matches
+        for (int i = 0; i < matches->n; i++) {
+            pmSourceMatch *match = matches->data[i]; // Match of interest
+            if (match->num > 1) {
+                if (i != numGood) {
+                    psFree(matches->data[numGood]);
+                    matches->data[numGood] = psMemIncrRefCounter(match);
+                }
+                numGood++;
+            }
+        }
+        matches->n = numGood;
+        for (int i = numGood; i < numMaster; i++) {
+            psFree(matches->data[i]);
+            matches->data[i] = NULL;
+        }
+    }
+
+    return matches;
+}
+
+
+psArray *pmSourceMatchMerge(psArray *sourceArrays, float radius)
+{
+    PS_ASSERT_ARRAY_NON_NULL(sourceArrays, NULL);
+    PS_ASSERT_FLOAT_LARGER_THAN(radius, 0.0, NULL);
+
+    psArray *matches = pmSourceMatchSources(sourceArrays, radius, false); // Source matches
+    if (!matches) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to match source lists.");
+        return NULL;
+    }
+
+    int index = 0;                      // Index to current position on list
     for (int i = 0; i < matches->n; i++) {
+        pmSource *source = NULL;        // Source to put in merged list
         pmSourceMatch *match = matches->data[i]; // Match of interest
-        if (match->num > 1) {
-            if (i != numGood) {
-                psFree(matches->data[numGood]);
-                matches->data[numGood] = psMemIncrRefCounter(match);
-                //                psFree(match);
-            }
-            numGood++;
-        }
-    }
-    matches->n = numGood;
-    for (int i = numGood; i < numMaster; i++) {
-        psFree(matches->data[i]);
-        matches->data[i] = NULL;
-    }
+        for (int j = 0; j < match->num && !source; j++) {
+            if (!isfinite(match->mag->data.F32[j]) || !isfinite(match->magErr->data.F32[j])) {
+                continue;
+            }
+            int imgIndex = match->image->data.S32[j]; // Index of image
+            int srcIndex = match->index->data.S32[j]; // Index of source for image
+            psArray *list = sourceArrays->data[imgIndex]; // List of interest
+            source = list->data[srcIndex];
+            break;
+        }
+
+        if (source) {
+            psFree(matches->data[index]);
+            matches->data[index] = psMemIncrRefCounter(source);
+            index++;
+        }
+    }
+
+    // Clear out the rest of the list
+    int num = index;                    // Number of good sources
+    for (; index < matches->n; index++) {
+        psFree(matches->data[index]);
+        matches->data[index] = NULL;
+    }
+    matches->n = num;
 
     return matches;
 }
-
 
 // Iterate on the star magnitudes and image transparencies
@@ -290,4 +328,5 @@
 static float sourceMatchRelphotIterate(psVector *trans, // Transparencies
                                        psVector *stars, // Star magnitudes
+                                       psVector *badImage, // Bad image mask
                                        const psArray *matches, // Array of matches
                                        const psVector *zp, // Zero points for each image (incl. airmass term)
@@ -363,5 +402,7 @@
     for (int i = 0; i < numImages; i++) {
         trans->data.F32[i] = accum->data.F64[i] / accumErr->data.F64[i];
-
+        if (!isfinite(trans->data.F32[i])) {
+            badImage->data.U8[i] = 0xFF;
+        }
         psTrace("psModules.objects", 3, "Transparency for image %d: %f\n", i, trans->data.F32[i]);
     }
@@ -379,4 +420,7 @@
             }
             int index = match->image->data.U32[j]; // Image index
+            if (badImage->data.U8[index]) {
+                continue;
+            }
             float mag = match->mag->data.F32[j]; // Measured magnitude
             float magErr2 = PS_SQR(match->magErr->data.F32[j]) + sysErr2; // Error in measured magnitude
@@ -402,4 +446,5 @@
 static int sourceMatchRelphotPhotometric(psVector *photo, // Photometric determination
                                          const psVector *trans, // Estimated transparencies
+                                         const psVector *badImage, // Bad image?
                                          int transIter, // Iterations for transparency
                                          float transClip, // Clipping level for transparency
@@ -409,8 +454,10 @@
     psAssert(photo && photo->type.type == PS_TYPE_U8, "Need photometric determination");
     psAssert(trans && trans->type.type == PS_TYPE_F32, "Need transparencies");
+    psAssert(badImage && badImage->type.type == PS_TYPE_U8, "Need bad image determination");
 
     int numImages = photo->n;              // Number of images
 
     psAssert(trans->n == numImages, "Not enough transparencies: %ld", trans->n);
+    psAssert(badImage->n == numImages, "Not enough bad image determinations: %ld", badImage->n);
     psAssert(transIter >= 0, "Iterations for transparency must be non-negative: %d", transIter);
     psAssert(transClip > 0, "Clipping level for transparency must be positive: %f", transClip);
@@ -421,5 +468,5 @@
     stats->clipSigma = transClip;
 
-    if (!psVectorStats(stats, trans, NULL, NULL, 0)) {
+    if (!psVectorStats(stats, trans, NULL, badImage, 0xFF)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to perform statistics on transparencies.");
         psFree(stats);
@@ -432,4 +479,7 @@
     int numPhoto = 0;                   // Number of photometric images
     for (int i = 0; i < numImages; i++) {
+        if (badImage->data.U8[i]) {
+            continue;
+        }
         if (trans->data.F32[i] < thresh) {
             photo->data.U8[i] = 0xFF;
@@ -451,4 +501,5 @@
                                       const psVector *zp, // Zero points for each image
                                       const psVector *photo, // Photometric image?
+                                      const psVector *badImage, // Bad image?
                                       float starClip, // Clipping for stars
                                       float sysErr2 // Systematic error squared
@@ -468,4 +519,6 @@
     psAssert(!photo || photo->type.type == PS_TYPE_U8, "Photometric determination is wrong type");
     psAssert(!photo || photo->n == numImages, "Not enough photometric determinations: %ld", photo->n);
+    psAssert(!badImage || badImage->type.type == PS_TYPE_U8, "Photometric determination is wrong type");
+    psAssert(!badImage || badImage->n == numImages, "Not enough bad determinations: %ld", badImage->n);
 
     starClip = PS_SQR(starClip);
@@ -481,4 +534,7 @@
             numMeasurements++;
             int index = match->image->data.U32[j]; // Image index
+            if (badImage->data.U8[index]) {
+                continue;
+            }
             float mag = match->mag->data.F32[j]; // Measured magnitude
             float magErr = match->magErr->data.F32[j]; // Error in measured magnitude
@@ -489,12 +545,12 @@
             float dev = mag + cal - stars->data.F32[i]; // Deviation
 
-	    // only reject detections from photometric images (non-photometric images can
-	    // have large errors.  XXX Or: allow a much higher rejection threshold
-	    if (photo->data.U8[index]) {
-	      if (PS_SQR(dev) > starClip * (PS_SQR(magErr) + sysErr2)) {
-                numRejected++;
-                match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j] = 0xFF;
-	      }
-	    }
+            // only reject detections from photometric images (non-photometric images can
+            // have large errors.  XXX Or: allow a much higher rejection threshold
+            if (photo->data.U8[index]) {
+                if (PS_SQR(dev) > starClip * (PS_SQR(magErr) + sysErr2)) {
+                    numRejected++;
+                    match->mask->data.PS_TYPE_VECTOR_MASK_DATA[j] = 0xFF;
+                }
+            }
         }
     }
@@ -506,12 +562,15 @@
 psVector *pmSourceMatchRelphot(const psArray *matches, // Array of matches
                                const psVector *zp, // Zero points for each image (including airmass term)
-                               int maxIter, // Maximum number of iterations
                                float tol, // Relative tolerance for convergence
+                               int iter1, // Number of iterations for pass 1
+                               float rej1, // Limit on rejection between iterations for pass 1
+                               float sys1, // Systematic error in measurements for pass 1
+                               int iter2, // Number of iterations for pass 2
+                               float rej2, // Limit on rejection between iterations for pass 2
+                               float sys2, // Systematic error in measurements for pass 2
                                float rejLimit, // Limit on rejection between iterations
                                int transIter, // Clipping iterations for transparency
                                float transClip, // Clipping level for transparency
-                               float photoLevel, // Level at which we declare image is photometric
-                               float starClip, // Clipping for stars
-                               float sysErr // Systematic error in measurements
+                               float photoLevel // Level at which we declare image is photometric
                                )
 {
@@ -521,5 +580,6 @@
     PS_ASSERT_FLOAT_LARGER_THAN(transClip, 0.0, NULL);
 
-    sysErr *= sysErr;
+    sys1 *= sys1;
+    sys2 *= sys2;
 
     int numImages = zp->n;              // Number of images
@@ -529,39 +589,20 @@
     psVector *photo = psVectorAlloc(numImages, PS_TYPE_U8); // Photometric determination for each image
     psVectorInit(photo, 0);
+    psVector *badImage = psVectorAlloc(numImages, PS_TYPE_U8); // Bad image?
+    psVectorInit(badImage, 0);
     psVector *stars = psVectorAlloc(numStars, PS_TYPE_F32); // Magnitudes for each star
 
-    float chi2 = sourceMatchRelphotIterate(trans, stars, matches, zp, photo, sysErr); // chi^2 for solution
+    float chi2 = sourceMatchRelphotIterate(trans, stars, badImage, matches, zp,
+                                           photo, sys1); // chi^2 for solution
     psTrace("psModules.objects", 1, "Initial: chi^2 = %f\n", chi2);
     float lastChi2 = INFINITY;          // chi^2 on last iteration
     float fracRej = INFINITY;        // Fraction of measurements rejected
 
-    // in the first passes, the transparencies are not well deteremined: use high systematic error and the rejection thresholds
-    for (int i = 0; i < 5; i++) {
+    // In the first passes, the transparencies are not well deteremined: use high systematic error and
+    // rejection thresholds
+    for (int i = 0; i < iter1; i++) {
 
         // Identify photometric nights
-        int numPhoto = sourceMatchRelphotPhotometric(photo, trans, transIter, transClip, photoLevel); // Number of photometric images
-        if (numPhoto < 0) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to perform photometric determination");
-            psFree(trans);
-            psFree(photo);
-            psFree(stars);
-            return NULL;
-        }
-        psTrace("psModules.objects", 3, "Pass 1: Determined %d/%d are photometric", numPhoto, numImages);
-
-	// XXX use 20 sigma rejection and 0.1 mag systematic error (move these to the recipe)
-        fracRej = sourceMatchRelphotReject(trans, stars, matches, zp, photo, 20.0, PS_SQR(0.1));
-        psTrace("psModules.objects", 3, "Pass 1: %f%% of measurements rejected", fracRej * 100);
-
-	// XXX use 0.05 mag systematic error (move these to the recipe)
-        chi2 = sourceMatchRelphotIterate(trans, stars, matches, zp, photo, PS_SQR(0.1)); // chi^2 for solution
-        psTrace("psModules.objects", 1, "Pass 1: iter = %d: chi^2 = %f rejected = %f\n", i, chi2, fracRej);
-    }
-
-    for (int i = 0; i < maxIter && (fabsf(lastChi2 - chi2) > tol * chi2 || fracRej > rejLimit); i++) {
-        lastChi2 = chi2;
-
-        // Identify photometric nights
-        int numPhoto = sourceMatchRelphotPhotometric(photo, trans, transIter, transClip,
+        int numPhoto = sourceMatchRelphotPhotometric(photo, trans, badImage, transIter, transClip,
                                                      photoLevel); // Number of photometric images
         if (numPhoto < 0) {
@@ -572,14 +613,37 @@
             return NULL;
         }
-        psTrace("psModules.objects", 3, "Determined %d/%d are photometric", numPhoto, numImages);
-
-        fracRej = sourceMatchRelphotReject(trans, stars, matches, zp, photo, starClip, sysErr);
-        psTrace("psModules.objects", 3, "%f%% of measurements rejected", fracRej * 100);
-
-        chi2 = sourceMatchRelphotIterate(trans, stars, matches, zp, photo, sysErr); // chi^2 for solution
-        psTrace("psModules.objects", 1, "iter = %d: chi^2 = %f rejected = %f\n", i, chi2, fracRej);
+        psTrace("psModules.objects", 3, "Pass 1: Determined %d/%d are photometric", numPhoto, numImages);
+
+        fracRej = sourceMatchRelphotReject(trans, stars, matches, zp, photo, badImage, rej1, sys1);
+        psTrace("psModules.objects", 3, "Pass 1: %f%% of measurements rejected", fracRej * 100);
+
+        chi2 = sourceMatchRelphotIterate(trans, stars, badImage, matches, zp, photo, sys1);
+        psTrace("psModules.objects", 1, "Pass 1: iter = %d: chi^2 = %f rejected = %f\n", i, chi2, fracRej);
+    }
+
+    for (int i = 0; i < iter2 && (fabsf(lastChi2 - chi2) > tol * chi2 || fracRej > rejLimit); i++) {
+        lastChi2 = chi2;
+
+        // Identify photometric nights
+        int numPhoto = sourceMatchRelphotPhotometric(photo, trans, badImage, transIter, transClip,
+                                                     photoLevel); // Number of photometric images
+        if (numPhoto < 0) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to perform photometric determination");
+            psFree(trans);
+            psFree(photo);
+            psFree(stars);
+            return NULL;
+        }
+        psTrace("psModules.objects", 3, "Pass 2: Determined %d/%d are photometric", numPhoto, numImages);
+
+        fracRej = sourceMatchRelphotReject(trans, stars, matches, zp, photo, badImage, rej2, sys2);
+        psTrace("psModules.objects", 3, "Pass 2: %f%% of measurements rejected", fracRej * 100);
+
+        chi2 = sourceMatchRelphotIterate(trans, stars, badImage, matches, zp, photo, sys2);
+        psTrace("psModules.objects", 1, "Pass 2: iter = %d: chi^2 = %f rejected = %f\n", i, chi2, fracRej);
     }
 
     psFree(photo);
+    psFree(badImage);
     psFree(stars);
 
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceMatch.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceMatch.h	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceMatch.h	(revision 23351)
@@ -33,6 +33,5 @@
 
 /// Allocator for pmSourceMatch
-pmSourceMatch *pmSourceMatchAlloc(int num // Maximum number of images
-                                  );
+pmSourceMatch *pmSourceMatchAlloc(void);
 
 /// Add a source to a match
@@ -48,19 +47,30 @@
 ///
 /// Returns an array of psSourceMatch
-psArray *pmSourceMatchSources(const psArray *sourceArrays, // Array of arrays of sources on each image
-                              float radius // Correlation radius
+psArray *pmSourceMatchSources(const psArray *sourceArrays, ///< Array of arrays of sources on each image
+                              float radius, ///< Matching radius
+                              bool cullSingles ///< Cull "matches" with only a single source?
                               );
+
+/// Merge two source lists
+///
+/// Sources are pulled from the lists into a new list, with no effort made to adjust them.
+psArray *pmSourceMatchMerge(psArray *sourceArrays, ///< Array of arrays of sources on each image
+                            float radius ///< Matching radius
+    );
 
 /// Perform relative photometry to calibrate images
 psVector *pmSourceMatchRelphot(const psArray *matches, // Array of matches
                                const psVector *zp, // Zero points for each image (including airmass term)
-                               int maxIter, // Maximum number of iterations
                                float tol, // Relative tolerance for convergence
+                               int iter1, // Number of iterations for pass 1
+                               float rej1, // Limit on rejection between iterations for pass 1
+                               float sys1, // Systematic error in measurements for pass 1
+                               int iter2, // Number of iterations for pass 2
+                               float rej2, // Limit on rejection between iterations for pass 2
+                               float sys2, // Systematic error in measurements for pass 2
                                float rejLimit, // Limit on rejection between iterations
                                int transIter, // Clipping iterations for transparency
                                float transClip, // Clipping level for transparency
-                               float photoLevel, // Level at which we declare image is photometric
-                               float starClip, // Clipping for stars
-                               float sysErr // Systematic error in measurements
+                               float photoLevel // Level at which we declare image is photometric
     );
 
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceSky.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceSky.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceSky.c	(revision 23351)
@@ -55,5 +55,5 @@
     psImageMaskType markVal)
 {
-    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
     PS_ASSERT_PTR_NON_NULL(source, false);
     PS_ASSERT_IMAGE_NON_NULL(source->pixels, false);
@@ -91,5 +91,5 @@
 
     if (isnan(value)) {
-        psTrace("psModules.objects", 3, "---- %s(false) end ----\n", __func__);
+        psTrace("psModules.objects", 10, "---- %s(false) end ----\n", __func__);
         return(false);
     }
@@ -98,5 +98,5 @@
     }
     source->moments->Sky = value;
-    psTrace("psModules.objects", 3, "---- %s(true) end ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s(true) end ----\n", __func__);
     return (true);
 }
@@ -111,5 +111,5 @@
 )
 {
-    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
     PS_ASSERT_PTR_NON_NULL(source, false);
     PS_ASSERT_IMAGE_NON_NULL(source->variance, false);
@@ -147,5 +147,5 @@
 
     if (isnan(value)) {
-        psTrace("psModules.objects", 3, "---- %s(false) end ----\n", __func__);
+        psTrace("psModules.objects", 10, "---- %s(false) end ----\n", __func__);
         return(false);
     }
@@ -154,5 +154,5 @@
     }
     source->moments->dSky = value;
-    psTrace("psModules.objects", 3, "---- %s(true) end ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s(true) end ----\n", __func__);
     return (true);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceUtils.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceUtils.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceUtils.c	(revision 23351)
@@ -49,5 +49,5 @@
                             pmModelType modelType)
 {
-    psTrace("psModules.objects", 3, "---- %s() begin ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() begin ----\n", __func__);
     PS_ASSERT_PTR_NON_NULL(source, NULL);
     PS_ASSERT_PTR_NON_NULL(source->moments, NULL);
@@ -61,5 +61,5 @@
     }
 
-    psTrace("psModules.objects", 3, "---- %s() end ----\n", __func__);
+    psTrace("psModules.objects", 10, "---- %s() end ----\n", __func__);
     return(model);
 }
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceVisual.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceVisual.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceVisual.c	(revision 23351)
@@ -7,39 +7,41 @@
 #include "pmSourceVisual.h"
 
-# if (HAVE_KAPA)
-# include <kapa.h>
+#if (HAVE_KAPA)
+#include <kapa.h>
+#include "pmVisual.h"
 
 // functions used to visualize the analysis as it goes
 // these are invoked by the -visual options
 
-static bool isVisual = false;
 static int kapa1 = -1;
+static bool plotPSF = true;
 // static int kapa2 = -1;
 // static int kapa3 = -1;
 
+bool pmSourceVisualClose() {
+    if (kapa1 != -1)
+        KiiClose(kapa1);
+    return true;
+}
+
 bool pmSourcePlotPoints3D (int myKapa, Graphdata *graphdata, psVector *xn, psVector *yn, psVector *zn, float theta, float phi);
 
-bool pmSourceSetVisual (bool mode) {
-
-    isVisual = mode;
-    return true;
-}
 
 bool pmSourceVisualPSFModelResid (pmTrend2D *trend, psVector *x, psVector *y, psVector *param, psVector *mask) {
 
-    KapaSection section;  // put the positive profile in one and the residuals in another? 
+    KapaSection section;  // put the positive profile in one and the residuals in another?
 
     Graphdata graphdata;
 
-    if (!isVisual) return true;
+    if (!pmVisualIsVisual() || !plotPSF) return true;
 
     if (kapa1 == -1) {
         kapa1 = KapaOpenNamedSocket ("kapa", "pmSource:plots");
-	if (kapa1 == -1) {
-	    fprintf (stderr, "failure to open kapa; visual mode disabled\n");
-	    isVisual = false;
-	    return false;
-	}
-    }  
+        if (kapa1 == -1) {
+            fprintf (stderr, "failure to open kapa; visual mode disabled\n");
+            pmVisualSetVisual(false);
+            return false;
+        }
+    }
 
     KapaClearPlots (kapa1);
@@ -55,11 +57,11 @@
 
     for (int i = 0; i < x->n; i++) {
-	model->data.F32[i] = pmTrend2DEval (trend, x->data.F32[i], y->data.F32[i]);
-	resid->data.F32[i] = param->data.F32[i] - model->data.F32[i];
-	if (mask->data.PS_TYPE_VECTOR_MASK_DATA[i]) continue;
-	min = PS_MIN (min, resid->data.F32[i]);
-	max = PS_MAX (max, resid->data.F32[i]);
-	Min = PS_MIN (min, param->data.F32[i]);
-	Max = PS_MAX (max, param->data.F32[i]);
+        model->data.F32[i] = pmTrend2DEval (trend, x->data.F32[i], y->data.F32[i]);
+        resid->data.F32[i] = param->data.F32[i] - model->data.F32[i];
+        if (mask->data.PS_TYPE_VECTOR_MASK_DATA[i]) continue;
+        min = PS_MIN (min, resid->data.F32[i]);
+        max = PS_MAX (max, resid->data.F32[i]);
+        Min = PS_MIN (min, param->data.F32[i]);
+        Max = PS_MAX (max, param->data.F32[i]);
     }
 
@@ -70,9 +72,9 @@
     psVector *Fn = psVectorAlloc (x->n, PS_TYPE_F32);
     for (int i = 0; i < x->n; i++) {
-	xn->data.F32[i] = x->data.F32[i] / 5000.0;
-	yn->data.F32[i] = y->data.F32[i] / 5000.0;
-	zn->data.F32[i] = (resid->data.F32[i] - min) / (max - min);
-	Zn->data.F32[i] = (param->data.F32[i] - Min) / (Max - Min);
-	Fn->data.F32[i] = (model->data.F32[i] - Min) / (Max - Min);
+        xn->data.F32[i] = x->data.F32[i] / 5000.0;
+        yn->data.F32[i] = y->data.F32[i] / 5000.0;
+        zn->data.F32[i] = (resid->data.F32[i] - min) / (max - min);
+        Zn->data.F32[i] = (param->data.F32[i] - Min) / (Max - Min);
+        Fn->data.F32[i] = (model->data.F32[i] - Min) / (Max - Min);
     }
 
@@ -152,9 +154,6 @@
     // pause and wait for user input:
     // continue, save (provide name), ??
-    char key[10];
-    fprintf (stdout, "[c]ontinue? ");
-    if (!fgets(key, 8, stdin)) {
-	psWarning("Unable to read option");
-    }
+    pmVisualAskUser(&plotPSF);
+
     return true;
 }
@@ -173,18 +172,18 @@
 
     for (int i = 0; i < xn->n; i++) {
-	xv->data.F32[2*i+0] = +xn->data.F32[i]*cos(theta) + yn->data.F32[i]*sin(theta)*cos(phi) + zn->data.F32[i]*sin(theta)*sin(phi);
-	yv->data.F32[2*i+0] = -xn->data.F32[i]*sin(theta) + yn->data.F32[i]*cos(theta)*cos(phi) + zn->data.F32[i]*cos(theta)*sin(phi);
-	zv->data.F32[2*i+0] = -yn->data.F32[i]*sin(phi)   + zn->data.F32[i]*cos(phi);
-	xv->data.F32[2*i+1] = +xn->data.F32[i]*cos(theta) + yn->data.F32[i]*sin(theta)*cos(phi);
-	yv->data.F32[2*i+1] = -xn->data.F32[i]*sin(theta) + yn->data.F32[i]*cos(theta)*cos(phi);
-	zv->data.F32[2*i+1] = -yn->data.F32[i]*sin(phi);
-	graphdata->xmin = PS_MIN(graphdata->xmin, xv->data.F32[2*i+0]);
-	graphdata->xmax = PS_MAX(graphdata->xmax, xv->data.F32[2*i+0]);
-	graphdata->ymin = PS_MIN(graphdata->ymin, zv->data.F32[2*i+0]);
-	graphdata->ymax = PS_MAX(graphdata->ymax, zv->data.F32[2*i+0]);
-	graphdata->xmin = PS_MIN(graphdata->xmin, xv->data.F32[2*i+1]);
-	graphdata->xmax = PS_MAX(graphdata->xmax, xv->data.F32[2*i+1]);
-	graphdata->ymin = PS_MIN(graphdata->ymin, zv->data.F32[2*i+1]);
-	graphdata->ymax = PS_MAX(graphdata->ymax, zv->data.F32[2*i+1]);
+        xv->data.F32[2*i+0] = +xn->data.F32[i]*cos(theta) + yn->data.F32[i]*sin(theta)*cos(phi) + zn->data.F32[i]*sin(theta)*sin(phi);
+        yv->data.F32[2*i+0] = -xn->data.F32[i]*sin(theta) + yn->data.F32[i]*cos(theta)*cos(phi) + zn->data.F32[i]*cos(theta)*sin(phi);
+        zv->data.F32[2*i+0] = -yn->data.F32[i]*sin(phi)   + zn->data.F32[i]*cos(phi);
+        xv->data.F32[2*i+1] = +xn->data.F32[i]*cos(theta) + yn->data.F32[i]*sin(theta)*cos(phi);
+        yv->data.F32[2*i+1] = -xn->data.F32[i]*sin(theta) + yn->data.F32[i]*cos(theta)*cos(phi);
+        zv->data.F32[2*i+1] = -yn->data.F32[i]*sin(phi);
+        graphdata->xmin = PS_MIN(graphdata->xmin, xv->data.F32[2*i+0]);
+        graphdata->xmax = PS_MAX(graphdata->xmax, xv->data.F32[2*i+0]);
+        graphdata->ymin = PS_MIN(graphdata->ymin, zv->data.F32[2*i+0]);
+        graphdata->ymax = PS_MAX(graphdata->ymax, zv->data.F32[2*i+0]);
+        graphdata->xmin = PS_MIN(graphdata->xmin, xv->data.F32[2*i+1]);
+        graphdata->xmax = PS_MAX(graphdata->xmax, xv->data.F32[2*i+1]);
+        graphdata->ymin = PS_MIN(graphdata->ymin, zv->data.F32[2*i+1]);
+        graphdata->ymax = PS_MAX(graphdata->ymax, zv->data.F32[2*i+1]);
     }
     xv->n = xn->n;
@@ -253,4 +252,15 @@
 }
 
-# else
-# endif
+#else
+
+bool pmSourceSetVisual(bool mode)
+{
+    return true;
+}
+
+bool pmSourceVisualPSFModelResid(pmTrend2D *trend, psVector *x, psVector *y, psVector *param, psVector *mask)
+{
+    return true;
+}
+
+#endif
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceVisual.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceVisual.h	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/objects/pmSourceVisual.h	(revision 23351)
@@ -1,9 +1,9 @@
 /* @file  pmKapaPlots.h
- * @brief functions to make plots with the external program 'kapa' 
+ * @brief functions to make plots with the external program 'kapa'
  *
  * @author EAM, IfA
  *
- * @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
- * @date $Date: 2008-11-08 01:52:34 $
+ * @version $Revision: 1.1.16.1 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2009-02-19 17:59:50 $
  * Copyright 2006 Institute for Astronomy, University of Hawaii
  */
@@ -12,8 +12,9 @@
 #define PM_SOURCE_VISUAL_H
 
+bool pmSourceVisualClose(void);
+
 /// @addtogroup Extras Miscellaneous Funtions
 /// @{
 
-bool pmSourceSetVisual (bool mode);
 bool pmSourceVisualPSFModelResid (pmTrend2D *trend, psVector *x, psVector *y, psVector *param, psVector *mask);
 
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/src/psmodules.h
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/src/psmodules.h	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/src/psmodules.h	(revision 23351)
@@ -101,4 +101,5 @@
 #include <pmReadoutCombine.h>
 #include <pmSubtractionVisual.h>
+#include <pmStackVisual.h>
 
 // the following headers are from psModule:objects
@@ -113,4 +114,5 @@
 #include <pmPSF.h>
 #include <pmModel.h>
+#include <pmSourceMasks.h>
 #include <pmSource.h>
 #include <pmSourceUtils.h>
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/test/detrend/tap_pmMaskBadPixels.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/test/detrend/tap_pmMaskBadPixels.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/test/detrend/tap_pmMaskBadPixels.c	(revision 23351)
@@ -282,5 +282,5 @@
         pmCell *cell = generateSimpleCell(NULL);
         pmReadout *in = generateSimpleReadout(cell);
-        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 0);
 
         ok(!pmMaskFlagSuspectPixels(out, NULL, 1.0, 1, 1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with NULL input pmReadout");
@@ -301,5 +301,5 @@
         psFree(in->image);
         in->image = NULL;
-        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 0);
         ok(!pmMaskFlagSuspectPixels(out, NULL, 1.0, 1, 1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with NULL input pmReadout->image");
         psFree(rng);
@@ -319,5 +319,5 @@
         psFree(in->image);
         in->image = psImageAlloc(0, 0, PS_TYPE_F32);
-        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 0);
         ok(!pmMaskFlagSuspectPixels(out, NULL, 1.0, 1, 1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with empty input pmReadout->image");
         psFree(rng);
@@ -337,5 +337,5 @@
         psFree(in->image);
         in->image = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_S32);
-        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 0);
         ok(!pmMaskFlagSuspectPixels(out, NULL, 1.0, 1, 1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with incorrect type for pmReadout->image");
         psFree(rng);
@@ -355,5 +355,5 @@
         psFree(in->mask);
         in->mask = psImageAlloc(0, 0, PS_TYPE_MASK);
-        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 0);
         ok(!pmMaskFlagSuspectPixels(out, NULL, 1.0, 1, 1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with incorrect type for pmReadout->image");
         psFree(rng);
@@ -373,5 +373,5 @@
         psFree(in->mask);
         in->mask = psImageAlloc(TEST_NUM_COLS/2, TEST_NUM_ROWS/2, PS_TYPE_MASK);
-        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 0);
         ok(!pmMaskFlagSuspectPixels(out, NULL, 1.0, 1, 1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with bad size for in->mask");
         psFree(rng);
@@ -391,5 +391,5 @@
         psFree(in->mask);
         in->mask = psImageAlloc(TEST_NUM_COLS, TEST_NUM_ROWS, PS_TYPE_S32);
-        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 0);
         ok(!pmMaskFlagSuspectPixels(out, NULL, 1.0, 1, 1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with bad type for in->mask");
         psFree(rng);
@@ -407,5 +407,5 @@
         pmCell *cell = generateSimpleCell(NULL);
         pmReadout *in = generateSimpleReadout(cell);
-        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 0);
         ok(!pmMaskFlagSuspectPixels(out, NULL, 1.0, 1, 1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with empty out image");
         psFree(rng);
@@ -423,5 +423,5 @@
         pmCell *cell = generateSimpleCell(NULL);
         pmReadout *in = generateSimpleReadout(cell);
-        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 0);
         ok(!pmMaskFlagSuspectPixels(out, NULL, 1.0, 1, 1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with incorrect type for out image");
         psFree(rng);
@@ -439,5 +439,5 @@
         pmCell *cell = generateSimpleCell(NULL);
         pmReadout *in = generateSimpleReadout(cell);
-        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 0);
         ok(!pmMaskFlagSuspectPixels(out, NULL, 1.0, 1, 1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with incorrect size for out image");
         psFree(rng);
@@ -455,5 +455,5 @@
         pmCell *cell = generateSimpleCell(NULL);
         pmReadout *in = generateSimpleReadout(cell);
-        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 0);
 
         ok(!pmMaskFlagSuspectPixels(out, in, 0.0, 1, 1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with rej input <= 0.0");
@@ -472,5 +472,5 @@
         pmCell *cell = generateSimpleCell(NULL);
         pmReadout *in = generateSimpleReadout(cell);
-        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 0);
+        psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 0);
 
         ok(!pmMaskFlagSuspectPixels(out, in, 1.0, 1, -1.0, rng), "pmMaskFlagSuspectPixels() returned NULL with frac input < 0.0");
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/test/imcombine/tap_pmImageCombine.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/test/imcombine/tap_pmImageCombine.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/test/imcombine/tap_pmImageCombine.c	(revision 23351)
@@ -9,5 +9,5 @@
 psArray *generate_images(void)
 {
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 12345);   // Random Number Generator
+    psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 12345);   // Random Number Generator
 
     // Generate images
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/test/objects/tap_pmFringe.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/test/objects/tap_pmFringe.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/test/objects/tap_pmFringe.c	(revision 23351)
@@ -89,5 +89,5 @@
         ok(fringe != NULL, "pmFringeRegionsAlloc() returned non-NULL");
         psImage *img = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
-        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS, 10);
+        psRandom *rng = psRandomAllocSpecific(PS_RANDOM_TAUS, 10);
         fringe->x = psVectorAlloc(NUM_FRINGE_PNTS/2, PS_TYPE_F32);
         fringe->y = psVectorAlloc(NUM_FRINGE_PNTS/2, PS_TYPE_F32);
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/test/objects/tap_pmSourceFitModel.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/test/objects/tap_pmSourceFitModel.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/test/objects/tap_pmSourceFitModel.c	(revision 23351)
@@ -27,5 +27,5 @@
 
     // build a gauss-deviate vector (mean = 0.0, sigma = 1.0)
-    psRandom *seed = psRandomAlloc (PS_RANDOM_TAUS, 0);
+    psRandom *seed = psRandomAllocSpecific (PS_RANDOM_TAUS, 0);
 
     static float radius[] = {3.0, 5.0, 7.0, 10.0, 15.0, 25.0};
Index: /branches/cnb_branches/cnb_branch_20090301/psModules/test/objects/tap_pmSourceFitModel_Delta.c
===================================================================
--- /branches/cnb_branches/cnb_branch_20090301/psModules/test/objects/tap_pmSourceFitModel_Delta.c	(revision 23350)
+++ /branches/cnb_branches/cnb_branch_20090301/psModules/test/objects/tap_pmSourceFitModel_Delta.c	(revision 23351)
@@ -23,5 +23,5 @@
 
     // build a gauss-deviate vector (mean = 0.0, sigma = 1.0)
-    psRandom *seed = psRandomAlloc (PS_RANDOM_TAUS, 0);
+    psRandom *seed = psRandomAllocSpecific (PS_RANDOM_TAUS, 0);
 
     // noise vector to noise up the image
@@ -167,5 +167,5 @@
 
     // build a gauss-deviate vector (mean = 0.0, sigma = 1.0)
-    psRandom *seed = psRandomAlloc (PS_RANDOM_TAUS, 0);
+    psRandom *seed = psRandomAllocSpecific (PS_RANDOM_TAUS, 0);
 
     static float radius[] = {3.0, 5.0, 7.0, 10.0, 15.0, 25.0};
