Index: /trunk/archive/scripts/src/Makefile
===================================================================
--- /trunk/archive/scripts/src/Makefile	(revision 4693)
+++ /trunk/archive/scripts/src/Makefile	(revision 4694)
@@ -8,5 +8,6 @@
 BUILD_TARGET = test_pmFPAfromHeader
 
-READ_OBJS = test_pmFPARead.o pmCameraFromHeader.o pmFPARead.o papFocalPlane.o psAdditionals.o papStuff.o
+READ_OBJS = test_pmFPARead.o pmCameraFromHeader.o pmFPARead.o papFocalPlane.o psAdditionals.o papStuff.o \
+		pmFPAConstruct.o pmFPAMorph.o pmFPAWrite.o
 READ_TARGET = test_pmFPARead
 
Index: /trunk/archive/scripts/src/megacam_raw.config
===================================================================
--- /trunk/archive/scripts/src/megacam_raw.config	(revision 4693)
+++ /trunk/archive/scripts/src/megacam_raw.config	(revision 4694)
@@ -125,5 +125,8 @@
 # Default PS concepts that may be specified by value
 DEFAULTS	METADATA
+	CELL.READDIR		S32	1		# Cell is read in x direction
 	CELL.BAD		S32	0
+#	CELL.YPARITY	S32	1
+# This doesn't work, yet.
 	CELL.YPARITY_DEPEND	STR	CHIP.NAME
 	CELL.YPARITY	METADATA
Index: /trunk/archive/scripts/src/megacam_splice.config
===================================================================
--- /trunk/archive/scripts/src/megacam_splice.config	(revision 4693)
+++ /trunk/archive/scripts/src/megacam_splice.config	(revision 4694)
@@ -88,4 +88,5 @@
 # Default PS concepts that may be specified by value
 DEFAULTS        METADATA
+	CELL.READDIR		S32	1		# Cell is read in x direction
         CELL.BAD                S32     0
 	CELL.XPARITY		S32	1
Index: /trunk/archive/scripts/src/papFocalPlane.c
===================================================================
--- /trunk/archive/scripts/src/papFocalPlane.c	(revision 4693)
+++ /trunk/archive/scripts/src/papFocalPlane.c	(revision 4694)
@@ -254,23 +254,12 @@
 }
 
-psMetadataItem *psCellGetValue(papFPA *fpa, // The FPA that contains the cell
-			       const char *chipName, // The name of the chip
-			       const char *cellName, // The name of the cell
+psMetadataItem *psCellGetValue(papCell *cell, // The cell
 			       const char *valueName // Name of value
 			       )
 {
-    psTrace(__func__, 3, "Trying to retrieve %s for %s:%s...\n", valueName, chipName, cellName);
-
-    bool mdStatus = true;		// Status of MD lookup
-    papChip *chip = psMetadataLookupChip(&mdStatus, fpa->chips, chipName); // The chip
-    if (!mdStatus) {
-	psError(PS_ERR_IO, false, "Unable to find chip named %s\n", chipName);
-	return NULL;
-    }
-    papCell *cell = psMetadataLookupCell(&mdStatus, chip->cells, cellName); // The cell
-    if (!mdStatus) {
-	psError(PS_ERR_IO, false, "Unable to find cell named %s in chip %s\n", cellName, chipName);
-	return NULL;
-    }
+    psTrace(__func__, 3, "Trying to retrieve %s...\n", valueName);
+
+    papChip *chip = cell->parent;	// The chip
+    papFPA *fpa = chip->parent;		// The FPA
 
     // Try cache, headers, database, defaults in order
@@ -293,17 +282,14 @@
 }
 
-float psCellGetValueF32(papFPA *fpa, // The FPA that contains the cell
-			const char *chipName, // The name of the chip
-			const char *cellName, // The name of the cell
+float psCellGetValueF32(papCell *cell, // The cell
 			const char *valueName // Name of value
 			)
 {
     float value = NAN;			// Result
-    psMetadataItem *item = psCellGetValue(fpa, chipName, cellName, valueName); // The item
-    if (! item) {
-	psError(PS_ERR_IO, true, "Unable to find value %s in %s:%s.\n", valueName, chipName, cellName);
+    psMetadataItem *item = psCellGetValue(cell, valueName); // The item
+    if (! item) {
+	psError(PS_ERR_IO, true, "Unable to find value %s in cell.\n", valueName);
     } else if (item->type != PS_META_F32) {
-	psError(PS_ERR_IO, true, "Metadata item type for %s (%s:%s) is not F32.\n", valueName, chipName,
-		cellName);
+	psError(PS_ERR_IO, true, "Metadata item type for %s is not F32.\n", valueName);
     } else {
 	value = item->data.F32;
@@ -313,17 +299,14 @@
 }
 
-int psCellGetValueS32(papFPA *fpa, // The FPA that contains the cell
-		      const char *chipName, // The name of the chip
-		      const char *cellName, // The name of the cell
+int psCellGetValueS32(papCell *cell, // The cell
 		      const char *valueName // Name of value
     )
 {
     int value = 0;			// Result
-    psMetadataItem *item = psCellGetValue(fpa, chipName, cellName, valueName); // The item
-    if (! item) {
-	psError(PS_ERR_IO, true, "Unable to find value %s in %s:%s.\n", valueName, chipName, cellName);
+    psMetadataItem *item = psCellGetValue(cell, valueName); // The item
+    if (! item) {
+	psError(PS_ERR_IO, true, "Unable to find value %s.\n", valueName);
     } else if (item->type != PS_META_S32) {
-	psError(PS_ERR_IO, true, "Metadata item type for %s (%s:%s) is not S32.\n", valueName, chipName,
-		cellName);
+	psError(PS_ERR_IO, true, "Metadata item type for %s is not S32.\n", valueName);
     } else {
 	value = item->data.S32;
@@ -334,17 +317,14 @@
 
 
-double psCellGetValueF64(papFPA *fpa, // The FPA that contains the cell
-			 const char *chipName, // The name of the chip
-			 const char *cellName, // The name of the cell
+double psCellGetValueF64(papCell *cell, // The cell
 			 const char *valueName // Name of value
 			 )
 {
     double value = NAN;			// Result
-    psMetadataItem *item = psCellGetValue(fpa, chipName, cellName, valueName); // The item
-    if (! item) {
-	psError(PS_ERR_IO, true, "Unable to find value %s in %s:%s.\n", valueName, chipName, cellName);
+    psMetadataItem *item = psCellGetValue(cell, valueName); // The item
+    if (! item) {
+	psError(PS_ERR_IO, true, "Unable to find value %s.\n", valueName);
     } else if (item->type != PS_META_F64) {
-	psError(PS_ERR_IO, true, "Metadata item type for %s (%s:%s) is not F64.\n", valueName, chipName,
-		cellName);
+	psError(PS_ERR_IO, true, "Metadata item type for %s is not F64.\n", valueName);
     } else {
 	value = item->data.F64;
@@ -355,17 +335,14 @@
 
 
-psString psCellGetValueString(papFPA *fpa, // The FPA that contains the cell
-			      const char *chipName, // The name of the chip
-			      const char *cellName, // The name of the cell
+psString psCellGetValueString(papCell *cell, // The cell
 			      const char *valueName // Name of value
     )
 {
     psString value = NULL;		// Result
-    psMetadataItem *item = psCellGetValue(fpa, chipName, cellName, valueName); // The item
-    if (! item) {
-	psError(PS_ERR_IO, true, "Unable to find value %s in %s:%s.\n", valueName, chipName, cellName);
+    psMetadataItem *item = psCellGetValue(cell, valueName); // The item
+    if (! item) {
+	psError(PS_ERR_IO, true, "Unable to find value %s.\n", valueName);
     } else if (item->type != PS_META_STR) {
-	psError(PS_ERR_IO, true, "Metadata item type for %s (%s:%s) is not STR.\n", valueName, chipName,
-		cellName);
+	psError(PS_ERR_IO, true, "Metadata item type for %s is not STR.\n", valueName);
     } else {
 	value = item->data.V;
@@ -394,6 +371,7 @@
     fpa->camera = psMemIncrRefCounter((psPtr)camera);
     fpa->db = db;
-    fpa->chips = psMetadataAlloc();
-
+    fpa->chips = psArrayAlloc(0);
+
+    fpa->extname = NULL;
     fpa->pixels = NULL;
     fpa->header = NULL;
@@ -418,5 +396,5 @@
 
 papChip *papChipAlloc(papFPA *fpa,	// FPA to which the chip belongs
-		      const char *name	// Name of the chip
+		      psString name	// Chip name
     )
 {
@@ -424,6 +402,6 @@
     psMemSetDeallocator(chip, (psFreeFcn)p_papChipFree);
 
-    // Push onto the FPA
-    psMetadataAdd(fpa->chips, PS_LIST_TAIL, name, PS_META_CHIP, "Chip added on allocation", chip);
+    // Push onto the array of chips
+    fpa->chips = psArrayAdd(fpa->chips, 0, chip);
 
     // Fill in the components
@@ -435,11 +413,12 @@
 
     chip->values = psMetadataAlloc();
-    chip->cells = psMetadataAlloc();
-
+    chip->parent = psMemIncrRefCounter(fpa);
+    chip->cells = psArrayAlloc(0);
+
+    chip->extname = NULL;
     chip->pixels = NULL;
     chip->header = NULL;
 
-    psMetadataAdd(chip->values, PS_LIST_TAIL, "CHIP.NAME", PS_META_STR, "Name of the chip",
-		  psStringCopy(name));
+    psMetadataAddStr(chip->values, PS_LIST_HEAD, "CHIP.NAME", "Chip name added at papChipAlloc", name);
 
     return chip;
@@ -453,4 +432,5 @@
     psFree(chip->values);
     psFree(chip->cells);
+    psFree(chip->parent);
 
     psFree(chip->pixels);
@@ -459,6 +439,5 @@
 
 papCell *papCellAlloc(papChip *chip,	// Chip to which the cell belongs
-		      const char *name,	// Name of the cell
-		      int nReadouts	// Number of readouts contained
+		      psString name	// Name of cell
     )
 {
@@ -466,6 +445,6 @@
     psMemSetDeallocator(cell, (psFreeFcn)p_papCellFree);
 
-    // Push onto the chip
-    psMetadataAdd(chip->cells, PS_LIST_TAIL, name, PS_META_CELL, "Cell added on allocation", cell);
+    // Push onto the array of chips
+    chip->cells = psArrayAdd(chip->cells, 0, cell);
 
     // Fill in components
@@ -480,11 +459,12 @@
 
     cell->values = psMetadataAlloc();
-    cell->readouts = psArrayAlloc(nReadouts);
-
+    cell->readouts = psArrayAlloc(0);
+    cell->parent = psMemIncrRefCounter(chip);
+
+    cell->extname = NULL;
     cell->pixels = NULL;
     cell->header = NULL;
 
-    psMetadataAdd(cell->values, PS_LIST_TAIL, "CELL.NAME", PS_META_STR, "Name of the cell",
-		  psStringCopy(name));
+    psMetadataAddStr(cell->values, PS_LIST_HEAD, "CELL.NAME", "Cell name added at papCellAlloc", name);
 
     return cell;
@@ -501,4 +481,5 @@
     psFree(cell->values);
     psFree(cell->readouts);
+    psFree(cell->parent);
 
     psFree(cell->pixels);
Index: /trunk/archive/scripts/src/papFocalPlane.h
===================================================================
--- /trunk/archive/scripts/src/papFocalPlane.h	(revision 4693)
+++ /trunk/archive/scripts/src/papFocalPlane.h	(revision 4694)
@@ -18,6 +18,7 @@
     const psMetadata *camera;		// Camera configuration
     psDB *db;				// Database
-    psMetadata *chips;			// The chips (referred to by name)
+    psArray *chips;			// The chips
     // FITS data
+    const char *extname;		// Extension name, if it corresponds to this level
     psArray *pixels;			// The pixel data, if it corresponds to this level
     psMetadata *header;			// The FITS header, if it corresponds to this level
@@ -33,6 +34,8 @@
     // Information
     psMetadata *values;			// Important values (cached)
-    psMetadata *cells;			// The cells (referred to by name)
+    psArray *cells;			// The cells (referred to by name)
+    papFPA *parent;			// Parent FPA
     // FITS data
+    const char *extname;		// Extension name, if it corresponds to this level
     psArray *pixels;			// The pixel data, if it corresponds to this level
     psMetadata *header;			// The FITS header, if it corresponds to this level
@@ -52,5 +55,7 @@
     psMetadata *values;			// Important values (cached)
     psArray *readouts;			// The readouts (referred to by number)
+    papChip *parent;			// Parent chip
     // FITS data
+    const char *extname;		// Extension name, if it corresponds to this level
     psArray *pixels;			// The pixel data, if it corresponds to this level
     psMetadata *header;			// The FITS header, if it corresponds to this level
@@ -74,29 +79,19 @@
 
 // Look for a particular value for a given cell (referred to by FPA+chip+cell)
-psMetadataItem *psCellGetValue(papFPA *fpa, // The FPA that contains the cell
-			       const char *chipName, // The name of the chip
-			       const char *cellName, // The name of the cell
+psMetadataItem *psCellGetValue(papCell *cell, // The cell
 			       const char *valueName // Name of value
     );
 
 // Type-specific functions provided as a convenience to the user
-float psCellGetValueF32(papFPA *fpa, // The FPA that contains the cell
-			const char *chipName, // The name of the chip
-			const char *cellName, // The name of the cell
+float psCellGetValueF32(papCell *cell, // The cell
 			const char *valueName // Name of value
 			);
-int psCellGetValueS32(papFPA *fpa, // The FPA that contains the cell
-		      const char *chipName, // The name of the chip
-		      const char *cellName, // The name of the cell
+int psCellGetValueS32(papCell *cell, // The cell
 		      const char *valueName // Name of value
 		      );
-double psCellGetValueF64(papFPA *fpa, // The FPA that contains the cell
-			 const char *chipName, // The name of the chip
-			 const char *cellName, // The name of the cell
+double psCellGetValueF64(papCell *cell, // The cell
 			 const char *valueName // Name of value
 			 );
-psString psCellGetValueString(papFPA *fpa, // The FPA that contains the cell
-			      const char *chipName, // The name of the chip
-			      const char *cellName, // The name of the cell
+psString psCellGetValueString(papCell *cell, // The cell
 			      const char *valueName // Name of value
 			      );
@@ -110,11 +105,10 @@
 
 papChip *papChipAlloc(papFPA *fpa,	// FPA to which the chip belongs
-		    const char *name	// Name of the chip
+		      psString name	// Name of chip
     );
 void p_papChipFree(papChip *chip);
 
 papCell *papCellAlloc(papChip *chip,	// Chip to which the cell belongs
-		      const char *name,	// Name of the cell
-		      int nReadouts	// Number of readouts contained
+		      psString name	// Name of cell
     );
 void p_papCellFree(papCell *cell);
Index: /trunk/archive/scripts/src/papmodule.h
===================================================================
--- /trunk/archive/scripts/src/papmodule.h	(revision 4693)
+++ /trunk/archive/scripts/src/papmodule.h	(revision 4694)
@@ -2,6 +2,9 @@
 
 #include "pmCameraFromHeader.h"
+#include "pmFPAConstruct.h"
 #include "pmFPARead.h"
 #include "papStuff.h"
 #include "psAdditionals.h"
 #include "papFocalPlane.h"
+#include "pmFPAMorph.h"
+#include "pmFPAWrite.h"
Index: /trunk/archive/scripts/src/pmFPAConstruct.c
===================================================================
--- /trunk/archive/scripts/src/pmFPAConstruct.c	(revision 4693)
+++ /trunk/archive/scripts/src/pmFPAConstruct.c	(revision 4694)
@@ -81,6 +81,6 @@
 	    // Extensions are chips; Content contains a list of cells
 	    while (contentItem = psMetadataGetAndIncrement(contentsIter)) {
-		const char *extName = contentItem->name; // The name of the extension
-		papChip *chip = papChipAlloc(fpa); // The chip
+		psString extName = contentItem->name; // The name of the extension
+		papChip *chip = papChipAlloc(fpa, extName); // The chip
 		chip->extname = extName;// Mark chip to receive FITS data
 		if (contentItem->type != PS_META_STR) {
@@ -97,5 +97,5 @@
 		while (cellName = psListGetAndIncrement(cellNamesIter)) {
 		    // Get the cell data
-		    papCell *cell = papCellAlloc(chip); // The cell
+		    papCell *cell = papCellAlloc(chip, cellName); // The cell
 		    psMetadata *cellData = getCellData(camera, cellName);
 		    metadataCopy(cell->values, cellData);
@@ -109,5 +109,5 @@
 	    psMetadata *chips = psMetadataAlloc(); // Given a chip name, holds the chip number
 	    while (contentItem = psMetadataGetAndIncrement(contentsIter)) {
-		const char *extName = contentItem->name; // The name of the extension
+		psString extName = contentItem->name; // The name of the extension
 		psTrace(__func__, 1, "Getting %s....\n", extName);
 
@@ -122,6 +122,6 @@
 		    psLogMsg(__func__, PS_LOG_WARN, "Unable to read contents of %s: ignored.\n", extName);
 		} else {
-		    const char *chipName = psListGet(contents, 0); // The name of the chip
-		    const char *cellType = psListGet(contents, 1); // The type of cell
+		    psString chipName = psListGet(contents, 0); // The name of the chip
+		    psString cellType = psListGet(contents, 1); // The type of cell
 		    psTrace(__func__, 7, "Extension is cell of type %s, from chip %s\n", cellType,
 			    chipName);
@@ -129,5 +129,5 @@
 		    papChip *chip = psMetadataLookupChip(&mdStatus, chips, chipName); // The chip
 		    if (! mdStatus && ! chip) {
-			chip = papChipAlloc(fpa);
+			chip = papChipAlloc(fpa, chipName);
 			psMetadataAdd(chips, PS_LIST_TAIL, chipName, PS_META_CHIP, "", chip);
 		    }
@@ -135,5 +135,5 @@
 		    psArray *images = NULL;
 		    psMetadata *header = NULL;
-		    papCell *cell = papCellAlloc(chip); // The cell
+		    papCell *cell = papCellAlloc(chip, extName); // The cell
 		    cell->extname = extName; // Mark cell to receive FITS data
 		    psMetadata *cellData = getCellData(camera, cellType);
@@ -147,5 +147,5 @@
 	    fpa->extname = "PHU";
 	    while (contentItem = psMetadataGetAndIncrement(contentsIter)) {
-		const char *chipName = contentItem->name; // The name of the chip
+		psString chipName = contentItem->name; // The name of the chip
 
 		if (contentItem->type != PS_META_STR) {
@@ -154,7 +154,7 @@
 		    continue;
 		}
-		const char *content = contentItem->data.V; // The content of the extension
+		psString content = contentItem->data.V; // The content of the extension
 		psTrace(__func__, 5, "Component cells are: %s\n", content);
-		papChip *chip = papChipAlloc(fpa); // The chip
+		papChip *chip = papChipAlloc(fpa, chipName); // The chip
 		psList *cellNames = papSplit(content, ", "); // Split the list of cells
 		psListIterator *cellNamesIter = psListIteratorAlloc(cellNames, PS_LIST_HEAD, false);
@@ -162,5 +162,5 @@
 		while (cellName = psListGetAndIncrement(cellNamesIter)) {
 		    psTrace(__func__, 7, "Processing cell %s....\n", cellName);
-		    papCell *cell = papCellAlloc(chip); // The cell
+		    papCell *cell = papCellAlloc(chip, cellName); // The cell
 		    psMetadata *cellData = getCellData(camera, cellName);
 		    metadataCopy(cell->values, cellData);
@@ -179,5 +179,6 @@
     } else if (strncmp(phuType, "CHIP", 4) == 0) {
 	// The FITS file contains a single chip only
-	papChip *chip = papChipAlloc(fpa); // The chip
+	psString chipName = psStringCopy("CHIP"); // Name for chip
+	papChip *chip = papChipAlloc(fpa, chipName); // The chip
 
 	if (strncmp(extType, "NONE", 4) == 0) {
@@ -193,7 +194,7 @@
 	    psList *cellNames = papSplit(contents, " ,"); // Names of cells
 	    psListIterator *cellIter = psListIteratorAlloc(cellNames, PS_LIST_HEAD, false); // Iterator
-	    const char *cellName = NULL;
+	    psString cellName = NULL;
 	    while (cellName = psListGetAndIncrement(cellIter)) {
-		papCell *cell = papCellAlloc(chip); // The cell
+		papCell *cell = papCellAlloc(chip, cellName); // The cell
 		psMetadata *cellData = getCellData(camera, cellName);
 		metadataCopy(cell->values, cellData);
@@ -219,10 +220,5 @@
 	    psMetadataItem *contentItem = NULL; // Item from metadata
 	    while (contentItem = psMetadataGetAndIncrement(contentsIter)) {
-		const char *extName = contentItem->name; // The name of the extension
-
-		psMemCheckCorruption(true);
-		
-		psTrace(__func__, 1, "Getting %s....\n", extName);
-
+		psString extName = contentItem->name; // The name of the extension
 		// Content is a cell type
 		if (contentItem->type != PS_META_STR) {
@@ -233,8 +229,8 @@
 		}
 		const char *cellType = contentItem->data.V; // The type of cell
-		psTrace(__func__, 2, "Cell type is %s\n", cellType);
+		psTrace(__func__, 5, "Cell type is %s\n", cellType);
 		psArray *images = NULL;
 		psMetadata *header = NULL;
-		papCell *cell = papCellAlloc(chip); // The cell
+		papCell *cell = papCellAlloc(chip, extName); // The cell
 		cell->extname = extName; // Mark cell to receive FITS data
 		psMetadata *cellData = getCellData(camera, cellType);
@@ -266,6 +262,9 @@
 {
     psTrace(__func__, 0, "FPA:\n");
-    if (fpa->pixels) {
-	psTrace(__func__, 1, "---> FPA contains pixels.\n");
+    if (fpa->extname) {
+	psTrace(__func__, 1, "---> FPA is extension %s.\n", fpa->extname);
+	if (! fpa->pixels) {
+	    psTrace(__func__, 1, "---> NO PIXELS for extension %s\n", fpa->extname);
+	}
     }
 
@@ -275,6 +274,9 @@
 	psTrace(__func__, 1, "Chip: %d\n", i);
 	papChip *chip = chips->data[i]; // The chip
-	if (chip->pixels) {
-	    psTrace(__func__, 2, "---> Chip contains pixels.\n");
+	if (chip->extname) {
+	    psTrace(__func__, 2, "---> Chip is extension %s.\n", chip->extname);
+	    if (! chip->pixels) {
+		psTrace(__func__, 2, "---> NO PIXELS for extension %s\n", chip->extname);
+	    }
 	}
 	// Iterate over the chip
@@ -283,6 +285,9 @@
 	    psTrace(__func__, 2, "Cell: %d\n", j);
 	    papCell *cell = cells->data[j]; // The cell
-	    if (cell->pixels) {
-		psTrace(__func__, 3, "---> Cell contains pixels.\n");
+	    if (cell->extname) {
+		psTrace(__func__, 3, "---> Cell is extension %s.\n", cell->extname);
+		if (! cell->pixels) {
+		    psTrace(__func__, 3, "---> NO PIXELS for extension %s\n", cell->extname);
+		}
 	    }
 	    psMetadataPrint(cell->values, 3);
@@ -292,11 +297,13 @@
 		papReadout *readout = readouts->data[k]; // The readout
 		psImage *image = readout->image; // The image
-		psTrace(__func__, 4, "Image: [%d:%d,%d:%d]\n", image->col0, image->col0 + 
-			image->numCols, image->row0, image->row0 + image->numRows);
+		psTrace(__func__, 4, "Image: [%d:%d,%d:%d] (%dx%d)\n", image->col0, image->col0 + 
+			image->numCols, image->row0, image->row0 + image->numRows, image->numCols,
+			image->numRows);
 		psList *overscans = readout->overscans;	// The list of overscans
 		psListIterator *overscansIter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false);
 		while (image = psListGetAndIncrement(overscansIter)) {
-		    psTrace(__func__, 4, "Overscan: [%d:%d,%d:%d]\n", image->col0, image->col0 + 
-			    image->numCols, image->row0, image->row0 + image->numRows);
+		    psTrace(__func__, 4, "Overscan: [%d:%d,%d:%d] (%dx%d)\n", image->col0, image->col0 + 
+			    image->numCols, image->row0, image->row0 + image->numRows, image->numCols,
+			    image->numRows);
 		}
 		psFree(overscansIter);
Index: /trunk/archive/scripts/src/pmFPAMorph.c
===================================================================
--- /trunk/archive/scripts/src/pmFPAMorph.c	(revision 4694)
+++ /trunk/archive/scripts/src/pmFPAMorph.c	(revision 4694)
@@ -0,0 +1,760 @@
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "pslib.h"
+#include "papmodule.h"
+
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+#define MIN(x,y) ((x) < (y) ? (x) : (y))
+
+
+// Flip the chip in x
+static psImage *xFlipImage(psImage *image, // Image to be flipped
+			   int size	// Size of image in flip dimension
+    )
+{
+    psImage *flipped = psImageAlloc(size, image->numRows, image->type.type); // Flipped image
+    switch (image->type.type) {
+      case PS_TYPE_U16:
+	for (int y = 0; y < image->numRows; y++) {
+	    for (int x = 0; x < image->numCols; x++) {
+		flipped->data.U16[y][size - x - 1] = image->data.U16[y][x];
+	    }
+	}
+	break;
+      default:
+	psError(PS_ERR_IO, true, "Image type %x not yet supported for flip.\n", image->type.type);
+    }
+
+    return flipped;
+}
+
+// Flip the chip in y
+static psImage *yFlipImage(psImage *image, // Image to be flipped
+			   int size	// Size of image in flip dimension
+    )
+{
+    psImage *flipped = psImageAlloc(image->numCols, size, image->type.type); // Flipped image
+    switch (image->type.type) {
+      case PS_TYPE_U16:
+	for (int y = 0; y < image->numRows; y++) {
+	    for (int x = 0; x < image->numCols; x++) {
+		flipped->data.U16[size - y - 1][x] = image->data.U16[y][x];
+	    }
+	}
+	break;
+      default:
+	psError(PS_ERR_IO, true, "Image type %x not yet supported for flip.\n", image->type.type);
+    }
+
+    return flipped;
+}
+
+// Return a list of the region strings and the method for interpreting them
+static psList *getRegions(psString *method, // Method for evaluating the regions
+			  psString methodValues // The METHOD:VALUES string
+    )
+{
+    char *values = strchr(methodValues, ':'); // The VALUES
+    *method = psStringNCopy(methodValues, strlen(methodValues) - strlen(values)); // The METHOD
+    psList *regionStrings = NULL;	// List of the region strings
+    if (strncmp(*method, "HEADER", 6) == 0 || strncmp(*method, "HD", 2) == 0) {
+	regionStrings = papSplit(values, ", ");
+    } else if (strncmp(*method, "VALUE", 5) == 0 || strncmp(*method, "VAL", 3) == 0) {
+	regionStrings = papSplit(values, "; ");
+    }
+
+    return regionStrings;
+}
+
+
+static psArray *spliceCells(psMetadata *header,	// Output header
+			    psList *outCells, // List of target cells (required for parity info)
+			    psList *inCells // List of cells to splice together
+			    )
+{
+    assert(header);
+    assert(outCells);
+    assert(inCells);
+
+    psTrace(__func__, 5, "Splicing together all cells in the bucket.\n");
+
+    if (inCells->size != outCells->size) {
+	psError(PS_ERR_IO, true, "Sizes of input (%d) and output (%d) lists of cells don't match.\n",
+		inCells->size, outCells->size);
+	return NULL;
+    }
+
+    int numCells = inCells->size;	// Number of cells
+    int readdir = 0;			// Read direction for CCD; currently unknown
+    int numReadouts = 0;		// Number of readouts in a cell
+    psArray *spliced = NULL;		// Array of spliced readouts
+    psElemType type = 0;		// Image type (U16, S32, F32, etc)
+
+    psVector *xSize = NULL;		// Size of spliced image in x
+    psVector *ySize = NULL;		// Size of spliced image in y
+
+    psVector *xFlip = psVectorAlloc(numCells, PS_TYPE_U8); // Do we need to flip in x?
+    psVector *yFlip = psVectorAlloc(numCells, PS_TYPE_U8); // Do we need to flip in y?
+
+    psListIterator *inCellsIter = psListIteratorAlloc(inCells, PS_LIST_HEAD, false); // Iterator for cells
+    papCell *inCell = NULL;		// Cell from list of input cells
+    psListIterator *outCellsIter = psListIteratorAlloc(outCells, PS_LIST_HEAD, false); // Iterator for cells
+    int cellNum = 0;			// Cell number; 
+    while (inCell = psListGetAndIncrement(inCellsIter)) {
+	psArray *readouts = inCell->readouts;	// The readouts comprising the cell
+
+	// Check the read direction
+	int cellReaddir = psCellGetValueS32(inCell, "CELL.READDIR"); // Read direction for cell
+	if (readdir == 0) {
+	    readdir = cellReaddir;
+	} else if (readdir != cellReaddir) {
+	    psError(PS_ERR_IO, true, "Trying to splice cells with different read directions (%d vs %d)\n",
+		    readdir, cellReaddir);
+	    // XXX Clean up before returning
+	    return NULL;
+	}
+	// Check the number of readouts
+	if (! spliced) {
+	    numReadouts = readouts->n;
+	    spliced = psArrayAlloc(numReadouts);
+	    xSize = psVectorAlloc(numReadouts, PS_TYPE_S32);
+	    ySize = psVectorAlloc(numReadouts, PS_TYPE_S32);
+	    for (int i = 0; i < numReadouts; i++) {
+		xSize->data.S32[i] = 0;
+		ySize->data.S32[i] = 0;
+	    }
+	} else if (readouts->n != numReadouts) {
+	    psError(PS_ERR_IO, true, "Trying to splice cells with different number of reads (%d vs %d)\n",
+		    numReadouts, readouts->n);
+	    // XXX Clean up before returning
+	    return NULL;
+	}
+
+	// Get the cell parities
+	papCell *outCell = psListGetAndIncrement(outCellsIter);	// Corresponding output cell
+	int xParityIn = psCellGetValueS32(inCell, "CELL.XPARITY");
+	int yParityIn = psCellGetValueS32(inCell, "CELL.YPARITY");
+	int xParityOut = psCellGetValueS32(outCell, "CELL.XPARITY");
+	int yParityOut = psCellGetValueS32(outCell, "CELL.YPARITY");
+	if (xParityIn == 0 || xParityOut == 0 || yParityIn == 0 || yParityOut == 0) {
+	    psError(PS_ERR_IO, false, "Unable to determine parity of cell!\n");
+	    // XXX Clean up before returning
+	    return NULL;
+	}
+
+	if (yParityIn == yParityOut) {
+	    yFlip->data.U8[cellNum] = 0;
+	} else {
+	    psTrace(__func__, 7, "Need to flip y.\n");
+	    yFlip->data.U8[cellNum] = 1;
+	}
+	if (xParityIn == xParityOut) {
+	    xFlip->data.U8[cellNum] = 0;
+	} else {
+	    psTrace(__func__, 7, "Need to flip x.\n");
+	    xFlip->data.U8[cellNum] = 1;
+	}
+	cellNum++;
+
+	// Calculate the sizes of the spliced images
+	for (int i = 0; i < numReadouts; i++) {
+	    papReadout *readout = readouts->data[i]; // The readout of interest
+	    psImage *image = readout->image; // The image pixels
+	    psList *overscans = readout->overscans; // The overscan regions
+
+	    // Check image type
+	    if (type == 0) {
+		type = image->type.type;
+	    } else if (image->type.type != type) {
+		psError(PS_ERR_IO, true, "Trying to splice readouts with different image types (%d vs %d)\n",
+			type, image->type.type);
+		// XXX Clean up before returning
+		return NULL;
+	    }
+
+	    // Get the size of the spliced image
+	    if (readdir == 1) {
+		psTrace(__func__, 9, "Image size: %dx%d\n", image->numCols, image->numRows);
+		xSize->data.S32[i] += image->numCols;
+		ySize->data.S32[i] = MAX(ySize->data.S32[i], image->numRows);
+	    } else if (readdir == 2) {
+		psTrace(__func__, 9, "Image size: %dx%d\n", image->numCols, image->numRows);
+		ySize->data.S32[i] += image->numRows;
+		xSize->data.S32[i] = MAX(xSize->data.S32[i], image->numCols);
+	    } else {
+		psError(PS_ERR_IO, true, "Invalid read direction: %d\n", readdir);
+		// XXX Clean up before returning
+		return NULL;
+	    }
+
+	    psListIterator *overscansIter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false);
+	    psImage *overscan = NULL;	// Overscan image
+	    while (overscan = psListGetAndIncrement(overscansIter)) {
+
+		// Check image type
+		if (type == 0) {
+		    type = image->type.type;
+		} else if (image->type.type != type) {
+		    psError(PS_ERR_IO, true, "Trying to splice readouts with different image types "
+			    "(%d vs %d)\n", type, image->type.type);
+		    // XXX Clean up before returning
+		    return NULL;
+		}
+
+		// Get the size of the spliced image
+		if (readdir == 1) {
+		    psTrace(__func__, 9, "Overscan size: %dx%d\n", image->numCols, image->numRows);
+		    xSize->data.S32[i] += overscan->numCols;
+		    ySize->data.S32[i] = MAX(ySize->data.S32[i], overscan->numRows);
+		} else if (readdir == 2) {
+		    psTrace(__func__, 9, "Overscan size: %dx%d\n", image->numCols, image->numRows);
+		    ySize->data.S32[i] += overscan->numRows;
+		    xSize->data.S32[i] = MAX(xSize->data.S32[i], overscan->numCols);
+		}
+	    }
+
+	}
+    }
+    psFree(inCellsIter);
+    psFree(outCellsIter);
+
+    // Make sure all the readouts have the same size
+    int numRows = ySize->data.S32[0];	// Number of rows for spliced image
+    int numCols = xSize->data.S32[0];	// Number of columns for spliced image
+    for (int i = 1; i < numReadouts; i++) {
+	if (xSize->data.S32[i] != numCols || ySize->data.S32[i] != numRows) {
+	    psError(PS_ERR_IO, true, "Spliced readouts would have different sizes: %dx%d vs %dx%d\n",
+		    numCols, numRows, xSize->data.S32[i], ySize->data.S32[i]);
+	}
+    }
+
+    psTrace(__func__, 5, "Spliced image has dimensions %dx%d\n", numCols, numRows);
+
+    // Now we have done the requisite checks, and know the sizes; we just go through and splice together
+    for (int i = 0; i < numReadouts; i++) {
+	psImage *splice = psImageAlloc(numCols, numRows, type); // The spliced image
+
+	psListIterator *inCellsIter = psListIteratorAlloc(inCells, PS_LIST_HEAD, false);// Iterator for cells
+	papCell *inCell = NULL;		// Cell from the list of cells
+	psListIterator *outCellsIter = psListIteratorAlloc(outCells, PS_LIST_HEAD, false); // Iterator
+	int cellNum = 0;		// Cell number
+	int x0 = 0, y0 = 0;		// Position at which the splice begins
+	while (inCell = psListGetAndIncrement(inCellsIter)) {
+	    psArray *readouts = inCell->readouts; // The readouts comprising the cell
+	    papReadout *readout = readouts->data[i]; // The specific readout of interest
+	    psImage *image = readout->image; // The image pixels
+	    psList *overscans = readout->overscans; // The overscan regions
+	    papCell *outCell = psListGetAndIncrement(outCellsIter); // Output cell
+	    
+	    psImage *toSplice = psMemIncrRefCounter(image); // Image to splice in
+	    if (xFlip->data.U8[cellNum]) {
+		int size = 0;		// Size of flipped image
+		if (readdir == 1) {
+		    size = toSplice->numCols;
+		} else if (readdir == 2) {
+		    size = numCols;
+		}
+		psImage *temp = xFlipImage(toSplice, size);
+		psFree(toSplice);
+		toSplice = temp;
+	    }
+	    if (yFlip->data.U8[cellNum]) {
+		int size = 0;		// Size of flipped image
+		if (readdir == 1) {
+		    size = numRows;
+		} else if (readdir == 2) {
+		    size = toSplice->numRows;
+		}
+		psImage *temp = yFlipImage(toSplice, size);
+		psFree(toSplice);
+		toSplice = temp;
+	    }
+
+	    (void)psImageOverlaySection(splice, toSplice, x0, y0, "=");
+
+	    if (readdir == 1) {
+		x0 += toSplice->numCols;
+	    } else if (readdir == 2) {
+		y0 += toSplice->numRows;
+	    }
+
+	    // Update the TRIMSEC for the output cell
+	    psString trimsecString = psCellGetValueString(outCell, "CELL.TRIMSEC");
+	    psString trimsecMethod = NULL; // Method of determining trimsec
+	    psList *trimsecs = getRegions(&trimsecMethod, trimsecString); // List of trimsecs
+	    if (strncmp(trimsecMethod, "HEADER", 6) != 0 && strncmp(trimsecMethod, "HD", 2) != 0) {
+		psError(PS_ERR_IO, true, "Can only splice cells if the CELL.TRIMSEC (= %s) is determined "
+			"from the header.\n", trimsecString);
+		// XXX Clean up before returning
+		return NULL;
+	    }
+	    if (trimsecs->size != 1) {
+		psError(PS_ERR_IO, true, "Multiple headers specified for CELL.TRIMSEC: %s\n", trimsecString);
+		// XXX Clean up before returning
+		return NULL;
+	    }
+	    psString trimsecName = trimsecs->head->data; // Grab header for TRIMSEC off the list
+	    psFree(trimsecs);
+
+	    // TRIMSEC region; add 1 to get FITS standard
+	    psRegion trimsecRegion = {x0 + 1, x0 + toSplice->numCols, y0 + 1, y0 + toSplice->numRows};
+	    psString trimsecValue = psRegionToString(trimsecRegion);
+	    psMetadataAddStr(header, PS_LIST_TAIL, trimsecName, "TRIMSEC from pmFPAMorph", trimsecValue);
+	    // XXX Does psMetadataAddStr overwrite a previous value?
+
+	    // Repeat the above for each overscan (flip, overlay, update BIASSEC)
+	    // Prepare the BIASSEC
+	    psString biassecString = psCellGetValueString(outCell, "CELL.BIASSEC");
+	    psString biassecMethod = NULL; // Method of determining biassec
+	    psList *biassecs = getRegions(&biassecMethod, biassecString); // List of biassecs
+	    if (strncmp(biassecMethod, "HEADER", 6) != 0 && strncmp(biassecMethod, "HD", 2) != 0) {
+		psError(PS_ERR_IO, true, "Can only splice cells if the CELL.BIASSEC (= %s) is determined "
+			"from the header.\n", biassecString);
+		// XXX Clean up before returning
+		return NULL;
+	    }
+	    if (biassecs->size != overscans->size);
+
+	    psListIterator *overscansIter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false);
+	    psImage *overscan = NULL;	// Overscan from list
+	    psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false);
+	    psString biassecName = NULL;// FITS header keyword for biassec
+	    while ((overscan = psListGetAndIncrement(overscansIter)) &&
+		   (biassecName = psListGetAndIncrement(biassecsIter))) {
+		// Splice the overscan on
+		psImage *toSplice = psMemIncrRefCounter(overscan); // Image to splice in
+		if (xFlip->data.U8[cellNum]) {
+		    int size = 0;	// Size of flipped image
+		    if (readdir == 1) {
+			size = toSplice->numCols;
+		    } else if (readdir == 2) {
+			size = numCols;
+		    }
+		    psImage *temp = xFlipImage(toSplice, size);
+		    psFree(toSplice);
+		    toSplice = temp;
+		}
+		if (yFlip->data.U8[cellNum]) {
+		    int size = 0;	// Size of flipped image
+		    if (readdir == 1) {
+			size = numRows;
+		    } else if (readdir == 2) {
+			size = toSplice->numRows;
+		    }
+		    psImage *temp = yFlipImage(toSplice, size);
+		    psFree(toSplice);
+		    toSplice = temp;
+		}
+		
+		(void)psImageOverlaySection(splice, toSplice, x0, y0, "=");
+
+		if (readdir == 1) {
+		    x0 += toSplice->numCols;
+		} else if (readdir == 2) {
+		    y0 += toSplice->numRows;
+		}
+
+		// Update the header
+		psRegion biassecRegion = {x0 + 1, x0 + toSplice->numCols, y0 + 1, y0 + toSplice->numRows};
+		psString biassecValue = psRegionToString(biassecRegion);
+		psMetadataAddStr(header, PS_LIST_TAIL, biassecName, "BIASSEC from pmFPAMorph", biassecValue);
+	    }
+	    psFree(overscansIter);
+	    psFree(biassecsIter);
+	    psFree(biassecs);
+	} // Iterating over input cells
+
+	spliced->data[i] = splice;
+    } // Iterating over readouts
+
+    return spliced;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+bool pmFPAMorph(papFPA *toFPA,		// FPA structure to which to morph
+		papFPA *fromFPA,	// FPA structure from which to morph
+		int chipNum,		// Chip number, in case the scopes are different
+		int cellNum		// Cell number, in case the scopes are different
+    )
+{
+    psList *targetCells = psListAlloc(NULL); // List of target cells
+    psList *sourceCells = psListAlloc(NULL); // List of source cells
+
+    psImage *targetPixels = NULL;	// The target pixels
+    psMetadata *targetHeader = NULL;	// The target headers
+
+    psArray *toChips = toFPA->chips;	// Array of chips
+    psArray *fromChips = fromFPA->chips;// Array of chips    
+
+    // First, copy over the low-level representations.
+    for (int i = 0; i < toChips->n; i++) {
+	papChip *toChip = toChips->data[i];
+	papChip *fromChip = NULL;
+	// Select the correct chip
+	if (toChips->n == 1 && fromChips->n > chipNum) {
+	    fromChip = fromChips->data[chipNum];
+	} else if (fromChips->n == 1 && toChips->n > chipNum) {
+	    fromChip = fromChips->data[0];
+	} else if (toChips->n == fromChips->n) {
+	    fromChip = fromChips->data[i];
+	} else {
+	    psError(PS_ERR_IO, true, "Unable to discern intended chip.\n");
+	    return false;
+	}
+	
+	psArray *toCells = toChip->cells; // Array of cells
+	psArray *fromCells = fromChip->cells; // Array of cells
+
+	for (int j = 0; j < toCells->n; j++) {
+	    papCell *toCell = toCells->data[j];
+	    papCell *fromCell = NULL;
+	    // Select the correct cell
+	    if (toCells->n == 1 && fromCells->n > cellNum) {
+		fromCell = fromCells->data[cellNum];
+	    } else if (fromCells->n == 1 && toCells->n > cellNum) {
+		fromCell = fromCells->data[0];
+	    } else if (toCells->n == fromCells->n) {
+		fromCell = fromCells->data[j];
+	    } else {
+		psError(PS_ERR_IO, true, "Unable to discern intended cell.\n");
+		return false;
+	    }
+
+	    psTrace(__func__, 5, "Putting chip %d, cell %d in the bucket.\n", i, j);
+	    psListAdd(targetCells, PS_LIST_TAIL, toCell);
+	    psListAdd(sourceCells, PS_LIST_TAIL, fromCell);
+
+	    int xParityIn = psCellGetValueS32(fromCell, "CELL.XPARITY");
+	    int yParityIn = psCellGetValueS32(fromCell, "CELL.YPARITY");
+	    int xParityOut = psCellGetValueS32(toCell, "CELL.XPARITY");
+	    int yParityOut = psCellGetValueS32(toCell, "CELL.YPARITY");
+	    psTrace(__func__, 3, "Chip %d, cell %d: in (%d,%d) out (%d,%d)\n", i, j, xParityIn, yParityIn,
+		    xParityOut, yParityOut);
+
+	    // Copy the cell contents over
+	    toCell->readouts = psMemIncrRefCounter(fromCell->readouts);
+	    
+	    if (toCell->extname) {
+		// Splice the component cells
+		if (! toCell->header) {
+		    toCell->header = psMetadataAlloc();
+		}
+		toCell->pixels = spliceCells(toCell->header, targetCells, sourceCells);
+		// Purge the lists
+		while (psListRemove(targetCells, PS_LIST_TAIL));
+		while (psListRemove(sourceCells, PS_LIST_TAIL));
+	    }
+
+	}
+
+	if (toChip->extname) {
+	    // Splice the component cells
+	    if (! toChip->header) {
+		toChip->header = psMetadataAlloc();
+	    }
+	    toChip->pixels = spliceCells(toChip->header, targetCells, sourceCells);
+	    // Purge the lists
+	    while (psListRemove(targetCells, PS_LIST_TAIL));
+	    while (psListRemove(sourceCells, PS_LIST_TAIL));
+	}
+
+    }
+
+    return true;
+}
+
+
+
+
+
+
+
+#if 0
+
+
+
+
+	    // Change parity if necessary
+	    int toXParity = psCellGetValueS32(toCell, "CELL.XPARITY");
+	    int toYParity = psCellGetValueS32(toCell, "CELL.YPARITY");
+	    int fromXParity = psCellGetValueS32(toCell, "CELL.XPARITY");
+	    int fromYParity = psCellGetValueS32(toCell, "CELL.YPARITY");
+
+	    if (toXParity != fromXParity) {
+		for (int i = 0; i < readouts->n; i++) {
+		    papReadout *readout = readouts->data[i]; // The readout to be flipped
+
+		    // Flip the image
+		    psImage *temp = readout->image; // Temporary image
+		    readout->image = xFlipImage(temp);
+		    psFree(temp);
+
+		    // Flip each of the bias regions
+		    psListIterator *overscansIter = psListIteratorAlloc(readout->overscans, PS_LIST_HEAD,
+									false);	// Iterator for overscans
+		    psList *overscans = psListAlloc(void); // List of flipped overscans
+		    psImage *overscan = NULL; // Overscan to flip
+		    while (overscan = psListGetAndIncrement(overscansIter)) {
+			psImage *flipped = xFlipImage(overscan);
+			psListAdd(overscans, PS_LIST_TAIL, flipped);
+		    }
+		    // Replace the list with the list of flipped images
+		    psFree(readout->overscans);
+		    readout->overscans = overscans;
+		}
+
+	    }
+
+	    if (toYParity != fromYParity) {
+		for (int i = 0; i < readouts->n; i++) {
+		    papReadout *readout = readouts->data[i]; // The readout to be flipped
+
+		    // Flip the image
+		    psImage *temp = readout->image; // Temporary image
+		    readout->image = yFlipImage(temp);
+		    psFree(temp);
+
+		    // Flip each of the bias regions
+		    psListIterator *overscansIter = psListIteratorAlloc(readout->overscans, PS_LIST_HEAD,
+									false);	// Iterator for overscans
+		    psList *overscans = psListAlloc(void); // List of flipped overscans
+		    psImage *overscan = NULL; // Overscan to flip
+		    while (overscan = psListGetAndIncrement(overscansIter)) {
+			psImage *flipped = yFlipImage(overscan);
+			psListAdd(overscans, PS_LIST_TAIL, flipped);
+		    }
+		    // Replace the list with the list of flipped images
+		    psFree(readout->overscans);
+		    readout->overscans = overscans;
+		}
+
+	    }
+	    
+
+
+
+
+
+
+
+
+
+
+
+
+   psArray *pixels = NULL;		// Pixels to copy from lower levels
+    psMetadata *header = NULL;		// Header to copy from lower levels
+
+    psArray *toChips = toFPA->chips;	// Array of chips
+    psArray *fromChips = fromFPA->chips; // Array of chips
+
+    if (toFPA->extname) {
+	// Need to stick pixels in at the FPA level by splicing stuff at lower levels
+	toFPA->pixels = pixels;
+	toFPA->header = header;
+    } else {
+	for (int i = 0; i < toChips->n; i++) {
+	    papChip *toChip = toChips->data[i];
+	    papChip *fromChip = NULL;
+	    // Select the correct chip
+	    if (toChips->n == 1 && fromChips->n > chipNum) {
+		fromChip = fromChips->data[chipNum];
+	    } else if (fromChips->n == 1 && toChips->n > chipNum) {
+		fromChip = fromChips->data[0];
+	    } else if (toChips->n == fromChips->n) {
+		fromChip = fromChips->data[i];
+	    } else {
+		psError(PS_ERR_IO, true, "Unable to discern intended chip.\n");
+		return false;
+	    }
+	
+	    psArray *toCells = toChip->cells; // Array of cells
+	    psArray *fromCells = fromChip->cells; // Array of cells
+
+	    if (toChip->extname) {
+		// Need to stick pixels in at the chip level by splicing stuff at lower levels
+		toChip->pixels = pixels;
+		toChip->header = header;
+	    } else {
+
+		psList *toBeSpliced = psListAlloc(void); // List of cells to be spliced
+		int numReadouts = 0;	// Number of readouts in each cell
+		psVector *columns = psVectorAlloc(fromCells->n, PS_TYPE_U32); // Number of columns in cells
+		psVector *rows = psVectorAlloc(fromCells->n, PS_TYPE_U32); // Number of rows in cells
+
+		int splicedCols = 0;	// Number of columns in spliced image
+		int splicedRows = 0;	// Number of rows in spliced image
+		bool scanIsRows = true;	// Is the scan done in rows (true) or columns (false)
+		int numScans = 0;	// Number of scans (either in rows or columns)
+
+		for (int j = 0; j < toCells->n; j++) {
+		    
+		    papCell *toCell = toCells->data[i];
+		    papCell *fromCell = NULL;
+		    // Select the correct cell
+		    if (toCells->n == 1 && fromCells->n > cellNum) {
+			fromCell = fromCells->data[cellNum];
+		    } else if (fromCells->n == 1 && toCells->n > cellNum) {
+			fromCell = fromCells->data[0];
+		    } else if (toCells->n == fromCells->n) {
+			fromCell = fromCells->data[i];
+		    } else {
+			psError(PS_ERR_IO, true, "Unable to discern intended cell.\n");
+			return false;
+		    }
+
+		    if (toCell->extname) {
+			// Need to stick pixels in at the cell level
+			// Since the levels match, we can just stuff them in if the dimensions match
+			// Actually, don't worry about checking the dimensions: if the bias sections
+			// are hard-wired, then we will later throw an error if the dimensions don't match,
+			// and if they are based on a header, we will fix the header.
+			if (checkDimensions(toCell, fromCell)) {
+			    toCell->readouts = fromCell->readouts;
+			} else {
+			    psError(PS_ERR_IO, true, "Cell size mismatch (chip %d, cell %d)\n", i, j);
+			    continue;
+			}
+		    } else {
+			// Put it on the list to be spliced together
+
+			// Check the number of readouts corresponds
+			if (numReadouts == 0) {
+			    numReadouts = fromCell->readouts->n;
+			} else if (fromCell->readouts->n != numReadouts) {
+			    psError(PS_ERR_IO, true, "Determined that cells have %d readouts, but chip %d, "
+				    "cell %d has %d readouts --- ignored.\n", numReadouts, i, j,
+				    fromCell->readouts->n);
+			    continue;
+			}
+
+			papReadout *readout = fromCell->readouts->data[0]; // The first readout
+
+			columns->data.U32[k] = readout->image->numCols; // Number of columns in image
+			rows->data.U32[k] = readout->image->numRows; // Number of rows in image
+
+
+
+			if (numScans != 0) {
+			    // Work out the scan direction
+			    if (splicedCols
+
+
+			psVector *biasRows = psVectorAlloc(numReadouts, PS_TYPE_U32); // Number of bias rows
+			psVector *biasCols = psVectorAlloc(numReadouts, PS_TYPE_U32); // Number of bias columns
+
+			psList *overscans = readout->overscans; // List of overscans
+			psListIterator *overscanIter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false);
+			psImage *overscan = NULL; // Overscan image section
+			int k = 0;	// Overscan number
+			while (overscan = psListGetAndIncrement(overscanIter)) {
+			    if (k == 0) {
+				// On first overscan, decide which is the scan direction
+				if (overscan->numRows == imageRows) {
+				    // Scan done in columns
+				    scanIsRows = false;
+				} else if (overscan->numCols == imageCols) {
+				    // Scan done in rows
+				    scanIsRows = true;
+				} else {
+				    psLogMsg(__func__, PS_LOG_WARN, "Unable to determine relation between "
+					     "overscans and image for chip %d, cell %d.\n", i, j);
+				    // Assume that the scan is done in rows (it usually is)
+				}
+			    }
+
+			    if (scanIsRows) {
+				splicedRows += overscan->numCols;
+			    } else {
+				splicedCols += overscan->numRows;
+			    }
+
+			    k++;
+			}
+
+			// Should check that all readouts have the same size
+			
+			psListAdd(toBeSpliced, PS_LIST_TAIL, fromCell);
+		    }
+		}
+
+		// Having gone through all the cells in the lowest level, we can now
+		// splice the cells together
+		psListIterator *spliceIter = psListIteratorAlloc(toBeSpliced, PS_LIST_HEAD, true);
+		
+		while (
+		    
+
+}
+
+
+
+// Check the image regions
+static bool checkRegions(papCell *toCell, // Destination cell
+			 papCell *fromCell, // Source cell
+			 psMetadata *toHeader, // Destination header
+			 psMetadata *fromHeader	// Source header
+    )
+{
+    // Cases:
+    // 1. Destination regions are specified by headers:
+    //    In this case, set the Destination headers appropriately.
+    // 2. Destination regions are specified by values:
+    //    Barf.
+
+    // Note that none of this accounts for negative indices in the region, as is generally
+    // allowed in psLib.  This could be alleviated by using the psRegionForImage() function
+    // on the fromImage.
+
+    psString toTrimString = psCellGetValueString(toCell, "CELL.TRIMSEC"); // TRIMSEC from the TO
+    psString fromTrimString = psCellGetValueString(fromCell, "CELL.TRIMSEC"); // TRIMSEC from the FROM
+    psString toBiasString = psCellGetValueString(toCell, "CELL.BIASSEC"); // BIASSEC from the TO
+    psString fromTrimString = psCellGetValueString(fromCell, "CELL.BIASSEC"); // BIASSEC from the FROM
+
+    psString fromTrim = NULL;	// The actual TRIMSEC region in string format from the FROM
+
+    if (strncmp(toTrimString, "HEADER:", 7) == 0 || strncmp(toTrimString, "HD:", 3) {
+	// We don't want to bother with all the checking, so we don't allow this
+	return false;
+    }
+
+    if (strncmp(fromTrimString, "HEADER:", 7) == 0 || strncmp(fromTrimString, "HD:", 3) {
+	char *keyword = strchr(fromTrimString, ':') + 1;
+	bool mdOK = false;	// Result of MD lookup
+	fromTrim = psMetadataLookupString(&mdOK, fromHeader, keyword);
+	if (! mdOK) {
+	    panic();
+	}
+    } else if (strncmp(fromTrimString, "VALUE:", 6) == 0 || strncmp(fromTrimString, "VAL:", 4) {
+	char *value = strchr(fromTrimString, ':') + 1;
+	fromTrim = psStringCopy(value);
+    }
+	       
+	       if (strncmp(toTrimString, "HEADER:", 7) == 0 || strncmp(toTrimString, "HD:", 3) {
+		   char *keyword = strchr(toTrimString, ':') + 1;
+		   psMetadataAdd(toHeader, PS_LIST_TAIL, keyword, PS_META_STR | PS_META_REPLACE,
+				 "Trimsec region from pmFPAMorph", fromTrim);
+	       } else if (strncmp(toTrimString, "VALUE:", 6) == 0 || strncmp(toTrimString, "VAL:", 4) {
+		   char *toTrim = strchr(toTrimString, ':') + 1;
+		   psRegion *toRegion = psRegionFromString(toTrim);
+		   psRegion *fromRegion = psRegionFromString(fromTrim);
+		   if (toRegion.x1 - toRegion.x0 != fromRegion.x1 - fromRegion.x0 ||
+		       toRegion.y1 - toRegion.y0 != fromRegion.y1 - fromRegion.y0) {
+		       psError(PS_ERR_IO, true, "Cell size of output is already specified, but does not match "
+			       "that of the input (chip %d, cell%d: %dx%d vs %dx%d\n", i, j,
+			       toRegion.x1 - toRegion.x0, toRegion.y1 - toRegion.y0,
+			       fromRegion.x1 - fromRegion.x0, fromRegion.y1 - fromRegion.y0);
+		       return false;
+		   }
+	       }
+		    
+
+
+			  }
+
+
+#endif
Index: /trunk/archive/scripts/src/pmFPAMorph.h
===================================================================
--- /trunk/archive/scripts/src/pmFPAMorph.h	(revision 4694)
+++ /trunk/archive/scripts/src/pmFPAMorph.h	(revision 4694)
@@ -0,0 +1,12 @@
+#ifndef PM_FPA_MORPH_H
+#define PM_FPA_MORPH_H
+
+// Morph one focal plane representation into another
+bool pmFPAMorph(papFPA *toFPA,		// FPA structure to which to morph
+		papFPA *fromFPA,	// FPA structure from which to morph
+		int chipNum,		// Chip number, in case the scopes are different
+		int cellNum		// Cell number, in case the scopes are different
+    );
+
+
+#endif
Index: /trunk/archive/scripts/src/pmFPARead.c
===================================================================
--- /trunk/archive/scripts/src/pmFPARead.c	(revision 4693)
+++ /trunk/archive/scripts/src/pmFPARead.c	(revision 4694)
@@ -5,4 +5,5 @@
 #include "papStuff.h"
 #include "papFocalPlane.h"
+#include "pmFPARead.h"
 
 // NOTE: Need to deal with header inheritance
@@ -11,42 +12,4 @@
 // File-static functions
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Copy metadata elements into another metadata
-static void metadataCopy(psMetadata *to, // Metadata to which to copy
-			 psMetadata *from // Metdata from which to copy
-    )
-{
-    psMetadataIterator *mdIter = psMetadataIteratorAlloc(from, PS_LIST_HEAD, NULL); // Iterator for the MD
-    psMetadataItem *item = NULL;	// Item from the metadata
-    while (item = psMetadataGetAndIncrement(mdIter)) {
-	psTrace(__func__, 9, "Adding %s to metadata...\n", item->name);
-	if (! psMetadataAddItem(to, item, PS_LIST_TAIL, PS_META_REPLACE)) {
-	    psLogMsg(__func__, PS_LOG_WARN, "Unable to add item (%s: %s) to metadata: ignored\n", item->name,
-		     item->comment);
-	}
-    }
-    psFree(mdIter);
-}
-
-// Read data for a particular cell from the camera configuration
-static psMetadata *getCellData(const psMetadata *camera, // The camera configuration
-			       const char *cellName // The name of the cell
-    )
-{
-    bool status = true;			// Result of MD lookup
-    psMetadata *cells = psMetadataLookupMD(&status, camera, "CELLS"); // The CELLS
-    if (! status) {
-	psError(PS_ERR_IO, false, "Unable to determine CELLS of camera.\n");
-	return NULL;
-    }
-
-    psMetadata *cellData = psMetadataLookupMD(&status, cells, cellName); // The data for the particular cell
-    if (! status) {
-	psError(PS_ERR_IO, false, "Unable to find specs for cell %s: ignored\n", cellName);
-	return NULL;
-    } else {
-	return cellData;
-    }
-}
 
 // Read a FITS extension into a chip
@@ -57,4 +20,5 @@
     )
 {
+    psTrace(__func__, 7, "Moving to extension %s...\n", extName);
     if (strncmp(extName, "PHU", 3) == 0) {
 	if (!psFitsMoveExtNum(fits, 0, false)) {
@@ -66,5 +30,12 @@
 	return false;
     }
+    psTrace(__func__, 7, "Reading header....\n");
     *header = psFitsReadHeader(*header, fits);
+    if (! *header) {
+	psError(PS_ERR_IO, false, "Unable to read FITS header!\n");
+	return false;
+    }
+
+    psTrace(__func__, 7, "Checking NAXIS....\n");
     bool mdStatus = false;
     int nAxis = psMetadataLookupS32(&mdStatus, *header, "NAXIS");
@@ -77,4 +48,5 @@
 		 "anyway.\n");
     }
+    psTrace(__func__, 9, "NAXIS = %d\n", nAxis);
 
     int numPlanes = 1;			// Number of planes
@@ -89,7 +61,11 @@
 
     // Read each plane into the array
+    psTrace(__func__, 7, "Reading %d planes into array....\n", numPlanes);
     *images = psArrayAlloc(numPlanes); // Array of images
     for (int i = 0; i < numPlanes; i++) {
-	(*images)->data[i] = psFitsReadImage((*images)->data[i], fits, region, 0);
+	psTrace(__func__, 9, "Reading plane %d\n", i);
+	psMemCheckCorruption(true);
+	(*images)->data[i] = psFitsReadImage(NULL, fits, region, i);
+	psTrace(__func__, 10, "Done\n");
         if (! (*images)->data[i]) {
 	    psError(PS_ERR_IO, false, "Unable to read image for extension %s, plane %d\n", extName, i);
@@ -119,4 +95,8 @@
 		    region.y1);
 	    numFound++;
+	    // Convert from FITS standard to PS standard
+	    region.x0 -= 1;
+	    region.y0 -= 1;
+	    // We don't touch the x1 and y1 values because they aren't included by psImageSubset.
 	    psImage *subImage = psImageSubset(image, region);
 	    psListAdd(list, PS_LIST_TAIL, subImage);
@@ -166,4 +146,8 @@
 		// We have the region: [x0:x1,y0:y1]
 		psRegion region = psRegionFromString(regionString);
+		// Convert from FITS standard to PS standard
+		region.x0 -= 1;
+		region.y0 -= 1;
+		// We don't touch the x1 and y1 values because they aren't included by psImageSubset.
 		psImage *subImage = psImageSubset(image, region);
 		psListAdd(subImages, PS_LIST_TAIL, subImage);
@@ -251,292 +235,57 @@
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
-papFPA *pmFPARead(psFits *fits, // A FITS file
-		  const psMetadata *camera, // The camera configuration
-		  psDB *db		// Database
-    )
-{
-    papFPA *fpa = papFPAAlloc(camera, db); // The FPA to fill out
-    bool mdStatus = true;		// Status from metadata lookups
-    const char *phuType = psMetadataLookupString(&mdStatus, camera, "PHU"); // What is the PHU?
-    const char *extType = psMetadataLookupString(&mdStatus, camera, "EXTENSIONS"); // What's in the extensions?
-    if (strncmp(phuType, "FPA", 3) == 0) {
-	// The FITS file contains an entire FPA
-
-	psMetadata *contents = psMetadataLookupMD(&mdStatus, camera, "CONTENTS"); // The CONTENTS
-	if (! mdStatus) {
-	    psError(PS_ERR_IO, false, "Unable to determine CONTENTS of camera.\n");
-	    psFree(fpa);
-	    return NULL;
-	}
-
-	// Set up iteration over the contents
-	psMetadataIterator *contentsIter = psMetadataIteratorAlloc(contents, PS_LIST_HEAD, NULL);
-	psMetadataItem *contentItem = NULL; // Item from the metadata
-
-	if (strncmp(extType, "CHIP", 4) == 0) {
-	    // Extensions are chips; Content contains a list of cells
-	    while (contentItem = psMetadataGetAndIncrement(contentsIter)) {
-		const char *extName = contentItem->name; // The name of the extension
-		papChip *chip = papChipAlloc(fpa, extName); // The chip
-		readExtension(&(chip->pixels), &(chip->header), fits, extName);
-		if (contentItem->type != PS_META_STR) {
-		    psLogMsg(__func__, PS_LOG_WARN, "Type of content item (%x) is not string: ignored\n",
-			     contentItem->type);
-		} else {
-		    const char *content = contentItem->data.V; // The content of the extension
-		    psTrace(__func__, 7, "Content of %s is: %s\n", extName, content);
-		    psList *cellNames = papSplit(content, " ,"); // A list of the component cells
-		    psListIterator *cellNamesIter = psListIteratorAlloc(cellNames, PS_LIST_HEAD, NULL);
-		    char *cellName = NULL; // The name of a cell
-		    while (cellName = psListGetAndIncrement(cellNamesIter)) {
-			// Get the cell data
-			papCell *cell = papCellAlloc(chip, cellName, ((psArray*)chip->pixels)->n); // The cell
-			psMetadata *cellData = getCellData(camera, cellName);
-			metadataCopy(cell->values, cellData);
-			portionCell(cell, chip->pixels, chip->header);
-		    }
-		    psFree(cellNamesIter);
-		    psFree(cellNames);
+bool pmFPARead(papFPA *fpa,		// FPA to read into
+	       psFits *fits		// FITS file from which to read
+    )
+{
+    psArray *pixels = NULL;		// Current pixels from FITS file (array of images)
+    psMetadata *header = NULL;		// Current header from FITS file
+
+    psTrace(__func__, 1, "Working on FPA...\n");
+    if (fpa->extname) {
+	psTrace(__func__, 3, "Reading pixels from extension %s into FPA.\n", fpa->extname);
+	if (! readExtension(&fpa->pixels, &fpa->header, fits, fpa->extname)) {
+	    psError(PS_ERR_IO, false, "Unable to read pixels from extension %s\n", fpa->extname);
+	    return false;
+	}
+	pixels = fpa->pixels;
+	header = fpa->header;
+    }
+
+    psArray *chips = fpa->chips;	// Array of chips
+    // Iterate over the FPA
+    for (int i = 0; i < chips->n; i++) {
+	papChip *chip = chips->data[i]; // The chip
+	psTrace(__func__, 2, "Working on chip %d...\n", i);
+	if (chip->extname) {
+	    psTrace(__func__, 3, "Reading pixels from extension %s into chip %d.\n", chip->extname, i);
+	    if (! readExtension(&chip->pixels, &chip->header, fits, chip->extname)) {
+		psError(PS_ERR_IO, false, "Unable to read pixels from extension %s\n", chip->extname);
+		return false;
+	    }
+	    pixels = chip->pixels;
+	    header = chip->header;
+	}
+	// Iterate over the chip
+	psArray *cells = chip->cells;	// Array of cells
+	for (int j = 0; j < cells->n; j++) {
+	    papCell *cell = cells->data[j]; // The cell
+	    psTrace(__func__, 3, "Working on cell %d...\n", j);
+	    if (cell->extname) {
+		psTrace(__func__, 5, "Reading pixels from extension %s into cell %d.\n", cell->extname, j);
+		if (! readExtension(&cell->pixels, &cell->header, fits, cell->extname)) {
+		    psError(PS_ERR_IO, false, "Unable to read pixels from extension %s\n", cell->extname);
+		    return false;
 		}
-	    }
-
-	} else if (strncmp(extType, "CELL", 4) == 0) {
-	    // Extensions are cells; Content contains a chip name and cell type
-	    psMetadata *chipNumbers = psMetadataAlloc(); // Given a chip name, holds the chip number
-	    while (contentItem = psMetadataGetAndIncrement(contentsIter)) {
-		const char *extName = contentItem->name; // The name of the extension
-		psTrace(__func__, 1, "Getting %s....\n", extName);
-
-		if (contentItem->type != PS_META_STR) {
-		    psLogMsg(__func__, PS_LOG_WARN, "Type of content item (%x) is not string: ignored\n",
-			     contentItem->type);
-		} else {
-		    const char *content = contentItem->data.V; // The content of the extension
-		    psList *contents = papSplit(content, ": "); // Split the name from the type
-		    if (contents->size != 2) {
-			psLogMsg(__func__, PS_LOG_WARN, "Unable to read contents of %s: ignored.\n", extName);
-		    } else {
-			const char *chipName = psListGet(contents, 0); // The name of the chip
-			const char *cellType = psListGet(contents, 1); // The type of cell
-			psTrace(__func__, 7, "Extension is cell of type %s, from chip %s\n", cellType,
-				chipName);
-
-			papChip *chip = psMetadataLookupChip(&mdStatus, fpa->chips, chipName); // The chip
-			if (! mdStatus && ! chip) {
-			    chip = papChipAlloc(fpa, chipName);
-			}
-			// The cell
-			psArray *images = NULL;
-			psMetadata *header = NULL;
-			psTrace(__func__, 7, "Reading extension %s\n", extName);
-			readExtension(&images, &header, fits, extName);
-			psTrace(__func__, 7, "Allocating cell %s\n", cellType);
-			papCell *cell = papCellAlloc(chip, cellType, images->n); // The cell
-			cell->pixels = images;
-			cell->header = header;
-			psMetadata *cellData = getCellData(camera, cellType);
-			metadataCopy(cell->values, cellData);
-			psTrace(__func__, 7, "Portioning cell....\n");
-			portionCell(cell, cell->pixels, cell->header);
-			psTrace(__func__, 7, "Done.\n");
-		    }
-		}
-	    }
-
-	} else if (strncmp(extType, "NONE", 4) == 0) {
-	    // No extensions; Content contains metadata, each entry is a chip with its component cells
-	    readExtension(&(fpa->pixels), &(fpa->header), fits, "PHU");
-	    while (contentItem = psMetadataGetAndIncrement(contentsIter)) {
-		const char *chipName = contentItem->name; // The name of the chip
-
-		if (contentItem->type != PS_META_STR) {
-		    psLogMsg(__func__, PS_LOG_WARN, "Type of content item (%x) is not string: ignored\n",
-			     contentItem->type);
-		} else {
-		    const char *content = contentItem->data.V; // The content of the extension
-		    papChip *chip = papChipAlloc(fpa, content); // The chip
-		    psList *cellNames = papSplit(content, ", "); // Split the list of cells
-		    psListIterator *cellNamesIter = psListIteratorAlloc(cellNames, PS_LIST_HEAD, false);
-		    char *cellName = NULL; // Name of the cell
-		    while (cellName = psListGetAndIncrement(cellNamesIter)) {
-			papCell *cell = papCellAlloc(chip, cellName, ((psArray*)fpa->pixels)->n); // The cell
-			psMetadata *cellData = getCellData(camera, cellName);
-			metadataCopy(cell->values, cellData);
-			portionCell(cell, fpa->pixels, fpa->header);
-		    }
-		    psFree(cellNamesIter);
-		}
-	    }
-
-	} else {
-	    psError(PS_ERR_IO, false, "EXTENSIONS in camera definition is not CHIP, CELL or NONE.\n");
-	    psFree(fpa);
-	    return NULL;
-	} // Type of extension
-
-	psFree(contentsIter);
-
-    } else if (strncmp(phuType, "CHIP", 4) == 0) {
-	// The FITS file contains a single chip only
-	papChip *chip = papChipAlloc(fpa, "CHIP"); // The chip
-	
-	if (strncmp(extType, "NONE", 4) == 0) {
-	    // There are no extensions --- only the PHU
-	    readExtension(&(fpa->pixels), &(fpa->header), fits, "PHU");
-
-	    const char *contents = psMetadataLookupString(&mdStatus, camera, "CONTENTS");
-	    if (! mdStatus) {
-		psError(PS_ERR_IO, false, "Unable to determine CONTENTS of camera.\n");
-		psFree(fpa);
-		return NULL;
-	    }
-	    psList *cellNames = papSplit(contents, " ,"); // Names of cells
-	    psListIterator *cellIter = psListIteratorAlloc(cellNames, PS_LIST_HEAD, false); // Iterator
-	    const char *cellName = NULL;
-	    while (cellName = psListGetAndIncrement(cellIter)) {
-		papCell *cell = papCellAlloc(chip, cellName, ((psArray*)fpa->pixels)->n); // The cell
-		psMetadata *cellData = getCellData(camera, cellName);
-		metadataCopy(cell->values, cellData);
-		portionCell(cell, fpa->pixels, fpa->header);
-	    }
-	    psFree(cellIter);
-	} else if (strncmp(extType, "CELL", 4) == 0) {
-	    // Extensions are cells
-	    psMetadata *contents = psMetadataLookupMD(&mdStatus, camera, "CONTENTS"); // The CONTENTS
-	    if (! mdStatus) {
-		psError(PS_ERR_IO, false, "Unable to determine CONTENTS of camera.\n");
-		psFree(fpa);
-		return NULL;
-	    }
-	    
-	    if (strncmp(extType, "CELL", 4) != 0) {
-		psLogMsg(__func__, PS_LOG_WARN, "EXTENSIONS in camera definition is %s, but PHU is CHIP.\n"
-			 "EXTENSIONS assumed to be CELL.\n", extType);
-	    }
-
-	    // Iterate through the contents
-	    psMetadataIterator *contentsIter = psMetadataIteratorAlloc(contents, PS_LIST_HEAD, NULL);
-	    psMetadataItem *contentItem = NULL; // Item from metadata
-	    while (contentItem = psMetadataGetAndIncrement(contentsIter)) {
-		const char *extName = contentItem->name; // The name of the extension
-
-		psMemCheckCorruption(true);
-		
-		psTrace(__func__, 1, "Getting %s....\n", extName);
-
-		// Content is a cell type
-		if (contentItem->type != PS_META_STR) {
-		    psLogMsg(__func__, PS_LOG_WARN,
-			     "CONTENT metadata for extension %s is not of type string, but %x --- ignored\n",
-			     extName, contentItem->type);
-		} else {
-		    const char *cellType = contentItem->data.V; // The type of cell
-		    psTrace(__func__, 2, "Cell type is %s\n", cellType);
-		    psArray *images = NULL;
-		    psMetadata *header = NULL;
-		    readExtension(&images, &header, fits, extName);
-		    papCell *cell = papCellAlloc(chip, extName, images->n); // The cell
-		    cell->pixels = images;
-		    cell->header = header;
-		    psMetadata *cellData = getCellData(camera, cellType);
-		    metadataCopy(cell->values, cellData);
-		    portionCell(cell, cell->pixels, cell->header);
-		}
-	    } // Iterating through contents
-	    psFree(contentsIter);
-
-	} else {
-	    psError(PS_ERR_IO, false, "EXTENSIONS in camera definition is neither CELL or NONE.\n");
-	    psFree(fpa);
-	    return NULL;
-	}
-
-    } else {
-	psError(PS_ERR_IO, true,
-		"The PHU type specified in the camera configuration (%s) is not FPA or CHIP.\n",
-		phuType);
-	psFree(fpa);
-	return NULL;
-    }
-
-    return fpa;
-}
-
-void pmFPAPrint(papFPA *fpa		// FPA to print
-    )
-{
-    // Print out the focal plane
-    psMetadataIterator *fpaIter = psMetadataIteratorAlloc(fpa->chips, PS_LIST_HEAD, NULL); // Iterator for FPA
-    psMetadataItem *fpaItem = NULL;	// Item from metadata
-    psTrace("pmFPARead", 0, "FPA:\n");
-    if (fpa->pixels) {
-	psTrace("pmFPARead", 1, "---> FPA contains pixels.\n");
-    }
-    while (fpaItem = psMetadataGetAndIncrement(fpaIter)) {
-	if (fpaItem->type != PS_META_CHIP) {
-	    psError(PS_ERR_IO, false, "FPA content is not a chip.\n");
-	} else {
-	    psTrace("pmFPARead", 1, "Chip: %s\n", fpaItem->name);
-	    papChip *chip = fpaItem->data.V; // The chip
-	    if (chip->pixels) {
-		psTrace("pmFPARead", 2, "---> Chip contains pixels.\n");
-	    }
-	    // Iterator for chip
-	    psMetadataIterator *chipIter = psMetadataIteratorAlloc(chip->cells, PS_LIST_HEAD, NULL); 
-	    psMetadataItem *chipItem = NULL; // Item from metadata
-	    while (chipItem = psMetadataGetAndIncrement(chipIter)) {
-		if (chipItem->type != PS_META_CELL) {
-		    psError(PS_ERR_IO, false, "Chip content is not a cell.\n");
-		} else {
-		    psTrace("pmFPARead", 2, "Cell: %s\n", chipItem->name);
-		    papCell *cell = chipItem->data.V; // The cell
-		    if (cell->pixels) {
-			psTrace("pmFPARead", 3, "---> Cell contains pixels.\n");
-		    }
-		    // Iterator for cell values
-		    psMetadataIterator *cellIter = psMetadataIteratorAlloc(cell->values, PS_LIST_HEAD, NULL);
-		    psMetadataItem *cellItem = NULL; // Item from metadata
-		    while (cellItem = psMetadataGetAndIncrement(cellIter)) {
-			switch(cellItem->type) {
-			  case PS_META_STR:
-			    psTrace("pmFPARead", 3, "%s: %s\n", cellItem->name, cellItem->data.V);
-			    break;
-			  case PS_META_F32:
-			    psTrace("pmFPARead", 3, "%s: %f\n", cellItem->name, cellItem->data.F32);
-			    break;
-			  case PS_META_S32:
-			    psTrace("pmFPARead", 3, "%s: %d\n", cellItem->name, cellItem->data.S32);
-			    break;
-			  case PS_META_META:
-			    psTrace("pmFPARead", 3, "%s:\n", cellItem->name);
-			    psMetadataPrint(cellItem->data.V, 4);
-			    break;
-			  default:
-			    psError(PS_ERR_IO, false, "Unknown type for cell (%x).\n", cellItem->type);
-			}
-		    } // Iterating through cell
-		    psFree(cellIter);
-
-		    // Iterator for cell
-		    psTrace("pmFPARead", 3, "Readouts:\n");
-		    psArray *readouts = cell->readouts;	// The readouts
-		    for (int i = 0; i < readouts->n; i++) {
-			papReadout *readout = readouts->data[i]; // The readout
-			psImage *image = readout->image; // The image
-			psTrace("pmFPARead", 4, "Image: [%d:%d,%d:%d]\n", image->col0, image->col0 + 
-				image->numCols, image->row0, image->row0 + image->numRows);
-			psList *overscans = readout->overscans;	// The list of overscans
-			psListIterator *overscansIter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false);
-			while (image = psListGetAndIncrement(overscansIter)) {
-			    psTrace("pmFPARead", 4, "Overscan: [%d:%d,%d:%d]\n", image->col0, image->col0 + 
-				image->numCols, image->row0, image->row0 + image->numRows);
-			}
-			psFree(overscansIter);
-		    }
-		}
-	    } // Iterating through chip
-	    psFree(chipIter);
-	}
-    } // Iterating through FPA
-    psFree(fpaIter);
-}
+		pixels = cell->pixels;
+		header = cell->header;
+	    }
+
+	    psTrace(__func__, 5, "Allocating readouts and extracting overscans etc for chip %d cell %d...\n",
+		    i, j);
+	    portionCell(cell, pixels, header);
+	}
+    }
+
+    return true;
+}
Index: /trunk/archive/scripts/src/pmFPARead.h
===================================================================
--- /trunk/archive/scripts/src/pmFPARead.h	(revision 4693)
+++ /trunk/archive/scripts/src/pmFPARead.h	(revision 4694)
@@ -4,13 +4,7 @@
 #include "papFocalPlane.h"
 
-// Read the contents of a FITS file (format specified by the camera configuration) into memory
-papFPA *pmFPARead(psFits *fits, // A FITS file
-		  const psMetadata *camera, // The camera configuration
-		  psDB *db		// Database handle
-    );
-
-// Print out the FPA
-void pmFPAPrint(papFPA *fpa		// FPA to print
-    );
+bool pmFPARead(papFPA *fpa,		// FPA to read into
+	       psFits *fits		// FITS file from which to read
+	       );
 
 #endif
Index: /trunk/archive/scripts/src/test_pmFPARead.c
===================================================================
--- /trunk/archive/scripts/src/test_pmFPARead.c	(revision 4693)
+++ /trunk/archive/scripts/src/test_pmFPARead.c	(revision 4694)
@@ -26,10 +26,12 @@
 
     (void)psTraceSetLevel(".", 0);
+    (void)psTraceSetLevel("readExtension", 10);
     (void)psTraceSetLevel("readMultipleRegions", 0);
     (void)psTraceSetLevel("portions", 0);
-    (void)psTraceSetLevel("pmFPARead", 10);
+    (void)psTraceSetLevel("pmFPAPrint", 10);
     (void)psTraceSetLevel("pmFPAfromHeader", 10);
     (void)psTraceSetLevel("pmCameraFromHeader", 10);
-
+    (void)psTraceSetLevel("pmFPAMorph", 10);
+    (void)psTraceSetLevel("spliceCells", 10);
 
 
@@ -53,13 +55,68 @@
     psMetadata *camera = pmCameraFromHeader(header, ipprc);
 
-    papFPA *fpa = pmFPARead(fits, camera, NULL);
+    papFPA *fpa = pmFPAConstruct(camera, NULL);
+
+    // Cut off a lot of the chips so I can fit things in memory
+    {
+	psArray *chips = fpa->chips;
+	papChip *chip = chips->data[0];
+	psFree(chips);
+	chips = psArrayAlloc(1);
+	chips->data[0] = chip;
+    }
+
+    (void)pmFPARead(fpa, fits);
     (void)pmFPAPrint(fpa);
 
-    printf("Test: %d %d\n", psCellGetValueS32(fpa, "ccd15", "left", "CELL.XPARITY"),
-	   psCellGetValueS32(fpa, "ccd15", "right", "CELL.XPARITY"));
-    printf("Test: %d %d\n", psCellGetValueS32(fpa, "ccd15", "left", "CELL.YPARITY"),
-	   psCellGetValueS32(fpa, "ccd15", "right", "CELL.YPARITY"));
-    printf("Test: %d %d\n", psCellGetValueS32(fpa, "ccd15", "left", "CELL.YPARITY"),
-	   psCellGetValueS32(fpa, "ccd15", "right", "CELL.YPARITY"));
+    // Morph to a splice image
+    psMetadata *newCamera = psMetadataConfigParse(NULL, &badLines, "megacam_splice.config", true);
+    papFPA *newfpa = pmFPAConstruct(newCamera, NULL);
+
+    // Cut off a lot of the chips so I can fit things in memory
+    {
+	psArray *chips = newfpa->chips;
+	papChip *chip = chips->data[0];
+	psFree(chips);
+	chips = psArrayAlloc(1);
+	chips->data[0] = chip;
+    }
+
+    pmFPAPrint(newfpa);
+    pmFPAMorph(newfpa, fpa, 0, 0);
+    pmFPAPrint(newfpa);
+
+#if 0
+    // Write out
+    psFits *newfits = psFitsAlloc("test.fits");
+    pmFPAWrite(fits, newfpa);
+    psFree(newfits);
+#endif
+
+#if 1
+    psArray *chips = newfpa->chips;
+    for (int i = 0; i < chips->n; i++) {
+	papChip *chip = chips->data[i];	// Chip of interest
+	psArray *cells = chip->cells;	// Array of cells
+
+	if (chip->extname) {
+	    psFits *fp = psFitsAlloc(chip->extname);
+	    psFitsWriteImage(fp, chip->header, chip->pixels->data[i], i);
+	    psFree(fp);
+	}
+
+	for (int j = 0; j < cells->n; j++) {
+	    papCell *cell = cells->data[j]; // Cell of interest
+
+	    if (cell->extname) {
+		psFits *fp = psFitsAlloc(cell->extname);
+		psFitsWriteImage(fp, cell->header, cell->pixels->data[i], i);
+		psFree(fp);
+	    }
+	}
+    }
+#endif
+
+    psFree(newfpa);
+    psFree(newCamera);
 
     // Tidy up
