Index: trunk/ppImage/src/Makefile.am
===================================================================
--- trunk/ppImage/src/Makefile.am	(revision 27989)
+++ trunk/ppImage/src/Makefile.am	(revision 28043)
@@ -44,4 +44,5 @@
 	ppImageRebinReadout.c \
 	ppImageMosaic.c \
+	ppImageMaskStats.c \
 	ppImagePhotom.c \
 	ppImageAstrom.c \
Index: trunk/ppImage/src/ppImage.h
===================================================================
--- trunk/ppImage/src/ppImage.h	(revision 27989)
+++ trunk/ppImage/src/ppImage.h	(revision 28043)
@@ -49,4 +49,6 @@
     bool applyParity;                   // Apply Cell parities
 
+  bool doMaskStats;                      // Calculate mask statistics
+  
     bool doCrosstalkMeasure;            // measure crosstalk signal
     bool doCrosstalkCorrect;            // apply crosstalk correction
@@ -110,4 +112,10 @@
 
     char *normClass;                    // class to use for per-class normalization
+
+  psU16 maskstat_static;
+  psU16 maskstat_dynamic;
+  psU16 maskstat_magic;
+  psU16 maskstat_advisory;
+  
 } ppImageOptions;
 
@@ -268,4 +276,7 @@
     );
 
+// Calculate Mask statistics
+bool ppImageMaskStats(pmConfig *config, pmFPAview *view, psMetadata *stats);
+
 // calculate stats from headers and concepts
 bool ppImageMetadataStats(pmConfig *config, // Configuration
Index: trunk/ppImage/src/ppImageLoop.c
===================================================================
--- trunk/ppImage/src/ppImageLoop.c	(revision 27989)
+++ trunk/ppImage/src/ppImageLoop.c	(revision 28043)
@@ -196,4 +196,11 @@
         }
 
+	if (options->doMaskStats) {
+	  //if (!ppImageMaskStats(config, view, options)) {
+	  if (!ppImageMaskStats(config, view, stats)) {
+	    ESCAPE("Unable to do Mask stats");
+	  }
+	}
+	
         // these may be used by ppImageSubtractBackground.
         // if these are defined as internal files, drop them here
@@ -252,5 +259,5 @@
         ppImageFileCheck(config);
     }
-
+    
     // Calculate summary statistics from FPA Metadata
     if (!ppImageMetadataStats(config, stats, options)) {
Index: trunk/ppImage/src/ppImageMaskStats.c
===================================================================
--- trunk/ppImage/src/ppImageMaskStats.c	(revision 28043)
+++ trunk/ppImage/src/ppImageMaskStats.c	(revision 28043)
@@ -0,0 +1,87 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+
+bool ppImageMaskStats(pmConfig *config, pmFPAview *view, psMetadata *stats) {
+  PS_ASSERT_PTR_NON_NULL(view, false);
+  PS_ASSERT_PTR_NON_NULL(config, false);
+
+  bool status;
+
+  pmFPAfile *input = psMetadataLookupPtr(&status, config->files, "PPIMAGE.CHIP");
+  if (!status) {
+    psError(PS_ERR_UNEXPECTED_NULL, true, "PPIMAGE.CHIP file is not defined");
+    return(false);
+  }
+
+  psMetadata *recipe = psMetadataLookupMetadata(&status, config->recipes, "PPIMAGE");
+  
+  psU16 staticMaskVal = psMetadataLookupU32(&status, recipe, "MASKSTAT.STATIC");
+  psU16 magicMaskVal = psMetadataLookupU32(&status, recipe, "MASKSTAT.MAGIC");
+  psU16 dynamicMaskVal = psMetadataLookupU32(&status, recipe, "MASKSTAT.DYNAMIC");
+  psU16 advisoryMaskVal = psMetadataLookupU32(&status, recipe, "MASKSTAT.ADVISORY");
+
+  psS32 Npix_valid = 0;
+  psS32 Npix_static = 0;
+  psS32 Npix_magic = 0;
+  psS32 Npix_dynamic = 0;
+  psS32 Npix_advisory = 0;
+
+  pmChip *chip = pmFPAviewThisChip(view, input->fpa);  // Chip of interest
+  if (!chip) {
+    psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find chip");
+    return(false);
+  }
+  if (chip->cells->n == 0) {
+    psWarning("Chip has no cells");
+    return(true);
+  }
+  if (chip->cells->n > 1) {
+    psWarning("Chip has %ld cells; only the first will be processed", chip->cells->n);
+  }
+  pmCell *cell = chip->cells->data[0]; // Cell of interest
+  if (!cell || !cell->process || !cell->file_exists) {
+    // Nothing to process
+    return(true);
+  }
+  if (cell->readouts->n == 0) {
+    psWarning("Cell has no readouts");
+    return(true);
+  }
+  if (cell->readouts->n > 1) {
+    psWarning("Cell has %ld readouts; only the first will be processed", cell->readouts->n);
+  }
+  pmReadout *readout = cell->readouts->data[0]; // Readout of interest;
+  if (!readout || !readout->data_exists) {
+    // Nothing to process
+    return(true);
+  }
+  
+  psImage *mask = readout->mask;  // Mask of interest;
+  psWarning("In ppImageMaskStats: %d %ld\n",Npix_valid, (long) mask);
+
+  if (!pmSingleImageMaskStats(mask,&Npix_valid,&Npix_static,&Npix_magic,
+			      &Npix_dynamic,&Npix_advisory,
+			      staticMaskVal,magicMaskVal,
+			      dynamicMaskVal,advisoryMaskVal)) {
+    psError(PS_ERR_UNKNOWN, false, "Unable to calculate masks for readout.");
+    return(false);
+  }
+  psMetadataAddS32(stats, PS_LIST_TAIL,"MASKFRAC_NPIX", 0,
+		   "Number of valid pixels", Npix_valid);
+  psMetadataAddF32(stats,PS_LIST_TAIL, "MASKFRAC_STATIC", 0,
+		   "Fraction of pixels statically masked", (float) Npix_static / Npix_valid);
+  psMetadataAddF32(stats,PS_LIST_TAIL, "MASKFRAC_DYNAMIC", 0,
+		   "Fraction of pixels dynamically masked", (float) Npix_dynamic / Npix_valid);
+  psMetadataAddF32(stats,PS_LIST_TAIL, "MASKFRAC_MAGIC", 0,
+		   "Fraction of pixels magically masked", (float) Npix_magic / Npix_valid);
+  psMetadataAddF32(stats,PS_LIST_TAIL, "MASKFRAC_ADVISORY", 0,
+		   "Fraction of pixels masked as an advisory", (float) Npix_advisory / Npix_valid);
+  psWarning("In ppImageMaskStats: %d %f %f %f %f\n",Npix_valid, (float) Npix_static / Npix_valid,
+	    (float) Npix_dynamic / Npix_valid, (float) Npix_magic / Npix_valid,
+	    (float) Npix_advisory / Npix_valid);
+  return(true);
+}
Index: trunk/ppImage/src/ppImageOptions.c
===================================================================
--- trunk/ppImage/src/ppImageOptions.c	(revision 27989)
+++ trunk/ppImage/src/ppImageOptions.c	(revision 28043)
@@ -41,5 +41,6 @@
     options->checkCTE        = false;   // Measure pixel-based variance
     options->applyParity     = false;   // Apply Cell parities
-
+    options->doMaskStats     = false;   // Calculate mask fractions
+    
     // output files requested
     options->BaseFITS        = false;   // create output image
@@ -243,4 +244,5 @@
     options->doPatternRow = psMetadataLookupBool(NULL, recipe, "PATTERN.ROW");
     options->doPatternCell = psMetadataLookupBool(NULL, recipe, "PATTERN.CELL");
+    options->doMaskStats = psMetadataLookupBool(NULL, recipe, "MASK.STATS");
 
     options->doStats = false;
@@ -341,4 +343,10 @@
     options->normClass = psMetadataLookupStr(NULL, recipe, "NORM.CLASS");
 
+    options->maskstat_static   = psMetadataLookupU16(NULL, recipe, "MASKSTAT.STATIC");
+    options->maskstat_dynamic  = psMetadataLookupU16(NULL, recipe, "MASKSTAT.DYNAMIC");
+    options->maskstat_magic    = psMetadataLookupU16(NULL, recipe, "MASKSTAT.MAGIC");
+    options->maskstat_advisory = psMetadataLookupU16(NULL, recipe, "MASKSTAT.ADVISORY");
+
+    
     return options;
 }
Index: trunk/ppImage/src/ppImageVersion.c
===================================================================
--- trunk/ppImage/src/ppImageVersion.c	(revision 27989)
+++ trunk/ppImage/src/ppImageVersion.c	(revision 28043)
@@ -68,4 +68,6 @@
     psString source  = ppImageSource();  // ppImage software source
 
+    psMetadataAddStr(header, PS_LIST_TAIL, "IMAGE_V", PS_META_REPLACE, NULL, PPIMAGE_VERSION);
+    
     psStringPrepend(&version, "ppImage version: ");
     psStringPrepend(&source, "ppImage source: ");
