Index: trunk/psModules/src/concepts/pmConcepts.c
===================================================================
--- trunk/psModules/src/concepts/pmConcepts.c	(revision 17874)
+++ 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);
+}
+
