Index: /trunk/psModules/src/camera/pmFPARead.c
===================================================================
--- /trunk/psModules/src/camera/pmFPARead.c	(revision 10080)
+++ /trunk/psModules/src/camera/pmFPARead.c	(revision 10081)
@@ -16,4 +16,15 @@
 
 #include "pmFPARead.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Definitions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Specify what to read
+typedef enum {
+    FPA_READ_TYPE_IMAGE,                // Read image
+    FPA_READ_TYPE_MASK,                 // Read mask
+    FPA_READ_TYPE_WEIGHT                // Read weight map
+} fpaReadType;
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -155,6 +166,177 @@
 }
 
+
+// Read into an cell; this is the engine for pmCellRead, pmCellReadMask, pmCellReadWeight
+// Does most of the work for the reading --- reads the HDU, and portions the HDU into readouts.
+static bool cellRead(pmCell *cell,      // Cell into which to read
+                     psFits *fits,      // FITS file from which to read
+                     psDB *db,          // Database handle, for concepts ingest
+                     fpaReadType type   // Type to read
+                    )
+{
+    assert(cell);
+    assert(fits);
+
+    pmHDU *hdu = pmHDUFromCell(cell);   // The HDU
+    if (!hdu) {
+        return true;                    // We read everything we could
+    }
+
+    bool (*hduReadFunc)(pmHDU*, psFits*); // Function to use to read the HDU
+    psArray **imageArray;               // Array of images in the HDU
+    psElemType imageType;               // Expected type for image
+    switch (type) {
+    case FPA_READ_TYPE_IMAGE:
+        hduReadFunc = pmHDURead;
+        imageArray = &hdu->images;
+        imageType = PS_TYPE_F32;
+        break;
+    case FPA_READ_TYPE_MASK:
+        hduReadFunc = pmHDUReadMask;
+        imageArray = &hdu->masks;
+        imageType = PS_TYPE_MASK;
+        break;
+    case FPA_READ_TYPE_WEIGHT:
+        hduReadFunc = pmHDUReadWeight;
+        imageArray = &hdu->weights;
+        imageType = PS_TYPE_F32;
+        break;
+    default:
+        psAbort(__func__, "Unknown read type: %x\n", type);
+    }
+
+    if (!(*imageArray) && !hduReadFunc(hdu, fits)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read HDU for cell.\n");
+        return false;
+    }
+
+    if (!pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_HEADER, true, NULL)) {
+        //psError(PS_ERR_UNKNOWN, false, "Failed to read concepts for cell");
+        //return false;
+        psWarning("Difficulty reading concepts for cell; attempting to proceed.");
+    }
+
+    // Having read the cell, we now have to cut it up
+    psRegion *trimsec = psMetadataLookupPtr(NULL, cell->concepts, "CELL.TRIMSEC");
+    psList *biassecs = psMetadataLookupPtr(NULL, cell->concepts, "CELL.BIASSEC");
+    if (psRegionIsNaN(*trimsec)) {
+        psError(PS_ERR_IO, false, "CELL.TRIMSEC is not set --- can't read cell.\n");
+        return false;
+    }
+
+    // Iterate over each of the image planes, converting type if necessary, and extracting the bits that
+    // matter (CELL.TRIMSEC, CELL.BIASSEC) into readouts with readoutCarve.
+    for (int i = 0; i < (*imageArray)->n; i++) {
+        psImage *source = (*imageArray)->data[i]; // Source image, from the i-th plane
+
+        // Type conversion here to support the modules, which don't have multiple type support yet
+        if (source->type.type != imageType) {
+            psImage *temp = psImageCopy(NULL, source, imageType); // Temporary image
+            psFree((*imageArray)->data[i]);
+            (*imageArray)->data[i] = temp;
+            source = temp;
+        }
+
+        pmReadout *readout;             // Readout into which to read
+        if (cell->readouts->n > i && cell->readouts->data[i]) {
+            readout = psMemIncrRefCounter(cell->readouts->data[i]);
+        } else {
+            readout = pmReadoutAlloc(cell);
+        }
+
+        psImage **target;               // Place in readout to put carved image
+        switch (type) {
+        case FPA_READ_TYPE_IMAGE:
+            target = &readout->image;
+            break;
+        case FPA_READ_TYPE_MASK:
+            target = &readout->mask;
+            break;
+        case FPA_READ_TYPE_WEIGHT:
+            target = &readout->weight;
+            break;
+        default:
+            psAbort(__func__, "Unknown read type: %x\n", type);
+        }
+
+        if (!readoutCarve(readout, target, source, trimsec, biassecs)) {
+            psError(PS_ERR_UNEXPECTED_NULL, false,
+                    "Unable to carve readout into image and bias sections for %d the plane.", i);
+            return NULL;
+        }
+        psFree(readout);                // Drop reference
+    }
+
+    pmCellSetDataStatus(cell, true);
+    return true;
+}
+
+
+// Read into an chip; this is the engine for pmChipRead, pmChipReadMask, pmChipReadWeight
+// Iterates over component cells, reading each
+static bool chipRead(pmChip *chip,      // Chip into which to read
+                     psFits *fits,      // FITS file from which to read
+                     psDB *db,          // Database handle, for concepts ingest
+                     fpaReadType type   // Type to read
+                    )
+{
+    assert(chip);
+    assert(fits);
+
+    bool success = false;               // Were we able to read at least one HDU?
+    psArray *cells = chip->cells;       // Array of cells
+    for (int i = 0; i < cells->n; i++) {
+        pmCell *cell = cells->data[i];  // The cell of interest
+        success |= cellRead(cell, fits, db, type);
+    }
+    if (success) {
+        if (!pmConceptsReadChip(chip, PM_CONCEPT_SOURCE_HEADER, true, true, NULL)) {
+            psError(PS_ERR_IO, false, "Failed to read concepts for FPA.\n");
+            return false;
+        }
+        // XXX probably could just use chip->data_exists
+        pmChipSetDataStatus(chip, true);
+    }
+
+    return success;
+}
+
+
+// Read into an FPA; this is the engine for pmFPARead, pmFPAReadMask, pmFPAReadWeight
+// Iterates over component chips, reading each
+static bool fpaRead(pmFPA *fpa,         // FPA into which to read
+                    psFits *fits,       // FITS file from which to read
+                    psDB *db,           // Database handle, for concepts ingest
+                    fpaReadType type    // Type to read
+                   )
+{
+    assert(fpa);
+    assert(fits);
+
+    bool success = false;               // Were we able to read at least one HDU?
+    psArray *chips = fpa->chips;        // Array of chips
+    for (int i = 0; i < chips->n; i++) {
+        pmChip *chip = chips->data[i];  // The cell of interest
+        success |= chipRead(chip, fits, db, type);
+    }
+    if (success) {
+        if (!pmConceptsReadFPA(fpa, PM_CONCEPT_SOURCE_HEADER, true, NULL)) {
+            psError(PS_ERR_IO, false, "Failed to read concepts for FPA.\n");
+            return false;
+        }
+    } else {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read any chips in FPA");
+    }
+
+    return success;
+}
+
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Reading images
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
@@ -325,55 +507,5 @@
     PS_ASSERT_PTR_NON_NULL(fits, false);
 
-    pmHDU *hdu = pmHDUFromCell(cell);   // The HDU
-    if (!hdu) {
-        return true;                    // We read everything we could
-    }
-    if (!hdu->images && !pmHDURead(hdu, fits)) {
-        psError(PS_ERR_UNKNOWN, false, "Unable to read HDU for cell.\n");
-        return false;
-    }
-
-    if (!pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_HEADER, true, NULL)) {
-        //psError(PS_ERR_UNKNOWN, false, "Failed to read concepts for cell");
-        //return false;
-        psWarning("Difficulty reading concepts for cell; attempting to proceed.");
-    }
-
-    // Having read the cell, we now have to cut it up
-    psRegion *trimsec = psMetadataLookupPtr(NULL, cell->concepts, "CELL.TRIMSEC");
-    psList *biassecs = psMetadataLookupPtr(NULL, cell->concepts, "CELL.BIASSEC");
-    if (psRegionIsNaN(*trimsec)) {
-        psError(PS_ERR_IO, false, "CELL.TRIMSEC is not set --- can't read cell.\n");
-        return false;
-    }
-
-    // Iterate over each of the image planes
-    for (int i = 0; i < hdu->images->n; i++) {
-        psImage *image = hdu->images->data[i]; // The i-th plane
-
-        // XXX: Type conversion here to support the modules, which don't have multiple type support yet
-        if (image->type.type != PS_TYPE_F32) {
-            psImage *temp = psImageCopy(NULL, image, PS_TYPE_F32); // Temporary image
-            psFree(hdu->images->data[i]);
-            hdu->images->data[i] = temp;
-            image = temp;
-        }
-
-        pmReadout *readout;             // Readout into which to read
-        if (cell->readouts->n > i && cell->readouts->data[i]) {
-            readout = psMemIncrRefCounter(cell->readouts->data[i]);
-        } else {
-            readout = pmReadoutAlloc(cell);
-        }
-        if (!readoutCarve(readout, &readout->image, image, trimsec, biassecs)) {
-            psError(PS_ERR_UNEXPECTED_NULL, false,
-                    "Unable to carve readout into image and bias sections for %d the plane.", i);
-            return NULL;
-        }
-        psFree(readout);                // Drop reference
-    }
-
-    pmCellSetDataStatus(cell, true);
-    return true;
+    return cellRead(cell, fits, db, FPA_READ_TYPE_IMAGE);
 }
 
@@ -383,20 +515,5 @@
     PS_ASSERT_PTR_NON_NULL(fits, false);
 
-    bool success = false;               // Were we able to read at least one HDU?
-    psArray *cells = chip->cells;       // Array of cells
-    for (int i = 0; i < cells->n; i++) {
-        pmCell *cell = cells->data[i];  // The cell of interest
-        success |= pmCellRead(cell, fits, db);
-    }
-    if (success) {
-        if (!pmConceptsReadChip(chip, PM_CONCEPT_SOURCE_HEADER, true, true, NULL)) {
-            psError(PS_ERR_IO, false, "Failed to read concepts for FPA.\n");
-            return false;
-        }
-        // XXX probably could just use chip->data_exists
-        pmChipSetDataStatus(chip, true);
-    }
-
-    return success;
+    return chipRead(chip, fits, db, FPA_READ_TYPE_IMAGE);
 }
 
@@ -406,20 +523,5 @@
     PS_ASSERT_PTR_NON_NULL(fits, false);
 
-    bool success = false;               // Were we able to read at least one HDU?
-    psArray *chips = fpa->chips;        // Array of chips
-    for (int i = 0; i < chips->n; i++) {
-        pmChip *chip = chips->data[i];  // The cell of interest
-        success |= pmChipRead(chip, fits, db);
-    }
-    if (success) {
-        if (!pmConceptsReadFPA(fpa, PM_CONCEPT_SOURCE_HEADER, true, NULL)) {
-            psError(PS_ERR_IO, false, "Failed to read concepts for FPA.\n");
-            return false;
-        }
-    } else {
-        psError(PS_ERR_UNKNOWN, false, "Unable to read any chips in FPA");
-    }
-
-    return success;
+    return fpaRead(fpa, fits, db, FPA_READ_TYPE_IMAGE);
 }
 
@@ -434,48 +536,5 @@
     PS_ASSERT_PTR_NON_NULL(fits, false);
 
-    pmHDU *hdu = pmHDUFromCell(cell);   // The HDU
-    if (!hdu) {
-        return false;                    // Nothing to see here; move along
-    }
-    if (!hdu->images && !pmHDURead(hdu, fits)) {
-        psError(PS_ERR_UNKNOWN, false, "Unable to read HDU for cell.\n");
-        return false;
-    }
-
-    if (!pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_HEADER, true, NULL)) {
-        psError(PS_ERR_UNKNOWN, false, "Failed to read concepts for cell");
-        return false;
-    }
-
-    // Having read the cell, we now have to cut it up
-    psRegion *trimsec = psMetadataLookupPtr(NULL, cell->concepts, "CELL.TRIMSEC");
-
-    // Iterate over each of the image planes
-    for (int i = 0; i < hdu->images->n; i++) {
-        psImage *image = hdu->images->data[i]; // The i-th plane
-
-        if (image->type.type != PS_TYPE_U8) {
-            psImage *temp = psImageCopy(NULL, image, PS_TYPE_U8); // Temporary image
-            psFree(hdu->images->data[i]);
-            hdu->images->data[i] = temp;
-            image = temp;
-        }
-
-        pmReadout *readout;             // Readout into which to read
-        if (cell->readouts->n > i && cell->readouts->data[i]) {
-            readout = psMemIncrRefCounter(cell->readouts->data[i]);
-        } else {
-            readout = pmReadoutAlloc(cell);
-        }
-        if (!readoutCarve(readout, &readout->mask, image, trimsec, NULL)) {
-            psError(PS_ERR_UNEXPECTED_NULL, false,
-                    "Unable to carve readout into image and bias sections for %d the plane.", i);
-            return NULL;
-        }
-        psFree(readout);                // Drop reference
-    }
-
-    pmCellSetDataStatus(cell, true);
-    return true;
+    return cellRead(cell, fits, db, FPA_READ_TYPE_MASK);
 }
 
@@ -485,20 +544,5 @@
     PS_ASSERT_PTR_NON_NULL(fits, false);
 
-    bool success = false;               // Were we able to read at least one HDU?
-    psArray *cells = chip->cells;       // Array of cells
-    for (int i = 0; i < cells->n; i++) {
-        pmCell *cell = cells->data[i];  // The cell of interest
-        success |= pmCellReadMask(cell, fits, db);
-    }
-    if (success) {
-        if (!pmConceptsReadChip(chip, PM_CONCEPT_SOURCE_HEADER, true, true, NULL)) {
-            psError(PS_ERR_IO, false, "Failed to read concepts for FPA.\n");
-            return false;
-        }
-        // XXX probably could just use chip->data_exists
-        pmChipSetDataStatus(chip, true);
-    }
-
-    return success;
+    return chipRead(chip, fits, db, FPA_READ_TYPE_MASK);
 }
 
@@ -508,20 +552,5 @@
     PS_ASSERT_PTR_NON_NULL(fits, false);
 
-    bool success = false;               // Were we able to read at least one HDU?
-    psArray *chips = fpa->chips;        // Array of chips
-    for (int i = 0; i < chips->n; i++) {
-        pmChip *chip = chips->data[i];  // The cell of interest
-        success |= pmChipReadMask(chip, fits, db);
-    }
-    if (success) {
-        if (!pmConceptsReadFPA(fpa, PM_CONCEPT_SOURCE_HEADER, true, NULL)) {
-            psError(PS_ERR_IO, false, "Failed to read concepts for FPA.\n");
-            return false;
-        }
-    } else {
-        psError(PS_ERR_UNKNOWN, false, "Unable to read any chips in FPA");
-    }
-
-    return success;
+    return fpaRead(fpa, fits, db, FPA_READ_TYPE_MASK);
 }
 
@@ -535,48 +564,5 @@
     PS_ASSERT_PTR_NON_NULL(fits, false);
 
-    pmHDU *hdu = pmHDUFromCell(cell);   // The HDU
-    if (!hdu) {
-        return false;                    // Nothing to see here; move along
-    }
-    if (!hdu->images && !pmHDURead(hdu, fits)) {
-        psError(PS_ERR_UNKNOWN, false, "Unable to read HDU for cell.\n");
-        return false;
-    }
-
-    if (!pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_HEADER, true, NULL)) {
-        psError(PS_ERR_UNKNOWN, false, "Failed to read concepts for cell");
-        return false;
-    }
-
-    // Having read the cell, we now have to cut it up
-    psRegion *trimsec = psMetadataLookupPtr(NULL, cell->concepts, "CELL.TRIMSEC");
-
-    // Iterate over each of the image planes
-    for (int i = 0; i < hdu->images->n; i++) {
-        psImage *image = hdu->images->data[i]; // The i-th plane
-
-        if (image->type.type != PS_TYPE_F32) {
-            psImage *temp = psImageCopy(NULL, image, PS_TYPE_F32); // Temporary image
-            psFree(hdu->images->data[i]);
-            hdu->images->data[i] = temp;
-            image = temp;
-        }
-
-        pmReadout *readout;             // Readout into which to read
-        if (cell->readouts->n > i && cell->readouts->data[i]) {
-            readout = psMemIncrRefCounter(cell->readouts->data[i]);
-        } else {
-            readout = pmReadoutAlloc(cell);
-        }
-        if (!readoutCarve(readout, &readout->weight, image, trimsec, NULL)) {
-            psError(PS_ERR_UNEXPECTED_NULL, false,
-                    "Unable to carve readout into image and bias sections for %d the plane.", i);
-            return NULL;
-        }
-        psFree(readout);                // Drop reference
-    }
-
-    pmCellSetDataStatus(cell, true);
-    return true;
+    return cellRead(cell, fits, db, FPA_READ_TYPE_WEIGHT);
 }
 
@@ -586,20 +572,5 @@
     PS_ASSERT_PTR_NON_NULL(fits, false);
 
-    bool success = false;               // Were we able to read at least one HDU?
-    psArray *cells = chip->cells;       // Array of cells
-    for (int i = 0; i < cells->n; i++) {
-        pmCell *cell = cells->data[i];  // The cell of interest
-        success |= pmCellReadWeight(cell, fits, db);
-    }
-    if (success) {
-        if (!pmConceptsReadChip(chip, PM_CONCEPT_SOURCE_HEADER, true, true, NULL)) {
-            psError(PS_ERR_IO, false, "Failed to read concepts for FPA.\n");
-            return false;
-        }
-        // XXX probably could just use chip->data_exists
-        pmChipSetDataStatus(chip, true);
-    }
-
-    return success;
+    return chipRead(chip, fits, db, FPA_READ_TYPE_WEIGHT);
 }
 
@@ -609,22 +580,10 @@
     PS_ASSERT_PTR_NON_NULL(fits, false);
 
-    bool success = false;               // Were we able to read at least one HDU?
-    psArray *chips = fpa->chips;        // Array of chips
-    for (int i = 0; i < chips->n; i++) {
-        pmChip *chip = chips->data[i];  // The cell of interest
-        success |= pmChipReadWeight(chip, fits, db);
-    }
-    if (success) {
-        if (!pmConceptsReadFPA(fpa, PM_CONCEPT_SOURCE_HEADER, true, NULL)) {
-            psError(PS_ERR_IO, false, "Failed to read concepts for FPA.\n");
-            return false;
-        }
-    } else {
-        psError(PS_ERR_UNKNOWN, false, "Unable to read any chips in FPA");
-    }
-
-    return success;
-}
-
+    return fpaRead(fpa, fits, db, FPA_READ_TYPE_WEIGHT);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Reading FITS tables
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
 int pmCellReadTable(pmCell *cell, psFits *fits, const char *name)
Index: /trunk/psModules/src/camera/pmFPAWrite.c
===================================================================
--- /trunk/psModules/src/camera/pmFPAWrite.c	(revision 10080)
+++ /trunk/psModules/src/camera/pmFPAWrite.c	(revision 10081)
@@ -14,4 +14,229 @@
 
 #include "pmFPAWrite.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Definitions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Specify what to read
+typedef enum {
+    FPA_WRITE_TYPE_IMAGE,               // Write image
+    FPA_WRITE_TYPE_MASK,                // Write mask
+    FPA_WRITE_TYPE_WEIGHT               // Write weight map
+} fpaWriteType;
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// File-static (private) functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Return the appropriate image array for the given type
+static psArray **appropriateImageArray(pmHDU *hdu, // HDU containing the image arrays
+                                       fpaWriteType type // Type to write
+                                      )
+{
+    switch (type) {
+    case FPA_WRITE_TYPE_IMAGE:
+        return &hdu->images;
+    case FPA_WRITE_TYPE_MASK:
+        return &hdu->masks;
+    case FPA_WRITE_TYPE_WEIGHT:
+        return &hdu->weights;
+    default:
+        psAbort(__func__, "Unknown write type: %x\n", type);
+    }
+    return NULL;
+}
+
+// Run the appropriate HDU write function
+static bool appropriateWriteFunc(pmHDU *hdu, // HDU to write
+                                 psFits *fits, // FITS file to which to write
+                                 fpaWriteType type // Type to write
+                                )
+{
+    switch (type) {
+    case FPA_WRITE_TYPE_IMAGE:
+        return pmHDUWrite(hdu, fits);
+    case FPA_WRITE_TYPE_MASK:
+        return pmHDUWriteMask(hdu, fits);
+    case FPA_WRITE_TYPE_WEIGHT:
+        return pmHDUWriteWeight(hdu, fits);
+    default:
+        psAbort(__func__, "Unknown write type: %x\n", type);
+    }
+    return false;
+}
+
+// Write a cell image/mask/weight
+static bool cellWrite(pmCell *cell,     // Cell to write
+                      psFits *fits,     // FITS file to which to write
+                      psDB *db,         // Database handle for "concepts" update
+                      bool blank,       // Write a blank PHU?
+                      fpaWriteType type // Type to write
+                     )
+{
+    assert(cell);
+    assert(fits);
+
+    psTrace ("pmFPAWrite", 5, "writing to Cell (%d)\n", blank);
+
+    pmHDU *hdu = cell->hdu;             // The HDU
+    if (!hdu) {
+        return true;                    // We wrote every HDU that exists
+    }
+
+    psArray **imageArray = appropriateImageArray(hdu, type); // Array of images in the HDU
+
+    // Generate the HDU if needed --- this is required after a pmFPACopy, or similar, which does not
+    // generate the HDU, but only copies the structure.
+    if (!hdu->blankPHU && !*imageArray && (!pmHDUGenerateForCell(cell) || !*imageArray)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to generate HDU for cell --- likely programming error.\n");
+        return false;
+    }
+
+    // We only write out a blank PHU if it's specifically requested.
+    bool writeBlank = blank && hdu->blankPHU && !*imageArray; // Write a blank PHU?
+    bool writeImage = !blank && !hdu->blankPHU && *imageArray; // Write an image?
+
+    if (writeBlank || writeImage) {
+        pmConceptSource source = PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CELLS |
+                                 PM_CONCEPT_SOURCE_DEFAULTS;
+        if (!pmConceptsWriteCell(cell, source, false, NULL)) {
+            psError(PS_ERR_IO, false, "Unable to write concepts for cell.\n");
+            return false;
+        }
+        if (!appropriateWriteFunc(hdu, fits, type)) {
+            psError(PS_ERR_IO, false, "Unable to write HDU for cell.\n");
+            return false;
+        }
+    }
+    // No lower levels to which to recurse
+
+    return true;
+}
+
+// Write a chip image/mask/weight
+static bool chipWrite(pmChip *chip,     // Chip to write
+                      psFits *fits,     // FITS file to which to write
+                      psDB *db,         // Database handle for "concepts" update
+                      bool blank,       // Write a blank PHU?
+                      bool recurse,     // Recurse to lower levels?
+                      fpaWriteType type // Type to write
+                     )
+{
+    assert(chip);
+    assert(fits);
+
+    pmHDU *hdu = chip->hdu;             // The HDU
+
+    psTrace ("pmFPAWrite", 5, "writing to Chip (%d, %d)\n", blank, recurse);
+
+    // If we have data at this level, try to write it out
+    if (hdu) {
+        psArray **imageArray = appropriateImageArray(hdu, type); // Array of images in HDU
+
+        // Generate the HDU if needed --- this is required after a pmFPACopy, or similar, which does not
+        // generate the HDU, but only copies the structure.
+        if (!blank && !hdu->blankPHU && !*imageArray && (!pmHDUGenerateForChip(chip) || !*imageArray)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to generate HDU for chip --- likely programming error.\n");
+            return false;
+        }
+
+        // We only write out a blank PHU if it's specifically requested.
+        bool writeBlank = blank && hdu->blankPHU && !*imageArray; // Write a blank HDU?
+        bool writeImage = !blank && !hdu->blankPHU && *imageArray; // Write an image?
+
+        if (writeBlank || writeImage) {
+            pmConceptSource source = PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CELLS |
+                                     PM_CONCEPT_SOURCE_DEFAULTS;
+            if (!pmConceptsWriteChip(chip, source, false, true, NULL)) {
+                psError(PS_ERR_IO, false, "Unable to write concepts for chip.\n");
+                return false;
+            }
+            if (!appropriateWriteFunc(hdu, fits, type)) {
+                psError(PS_ERR_IO, false, "Unable to write HDU for chip.\n");
+                return false;
+            }
+        }
+    }
+
+    // Recurse to lower level if specifically requested.
+    // XXX recursion implies blank == false (must be called on correct level?)
+    if (recurse) {
+        psArray *cells = chip->cells;       // Array of cells
+        for (int i = 0; i < cells->n; i++) {
+            pmCell *cell = cells->data[i];  // The cell of interest
+            if (!cellWrite(cell, fits, db, false, type)) {
+                psError(PS_ERR_IO, false, "Unable to write Chip.\n");
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+// Write an FPA image/mask/weight
+static bool fpaWrite(pmFPA *fpa,        // FPA to write
+                     psFits *fits,      // FITS file to which to write
+                     psDB *db,          // Database handle for "concepts" update
+                     bool blank,        // Write a blank PHU?
+                     bool recurse,      // Recurse to lower levels?
+                     fpaWriteType type  // Type to write
+                    )
+{
+    assert(fpa);
+    assert(fits);
+
+    pmHDU *hdu = fpa->hdu;              // The HDU
+
+    psTrace ("pmFPAWrite", 5, "writing to FPA (%d, %d)\n", blank, recurse);
+
+    // If we have data at this level, try to write it out
+    if (hdu) {
+        psArray **imageArray = appropriateImageArray(hdu, type); // Array of images in HDU
+
+        // Generate the HDU if needed --- this is required after a pmFPACopy, or similar, which does not
+        // generate the HDU, but only copies the structure.
+        if (!blank && !hdu->blankPHU && !*imageArray && (!pmHDUGenerateForFPA(fpa) || !*imageArray)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to generate HDU for FPA --- likely programming error.\n");
+            return false;
+        }
+
+        // We only write out a blank PHU if it's specifically requested.
+        bool writeBlank = blank && hdu->blankPHU && !*imageArray; // Write a blank PHU?
+        bool writeImage = !blank && !hdu->blankPHU && *imageArray; // Write an image?
+
+        if (writeBlank || writeImage) {
+            pmConceptSource source = PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CELLS |
+                                     PM_CONCEPT_SOURCE_DEFAULTS;
+            if (!pmConceptsWriteFPA(fpa, source, true, NULL)) {
+                psError(PS_ERR_IO, false, "Unable to write concepts for FPA.\n");
+                return false;
+            }
+            if (!appropriateWriteFunc(hdu, fits, type))  {
+                psError(PS_ERR_IO, false, "Unable to write HDU for FPA.\n");
+                return false;
+            }
+        }
+    }
+
+    // Recurse to lower levels if requested
+    if (recurse) {
+        psArray *chips = fpa->chips;        // Array of chips
+        for (int i = 0; i < chips->n; i++) {
+            pmChip *chip = chips->data[i];  // The chip of interest
+            if (!chipWrite(chip, fits, db, false, true, type)) {
+                psError(PS_ERR_IO, false, "Unable to write FPA.\n");
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
 bool pmReadoutWriteNext(pmReadout *readout, psFits *fits, int z)
@@ -82,51 +307,10 @@
 
 
-
-
-bool pmCellWrite(pmCell *cell,          // Cell to write
-                 psFits *fits,          // FITS file to which to write
-                 psDB *db,              // Database handle for "concepts" update
-                 bool blank             // Write a blank PHU?
-                )
+bool pmCellWrite(pmCell *cell, psFits *fits, psDB *db, bool blank)
 {
     PS_ASSERT_PTR_NON_NULL(cell, false);
     PS_ASSERT_PTR_NON_NULL(fits, false);
-
-    psTrace ("pmFPAWrite", 5, "writing to Cell (%d)\n", blank);
-
-    pmHDU *hdu = cell->hdu;             // The HDU
-    if (!hdu) {
-        return true;                    // We wrote every HDU that exists
-    }
-
-    // Generate the HDU if needed --- this is required after a pmFPACopy, or similar, which does not
-    // generate the HDU, but only copies the structure.
-    if (!hdu->blankPHU && !hdu->images) {
-        if (!pmHDUGenerateForCell(cell) || !hdu->images) {
-            psAbort(__func__, "Unable to generate HDU for cell --- likely programming error.\n");
-        }
-    }
-
-    // We only write out a blank PHU if it's specifically requested.
-    bool writeBlank = blank && hdu->blankPHU && !hdu->images; // Write a blank PHU?
-    bool writeImage = !blank && !hdu->blankPHU && hdu->images; // Write an image?
-
-    if (writeBlank || writeImage) {
-        pmConceptSource source = PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CELLS |
-                                 PM_CONCEPT_SOURCE_DEFAULTS;
-        if (!pmConceptsWriteCell(cell, source, false, NULL)) {
-            psError(PS_ERR_IO, false, "Unable to write concepts for cell.\n");
-            return false;
-        }
-        if (!pmHDUWrite(hdu, fits)) {
-            psError(PS_ERR_IO, false, "Unable to write HDU for cell.\n");
-            return false;
-        }
-    }
-    // No lower levels to which to recurse
-
-    return true;
-}
-
+    return cellWrite(cell, fits, db, blank, FPA_WRITE_TYPE_IMAGE);
+}
 
 bool pmChipWrite(pmChip *chip, psFits *fits, psDB *db, bool blank, bool recurse)
@@ -134,54 +318,6 @@
     PS_ASSERT_PTR_NON_NULL(chip, false);
     PS_ASSERT_PTR_NON_NULL(fits, false);
-
-    pmHDU *hdu = chip->hdu;             // The HDU
-
-    psTrace ("pmFPAWrite", 5, "writing to Chip (%d, %d)\n", blank, recurse);
-
-    // If we have data at this level, try to write it out
-    if (hdu) {
-        // Generate the HDU if needed --- this is required after a pmFPACopy, or similar, which does not
-        // generate the HDU, but only copies the structure.
-        if (!blank && !hdu->blankPHU && !hdu->images) {
-            if (!pmHDUGenerateForChip(chip) || !hdu->images) {
-                psAbort (__func__, "Unable to generate HDU for chip --- likely programming error.\n");
-            }
-        }
-
-        // We only write out a blank PHU if it's specifically requested.
-        bool writeBlank = blank && hdu->blankPHU && !hdu->images; // Write a blank HDU?
-        bool writeImage = !blank && !hdu->blankPHU && hdu->images; // Write an image?
-
-        if (writeBlank || writeImage) {
-            pmConceptSource source = PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CELLS |
-                                     PM_CONCEPT_SOURCE_DEFAULTS;
-            if (!pmConceptsWriteChip(chip, source, false, true, NULL)) {
-                psError(PS_ERR_IO, false, "Unable to write concepts for chip.\n");
-                return false;
-            }
-            if (!pmHDUWrite(hdu, fits)) {
-                psError(PS_ERR_IO, false, "Unable to write HDU for chip.\n");
-                return false;
-            }
-        }
-    }
-
-    // Recurse to lower level if specifically requested.
-    // XXX recursion implies blank == false (must be called on correct level?)
-    if (recurse) {
-        psArray *cells = chip->cells;       // Array of cells
-        for (int i = 0; i < cells->n; i++) {
-            pmCell *cell = cells->data[i];  // The cell of interest
-            if (!pmCellWrite(cell, fits, db, false)) {
-                psError(PS_ERR_IO, false, "Unable to write Chip.\n");
-                return false;
-            }
-        }
-    }
-
-    return true;
-}
-
-
+    return chipWrite(chip, fits, db, blank, recurse, FPA_WRITE_TYPE_IMAGE);
+}
 
 bool pmFPAWrite(pmFPA *fpa, psFits *fits, psDB *db, bool blank, bool recurse)
@@ -189,165 +325,50 @@
     PS_ASSERT_PTR_NON_NULL(fpa, false);
     PS_ASSERT_PTR_NON_NULL(fits, false);
-
-    pmHDU *hdu = fpa->hdu;              // The HDU
-
-    psTrace ("pmFPAWrite", 5, "writing to FPA (%d, %d)\n", blank, recurse);
-
-    // If we have data at this level, try to write it out
-    if (hdu) {
-        // Generate the HDU if needed --- this is required after a pmFPACopy, or similar, which does not
-        // generate the HDU, but only copies the structure.
-        if (!blank && !hdu->blankPHU && !hdu->images) {
-            if (!pmHDUGenerateForFPA(fpa)) {
-                psAbort("pmFPAWrite", "error generating HDU");
-            }
-            if (!hdu->images) {
-                psAbort("pmFPAWrite", "programming error: failure generating HDU");
-            }
-        }
-
-        // We only write out a blank PHU if it's specifically requested.
-        bool writeBlank = blank && hdu->blankPHU && !hdu->images; // Write a blank PHU?
-        bool writeImage = !blank && !hdu->blankPHU && hdu->images; // Write an image?
-
-        if (writeBlank || writeImage) {
-            pmConceptSource source = PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CELLS |
-                                     PM_CONCEPT_SOURCE_DEFAULTS;
-            if (!pmConceptsWriteFPA(fpa, source, true, NULL)) {
-                psError(PS_ERR_IO, false, "Unable to write concepts for FPA.\n");
-                return false;
-            }
-            if (!pmHDUWrite(hdu, fits))  {
-                psError(PS_ERR_IO, false, "Unable to write HDU for FPA.\n");
-                return false;
-            }
-        }
-    }
-
-    // Recurse to lower levels if requested
-    if (recurse) {
-        psArray *chips = fpa->chips;        // Array of chips
-        for (int i = 0; i < chips->n; i++) {
-            pmChip *chip = chips->data[i];  // The chip of interest
-            if (!pmChipWrite(chip, fits, db, false, true)) {
-                psError(PS_ERR_IO, false, "Unable to write FPA.\n");
-                return false;
-            }
-        }
-    }
-
-    return true;
-}
-
-
-
-bool pmCellWriteMask(pmCell *cell,          // Cell to write
-                     psFits *fits,          // FITS file to which to write
-                     psDB *db           // Database handle for "concepts" update
-                    )
+    return fpaWrite(fpa, fits, db, blank, recurse, FPA_WRITE_TYPE_IMAGE);
+}
+
+
+bool pmCellWriteMask(pmCell *cell, psFits *fits, psDB *db, bool blank)
 {
     PS_ASSERT_PTR_NON_NULL(cell, false);
     PS_ASSERT_PTR_NON_NULL(fits, false);
-
-    pmHDU *hdu = cell->hdu;             // The HDU
-    if (!hdu) {
-        return true;                    // We wrote every HDU that exists
-    }
-
-    psTrace ("pmFPAWrite", 5, "writing mask to Cell\n");
-
-    // Generate the HDU if needed --- this is required after a pmFPACopy, or similar, which does not
-    // generate the HDU, but only copies the structure.
-    if (!hdu->blankPHU && !hdu->masks) {
-        if (!pmHDUGenerateForCell(cell) || !hdu->masks) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to generate HDU for cell --- likely programming error.\n");
-            return false;
-        }
-    }
-
-    pmConceptSource source = PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CELLS | PM_CONCEPT_SOURCE_DEFAULTS;
-    if (!pmConceptsWriteCell(cell, source, false, NULL)) {
-        psError(PS_ERR_IO, false, "Unable to write concepts for cell.\n");
-        return false;
-    }
-    if (!pmHDUWriteMask(hdu, fits)) {
-        psError(PS_ERR_IO, false, "Unable to write HDU for cell.\n");
-        return false;
-    }
-
-    return true;
-}
-
-
-bool pmChipWriteMask(pmChip *chip, psFits *fits, psDB *db)
+    return cellWrite(cell, fits, db, blank, FPA_WRITE_TYPE_IMAGE);
+}
+
+bool pmChipWriteMask(pmChip *chip, psFits *fits, psDB *db, bool blank, bool recurse)
 {
     PS_ASSERT_PTR_NON_NULL(chip, false);
     PS_ASSERT_PTR_NON_NULL(fits, false);
-
-    pmHDU *hdu = chip->hdu;             // The HDU
-    if (!hdu) {
-        return true;                    // We wrote every HDU that exists
-    }
-
-    psTrace ("pmFPAWrite", 5, "writing mask to Chip\n");
-
-    // Generate the HDU if needed --- this is required after a pmFPACopy, or similar, which does not
-    // generate the HDU, but only copies the structure.
-    if (!hdu->blankPHU && !hdu->masks) {
-        if (!pmHDUGenerateForChip(chip) || !hdu->masks) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to generate HDU for chip --- likely programming error.\n");
-            return false;
-        }
-    }
-
-    pmConceptSource source = PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CELLS | PM_CONCEPT_SOURCE_DEFAULTS;
-    if (!pmConceptsWriteChip(chip, source, false, true, NULL)) {
-        psError(PS_ERR_IO, false, "Unable to write concepts for chip.\n");
-        return false;
-    }
-    if (!pmHDUWriteMask(hdu, fits)) {
-        psError(PS_ERR_IO, false, "Unable to write HDU for chip.\n");
-        return false;
-    }
-
-    return true;
-}
-
-
-
-bool pmFPAWriteMask(pmFPA *fpa, psFits *fits, psDB *db)
+    return chipWrite(chip, fits, db, blank, recurse, FPA_WRITE_TYPE_MASK);
+}
+
+bool pmFPAWriteMask(pmFPA *fpa, psFits *fits, psDB *db, bool blank, bool recurse)
 {
     PS_ASSERT_PTR_NON_NULL(fpa, false);
     PS_ASSERT_PTR_NON_NULL(fits, false);
-
-    pmHDU *hdu = fpa->hdu;              // The HDU
-    if (!hdu) {
-        return true;                    // We wrote every HDU that exists
-    }
-
-    psTrace ("pmFPAWrite", 5, "writing mask to FPA\n");
-
-    // Generate the HDU if needed --- this is required after a pmFPACopy, or similar, which does not
-    // generate the HDU, but only copies the structure.
-    if (!hdu->blankPHU && !hdu->masks) {
-        if (!pmHDUGenerateForFPA(fpa) || !hdu->masks) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to generate HDU for chip --- likely programming error.\n");
-            return false;
-        }
-    }
-
-    pmConceptSource source = PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CELLS | PM_CONCEPT_SOURCE_DEFAULTS;
-    if (!pmConceptsWriteFPA(fpa, source, true, NULL)) {
-        psError(PS_ERR_IO, false, "Unable to write concepts for FPA.\n");
-        return false;
-    }
-    if (!pmHDUWriteMask(hdu, fits))  {
-        psError(PS_ERR_IO, false, "Unable to write HDU for FPA.\n");
-        return false;
-    }
-
-    return true;
-}
-
+    return fpaWrite(fpa, fits, db, blank, recurse, FPA_WRITE_TYPE_MASK);
+}
+
+
+bool pmCellWriteWeight(pmCell *cell, psFits *fits, psDB *db, bool blank)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    return cellWrite(cell, fits, db, blank, FPA_WRITE_TYPE_WEIGHT);
+}
+
+bool pmChipWriteWeight(pmChip *chip, psFits *fits, psDB *db, bool blank, bool recurse)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    return chipWrite(chip, fits, db, blank, recurse, FPA_WRITE_TYPE_WEIGHT);
+}
+
+bool pmFPAWriteWeight(pmFPA *fpa, psFits *fits, psDB *db, bool blank, bool recurse)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    return fpaWrite(fpa, fits, db, blank, recurse, FPA_WRITE_TYPE_WEIGHT);
+}
 
 
Index: /trunk/psModules/src/camera/pmFPAWrite.h
===================================================================
--- /trunk/psModules/src/camera/pmFPAWrite.h	(revision 10080)
+++ /trunk/psModules/src/camera/pmFPAWrite.h	(revision 10081)
@@ -7,6 +7,6 @@
 /// @author Paul Price, IfA
 ///
-/// @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
-/// @date $Date: 2006-11-15 00:40:02 $
+/// @version $Revision: 1.7 $ $Name: not supported by cvs2svn $
+/// @date $Date: 2006-11-18 23:43:28 $
 ///
 /// Copyright 2005-2006 Institute for Astronomy, University of Hawaii
@@ -69,28 +69,82 @@
 /// Write a cell mask to a FITS file
 ///
-/// Generates CELL.TRIMSEC, CELL.BIASSEC and the HDU pixels if required.  Writes the concepts to the various
-/// locations, and then the HDU mask to the FITS file.
+/// Generates CELL.TRIMSEC, CELL.BIASSEC and the HDU mask pixels if required.  A blank (i.e., image-less
+/// header) is written only if specifically requested.  Writes the concepts to the various locations, and then
+/// the HDU mask to the FITS file.  This function should be called at the beginning of the output cell loop
+/// with blank=true in order to produce the correct file structure.
 bool pmCellWriteMask(pmCell *cell,      ///<  Cell to write
                      psFits *fits,      ///<  FITS file to which to write
-                     psDB *db           ///<  Database handle for "concepts" update
+                     psDB *db,          ///<  Database handle for "concepts" update
+                     bool blank         ///<  Write a blank PHU?
                     );
 
 /// Write a chip mask to a FITS file
 ///
-/// Generates CELL.TRIMSEC, CELL.BIASSEC and the HDU pixels if required.  Writes the concepts to the various
-/// locations, and then the HDU mask to the FITS file.
+/// Generates CELL.TRIMSEC, CELL.BIASSEC and the HDU mask pixels if required.  A blank (i.e., image-less
+/// header) is written only if specifically requested.  Writes the concepts to the various locations, and then
+/// the HDU mask to the FITS file, optionally recursing to lower levels.  This function should be called at
+/// the beginning of the output chip loop with blank=true and recurse=false in order to produce the correct
+/// file structure.
 bool pmChipWriteMask(pmChip *chip,      ///<  Chip to write
                      psFits *fits,      ///<  FITS file to which to write
-                     psDB *db           ///<  Database handle for "concepts" update
+                     psDB *db,          ///<  Database handle for "concepts" update
+                     bool blank,        ///<  Write a blank PHU?
+                     bool recurse       ///<  Recurse to lower levels?
                     );
 
 /// Write an FPA mask to a FITS file
 ///
-/// Generates CELL.TRIMSEC, CELL.BIASSEC and the HDU pixels if required.  Writes the concepts to the various
-/// locations, and then the HDU mask to the FITS file.
+/// Generates CELL.TRIMSEC, CELL.BIASSEC and the HDU mask pixels if required.  A blank (i.e., image-less
+/// header) is written only if specifically requested.  Writes the concepts to the various locations, and then
+/// the HDU mask to the FITS file, optionally recursing to lower levels.  This function should be called at
+/// the beginning of the output FPA loop with blank=true and recurse=false in order to produce the correct
+/// file structure.
 bool pmFPAWriteMask(pmFPA *fpa,         ///<  FPA to write
                     psFits *fits,       ///<  FITS file to which to write
-                    psDB *db            ///<  Database handle for "concepts" update
+                    psDB *db,           ///<  Database handle for "concepts" update
+                    bool blank,         ///<  Write a blank PHU?
+                    bool recurse        ///<  Recurse to lower levels?
                    );
+
+/// Write a cell weight to a FITS file
+///
+/// Generates CELL.TRIMSEC, CELL.BIASSEC and the HDU weight pixels if required.  A blank (i.e., image-less
+/// header) is written only if specifically requested.  Writes the concepts to the various locations, and then
+/// the HDU weight to the FITS file.  This function should be called at the beginning of the output cell loop
+/// with blank=true in order to produce the correct file structure.
+bool pmCellWriteWeight(pmCell *cell,    ///<  Cell to write
+                       psFits *fits,    ///<  FITS file to which to write
+                       psDB *db,        ///<  Database handle for "concepts" update
+                       bool blank       ///<  Write a blank PHU?
+                      );
+
+/// Write a chip weight to a FITS file
+///
+/// Generates CELL.TRIMSEC, CELL.BIASSEC and the HDU weight pixels if required.  A blank (i.e., image-less
+/// header) is written only if specifically requested.  Writes the concepts to the various locations, and then
+/// the HDU weight to the FITS file, optionally recursing to lower levels.  This function should be called at
+/// the beginning of the output chip loop with blank=true and recurse=false in order to produce the correct
+/// file structure.
+bool pmChipWriteWeight(pmChip *chip,    ///<  Chip to write
+                       psFits *fits,    ///<  FITS file to which to write
+                       psDB *db,        ///<  Database handle for "concepts" update
+                       bool blank,      ///<  Write a blank PHU?
+                       bool recurse     ///<  Recurse to lower levels?
+                      );
+
+/// Write an FPA weight to a FITS file
+///
+/// Generates CELL.TRIMSEC, CELL.BIASSEC and the HDU weight pixels if required.  A blank (i.e., image-less
+/// header) is written only if specifically requested.  Writes the concepts to the various locations, and then
+/// the HDU weight to the FITS file, optionally recursing to lower levels.  This function should be called at
+/// the beginning of the output FPA loop with blank=true and recurse=false in order to produce the correct
+/// file structure.
+bool pmFPAWriteWeight(pmFPA *fpa,       ///<  FPA to write
+                      psFits *fits,     ///<  FITS file to which to write
+                      psDB *db,         ///<  Database handle for "concepts" update
+                      bool blank,       ///<  Write a blank PHU?
+                      bool recurse      ///<  Recurse to lower levels?
+                     );
+
 
 /// Write a FITS table from the cell's analysis metadata.
Index: /trunk/psModules/src/camera/pmFPAfile.c
===================================================================
--- /trunk/psModules/src/camera/pmFPAfile.c	(revision 10080)
+++ /trunk/psModules/src/camera/pmFPAfile.c	(revision 10081)
@@ -374,4 +374,10 @@
         return PM_FPA_FILE_JPEG;
     }
+    if (!strcasecmp (type, "MASK"))     {
+        return PM_FPA_FILE_MASK;
+    }
+    if (!strcasecmp (type, "WEIGHT"))     {
+        return PM_FPA_FILE_WEIGHT;
+    }
     if (!strcasecmp (type, "FRINGE")) {
         return PM_FPA_FILE_FRINGE;
Index: /trunk/psModules/src/camera/pmFPAfile.h
===================================================================
--- /trunk/psModules/src/camera/pmFPAfile.h	(revision 10080)
+++ /trunk/psModules/src/camera/pmFPAfile.h	(revision 10081)
@@ -7,6 +7,6 @@
 *  @author EAM, IfA
 *
-*  @version $Revision: 1.7 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2006-11-13 22:15:55 $
+*  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2006-11-18 23:43:28 $
 *
 *  Copyright 2004-2005 Institute for Astronomy, University of Hawaii
@@ -37,4 +37,6 @@
     PM_FPA_FILE_JPEG,
     PM_FPA_FILE_MANAPLOT,
+    PM_FPA_FILE_MASK,
+    PM_FPA_FILE_WEIGHT,
     PM_FPA_FILE_FRINGE,
 } pmFPAfileType;
Index: /trunk/psModules/src/camera/pmFPAfileFitsIO.c
===================================================================
--- /trunk/psModules/src/camera/pmFPAfileFitsIO.c	(revision 10080)
+++ /trunk/psModules/src/camera/pmFPAfileFitsIO.c	(revision 10081)
@@ -61,18 +61,21 @@
 }
 
-// given an already-opened fits file, read the components corresponding
-// to the specified view
-bool pmFPAviewReadFitsImage (const pmFPAview *view, pmFPAfile *file)
-{
-    PS_ASSERT_PTR_NON_NULL(view, false);
-    PS_ASSERT_PTR_NON_NULL(file, false);
-
-    bool status;
-    pmFPA *fpa = file->fpa;
-    psFits *fits = file->fits;
-
-    if (view->chip == -1) {
-        status = pmFPARead (fpa, fits, NULL);
-        return status;
+
+// given an already-opened fits file, read the components corresponding to the specified view
+static bool fpaViewReadFitsImage(const pmFPAview *view, // FPA view, specifying the level of interest
+                                 pmFPAfile *file, // FPA file of interest
+                                 bool (*fpaReadFunc)(pmFPA*, psFits*, psDB*), // Function to read FPA
+                                 bool (*chipReadFunc)(pmChip*, psFits*, psDB*), // Function to read chip
+                                 bool (*cellReadFunc)(pmCell*, psFits*, psDB*) // Function to read cell
+                                )
+{
+    assert(view);
+    assert(file);
+
+    pmFPA *fpa = file->fpa;             // FPA of interest
+    psFits *fits = file->fits;          // FITS file from which to read
+
+    if (view->chip == -1) {
+        return fpaReadFunc(fpa, fits, NULL);
     }
 
@@ -81,9 +84,8 @@
         return false;
     }
-    pmChip *chip = fpa->chips->data[view->chip];
-
-    if (view->cell == -1) {
-        status = pmChipRead (chip, fits, NULL);
-        return status;
+    pmChip *chip = fpa->chips->data[view->chip]; // Chip of interest
+
+    if (view->cell == -1) {
+        return chipReadFunc(chip, fits, NULL);
     }
 
@@ -92,11 +94,10 @@
         return false;
     }
-    pmCell *cell = chip->cells->data[view->cell];
+    pmCell *cell = chip->cells->data[view->cell]; // Cell of interest
 
     if (view->readout == -1) {
-        status = pmCellRead (cell, fits, NULL);
-        return status;
-    }
-    psError(PS_ERR_UNKNOWN, true, "Returning false");
+        return cellReadFunc(cell, fits, NULL);
+    }
+    psError(PS_ERR_UNKNOWN, true, "Bad view: %d,%d", view->chip, view->cell);
     return false;
 
@@ -118,4 +119,26 @@
     return true;
     #endif
+}
+
+
+bool pmFPAviewReadFitsImage(const pmFPAview *view, pmFPAfile *file)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    return fpaViewReadFitsImage(view, file, pmFPARead, pmChipRead, pmCellRead);
+}
+
+bool pmFPAviewReadFitsMask(const pmFPAview *view, pmFPAfile *file)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    return fpaViewReadFitsImage(view, file, pmFPAReadMask, pmChipReadMask, pmCellReadMask);
+}
+
+bool pmFPAviewReadFitsWeight(const pmFPAview *view, pmFPAfile *file)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    return fpaViewReadFitsImage(view, file, pmFPAReadWeight, pmChipReadWeight, pmCellReadWeight);
 }
 
@@ -126,16 +149,20 @@
 // out data in an inconsistent fashion
 // the calls below should recurse down the element to write out all components.
-bool pmFPAviewWriteFitsImage (const pmFPAview *view, pmFPAfile *file)
-{
-    PS_ASSERT_PTR_NON_NULL(view, false);
-    PS_ASSERT_PTR_NON_NULL(file, false);
-
-    pmFPA *fpa = file->fpa;
-    psFits *fits = file->fits;
+static bool fpaViewWriteFitsImage(const pmFPAview *view, // FPA view, specifying the level of interest
+                                  pmFPAfile *file, // FPA file of interest
+                                  bool (*fpaWriteFunc)(pmFPA*, psFits*, psDB*, bool, bool), // Func for FPA
+                                  bool (*chipWriteFunc)(pmChip*, psFits*, psDB*, bool, bool), // Func for chip
+                                  bool (*cellWriteFunc)(pmCell*, psFits*, psDB*, bool) // Func for cell
+                                 )
+{
+    assert(view);
+    assert(file);
+
+    pmFPA *fpa = file->fpa;             // FPA of interest
+    psFits *fits = file->fits;          // FITS file
 
     // pmFPAWrite takes care of all PHUs as needed
     if (view->chip == -1) {
-        pmFPAWrite(fpa, fits, NULL, false, true);
-        return true;
+        return fpaWriteFunc(fpa, fits, NULL, false, true);
     }
 
@@ -144,9 +171,8 @@
         return false;
     }
-    pmChip *chip = fpa->chips->data[view->chip];
-
-    if (view->cell == -1) {
-        pmChipWrite (chip, fits, NULL, false, true);
-        return true;
+    pmChip *chip = fpa->chips->data[view->chip]; // Chip of interest
+
+    if (view->cell == -1) {
+        return chipWriteFunc(chip, fits, NULL, false, true);
     }
 
@@ -155,10 +181,10 @@
         return false;
     }
-    pmCell *cell = chip->cells->data[view->cell];
+    pmCell *cell = chip->cells->data[view->cell]; // Cell of interest
 
     if (view->readout == -1) {
-        return pmCellWrite (cell, fits, NULL, false);
-    }
-    psError(PS_ERR_UNKNOWN, true, "Returning false");
+        return cellWriteFunc(cell, fits, NULL, false);
+    }
+    psError(PS_ERR_UNKNOWN, true, "Bad view: %d,%d", view->chip, view->cell);
     return false;
 
@@ -182,34 +208,28 @@
 }
 
-// this old code was used to write the blank by hand.
-// pmFPAWrite now takes care of this choice.
-# if 0
-// do we need to write out a PHU for this entry?
-if (file->phu == NULL)
-    {
-        pmHDU *hdu = pmFPAviewThisHDU (view, file->fpa);
-        pmHDU *phu = pmFPAviewThisPHU (view, file->fpa);
-        // if this hdu is the phu, the write function below will create the phu
-        if (hdu != phu) {
-            // we assume that the PHU is just a header
-            // header may not be defined for constructed images; make a dummy one
-            psMetadata *outhead = NULL;
-            if (phu->header) {
-                outhead = psMetadataCopy (NULL, phu->header);
-            } else {
-                outhead = psMetadataAlloc ();
-            }
-            psMetadataAdd (outhead, PS_LIST_TAIL, "EXTEND", PS_DATA_BOOL | PS_META_REPLACE, "this file has extensions", true);
-            psFitsWriteBlank (file->fits, outhead, "");
-            file->phu = phu->header;
-            psTrace ("pmFPAfile", 5, "wrote phu %s (type: %d)\n", file->filename, file->type);
-            psFree (outhead);
-        }
-    }
-# endif
+bool pmFPAviewWriteFitsImage(const pmFPAview *view, pmFPAfile *file)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    return fpaViewWriteFitsImage(view, file, pmFPAWrite, pmChipWrite, pmCellWrite);
+}
+
+bool pmFPAviewWriteFitsMask(const pmFPAview *view, pmFPAfile *file)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    return fpaViewWriteFitsImage(view, file, pmFPAWriteMask, pmChipWriteMask, pmCellWriteMask);
+}
+
+bool pmFPAviewWriteFitsWeight(const pmFPAview *view, pmFPAfile *file)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    return fpaViewWriteFitsImage(view, file, pmFPAWriteWeight, pmChipWriteWeight, pmCellWriteWeight);
+}
 
 // given an already-opened fits file, read the components corresponding
 // to the specified view
-bool pmFPAviewFreeFitsImage (const pmFPAview *view, pmFPAfile *file)
+bool pmFPAviewFreeData(const pmFPAview *view, pmFPAfile *file)
 {
     PS_ASSERT_PTR_NON_NULL(view, false);
@@ -270,4 +290,7 @@
 }
 
+#if 0
+// Shouldn't need this --- when we want to free fringe data, we want to free the whole level, not just the
+// table.
 
 // Free the table within a cell
@@ -329,2 +352,3 @@
 }
 
+#endif
Index: /trunk/psModules/src/camera/pmFPAfileFitsIO.h
===================================================================
--- /trunk/psModules/src/camera/pmFPAfileFitsIO.h	(revision 10080)
+++ /trunk/psModules/src/camera/pmFPAfileFitsIO.h	(revision 10081)
@@ -6,7 +6,8 @@
 *
 *  @author EAM, IfA
+*  @author PAP, IfA
 *
-*  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2006-11-13 22:15:55 $
+*  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2006-11-18 23:43:28 $
 *
 *  Copyright 2004-2005 Institute for Astronomy, University of Hawaii
@@ -16,22 +17,49 @@
 #define PM_FPA_FILE_FITS_IO_H
 
-// read an image into the current view
-bool pmFPAviewReadFitsImage (const pmFPAview *view, pmFPAfile *file);
+/// Read an image into the current view
+bool pmFPAviewReadFitsImage(const pmFPAview *view, ///< View specifying level of interest
+                            pmFPAfile *file ///< FPA file into which to read
+                           );
 
-// write the components for the specified view
-bool pmFPAviewWriteFitsImage (const pmFPAview *view, pmFPAfile *file);
+/// Read a mask into the current view
+bool pmFPAviewReadFitsMask(const pmFPAview *view, ///< View specifying level of interest
+                           pmFPAfile *file ///< FPA file into which to read
+                          );
+/// Read a weight map into the current view
+bool pmFPAviewReadFitsWeight(const pmFPAview *view,  ///< View specifying level of interest
+                             pmFPAfile *file ///< FPA file into which to read
+                            );
 
-// free the appropriate image containers
-bool pmFPAviewFreeFitsImage (const pmFPAview *view, pmFPAfile *file);
+/// Write the image for the specified view
+bool pmFPAviewWriteFitsImage(const pmFPAview *view, ///< View specifying level of interest
+                             pmFPAfile *file ///< FPA file to write
+                            );
 
+/// Write the mask for the specified view
+bool pmFPAviewWriteFitsMask(const pmFPAview *view, ///< View specifying level of interest
+                            pmFPAfile *file ///< FPA file to write
+                           );
 
-// read a table into the current view
-bool pmFPAviewReadFitsTable(const pmFPAview *view, pmFPAfile *file, const char *name);
+/// Write the weight map for the specified view
+bool pmFPAviewWriteFitsWeight(const pmFPAview *view, ///< View specifying level of interest
+                              pmFPAfile *file ///< FPA file to write
+                             );
 
-// write the table for the specified view
-bool pmFPAviewWriteFitsTable(const pmFPAview *view, pmFPAfile *file, const char *name);
+/// Free the data for the specified view
+bool pmFPAviewFreeData(const pmFPAview *view, ///< View specifying level of interest
+                       pmFPAfile *file  ///< FPA file to free data
+                      );
 
-// free the appropriate table containers
-bool pmFPAviewFreeFitsTable(const pmFPAview *view, pmFPAfile *file, const char *name);
+/// Read a table into the current view
+bool pmFPAviewReadFitsTable(const pmFPAview *view, ///<  View specifying level of interest
+                            pmFPAfile *file, ///< FPA file into which to read
+                            const char *name ///< Name of table
+                           );
+
+/// Write the table for the specified view
+bool pmFPAviewWriteFitsTable(const pmFPAview *view, ///<  View specifying level of interest
+                             pmFPAfile *file, ///< FPA file to write
+                             const char *name ///< Name of table
+                            );
 
 # endif
Index: /trunk/psModules/src/camera/pmFPAfileIO.c
===================================================================
--- /trunk/psModules/src/camera/pmFPAfileIO.c	(revision 10080)
+++ /trunk/psModules/src/camera/pmFPAfileIO.c	(revision 10081)
@@ -225,4 +225,6 @@
         // open the FITS types:
     case PM_FPA_FILE_IMAGE:
+    case PM_FPA_FILE_MASK:
+    case PM_FPA_FILE_WEIGHT:
     case PM_FPA_FILE_FRINGE:
         psTrace ("pmFPAfile", 5, "opening %s (type: %d)\n", file->filename, file->type);
@@ -311,5 +313,19 @@
     switch (file->type) {
     case PM_FPA_FILE_IMAGE:
-        if (!pmFPAviewReadFitsImage (view, file)) {
+        if (!pmFPAviewReadFitsImage(view, file)) {
+            psError(PS_ERR_UNKNOWN, false, "skipping %s (type: %d)\n", file->filename, file->type);
+            return false;
+        }
+        psTrace ("pmFPAfile", 5, "reading %s (type: %d)\n", file->filename, file->type);
+        break;
+    case PM_FPA_FILE_MASK:
+        if (!pmFPAviewReadFitsMask(view, file)) {
+            psError(PS_ERR_UNKNOWN, false, "skipping %s (type: %d)\n", file->filename, file->type);
+            return false;
+        }
+        psTrace ("pmFPAfile", 5, "reading %s (type: %d)\n", file->filename, file->type);
+        break;
+    case PM_FPA_FILE_WEIGHT:
+        if (!pmFPAviewReadFitsWeight(view, file)) {
             psError(PS_ERR_UNKNOWN, false, "skipping %s (type: %d)\n", file->filename, file->type);
             return false;
@@ -384,5 +400,8 @@
     switch (file->type) {
     case PM_FPA_FILE_IMAGE:
-        if (pmFPAviewFreeFitsImage (view, file)) {
+    case PM_FPA_FILE_MASK:
+    case PM_FPA_FILE_WEIGHT:
+    case PM_FPA_FILE_FRINGE:
+        if (pmFPAviewFreeData(view, file)) {
             psTrace ("pmFPAfile", 5, "freed %s for %s (type: %d)\n", file->filename, file->name, file->type);
             if (file->filename == NULL) {
@@ -394,11 +413,4 @@
         }
         break;
-    case PM_FPA_FILE_FRINGE:
-        if (!pmFPAviewFreeFitsTable(view, file, "FRINGE")) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to free fringe data.\n");
-            return false;
-        }
-        break;
-
     case PM_FPA_FILE_SX:
     case PM_FPA_FILE_RAW:
@@ -469,10 +481,18 @@
     switch (file->type) {
     case PM_FPA_FILE_IMAGE:
+        pmFPAviewWriteFitsImage(view, file);
+        psTrace ("pmFPAfile", 5, "wrote image %s (fpa: %p)\n", file->filename, file->fpa);
+        break;
+    case PM_FPA_FILE_MASK:
+        pmFPAviewWriteFitsMask(view, file);
+        psTrace ("pmFPAfile", 5, "wrote mask %s (fpa: %p)\n", file->filename, file->fpa);
+        break;
+    case PM_FPA_FILE_WEIGHT:
+        pmFPAviewWriteFitsWeight(view, file);
+        psTrace ("pmFPAfile", 5, "wrote weight %s (fpa: %p)\n", file->filename, file->fpa);
+        break;
+    case PM_FPA_FILE_FRINGE:
         pmFPAviewWriteFitsImage (view, file);
         psTrace ("pmFPAfile", 5, "wrote image %s (fpa: %p)\n", file->filename, file->fpa);
-        break;
-    case PM_FPA_FILE_FRINGE:
-        pmFPAviewWriteFitsImage (view, file);
-        psTrace ("pmFPAfile", 5, "wrote table %s (fpa: %p)\n", file->filename, file->fpa);
         pmFPAviewWriteFitsTable(view, file, "FRINGE");
         psTrace ("pmFPAfile", 5, "wrote fringe table %s (fpa: %p)\n", file->filename, file->fpa);
@@ -554,4 +574,6 @@
     switch (file->type) {
     case PM_FPA_FILE_IMAGE:
+    case PM_FPA_FILE_MASK:
+    case PM_FPA_FILE_WEIGHT:
     case PM_FPA_FILE_FRINGE:
         // create FPA structure component based on view
@@ -608,4 +630,6 @@
         // check the FITS types
     case PM_FPA_FILE_IMAGE:
+    case PM_FPA_FILE_MASK:
+    case PM_FPA_FILE_WEIGHT:
     case PM_FPA_FILE_FRINGE:
     case PM_FPA_FILE_CMF:
Index: /trunk/psModules/src/camera/pmHDU.c
===================================================================
--- /trunk/psModules/src/camera/pmHDU.c	(revision 10080)
+++ /trunk/psModules/src/camera/pmHDU.c	(revision 10081)
@@ -85,5 +85,5 @@
     if (!hdu->header) {
         psTrace("psModules.camera", 5, "Reading the header...\n");
-        hdu->header = psFitsReadHeader(NULL, fits);
+        hdu->header = psFitsReadHeader(hdu->header, fits);
         if (! hdu->header) {
             psError(PS_ERR_IO, false, "Unable to read header for extension %s\n", hdu->extname);
@@ -95,9 +95,14 @@
 }
 
+// Read an HDU from a FITS file
 // XXX: Add a region specifier?
-bool pmHDURead(pmHDU *hdu, psFits *fits)
-{
-    PS_ASSERT_PTR_NON_NULL(hdu, false);
-    PS_ASSERT_PTR_NON_NULL(fits, false);
+bool hduRead(pmHDU *hdu,                // HDU to write
+             psArray **images,          // Images into which to read
+             psFits *fits               // FITS file to read
+            )
+{
+    assert(hdu);
+    assert(images);
+    assert(fits);
 
     // Read the header; includes the move
@@ -111,11 +116,11 @@
     }
 
-    if (hdu->images) {
+    if (*images) {
         psLogMsg(__func__, PS_LOG_WARN, "HDU %s has already been read --- overwriting.\n", hdu->extname);
-        psFree(hdu->images);        // Blow away anything existing
+        psFree(*images);                // Blow away anything existing
     }
     psTrace("psModules.camera", 5, "Reading the pixels...\n");
-    hdu->images = psFitsReadImageCube(fits, psRegionSet(0,0,0,0));
-    if (! hdu->images) {
+    *images = psFitsReadImageCube(fits, psRegionSet(0,0,0,0));
+    if (!*images) {
         psError(PS_ERR_IO, false, "Unable to read pixels for extension %s\n", hdu->extname);
         return false;
@@ -124,9 +129,33 @@
 }
 
+bool pmHDURead(pmHDU *hdu, psFits *fits)
+{
+    PS_ASSERT_PTR_NON_NULL(hdu, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+
+    return hduRead(hdu, &hdu->images, fits);
+}
+
+bool pmHDUReadMask(pmHDU *hdu, psFits *fits)
+{
+    PS_ASSERT_PTR_NON_NULL(hdu, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+
+    return hduRead(hdu, &hdu->masks, fits);
+}
+
+bool pmHDUReadWeight(pmHDU *hdu, psFits *fits)
+{
+    PS_ASSERT_PTR_NON_NULL(hdu, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+
+    return hduRead(hdu, &hdu->weights, fits);
+}
+
 // Write an HDU to a FITS file
-bool hduWrite(pmHDU *hdu,               // HDU to write
-              psArray *images,          // Images to write
-              psFits *fits              // FITS file to which to write
-             )
+static bool hduWrite(pmHDU *hdu,        // HDU to write
+                     psArray *images,   // Images to write
+                     psFits *fits       // FITS file to which to write
+                    )
 {
     assert(hdu);
@@ -182,2 +211,9 @@
 }
 
+bool pmHDUWriteWeight(pmHDU *hdu, psFits *fits)
+{
+    PS_ASSERT_PTR_NON_NULL(hdu, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+
+    return hduWrite(hdu, hdu->weights, fits);
+}
Index: /trunk/psModules/src/camera/pmHDU.h
===================================================================
--- /trunk/psModules/src/camera/pmHDU.h	(revision 10080)
+++ /trunk/psModules/src/camera/pmHDU.h	(revision 10081)
@@ -7,6 +7,6 @@
 /// @author Paul Price, IfA
 ///
-/// @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
-/// @date $Date: 2006-11-15 00:40:02 $
+/// @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
+/// @date $Date: 2006-11-18 23:43:28 $
 ///
 /// Copyright 2005-2006 Institute for Astronomy, University of Hawaii
@@ -51,4 +51,18 @@
               );
 
+/// Read the HDU header and mask
+///
+/// Moves to the appropriate extension
+bool pmHDUReadMask(pmHDU *hdu,          ///< HDU to read
+                   psFits *fits         ///< FITS file to read from
+                  );
+
+/// Read the HDU header and weight map
+///
+/// Moves to the appropriate extension
+bool pmHDUReadWeight(pmHDU *hdu,        ///< HDU to read
+                     psFits *fits       ///< FITS file to read from
+                    );
+
 /// Write the HDU header and pixels
 bool pmHDUWrite(pmHDU *hdu,             ///< HDU to write
@@ -57,7 +71,12 @@
 
 /// Write the HDU header and mask
-bool pmHDUWriteMask(pmHDU *hdu,         ///< HDU to write mask
+bool pmHDUWriteMask(pmHDU *hdu,         ///< HDU to write
                     psFits *fits        ///< FITS file to write to
                    );
 
+/// Write the HDU header and weight map
+bool pmHDUWriteWeight(pmHDU *hdu,       ///< HDU to write
+                      psFits *fits      ///< FITS file to write to
+                     );
+
 #endif
