Index: trunk/ppImage/src/ppImageAuxiliaryMask.c
===================================================================
--- trunk/ppImage/src/ppImageAuxiliaryMask.c	(revision 35528)
+++ trunk/ppImage/src/ppImageAuxiliaryMask.c	(revision 35528)
@@ -0,0 +1,188 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppImage.h"
+
+psImage *readAuxiliaryMask(pmConfig *config, psString fileName)
+{
+    psString realName = pmConfigConvertFilename(fileName, config, false, false);
+    if (!realName) {
+        psError (psErrorCodeLast(), false, "unable to resolve %s", fileName);
+        return NULL;
+    }
+
+    psFits *fits = psFitsOpen(realName, "r");
+    if (!fits) {
+        psError (psErrorCodeLast(), false, "psFitsOpen failed for %s", realName);
+        psFree(realName);
+        return NULL;
+    }
+    psMetadata *header = psFitsReadHeader(NULL, fits);
+    if (!header) {
+        psFree(fits);
+        psError (psErrorCodeLast(), false, "psFitsReadHeader failed for %s", realName);
+        psFree(realName);
+        return NULL;
+    }
+    psRegion region = {0, 0, 0, 0};
+    psImage *image = psFitsReadImage(fits, region, 0);
+    psFree(fits);
+    psFree(header);
+    if (!image) {
+        psError (psErrorCodeLast(), false, "psFitsReadImage failed for %s", realName);
+        psFree(realName);
+        return NULL;
+    }
+    psFree(realName);
+
+    return image;
+}
+
+// In this function, we augment the mask with the more conservative auxiliary mask
+bool ppImageAuxiliaryMask(pmConfig *config, const pmFPAview *view, const ppImageOptions *options)
+{
+    psAssert(config, "Need configuration");
+    psAssert(view, "Need view to chip");
+    psAssert(options, "Need options");
+
+    if (!options->doAuxMask) {
+        return true;
+    }
+
+    bool status;                        // Status of MD lookup
+    pmFPAfile *input = psMetadataLookupPtr(&status, config->files, "PPIMAGE.CHIP"); // File to correct
+    if (!status) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "PPIMAGE.CHIP file is not defined");
+        return false;
+    }
+
+    psTime *time = psMetadataLookupPtr(NULL, input->fpa->concepts, "FPA.TIME");
+    if (time->sec == 0 && time->nsec == 0) {
+        psLogMsg ("psModules.camera", PS_LOG_WARN, "FPA.TIME has not been set.\n");
+    }
+
+    // XXX careful about this: is this set correctly in the camera.config files?
+    char *cameraName = psMetadataLookupStr(NULL, input->fpa->concepts, "FPA.CAMERA");
+    pmDetrendSelectOptions *detrendOptions = pmDetrendSelectOptionsAlloc(cameraName, *time, PM_DETREND_TYPE_AUXMASK);
+
+    pmDetrendSelectResults *results = pmDetrendSelect(detrendOptions, config);
+    psFree(detrendOptions);
+    if (!results) {
+        psError (psErrorCodeLast(), false, "no matching auxiliary mask found");
+        return false;
+    }
+
+    pmFPALevel fileLevel = pmFPALevelFromName(results->level);
+    if (fileLevel != PM_FPA_LEVEL_CHIP) {
+         psError (PM_ERR_CONFIG, false, "invalid file level %d for selected auxiliary mask", fileLevel);
+         return false;
+    }
+    
+#ifdef notdef
+    psMetadata *ppImageRecipe = psMetadataLookupPtr(NULL, config->recipes, RECIPE_NAME);
+    psAssert(ppImageRecipe, "Need PPIMAGE recipe");
+    psMetadata *psphotRecipe = psMetadataLookupPtr(NULL, config->recipes, PSPHOT_RECIPE);
+    psAssert(psphotRecipe, "Need PSPHOT recipe");
+
+    // XXX Should this be options->maskValue or options->maskValue & ~options->satMask?
+    //     The latter will leave saturated pixels high
+    psImageMaskType maskVal = options->maskValue;
+
+    // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
+    psMetadataAddImageMask(psphotRecipe, PS_LIST_TAIL, "MASK.PSPHOT", PS_META_REPLACE, "user-defined mask", maskVal);
+#endif
+
+    // XXX: shouldn't most of these psWarnings be psAsserts?
+
+    // Since we are working on a chip-mosaicked image, there should only be a single cell and readout
+    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 *ro = cell->readouts->data[0]; // Readout of interest
+    if (!ro || !ro->data_exists) {
+        // Nothing to process
+        return true;
+    }
+    // not needed psImage *image = ro->image;
+    psImage *mask = ro->mask;
+
+    // now read the mask file
+    psString class_id = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME");
+
+    char *auxMaskFileName = pmDetrendFile(results->detID, class_id, config);
+    if (!auxMaskFileName) {
+         psError (PM_ERR_CONFIG, false, "unable to find auxiliary mask file name for %s %s", results->detID, class_id);
+         return false;
+    }
+
+    psFree(results);
+
+    psImage *auxMask = readAuxiliaryMask(config, auxMaskFileName);
+    psFree(auxMaskFileName);
+    if (!auxMask) {
+        // readAuxiliaryMask prints enough diagnostic information
+        return false;
+    }
+
+    // if the cell has video and the recipe value
+    if (options->hasVideo && options->auxVideoMask && strcmp(options->auxVideoMask, "NULL")) {
+        psImage *videoMask = readAuxiliaryMask(config, options->auxVideoMask);
+        if (!videoMask) {
+            return false;
+        }
+        // auxMask *= videoMask
+        if (!psBinaryOp(auxMask, auxMask, "*", videoMask)) {
+            psError(PS_ERR_UNKNOWN, false, "mulitplication of auxiliary mask and auxiliary video mask failed");
+            return false;
+        }
+        psFree(videoMask);
+    }
+
+    if ((mask->numRows != auxMask->numRows) || (mask->numCols != auxMask->numCols) ||
+        (mask->row0 != auxMask->row0) || (mask->col0 != auxMask->col0)) {
+        psError(PS_ERR_IO, false, "structure of auxiliary mask does not match this chip");
+        return false;
+    }
+
+    psImageMaskType maskDetector = pmConfigMaskGet("DETECTOR", config);
+
+    int numCols = mask->numCols, numRows = mask->numRows; // Size of image
+    for (int y = 0; y < numRows; y++) {
+        for (int x = 0; x < numCols; x++) {
+            // auxMask is floating point type image with pixel value 0 meaning mask it
+            // XXX: support other types and get the mask value from the header
+            if (auxMask->data.F32[y][x] == 0) {
+                mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] |= maskDetector;
+                // I don't need to do this.
+                // The background subtraction code runs after us and will do it there
+                // image->data.F32[y][x] = 0.0;
+	    }
+        }
+    }
+
+    psFree(auxMask);
+
+    return true;
+}
