Index: trunk/psModules/src/camera/pmFPA.c
===================================================================
--- trunk/psModules/src/camera/pmFPA.c	(revision 17898)
+++ trunk/psModules/src/camera/pmFPA.c	(revision 17911)
@@ -305,5 +305,6 @@
     tmpCell->conceptsRead = PM_CONCEPT_SOURCE_NONE;
     if (!psMetadataAddStr(tmpCell->concepts, PS_LIST_HEAD, "CELL.NAME", 0, NULL, name)) {
-        psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not add CELL.NAME to metadata.\n");
+        psErrorClear();
+        psWarning("Could not add CELL.NAME to metadata.");
     }
     pmConceptsBlankCell(tmpCell);
@@ -343,5 +344,6 @@
     tmpChip->conceptsRead = PM_CONCEPT_SOURCE_NONE;
     if (!psMetadataAddStr(tmpChip->concepts, PS_LIST_HEAD, "CHIP.NAME", 0, NULL, name)) {
-        psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not add CHIP.NAME %s to concepts.\n", name);
+        psErrorClear();
+        psWarning("Could not add CHIP.NAME %s to concepts.", name);
     }
     pmConceptsBlankChip(tmpChip);
@@ -355,5 +357,5 @@
 }
 
-pmFPA *pmFPAAlloc(const psMetadata *camera)
+pmFPA *pmFPAAlloc(const psMetadata *camera, const char *cameraName)
 {
     pmFPA *tmpFPA = (pmFPA *) psAlloc(sizeof(pmFPA));
@@ -373,4 +375,10 @@
     pmConceptsBlankFPA(tmpFPA);
 
+    if (!psMetadataAddStr(tmpFPA->concepts, PS_LIST_TAIL, "FPA.CAMERA", PS_META_REPLACE,
+                          "Camera name (according to configuration)", cameraName)) {
+        psErrorClear();
+        psWarning("Could not add FPA.CAMERA %s to concepts.", cameraName);
+    }
+
     return tmpFPA;
 }
Index: trunk/psModules/src/camera/pmFPA.h
===================================================================
--- trunk/psModules/src/camera/pmFPA.h	(revision 17898)
+++ trunk/psModules/src/camera/pmFPA.h	(revision 17911)
@@ -6,6 +6,6 @@
  * @author Eugene Magnier, IfA
  *
- * @version $Revision: 1.21 $ $Name: not supported by cvs2svn $
- * @date $Date: 2008-03-31 22:39:06 $
+ * @version $Revision: 1.22 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-06-05 01:31:33 $
  * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
  */
@@ -169,5 +169,7 @@
 
 /// Allocate an FPA
-pmFPA *pmFPAAlloc(const psMetadata *camera); ///< Camera configuration (to store in FPA)
+pmFPA *pmFPAAlloc(const psMetadata *camera, ///< Camera configuration (to store in FPA)
+                  const char *cameraName ///< Name of camera (for FPA.CAMERA concept)
+    );
 bool psMemCheckFPA(psPtr ptr);
 
Index: trunk/psModules/src/camera/pmFPAConstruct.c
===================================================================
--- trunk/psModules/src/camera/pmFPAConstruct.c	(revision 17898)
+++ trunk/psModules/src/camera/pmFPAConstruct.c	(revision 17911)
@@ -1102,5 +1102,5 @@
 // It returns a view corresponding to the PHU
 static pmFPAview *addSource(pmFPA *fpa,       // The FPA
-                            const char *fpaname, // The desired FPA name
+                            const char *fpaObs, // The desired FPA observation name
                             const pmFPAview *phuView, // The view corresponding to the PHU, or NULL
                             const psMetadata *header, // The PHU header, or NULL
@@ -1115,16 +1115,17 @@
     bool mdok;                          // Status of MD lookup
 
-    // If FPA.NAME is already defined, new name must match it; otherwise, warn the user that something
+    // If FPA.OBS is already defined, new name must match it; otherwise, warn the user that something
     // potentially bad is happening.
-    if (fpaname && install) {
-        const char *currentName = psMetadataLookupStr(&mdok, fpa->concepts, "FPA.NAME"); // Current name
-        if (mdok && currentName && strlen(currentName) > 0 && strcmp(currentName, fpaname) != 0) {
-            psWarning("FPA.NAME for new source (%s) doesn't match FPA.NAME for current "
-                     "fpa (%s).\n", fpaname, currentName);
-        }
-        psMetadataAddStr(fpa->concepts, PS_LIST_HEAD, "FPA.NAME", PS_META_REPLACE, "Name of FPA", fpaname);
-    } else if (!psMetadataLookup(fpa->concepts, "FPA.NAME")) {
-        // Make sure there is an FPA.NAME
-        psMetadataAddStr(fpa->concepts, PS_LIST_HEAD, "FPA.NAME", 0, "Name of FPA", "UNKNOWN");
+    if (fpaObs && install) {
+        const char *currentName = psMetadataLookupStr(&mdok, fpa->concepts, "FPA.OBS"); // Current name
+        if (mdok && currentName && strlen(currentName) > 0 && strcmp(currentName, fpaObs) != 0) {
+            psWarning("FPA.OBS for new source (%s) doesn't match FPA.OBS for current fpa (%s).",
+                      fpaObs, currentName);
+        }
+        psMetadataAddStr(fpa->concepts, PS_LIST_HEAD, "FPA.OBS", PS_META_REPLACE, "Observation identifier",
+                         fpaObs);
+    } else if (!psMetadataLookup(fpa->concepts, "FPA.OBS")) {
+        // Make sure there is an FPA.OBS
+        psMetadataAddStr(fpa->concepts, PS_LIST_HEAD, "FPA.OBS", 0, "Observation identifier", "UNKNOWN");
     }
 
@@ -1308,9 +1309,9 @@
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
-pmFPA *pmFPAConstruct(const psMetadata *camera)
+pmFPA *pmFPAConstruct(const psMetadata *camera, const char *cameraName)
 {
     PS_ASSERT_PTR_NON_NULL(camera, NULL);
 
-    pmFPA *fpa = pmFPAAlloc(camera);    // The FPA to fill out
+    pmFPA *fpa = pmFPAAlloc(camera, cameraName);    // The FPA to fill out
 
     bool mdok = true;                   // Status from MD lookups
@@ -1349,5 +1350,5 @@
 }
 
-bool pmFPAAddSourceFromFormat(pmFPA *fpa, const char *fpaname, const psMetadata *format)
+bool pmFPAAddSourceFromFormat(pmFPA *fpa, const char *fpaObs, const psMetadata *format)
 {
     PS_ASSERT_PTR_NON_NULL(fpa, false);
@@ -1358,5 +1359,5 @@
     pmFPAview *view = pmFPAviewAlloc(0);// View for current level
     if (phuLevel == PM_FPA_LEVEL_FPA) {
-        if (!pmFPAAddSourceFromView(fpa, fpaname, view, format)) {
+        if (!pmFPAAddSourceFromView(fpa, fpaObs, view, format)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to add PHU to FPA.");
             psFree(view);
@@ -1367,5 +1368,5 @@
         while ((chip = pmFPAviewNextChip(view, fpa, 1))) {
             if (phuLevel == PM_FPA_LEVEL_CHIP) {
-                if (!pmFPAAddSourceFromView(fpa, fpaname, view, format)) {
+                if (!pmFPAAddSourceFromView(fpa, fpaObs, view, format)) {
                     psError(PS_ERR_UNKNOWN, false, "Unable to add PHU to FPA.");
                     psFree(view);
@@ -1376,5 +1377,5 @@
                 while ((cell = pmFPAviewNextCell(view, fpa, 1))) {
                     if (phuLevel == PM_FPA_LEVEL_CELL) {
-                        if (!pmFPAAddSourceFromView(fpa, fpaname, view, format)) {
+                        if (!pmFPAAddSourceFromView(fpa, fpaObs, view, format)) {
                             psError(PS_ERR_UNKNOWN, false, "Unable to add PHU to FPA.");
                             psFree(view);
@@ -1391,5 +1392,5 @@
 }
 
-bool pmFPAAddSourceFromView(pmFPA *fpa, const char *fpaname, const pmFPAview *phuView,
+bool pmFPAAddSourceFromView(pmFPA *fpa, const char *fpaObs, const pmFPAview *phuView,
                             const psMetadata *format)
 {
@@ -1398,5 +1399,5 @@
     PS_ASSERT_PTR_NON_NULL(format, false);
 
-    pmFPAview *view = addSource(fpa, fpaname, phuView, NULL, format, true);
+    pmFPAview *view = addSource(fpa, fpaObs, phuView, NULL, format, true);
     bool status = (view != NULL);
     psFree(view);
@@ -1418,11 +1419,11 @@
 
     // Check the name of the FPA
-    psString fpaname = phuNameFromHeader("FPA.NAME", fileInfo, phu); // New name for the FPA
-    if (!fpaname || strlen(fpaname) == 0) {
-        psWarning("Unable to determine FPA.NAME: check for FPA.NAME in FILE in camera format");
-    }
-
-    pmFPAview *view = addSource(fpa, fpaname, NULL, phu, format, true); // View of PHU, to return
-    psFree(fpaname);
+    psString fpaObs = phuNameFromHeader("FPA.OBS", fileInfo, phu); // New observation name for the FPA
+    if (!fpaObs || strlen(fpaObs) == 0) {
+        psWarning("Unable to determine FPA.OBS: check for FPA.OBS in FILE in camera format");
+    }
+
+    pmFPAview *view = addSource(fpa, fpaObs, NULL, phu, format, true); // View of PHU, to return
+    psFree(fpaObs);
 
     return view;
Index: trunk/psModules/src/camera/pmFPAConstruct.h
===================================================================
--- trunk/psModules/src/camera/pmFPAConstruct.h	(revision 17898)
+++ trunk/psModules/src/camera/pmFPAConstruct.h	(revision 17911)
@@ -4,6 +4,6 @@
  * @author Paul Price, IfA
  *
- * @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
- * @date $Date: 2008-03-11 01:27:05 $
+ * @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-06-05 01:31:33 $
  * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
  */
@@ -21,5 +21,6 @@
 /// as the corresponding values (whitespace separated).  The FPA hierarchy is created devoid of any
 /// input/output sources (i.e., HDUs).
-pmFPA *pmFPAConstruct(const psMetadata *camera ///< The camera configuration
+pmFPA *pmFPAConstruct(const psMetadata *camera, ///< The camera configuration
+                      const char *cameraName ///< Name of the camera (for FPA.CAMERA concept)
                      );
 
@@ -28,5 +29,5 @@
 /// This is suitable for generating an output FPA given the desired format.
 bool pmFPAAddSourceFromFormat(pmFPA *fpa, ///< The FPA
-                              const char *fpaname, ///< FPA.NAME for the source
+                              const char *fpaObs, ///< FPA.NAME for the source
                               const psMetadata *format ///< Format of file
     );
@@ -37,5 +38,5 @@
 /// configuration is required in order to describe how the FPA is laid out in terms of disk files.
 bool pmFPAAddSourceFromView(pmFPA *fpa,   ///< The FPA
-                            const char *fpaname, ///< FPA.NAME for the source
+                            const char *fpaObs, ///< FPA.NAME for the source
                             const pmFPAview *phuView, ///< The view, corresponding to the PHU
                             const psMetadata *format ///< Format of file
Index: trunk/psModules/src/camera/pmFPAWrite.c
===================================================================
--- trunk/psModules/src/camera/pmFPAWrite.c	(revision 17898)
+++ trunk/psModules/src/camera/pmFPAWrite.c	(revision 17911)
@@ -37,5 +37,5 @@
 
 
-// Update the FPA.NAME, CHIP.NAME and CELL.NAME in the FITS header, if required
+// Update the FPA.OBS, CHIP.NAME and CELL.NAME in the FITS header, if required
 bool pmFPAUpdateNames(pmFPA *fpa, pmChip *chip, pmCell *cell)
 {
@@ -55,8 +55,9 @@
     }
     if (fpa) {
-        const char *fpaNameHdr = psMetadataLookupStr(&mdok, fileData, "FPA.NAME");
-        if (mdok && fpaNameHdr && strlen(fpaNameHdr) > 0) {
-            const char *fpaName = psMetadataLookupStr(NULL, fpa->concepts, "FPA.NAME");
-            psMetadataAddStr(hdu->header, PS_LIST_TAIL, fpaNameHdr, PS_META_REPLACE, "FPA name", fpaName);
+        const char *fpaObsHdr = psMetadataLookupStr(&mdok, fileData, "FPA.OBS");
+        if (mdok && fpaObsHdr && strlen(fpaObsHdr) > 0) {
+            const char *fpaObs = psMetadataLookupStr(NULL, fpa->concepts, "FPA.OBS");
+            psMetadataAddStr(hdu->header, PS_LIST_TAIL, fpaObsHdr, PS_META_REPLACE,
+                             "Observation identifier", fpaObs);
         }
     }
Index: trunk/psModules/src/camera/pmFPAfile.c
===================================================================
--- trunk/psModules/src/camera/pmFPAfile.c	(revision 17898)
+++ trunk/psModules/src/camera/pmFPAfile.c	(revision 17911)
@@ -181,8 +181,8 @@
   newName = psStringCopy(rule);
 
-  if (strstr (newName, "{FPA.NAME}") != NULL) {
-    char *name = psMetadataLookupStr (NULL, fpa->concepts, "FPA.NAME");
+  if (strstr (newName, "{FPA.OBS}") != NULL) {
+    char *name = psMetadataLookupStr (NULL, fpa->concepts, "FPA.OBS");
     if (name != NULL) {
-      psStringSubstitute(&newName, "fpa", "{FPA.NAME}");
+      psStringSubstitute(&newName, "fpa", "{FPA.OBS}");
     }
   }
Index: trunk/psModules/src/camera/pmFPAfileDefine.c
===================================================================
--- trunk/psModules/src/camera/pmFPAfileDefine.c	(revision 17898)
+++ trunk/psModules/src/camera/pmFPAfileDefine.c	(revision 17911)
@@ -237,5 +237,5 @@
         file->fpa = psMemIncrRefCounter(fpa);
     } else {
-        file->fpa = pmFPAConstruct(file->camera);
+        file->fpa = pmFPAConstruct(file->camera, file->cameraName);
     }
 
@@ -482,5 +482,5 @@
 
     // build the template fpa, set up the basic view
-    fpa = pmFPAConstruct(config->camera);
+    fpa = pmFPAConstruct(config->camera, config->cameraName);
     if (!fpa) {
         psError(PS_ERR_IO, false, "Failed to construct FPA from %s", realName);
@@ -783,5 +783,5 @@
 
     // build the template fpa, set up the basic view
-    fpa = pmFPAConstruct (config->camera);
+    fpa = pmFPAConstruct(config->camera, config->cameraName);
     if (!fpa) {
         psError(PS_ERR_IO, false, "Failed to construct FPA from %s", (char *)infiles->data[0]);
@@ -867,5 +867,5 @@
 
     // build the template fpa, set up the basic view
-    pmFPA *fpa = pmFPAConstruct (config->camera);
+    pmFPA *fpa = pmFPAConstruct(config->camera, config->cameraName);
     if (!fpa) {
         psError(PS_ERR_IO, false, "Failed to construct FPA for %s", filename);
@@ -949,5 +949,5 @@
     if (!file) {
         // build the template fpa, set up the basic view
-        fpa = pmFPAConstruct (config->camera);
+        fpa = pmFPAConstruct(config->camera, config->cameraName);
         if (!fpa) {
             psError(PS_ERR_IO, false, "Failed to construct FPA for %s", filename);
@@ -1090,5 +1090,5 @@
     PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
 
-    pmFPA *fpa = pmFPAConstruct(src->camera);
+    pmFPA *fpa = pmFPAConstruct(src->camera, psMetadataLookupStr(NULL, src->concepts, "FPA.CAMERA"));
     // XXX should this use DefineOutputForFormat?
     pmFPAfile *file = pmFPAfileDefineOutput (config, fpa, filename);
@@ -1139,5 +1139,5 @@
         return NULL;
     }
-    file->fpa = pmFPAConstruct(file->camera);
+    file->fpa = pmFPAConstruct(file->camera, file->cameraName);
 
     return file;
Index: trunk/psModules/src/camera/pmFPAfileFitsIO.c
===================================================================
--- trunk/psModules/src/camera/pmFPAfileFitsIO.c	(revision 17898)
+++ trunk/psModules/src/camera/pmFPAfileFitsIO.c	(revision 17911)
@@ -67,13 +67,13 @@
     }
 
-    pmFPA *nameSource = file->src; // Source of FPA.NAME
+    pmFPA *nameSource = file->src; // Source of FPA.OBS
     if (!nameSource) {
         nameSource = file->fpa;
     }
     bool mdok;                  // Status of MD lookup
-    const char *fpaname = psMetadataLookupStr(&mdok, nameSource->concepts, "FPA.NAME"); // Name of FPA
-
-    pmFPA *copy = pmFPAConstruct(file->camera);  // FPA to return
-    if (!pmFPAAddSourceFromView(copy, fpaname, phuView, file->format)) {
+    const char *fpaObs = psMetadataLookupStr(&mdok, nameSource->concepts, "FPA.OBS"); // Observation id
+
+    pmFPA *copy = pmFPAConstruct(file->camera, file->cameraName);  // FPA to return
+    if (!pmFPAAddSourceFromView(copy, fpaObs, phuView, file->format)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to insert HDU into FPA for writing.\n");
         psFree(copy);
Index: trunk/psModules/src/camera/pmFPAfileIO.c
===================================================================
--- trunk/psModules/src/camera/pmFPAfileIO.c	(revision 17898)
+++ trunk/psModules/src/camera/pmFPAfileIO.c	(revision 17911)
@@ -263,13 +263,14 @@
             }
 
-            pmFPA *nameSource = file->src; // Source of FPA.NAME
+            pmFPA *nameSource = file->src; // Source of FPA.OBS
             if (!nameSource) {
                 nameSource = file->fpa;
             }
             bool mdok;                  // Status of MD lookup
-            const char *fpaname = psMetadataLookupStr(&mdok, nameSource->concepts, "FPA.NAME"); // Name of FPA
-
-            pmFPAAddSourceFromView(file->fpa, fpaname, view, format);
-            psTrace ("psModules.camera", 5, "created fpa data elements for %s (%s) (%d:%d:%d)\n", file->name, file->name, view->chip, view->cell, view->readout);
+            const char *fpaObs = psMetadataLookupStr(&mdok, nameSource->concepts, "FPA.OBS"); // Obs. id
+
+            pmFPAAddSourceFromView(file->fpa, fpaObs, view, format);
+            psTrace ("psModules.camera", 5, "created fpa data elements for %s (%s) (%d:%d:%d)\n",
+                     file->name, file->name, view->chip, view->cell, view->readout);
             break;
     }
@@ -349,5 +350,5 @@
     // (existing) fpa
     if (file->type == PM_FPA_FILE_CMF) {
-	if (!pmFPAviewCheckDataStatusForSources (view, file)) {
+        if (!pmFPAviewCheckDataStatusForSources (view, file)) {
         psTrace("psModules.camera", 6, "skip write for %s, no data for this entry", file->name);
         return true;
@@ -778,36 +779,36 @@
         file->fits->options = psMemIncrRefCounter(file->options);
 
-	// in most cases, we have already open and read the phu and determined the format.
-	// in some cases, (eg DetDB images), we have only just determined the filename.
-	// we need to check the file format before we can work with the file
-	if (!file->format) {
-	  psMetadata *phu = psFitsReadHeader (NULL, file->fits);
-	  if (!phu) {
-	    psError(PS_ERR_IO, false, "Failed to read file header %s\n", file->filename);
-	    return false;
-	  }
-
-	  // determine the current format from the header
-	  // determine camera if not specified already
-	  file->format = pmConfigCameraFormatFromHeader (config, phu, true);
-	  if (!file->format) {
-	    psError(PS_ERR_IO, false, "Failed to read CCD format from %s\n", file->filename);
-	    psFree(phu);
-	    return false;
-	  }
-	  psFree(phu);
-	}
-
-	// if needed, set the optional EXTWORD field based on the camera value
-	psMetadata *fileMenu = psMetadataLookupMetadata (NULL, file->format, "FILE");
-	if (!fileMenu) {
-	  psError (PS_ERR_IO, true, "FILE METADATA missing from camera format %s\n",
-		   config->formatName);
-	  return false;
-	}
-	char *extword = psMetadataLookupStr (&status, fileMenu, "EXTWORD");
-	if (status) {
-	  psFitsSetExtnameWord (file->fits, extword);
-	}
+        // in most cases, we have already open and read the phu and determined the format.
+        // in some cases, (eg DetDB images), we have only just determined the filename.
+        // we need to check the file format before we can work with the file
+        if (!file->format) {
+          psMetadata *phu = psFitsReadHeader (NULL, file->fits);
+          if (!phu) {
+            psError(PS_ERR_IO, false, "Failed to read file header %s\n", file->filename);
+            return false;
+          }
+
+          // determine the current format from the header
+          // determine camera if not specified already
+          file->format = pmConfigCameraFormatFromHeader (config, phu, true);
+          if (!file->format) {
+            psError(PS_ERR_IO, false, "Failed to read CCD format from %s\n", file->filename);
+            psFree(phu);
+            return false;
+          }
+          psFree(phu);
+        }
+
+        // if needed, set the optional EXTWORD field based on the camera value
+        psMetadata *fileMenu = psMetadataLookupMetadata (NULL, file->format, "FILE");
+        if (!fileMenu) {
+          psError (PS_ERR_IO, true, "FILE METADATA missing from camera format %s\n",
+                   config->formatName);
+          return false;
+        }
+        char *extword = psMetadataLookupStr (&status, fileMenu, "EXTWORD");
+        if (status) {
+          psFitsSetExtnameWord (file->fits, extword);
+        }
 
         // XXX these are probably only needed for WRITE files
Index: trunk/psModules/src/concepts/pmConcepts.c
===================================================================
--- trunk/psModules/src/concepts/pmConcepts.c	(revision 17898)
+++ trunk/psModules/src/concepts/pmConcepts.c	(revision 17911)
@@ -462,4 +462,42 @@
 }
 
+// Register a concept
+#define CONCEPT_REGISTER_FUNCTION(TYPENAME, SUFFIX, DEFAULT) \
+static void conceptRegister##SUFFIX(const char *name, /* Name of concept */ \
+                                    const char *comment, /* Comment for concept */ \
+                                    pmConceptParseFunc parse, /* Parsing function */ \
+                                    pmConceptFormatFunc format, /* Formatting function */ \
+                                    bool required, /* Required concept? */ \
+                                    pmFPALevel level /* Level at which concept applies */ \
+    ) \
+{ \
+    psMetadataItem *item = psMetadataItemAlloc##TYPENAME(name, comment, DEFAULT); /* Item to add */ \
+    pmConceptRegister(item, parse, format, required, level); \
+    psFree(item); \
+    return; \
+}
+
+CONCEPT_REGISTER_FUNCTION(Str, Str, "");
+CONCEPT_REGISTER_FUNCTION(F32, F32, NAN);
+CONCEPT_REGISTER_FUNCTION(F64, F64, NAN);
+CONCEPT_REGISTER_FUNCTION(S32, Enum, -1); // For enums: set default to -1
+CONCEPT_REGISTER_FUNCTION(S32, S32, 0); // For values: set default to 0
+
+static void conceptRegisterTime(const char *name, /* Name of concept */ \
+                                const char *comment, /* Comment for concept */ \
+                                bool required, /* Required concept? */ \
+                                pmFPALevel level /* Level at which concept applies */ \
+    )
+{
+    psTime *time = psTimeAlloc(PS_TIME_TAI); // Blank time
+    // Not particularly distinguishing, but should be good enough
+    time->sec = 0;
+    time->nsec = 0;
+    psMetadataItem *item = psMetadataItemAlloc(name, PS_DATA_TIME, comment, time);
+    psFree(time);
+    pmConceptRegister(item, (pmConceptParseFunc)p_pmConceptParse_TIME,
+                      (pmConceptFormatFunc)p_pmConceptFormat_TIME, required, level);
+    psFree(item);
+}
 
 bool pmConceptsInit(void)
@@ -470,4 +508,5 @@
 
     bool init = false;                  // Did we initialise anything?
+
     if (! conceptsFPA) {
         conceptsFPA = psMetadataAlloc();
@@ -475,284 +514,57 @@
 
         // Install the standard concepts
-
-        // FPA.TELESCOPE
-        {
-            psMetadataItem *fpaTelescope = psMetadataItemAllocStr("FPA.TELESCOPE", "Camera used", "");
-            pmConceptRegister(fpaTelescope, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(fpaTelescope);
-        }
-
-        // FPA.INSTRUMENT
-        {
-            psMetadataItem *fpaInstrument = psMetadataItemAllocStr("FPA.INSTRUMENT", "Camera used", "");
-            pmConceptRegister(fpaInstrument, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(fpaInstrument);
-        }
-
-        // FPA.DETECTOR
-        {
-            psMetadataItem *fpaDetector = psMetadataItemAllocStr("FPA.DETECTOR", "Detector used", "");
-            pmConceptRegister(fpaDetector, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(fpaDetector);
-        }
-
-        // FPA.CAMERA
-        // XXX keep or deprecate?
-        {
-            psMetadataItem *fpaCamera = psMetadataItemAllocStr("FPA.CAMERA", "Camera used", "");
-            pmConceptRegister(fpaCamera, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(fpaCamera);
-        }
-
-        // FPA.COMMENT
-        {
-            psMetadataItem *item = psMetadataItemAllocStr("FPA.COMMENT", "Obs Comment", "");
-            pmConceptRegister(item, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(item);
-        }
-
-        // FPA.FOCUS
-        {
-            psMetadataItem *fpaFocus = psMetadataItemAllocF32("FPA.FOCUS", "Telescope focus", NAN);
-            pmConceptRegister(fpaFocus, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(fpaFocus);
-        }
-
-        // FPA.AIRMASS
-        {
-            psMetadataItem *fpaAirmass = psMetadataItemAllocF32("FPA.AIRMASS", "Airmass at boresight", NAN);
-            pmConceptRegister(fpaAirmass, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(fpaAirmass);
-        }
-
-        // FPA.FILTERID
-        {
-            psMetadataItem *fpaFilterid = psMetadataItemAllocStr("FPA.FILTERID",
-                                                                 "Filter used (parsed, abstract name) ", "");
-            pmConceptRegister(fpaFilterid, (pmConceptParseFunc)p_pmConceptParse_FPA_FILTER,
-                              (pmConceptFormatFunc)p_pmConceptFormat_FPA_FILTER, false, PM_FPA_LEVEL_FPA);
-            psFree(fpaFilterid);
-        }
-
-        // FPA.FILTER
-        {
-            psMetadataItem *fpaFilter = psMetadataItemAllocStr("FPA.FILTER", "Filter used", "");
-            pmConceptRegister(fpaFilter, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(fpaFilter);
-        }
-
-        // FPA.POSANGLE
-        {
-            psMetadataItem *fpaPosangle = psMetadataItemAllocF32("FPA.POSANGLE",
-                                          "Position angle of instrument", NAN);
-            pmConceptRegister(fpaPosangle, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(fpaPosangle);
-        }
-
-        // FPA.RADECSYS
-        {
-            psMetadataItem *fpaRadecsys = psMetadataItemAllocStr("FPA.RADECSYS",
-                                          "Celestial coordinate system", "");
-            pmConceptRegister(fpaRadecsys, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(fpaRadecsys);
-        }
-
-        // FPA.RA
-        {
-            psMetadataItem *fpaRa = psMetadataItemAllocF64("FPA.RA", "Right Ascension of boresight", NAN);
-            pmConceptRegister(fpaRa, (pmConceptParseFunc)p_pmConceptParse_FPA_Coords,
-                              (pmConceptFormatFunc)p_pmConceptFormat_FPA_Coords, false, PM_FPA_LEVEL_FPA);
-            psFree(fpaRa);
-        }
-
-        // FPA.DEC
-        {
-            psMetadataItem *fpaDec = psMetadataItemAllocF64("FPA.DEC", "Declination of boresight", NAN);
-            pmConceptRegister(fpaDec, (pmConceptParseFunc)p_pmConceptParse_FPA_Coords,
-                              (pmConceptFormatFunc)p_pmConceptFormat_FPA_Coords, false, PM_FPA_LEVEL_FPA);
-            psFree(fpaDec);
-        }
-
-        // FPA.OBSTYPE
-        {
-            psMetadataItem *fpaObstype = psMetadataItemAllocStr("FPA.OBSTYPE", "Type of observation", "");
-            pmConceptRegister(fpaObstype, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(fpaObstype);
-        }
-
-        // FPA.OBJECT
-        {
-            psMetadataItem *fpaObject = psMetadataItemAllocStr("FPA.OBJECT", "Object of observation", "");
-            pmConceptRegister(fpaObject, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(fpaObject);
-        }
-
-        // FPA.ALT
-        {
-            psMetadataItem *fpaAlt = psMetadataItemAllocF64("FPA.ALT", "Altitude of telescope", NAN);
-            pmConceptRegister(fpaAlt, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(fpaAlt);
-        }
-
-        // FPA.AZ
-        {
-            psMetadataItem *fpaAz = psMetadataItemAllocF64("FPA.AZ", "Azimuth of telescope", NAN);
-            pmConceptRegister(fpaAz, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(fpaAz);
-        }
-
-        // FPA.TIMESYS
-        {
-            psMetadataItem *fpaTimesys = psMetadataItemAllocS32("FPA.TIMESYS", "Time system", -1);
-            pmConceptRegister(fpaTimesys, (pmConceptParseFunc)p_pmConceptParse_TIMESYS,
-                              (pmConceptFormatFunc)p_pmConceptFormat_TIMESYS, false, PM_FPA_LEVEL_FPA);
-            psFree(fpaTimesys);
-        }
-
-        // FPA.TIME
-        {
-            psTime *time = psTimeAlloc(PS_TIME_TAI); // Blank time
-            // Not particularly distinguishing, but should be good enough
-            time->sec = 0;
-            time->nsec = 0;
-            psMetadataItem *fpaTime = psMetadataItemAlloc("FPA.TIME", PS_DATA_TIME,
-                                      "Time of exposure", time);
-            psFree(time);
-            pmConceptRegister(fpaTime, (pmConceptParseFunc)p_pmConceptParse_TIME,
-                              (pmConceptFormatFunc)p_pmConceptFormat_TIME, false, PM_FPA_LEVEL_FPA);
-            psFree(fpaTime);
-        }
-
-        // FPA.TEMP
-        {
-            psMetadataItem *fpaTemp = psMetadataItemAllocF32("FPA.TEMP", "Temperature of focal plane", NAN);
-            pmConceptRegister(fpaTemp, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(fpaTemp);
-        }
-
-        // FPA.M1X,Y,Z,TIP,TILT
-        {
-            psMetadataItem *item = psMetadataItemAllocF32("FPA.M1X", "Primary Mirror X Position", NAN);
-            pmConceptRegister(item, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(item);
-        }
-        {
-            psMetadataItem *item = psMetadataItemAllocF32("FPA.M1Y", "Primary Mirror Y Position", NAN);
-            pmConceptRegister(item, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(item);
-        }
-        {
-            psMetadataItem *item = psMetadataItemAllocF32("FPA.M1Z", "Primary Mirror Z Position", NAN);
-            pmConceptRegister(item, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(item);
-        }
-        {
-            psMetadataItem *item = psMetadataItemAllocF32("FPA.M1TIP", "Primary Mirror TIP", NAN);
-            pmConceptRegister(item, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(item);
-        }
-        {
-            psMetadataItem *item = psMetadataItemAllocF32("FPA.M1TILT", "Primary Mirror TILT", NAN);
-            pmConceptRegister(item, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(item);
-        }
-
-        // FPA.M2X,Y,Z,TIP,TILT
-        {
-            psMetadataItem *item = psMetadataItemAllocF32("FPA.M2X", "Primary Mirror X Position", NAN);
-            pmConceptRegister(item, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(item);
-        }
-        {
-            psMetadataItem *item = psMetadataItemAllocF32("FPA.M2Y", "Primary Mirror Y Position", NAN);
-            pmConceptRegister(item, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(item);
-        }
-        {
-            psMetadataItem *item = psMetadataItemAllocF32("FPA.M2Z", "Primary Mirror Z Position", NAN);
-            pmConceptRegister(item, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(item);
-        }
-        {
-            psMetadataItem *item = psMetadataItemAllocF32("FPA.M2TIP", "Primary Mirror TIP", NAN);
-            pmConceptRegister(item, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(item);
-        }
-        {
-            psMetadataItem *item = psMetadataItemAllocF32("FPA.M2TILT", "Primary Mirror TILT", NAN);
-            pmConceptRegister(item, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(item);
-        }
-
-        // FPA.ENV.*
-        {
-            psMetadataItem *item = psMetadataItemAllocF32("FPA.ENV.TEMP", "Environment: Temperature", NAN);
-            pmConceptRegister(item, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(item);
-        }
-        {
-            psMetadataItem *item = psMetadataItemAllocF32("FPA.ENV.HUMID", "Environment: Humidity", NAN);
-            pmConceptRegister(item, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(item);
-        }
-        {
-            psMetadataItem *item = psMetadataItemAllocF32("FPA.ENV.WIND", "Environment: Wind Speed", NAN);
-            pmConceptRegister(item, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(item);
-        }
-        {
-            psMetadataItem *item = psMetadataItemAllocF32("FPA.ENV.DIR", "Environment: Wind Direction", NAN);
-            pmConceptRegister(item, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(item);
-        }
-
-        // FPA.TELTEMP.*
-        {
-            psMetadataItem *item = psMetadataItemAllocF32("FPA.TELTEMP.M1", "Telescope Temperatures: M1", NAN);
-            pmConceptRegister(item, p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(item);
-        }
-        {
-            psMetadataItem *item = psMetadataItemAllocF32("FPA.TELTEMP.M1CELL", "Telescope Temperatures: M1 CELL", NAN);
-            pmConceptRegister(item, p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(item);
-        }
-        {
-            psMetadataItem *item = psMetadataItemAllocF32("FPA.TELTEMP.M2", "Telescope Temperatures: M2", NAN);
-            pmConceptRegister(item, p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(item);
-        }
-        {
-            psMetadataItem *item = psMetadataItemAllocF32("FPA.TELTEMP.SPIDER", "Telescope Temperatures: SPIDER", NAN);
-            pmConceptRegister(item, p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(item);
-        }
-        {
-            psMetadataItem *item = psMetadataItemAllocF32("FPA.TELTEMP.TRUSS", "Telescope Temperatures: TRUSS", NAN);
-            pmConceptRegister(item, p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(item);
-        }
-        {
-            psMetadataItem *item = psMetadataItemAllocF32("FPA.TELTEMP.EXTRA", "Telescope Temperatures: EXTRA", NAN);
-            pmConceptRegister(item, p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(item);
-        }
-
-        // FPA.PON.TIME
-        {
-            psMetadataItem *item = psMetadataItemAllocF32("FPA.PON.TIME", "Power On Time", NAN);
-            pmConceptRegister(item, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(item);
-        }
-
-        // FPA.EXPOSURE
-        {
-            psMetadataItem *fpaExposure = psMetadataItemAllocF32("FPA.EXPOSURE",
-                                          "Exposure time (sec)", NAN);
-            pmConceptRegister(fpaExposure, NULL, NULL, false, PM_FPA_LEVEL_FPA);
-            psFree(fpaExposure);
-        }
-
-        // Done with FPA level concepts
+        conceptRegisterStr("FPA.TELESCOPE", "Telescope of origin", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterStr("FPA.INSTRUMENT", "Instrument name (according to the instrument)",
+                           NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterStr("FPA.DETECTOR", "Detector name", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterStr("FPA.COMMENT", "Observation comment", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.FOCUS", "Telescope focus", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.AIRMASS", "Airmass at boresight", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterStr("FPA.FILTERID", "Filter used (parsed, abstract name)",
+                           NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterStr("FPA.FILTER", "Filter used (instrument name)",
+                           NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.POSANGLE", "Position angle of instrument",
+                           NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterStr("FPA.RADECSYS", "Celestial coordinate system",
+                           NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF64("FPA.RA", "Right Ascension of boresight", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF64("FPA.DEC", "Declination of boresight", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterStr("FPA.OBSTYPE", "Type of observation", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterStr("FPA.OBJECT", "Object of observation", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF64("FPA.ALT", "Altitude of boresight", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF64("FPA.AZ", "Azimuth of boresight", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterEnum("FPA.TIMESYS", "Time system", p_pmConceptParse_TIMESYS,
+                            p_pmConceptFormat_TIMESYS, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.TEMP", "Temperature of focal plane", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.M1X", "Primary Mirror X Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.M1Y", "Primary Mirror Y Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.M1Z", "Primary Mirror Z Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.M1TIP", "Primary Mirror TIP", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.M1TILT", "Primary Mirror TILT", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.M2X", "Primary Mirror X Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.M2Y", "Primary Mirror Y Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.M2Z", "Primary Mirror Z Position", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.M2TIP", "Primary Mirror TIP", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.M2TILT", "Primary Mirror TILT", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.ENV.TEMP", "Environment: Temperature", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.ENV.HUMID", "Environment: Humidity", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.ENV.WIND", "Environment: Wind speed", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.ENV.DIR", "Environment: Wind direction", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.TELTEMP.M1", "Telescope Temperatures: M1",
+                           p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.TELTEMP.M1CELL", "Telescope Temperatures: M1 cell",
+                           p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.TELTEMP.M2", "Telescope Temperatures: M2",
+                           p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.TELTEMP.SPIDER", "Telescope Temperatures: spider",
+                           p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.TELTEMP.TRUSS", "Telescope Temperatures: truss",
+                           p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.TELTEMP.EXTRA", "Telescope Temperatures: extra",
+                           p_pmConceptParse_TELTEMPS, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.PON.TIME", "Power On Time", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterF32("FPA.EXPOSURE", "Exposure time (sec)", NULL, NULL, false, PM_FPA_LEVEL_FPA);
+        conceptRegisterTime("FPA.TIME", "Time of exposure", false, PM_FPA_LEVEL_FPA);
     }
     if (! conceptsChip) {
@@ -761,67 +573,21 @@
 
         // Install the standard concepts
-
-        // CHIP.XPARITY
-        {
-            psMetadataItem *chipXparity = psMetadataItemAllocS32("CHIP.XPARITY",
-                                          "Orientation in x compared to the rest of the FPA", 0);
-            pmConceptRegister(chipXparity, NULL, NULL, true, PM_FPA_LEVEL_CHIP);
-            psFree(chipXparity);
-        }
-
-        // CHIP.YPARITY
-        {
-            psMetadataItem *chipYparity = psMetadataItemAllocS32("CHIP.YPARITY",
-                                          "Orientation in y compared to the rest of the FPA", 0);
-            pmConceptRegister(chipYparity, NULL, NULL, true, PM_FPA_LEVEL_CHIP);
-            psFree(chipYparity);
-        }
-
-        // CHIP.X0
-        {
-            psMetadataItem *chipX0 = psMetadataItemAllocS32("CHIP.X0", "Position of (0,0) on the FPA", 0);
-            pmConceptRegister(chipX0, (pmConceptParseFunc)p_pmConceptParse_Positions,
-                              (pmConceptFormatFunc)p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CHIP);
-            psFree(chipX0);
-        }
-
-        // CHIP.Y0
-        {
-            psMetadataItem *chipY0 = psMetadataItemAllocS32("CHIP.Y0", "Position of (0,0) on the FPA", 0);
-            pmConceptRegister(chipY0, (pmConceptParseFunc)p_pmConceptParse_Positions,
-                              (pmConceptFormatFunc)p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CHIP);
-            psFree(chipY0);
-        }
-
-        // CHIP.XSIZE
-        {
-            psMetadataItem *chipXsize = psMetadataItemAllocS32("CHIP.XSIZE", "Size of chip (pixels)", 0);
-            pmConceptRegister(chipXsize, NULL, NULL, true, PM_FPA_LEVEL_CHIP);
-            psFree(chipXsize);
-        }
-
-        // CHIP.YSIZE
-        {
-            psMetadataItem *chipYsize = psMetadataItemAllocS32("CHIP.YSIZE", "Size of chip (pixels)", 0);
-            pmConceptRegister(chipYsize, NULL, NULL, true, PM_FPA_LEVEL_CHIP);
-            psFree(chipYsize);
-        }
-
-        // CHIP.TEMP
-        {
-            psMetadataItem *chipTemp = psMetadataItemAllocF32("CHIP.TEMP", "Temperature of chip", NAN);
-            pmConceptRegister(chipTemp, NULL, NULL, false, PM_FPA_LEVEL_CHIP);
-            psFree(chipTemp);
-        }
-
-        // CHIP.ID
-        {
-            psMetadataItem *chipID = psMetadataItemAllocStr("CHIP.ID", "Chip identifier", "");
-            pmConceptRegister(chipID, NULL, NULL, false, PM_FPA_LEVEL_CHIP);
-            psFree(chipID);
-        }
-
-        // Done with chip level concepts
-    }
+        conceptRegisterS32("CHIP.XPARITY", "Orientation in x compared to the rest of the FPA",
+                           NULL, NULL, true, PM_FPA_LEVEL_CHIP);
+
+        conceptRegisterS32("CHIP.YPARITY", "Orientation in y compared to the rest of the FPA",
+                           NULL, NULL, true, PM_FPA_LEVEL_CHIP);
+        conceptRegisterS32("CHIP.X0", "Position of (0,0) on the FPA",
+                           (pmConceptParseFunc)p_pmConceptParse_Positions,
+                           (pmConceptFormatFunc)p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CHIP);
+        conceptRegisterS32("CHIP.Y0", "Position of (0,0) on the FPA",
+                           (pmConceptParseFunc)p_pmConceptParse_Positions,
+                           (pmConceptFormatFunc)p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CHIP);
+        conceptRegisterS32("CHIP.XSIZE", "Size of chip (pixels)", NULL, NULL, true, PM_FPA_LEVEL_CHIP);
+        conceptRegisterS32("CHIP.YSIZE", "Size of chip (pixels)", NULL, NULL, true, PM_FPA_LEVEL_CHIP);
+        conceptRegisterF32("CHIP.TEMP", "Temperature of chip", NULL, NULL, false, PM_FPA_LEVEL_CHIP);
+        conceptRegisterStr("CHIP.ID", "Chip identifier", NULL, NULL, false, PM_FPA_LEVEL_CHIP);
+    }
+
     if (! conceptsCell) {
         conceptsCell = psMetadataAlloc();
@@ -829,59 +595,15 @@
 
         // Install the standard concepts
-
-        // CELL.GAIN
-        {
-            psMetadataItem *cellGain = psMetadataItemAllocF32("CELL.GAIN", "CCD gain (e/count)", NAN);
-            pmConceptRegister(cellGain, NULL, NULL, true, PM_FPA_LEVEL_CELL);
-            psFree(cellGain);
-        }
-
-        // CELL.READNOISE
-        {
-            psMetadataItem *cellReadnoise = psMetadataItemAllocF32("CELL.READNOISE",
-                                            "CCD read noise (e)", NAN);
-            pmConceptRegister(cellReadnoise, NULL, NULL, true, PM_FPA_LEVEL_CELL);
-            psFree(cellReadnoise);
-        }
-
-        // CELL.SATURATION
-        {
-            psMetadataItem *cellSaturation = psMetadataItemAllocF32("CELL.SATURATION",
-                                             "Saturation level (counts)", NAN);
-            pmConceptRegister(cellSaturation, NULL, NULL, true, PM_FPA_LEVEL_CELL);
-            psFree(cellSaturation);
-        }
-
-        // CELL.BAD
-        {
-            psMetadataItem *cellBad = psMetadataItemAllocF32("CELL.BAD", "Bad level (counts)", NAN);
-            pmConceptRegister(cellBad, NULL, NULL, true, PM_FPA_LEVEL_CELL);
-            psFree(cellBad);
-        }
-
-        // CELL.XPARITY
-        {
-            psMetadataItem *cellXparity = psMetadataItemAllocS32("CELL.XPARITY",
-                                          "Orientation in x compared to the rest of the chip", 0);
-            pmConceptRegister(cellXparity, NULL, NULL, true, PM_FPA_LEVEL_CELL);
-            psFree(cellXparity);
-        }
-
-        // CELL.YPARITY
-        {
-            psMetadataItem *cellYparity = psMetadataItemAllocS32("CELL.YPARITY",
-                                          "Orientation in y compared to the rest of the chip", 0);
-            pmConceptRegister(cellYparity, NULL, NULL, true, PM_FPA_LEVEL_CELL);
-            psFree(cellYparity);
-        }
-
-        // CELL.READDIR
-        {
-            psMetadataItem *cellReaddir = psMetadataItemAllocS32("CELL.READDIR",
-                                          "Read direction, rows=1, cols=2", 0);
-            pmConceptRegister(cellReaddir, NULL, NULL, true, PM_FPA_LEVEL_CELL);
-            psFree(cellReaddir);
-        }
-
+        conceptRegisterF32("CELL.GAIN", "CCD gain (e/count)", NULL, NULL, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterF32("CELL.READNOISE", "CCD read noise (e)", NULL, NULL, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterF32("CELL.SATURATION", "Saturation level (counts)",
+                           NULL, NULL, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterF32("CELL.BAD", "Bad level (counts)", NULL, NULL, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterS32("CELL.XPARITY", "Orientation in x compared to the rest of the chip",
+                           NULL, NULL, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterS32("CELL.YPARITY", "Orientation in y compared to the rest of the chip",
+                           NULL, NULL, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterS32("CELL.READDIR", "Read direction, rows=1, cols=2",
+                           NULL, NULL, true, PM_FPA_LEVEL_CELL);
 
         // These (CELL.EXPOSURE and CELL.DARKTIME) used to be READOUT.EXPOSURE and READOUT.DARKTIME, but that
@@ -890,20 +612,28 @@
         // 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, false, PM_FPA_LEVEL_CELL);
-            psFree(cellExposure);
-        }
-
-        // CELL.DARKTIME
-        {
-            psMetadataItem *cellDarktime = psMetadataItemAllocF32("CELL.DARKTIME",
-                                           "Time since flush (sec)", NAN);
-            pmConceptRegister(cellDarktime, NULL, NULL, false, PM_FPA_LEVEL_CELL);
-            psFree(cellDarktime);
-        }
+        conceptRegisterF32("CELL.EXPOSURE", "Exposure time (sec)", NULL, NULL, false, PM_FPA_LEVEL_CELL);
+        conceptRegisterF32("CELL.DARKTIME", "Time since flush (sec)", NULL, NULL, false, PM_FPA_LEVEL_CELL);
+
+        conceptRegisterS32("CELL.XBIN", "Binning in x", (pmConceptParseFunc)p_pmConceptParse_CELL_Binning,
+                           (pmConceptFormatFunc)p_pmConceptFormat_CELL_XBIN, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterS32("CELL.YBIN", "Binning in y",(pmConceptParseFunc)p_pmConceptParse_CELL_Binning,
+                           (pmConceptFormatFunc)p_pmConceptFormat_CELL_YBIN, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterEnum("CELL.TIMESYS", "Time system", (pmConceptParseFunc)p_pmConceptParse_TIMESYS,
+                            (pmConceptFormatFunc)p_pmConceptFormat_TIMESYS, false, PM_FPA_LEVEL_CELL);
+        conceptRegisterTime("CELL.TIME", "Time of exposure", false, PM_FPA_LEVEL_CELL);
+        conceptRegisterS32("CELL.X0", "Position of (0,0) on the chip",
+                           (pmConceptParseFunc)p_pmConceptParse_Positions,
+                           (pmConceptFormatFunc)p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterS32("CELL.Y0", "Position of (0,0) on the chip",
+                           (pmConceptParseFunc)p_pmConceptParse_Positions,
+                           (pmConceptFormatFunc)p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterS32("CELL.XSIZE", "Size of cell (pixels)", NULL, NULL, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterS32("CELL.YSIZE", "Size of cell (pixels)", NULL, NULL, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterS32("CELL.XWINDOW", "Start of cell window (pixels)",
+                           (pmConceptParseFunc)p_pmConceptParse_Positions,
+                           (pmConceptFormatFunc)p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CELL);
+        conceptRegisterS32("CELL.YWINDOW", "Start of cell window (pixels)",
+                           (pmConceptParseFunc)p_pmConceptParse_Positions,
+                           (pmConceptFormatFunc)p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CELL);
 
         // CELL.TRIMSEC
@@ -930,89 +660,4 @@
         }
 
-        // CELL.XBIN
-        {
-            psMetadataItem *cellXbin = psMetadataItemAllocS32("CELL.XBIN", "Binning in x", 0);
-            pmConceptRegister(cellXbin, (pmConceptParseFunc)p_pmConceptParse_CELL_Binning,
-                              (pmConceptFormatFunc)p_pmConceptFormat_CELL_XBIN, true, PM_FPA_LEVEL_CELL);
-            psFree(cellXbin);
-        }
-
-        // CELL.YBIN
-        {
-            psMetadataItem *cellYbin = psMetadataItemAllocS32("CELL.YBIN", "Binning in y", 0);
-            pmConceptRegister(cellYbin, (pmConceptParseFunc)p_pmConceptParse_CELL_Binning,
-                              (pmConceptFormatFunc)p_pmConceptFormat_CELL_YBIN, true, PM_FPA_LEVEL_CELL);
-            psFree(cellYbin);
-        }
-
-        // CELL.TIMESYS
-        {
-            psMetadataItem *cellTimesys = psMetadataItemAllocS32("CELL.TIMESYS", "Time system", -1);
-            pmConceptRegister(cellTimesys, (pmConceptParseFunc)p_pmConceptParse_TIMESYS,
-                              (pmConceptFormatFunc)p_pmConceptFormat_TIMESYS, false, PM_FPA_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)p_pmConceptParse_TIME,
-                              (pmConceptFormatFunc)p_pmConceptFormat_TIME, false, PM_FPA_LEVEL_CELL);
-            psFree(cellTime);
-        }
-
-        // CELL.X0
-        {
-            psMetadataItem *cellX0 = psMetadataItemAllocS32("CELL.X0", "Position of (0,0) on the chip", 0);
-            pmConceptRegister(cellX0, (pmConceptParseFunc)p_pmConceptParse_Positions,
-                              (pmConceptFormatFunc)p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CELL);
-            psFree(cellX0);
-        }
-
-        // CELL.Y0
-        {
-            psMetadataItem *cellY0 = psMetadataItemAllocS32("CELL.Y0", "Position of (0,0) on the chip", 0);
-            pmConceptRegister(cellY0, (pmConceptParseFunc)p_pmConceptParse_Positions,
-                              (pmConceptFormatFunc)p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CELL);
-            psFree(cellY0);
-        }
-
-        // CELL.XSIZE
-        {
-            psMetadataItem *cellXsize = psMetadataItemAllocS32("CELL.XSIZE", "Size of cell (pixels)", 0);
-            pmConceptRegister(cellXsize, NULL, NULL, true, PM_FPA_LEVEL_CELL);
-            psFree(cellXsize);
-        }
-
-        // CELL.YSIZE
-        {
-            psMetadataItem *cellYsize = psMetadataItemAllocS32("CELL.YSIZE", "Size of cell (pixels)", 0);
-            pmConceptRegister(cellYsize, NULL, NULL, true, PM_FPA_LEVEL_CELL);
-            psFree(cellYsize);
-        }
-
-        // CELL.XWINDOW
-        {
-            psMetadataItem *cellXwindow = psMetadataItemAllocS32("CELL.XWINDOW",
-                                                                 "Start of cell window (pixels)", 0);
-            pmConceptRegister(cellXwindow, (pmConceptParseFunc)p_pmConceptParse_Positions,
-                              (pmConceptFormatFunc)p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CELL);
-            psFree(cellXwindow);
-        }
-
-        // CELL.YWINDOW
-        {
-            psMetadataItem *cellYwindow = psMetadataItemAllocS32("CELL.YWINDOW",
-                                                                 "Start of cell window (pixels)", 0);
-            pmConceptRegister(cellYwindow, (pmConceptParseFunc)p_pmConceptParse_Positions,
-                              (pmConceptFormatFunc)p_pmConceptFormat_Positions, true, PM_FPA_LEVEL_CELL);
-            psFree(cellYwindow);
-        }
     }
 
@@ -1037,5 +682,5 @@
 // List of concepts not to copy, for each level.
 // Must be NULL-terminated
-static const char *dontCopyConceptsFPA[] = { "FPA.NAME", 0 };
+static const char *dontCopyConceptsFPA[] = { "FPA.NAME", "FPA.CAMERA", 0 };
 static const char *dontCopyConceptsChip[] = { "CHIP.NAME", 0 };
 static const char *dontCopyConceptsCell[] = { "CELL.NAME", "CELL.TRIMSEC", "CELL.BIASSEC", 0 };
@@ -1202,5 +847,5 @@
 // Interpolate the concept.  Generalises the FPA/Chip/Cell
 #define CONCEPT_INTERPOLATE(SOURCE, NAME) \
-    if (strncmp(concept, NAME, 4) == 0) { \
+    if (strncmp(concept, NAME, strlen(NAME)) == 0) { \
         if (!(SOURCE)) { \
             psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Cannot interpolate %s because %s not provided", \
@@ -1274,2 +919,49 @@
     return string;
 }
+
+
+psMetadataItem *p_pmConceptsDepend(const char *name, const psMetadata *menu, const psMetadata *source,
+                                   const pmFPA *fpa, const pmChip *chip, const pmCell *cell)
+{
+    psAssert(name && strlen(name) > 0, "Concept name is empty");
+    psAssert(menu, "Must have menu");
+    psAssert(source, "Must have source");
+
+    // Check for DEPEND
+    psString depend = NULL; // The CONCEPT.DEPEND
+    psStringAppend(&depend, "%s.DEPEND", name);
+    bool mdok;                          // Status of MD lookup
+    const char *dependConcept = psMetadataLookupStr(&mdok, source, depend); // The concept name
+    if (!mdok || !dependConcept || strlen(dependConcept) == 0) {
+        psError(PS_ERR_IO, true, "Unable to parse %s: couldn't find %s in DEFAULTS.\n", name, depend);
+        psFree(depend);
+        return NULL;
+    }
+    psFree(depend);
+    // Now look up the depend value
+    psMetadataItem *dependValue = NULL; // The value of the concept we're looking up
+    if (cell) {
+        dependValue = psMetadataLookup(cell->concepts, dependConcept);
+    }
+    if (chip && !dependValue) {
+        dependValue = psMetadataLookup(chip->concepts, dependConcept);
+    }
+    if (fpa && !dependValue) {
+        dependValue = psMetadataLookup(chip->concepts, dependConcept);
+    }
+    if (!dependValue) {
+        // Not an error --- it may be specified some other way
+        psTrace("psModules.concepts", 7, "Couldn't find DEPEND for %s", name);
+        return NULL;
+    }
+    if (dependValue->type != PS_DATA_STRING) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "%s is required to resolve %s in DEFAULTS, "
+                "but it is not of type STRING.\n", dependConcept, name);
+        return NULL;
+    }
+    const char *key = dependValue->data.V; // The key to the DEPEND menu
+    psTrace("psModules.concepts", 7, "%s.DEPEND resolves to %s....\n", name, key);
+
+    return psMetadataLookup(menu, key);
+}
+
Index: trunk/psModules/src/concepts/pmConcepts.h
===================================================================
--- trunk/psModules/src/concepts/pmConcepts.h	(revision 17898)
+++ trunk/psModules/src/concepts/pmConcepts.h	(revision 17911)
@@ -4,6 +4,6 @@
  * @author Paul Price, IfA
  *
- * @version $Revision: 1.16 $ $Name: not supported by cvs2svn $
- * @date $Date: 2008-05-30 21:35:51 $
+ * @version $Revision: 1.17 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-06-05 01:31:33 $
  * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
  */
@@ -231,4 +231,14 @@
     );
 
+/// Look up a dependency menu to get a concept's value
+///
+/// Returns a psMetadataItem with the concept value
+psMetadataItem *p_pmConceptsDepend(const char *name, ///< Name of concept for which to get dependent value
+                                   const psMetadata *menu, ///< Menu in which to look up key
+                                   const psMetadata *source, ///< Source metadata with CONCEPT.DEPEND
+                                   const pmFPA *fpa, ///< FPA for dependency
+                                   const pmChip *chip, ///< Chip for dependency
+                                   const pmCell *cell ///< Cell for dependency
+    );
 
 /// @}
Index: trunk/psModules/src/concepts/pmConceptsRead.c
===================================================================
--- trunk/psModules/src/concepts/pmConceptsRead.c	(revision 17898)
+++ trunk/psModules/src/concepts/pmConceptsRead.c	(revision 17911)
@@ -181,4 +181,19 @@
 }
 
+psMetadataItem *p_pmConceptsReadSingleFromDefaults(const char *name, const psMetadata *defaults,
+                                                  const pmFPA *fpa, const pmChip *chip, const pmCell *cell)
+{
+    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
+    PS_ASSERT_METADATA_NON_NULL(defaults, NULL);
+
+    psMetadataItem *item = psMetadataLookup(defaults, name); // The concept, or NULL
+    psTrace("psModules.concepts", 10, "%s: %p\n", name, item);
+    if (item && item->type == PS_DATA_METADATA) {
+        // This is a menu
+        psTrace("psModules.concepts", 5, "%s is of type METADATA.\n", name);
+        item = p_pmConceptsDepend(name, item->data.md, defaults, fpa, chip, cell);
+    }
+    return item;
+}
 
 bool p_pmConceptsReadFromDefaults(psMetadata *target, const psMetadata *specs,
@@ -209,47 +224,5 @@
         pmConceptSpec *spec = specItem->data.V; // The specification
         psString name = specItem->name; // The concept name
-        psMetadataItem *conceptItem = psMetadataLookup(defaults, name); // The concept, or NULL
-        psTrace("psModules.concepts", 10, "%s: %p\n", name, conceptItem);
-        if (conceptItem && conceptItem->type == PS_DATA_METADATA) {
-            psTrace("psModules.concepts", 5, "%s is of type METADATA.\n", name);
-            // Check for DEPEND
-            psMetadata *dependMenu = conceptItem->data.V; // The DEPEND menu
-            psString depend = NULL; // The CONCEPT.DEPEND
-            psStringAppend(&depend, "%s.DEPEND", name);
-            const char *dependConcept = psMetadataLookupStr(&mdok, defaults, depend); // The concept name
-            if (!mdok || !dependConcept || strlen(dependConcept) == 0) {
-                psError(PS_ERR_IO, true, "Unable to parse %s: couldn't find %s in DEFAULTS.\n", name,
-                        depend);
-                psFree(depend);
-                continue;
-            }
-            psFree(depend);
-            // Now look up the depend value
-            psMetadataItem *dependValue = NULL; // The value of the concept we're looking up
-            if (cell) {
-                dependValue = psMetadataLookup(cell->concepts, dependConcept);
-            }
-            if (chip && !dependValue) {
-                dependValue = psMetadataLookup(chip->concepts, dependConcept);
-            }
-            if (fpa && !dependValue) {
-                dependValue = psMetadataLookup(chip->concepts, dependConcept);
-            }
-            if (!dependValue) {
-                psError(PS_ERR_IO, true, "Unable to find %s to resolve %s in DEFAULTS.\n",
-                        dependConcept, name);
-                continue;
-            }
-            if (dependValue->type != PS_DATA_STRING) {
-                psError(PS_ERR_BAD_PARAMETER_TYPE, true, "%s is required to resolve %s in DEFAULTS, "
-                        "but it is not of type STRING.\n", dependConcept, name);
-                continue;
-            }
-            const char *dependKey = dependValue->data.V; // The key to the DEPEND menu
-            psTrace("psModules.concepts", 7, "%s.DEPEND resolves to %s....\n", name, dependKey);
-
-            conceptItem = psMetadataLookup(dependMenu, dependKey);
-            // Now we can parse this as we would ordinarily
-        }
+        psMetadataItem *conceptItem = p_pmConceptsReadSingleFromDefaults(name, defaults, fpa, chip, cell);
         if (conceptItem && !conceptParse(spec, conceptItem, PM_CONCEPT_SOURCE_DEFAULTS,
                                          cameraFormat, target, fpa, chip, cell)) {
@@ -282,5 +255,5 @@
     psMetadata *cameraFormat = hduLow->format; // The camera format
     bool mdok = true;                   // Status of MD lookup
-    psMetadata *transSpec = psMetadataLookupMetadata(&mdok, cameraFormat, "TRANSLATION"); // The TRANSLATION spec
+    psMetadata *transSpec = psMetadataLookupMetadata(&mdok, cameraFormat, "TRANSLATION"); // TRANSLATION spec
     if (!mdok || !transSpec) {
         psError(PS_ERR_IO, true, "Failed to find \"TRANSLATION\"");
@@ -318,42 +291,59 @@
         }
 
-        if (! headerItem) {
-            psString keywords = psMetadataLookupStr(&mdok, transSpec, name); // The FITS keywords
-            if (mdok && strlen(keywords) > 0) {
-                // In case there are multiple headers
-                psList *keys = psStringSplit(keywords, " ,;", true); // List of keywords
-                if (keys->n == 1) {
-                    // Only one key --- proceed as usual
+        if (!headerItem) {
+            psMetadataItem *formatItem = psMetadataLookup(transSpec, name); // Item with keyword
+            if (!formatItem) {
+                continue;
+            }
+            if (formatItem->type == PS_DATA_METADATA) {
+                // This is a menu
+                psTrace("psModules.concepts", 5, "%s is of type METADATA.\n", name);
+                formatItem = p_pmConceptsDepend(name, formatItem->data.md, transSpec, fpa, chip, cell);
+                if (!formatItem) {
+                    continue;
+                }
+            }
+            if (formatItem->type != PS_DATA_STRING) {
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Type for concept %s in TRANSLATION is not STR",
+                        name);
+                psFree(specsIter);
+                return false;
+            }
+            psString keywords = formatItem->data.str; // The FITS keywords
+
+            // In case there are multiple headers
+            psList *keys = psStringSplit(keywords, " ,;", true); // List of keywords
+            if (keys->n == 1) {
+                // Only one key --- proceed as usual
+                if (hduLow->header) {
+                    headerItem = psMetadataLookup(hduLow->header, keywords);
+                }
+                if (!headerItem && hduHigh != hduLow && hduHigh->header) {
+                    headerItem = psMetadataLookup(hduHigh->header, keywords);
+                }
+                psMemIncrRefCounter(headerItem);
+            } else {
+                psListIterator *keysIter = psListIteratorAlloc(keys, PS_LIST_HEAD, false); // Iterator
+                psString key = NULL; // Item from iteration
+                psList *values = psListAlloc(NULL); // List containing the values
+                while ((key = psListGetAndIncrement(keysIter))) {
+                    psMetadataItem *value = NULL;
                     if (hduLow->header) {
-                        headerItem = psMetadataLookup(hduLow->header, keywords);
-                    }
-                    if (!headerItem && hduHigh != hduLow && hduHigh->header) {
-                        headerItem = psMetadataLookup(hduHigh->header, keywords);
-                    }
-                    psMemIncrRefCounter(headerItem);
-                } else {
-                    psListIterator *keysIter = psListIteratorAlloc(keys, PS_LIST_HEAD, false); // Iterator
-                    psString key = NULL; // Item from iteration
-                    psList *values = psListAlloc(NULL); // List containing the values
-                    while ((key = psListGetAndIncrement(keysIter))) {
-                        psMetadataItem *value = NULL;
-                        if (hduLow->header) {
-                            value = psMetadataLookup(hduLow->header, key);
-                        }
-                        if (!value && hduHigh != hduLow && hduHigh->header) {
-                            value = psMetadataLookup(hduHigh->header, key);
-                        }
-                        if (value) {
-                            psListAdd(values, PS_LIST_TAIL, value);
-                        } else {
-                            psWarning("Unable to find header %s --- assuming value is NULL", key);
-                        }
-                    }
-                    psFree(keysIter);
-                    headerItem = psMetadataItemAlloc(name, PS_DATA_LIST, specItem->comment, values);
-                    psFree(values);
-                }
-                psFree(keys);
-            }
+                        value = psMetadataLookup(hduLow->header, key);
+                    }
+                    if (!value && hduHigh != hduLow && hduHigh->header) {
+                        value = psMetadataLookup(hduHigh->header, key);
+                    }
+                    if (value) {
+                        psListAdd(values, PS_LIST_TAIL, value);
+                    } else {
+                        psWarning("Unable to find header %s --- assuming value is NULL", key);
+                    }
+                }
+                psFree(keysIter);
+                headerItem = psMetadataItemAlloc(name, PS_DATA_LIST, specItem->comment, values);
+                psFree(values);
+            }
+            psFree(keys);
         }
 
@@ -371,4 +361,48 @@
     psFree(specsIter);
     return status;
+}
+
+psMetadataItem *p_pmConceptsReadSingleFromDatabase(const char *name, const psMetadata *database, psDB *db,
+                                                   const pmFPA *fpa, const pmChip *chip, const pmCell *cell)
+{
+    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
+    PS_ASSERT_METADATA_NON_NULL(database, NULL);
+
+    psMetadataItem *item = psMetadataLookup(database, name); // Item to return
+    if (item && item->type == PS_DATA_METADATA) {
+        // This is a menu
+        psTrace("psModules.concepts", 5, "%s is of type METADATA.\n", name);
+        item = p_pmConceptsDepend(name, item->data.md, database, fpa, chip, cell);
+    }
+    if (!item) {
+        return NULL;
+    }
+    if (item->type != PS_DATA_STRING) {
+        psWarning("%s in DATABASE in camera format is not of type STR --- ignored.", name);
+        return NULL;
+    }
+
+    psString sql = pmConceptsInterpolate(item->data.str, fpa, chip, cell);
+    if (!p_psDBRunQuery(db, sql)) {
+        psWarning("Unable to query database for concept %s --- ignored.", name);
+        psFree(sql);
+        return NULL;
+    }
+    psFree(sql);
+
+    psArray *rows = p_psDBFetchResult(db); // Rows returned from the query
+    if (rows->n == 0) {
+        psWarning("No rows returned from database query for concept %s --- ignored.", name);
+        return NULL;
+    }
+    if (rows->n > 1) {
+        psWarning("Multiple rows returned from database query for concept %s --- using the first", name);
+    }
+    psMetadata *row = rows->data[0]; // First (and only) row
+    if (row->list->n > 1) {
+        psWarning("Multiple columns returned from database query for concept %s --- using the first", name);
+    }
+
+    return psMetadataGet(row, PS_LIST_HEAD); // Item of interest
 }
 
@@ -400,38 +434,9 @@
         pmConceptSpec *spec = specItem->data.V; // The specification
         psString name = specItem->name; // The concept name
-        const char *dbLookup = psMetadataLookupStr(&mdok, dbSpec, name);
-        if (mdok && dbLookup) {
-            psString sql = pmConceptsInterpolate(dbLookup, fpa, chip, cell);
-
-            if (!p_psDBRunQuery(db, sql)) {
-                psWarning("Unable to query database for concept %s --- ignored.", name);
-                psFree(sql);
-                continue;
-            }
-            psFree(sql);
-
-            psArray *rows = p_psDBFetchResult(db); // Rows returned from the query
-            if (rows->n == 0) {
-                psWarning("No rows returned from database query for concept %s --- ignored.", name);
-                continue;
-            }
-            if (rows->n > 1) {
-                psWarning("Multiple rows returned from database query for concept %s --- using the first",
-                          name);
-            }
-            psMetadata *row = rows->data[0]; // First (and only) row
-            if (row->list->n > 1) {
-                psWarning("Multiple columns returned from database query for concept %s --- using the first",
-                          name);
-            }
-            psMetadataItem *conceptItem = psMetadataGet(row, PS_LIST_HEAD); // Item of interest
-
-            // Now we have the result
-            if (!conceptParse(spec, conceptItem, PM_CONCEPT_SOURCE_DATABASE,
-                              cameraFormat, target, fpa, chip, cell)) {
-                psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to parse concept %s from database.\n",
-                        name);
-                status = false;
-            }
+        psMetadataItem *conceptItem = p_pmConceptsReadSingleFromDatabase(name, dbSpec, db, fpa, chip, cell);
+        if (conceptItem && !conceptParse(spec, conceptItem, PM_CONCEPT_SOURCE_DATABASE,
+                                         cameraFormat, target, fpa, chip, cell)) {
+            psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to parse concept %s from database.\n", name);
+            status = false;
         }
     } // Iterating through the concept specifications
Index: trunk/psModules/src/concepts/pmConceptsRead.h
===================================================================
--- trunk/psModules/src/concepts/pmConceptsRead.h	(revision 17898)
+++ trunk/psModules/src/concepts/pmConceptsRead.h	(revision 17911)
@@ -4,6 +4,6 @@
  * @author Paul Price, IfA
  *
- * @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
- * @date $Date: 2007-03-30 21:12:56 $
+ * @version $Revision: 1.7 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-06-05 01:31:33 $
  * Copyright 2005-2006 Institute for Astronomy, University of Hawaii
  */
@@ -22,4 +22,15 @@
                                const pmCell *cell ///< The cell
                               );
+
+/// Read a single concept from the DEFAULTS in the camera format
+///
+/// The returned item is NOT parsed, but any interpolation for DEPEND is done.
+psMetadataItem *p_pmConceptsReadSingleFromDefaults(
+    const char *name,                   ///< Name of concept
+    const psMetadata *defaults,         ///< DEFAULTS specifications
+    const pmFPA *fpa,                   ///< The FPA
+    const pmChip *chip,                 ///< The chip, or NULL
+    const pmCell *cell                  ///< The cell, or NULL
+    );
 
 /// Read concepts from the DEFAULTS in the camera format file.
@@ -45,4 +56,16 @@
                                );
 
+/// Read a single concept from the DATABASE specification in the camera format
+///
+/// The returned item is NOT parsed, but any interpolation for DEPEND is done.
+psMetadataItem *p_pmConceptsReadSingleFromDatabase(
+    const char *name,                   ///< Name of concept
+    const psMetadata *database,         ///< DATABASE specification
+    psDB *db,                           ///< Database handle
+    const pmFPA *fpa,                   ///< The FPA
+    const pmChip *chip,                 ///< The chip, or NULL
+    const pmCell *cell                  ///< The cell, or NULL
+    );
+
 /// Read concepts from the header DATABASE in the camera format file.
 ///
Index: trunk/psModules/src/concepts/pmConceptsWrite.c
===================================================================
--- trunk/psModules/src/concepts/pmConceptsWrite.c	(revision 17898)
+++ trunk/psModules/src/concepts/pmConceptsWrite.c	(revision 17911)
@@ -6,5 +6,5 @@
 #include <assert.h>
 #include <string.h>
-#include <strings.h>		/* for strn?casecmp */
+#include <strings.h>            /* for strn?casecmp */
 #include <pslib.h>
 
@@ -340,53 +340,31 @@
         pmConceptSpec *spec = specItem->data.V; // The specification
         psString name = specItem->name; // The concept name
-        psMetadataItem *defaultItem = psMetadataLookup(defaults, name); // The item from the DEFAULTS
-        if (defaultItem) {
-            psMetadataItem *conceptItem = NULL; // The item from the concepts
-            if (defaultItem->type == PS_DATA_METADATA) {
-                // It's a menu --- need to look up the .DEPEND
-                psString dependName = NULL; // The concept name with ".DEPEND" on the end
-                psStringAppend(&dependName, "%s.DEPEND", defaultItem->name);
-                psString dependKey = psMetadataLookupStr(&mdok, defaults, dependName); // The keyword
-                if (!mdok || !dependKey || strlen(dependKey) == 0) {
-                    psWarning("Can't find %s in the DEFAULTS for %s --- ignored.\n",
-                             dependName, name);
-                    psFree(dependName);
-                    continue;
-                }
-                psString dependValue = psMetadataLookupStr(&mdok, concepts, dependKey); // The value
-                if (!mdok || !dependValue || strlen(dependValue) == 0) {
-                    psWarning("Concept %s specified by %s isn't of type STR -- "
-                             "ignored.\n", dependKey, dependName);
-                    psFree(dependName);
-                    continue;
-                }
-                // Get the actual item of interest, and correct the name to match the concept name
-                defaultItem = psMetadataLookup(defaultItem->data.md, dependValue);
-                if (!defaultItem) {
-                    psWarning("Concept dependency name %s is not found "
-                             "in concept table for %s -- ignored.\n", dependValue, dependName);
-                    psFree(dependName);
-                    continue;
-                }
-                defaultItem = psMetadataItemCopy(defaultItem);
-                psFree(dependName);
-                psFree(defaultItem->name);
-                defaultItem->name = psStringCopy(name);
-            } else {
-                psMemIncrRefCounter(defaultItem);
-            }
-            conceptItem = psMetadataLookup(concepts, name); // The item from the concepts
-            psMetadataItem *formatted = conceptFormat(spec, conceptItem, PM_CONCEPT_SOURCE_DEFAULTS,
-                                                      cameraFormat, fpa, chip, cell);
-            if (!formatted) {
-                continue;
-            }
-            if (! compareConcepts(formatted, defaultItem)) {
-                psWarning("Concept %s is specified by the DEFAULTS in the camera "
-                         "format, but the values don't match.\n", name);
-            }
-            psFree(defaultItem);
-            psFree(formatted);
-        }
+
+        psMetadataItem *defaultItem = p_pmConceptsReadSingleFromDefaults(name, defaults, fpa, chip, cell);
+        if (!defaultItem) {
+            continue;
+        }
+        psMetadataItem *conceptItem = psMetadataLookup(concepts, name); // The item from the concepts
+        psMetadataItem *formatted = conceptFormat(spec, conceptItem, PM_CONCEPT_SOURCE_DEFAULTS,
+                                                  cameraFormat, fpa, chip, cell);
+        if (!formatted) {
+            continue;
+        }
+
+        if (strcmp(defaultItem->name, name) != 0) {
+            // Correct the name to match the concept name
+            defaultItem = psMetadataItemCopy(defaultItem);
+            psFree(defaultItem->name);
+            defaultItem->name = psStringCopy(name);
+        } else {
+            psMemIncrRefCounter(defaultItem);
+        }
+
+        if (!compareConcepts(formatted, defaultItem)) {
+            psWarning("Concept %s is specified by the DEFAULTS in the camera "
+                      "format, but the values don't match.\n", name);
+        }
+        psFree(defaultItem);
+        psFree(formatted);
     }
     psFree(specsIter);
@@ -418,20 +396,28 @@
         psString name = specItem->name; // The concept name
         psMetadataItem *headerItem = psMetadataLookup(translation, name); // The item from the TRANSLATION
-        if (headerItem) {
-            if (headerItem->type != PS_DATA_STRING) {
-                psWarning("TRANSLATION keyword for concept %s isn't of type STR ---"
-                         " ignored.", name);
+        if (!headerItem) {
+            continue;
+        }
+        if (headerItem->type == PS_DATA_METADATA) {
+            // This is a menu
+            psTrace("psModules.concepts", 5, "%s is of type METADATA.\n", name);
+            headerItem = p_pmConceptsDepend(name, headerItem->data.md, translation, fpa, chip, cell);
+            if (!headerItem) {
                 continue;
             }
-            psTrace("psModules.concepts", 3, "Writing %s to header %s\n", name, headerItem->data.str);
-            psMetadataItem *conceptItem = psMetadataLookup(concepts, name); // The item from the concepts
-            psMetadataItem *formatted = conceptFormat(spec, conceptItem, PM_CONCEPT_SOURCE_HEADER,
-                                                      cameraFormat, fpa, chip, cell);
-            if (!formatted) {
-                continue;
-            }
-            writeHeader(hdu, headerItem->data.V, formatted);
-            psFree(formatted);
-        }
+        }
+        if (headerItem->type != PS_DATA_STRING) {
+            psWarning("TRANSLATION keyword for concept %s isn't of type STR --- ignored.", name);
+            continue;
+        }
+        psTrace("psModules.concepts", 3, "Writing %s to header %s\n", name, headerItem->data.str);
+        psMetadataItem *conceptItem = psMetadataLookup(concepts, name); // The item from the concepts
+        psMetadataItem *formatted = conceptFormat(spec, conceptItem, PM_CONCEPT_SOURCE_HEADER,
+                                                  cameraFormat, fpa, chip, cell);
+        if (!formatted) {
+            continue;
+        }
+        writeHeader(hdu, headerItem->data.V, formatted);
+        psFree(formatted);
     }
     psFree(specsIter);
@@ -470,99 +456,31 @@
         psString name = specItem->name; // The concept name
 
-        psMetadataItem *dbItem = psMetadataLookup(database, name); // The item from the DATABASE
-        if (dbItem) {
-            if (dbItem->type != PS_DATA_METADATA) {
-                psWarning("DATABASE keyword for concept %s isn't of type METADATA "
-                         "--- ignored.\n", name);
-                continue;
-            }
-
-            psMetadataItem *conceptItem = psMetadataLookup(concepts, name); // The item from the concepts
-            psMetadataItem *formatted = conceptFormat(spec, conceptItem, PM_CONCEPT_SOURCE_DATABASE,
-                                                      cameraFormat, fpa, chip, cell);
-            if (!formatted) {
-                continue;
-            }
-
-            psMetadata *dbLookup = dbItem->data.V; // How to look up the value of interest
-            // Name of the table
-            const char *tableName = psMetadataLookupStr(&mdok, dbLookup, "TABLE");
-            // Name of "where" columns
-            const char *givenCols = psMetadataLookupStr(&mdok, dbLookup, "GIVENDBCOL");
-            // Values for "where" columns
-            const char *givenPS = psMetadataLookupStr(&mdok, dbLookup, "GIVENPS");
-
-            // Now, need to get the "given"s
-            if (strlen(givenCols) || strlen(givenPS)) {
-                psList *cols = psStringSplit(givenCols, ",;", true); // List of column names
-                psList *values = psStringSplit(givenPS, ",;", true); // List of value names for the columns
-                psMetadata *selection = psMetadataAlloc(); // The stuff to select in the DB
-                if (cols->n != values->n) {
-                    psLogMsg(__func__, PS_LOG_WARN,
-                             "The GIVENDBCOL and GIVENPS entries for %s do not have "
-                             "the same number of entries --- ignored.\n", name);
-                } else {
-                    // Iterators for the lists
-                    psListIterator *colsIter = psListIteratorAlloc(cols, PS_LIST_HEAD, false);
-                    psListIterator *valuesIter = psListIteratorAlloc(values, PS_LIST_HEAD, false);
-                    char *column = NULL;    // Name of the column
-                    while ((column = psListGetAndIncrement(colsIter))) {
-                        char *dependName = psListGetAndIncrement(valuesIter); // Name for the value
-                        if (!strlen(column) || !strlen(name)) {
-                            psWarning("One of the columns or value names for %s is "
-                                     " empty --- ignored.\n", name);
-                        } else {
-                            // Search for the value name
-                            psMetadataItem *item = NULL; // The value
-                            if (!item && cell) {
-                                item = psMetadataLookup(cell->concepts, dependName);
-                            }
-                            if (!item && chip) {
-                                item = psMetadataLookup(chip->concepts, dependName);
-                            }
-                            if (!item && fpa) {
-                                item = psMetadataLookup(fpa->concepts, dependName);
-                            }
-                            if (! item) {
-                                psLogMsg(__func__, PS_LOG_ERROR,
-                                         "Unable to find the value name %s for DB "
-                                         " lookup on %s --- ignored.\n", dependName, name);
-                            } else {
-                                // We need to create a new psMetadataItem.  I don't think we can't simply
-                                // hack the existing one, since that could conceivably cause memory leaks
-                                psMetadataAddItem(selection, formatted, PS_LIST_TAIL, PS_META_REPLACE);
-                                psFree(formatted);
-                            }
-                        }
-                        psFree(dependName);
-                        psFree(column);
-                    } // Iterating through the columns
-                    psFree(colsIter);
-                    psFree(valuesIter);
-
-                    // Check first to make sure we're only going to touch one row
-                    psArray *dbResult = psDBSelectRows(db, tableName, selection, 2); // Lookup result
-                    // Note that we use limit=2 in order to test if there are multiple rows returned
-                    if (! dbResult || dbResult->n == 0) {
-                        psWarning("Unable to find any rows in DB for %s --- "
-                                 "ignored\n", name);
-                        return false;
-                    } else {
-                        if (dbResult->n > 1) {
-                            psWarning("Multiple rows returned in DB lookup for %s "
-                                     "--- ignored.\n", name);
-                        }
-                        // Update the DB
-                        psMetadata *update = psMetadataAlloc();
-                        psMetadataAddItem(update, conceptItem, PS_LIST_HEAD, 0);
-                        psDBUpdateRows(db, tableName, selection, update);
-                        psFree(update);
-                        return true;
-                    }
-                }
-                psFree(cols);
-                psFree(values);
-            } // Doing the "given"s.
-        }
+        psMetadataItem *dbItem = p_pmConceptsReadSingleFromDatabase(name, database, db, fpa, chip, cell);
+        if (!dbItem) {
+            continue;
+        }
+
+        psMetadataItem *conceptItem = psMetadataLookup(concepts, name); // The item from the concepts
+        psMetadataItem *formatted = conceptFormat(spec, conceptItem, PM_CONCEPT_SOURCE_DATABASE,
+                                                  cameraFormat, fpa, chip, cell);
+        if (!formatted) {
+            continue;
+        }
+
+        if (strcmp(dbItem->name, name) != 0) {
+            // Correct the name to match the concept name
+            dbItem = psMetadataItemCopy(dbItem);
+            psFree(dbItem->name);
+            dbItem->name = psStringCopy(name);
+        } else {
+            psMemIncrRefCounter(dbItem);
+        }
+
+        if (!compareConcepts(formatted, dbItem)) {
+            psWarning("Concept %s is specified by the DATABASE in the camera "
+                      "format, but the values don't match.\n", name);
+        }
+        psFree(dbItem);
+        psFree(formatted);
     }
     psFree(specsIter);
@@ -570,3 +488,2 @@
     #endif
 }
-
