Index: /trunk/psModules/src/concepts/pmConcepts.c
===================================================================
--- /trunk/psModules/src/concepts/pmConcepts.c	(revision 9509)
+++ /trunk/psModules/src/concepts/pmConcepts.c	(revision 9510)
@@ -153,4 +153,11 @@
     }
 
+    if (source & PM_CONCEPT_SOURCE_PHU && !(*read & PM_CONCEPT_SOURCE_PHU)) {
+        if (!pmConceptsReadFromHeader(*specs, fpa, chip, cell, target)) {
+            psError(PS_ERR_UNKNOWN, false, "Error reading concepts from header.\n");
+            success = false;
+        }
+    }
+
     if (source & PM_CONCEPT_SOURCE_HEADER && !(*read & PM_CONCEPT_SOURCE_HEADER)) {
         if (pmConceptsReadFromHeader(*specs, fpa, chip, cell, target)) {
@@ -201,5 +208,5 @@
         pmConceptsWriteToDefaults(*specs, fpa, chip, cell, concepts);
     }
-    if (source & PM_CONCEPT_SOURCE_HEADER) {
+    if (source & (PM_CONCEPT_SOURCE_PHU | PM_CONCEPT_SOURCE_HEADER)) {
         pmConceptsWriteToHeader(*specs, fpa, chip, cell, concepts);
     }
@@ -218,4 +225,24 @@
     psTrace("psModules.concepts", 5, "Blanking FPA concepts: %p %p\n", conceptsFPA, fpa->concepts);
     return conceptsBlank(&conceptsFPA, fpa->concepts);
+}
+
+// Read the concepts for the given set of fpa, chip, cell
+bool pmConceptsRead(pmFPA *fpa,         // FPA for which to read concepts
+                    pmChip *chip,       // Chip for which to read concepts, or NULL
+                    pmCell *cell,       // Cell for which to read concepts, or NULL
+                    pmConceptSource source, // The source of the concepts to read
+                    psDB *db            // Database handle
+                   )
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, false);
+    bool success = conceptsRead(&conceptsFPA, fpa, chip, cell, &fpa->conceptsRead, source, db, fpa->concepts);
+    if (chip) {
+        success |= conceptsRead(&conceptsChip, fpa, chip, cell, &chip->conceptsRead, source, db, chip->concepts);
+    }
+    if (cell) {
+        success |= conceptsRead(&conceptsCell, fpa, chip, cell, &cell->conceptsRead, source, db, cell->concepts);
+    }
+
+    return success;
 }
 
@@ -501,4 +528,26 @@
         }
 
+        // FPA.TIMESYS
+        {
+            psMetadataItem *fpaTimesys = psMetadataItemAllocS32("FPA.TIMESYS", "Time system", -1);
+            pmConceptRegister(fpaTimesys, (pmConceptParseFunc)p_pmConceptParse_TIMESYS,
+                              (pmConceptFormatFunc)p_pmConceptFormat_TIMESYS, 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, PM_FPA_LEVEL_FPA);
+            psFree(fpaTime);
+        }
+
         // Done with FPA level concepts
     }
@@ -675,6 +724,6 @@
         {
             psMetadataItem *cellTimesys = psMetadataItemAllocS32("CELL.TIMESYS", "Time system", -1);
-            pmConceptRegister(cellTimesys, (pmConceptParseFunc)p_pmConceptParse_CELL_TIMESYS,
-                              (pmConceptFormatFunc)p_pmConceptFormat_CELL_TIMESYS, PM_FPA_LEVEL_CELL);
+            pmConceptRegister(cellTimesys, (pmConceptParseFunc)p_pmConceptParse_TIMESYS,
+                              (pmConceptFormatFunc)p_pmConceptFormat_TIMESYS, PM_FPA_LEVEL_CELL);
             psFree(cellTimesys);
         }
@@ -689,6 +738,6 @@
                                        "Time of exposure", time);
             psFree(time);
-            pmConceptRegister(cellTime, (pmConceptParseFunc)p_pmConceptParse_CELL_TIME,
-                              (pmConceptFormatFunc)p_pmConceptFormat_CELL_TIME, PM_FPA_LEVEL_CELL);
+            pmConceptRegister(cellTime, (pmConceptParseFunc)p_pmConceptParse_TIME,
+                              (pmConceptFormatFunc)p_pmConceptFormat_TIME, PM_FPA_LEVEL_CELL);
             psFree(cellTime);
         }
Index: /trunk/psModules/src/concepts/pmConcepts.h
===================================================================
--- /trunk/psModules/src/concepts/pmConcepts.h	(revision 9509)
+++ /trunk/psModules/src/concepts/pmConcepts.h	(revision 9510)
@@ -37,8 +37,17 @@
     PM_CONCEPT_SOURCE_CAMERA   = 0x01,  // Concept comes from the camera information
     PM_CONCEPT_SOURCE_DEFAULTS = 0x02,  // Concept comes from defaults
-    PM_CONCEPT_SOURCE_HEADER   = 0x04,  // Concept comes from FITS header
-    PM_CONCEPT_SOURCE_DATABASE = 0x08,  // Concept comes from database
-    PM_CONCEPT_SOURCE_ALL      = 0x0e   // All concepts
+    PM_CONCEPT_SOURCE_PHU      = 0x04,  // Concept comes from PHU
+    PM_CONCEPT_SOURCE_HEADER   = 0x08,  // Concept comes from FITS header
+    PM_CONCEPT_SOURCE_DATABASE = 0x10,  // Concept comes from database
+    PM_CONCEPT_SOURCE_ALL      = 0xff   // All concepts
 } pmConceptSource;
+
+// Read the concepts for the given set of fpa, chip, cell
+bool pmConceptsRead(pmFPA *fpa,         // FPA for which to read concepts
+                    pmChip *chip,       // Chip for which to read concepts, or NULL
+                    pmCell *cell,       // Cell for which to read concepts, or NULL
+                    pmConceptSource source, // The source of the concepts to read
+                    psDB *db            // Database handle
+                   );
 
 // Set blanks, read or write concepts at the appropriate level
Index: /trunk/psModules/src/concepts/pmConceptsStandard.c
===================================================================
--- /trunk/psModules/src/concepts/pmConceptsStandard.c	(revision 9509)
+++ /trunk/psModules/src/concepts/pmConceptsStandard.c	(revision 9510)
@@ -282,5 +282,5 @@
 
 
-psMetadataItem *p_pmConceptParse_CELL_TIMESYS(psMetadataItem *concept,
+psMetadataItem *p_pmConceptParse_TIMESYS(psMetadataItem *concept,
         psMetadataItem *pattern,
         psMetadata *cameraFormat,
@@ -295,5 +295,5 @@
     psString sys = concept->data.V;     // The time system string
     if (concept->type != PS_DATA_STRING || strlen(sys) <= 0) {
-        psError(PS_ERR_UNKNOWN, true, "Can't interpret CELL.TIMESYS --- assuming UTC.\n");
+        psError(PS_ERR_UNKNOWN, true, "Can't interpret %s --- assuming UTC.\n", pattern->name);
     } else if (strcasecmp(sys, "TAI") == 0) {
         timeSys = PS_TIME_TAI;
@@ -305,5 +305,5 @@
         timeSys = PS_TIME_TT;
     } else {
-        psError(PS_ERR_UNKNOWN, true, "Can't interpret CELL.TIMESYS --- assuming UTC.\n");
+        psError(PS_ERR_UNKNOWN, true, "Can't interpret %s --- assuming UTC.\n", pattern->name);
     }
 
@@ -311,22 +311,34 @@
 }
 
-psMetadataItem *p_pmConceptParse_CELL_TIME(psMetadataItem *concept,
-        psMetadataItem *pattern,
-        psMetadata *cameraFormat,
-        pmFPA *fpa,
-        pmChip *chip,
-        pmCell *cell)
+psMetadataItem *p_pmConceptParse_TIME(psMetadataItem *concept,
+                                      psMetadataItem *pattern,
+                                      psMetadata *cameraFormat,
+                                      pmFPA *fpa,
+                                      pmChip *chip,
+                                      pmCell *cell)
 {
     assert(concept);
     assert(cameraFormat);
-    assert(cell);
-
-    // Need CELL.TIMESYS first
-    bool mdok = true;                  // Result of MD lookup
-    psTimeType timeSys = psMetadataLookupS32(&mdok, cell->concepts, "CELL.TIMESYS"); // The time system
+
+    // Need TIMESYS first
+    psString timesysName = psStringCopy(pattern->name); // e.g., "CELL.TIME" --> "CELL.TIMESYS"
+    timesysName = psStringSubstitute(timesysName, "TIMESYS", "TIME");
+    bool mdok = false;                  // Result of MD lookup
+    psTimeType timeSys;                 // The time system
+    if (cell) {
+        timeSys = psMetadataLookupS32(&mdok, cell->concepts, timesysName);
+    }
+    if (!mdok && chip) {
+        timeSys = psMetadataLookupS32(&mdok, chip->concepts, timesysName);
+    }
+    if (!mdok && fpa) {
+        timeSys = psMetadataLookupS32(&mdok, fpa->concepts, timesysName);
+    }
     if (!mdok) {
-        psLogMsg(__func__, PS_LOG_WARN, "Unable to find CELL.TIMESYS in concepts --- assuming UTC.\n");
+        psLogMsg(__func__, PS_LOG_WARN, "Unable to find %s in concepts when parsing %s --- assuming UTC.\n",
+                 timesysName, pattern->name);
         timeSys = PS_TIME_UTC;
     }
+    psFree(timesysName);
 
     // Work out how the time is represented
@@ -344,7 +356,7 @@
     }
 
-    psString timeFormat = psMetadataLookupStr(&mdok, formats, "CELL.TIME");
+    psString timeFormat = psMetadataLookupStr(&mdok, formats, pattern->name);
     if (!mdok || strlen(timeFormat) == 0) {
-        psError(PS_ERR_UNKNOWN, true, "Unable to find CELL.TIME in FORMATS.\n");
+        psError(PS_ERR_UNKNOWN, true, "Unable to find %s in FORMATS.\n", pattern->name);
         return NULL;
     }
@@ -377,6 +389,6 @@
             usaTime = false;
         } else {
-            psError(PS_ERR_UNKNOWN, true, "Unrecognised FORMATS option for CELL.TIME: %s --- "
-                    "ignored.\n", timeFormat);
+            psError(PS_ERR_UNKNOWN, true, "Unrecognised FORMATS option for %s: %s --- "
+                    "ignored.\n", pattern->name, timeFormat);
             psFree(timeFormatsIter);
             psFree(timeFormats);
@@ -481,6 +493,6 @@
                 time = psTimeFromMJD(timeValue);
             } else {
-                psError(PS_ERR_UNKNOWN, true, "Not sure how to parse CELL.TIME (%f) --- trying JD\n",
-                        timeValue);
+                psError(PS_ERR_UNKNOWN, true, "Not sure how to parse %s (%f) --- trying JD\n",
+                        pattern->name, timeValue);
                 time = psTimeFromJD(timeValue);
             }
@@ -494,6 +506,6 @@
                 time = psTimeFromMJD(timeValue);
             } else {
-                psError(PS_ERR_UNKNOWN, true, "Not sure how to parse CELL.TIME (%f) --- trying JD\n",
-                        timeValue);
+                psError(PS_ERR_UNKNOWN, true, "Not sure how to parse %s (%f) --- trying JD\n",
+                        pattern->name, timeValue);
                 time = psTimeFromJD(timeValue);
             }
@@ -501,5 +513,5 @@
         }
     default:
-        psError(PS_ERR_UNKNOWN, true, "Unable to parse CELL.TIME.\n");
+        psError(PS_ERR_UNKNOWN, true, "Unable to parse %s.\n", pattern->name);
         return NULL;
     }
@@ -652,5 +664,5 @@
 
 
-psMetadataItem *p_pmConceptFormat_CELL_TIMESYS(psMetadataItem *concept,
+psMetadataItem *p_pmConceptFormat_TIMESYS(psMetadataItem *concept,
         psMetadata *cameraFormat,
         pmFPA *fpa,
@@ -681,9 +693,9 @@
 }
 
-psMetadataItem *p_pmConceptFormat_CELL_TIME(psMetadataItem *concept,
-        psMetadata *cameraFormat,
-        pmFPA *fpa,
-        pmChip *chip,
-        pmCell *cell)
+psMetadataItem *p_pmConceptFormat_TIME(psMetadataItem *concept,
+                                       psMetadata *cameraFormat,
+                                       pmFPA *fpa,
+                                       pmChip *chip,
+                                       pmCell *cell)
 {
     psTime *time = concept->data.V;     // The time
@@ -700,5 +712,5 @@
     psMetadata *formats = psMetadataLookupMD(&mdok, cameraFormat, "FORMATS"); // The formats
     if (mdok && formats) {
-        psString format = psMetadataLookupStr(&mdok, formats, "CELL.TIME"); // The formats for CELL.TIME
+        psString format = psMetadataLookupStr(&mdok, formats, concept->name); // The formats for eg, CELL.TIME
         if (mdok && format && strlen(format) > 0) {
             psList *formatList = psStringSplit(format, " ,;", false); // List of formats specified
@@ -732,6 +744,6 @@
                     separateTime = false;
                 } else {
-                    psLogMsg(__func__, PS_LOG_WARN, "Unrecognised FORMATS option for CELL.TIME: %s --- "
-                             "ignored.\n", format);
+                    psLogMsg(__func__, PS_LOG_WARN, "Unrecognised FORMATS option for %s: %s --- "
+                             "ignored.\n", concept->name, format);
                 }
             }
Index: /trunk/psModules/src/concepts/pmConceptsStandard.h
===================================================================
--- /trunk/psModules/src/concepts/pmConceptsStandard.h	(revision 9509)
+++ /trunk/psModules/src/concepts/pmConceptsStandard.h	(revision 9510)
@@ -36,5 +36,5 @@
         pmChip *chip,
         pmCell *cell);
-psMetadataItem *p_pmConceptParse_CELL_TIMESYS(psMetadataItem *concept,
+psMetadataItem *p_pmConceptParse_TIMESYS(psMetadataItem *concept,
         psMetadataItem *pattern,
         psMetadata *cameraFormat,
@@ -42,10 +42,10 @@
         pmChip *chip,
         pmCell *cell);
-psMetadataItem *p_pmConceptParse_CELL_TIME(psMetadataItem *concept,
-        psMetadataItem *pattern,
-        psMetadata *cameraFormat,
-        pmFPA *fpa,
-        pmChip *chip,
-        pmCell *cell);
+psMetadataItem *p_pmConceptParse_TIME(psMetadataItem *concept,
+                                      psMetadataItem *pattern,
+                                      psMetadata *cameraFormat,
+                                      pmFPA *fpa,
+                                      pmChip *chip,
+                                      pmCell *cell);
 psMetadataItem *p_pmConceptParse_Positions(psMetadataItem *concept,
         psMetadataItem *pattern,
@@ -74,14 +74,14 @@
         pmChip *chip,
         pmCell *cell);
-psMetadataItem *p_pmConceptFormat_CELL_TIMESYS(psMetadataItem *concept,
+psMetadataItem *p_pmConceptFormat_TIMESYS(psMetadataItem *concept,
         psMetadata *cameraFormat,
         pmFPA *fpa,
         pmChip *chip,
         pmCell *cell);
-psMetadataItem *p_pmConceptFormat_CELL_TIME(psMetadataItem *concept,
-        psMetadata *cameraFormat,
-        pmFPA *fpa,
-        pmChip *chip,
-        pmCell *cell);
+psMetadataItem *p_pmConceptFormat_TIME(psMetadataItem *concept,
+                                       psMetadata *cameraFormat,
+                                       pmFPA *fpa,
+                                       pmChip *chip,
+                                       pmCell *cell);
 psMetadataItem *p_pmConceptFormat_Positions(psMetadataItem *concept,
         psMetadata *cameraFormat,
