Index: /trunk/psModules/src/camera/Makefile.am
===================================================================
--- /trunk/psModules/src/camera/Makefile.am	(revision 7167)
+++ /trunk/psModules/src/camera/Makefile.am	(revision 7168)
@@ -13,4 +13,5 @@
 	pmHDU.c \
 	pmHDUUtils.c \
+	pmHDUGenerate.c \
 	pmReadout.c \
 	pmChipMosaic.c \
@@ -33,4 +34,5 @@
 	pmHDU.h \
 	pmHDUUtils.h \
+	pmHDUGenerate.h \
 	pmReadout.h \
 	pmChipMosaic.h \
Index: /trunk/psModules/src/camera/pmFPAConstruct.c
===================================================================
--- /trunk/psModules/src/camera/pmFPAConstruct.c	(revision 7167)
+++ /trunk/psModules/src/camera/pmFPAConstruct.c	(revision 7168)
@@ -10,4 +10,5 @@
 #include "pmFPAview.h"
 #include "pmFPAUtils.h"
+#include "pmHDUUtils.h"
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -690,16 +691,5 @@
     psTrace(__func__, 1, "FPA:\n");
     if (fpa->hdu) {
-        psTrace(__func__, 2, "---> FPA is extension %s.\n", fpa->hdu->extname);
-        if (! fpa->hdu->images) {
-            psTrace(__func__, 2, "---> NO PIXELS read in for extension %s\n", fpa->hdu->extname);
-        }
-        if (header) {
-            if (fpa->hdu->header) {
-                psTrace(__func__, 2, "---> Header:\n");
-                psMetadataPrint(fpa->hdu->header, 8);
-            } else {
-                psTrace(__func__, 2, "---> NO HEADER read in for extension %s\n", fpa->hdu->extname);
-            }
-        }
+        pmHDUPrint(fpa->hdu, 2, header);
     }
     if (concepts) {
@@ -713,16 +703,5 @@
         pmChip *chip = chips->data[i]; // The chip
         if (chip->hdu) {
-            psTrace(__func__, 4, "---> Chip is extension %s.\n", chip->hdu->extname);
-            if (header) {
-                if (chip->hdu->header) {
-                    psTrace(__func__, 4, "---> Header:\n");
-                    psMetadataPrint(chip->hdu->header, 8);
-                } else {
-                    psTrace(__func__, 4, "---> NO HEADER read in for extension %s\n", chip->hdu->extname);
-                }
-            }
-            if (! chip->hdu->images) {
-                psTrace(__func__, 4, "---> NO PIXELS read in for extension %s\n", chip->hdu->extname);
-            }
+            pmHDUPrint(chip->hdu, 4, header);
         }
         if (concepts) {
@@ -736,16 +715,5 @@
             pmCell *cell = cells->data[j]; // The cell
             if (cell->hdu) {
-                psTrace(__func__, 6, "---> Cell is extension %s.\n", cell->hdu->extname);
-                if (header) {
-                    if (cell->hdu->header) {
-                        psTrace(__func__, 6, "---> Header:\n");
-                        psMetadataPrint(cell->hdu->header, 8);
-                    } else {
-                        psTrace(__func__, 6, "---> NO HEADER read in for extension %s\n", cell->hdu->extname);
-                    }
-                }
-                if (! cell->hdu->images) {
-                    psTrace(__func__, 6, "---> NO PIXELS read in for extension %s\n", cell->hdu->extname);
-                }
+                pmHDUPrint(cell->hdu, 6, header);
             }
             if (concepts) {
@@ -753,13 +721,14 @@
             }
 
-            psTrace(__func__, 7, "Readouts:\n");
             psArray *readouts = cell->readouts; // Array of readouts
             for (int k = 0; k < readouts->n; k++) {
                 pmReadout *readout = readouts->data[k]; // The readout
-                psTrace(__func__, 8, "row0: %d\n", readout->row0);
+                psTrace(__func__, 6, "Readout %d:\n", k);
+                psTrace(__func__, 7, "row0: %d\n", readout->row0);
+                psTrace(__func__, 7, "col0: %d\n", readout->col0);
                 psImage *image = readout->image; // The image
                 psList *bias = readout->bias; // The list of bias images
                 if (image) {
-                    psTrace(__func__, 8, "Image: [%d:%d,%d:%d] (%dx%d)\n", image->col0, image->col0 +
+                    psTrace(__func__, 7, "Image: [%d:%d,%d:%d] (%dx%d)\n", image->col0, image->col0 +
                             image->numCols, image->row0, image->row0 + image->numRows, image->numCols,
                             image->numRows);
@@ -769,5 +738,5 @@
                     psImage *biasImage = NULL; // Bias image from iteration
                     while ((biasImage = psListGetAndIncrement(biasIter))) {
-                        psTrace(__func__, 8, "Bias:  [%d:%d,%d:%d] (%dx%d)\n", biasImage->col0,
+                        psTrace(__func__, 7, "Bias:  [%d:%d,%d:%d] (%dx%d)\n", biasImage->col0,
                                 biasImage->col0 + biasImage->numCols, biasImage->row0,
                                 biasImage->row0 + biasImage->numRows, biasImage->numCols, biasImage->numRows);
Index: /trunk/psModules/src/camera/pmFPACopy.c
===================================================================
--- /trunk/psModules/src/camera/pmFPACopy.c	(revision 7167)
+++ /trunk/psModules/src/camera/pmFPACopy.c	(revision 7168)
@@ -10,346 +10,38 @@
 #include "pmHDU.h"
 #include "pmHDUUtils.h"
+#include "pmHDUGenerate.h"
+
 #include "pmFPACopy.h"
 
-#define MAX(x,y) ((x) > (y) ? (x) : (y))
-
-
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // File-static functions
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
-// Add cells in a chip to a list
-static bool addCellsFromChip(psList *list, // List of cells
-                             const pmChip *chip // The chip from which to add cells
-                            )
-{
-    assert(list);
-    assert(chip);
-
-    psArray *cells = chip->cells;       // Array of cells
-    bool result = true;                 // Result of adding cells
-    for (int i = 0; i < cells->n; i++) {
-        pmCell *cell = cells->data[i];  // A cell
-        result |= psListAdd(list, PS_LIST_TAIL, cell);
-    }
-
-    return result;
-}
-
-// Add cells in an FPA to a list
-static bool addCellsFromFPA(psList *list, // List of cells
-                            const pmFPA *fpa // The FPA from which to add cells
-                           )
-{
-    assert(list);
-    assert(fpa);
-
-    psArray *chips = fpa->chips;        // Array of chips
-    bool result = true;                 // Result of adding cells
-    for (int i = 0; i < chips->n; i++) {
-        pmChip *chip = chips->data[i];  // A chip
-        result |= addCellsFromChip(list, chip);
-    }
-
-    return result;
-}
-
-// Get a list of cells that share the HDU for the target cell
-static bool cellList(psList *targets,   // The list of target cells
-                     psList *sources,   // The list of source cells
-                     pmCell *targetCell, // The target cell
-                     pmCell *sourceCell // The source cell
-                    )
-{
-    assert(targetCell);
-    assert(sourceCell);
-
-    if (targetCell->hdu) {
-        if (targets) {
-            psListAdd(targets, PS_LIST_TAIL, targetCell);
-        }
-        if (sources) {
-            psListAdd(sources, PS_LIST_TAIL, sourceCell);
-        }
-    } else {
-        pmChip *targetChip = targetCell->parent; // The target parent chip
-        pmChip *sourceChip = sourceCell->parent; // The source parent chip
-        if (targetChip->hdu) {
-            if (targets) {
-                addCellsFromChip(targets, targetChip);
-            }
-            if (sources) {
-                addCellsFromChip(sources, sourceChip);
-            }
-        } else {
-            pmFPA *targetFPA = targetChip->parent; // The target parent FPA
-            pmFPA *sourceFPA = sourceChip->parent; // The source parent FPA
-            if (targetFPA->hdu) {
-                if (targets) {
-                    addCellsFromFPA(targets, targetFPA);
-                }
-                if (sources) {
-                    addCellsFromFPA(sources, sourceFPA);
-                }
-            } else {
-                psError(PS_ERR_IO, true, "Unable to find HDU for cell to generate list!\n");
-                return false;
-            }
-        }
-    }
-
-    return true;
-}
-
-// Get the maximum extent of the HDU from the trimsec and biassecs
-static bool sizeHDU(int *xSize, int *ySize, // Size of HDU
-                    psList *cells       // List of cells
-                   )
-{
-    psListIterator *cellsIter = psListIteratorAlloc(cells, PS_LIST_HEAD, false); // Iterator for cells
-    pmCell *cell = NULL;                // The cell from iteration
-    bool mdok = true;                   // Status of MD lookup
-    *xSize = 0;
-    *ySize = 0;
-    while ((cell = psListGetAndIncrement(cellsIter))) {
-        psRegion *trimsec = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.TRIMSEC"); // Trim section
-        if (mdok && trimsec && !psRegionIsBad(*trimsec)) {
-            *xSize = MAX(trimsec->x1, *xSize);
-            *ySize = MAX(trimsec->y1, *ySize);
-        } else {
-            psFree(cellsIter);
-            return false;
-        }
-        psList *biassecs = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.BIASSEC"); // Bias sections
-        if (mdok && biassecs) {
-            psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false); // Iterator
-            psRegion *biassec = NULL;   // The bias section
-            while ((biassec = psListGetAndIncrement(biassecsIter))) {
-                if (!psRegionIsBad(*trimsec)) {
-                    *xSize = MAX(biassec->x1, *xSize);
-                    *ySize = MAX(biassec->y1, *ySize);
-                } else {
-                    psFree(biassecsIter);
-                    psFree(cellsIter);
-                    return false;
-                }
-            }
-            psFree(biassecsIter);
-        }
-    }
-    psFree(cellsIter);
-
-    return (*xSize != 0 && *ySize != 0);
-}
-
-
-static psRegion sectionForImage(int *position, // Position on the output image, updated
-                                const psImage *image, // Image containing the sizes and offsets
-                                int readdir // Read direction, 1=rows, 2=cols
-                               )
-{
-    psRegion region;
-    switch (readdir) {
-    case 1:                           // Read direction is rows
-        region = psRegionSet(*position, *position + image->numCols, image->row0,
-                             image->row0 + image->numRows);
-        *position += image->numCols;
-        break;
-    case 2:                           // Read direction is columns
-        region = psRegionSet(image->col0, image->col0 + image->numCols, *position,
-                             *position + image->numRows);
-        *position += image->numRows;
-        break;
-    default:
-        psAbort(__func__, "Shouldn't ever get here!\n");
-    }
-
-    return region;
-}
-
-static bool doBiasSections(int *position, // Position on the output image, updated
-                           pmCell *target, // Target cell
-                           const pmCell *source // Source cell
+// Copy pixels from a target image to a source image, with flips
+static psImage *copyPixels(psImage *source, // Source image (from source cell)
+                           bool xFlip,      // Flip in x?
+                           bool yFlip       // Flip in y?
                           )
 {
-    psMetadataItem *biassecItem = psMetadataLookup(target->concepts, "CELL.BIASSEC"); // Bias sections
-    if (!biassecItem) {
-        psLogMsg(__func__, PS_LOG_WARN, "CELL.BIASSEC has not been initialised in target cell --- "
-                 "ignored.\n");
-        return false;
-    }
-    psFree(biassecItem->data.V);        // Blow away the old list
-    psList *biassecs = psListAlloc(NULL);
-    biassecItem->data.V = biassecs;
-
-    bool mdok = true;                   // Status of MD lookup
-    int readdir = psMetadataLookupS32(&mdok, source->concepts, "CELL.READDIR"); // Read direction
-    if (!mdok || (readdir != 1 && readdir != 2)) {
-        // Probably unnecessary, but just in case....
-        psLogMsg(__func__, PS_LOG_WARN, "CELL.READDIR is not set in source cell --- ignored.\n");
-        return false;
-    }
-
-    pmReadout *readout = source->readouts->data[0]; // The first source readout, as representative
-    psList *biases = readout->bias; // The bias images from the source readout
-
-    psListIterator *biasIter = psListIteratorAlloc(biases, PS_LIST_HEAD, true); // Iterator for biases
-    psImage *bias = NULL;       // Bias image from iteration
-    while ((bias = psListGetAndIncrement(biasIter))) {
-        // Construct a region
-        psRegion *biassec = psAlloc(sizeof(psRegion)); // The new region; need a psMemBlock
-        *biassec = sectionForImage(position, bias, readdir);
-        psListAdd(biassecs, PS_LIST_TAIL, biassec);
-        psFree(biassec);        // Drop reference
-    }
-    psFree(biasIter);
-
-    return true;
-}
-
-// Generate CELL.TRIMSEC and CELL.BIASSEC for the target cells
-static bool generateTrimBias(psList *targets, // List of target cells
-                             psList *sources // List of source cells
-                            )
-{
-    pmCell *target = NULL, *source = NULL; // Cells from iteration
-    int numCells = targets->n;          // Number of cells
-    int cellNum = 0;                    // The cell number
-    int position = 0;                   // Position on the image
-    bool mdok = true;                   // Status of MD lookup
-
-    // First run through to do the LHS biases
-    psListIterator *targetsIter = psListIteratorAlloc(targets, PS_LIST_HEAD, false); // Iterator for targets
-    psListIterator *sourcesIter = psListIteratorAlloc(sources, PS_LIST_HEAD, false); // Iterator for sources
-    bool done = false;                   // Done with iteration?
-    while ((target = psListGetAndIncrement(targetsIter)) && (source = psListGetAndIncrement(sourcesIter)) &&
-            !done) {
-        if (cellNum <= numCells/2 - 1) {
-            doBiasSections(&position, target, source);
-            cellNum++;
-        } else {
-            done = true;
-        }
-    }
-
-    // Second run through to do the trim sections
-    psListIteratorSet(targetsIter, PS_LIST_HEAD);
-    psListIteratorSet(sourcesIter, PS_LIST_HEAD);
-    while ((target = psListGetAndIncrement(targetsIter)) && (source = psListGetAndIncrement(sourcesIter))) {
-        psRegion *trimsec = psMetadataLookupPtr(&mdok, target->concepts, "CELL.TRIMSEC"); // Trim section
-        if (!mdok || !trimsec) {
-            psLogMsg(__func__, PS_LOG_WARN, "CELL.TRIMSEC has not been initialised in target cell --- "
-                     "ignored.\n");
-            continue;
-        }
-
-        int readdir = psMetadataLookupS32(&mdok, source->concepts, "CELL.READDIR"); // Read direction
-        if (!mdok || (readdir != 1 && readdir != 2)) {
-            // Probably unnecessary, but just in case....
-            psLogMsg(__func__, PS_LOG_WARN, "CELL.READDIR is not set in source cell --- ignored.\n");
-            continue;
-        }
-
-        pmReadout *readout = source->readouts->data[0]; // The first source readout, as representative
-        psImage *image = readout->image;// The proper image
-        *trimsec = sectionForImage(&position, image, readdir);
-    }
-
-    // A final run through to do the RHS biases
-    psListIteratorSet(targetsIter, cellNum);
-    psListIteratorSet(sourcesIter, cellNum);
-    while ((target = psListGetAndIncrement(targetsIter)) && (source = psListGetAndIncrement(sourcesIter))) {
-        doBiasSections(&position, target, source);
-    }
-
-    // Clean up
-    psFree(targetsIter);
-    psFree(sourcesIter);
-
-    return (position > 0);
-}
-
-
-// Generate an HDU with the pixels
-static bool generateHDU(pmCell *target,  // The target cell
-                        pmCell *source, // The source cell
-                        int xBin, int yBin // Binning in x and y
-                       )
-{
-    // Get the HDU and a list of cells below it
-    pmHDU *hdu = pmHDUFromCell(target); // The HDU in the target cell
-    psList *targetCells = psListAlloc(NULL); // List of target cells below the target HDU
-    psList *sourceCells = psListAlloc(NULL); // List of source cells below the target HDU
-    if (! cellList(targetCells, sourceCells, target, source)) {
-        psError(PS_ERR_IO, true, "Unable to find cells to generate HDU!\n");
-        return false;
-    }
-
-    // Check the number of readouts
-    int numReadouts = -1;               // Number of readouts
-    {
-        psListIterator *iter = psListIteratorAlloc(sourceCells, PS_LIST_HEAD, false); // Iterator for cells
-        pmCell *cell = NULL;                // The cell from iteration
-        while ((cell = psListGetAndIncrement(iter)))
-        {
-            psArray *readouts = cell->readouts;
-            if (numReadouts == -1) {
-                numReadouts = readouts->n;
-            } else if (readouts->n != numReadouts) {
-                psError(PS_ERR_IO, true, "Number of readouts doesn't match: %d vs %d\n", readouts->n,
-                        numReadouts);
-                return false;
-            }
-
-        }
-        psFree(iter);
-    }
-
-    // Get the size of the HDU, either from existing trimsec and biassec, or generate these and try again
-    int xSize = 0, ySize = 0;           // Size of HDU
-    if (!sizeHDU(&xSize, &ySize, targetCells) && !(generateTrimBias(targetCells, sourceCells) &&
-            sizeHDU(&xSize, &ySize, targetCells))) {
-        psError(PS_ERR_IO, true, "Unable to determine size of HDU!\n");
-        return false;
-    }
-    psFree(targetCells);
-    psFree(sourceCells);
-
-    xSize = (int)ceilf((float)xSize/(float)xBin);
-    ySize = (int)ceilf((float)ySize/(float)yBin);
-
-    hdu->images = psArrayAlloc(numReadouts);
-    hdu->images->n = numReadouts;
-    for (int i = 0; i < numReadouts; i++) {
-        psImage *image = psImageAlloc(xSize, ySize, PS_TYPE_F32);
-        psImageInit(image, 0.0);
-        hdu->images->data[i] = image;
-    }
-
-    return true;
-}
-
-// Copy pixels from a target image to a source image, with flips
-static bool copyPixels(psImage *target, // Target image (HDU pixels)
-                       psImage *source, // Source image (from source cell)
-                       psRegion region, // Region for pasting
-                       bool xFlip,      // Flip in x?
-                       bool yFlip       // Flip in y?
-                      )
-{
-    psImage *overlay = psMemIncrRefCounter(source);
+    psImage *copy = psMemIncrRefCounter(source);
+    bool copied = false;                // Have the pixels been copied?
     if (xFlip) {
-        psImage *temp = psImageFlipX(overlay);
-        psFree(overlay);
-        overlay = temp;
+        psImage *temp = psImageFlipX(copy); // Flipped version
+        psFree(copy);
+        copy = temp;
+        copied = true;
     }
     if (yFlip) {
-        psImage *temp = psImageFlipY(overlay);
-        psFree(overlay);
-        overlay = temp;
-    }
-    int numPix = psImageOverlaySection(target, overlay, region.x0, region.y0, "=");
-    psFree(overlay);
-    return (numPix > 0);
+        psImage *temp = psImageFlipY(copy); // Flipped version
+        psFree(copy);
+        copy = temp;
+        copied = true;
+    }
+    if (!copied) {
+        psFree(copy);
+        copy = psImageCopy(NULL, source, source->type.type);
+    }
+
+    return copy;
 }
 
@@ -367,5 +59,4 @@
 }
 
-
 static pmHDU *findPHU(const pmCell *cell// The cell for which to find the PHU
                      )
@@ -387,21 +78,4 @@
 }
 
-
-static bool copyPHU(pmCell *targetCell, // The target cell
-                    pmCell *sourceCell  // The source cell
-                   )
-{
-    // Find the respective PHUs
-    pmHDU *targetPHU = findPHU(targetCell); // The target PHU
-    if (targetPHU->header) {
-        return false;                   // No work required
-    }
-    // Copy the header over
-    pmHDU *sourcePHU = findPHU(sourceCell); // The source PHU
-    targetPHU->header = psMetadataCopy(NULL, sourcePHU->header);
-    return true;
-}
-
-
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // File-static engine functions --- these do all the work.  Actually, cellCopy does all the work; the others
@@ -409,104 +83,107 @@
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
-static int cellCopy(pmCell *target,     // The target cell
-                    pmCell *source,     // The source cell, to be copied
-                    bool pixels,        // Copy the pixels?
-                    int xBin, int yBin  // (Relative) binning factors in x and y
-                   )
+static bool cellCopy(pmCell *target,     // The target cell
+                     pmCell *source,     // The source cell, to be copied
+                     bool pixels,        // Copy the pixels?
+                     int xBin, int yBin  // (Relative) binning factors in x and y
+                    )
 {
     assert(target);
     assert(source);
+    assert(xBin > 0 && yBin > 0);
 
     psArray *sourceReadouts = source->readouts; // The source readouts
     int numReadouts = sourceReadouts->n; // Number of readouts copied
 
-    // Copy any headers
-    if (target->hdu && !target->hdu->phu) {
-        pmHDU *sourceHDU = pmHDUFromCell(source);
-        target->hdu->header = psMetadataCopy(target->hdu->header, sourceHDU->header);
-    }
-
-    pmHDU *hdu = pmHDUFromCell(target); // The target HDU; we need to fix this up
-    if (!hdu->images) {
-        generateHDU(target, source, xBin, yBin);
-    }
-    if (!hdu->header) {
-        hdu->header = psMetadataAlloc();
-    }
-    // Copy the PHU over as well, if required
-    copyPHU(target, source);
-
     // Need to check/change CELL.XPARITY and CELL.YPARITY
-    bool xFlip = (psMetadataLookupS32(NULL, target->concepts, "CELL.XPARITY") !=
-                  psMetadataLookupS32(NULL, source->concepts, "CELL.XPARITY")); // Switch parity in x?
-    bool yFlip = (psMetadataLookupS32(NULL, target->concepts, "CELL.YPARITY") !=
-                  psMetadataLookupS32(NULL, source->concepts, "CELL.YPARITY")); // Switch parity in y?
-    psTrace(__func__, 3, "xFlip: %d; yFlip: %d\n", xFlip, yFlip);
-
     bool mdok = true;                   // Status of MD lookup
-    psRegion *trimsec = psMetadataLookupPtr(&mdok, target->concepts, "CELL.TRIMSEC"); // The trim section
-    if (!mdok || !trimsec || psRegionIsBad(*trimsec)) {
-        psError(PS_ERR_IO, true, "CELL.TRIMSEC isn't set!\n");
-        return 0;
-    }
-    psList *biassecs = psMetadataLookupPtr(&mdok, target->concepts, "CELL.BIASSEC"); // The bias sections
-    if (!mdok || !biassecs) {
-        psError(PS_ERR_IO, true, "CELL.BIASSEC isn't set!\n");
-        return 0;
-    }
-
+    bool xFlip = false;                 // Switch parity in x?
+    bool yFlip = false;                 // Switch parity in y?
+    {
+        int xParityTarget = psMetadataLookupS32(&mdok, target->concepts, "CELL.XPARITY"); // Target x parity
+        if (mdok && (xParityTarget == 1 || xParityTarget == -1))
+        {
+            int xParitySource = psMetadataLookupS32(&mdok, source->concepts, "CELL.XPARITY"); // Source parity
+            if (mdok && (xParitySource == 1 || xParitySource == -1) && xParityTarget != xParitySource) {
+                xFlip = true;
+            }
+        }
+        int yParityTarget = psMetadataLookupS32(&mdok, target->concepts, "CELL.YPARITY"); // Target y parity
+        if (mdok && (yParityTarget == 1 || yParityTarget == -1))
+        {
+            int yParitySource = psMetadataLookupS32(&mdok, source->concepts, "CELL.YPARITY"); // Source parity
+            if (mdok && (yParitySource == 1 || yParitySource == -1) && yParityTarget != yParitySource) {
+                yFlip = true;
+            }
+        }
+        psTrace(__func__, 3, "xFlip: %d; yFlip: %d\n", xFlip, yFlip);
+    }
+
+    if (pixels && (xBin != 1 || yBin != 1)) {
+        psLogMsg(__func__, PS_LOG_WARN, "Unable to copy pixels if binning is set --- copy turned off.\n");
+        pixels = false;
+    }
+
+    // Perform deep copy of the images.  I would prefer *not* to do a deep copy, in the interests of speed (we
+    // still need to do another deep copy into the HDU for when we write out), but this is the only way I can
+    // think of to provide security against copying a cell and then unknowingly changing the source when
+    // manipulating the target.
     for (int i = 0; i < numReadouts; i++) {
         pmReadout *sourceReadout = sourceReadouts->data[i]; // The source readout
-        psImage *sourceImage = sourceReadout->image; // The source image
         pmReadout *targetReadout = pmReadoutAlloc(target); // The target readout; this adds it to the cell
-        if (sourceImage->numCols != trimsec->x1 - trimsec->x0 ||
-                sourceImage->numRows != trimsec->y1 - trimsec->y0) {
-            psString trimsecString = psRegionToString(*trimsec); // String with the trim section
-            psLogMsg(__func__, PS_LOG_WARN, "Source image size (%dx%d) for readout %d doesn't match "
-                     "CELL.TRIMSEC for target (%s) -- ignored.\n", sourceImage->numCols, sourceImage->numRows,
-                     i, trimsecString);
-            psFree(trimsecString);
-        } else {
-            binRegion(trimsec, xBin, yBin);
-            if (pixels) {
-                copyPixels(hdu->images->data[i], sourceImage, *trimsec, xFlip, yFlip);
-            }
-            targetReadout->image = psImageSubset(hdu->images->data[i], *trimsec);
-        }
-
-        psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false); // Iterator
-        psRegion *biassec = NULL;       // Bias section from iteration
-        psListIterator *biasIter = psListIteratorAlloc(sourceReadout->bias, PS_LIST_HEAD, false); // Iterator
-        psImage *bias = NULL;           // Bias image from iteration
-        while ((biassec = psListGetAndIncrement(biassecsIter)) && (bias = psListGetAndIncrement(biasIter))) {
-            if (psRegionIsBad(*biassec)) {
-                psString biassecString = psRegionToString(*biassec); // String for bias section
-                psLogMsg(__func__, PS_LOG_WARN, "Bias section (%s) isn't set --- ignored.\n", biassecString);
-                psFree(biassecString);
-                continue;
-            }
-            if (bias->numCols != biassec->x1 - biassec->x0 ||
-                    bias->numRows != biassec->y1 - biassec->y0) {
-                psString biassecString = psRegionToString(*biassec); // String with the bias section
-                psLogMsg(__func__, PS_LOG_WARN, "Source image size (%dx%d) for readout %d doesn't match "
-                         "CELL.BIASSEC for target (%s) -- ignored.\n", bias->numCols, bias->numRows, i,
-                         biassecString);
-                psFree(biassecString);
-            } else {
-                binRegion(biassec, xBin, yBin);
-                if (pixels) {
-                    copyPixels(hdu->images->data[i], bias, *biassec, xFlip, yFlip);
+
+        // Copy attributes
+        targetReadout->col0 = sourceReadout->col0;
+        targetReadout->row0 = sourceReadout->row0;
+        targetReadout->process = sourceReadout->process;
+        targetReadout->file_exists = sourceReadout->file_exists;
+        targetReadout->data_exists = sourceReadout->data_exists;
+
+        if (pixels) {
+            // Copy image
+            if (sourceReadout->image) {
+                if (targetReadout->image) {
+                    psFree(targetReadout->image);
                 }
-                psImage *newBias = psImageSubset(hdu->images->data[i], *biassec);
-                psListAdd(targetReadout->bias, PS_LIST_TAIL, newBias);
-                psFree(newBias);        // Drop reference
-            }
+                targetReadout->image = copyPixels(sourceReadout->image, xFlip, yFlip);
+            }
+
+            // Copy mask
+            if (sourceReadout->mask) {
+                if (targetReadout->mask) {
+                    psFree(targetReadout->mask);
+                }
+                targetReadout->mask = copyPixels(sourceReadout->mask, xFlip, yFlip);
+            }
+
+            // Copy weight
+            if (sourceReadout->weight) {
+                if (targetReadout->weight) {
+                    psFree(targetReadout->weight);
+                }
+                targetReadout->weight = copyPixels(sourceReadout->weight, xFlip, yFlip);
+            }
+
+            // Copy bias
+            while (targetReadout->bias->n > 0) {
+                psListRemove(targetReadout->bias, PS_LIST_HEAD);
+            }
+            // Iterate over the biases
+            psListIterator *biasIter = psListIteratorAlloc(sourceReadout->bias, PS_LIST_HEAD, false);
+            psImage *bias = NULL;           // Bias image from iteration
+            while ((bias = psListGetAndIncrement(biasIter))) {
+                psImage *biasCopy = copyPixels(bias, xFlip, yFlip);
+                psListAdd(targetReadout->bias, PS_LIST_TAIL, biasCopy);
+                psFree(biasCopy);           // Drop reference
+            }
+            psFree(biasIter);
         }
         psFree(targetReadout);          // Drop reference
-        psFree(biassecsIter);
-        psFree(biasIter);
-    }
-
-    // Copy the remaining "concepts" over
+    }
+
+    // Copy the remaining "concepts" over.  Don't copy the TRIMSEC or BIASSEC, since these will be created by
+    // pmHDUGenerate if they don't already exist in the target.  Don't copy the XPARITY or YPARITY, since
+    // we've used those to do the flips.  Don't copy the X0 and Y0 because they are updated below (and are
+    // dependent upon the flips we've done above).
     psMetadataIterator *conceptsIter = psMetadataIteratorAlloc(source->concepts, PS_LIST_HEAD, NULL);
     psMetadataItem *conceptItem = NULL; // Item from iteration
@@ -520,4 +197,23 @@
     }
     psFree(conceptsIter);
+
+    // Need to update CELL.TRIMSEC and CELL.BIASSEC if we changed the binning and they exist already.
+    if (xBin != 1 || yBin != 1) {
+        psRegion *trimsec = psMetadataLookupPtr(&mdok, target->concepts, "CELL.TRIMSEC"); // The trim section
+        if (mdok && trimsec && !psRegionIsBad(*trimsec)) {
+            binRegion(trimsec, xBin, yBin);
+        }
+        psList *biassecs = psMetadataLookupPtr(&mdok, target->concepts, "CELL.BIASSEC"); // The bias sections
+        if (mdok && biassecs && biassecs->n > 0) {
+            psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, true); // Iterator
+            psRegion *biassec = NULL;   // Bias section, from iteration
+            while ((biassec = psListGetAndIncrement(biassecsIter))) {
+                if (!psRegionIsBad(*biassec)) {
+                    binRegion(biassec, xBin, yBin);
+                }
+            }
+            psFree(biassecsIter);
+        }
+    }
 
     // Need to update CELL.X0 and CELL.Y0 if we flipped
@@ -553,12 +249,26 @@
     binItem->data.S32 *= yBin;
 
-    return numReadouts;
-}
-
-static int chipCopy(pmChip *target,          // The target chip
-                    pmChip *source,          // The source chip, to be copied
-                    bool pixels,             // Copy the pixels?
-                    int xBin, int yBin       // (Relative) binning factors in x and y
-                   )
+
+    // Copy any headers
+    pmHDU *targetHDU = pmHDUFromCell(target); // The target HDU
+    if (targetHDU && !targetHDU->header) {
+        pmHDU *sourceHDU = pmHDUFromCell(source); // The source HDU
+        targetHDU->header = psMetadataCopy(targetHDU->header, sourceHDU->header);
+    }
+    // Copy the PHU over as well, if required
+    pmHDU *targetPHU = findPHU(target); // The target PHU
+    if (targetPHU && targetPHU != targetHDU && !targetPHU->header) {
+        pmHDU *sourcePHU = findPHU(source); // The source PHU
+        targetPHU->header = psMetadataCopy(targetPHU->header, sourcePHU->header);
+    }
+
+    return true;
+}
+
+static bool chipCopy(pmChip *target,          // The target chip
+                     pmChip *source,          // The source chip, to be copied
+                     bool pixels,             // Copy the pixels?
+                     int xBin, int yBin       // (Relative) binning factors in x and y
+                    )
 {
     assert(target);
@@ -573,11 +283,5 @@
     }
 
-    // Copy any headers
-    if (target->hdu && !target->hdu->phu) {
-        pmHDU *sourceHDU = pmHDUFromChip(source);
-        target->hdu->header = psMetadataCopy(target->hdu->header, sourceHDU->header);
-    }
-
-    int numCells = 0;                   // Number of cells copied
+    bool status = true;                 // Status of copy
     for (int i = 0; i < targetCells->n; i++) {
         pmCell *targetCell = targetCells->data[i]; // The target cell
@@ -586,8 +290,5 @@
         if (cellNum >= 0) {
             pmCell *sourceCell = sourceCells->data[cellNum]; // The source cell
-            int numReadouts = cellCopy(targetCell, sourceCell, pixels, xBin, yBin); // Number of readouts
-            // copied
-            psTrace(__func__, 5, "Copied %d readouts for cell %s\n", numReadouts, cellName);
-            numCells++;
+            status &= cellCopy(targetCell, sourceCell, pixels, xBin, yBin);
         }
     }
@@ -596,6 +297,5 @@
     psMetadataCopy(target->concepts, source->concepts);
 
-    return numCells;
-
+    return status;
 }
 
@@ -617,11 +317,5 @@
     }
 
-    // Copy any headers
-    if (target->hdu && !target->hdu->phu) {
-        pmHDU *sourceHDU = pmHDUFromFPA(source);
-        target->hdu->header = psMetadataCopy(target->hdu->header, sourceHDU->header);
-    }
-
-    int numChips = 0;                   // Number of chips copied
+    bool status = true;                 // Status of copy
     for (int i = 0; i < targetChips->n; i++) {
         pmChip *targetChip = targetChips->data[i]; // The target chip
@@ -630,7 +324,5 @@
         if (chipNum >= 0) {
             pmChip *sourceChip = sourceChips->data[chipNum]; // The source chip
-            int numCells = chipCopy(targetChip, sourceChip, pixels, xBin, yBin); // Number of cells copied
-            psTrace(__func__, 5, "Copied %d cells for chip %s\n", numCells, chipName);
-            numChips++;
+            status &= chipCopy(targetChip, sourceChip, pixels, xBin, yBin);
         }
     }
@@ -639,5 +331,5 @@
     psMetadataCopy(target->concepts, source->concepts);
 
-    return numChips;
+    return status;
 }
 
@@ -645,5 +337,4 @@
 // Public functions
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
 
 int pmFPACopy(pmFPA *target,            // The target FPA
Index: /trunk/psModules/src/camera/pmFPARead.c
===================================================================
--- /trunk/psModules/src/camera/pmFPARead.c	(revision 7167)
+++ /trunk/psModules/src/camera/pmFPARead.c	(revision 7168)
@@ -5,5 +5,4 @@
 
 #include "pmFPA.h"
-#include "pmFPARead.h"
 #include "pmHDU.h"
 #include "pmHDUUtils.h"
@@ -11,4 +10,6 @@
 #include "psRegionIsBad.h"
 #include "pmFPAHeader.h"
+
+#include "pmFPARead.h"
 
 #define MAX(x,y) ((x) > (y) ? (x) : (y))
@@ -74,4 +75,61 @@
 }
 
+// Read a component of a readout
+psImage *readoutReadComponent(psFits *fits, // FITS file from which to read
+                              const psRegion *region, // Region to read
+                              int readdir, // Read direction (1=rows, 2=cols)
+                              int min,  // Minimum row/col number to read
+                              int max,   // Maximum row/col number to read
+                              int z,     // Image plane to read
+                              float bad // Bad value
+                             )
+{
+    bool resize = false;                // Do we need to resize the image once read?
+    psRegion toRead = psRegionSet(region->x0, region->x1, region->y0, region->y1); // Region to read
+    psRegion fullSize = toRead;         // Full sized region
+    if (readdir == 1) {
+        if (toRead.y0 <= min) {
+            toRead.y0 = min;
+        } else {
+            fullSize.y0 = min;
+            resize = true;
+        }
+        if (toRead.y1 >= max) {
+            toRead.y1 = max;
+        } else {
+            fullSize.y1 = max;
+            resize = false;
+        }
+    } else if (readdir == 2) {
+        if (toRead.x0 <= min) {
+            toRead.x0 = min;
+        } else {
+            fullSize.x0 = min;
+            resize = true;
+        }
+        if (toRead.x1 >= max) {
+            toRead.x1 = max;
+        } else {
+            fullSize.x1 = max;
+            resize = false;
+        }
+    } else {
+        psAbort(__func__, "Read direction can only be 1 (rows) or 2 (cols).\n");
+    }
+
+    psImage *image = psFitsReadImage(fits, toRead, z); // Desired pixels
+
+    if (resize) {
+        // For some reason, the region of interest is smaller than the number of pixels we want.
+        psImage *temp = psImageAlloc(fullSize.x1 - fullSize.x0, fullSize.y1 - fullSize.y0, image->type.type);
+        psImageInit(temp, bad);
+        psImageOverlaySection(temp, image, toRead.x0, toRead.y0, "=");
+        psFree(image);
+        image = temp;
+    }
+
+    return image;
+}
+
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // Public functions
@@ -79,4 +137,7 @@
 
 // Read the next readout; return true if we read pixels in.
+//
+// Note that this doesn't put pixels in the HDU.  It is therefore NOT COMPATIBLE with pmCellWrite,
+// pmChipWrite, and pmFPAWrite.  Use pmReadoutWriteNext to write the data that's read by this function.
 bool pmReadoutReadNext(pmReadout *readout, // Readout into which to read
                        psFits *fits,    // FITS file from which to read
@@ -98,5 +159,6 @@
 
     // Make sure we have the information we need
-    pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_HEADER, false, NULL);
+    pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CAMERA | PM_CONCEPT_SOURCE_DEFAULTS,
+                       false, NULL);
 
     // Get the trim and bias sections
@@ -113,7 +175,12 @@
     }
     int readdir = psMetadataLookupS32(&mdok, cell->concepts, "CELL.READDIR"); // Read direction
-    if (!mdok || readdir == 0 || (readdir != -1 && readdir != 1)) {
+    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;
     }
 
@@ -159,48 +226,44 @@
 
     // Read the FITS image
-    int offset = readout->row0;         // The row number to read
+    int offset = 0;                     // The offset from the start of the image to where we are now
     if (readout->image) {
-        if (readdir > 0) {
+        if (readdir == 1) {
             // Reading rows
-            offset += readout->image->numRows;
+            readout->row0 += readout->image->numRows;
+            offset = readout->row0;
         } else {
             // Reading columns
-            offset += readout->image->numCols;
-        }
-    }
-    if ((readdir > 0 && offset >= naxis2) || (readdir < 0 && offset > naxis1)) {
+            readout->col0 += readout->image->numCols;
+            offset = readout->col0;
+        }
+    }
+    if ((readdir == 1 && offset >= naxis2) || (readdir == 2 && offset > naxis1)) {
         // We've read everything there is
         return false;
     }
     int upper = offset + numScans;      // The upper limit for the pixel read
-    if (readdir > 0 && upper > naxis2) {
+    if (readdir == 1 && upper > naxis2) {
         upper = naxis2;
-    } else if (readdir < 0 && upper > naxis1) {
+    } else if (readdir == 2 && upper > naxis1) {
         upper = naxis1;
     }
-    psRegion region = {0, 0, 0, 0};     // Region to be read
-    if (readdir > 0) {
-        region = psRegionSet(0, naxis1, offset, upper); // Read rows
-    } else {
-        region = psRegionSet(offset, upper, 0, naxis2); // Read columns
-    }
-    psImage *image = psFitsReadImage(fits, region, z); // The image
-
-    // Stick the image into the HDU and the readout
-    if (!hdu->images) {
-        hdu->images = psArrayAlloc(naxis3);
-        hdu->images->n = naxis3;
-    }
-    if (hdu->images->nalloc != naxis3) {
-        hdu->images = psArrayRealloc(hdu->images, naxis3);
-        hdu->images->n = naxis3;
-    }
-    if (hdu->images->data[z]) {
-        psFree(hdu->images->data[z]);
-    }
-    hdu->images->data[z] = image;
-    readout->row0 = region.y0;
-    readout->col0 = region.x0;
-    readoutCarve(readout, image, trimsec, biassecs);
+
+    // Blow away existing data
+    psFree(readout->image);
+    while (readout->bias->n > 0) {
+        psListRemove(readout->bias, PS_LIST_HEAD);
+    }
+
+    // Get the new the trim section
+    readout->image = readoutReadComponent(fits, trimsec, readdir, offset, upper, z, bad); // The image
+
+    // Get the new bias sections
+    psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false); // Iterator for BIASSEC
+    psRegion *biassec = NULL;           // Bias section from iteration
+    while ((biassec = psListGetAndIncrement(biassecsIter))) {
+        psImage *bias = readoutReadComponent(fits, biassec, readdir, offset, upper, z, bad); // The bias
+        psListAdd(readout->bias, PS_LIST_TAIL, bias);
+    }
+    psFree(biassecsIter);
 
     return true;
Index: /trunk/psModules/src/camera/pmFPAWrite.c
===================================================================
--- /trunk/psModules/src/camera/pmFPAWrite.c	(revision 7167)
+++ /trunk/psModules/src/camera/pmFPAWrite.c	(revision 7168)
@@ -6,6 +6,8 @@
 #include "pmHDU.h"
 #include "pmHDUUtils.h"
+#include "pmHDUGenerate.h"
 #include "pmConcepts.h"
 
+#include "pmFPAWrite.h"
 
 bool pmReadoutWriteNext(pmReadout *readout, // Readout to write
@@ -51,5 +53,5 @@
         return false;
     }
-    psImage *image = hdu->images->data[z]; // The image from the HDU to write
+    psImage *image = readout->image; // The image from the HDU to write
     if (readout->row0 == 0 && readout->col0 == 0 && z == 0) {
         // Then we can assume that nothing has been written to the FITS file for now
@@ -68,5 +70,6 @@
 
     // We can simply update an existing HDU
-    if (!psFitsMoveExtName(fits, hdu->extname)) {
+    if (((hdu->phu || strcasecmp(hdu->extname, "PHU") == 0) && !psFitsMoveExtNum(fits, 0, false)) ||
+            !psFitsMoveExtName(fits, hdu->extname)) {
         psError(PS_ERR_IO, false, "Unable to move to extension %s\n", hdu->extname);
         return false;
@@ -86,4 +89,8 @@
     pmHDU *hdu = cell->hdu;             // The HDU
     if (hdu && ((!pixels && hdu->phu) || pixels)) {
+        if (pixels && !hdu->images && !pmHDUGenerateForCell(cell)) {
+            psError(PS_ERR_IO, false, "Unable to generate HDU for cell.\n");
+            return false;
+        }
         bool status = pmConceptsWriteCell(cell, PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CAMERA |
                                           PM_CONCEPT_SOURCE_DEFAULTS, false, NULL);
@@ -107,4 +114,8 @@
     pmHDU *hdu = chip->hdu;             // The HDU
     if (hdu && ((!pixels && hdu->phu) || pixels)) {
+        if (pixels && !hdu->images && !pmHDUGenerateForChip(chip)) {
+            psError(PS_ERR_IO, false, "Unable to generate HDU for chip.\n");
+            return false;
+        }
         bool status = pmConceptsWriteChip(chip, PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CAMERA |
                                           PM_CONCEPT_SOURCE_DEFAULTS, false, NULL);
@@ -130,5 +141,4 @@
 
 
-
 bool pmFPAWrite(pmFPA *fpa,             // FPA to write
                 psFits *fits,           // FITS file to which to write
@@ -139,4 +149,8 @@
     pmHDU *hdu = fpa->hdu;              // The HDU
     if (hdu && ((!pixels && hdu->phu) || pixels)) {
+        if (pixels && !hdu->images && !pmHDUGenerateForFPA(fpa)) {
+            psError(PS_ERR_IO, false, "Unable to generate HDU for FPA.\n");
+            return false;
+        }
         bool status = pmConceptsWriteFPA(fpa, PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CAMERA |
                                          PM_CONCEPT_SOURCE_DEFAULTS, NULL);
Index: /trunk/psModules/src/camera/pmHDU.c
===================================================================
--- /trunk/psModules/src/camera/pmHDU.c	(revision 7167)
+++ /trunk/psModules/src/camera/pmHDU.c	(revision 7168)
@@ -187,4 +187,5 @@
     // Only a header
     if (!hdu->images && !hdu->table) {
+        #if 0
         // Tell CFITSIO there's nothing there
         psMetadataItem *naxis = psMetadataLookup(hdu->header, "NAXIS");
@@ -192,4 +193,5 @@
             naxis->data.S32 = 0;
         }
+        #endif
 
         if (!psFitsWriteHeader(hdu->header, fits)) {
Index: /trunk/psModules/src/camera/pmHDUGenerate.c
===================================================================
--- /trunk/psModules/src/camera/pmHDUGenerate.c	(revision 7168)
+++ /trunk/psModules/src/camera/pmHDUGenerate.c	(revision 7168)
@@ -0,0 +1,569 @@
+#include <stdio.h>
+#include <assert.h>
+#include <pslib.h>
+
+#include "pmFPA.h"
+#include "pmHDU.h"
+#include "pmHDUUtils.h"
+#include "psRegionIsBad.h"
+
+#include "pmHDUGenerate.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// File-static functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Add cells in a chip to a list
+static bool addCellsFromChip(psList *list, // List of cells
+                             const pmChip *chip // The chip from which to add cells
+                            )
+{
+    assert(list);
+    assert(chip);
+
+    psArray *cells = chip->cells;       // Array of cells
+    bool result = true;                 // Result of adding cells
+    for (int i = 0; i < cells->n; i++) {
+        pmCell *cell = cells->data[i];  // A cell
+        result |= psListAdd(list, PS_LIST_TAIL, cell);
+    }
+
+    return result;
+}
+
+// Add cells in an FPA to a list
+static bool addCellsFromFPA(psList *list, // List of cells
+                            const pmFPA *fpa // The FPA from which to add cells
+                           )
+{
+    assert(list);
+    assert(fpa);
+
+    psArray *chips = fpa->chips;        // Array of chips
+    bool result = true;                 // Result of adding cells
+    for (int i = 0; i < chips->n; i++) {
+        pmChip *chip = chips->data[i];  // A chip
+        result |= addCellsFromChip(list, chip);
+    }
+
+    return result;
+}
+
+// Get the maximum extent of the HDU from the trimsec and biassecs
+static bool sizeHDU(int *xSize, int *ySize, // Size of HDU
+                    psList *cells       // List of cells
+                   )
+{
+    psListIterator *cellsIter = psListIteratorAlloc(cells, PS_LIST_HEAD, false); // Iterator for cells
+    pmCell *cell = NULL;                // The cell from iteration
+    bool mdok = true;                   // Status of MD lookup
+    *xSize = 0;
+    *ySize = 0;
+    while ((cell = psListGetAndIncrement(cellsIter))) {
+        psRegion *trimsec = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.TRIMSEC"); // Trim section
+        if (mdok && trimsec && !psRegionIsBad(*trimsec)) {
+            *xSize = PS_MAX(trimsec->x1, *xSize);
+            *ySize = PS_MAX(trimsec->y1, *ySize);
+        } else {
+            psFree(cellsIter);
+            return false;
+        }
+        psList *biassecs = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.BIASSEC"); // Bias sections
+        if (mdok && biassecs) {
+            psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false); // Iterator
+            psRegion *biassec = NULL;   // The bias section
+            while ((biassec = psListGetAndIncrement(biassecsIter))) {
+                if (!psRegionIsBad(*trimsec)) {
+                    *xSize = PS_MAX(biassec->x1, *xSize);
+                    *ySize = PS_MAX(biassec->y1, *ySize);
+                } else {
+                    psFree(biassecsIter);
+                    psFree(cellsIter);
+                    return false;
+                }
+            }
+            psFree(biassecsIter);
+        }
+    }
+    psFree(cellsIter);
+
+    return (*xSize != 0 && *ySize != 0);
+}
+
+
+static psRegion *sectionForImage(int *position, // Position on the output image, updated
+                                 const psImage *image, // Image containing the sizes and offsets
+                                 int readdir // Read direction, 1=rows, 2=cols
+                                )
+{
+    psRegion *region;
+    switch (readdir) {
+    case 1:                           // Read direction is rows
+        region = psRegionAlloc(*position, *position + image->numCols, image->row0,
+                               image->row0 + image->numRows);
+        *position += image->numCols;
+        break;
+    case 2:                           // Read direction is columns
+        region = psRegionAlloc(image->col0, image->col0 + image->numCols, *position,
+                               *position + image->numRows);
+        *position += image->numRows;
+        break;
+    default:
+        psAbort(__func__, "Shouldn't ever get here!\n");
+    }
+
+    return region;
+}
+
+static bool doBiasSections(int *position, // Position on the output image, updated
+                           int *readdir,// Read direction for cells
+                           pmCell *cell // Cell
+                          )
+{
+    psMetadataItem *biassecItem = psMetadataLookup(cell->concepts, "CELL.BIASSEC"); // Bias sections
+    if (!biassecItem) {
+        psLogMsg(__func__, PS_LOG_WARN, "CELL.BIASSEC has not been initialised in cell --- "
+                 "ignored.\n");
+        return false;
+    }
+    psFree(biassecItem->data.V);        // Blow away the old list
+    psList *biassecs = psListAlloc(NULL);
+    biassecItem->data.V = biassecs;
+
+    bool mdok = true;                   // Status of MD lookup
+    int cellreaddir = psMetadataLookupS32(&mdok, cell->concepts, "CELL.READDIR"); // Read direction
+    if (!mdok || (cellreaddir != 1 && cellreaddir != 2)) {
+        // Probably unnecessary, but just in case....
+        psLogMsg(__func__, PS_LOG_WARN, "CELL.READDIR is not set in cell --- ignored.\n");
+        return false;
+    }
+    if (*readdir == 0) {
+        *readdir = cellreaddir;
+    } else if (*readdir != cellreaddir) {
+        psLogMsg(__func__, PS_LOG_WARN, "CELL.READDIR does not match read direction for HDU --- ignored.\n");
+        return false;
+    }
+
+    pmReadout *readout = cell->readouts->data[0]; // The first readout, as representative
+    psList *biases = readout->bias; // The bias images from the readout
+
+    psListIterator *biasIter = psListIteratorAlloc(biases, PS_LIST_HEAD, true); // Iterator for biases
+    psImage *bias = NULL;       // Bias image from iteration
+    while ((bias = psListGetAndIncrement(biasIter))) {
+        // Construct a region
+        psRegion *biassec = sectionForImage(position, bias, *readdir);
+        psListAdd(biassecs, PS_LIST_TAIL, biassec);
+        psFree(biassec);        // Drop reference
+    }
+    psFree(biasIter);
+
+    return true;
+}
+
+// Generate CELL.TRIMSEC and CELL.BIASSEC for the cells
+static bool generateTrimBias(psList *cells // List of cells below the HDU
+                            )
+{
+    pmCell *cell = NULL;                // Cell from iteration
+    int numCells = cells->n;            // Number of cells
+    int cellNum = 0;                    // The cell number
+    int position = 0;                   // Position on the image
+    bool mdok = true;                   // Status of MD lookup
+    int readdir = 0;                    // Read direction (1=rows, 2=cols)
+
+    // First run through to do the LHS biases
+    psListIterator *cellsIter = psListIteratorAlloc(cells, PS_LIST_HEAD, false); // Iterator for cells
+    bool done = false;                  // Done with iteration (due to being halfway through)?
+    while ((cell = psListGetAndIncrement(cellsIter)) && !done) {
+        if (cellNum <= numCells/2 - 1) {
+            doBiasSections(&position, &readdir, cell);
+            cellNum++;
+        } else {
+            done = true;
+        }
+    }
+
+    // Second run through to do the trim sections
+    psListIteratorSet(cellsIter, PS_LIST_HEAD);
+    while ((cell = psListGetAndIncrement(cellsIter))) {
+        psRegion *trimsec = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.TRIMSEC"); // Trim section
+        if (!mdok || !trimsec) {
+            psLogMsg(__func__, PS_LOG_WARN, "CELL.TRIMSEC has not been initialised in cell --- "
+                     "ignored.\n");
+            continue;
+        }
+
+        int cellreaddir = psMetadataLookupS32(&mdok, cell->concepts, "CELL.READDIR"); // Read direction
+        if (!mdok || (cellreaddir != 1 && cellreaddir != 2)) {
+            // Probably unnecessary, but just in case....
+            psLogMsg(__func__, PS_LOG_WARN, "CELL.READDIR is not set in cell --- ignored.\n");
+            continue;
+        }
+        if (readdir == 0 && mdok && cellreaddir != 0) {
+            readdir = cellreaddir;
+        } else if (readdir != cellreaddir) {
+            psLogMsg(__func__, PS_LOG_WARN, "CELL.READDIR for cells within the HDU do not match!\n");
+            cellreaddir = readdir;
+        }
+
+        pmReadout *readout = cell->readouts->data[0]; // The first readout, as representative
+        psImage *image = readout->image;// The proper image
+
+        psFree(trimsec);                // Drop old reference
+        trimsec = sectionForImage(&position, image, cellreaddir); // New reference
+    }
+
+    // A final run through to do the RHS biases
+    psListIteratorSet(cellsIter, cellNum);
+    while ((cell = psListGetAndIncrement(cellsIter))) {
+        doBiasSections(&position, &readdir, cell);
+    }
+
+    // Clean up
+    psFree(cellsIter);
+
+    return (position > 0);
+}
+
+// Generate the HDU, given a list of cells below that HDU.  This is the main engine function, that does all
+// the work.
+static bool generateHDU(pmHDU *hdu,     // HDU to generate
+                        psList *cells   // List of cells
+                       )
+{
+    // Check the number of readouts is consistent within the HDU
+    int numReadouts = -1;               // Number of readouts
+    psElemType type = 0;                // Type of readout images
+    {
+        psListIterator *iter = psListIteratorAlloc(cells, PS_LIST_HEAD, false); // Iterator for cells
+        pmCell *cell = NULL;                // The cell from iteration
+        while ((cell = psListGetAndIncrement(iter)))
+        {
+            psArray *readouts = cell->readouts;
+            if (numReadouts == -1) {
+                numReadouts = readouts->n;
+            } else if (readouts->n != numReadouts) {
+                psError(PS_ERR_IO, true, "Number of readouts doesn't match: %d vs %d\n", readouts->n,
+                        numReadouts);
+                return false;
+            }
+            for (int i = 0; i < numReadouts; i++) {
+                pmReadout *readout = readouts->data[i]; // The readout
+                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);
+                }
+            }
+        }
+        psFree(iter);
+    }
+    if (numReadouts == 0 || type == 0) {
+        psError(PS_ERR_IO, true, "Unable to find images within HDU.\n");
+        psFree(cells);
+        return false;
+    }
+
+    // Get the size of the HDU, either from existing trimsec and biassec, or generate these and try again
+    int xSize = 0, ySize = 0;           // Size of HDU
+    if (!sizeHDU(&xSize, &ySize, cells) && !(generateTrimBias(cells) && sizeHDU(&xSize, &ySize, cells))) {
+        psError(PS_ERR_IO, true, "Unable to determine size of HDU!\n");
+        return false;
+    }
+
+    // Generate the HDU
+    hdu->images = psArrayAlloc(numReadouts);
+    hdu->images->n = numReadouts;
+    for (int i = 0; i < numReadouts; i++) {
+        psImage *image = psImageAlloc(xSize, ySize, type);
+        psImageInit(image, 0.0);
+        hdu->images->data[i] = image;
+    }
+
+    // Insert the pixels into the HDU
+    {
+        psListIterator *iter = psListIteratorAlloc(cells, PS_LIST_HEAD, false); // Iterator for cells
+        pmCell *cell = NULL;           // The cell from iteration
+        bool mdok = true;               // Result of MD lookup
+        while ((cell = psListGetAndIncrement(iter)))
+        {
+            psRegion *trimsec = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.TRIMSEC"); // Trim section
+            if (!mdok || !trimsec) {
+                psAbort(__func__, "Shouldn't ever get here --- CELL.TRIMSEC should have been set above.\n");
+            }
+            psList *biassecs = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.BIASSEC"); // Bias secionts
+            if (!mdok || !biassecs) {
+                psAbort(__func__, "Shouldn't ever get here --- CELL.BIASSEC should have been set above.\n");
+            }
+            psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false); // Iterator
+
+            psArray *readouts = cell->readouts; // Array of readouts
+            psArray *hduImages = hdu->images; // Array of images in the HDU
+            for (int i = 0; i < readouts->n; i++) {
+                pmReadout *readout = readouts->data[i]; // The readout of interest
+                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 (biassecs->n != readout->bias->n) {
+                    psLogMsg(__func__, PS_LOG_WARN, "Number of bias sections (%d) and number of biases (%d)"
+                             " do not match.\n", biassecs->n, readout->bias->n);
+                }
+                psListIterator *biasIter = psListIteratorAlloc(readout->bias, PS_LIST_HEAD, false); // Iteratr
+                psImage *bias = NULL;   // Bias image, from iteration
+                psListIteratorSet(biassecsIter, PS_LIST_HEAD);
+                psRegion *biassec = NULL; // Bias region, from iteration
+                psList *newBias = psListAlloc(NULL); // New list of bias images
+                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);
+                    psListAdd(newBias, PS_LIST_TAIL, bias);
+                }
+                psFree(biasIter);
+
+                // Add on the new list of bias images
+                psFree(readout->bias);
+                readout->bias = newBias;
+            }
+            psFree(biassecsIter);
+        } // Iterating over cells within the HDU
+        psFree(iter);
+    }
+    psFree(cells);
+
+    return true;
+}
+
+// Return the level that an extension applies to
+static pmFPALevel extensionLevel(pmHDU *hdu // HDU to check
+                                )
+{
+    if (!hdu->format) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "HDU does not have a camera format.\n");
+        return PM_FPA_LEVEL_NONE;
+    }
+    bool mdok = true;                   // Status of MD lookup
+    psMetadata *file = psMetadataLookupMD(&mdok, hdu->format, "FILE"); // File information for camera format
+    if (!mdok || !file) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Can't file FILE information for camera format "
+                "configuration.\n");
+        return PM_FPA_LEVEL_NONE;
+    }
+    psString extensions = psMetadataLookupStr(&mdok, file, "EXTENSIONS"); // Where the HDUs are
+    if (!mdok || !extensions || strlen(extensions) == 0) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Can't find EXTENSIONS in the FILE information of the camera "
+                "format configuration.\n");
+        return PM_FPA_LEVEL_NONE;
+    }
+    if (strcasecmp(extensions, "CELL") == 0) {
+        return PM_FPA_LEVEL_CELL;
+    }
+    if (strcasecmp(extensions, "CHIP") == 0) {
+        return PM_FPA_LEVEL_CHIP;
+    }
+    if (strcasecmp(extensions, "FPA") == 0) {
+        return PM_FPA_LEVEL_FPA;
+    }
+    if (strcasecmp(extensions, "NONE") == 0) {
+        return PM_FPA_LEVEL_NONE;
+    }
+    // No idea what it is
+    psError(PS_ERR_IO, true, "EXTENSIONS (%s) in FILE information is not FPA, CHIP, CELL or NONE.\n",
+            extensions);
+    return PM_FPA_LEVEL_NONE;
+}
+
+// Generate HDU for cells belonging to a chip --- just an iterator
+static bool generateForCells(pmChip *chip // Chip for which to generate HDUs
+                            )
+{
+    psArray *cells = chip->cells;       // Array of cells
+    bool status = true;                 // Status of HDU generation
+    for (long i = 0; i < cells->n; i++) {
+        status |= pmHDUGenerateForCell(cells->data[i]);
+    }
+    return status;
+}
+
+// Generate HDU for chips belonging to an FPA --- just an iterator
+static bool generateForChips(pmFPA *fpa // FPA for which to generate HDUs
+                            )
+{
+    psArray *chips = fpa->chips;        // Array of chips
+    bool status = true;                 // Status of HDU generation
+    for (long i = 0; i < chips->n; i++) {
+        status |= pmHDUGenerateForChip(chips->data[i]);
+    }
+    return status;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Generate an HDU from a cell with pixels
+bool pmHDUGenerateForCell(pmCell *cell  // The cell
+                         )
+{
+    // Get the HDU and a list of cells below it
+    pmHDU *hdu = pmHDUFromCell(cell); // The HDU in the cell
+    if (!hdu) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Can't find an HDU for cell.\n");
+        return false;
+    }
+    if (hdu->images) {
+        // It's already here!
+        return true;
+    }
+
+    pmFPALevel extLevel = extensionLevel(hdu);
+    switch (extLevel) {
+    case PM_FPA_LEVEL_NONE:
+    case PM_FPA_LEVEL_CELL: {
+            psList *cells = psListAlloc(NULL); // List of cells below the HDU
+
+            if (cell->hdu) {
+                psListAdd(cells, PS_LIST_TAIL, cell);
+            } else {
+                pmChip *chip = cell->parent;    // The parent chip
+                if (chip->hdu) {
+                    addCellsFromChip(cells, chip);
+                } else {
+                    pmFPA *fpa = chip->parent;  // The parent FPA
+                    if (fpa->hdu) {
+                        addCellsFromFPA(cells, fpa);
+                    }
+                }
+            }
+            if (cells->n == 0) {
+                // Nothing to do
+                return true;
+            }
+
+            return generateHDU(hdu, cells);
+        }
+    case PM_FPA_LEVEL_CHIP:
+    case PM_FPA_LEVEL_FPA:
+        return pmHDUGenerateForChip(cell->parent);
+    default:
+        psAbort(__func__, "Shouldn't ever get here.\n");
+    }
+    return false;
+}
+
+// Generate an HDU from a chip with pixels
+bool pmHDUGenerateForChip(pmChip *chip  // The chip
+                         )
+{
+    // Get the HDU and a list of cells below it
+    pmHDU *hdu = pmHDUFromChip(chip);   // The HDU in the chip
+    if (!hdu) {
+        // Nothing here; need to look further down
+        return generateForCells(chip);
+    }
+    if (hdu->images) {
+        // It's already here!
+        return true;
+    }
+
+    pmFPALevel extLevel = extensionLevel(hdu);
+    switch (extLevel) {
+    case PM_FPA_LEVEL_NONE:
+    case PM_FPA_LEVEL_CELL:
+        // Work on lower levels
+        return generateForCells(chip);
+    case PM_FPA_LEVEL_CHIP: {
+            // Work on this level
+            psList *cells = psListAlloc(NULL);  // List of cells below the HDU
+            if (chip->hdu) {
+                addCellsFromChip(cells, chip);
+            } else {
+                pmFPA *fpa = chip->parent;  // The parent FPA
+                if (fpa->hdu) {
+                    addCellsFromFPA(cells, fpa);
+                }
+            }
+            if (cells->n == 0) {
+                // Nothing to do
+                return true;
+            }
+
+            return generateHDU(hdu, cells);
+        }
+    case PM_FPA_LEVEL_FPA:
+        // Work on higher levels
+        return pmHDUGenerateForFPA(chip->parent);
+    default:
+        psAbort(__func__, "Shouldn't ever get here.\n");
+    }
+    return false;
+}
+
+
+// Generate an HDU from an FPA with pixels
+bool pmHDUGenerateForFPA(pmFPA *fpa     // The fpa
+                        )
+{
+    // Get the HDU and a list of cells below it
+    pmHDU *hdu = pmHDUFromFPA(fpa);     // The HDU in the FPA
+    if (!hdu) {
+        // Nothing here; need to look further down
+        return generateForChips(fpa);
+    }
+    if (hdu->images) {
+        // It's already here!
+        return true;
+    }
+
+    pmFPALevel extLevel = extensionLevel(hdu);
+    switch (extLevel) {
+    case PM_FPA_LEVEL_NONE:
+    case PM_FPA_LEVEL_CELL:
+    case PM_FPA_LEVEL_CHIP:
+        // Work on lower levels
+        return generateForChips(fpa);
+    case PM_FPA_LEVEL_FPA: {
+            // Work on this level
+            psList *cells = psListAlloc(NULL); // List of cells below the HDU
+            if (fpa->hdu) {
+                addCellsFromFPA(cells, fpa);
+            }
+            if (cells->n == 0) {
+                // Nothing to do
+                return true;
+            }
+
+            return generateHDU(hdu, cells);
+        }
+    default:
+        psAbort(__func__, "Shouldn't ever get here.\n");
+    }
+    return false;
+}
Index: /trunk/psModules/src/camera/pmHDUGenerate.h
===================================================================
--- /trunk/psModules/src/camera/pmHDUGenerate.h	(revision 7168)
+++ /trunk/psModules/src/camera/pmHDUGenerate.h	(revision 7168)
@@ -0,0 +1,19 @@
+#ifndef PM_HDU_GENERATE_H
+#define PM_HDU_GENERATE_H
+
+#include "pmFPA.h"
+
+// Generate an HDU for a cell with pixels
+bool pmHDUGenerateForCell(pmCell *cell  // The cell for which to generate an HDU
+                         );
+
+// Generate an HDU for a cell with pixels
+bool pmHDUGenerateForChip(pmChip *chip  // The chip for which to generate an HDU
+                         );
+
+// Generate an HDU from an FPA with pixels
+bool pmHDUGenerateForFPA(pmFPA *fpa     // The fpa for which to generate an HDU
+                        );
+
+
+#endif
Index: /trunk/psModules/src/camera/pmHDUUtils.c
===================================================================
--- /trunk/psModules/src/camera/pmHDUUtils.c	(revision 7167)
+++ /trunk/psModules/src/camera/pmHDUUtils.c	(revision 7168)
@@ -59,2 +59,57 @@
 }
 
+void pmHDUPrint(pmHDU *hdu,             // HDU to print
+                int level,              // Level at which to print
+                bool header             // Print header?
+               )
+{
+    if (hdu->phu) {
+        psTrace(__func__, level, "HDU: %s (PHU)\n", hdu->extname);
+    } else {
+        psTrace(__func__, level, "HDU: %s\n", hdu->extname);
+    }
+
+    psTrace(__func__, level + 1, "Format: %x\n", hdu->format);
+    if (header) {
+        if (hdu->header) {
+            psTrace(__func__, level + 1, "Header:\n");
+            psMetadataPrint(hdu->header, level + 2);
+        } else {
+            psTrace(__func__, level + 1, "No header.\n");
+        }
+    }
+
+    if (hdu->images) {
+        psTrace(__func__, level + 1, "Images:\n");
+        for (long i = 0; i < hdu->images->n; i++) {
+            psImage *image = hdu->images->data[i]; // Image of interest
+            psTrace(__func__, level + 2, "%ld: %dx%d\n", i, image->numCols, image->numRows);
+        }
+    } else {
+        psTrace(__func__, level + 1, "NO images.\n");
+    }
+
+    if (hdu->masks) {
+        psTrace(__func__, level + 1, "Masks:\n");
+        for (long i = 0; i < hdu->masks->n; i++) {
+            psImage *mask = hdu->masks->data[i]; // Mask of interest
+            psTrace(__func__, level + 2, "%ld: %dx%d\n", i, mask->numCols, mask->numRows);
+        }
+    } else {
+        psTrace(__func__, level + 1, "NO masks.\n");
+    }
+
+
+    if (hdu->weights) {
+        psTrace(__func__, level + 1, "Weights:\n");
+        for (long i = 0; i < hdu->masks->n; i++) {
+            psImage *weight = hdu->weights->data[i]; // Weight image of interest
+            psTrace(__func__, level + 2, "%ld: %dx%d\n", i, weight->numCols, weight->numRows);
+        }
+    } else {
+        psTrace(__func__, level + 1, "NO weights.\n");
+    }
+
+
+    return;
+}
Index: /trunk/psModules/src/camera/pmHDUUtils.h
===================================================================
--- /trunk/psModules/src/camera/pmHDUUtils.h	(revision 7167)
+++ /trunk/psModules/src/camera/pmHDUUtils.h	(revision 7168)
@@ -22,3 +22,9 @@
                         );
 
+// Print details about an HDU
+void pmHDUPrint(pmHDU *hdu,             // HDU to print
+                int level,              // Level at which to print
+                bool header             // Print header?
+               );
+
 #endif
