Index: trunk/psModules/src/camera/pmFPARead.c
===================================================================
--- trunk/psModules/src/camera/pmFPARead.c	(revision 9950)
+++ trunk/psModules/src/camera/pmFPARead.c	(revision 9983)
@@ -263,8 +263,14 @@
             readout->row0 += readout->image->numRows;
             offset = readout->row0;
+            if (numScans == 0) {
+                numScans = naxis2 - offset;
+            }
         } else {
             // Reading columns
             readout->col0 += readout->image->numCols;
             offset = readout->col0;
+            if (numScans == 0) {
+                numScans = naxis1 - offset;
+            }
         }
     }
Index: trunk/psModules/src/camera/pmFPAWrite.c
===================================================================
--- trunk/psModules/src/camera/pmFPAWrite.c	(revision 9950)
+++ trunk/psModules/src/camera/pmFPAWrite.c	(revision 9983)
@@ -242,4 +242,114 @@
 
 
+bool pmCellWriteMask(pmCell *cell,          // Cell to write
+                     psFits *fits,          // FITS file to which to write
+                     psDB *db           // Database handle for "concepts" update
+                    )
+{
+    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)
+{
+    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)
+{
+    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;
+}
+
+
+
 int pmCellWriteTable(psFits *fits, const pmCell *cell, const char *name)
 {
Index: trunk/psModules/src/camera/pmFPAWrite.h
===================================================================
--- trunk/psModules/src/camera/pmFPAWrite.h	(revision 9950)
+++ trunk/psModules/src/camera/pmFPAWrite.h	(revision 9983)
@@ -7,6 +7,6 @@
 /// @author Paul Price, IfA
 ///
-/// @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
-/// @date $Date: 2006-11-13 22:15:05 $
+/// @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
+/// @date $Date: 2006-11-15 00:40:02 $
 ///
 /// Copyright 2005-2006 Institute for Astronomy, University of Hawaii
@@ -67,4 +67,31 @@
                );
 
+/// 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.
+bool pmCellWriteMask(pmCell *cell,      ///<  Cell to write
+                     psFits *fits,      ///<  FITS file to which to write
+                     psDB *db           ///<  Database handle for "concepts" update
+                    );
+
+/// 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.
+bool pmChipWriteMask(pmChip *chip,      ///<  Chip to write
+                     psFits *fits,      ///<  FITS file to which to write
+                     psDB *db           ///<  Database handle for "concepts" update
+                    );
+
+/// 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.
+bool pmFPAWriteMask(pmFPA *fpa,         ///<  FPA to write
+                    psFits *fits,       ///<  FITS file to which to write
+                    psDB *db            ///<  Database handle for "concepts" update
+                   );
+
 /// Write a FITS table from the cell's analysis metadata.
 ///
Index: trunk/psModules/src/camera/pmHDU.c
===================================================================
--- trunk/psModules/src/camera/pmHDU.c	(revision 9950)
+++ trunk/psModules/src/camera/pmHDU.c	(revision 9983)
@@ -124,19 +124,16 @@
 }
 
-// XXX: Add a region specifier?
-bool pmHDUWrite(pmHDU *hdu, psFits *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
+             )
 {
-    PS_ASSERT_PTR_NON_NULL(hdu, false);
-    PS_ASSERT_PTR_NON_NULL(fits, false);
+    assert(hdu);
+    assert(fits);
 
     psTrace("psModules.camera", 7, "Writing HDU %s\n", hdu->extname);
 
-    if (hdu->images && !hdu->header) {
-        psError(PS_ERR_IO, true, "Both image and table data provided in HDU, but no header --- "
-                "don't know which to write!\n");
-        return false;
-    }
-
-    if (!hdu->images && !hdu->header) {
+    if (!images && !hdu->header) {
         psLogMsg(__func__, PS_LOG_WARN, "Nothing to write for HDU %s\n", hdu->extname);
         return false;
@@ -154,11 +151,11 @@
 
     // Only a header
-    if (!hdu->images && !psFitsWriteBlank(fits, hdu->header, extname)) {
+    if (!images && !psFitsWriteBlank(fits, hdu->header, extname)) {
         psError(PS_ERR_IO, false, "Unable to write header for extension %s\n", extname);
     }
 
-    if (hdu->images) {
+    if (images) {
         psTrace("psModules.camera", 9, "Writing pixels for %s\n", hdu->extname);
-        if (!psFitsWriteImageCube(fits, hdu->header, hdu->images, extname)) {
+        if (!psFitsWriteImageCube(fits, hdu->header, images, extname)) {
             psError(PS_ERR_IO, false, "Unable to write image to extension %s\n", hdu->extname);
             return false;
@@ -168,2 +165,19 @@
 }
 
+// XXX: Add a region specifier?
+bool pmHDUWrite(pmHDU *hdu, psFits *fits)
+{
+    PS_ASSERT_PTR_NON_NULL(hdu, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+
+    return hduWrite(hdu, hdu->images, fits);
+}
+
+bool pmHDUWriteMask(pmHDU *hdu, psFits *fits)
+{
+    PS_ASSERT_PTR_NON_NULL(hdu, false);
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+
+    return hduWrite(hdu, hdu->masks, fits);
+}
+
Index: trunk/psModules/src/camera/pmHDU.h
===================================================================
--- trunk/psModules/src/camera/pmHDU.h	(revision 9950)
+++ trunk/psModules/src/camera/pmHDU.h	(revision 9983)
@@ -7,6 +7,6 @@
 /// @author Paul Price, IfA
 ///
-/// @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
-/// @date $Date: 2006-10-21 03:00:43 $
+/// @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+/// @date $Date: 2006-11-15 00:40:02 $
 ///
 /// Copyright 2005-2006 Institute for Astronomy, University of Hawaii
@@ -56,3 +56,8 @@
                );
 
+/// Write the HDU header and mask
+bool pmHDUWriteMask(pmHDU *hdu,         ///< HDU to write mask
+                    psFits *fits        ///< FITS file to write to
+                   );
+
 #endif
Index: trunk/psModules/src/camera/pmHDUGenerate.c
===================================================================
--- trunk/psModules/src/camera/pmHDUGenerate.c	(revision 9950)
+++ trunk/psModules/src/camera/pmHDUGenerate.c	(revision 9983)
@@ -218,7 +218,18 @@
 
         pmReadout *readout = cell->readouts->data[0]; // The first readout, as representative
-        psImage *image = readout->image;// The proper image
+        // The proper image, used to get the size
+        psImage *image = readout->image ? readout->image : (readout->mask ? readout->mask : readout->weight);
         if (!image) {
             continue;
+        }
+        if (readout->mask &&
+                (readout->mask->numCols != image->numCols || readout->mask->numRows != image->numRows)) {
+            psLogMsg(__func__, PS_LOG_WARN, "Image and mask have different sizes (%dx%d vs %dx%d)!\n",
+                     image->numCols, image->numRows, readout->mask->numCols, readout->mask->numRows);
+        }
+        if (readout->weight &&
+                (readout->weight->numCols != image->numCols || readout->weight->numRows != image->numRows)) {
+            psLogMsg(__func__, PS_LOG_WARN, "Image and weight have different sizes (%dx%d vs %dx%d)!\n",
+                     image->numCols, image->numRows, readout->weight->numCols, readout->weight->numRows);
         }
         // New reference
@@ -239,4 +250,47 @@
     return (position > 0);
 }
+
+// Check the type for a current image against a previous type
+static psElemType checkTypes(psElemType previous, // Previously defined type, or 0
+                             psElemType current // Current type
+                            )
+{
+    if (previous == 0) {
+        return current;
+    }
+
+    if (previous != current) {
+        psLogMsg(__func__, PS_LOG_WARN, "Images within the HDU are of different types "
+                 "(%x vs %x) --- promoting\n", previous, current);
+        return PS_MAX(previous, current);
+    }
+
+    return previous;
+}
+
+
+// Paste the source image into the target, according to the provided region.  The source is then updated to
+// reference the region within the target.
+static void pasteImage(psImage *target, // Target image, into which the paste is made
+                       psImage **sourcePtr,// Source image, from which the paste is made, and then changed
+                       psRegion *region // Image section into which to paste
+                      )
+{
+    psImage *source = *sourcePtr;       // Dereference pointer, for convenience
+    if (source->numCols != region->x1 - region->x0 || source->numRows != region->y1 - region->y0) {
+        psString regionString = psRegionToString(*region);
+        psLogMsg(__func__, PS_LOG_WARN, "Image size (%dx%d) does not match region (%s).\n",
+                 source->numCols, source->numRows, regionString);
+        psFree(regionString);
+    }
+    psImageOverlaySection(target, source, region->x0, region->y0, "=");
+
+    // Reference the HDU version, so that subsequent changes will touch the HDU
+    psFree(source);
+    *sourcePtr = psImageSubset(target, *region);
+
+    return;
+}
+
 
 // Generate the HDU, given a list of cells below that HDU.  This is the main engine function, that does all
@@ -248,5 +302,7 @@
     // Check the number of readouts is consistent within the HDU
     int numReadouts = -1;               // Number of readouts
-    psElemType type = 0;                // Type of readout images
+    psElemType imageType = 0;           // Type of readout images
+    psElemType maskType = 0;   // Type of readout masks
+    psElemType weightType = 0;          // Type of readout weights
     {
         psListIterator *iter = psListIteratorAlloc(cells, PS_LIST_HEAD, false); // Iterator for cells
@@ -264,14 +320,16 @@
             for (int i = 0; i < numReadouts; i++) {
                 pmReadout *readout = readouts->data[i]; // The readout
-                if (!readout || !readout->image) {
+                if (!readout) {
                     continue;
                 }
-                psElemType imageType = readout->image->type.type; // Type for this image
-                if (type == 0) {
-                    type = imageType;
-                } else if (type != imageType) {
-                    psLogMsg(__func__, PS_LOG_WARN, "Images within the HDU are of different types "
-                             "(%x vs %x) --- promoting\n", type, imageType);
-                    type = PS_MAX(type, imageType);
+
+                if (readout->image) {
+                    imageType = checkTypes(imageType, readout->image->type.type);
+                }
+                if (readout->mask) {
+                    maskType = checkTypes(maskType, readout->mask->type.type);
+                }
+                if (readout->weight) {
+                    weightType = checkTypes(weightType, readout->weight->type.type);
                 }
             }
@@ -279,5 +337,5 @@
         psFree(iter);
     }
-    if (numReadouts == 0 || type == 0) {
+    if (numReadouts == 0 || (imageType == 0 && maskType == 0 && weightType == 0)) {
         // Nothing from which to create an HDU
         psFree(cells);
@@ -293,9 +351,27 @@
 
     // Generate the HDU
-    hdu->images = psArrayAlloc(numReadouts);
-    for (int i = 0; i < numReadouts; i++) {
-        psImage *image = psImageAlloc(xSize, ySize, type);
-        psImageInit(image, 0.0);
-        hdu->images->data[i] = image;
+    if (imageType) {
+        hdu->images = psArrayAlloc(numReadouts);
+        for (int i = 0; i < numReadouts; i++) {
+            psImage *image = psImageAlloc(xSize, ySize, imageType);
+            psImageInit(image, 0.0);
+            hdu->images->data[i] = image;
+        }
+    }
+    if (maskType) {
+        hdu->masks = psArrayAlloc(numReadouts);
+        for (int i = 0; i < numReadouts; i++) {
+            psImage *mask = psImageAlloc(xSize, ySize, maskType);
+            psImageInit(mask, 0);
+            hdu->masks->data[i] = mask;
+        }
+    }
+    if (weightType) {
+        hdu->weights = psArrayAlloc(numReadouts);
+        for (int i = 0; i < numReadouts; i++) {
+            psImage *weight = psImageAlloc(xSize, ySize, weightType);
+            psImageInit(weight, 0.0);
+            hdu->weights->data[i] = weight;
+        }
     }
 
@@ -319,4 +395,6 @@
             psArray *readouts = cell->readouts; // Array of readouts
             psArray *hduImages = hdu->images; // Array of images in the HDU
+            psArray *hduMasks = hdu->masks; // Array of masks in the HDU
+            psArray *hduWeights = hdu->weights; // Array of weights in the HDU
             for (int i = 0; i < readouts->n; i++) {
                 pmReadout *readout = readouts->data[i]; // The readout of interest
@@ -324,19 +402,14 @@
                     continue;
                 }
-                psImage *image = readout->image; // The image pixels
-                psImage *hduImage = hduImages->data[i]; // The HDU image of interest
-
-                if (image->numCols != trimsec->x1 - trimsec->x0 ||
-                        image->numRows != trimsec->y1 - trimsec->y0) {
-                    psString trimsecString = psRegionToString(*trimsec);
-                    psLogMsg(__func__, PS_LOG_WARN, "Image size (%dx%d) does not match CELL.TRIMSEC (%s).\n",
-                             image->numCols, image->numRows, trimsecString);
-                    psFree(trimsecString);
-                }
-                psImageOverlaySection(hduImage, image, trimsec->x0, trimsec->y0, "=");
-
-                // Reference the HDU version, so that subsequent changes will touch the HDU
-                psFree(image);
-                readout->image = psImageSubset(hduImage, *trimsec);
+
+                if (readout->image) {
+                    pasteImage(hduImages->data[i], &readout->image, trimsec);
+                }
+                if (readout->mask) {
+                    pasteImage(hduMasks->data[i], &readout->mask, trimsec);
+                }
+                if (readout->weight) {
+                    pasteImage(hduWeights->data[i], &readout->weight, trimsec);
+                }
 
                 if (biassecs->n != readout->bias->n) {
@@ -351,16 +424,5 @@
                 while ((bias = psListGetAndIncrement(biasIter)) &&
                         (biassec = psListGetAndIncrement(biassecsIter))) {
-                    if (bias->numCols != biassec->x1 - biassec->x0 ||
-                            bias->numRows != biassec->y1 - biassec->y0) {
-                        psString biassecString = psRegionToString(*biassec);
-                        psLogMsg(__func__, PS_LOG_WARN, "Bias size (%dx%d) does not match CELL.BIASSEC (%s)."
-                                 "\n", bias->numCols, bias->numRows, biassecString);
-                        psFree(biassecString);
-                    }
-
-                    psImageOverlaySection(hduImage, bias, biassec->x0, biassec->y0, "=");
-
-                    // Reference the HDU version, so that subsequent changes will touch the HDU
-                    bias = psImageSubset(hduImage, *biassec);
+                    pasteImage(hduImages->data[i], &bias, biassec);
                     psListAdd(newBias, PS_LIST_TAIL, bias);
                 }
