Index: trunk/psModules/src/camera/pmFPARead.c
===================================================================
--- trunk/psModules/src/camera/pmFPARead.c	(revision 16355)
+++ trunk/psModules/src/camera/pmFPARead.c	(revision 16365)
@@ -32,7 +32,216 @@
 } fpaReadType;
 
+// Desired type for pixels; the index corresponds to the fpaReadType, above.
+static psElemType pixelTypes[] = {
+    PS_TYPE_F32,
+    PS_TYPE_MASK,
+    PS_TYPE_F32,
+    0
+};
+
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // File-static functions
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Determine number of readouts in the FITS file
+// In the process, reads the header and concepts
+static bool cellNumReadouts(pmCell *cell,    // Cell of interest
+                            psFits *fits     // FITS file
+    )
+{
+    assert(cell);
+    assert(fits);
+
+    // Get the HDU and read the header
+    pmHDU *hdu = pmHDUFromCell(cell);   // The HDU
+    if (!hdu || hdu->blankPHU) {
+        psError(PS_ERR_IO, true, "Unable to find HDU");
+        return false;
+    }
+    if (!pmCellReadHeader(cell, fits)) {
+        psError(PS_ERR_IO, false, "Unable to read header for cell!\n");
+        return false;
+    }
+    if (!pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CELLS |
+                            PM_CONCEPT_SOURCE_DEFAULTS, true, NULL)) {
+        psError(PS_ERR_IO, false, "Failed to read concepts for cell.\n");
+        return false;
+    }
+
+    // Get the size of the third dimension
+    bool mdok;                          // Status of MD lookup
+    int naxis = psMetadataLookupS32(&mdok, hdu->header, "NAXIS"); // The number of axes
+    if (!mdok) {
+        psError(PS_ERR_IO, true, "Unable to find NAXIS in header for extension %s\n", hdu->extname);
+        return false;
+    }
+    if (naxis == 0) {
+        // No pixels to read
+        psError(PS_ERR_IO, true, "No pixels in extension %s.", hdu->extname);
+        return false;
+    }
+    if (naxis < 2 || naxis > 3) {
+        psError(PS_ERR_IO, true, "NAXIS in header of extension %s (= %d) is not valid.\n",
+                hdu->extname, naxis);
+        return false;
+    }
+    int naxis3;                     // Number of image planes
+    if (naxis == 3) {
+        naxis3 = psMetadataLookupS32(&mdok, hdu->header, "NAXIS3");
+        if (!mdok) {
+            psError(PS_ERR_IO, true, "Unable to find NAXIS3 in header for extension %s\n", hdu->extname);
+            return false;
+        }
+    } else {
+        naxis3 = 1;
+    }
+
+    return naxis3;
+}
+
+
+// Determine readout scan properties: the next and last scans
+// Requires that cellNumReadouts() has been called before (for header and concepts to have been read)
+// In the process, adjusts the TRIMSEC
+static bool readoutScanProperties(int *next, // Index of next scan
+                                  int *last, // Index of last scan
+                                  pmReadout *readout, // Readout of interest
+                                  int numScans, // Number of scans to read at a time
+                                  fpaReadType type // Type of image
+    )
+{
+    assert(next);
+    assert(last);
+    assert(readout);
+
+    psImage *image;                     // Appropriate image from readout
+    switch (type) {
+      case FPA_READ_TYPE_IMAGE:
+        image = readout->image;
+        break;
+      case FPA_READ_TYPE_MASK:
+        image = readout->mask;
+        break;
+      case FPA_READ_TYPE_WEIGHT:
+        image = readout->weight;
+        break;
+      default:
+        psAbort("Unknown read type: %x\n", type);
+    }
+
+    // Header and concepts have been read by a call to cellNumReadouts(), so we can just assume they're there.
+
+    // Get the trim and bias sections
+    pmCell *cell = readout->parent;     // Parent cell
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    pmHDU *hdu = pmHDUFromCell(cell);   // HDU for data
+    bool mdok = true;                   // Status of MD lookup
+    psRegion *trimsec = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.TRIMSEC"); // Trim sections
+    if (!mdok || !trimsec || psRegionIsNaN(*trimsec)) {
+        psError(PS_ERR_IO, true, "CELL.TRIMSEC is not set.\n");
+        return false;
+    }
+    int readdir = psMetadataLookupS32(&mdok, cell->concepts, "CELL.READDIR"); // Read direction
+    if (!mdok || readdir == 0 || (readdir != 1 && readdir != 2)) {
+        psError(PS_ERR_IO, true, "CELL.READDIR is not set to -1 or +1.\n");
+        return false;
+    }
+
+    // Rationalize trimsec against naxis1, naxis2:  valid range for trimsec is 1-Nx,1-Ny
+    if (trimsec->x1 < 1) {
+        int naxis1 = psMetadataLookupS32(&mdok, hdu->header, "NAXIS1"); // The number of columns
+        if (!mdok) {
+            psError(PS_ERR_IO, true, "Unable to find NAXIS1 in header for extension %s\n", hdu->extname);
+            return false;
+        }
+        trimsec->x1 = naxis1 + trimsec->x1;
+    }
+    if (trimsec->y1 < 1) {
+        int naxis2 = psMetadataLookupS32(&mdok, hdu->header, "NAXIS2"); // The number of columns
+        if (!mdok) {
+            psError(PS_ERR_IO, true, "Unable to find NAXIS2 in header for extension %s\n", hdu->extname);
+            return false;
+        }
+        trimsec->y1 = naxis2 + trimsec->y1;
+    }
+
+
+    // Calculate the segment offset and upper limit
+    if (readdir == 1) {
+        // Reading rows
+        if (numScans == 0) {
+            *next = trimsec->x0;
+        } else {
+            *next = image ? readout->row0 + numScans : 0;
+        }
+        *last = trimsec->y1;
+    } else {
+        // Reading cols
+        if (numScans == 0) {
+            *next = trimsec->y0;
+        } else {
+            *next = image ? readout->row0 + numScans : 0;
+        }
+        *last = trimsec->x1;
+    }
+
+    return true;
+}
+
+static psImage **readoutImageByType(pmReadout *readout, // Readout of interest
+                                    fpaReadType type // Type of image
+    )
+{
+    switch (type) {
+      case FPA_READ_TYPE_IMAGE:
+        return &readout->image;
+      case FPA_READ_TYPE_MASK:
+        return &readout->mask;
+      case FPA_READ_TYPE_WEIGHT:
+        return &readout->weight;
+      default:
+        psAbort("Unknown read type: %x\n", type);
+    }
+}
+
+
+static bool readoutMore(pmReadout *readout, // Readout of interest
+                        psFits *fits,    // FITS file
+                        int z,          // Plane number
+                        int numScans,   // Number of scans to read at a time
+                        fpaReadType type // Type of image
+    )
+{
+    assert(readout);
+    assert(fits);
+
+    psImage *image = *readoutImageByType(readout, type);
+
+    if (!image) {
+        return true;
+    } else if (numScans == 0) {
+        // Can only read the entire image once
+        return false;
+    }
+
+    pmCell *cell = readout->parent;     // Parent cell
+    if (!cell) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find parent cell.");
+        return false;
+    }
+    int naxis3 = cellNumReadouts(cell, fits); // Number of planes
+    if (z < naxis3) {
+        return false;
+    }
+
+    int next;                           // Next position
+    int last;                           // Last position
+    if (!readoutScanProperties(&next, &last, readout, numScans, type)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to determine readout properties.");
+        return false;
+    }
+
+    return (next < last);
+}
 
 // Carve a readout from the image pixels
@@ -41,5 +250,5 @@
                          const psRegion *trimsec, // Trim section
                          const psList *biassecs, // Bias sections
-                         fpaReadType type
+                         fpaReadType type // Type of image
                         )
 {
@@ -63,56 +272,39 @@
                                  );
 
-    // place the image subset in the appropriate target location, freeing if needed
-    switch (type) {
-      case FPA_READ_TYPE_IMAGE:
-        if (readout->image) {
-            psFree (readout->image);
-        }
-        readout->image = psImageSubset(image, region);
-        break;
-      case FPA_READ_TYPE_MASK:
-        if (readout->mask) {
-            psFree (readout->mask);
-        }
-        readout->mask = psImageSubset(image, region);
-        break;
-      case FPA_READ_TYPE_WEIGHT:
-        if (readout->weight) {
-            psFree (readout->weight);
-        }
-        readout->weight = psImageSubset(image, region);
-        break;
-      default:
-        psAbort("Unknown read type: %x\n", type);
-    }
-
-    // Get the list of overscans
-    // XXX should this step only be performed for IMAGE, not MASK and WEIGHT types?
-    // XXX that would allow us to overlay a MASK and WEIGHT which have been trimmed...
-    if (readout->bias->n != 0) {
-        // Make way!
-        psFree(readout->bias);
-        readout->bias = psListAlloc(NULL);
-    }
-    psListIterator *iter = psListIteratorAlloc((psList*)biassecs, PS_LIST_HEAD, false); // Iterator
-    psRegion *biassec = NULL;       // A BIASSEC region from the list
-    while ((biassec = psListGetAndIncrement(iter))) {
-        if (psRegionIsNaN(*biassec)) {
-            psString regionString = psRegionToString(*biassec);
-            psError(PS_ERR_IO, true, "Invalid bias section: %s\n", regionString);
-            psFree(regionString);
-            psFree(readout);
-            return false;
-        }
-        psRegion region = psRegionSet(PS_MAX(biassec->x0 - readout->col0, 0), // x0
-                                      PS_MIN(biassec->x1 - readout->col0, image->numCols), // x1
-                                      PS_MAX(biassec->y0 - readout->row0, 0), // y0
-                                      PS_MIN(biassec->y1 - readout->row0, image->numRows) // y1
-                                     );
-        psImage *overscan = psImageSubset(image, region);
-        psListAdd(readout->bias, PS_LIST_TAIL, overscan);
-        psFree(overscan);
-    }
-    psFree(iter);
+    // Place the image subset in the appropriate target location, freeing if needed
+    psImage **target = readoutImageByType(readout, type); // Target image
+    if (*target) {
+        psFree(*target);
+    }
+    *target = psImageSubset(image, region);
+
+    // Get the list of overscans: only for IMAGE types (no overscan for MASK and WEIGHT)
+    if (type == FPA_READ_TYPE_IMAGE) {
+        if (readout->bias->n != 0) {
+            // Make way!
+            psFree(readout->bias);
+            readout->bias = psListAlloc(NULL);
+        }
+        psListIterator *iter = psListIteratorAlloc((psList*)biassecs, PS_LIST_HEAD, false); // Iterator
+        psRegion *biassec = NULL;       // A BIASSEC region from the list
+        while ((biassec = psListGetAndIncrement(iter))) {
+            if (psRegionIsNaN(*biassec)) {
+                psString regionString = psRegionToString(*biassec);
+                psError(PS_ERR_IO, true, "Invalid bias section: %s\n", regionString);
+                psFree(regionString);
+                psFree(readout);
+                return false;
+            }
+            psRegion region = psRegionSet(PS_MAX(biassec->x0 - readout->col0, 0), // x0
+                                          PS_MIN(biassec->x1 - readout->col0, image->numCols), // x1
+                                          PS_MAX(biassec->y0 - readout->row0, 0), // y0
+                                          PS_MIN(biassec->y1 - readout->row0, image->numRows) // y1
+                );
+            psImage *overscan = psImageSubset(image, region);
+            psListAdd(readout->bias, PS_LIST_TAIL, overscan);
+            psFree(overscan);
+        }
+        psFree(iter);
+    }
 
     return true;
@@ -123,5 +315,4 @@
 // out the edges of the region with 'bad' pixels.  The output image always has max-min rows.
 // The region represents the maximum bounds of the full image
-
 static psImage *readoutReadComponent(psImage *image, // Image into which to read
                                      psFits *fits, // FITS file from which to read
@@ -131,5 +322,6 @@
                                      int max,   // Maximum row/col number to read
                                      int z,     // Image plane to read
-                                     float bad // Bad value
+                                     float bad, // Bad value
+                                     psElemType type // Expected type for image
     )
 {
@@ -138,18 +330,16 @@
     assert((readdir == 1) || (readdir == 2));
 
-    int nRead = 0;
-    int nScans = max - min;
-    assert (nScans > 0);
+    int nRead = 0;                      // Number of scans read
+    int nScans = max - min;             // Number of scans desired
+    assert(nScans > 0);
 
     psRegion toRead = *fullImage;  // full image region
 
-    int dX = 0;
-    int dY = 0;
-    int nX = 0;
-    int nY = 0;
+    int dX = 0, dY = 0;                 // Offset from image in FITS file to lower left corner of what's read
+    int nX = 0, nY = 0;                 // Size of region to read
 
     if (readdir == 1) {
-        toRead.y0 = PS_MAX (toRead.y0, min);
-        toRead.y1 = PS_MIN (toRead.y1, max);
+        toRead.y0 = PS_MAX(toRead.y0, min);
+        toRead.y1 = PS_MIN(toRead.y1, max);
         nRead = toRead.y1 - toRead.y0;
         if (min < fullImage->y0) {
@@ -159,6 +349,6 @@
         nY = nScans;
     } else {
-        toRead.x0 = PS_MAX (toRead.x0, min);
-        toRead.x1 = PS_MIN (toRead.x1, max);
+        toRead.x0 = PS_MAX(toRead.x0, min);
+        toRead.x1 = PS_MIN(toRead.x1, max);
         nRead = toRead.x1 - toRead.x0;
         if (min < fullImage->x0) {
@@ -174,12 +364,15 @@
     psTrace("psModules.camera", 7, "Image is %dx%d\n", image->numCols, image->numRows);
 
-    // XXX: We only support F32 for now
-    if (image->type.type != PS_TYPE_F32) {
-        psImage *temp = psImageCopy(NULL, image, PS_TYPE_F32);
+    // Ensure the pixel type corresponds to what we desire
+    if (image->type.type != type) {
+        psImage *temp = psImageCopy(NULL, image, type);
         psFree(image);
         image = temp;
     }
 
+    // Resize the image so that it matches the number of scans requested
     // XXX this modification is not carried back up stream: it affects readout->row0,col0
+    //
+    // XXXXX Do we really want to do this???  Why???
     if (nRead < nScans) {
         // The region of interest is smaller than the number of pixels we want.
@@ -195,4 +388,129 @@
 }
 
+// Read a chunk of a readout (or the whole lot)
+static bool readoutReadChunk(pmReadout *readout, // Readout into which to read
+                             psFits *fits, // FITS file
+                             int z,     // Desired image plane
+                             int numScans, // Number of scans (row or col depends on CELL.READDIR); 0 for all
+                             fpaReadType type // Type of image
+    )
+{
+    assert(readout);
+    assert(fits);
+    assert(z >= 0);
+    assert(numScans >= 0);
+
+    psImage **imagePtr = readoutImageByType(readout, type); // Pointer to the image of interest
+    if (*imagePtr && numScans == 0) {
+        psError(PS_ERR_UNKNOWN, true, "Already read entire image --- won't clobber.");
+        return false;
+    }
+
+    pmCell *cell = readout->parent;     // The parent cell
+    if (!cell) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find parent cell.");
+        return false;
+    }
+
+    int naxis3 = cellNumReadouts(cell, fits); // Number of image planes
+    if (z >= naxis3) {
+        psError(PS_ERR_IO, false, "Desired image plane (%d) exceeds available number (%d).",
+                z, naxis3);
+        return false;
+    }
+
+    int next;                           // Next position
+    int last;                           // Last position
+    if (!readoutScanProperties(&next, &last, readout, numScans, type)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to determine readout properties.");
+        return false;
+    }
+    if (next >= last) {
+        psError(PS_ERR_IO, true, "No more of the readout to read.");
+        return false;
+    }
+
+    pmHDU *hdu = pmHDUFromCell(cell);   // The HDU
+    assert(hdu && !hdu->blankPHU);      // Checked by cellNumReadouts()
+
+    bool mdok;                          // Status of MD lookup
+    int readdir = psMetadataLookupS32(&mdok, cell->concepts, "CELL.READDIR"); // Read direction
+    if (!mdok || readdir == 0 || (readdir != 1 && readdir != 2)) {
+        psError(PS_ERR_IO, true, "CELL.READDIR is not set to -1 or +1.\n");
+        return false;
+    }
+    float bad = psMetadataLookupF32(&mdok, cell->concepts, "CELL.BAD"); // Bad level
+    if (!mdok) {
+        psLogMsg(__func__, PS_LOG_WARN, "CELL.BAD is not set --- assuming zero.\n");
+        bad = 0.0;
+    }
+    psRegion *trimsec = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.TRIMSEC"); // Trim sections
+    if (!mdok || !trimsec || psRegionIsNaN(*trimsec)) {
+        psError(PS_ERR_IO, true, "CELL.TRIMSEC is not set.\n");
+        return false;
+    }
+
+    // Check the third dimension
+    int naxis = psMetadataLookupS32(&mdok, hdu->header, "NAXIS"); // The number of axes
+    if (!mdok) {
+        psError(PS_ERR_IO, true, "Unable to find NAXIS in header for extension %s\n", hdu->extname);
+        return false;
+    }
+    if (naxis == 0) {
+        // No pixels to read
+        psError(PS_ERR_IO, true, "No pixels in extension %s.", hdu->extname);
+        return false;
+    }
+    if (naxis < 2 || naxis > 3) {
+        psError(PS_ERR_IO, true, "NAXIS in header of extension %s (= %d) is not valid.\n",
+                hdu->extname, naxis);
+        return false;
+    }
+
+    // Calculate limits, adjust readout->row0,col0
+    if (readdir == 1) {
+        // Reading rows
+        readout->row0 = next;
+        readout->col0 = trimsec->x0;
+    } else {
+        // Reading cols
+        readout->col0 = next;
+        readout->row0 = trimsec->y0;
+    }
+    int upper = next + numScans;        // Upper limit to next section
+
+    // Blow away existing data.
+    // Do this before returning, so that we're not returning data from a previous read
+    psImage **image = readoutImageByType(readout, type);
+    psFree(*image);
+    *image = NULL;
+    *image = readoutReadComponent(*image, fits, trimsec, readdir, next, upper, z, bad, pixelTypes[type]);
+
+    // Read overscans only for "image" type --- weights and masks shouldn't record overscans
+    if (type == FPA_READ_TYPE_IMAGE) {
+        // Blow away existing data
+        while (readout->bias->n > 0) {
+            psListRemove(readout->bias, PS_LIST_HEAD);
+        }
+
+        // Get the new bias sections
+        psList *biassecs = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.BIASSEC"); // Bias sections
+        if (!mdok || !biassecs) {
+            psError(PS_ERR_IO, true, "CELL.BIASSEC is not set.\n");
+            return false;
+        }
+        psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false); // Iterator
+        psRegion *biassec = NULL;           // Bias section from iteration
+        while ((biassec = psListGetAndIncrement(biassecsIter))) {
+            psImage *bias = readoutReadComponent(NULL, fits, biassec, readdir, next, upper, z,
+                                                 bad, pixelTypes[type]); // The bias
+            psListAdd(readout->bias, PS_LIST_TAIL, bias);
+            psFree(bias);                   // Drop reference
+        }
+        psFree(biassecsIter);
+    }
+
+    return true;
+}
 
 // Read into an cell; this is the engine for pmCellRead, pmCellReadMask, pmCellReadWeight
@@ -260,17 +578,14 @@
     // set up pointers for the different possible image arrays
     psArray *imageArray = NULL; // Array of images in the HDU
-    psElemType imageType = PS_TYPE_F32; // Expected type for image
+    psElemType imageType = pixelTypes[type]; // Expected type for image
     switch (type) {
       case FPA_READ_TYPE_IMAGE:
         imageArray = hdu->images;
-        imageType = PS_TYPE_F32;
         break;
       case FPA_READ_TYPE_MASK:
         imageArray = hdu->masks;
-        imageType = PS_TYPE_MASK;
         break;
       case FPA_READ_TYPE_WEIGHT:
         imageArray = hdu->weights;
-        imageType = PS_TYPE_F32;
         break;
       default:
@@ -387,5 +702,6 @@
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
-// XXX need to pass back the error conditions as well as the 'keep going' state
+// pmReadoutReadNext is maintained here (for now) to maintain backwards compatibility.
+// pmReadoutReadNext has been replaced by pmReadoutRead, pmReadoutReadChunk, pmReadoutMore
 bool pmReadoutReadNext(bool *status, pmReadout *readout, psFits *fits, int z, int numScans)
 {
@@ -405,5 +721,4 @@
     if (!hdu || hdu->blankPHU) {
         // XXX is this an error condition?
-        psTrace("psModules.camera", 7, "no HDU or pixel-less PHU: skipping\n");
         *status = true;
         return false;
@@ -453,5 +768,4 @@
     if (naxis == 0) {
         // No pixels to read, as for a PHU.
-        psTrace("psModules.camera", 7, "pixel-less HDU: skipping\n");
         *status = true;
         return false;
@@ -472,5 +786,4 @@
     if (z >= naxis3) {
         // Nothing to see here.  Move along.
-        psTrace("psModules.camera", 7, "requested plane off edge of cube: skipping\n");
         *status = true;
         return false;
@@ -547,5 +860,6 @@
 
     // Get the new the trim section
-    readout->image = readoutReadComponent(readout->image, fits, trimsec, readdir, offset, upper, z, bad); // The image
+    readout->image = readoutReadComponent(readout->image, fits, trimsec, readdir, offset, upper, z, bad,
+                                          PS_TYPE_F32); // The image
 
     // Get the new bias sections
@@ -553,5 +867,6 @@
     psRegion *biassec = NULL;           // Bias section from iteration
     while ((biassec = psListGetAndIncrement(biassecsIter))) {
-        psImage *bias = readoutReadComponent(NULL, fits, biassec, readdir, offset, upper, z, bad); // The bias
+        psImage *bias = readoutReadComponent(NULL, fits, biassec, readdir, offset, upper, z, bad,
+                                             PS_TYPE_F32); // The bias
         psListAdd(readout->bias, PS_LIST_TAIL, bias);
         psFree(bias);                   // Drop reference
@@ -564,8 +879,43 @@
 
 
+
+bool pmReadoutMore(pmReadout *readout, psFits *fits, int z, int numScans)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return readoutMore(readout, fits, z, numScans, FPA_READ_TYPE_IMAGE);
+}
+
+bool pmReadoutReadChunk(pmReadout *readout, psFits *fits, int z, int numScans)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_INT_NONNEGATIVE(z, false);
+    PS_ASSERT_INT_NONNEGATIVE(numScans, false);
+
+    return readoutReadChunk(readout, fits, z, numScans, FPA_READ_TYPE_IMAGE);
+}
+
+bool pmReadoutRead(pmReadout *readout, psFits *fits, int z)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return readoutReadChunk(readout, fits, z, 0, FPA_READ_TYPE_IMAGE);
+}
+
+int pmCellNumReadouts(pmCell *cell, psFits *fits)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return cellNumReadouts(cell, fits);
+}
+
 bool pmCellRead(pmCell *cell, psFits *fits, psDB *db)
 {
     PS_ASSERT_PTR_NON_NULL(cell, false);
-    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
 
     return cellRead(cell, fits, db, FPA_READ_TYPE_IMAGE);
@@ -575,5 +925,5 @@
 {
     PS_ASSERT_PTR_NON_NULL(chip, false);
-    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
 
     return chipRead(chip, fits, db, FPA_READ_TYPE_IMAGE);
@@ -583,5 +933,5 @@
 {
     PS_ASSERT_PTR_NON_NULL(fpa, false);
-    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
 
     return fpaRead(fpa, fits, db, FPA_READ_TYPE_IMAGE);
@@ -593,8 +943,16 @@
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
+bool pmReadoutReadMask(pmReadout *readout, psFits *fits, int z)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return readoutReadChunk(readout, fits, z, 0, FPA_READ_TYPE_MASK);
+}
+
 bool pmCellReadMask(pmCell *cell, psFits *fits, psDB *db)
 {
     PS_ASSERT_PTR_NON_NULL(cell, false);
-    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
 
     return cellRead(cell, fits, db, FPA_READ_TYPE_MASK);
@@ -604,5 +962,5 @@
 {
     PS_ASSERT_PTR_NON_NULL(chip, false);
-    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
 
     return chipRead(chip, fits, db, FPA_READ_TYPE_MASK);
@@ -612,5 +970,5 @@
 {
     PS_ASSERT_PTR_NON_NULL(fpa, false);
-    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
 
     return fpaRead(fpa, fits, db, FPA_READ_TYPE_MASK);
@@ -621,8 +979,16 @@
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
+bool pmReadoutReadWeight(pmReadout *readout, psFits *fits, int z)
+{
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    return readoutReadChunk(readout, fits, z, 0, FPA_READ_TYPE_WEIGHT);
+}
+
 bool pmCellReadWeight(pmCell *cell, psFits *fits, psDB *db)
 {
     PS_ASSERT_PTR_NON_NULL(cell, false);
-    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
 
     return cellRead(cell, fits, db, FPA_READ_TYPE_WEIGHT);
@@ -632,5 +998,5 @@
 {
     PS_ASSERT_PTR_NON_NULL(chip, false);
-    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
 
     return chipRead(chip, fits, db, FPA_READ_TYPE_WEIGHT);
@@ -640,5 +1006,5 @@
 {
     PS_ASSERT_PTR_NON_NULL(fpa, false);
-    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
 
     return fpaRead(fpa, fits, db, FPA_READ_TYPE_WEIGHT);
@@ -652,5 +1018,5 @@
 {
     PS_ASSERT_PTR_NON_NULL(cell, false);
-    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
 
     return cellRead(cell, fits, db, FPA_READ_TYPE_HEADER);
@@ -660,5 +1026,5 @@
 {
     PS_ASSERT_PTR_NON_NULL(chip, false);
-    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
 
     return chipRead(chip, fits, db, FPA_READ_TYPE_HEADER);
@@ -668,5 +1034,5 @@
 {
     PS_ASSERT_PTR_NON_NULL(fpa, false);
-    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_FITS_NON_NULL(fits, false);
 
     return fpaRead(fpa, fits, db, FPA_READ_TYPE_HEADER);
@@ -680,5 +1046,5 @@
 {
     PS_ASSERT_PTR_NON_NULL(cell, 0);
-    PS_ASSERT_PTR_NON_NULL(fits, 0);
+    PS_ASSERT_FITS_NON_NULL(fits, 0);
     PS_ASSERT_STRING_NON_EMPTY(name, 0);
 
@@ -739,5 +1105,5 @@
 {
     PS_ASSERT_PTR_NON_NULL(chip, 0);
-    PS_ASSERT_PTR_NON_NULL(fits, 0);
+    PS_ASSERT_FITS_NON_NULL(fits, 0);
     PS_ASSERT_STRING_NON_EMPTY(name, 0);
 
@@ -756,5 +1122,5 @@
 {
     PS_ASSERT_PTR_NON_NULL(fpa, 0);
-    PS_ASSERT_PTR_NON_NULL(fits, 0);
+    PS_ASSERT_FITS_NON_NULL(fits, 0);
     PS_ASSERT_STRING_NON_EMPTY(name, 0);
 
Index: trunk/psModules/src/camera/pmFPARead.h
===================================================================
--- trunk/psModules/src/camera/pmFPARead.h	(revision 16355)
+++ trunk/psModules/src/camera/pmFPARead.h	(revision 16365)
@@ -4,6 +4,6 @@
  * @author Paul Price, IfA
  *
- * @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
- * @date $Date: 2007-06-12 22:22:33 $
+ * @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-02-08 03:16:12 $
  * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
  */
@@ -15,5 +15,28 @@
 /// @{
 
-/// Read a readout incrementally
+/// Check to see if there is more to read when reading a readout incrementally
+bool pmReadoutMore(pmReadout *readout,  ///< Readout of interest
+                   psFits *fits,        ///< FITS file from which to read
+                   int z,               ///< Readout number/plane; zero-offset indexing
+                   int numScans         ///< Number of scans (rows/cols) to read
+    );
+
+/// Read a chunk of a readout.
+///
+/// Allows reading the readout incrementally
+bool pmReadoutReadChunk(pmReadout *readout,  ///< Readout of interest
+                        psFits *fits,        ///< FITS file from which to read
+                        int z,               ///< Readout number/plane; zero-offset indexing
+                        int numScans         ///< Number of scans (rows/cols) to read
+    );
+
+/// Read the entire readout
+bool pmReadoutRead(pmReadout *readout,  ///< Readout of interest
+                   psFits *fits,        ///< FITS file from which to read
+                   int z               ///< Readout number/plane; zero-offset indexing
+    );
+
+/// Read a readout incrementally --- this is maintained temporarily only for backwards compatibility; it has
+/// been replaced by pmReadoutRead, pmReadoutReadChunk and pmReadoutMore.
 ///
 /// Multiple calls to this function moves through a readout within a cell incrementally.  It is required to
@@ -25,10 +48,15 @@
 /// Use pmReadoutWriteNext to write the data that's read by this function.  This function is intended for
 /// reading in many readouts into memory at once (e.g., for stacking) where the input is not written out.
-bool pmReadoutReadNext(bool *status,	// non-error exit condition?
-		       pmReadout *readout, // Readout into which to read
+bool pmReadoutReadNext(bool *status,    // non-error exit condition?
+                       pmReadout *readout, // Readout into which to read
                        psFits *fits,    // FITS file from which to read
                        int z,           // Readout number/plane; zero-offset indexing
                        int numRows      // The number of rows to read
                       );
+
+/// Return the number of readouts within a cell
+///
+/// This function is type-independent (doesn't matter if you are interested in the image/mask/weight).
+int pmCellNumReadouts(pmCell *cell, psFits *fits);
 
 /// Read an entire cell
@@ -57,10 +85,34 @@
               );
 
+// Mask functions follow
+
+/// Check to see if there is more to read when reading a readout incrementally into the mask
+bool pmReadoutMoreMask(pmReadout *readout, ///< Readout of interest
+                       psFits *fits,    ///< FITS file from which to read
+                       int z,           ///< Readout number/plane; zero-offset indexing
+                       int numScans     ///< Number of scans (rows/cols) to read
+    );
+
+/// Read a chunk of a readout into the mask
+///
+/// Allows reading the readout incrementally
+bool pmReadoutReadChunkMask(pmReadout *readout, ///< Readout of interest
+                            psFits *fits, ///< FITS file from which to read
+                            int z,      ///< Readout number/plane; zero-offset indexing
+                            int numScans ///< Number of scans (rows/cols) to read
+    );
+
+/// Read the entire readout into the mask
+bool pmReadoutReadMask(pmReadout *readout, ///< Readout of interest
+                       psFits *fits,    ///< FITS file from which to read
+                       int z            ///< Readout number/plane; zero-offset indexing
+    );
+
 /// Read an entire cell into the mask
 ///
 /// Same as pmCellRead, but reads into the mask element of the readouts.
-bool pmCellReadMask(pmCell *cell,           // Cell to read into
-                    psFits *fits,           // FITS file from which to read
-                    psDB *db                // Database handle, for "concepts" ingest
+bool pmCellReadMask(pmCell *cell,       // Cell to read into
+                    psFits *fits,       // FITS file from which to read
+                    psDB *db            // Database handle, for "concepts" ingest
                    );
 
@@ -68,7 +120,7 @@
 ///
 /// Same as pmChipRead, but reads into the mask element of the readouts.
-bool pmChipReadMask(pmChip *chip,           // Chip to read into
-                    psFits *fits,           // FITS file from which to read
-                    psDB *db                // Database handle, for "concepts" ingest
+bool pmChipReadMask(pmChip *chip,       // Chip to read into
+                    psFits *fits,       // FITS file from which to read
+                    psDB *db            // Database handle, for "concepts" ingest
                    );
 
@@ -76,8 +128,32 @@
 ///
 /// Same as pmFPARead, but reads into the mask element of the readouts.
-bool pmFPAReadMask(pmFPA *fpa,              // FPA to read into
-                   psFits *fits,            // FITS file from which to read
-                   psDB *db                 // Database handle, for "concepts" ingest
+bool pmFPAReadMask(pmFPA *fpa,          // FPA to read into
+                   psFits *fits,        // FITS file from which to read
+                   psDB *db             // Database handle, for "concepts" ingest
                   );
+
+// Weight functions follow
+
+/// Check to see if there is more to read when reading a readout incrementally into the weight
+bool pmReadoutMoreWeight(pmReadout *readout, ///< Readout of interest
+                         psFits *fits,  ///< FITS file from which to read
+                         int z,         ///< Readout number/plane; zero-offset indexing
+                         int numScans   ///< Number of scans (rows/cols) to read
+    );
+
+/// Read a chunk of a readout into the weight
+///
+/// Allows reading the readout incrementally
+bool pmReadoutReadChunkWeight(pmReadout *readout, ///< Readout of interest
+                            psFits *fits, ///< FITS file from which to read
+                            int z,      ///< Readout number/plane; zero-offset indexing
+                            int numScans ///< Number of scans (rows/cols) to read
+    );
+
+/// Read the entire readout into the weight
+bool pmReadoutReadWeight(pmReadout *readout, ///< Readout of interest
+                         psFits *fits,  ///< FITS file from which to read
+                         int z          ///< Readout number/plane; zero-offset indexing
+    );
 
 /// Read an entire cell into the weight
@@ -109,6 +185,6 @@
 /// Same as pmCellRead, but reads only the headers of the readouts.
 bool pmCellReadHeaderSet(pmCell *cell,           // Cell to read into
-			 psFits *fits,           // FITS file from which to read
-			 psDB *db                // Database handle, for "concepts" ingest
+                         psFits *fits,           // FITS file from which to read
+                         psDB *db                // Database handle, for "concepts" ingest
     );
 
@@ -125,6 +201,6 @@
 /// Same as pmFPARead, but reads only the headers of the readouts.
 bool pmFPAReadHeaderSet(pmFPA *fpa,              // FPA to read into
-			psFits *fits,            // FITS file from which to read
-			psDB *db                 // Database handle, for "concepts" ingest
+                        psFits *fits,            // FITS file from which to read
+                        psDB *db                 // Database handle, for "concepts" ingest
     );
 
