Index: /trunk/ppImage/src/Makefile.am
===================================================================
--- /trunk/ppImage/src/Makefile.am	(revision 18555)
+++ /trunk/ppImage/src/Makefile.am	(revision 18556)
@@ -29,4 +29,5 @@
 	ppImageReplaceBackground.c \
 	ppImageDefineFile.c \
+	ppImageSetMaskBits.c \
 	ppImageFileCheck.c \
 	ppImageVersion.c \
@@ -58,4 +59,5 @@
 	ppImageReplaceBackground.c \
 	ppImageDefineFile.c \
+	ppImageSetMaskBits.c \
 	ppImageFileCheck.c \
 	ppImageVersion.c \
Index: /trunk/ppImage/src/ppImage.h
===================================================================
--- /trunk/ppImage/src/ppImage.h	(revision 18555)
+++ /trunk/ppImage/src/ppImage.h	(revision 18556)
@@ -56,4 +56,5 @@
     // make values for abstract concepts of masking
     psMaskType maskValue;               // apply this bit-mask to choose masked bits
+    psMaskType markValue;               // apply this bit-mask to choose masked bits
     psMaskType satMask;                 // Mask value to give saturated pixels
     psMaskType badMask;                 // Mask value to give bad pixels
@@ -101,4 +102,5 @@
 // Determine what type of camera, and initialise
 ppImageOptions *ppImageParseCamera(pmConfig *config);
+bool ppImageSetMaskBits (pmConfig *config, ppImageOptions *options);
 
 // Loop over the input
@@ -134,4 +136,6 @@
 bool ppImageMosaicFPA (pmConfig *config, const ppImageOptions *options,
                        const char *outFile, const char *inFile);
+
+bool ppImageSetMaskBits (pmConfig *config, ppImageOptions *options);
 
 void ppImageFileCheck (pmConfig *config);
Index: /trunk/ppImage/src/ppImageDetrendReadout.c
===================================================================
--- /trunk/ppImage/src/ppImageDetrendReadout.c	(revision 18555)
+++ /trunk/ppImage/src/ppImageDetrendReadout.c	(revision 18556)
@@ -74,5 +74,5 @@
     if (options->doShutter) {
         pmReadout *shutter = pmFPAfileThisReadout(config->files, detview, "PPIMAGE.SHUTTER");
-        if (!pmShutterCorrectionApply(input, shutter, pmConfigMask("FLAT", config))) {
+        if (!pmShutterCorrectionApply(input, shutter, pmConfigMaskGet("FLAT", config))) {
             return false;
         }
Index: /trunk/ppImage/src/ppImageLoop.c
===================================================================
--- /trunk/ppImage/src/ppImageLoop.c	(revision 18555)
+++ /trunk/ppImage/src/ppImageLoop.c	(revision 18556)
@@ -62,4 +62,9 @@
                     continue;
                 }
+
+		// XXX set the options->*Mask values here (after the mask images have been loaded
+		// and before any of the value are used)
+		if (!ppImageSetMaskBits (config, options)) 
+		    ESCAPE ("Unable to set bit masks");
 
                 // perform the detrend analysis
Index: /trunk/ppImage/src/ppImageMetadataStats.c
===================================================================
--- /trunk/ppImage/src/ppImageMetadataStats.c	(revision 18555)
+++ /trunk/ppImage/src/ppImageMetadataStats.c	(revision 18556)
@@ -13,6 +13,4 @@
 	return true;
     }
-
-    psMaskType maskVal = options->satMask | options->badMask | options->maskValue;
 
     // Extract statistics from the last output fpa
@@ -50,5 +48,5 @@
     pmFPAview *view = pmFPAviewAlloc(0);
 
-    if (!ppStatsMetadata(stats, output->fpa, view, maskVal, config)) {
+    if (!ppStatsMetadata(stats, output->fpa, view, options->maskValue, config)) {
 	psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate stats for image.");
 	psFree(view);
Index: /trunk/ppImage/src/ppImageMosaic.c
===================================================================
--- /trunk/ppImage/src/ppImageMosaic.c	(revision 18555)
+++ /trunk/ppImage/src/ppImageMosaic.c	(revision 18556)
@@ -32,6 +32,6 @@
             in->name, out->name, in->xBin, in->yBin, out->xBin, out->yBin);
 
-    // XXX mosaic the chip, making a deep copy.  this has the side effect of making the
-    // output image products pure trimmed images, but also increases the memory footprint.
+    // Mosaic the chip, making a deep copy.  This has the side effect of making the output
+    // image products pure trimmed images, but also increases the memory footprint.
     status = pmChipMosaic(outChip, inChip, true, options->blankMask);
     return status;
Index: /trunk/ppImage/src/ppImageOptions.c
===================================================================
--- /trunk/ppImage/src/ppImageOptions.c	(revision 18555)
+++ /trunk/ppImage/src/ppImageOptions.c	(revision 18556)
@@ -50,9 +50,10 @@
 
     // default flags for various activities
-    options->maskValue       = 0x00;    // Default mask value
-    options->satMask         = 0x00;    // Saturated pixels
-    options->badMask         = 0x00;    // Bad pixels
-    options->flatMask        = 0x00;    // Bad flat pixels
-    options->blankMask       = 0x00;    // Blank (no data, cell gap) pixels
+    options->maskValue       = 0x00;    // Default mask value (used to skip / ignore pixels)
+    options->satMask         = 0x00;    // Saturated pixels (supplied to pmReadoutGenerateMask)
+    options->badMask         = 0x00;    // Bad (low) pixels (supplied to pmReadoutGenerateMask)
+    options->flatMask        = 0x00;    // Bad flat pixels (supplied to pmFlatField)
+    options->blankMask       = 0x00;    // Blank (no data, cell gap) pixels (supplied to pmChipMosaic, pmFPAMosaic)
+    options->markValue       = 0x00;    // A safe bit for internal marking
 
     // Non-linearity default options
@@ -190,15 +191,6 @@
     options->doWeightBuild = psMetadataLookupBool(NULL, recipe, "WEIGHT.BUILD");
 
-    // Mask recipe options
+    // Mask recipe options (note that mask bit values are set in ppImageSetMaskBits.c)
     options->doMask = psMetadataLookupBool(NULL, recipe, "MASK");
-    const char *masks = psMetadataLookupStr(&status, recipe, "MASK.VALUE");
-    if (status) {
-        options->maskValue = pmConfigMask(masks, config);
-    }
-    options->satMask = pmConfigMask("SAT", config);
-    options->badMask = pmConfigMask("BAD", config);
-    options->flatMask = pmConfigMask("FLAT", config);
-    options->blankMask = pmConfigMask("BLANK", config);
-    // XXX should it be an error for these to not exist?
 
     options->doBias = psMetadataLookupBool(NULL, recipe, "BIAS");
Index: /trunk/ppImage/src/ppImageParseCamera.c
===================================================================
--- /trunk/ppImage/src/ppImageParseCamera.c	(revision 18555)
+++ /trunk/ppImage/src/ppImageParseCamera.c	(revision 18556)
@@ -20,5 +20,5 @@
     }
 
-    // if MASK or WEIGHT was supplied on command line, bind files to 'input'
+    // if MASK or WEIGHT was supplied on command line, bind files to 'input'.
     // the mask and weight will be mosaicked with the image
     pmFPAfile *inputMask = pmFPAfileBindFromArgs(&status, input, config, "PPIMAGE.INPUT.MASK", "PPIMAGE.INPUT.MASK");
@@ -27,7 +27,9 @@
         return NULL;
     }
-    if (inputMask && inputMask->type != PM_FPA_FILE_MASK) {
+    if (inputMask) {
+      if (inputMask->type != PM_FPA_FILE_MASK) {
         psError(PS_ERR_IO, true, "PPIMAGE.INPUT.MASK is not of type MASK");
         return NULL;
+      }
     }
 
@@ -66,4 +68,5 @@
     }
     if (options->doMask) {
+
         if (!ppImageDefineFile (config, input->fpa, "PPIMAGE.MASK", "MASK", PM_FPA_FILE_MASK, PM_DETREND_TYPE_MASK)) {
             psError (PS_ERR_IO, false, "Can't find a mask image source");
@@ -71,5 +74,25 @@
             return NULL;
         }
-	// Need to read the names of bit masks from the mask header and set them in the recipe
+	// XXX have ppImageDefineFile return the pmFPAfile?
+	pmFPAfile *mask = psMetadataLookupPtr(&status, config->files, "PPIMAGE.MASK");
+	psAssert (mask, "mask not defined?  not possible!");
+
+	// Need to read the names of bit masks from the mask header and set them in the
+	// recipe.  If we are loading this from the detrend db, this action will happen
+	// when the file is resolved.
+	if (!mask->detrend) {
+	    // XXX need to load the mask bit names from one of the headers
+	    // this grabs the first available hdu : no guarantee that it will be valid, though
+	    pmHDU *hdu = pmHDUGetFirst (mask->fpa);
+	    if (!hdu) {
+		psError(PS_ERR_IO, true, "no valid HDU for PPIMAGE.INPUT.MASK");
+		return NULL;
+	    }
+	    // XXX is this consistent with the pmConfigMaskReadHeader call above?
+	    if (!pmConfigMaskReadHeader (config, hdu->header)) {
+		psError(PS_ERR_IO, false, "error in mask bits");
+		return NULL;
+	    }
+	}
     }
     if (options->doShutter) {
Index: /trunk/ppImage/src/ppImagePixelStats.c
===================================================================
--- /trunk/ppImage/src/ppImagePixelStats.c	(revision 18555)
+++ /trunk/ppImage/src/ppImagePixelStats.c	(revision 18556)
@@ -9,6 +9,4 @@
 
     bool mdok;              // Status of MD lookup
-
-    psMaskType maskVal = options->satMask | options->badMask | options->maskValue;
 
     // loop over the cells/readouts for this chip
@@ -48,5 +46,5 @@
 	}
 
-	if (!ppStatsPixels(stats, output->fpa, view, maskVal, config)) {
+	if (!ppStatsPixels(stats, output->fpa, view, options->maskValue, config)) {
 	    psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate stats for image.");
 	    psFree (view);
Index: /trunk/ppImage/src/ppImageReplaceBackground.c
===================================================================
--- /trunk/ppImage/src/ppImageReplaceBackground.c	(revision 18555)
+++ /trunk/ppImage/src/ppImageReplaceBackground.c	(revision 18556)
@@ -40,6 +40,7 @@
     psAssert (replaceSkyMode != MODE_NONE, "replace sky mode not defined");
 
-    // define the mask value to be used in ppImage
-    psMaskType maskVal  = options->satMask | options->badMask | options->flatMask | options->blankMask;
+    // XXX Should this be options->maskValue or & ~options->satMask? the latter will leave saturated pixels high
+    // psMaskType maskVal  = options->maskValue & ~options->satMask;
+    psMaskType maskVal  = options->maskValue;
 
     pmFPAfile *modelFile = NULL;
Index: /trunk/ppImage/src/ppImageSetMaskBits.c
===================================================================
--- /trunk/ppImage/src/ppImageSetMaskBits.c	(revision 18556)
+++ /trunk/ppImage/src/ppImageSetMaskBits.c	(revision 18556)
@@ -0,0 +1,44 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+bool ppImageSetMaskBits (pmConfig *config, ppImageOptions *options) {
+
+    if (!pmConfigMaskSetBits (&options->maskValue, &options->markValue, config)) {
+	psError (PS_ERR_UNKNOWN, true, "Unable to define the mask bit values");
+	return false;
+    }
+
+    // at this point we know we have valid values for required entries SAT, BAD, FLAT, BLANK:
+
+    // mask for non-linear flat corrections
+    options->flatMask = pmConfigMaskGet("FLAT", config); 
+    psAssert (options->flatMask, "flat mask not set");
+
+    // mask for non-existent data  (default to DETECTOR if not defined)
+    options->blankMask = pmConfigMaskGet("BLANK", config); 
+    psAssert (options->blankMask, "blank mask not set");
+
+    // mask for saturated data  (default to RANGE if not defined)
+    options->satMask = pmConfigMaskGet("SAT", config);
+    psAssert (options->satMask, "sat mask not set");
+    
+    // mask for below-range data  (default to RANGE if not defined)
+    options->badMask = pmConfigMaskGet("BAD", config);
+    psAssert (options->badMask, "bad mask not set");
+
+    // save MASK and MARK on the PSPHOT recipe
+    psMetadata *recipe = psMetadataLookupPtr (NULL, config->recipes, PSPHOT_RECIPE);
+    if (!recipe) {
+        psError(PSPHOT_ERR_CONFIG, false, "missing recipe %s", PSPHOT_RECIPE);
+        return false;
+    }
+
+    // set maskValue and markValue in the psphot recipe
+    psMetadataAddU8 (recipe, PS_LIST_TAIL, "MASK.PSPHOT", PS_META_REPLACE, "user-defined mask", options->maskValue);
+    psMetadataAddU8 (recipe, PS_LIST_TAIL, "MARK.PSPHOT", PS_META_REPLACE, "user-defined mask", options->markValue);
+
+    return true;
+}
Index: /trunk/ppImage/src/ppImageStats.c
===================================================================
--- /trunk/ppImage/src/ppImageStats.c	(revision 18555)
+++ /trunk/ppImage/src/ppImageStats.c	(revision 18556)
@@ -57,7 +57,5 @@
 	    }
 
-	    if (!ppStatsFPA(stats, output->fpa, view,
-			 options->satMask | options->badMask | options->maskValue,
-			 config)) {
+	    if (!ppStatsFPA(stats, output->fpa, view, options->maskValue, config)) {
 		psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate stats for image.");
 		psFree(view);
