Index: /branches/rel10_ifa/psModules/src/astrom/Makefile.am
===================================================================
--- /branches/rel10_ifa/psModules/src/astrom/Makefile.am	(revision 6617)
+++ /branches/rel10_ifa/psModules/src/astrom/Makefile.am	(revision 6618)
@@ -1,5 +1,5 @@
 noinst_LTLIBRARIES = libpsmoduleastrom.la
 
-libpsmoduleastrom_la_CPPFLAGS = $(SRCINC) $(PSMODULE_CFLAGS)
+libpsmoduleastrom_la_CPPFLAGS = $(SRCINC) $(PSMODULE_CFLAGS) -I../pslib/
 libpsmoduleastrom_la_LDFLAGS  = -release $(PACKAGE_VERSION)
 libpsmoduleastrom_la_SOURCES  = \
Index: /branches/rel10_ifa/psModules/src/astrom/pmConcepts.c
===================================================================
--- /branches/rel10_ifa/psModules/src/astrom/pmConcepts.c	(revision 6617)
+++ /branches/rel10_ifa/psModules/src/astrom/pmConcepts.c	(revision 6618)
@@ -158,384 +158,394 @@
 }
 
-
-// Set the concepts for a given FPA to blanks
-bool pmConceptsBlankFPA(pmFPA *fpa    // FPA for which to set blank concepts
-                       )
-{
-    psTrace("psModule.concepts", 5, "Blanking FPA concepts: %x %x\n", conceptsFPA, fpa->concepts);
-    return conceptsBlank(&conceptsFPA, fpa->concepts);
-}
-
-// Read the concepts for a given FPA
-bool pmConceptsReadFPA(pmFPA *fpa,      // FPA for which to read concepts
-                       pmConceptSource source, // The source of the concepts to read
-                       psDB *db         // Database handle
-                      )
-{
-    psTrace("psModule.concepts", 5, "Reading FPA concepts: %x %x\n", conceptsFPA, fpa->concepts);
-    return conceptsRead(&conceptsFPA, fpa, NULL, NULL, source, db, fpa->concepts);
-}
-
-// Read the concepts for a given FPA
-bool pmConceptsWriteFPA(pmFPA *fpa,     // FPA for which to write concepts
-                        pmConceptSource source, // The source of the concepts to read
-                        psDB *db        // Database handle
-                       )
-{
-    psTrace("psModule.concepts", 5, "Writing FPA concepts: %x %x\n", conceptsFPA, fpa->concepts);
-    return conceptsWrite(&conceptsFPA, fpa, NULL, NULL, source, db, fpa->concepts);
-}
-
-// Set the concepts for a given chip to blanks
-bool pmConceptsBlankChip(pmChip *chip // FPA for which to set blank concepts
-                        )
-{
-    psTrace("psModule.concepts", 5, "Blanking chip concepts: %x %x\n", conceptsChip, chip->concepts);
-    return conceptsBlank(&conceptsChip, chip->concepts);
-}
-
-// Read the concepts for a given FPA
-bool pmConceptsReadChip(pmChip *chip,   // Chip for which to read concepts
-                        pmConceptSource source, // The source of the concepts to read
-                        psDB *db        // Database handle
-                       )
-{
-    psTrace("psModule.concepts", 5, "Reading chip concepts: %x %x\n", conceptsChip, chip->concepts);
-    pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
-    return conceptsRead(&conceptsChip, fpa, chip, NULL, source, db, chip->concepts);
-}
-
-// Read the concepts for a given FPA
-bool pmConceptsWriteChip(pmChip *chip,  // Chip for which to write concepts
-                         pmConceptSource source, // The source of the concepts to read
-                         psDB *db        // Database handle
-                        )
-{
-    psTrace("psModule.concepts", 5, "Writing chip concepts: %x %x\n", conceptsChip, chip->concepts);
-    pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
-    return conceptsWrite(&conceptsChip, fpa, chip, NULL, source, db, chip->concepts);
-}
-
-// Set the concepts for a given chip to blanks
-bool pmConceptsBlankCell(pmCell *cell // Cell for which to set blank concepts
-                        )
-{
-    psTrace("psModule.concepts", 5, "Blanking cell concepts: %x %x\n", conceptsCell, cell->concepts);
-    return conceptsBlank(&conceptsCell, cell->concepts);
-}
-
-// Read the concepts for a given FPA
-bool pmConceptsReadCell(pmCell *cell,   // Cell for which to read concepts
-                        pmConceptSource source, // The source of the concepts to read
-                        psDB *db        // Database handle
-                       )
-{
-    psTrace("psModule.concepts", 5, "Writing cell concepts: %x %x\n", conceptsCell, cell->concepts);
-    pmChip *chip = cell->parent;        // Chip to which the cell belongs
-    pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
-    return conceptsRead(&conceptsCell, fpa, chip, cell, source, db, cell->concepts);
-}
-
-// Read the concepts for a given FPA
-bool pmConceptsWriteCell(pmCell *cell,  // FPA for which to write concepts
-                         pmConceptSource source, // The source of the concepts to read
-                         psDB *db       // Database handle
-                        )
-{
-    psTrace("psModule.concepts", 5, "Writing cell concepts: %x %x\n", conceptsCell, cell->concepts);
-    pmChip *chip = cell->parent;        // Chip to which the cell belongs
-    pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
-    return conceptsWrite(&conceptsCell, fpa, chip, cell, source, db, cell->concepts);
-}
-
-
-bool pmConceptsInit(void)
-{
-    bool init = false;                  // Did we initialise anything?
-    if (! conceptsFPA) {
-        conceptsFPA = psMetadataAlloc();
-        init = true;
-
-        // Install the standard concepts
-
-        #if 0
-        // FPA.NAME
-        {
-            psMetadataItem *fpaName = psMetadataItemAllocStr("FPA.NAME", "Name of FPA", "");
-            pmConceptRegister(fpaName, NULL, NULL, PM_CONCEPT_LEVEL_FPA);
-            psFree(fpaName);
+#if 0
+bool pmConceptsRead(pmFPAview *view,
+                    pmFPAlevel levels,
+                    pmConceptSource source,
+                    psDB *db
+                   )
+{
+    pmFPA *fpa = view->fpa;             // The FPA
+
+    #endif
+
+    // Set the concepts for a given FPA to blanks
+    bool pmConceptsBlankFPA(pmFPA *fpa    // FPA for which to set blank concepts
+                           ) {
+        psTrace("psModule.concepts", 5, "Blanking FPA concepts: %x %x\n", conceptsFPA, fpa->concepts);
+        return conceptsBlank(&conceptsFPA, fpa->concepts);
+    }
+
+    // Read the concepts for a given FPA
+    bool pmConceptsReadFPA(pmFPA *fpa,      // FPA for which to read concepts
+                           pmConceptSource source, // The source of the concepts to read
+                           psDB *db         // Database handle
+                          ) {
+        psTrace("psModule.concepts", 5, "Reading FPA concepts: %x %x\n", conceptsFPA, fpa->concepts);
+        return conceptsRead(&conceptsFPA, fpa, NULL, NULL, source, db, fpa->concepts);
+    }
+
+    // Read the concepts for a given FPA
+    bool pmConceptsWriteFPA(pmFPA *fpa,     // FPA for which to write concepts
+                            pmConceptSource source, // The source of the concepts to read
+                            psDB *db        // Database handle
+                           ) {
+        psTrace("psModule.concepts", 5, "Writing FPA concepts: %x %x\n", conceptsFPA, fpa->concepts);
+        return conceptsWrite(&conceptsFPA, fpa, NULL, NULL, source, db, fpa->concepts);
+    }
+
+    // Set the concepts for a given chip to blanks
+    bool pmConceptsBlankChip(pmChip *chip // FPA for which to set blank concepts
+                            ) {
+        psTrace("psModule.concepts", 5, "Blanking chip concepts: %x %x\n", conceptsChip, chip->concepts);
+        return conceptsBlank(&conceptsChip, chip->concepts);
+    }
+
+    // Read the concepts for a given FPA
+    bool pmConceptsReadChip(pmChip *chip,   // Chip for which to read concepts
+                            pmConceptSource source, // The source of the concepts to read
+                            bool propagate, // Propagate to higher levels as well?
+                            psDB *db        // Database handle
+                           ) {
+        psTrace("psModule.concepts", 5, "Reading chip concepts: %x %x\n", conceptsChip, chip->concepts);
+        pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
+        return conceptsRead(&conceptsChip, fpa, chip, NULL, source, db, chip->concepts) &&
+               ((propagate && conceptsRead(&conceptsFPA, fpa, chip, NULL, source, db, fpa->concepts)) ||
+                !propagate);
+    }
+
+    // Read the concepts for a given FPA
+    bool pmConceptsWriteChip(pmChip *chip,  // Chip for which to write concepts
+                             pmConceptSource source, // The source of the concepts to read
+                             bool propagate,// Propagate to higher levels as well?
+                             psDB *db        // Database handle
+                            ) {
+        psTrace("psModule.concepts", 5, "Writing chip concepts: %x %x\n", conceptsChip, chip->concepts);
+        pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
+        return conceptsWrite(&conceptsChip, fpa, chip, NULL, source, db, chip->concepts) &&
+               ((propagate && conceptsWrite(&conceptsFPA, fpa, chip, NULL, source, db, fpa->concepts)) ||
+                !propagate);
+    }
+
+    // Set the concepts for a given chip to blanks
+    bool pmConceptsBlankCell(pmCell *cell // Cell for which to set blank concepts
+                            ) {
+        psTrace("psModule.concepts", 5, "Blanking cell concepts: %x %x\n", conceptsCell, cell->concepts);
+        return conceptsBlank(&conceptsCell, cell->concepts);
+    }
+
+    // Read the concepts for a given FPA
+    bool pmConceptsReadCell(pmCell *cell,   // Cell for which to read concepts
+                            pmConceptSource source, // The source of the concepts to read
+                            bool propagate,// Propagate to higher levels as well?
+                            psDB *db        // Database handle
+                           ) {
+        psTrace("psModule.concepts", 5, "Reading cell concepts: %x %x\n", conceptsCell, cell->concepts);
+        pmChip *chip = cell->parent;        // Chip to which the cell belongs
+        pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
+        return conceptsRead(&conceptsCell, fpa, chip, cell, source, db, cell->concepts) &&
+               ((propagate && conceptsRead(&conceptsChip, fpa, chip, cell, source, db, chip->concepts) &&
+                 conceptsRead(&conceptsFPA, fpa, chip, cell, source, db, fpa->concepts)) || !propagate);
+    }
+
+    // Read the concepts for a given FPA
+    bool pmConceptsWriteCell(pmCell *cell,  // FPA for which to write concepts
+                             pmConceptSource source, // The source of the concepts to read
+                             bool propagate,// Propagate to higher levels as well?
+                             psDB *db       // Database handle
+                            ) {
+        psTrace("psModule.concepts", 5, "Writing cell concepts: %x %x\n", conceptsCell, cell->concepts);
+        pmChip *chip = cell->parent;        // Chip to which the cell belongs
+        pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
+        return conceptsWrite(&conceptsCell, fpa, chip, cell, source, db, cell->concepts) &&
+               ((propagate && conceptsWrite(&conceptsChip, fpa, chip, cell, source, db, chip->concepts) &&
+                 conceptsWrite(&conceptsFPA, fpa, chip, cell, source, db, fpa->concepts)) || !propagate);
+    }
+
+
+    bool pmConceptsInit(void) {
+        bool init = false;                  // Did we initialise anything?
+        if (! conceptsFPA) {
+            conceptsFPA = psMetadataAlloc();
+            init = true;
+
+            // Install the standard concepts
+
+            #if 0
+            // FPA.NAME
+            {
+                psMetadataItem *fpaName = psMetadataItemAllocStr("FPA.NAME", "Name of FPA", "");
+                pmConceptRegister(fpaName, NULL, NULL, PM_CONCEPT_LEVEL_FPA);
+                psFree(fpaName);
+            }
+            #endif
+
+            // FPA.AIRMASS
+            {
+                psMetadataItem *fpaAirmass = psMetadataItemAllocF32("FPA.AIRMASS", "Airmass at boresight", 0.0);
+                pmConceptRegister(fpaAirmass, NULL, NULL, PM_CONCEPT_LEVEL_FPA);
+                psFree(fpaAirmass);
+            }
+
+            // FPA.FILTER
+            {
+                psMetadataItem *fpaFilter = psMetadataItemAllocStr("FPA.FILTER", "Filter used", "");
+                pmConceptRegister(fpaFilter, NULL, NULL, PM_CONCEPT_LEVEL_FPA);
+                psFree(fpaFilter);
+            }
+
+            // FPA.POSANGLE
+            {
+                psMetadataItem *fpaPosangle = psMetadataItemAllocF32("FPA.POSANGLE",
+                                              "Position angle of instrument", 0.0);
+                pmConceptRegister(fpaPosangle, NULL, NULL, PM_CONCEPT_LEVEL_FPA);
+                psFree(fpaPosangle);
+            }
+
+            // FPA.RADECSYS
+            {
+                psMetadataItem *fpaRadecsys = psMetadataItemAllocStr("FPA.RADECSYS",
+                                              "Celestial coordinate system", "");
+                pmConceptRegister(fpaRadecsys, NULL, NULL, PM_CONCEPT_LEVEL_FPA);
+                psFree(fpaRadecsys);
+            }
+
+            // FPA.RA
+            {
+                psMetadataItem *fpaRa = psMetadataItemAllocF64("FPA.RA", "Right Ascension of boresight", NAN);
+                pmConceptRegister(fpaRa, (pmConceptParseFunc)pmConceptParse_FPA_Coords,
+                                  (pmConceptFormatFunc)pmConceptFormat_FPA_Coords, PM_CONCEPT_LEVEL_FPA);
+                psFree(fpaRa);
+            }
+
+            // FPA.DEC
+            {
+                psMetadataItem *fpaDec = psMetadataItemAllocF64("FPA.DEC", "Declination of boresight", NAN);
+                pmConceptRegister(fpaDec, (pmConceptParseFunc)pmConceptParse_FPA_Coords,
+                                  (pmConceptFormatFunc)pmConceptFormat_FPA_Coords, PM_CONCEPT_LEVEL_FPA);
+                psFree(fpaDec);
+            }
+
+            // Done with FPA level concepts
         }
-        #endif
-
-        // FPA.AIRMASS
-        {
-            psMetadataItem *fpaAirmass = psMetadataItemAllocF32("FPA.AIRMASS", "Airmass at boresight", 0.0);
-            pmConceptRegister(fpaAirmass, NULL, NULL, PM_CONCEPT_LEVEL_FPA);
-            psFree(fpaAirmass);
+        if (! conceptsChip) {
+            conceptsChip = psMetadataAlloc();
+            init = true;
+            // There are no standard concepts at the chip level to be installed
         }
-
-        // FPA.FILTER
-        {
-            psMetadataItem *fpaFilter = psMetadataItemAllocStr("FPA.FILTER", "Filter used", "");
-            pmConceptRegister(fpaFilter, NULL, NULL, PM_CONCEPT_LEVEL_FPA);
-            psFree(fpaFilter);
+        if (! conceptsCell) {
+            conceptsCell = psMetadataAlloc();
+            init = true;
+
+            // Install the standard concepts
+
+            // CELL.GAIN
+            {
+                psMetadataItem *cellGain = psMetadataItemAllocF32("CELL.GAIN", "CCD gain (e/count)", NAN);
+                pmConceptRegister(cellGain, NULL, NULL, PM_CONCEPT_LEVEL_CELL);
+                psFree(cellGain);
+            }
+
+            // CELL.READNOISE
+            {
+                psMetadataItem *cellReadnoise = psMetadataItemAllocF32("CELL.READNOISE",
+                                                "CCD read noise (e)", NAN);
+                pmConceptRegister(cellReadnoise, NULL, NULL, PM_CONCEPT_LEVEL_CELL);
+                psFree(cellReadnoise);
+            }
+
+            // CELL.SATURATION
+            {
+                psMetadataItem *cellSaturation = psMetadataItemAllocF32("CELL.SATURATION",
+                                                 "Saturation level (counts)", NAN);
+                pmConceptRegister(cellSaturation, NULL, NULL, PM_CONCEPT_LEVEL_CELL);
+                psFree(cellSaturation);
+            }
+
+            // CELL.BAD
+            {
+                psMetadataItem *cellBad = psMetadataItemAllocF32("CELL.BAD", "Bad level (counts)", NAN);
+                pmConceptRegister(cellBad, NULL, NULL, PM_CONCEPT_LEVEL_CELL);
+                psFree(cellBad);
+            }
+
+            // CELL.XPARITY
+            {
+                psMetadataItem *cellXparity = psMetadataItemAllocS32("CELL.XPARITY",
+                                              "Orientation in x compared to the rest of the FPA", 0);
+                pmConceptRegister(cellXparity, NULL, NULL, PM_CONCEPT_LEVEL_CELL);
+                psFree(cellXparity);
+            }
+
+            // CELL.YPARITY
+            {
+                psMetadataItem *cellYparity = psMetadataItemAllocS32("CELL.YPARITY",
+                                              "Orientation in x compared to the rest of the FPA", 0);
+                pmConceptRegister(cellYparity, NULL, NULL, PM_CONCEPT_LEVEL_CELL);
+                psFree(cellYparity);
+            }
+
+            // CELL.READDIR
+            {
+                psMetadataItem *cellReaddir = psMetadataItemAllocS32("CELL.READDIR",
+                                              "Read direction, rows=1, cols=2", 1);
+                pmConceptRegister(cellReaddir, NULL, NULL, PM_CONCEPT_LEVEL_CELL);
+                psFree(cellReaddir);
+            }
+
+
+            // These (CELL.EXPOSURE and CELL.DARKTIME) used to be READOUT.EXPOSURE and READOUT.DARKTIME, but that
+            // doesn't really make sense at the moment.  Maybe we need to add a "parent" link to the readouts.
+            // But then how are the exposure times REALLY derived?  They're not in the FITS headers, because a
+            // readout is a plane in a 3D image.  We'll have to dream up some additional suffix to specify these,
+            // but for now....
+
+            // CELL.EXPOSURE
+            {
+                psMetadataItem *cellExposure = psMetadataItemAllocF32("CELL.EXPOSURE",
+                                               "Exposure time (sec)", NAN);
+                pmConceptRegister(cellExposure, NULL, NULL, PM_CONCEPT_LEVEL_CELL);
+                psFree(cellExposure);
+            }
+
+            // CELL.DARKTIME
+            {
+                psMetadataItem *cellDarktime = psMetadataItemAllocF32("CELL.DARKTIME",
+                                               "Time since flush (sec)", NAN);
+                pmConceptRegister(cellDarktime, NULL, NULL, PM_CONCEPT_LEVEL_CELL);
+                psFree(cellDarktime);
+            }
+
+            // CELL.TRIMSEC
+            {
+                psRegion *trimsec = psAlloc(sizeof(psRegion)); // Blank trimsec
+                trimsec->x0 = trimsec->y0 = trimsec->x1 = trimsec->y1 = NAN;
+                psMetadataItem *cellTrimsec = psMetadataItemAllocPtr("CELL.TRIMSEC", PS_DATA_UNKNOWN,
+                                              "Trim section", trimsec);
+                psFree(trimsec);
+                pmConceptRegister(cellTrimsec, (pmConceptParseFunc)pmConceptParse_CELL_TRIMSEC,
+                                  (pmConceptFormatFunc)pmConceptFormat_CELL_TRIMSEC, PM_CONCEPT_LEVEL_CELL);
+                psFree(cellTrimsec);
+            }
+
+            // CELL.BIASSEC
+            {
+                psList *biassecs = psListAlloc(NULL); // Blank biassecs
+                psMetadataItem *cellBiassec = psMetadataItemAllocPtr("CELL.BIASSEC", PS_DATA_LIST,
+                                              "Bias sections", biassecs);
+                psFree(biassecs);
+                pmConceptRegister(cellBiassec, (pmConceptParseFunc)pmConceptParse_CELL_BIASSEC,
+                                  (pmConceptFormatFunc)pmConceptFormat_CELL_BIASSEC, PM_CONCEPT_LEVEL_CELL);
+                psFree(cellBiassec);
+            }
+
+            // CELL.XBIN
+            {
+                psMetadataItem *cellXbin = psMetadataItemAllocS32("CELL.XBIN", "Binning in x", 0);
+                pmConceptRegister(cellXbin, (pmConceptParseFunc)pmConceptParse_CELL_Binning,
+                                  (pmConceptFormatFunc)pmConceptFormat_CELL_XBIN, PM_CONCEPT_LEVEL_CELL);
+                psFree(cellXbin);
+            }
+
+            // CELL.YBIN
+            {
+                psMetadataItem *cellYbin = psMetadataItemAllocS32("CELL.YBIN", "Binning in y", 0);
+                pmConceptRegister(cellYbin, (pmConceptParseFunc)pmConceptParse_CELL_Binning,
+                                  (pmConceptFormatFunc)pmConceptFormat_CELL_YBIN, PM_CONCEPT_LEVEL_CELL);
+                psFree(cellYbin);
+            }
+
+            // CELL.TIMESYS
+            {
+                psMetadataItem *cellTimesys = psMetadataItemAllocS32("CELL.TIMESYS", "Time system", -1);
+                pmConceptRegister(cellTimesys, (pmConceptParseFunc)pmConceptParse_CELL_TIMESYS,
+                                  (pmConceptFormatFunc)pmConceptFormat_CELL_TIMESYS, PM_CONCEPT_LEVEL_CELL);
+                psFree(cellTimesys);
+            }
+
+            // CELL.TIME
+            {
+                psTime *time = psTimeAlloc(PS_TIME_TAI); // Blank time
+                // Not particularly distinguishing, but should be good enough
+                time->sec = 0;
+                time->nsec = 0;
+                psMetadataItem *cellTime = psMetadataItemAlloc("CELL.TIME", PS_DATA_TIME,
+                                           "Time of exposure", time);
+                psFree(time);
+                pmConceptRegister(cellTime, (pmConceptParseFunc)pmConceptParse_CELL_TIME,
+                                  (pmConceptFormatFunc)pmConceptFormat_CELL_TIME, PM_CONCEPT_LEVEL_CELL);
+                psFree(cellTime);
+            }
+
+            // CELL.X0
+            {
+                psMetadataItem *cellX0 = psMetadataItemAllocS32("CELL.X0", "Position of (0,0) on the chip", 0);
+                pmConceptRegister(cellX0, (pmConceptParseFunc)pmConceptParse_CELL_Positions,
+                                  (pmConceptFormatFunc)pmConceptFormat_CELL_Positions, PM_CONCEPT_LEVEL_CELL);
+                psFree(cellX0);
+            }
+
+            // CELL.Y0
+            {
+                psMetadataItem *cellY0 = psMetadataItemAllocS32("CELL.Y0", "Position of (0,0) on the chip", 0);
+                pmConceptRegister(cellY0, (pmConceptParseFunc)pmConceptParse_CELL_Positions,
+                                  (pmConceptFormatFunc)pmConceptFormat_CELL_Positions, PM_CONCEPT_LEVEL_CELL);
+                psFree(cellY0);
+            }
+
         }
 
-        // FPA.POSANGLE
-        {
-            psMetadataItem *fpaPosangle = psMetadataItemAllocF32("FPA.POSANGLE",
-                                          "Position angle of instrument", 0.0);
-            pmConceptRegister(fpaPosangle, NULL, NULL, PM_CONCEPT_LEVEL_FPA);
-            psFree(fpaPosangle);
-        }
-
-        // FPA.RADECSYS
-        {
-            psMetadataItem *fpaRadecsys = psMetadataItemAllocStr("FPA.RADECSYS",
-                                          "Celestial coordinate system", "");
-            pmConceptRegister(fpaRadecsys, NULL, NULL, PM_CONCEPT_LEVEL_FPA);
-            psFree(fpaRadecsys);
-        }
-
-        // FPA.RA
-        {
-            psMetadataItem *fpaRa = psMetadataItemAllocF64("FPA.RA", "Right Ascension of boresight", NAN);
-            pmConceptRegister(fpaRa, (pmConceptParseFunc)pmConceptParse_FPA_Coords,
-                              (pmConceptFormatFunc)pmConceptFormat_FPA_Coords, PM_CONCEPT_LEVEL_FPA);
-            psFree(fpaRa);
-        }
-
-        // FPA.DEC
-        {
-            psMetadataItem *fpaDec = psMetadataItemAllocF64("FPA.DEC", "Declination of boresight", NAN);
-            pmConceptRegister(fpaDec, (pmConceptParseFunc)pmConceptParse_FPA_Coords,
-                              (pmConceptFormatFunc)pmConceptFormat_FPA_Coords, PM_CONCEPT_LEVEL_FPA);
-            psFree(fpaDec);
-        }
-
-        // Done with FPA level concepts
-    }
-    if (! conceptsChip) {
-        conceptsChip = psMetadataAlloc();
-        init = true;
-        // There are no standard concepts at the chip level to be installed
-    }
-    if (! conceptsCell) {
-        conceptsCell = psMetadataAlloc();
-        init = true;
-
-        // Install the standard concepts
-
-        // CELL.GAIN
-        {
-            psMetadataItem *cellGain = psMetadataItemAllocF32("CELL.GAIN", "CCD gain (e/count)", NAN);
-            pmConceptRegister(cellGain, NULL, NULL, PM_CONCEPT_LEVEL_CELL);
-            psFree(cellGain);
-        }
-
-        // CELL.READNOISE
-        {
-            psMetadataItem *cellReadnoise = psMetadataItemAllocF32("CELL.READNOISE",
-                                            "CCD read noise (e)", NAN);
-            pmConceptRegister(cellReadnoise, NULL, NULL, PM_CONCEPT_LEVEL_CELL);
-            psFree(cellReadnoise);
-        }
-
-        // CELL.SATURATION
-        {
-            psMetadataItem *cellSaturation = psMetadataItemAllocF32("CELL.SATURATION",
-                                             "Saturation level (counts)", NAN);
-            pmConceptRegister(cellSaturation, NULL, NULL, PM_CONCEPT_LEVEL_CELL);
-            psFree(cellSaturation);
-        }
-
-        // CELL.BAD
-        {
-            psMetadataItem *cellBad = psMetadataItemAllocF32("CELL.BAD", "Bad level (counts)", NAN);
-            pmConceptRegister(cellBad, NULL, NULL, PM_CONCEPT_LEVEL_CELL);
-            psFree(cellBad);
-        }
-
-        // CELL.XPARITY
-        {
-            psMetadataItem *cellXparity = psMetadataItemAllocS32("CELL.XPARITY",
-                                          "Orientation in x compared to the rest of the FPA", 0);
-            pmConceptRegister(cellXparity, NULL, NULL, PM_CONCEPT_LEVEL_CELL);
-            psFree(cellXparity);
-        }
-
-        // CELL.YPARITY
-        {
-            psMetadataItem *cellYparity = psMetadataItemAllocS32("CELL.YPARITY",
-                                          "Orientation in x compared to the rest of the FPA", 0);
-            pmConceptRegister(cellYparity, NULL, NULL, PM_CONCEPT_LEVEL_CELL);
-            psFree(cellYparity);
-        }
-
-        // CELL.READDIR
-        {
-            psMetadataItem *cellReaddir = psMetadataItemAllocS32("CELL.READDIR",
-                                          "Read direction, rows=1, cols=2", 1);
-            pmConceptRegister(cellReaddir, NULL, NULL, PM_CONCEPT_LEVEL_CELL);
-            psFree(cellReaddir);
-        }
-
-
-        // These (CELL.EXPOSURE and CELL.DARKTIME) used to be READOUT.EXPOSURE and READOUT.DARKTIME, but that
-        // doesn't really make sense at the moment.  Maybe we need to add a "parent" link to the readouts.
-        // But then how are the exposure times REALLY derived?  They're not in the FITS headers, because a
-        // readout is a plane in a 3D image.  We'll have to dream up some additional suffix to specify these,
-        // but for now....
-
-        // CELL.EXPOSURE
-        {
-            psMetadataItem *cellExposure = psMetadataItemAllocF32("CELL.EXPOSURE",
-                                           "Exposure time (sec)", NAN);
-            pmConceptRegister(cellExposure, NULL, NULL, PM_CONCEPT_LEVEL_CELL);
-            psFree(cellExposure);
-        }
-
-        // CELL.DARKTIME
-        {
-            psMetadataItem *cellDarktime = psMetadataItemAllocF32("CELL.DARKTIME",
-                                           "Time since flush (sec)", NAN);
-            pmConceptRegister(cellDarktime, NULL, NULL, PM_CONCEPT_LEVEL_CELL);
-            psFree(cellDarktime);
-        }
-
-        // CELL.TRIMSEC
-        {
-            psRegion *trimsec = psAlloc(sizeof(psRegion)); // Blank trimsec
-            trimsec->x0 = trimsec->y0 = trimsec->x1 = trimsec->y1 = NAN;
-            psMetadataItem *cellTrimsec = psMetadataItemAllocPtr("CELL.TRIMSEC", PS_DATA_UNKNOWN,
-                                          "Trim section", trimsec);
-            psFree(trimsec);
-            pmConceptRegister(cellTrimsec, (pmConceptParseFunc)pmConceptParse_CELL_TRIMSEC,
-                              (pmConceptFormatFunc)pmConceptFormat_CELL_TRIMSEC, PM_CONCEPT_LEVEL_CELL);
-            psFree(cellTrimsec);
-        }
-
-        // CELL.BIASSEC
-        {
-            psList *biassecs = psListAlloc(NULL); // Blank biassecs
-            psMetadataItem *cellBiassec = psMetadataItemAllocPtr("CELL.BIASSEC", PS_DATA_LIST,
-                                          "Bias sections", biassecs);
-            psFree(biassecs);
-            pmConceptRegister(cellBiassec, (pmConceptParseFunc)pmConceptParse_CELL_BIASSEC,
-                              (pmConceptFormatFunc)pmConceptFormat_CELL_BIASSEC, PM_CONCEPT_LEVEL_CELL);
-            psFree(cellBiassec);
-        }
-
-        // CELL.XBIN
-        {
-            psMetadataItem *cellXbin = psMetadataItemAllocS32("CELL.XBIN", "Binning in x", 0);
-            pmConceptRegister(cellXbin, (pmConceptParseFunc)pmConceptParse_CELL_Binning,
-                              (pmConceptFormatFunc)pmConceptFormat_CELL_XBIN, PM_CONCEPT_LEVEL_CELL);
-            psFree(cellXbin);
-        }
-
-        // CELL.YBIN
-        {
-            psMetadataItem *cellYbin = psMetadataItemAllocS32("CELL.YBIN", "Binning in y", 0);
-            pmConceptRegister(cellYbin, (pmConceptParseFunc)pmConceptParse_CELL_Binning,
-                              (pmConceptFormatFunc)pmConceptFormat_CELL_YBIN, PM_CONCEPT_LEVEL_CELL);
-            psFree(cellYbin);
-        }
-
-        // CELL.TIMESYS
-        {
-            psMetadataItem *cellTimesys = psMetadataItemAllocS32("CELL.TIMESYS", "Time system", -1);
-            pmConceptRegister(cellTimesys, (pmConceptParseFunc)pmConceptParse_CELL_TIMESYS,
-                              (pmConceptFormatFunc)pmConceptFormat_CELL_TIMESYS, PM_CONCEPT_LEVEL_CELL);
-            psFree(cellTimesys);
-        }
-
-        // CELL.TIME
-        {
-            psTime *time = psTimeAlloc(PS_TIME_TAI); // Blank time
-            // Not particularly distinguishing, but should be good enough
-            time->sec = 0;
-            time->nsec = 0;
-            psMetadataItem *cellTime = psMetadataItemAlloc("CELL.TIME", PS_DATA_TIME,
-                                       "Time of exposure", time);
-            psFree(time);
-            pmConceptRegister(cellTime, (pmConceptParseFunc)pmConceptParse_CELL_TIME,
-                              (pmConceptFormatFunc)pmConceptFormat_CELL_TIME, PM_CONCEPT_LEVEL_CELL);
-            psFree(cellTime);
-        }
-
-        // CELL.X0
-        {
-            psMetadataItem *cellX0 = psMetadataItemAllocS32("CELL.X0", "Position of (0,0) on the chip", 0);
-            pmConceptRegister(cellX0, (pmConceptParseFunc)pmConceptParse_CELL_Positions,
-                              (pmConceptFormatFunc)pmConceptFormat_CELL_Positions, PM_CONCEPT_LEVEL_CELL);
-            psFree(cellX0);
-        }
-
-        // CELL.Y0
-        {
-            psMetadataItem *cellY0 = psMetadataItemAllocS32("CELL.Y0", "Position of (0,0) on the chip", 0);
-            pmConceptRegister(cellY0, (pmConceptParseFunc)pmConceptParse_CELL_Positions,
-                              (pmConceptFormatFunc)pmConceptFormat_CELL_Positions, PM_CONCEPT_LEVEL_CELL);
-            psFree(cellY0);
-        }
-
-    }
-
-    conceptsInitialised = true;
-
-    return init;
-}
-
-void pmConceptsDone(void)
-{
-    psFree(conceptsFPA);
-    psFree(conceptsChip);
-    psFree(conceptsCell);
-}
-
-
-// Copy concepts from one FPA to another
-bool pmFPACopyConcepts(pmFPA *target,   // The target FPA
-                       pmFPA *source    // The target FPA
-                      )
-{
-    // Copy FPA concepts
-    target->concepts = psMetadataCopy(target->concepts, source->concepts);
-
-    // Copy chip concepts
-    psArray *targetChips = target->chips; // Chips in target
-    psArray *sourceChips = source->chips; // Chips in source
-    if (targetChips->n != sourceChips->n) {
-        psError(PS_ERR_IO, true, "Number of chips in target (%d) and source (%d) differ --- unable to copy "
-                "concepts.\n", targetChips->n, sourceChips->n);
-        return false;
-    }
-    for (int i = 0; i < targetChips->n; i++) {
-        pmChip *targetChip = targetChips->data[i]; // Target chip of interest
-        pmChip *sourceChip = sourceChips->data[i]; // Source chip of interest
-        if (! targetChip || ! sourceChip) {
-            continue;
-        }
-        targetChip->concepts = psMetadataCopy(targetChip->concepts, sourceChip->concepts);
-
-        // Copy cell concepts
-        psArray *targetCells = targetChip->cells; // Cells in target
-        psArray *sourceCells = sourceChip->cells; // Cells in source
-        if (targetCells->n != sourceCells->n) {
-            psError(PS_ERR_IO, true, "Number of cells in target (%d) and source (%d) differ for chip %d ---"
-                    " unable to copy concepts.\n", targetCells->n, sourceCells->n, i);
+        conceptsInitialised = true;
+
+        return init;
+    }
+
+    void pmConceptsDone(void) {
+        psFree(conceptsFPA);
+        psFree(conceptsChip);
+        psFree(conceptsCell);
+    }
+
+
+    // Copy concepts from one FPA to another
+    bool pmFPACopyConcepts(pmFPA *target,   // The target FPA
+                           pmFPA *source    // The target FPA
+                          ) {
+        // Copy FPA concepts
+        target->concepts = psMetadataCopy(target->concepts, source->concepts);
+
+        // Copy chip concepts
+        psArray *targetChips = target->chips; // Chips in target
+        psArray *sourceChips = source->chips; // Chips in source
+        if (targetChips->n != sourceChips->n) {
+            psError(PS_ERR_IO, true, "Number of chips in target (%d) and source (%d) differ --- unable to copy "
+                    "concepts.\n", targetChips->n, sourceChips->n);
             return false;
         }
-        for (int j = 0; j < targetCells->n; j++) {
-            pmCell *targetCell = targetCells->data[j]; // Target chip of interest
-            pmCell *sourceCell = sourceCells->data[j]; // Source chip of interest
-            if (! targetCell || ! sourceCell) {
+        for (int i = 0; i < targetChips->n; i++) {
+            pmChip *targetChip = targetChips->data[i]; // Target chip of interest
+            pmChip *sourceChip = sourceChips->data[i]; // Source chip of interest
+            if (! targetChip || ! sourceChip) {
                 continue;
             }
-            targetCell->concepts = psMetadataCopy(targetCell->concepts, sourceCell->concepts);
+            targetChip->concepts = psMetadataCopy(targetChip->concepts, sourceChip->concepts);
+
+            // Copy cell concepts
+            psArray *targetCells = targetChip->cells; // Cells in target
+            psArray *sourceCells = sourceChip->cells; // Cells in source
+            if (targetCells->n != sourceCells->n) {
+                psError(PS_ERR_IO, true, "Number of cells in target (%d) and source (%d) differ for chip %d ---"
+                        " unable to copy concepts.\n", targetCells->n, sourceCells->n, i);
+                return false;
+            }
+            for (int j = 0; j < targetCells->n; j++) {
+                pmCell *targetCell = targetCells->data[j]; // Target chip of interest
+                pmCell *sourceCell = sourceCells->data[j]; // Source chip of interest
+                if (! targetCell || ! sourceCell) {
+                    continue;
+                }
+                targetCell->concepts = psMetadataCopy(targetCell->concepts, sourceCell->concepts);
+            }
         }
-    }
-
-    return true;
-}
+
+        return true;
+    }
Index: /branches/rel10_ifa/psModules/src/astrom/pmConcepts.h
===================================================================
--- /branches/rel10_ifa/psModules/src/astrom/pmConcepts.h	(revision 6617)
+++ /branches/rel10_ifa/psModules/src/astrom/pmConcepts.h	(revision 6618)
@@ -65,8 +65,10 @@
 bool pmConceptsReadChip(pmChip *chip,   // Chip for which to read concepts
                         pmConceptSource source, // Source for concepts
+                        bool propagate, // Propagate to higher levels as well?
                         psDB *db        // Database handle
                        );
 bool pmConceptsWriteChip(pmChip *chip,  // Chip for which to write concepts
                          pmConceptSource source, // Source for concepts
+                         bool propagate,// Propagate to higher levels as well?
                          psDB *db       // Database handle
                         );
@@ -75,8 +77,10 @@
 bool pmConceptsReadCell(pmCell *cell,   // Cell for which to read concepts
                         pmConceptSource source, // Source for concepts
+                        bool propagate, // Propagate to higher levels as well?
                         psDB *db        // Database handle
                        );
 bool pmConceptsWriteCell(pmCell *cell,  // FPA for which to write concepts
                          pmConceptSource source, // Source for concepts
+                         bool propagate,// Propagate to higher levels as well?
                          psDB *db       // Database handle
                         );
Index: /branches/rel10_ifa/psModules/src/astrom/pmConceptsRead.c
===================================================================
--- /branches/rel10_ifa/psModules/src/astrom/pmConceptsRead.c	(revision 6617)
+++ /branches/rel10_ifa/psModules/src/astrom/pmConceptsRead.c	(revision 6618)
@@ -2,4 +2,5 @@
 
 #include "pslib.h"
+#include "psMetadataItemParse.h"
 
 #include "pmFPA.h"
@@ -12,88 +13,4 @@
 // File-static functions
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-
-static float parseF32(psMetadataItem *item
-                     )
-{
-    switch (item->type) {
-    case PS_DATA_F32:
-        return item->data.F32;
-    case PS_DATA_F64:
-        // Assume it's OK to truncate to floating point from double
-        return (float)item->data.F64;
-    case PS_DATA_S32:
-        // Promote to float
-        return (float)item->data.S32;
-    default:
-        psError(PS_ERR_IO, true, "Concept %s (%s) is not of floating point type (%x) --- treating as NaN.\n",
-                item->name, item->comment, item->type);
-        return NAN;
-    }
-}
-
-static double parseF64(psMetadataItem *item
-                      )
-{
-    switch (item->type) {
-    case PS_TYPE_F64:
-        return item->data.F64;
-    case PS_TYPE_F32:
-        // Promote to double
-        return (double)item->data.F32;
-    case PS_TYPE_S32:
-        // Promote to double
-        return (double)item->data.S32;
-    default:
-        psError(PS_ERR_IO, true, "Concept %s (%s) is not of double-precision floating point type (%x) "
-                "--- treating as NaN.\n", item->name, item->comment, item->type);
-        return NAN;
-    }
-}
-
-static int parseS32(psMetadataItem *item
-                   )
-{
-    switch (item->type) {
-    case PS_TYPE_S32:
-        return item->data.S32;
-    case PS_TYPE_F32:
-        psLogMsg(__func__, PS_LOG_WARN, "Concept %s (%s) should be S32, but is F32 --- converting.\n",
-                 item->name, comment);
-        return (int)item->data.F32;
-    case PS_TYPE_F64:
-        psLogMsg(__func__, PS_LOG_WARN, "Concept %s (%s) should be S32, but is F64 --- converting.\n",
-                 item->name, comment);
-        return (int)item->data.F64;
-    default:
-        psError(PS_ERR_IO, true, "Concept %s (%s) is not of integer type (%x) --- treating as zero.\n",
-                item->name, item->comment, item->type);
-        return 0;
-    }
-}
-
-static psString parseString(psMetadataItem *item
-                           )
-{
-    switch (item->type) {
-    case PS_DATA_STRING:
-        return psMemIncrRefCounter(item->data.V);
-    case PS_DATA_F32: {
-            psString value = NULL;    // String to return
-            psStringAppend(&value, "%f", item->data.F32);
-            return value;
-        }
-    case PS_DATA_S32: {
-            psString value = NULL;    // String to return
-            psStringAppend(&value, "%d", item->data.S32);
-            return value;
-        }
-    default:
-        psError(PS_ERR_IO, true, "Concept %s (%s) is not of string type (%x) --- treating as "
-                "undefined.\n", item->name, item->comment, item->type);
-        return psStringCopy("");
-    }
-}
-
 
 // This function gets called for the really boring concepts --- where all you have to do is parse from a
@@ -106,5 +23,6 @@
     switch (pattern->type) {
     case PS_DATA_STRING: {
-            psString string = parseString(concept); // Get the string, so I can free it after it goes on the MDI
+            psString string = psMetadataItemParseString(concept); // Get the string, so I can free it after it
+            // goes on the MetadataItem
             psMetadataItem *item = psMetadataItemAllocStr(pattern->name, pattern->comment, string);
             psFree(string);
@@ -112,9 +30,9 @@
         }
     case PS_DATA_S32:
-        return psMetadataItemAllocS32(pattern->name, pattern->comment, parseS32(concept));
+        return psMetadataItemAllocS32(pattern->name, pattern->comment, psMetadataItemParseS32(concept));
     case PS_DATA_F32:
-        return psMetadataItemAllocF32(pattern->name, pattern->comment, parseF32(concept));
+        return psMetadataItemAllocF32(pattern->name, pattern->comment, psMetadataItemParseF32(concept));
     case PS_DATA_F64:
-        return psMetadataItemAllocF64(pattern->name, pattern->comment, parseF64(concept));
+        return psMetadataItemAllocF64(pattern->name, pattern->comment, psMetadataItemParseF64(concept));
     default:
         psLogMsg(__func__, PS_LOG_WARN, "Concept %s (%s) is not of a standard type (%x)\n",
Index: /branches/rel10_ifa/psModules/src/astrom/pmFPAConstruct.c
===================================================================
--- /branches/rel10_ifa/psModules/src/astrom/pmFPAConstruct.c	(revision 6617)
+++ /branches/rel10_ifa/psModules/src/astrom/pmFPAConstruct.c	(revision 6618)
@@ -1,9 +1,12 @@
 #include <stdio.h>
-#include <strings.h>
+#include <assert.h>
+#include <string.h>
 #include "pslib.h"
+#include "psMetadataItemParse.h"
 
 #include "pmFPA.h"
 #include "pmConcepts.h"
 #include "pmFPAConstruct.h"
+#include "pmFPAview.h"
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -31,8 +34,8 @@
 }
 
-// Find a chip by name
-static pmChip *findChip(pmFPA *fpa, // FPA in which to find the chip
-                        const char *name // Name of the chip
-                       )
+// Find a chip by name; return the index
+static int findChip(pmFPA *fpa, // FPA in which to find the chip
+                    const char *name // Name of the chip
+                   )
 {
     psArray *chips = fpa->chips;    // Array of chips
@@ -41,16 +44,16 @@
         psString testName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME"); // Name of this chip
         if (strcmp(name, testName) == 0) {
-            return chip;
+            return i;
         }
     }
 
     psError(PS_ERR_IO, true, "Unable to find chip %s\n", name);
-    return NULL;
-}
-
-// Find a cell by name
-static pmCell *findCell(pmChip *chip, // Chip in which to find the cell
-                        const char *name // Name of the cell
-                       )
+    return -1;
+}
+
+// Find a cell by name; return the index
+static int findCell(pmChip *chip, // Chip in which to find the cell
+                    const char *name // Name of the cell
+                   )
 {
     psArray *cells = chip->cells;    // Array of cells
@@ -59,10 +62,10 @@
         psString testName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME"); // Name of this cell
         if (strcmp(name, testName) == 0) {
-            return cell;
+            return i;
         }
     }
 
     psError(PS_ERR_IO, true, "Unable to find cell %s\n", name);
-    return NULL;
+    return -1;
 }
 
@@ -100,22 +103,23 @@
 }
 
-// Get the chip name from a primary header lookup
-static psString chipNameFromHeader(const psMetadata *format, // FORMAT within the camera format description
-                                   const psMetadata *header // Primary header
-                                  )
+// Get the name of a PHU chip or cell from the header
+static psString phuNameFromHeader(const char *name, // The name to lookup: "CELL.NAME" or "CHIP.NAME"
+                                  const psMetadata *format, // FORMAT within the camera format description
+                                  const psMetadata *header // Primary header
+                                 )
 {
     bool mdok = true;                   // Result of MD lookup
-    psString chipNameHeader = psMetadataLookupStr(&mdok, format, "CHIP.NAME");
-    if (!mdok || strlen(chipNameHeader) == 0) {
-        psError(PS_ERR_IO, false, "Unable to find CHIP.NAME in the FORMAT.\n");
+    psString keyword = psMetadataLookupStr(&mdok, format, name);
+    if (!mdok || strlen(keyword) == 0) {
+        psError(PS_ERR_IO, false, "Unable to find %s in the FORMAT.\n", name);
         return false;
     }
-    psString chipName = psMetadataLookupStr(&mdok, header, chipNameHeader);
-    if (!chipName || strlen(chipName) == 0) {
-        psError(PS_ERR_IO, false, "Unable to find %s in primary header to identify chip name.\n", chipName);
+    psString result = psMetadataLookupStr(&mdok, header, keyword);
+    if (!mdok || strlen(result) == 0) {
+        psError(PS_ERR_IO, false, "Unable to find %s in primary header to identify %s.\n", keyword, name);
         return NULL;
     }
 
-    return chipName;
+    return result;
 }
 
@@ -159,18 +163,16 @@
 
 // Add an input file to the FPA
-bool pmFPAAddSource(pmFPA *fpa,         // FPA to which to add
-                    pmChip *chip,       // Chip to which to add, or NULL
-                    pmCell *cell,       // Cell to which to add, or NULL
-                    psMetadata *phu,    // Primary header of file
-                    psMetadata *format // Format of file
-                   )
-{
-    // Just in case someone's playing silly billy...
-    if (cell && !chip) {
-        chip = cell->parent;
-    }
-    if (chip && !fpa) {
-        fpa = chip->parent;
-    }
+pmFPAview *pmFPAAddSource(pmFPA *fpa, // The FPA
+                          psMetadata *phu, // Primary header of file
+                          psMetadata *format // Format of file
+                         )
+{
+    assert(fpa);
+    assert(phu);
+    assert(format);
+
+    pmChip *chip = NULL;                // The chip, if any one in particular
+    pmCell *cell = NULL;                // The cell, if any one in particular
+    psString phuChipName = NULL;        // Name of the chip from the PHU
 
     bool mdok = true;                   // Status from metadata lookups
@@ -178,6 +180,24 @@
     if (!mdok || !formatSpec) {
         psError(PS_ERR_IO, false, "Unable to find FORMAT in the camera format configuration.\n");
-        return false;
-    }
+        return NULL;
+    }
+
+    // Check the name of the FPA
+    const char *nameKeyword = psMetadataLookupStr(&mdok, formatSpec, "FPA.NAME"); // Keyword that gives name
+    if (!mdok || strlen(nameKeyword) == 0) {
+        psError(PS_ERR_IO, false, "Unable to find FPA.NAME in the format specification.\n");
+        return NULL;
+    }
+    psMetadataItem *fpaNameItem = psMetadataLookup(phu, nameKeyword); // Name of the FPA
+    const char *newFPAname = psMetadataItemParseString(fpaNameItem); // FPA.NAME for new source
+    fpaNameItem = psMetadataLookup(fpa->concepts, "FPA.NAME"); // Current name of FPA
+    if (fpaNameItem && fpaNameItem->type == PS_DATA_STRING) {
+        const char *currentFPAname = fpaNameItem->data.V; // Name of the current FPA
+        if (currentFPAname && strlen(currentFPAname) > 0 && strcmp(currentFPAname, newFPAname) != 0) {
+            psLogMsg(__func__, PS_LOG_WARN, "FPA.NAME for new source (%s) doesn't match FPA.NAME for current "
+                     "fpa (%s).\n", newFPAname, currentFPAname);
+        }
+    }
+    psMetadataAddStr(fpa->concepts, PS_LIST_HEAD, "FPA.NAME", PS_META_REPLACE, "Name of FPA", newFPAname);
 
     // Where does the PHU go?
@@ -185,19 +205,77 @@
     if (!mdok || strlen(phuType) == 0) {
         psError(PS_ERR_IO, false, "Unable to find PHU in the format specification.\n");
-        return false;
+        return NULL;
     }
     pmHDU *phdu = pmHDUAlloc("PHU");    // The primary header data unit
     phdu->header = psMemIncrRefCounter(phu);
     phdu->format = psMemIncrRefCounter(format);
-    if (fpa && !fpa->hdu && strcasecmp(phuType, "FPA") == 0) {
+
+    // Generate the view
+    pmFPAview *view = pmFPAviewAlloc(0); // The FPA view corresponding to the PHU, to be returned
+    view->chip = -1;
+    view->cell = -1;
+    view->readout = -1;
+    view->fpa = psMemIncrRefCounter(fpa);
+    if (strcasecmp(phuType, "FPA") == 0) {
+        if (fpa->hdu) {
+            // Something's already here
+            psError(PS_ERR_IO, true, "Unable to add source since FPA already has a PHU.\n");
+            psFree(view);
+            return NULL;
+        }
         fpa->hdu = phdu;
         pmConceptsReadFPA(fpa, PM_CONCEPT_SOURCE_HEADER, NULL);
-    } else if (chip && !chip->hdu && strcasecmp(phuType, "CHIP") == 0) {
-        chip->hdu = phdu;
-        pmConceptsReadChip(chip, PM_CONCEPT_SOURCE_HEADER, NULL);
-    } else if (cell && !cell->hdu && strcasecmp(phuType, "CELL") == 0) {
-        // cell->hdu = phdu;
-        psError(PS_ERR_IO, true, "The case of PHU == CELL has not been written yet!\n");
-        return false;
+    } else {
+        // Get the chip
+        phuChipName = phuNameFromHeader("CHIP.NAME", formatSpec, phu); // Name of the chip
+        int chipNum = findChip(fpa, phuChipName); // Chip number
+        if (chipNum == -1) {
+            psError(PS_ERR_IO, true, "Unable to find chip %s in FPA.\n", phuChipName);
+            psFree(view);
+            return NULL;
+        }
+        chip = fpa->chips->data[chipNum];
+        view->chip = chipNum;
+
+        if (strcasecmp(phuType, "CHIP") == 0) {
+            if (chip->hdu) {
+                // Something's already here
+                psError(PS_ERR_IO, true, "Unable to add source since chip already has a PHU.\n");
+                psFree (view);
+                return NULL;
+            }
+            chip->hdu = phdu;
+            pmConceptsReadChip(chip, PM_CONCEPT_SOURCE_HEADER, true, NULL);
+        } else if (strcasecmp(phuType, "CELL") == 0) {
+            psError(PS_ERR_IO, true, "Not yet sure how to handle PHU=CELL.\n");
+            psFree(view);
+            return NULL;
+            #if 0
+            // Added this for possible future plans which might allow PHU=CELL
+            phuCellName = phuNameFromHeader("CELL.NAME", formatSpec, phu); // Name of the cell
+            int cellNum = findCell(chip, phuCellName); // Cell number
+            if (cellNum == -1) {
+                psError(PS_ERR_IO, true, "Unable to find cell %s in chip %s.\n", phuCellName, phuChipName);
+                psFree(view);
+                return NULL;
+            }
+            cell = chip->cells->data[cellNum];
+            view->cell = cellNum;
+
+            if (cell->hdu) {
+                // Something's already here
+                psError(PS_ERR_IO, true, "Unable to add source since cell already has a PHU.\n");
+                psFree(view);
+                return NULL;
+            }
+            cell->hdu = phdu;
+            pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_HEADER, true, NULL);
+            #endif
+
+        } else {
+            psError(PS_ERR_IO, true, "The format of the PHU (%s) is not FPA, CHIP or CELL.\n", phuType);
+            psFree(view);
+            return NULL;
+        }
     }
 
@@ -206,32 +284,35 @@
     if (!mdok || strlen(extType) == 0) {
         psError(PS_ERR_IO, false, "Unable to find EXTENSIONS in the format specification.\n");
-        return false;
+        psFree(view);
+        return NULL;
     }
     psMetadata *contents = psMetadataLookupMD(&mdok, format, "CONTENTS"); // The contents of the FITS file
     if (!mdok || !contents) {
         psError(PS_ERR_IO, false, "Unable to find CONTENTS in the camera format configuration.\n");
-        return false;
-    }
-
+        psFree(view);
+        return NULL;
+    }
+
+
+    // Now, different actions, according to what's in the file:
+
+    // No extensions --- it's all in the PHU
     if (strcasecmp(extType, "NONE") == 0) {
-        // No extensions --- it's all in the PHU
-        psString chipType = chipNameFromHeader(formatSpec, phu); // Type of chip
-        printf("chipType: %s<---\n", chipType);
-        #if 1                                   // This is here for the courtesy of MegaCam, which has "CCD13   "
-
-        char *space = NULL;             // Position of a space
-        if ((space = strchr(chipType, ' '))) {
-            psString temp = psStringNCopy(chipType, strlen(chipType) - strlen(space));
-            // Free memory???
-            chipType = temp;
-        }
-        #endif
-
-        psString content = psMetadataLookupStr(&mdok, contents, chipType); // The content line
+        if (strcasecmp(phuType, "FPA") == 0) {
+            psError(PS_ERR_IO, true, "Not sure how to handle the case PHU=FPA, EXTENSIONS=NONE.\n");
+            psFree(view);
+            return NULL;
+        }
+        if (strcasecmp(phuType, "CELL") == 0) {
+            psError(PS_ERR_IO, true, "Not sure how to handle the case PHU=CELL, EXTENSIONS=NONE.\n");
+            psFree(view);
+            return NULL;
+        }
+
+        psString content = psMetadataLookupStr(&mdok, contents, phuChipName); // The content line
         if (!mdok || strlen(content) == 0) {
-            psError(PS_ERR_IO, true, "Cannot find chip %s in the list of CONTENTS.\n");
-            psFree(chipType);
-            psFree(content);
-            return false;
+            psError(PS_ERR_IO, true, "Cannot find chip %s in the list of CONTENTS.\n", phuChipName);
+            psFree(view);
+            return NULL;
         }
 
@@ -241,20 +322,28 @@
         parsePairs(&names, &values, content);
         for (int i = 0; i < names->n; i++) {
-            psString chipName = names->data[i]; // The name of the chip
-            psString cellName = values->data[i]; // The name of the cell
-            pmChip *chip = findChip(fpa, chipName); // The chip we're looking for
-            if (!chip) {
-                psLogMsg(__func__, PS_LOG_WARN, "Unable to find chip %s in fpa --- ignored.\n", chipName);
+            psString chipName = names->data[i]; // The name of the chip specified
+            if (phuChipName && strcmp(phuChipName, chipName) != 0) {
+                psLogMsg(__func__, PS_LOG_WARN, "Chip name in CONTENT (%s) does not match chip name from PHU "
+                         "(%s) --- there may be problems when reading!\n", chipName, phuChipName);
+                // Find the other chip
+                int chipNum = findChip(fpa, chipName); // The chip we're looking for
+                if (chipNum == -1) {
+                    psLogMsg(__func__, PS_LOG_WARN, "Unable to find chip %s in fpa --- ignored.\n", chipName);
+                    continue;
+                }
+                chip = fpa->chips->data[chipNum];
+            }
+            psString cellName = values->data[i]; // The name of the cell specified
+            int cellNum = findCell(chip, cellName); // The cell we're looking for
+            if (cellNum == -1) {
+                psLogMsg(__func__, PS_LOG_WARN, "Unable to find cell %s in chip %s --- ignored.\n",
+                         cellName, chipName);
                 continue;
             }
-            pmCell *cell = findCell(chip, cellName); // The cell we're looking for
-            if (!cell) {
-                psLogMsg(__func__, PS_LOG_WARN, "Unable to find cell %s in chip %s --- ignored.\n", cellName,
-                         chipName);
-                continue;
-            }
+            cell = chip->cells->data[cellNum];
+
             psMetadata *cellData = getCellData(format, cellName); // Data for this cell
 
-            // Have already plugged in the PHU
+            // Have already plugged in the PHU, which is all we have
 
             // Put in the cell data
@@ -265,11 +354,12 @@
             }
             cell->config = psMemIncrRefCounter(cellData);
-            pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_CAMERA | PM_CONCEPT_SOURCE_DEFAULTS, NULL);
-        }
-        return true;
-    }
-
-
-    // Go through the contents
+            pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_CAMERA | PM_CONCEPT_SOURCE_DEFAULTS, false, NULL);
+        }
+
+        return view;
+    }
+
+
+    // Otherwise, go through the contents
     psMetadataIterator *contentsIter = psMetadataIteratorAlloc(contents, PS_LIST_HEAD, NULL);
     psMetadataItem *contentsItem = NULL; // Item from contents
@@ -295,15 +385,17 @@
                 psString chipName = names->data[i]; // The name of the chip
                 psString cellName = values->data[i]; // The name of the cell
-                pmChip *chip = findChip(fpa, chipName); // The chip we're looking for
-                if (!chip) {
+                int chipNum = findChip(fpa, chipName); // The chip we're looking for
+                if (chipNum == -1) {
                     psLogMsg(__func__, PS_LOG_WARN, "Unable to find chip %s in FPA --- ignored.\n", chipName);
                     continue;
                 }
-                pmCell *cell = findCell(chip, cellName); // The cell we're looking for
-                if (!cell) {
+                chip = fpa->chips->data[chipNum];
+                int cellNum = findCell(chip, cellName); // The cell we're looking for
+                if (cellNum == -1) {
                     psLogMsg(__func__, PS_LOG_WARN, "Unable to find cell %s in chip %s --- ignored\n",
                              cellName, chipName);
                     continue;
                 }
+                pmCell *cell = chip->cells->data[cellNum];
                 psMetadata *cellData = getCellData(format, cellName); // Data for this cell
 
@@ -322,5 +414,5 @@
                 }
                 cell->config = psMemIncrRefCounter(cellData);
-                pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_CAMERA | PM_CONCEPT_SOURCE_DEFAULTS, NULL);
+                pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_CAMERA | PM_CONCEPT_SOURCE_DEFAULTS, false, NULL);
             }
 
@@ -331,10 +423,11 @@
             if (! fpa->hdu->phu) {
                 // Need to look up the name of the chip
-                chipName = chipNameFromHeader(formatSpec, phu);
-                chip = findChip(fpa, chipName);
-                if (!chip) {
+                chipName = phuNameFromHeader("CHIP.NAME", formatSpec, phu);
+                int chipNum = findChip(fpa, chipName);
+                if (chipNum == -1) {
                     psLogMsg(__func__, PS_LOG_WARN, "Unable to find chip %s in FPA --- ignored.\n", chipName);
                     continue;
                 }
+                chip = fpa->chips->data[chipNum];
             }
             for (int i = 0; i < names->n; i++) {
@@ -345,20 +438,22 @@
                     chipName = names->data[i];
                     cellName = cellType;
-                    chip = findChip(fpa, chipName);
-                    if (! chip) {
+                    int chipNum = findChip(fpa, chipName); // The chip we're looking for
+                    if (chipNum == -1) {
                         psLogMsg(__func__, PS_LOG_WARN, "Unable to find chip %s in FPA --- ignored.\n",
                                  chipName);
                         continue;
                     }
+                    chip = fpa->chips->data[chipNum];
                 } else {
                     // We've got cellName:cellType and the chipName comes from before
                     cellName = names->data[i];
                 }
-                pmCell *cell = findCell(chip, cellName); // The cell we're looking for
-                if (!cell) {
+                int cellNum = findCell(chip, cellName); // The cell we're looking for
+                if (cellNum == -1) {
                     psLogMsg(__func__, PS_LOG_WARN, "Unable to find cell %s in chip %s --- ignored.\n",
                              cellName, chipName);
                     continue;
                 }
+                pmCell *cell = chip->cells->data[cellNum];
                 psMetadata *cellData = getCellData(format, cellType); // Data for this cell
 
@@ -378,5 +473,5 @@
                 }
                 cell->config = psMemIncrRefCounter(cellData);
-                pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_CAMERA | PM_CONCEPT_SOURCE_DEFAULTS, NULL);
+                pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_CAMERA | PM_CONCEPT_SOURCE_DEFAULTS, false, NULL);
             }
 
@@ -390,5 +485,5 @@
     pmConceptsReadFPA(fpa, PM_CONCEPT_SOURCE_DEFAULTS, NULL);
 
-    return true;
+    return view;
 }
 
@@ -663,7 +758,9 @@
                 pmReadout *readout = readouts->data[k]; // The readout
                 psImage *image = readout->image; // The image
-                psTrace(__func__, 8, "Image: [%d:%d,%d:%d] (%dx%d)\n", image->col0, image->col0 +
-                        image->numCols, image->row0, image->row0 + image->numRows, image->numCols,
-                        image->numRows);
+                if (image) {
+                    psTrace(__func__, 8, "Image: [%d:%d,%d:%d] (%dx%d)\n", image->col0, image->col0 +
+                            image->numCols, image->row0, image->row0 + image->numRows, image->numCols,
+                            image->numRows);
+                }
             } // Iterating over cell
         } // Iterating over chip
Index: /branches/rel10_ifa/psModules/src/astrom/pmFPAConstruct.h
===================================================================
--- /branches/rel10_ifa/psModules/src/astrom/pmFPAConstruct.h	(revision 6617)
+++ /branches/rel10_ifa/psModules/src/astrom/pmFPAConstruct.h	(revision 6618)
@@ -4,4 +4,5 @@
 #include "pslib.h"
 #include "pmFPA.h"
+#include "pmFPAview.h"
 
 // Construct an FPA instance on the basis of a camera configuration
@@ -9,11 +10,8 @@
                      );
 
-// Add an input file to the FPA
-bool pmFPAAddSource(pmFPA *fpa,         // FPA to which to add
-                    pmChip *chip,       // Chip to which to add, or NULL
-                    pmCell *cell,       // Cell to which to add, or NULL
-                    psMetadata *phu,    // Primary header of file
-                    psMetadata *format  // Format of file
-                   );
+pmFPAview *pmFPAAddSource(pmFPA *fpa, // The FPA
+                          psMetadata *phu, // Primary header of file
+                          psMetadata *format // Format of file
+                         );
 
 // Print out the FPA
Index: /branches/rel10_ifa/psModules/src/astrom/pmFPARead.c
===================================================================
--- /branches/rel10_ifa/psModules/src/astrom/pmFPARead.c	(revision 6617)
+++ /branches/rel10_ifa/psModules/src/astrom/pmFPARead.c	(revision 6618)
@@ -137,4 +137,8 @@
 #endif
 
+static bool badRegion(const psRegion *region)
+{
+    return isnan(region->x0) || isnan(region->x1) || isnan(region->y0) || isnan(region->y1);
+}
 
 // Carve a readout from the image pixels
@@ -148,4 +152,11 @@
 
     // The image corresponding to the trim region
+    if (badRegion(trimsec)) {
+        psString regionString = psRegionToString(*trimsec);
+        psError(PS_ERR_IO, true, "Invalid trim section: %s\n", regionString);
+        psFree(regionString);
+        psFree(readout);
+        return NULL;
+    }
     readout->image = psMemIncrRefCounter(psImageSubset(image, *trimsec));
 
@@ -154,4 +165,11 @@
     psRegion *biassec = NULL;       // A BIASSEC region from the list
     while ((biassec = psListGetAndIncrement(iter))) {
+        if (badRegion(trimsec)) {
+            psString regionString = psRegionToString(*biassec);
+            psError(PS_ERR_IO, true, "Invalid bias section: %s\n", regionString);
+            psFree(regionString);
+            psFree(readout);
+            return NULL;
+        }
         psImage *overscan = psMemIncrRefCounter(psImageSubset(image, *biassec));
         psListAdd(readout->bias, PS_LIST_TAIL, overscan);
@@ -192,5 +210,5 @@
     }
 
-    pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_HEADER, NULL);
+    pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_HEADER, false, NULL);
 
     // Having read the cell, we now have to cut it up
@@ -216,15 +234,15 @@
                )
 {
+    bool success = false;               // Were we able to read at least one HDU?
     psArray *cells = chip->cells;       // Array of cells
     for (int i = 0; i < cells->n; i++) {
         pmCell *cell = cells->data[i];  // The cell of interest
-        if (!pmCellRead(cell, fits, db)) {
-            psError(PS_ERR_IO, false, "Unable to read cell %d.\n", i);
-            return false;
-        }
-        pmConceptsReadChip(chip, PM_CONCEPT_SOURCE_HEADER, NULL);
-    }
-
-    return true;
+        success |= pmCellRead(cell, fits, db);
+    }
+    if (success) {
+        pmConceptsReadChip(chip, PM_CONCEPT_SOURCE_HEADER, false, NULL);
+    }
+
+    return success;
 }
 
@@ -235,15 +253,17 @@
               )
 {
-    psArray *chips = fpa->chips;       // Array of chips
+    bool success = false;               // Were we able to read at least one HDU?
+    psArray *chips = fpa->chips;        // Array of chips
     for (int i = 0; i < chips->n; i++) {
         pmChip *chip = chips->data[i];  // The cell of interest
-        if (!pmChipRead(chip, fits, db)) {
-            psError(PS_ERR_IO, false, "Unable to read chip %d.\n", i);
-            return false;
-        }
+        success |= pmChipRead(chip, fits, db);
+    }
+    if (success) {
         pmConceptsReadFPA(fpa, PM_CONCEPT_SOURCE_HEADER, NULL);
-    }
-
-    return true;
+    } else {
+        psLogMsg(__func__, PS_LOG_WARN, "Unable to read any chips in FPA.\n");
+    }
+
+    return success;
 }
 
Index: /branches/rel10_ifa/psModules/src/astrom/pmFPAview.h
===================================================================
--- /branches/rel10_ifa/psModules/src/astrom/pmFPAview.h	(revision 6617)
+++ /branches/rel10_ifa/psModules/src/astrom/pmFPAview.h	(revision 6618)
@@ -7,6 +7,6 @@
 *  @author EAM, IfA
 *
-*  @version $Revision: 1.1.2.5 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2006-03-14 04:40:37 $
+*  @version $Revision: 1.1.2.6 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2006-03-17 01:47:44 $
 *
 *  Copyright 2004-2005 Institute for Astronomy, University of Hawaii
@@ -32,9 +32,9 @@
 typedef struct
 {
-    int chip;
-    int cell;
-    int readout;
-    int nRows;
-    int iRows;
+    int chip;                           // Number of the chip, or -1 for all
+    int cell;                           // Number of the cell, or -1 for all
+    int readout;                        // Number of the readout, or -1 for all
+    int nRows;                          // Maximum number of rows per readout segment read, or 0 for all
+    int iRows;                          // Starting point for this read
     pmFPA *fpa;
 }
