Index: trunk/archive/scripts/src/phase2/pmFPAConceptsGet.c
===================================================================
--- trunk/archive/scripts/src/phase2/pmFPAConceptsGet.c	(revision 5633)
+++ trunk/archive/scripts/src/phase2/pmFPAConceptsGet.c	(revision 5786)
@@ -14,30 +14,30 @@
 
 psMetadataItem *p_pmFPAConceptGetFromCamera(pmCell *cell, // The cell
-					    const char *concept // Name of concept
+                                            const char *concept // Name of concept
     )
 {
     if (cell) {
-	psMetadata *camera = cell->camera;	// Camera data
-	// Need the CELL.NAME first, which should be in the cell->concepts since creation of the cell
-	bool mdStatus = true;		// Status of MD lookup
-	// Finally, the info that we want
-	psMetadataItem *item = psMetadataLookup(camera, concept);
-	return item;
+        psMetadata *camera = cell->camera;      // Camera data
+        // Need the CELL.NAME first, which should be in the cell->concepts since creation of the cell
+        bool mdStatus = true;           // Status of MD lookup
+        // Finally, the info that we want
+        psMetadataItem *item = psMetadataLookup(camera, concept);
+        return item;
     }
     return NULL;
-}   
+}
 
 
 psMetadataItem *p_pmFPAConceptGetFromHeader(pmFPA *fpa, // The FPA that contains the chip
-					    pmChip *chip, // The chip that contains the cell
-					    pmCell *cell, // The cell
-					    const char *concept // Name of concept
-    )
-{
-    bool mdStatus = true;		// Status of MD lookup
+                                            pmChip *chip, // The chip that contains the cell
+                                            pmCell *cell, // The cell
+                                            const char *concept // Name of concept
+    )
+{
+    bool mdStatus = true;               // Status of MD lookup
     psMetadata *translation = psMetadataLookupMD(&mdStatus, fpa->camera, "TRANSLATION"); // FITS translation
     if (! mdStatus) {
-	psError(PS_ERR_IO, false, "Unable to find TRANSLATION in camera configuration.\n");
-	return NULL;
+        psError(PS_ERR_IO, false, "Unable to find TRANSLATION in camera configuration.\n");
+        return NULL;
     }
 
@@ -45,36 +45,36 @@
     const char *keyword = psMetadataLookupString(&mdStatus, translation, concept);
     if (mdStatus && strlen(keyword) > 0) {
-	// We have a FITS header to look up --- search each level
-	if (cell && cell->hdu) {
-	    psMetadataItem *cellItem = psMetadataLookup(cell->hdu->header, keyword);
-	    if (cellItem) {
-		// XXX: Need to clean up before returning
-		return cellItem;
-	    }
-	}
-
-	if (chip && chip->hdu) {
-	    psMetadataItem *chipItem = psMetadataLookup(chip->hdu->header, keyword);
-	    if (chipItem) {
-		// XXX: Need to clean up before returning
-		return chipItem;
-	    }
-	}
-
-	if (fpa->hdu) {
-	    psMetadataItem *fpaItem = psMetadataLookup(fpa->hdu->header, keyword);
-	    if (fpaItem) {
-		// XXX: Need to clean up before returning
-		return fpaItem;
-	    }
-	}
-
-	if (fpa->phu) {
-	    psMetadataItem *fpaItem = psMetadataLookup(fpa->phu, keyword);
-	    if (fpaItem) {
-		// XXX: Need to clean up before returning
-		return fpaItem;
-	    }
-	}
+        // We have a FITS header to look up --- search each level
+        if (cell && cell->hdu) {
+            psMetadataItem *cellItem = psMetadataLookup(cell->hdu->header, keyword);
+            if (cellItem) {
+                // XXX: Need to clean up before returning
+                return cellItem;
+            }
+        }
+
+        if (chip && chip->hdu) {
+            psMetadataItem *chipItem = psMetadataLookup(chip->hdu->header, keyword);
+            if (chipItem) {
+                // XXX: Need to clean up before returning
+                return chipItem;
+            }
+        }
+
+        if (fpa->hdu) {
+            psMetadataItem *fpaItem = psMetadataLookup(fpa->hdu->header, keyword);
+            if (fpaItem) {
+                // XXX: Need to clean up before returning
+                return fpaItem;
+            }
+        }
+
+        if (fpa->phu) {
+            psMetadataItem *fpaItem = psMetadataLookup(fpa->phu, keyword);
+            if (fpaItem) {
+                // XXX: Need to clean up before returning
+                return fpaItem;
+            }
+        }
     }
 
@@ -86,51 +86,51 @@
 // Look for a default
 psMetadataItem *p_pmFPAConceptGetFromDefault(pmFPA *fpa, // The FPA that contains the chip
-					     pmChip *chip, // The chip that contains the cell
-					     pmCell *cell, // The cell
-					     const char *concept // Name of concept
-    )
-{
-    bool mdOK = true;			// Status of MD lookup
+                                             pmChip *chip, // The chip that contains the cell
+                                             pmCell *cell, // The cell
+                                             const char *concept // Name of concept
+    )
+{
+    bool mdOK = true;                   // Status of MD lookup
     psMetadata *defaults = psMetadataLookupMD(&mdOK, fpa->camera, "DEFAULTS");
     if (! mdOK) {
-	psError(PS_ERR_IO, false, "Unable to find DEFAULTS in camera configuration.\n");
-	return NULL;
+        psError(PS_ERR_IO, false, "Unable to find DEFAULTS in camera configuration.\n");
+        return NULL;
     }
 
     psMetadataItem *defItem = psMetadataLookup(defaults, concept);
     if (defItem) {
-	if (defItem->type == PS_DATA_METADATA) {
-	    // A dependent default
-	    psTrace(__func__, 7, "Evaluating dependent default....\n");
-	    psMetadata *dependents = defItem->data.V; // The list of dependents
-	    // Find out what it depends on
-	    psString dependName = psStringCopy(concept);
-	    psStringAppend(&dependName, ".DEPEND");
-	    psString dependsOn = psMetadataLookupString(&mdOK, defaults, dependName);
-	    if (! mdOK) {
-		psError(PS_ERR_IO, false, "Unable to find %s in camera configuration for dependent default"
-			" --- ignored\n", dependName);
-		// XXX: Need to clean up before returning
-		return NULL;
-	    }
-	    psFree(dependName);
-	    // Find the value of the dependent concept
-	    psMetadataItem *depItem = p_pmFPAConceptGetFromHeader(fpa, chip, cell, dependsOn);
-	    if (! depItem) {
-		psError(PS_ERR_IO, true, "Unable to find value for %s (required for %s)\n", dependsOn,
-			concept);
-		return NULL;
-	    }
-	    if (depItem->type != PS_DATA_STRING) {
-		psError(PS_ERR_IO, true, "Value of %s is not of type string, as required for dependency"
-			" --- ignored.\n", dependsOn);
-	    }
-
-	    defItem = psMetadataLookup(dependents, depItem->data.V);	// This is now what we were after
-	}
+        if (defItem->type == PS_DATA_METADATA) {
+            // A dependent default
+            psTrace(__func__, 7, "Evaluating dependent default....\n");
+            psMetadata *dependents = defItem->data.V; // The list of dependents
+            // Find out what it depends on
+            psString dependName = psStringCopy(concept);
+            psStringAppend(&dependName, ".DEPEND");
+            psString dependsOn = psMetadataLookupString(&mdOK, defaults, dependName);
+            if (! mdOK) {
+                psError(PS_ERR_IO, false, "Unable to find %s in camera configuration for dependent default"
+                        " --- ignored\n", dependName);
+                // XXX: Need to clean up before returning
+                return NULL;
+            }
+            psFree(dependName);
+            // Find the value of the dependent concept
+            psMetadataItem *depItem = p_pmFPAConceptGetFromHeader(fpa, chip, cell, dependsOn);
+            if (! depItem) {
+                psError(PS_ERR_IO, true, "Unable to find value for %s (required for %s)\n", dependsOn,
+                        concept);
+                return NULL;
+            }
+            if (depItem->type != PS_DATA_STRING) {
+                psError(PS_ERR_IO, true, "Value of %s is not of type string, as required for dependency"
+                        " --- ignored.\n", dependsOn);
+            }
+
+            defItem = psMetadataLookup(dependents, depItem->data.V);    // This is now what we were after
+        }
     }
 
     // XXX: Need to clean up before returning
-    return defItem;			// defItem is either NULL or points to what was desired
+    return defItem;                     // defItem is either NULL or points to what was desired
 }
 
@@ -139,93 +139,93 @@
 // XXX: Not tested
 psMetadataItem *p_pmFPAConceptGetFromDB(pmFPA *fpa, // The FPA that contains the chip
-					pmChip *chip, // The chip that contains the cell
-					pmCell *cell, // The cell
-					psDB *db,	// DB handle
-					const char *concept // Name of concept
+                                        pmChip *chip, // The chip that contains the cell
+                                        pmCell *cell, // The cell
+                                        psDB *db,       // DB handle
+                                        const char *concept // Name of concept
     )
 {
     if (! db) {
-	// No database initialised
-	return NULL;
-    }
-
-    bool mdStatus = true;		// Status of MD lookup
+        // No database initialised
+        return NULL;
+    }
+
+    bool mdStatus = true;               // Status of MD lookup
     psMetadata *database = psMetadataLookupMD(&mdStatus, fpa->camera, "DATABASE");
     if (! mdStatus) {
-	// No error, because not everyone needs to use the DB
-	return NULL;
+        // No error, because not everyone needs to use the DB
+        return NULL;
     }
 
     psMetadata *dbLookup = psMetadataLookupMD(&mdStatus, database, concept);
     if (dbLookup) {
-	const char *tableName = psMetadataLookupString(&mdStatus, dbLookup, "TABLE"); // Name of the table
-	const char *colName = psMetadataLookupString(&mdStatus, dbLookup, "COLUMN"); // Name of the column
-	const char *givenCols = psMetadataLookupString(&mdStatus, dbLookup, "GIVENDBCOL"); // Name of "where"
-											   // columns
-	const char *givenPS = psMetadataLookupString(&mdStatus, dbLookup, "GIVENPS"); // Values for "where"
-										      // columns
-	
-	// Now, need to get the "given"s
-	if (strlen(givenCols) || strlen(givenPS)) {
-	    psList *cols = papSplit(givenCols, ",;"); // List of column names
-	    psList *values = papSplit(givenPS, ",;"); // 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", concept);
-	    } 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 *name = psListGetAndIncrement(valuesIter); // Name for the value
-		    if (!strlen(column) || !strlen(name)) {
-			psLogMsg(__func__, PS_LOG_WARN, "One of the columns or value names for %s is "
-				 " empty --- ignored.\n", concept);
-		    } else {
-			// Search for the value name
-			psMetadataItem *item = p_pmFPAConceptGetFromHeader(fpa, chip, cell, name);
-			if (! item) {
-			    item = p_pmFPAConceptGetFromDefault(fpa, chip, cell, name);
-			}
-			if (! item) {
-			    psLogMsg(__func__, PS_LOG_ERROR, "Unable to find the value name %s for DB "
-				     " lookup on %s --- ignored.\n", name, concept);
-			} 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
-			    psMetadataItem *newItem = psMetadataItemAlloc(concept, item->type,
-									  item->comment, item->data.V);
-			    psMetadataAddItem(selection, newItem, PS_LIST_TAIL, PS_META_REPLACE);
-			    psFree(newItem);
-			}
-		    }
-		    psFree(name);
-		    psFree(column);
-		} // Iterating through the columns
-		psFree(colsIter);
-		psFree(valuesIter);
-		
-		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
-		
-		psMetadataItem *result = NULL; // The final result of the DB lookup
-		if (dbResult->n == 0) {
-		    psLogMsg(__func__, PS_LOG_WARN, "Unable to find any rows in DB for %s --- ignored\n", 
-			     concept);
-		} else {
-		    if (dbResult-> n > 1) {
-			psLogMsg(__func__, PS_LOG_WARN, "Multiple rows returned in DB lookup for %s --- "
-				 " using the first one only.\n", concept);
-		    }
-		    result = (psMetadataItem*)dbResult->data[0];
-		}
-		// XXX: Need to clean up before returning
-		return result;
-	    }
-	    psFree(cols);
-	    psFree(values);
-	}
+        const char *tableName = psMetadataLookupString(&mdStatus, dbLookup, "TABLE"); // Name of the table
+        const char *colName = psMetadataLookupString(&mdStatus, dbLookup, "COLUMN"); // Name of the column
+        const char *givenCols = psMetadataLookupString(&mdStatus, dbLookup, "GIVENDBCOL"); // Name of "where"
+                                                                                           // columns
+        const char *givenPS = psMetadataLookupString(&mdStatus, dbLookup, "GIVENPS"); // Values for "where"
+                                                                                      // columns
+
+        // Now, need to get the "given"s
+        if (strlen(givenCols) || strlen(givenPS)) {
+            psList *cols = papSplit(givenCols, ",;"); // List of column names
+            psList *values = papSplit(givenPS, ",;"); // 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", concept);
+            } 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 *name = psListGetAndIncrement(valuesIter); // Name for the value
+                    if (!strlen(column) || !strlen(name)) {
+                        psLogMsg(__func__, PS_LOG_WARN, "One of the columns or value names for %s is "
+                                 " empty --- ignored.\n", concept);
+                    } else {
+                        // Search for the value name
+                        psMetadataItem *item = p_pmFPAConceptGetFromHeader(fpa, chip, cell, name);
+                        if (! item) {
+                            item = p_pmFPAConceptGetFromDefault(fpa, chip, cell, name);
+                        }
+                        if (! item) {
+                            psLogMsg(__func__, PS_LOG_ERROR, "Unable to find the value name %s for DB "
+                                     " lookup on %s --- ignored.\n", name, concept);
+                        } 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
+                            psMetadataItem *newItem = psMetadataItemAlloc(concept, item->type,
+                                                                          item->comment, item->data.V);
+                            psMetadataAddItem(selection, newItem, PS_LIST_TAIL, PS_META_REPLACE);
+                            psFree(newItem);
+                        }
+                    }
+                    psFree(name);
+                    psFree(column);
+                } // Iterating through the columns
+                psFree(colsIter);
+                psFree(valuesIter);
+
+                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
+
+                psMetadataItem *result = NULL; // The final result of the DB lookup
+                if (dbResult->n == 0) {
+                    psLogMsg(__func__, PS_LOG_WARN, "Unable to find any rows in DB for %s --- ignored\n",
+                             concept);
+                } else {
+                    if (dbResult-> n > 1) {
+                        psLogMsg(__func__, PS_LOG_WARN, "Multiple rows returned in DB lookup for %s --- "
+                                 " using the first one only.\n", concept);
+                    }
+                    result = (psMetadataItem*)dbResult->data[0];
+                }
+                // XXX: Need to clean up before returning
+                return result;
+            }
+            psFree(cols);
+            psFree(values);
+        }
     } // Doing the "given"s.
 
@@ -236,8 +236,8 @@
 // Concept lookup
 psMetadataItem *p_pmFPAConceptGet(pmFPA *fpa, // The FPA
-				  pmChip *chip,// The chip
-				  pmCell *cell,	// The cell
-				  psDB *db, // DB handle
-				  const char *concept // Concept name
+                                  pmChip *chip,// The chip
+                                  pmCell *cell, // The cell
+                                  psDB *db, // DB handle
+                                  const char *concept // Concept name
     )
 {
@@ -245,5 +245,5 @@
     psMetadataItem *item = p_pmFPAConceptGetFromCamera(cell, concept);
     if (! item) {
-	item = p_pmFPAConceptGetFromHeader(fpa, chip, cell, concept);
+        item = p_pmFPAConceptGetFromHeader(fpa, chip, cell, concept);
     }
     if (! item) {
@@ -257,11 +257,11 @@
 
 
-void p_pmFPAConceptGetF32(pmFPA *fpa,	// The FPA
-			  pmChip *chip, // The chip
-			  pmCell *cell, // The cell
-			  psDB *db,	// DB handle
-			  psMetadata *concepts, // The concepts MD
-			  const char *name, // Name of the concept
-			  const char *comment // Comment for concept
+void p_pmFPAConceptGetF32(pmFPA *fpa,   // The FPA
+                          pmChip *chip, // The chip
+                          pmCell *cell, // The cell
+                          psDB *db,     // DB handle
+                          psMetadata *concepts, // The concepts MD
+                          const char *name, // Name of the concept
+                          const char *comment // Comment for concept
     )
 {
@@ -269,35 +269,35 @@
     float value = NAN;
     if (item) {
-	switch (item->type) {
-	  case PS_DATA_F32:
-	    value = item->data.F32;
-	    break;
-	  case PS_DATA_F64:
-	    // Assume it's OK to truncate to floating point from double
-	    value = (float)item->data.F64;
-	    break;
-	  case PS_DATA_S32:
-	    // Promote to float
-	    value = (float)item->data.S32;
-	    break;
-	  default:
-	    psError(PS_ERR_IO, true, "Concept %s (%s) is not of floating point type (%x) --- treating as "
-		    "undefined.\n", name, comment, item->type);
-	}
+        switch (item->type) {
+          case PS_DATA_F32:
+            value = item->data.F32;
+            break;
+          case PS_DATA_F64:
+            // Assume it's OK to truncate to floating point from double
+            value = (float)item->data.F64;
+            break;
+          case PS_DATA_S32:
+            // Promote to float
+            value = (float)item->data.S32;
+            break;
+          default:
+            psError(PS_ERR_IO, true, "Concept %s (%s) is not of floating point type (%x) --- treating as "
+                    "undefined.\n", name, comment, item->type);
+        }
     } else {
-	psError(PS_ERR_IO, true, "Concept %s (%s) is not defined.\n", name, comment);
+        psError(PS_ERR_IO, true, "Concept %s (%s) is not defined.\n", name, comment);
     }
     psTrace(__func__, 7, "Adding %s (%s): %f\n", name, comment, value);
 
-    psMetadataAdd(concepts, PS_LIST_TAIL, name, PS_TYPE_F32, comment, value);
-}
-
-void p_pmFPAConceptGetF64(pmFPA *fpa,	// The FPA
-			  pmChip *chip, // The chip
-			  pmCell *cell, // The cell
-			  psDB *db,	// DB handle
-			  psMetadata *concepts, // The concepts MD
-			  const char *name, // Name of the concept
-			  const char *comment // Comment for concept
+    psMetadataAdd(concepts, PS_LIST_TAIL, name, PS_TYPE_F32 | PS_META_REPLACE, comment, value);
+}
+
+void p_pmFPAConceptGetF64(pmFPA *fpa,   // The FPA
+                          pmChip *chip, // The chip
+                          pmCell *cell, // The cell
+                          psDB *db,     // DB handle
+                          psMetadata *concepts, // The concepts MD
+                          const char *name, // Name of the concept
+                          const char *comment // Comment for concept
     )
 {
@@ -305,35 +305,35 @@
     double value = NAN;
     if (item) {
-	switch (item->type) {
-	  case PS_TYPE_F64:
-	    value = item->data.F64;
-	    break;
-	  case PS_TYPE_F32:
-	    // Promote to double
-	    value = (double)item->data.F32;
-	    break;
-	  case PS_TYPE_S32:
-	    // Promote to double
-	    value = (double)item->data.S32;
-	    break;
-	  default:
-	    psError(PS_ERR_IO, true, "Concept %s (%s) is not of double-precision floating point type (%x) "
-		    "--- treating as undefined.\n", name, comment, item->type);
-	}
+        switch (item->type) {
+          case PS_TYPE_F64:
+            value = item->data.F64;
+            break;
+          case PS_TYPE_F32:
+            // Promote to double
+            value = (double)item->data.F32;
+            break;
+          case PS_TYPE_S32:
+            // Promote to double
+            value = (double)item->data.S32;
+            break;
+          default:
+            psError(PS_ERR_IO, true, "Concept %s (%s) is not of double-precision floating point type (%x) "
+                    "--- treating as undefined.\n", name, comment, item->type);
+        }
     } else {
-	psError(PS_ERR_IO, true, "Concept %s (%s) is not defined.\n", name, comment);
+        psError(PS_ERR_IO, true, "Concept %s (%s) is not defined.\n", name, comment);
     }
     psTrace(__func__, 7, "Adding %s (%s): %f\n", name, comment, value);
 
-    psMetadataAdd(concepts, PS_LIST_TAIL, name, PS_TYPE_F64, comment, value);
-}
-
-void p_pmFPAConceptGetS32(pmFPA *fpa,	// The FPA
-			  pmChip *chip,	// The chip
-			  pmCell *cell,	// The cell
-			  psDB *db,	// DB handle
-			  psMetadata *concepts, // The concepts MD
-			  const char *name, // Name of the concept
-			  const char *comment // Comment for concept
+    psMetadataAdd(concepts, PS_LIST_TAIL, name, PS_TYPE_F64 | PS_META_REPLACE, comment, value);
+}
+
+void p_pmFPAConceptGetS32(pmFPA *fpa,   // The FPA
+                          pmChip *chip, // The chip
+                          pmCell *cell, // The cell
+                          psDB *db,     // DB handle
+                          psMetadata *concepts, // The concepts MD
+                          const char *name, // Name of the concept
+                          const char *comment // Comment for concept
     )
 {
@@ -341,37 +341,37 @@
     int value = 0;
     if (item) {
-	switch (item->type) {
-	  case PS_TYPE_S32:
-	    value = item->data.S32;
-	    break;
-	  case PS_TYPE_F32:
-	    psLogMsg(__func__, PS_LOG_WARN, "Concept %s (%s) should be S32, but is F32 --- converting.\n",
-		     name, comment);
-	    value = (int)item->data.F32;
-	    break;
-	  case PS_TYPE_F64:
-	    psLogMsg(__func__, PS_LOG_WARN, "Concept %s (%s) should be S32, but is F64 --- converting.\n",
-		     name, comment);
-	    value = (int)item->data.F64;
-	    break;
-	  default:
-	    psError(PS_ERR_IO, true, "Concept %s (%s) is not of integer type (%x) --- treating as "
-		    "undefined.\n", name, comment, item->type);
-	}
+        switch (item->type) {
+          case PS_TYPE_S32:
+            value = item->data.S32;
+            break;
+          case PS_TYPE_F32:
+            psLogMsg(__func__, PS_LOG_WARN, "Concept %s (%s) should be S32, but is F32 --- converting.\n",
+                     name, comment);
+            value = (int)item->data.F32;
+            break;
+          case PS_TYPE_F64:
+            psLogMsg(__func__, PS_LOG_WARN, "Concept %s (%s) should be S32, but is F64 --- converting.\n",
+                     name, comment);
+            value = (int)item->data.F64;
+            break;
+          default:
+            psError(PS_ERR_IO, true, "Concept %s (%s) is not of integer type (%x) --- treating as "
+                    "undefined.\n", name, comment, item->type);
+        }
     } else {
-	psError(PS_ERR_IO, true, "Concept %s (%s) is not defined.\n", name, comment);
+        psError(PS_ERR_IO, true, "Concept %s (%s) is not defined.\n", name, comment);
     }
     psTrace(__func__, 7, "Adding %s (%s): %d\n", name, comment, value);
-    
-    psMetadataAdd(concepts, PS_LIST_TAIL, name, PS_TYPE_S32, comment, value);
+
+    psMetadataAdd(concepts, PS_LIST_TAIL, name, PS_TYPE_S32 | PS_META_REPLACE, comment, value);
 }
 
 void p_pmFPAConceptGetString(pmFPA *fpa, // The FPA
-			     pmChip *chip, // The chip
-			     pmCell *cell, // The cell
-			     psDB *db,	// DB handle
-			     psMetadata *concepts, // The concepts MD
-			     const char *name, // Name of the concept
-			     const char *comment // Comment for concept
+                             pmChip *chip, // The chip
+                             pmCell *cell, // The cell
+                             psDB *db,  // DB handle
+                             psMetadata *concepts, // The concepts MD
+                             const char *name, // Name of the concept
+                             const char *comment // Comment for concept
     )
 {
@@ -379,25 +379,25 @@
     psString value = NULL;
     if (item) {
-	switch (item->type) {
-	  case PS_DATA_STRING:
-	    value = psMemIncrRefCounter(item->data.V);
-	    break;
-	  case PS_DATA_F32:
-	    psStringAppend(&value, "%f", item->data.F32);
-	    break;
-	  case PS_DATA_S32:
-	    psStringAppend(&value, "%d", item->data.S32);
-	    break;
-	  default:
-	    psError(PS_ERR_IO, true, "Concept %s (%s) is not of string type (%x) --- treating as "
-		    "undefined.\n", name, comment, item->type);
-	}
+        switch (item->type) {
+          case PS_DATA_STRING:
+            value = psMemIncrRefCounter(item->data.V);
+            break;
+          case PS_DATA_F32:
+            psStringAppend(&value, "%f", item->data.F32);
+            break;
+          case PS_DATA_S32:
+            psStringAppend(&value, "%d", item->data.S32);
+            break;
+          default:
+            psError(PS_ERR_IO, true, "Concept %s (%s) is not of string type (%x) --- treating as "
+                    "undefined.\n", name, comment, item->type);
+        }
     } else {
-	psError(PS_ERR_IO, true, "Concept %s (%s) is not defined.\n", name, comment);
-	value = psStringCopy("");
+        psError(PS_ERR_IO, true, "Concept %s (%s) is not defined.\n", name, comment);
+        value = psStringCopy("");
     }
     psTrace(__func__, 7, "Adding %s (%s): %s\n", name, comment, value);
 
-    psMetadataAdd(concepts, PS_LIST_TAIL, name, PS_DATA_STRING, comment, value);
+    psMetadataAdd(concepts, PS_LIST_TAIL, name, PS_DATA_STRING | PS_META_REPLACE, comment, value);
     psFree(value);
 }
@@ -409,10 +409,10 @@
 
 // Ingest concepts for the FPA
-void pmFPAIngestConcepts(pmFPA *fpa,	// The FPA
-			 psDB *db	// DB handle
+void pmFPAIngestConcepts(pmFPA *fpa,    // The FPA
+                         psDB *db       // DB handle
     )
 {
     if (! fpa->concepts) {
-	fpa->concepts = psMetadataAlloc();
+        fpa->concepts = psMetadataAlloc();
     }
 
@@ -436,68 +436,68 @@
     // FPA.RA
     {
-	double ra = NAN;		// The RA
-	psMetadataItem *raItem = p_pmFPAConceptGet(fpa, NULL, NULL, db, "FPA.RA"); // The FPA.RA item
-	if (raItem) {
-	    switch (raItem->type) {
-	      case PS_TYPE_F32:
-		ra = raItem->data.F32;
-		break;
-	      case PS_TYPE_F64:
-		ra = raItem->data.F64;
-		break;
-	      case PS_DATA_STRING:
-		// Sexagesimal format
-		{
-		    int big, medium;
-		    float small;
-		    // XXX: Upgrade path is to allow dd:mm.mmm
-		    if (sscanf(raItem->data.V, "%d:%d:%f", &big, &medium, &small) != 3 &&
-			sscanf(raItem->data.V, "%d %d %f", &big, &medium, &small) != 3) {
-			psError(PS_ERR_IO, true, "Cannot interpret FPA.RA: %s\n", raItem->data.V);
-			break;
-		    }
-		    ra = abs(big) + (float)medium/60.0 + small/3600.0;
-		    if (big < 0) {
-			ra *= -1.0;
-		    }
-		}
-		break;
-	      default:
-		psError(PS_ERR_IO, true, "FPA.RA is of an unexpected type: %x\n", raItem->type);
-	    }
-	    
-	    // How to interpret the RA
-	    const psMetadata *camera = fpa->camera; // Camera configuration data
-	    bool mdok = true;		// Status of MD lookup
-	    psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS");
-	    if (mdok && formats) {
-		psString raFormat = psMetadataLookupString(&mdok, formats, "FPA.RA");
-		if (mdok && strlen(raFormat) > 0) {
-		    if (strcasecmp(raFormat, "HOURS") == 0) {
-			ra *= M_PI / 12.0;
-		    } else if (strcasecmp(raFormat, "DEGREES") == 0) {
-			ra *= M_PI / 180.0;
-		    } else if (strcasecmp(raFormat, "RADIANS") == 0) {
-			// No action required
-		    } else {
-			psLogMsg(__func__, PS_LOG_WARN, "Don't understand FPA.RA in FORMATS (%s) --- assuming"
-				 " HOURS.\n");
-			ra *= M_PI / 12.0;
-		    }
-		} else {
-		    psError(PS_ERR_IO, false, "Unable to find FPA.RA in FORMATS --- assuming HOURS.\n");
-		    ra *= M_PI / 12.0;
-		}
-	    } else {
-		psError(PS_ERR_IO, false, "Unable to find FORMAT metadata in camera configuration --- "
-			"assuming format for FPA.RA is HOURS.\n");
-		ra *= M_PI / 12.0;
-	    }
-	} else {
-	    psError(PS_ERR_IO, false, "Couldn't find FPA.RA.\n");
-	}
-
-	psMetadataAdd(fpa->concepts, PS_LIST_TAIL, "FPA.RA", PS_DATA_F64, 
-		      "Right Ascension of the boresight (radians)", ra);
+        double ra = NAN;                // The RA
+        psMetadataItem *raItem = p_pmFPAConceptGet(fpa, NULL, NULL, db, "FPA.RA"); // The FPA.RA item
+        if (raItem) {
+            switch (raItem->type) {
+              case PS_TYPE_F32:
+                ra = raItem->data.F32;
+                break;
+              case PS_TYPE_F64:
+                ra = raItem->data.F64;
+                break;
+              case PS_DATA_STRING:
+                // Sexagesimal format
+                {
+                    int big, medium;
+                    float small;
+                    // XXX: Upgrade path is to allow dd:mm.mmm
+                    if (sscanf(raItem->data.V, "%d:%d:%f", &big, &medium, &small) != 3 &&
+                        sscanf(raItem->data.V, "%d %d %f", &big, &medium, &small) != 3) {
+                        psError(PS_ERR_IO, true, "Cannot interpret FPA.RA: %s\n", raItem->data.V);
+                        break;
+                    }
+                    ra = abs(big) + (float)medium/60.0 + small/3600.0;
+                    if (big < 0) {
+                        ra *= -1.0;
+                    }
+                }
+                break;
+              default:
+                psError(PS_ERR_IO, true, "FPA.RA is of an unexpected type: %x\n", raItem->type);
+            }
+
+            // How to interpret the RA
+            const psMetadata *camera = fpa->camera; // Camera configuration data
+            bool mdok = true;           // Status of MD lookup
+            psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS");
+            if (mdok && formats) {
+                psString raFormat = psMetadataLookupString(&mdok, formats, "FPA.RA");
+                if (mdok && strlen(raFormat) > 0) {
+                    if (strcasecmp(raFormat, "HOURS") == 0) {
+                        ra *= M_PI / 12.0;
+                    } else if (strcasecmp(raFormat, "DEGREES") == 0) {
+                        ra *= M_PI / 180.0;
+                    } else if (strcasecmp(raFormat, "RADIANS") == 0) {
+                        // No action required
+                    } else {
+                        psLogMsg(__func__, PS_LOG_WARN, "Don't understand FPA.RA in FORMATS (%s) --- assuming"
+                                 " HOURS.\n");
+                        ra *= M_PI / 12.0;
+                    }
+                } else {
+                    psError(PS_ERR_IO, false, "Unable to find FPA.RA in FORMATS --- assuming HOURS.\n");
+                    ra *= M_PI / 12.0;
+                }
+            } else {
+                psError(PS_ERR_IO, false, "Unable to find FORMAT metadata in camera configuration --- "
+                        "assuming format for FPA.RA is HOURS.\n");
+                ra *= M_PI / 12.0;
+            }
+        } else {
+            psError(PS_ERR_IO, false, "Couldn't find FPA.RA.\n");
+        }
+
+        psMetadataAdd(fpa->concepts, PS_LIST_TAIL, "FPA.RA", PS_DATA_F64 | PS_META_REPLACE,
+                      "Right Ascension of the boresight (radians)", ra);
 
     }
@@ -505,68 +505,68 @@
     // FPA.DEC
     {
-	double dec = NAN;		// The DEC
-	psMetadataItem *decItem = p_pmFPAConceptGet(fpa, NULL, NULL, db, "FPA.DEC"); // The FPA.DEC item
-	if (decItem) {
-	    switch (decItem->type) {
-	      case PS_TYPE_F32:
-		dec = decItem->data.F32;
-		break;
-	      case PS_TYPE_F64:
-		dec = decItem->data.F64;
-		break;
-	      case PS_DATA_STRING:
-		// Sexagesimal format
-		{
-		    int big, medium;
-		    float small;
-		    // XXX: Upgrade path is to allow dd:mm.mmm
-		    if (sscanf(decItem->data.V, "%d:%d:%f", &big, &medium, &small) != 3 &&
-			sscanf(decItem->data.V, "%d %d %f", &big, &medium, &small) != 3) {
-			psError(PS_ERR_IO, true, "Cannot interpret FPA.DEC: %s\n", decItem->data.V);
-			break;
-		    }
-		    dec = abs(big) + (float)medium/60.0 + small/3600.0;
-		    if (big < 0) {
-			dec *= -1.0;
-		    }
-		}
-		break;
-	      default:
-		psError(PS_ERR_IO, true, "FPA.DEC is of an unexpected type: %x\n", decItem->type);
-	    }
-	    
-	    // How to interpret the DEC
-	    const psMetadata *camera = fpa->camera; // Camera configuration data
-	    bool mdok = true;		// Status of MD lookup
-	    psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS");
-	    if (mdok && formats) {
-		psString decFormat = psMetadataLookupString(&mdok, formats, "FPA.DEC");
-		if (mdok && strlen(decFormat) > 0) {
-		    if (strcasecmp(decFormat, "HOURS") == 0) {
-			dec *= M_PI / 12.0;
-		    } else if (strcasecmp(decFormat, "DEGREES") == 0) {
-			dec *= M_PI / 180.0;
-		    } else if (strcasecmp(decFormat, "RADIANS") == 0) {
-			// No action required
-		    } else {
-			psLogMsg(__func__, PS_LOG_WARN, "Don't understand FPA.DEC in FORMATS (%s) --- "
-				 "assuming DEGREES.\n");
-			dec *= M_PI / 180.0;
-		    }
-		} else {
-		    psError(PS_ERR_IO, false, "Unable to find FPA.DEC in FORMATS --- assuming DEGREES.\n");
-		    dec *= M_PI / 180.0;
-		}
-	    } else {
-		psError(PS_ERR_IO, false, "Unable to find FORMATS metadata in camera configuration --- "
-			"assuming format for FPA.DEC is DEGREES.\n");
-		dec *= M_PI / 180.0;
-	    }
-	} else {
-	    psError(PS_ERR_IO, false, "Couldn't find FPA.DEC.\n");
-	}
-
-	psMetadataAdd(fpa->concepts, PS_LIST_TAIL, "FPA.DEC", PS_DATA_F64, 
-		      "Declination of the boresight (radians)", dec);
+        double dec = NAN;               // The DEC
+        psMetadataItem *decItem = p_pmFPAConceptGet(fpa, NULL, NULL, db, "FPA.DEC"); // The FPA.DEC item
+        if (decItem) {
+            switch (decItem->type) {
+              case PS_TYPE_F32:
+                dec = decItem->data.F32;
+                break;
+              case PS_TYPE_F64:
+                dec = decItem->data.F64;
+                break;
+              case PS_DATA_STRING:
+                // Sexagesimal format
+                {
+                    int big, medium;
+                    float small;
+                    // XXX: Upgrade path is to allow dd:mm.mmm
+                    if (sscanf(decItem->data.V, "%d:%d:%f", &big, &medium, &small) != 3 &&
+                        sscanf(decItem->data.V, "%d %d %f", &big, &medium, &small) != 3) {
+                        psError(PS_ERR_IO, true, "Cannot interpret FPA.DEC: %s\n", decItem->data.V);
+                        break;
+                    }
+                    dec = abs(big) + (float)medium/60.0 + small/3600.0;
+                    if (big < 0) {
+                        dec *= -1.0;
+                    }
+                }
+                break;
+              default:
+                psError(PS_ERR_IO, true, "FPA.DEC is of an unexpected type: %x\n", decItem->type);
+            }
+
+            // How to interpret the DEC
+            const psMetadata *camera = fpa->camera; // Camera configuration data
+            bool mdok = true;           // Status of MD lookup
+            psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS");
+            if (mdok && formats) {
+                psString decFormat = psMetadataLookupString(&mdok, formats, "FPA.DEC");
+                if (mdok && strlen(decFormat) > 0) {
+                    if (strcasecmp(decFormat, "HOURS") == 0) {
+                        dec *= M_PI / 12.0;
+                    } else if (strcasecmp(decFormat, "DEGREES") == 0) {
+                        dec *= M_PI / 180.0;
+                    } else if (strcasecmp(decFormat, "RADIANS") == 0) {
+                        // No action required
+                    } else {
+                        psLogMsg(__func__, PS_LOG_WARN, "Don't understand FPA.DEC in FORMATS (%s) --- "
+                                 "assuming DEGREES.\n");
+                        dec *= M_PI / 180.0;
+                    }
+                } else {
+                    psError(PS_ERR_IO, false, "Unable to find FPA.DEC in FORMATS --- assuming DEGREES.\n");
+                    dec *= M_PI / 180.0;
+                }
+            } else {
+                psError(PS_ERR_IO, false, "Unable to find FORMATS metadata in camera configuration --- "
+                        "assuming format for FPA.DEC is DEGREES.\n");
+                dec *= M_PI / 180.0;
+            }
+        } else {
+            psError(PS_ERR_IO, false, "Couldn't find FPA.DEC.\n");
+        }
+
+        psMetadataAdd(fpa->concepts, PS_LIST_TAIL, "FPA.DEC", PS_DATA_F64 | PS_META_REPLACE,
+                      "Declination of the boresight (radians)", dec);
 
     }
@@ -577,12 +577,12 @@
 
 // Ingest concepts for the chip
-bool pmChipIngestConcepts(pmChip *chip,	// The chip
-			  psDB *db	// DB handle
-    )
-{
-    pmFPA *fpa = chip->parent;		// The parent FPA
+bool pmChipIngestConcepts(pmChip *chip, // The chip
+                          psDB *db      // DB handle
+    )
+{
+    pmFPA *fpa = chip->parent;          // The parent FPA
 
     if (! chip->concepts) {
-	chip->concepts = psMetadataAlloc();
+        chip->concepts = psMetadataAlloc();
     }
 
@@ -591,42 +591,430 @@
     // Pau.
 }
+
+
+// Add corrective to a position --- in case the user wants FORTRAN indexing (like the FITS standard...)
+static bool correctPosition(pmFPA *fpa, // FPA, contains the camera configuration
+                            pmCell *cell, // Cell containing the concept to correct
+                            const char *conceptName // Name of concept to correct
+    )
+{
+    bool mdok = false;              // Result of MD lookup
+    psMetadata *formats = psMetadataLookupMD(&mdok, fpa->camera, "FORMATS");
+    if (mdok && formats) {
+        psString format = psMetadataLookupString(&mdok, formats, conceptName);
+        if (mdok && strlen(format) > 0 && strcasecmp(format, "FORTRAN") == 0) {
+            psMetadataItem *valueItem = psMetadataLookup(cell->concepts, conceptName);
+            valueItem->data.S32 -= 1;
+        }
+    }
+    return true;
+}
+
+bool p_pmCellIngestConcept(pmFPA *fpa,  // The FPA
+                           pmChip *chip, // The chip
+                           pmCell *cell, // The cell
+                           psDB *db,    // DB handle
+                           const char *concept // Name of the concept
+    )
+{
+    if (strcmp(concept, "CELL.GAIN") == 0) {
+        p_pmFPAConceptGetF32(fpa, chip, cell, db, cell->concepts, "CELL.GAIN", "CCD gain (e/count)");
+    } else if (strcmp(concept, "CELL.READNOISE") == 0) {
+        p_pmFPAConceptGetF32(fpa, chip, cell, db, cell->concepts, "CELL.READNOISE", "CCD read noise (e)");
+    } else if (strcmp(concept, "CELL.SATURATION") == 0) {
+        p_pmFPAConceptGetF32(fpa, chip, cell, db, cell->concepts, "CELL.SATURATION",
+                             "Saturation level (ADU)");
+    } else if (strcmp(concept, "CELL.BAD") == 0) {
+        p_pmFPAConceptGetF32(fpa, chip, cell, db, cell->concepts, "CELL.BAD", "Bad level (ADU)");
+    } else if (strcmp(concept, "CELL.XPARITY") == 0) {
+        p_pmFPAConceptGetS32(fpa, chip, cell, db, cell->concepts, "CELL.XPARITY",
+                             "Orientation in x compared to the rest of the FPA");
+    } else if (strcmp(concept, "CELL.YPARITY") == 0) {
+        p_pmFPAConceptGetS32(fpa, chip, cell, db, cell->concepts, "CELL.YPARITY",
+                             "Orientation in y compared to the rest of the FPA");
+    } else if (strcmp(concept, "CELL.READDIR") == 0) {
+        p_pmFPAConceptGetS32(fpa, chip, cell, db, cell->concepts, "CELL.READDIR",
+                             "Read direction: 1=row, 2=col");
+    } else if (strcmp(concept, "CELL.EXPOSURE") == 0) { // used to be READOUT.EXPOSURE
+        p_pmFPAConceptGetF32(fpa, chip, cell, db, cell->concepts, "CELL.EXPOSURE", "Exposure time (sec)");
+    } else if (strcmp(concept, "CELL.DARKTIME") == 0) { // used to be READOUT.DARKTIME
+        p_pmFPAConceptGetF32(fpa, chip, cell, db, cell->concepts, "CELL.DARKTIME",
+                             "Time since CCD flush (sec)");
+        // These take some extra work
+    } else if (strcmp(concept, "CELL.TRIMSEC") == 0) {
+        psRegion *trimsec = psAlloc(sizeof(psRegion)); // Make space for a psRegion (usually passed by value)
+
+        psMetadataItem *secItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.TRIMSEC");
+        if (! secItem) {
+            psError(PS_ERR_IO, false, "Couldn't find CELL.TRIMSEC.\n");
+            *trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
+        } else if (secItem->type != PS_DATA_STRING) {
+            psError(PS_ERR_IO, true, "CELL.TRIMSEC is not of type STR (%x)\n", secItem->type);
+            *trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
+        } else {
+            psString section = secItem->data.V; // The section string
+
+            psMetadataItem *sourceItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.TRIMSEC.SOURCE");
+            if (! sourceItem) {
+                psError(PS_ERR_IO, false, "Couldn't find CELL.TRIMSEC.SOURCE.\n");
+                *trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
+            } else if (sourceItem->type != PS_DATA_STRING) {
+                psError(PS_ERR_IO, true, "CELL.TRIMSEC.SOURCE is not of type STR (%x)\n", sourceItem->type);
+                *trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
+            } else {
+                psString source = sourceItem->data.V; // The source string
+
+                if (strcasecmp(source, "VALUE") == 0) {
+                    *trimsec = psRegionFromString(section);
+                } else if (strcasecmp(source, "HEADER") == 0) {
+                    psMetadata *header = NULL; // The FITS header
+                    if (cell->hdu) {
+                        header = cell->hdu->header;
+                    } else if (chip->hdu) {
+                        header = chip->hdu->header;
+                    } else if (fpa->hdu) {
+                        header = fpa->hdu->header;
+                    }
+                    if (! header) {
+                        psError(PS_ERR_IO, true, "Unable to find FITS header!\n");
+                        *trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
+                    } else {
+                        bool mdok = true;               // Status of MD lookup
+                        psString secValue = psMetadataLookupString(&mdok, header, section);
+                        if (! mdok || ! secValue) {
+                            psError(PS_ERR_IO, false, "Unable to locate header %s\n", section);
+                            *trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
+                        } else {
+                            *trimsec = psRegionFromString(secValue);
+                        }
+                    }
+                } else {
+                    psError(PS_ERR_IO, true, "CELL.TRIMSEC.SOURCE (%s) is not HEADER or VALUE --- trying "
+                            "VALUE.\n", source);
+                    *trimsec = psRegionFromString(section);
+                } // Value of CELL.TRIMSEC.SOURCE
+            } // Looking up CELL.TRIMSEC.SOURCE
+        } // Looking up CELL.TRIMSEC
+
+        psMetadataAdd(cell->concepts, PS_LIST_TAIL, "CELL.TRIMSEC", PS_DATA_UNKNOWN | PS_META_REPLACE,
+                      "Trim section", trimsec);
+        psFree(trimsec);
+
+    } else if (strcmp(concept, "CELL.BIASSEC") == 0) {
+        psList *biassecs = psListAlloc(NULL); // List of bias sections
+
+        psMetadataItem *secItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.BIASSEC");
+        if (! secItem) {
+            psError(PS_ERR_IO, false, "Couldn't find CELL.BIASSEC.\n");
+        } else if (secItem->type != PS_DATA_STRING) {
+            psError(PS_ERR_IO, true, "CELL.BIASSEC is not of type STR (%x)\n", secItem->type);
+        } else {
+            psString sections = secItem->data.V; // The section string
+
+            psMetadataItem *sourceItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.BIASSEC.SOURCE");
+            if (! sourceItem) {
+                psError(PS_ERR_IO, false, "Couldn't find CELL.BIASSEC.SOURCE.\n");
+            } else if (sourceItem->type != PS_DATA_STRING) {
+                psError(PS_ERR_IO, true, "CELL.BIASSEC.SOURCE is not of type STR (%x)\n", sourceItem->type);
+            } else {
+                psString source = sourceItem->data.V; // The source string
+
+                psList *secList = papSplit(sections, " ;"); // List of sections
+                psListIterator *secIter = psListIteratorAlloc(secList, PS_LIST_HEAD, false); // Iterator over
+                                                                                             // sections
+                psString aSection = NULL; // A section from the list
+                while (aSection = psListGetAndIncrement(secIter)) {
+                    psRegion *region = psAlloc(sizeof(psRegion)); // Make space for a psRegion (usually passed
+                                                                  // by value)
+
+                    if (strcasecmp(source, "VALUE") == 0) {
+                        *region = psRegionFromString(aSection);
+                    } else if (strcasecmp(source, "HEADER") == 0) {
+                        psMetadata *header = NULL; // The FITS header
+                        if (cell->hdu) {
+                            header = cell->hdu->header;
+                        } else if (chip->hdu) {
+                            header = chip->hdu->header;
+                        } else if (fpa->hdu) {
+                            header = fpa->hdu->header;
+                        }
+                        if (! header) {
+                            psError(PS_ERR_IO, true, "Unable to find FITS header!\n");
+                            *region = psRegionSet(0.0,0.0,0.0,0.0);
+                        } else {
+                            bool mdok = true;           // Status of MD lookup
+                            psString secValue = psMetadataLookupString(&mdok, header, aSection);
+                            if (! mdok || ! secValue) {
+                                psError(PS_ERR_IO, false, "Unable to locate header %s\n", aSection);
+                                *region = psRegionSet(0.0,0.0,0.0,0.0);
+                            } else {
+                                *region = psRegionFromString(secValue);
+                            }
+                        }
+                    } else {
+                        psError(PS_ERR_IO, true, "CELL.BIASSEC.SOURCE (%s) is not HEADER or VALUE --- trying "
+                                "VALUE.\n", source);
+                        *region = psRegionFromString(aSection);
+                    } // Value of CELL.BIASSEC.SOURCE
+
+                    psListAdd(biassecs, PS_LIST_TAIL, region);
+                    psFree(region);
+                } // Iterating over multiple sections
+                psFree(secIter);
+                psFree(secList);
+            } // Looking up CELL.BIASSEC.SOURCE
+        } // Looking up CELL.BIASSEC
+
+        psMetadataAdd(cell->concepts, PS_LIST_TAIL, "CELL.BIASSEC", PS_DATA_LIST | PS_META_REPLACE,
+                      "Bias sections", biassecs);
+        psFree(biassecs);
+
+    } else if (strcmp(concept, "CELL.XBIN") == 0) {
+        int xBin = 1;                   // Binning factor in x
+        psMetadataItem *binItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.XBIN");
+        if (! binItem) {
+            psError(PS_ERR_IO, false, "Couldn't find CELL.XBIN.\n");
+        } else if (binItem->type == PS_DATA_STRING) {
+            psString binString = binItem->data.V; // The string containing the binning
+            if (sscanf(binString, "%d %*d", &xBin) != 1 &&
+                sscanf(binString, "%d,%*d", &xBin) != 1) {
+                psError(PS_ERR_IO, true, "Unable to read string to get x binning: %s\n", binString);
+            }
+        } else if (binItem->type == PS_TYPE_S32) {
+            xBin = binItem->data.S32;
+        } else {
+            psError(PS_ERR_IO, true, "Note sure how to interpret CELL.XBIN of type %x --- assuming 1.\n",
+                    binItem->type);
+        }
+
+        psMetadataAdd(cell->concepts, PS_LIST_TAIL, "CELL.XBIN", PS_TYPE_S32 | PS_META_REPLACE,
+                      "Binning in x", xBin);
+
+    } else if (strcmp(concept, "CELL.YBIN") == 0) {
+        int yBin = 1;                   // Binning factor in y
+        psMetadataItem *binItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.YBIN");
+        if (! binItem) {
+            psError(PS_ERR_IO, false, "Couldn't find CELL.YBIN.\n");
+        } else if (binItem->type == PS_DATA_STRING) {
+            psString binString = binItem->data.V; // The string containing the binning
+            if (sscanf(binString, "%*d %d", &yBin) != 1 &&
+                sscanf(binString, "%*d,%d", &yBin) != 1) {
+                psError(PS_ERR_IO, true, "Unable to read string to get y binning: %s\n", binString);
+            }
+        } else if (binItem->type == PS_TYPE_S32) {
+            yBin = binItem->data.S32;
+        } else {
+            psError(PS_ERR_IO, true, "Note sure how to interpret CELL.YBIN of type %x --- assuming 1.\n",
+                    binItem->type);
+        }
+
+        psMetadataAdd(cell->concepts, PS_LIST_TAIL, "CELL.YBIN", PS_TYPE_S32 | PS_META_REPLACE,
+                      "Binning in y", yBin);
+
+    } else if (strcmp(concept, "CELL.TIME") == 0 || strcmp(concept, "CELL.TIMESYS") == 0) {
+        // Do CELL.TIME and CELL.TIMESYS together
+        psTime *time = NULL;            // The time
+        psTimeType timeSys = PS_TIME_UTC; // The time system
+
+        // CELL.TIMESYS
+        psMetadataItem *sysItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.TIMESYS");
+        if (! sysItem) {
+            psError(PS_ERR_IO, true, "Couldn't find CELL.TIMESYS --- assuming UTC.\n");
+        } else if (sysItem->type != PS_DATA_STRING) {
+            psError(PS_ERR_IO, true, "CELL.TIMESYS isn't of type STRING --- assuming UTC.\n");
+        } else {
+            psString sys = sysItem->data.V; // The time system string
+            if (strcasecmp(sys, "TAI") == 0) {
+                timeSys = PS_TIME_TAI;
+            } else if (strcasecmp(sys, "UTC") == 0) {
+                timeSys = PS_TIME_UTC;
+            } else if (strcasecmp(sys, "UT1") == 0) {
+                timeSys = PS_TIME_UT1;
+            } else if (strcasecmp(sys, "TT") == 0) {
+                timeSys = PS_TIME_TT;
+            } else {
+                psError(PS_ERR_IO, true, "Can't interpret CELL.TIMESYS --- assuming UTC.\n");
+            }
+        }
+        psMetadataAdd(cell->concepts, PS_LIST_TAIL, "CELL.TIMESYS", PS_TYPE_S32 | PS_META_REPLACE,
+                      "Time system", timeSys);
+
+        psMetadataItem *timeItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.TIME");
+        if (! timeItem) {
+            psError(PS_ERR_IO, false, "Couldn't find CELL.TIME.\n");
+        } else {
+            // Get format
+            const psMetadata *camera = fpa->camera; // The camera configuration data
+            bool mdok = true;           // Status of MD lookup
+            psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS");
+            if (mdok && formats) {
+                psString timeFormat = psMetadataLookupString(&mdok, formats, "CELL.TIME");
+                if (mdok && strlen(timeFormat) > 0) {
+                    switch (timeItem->type) {
+                      case PS_DATA_STRING:
+                        {
+                            psString timeString = timeItem->data.V;     // String with the time
+                            if (strcasecmp(timeFormat, "ISO") == 0) {
+                                // timeString contains an ISO time
+                                time = psTimeFromISO(timeString, timeSys);
+                            } else if (strstr(timeFormat, "SEPARATE")) {
+                                // timeString contains headers for the date and time
+                                psMetadata *header = NULL; // The FITS header
+                                if (cell->hdu) {
+                                    header = cell->hdu->header;
+                                } else if (chip->hdu) {
+                                    header = chip->hdu->header;
+                                } else if (fpa->hdu) {
+                                    header = fpa->hdu->header;
+                                }
+                                if (! header) {
+                                    psError(PS_ERR_IO, true, "Unable to find FITS header!\n");
+                                } else {
+                                    // Get the headers
+                                    char *stuff1 = strpbrk(timeString, " ,;");
+                                    psString dateName = psStringNCopy(timeString,
+                                                                      strlen(timeString) - strlen(stuff1));
+                                    char *stuff2 = strpbrk(stuff1, "abcdefghijklmnopqrstuvwxyz"
+                                                           "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+                                    psString timeName = psStringCopy(stuff2);
+
+                                    bool mdok = true; // Status of MD lookup
+                                    psString dateString = psMetadataLookupString(&mdok, header, dateName);
+                                    psFree(dateName);
+                                    int day = 0, month = 0, year = 0;
+                                    if (sscanf(dateString, "%d-%d-%d", &day, &month, &year) != 3 &&
+                                        sscanf(dateString, "%d/%d/%d", &day, &month, &year) != 3) {
+                                        psError(PS_ERR_IO, true, "Unable to read date: %s\n", dateString);
+                                    } else {
+                                        if (strstr(timeFormat, "BACKWARDS")) {
+                                            int temp = day;
+                                            day = year;
+                                            year = temp;
+                                        }
+                                        if (strstr(timeFormat, "PRE2000") || year < 2000) {
+                                            year += 2000;
+                                        }
+
+                                        psMetadataItem *timeItem = psMetadataLookup(header, timeName);
+                                        if (! timeItem) {
+                                            psError(PS_ERR_IO, false, "Unable to find time header: %s\n",
+                                                    timeName);
+                                        } else if (timeItem->type == PS_DATA_STRING) {
+                                            // Time is a string, in the usual way:
+                                            psStringAppend(&dateString, "T%s", timeItem->data.V);
+                                        } else {
+                                            // Assume that time is specified in Second of Day
+                                            double seconds = NAN;
+                                            switch (timeItem->type) {
+                                              case PS_TYPE_S32:
+                                                seconds = timeItem->data.S32;
+                                                break;
+                                              case PS_TYPE_F32:
+                                                seconds = timeItem->data.F32;
+                                                break;
+                                              case PS_TYPE_F64:
+                                                seconds = timeItem->data.F64;
+                                                break;
+                                              default:
+                                                psError(PS_ERR_IO, true, "Time header (%s) is not of an "
+                                                        "expected type: %x\n", timeName, timeItem->type);
+                                            }
+                                            // Now print to timeString as "hh:mm:ss.ss"
+                                            int hours = seconds / 3600;
+                                            seconds -= (double)hours * 3600.0;
+                                            int minutes = seconds / 60;
+                                            seconds -= (double)minutes * 60.0;
+                                            psStringAppend(&dateString, "T%02d:%02d:%02f", hours, minutes,
+                                                           seconds);
+                                        }
+                                        time = psTimeFromISO(dateString, timeSys);
+                                    } // Reading date and time
+                                    psFree(timeName);
+                                } // Reading headers
+                            } else {
+                                psError(PS_ERR_IO, true, "Not sure how to parse CELL.TIME (%s) --- trying "
+                                        "ISO\n", timeString);
+                                time = psTimeFromISO(timeString, timeSys);
+                            } // Interpreting the time string
+                        }
+                        break;
+                      case PS_TYPE_F32:
+                        {
+                            double timeValue = (double)timeItem->data.F32;
+                            if (strcasecmp(timeFormat, "JD") == 0) {
+                                time = psTimeFromJD(timeValue);
+                            } else if (strcasecmp(timeFormat, "MJD") == 0) {
+                                time = psTimeFromMJD(timeValue);
+                            } else {
+                                psError(PS_ERR_IO, true, "Not sure how to parse CELL.TIME (%f) --- trying "
+                                        "JD\n", timeValue);
+                                time = psTimeFromJD(timeValue);
+                            }
+                        }
+                        break;
+                      case PS_TYPE_F64:
+                        {
+                            double timeValue = (double)timeItem->data.F64;
+                            if (strcasecmp(timeFormat, "JD") == 0) {
+                                time = psTimeFromJD(timeValue);
+                            } else if (strcasecmp(timeFormat, "MJD") == 0) {
+                                time = psTimeFromMJD(timeValue);
+                            } else {
+                                psError(PS_ERR_IO, true, "Not sure how to parse CELL.TIME (%f) --- trying "
+                                        "JD\n", timeValue);
+                                time = psTimeFromJD(timeValue);
+                            }
+                        }
+                        break;
+                      default:
+                        psError(PS_ERR_IO, true, "Unable to parse CELL.TIME.\n");
+                    }
+                } else {
+                    psError(PS_ERR_IO, false, "Unable to find CELL.TIME in FORMATS.\n");
+                } // Getting the format
+            } else {
+                psError(PS_ERR_IO, false, "Unable to find FORMATS in camera configuration.\n");
+            } // Getting the formats
+        } // Getting CELL.TIME
+
+        psMetadataAdd(cell->concepts, PS_LIST_TAIL, "CELL.TIME", PS_DATA_TIME | PS_META_REPLACE,
+                      "Time of exposure", time);
+        psFree(time);
+
+        // These are new and experimental concepts: CELL.X0 and CELL.Y0
+    } else if (strcmp(concept, "CELL.X0") == 0) {
+        p_pmFPAConceptGetS32(fpa, chip, cell, db, cell->concepts, "CELL.X0", "Position of (0,0) on the chip");
+        correctPosition(fpa, cell, "CELL.X0");
+    } else if (strcmp(concept, "CELL.Y0") == 0) {
+        p_pmFPAConceptGetS32(fpa, chip, cell, db, cell->concepts, "CELL.Y0", "Position of (0,0) on the chip");
+        correctPosition(fpa, cell, "CELL.Y0");
+    }
+
+    return true;
+}
+
 
 
 // Ingest concepts for the cell
 bool pmCellIngestConcepts(pmCell *cell, // The cell
-			  psDB *db	// DB handle
-    )
-{
-    pmChip *chip = cell->parent;	// The parent chip
-    pmFPA *fpa = chip->parent;		// The parent FPA
+                          psDB *db      // DB handle
+    )
+{
+    pmChip *chip = cell->parent;        // The parent chip
+    pmFPA *fpa = chip->parent;          // The parent FPA
 
     if (! cell->concepts) {
-	cell->concepts = psMetadataAlloc();
+        cell->concepts = psMetadataAlloc();
     }
 
     // CELL.NAME --- added by pmFPAConstruct
 
-    // CELL.GAIN
-    p_pmFPAConceptGetF32(fpa, chip, cell, db, cell->concepts, "CELL.GAIN", "CCD gain (e/count)");
-
-    // CELL.READNOISE
-    p_pmFPAConceptGetF32(fpa, chip, cell, db, cell->concepts, "CELL.READNOISE", "CCD read noise (e)");
-
-    // CELL.SATURATION
-    p_pmFPAConceptGetF32(fpa, chip, cell, db, cell->concepts, "CELL.SATURATION", "Saturation level (ADU)");
-
-    // CELL.BAD
-    p_pmFPAConceptGetF32(fpa, chip, cell, db, cell->concepts, "CELL.BAD", "Bad level (ADU)");
-
-    // CELL.XPARITY
-    p_pmFPAConceptGetS32(fpa, chip, cell, db, cell->concepts, "CELL.XPARITY", "Orientation in x compared to the "
-		  "rest of the FPA");
-
-    // CELL.YPARITY
-    p_pmFPAConceptGetS32(fpa, chip, cell, db, cell->concepts, "CELL.YPARITY", "Orientation in y compared to the "
-		  "rest of the FPA");
-
-    // CELL.READDIR
-    p_pmFPAConceptGetS32(fpa, chip, cell, db, cell->concepts, "CELL.READDIR", "Read direction: 1=row, 2=col");
+    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.GAIN");
+    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.READNOISE");
+    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.SATURATION");
+    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.BAD");
+    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.XPARITY");
+    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.YPARITY");
+    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.READDIR");
 
     // These used to be pmReadoutGetExposure and pmReadoutGetDarkTime, but that doesn't really make sense at
@@ -634,383 +1022,16 @@
     // REALLY derived?  They're not in the FITS headers, because a readout is a plane in a 3D image.  We'll
     // have to dream up some additional suffix to specify these, but for now....
-
-    // CELL.EXPOSURE (used to be READOUT.EXPOSURE)
-    p_pmFPAConceptGetF32(fpa, chip, cell, db, cell->concepts, "CELL.EXPOSURE", "Exposure time (sec)");
-
-    // CELL.DARKTIME (used to be READOUT.DARKTIME)
-    p_pmFPAConceptGetF32(fpa, chip, cell, db, cell->concepts, "CELL.DARKTIME", "Time since CCD flush (sec)");
-
-    // These take some extra work
-
-    // CELL.TRIMSEC
-    {
-	psRegion *trimsec = psAlloc(sizeof(psRegion)); // Make space for a psRegion (usually passed by value)
-
-	psMetadataItem *secItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.TRIMSEC");
-	if (! secItem) {
-	    psError(PS_ERR_IO, false, "Couldn't find CELL.TRIMSEC.\n");
-	    *trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
-	} else if (secItem->type != PS_DATA_STRING) {
-	    psError(PS_ERR_IO, true, "CELL.TRIMSEC is not of type STR (%x)\n", secItem->type);
-	    *trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
-	} else {
-	    psString section = secItem->data.V;	// The section string
-
-	    psMetadataItem *sourceItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.TRIMSEC.SOURCE");
-	    if (! sourceItem) {
-		psError(PS_ERR_IO, false, "Couldn't find CELL.TRIMSEC.SOURCE.\n");
-		*trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
-	    } else if (sourceItem->type != PS_DATA_STRING) {
-		psError(PS_ERR_IO, true, "CELL.TRIMSEC.SOURCE is not of type STR (%x)\n", sourceItem->type);
-		*trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
-	    } else {
-		psString source = sourceItem->data.V; // The source string
-	    
-		if (strcasecmp(source, "VALUE") == 0) {
-		    *trimsec = psRegionFromString(section);
-		} else if (strcasecmp(source, "HEADER") == 0) {
-		    psMetadata *header = NULL; // The FITS header
-		    if (cell->hdu) {
-			header = cell->hdu->header;
-		    } else if (chip->hdu) {
-			header = chip->hdu->header;
-		    } else if (fpa->hdu) {
-			    header = fpa->hdu->header;
-		    }
-		    if (! header) {
-			psError(PS_ERR_IO, true, "Unable to find FITS header!\n");
-			*trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
-		    } else {
-			bool mdok = true;		// Status of MD lookup
-			psString secValue = psMetadataLookupString(&mdok, header, section);
-			if (! mdok || ! secValue) {
-			    psError(PS_ERR_IO, false, "Unable to locate header %s\n", section);
-			    *trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
-			} else {
-			    *trimsec = psRegionFromString(secValue);
-			}
-		    }
-		} else {
-		    psError(PS_ERR_IO, true, "CELL.TRIMSEC.SOURCE (%s) is not HEADER or VALUE --- trying "
-			    "VALUE.\n", source);
-		    *trimsec = psRegionFromString(section);
-		} // Value of CELL.TRIMSEC.SOURCE
-	    } // Looking up CELL.TRIMSEC.SOURCE
-	} // Looking up CELL.TRIMSEC
-
-	psMetadataAdd(cell->concepts, PS_LIST_TAIL, "CELL.TRIMSEC", PS_DATA_UNKNOWN,
-		      "Trim section", trimsec);
-	psFree(trimsec);
-    }
-
-    // CELL.BIASSEC
-    {
-	psList *biassecs = psListAlloc(NULL); // List of bias sections
-
-	psMetadataItem *secItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.BIASSEC");
-	if (! secItem) {
-	    psError(PS_ERR_IO, false, "Couldn't find CELL.BIASSEC.\n");
-	} else if (secItem->type != PS_DATA_STRING) {
-	    psError(PS_ERR_IO, true, "CELL.BIASSEC is not of type STR (%x)\n", secItem->type);
-	} else {
-	    psString sections = secItem->data.V; // The section string
-
-	    psMetadataItem *sourceItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.BIASSEC.SOURCE");
-	    if (! sourceItem) {
-		psError(PS_ERR_IO, false, "Couldn't find CELL.BIASSEC.SOURCE.\n");
-	    } else if (sourceItem->type != PS_DATA_STRING) {
-		psError(PS_ERR_IO, true, "CELL.BIASSEC.SOURCE is not of type STR (%x)\n", sourceItem->type);
-	    } else {
-		psString source = sourceItem->data.V; // The source string
-
-		psList *secList = papSplit(sections, " ;"); // List of sections
-		psListIterator *secIter = psListIteratorAlloc(secList, PS_LIST_HEAD, false); // Iterator over
-											     // sections
-		psString aSection = NULL; // A section from the list
-		while (aSection = psListGetAndIncrement(secIter)) {
-		    psRegion *region = psAlloc(sizeof(psRegion)); // Make space for a psRegion (usually passed
-								  // by value)
-
-		    if (strcasecmp(source, "VALUE") == 0) {
-			*region = psRegionFromString(aSection);
-		    } else if (strcasecmp(source, "HEADER") == 0) {
-			psMetadata *header = NULL; // The FITS header
-			if (cell->hdu) {
-			    header = cell->hdu->header;
-			} else if (chip->hdu) {
-			    header = chip->hdu->header;
-			} else if (fpa->hdu) {
-			    header = fpa->hdu->header;
-			}
-			if (! header) {
-			    psError(PS_ERR_IO, true, "Unable to find FITS header!\n");
-			    *region = psRegionSet(0.0,0.0,0.0,0.0);
-			} else {
-			    bool mdok = true;		// Status of MD lookup
-			    psString secValue = psMetadataLookupString(&mdok, header, aSection);
-			    if (! mdok || ! secValue) {
-				psError(PS_ERR_IO, false, "Unable to locate header %s\n", aSection);
-				*region = psRegionSet(0.0,0.0,0.0,0.0);
-			    } else {
-				*region = psRegionFromString(secValue);
-			    }
-			}
-		    } else {
-			psError(PS_ERR_IO, true, "CELL.BIASSEC.SOURCE (%s) is not HEADER or VALUE --- trying "
-				"VALUE.\n", source);
-			*region = psRegionFromString(aSection);
-		    } // Value of CELL.BIASSEC.SOURCE
-		    
-		    psListAdd(biassecs, PS_LIST_TAIL, region);
-		    psFree(region);
-		} // Iterating over multiple sections
-		psFree(secIter);
-		psFree(secList);
-	    } // Looking up CELL.BIASSEC.SOURCE
-	} // Looking up CELL.BIASSEC
-
-	psMetadataAdd(cell->concepts, PS_LIST_TAIL, "CELL.BIASSEC", PS_DATA_LIST, "Bias sections", biassecs);
-	psFree(biassecs);
-    }
-
-    // CELL.XBIN
-    {
-	int xBin = 1;			// Binning factor in x
-	psMetadataItem *binItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.XBIN");
-	if (! binItem) {
-	    psError(PS_ERR_IO, false, "Couldn't find CELL.XBIN.\n");
-	} else if (binItem->type == PS_DATA_STRING) {
-	    psString binString = binItem->data.V; // The string containing the binning
-	    if (sscanf(binString, "%d %*d", &xBin) != 1 &&
-		sscanf(binString, "%d,%*d", &xBin) != 1) {
-		psError(PS_ERR_IO, true, "Unable to read string to get x binning: %s\n", binString);
-	    }
-	} else if (binItem->type == PS_TYPE_S32) {
-	    xBin = binItem->data.S32;
-	} else {
-	    psError(PS_ERR_IO, true, "Note sure how to interpret CELL.XBIN of type %x --- assuming 1.\n",
-		    binItem->type);
-	}
-
-	psMetadataAdd(cell->concepts, PS_LIST_TAIL, "CELL.XBIN", PS_TYPE_S32, "Binning in x", xBin);
-    }
-
-    // CELL.XBIN
-    {
-	int yBin = 1;			// Binning factor in y
-	psMetadataItem *binItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.YBIN");
-	if (! binItem) {
-	    psError(PS_ERR_IO, false, "Couldn't find CELL.YBIN.\n");
-	} else if (binItem->type == PS_DATA_STRING) {
-	    psString binString = binItem->data.V; // The string containing the binning
-	    if (sscanf(binString, "%*d %d", &yBin) != 1 &&
-		sscanf(binString, "%*d,%d", &yBin) != 1) {
-		psError(PS_ERR_IO, true, "Unable to read string to get y binning: %s\n", binString);
-	    }
-	} else if (binItem->type == PS_TYPE_S32) {
-	    yBin = binItem->data.S32;
-	} else {
-	    psError(PS_ERR_IO, true, "Note sure how to interpret CELL.YBIN of type %x --- assuming 1.\n",
-		    binItem->type);
-	}
-
-	psMetadataAdd(cell->concepts, PS_LIST_TAIL, "CELL.YBIN", PS_TYPE_S32, "Binning in y", yBin);
-    }
-
-    // CELL.TIME and CELL.TIMESYS
-    {
-	psTime *time = NULL;		// The time
-	psTimeType timeSys = PS_TIME_UTC; // The time system
-
-	// CELL.TIMESYS
-	psMetadataItem *sysItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.TIMESYS");
-	if (! sysItem) {
-	    psError(PS_ERR_IO, true, "Couldn't find CELL.TIMESYS --- assuming UTC.\n");
-	} else if (sysItem->type != PS_DATA_STRING) {
-	    psError(PS_ERR_IO, true, "CELL.TIMESYS isn't of type STRING --- assuming UTC.\n");
-	} else {
-	    psString sys = sysItem->data.V; // The time system string
-	    if (strcasecmp(sys, "TAI") == 0) {
-		timeSys = PS_TIME_TAI;
-	    } else if (strcasecmp(sys, "UTC") == 0) {
-		timeSys = PS_TIME_UTC;
-	    } else if (strcasecmp(sys, "UT1") == 0) {
-		timeSys = PS_TIME_UT1;
-	    } else if (strcasecmp(sys, "TT") == 0) {
-		timeSys = PS_TIME_TT;
-	    } else {
-		psError(PS_ERR_IO, true, "Can't interpret CELL.TIMESYS --- assuming UTC.\n");
-	    }
-	}
-	psMetadataAdd(cell->concepts, PS_LIST_TAIL, "CELL.TIMESYS", PS_TYPE_S32, "Time system", timeSys);
-
-	psMetadataItem *timeItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.TIME");
-	if (! timeItem) {
-	    psError(PS_ERR_IO, false, "Couldn't find CELL.TIME.\n");
-	} else {
-	    // Get format
-	    const psMetadata *camera = fpa->camera; // The camera configuration data
-	    bool mdok = true;		// Status of MD lookup
-	    psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS");
-	    if (mdok && formats) {
-		psString timeFormat = psMetadataLookupString(&mdok, formats, "CELL.TIME");
-		if (mdok && strlen(timeFormat) > 0) {
-		    switch (timeItem->type) {
-		      case PS_DATA_STRING:
-			{
-			    psString timeString = timeItem->data.V;	// String with the time
-			    if (strcasecmp(timeFormat, "ISO") == 0) {
-				// timeString contains an ISO time
-				time = psTimeFromISO(timeString, timeSys);
-			    } else if (strstr(timeFormat, "SEPARATE")) {
-				// timeString contains headers for the date and time
-				psMetadata *header = NULL; // The FITS header
-				if (cell->hdu) {
-				    header = cell->hdu->header;
-				} else if (chip->hdu) {
-				    header = chip->hdu->header;
-				} else if (fpa->hdu) {
-				    header = fpa->hdu->header;
-				}
-				if (! header) {
-				    psError(PS_ERR_IO, true, "Unable to find FITS header!\n");
-				} else {
-				    // Get the headers
-				    char *stuff1 = strpbrk(timeString, " ,;");
-				    psString dateName = psStringNCopy(timeString,
-								      strlen(timeString) - strlen(stuff1));
-				    char *stuff2 = strpbrk(stuff1,
-							   "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
-				    psString timeName = psStringCopy(stuff2);
-				    
-				    bool mdok = true; // Status of MD lookup
-				    psString dateString = psMetadataLookupString(&mdok, header, dateName);
-				    psFree(dateName);
-				    int day = 0, month = 0, year = 0;
-				    if (sscanf(dateString, "%d-%d-%d", &day, &month, &year) != 3 &&
-					sscanf(dateString, "%d/%d/%d", &day, &month, &year) != 3) {
-					psError(PS_ERR_IO, true, "Unable to read date: %s\n", dateString);
-				    } else {
-					if (strstr(timeFormat, "BACKWARDS")) {
-					    int temp = day;
-					    day = year;
-					    year = temp;
-					}
-					if (strstr(timeFormat, "PRE2000") || year < 2000) {
-					    year += 2000;
-					}
-					
-					psMetadataItem *timeItem = psMetadataLookup(header, timeName);
-					if (! timeItem) {
-					    psError(PS_ERR_IO, false, "Unable to find time header: %s\n",
-						    timeName);
-					} else if (timeItem->type == PS_DATA_STRING) {
-					    // Time is a string, in the usual way:
-					    psStringAppend(&dateString, "T%s", timeItem->data.V);
-					} else {
-					    // Assume that time is specified in Second of Day
-					    double seconds = NAN;
-					    switch (timeItem->type) {
-					      case PS_TYPE_S32:
-						seconds = timeItem->data.S32;
-						break;
-					      case PS_TYPE_F32:
-						seconds = timeItem->data.F32;
-						break;
-					      case PS_TYPE_F64:
-						seconds = timeItem->data.F64;
-						break;
-					      default:
-						psError(PS_ERR_IO, true, "Time header (%s) is not of an "
-							"expected type: %x\n", timeName, timeItem->type);
-					    }
-					    // Now print to timeString as "hh:mm:ss.ss"
-					    int hours = seconds / 3600;
-					    seconds -= (double)hours * 3600.0;
-					    int minutes = seconds / 60;
-					    seconds -= (double)minutes * 60.0;
-					    psStringAppend(&dateString, "T%02d:%02d:%02f", hours, minutes,
-							   seconds);
-					}
-					time = psTimeFromISO(dateString, timeSys);
-				    } // Reading date and time
-				    psFree(timeName);
-				} // Reading headers
-			    } else {
-				psError(PS_ERR_IO, true, "Not sure how to parse CELL.TIME (%s) --- trying "
-					"ISO\n", timeString);
-				time = psTimeFromISO(timeString, timeSys);
-			    } // Interpreting the time string
-			}
-			break;
-		      case PS_TYPE_F32:
-			{
-			    double timeValue = (double)timeItem->data.F32;
-			    if (strcasecmp(timeFormat, "JD") == 0) {
-				time = psTimeFromJD(timeValue);
-			    } else if (strcasecmp(timeFormat, "MJD") == 0) {
-				time = psTimeFromMJD(timeValue);
-			    } else {
-				psError(PS_ERR_IO, true, "Not sure how to parse CELL.TIME (%f) --- trying "
-					"JD\n", timeValue);
-				time = psTimeFromJD(timeValue);
-			    }
-			}
-			break;
-		      case PS_TYPE_F64:
-			{
-			    double timeValue = (double)timeItem->data.F64;
-			    if (strcasecmp(timeFormat, "JD") == 0) {
-				time = psTimeFromJD(timeValue);
-			    } else if (strcasecmp(timeFormat, "MJD") == 0) {
-				time = psTimeFromMJD(timeValue);
-			    } else {
-				psError(PS_ERR_IO, true, "Not sure how to parse CELL.TIME (%f) --- trying "
-					"JD\n", timeValue);
-				time = psTimeFromJD(timeValue);
-			    }
-			}
-			break;
-		      default:
-			psError(PS_ERR_IO, true, "Unable to parse CELL.TIME.\n");
-		    }
-		} else {
-		    psError(PS_ERR_IO, false, "Unable to find CELL.TIME in FORMATS.\n");
-		} // Getting the format
-	    } else {
-		psError(PS_ERR_IO, false, "Unable to find FORMATS in camera configuration.\n");
-	    } // Getting the formats
-	} // Getting CELL.TIME
-
-	psMetadataAdd(cell->concepts, PS_LIST_TAIL, "CELL.TIME", PS_DATA_UNKNOWN, "Time of exposure", time);
-	psFree(time);
-    }
-
-    // These are new and experimental concepts: CELL.X0 and CELL.Y0
-
-    // CELL.X0
-    p_pmFPAConceptGetS32(fpa, chip, cell, db, cell->concepts, "CELL.X0", "Position of (0,0) on the chip ");
-    // CELL.Y0
-    p_pmFPAConceptGetS32(fpa, chip, cell, db, cell->concepts, "CELL.Y0", "Position of (0,0) on the chip");
-    // Add corrective
-    {
-	const psMetadata *camera = fpa->camera;
-	bool mdok = false;		// Result of MD lookup
-	psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS");
-	if (mdok && formats) {
-	    psString format = psMetadataLookupString(&mdok, formats, "CELL.X0");
-	    if (mdok && strlen(format) > 0 && strcasecmp(format, "FORTRAN") == 0) {
-		psMetadataItem *cellx0 = psMetadataLookup(cell->concepts, "CELL.X0");
-		cellx0->data.S32 -= 1;
-	    }
-	    format = psMetadataLookupString(&mdok, formats, "CELL.Y0");
-	    if (mdok && strlen(format) > 0 && strcasecmp(format, "FORTRAN") == 0) {
-		psMetadataItem *celly0 = psMetadataLookup(cell->concepts, "CELL.Y0");
-		celly0->data.S32 -= 1;
-	    }
-	}
-    }
-
-    // Pau.
+    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.EXPOSURE");
+    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.DARKTIME");
+
+
+    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.TRIMSEC");
+    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.BIASSEC");
+    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.TIME"); // This also does CELL.TIMESYS
+    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.XBIN");
+    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.YBIN");
+    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.X0");
+    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.Y0");
+
 }
 
@@ -1018,16 +1039,16 @@
 // Retrieve a concept
 psMetadataItem *pmCellGetConcept(pmCell *cell, // The cell
-				 const char *concept // The concept
+                                 const char *concept // The concept
     )
 {
     psMetadataItem *item = psMetadataLookup(cell->concepts, concept);
     if (! item) {
-	pmChip *chip = cell->parent;
-	item = psMetadataLookup(chip->concepts, concept);
-	if (! item) {
-	    pmFPA *fpa = chip->parent;
-	    item = psMetadataLookup(fpa->concepts, concept);
-	}
-    }
-    return item;			// item is either NULL or is what we want.
-}
+        pmChip *chip = cell->parent;
+        item = psMetadataLookup(chip->concepts, concept);
+        if (! item) {
+            pmFPA *fpa = chip->parent;
+            item = psMetadataLookup(fpa->concepts, concept);
+        }
+    }
+    return item;                        // item is either NULL or is what we want.
+}
