Index: /branches/eam_rel9_p0/psModules/src/astrom/pmChipMosaic.c
===================================================================
--- /branches/eam_rel9_p0/psModules/src/astrom/pmChipMosaic.c	(revision 6123)
+++ /branches/eam_rel9_p0/psModules/src/astrom/pmChipMosaic.c	(revision 6124)
@@ -131,4 +131,79 @@
 }
 
+
+// Set the concepts in the new cell, based on the values in the old one
+static bool cellConcepts(pmCell *target,// Target cell
+                         psArray *sources, // Source cells
+                         int xBin, int yBin // Binning
+                        )
+{
+    bool success = true;                // Result of setting everything
+    float gain       = 0.0;             // Gain
+    float readnoise  = 0.0;             // Read noise
+    float saturation = 0.0;             // Saturation level
+    float bad        = 0.0;             // Bad level
+    float exposure   = 0.0;             // Exposure time
+    float darktime   = 0.0;             // Dark time
+    double time      = 0.0;             // Time of observation
+    psTimeType timeSys = 0;             // Time system
+
+    int nCells = 0;                     // Number of cells;
+    for (int i = 0; i < sources->n; i++) {
+        pmCell *cell = sources->data[i];// The cell of interest
+        if (!cell) {
+            continue;
+        }
+        nCells++;
+        gain       += psMetadataLookupF32(NULL, cell->concepts, "CELL.GAIN");
+        readnoise  += psMetadataLookupF32(NULL, cell->concepts, "CELL.READNOISE");
+        saturation += psMetadataLookupF32(NULL, cell->concepts, "CELL.SATURATION");
+        bad        += psMetadataLookupF32(NULL, cell->concepts, "CELL.BAD");
+        exposure   += psMetadataLookupF32(NULL, cell->concepts, "CELL.EXPOSURE");
+        darktime   += psMetadataLookupF32(NULL, cell->concepts, "CELL.DARKTIME");
+        time       += psTimeToMJD(psMetadataLookupPtr(NULL, cell->concepts, "CELL.TIME"));
+        if (i == 0) {
+            timeSys = psMetadataLookupS32(NULL, cell->concepts, "CELL.TIMESYS");
+        } else if (timeSys != psMetadataLookupS32(NULL, cell->concepts, "CELL.TIMESYS")) {
+            psLogMsg(__func__, PS_LOG_ERROR, "Differing time systems in use: %d vs %d\n", timeSys,
+                     psMetadataLookupS32(NULL, cell->concepts, "CELL.TIMESYS"));
+            success = false;
+        }
+    }
+    gain       /= (float)nCells;
+    readnoise  /= (float)nCells;
+    saturation /= (float)nCells;
+    bad        /= (float)nCells;
+    exposure   /= (float)nCells;
+    darktime   /= (float)nCells;
+    psTime *timePtr = psTimeFromMJD(time/(double)nCells);
+    timePtr = psTimeConvert(timePtr, timeSys);
+
+    // XXX *REALLY* need a generic "concept update" function that handles the type and comments transparently.
+    psMetadataAddF32(target->concepts, PS_LIST_TAIL, "CELL.GAIN", PS_META_REPLACE, "Gain (e/ADU)", gain);
+    psMetadataAddF32(target->concepts, PS_LIST_TAIL, "CELL.READNOISE", PS_META_REPLACE, "Read noise (e)", readnoise);
+    psMetadataAddF32(target->concepts, PS_LIST_TAIL, "CELL.SATURATION", PS_META_REPLACE, "Saturation level (ADU)", saturation);
+    psMetadataAddF32(target->concepts, PS_LIST_TAIL, "CELL.BAD", PS_META_REPLACE, "Bad level (ADU)", bad);
+    psMetadataAddF32(target->concepts, PS_LIST_TAIL, "CELL.EXPOSURE", PS_META_REPLACE, "Exposure time (sec)", exposure);
+    psMetadataAddF32(target->concepts, PS_LIST_TAIL, "CELL.DARKTIME", PS_META_REPLACE, "Time since last CCD flush (sec)", darktime);
+    psMetadataAddPtr(target->concepts, PS_LIST_TAIL, "CELL.TIME", PS_DATA_TIME | PS_META_REPLACE, "Time of observation", timePtr);
+    psMetadataAddS32(target->concepts, PS_LIST_TAIL, "CELL.TIMESYS", PS_META_REPLACE, "Time system", timeSys);
+    psFree(timePtr);
+
+    // Now fill in the ones I know by other means
+    psMetadataAddS32(target->concepts, PS_LIST_TAIL, "CELL.X0", PS_META_REPLACE, "Position of (0,0) on the chip", 0);
+    psMetadataAddS32(target->concepts, PS_LIST_TAIL, "CELL.Y0", PS_META_REPLACE, "Position of (0,0) on the chip", 0);
+    psMetadataAddS32(target->concepts, PS_LIST_TAIL, "CELL.XPARITY", PS_META_REPLACE, "Orientation in x compared to the rest of the FPA", 1);
+    psMetadataAddS32(target->concepts, PS_LIST_TAIL, "CELL.YPARITY", PS_META_REPLACE, "Orientation in x compared to the rest of the FPA", 1);
+    psMetadataAddS32(target->concepts, PS_LIST_TAIL, "CELL.XBIN", PS_META_REPLACE, "Binning in x", xBin);
+    psMetadataAddS32(target->concepts, PS_LIST_TAIL, "CELL.YBIN", PS_META_REPLACE, "Binning in x", yBin);
+    psMetadataAddS32(target->concepts, PS_LIST_TAIL, "CELL.READDIR", PS_META_REPLACE, "Read direction (faked)", 1);
+    psRegion *trimsec = psMetadataLookupPtr(NULL, target->concepts, "CELL.TRIMSEC");
+    trimsec->x0 = trimsec->x1 = trimsec->y0 = trimsec->y1 = 0.0;
+
+    return success;
+}
+
+
+
 // Mosaic a chip together into a single image
 int pmChipMosaic(pmChip *chip,// Chip to mosaic
@@ -152,4 +227,7 @@
     for (int i = 0; i < cells->n; i++) {
         pmCell *cell = cells->data[i];  // The cell of interest
+        if (!cell) {
+            continue;
+        }
         x0->data.S32[i] = psMetadataLookupS32(NULL, cell->concepts, "CELL.X0");
         y0->data.S32[i] = psMetadataLookupS32(NULL, cell->concepts, "CELL.Y0");
@@ -206,7 +284,33 @@
     psFree(masks);
     int nCells = cells->n;
+
+    // Modify the "original" pixels
+    if (chip->parent->hdu) {
+        psLogMsg(__func__, PS_LOG_WARN, "The original format has the entire FPA in a single extension.  "
+                 "The FPA hierarchy may be invalid following the pmChipMosaic.\n");
+    } else {
+        if (! chip->hdu) {
+            psString chipName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME");
+            chip->hdu = p_pmHDUAlloc(chipName);
+        }
+        p_pmHDU *hdu = chip->hdu;
+        psArrayElementsFree(hdu->images);
+        psArrayElementsFree(hdu->weights);
+        psArrayElementsFree(hdu->masks);
+        hdu->images  = psArrayAlloc(1);
+        hdu->weights = psArrayAlloc(1);
+        hdu->masks   = psArrayAlloc(1);
+        hdu->images->data[0]  = image;
+        hdu->weights->data[0] = weight;
+        hdu->masks->data[0]   = mask;
+        psMetadataAddS32(hdu->header, PS_LIST_TAIL, "NAXIS1", PS_META_REPLACE, "Number of columns", image->numCols);
+        psMetadataAddS32(hdu->header, PS_LIST_TAIL, "NAXIS2", PS_META_REPLACE, "Number of rows", image->numRows);
+    }
+
+    // Chop off all the component cells, and put in a new one
+    chip->cells = psArrayAlloc(0);
+    pmCell *cell = pmCellAlloc(chip, NULL, __func__); // New cell
+    cellConcepts(cell, cells, xBinChip, yBinChip);
     psFree(cells);
-
-    // Chop off all the component cells, and put in a new one
 
     #if 1
@@ -214,20 +318,27 @@
     // the double free is coming from.  I'm going to drop the pointers on the array and create a memory
     // leak.  We can clean this up later, when we're not under as much pressure.
-    chip->cells = psArrayAlloc(0);
     #else
 
     psArrayElementsFree(chip->cells);
-    chip->cells = psArrayRealloc(chip->cells, 0);
     chip->cells->n = 0;
     #endif
 
-    pmCell *cell = pmCellAlloc(chip, NULL, __func__); // New cell
     cell->exists = true;
     cell->process = true;
     pmReadout *readout = pmReadoutAlloc(cell); // New readout
-    readout->image = image;
-    readout->weight = weight;
-    readout->mask = mask;
-    //psFree(readout);
+    psRegion entire = {0.0, 0.0, 0.0, 0.0};
+    // Want the readouts to contain a subimage, but that subimage is the whole image.
+    // This preserves the relationship there was before, where freeing the parent frees the child.
+    readout->image = psImageSubset(image, entire);
+    readout->weight = psImageSubset(weight, entire);
+    readout->mask = psImageSubset(mask, entire);
+    psFree(readout);
+
+    // Well, we've stuffed around with the camera configuration, so it's no longer valid...
+    #if 0
+
+    psFree(chip->parent->camera);
+    chip->parent->camera = NULL;
+    #endif
 
     return nCells;
Index: /branches/eam_rel9_p0/psModules/src/astrom/pmConcepts.c
===================================================================
--- /branches/eam_rel9_p0/psModules/src/astrom/pmConcepts.c	(revision 6123)
+++ /branches/eam_rel9_p0/psModules/src/astrom/pmConcepts.c	(revision 6124)
@@ -1,2 +1,4 @@
+// XXX *REALLY* need generic "concept update" and "concept read" functions that handles the type transparently
+
 #include <stdio.h>
 #include <assert.h>
@@ -156,4 +158,7 @@
 {
     pmConceptsInit();
+    if (! fpa->camera) {
+        return false;
+    }
     psMetadataIterator *iter = psMetadataIteratorAlloc(source, PS_LIST_HEAD, NULL); // Iterator on concepts
     psMetadataItem *item = NULL;    // Item from the concepts
Index: /branches/eam_rel9_p0/psModules/src/astrom/pmConceptsRead.c
===================================================================
--- /branches/eam_rel9_p0/psModules/src/astrom/pmConceptsRead.c	(revision 6123)
+++ /branches/eam_rel9_p0/psModules/src/astrom/pmConceptsRead.c	(revision 6124)
@@ -275,8 +275,8 @@
                     "undefined.\n", name, item->comment, item->type);
         }
+        psTrace(__func__, 7, "Adding %s (%s): %f\n", name, item->comment, value);
     } else {
         psError(PS_ERR_IO, true, "Concept %s is not defined.\n", name);
     }
-    psTrace(__func__, 7, "Adding %s (%s): %f\n", name, item->comment, value);
 
     return value;
@@ -309,8 +309,8 @@
                     "--- treating as undefined.\n", name, item->comment, item->type);
         }
+        psTrace(__func__, 7, "Adding %s (%s): %f\n", name, item->comment, value);
     } else {
         psError(PS_ERR_IO, true, "Concept %s is not defined.\n", name);
     }
-    psTrace(__func__, 7, "Adding %s (%s): %f\n", name, item->comment, value);
 
     return value;
@@ -345,8 +345,8 @@
                     "undefined.\n", name, item->comment, item->type);
         }
+        psTrace(__func__, 7, "Read concept %s (%s): %d\n", name, item->comment, value);
     } else {
         psError(PS_ERR_IO, true, "Concept %s is not defined.\n", name);
     }
-    psTrace(__func__, 7, "Read concept %s (%s): %d\n", name, item->comment, value);
 
     return value;
@@ -377,9 +377,9 @@
                     "undefined.\n", name, item->comment, item->type);
         }
+        psTrace(__func__, 7, "Read concept %s (%s): %s\n", name, item->comment, value);
     } else {
         psError(PS_ERR_IO, true, "Concept %s is not defined.\n", name);
         value = psStringCopy("");
     }
-    psTrace(__func__, 7, "Read concept %s (%s): %s\n", name, item->comment, value);
 
     return value;
Index: /branches/eam_rel9_p0/psModules/src/astrom/pmConceptsStandard.c
===================================================================
--- /branches/eam_rel9_p0/psModules/src/astrom/pmConceptsStandard.c	(revision 6123)
+++ /branches/eam_rel9_p0/psModules/src/astrom/pmConceptsStandard.c	(revision 6124)
@@ -321,69 +321,78 @@
 {
     psList *biassecs = psListAlloc(NULL); // List of bias sections
+    psMetadataItem *item = psMetadataItemAlloc("CELL.BIASSEC", PS_DATA_LIST, "Bias sections", biassecs);
+    psFree(biassecs);               // Drop reference
 
     psMetadataItem *secItem = pmConceptRead(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) {
+        return item;
+    }
+    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 = pmConceptRead(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);
+        return item;
+    }
+
+    psString sections = secItem->data.V; // The section string
+
+    psMetadataItem *sourceItem = pmConceptRead(fpa, chip, cell, db, "CELL.BIASSEC.SOURCE");
+    if (! sourceItem) {
+        psError(PS_ERR_IO, false, "Couldn't find CELL.BIASSEC.SOURCE.\n");
+        return item;
+    } else if (sourceItem->type != PS_DATA_STRING) {
+        psError(PS_ERR_IO, true, "CELL.BIASSEC.SOURCE is not of type STR (%x)\n", sourceItem->type);
+        return item;
+    }
+
+    psString source = sourceItem->data.V; // The source string
+
+    if (! strcasecmp(source, "NONE")) {
+        return item;                // There is no biassec
+    }
+
+    psList *secList = psStringSplit(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 = psMetadataLookupStr(&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 {
-            psString source = sourceItem->data.V; // The source string
-
-            psList *secList = psStringSplit(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 = psMetadataLookupStr(&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
-
-    psMetadataItem *item = psMetadataItemAlloc("CELL.BIASSEC", PS_DATA_LIST, "Bias sections", biassecs);
-    psFree(biassecs);
+            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);
+
     return item;
 }
@@ -689,4 +698,11 @@
     psMetadataItem *biassecItem = psMetadataLookup(cell->concepts, "CELL.BIASSEC");
     psList *biassecs = biassecItem->data.V; // The biassecs region list
+
+    if (biassecs->n == 0) {
+        psMetadataItem *noneItem = psMetadataItemAllocStr("CELL.BIASSEC", "Bias section", "NONE");
+        pmConceptWriteItem(fpa, chip, cell, db, noneItem);
+        return true;
+    }
+
     psString source = pmConceptReadString(fpa, chip, cell, db, "CELL.TRIMSEC.SOURCE"); // The source string
 
Index: /branches/eam_rel9_p0/psModules/src/astrom/pmConceptsWrite.c
===================================================================
--- /branches/eam_rel9_p0/psModules/src/astrom/pmConceptsWrite.c	(revision 6123)
+++ /branches/eam_rel9_p0/psModules/src/astrom/pmConceptsWrite.c	(revision 6124)
@@ -73,4 +73,7 @@
                            )
 {
+    if (! cell->camera) {
+        return false;
+    }
     if (cell) {
         psMetadataItem *item = psMetadataLookup(cell->camera, concept->name); // Info we want
@@ -90,4 +93,7 @@
     bool mdok = true;                   // Status of MD lookup
     bool status = false;                // Status of setting header
+    if (! fpa->camera) {
+        return false;
+    }
     psMetadata *translation = psMetadataLookupMD(&mdok, fpa->camera, "TRANSLATION"); // FITS translation
     if (! mdok) {
@@ -158,4 +164,7 @@
 {
     bool mdOK = true;                   // Status of MD lookup
+    if (! fpa->camera) {
+        return false;
+    }
     psMetadata *defaults = psMetadataLookupMD(&mdOK, fpa->camera, "DEFAULTS");
     if (! mdOK || ! defaults) {
@@ -224,4 +233,7 @@
 
     bool mdStatus = true;               // Status of MD lookup
+    if (! fpa->camera) {
+        return false;
+    }
     psMetadata *database = psMetadataLookupMD(&mdStatus, fpa->camera, "DATABASE");
     if (! mdStatus) {
@@ -319,4 +331,8 @@
                        )
 {
+    if (! fpa->camera) {
+        return false;
+    }
+
     // Try headers, database, defaults in order
     psTrace(__func__, 3, "Trying to set concept %s...\n", concept->name);
Index: /branches/eam_rel9_p0/psModules/src/astrom/pmFPARead.c
===================================================================
--- /branches/eam_rel9_p0/psModules/src/astrom/pmFPARead.c	(revision 6123)
+++ /branches/eam_rel9_p0/psModules/src/astrom/pmFPARead.c	(revision 6124)
@@ -161,5 +161,5 @@
 
     // Read in....
-    psTrace(__func__, 1, "Working on FPA...\n");
+    psTrace(__func__, 1, "Reading FPA...\n");
     if (fpa->hdu) {
         hdu = fpa->hdu;
@@ -217,8 +217,8 @@
                 }
             }
+            psTrace(__func__, 5, "Reading concepts for cell %d...\n", j);
             pmConceptsReadCell(cell, db);
 
-            psTrace(__func__, 5, "Allocating readouts for chip %d cell %d...\n",
-                    i, j);
+            psTrace(__func__, 5, "Allocating readouts for chip %d cell %d...\n", i, j);
             generateReadouts(cell, hdu);
 
@@ -227,4 +227,6 @@
         chip->exists = true;
     }
+
+    psTrace(__func__, 1, "Done reading FPA...\n");
 
     return true;
Index: /branches/eam_rel9_p0/psModules/src/astrom/pmFPAWrite.c
===================================================================
--- /branches/eam_rel9_p0/psModules/src/astrom/pmFPAWrite.c	(revision 6123)
+++ /branches/eam_rel9_p0/psModules/src/astrom/pmFPAWrite.c	(revision 6124)
@@ -27,4 +27,6 @@
 {
     bool status = true;                 // Status of writing, to return
+
+    psTrace(__func__, 1, "Writing FPA...\n");
 
     psTrace(__func__, 7, "Outgesting FPA concepts...\n");
@@ -70,5 +72,5 @@
     }
 
-
+    psTrace(__func__, 1, "Done writing FPA...\n");
 
     return status;
