Index: /trunk/archive/scripts/src/phase2/Makefile
===================================================================
--- /trunk/archive/scripts/src/phase2/Makefile	(revision 5563)
+++ /trunk/archive/scripts/src/phase2/Makefile	(revision 5564)
@@ -3,7 +3,8 @@
 
 #DEVFLAGS += -DPRODUCTION	# Produce binary as for production (don't use old APIs)
-DEVFLAGS += -DTESTING		# Testing version
+DEVFLAGS += -DTESTING 		# Testing version
+#DEVFLAGS += -DPS_NO_TRACE	# No trace messages
 
-CFLAGS += -O0 -g -std=c99 -Werror -I$(JHBUILD)/include/ -I$(JHBUILD)/include/libxml2/ -DPS_NO_TRACE -D_GNU_SOURCE $(DEVFLAGS)
+CFLAGS += -O0 -g -std=c99 -Werror -I$(JHBUILD)/include/ -I$(JHBUILD)/include/libxml2/ -D_GNU_SOURCE $(DEVFLAGS)
 PSLIB += -L$(JHBUILD)/lib/ -lpslib -lgsl -lgslcblas -lfftw3f -lsla -lcfitsio -lm -L$(JHBUILD)/lib/libxml2/ -L$(JHBUILD)/lib/mysql/ -lxml2 -lmysqlclient
 PSMODULE += -L$(JHBUILD)/lib/ -lpsmodule
Index: /trunk/archive/scripts/src/phase2/ipprc.config
===================================================================
--- /trunk/archive/scripts/src/phase2/ipprc.config	(revision 5563)
+++ /trunk/archive/scripts/src/phase2/ipprc.config	(revision 5564)
@@ -22,5 +22,7 @@
 TRACE		METADATA			# Trace levels
 	pmConfigRead		S32	10
-	pmFPARead		S32	10
 	pap			S32	10
+	pmFPAWriteMask		S32	10
+	pmFPAWriteWeight	S32	10
+	p_pmFPATranslateName	S32	10
 END
Index: /trunk/archive/scripts/src/phase2/megacam_raw.config
===================================================================
--- /trunk/archive/scripts/src/phase2/megacam_raw.config	(revision 5563)
+++ /trunk/archive/scripts/src/phase2/megacam_raw.config	(revision 5564)
@@ -159,2 +159,11 @@
 	PHASE2		STR	phase2.config		# Phase 2 recipe details
 END
+
+
+# How to get the supplementary stuff: mask and weight
+SUPPLEMENTARY	METADATA
+	MASK.SOURCE	STR	FILE		# Source type for mask: EXT | FILE
+	MASK.NAME	STR	%a_mask.fits	# Name for mask extension or filename
+	WEIGHT.SOURCE	STR	EXT		# Source type for weight: EXT | FILE
+	WEIGHT.NAME	STR	%f-weight	# Name for weight extension or filename
+END
Index: /trunk/archive/scripts/src/phase2/papPhase2.c
===================================================================
--- /trunk/archive/scripts/src/phase2/papPhase2.c	(revision 5563)
+++ /trunk/archive/scripts/src/phase2/papPhase2.c	(revision 5564)
@@ -29,5 +29,5 @@
 // * Write calibrated image
 // * Write source catalogue, astrometry, etc.
- 
+
 // Currently neglecting different input types (F32, S32, U16, etc)
 
@@ -302,4 +302,53 @@
 #else
     psFree(inputFile);
+#endif
+
+
+#if 1
+    {
+	// Generate mask and weight frame
+	p_pmHDU *hdu = NULL;
+	if (input->hdu) {
+	    hdu = input->hdu;
+	}
+	psArray *chips = input->chips;
+	for (int chipNum = 0; chipNum < chips->n; chipNum++) {
+	    pmChip *chip = chips->data[chipNum];
+	    if (chip->valid) {
+		if (chip->hdu) {
+		    hdu = chip->hdu;
+		}
+		psArray *cells = chip->cells;
+		for (int cellNum = 0; cellNum < cells->n; cellNum++) {
+		    pmCell *cell = cells->data[cellNum];
+		    if (cell->valid) {
+			if (cell->hdu) {
+			    hdu = cell->hdu;
+			}
+			
+			hdu->masks = psArrayAlloc(hdu->images->n);
+			hdu->weights = psArrayAlloc(hdu->images->n);
+			psArray *readouts = cell->readouts;
+			for (int readNum = 0; readNum < hdu->images->n; readNum++) {
+			    psImage *image = hdu->images->data[readNum];
+			    psImage *mask = psImageAlloc(image->numCols, image->numRows, PS_TYPE_U8);
+			    psImage *weight = psImageAlloc(image->numCols, image->numRows, PS_TYPE_F32);
+			    pmReadout *readout = readouts->data[readNum];
+			    for (int j = 0; j < image->numRows; j++) {
+				for (int i = 0; i < image->numCols; i++) {
+				    mask->data.U8[j][i] = j + i;
+				    weight->data.F32[j][i] = sqrtf(image->data.F32[j][i]);
+				}
+			    }
+			    hdu->masks->data[readNum] = mask;
+			    hdu->weights->data[readNum] = weight;
+			    readout->mask = psMemIncrRefCounter(mask);
+			    readout->weight = psMemIncrRefCounter(weight);
+			}
+		    }
+		}
+	    }
+	}
+    }
 #endif
 
@@ -741,5 +790,5 @@
 						       inputReadout->image->numRows,
 						       PS_TYPE_U8);
-		    pmReadout *dummyMask = pmReadoutAlloc(NULL, dummyImage, NULL, 0, 0, 1, 1, 1, 1);
+		    pmReadout *dummyMask = pmReadoutAlloc(NULL, dummyImage, NULL, 0, 0, 1, 1);
 		    (void)pmFlatField(inputReadout, dummyMask, flatReadout);
 		    psFree(dummyMask);
@@ -771,5 +820,7 @@
     // Write the output
     pmFPAWrite(outputFile, input, database);
-//    pmFPAWriteMask(outputMaskFile, input);
+    pmFPAWriteMask(input, outputFile);
+    pmFPAWriteWeight(input, outputFile);
+
 #ifdef PRODUCTION
     psFitsClose(outputFile);
Index: /trunk/archive/scripts/src/phase2/phase2.config
===================================================================
--- /trunk/archive/scripts/src/phase2/phase2.config	(revision 5563)
+++ /trunk/archive/scripts/src/phase2/phase2.config	(revision 5564)
@@ -3,5 +3,5 @@
 # List of tasks to perform
 MASK		BOOL	FALSE		# Mask bad pixels
-NONLIN		BOOL	TRUE		# Non-linearity correction
+NONLIN		BOOL	FALSE		# Non-linearity correction
 BIAS		BOOL	FALSE		# Bias subtraction
 DARK		BOOL	FALSE		# Dark subtraction
Index: /trunk/archive/scripts/src/phase2/pmFPA.c
===================================================================
--- /trunk/archive/scripts/src/phase2/pmFPA.c	(revision 5563)
+++ /trunk/archive/scripts/src/phase2/pmFPA.c	(revision 5564)
@@ -40,8 +40,8 @@
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
-pmPixelData *pmPixelDataAlloc(const char *extname)
-{
-    pmPixelData *pd = psAlloc(sizeof(pmPixelData));
-    psMemSetDeallocator(pd, (psFreeFunc)p_pmPixelDataFree);
+p_pmHDU *p_pmHDUAlloc(const char *extname)
+{
+    p_pmHDU *pd = psAlloc(sizeof(p_pmHDU));
+    psMemSetDeallocator(pd, (psFreeFunc)p_pmHDUFree);
 
     pd->extname = extname;
@@ -54,10 +54,10 @@
 }
 
-void p_pmPixelDataFree(pmPixelData *pd)
-{
-    psFree(pd->header);
-    psFree(pd->images);
-    psFree(pd->masks);
-    psFree(pd->weights);
+void p_pmHDUFree(p_pmHDU *hdu)
+{
+    psFree(hdu->header);
+    psFree(hdu->images);
+    psFree(hdu->masks);
+    psFree(hdu->weights);
 }
 
@@ -77,5 +77,5 @@
     fpa->chips = psArrayAlloc(0);
 
-    fpa->data = NULL;
+    fpa->hdu = NULL;
 
     return fpa;
@@ -92,5 +92,5 @@
     psFree(fpa->chips);
 
-    psFree(fpa->data);
+    psFree(fpa->hdu);
 }
 
@@ -119,5 +119,5 @@
     chip->valid = true;    
 
-    chip->data = NULL;
+    chip->hdu = NULL;
 
     psMetadataAddStr(chip->concepts, PS_LIST_HEAD, "CHIP.NAME", 0, "Chip name added at pmChipAlloc", name);
@@ -134,5 +134,5 @@
     psFree(chip->cells);
 
-    psFree(chip->data);
+    psFree(chip->hdu);
 
     // We don't free the parent member, since that would generate a circular call.  We don't increment the
@@ -156,7 +156,5 @@
 
     cell->toChip = NULL;
-    cell->fromChip = NULL;
     cell->toFPA = NULL;
-    cell->toTP = NULL;
     cell->toSky = NULL;
 
@@ -171,5 +169,5 @@
     cell->valid = true;
 
-    cell->data = NULL;
+    cell->hdu = NULL;
 
     return cell;
@@ -179,7 +177,5 @@
 {
     psFree(cell->toChip);
-    psFree(cell->fromChip);
     psFree(cell->toFPA);
-    psFree(cell->toTP);
     psFree(cell->toSky);
 
@@ -188,5 +184,5 @@
     psFree(cell->readouts);
 
-    psFree(cell->data);
+    psFree(cell->hdu);
 
     // We don't free the parent member, since that would generate a circular call.  We don't increment the
@@ -197,5 +193,5 @@
 			  psImage *image, // The pixels
 			  psImage *mask,// The mask pixels
-			  int col0, int row0, int colParity, int rowParity, int colBin, int rowBin // Data
+			  int col0, int row0, int colBin, int rowBin // Data
     )
 {
@@ -215,6 +211,4 @@
     *(int*)&readout->col0 = col0;
     *(int*)&readout->row0 = row0;
-    *(int*)&readout->colParity = colParity;
-    *(int*)&readout->rowParity = rowParity;
     *(int*)&readout->colBins = colBin;
     *(int*)&readout->rowBins = rowBin;
Index: /trunk/archive/scripts/src/phase2/pmFPA.h
===================================================================
--- /trunk/archive/scripts/src/phase2/pmFPA.h	(revision 5563)
+++ /trunk/archive/scripts/src/phase2/pmFPA.h	(revision 5564)
@@ -15,54 +15,70 @@
     psArray *masks;			// The mask data, if it corresponds to this level
     psArray *weights;			// The weight data, if it corresponds to this level
-} pmPixelData;
+} p_pmHDU;
 
 typedef struct {
     // Astrometric transformations
-    psPlaneDistort* fromTangentPlane;	// Transformation from tangent plane to focal plane
-    psPlaneDistort* toTangentPlane;	// Transformation from focal plane to tangent plane
-    psProjection *projection;		// Projection from tangent plane to sky
+    psPlaneDistort* fromTangentPlane;  // Transformation from tangent plane to focal plane
+    psPlaneDistort* toTangentPlane;    // Transformation from focal plane to tangent plane
+    psProjection *projection;          // Projection from tangent plane to sky
     // Information
-    psMetadata *camera;			// Camera configuration
-    psMetadata *concepts;		// Values for concepts
-    psMetadata *phu;			// Primary Header
-    psArray *chips;			// The chips
-    pmPixelData *data;			// FITS data
+    psMetadata *concepts;              // Cache for PS concepts
+    psMetadata *analysis;              // FPA-level analysis metadata
+    const psMetadata *camera;          // Camera configuration
+    psArray *chips;                    // The chips
+    p_pmHDU *hdu;		       // FITS data
+    psMetadata *phu;		       // Primary Header
 } pmFPA;
 
 typedef struct {
     // Offset specifying position on focal plane
-    const int col0;			// Offset from the left of FPA
-    const int row0;			// Offset from the bottom of FPA
+    int col0;                          // Offset from the left of FPA.
+    int row0;                          // Offset from the bottom of FPA.
     // Astrometric transformations
-    psPlaneTransform* toFPA;		// Transformation from chip to FPA coordinates
-    psPlaneTransform* fromFPA;		// Transformation from FPA to chip coordinates
+    psPlaneTransform* toFPA;           // Transformation from chip to FPA coordinates
+    psPlaneTransform* fromFPA;         // Transformation from FPA to chip coordinates
     // Information
-    psMetadata *concepts;		// Values for concepts
-    psArray *cells;			// The cells (referred to by name)
-    pmFPA *parent;			// Parent FPA
-    bool valid;				// Valid for reading in?
-    pmPixelData *data;			// FITS data
+    psMetadata *concepts;              // Cache for PS concepts
+    psMetadata *analysis;              // Chip-level analysis metadata
+    psArray *cells;                    // The cells (referred to by name)
+    pmFPA *parent;                     // Parent FPA
+    bool valid;                        // Do we bother about reading and working with this chip?
+    p_pmHDU *hdu;			// FITS data
 } pmChip;
 
 typedef struct {
     // Offset specifying position on chip
-    const int col0;			// Offset from the left of chip
-    const int row0;			// Offset from the bottom of chip
+    int col0;                          // Offset from the left of chip.
+    int row0;                          // Offset from the bottom of chip.
     // Astrometric transformations
-    psPlaneTransform* toChip;		// Transformations from cell to chip coordinates
-    psPlaneTransform* fromChip;		// Transformations from cell to chip coordinates
-    psPlaneTransform* toFPA;		// Transformations from cell to FPA coordinates
-    psPlaneTransform* toTP;		// Transformations from cell to FPA coordinates
-    psPlaneTransform* toSky;		// Transformations from cell to tangent plane coordinates
+    psPlaneTransform* toChip;          // Transformations from cell to chip coordinates
+    psPlaneTransform* toFPA;           // Transformations from cell to FPA coordinates
+    psPlaneTransform* toSky;           // Transformations from cell to sky coordinates
     // Information
-    psMetadata *concepts;		// Values for concepts
-    psMetadata *camera;			// Camera information
-    psArray *readouts;			// The readouts (referred to by number)
-    pmChip *parent;			// Parent chip
-    bool valid;				// Valid for operating on?
-    pmPixelData *data;			// FITS data
+    psMetadata *concepts;              // Cache for PS concepts
+    psMetadata *camera;		       // Camera information
+    psMetadata *analysis;              // Cell-level analysis metadata
+    psArray *readouts;                 // The readouts (referred to by number)
+    pmChip *parent;                    // Parent chip
+    bool valid;                        // Do we bother about reading and working with this cell?
+    p_pmHDU *hdu;		       // FITS data
 } pmCell;
 
+typedef struct {
+    // Position on the cell
+    int col0;                          // Offset from the left of cell.
+    int row0;                          // Offset from the bottom of cell.
+    int colBins;                       // Amount of binning in x-dimension and parity (from sign)
+    int rowBins;                       // Amount of binning in y-dimension and parity (from sign)
+    // Information
+    psImage *image;                    // Imaging area of readout
+    psImage *mask;                     // Mask for image
+    psImage *weight;                   // Weight for image
+    psList *bias;                      // List of bias section (sub-)images
+    psMetadata *analysis;              // Readout-level analysis metadata
+    pmCell *parent;                    // Parent cell
+} pmReadout;
 
+#if 0
 typedef struct {
     // Details for position on the cell
@@ -80,5 +96,5 @@
     psImage *weight;			// Weight image
 } pmReadout;
-
+#endif
 
 psList *pmReadoutGetBias(pmReadout *readout // Readout for which to get the bias sections
@@ -87,6 +103,6 @@
 
 // Allocators and deallocators
-pmPixelData *pmPixelDataAlloc(const char *extname);
-void p_pmPixelDataFree(pmPixelData *pd);
+p_pmHDU *p_pmHDUAlloc(const char *extname);
+void p_pmHDUFree(p_pmHDU *hdu);
 pmFPA *pmFPAAlloc(const psMetadata *camera // Camera configuration
     );
@@ -107,5 +123,5 @@
 			  psImage *image, // The pixels
 			  psImage *mask,// The mask pixels
-			  int col0, int row0, int colParity, int rowParity, int colBin, int rowBin // Data
+			  int col0, int row0, int colBin, int rowBin // Data
     );
 void p_pmReadoutFree(pmReadout *readout);
Index: /trunk/archive/scripts/src/phase2/pmFPAConceptsGet.c
===================================================================
--- /trunk/archive/scripts/src/phase2/pmFPAConceptsGet.c	(revision 5563)
+++ /trunk/archive/scripts/src/phase2/pmFPAConceptsGet.c	(revision 5564)
@@ -46,6 +46,6 @@
     if (mdStatus && strlen(keyword) > 0) {
 	// We have a FITS header to look up --- search each level
-	if (cell && cell->data) {
-	    psMetadataItem *cellItem = psMetadataLookup(cell->data->header, keyword);
+	if (cell && cell->hdu) {
+	    psMetadataItem *cellItem = psMetadataLookup(cell->hdu->header, keyword);
 	    if (cellItem) {
 		// XXX: Need to clean up before returning
@@ -54,6 +54,6 @@
 	}
 
-	if (chip && chip->data) {
-	    psMetadataItem *chipItem = psMetadataLookup(chip->data->header, keyword);
+	if (chip && chip->hdu) {
+	    psMetadataItem *chipItem = psMetadataLookup(chip->hdu->header, keyword);
 	    if (chipItem) {
 		// XXX: Need to clean up before returning
@@ -62,6 +62,6 @@
 	}
 
-	if (fpa->data) {
-	    psMetadataItem *fpaItem = psMetadataLookup(fpa->data->header, keyword);
+	if (fpa->hdu) {
+	    psMetadataItem *fpaItem = psMetadataLookup(fpa->hdu->header, keyword);
 	    if (fpaItem) {
 		// XXX: Need to clean up before returning
@@ -371,5 +371,11 @@
 	switch (item->type) {
 	  case PS_DATA_STRING:
-	    value = item->data.V;
+	    value = psMemIncrRefCounter(item->data.V);
+	    break;
+	  case PS_DATA_F32:
+	    psStringAppend(&value, "%f", item->data.F32);
+	    break;
+	  case PS_DATA_S32:
+	    psStringAppend(&value, "%d", item->data.S32);
 	    break;
 	  default:
@@ -384,4 +390,5 @@
 
     psMetadataAdd(concepts, PS_LIST_TAIL, name, PS_DATA_STRING, comment, value);
+    psFree(value);
 }
 
@@ -400,5 +407,6 @@
     }
 
-    // FPA.NAME --- added by pmFPAConstruct
+    // FPA.NAME
+    p_pmFPAConceptGetString(fpa, NULL, NULL, db, fpa->concepts, "FPA.NAME", "Name of FPA");
 
     // FPA.AIRMASS
@@ -450,5 +458,5 @@
 	    
 	    // How to interpret the RA
-	    psMetadata *camera = fpa->camera; // Camera configuration data
+	    const psMetadata *camera = fpa->camera; // Camera configuration data
 	    bool mdok = true;		// Status of MD lookup
 	    psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS");
@@ -519,5 +527,5 @@
 	    
 	    // How to interpret the DEC
-	    psMetadata *camera = fpa->camera; // Camera configuration data
+	    const psMetadata *camera = fpa->camera; // Camera configuration data
 	    bool mdok = true;		// Status of MD lookup
 	    psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS");
@@ -653,10 +661,10 @@
 		} else if (strcasecmp(source, "HEADER") == 0) {
 		    psMetadata *header = NULL; // The FITS header
-		    if (cell->data) {
-			header = cell->data->header;
-		    } else if (chip->data) {
-			header = chip->data->header;
-		    } else if (fpa->data) {
-			    header = fpa->data->header;
+		    if (cell->hdu) {
+			header = cell->hdu->header;
+		    } else if (chip->hdu) {
+			header = chip->hdu->header;
+		    } else if (fpa->hdu) {
+			    header = fpa->hdu->header;
 		    }
 		    if (! header) {
@@ -718,10 +726,10 @@
 		    } else if (strcasecmp(source, "HEADER") == 0) {
 			psMetadata *header = NULL; // The FITS header
-			if (cell->data) {
-			    header = cell->data->header;
-			} else if (chip->data) {
-			    header = chip->data->header;
-			} else if (fpa->data) {
-			    header = fpa->data->header;
+			if (cell->hdu) {
+			    header = cell->hdu->header;
+			} else if (chip->hdu) {
+			    header = chip->hdu->header;
+			} else if (fpa->hdu) {
+			    header = fpa->hdu->header;
 			}
 			if (! header) {
@@ -832,5 +840,5 @@
 	} else {
 	    // Get format
-	    psMetadata *camera = fpa->camera; // The camera configuration data
+	    const psMetadata *camera = fpa->camera; // The camera configuration data
 	    bool mdok = true;		// Status of MD lookup
 	    psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS");
@@ -848,10 +856,10 @@
 				// timeString contains headers for the date and time
 				psMetadata *header = NULL; // The FITS header
-				if (cell->data) {
-				    header = cell->data->header;
-				} else if (chip->data) {
-				    header = chip->data->header;
-				} else if (fpa->data) {
-				    header = fpa->data->header;
+				if (cell->hdu) {
+				    header = cell->hdu->header;
+				} else if (chip->hdu) {
+				    header = chip->hdu->header;
+				} else if (fpa->hdu) {
+				    header = fpa->hdu->header;
 				}
 				if (! header) {
Index: /trunk/archive/scripts/src/phase2/pmFPAConceptsSet.c
===================================================================
--- /trunk/archive/scripts/src/phase2/pmFPAConceptsSet.c	(revision 5563)
+++ /trunk/archive/scripts/src/phase2/pmFPAConceptsSet.c	(revision 5564)
@@ -130,23 +130,23 @@
 
 	// We have a FITS header to look up --- search each level
-	if (cell && cell->data) {
-	    psMetadataItem *cellItem = psMetadataLookup(cell->data->header, keyword);
+	if (cell && cell->hdu) {
+	    psMetadataItem *cellItem = psMetadataLookup(cell->hdu->header, keyword);
 	    if (cellItem) {
 		// XXX: Need to clean up before returning
-		psMetadataAddItem(cell->data->header, headerItem, PS_LIST_TAIL, PS_META_REPLACE);
+		psMetadataAddItem(cell->hdu->header, headerItem, PS_LIST_TAIL, PS_META_REPLACE);
 		status = true;
 	    }
-	} else if (chip && chip->data) {
-	    psMetadataItem *chipItem = psMetadataLookup(chip->data->header, keyword);
+	} else if (chip && chip->hdu) {
+	    psMetadataItem *chipItem = psMetadataLookup(chip->hdu->header, keyword);
 	    if (chipItem) {
 		// XXX: Need to clean up before returning
-		psMetadataAddItem(chip->data->header, headerItem, PS_LIST_TAIL, PS_META_REPLACE);
+		psMetadataAddItem(chip->hdu->header, headerItem, PS_LIST_TAIL, PS_META_REPLACE);
 		status = true;
 	    }
-	} else if (fpa->data) {
-	    psMetadataItem *fpaItem = psMetadataLookup(fpa->data->header, keyword);
+	} else if (fpa->hdu) {
+	    psMetadataItem *fpaItem = psMetadataLookup(fpa->hdu->header, keyword);
 	    if (fpaItem) {
 		// XXX: Need to clean up before returning
-		psMetadataAddItem(fpa->data->header, headerItem, PS_LIST_TAIL, PS_META_REPLACE);
+		psMetadataAddItem(fpa->hdu->header, headerItem, PS_LIST_TAIL, PS_META_REPLACE);
 		status = true;
 	    }
@@ -376,5 +376,6 @@
     )
 {
-    // FPA.NAME --- don't need to worry about this
+    // FPA.NAME
+    setConcept(fpa, NULL, NULL, db, fpa->concepts, "FPA.NAME");
 
     // FPA.AIRMASS
@@ -397,5 +398,5 @@
 
 	// How to interpret the RA
-	psMetadata *camera = fpa->camera; // Camera configuration data
+	const psMetadata *camera = fpa->camera; // Camera configuration data
 	bool mdok = true;		// Status of MD lookup
 	psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS");
@@ -444,5 +445,5 @@
 
 	// How to interpret the DEC
-	psMetadata *camera = fpa->camera; // Camera configuration data
+	const psMetadata *camera = fpa->camera; // Camera configuration data
 	bool mdok = true;		// Status of MD lookup
 	psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS");
@@ -571,10 +572,10 @@
 		psString keyword = psMetadataLookupString(NULL, cell->camera, "CELL.TRIMSEC");
 		psMetadata *header = NULL; // The FITS header
-		if (cell->data) {
-		    header = cell->data->header;
-		} else if (chip->data) {
-		    header = chip->data->header;
-		} else if (fpa->data) {
-		    header = fpa->data->header;
+		if (cell->hdu) {
+		    header = cell->hdu->header;
+		} else if (chip->hdu) {
+		    header = chip->hdu->header;
+		} else if (fpa->hdu) {
+		    header = fpa->hdu->header;
 		}
 		if (! header) {
@@ -651,10 +652,10 @@
 		} else {
 		    psMetadata *header = NULL; // The FITS header
-		    if (cell->data) {
-			header = cell->data->header;
-		    } else if (chip->data) {
-			header = chip->data->header;
-		    } else if (fpa->data) {
-			header = fpa->data->header;
+		    if (cell->hdu) {
+			header = cell->hdu->header;
+		    } else if (chip->hdu) {
+			header = chip->hdu->header;
+		    } else if (fpa->hdu) {
+			header = fpa->hdu->header;
 		    }
 		    if (! header) {
@@ -689,5 +690,5 @@
 
 	// If these are specified by the header, we need to check to see if it's the same header keyword
-	psMetadata *camera = fpa->camera;
+	const psMetadata *camera = fpa->camera;
 	psMetadata *translation = psMetadataLookupMD(NULL, camera, "TRANSLATION");
 	psString xKeyword = psMetadataLookupString(NULL, translation, "CELL.XBIN");
@@ -707,10 +708,10 @@
 							     binString);
 	    psMetadata *header = NULL; // The FITS header
-	    if (cell->data) {
-		header = cell->data->header;
-	    } else if (chip->data) {
-		header = chip->data->header;
-	    } else if (fpa->data) {
-		header = fpa->data->header;
+	    if (cell->hdu) {
+		header = cell->hdu->header;
+	    } else if (chip->hdu) {
+		header = chip->hdu->header;
+	    } else if (fpa->hdu) {
+		header = fpa->hdu->header;
 	    }
 	    if (! header) {
@@ -784,10 +785,10 @@
 		// We're working with two separate headers
 		psMetadata *header = NULL; // The FITS header
-		if (cell->data) {
-		    header = cell->data->header;
-		} else if (chip->data) {
-		    header = chip->data->header;
-		} else if (fpa->data) {
-		    header = fpa->data->header;
+		if (cell->hdu) {
+		    header = cell->hdu->header;
+		} else if (chip->hdu) {
+		    header = chip->hdu->header;
+		} else if (fpa->hdu) {
+		    header = fpa->hdu->header;
 		}
 		if (! header) {
@@ -795,5 +796,5 @@
 		} else {
 		    // Get the headers
-		    psMetadata *camera = fpa->camera;
+		    const psMetadata *camera = fpa->camera;
 		    psMetadata *translation = psMetadataLookupMD(NULL, camera, "TRANSLATION");
 		    psString keywords = psMetadataLookupString(NULL, translation, "CELL.TIME");
Index: /trunk/archive/scripts/src/phase2/pmFPAConstruct.c
===================================================================
--- /trunk/archive/scripts/src/phase2/pmFPAConstruct.c	(revision 5563)
+++ /trunk/archive/scripts/src/phase2/pmFPAConstruct.c	(revision 5564)
@@ -88,5 +88,5 @@
 		psString extName = contentItem->name; // The name of the extension
 		pmChip *chip = pmChipAlloc(fpa, extName); // The chip
-		chip->data = pmPixelDataAlloc(extName); // Prepare chip to receive FITS data
+		chip->hdu = p_pmHDUAlloc(extName); // Prepare chip to receive FITS data
 		if (contentItem->type != PS_DATA_STRING) {
 		    psLogMsg(__func__, PS_LOG_WARN, "Type of content item (%x) is not string: ignored\n",
@@ -146,5 +146,5 @@
 		    pmCell *cell = pmCellAlloc(chip, cellData, extName); // The cell
 //		    psFree(cellData);
-		    cell->data = pmPixelDataAlloc(extName); // Prepare cell to receive FITS data
+		    cell->hdu = p_pmHDUAlloc(extName); // Prepare cell to receive FITS data
 
 		    psFree(chip);
@@ -157,5 +157,5 @@
 	} else if (strcasecmp(extType, "NONE") == 0) {
 	    // No extensions; Content contains metadata, each entry is a chip with its component cells
-	    fpa->data = pmPixelDataAlloc("PHU"); // Prepare FPA to receive FITS data
+	    fpa->hdu = p_pmHDUAlloc("PHU"); // Prepare FPA to receive FITS data
 	    while (contentItem = psMetadataGetAndIncrement(contentsIter)) {
 		psString chipName = contentItem->name; // The name of the chip
@@ -196,5 +196,5 @@
 	if (strcasecmp(extType, "NONE") == 0) {
 	    // There are no extensions --- only the PHU
-	    chip->data = pmPixelDataAlloc("PHU");
+	    chip->hdu = p_pmHDUAlloc("PHU");
 
 	    const char *contents = psMetadataLookupString(&mdStatus, camera, "CONTENTS");
@@ -247,5 +247,5 @@
 		pmCell *cell = pmCellAlloc(chip, cellData, extName); // The cell
 //		psFree(cellData);
-		cell->data = pmPixelDataAlloc(extName); // Prepare cell to receive FITS data
+		cell->hdu = p_pmHDUAlloc(extName); // Prepare cell to receive FITS data
 	    } // Iterating through contents
 	    psFree(contentsIter);
@@ -273,8 +273,8 @@
 {
     psTrace(__func__, 0, "FPA:\n");
-    if (fpa->data) {
-	psTrace(__func__, 1, "---> FPA is extension %s.\n", fpa->data->extname);
-	if (! fpa->data->images) {
-	    psTrace(__func__, 1, "---> NO PIXELS for extension %s\n", fpa->data->extname);
+    if (fpa->hdu) {
+	psTrace(__func__, 1, "---> FPA is extension %s.\n", fpa->hdu->extname);
+	if (! fpa->hdu->images) {
+	    psTrace(__func__, 1, "---> NO PIXELS for extension %s\n", fpa->hdu->extname);
 	}
     }
@@ -285,8 +285,8 @@
 	psTrace(__func__, 1, "Chip: %d\n", i);
 	pmChip *chip = chips->data[i]; // The chip
-	if (chip->data) {
-	    psTrace(__func__, 2, "---> Chip is extension %s.\n", chip->data->extname);
-	    if (! chip->data->images) {
-		psTrace(__func__, 2, "---> NO PIXELS for extension %s\n", chip->data->extname);
+	if (chip->hdu) {
+	    psTrace(__func__, 2, "---> Chip is extension %s.\n", chip->hdu->extname);
+	    if (! chip->hdu->images) {
+		psTrace(__func__, 2, "---> NO PIXELS for extension %s\n", chip->hdu->extname);
 	    }
 	}
@@ -296,8 +296,8 @@
 	    psTrace(__func__, 2, "Cell: %d\n", j);
 	    pmCell *cell = cells->data[j]; // The cell
-	    if (cell->data) {
-		psTrace(__func__, 3, "---> Cell is extension %s.\n", cell->data->extname);
-		if (! cell->data->images) {
-		    psTrace(__func__, 3, "---> NO PIXELS for extension %s\n", cell->data->extname);
+	    if (cell->hdu) {
+		psTrace(__func__, 3, "---> Cell is extension %s.\n", cell->hdu->extname);
+		if (! cell->hdu->images) {
+		    psTrace(__func__, 3, "---> NO PIXELS for extension %s\n", cell->hdu->extname);
 		}
 	    }
Index: /trunk/archive/scripts/src/phase2/pmFPARead.c
===================================================================
--- /trunk/archive/scripts/src/phase2/pmFPARead.c	(revision 5563)
+++ /trunk/archive/scripts/src/phase2/pmFPARead.c	(revision 5564)
@@ -17,9 +17,9 @@
 
 // Read a FITS extension into a chip
-static bool readExtension(pmPixelData *pixelData, // Pixel data into which to read
+static bool readExtension(p_pmHDU *hdu, // Pixel data into which to read
 			  psFits *fits	// The FITS file from which to read
     )
 {
-    const char *extName = pixelData->extname; // Extension name
+    const char *extName = hdu->extname; // Extension name
 
     psTrace(__func__, 7, "Moving to extension %s...\n", extName);
@@ -95,6 +95,6 @@
 
     // Update the HDU with the new data
-    pixelData->images = pixels;
-    pixelData->header = header;
+    hdu->images = pixels;
+    hdu->header = header;
 
     // XXX: Insert mask and weight reading here
@@ -105,9 +105,9 @@
 // Portion out an image into the cell
 static bool generateReadouts(pmCell *cell, // The cell that gets its bits
-			     pmPixelData *data // Pixel data, containing image, mask, weights
+			     p_pmHDU *hdu // Pixel data, containing image, mask, weights
     )
 {
-    psArray *images = data->images;	// Array of images (each of which is a readout)
-    psArray *masks = data->masks;	// Array of masks (one for each readout)
+    psArray *images = hdu->images;	// Array of images (each of which is a readout)
+    psArray *masks = hdu->masks;	// Array of masks (one for each readout)
     // Iterate over each of the image planes
     for (int i = 0; i < images->n; i++) {
@@ -118,5 +118,5 @@
 	}
 
-	pmReadout *readout = pmReadoutAlloc(cell, image, mask, 0,0,0,0,1,1);
+	pmReadout *readout = pmReadoutAlloc(cell, image, mask, 0,0,1,1);
 	psFree(readout);		// This seems silly, but the alloc registers it with the cell, so we
 					// don't have to do anything with it.  Perhaps we should change
@@ -137,5 +137,5 @@
     )
 {
-    pmPixelData *pixelData = NULL;	// Pixel data from FITS file
+    p_pmHDU *hdu = NULL;	// Pixel data from FITS file
 
     // Read the PHU, if required
@@ -152,9 +152,9 @@
     // Read in....
     psTrace(__func__, 1, "Working on FPA...\n");
-    if (fpa->data) {
-	pixelData= fpa->data;
-	psTrace(__func__, 3, "Reading pixels from extension %s into FPA.\n", pixelData->extname);
-	if (! readExtension(pixelData, fits)) {
-	    psError(PS_ERR_IO, false, "Unable to read pixels from extension %s\n", pixelData->extname);
+    if (fpa->hdu) {
+	hdu = fpa->hdu;
+	psTrace(__func__, 3, "Reading pixels from extension %s into FPA.\n", hdu->extname);
+	if (! readExtension(hdu, fits)) {
+	    psError(PS_ERR_IO, false, "Unable to read pixels from extension %s\n", hdu->extname);
 	    return false;
 	}
@@ -174,9 +174,9 @@
 	psTrace(__func__, 2, "Reading in chip %d...\n", i);
 
-	if (chip->data) {
-	    pixelData = chip->data;
-	    psTrace(__func__, 3, "Reading pixels from extension %s into chip %d.\n", pixelData->extname, i);
-	    if (! readExtension(pixelData, fits)) {
-		psError(PS_ERR_IO, false, "Unable to read pixels from extension %s\n", pixelData->extname);
+	if (chip->hdu) {
+	    hdu = chip->hdu;
+	    psTrace(__func__, 3, "Reading pixels from extension %s into chip %d.\n", hdu->extname, i);
+	    if (! readExtension(hdu, fits)) {
+		psError(PS_ERR_IO, false, "Unable to read pixels from extension %s\n", hdu->extname);
 		return false;
 	    }
@@ -196,11 +196,11 @@
 	    psTrace(__func__, 3, "Reading in cell %d...\n", j);
 
-	    if (cell->data) {
-		pixelData = cell->data;
-		psTrace(__func__, 5, "Reading pixels from extension %s into cell %d.\n", pixelData->extname,
+	    if (cell->hdu) {
+		hdu = cell->hdu;
+		psTrace(__func__, 5, "Reading pixels from extension %s into cell %d.\n", hdu->extname,
 			j);
-		if (! readExtension(pixelData, fits)) {
+		if (! readExtension(hdu, fits)) {
 		    psError(PS_ERR_IO, false, "Unable to read pixels from extension %s\n",
-			    pixelData->extname);
+			    hdu->extname);
 		    return false;
 		}
@@ -210,5 +210,5 @@
 	    psTrace(__func__, 5, "Allocating readouts for chip %d cell %d...\n",
 		    i, j);
-	    generateReadouts(cell, pixelData);
+	    generateReadouts(cell, hdu);
 	}
     }
@@ -216,2 +216,416 @@
     return true;
 }
+
+// Translate a name from the configuration file, containing something like "%a_%d" to a real value
+psString p_pmFPATranslateName(psString name, // The name to translate
+			      pmCell *cell // The cell for which to translate
+    )
+{
+    // %a is the FPA.NAME
+    // %b is the CHIP.NAME
+    // %c is the CELL.NAME
+    // %d is the chip number
+    // %e if the cell number
+    // %f is the extension name
+
+    psString translation = psMemIncrRefCounter(name); // The translated string
+    char *temp = NULL;			// Temporary string
+
+    pmChip *chip = cell->parent;	// Chip of interest
+    pmFPA *fpa = chip->parent;		// FPA of interest
+
+    // FPA.NAME
+    if (temp = strstr(translation, "%a")) {
+	// This is not particularly friendly to the memory allocation, but the cache should make it OK,
+	// and there's not much of it anyway...
+	psFree(translation);
+	translation = psStringNCopy(name, strlen(name) - strlen(temp));	// Copy first part of string
+	psString fpaName = psMetadataLookupString(NULL, fpa->concepts, "FPA.NAME"); // The value of FPA.NAME
+	psStringAppend(&translation, "%s%s", fpaName, temp + 2);
+	// So "translation" now contains the first part, the replaced string, and the last part.
+    }
+
+    // CHIP.NAME
+    if (temp = strstr(translation, "%b")) {
+	// This is not particularly friendly to the memory allocation, but the cache should make it OK,
+	// and there's not much of it anyway...
+	psFree(translation);
+	translation = psStringNCopy(name, strlen(name) - strlen(temp));	// Copy first part of string
+	psString chipName = psMetadataLookupString(NULL, chip->concepts, "CHIP.NAME"); // CHIP.NAME's value
+	psStringAppend(&translation, "%s%s", chipName, temp + 2);
+	// So "translation" now contains the first part, the replaced string, and the last part.
+    }
+
+    // CELL.NAME
+    if (temp = strstr(translation, "%c")) {
+	// This is not particularly friendly to the memory allocation, but the cache should make it OK,
+	// and there's not much of it anyway...
+	psFree(translation);
+	translation = psStringNCopy(name, strlen(name) - strlen(temp));	// Copy first part of string
+	psString cellName = psMetadataLookupString(NULL, cell->concepts, "CELL.NAME"); // CELL.NAME's value
+	psStringAppend(&translation, "%s%s", cellName, temp + 2);
+	// So "translation" now contains the first part, the replaced string, and the last part.
+    }
+
+    // Chip number
+    if (temp = strstr(translation, "%d")) {
+	// This is not particularly friendly to the memory allocation, but the cache should make it OK,
+	// and there's not much of it anyway...
+	psFree(translation);
+	translation = psStringNCopy(name, strlen(name) - strlen(temp));	// Copy first part of string
+	// Search for the pointer to get the chip number
+	int chipNum = -1;
+	psArray *chips = fpa->chips;	// The array of chips
+	for (int i = 0; i < chips->n && chipNum < 0; i++) {
+	    if (chips->data[i] == chip) {
+		chipNum = i;
+	    }
+	}
+	if (chipNum < 0) {
+	    psError(PS_ERR_IO, true, "Unable to find chip to get name: %s\n", name);
+	    // Try to muddle on by leaving the number out
+	    psStringAppend(&translation, "%s", temp + 2);
+	} else {
+	    psStringAppend(&translation, "%d%s", chipNum, temp + 2);
+	}
+	// So "translation" now contains the first part, the replaced string, and the last part.
+    }
+
+    // Cell number
+    if (temp = strstr(translation, "%e")) {
+	// This is not particularly friendly to the memory allocation, but the cache should make it OK,
+	// and there's not much of it anyway...
+	psFree(translation);
+	translation = psStringNCopy(name, strlen(name) - strlen(temp));	// Copy first part of string
+	// Search for the pointer to get the cell number
+	int cellNum = -1;
+	psArray *cells = chip->cells;	// The array of cells
+	for (int i = 0; i < cells->n && cellNum < 0; i++) {
+	    if (cells->data[i] == cell) {
+		cellNum = i;
+	    }
+	}
+	if (cellNum < 0) {
+	    psError(PS_ERR_IO, true, "Unable to find cell to get name: %s\n", name);
+	    // Try to muddle on by leaving the number out
+	    psStringAppend(&translation, "%s", temp + 2);
+	} else {
+	    psStringAppend(&translation, "%d%s", cellNum, temp + 2);
+	}
+	// So "translation" now contains the first part, the replaced string, and the last part.
+    }
+
+    // Extension name
+    if (temp = strstr(translation, "%f")) {
+	// This is not particularly friendly to the memory allocation, but the cache should make it OK,
+	// and there's not much of it anyway...
+	psFree(translation);
+	translation = psStringNCopy(name, strlen(name) - strlen(temp));	// Copy first part of string
+	const char *extname = NULL;
+	if (cell->hdu) {
+	    extname = cell->hdu->extname;
+	} else if (chip->hdu) {
+	    extname = chip->hdu->extname;
+	} else if (fpa->hdu) {
+	    extname = fpa->hdu->extname;
+	}
+	psStringAppend(&translation, "%s%s", extname, temp + 2);
+	// So "translation" now contains the first part, the replaced string, and the last part.
+    }
+
+    return translation;
+}
+
+// Read a mask into the FPA
+bool pmFPAReadMask(pmFPA *fpa,		// FPA to read into
+		   psFits *source	// Source FITS file (for the original data)
+    )
+{
+    const psMetadata *camera = fpa->camera; // Camera configuration for FPA
+    bool mdok = false;			// Status of MD lookup
+
+    // Get the required information from the camera configuration
+    psMetadata *supps = psMetadataLookupMD(&mdok, camera, "SUPPLEMENTARY"); // Rules for supplementary data
+    if (! mdok || ! supps) {
+	psError(PS_ERR_IO, false, "Unable to find SUPPLEMENTARY in camera configuration!\n");
+	return false;
+    }
+    psString sourceType = psMetadataLookupString(&mdok, supps, "MASK.SOURCE"); // Type of source: EXT | FILE
+    if (! mdok || strlen(sourceType) <= 0) {
+	psError(PS_ERR_IO, false, "Unable to find MASK.SOURCE in SUPPLEMENTARY section of camera "
+		"configuration!\n");
+	return false;
+    }
+    psString name = psMetadataLookupString(&mdok, supps, "MASK.NAME"); // Name of mask
+    if (! mdok || strlen(sourceType) <= 0) {
+	psError(PS_ERR_IO, false, "Unable to find MASK.NAME in SUPPLEMENTARY section of camera "
+		"configuration!\n");
+	return false;
+    }
+
+    // Go through the FPA to each cell/readout to get the mask
+    p_pmHDU *hdu = fpa->hdu;		// The HDU into which we will read the mask
+    psArray *chips = fpa->chips;	// Array of chips
+    for (int chipNum = 0; chipNum < chips->n; chipNum++) {
+	pmChip *chip = chips->data[chipNum]; // The current chip of interest
+	if (chip->valid) {
+	    if (chip->hdu) {
+		hdu = chip->hdu;
+	    }
+	    psArray *cells = chip->cells;	// Array of cells
+	    for (int cellNum = 0; cellNum < cells->n; cellNum++) {
+		pmCell *cell = cells->data[cellNum]; // The current cell of interest
+		if (cell->valid) {
+		    if (cell->hdu) {
+			hdu = cell->hdu;
+		    }
+
+		    // Now, need to find out where to get the pixels
+		    psFits *maskSource = source;	// Source of mask image
+		    if (strcasecmp(sourceType, "FILE") == 0) {
+			// Source is a file (with optional extension, e.g., "myMaskFile.fits:thisExt"
+			psString filenameExt = p_pmFPATranslateName(name, cell);
+			char *colon = strchr(filenameExt, ':');	// Pointer to a colon in the filename-extn
+			psString filename = NULL; // The filename
+			psString extname = NULL;// The extenstion name
+			if (colon) {
+			    filename = psStringNCopy(filenameExt, strlen(filenameExt) - strlen(colon));
+			    if (strlen(colon) > 1) {
+				extname = psStringCopy(colon + 1);
+			    }
+			} else {
+			    filename = psMemIncrRefCounter(filenameExt);
+			}
+			
+			maskSource = psFitsAlloc(filename);
+			if (extname) {
+			    if (! psFitsMoveExtName(maskSource, extname)) {
+				psLogMsg(__func__, PS_LOG_WARN, "Unable to find extension %s to read mask.\n",
+					 extname);
+				return false;
+			    }
+			}
+			psFree(filename);
+			psFree(extname);
+			psFree(filenameExt);
+		    } else if (strncasecmp(sourceType, "EXT", 3) == 0) {
+			// Source is an extension in the original file
+			psString extname = p_pmFPATranslateName(name, cell);
+			psFitsMoveExtName(maskSource, extname);
+		    }
+		    
+		    // We've arrived where the pixels are.  Now we need to read them in.
+		    psMetadata *header = psFitsReadHeader(NULL, maskSource); // The header
+		    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 for "
+				 "mask (%s)!\n", name);
+		    }
+		    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");
+			    // Try to proceed by taking only the first plane
+			    numPlanes = 1;
+			}
+			if (numPlanes != 1 && numPlanes != cell->readouts->n) {
+			    psError(PS_ERR_IO, false, "Number of masks (%d) does not match number of "
+				    "readouts (%d)\n", numPlanes, cell->readouts->n);
+			    // Try to proceed by taking only the first plane
+			    numPlanes = 1;
+			}
+		    }
+
+		    hdu->masks = psArrayAlloc(hdu->images->n);
+		    
+		    // Read each plane into the array
+		    psArray *readouts = cell->readouts;	// The array of readouts
+		    for (int i = 0; i < hdu->masks->n; i++) {
+			psImage *mask = NULL; // The mask to be added
+			if (i < numPlanes) {
+			    // Read the mask from the file
+			    psTrace(__func__, 9, "Reading plane %d\n", i);
+			    psRegion region = {0, 0, 0, 0};
+			    mask = psFitsReadImage(NULL, maskSource, region, i);
+			} else {
+			    // One mask in the file is provided for all planes in the original image
+			    psTrace(__func__, 9, "Copying plane 0 into plane %d\n", i);
+			    psImage *original = hdu->masks->data[0];
+			    mask = psImageCopy(NULL, original, original->type.type);
+			}
+			hdu->masks->data[0] = mask;
+			pmReadout *readout = readouts->data[i];
+			readout->mask = mask;
+			// Check the dimensions
+			// XXX: Perhaps this check should be against the CELL.TRIMSEC, not the image size?
+			if (mask->numCols < readout->image->numCols ||
+			    mask->numRows < readout->image->numRows)
+			{
+			    psError(PS_ERR_IO, false, "Mask size (%dx%d) not compatible with image (%dx%d)\n",
+				    mask->numCols, mask->numRows, readout->image->numCols,
+				    readout->image->numRows);
+			    return false;
+			}
+		    } // Iterating over readouts
+		} // Valid cells
+	    } // Iterating over cells
+	} // Valid chips
+    } // Iterating over chips
+
+    return true;
+}
+
+
+// Read a mask into the FPA
+// This is just a copy of the above pmFPAReadMask, replacing "mask" with "weight" throughout.
+bool pmFPAReadWeight(pmFPA *fpa,	// FPA to read into
+		     psFits *source	// Source FITS file (for the original data)
+    )
+{
+    const psMetadata *camera = fpa->camera; // Camera configuration for FPA
+    bool mdok = false;			// Status of MD lookup
+
+    // Get the required information from the camera configuration
+    psMetadata *supps = psMetadataLookupMD(&mdok, camera, "SUPPLEMENTARY"); // Rules for supplementary data
+    if (! mdok || ! supps) {
+	psError(PS_ERR_IO, false, "Unable to find SUPPLEMENTARY in camera configuration!\n");
+	return false;
+    }
+    psString sourceType = psMetadataLookupString(&mdok, supps, "WEIGHT.SOURCE"); // Type of source: EXT | FILE
+    if (! mdok || strlen(sourceType) <= 0) {
+	psError(PS_ERR_IO, false, "Unable to find WEIGHT.SOURCE in SUPPLEMENTARY section of camera "
+		"configuration!\n");
+	return false;
+    }
+    psString name = psMetadataLookupString(&mdok, supps, "WEIGHT.NAME"); // Name of weight
+    if (! mdok || strlen(sourceType) <= 0) {
+	psError(PS_ERR_IO, false, "Unable to find WEIGHT.NAME in SUPPLEMENTARY section of camera "
+		"configuration!\n");
+	return false;
+    }
+
+    // Go through the FPA to each cell/readout to get the weight
+    p_pmHDU *hdu = fpa->hdu;		// The HDU into which we will read the weight
+    psArray *chips = fpa->chips;	// Array of chips
+    for (int chipNum = 0; chipNum < chips->n; chipNum++) {
+	pmChip *chip = chips->data[chipNum]; // The current chip of interest
+	if (chip->valid) {
+	    if (chip->hdu) {
+		hdu = chip->hdu;
+	    }
+	    psArray *cells = chip->cells;	// Array of cells
+	    for (int cellNum = 0; cellNum < cells->n; cellNum++) {
+		pmCell *cell = cells->data[cellNum]; // The current cell of interest
+		if (cell->valid) {
+		    if (cell->hdu) {
+			hdu = cell->hdu;
+		    }
+
+		    // Now, need to find out where to get the pixels
+		    psFits *weightSource = source;	// Source of weight image
+		    if (strcasecmp(sourceType, "FILE") == 0) {
+			// Source is a file (with optional extension, e.g., "myWeightFile.fits:thisExt"
+			psString filenameExt = p_pmFPATranslateName(name, cell);
+			char *colon = strchr(filenameExt, ':');	// Pointer to a colon in the filename-extn
+			psString filename = NULL; // The filename
+			psString extname = NULL;// The extenstion name
+			if (colon) {
+			    filename = psStringNCopy(filenameExt, strlen(filenameExt) - strlen(colon));
+			    if (strlen(colon) > 1) {
+				extname = psStringCopy(colon + 1);
+			    }
+			} else {
+			    filename = psMemIncrRefCounter(filenameExt);
+			}
+			
+			weightSource = psFitsAlloc(filename);
+			if (extname) {
+			    if (! psFitsMoveExtName(weightSource, extname)) {
+				psLogMsg(__func__, PS_LOG_WARN, "Unable to find extension %s to read "
+					 "weight.\n", extname);
+				return false;
+			    }
+			}
+			psFree(filename);
+			psFree(extname);
+			psFree(filenameExt);
+		    } else if (strncasecmp(sourceType, "EXT", 3) == 0) {
+			// Source is an extension in the original file
+			psString extname = p_pmFPATranslateName(name, cell);
+			psFitsMoveExtName(weightSource, extname);
+		    }
+		    
+		    // We've arrived where the pixels are.  Now we need to read them in.
+		    psMetadata *header = psFitsReadHeader(NULL, weightSource); // The header
+		    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 for "
+				 "weight (%s)!\n", name);
+		    }
+		    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");
+			    // Try to proceed by taking only the first plane
+			    numPlanes = 1;
+			}
+			if (numPlanes != 1 && numPlanes != cell->readouts->n) {
+			    psError(PS_ERR_IO, false, "Number of weights (%d) does not match number of "
+				    "readouts (%d)\n", numPlanes, cell->readouts->n);
+			    // Try to proceed by taking only the first plane
+			    numPlanes = 1;
+			}
+		    }
+
+		    hdu->weights = psArrayAlloc(hdu->images->n);
+		    
+		    // Read each plane into the array
+		    psArray *readouts = cell->readouts;	// The array of readouts
+		    for (int i = 0; i < hdu->weights->n; i++) {
+			psImage *weight = NULL; // The weight to be added
+			if (i < numPlanes) {
+			    // Read the weight from the file
+			    psTrace(__func__, 9, "Reading plane %d\n", i);
+			    psRegion region = {0, 0, 0, 0};
+			    weight = psFitsReadImage(NULL, weightSource, region, i);
+			} else {
+			    // One weight in the file is provided for all planes in the original image
+			    psTrace(__func__, 9, "Copying plane 0 into plane %d\n", i);
+			    psImage *original = hdu->weights->data[0];
+			    weight = psImageCopy(NULL, original, original->type.type);
+			}
+			hdu->weights->data[0] = weight;
+			pmReadout *readout = readouts->data[i];
+			readout->weight = weight;
+			// Check the dimensions
+			// XXX: Perhaps this check should be against the CELL.TRIMSEC, not the image size?
+			if (weight->numCols < readout->image->numCols ||
+			    weight->numRows < readout->image->numRows)
+			{
+			    psError(PS_ERR_IO, false, "Weight size (%dx%d) not compatible with image "
+				    "(%dx%d)\n", weight->numCols, weight->numRows, readout->image->numCols,
+				    readout->image->numRows);
+			    return false;
+			}
+		    } // Iterating over readouts
+		} // Valid cells
+	    } // Iterating over cells
+	} // Valid chips
+    } // Iterating over chips
+
+    return true;
+}
Index: /trunk/archive/scripts/src/phase2/pmFPARead.h
===================================================================
--- /trunk/archive/scripts/src/phase2/pmFPARead.h	(revision 5563)
+++ /trunk/archive/scripts/src/phase2/pmFPARead.h	(revision 5564)
@@ -10,3 +10,16 @@
     );
 
+psString p_pmFPATranslateName(psString name, // The name to translate
+			      pmCell *cell // The cell for which to translate
+    );
+
+bool pmFPAReadMask(pmFPA *fpa,		// FPA to read into
+		     psFits *source	// Source FITS file (for the original data)
+    );
+
+bool pmFPAReadWeight(pmFPA *fpa,	// FPA to read into
+		     psFits *source	// Source FITS file (for the original data)
+    );
+
+
 #endif
Index: /trunk/archive/scripts/src/phase2/pmFPAWrite.c
===================================================================
--- /trunk/archive/scripts/src/phase2/pmFPAWrite.c	(revision 5563)
+++ /trunk/archive/scripts/src/phase2/pmFPAWrite.c	(revision 5564)
@@ -5,12 +5,13 @@
 #include "pmFPA.h"
 #include "pmFPAConceptsSet.h"
+#include "pmFPARead.h"
 
 static bool writeHDU(psFits *fits,	// FITS file to which to write
-		     pmPixelData *pixelData // Pixel data to write
+		     p_pmHDU *hdu	// Pixel data to write
     )
 {
     bool status = true;			// Status of write, to return
-    for (int i = 0; i < pixelData->images->n; i++) {
-	status &= psFitsWriteImage(fits, pixelData->header, pixelData->images->data[i], i);
+    for (int i = 0; i < hdu->images->n; i++) {
+	status &= psFitsWriteImage(fits, hdu->header, hdu->images->data[i], i);
 	// XXX: Insert here the writing on mask and weight images
     }
@@ -47,12 +48,12 @@
 		    pmCellOutgestConcepts(cell, db);
 
-		    if (cell->data && strlen(cell->data->extname) > 0) {
-			status &= writeHDU(fits, cell->data);
+		    if (cell->hdu && strlen(cell->hdu->extname) > 0) {
+			status &= writeHDU(fits, cell->hdu);
 		    }
 		}
 	    }
 	    
-	    if (chip->data && strlen(chip->data->extname) > 0) {
-		status &= writeHDU(fits, chip->data);
+	    if (chip->hdu && strlen(chip->hdu->extname) > 0) {
+		status &= writeHDU(fits, chip->hdu);
 	    }
 	}
@@ -60,8 +61,213 @@
     }
 
-    if (fpa->data && strlen(fpa->data->extname) > 0) {
-	status &= writeHDU(fits, fpa->data);
+    if (fpa->hdu && strlen(fpa->hdu->extname) > 0) {
+	status &= writeHDU(fits, fpa->hdu);
     }
 
     return status;
 }
+
+
+bool pmFPAWriteMask(pmFPA *fpa, 	// FPA containing mask to write
+		    psFits *fits	// FITS file for image
+    )
+{
+    const psMetadata *camera = fpa->camera; // Camera configuration for FPA
+    bool mdok = false;			// Status of MD lookup
+
+    // Get the required information from the camera configuration
+    psMetadata *supps = psMetadataLookupMD(&mdok, camera, "SUPPLEMENTARY"); // Rules for supplementary data
+    if (! mdok || ! supps) {
+	psError(PS_ERR_IO, false, "Unable to find SUPPLEMENTARY in camera configuration!\n");
+	return false;
+    }
+    psString sourceType = psMetadataLookupString(&mdok, supps, "MASK.SOURCE"); // Type of source: EXT | FILE
+    if (! mdok || strlen(sourceType) <= 0) {
+	psError(PS_ERR_IO, false, "Unable to find MASK.SOURCE in SUPPLEMENTARY section of camera "
+		"configuration!\n");
+	return false;
+    }
+    psString name = psMetadataLookupString(&mdok, supps, "MASK.NAME"); // Name of mask
+    if (! mdok || strlen(sourceType) <= 0) {
+	psError(PS_ERR_IO, false, "Unable to find MASK.NAME in SUPPLEMENTARY section of camera "
+		"configuration!\n");
+	return false;
+    }
+
+    // Go through the FPA to each cell/readout to get the mask
+    p_pmHDU *hdu = fpa->hdu;		// The HDU into which we will read the mask
+    psArray *chips = fpa->chips;	// Array of chips
+    for (int chipNum = 0; chipNum < chips->n; chipNum++) {
+	pmChip *chip = chips->data[chipNum]; // The current chip of interest
+	if (chip->valid) {
+	    if (chip->hdu) {
+		hdu = chip->hdu;
+	    }
+	    psArray *cells = chip->cells;	// Array of cells
+	    for (int cellNum = 0; cellNum < cells->n; cellNum++) {
+		pmCell *cell = cells->data[cellNum]; // The current cell of interest
+		if (cell->valid) {
+		    if (cell->hdu) {
+			hdu = cell->hdu;
+		    }
+
+		    // Now, need to find out where to write the pixels
+		    psFits *maskDest = psMemIncrRefCounter(fits); // Destination of mask image
+		    psMetadata *header = psMetadataAlloc(); // A dummy header, containing the extension name
+		    if (strcasecmp(sourceType, "FILE") == 0) {
+			// Source is a file (with optional extension, e.g., "myMaskFile.fits:thisExt"
+			psString filenameExt = p_pmFPATranslateName(name, cell);
+			char *colon = strchr(filenameExt, ':');	// Pointer to a colon in the filename-extn
+			psString filename = NULL; // The filename
+			psString extname = NULL;// The extenstion name
+			if (colon) {
+			    filename = psStringNCopy(filenameExt, strlen(filenameExt) - strlen(colon));
+			    if (strlen(colon) > 1) {
+				extname = psStringCopy(colon + 1);
+			    }
+			} else {
+			    filename = psMemIncrRefCounter(filenameExt);
+			}
+
+			psFree(maskDest);
+			maskDest = psFitsAlloc(filename);
+			if (extname) {
+			    psMetadataAddStr(header, PS_LIST_TAIL, "EXTNAME", 0, "Extension name", extname);
+			}
+			psFree(filename);
+			psFree(extname);
+			psFree(filenameExt);
+		    } else if (strncasecmp(sourceType, "EXT", 3) == 0) {
+			// Source is an extension in the original file
+			psString extname = p_pmFPATranslateName(name, cell);
+			psMetadataAddStr(header, PS_LIST_TAIL, "EXTNAME", 0, "Extension name", extname);
+			psFree(extname);
+		    }
+		    
+		    // We've arrived where the pixels are.  Now we need to write them out.
+		    psArray *readouts = cell->readouts; // The array of readouts
+		    for (int readNum = 0; readNum < readouts->n; readNum++) {
+			pmReadout *readout = readouts->data[readNum]; // The readout of interest
+			if (! readout->mask) {
+			    psLogMsg(__func__, PS_LOG_WARN, "No mask to write out in %d,%d,%d\n",
+				     chipNum, cellNum, readNum);
+			} else {
+			    // XXX: Need to add the extname to the existing header
+			    if (! psFitsWriteImage(maskDest, header, readout->mask, readNum)) {
+				psError(PS_ERR_IO, false, "Unable to write mask plane %d in extension %s\n",
+					readNum, hdu->extname);
+				return false;
+			    }
+			}
+		    } // Iterating over readouts
+		    psFree(header);
+		    psFree(maskDest);
+		} // Valid cells
+	    } // Iterating over cells
+	} // Valid chips
+    } // Iterating over chips
+
+    return true;
+}
+
+
+bool pmFPAWriteWeight(pmFPA *fpa, 	// FPA containing mask to write
+		      psFits *fits	// FITS file for image
+    )
+{
+    const psMetadata *camera = fpa->camera; // Camera configuration for FPA
+    bool mdok = false;			// Status of MD lookup
+
+    // Get the required information from the camera configuration
+    psMetadata *supps = psMetadataLookupMD(&mdok, camera, "SUPPLEMENTARY"); // Rules for supplementary data
+    if (! mdok || ! supps) {
+	psError(PS_ERR_IO, false, "Unable to find SUPPLEMENTARY in camera configuration!\n");
+	return false;
+    }
+    psString sourceType = psMetadataLookupString(&mdok, supps, "WEIGHT.SOURCE"); // Type of source: EXT | FILE
+    if (! mdok || strlen(sourceType) <= 0) {
+	psError(PS_ERR_IO, false, "Unable to find WEIGHT.SOURCE in SUPPLEMENTARY section of camera "
+		"configuration!\n");
+	return false;
+    }
+    psString name = psMetadataLookupString(&mdok, supps, "WEIGHT.NAME"); // Name of weight
+    if (! mdok || strlen(sourceType) <= 0) {
+	psError(PS_ERR_IO, false, "Unable to find WEIGHT.NAME in SUPPLEMENTARY section of camera "
+		"configuration!\n");
+	return false;
+    }
+
+    // Go through the FPA to each cell/readout to get the weight
+    p_pmHDU *hdu = fpa->hdu;		// The HDU into which we will read the weight
+    psArray *chips = fpa->chips;	// Array of chips
+    for (int chipNum = 0; chipNum < chips->n; chipNum++) {
+	pmChip *chip = chips->data[chipNum]; // The current chip of interest
+	if (chip->valid) {
+	    if (chip->hdu) {
+		hdu = chip->hdu;
+	    }
+	    psArray *cells = chip->cells;	// Array of cells
+	    for (int cellNum = 0; cellNum < cells->n; cellNum++) {
+		pmCell *cell = cells->data[cellNum]; // The current cell of interest
+		if (cell->valid) {
+		    if (cell->hdu) {
+			hdu = cell->hdu;
+		    }
+
+		    // Now, need to find out where to write the pixels
+		    psFits *weightDest = psMemIncrRefCounter(fits); // Destination of weight image
+		    psMetadata *header = psMetadataAlloc(); // A dummy header, containing the extension name
+		    if (strcasecmp(sourceType, "FILE") == 0) {
+			// Source is a file (with optional extension, e.g., "myWeightFile.fits:thisExt"
+			psString filenameExt = p_pmFPATranslateName(name, cell);
+			char *colon = strchr(filenameExt, ':');	// Pointer to a colon in the filename-extn
+			psString filename = NULL; // The filename
+			psString extname = NULL;// The extenstion name
+			if (colon) {
+			    filename = psStringNCopy(filenameExt, strlen(filenameExt) - strlen(colon));
+			    if (strlen(colon) > 1) {
+				extname = psStringCopy(colon + 1);
+			    }
+			} else {
+			    filename = psMemIncrRefCounter(filenameExt);
+			}
+
+			psFree(weightDest);
+			weightDest = psFitsAlloc(filename);
+			if (extname) {
+			    psMetadataAddStr(header, PS_LIST_TAIL, "EXTNAME", 0, "Extension name", extname);
+			}
+			psFree(filename);
+			psFree(extname);
+			psFree(filenameExt);
+		    } else if (strncasecmp(sourceType, "EXT", 3) == 0) {
+			// Source is an extension in the original file
+			psString extname = p_pmFPATranslateName(name, cell);
+			psMetadataAddStr(header, PS_LIST_TAIL, "EXTNAME", 0, "Extension name", extname);
+			psFree(extname);
+		    }
+
+		    // We've arrived where the pixels are.  Now we need to write them out.
+		    psArray *readouts = cell->readouts; // The array of readouts
+		    for (int readNum = 0; readNum < readouts->n; readNum++) {
+			pmReadout *readout = readouts->data[readNum]; // The readout of interest
+			if (! readout->weight) {
+			    psLogMsg(__func__, PS_LOG_WARN, "No weight image to write out in %d,%d,%d\n",
+				     chipNum, cellNum, readNum);
+			} else {
+			    if (! psFitsWriteImage(weightDest, header, readout->weight, readNum)) {
+				psError(PS_ERR_IO, false, "Unable to write weight plane %d in extension %s\n",
+					readNum, hdu->extname);
+				return false;
+			    }
+			}
+		    } // Iterating over readouts
+		    psFree(header);
+		    psFree(weightDest);
+		} // Valid cells
+	    } // Iterating over cells
+	} // Valid chips
+    } // Iterating over chips
+
+    return true;
+}
Index: /trunk/archive/scripts/src/phase2/pmFPAWrite.h
===================================================================
--- /trunk/archive/scripts/src/phase2/pmFPAWrite.h	(revision 5563)
+++ /trunk/archive/scripts/src/phase2/pmFPAWrite.h	(revision 5564)
@@ -9,4 +9,12 @@
     );
 
+bool pmFPAWriteMask(pmFPA *fpa, 	// FPA containing mask to write
+		    psFits *fits	// FITS file for image
+    );
+
+bool pmFPAWriteWeight(pmFPA *fpa, 	// FPA containing mask to write
+		      psFits *fits	// FITS file for image
+    );
+
 
 #endif
