Index: /branches/rel10_ifa/psModules/src/astrom/pmConcepts.c
===================================================================
--- /branches/rel10_ifa/psModules/src/astrom/pmConcepts.c	(revision 6733)
+++ /branches/rel10_ifa/psModules/src/astrom/pmConcepts.c	(revision 6734)
@@ -76,5 +76,5 @@
 
 // Set all registered concepts to blank value for the specified level
-static bool conceptsBlank(psMetadata **specs,  // One of the concepts specifications
+static bool conceptsBlank(psMetadata **specs, // One of the concepts specifications
                           psMetadata *target // Place to install the concepts
                          )
@@ -89,5 +89,14 @@
         pmConceptSpec *spec = specItem->data.V; // The specification
         psMetadataItem *blank = spec->blank; // The concept
-        psMetadataAddItem(target, blank, PS_LIST_TAIL, PS_META_REPLACE);
+        psMetadataItem *copy = NULL;    // Copy of the blank concept
+        // Trap the lists, which can't be copied in the ordinary way without a warning
+        if (blank->type == PS_DATA_LIST) {
+            copy = psMetadataItemAlloc(blank->name, PS_DATA_LIST, blank->comment, blank->data.V);
+        } else {
+            copy = psMetadataItemCopy(blank);
+        }
+        if (!psMetadataAddItem(target, copy, PS_LIST_TAIL, PS_META_REPLACE)) {
+            psLogMsg(__func__, PS_LOG_WARN, "Unable to add blank version of concept %s\n", blank->name);
+        }
     }
     psFree(specsIter);
@@ -158,394 +167,401 @@
 }
 
-#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
-        }
-        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);
+// 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
+    }
+    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_REGION,
+                                          "Trim section", trimsec);
+            psFree(trimsec);
+            pmConceptRegister(cellTrimsec, (pmConceptParseFunc)pmConceptParse_CELL_TRIMSEC,
+                              #if 0
+                              (pmConceptFormatFunc)pmConceptFormat_CELL_TRIMSEC,
+                              #else
+                              NULL,
+                              #endif
+                              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);
             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) {
+        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;
             }
-            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;
-    }
+            targetCell->concepts = psMetadataCopy(targetCell->concepts, sourceCell->concepts);
+        }
+    }
+
+    return true;
+}
Index: /branches/rel10_ifa/psModules/src/astrom/pmConceptsStandard.c
===================================================================
--- /branches/rel10_ifa/psModules/src/astrom/pmConceptsStandard.c	(revision 6733)
+++ /branches/rel10_ifa/psModules/src/astrom/pmConceptsStandard.c	(revision 6734)
@@ -156,5 +156,5 @@
     }
 
-    psMetadataItem *item = psMetadataItemAllocPtr(pattern->name, PS_DATA_UNKNOWN, pattern->comment, trimsec);
+    psMetadataItem *item = psMetadataItemAllocPtr(pattern->name, PS_DATA_REGION, pattern->comment, trimsec);
     psFree(trimsec);
     return item;
@@ -434,5 +434,5 @@
 
 
-
+#if 0
 psMetadataItem *pmConceptFormat_CELL_TRIMSEC(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
 {
@@ -446,29 +446,25 @@
     return formatted;
 }
-
-
-bool pmConceptFormat_CELL_BIASSEC(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
-{
+#endif
+
+psMetadataItem *pmConceptFormat_CELL_BIASSEC(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
+{
+    // Return a metadata item containing a list of metadata items of region strings
     psList *biassecs = concept->data.V; // The biassecs region list
-    psString biassecString = NULL;      // String containing the biassecs
     psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false); // Iterator
     psRegion *region = NULL;            // Region from iteration
-    bool first = true;                  // Are we on the first one?
+    psList *new = psListAlloc(NULL);    // New list containing metadatas
     while ((region = psListGetAndIncrement(biassecsIter))) {
         psString regionString = psRegionToString(*region); // The string region "[x0:x1,y0:y1]"
-        if (first) {
-            psStringAppend(&biassecString, "%s", regionString);
-            first = false;
-        } else {
-            psStringAppend(&biassecString, ";%s", regionString); // Put in a semi-colon
-        }
-        psFree(regionString);
+        psMetadataItem *item = psMetadataItemAllocStr(concept->name, concept->comment, regionString);
+        psFree(regionString);           // Drop reference
+        psListAdd(new, PS_LIST_TAIL, item);
+        psFree(item);                   // Drop reference
     }
     psFree(biassecsIter);
-    psMetadataItem *formatted = psMetadataItemAllocStr(concept->name, concept->comment, biassecString);
-    psFree(biassecString);              // Drop reference
+    psMetadataItem *formatted = psMetadataItemAllocPtr(concept->name, PS_DATA_LIST, concept->comment, new);
+    psFree(new);                        // Drop reference
     return formatted;
 }
-
 
 // This function actually does both CELL.XBIN and CELL.YBIN if CELL.XBIN and CELL.YBIN are specified by the
Index: /branches/rel10_ifa/psModules/src/astrom/pmConceptsStandard.h
===================================================================
--- /branches/rel10_ifa/psModules/src/astrom/pmConceptsStandard.h	(revision 6733)
+++ /branches/rel10_ifa/psModules/src/astrom/pmConceptsStandard.h	(revision 6734)
@@ -14,6 +14,8 @@
 psMetadataItem *pmConceptParse_CELL_TIME(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
 psMetadataItem *pmConceptParse_CELL_Positions(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
+#if 0
 psMetadataItem *pmConceptFormat_CELL_TRIMSEC(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
-bool pmConceptFormat_CELL_BIASSEC(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
+#endif
+psMetadataItem *pmConceptFormat_CELL_BIASSEC(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
 psMetadataItem *pmConceptFormat_CELL_XBIN(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
 psMetadataItem *pmConceptFormat_CELL_YBIN(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
Index: /branches/rel10_ifa/psModules/src/astrom/pmConceptsWrite.c
===================================================================
--- /branches/rel10_ifa/psModules/src/astrom/pmConceptsWrite.c	(revision 6733)
+++ /branches/rel10_ifa/psModules/src/astrom/pmConceptsWrite.c	(revision 6734)
@@ -95,8 +95,9 @@
 }
 
-static bool writeHeader(pmHDU *hdu,     // HDU for which to add to the header
-                        const char *keyword, // Keyword to add
-                        psMetadataItem *item // Item to add to the header
-                       )
+// Write a single value to a header
+static bool writeSingleHeader(pmHDU *hdu, // HDU for which to add to the header
+                              const char *keyword, // Keyword to add
+                              psMetadataItem *item // Item to add to the header
+                             )
 {
     switch (item->type) {
@@ -113,4 +114,11 @@
         return psMetadataAddF64(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, item->comment,
                                 item->data.F64);
+    case PS_DATA_REGION: {
+            psString region = psRegionToString(*(psRegion*)item->data.V);
+            bool result = psMetadataAddStr(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, item->comment,
+                                           region);
+            psFree(region);
+            return result;
+        }
     default:
         psLogMsg(__func__, PS_LOG_WARN, "Type of %s is not suitable for a FITS header --- not added.\n",
@@ -118,4 +126,35 @@
         return false;
     }
+}
+
+
+// Write potentially multiple values to a header
+static bool writeHeader(pmHDU *hdu,     // HDU for which to add to the header
+                        const char *keywords, // Keywords to add
+                        psMetadataItem *item // Item to add to the header
+                       )
+{
+    bool status = true;                 // Status of writing headers, to be returned
+    if (item->type == PS_DATA_LIST) {
+        psList *values = item->data.V;  // List of outputs
+        psList *keys = psStringSplit(keywords, " ,;"); // List of keywords
+        if (keys->n != values->n) {
+            psLogMsg(__func__, PS_LOG_WARN, "Number of keywords (%d) does not match number of values (%d).\n",
+                     keys->n, values->n);
+        }
+        psListIterator *keysIter = psListIteratorAlloc(keys, PS_LIST_HEAD, false); // Iterator for keywords
+        psListIterator *valuesIter = psListIteratorAlloc(values, PS_LIST_HEAD, false); // Iterator for values
+        psString key = NULL;            // Keyword from iteration
+        psMetadataItem *value = NULL;   // Value from iteration
+        while ((key = psListGetAndIncrement(keysIter)) && (value = psListGetAndIncrement(valuesIter))) {
+            status |= writeSingleHeader(hdu, key, value);
+        }
+        psFree(keysIter);
+        psFree(valuesIter);
+        psFree(keys);
+    } else {
+        status = writeSingleHeader(hdu, keywords, item);
+    }
+    return status;
 }
 
@@ -156,4 +195,5 @@
                 psString source = psMetadataLookupStr(&mdok, cell->config, nameSource); // The source
                 if (mdok && strlen(source) > 0) {
+                    psTrace(__func__, 8, "%s is %s\n", nameSource, source);
                     if (strcasecmp(source, "HEADER") == 0) {
                         if (cameraItem->type != PS_DATA_STRING) {
@@ -162,11 +202,13 @@
                             continue;
                         }
+                        psTrace(__func__, 8, "Writing %s to header %s\n", name, cameraItem->data.V);
                         writeHeader(hdu, cameraItem->data.V, formatted);
                         continue;
                     }
                     if (strcasecmp(source, "VALUE") == 0) {
+                        psTrace(__func__, 8, "Checking %s against camera format.\n", name);
                         if (! compareConcepts(cameraItem, formatted)) {
                             psLogMsg(__func__, PS_LOG_WARN, "Concept %s is specified by value in the camera "
-                                     "format, but the values don't match.\n");
+                                     "format, but the values don't match.\n", name);
                         }
                         continue;
@@ -179,5 +221,5 @@
                 if (! compareConcepts(cameraItem, formatted)) {
                     psLogMsg(__func__, PS_LOG_WARN, "Concept %s is specified by value in the camera "
-                             "format, but the values don't match.\n");
+                             "format, but the values don't match.\n", name);
                 }
             }
Index: /branches/rel10_ifa/psModules/src/astrom/pmFPAConstruct.c
===================================================================
--- /branches/rel10_ifa/psModules/src/astrom/pmFPAConstruct.c	(revision 6733)
+++ /branches/rel10_ifa/psModules/src/astrom/pmFPAConstruct.c	(revision 6734)
@@ -682,4 +682,5 @@
 // Print out the focal plane structure
 void pmFPAPrint(pmFPA *fpa,             // FPA to print
+                bool header,            // Print headers?
                 bool concepts           // Print concepts?
                )
@@ -703,4 +704,8 @@
         if (chip->hdu) {
             psTrace(__func__, 4, "---> Chip is extension %s.\n", chip->hdu->extname);
+            if (chip->hdu->header && header) {
+                psTrace(__func__, 4, "---> Header:\n");
+                psMetadataPrint(chip->hdu->header, 8);
+            }
             if (! chip->hdu->images) {
                 psTrace(__func__, 4, "---> NO PIXELS read in for extension %s\n", chip->hdu->extname);
@@ -718,4 +723,8 @@
             if (cell->hdu) {
                 psTrace(__func__, 6, "---> Cell is extension %s.\n", cell->hdu->extname);
+                if (cell->hdu->header && header) {
+                    psTrace(__func__, 4, "---> Header:\n");
+                    psMetadataPrint(cell->hdu->header, 8);
+                }
                 if (! cell->hdu->images) {
                     psTrace(__func__, 6, "---> NO PIXELS read in for extension %s\n", cell->hdu->extname);
@@ -731,4 +740,5 @@
                 pmReadout *readout = readouts->data[k]; // The readout
                 psImage *image = readout->image; // The image
+                psList *bias = readout->bias; // The list of bias images
                 if (image) {
                     psTrace(__func__, 8, "Image: [%d:%d,%d:%d] (%dx%d)\n", image->col0, image->col0 +
@@ -736,4 +746,14 @@
                             image->numRows);
                 }
+                if (bias) {
+                    psListIterator *biasIter = psListIteratorAlloc(bias, PS_LIST_HEAD, false); // Iterator
+                    psImage *biasImage = NULL; // Bias image from iteration
+                    while ((biasImage = psListGetAndIncrement(biasIter))) {
+                        psTrace(__func__, 8, "Bias:  [%d:%d,%d:%d] (%dx%d)\n", biasImage->col0,
+                                biasImage->col0 + biasImage->numCols, biasImage->row0,
+                                biasImage->row0 + biasImage->numRows, biasImage->numCols, biasImage->numRows);
+                    }
+                    psFree(biasIter);
+                }
             } // Iterating over cell
         } // Iterating over chip
Index: /branches/rel10_ifa/psModules/src/astrom/pmFPAConstruct.h
===================================================================
--- /branches/rel10_ifa/psModules/src/astrom/pmFPAConstruct.h	(revision 6733)
+++ /branches/rel10_ifa/psModules/src/astrom/pmFPAConstruct.h	(revision 6734)
@@ -17,7 +17,7 @@
 // Print out the FPA
 void pmFPAPrint(pmFPA *fpa,             // FPA to print
+                bool header,            // Print headers?
                 bool concepts           // Print concepts?
                );
 
-
 #endif
Index: /branches/rel10_ifa/psModules/src/astrom/pmFPACopy.c
===================================================================
--- /branches/rel10_ifa/psModules/src/astrom/pmFPACopy.c	(revision 6733)
+++ /branches/rel10_ifa/psModules/src/astrom/pmFPACopy.c	(revision 6734)
@@ -117,4 +117,7 @@
             *xSize = MAX(trimsec->x1, *xSize);
             *ySize = MAX(trimsec->y1, *ySize);
+        } else {
+            psFree(cellsIter);
+            return false;
         }
         psList *biassecs = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.BIASSEC"); // Bias sections
@@ -126,4 +129,8 @@
                     *xSize = MAX(biassec->x1, *xSize);
                     *ySize = MAX(biassec->y1, *ySize);
+                } else {
+                    psFree(biassecsIter);
+                    psFree(cellsIter);
+                    return false;
                 }
             }
@@ -133,5 +140,5 @@
     psFree(cellsIter);
 
-    return (*xSize == 0 || *ySize == 0);
+    return (*xSize != 0 && *ySize != 0);
 }
 
@@ -145,10 +152,10 @@
     switch (readdir) {
     case 1:                           // Read direction is rows
-        region = psRegionSet(*position, image->row0, *position + image->numCols,
+        region = psRegionSet(*position, *position + image->numCols, image->row0,
                              image->row0 + image->numRows);
         *position += image->numCols;
         break;
     case 2:                           // Read direction is columns
-        region = psRegionSet(image->col0, *position, image->col0 + image->numCols,
+        region = psRegionSet(image->col0, image->col0 + image->numCols, *position,
                              *position + image->numRows);
         *position += image->numRows;
@@ -209,5 +216,5 @@
     int numCells = targets->n;          // Number of cells
     int cellNum = 0;                    // The cell number
-    int position;                       // Position on the image
+    int position = 0;                   // Position on the image
     bool mdok = true;                   // Status of MD lookup
 
@@ -218,6 +225,7 @@
     while ((target = psListGetAndIncrement(targetsIter)) && (source = psListGetAndIncrement(sourcesIter)) &&
             !done) {
-        if (cellNum < numCells/2) {
+        if (cellNum <= numCells/2 - 1) {
             doBiasSections(&position, target, source);
+            cellNum++;
         } else {
             done = true;
@@ -249,6 +257,6 @@
 
     // A final run through to do the RHS biases
-    psListIteratorSet(targetsIter, numCells/2);
-    psListIteratorSet(sourcesIter, numCells/2);
+    psListIteratorSet(targetsIter, cellNum);
+    psListIteratorSet(sourcesIter, cellNum);
     while ((target = psListGetAndIncrement(targetsIter)) && (source = psListGetAndIncrement(sourcesIter))) {
         doBiasSections(&position, target, source);
@@ -299,9 +307,24 @@
     // Get the size of the HDU, either from existing trimsec and biassec, or generate these and try again
     int xSize = 0, ySize = 0;           // Size of HDU
-    if (!sizeHDU(&xSize, &ySize, targetCells) || !(generateTrimBias(targetCells, sourceCells) &&
+    #if 1
+
+    if (!sizeHDU(&xSize, &ySize, targetCells) && !(generateTrimBias(targetCells, sourceCells) &&
             sizeHDU(&xSize, &ySize, targetCells))) {
         psError(PS_ERR_IO, true, "Unable to determine size of HDU!\n");
         return false;
     }
+    #else
+    if (!sizeHDU(&xSize, &ySize, targetCells)) {
+        if (generateTrimBias(targetCells, sourceCells)) {
+            if (!sizeHDU(&xSize, &ySize, targetCells)) {
+                psError(PS_ERR_IO, true, "Unable to determine size of HDU!\n");
+                return false;
+            }
+        } else {
+            psError(PS_ERR_IO, true, "Unable to determine size of HDU!\n");
+            return false;
+        }
+    }
+    #endif
 
     hdu->images = psArrayAlloc(numReadouts);
@@ -314,32 +337,4 @@
     return true;
 }
-
-
-#define UPDATE_CASE(TYPENAME,TYPE) \
-case TYPENAME: targetItem->data.TYPE = sourceItem->data.TYPE; \
-break;
-
-// Update a particular "concept" for the target cell from the source cell
-static bool updateConcept(pmCell *target, // Target cell
-                          const pmCell *source, // Source cell
-                          const char *concept // Concept name
-                         )
-{
-    psMetadataItem *targetItem = psMetadataLookup(target->concepts, concept); // Concept from the target
-    psMetadataItem *sourceItem = psMetadataLookup(source->concepts, concept); // Concept from the source
-
-    switch (targetItem->type) {
-        UPDATE_CASE(PS_TYPE_S32, S32);
-        UPDATE_CASE(PS_TYPE_F32, F32);
-        UPDATE_CASE(PS_TYPE_F64, F64);
-        UPDATE_CASE(PS_DATA_STRING, V);
-    default:
-        psLogMsg(__func__, PS_LOG_WARN, "Unsupported type (%x) for concept %s --- ignored.\n",
-                 targetItem->type, concept);
-        return false;
-    }
-    return true;
-}
-
 
 // Copy pixels from a target image to a source image, with flips
@@ -386,4 +381,12 @@
         return false;
     }
+
+    #if 1
+    // Copy any headers
+    if (target->hdu && !target->hdu->phu) {
+        pmHDU *sourceHDU = pmHDUFromFPA(source);
+        target->hdu->header = psMetadataCopy(target->hdu->header, sourceHDU->header);
+    }
+    #endif
 
     int numChips = 0;                   // Number of chips copied
@@ -400,4 +403,7 @@
     }
 
+    // Update the concepts
+    psMetadataCopy(target->concepts, source->concepts);
+
     return numChips;
 }
@@ -417,4 +423,12 @@
         return false;
     }
+
+    #if 1
+    // Copy any headers
+    if (target->hdu && !target->hdu->phu) {
+        pmHDU *sourceHDU = pmHDUFromChip(source);
+        target->hdu->header = psMetadataCopy(target->hdu->header, sourceHDU->header);
+    }
+    #endif
 
     int numCells = 0;                   // Number of cells copied
@@ -431,4 +445,7 @@
     }
 
+    // Update the concepts
+    psMetadataCopy(target->concepts, source->concepts);
+
     return numCells;
 
@@ -442,31 +459,28 @@
     assert(source);
 
-    psArray *targetReadouts = target->readouts; // The target readouts
     psArray *sourceReadouts = source->readouts; // The source readouts
-    if (targetReadouts->n != sourceReadouts->n) {
-        psError(PS_ERR_IO, true, "Number of source readouts (%d) differs from the number of target readouts "
-                "(%d)\n", sourceReadouts->n, targetReadouts->n);
-        return false;
-    }
-    int numReadouts = targetReadouts->n; // Number of readouts copied
+    int numReadouts = sourceReadouts->n; // Number of readouts copied
+
+    #if 1
+    // Copy any headers
+    if (target->hdu && !target->hdu->phu) {
+        pmHDU *sourceHDU = pmHDUFromCell(source);
+        target->hdu->header = psMetadataCopy(target->hdu->header, sourceHDU->header);
+    }
+    #endif
 
     pmHDU *hdu = pmHDUFromCell(target); // The target HDU; we need to fix this up
-    if (! hdu->images) {
+    if (!hdu->images) {
         generateHDU(target, source);
     }
-
-    // Need to check CELL.X0, CELL.Y0, CELL.XBIN, CELL.YBIN should be unchanged; we can check this by
-    // setting the values and leaving it to the pmConceptWrite functions to do the checking against the
-    // camera configuration when it comes time to write them out.
-    updateConcept(target, source, "CELL.X0");
-    updateConcept(target, source, "CELL.Y0");
-    updateConcept(target, source, "CELL.XBIN");
-    updateConcept(target, source, "CELL.YBIN");
+    if (!hdu->header) {
+        hdu->header = psMetadataAlloc();
+    }
 
     // Need to check/change CELL.XPARITY and CELL.YPARITY
     bool xFlip = (psMetadataLookupS32(NULL, target->concepts, "CELL.XPARITY") !=
-                  psMetadataLookupS32(NULL, target->concepts, "CELL.XPARITY")); // Switch parity in x?
+                  psMetadataLookupS32(NULL, source->concepts, "CELL.XPARITY")); // Switch parity in x?
     bool yFlip = (psMetadataLookupS32(NULL, target->concepts, "CELL.YPARITY") !=
-                  psMetadataLookupS32(NULL, target->concepts, "CELL.YPARITY")); // Switch parity in y?
+                  psMetadataLookupS32(NULL, source->concepts, "CELL.YPARITY")); // Switch parity in y?
 
     bool mdok = true;                   // Status of MD lookup
@@ -482,8 +496,8 @@
     }
 
-    for (int i = 0; i < targetReadouts->n; i++) {
+    for (int i = 0; i < numReadouts; i++) {
         pmReadout *sourceReadout = sourceReadouts->data[i]; // The source readout
         psImage *sourceImage = sourceReadout->image; // The source image
-        pmReadout *targetReadout = targetReadouts->data[i]; // The target readout
+        pmReadout *targetReadout = pmReadoutAlloc(target); // The target readout; this adds it to the cell
         if (sourceImage->numCols != trimsec->x1 - trimsec->x0 ||
                 sourceImage->numRows != trimsec->y1 - trimsec->y0) {
@@ -500,5 +514,7 @@
         psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false); // Iterator
         psRegion *biassec = NULL;       // Bias section from iteration
-        while ((biassec = psListGetAndIncrement(biassecsIter))) {
+        psListIterator *biasIter = psListIteratorAlloc(sourceReadout->bias, PS_LIST_HEAD, false); // Iterator
+        psImage *bias = NULL;           // Bias image from iteration
+        while ((biassec = psListGetAndIncrement(biassecsIter)) && (bias = psListGetAndIncrement(biasIter))) {
             if (psRegionIsBad(*biassec)) {
                 psString biassecString = psRegionToString(*biassec); // String for bias section
@@ -507,20 +523,43 @@
                 continue;
             }
-            if (sourceImage->numCols != biassec->x1 - biassec->x0 ||
-                    sourceImage->numRows != biassec->y1 - biassec->y0) {
+            if (bias->numCols != biassec->x1 - biassec->x0 ||
+                    bias->numRows != biassec->y1 - biassec->y0) {
                 psString biassecString = psRegionToString(*biassec); // String with the bias section
                 psLogMsg(__func__, PS_LOG_WARN, "Source image size (%dx%d) for readout %d doesn't match "
-                         "CELL.BIASSEC for target (%s) -- ignored.\n", sourceImage->numCols,
-                         sourceImage->numRows, i, biassecString);
+                         "CELL.BIASSEC for target (%s) -- ignored.\n", bias->numCols, bias->numRows, i,
+                         biassecString);
                 psFree(biassecString);
             } else {
-                copyPixels(hdu->images->data[i], sourceImage, *biassec, xFlip, yFlip);
-                psImage *bias = psMemIncrRefCounter(psImageSubset(hdu->images->data[i], *biassec));
-                psListAdd(targetReadout->bias, PS_LIST_TAIL, bias);
-                psFree(bias);           // Drop reference
+                copyPixels(hdu->images->data[i], bias, *biassec, xFlip, yFlip);
+                psImage *newBias = psMemIncrRefCounter(psImageSubset(hdu->images->data[i], *biassec));
+                psListAdd(targetReadout->bias, PS_LIST_TAIL, newBias);
+                psFree(newBias);        // Drop reference
             }
         }
         psFree(biassecsIter);
-
+        psFree(biasIter);
+    }
+
+    // Copy the remaining "concepts" over
+    psMetadataIterator *conceptsIter = psMetadataIteratorAlloc(source->concepts, PS_LIST_HEAD, NULL);
+    psMetadataItem *conceptItem = NULL; // Item from iteration
+    while ((conceptItem = psMetadataGetAndIncrement(conceptsIter))) {
+        psString name = conceptItem->name; // Name of concept
+        if (strcmp(name, "CELL.TRIMSEC") != 0 && strcmp(name, "CELL.BIASSEC") != 0 &&
+                strcmp(name, "CELL.XPARITY") != 0 && strcmp(name, "CELL.YPARITY") != 0) {
+            psMetadataAddItem(target->concepts, conceptItem, PS_LIST_TAIL, PS_META_REPLACE);
+        }
+    }
+
+    // Need to update CELL.X0 and CELL.Y0 if we flipped
+    if (xFlip) {
+        psMetadataItem *xZero = psMetadataLookup(target->concepts, "CELL.X0"); // CELL.X0
+        pmReadout *readout = target->readouts->data[0]; // A representative readout
+        xZero->data.S32 -= readout->image->numCols - 1;
+    }
+    if (yFlip) {
+        psMetadataItem *yZero = psMetadataLookup(target->concepts, "CELL.Y0"); // CELL.Y0
+        pmReadout *readout = target->readouts->data[0]; // A representative readout
+        yZero->data.S32 -= readout->image->numRows - 1;
     }
 
Index: /branches/rel10_ifa/psModules/src/astrom/pmFPAWrite.c
===================================================================
--- /branches/rel10_ifa/psModules/src/astrom/pmFPAWrite.c	(revision 6733)
+++ /branches/rel10_ifa/psModules/src/astrom/pmFPAWrite.c	(revision 6734)
@@ -13,5 +13,6 @@
                 )
 {
-    pmConceptsWriteCell(cell, PM_CONCEPT_SOURCE_HEADER, false, NULL);
+    pmConceptsWriteCell(cell, PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CAMERA | PM_CONCEPT_SOURCE_DEFAULTS,
+                        false, NULL);
 
     pmHDU *hdu = cell->hdu;             // The HDU
@@ -30,5 +31,6 @@
                 )
 {
-    pmConceptsWriteChip(chip, PM_CONCEPT_SOURCE_HEADER, false, NULL);
+    pmConceptsWriteChip(chip, PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CAMERA | PM_CONCEPT_SOURCE_DEFAULTS,
+                        false, NULL);
 
     pmHDU *hdu = chip->hdu;             // The HDU
@@ -56,5 +58,6 @@
                )
 {
-    pmConceptsWriteFPA(fpa, PM_CONCEPT_SOURCE_HEADER, NULL);
+    pmConceptsWriteFPA(fpa, PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CAMERA | PM_CONCEPT_SOURCE_DEFAULTS,
+                       NULL);
 
     pmHDU *hdu = fpa->hdu;              // The HDU
Index: /branches/rel10_ifa/psModules/src/astrom/pmHDU.c
===================================================================
--- /branches/rel10_ifa/psModules/src/astrom/pmHDU.c	(revision 6733)
+++ /branches/rel10_ifa/psModules/src/astrom/pmHDU.c	(revision 6734)
@@ -4,4 +4,5 @@
 #include "pslib.h"
 #include "pmFPA.h"
+#include "psAdditionals.h"
 
 
@@ -35,8 +36,10 @@
         hdu->extname = psStringCopy(extname);
     }
-    hdu->format = NULL;
-    hdu->header = NULL;
-    hdu->images = NULL;
-    hdu->table  = NULL;
+    hdu->format  = NULL;
+    hdu->header  = NULL;
+    hdu->images  = NULL;
+    hdu->table   = NULL;
+    hdu->weights = NULL;
+    hdu->masks   = NULL;
 
     return hdu;
Index: /branches/rel10_ifa/psModules/src/astrom/pmHDUUtils.c
===================================================================
--- /branches/rel10_ifa/psModules/src/astrom/pmHDUUtils.c	(revision 6733)
+++ /branches/rel10_ifa/psModules/src/astrom/pmHDUUtils.c	(revision 6734)
@@ -59,3 +59,2 @@
 }
 
-
Index: /branches/rel10_ifa/psModules/src/config/pmConfig.c
===================================================================
--- /branches/rel10_ifa/psModules/src/config/pmConfig.c	(revision 6733)
+++ /branches/rel10_ifa/psModules/src/config/pmConfig.c	(revision 6734)
@@ -3,6 +3,6 @@
  *  @author PAP, IfA
  *
- *  @version $Revision: 1.7.4.6 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2006-03-28 02:16:26 $
+ *  @version $Revision: 1.7.4.7 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-03-30 20:08:30 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -539,2 +539,26 @@
     return(psDBInit(dbServer, dbUsername, dbPassword, dbName));
 }
+
+
+bool pmConfigConformHeader(psMetadata *header, // Header to conform
+                           const psMetadata *format // Camera format
+                          )
+{
+    bool mdok = true;                   // Status of MD lookup
+    psMetadata *rules = psMetadataLookupMD(&mdok, format, "RULE"); // How to identify this format
+    if (!mdok || !rules) {
+        psError(PS_ERR_IO, false, "Unable to find RULE in camer format.\n");
+        return false;
+    }
+
+    psMetadataIterator *rulesIter = psMetadataIteratorAlloc(rules, PS_LIST_HEAD, NULL); // Iterator for rules
+    psMetadataItem *rulesItem = NULL;   // Item from iteration
+    while ((rulesItem = psMetadataGetAndIncrement(rulesIter))) {
+        psMetadataItem *newItem = psMetadataItemCopy(rulesItem); // Copy of item
+        psMetadataAddItem(header, newItem, PS_LIST_TAIL, PS_META_REPLACE);
+        psFree(newItem);                // Drop reference
+    }
+    psFree(rulesIter);
+
+    return true;
+}
Index: /branches/rel10_ifa/psModules/src/config/pmConfig.h
===================================================================
--- /branches/rel10_ifa/psModules/src/config/pmConfig.h	(revision 6733)
+++ /branches/rel10_ifa/psModules/src/config/pmConfig.h	(revision 6734)
@@ -3,6 +3,6 @@
  *  @author PAP, IfA
  *
- *  @version $Revision: 1.3.4.4 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2006-03-28 02:16:26 $
+ *  @version $Revision: 1.3.4.5 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2006-03-30 20:08:30 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -100,4 +100,12 @@
 );
 
+/** pmConfigConformHeader
+ *
+ * Make the supplied header conform to the nominated camera format.
+ */
+bool pmConfigConformHeader(psMetadata *header, // Header to conform
+                           const psMetadata *format // Camera format
+                          );
+
 
 #endif
Index: /branches/rel10_ifa/psModules/src/pslib/psImageFlip.c
===================================================================
--- /branches/rel10_ifa/psModules/src/pslib/psImageFlip.c	(revision 6733)
+++ /branches/rel10_ifa/psModules/src/pslib/psImageFlip.c	(revision 6734)
@@ -36,5 +36,5 @@
     }
 
-    return image;
+    return output;
 }
 
@@ -73,4 +73,4 @@
     }
 
-    return image;
+    return output;
 }
Index: /branches/rel10_ifa/psModules/src/psmodules.h
===================================================================
--- /branches/rel10_ifa/psModules/src/psmodules.h	(revision 6733)
+++ /branches/rel10_ifa/psModules/src/psmodules.h	(revision 6734)
@@ -7,4 +7,5 @@
 // the following headers are from psModule:pslib
 #include <psImageJpeg.h>
+#include <psImageFlip.h>
 #include <psLine.h>
 #include <psPolynomialUtils.h>
