Index: /trunk/archive/scripts/src/Makefile
===================================================================
--- /trunk/archive/scripts/src/Makefile	(revision 4308)
+++ /trunk/archive/scripts/src/Makefile	(revision 4309)
@@ -1,11 +1,13 @@
 SHELL = /bin/sh
 CC = gcc
-CFLAGS += -O2 -g -std=c99 -I/home/mithrandir/price/pan-starrs/jhroot/i686-pc-linux-gnu/include/ -D_GNU_SOURCE
+CFLAGS += -O0 -g -std=c99 -Werror -I/home/mithrandir/price/pan-starrs/jhroot/i686-pc-linux-gnu/include/ -D_GNU_SOURCE
 PSLIB += -L/home/mithrandir/price/pan-starrs/jhroot/i686-pc-linux-gnu/lib/ -lpslib -lgsl -lgslcblas -lfftw3f -lsla -lcfitsio -lm -lxml2 -lmysqlclient
 LDFLAGS += $(PSLIB)
 
-OBJS = test_pmFPAfromHeader.o pmCameraFromHeader.o pmFPAFromHeader.o psAdditionals.o papStuff.o
+BUILD_OBJS = test_pmFPAfromHeader.o pmCameraFromHeader.o pmFPAFromHeader.o psAdditionals.o papStuff.o
+BUILD_TARGET = test_pmFPAfromHeader
 
-TARGET = test_pmFPAfromHeader
+READ_OBJS = test_pmFPARead.o pmCameraFromHeader.o pmFPARead.o papFocalPlane.o psAdditionals.o papStuff.o
+READ_TARGET = test_pmFPARead
 
 .PHONY: tags clean empty
@@ -14,6 +16,6 @@
 		$(CC) -c $(CFLAGS) $(OPTFLAGS) $<
 
-$(TARGET):	$(OBJS)
-		$(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(OPTFLAGS)
+$(READ_TARGET):	$(READ_OBJS)
+		$(CC) $(CFLAGS) -o $@ $(READ_OBJS) $(LDFLAGS) $(OPTFLAGS)
 
 clean:
Index: /trunk/archive/scripts/src/gpc1_raw.config
===================================================================
--- /trunk/archive/scripts/src/gpc1_raw.config	(revision 4308)
+++ /trunk/archive/scripts/src/gpc1_raw.config	(revision 4309)
@@ -17,8 +17,68 @@
 CONTENTS	METADATA
 	# Extension name, type
-	cell00	STR	pitch10u
-	cell01	STR	pitch10u
-	cell10	STR	pitch10u
-	cell11	STR	pitch12u
+	xy00	STR	pitch10u
+	xy01	STR	pitch10u
+	xy02	STR	pitch10u
+	xy03	STR	pitch10u
+	xy04	STR	pitch10u
+	xy05	STR	pitch10u
+	xy06	STR	pitch10u
+	xy07	STR	pitch10u
+	xy10	STR	pitch10u
+	xy11	STR	pitch10u
+	xy12	STR	pitch10u
+	xy13	STR	pitch10u
+	xy14	STR	pitch10u
+	xy15	STR	pitch10u
+	xy16	STR	pitch10u
+	xy17	STR	pitch10u
+	xy20	STR	pitch10u
+	xy21	STR	pitch10u
+	xy22	STR	pitch10u
+	xy23	STR	pitch10u
+	xy24	STR	pitch10u
+	xy25	STR	pitch10u
+	xy26	STR	pitch10u
+	xy27	STR	pitch10u
+	xy30	STR	pitch10u
+	xy31	STR	pitch10u
+	xy32	STR	pitch10u
+	xy33	STR	pitch10u
+	xy34	STR	pitch10u
+	xy35	STR	pitch10u
+	xy36	STR	pitch10u
+	xy37	STR	pitch10u
+	xy40	STR	pitch10u
+	xy41	STR	pitch10u
+	xy42	STR	pitch10u
+	xy43	STR	pitch10u
+	xy44	STR	pitch10u
+	xy45	STR	pitch10u
+	xy46	STR	pitch10u
+	xy47	STR	pitch10u
+	xy50	STR	pitch10u
+	xy51	STR	pitch10u
+	xy52	STR	pitch10u
+	xy53	STR	pitch10u
+	xy54	STR	pitch10u
+	xy55	STR	pitch10u
+	xy56	STR	pitch10u
+	xy57	STR	pitch10u
+	xy60	STR	pitch10u
+	xy61	STR	pitch10u
+	xy62	STR	pitch10u
+	xy63	STR	pitch10u
+	xy64	STR	pitch10u
+	xy65	STR	pitch10u
+	xy66	STR	pitch10u
+	xy67	STR	pitch10u
+	xy70	STR	pitch10u
+	xy71	STR	pitch10u
+	xy72	STR	pitch10u
+	xy73	STR	pitch10u
+	xy74	STR	pitch10u
+	xy75	STR	pitch10u
+	xy76	STR	pitch10u
+	xy77	STR	pitch10u
 END
 
@@ -26,10 +86,11 @@
 CELLS	METADATA
 	pitch10u	METADATA
-		CELL.BIASSEC	STR	VALUE:[1:10,1:614];[625:674,1:614]
-		CELL.TRIMSEC	STR	VALUE:[11:624,1:614]
+		CELL.BIASSEC	STR	VALUE:[575:606,1:594]
+		CELL.TRIMSEC	STR	VALUE:[1:574,1:594]
 	#	CELL.BIASSEC	STR	HEADER:BIASSEC
-	#	CELL.TRIMSEC	STR	HEADER:TRIMSEC
+	#	CELL.TRIMSEC	STR	HEADER:DATASEC
 	END
 
+	# This is just in here for fun
 	pitch12u	METADATA
 		CELL.BIASSEC	STR	VALUE:[1:10,1:512];[523:574,1:512]
Index: /trunk/archive/scripts/src/papFocalPlane.c
===================================================================
--- /trunk/archive/scripts/src/papFocalPlane.c	(revision 4308)
+++ /trunk/archive/scripts/src/papFocalPlane.c	(revision 4309)
@@ -1,6 +1,8 @@
 #include <stdio.h>
+#include <string.h>
 #include "pslib.h"
 #include "papFocalPlane.h"
-#include "pmRead.h"
+#include "psAdditionals.h"
+#include "pmFPARead.h"
 
 // I'm sure this code, especially the DB lookup, leaks memory something chronic....
@@ -8,7 +10,7 @@
 
 // Look for a value already cached
-static psMetadataItem *getValueFromCache(const psFPA *fpa, // The FPA that contains the chip
-					 const psChip *chip, // The chip that contains the cell
-					 const psCell *cell, // The cell
+static psMetadataItem *getValueFromCache(const papFPA *fpa, // The FPA that contains the chip
+					 const papChip *chip, // The chip that contains the cell
+					 const papCell *cell, // The cell
 					 const char *valueName // Name of value
     )
@@ -31,7 +33,7 @@
 
 // Put a value that we've found into the cache
-static void setValueInCache(psFPA *fpa, // The FPA that contains the chip
-			    psChip *chip, // The chip that contains the cell
-			    psCell *cell, // The cell
+static void setValueInCache(papFPA *fpa, // The FPA that contains the chip
+			    papChip *chip, // The chip that contains the cell
+			    papCell *cell, // The cell
 			    const char *valueName, // Name of value
 			    const psMetadataItem *value	// Item containing the value
@@ -56,12 +58,14 @@
 
 
-static psMetadataItem *getValueFromHeader(const psFPA *fpa, // The FPA that contains the chip
-					  const psChip *chip, // The chip that contains the cell
-					  const psCell *cell, // The cell
+static psMetadataItem *getValueFromHeader(papFPA *fpa, // The FPA that contains the chip
+					  papChip *chip, // The chip that contains the cell
+					  papCell *cell, // The cell
 					  const char *valueName // Name of value
     )
 {
+    bool mdStatus = true;		// Status of MD lookup
+
     // Look for how to translate the concept into a FITS header name
-    const char *header = psMetadataLookupString(fpa->fits, valueName);
+    const char *header = psMetadataLookupString(&mdStatus, fpa->fits, valueName);
     if (strlen(header) > 0) {
 	// We have a FITS header to look up --- search each level
@@ -70,5 +74,5 @@
 	    if (cellItem) {
 		// XXX: Need to clean up before returning
-		setValueInCache(NULL, NULL, cell, cellItem);
+		setValueInCache(NULL, NULL, cell, valueName, cellItem);
 		return cellItem;
 	    }
@@ -79,5 +83,5 @@
 	    if (chipItem) {
 		// XXX: Need to clean up before returning
-		setValueInCache(NULL, chip, cell, chipItem);
+		setValueInCache(NULL, chip, cell, valueName, chipItem);
 		return chipItem;
 	    }
@@ -88,5 +92,5 @@
 	    if (fpaItem) {
 		// XXX: Need to clean up before returning
-		setValueInCache(fpa, chip, cell, fpaItem);
+		setValueInCache(fpa, chip, cell, valueName, fpaItem);
 		return fpaItem;
 	    }
@@ -100,13 +104,13 @@
 
 // Look for a default
-static psMetadataItem *getValueFromDefault(const psFPA *fpa, // The FPA that contains the chip
-					   const psChip *chip, // The chip that contains the cell
-					   const psCell *cell, // The cell
+static psMetadataItem *getValueFromDefault(papFPA *fpa, // The FPA that contains the chip
+					   papChip *chip, // The chip that contains the cell
+					   papCell *cell, // The cell
 					   const char *valueName // Name of value
     )
 {
-    psMetadataItem *defItem = psMetadataLookup(fpa->defaults, valueName);
+    psMetadataItem *defItem = psMetadataLookup((psMetadata*)fpa->defaults, valueName);
     if (defItem) {
-	setValueInCache(fpa, chip, cell, defItem);
+	setValueInCache(fpa, chip, cell, valueName, defItem);
     }
     return defItem;			// defItem is either NULL or points to what was desired
@@ -115,7 +119,7 @@
 
 // Look for a database lookup
-static psMetadataItem *getValueFromDB(const psFPA *fpa, // The FPA that contains the chip
-				      const psChip *chip, // The chip that contains the cell
-				      const psCell *cell, // The cell
+static psMetadataItem *getValueFromDB(papFPA *fpa, // The FPA that contains the chip
+				      papChip *chip, // The chip that contains the cell
+				      papCell *cell, // The cell
 				      const char *valueName // Name of value
     )
@@ -123,11 +127,11 @@
     if (fpa->db) {
 	// The database has been initialised
-
-	psMetadata *dbLookup = psMetadataLookupMD(fpa->database, valueName);
+	bool mdStatus = true;		// Status of MD lookup
+	psMetadata *dbLookup = psMetadataLookupMD(&mdStatus, fpa->database, valueName);
 	if (dbLookup) {
-	    const char *tableName = psMetadataLookupString(dbLookup, "TABLE"); // Name of the table
-	    const char *colName = psMetadataLookupString(dbLookup, "COLUMN"); // Name of the column
-	    const char *givenCols = psMetadataLookupString(dbLookup, "GIVENDBCOL");	// Name of "where" columns
-	    const char *givenPS = psMetadataLookupString(dbLookup, "GIVENPS"); // Values for "where" columns
+	    const char *tableName = psMetadataLookupString(&mdStatus, dbLookup, "TABLE"); // Name of the table
+	    const char *colName = psMetadataLookupString(&mdStatus, dbLookup, "COLUMN"); // Name of the column
+	    const char *givenCols = psMetadataLookupString(&mdStatus, dbLookup, "GIVENDBCOL");	// Name of "where" columns
+	    const char *givenPS = psMetadataLookupString(&mdStatus, dbLookup, "GIVENPS"); // Values for "where" columns
 	    
 	    // Now, need to get the "given"s
@@ -135,5 +139,5 @@
 		psList *cols = papSplit(givenCols, ",;"); // List of column names
 		psList *values = papSplit(givenPS, ",;"); // List of value names for the columns
-		psMetadata *selection = psMetadataAlloc(NULL); // The stuff to select in the DB
+		psMetadata *selection = psMetadataAlloc(); // The stuff to select in the DB
 		if (cols->size != values->size) {
 		    psLogMsg(__func__, PS_LOG_WARN, "The GIVENDBCOL and GIVENPS entries for %s do not have "
@@ -159,5 +163,5 @@
 			    }
 			    if (! item) {
-				psLogMsg(__func__, PS_LOG_ERR, "Unable to find the value name %s for DB "
+				psLogMsg(__func__, PS_LOG_ERROR, "Unable to find the value name %s for DB "
 					 " lookup on %s --- ignored.\n", name, valueName);
 			    } else {
@@ -199,5 +203,5 @@
 }
 
-psMetadataItem *psCellGetValue(const psFPA *fpa, // The FPA that contains the 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
@@ -205,6 +209,7 @@
 			       )
 {
-    psChip *chip = psMetadataLookupChip(fpa, chipName);	// The chip
-    psCell *cell = psMetadataLookupCell(chip, cellName); // The cell
+    bool mdStatus = true;		// Status of MD lookup
+    papChip *chip = psMetadataLookupChip(&mdStatus, fpa->chips, chipName); // The chip
+    papCell *cell = psMetadataLookupCell(&mdStatus, chip->cells, cellName); // The cell
 
     // Try cache, headers, database, defaults in order
@@ -223,5 +228,5 @@
 }
 
-float psCellGetValueF32(const psFPA *fpa, // The FPA that contains the cell
+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
@@ -244,5 +249,5 @@
 }
 
-int psCellGetValueS32(const psFPA *fpa, // The FPA that contains the cell
+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
@@ -250,5 +255,5 @@
     )
 {
-    int value = NAN;			// Result
+    int value = 0;			// Result
     psMetadataItem *item = psCellGetValue(fpa, chipName, cellName, valueName); // The item
     if (! item) {
@@ -266,5 +271,5 @@
 
 
-double psCellGetValueF64(const psFPA *fpa, // The FPA that contains the cell
+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
@@ -288,5 +293,5 @@
 
 
-psString psCellGetValueString(const psFPA *fpa, // The FPA that contains the cell
+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
@@ -308,2 +313,136 @@
     return value;
 }
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Allocators
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+papFPA *papFPAAlloc(psMetadata *fits, // FITS translation info
+		    psMetadata *database, // Database lookup info
+		    psMetadata *defaults, // Defaults info
+		    psDB *db		// Database handle
+    )
+{
+    papFPA *fpa = psAlloc(sizeof(papFPA));// The FPA
+    psMemSetDeallocator(fpa, (psFreeFcn)p_papFPAFree);
+
+    // Fill in the various components
+    fpa->fits = psMemIncrRefCounter(fits);
+    fpa->database = psMemIncrRefCounter(database);
+    fpa->defaults = psMemIncrRefCounter(defaults);
+
+    fpa->values = psMetadataAlloc();
+    fpa->header = NULL;
+    fpa->db = psMemIncrRefCounter(db);
+
+    fpa->chips = psMetadataAlloc();
+    fpa->images = NULL;
+
+    return fpa;
+}
+
+void p_papFPAFree(papFPA *fpa)
+{
+    psFree((psPtr)fpa->fits);
+    psFree((psPtr)fpa->database);
+    psFree((psPtr)fpa->defaults);
+
+    psFree(fpa->values);
+    psFree(fpa->header);
+    if (fpa->db) {
+	psDBCleanup((psDB*)fpa->db);
+    }
+
+    psFree(fpa->chips);
+    psFree(fpa->images);
+}
+
+papChip *papChipAlloc(papFPA *fpa,		// FPA to which the chip belongs
+		    const char *name	// Name of the chip
+    )
+{
+    papChip *chip = psAlloc(sizeof(papChip)); // The chip
+    psMemSetDeallocator(chip, (psFreeFcn)p_papChipFree);
+    psMetadataAdd(fpa->chips, PS_LIST_TAIL, name, PS_META_CHIP, "Added on allocation of chip", chip);
+
+    chip->values = psMetadataAlloc();
+    chip->header = NULL;
+
+    chip->cells = psMetadataAlloc();
+    chip->images = NULL;
+
+    return chip;
+}
+
+void p_papChipFree(papChip *chip)
+{
+    psFree(chip->values);
+    psFree(chip->header);
+    psFree(chip->cells);
+    psFree(chip->images);
+}
+
+papCell *papCellAlloc(papChip *chip,	// Chip to which the cell belongs
+		    const char *name,	// Name of the cell
+		    int nReadouts	// Number of readouts contained
+    )
+{
+    papCell *cell = psAlloc(sizeof(papCell)); // The cell
+    psMemSetDeallocator(cell, (psFreeFcn)p_papCellFree);
+    psMetadataAdd(chip->cells, PS_LIST_TAIL, name, PS_META_CELL, "Added on allocation of cell", cell);
+
+    cell->values = psMetadataAlloc();
+    cell->header = NULL;
+
+    cell->readouts = psArrayAlloc(nReadouts);
+    cell->images = NULL;
+
+    return cell;
+}
+
+void p_papCellFree(papCell *cell)
+{
+    psFree(cell->values);
+    psFree(cell->header);
+    psFree(cell->readouts);
+    psFree(cell->images);
+}
+
+papReadout *papReadoutAlloc(papCell *cell, // Cell to which the readout belongs
+			    int readoutNum, // Number of the readout
+			    psImage *image, // The pixels
+			    psList *overscans, // The overscan images
+			    int col0, int row0, int colParity, int rowParity, int colBin, int rowBin // Data
+    )
+{
+    papReadout *readout = psAlloc(sizeof(papReadout));
+    psMemSetDeallocator(readout, (psFreeFcn)p_papReadoutFree);
+    psArray *readouts = cell->readouts;
+    if (readoutNum >= readouts->nalloc) {
+	readouts = psArrayRealloc(readouts, readoutNum);
+	cell->readouts = readouts;
+    }
+    readouts->data[readoutNum] = readout;
+    
+    // Set the components
+    readout->image = image;
+    readout->overscans = overscans;
+    
+    readout->values = psMetadataAlloc();
+    
+    *(int*)&readout->col0 = col0;
+    *(int*)&readout->row0 = row0;
+    *(int*)&readout->colParity = colParity;
+    *(int*)&readout->rowParity = rowParity;
+    *(int*)&readout->colBins = colBin;
+    *(int*)&readout->rowBins = rowBin;
+
+    return readout;
+}
+
+void p_papReadoutFree(papReadout *readout)
+{
+    psFree(readout->image);
+    psFree(readout->overscans);
+    psFree(readout->values);
+}
Index: /trunk/archive/scripts/src/papFocalPlane.h
===================================================================
--- /trunk/archive/scripts/src/papFocalPlane.h	(revision 4308)
+++ /trunk/archive/scripts/src/papFocalPlane.h	(revision 4309)
@@ -3,12 +3,19 @@
 
 #include "pslib.h"
+#include "papStuff.h"
+
+// Temporary metadata types
+#define PS_META_CHIP PS_META_UNKNOWN
+#define PS_META_CELL PS_META_UNKNOWN
+
 
 typedef struct {
     // How to get information
-    psMetadata *defaults;		// IPP concepts supplied with defaults
-    psMetadata *database;		// IPP concepts supplied from database lookup
-    psMetadata *fits;			// IPP concepts supplied from FITS headers
-
+    const psMetadata *defaults;		// IPP concepts supplied with defaults
+    const psMetadata *database;		// IPP concepts supplied from database lookup
+    const psMetadata *fits;		// IPP concepts supplied from FITS headers
+    
     // A source of information
+    psMetadata *values;			// Important values (cached)
     psMetadata *header;			// The FITS header
     psDB *db;				// The database handle
@@ -17,6 +24,6 @@
     psMetadata *chips;			// The chips (referred to by name)
 
-    psImage *image;			// The pixel data, if it corresponds to this level
-} psFPA;
+    psArray *images;			// The pixel data, if it corresponds to this level
+} papFPA;
 
 typedef struct {
@@ -28,6 +35,6 @@
     psMetadata *cells;			// The cells (referred to by name)
 
-    psImage *image;			// The pixel data, if it corresponds to this level
-} psChip;
+    psArray *images;			// The pixel data, if it corresponds to this level
+} papChip;
 
 typedef struct {
@@ -39,6 +46,6 @@
     psArray *readouts;			// The readouts (referred to by number)
 
-    psImage *image;			// The pixel data, if it corresponds to this level
-} psCell;
+    psArray *images;			// The pixel data, if it corresponds to this level
+} papCell;
 
 
@@ -53,9 +60,9 @@
     psList *overscans;			// Array of subimages containing the overscan region(s)
     psMetadata* values;			// readout-level metadata
-} psReadout;
+} papReadout;
 
 
 // Look for a particular value for a given cell (referred to by FPA+chip+cell)
-psMetadataItem *psCellGetValue(const psFPA *fpa, // The FPA that contains the 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
@@ -64,20 +71,20 @@
 
 // Type-specific functions provided as a convenience to the user
-float psCellGetValueF32(const psFPA *fpa, // The FPA that contains the cell
+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
 			const char *valueName // Name of value
 			);
-int psCellGetValueS32(const psFPA *fpa, // The FPA that contains the cell
+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
 		      const char *valueName // Name of value
 		      );
-double psCellGetValueF64(const psFPA *fpa, // The FPA that contains the cell
+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
 			 const char *valueName // Name of value
 			 );
-psString psCellGetValueString(const psFPA *fpa, // The FPA that contains the cell
+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
@@ -86,3 +93,30 @@
 
 
+// Allocators and deallocators
+papFPA *papFPAAlloc(psMetadata *fits, // FITS translation info
+		    psMetadata *database, // Database lookup info
+		    psMetadata *defaults, // Defaults info
+		    psDB *db		// Database handle
+    );
+void p_papFPAFree(papFPA *fpa);
+
+papChip *papChipAlloc(papFPA *fpa,		// FPA to which the chip belongs
+		    const char *name	// Name of the 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
+    );
+void p_papCellFree(papCell *cell);
+
+papReadout *papReadoutAlloc(papCell *cell,	// Cell to which the readout belongs
+			  int readoutNum, // Number of the readout
+			  psImage *image, // The pixels
+			  psList *overscans, // The overscan images
+			  int col0, int row0, int colParity, int rowParity, int colBin, int rowBin // Data
+    );
+void p_papReadoutFree(papReadout *readout);
+
 #endif
Index: /trunk/archive/scripts/src/papStuff.c
===================================================================
--- /trunk/archive/scripts/src/papStuff.c	(revision 4308)
+++ /trunk/archive/scripts/src/papStuff.c	(revision 4309)
@@ -2,4 +2,5 @@
 #include <string.h>
 #include "pslib.h"
+#include "papStuff.h"
 
 // Split string on given characters
@@ -23,5 +24,5 @@
 	    } else {
 		// We're at the end of the word
-		char *word = psStringNCopy(&string[start], i - start);
+		psString word = psStringNCopy(&string[start], i - start);
 		(void)psListAdd(values, PS_LIST_TAIL, word);
 		start = i + 1;
@@ -31,5 +32,5 @@
     if (start < length) {
 	// Copy the last word
-	char *word = psStringNCopy(&string[start], length - start);
+	psString word = psStringNCopy(&string[start], length - start);
 	(void)psListAdd(values, PS_LIST_TAIL, word);
     }
Index: /trunk/archive/scripts/src/papStuff.h
===================================================================
--- /trunk/archive/scripts/src/papStuff.h	(revision 4308)
+++ /trunk/archive/scripts/src/papStuff.h	(revision 4309)
@@ -4,4 +4,7 @@
 #include "pslib.h"
 
+// psString is a psLib version of a char*
+typedef char* psString;
+
 // Split string on given characters
 psList *papSplit(const char *string, const char *splitters);
Index: /trunk/archive/scripts/src/papmodule.h
===================================================================
--- /trunk/archive/scripts/src/papmodule.h	(revision 4309)
+++ /trunk/archive/scripts/src/papmodule.h	(revision 4309)
@@ -0,0 +1,6 @@
+// Load various include files
+
+#include "pmCameraFromHeader.h"
+#include "pmFPARead.h"
+#include "papStuff.h"
+#include "psAdditionals.h"
Index: /trunk/archive/scripts/src/pmCameraFromHeader.c
===================================================================
--- /trunk/archive/scripts/src/pmCameraFromHeader.c	(revision 4308)
+++ /trunk/archive/scripts/src/pmCameraFromHeader.c	(revision 4309)
@@ -2,5 +2,5 @@
 #include <string.h>
 #include "pslib.h"
-#include "psmodule.h"
+#include "papmodule.h"
 
 // Work out what camera we have, based on the FITS header and a set of rules specified in the IPP
@@ -31,5 +31,5 @@
 	} else if (instrumentItem->type == PS_META_STR) {
 	    psTrace(__func__, 5, "Reading instrument configuration for %s...\n", instrumentItem->name);
-	    instrument = psMetadataParseConfig(NULL, NULL, instrumentItem->data.V, true);
+	    instrument = psMetadataConfigParse(NULL, NULL, instrumentItem->data.V, true);
 	}
 
Index: /trunk/archive/scripts/src/pmFPAFromHeader.c
===================================================================
--- /trunk/archive/scripts/src/pmFPAFromHeader.c	(revision 4308)
+++ /trunk/archive/scripts/src/pmFPAFromHeader.c	(revision 4309)
@@ -80,23 +80,11 @@
 	}
 
-	bool extIsChips;		// Are the extensions chips (true) or cells (false)?
 	if (strcmp(extType, "CHIP") == 0) {
-	    extIsChips = true;	// Extensions are chips
-	} else if (strcmp(extType, "CELL") == 0) {
-	    extIsChips = false;	// Extensions are cells
-	} else {
-	    psError(PS_ERR_IO, false, "EXTENSIONS in camera definition is neither CHIP or CELL.\n");
-	    psFree(fpa);
-	    return NULL;
-	}
-
-	// Iterate through the contents
-	psMetadataIterator *contentsIter = psMetadataIteratorAlloc(contents, PS_LIST_HEAD, NULL);
-	psMetadataItem *contentItem = NULL; // Item from the metadata
-	while (contentItem = psMetadataGetAndIncrement(contentsIter)) {
-	    const char *extName = contentItem->name; // The name of the extension
-		
-	    if (extIsChips) {
-		// Content contains a list of cells
+	    // Extensions are chips; Content contains a list of cells
+	    psMetadataIterator *contentsIter = psMetadataIteratorAlloc(contents, PS_LIST_HEAD, NULL);
+	    psMetadataItem *contentItem = NULL; // Item from the metadata
+	    while (contentItem = psMetadataGetAndIncrement(contentsIter)) {
+		const char *extName = contentItem->name; // The name of the extension
+
 		if (contentItem->type != PS_META_STR) {
 		    psLogMsg(__func__, PS_LOG_WARN, "Type of content item (%x) is not string: ignored\n",
@@ -120,6 +108,14 @@
 		    psMetadataAdd(fpa, PS_LIST_TAIL, extName, PS_META_META, "", chip);
 		}
-	    } else {
-		// Content contains a chip name and cell type
+	    }
+	    psFree(contentsIter);
+
+	} else if (strcmp(extType, "CELL") == 0) {
+	    // Extensions are cells; Content contains a chip name and cell type
+	    psMetadataIterator *contentsIter = psMetadataIteratorAlloc(contents, PS_LIST_HEAD, NULL);
+	    psMetadataItem *contentItem = NULL; // Item from the metadata
+	    while (contentItem = psMetadataGetAndIncrement(contentsIter)) {
+		const char *extName = contentItem->name; // The name of the extension
+
 		if (contentItem->type != PS_META_STR) {
 		    psLogMsg(__func__, PS_LOG_WARN, "Type of content item (%x) is not string: ignored\n",
@@ -148,8 +144,38 @@
 		    }
 		}
-
-	    } // Type of extension
-	} // Iterating through contents
-	psFree(contentsIter);
+	    }
+	    psFree(contentsIter);
+	} else if (strcmp(extType, "NONE") == 0) {
+	    // No extensions; Content contains metadata, each entry is a chip with its component cells
+	    psMetadataIterator *contentsIter = psMetadataIteratorAlloc(contents, PS_LIST_HEAD, NULL);
+	    psMetadataItem *contentItem = NULL; // Item from the metadata
+	    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
+		    psMetadata *chip = psMetadataAlloc(); // 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)) {
+			psMetadata *cell = getCellData(camera, cellName);
+			psMetadataAdd(cell, PS_LIST_HEAD, "EXTENSION", PS_META_STR, "", "PHU");
+			psMetadataAdd(chip, PS_LIST_TAIL, cellName, PS_META_META, "", cell);
+		    }
+		    psFree(cellNamesIter);
+		    // Add the chip onto the FPA
+		    psMetadataAdd(fpa, PS_LIST_TAIL, chipName, PS_META_META, "", chip);
+		}
+	    }
+	    psFree(contentsIter);
+	} else {
+	    psError(PS_ERR_IO, false, "EXTENSIONS in camera definition is not CHIP, CELL or NONE.\n");
+	    psFree(fpa);
+	    return NULL;
+	} // Type of extension
 
     } else if (strcmp(phuType, "CHIP") == 0) {
Index: /trunk/archive/scripts/src/pmFPARead.c
===================================================================
--- /trunk/archive/scripts/src/pmFPARead.c	(revision 4309)
+++ /trunk/archive/scripts/src/pmFPARead.c	(revision 4309)
@@ -0,0 +1,532 @@
+#include <stdio.h>
+#include <string.h>
+#include "pslib.h"
+#include "papmodule.h"
+#include "papStuff.h"
+#include "papFocalPlane.h"
+
+// NOTE: Need to deal with header inheritance
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// File-static functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Copy a metadata
+static psMetadata *metadataCopy(psMetadata *md)
+{
+    psMetadata *new = psMetadataAlloc();// The metadata to return
+    psMetadataIterator *mdIter = psMetadataIteratorAlloc(md, PS_LIST_HEAD, NULL); // Iterator for the MD
+    psMetadataItem *item = NULL;	// Item from the metadata
+    while (item = psMetadataGetAndIncrement(mdIter)) {
+	if (! psMetadataAddItem(new, 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);
+
+    return new;
+}
+
+// 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 {
+#if 0
+	psMetadata *newCell = metadataCopy(cellData); // A copy of the cell, to be returned
+	return newCell;
+#else
+	return psMemIncrRefCounter(cellData);
+#endif
+    }
+}
+
+// Read a FITS extension into a chip
+static bool readExtension(psArray **images, // Array of images in which to read pixels
+			  psMetadata **header, // Metadata in which to read header
+			  psFits *fits,	// The FITS file from which to read
+			  const char *extName // The name of the extension
+    )
+{
+    if (! psFitsMoveExtName(fits, extName)) {
+	psError(PS_ERR_IO, false, "Unable to find extension %s in FITS file!\n", extName);
+	return false;
+    }
+    *header = psFitsReadHeader(*header, fits);
+    bool mdStatus = false;
+    int nAxis = psMetadataLookupS32(&mdStatus, *header, "NAXIS");
+    if (!mdStatus) {
+	psLogMsg(__func__, PS_LOG_WARN, "There is no NAXIS keyword in the FITS header of extension %s!\n",
+		 extName);
+    }
+    if (nAxis != 2 && nAxis != 3) {
+	psLogMsg(__func__, PS_LOG_WARN, "Image is not 2- or 3-dimensional --- reading into a single image "
+		 "anyway.\n");
+    }
+
+    int numPlanes = 1;			// Number of planes
+    if (nAxis > 3) {
+	numPlanes = psMetadataLookupS32(&mdStatus, *header, "NAXIS3");
+	if (!mdStatus) {
+	    psError(PS_ERR_IO, false, "Unable to read NAXIS3 for 3-dimensional image!\n");
+	    return false;
+	}
+    }
+    psRegion region = {0, 0, 0, 0};	// Region to read is everything
+
+    // Read each plane into the array
+    *images = psArrayAlloc(numPlanes); // Array of images
+    for (int i = 0; i < numPlanes; i++) {
+	(*images)->data[i] = psFitsReadImage((*images)->data[i], fits, region, 0);
+        if (! (*images)->data[i]) {
+	    psError(PS_ERR_IO, false, "Unable to read image for extension %s, plane %d\n", extName, i);
+	    return false;
+	}
+    }
+
+    return true;
+}
+
+// Read a list of regions (e.g., [x0:x1,y0:y1];[x2:x3,y2:y3]...), extract subimages and put them on a list
+static int readMultipleRegions(psList *list, // List to which to add images
+			       const char *string, // String from which to read regions.
+			       psImage *image // Image from which to get the region
+    )
+{
+    int numFound = 0;			// Number of regions found
+    char *stringCopy = psStringCopy(string); // Copy of the string (to preserve it intact)
+    char *startOfRegion = stringCopy; // The string containing all the regions yet to be parsed
+    while (startOfRegion) {
+	psRegion region = psRegionFromString(startOfRegion); // The region
+	if (isnan(region.x0) || isnan(region.x1) || isnan(region.y0) || isnan(region.y1)) {
+	    psLogMsg(__func__, PS_LOG_WARN, "Unable to read region (%s) --- ignored.\nOriginal string was "
+		     "%s.", startOfRegion, string);
+	} else {
+	    psTrace(__func__, 9, "region found: %f,%f --> %f,%f\n", region.x0, region.y0, region.x1,
+		    region.y1);
+	    numFound++;
+	    psImage *subImage = psImageSubset(image, region);
+	    psListAdd(list, PS_LIST_TAIL, subImage);
+	}
+
+	startOfRegion = strchr(startOfRegion, ';');
+	if (startOfRegion) {
+	    // Eat the semi-colon
+	    startOfRegion += 1;
+	}
+    }
+    psFree(stringCopy);
+
+    return numFound;
+}
+
+
+// Return a list of portions
+static psList *portions(psImage *image,	// The image to portion up
+			const char *methodValue, // How to determine the portions
+			psMetadata *header // FITS header from the file
+    )
+{
+    psList *subImages = psListAlloc(NULL); // The list of subimages, to be returned
+
+    psTrace(__func__, 7, "Parsing %s to get subimages....\n", methodValue);
+    // Parse the methodValue string into the METHOD:VALUE (e.g., HEADER:TRIMSEC, or VALUE:[1:100,1:100])
+    char *value = strchr(methodValue, ':') + 1; // Find the first colon, which is the split character
+    if (! value) {
+	psError(PS_ERR_IO, true, "Unable to separate string (%s) into METHOD:VALUE\n", methodValue);
+	return false;
+    }
+    psTrace(__func__, 7, "Value is %s\n", value);
+
+    if (strncmp(methodValue, "HEADER:", 7) == 0 || strncmp(methodValue, "HD:", 3) == 0) {
+	// Read the portions from the header
+	psList *keywords = papSplit(value, ",;"); // List of headers to look at
+	psListIterator *keywordsIter = psListIteratorAlloc(keywords, PS_LIST_HEAD, false); // Iterator
+	psString keyword = NULL;	// The header keyword
+	bool mdStatus = false;		// Status for MD lookup
+	while (keyword = psListGetAndIncrement(keywordsIter)) {
+	    psString regionString = psMetadataLookupString(&mdStatus, header, keyword);
+	    if (! mdStatus) {
+		psLogMsg(__func__, PS_LOG_WARN, "Unable to find header %s to determine image region --- "
+			 "ignored.\n", keyword);
+	    } else {
+		// We have the region: [x0:x1,y0:y1]
+		psRegion region = psRegionFromString(regionString);
+		psImage *subImage = psImageSubset(image, region);
+		psListAdd(subImages, PS_LIST_TAIL, subImage);
+	    }
+	}
+    } else if (strncmp(methodValue, "VALUE:", 6) == 0 || strncmp(methodValue, "VAL:", 4) == 0) {
+	// Read the portions right here
+	if (readMultipleRegions(subImages, value, image) == 0) {
+	    psLogMsg(__func__, PS_LOG_WARN, "No value specified: %s --- ignored.\n", methodValue);
+	}
+    } else {
+	// Try to interpret the methodValue as a "value" only (ie, no "HEADER:" or "VALUE:")
+	// Otherwise, we give up
+	if (readMultipleRegions(subImages, methodValue, image) == 0) {
+	    psLogMsg(__func__, PS_LOG_WARN, "Can't parse %s into a region --- ignored.\n", methodValue);
+	}
+    }
+
+    psTrace(__func__, 7, "%d regions found.\n", subImages->size);
+
+    return subImages;
+}
+
+// Portion out an image into the cell
+static bool portionCell(papCell *cell,	// The cell that gets its bits
+			psArray *images, // Array of images from which the readouts are assigned
+			psMetadata *header // FITS header from the file
+    )
+{
+    bool mdStatus = false;		// Status of MD lookup
+
+    // How do we get the trim section?
+    psString trimsecString = psMetadataLookupString(&mdStatus, cell->values, "CELL.TRIMSEC");
+    if (! mdStatus) {
+	psError(PS_ERR_IO, false, "Unable to find CELL.TRIMSEC specified --- unable to read cell.\n");
+	return false;
+    }
+
+    // How do we get the bias section(s)?
+    psString biassecString = psMetadataLookupString(&mdStatus, cell->values, "CELL.BIASSEC");
+    if (! mdStatus) {
+	psError(PS_ERR_IO, false, "Unable to find CELL.BIASSEC specified --- unable to read cell.\n");
+	return false;
+    }
+
+    // Iterate over each of the image planes
+    psArray *readouts = psArrayAlloc(images->n);
+    for (int i = 0; i < images->n; i++) {
+	// Get the TRIMSEC region
+	psList *trimsecs = portions(images->data[i], trimsecString, header);
+	if (trimsecs->size == 0) {
+	    psError(PS_ERR_IO, true, "No TRIMSEC region found for image.  Specified: %s\n", trimsecString);
+	    readouts->data[i] = NULL;
+	} else {
+	    // There can be only ONE!
+	    if (trimsecs->size > 1) {
+		psLogMsg(__func__, PS_LOG_WARN, "Multiple TRIMSECs were specified --- using only the first."
+			 "\n");
+	    }
+	    psImage *trimsec = psListGet(trimsecs, PS_LIST_HEAD);
+	    psFree(trimsecs);
+
+	    // Get the BIASSEC regions
+	    psList *biassecs = portions(images->data[i], biassecString, header);
+	    if (biassecs->size == 0) {
+		psLogMsg(__func__, PS_LOG_WARN, "No BIASSEC region found for image.  Specified: %s\n",
+			 biassecString);
+	    }
+
+	    papReadout *readout = papReadoutAlloc(cell, i, trimsec, biassecs, 0,0,0,0,1,1);
+	    readouts->data[i] = readout;
+	}
+
+    }
+
+    // Stuff the array of readouts into the cell
+    cell->readouts = readouts;
+
+    return true;
+
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Public functions
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+papFPA *pmFPARead(const psMetadata *camera, // The camera configuration
+		  psFits *fits // A FITS file
+    )
+{
+    bool mdStatus = true;		// Status from metadata lookups
+    // Get the configuration information out of the camera config
+    psMetadata *translation = psMetadataLookupMD(&mdStatus, camera, "TRANSLATION");
+    psMetadata *database = psMetadataLookupMD(&mdStatus, camera, "DATABASE");
+    psMetadata *defaults = psMetadataLookupMD(&mdStatus, camera, "DEFAULTS");
+    psDB *dbh = NULL;
+
+    // Generate a build tree
+    papFPA *fpa = papFPAAlloc(translation, database, defaults, dbh); // The FPA to fill out
+
+    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->images), &(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
+		    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->images)->n); // The cell
+			cell->values = getCellData(camera, cellName);
+			portionCell(cell, chip->images, chip->header);
+		    }
+		    psFree(cellNamesIter);
+		    psFree(cellNames);
+		}
+	    }
+
+	} else if (strncmp(extType, "CELL", 4) == 0) {
+	    // Extensions are cells; Content contains a chip name and cell type
+	    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
+
+			papChip *chip = psMetadataLookupChip(&mdStatus, fpa->chips, chipName); // The chip
+			if (! mdStatus && ! chip) {
+			    papChip *chip = papChipAlloc(fpa, "CHIP");
+			}
+			// The cell
+			psArray *images = NULL;
+			psMetadata *header = NULL;
+			readExtension(&images, &header, fits, extName);
+			papCell *cell = papCellAlloc(chip, extName, images->n); // The cell
+			cell->images = images;
+			cell->header = header;
+			cell->values = getCellData(camera, extName);
+			portionCell(cell, cell->images, cell->header);
+		    }
+		}
+	    }
+
+	} else if (strncmp(extType, "NONE", 4) == 0) {
+	    // No extensions; Content contains metadata, each entry is a chip with its component cells
+	    readExtension(&(fpa->images), &(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, "CHIP"); // 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, "CELL", ((psArray*)fpa->images)->n); // The cell
+			cell->values = getCellData(camera, cellName);
+			portionCell(cell, fpa->images, 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->images), &(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->images)->n); // The cell
+		cell->values = getCellData(camera, cellName);
+		portionCell(cell, fpa->images, 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->images = images;
+		    cell->header = header;
+		    cell->values = getCellData(camera, cellType);
+		    portionCell(cell, cell->images, 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;
+    }
+
+    
+    // Print out the build tree
+    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->images) {
+	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->images) {
+		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->images) {
+			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_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);
+
+    return fpa;
+
+}
Index: /trunk/archive/scripts/src/pmFPARead.h
===================================================================
--- /trunk/archive/scripts/src/pmFPARead.h	(revision 4309)
+++ /trunk/archive/scripts/src/pmFPARead.h	(revision 4309)
@@ -0,0 +1,12 @@
+#ifndef PM_FPA_READ_H
+#define PM_FPA_READ_H
+
+#include "papFocalPlane.h"
+
+// Read the contents of a FITS file (format specified by the camera configuration) into memory
+papFPA *pmFPARead(const psMetadata *camera, // The camera configuration
+		  psFits *fits // A FITS file
+    );
+
+
+#endif
Index: /trunk/archive/scripts/src/psAdditionals.c
===================================================================
--- /trunk/archive/scripts/src/psAdditionals.c	(revision 4308)
+++ /trunk/archive/scripts/src/psAdditionals.c	(revision 4309)
@@ -2,5 +2,5 @@
 #include <string.h>
 #include "pslib.h"
-#include "psmodule.h"
+#include "papmodule.h"
 
 
@@ -35,8 +35,8 @@
 
 
-const char *psMetadataLookupString(bool *status, const psMetadata *md, const char *key)
+char *psMetadataLookupString(bool *status, const psMetadata *md, const char *key)
 {
     psMetadataItem *item = psMetadataLookup((psMetadata*)md, key); // The metadata with instruments
-    const char *value = NULL;		// The value to return
+    char *value = NULL;			// The value to return
     if (!item) {
 	// The given key isn't in the metadata
@@ -64,4 +64,61 @@
 }
 
+papChip *psMetadataLookupChip(bool *status, const psMetadata *md, const char *key)
+{
+    psMetadataItem *item = psMetadataLookup((psMetadata*)md, key); // The metadata with instruments
+    papChip *value = NULL;		// The value to return
+    if (!item) {
+	// The given key isn't in the metadata
+	if (status) {
+	    *status = false;
+	} else {
+	    psError(PS_ERR_IO, true, "Couldn't find %s in the metadata.\n");
+	}
+    } else if (item->type != PS_META_CHIP) {
+	// The value at the key isn't metadata
+	if (status) {
+	    *status = false;
+	} else {
+	    psLogMsg(__func__, PS_LOG_WARN, "%s isn't of type PS_META_CHIP, as expected.\n");
+	}
+	value = NULL;
+    } else {
+	// We have the requested metadata
+	if (status) {
+	    *status = true;
+	}
+	value = item->data.V; // The requested metadata
+    }
+    return value;
+}
+
+papCell *psMetadataLookupCell(bool *status, const psMetadata *md, const char *key)
+{
+    psMetadataItem *item = psMetadataLookup((psMetadata*)md, key); // The metadata with instruments
+    papCell *value = NULL;		// The value to return
+    if (!item) {
+	// The given key isn't in the metadata
+	if (status) {
+	    *status = false;
+	} else {
+	    psError(PS_ERR_IO, true, "Couldn't find %s in the metadata.\n");
+	}
+    } else if (item->type != PS_META_CELL) {
+	// The value at the key isn't metadata
+	if (status) {
+	    *status = false;
+	} else {
+	    psLogMsg(__func__, PS_LOG_WARN, "%s isn't of type PS_META_CHIP, as expected.\n");
+	}
+	value = NULL;
+    } else {
+	// We have the requested metadata
+	if (status) {
+	    *status = true;
+	}
+	value = item->data.V; // The requested metadata
+    }
+    return value;
+}
 
 void psMetadataPrint(psMetadata *md, int level)
Index: /trunk/archive/scripts/src/psAdditionals.h
===================================================================
--- /trunk/archive/scripts/src/psAdditionals.h	(revision 4308)
+++ /trunk/archive/scripts/src/psAdditionals.h	(revision 4309)
@@ -8,5 +8,9 @@
 
 // Get a value from the metadata that we believe should be a string
-const char *psMetadataLookupString(bool *status, const psMetadata *md, const char *key);
+char *psMetadataLookupString(bool *status, const psMetadata *md, const char *key);
+
+papChip *psMetadataLookupChip(bool *status, const psMetadata *md, const char *key);
+papCell *psMetadataLookupCell(bool *status, const psMetadata *md, const char *key);
+
 
 // Print out the metadata
Index: /trunk/archive/scripts/src/test_pmFPARead.c
===================================================================
--- /trunk/archive/scripts/src/test_pmFPARead.c	(revision 4309)
+++ /trunk/archive/scripts/src/test_pmFPARead.c	(revision 4309)
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include "pslib.h"
+#include "papmodule.h"
+#include "papFocalPlane.h"
+
+psMemoryId memPrint(const psMemBlock *ptr)
+{
+    fprintf(stderr, "File: %s\n", ptr->file);
+    fprintf(stderr, "Memory block %qd:\n"
+	    "\tFile %s, line %d, size %d\n"
+	    "\tRefCounter: %qd\n"
+	    "\tPosts: %x %x %x\n",
+	    ptr->id, ptr->file, ptr->lineno, ptr->userMemorySize, ptr->refCounter,
+	    ptr->startblock, ptr->endblock, *(psPtr *)((int8_t *) (ptr + 1) + ptr->userMemorySize));
+    return 0;
+}
+ 
+int main(int argc, char *argv[])
+{
+#if 0
+    psMemAllocateCallbackSet(memPrint);
+    psMemAllocateCallbackSetID(921);
+    psMemFreeCallbackSet(memPrint);
+    psMemFreeCallbackSetID(921);
+#endif
+
+    psTraceSetLevel("readMultipleRegions", 0);
+    psTraceSetLevel("portions", 0);
+    psTraceSetLevel("pmFPARead", 10);
+    
+
+    if (argc != 3) {
+	printf("Usage: %s CONFIG IMAGE\n", argv[0]);
+	exit(EXIT_FAILURE);
+    }
+    const char *configName = argv[1];
+    const char *imageName = argv[2];
+	
+    int badLines = 0;			// Number of bad lines in camera configuration
+
+    (void)psTraceSetLevel("pmFPAfromHeader", 10);
+
+    psMetadata *camera = psMetadataConfigParse(NULL, &badLines, configName, true); // Camera config
+
+    if (badLines > 0) {
+	printf("%d bad lines encountered while reading %s\n", badLines, configName);
+    }
+
+    printf("Camera information:\n");
+    psMetadataPrint(camera, 0);
+   
+    psFits *fits = psFitsAlloc(imageName);
+    papFPA *fpa = pmFPARead(camera, fits);
+
+    psMemCheckCorruption(true);
+
+    psFree(camera);
+    psFree(fits);
+
+    psFree(fpa);
+
+    return 0;
+}
