Index: /trunk/psModules/src/camera/pmFPAfileDefine.c
===================================================================
--- /trunk/psModules/src/camera/pmFPAfileDefine.c	(revision 13703)
+++ /trunk/psModules/src/camera/pmFPAfileDefine.c	(revision 13704)
@@ -93,16 +93,16 @@
 }
 
-// define a pmFPAfile, bind to the optional fpa if supplied
-pmFPAfile *pmFPAfileDefineOutput(const pmConfig *config, pmFPA *fpa, const char *name)
-{
-    PS_ASSERT_PTR_NON_NULL(config, NULL);
-    PS_ASSERT_PTR_NON_NULL(config->files, NULL);
-    PS_ASSERT_PTR_NON_NULL(config->camera, NULL);
-    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
-
+// Define an output pmFPAfile
+static pmFPAfile *fpaFileDefineOutput(const pmConfig *config, // Configuration
+                                      pmFPA *fpa, // Optional FPA to bind
+                                      const char *name, // Name of file rule
+                                      const char *cameraName, // Name of camera configuration to use
+                                      const char *formatName // Name of camera format to use
+    )
+{
     bool status;
 
     // select the FILERULES from the camera config
-    psMetadata *filerules = psMetadataLookupPtr (&status, config->camera, "FILERULES");
+    psMetadata *filerules = psMetadataLookupPtr(&status, config->camera, "FILERULES");
     if (filerules == NULL) {
         psError(PS_ERR_IO, true, "Can't find FILERULES in the CAMERA configuration!");
@@ -112,5 +112,5 @@
     // select the name from the FILERULES
     // check for alias name (type == STR, name is aliased name)
-    const char *realname = psMetadataLookupStr (&status, filerules, name);
+    const char *realname = psMetadataLookupStr(&status, filerules, name);
     if (!realname || strlen(realname) == 0) {
         realname = name;
@@ -123,12 +123,12 @@
     }
 
-    pmFPAfile *file = pmFPAfileAlloc ();
+    pmFPAfile *file = pmFPAfileAlloc();
 
     // save the name of this pmFPAfile
-    file->name = psStringCopy (name);
-
-    file->filerule = psMemIncrRefCounter(psMetadataLookupStr (&status, data, "FILENAME.RULE"));
-
-    const char *type = psMetadataLookupStr (&status, data, "FILE.TYPE");
+    file->name = psStringCopy(name);
+
+    file->filerule = psMemIncrRefCounter(psMetadataLookupStr(&status, data, "FILENAME.RULE"));
+
+    const char *type = psMetadataLookupStr(&status, data, "FILE.TYPE");
     file->type = pmFPAfileTypeFromString(type);
     if (file->type == PM_FPA_FILE_NONE) {
@@ -153,17 +153,40 @@
     }
 
-    psMetadata *camera;                 // Camera to use
-    if (fpa && fpa->camera) {
-        camera = (psMetadata*)fpa->camera; // Casting away const, so I can put it in the file
+    // Use the camera we were told to, the camera of the provided FPA, or default to the default camera
+    psMetadata *camera;                 // Camera configuration
+    if (!cameraName || strlen(cameraName) == 0) {
+        if (fpa && fpa->camera) {
+            camera = (psMetadata*)fpa->camera; // Casting away const, so I can put it in the file
+        } else {
+            camera = config->camera;
+        }
     } else {
-        camera = config->camera;
+        bool mdok;                      // Status of MD lookup
+        psMetadata *cameras = psMetadataLookupMetadata(&mdok, config->site, "CAMERAS"); // Known cameras
+        if (!mdok || !cameras) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find CAMERAS in the site configuration.\n");
+            return NULL;
+        }
+        camera = psMetadataLookupMetadata(&mdok, cameras, cameraName); // Camera configuration of interest
+        if (!mdok || !camera) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find automatically generated "
+                    "camera configuration %s in site configuration.\n", cameraName);
+            return NULL;
+        }
+
+        if (fpa && fpa->camera && fpa->camera != camera) {
+            psAbort("Camera of bound FPA is not the requested camera --- there is an inconsistency!");
+        }
     }
     file->camera = psMemIncrRefCounter(camera);
 
-    // select the format list from the selected camera
-    const char *formatName = psMetadataLookupStr (&status, data, "FILE.FORMAT");
-    if (!formatName || strcmp(formatName, "NONE") == 0) {
-        // Try to get by with the default
-        formatName = config->formatName;
+    // Use the format we were told to, the format specified in the file rule, or default to the default format
+    if (!formatName || strlen(formatName) == 0) {
+        // select the format list from the selected camera
+        formatName = psMetadataLookupStr(&status, data, "FILE.FORMAT");
+        if (!formatName || strcmp(formatName, "NONE") == 0) {
+            // Try to get by with the default
+            formatName = config->formatName;
+        }
     }
     psMetadata *formats = psMetadataLookupMetadata(&status, file->camera, "FORMATS"); // List of formats
@@ -177,5 +200,9 @@
     file->format = psMemIncrRefCounter(format);
 
-    file->fpa = psMemIncrRefCounter(fpa);
+    if (fpa) {
+        file->fpa = psMemIncrRefCounter(fpa);
+    } else {
+        file->fpa = pmFPAConstruct(file->camera);
+    }
 
     file->fileLevel = pmFPAPHULevel(format);
@@ -219,4 +246,14 @@
 }
 
+// define a pmFPAfile, bind to the optional fpa if supplied
+pmFPAfile *pmFPAfileDefineOutput(const pmConfig *config, pmFPA *fpa, const char *name)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_PTR_NON_NULL(config->files, NULL);
+    PS_ASSERT_PTR_NON_NULL(config->camera, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(name, NULL);
+
+    return fpaFileDefineOutput(config, fpa, name, NULL, NULL);
+}
 
 // search for argname on the config->argument list
@@ -245,5 +282,5 @@
     psArray *infiles = psMetadataLookupPtr(&status, config->arguments, argname);
     if (!status) {
-	if (success) *success = true;
+        if (success) *success = true;
         return NULL;
     }
@@ -415,5 +452,5 @@
     if (!status) {
         // this is not an error: this just means no matching argument was supplied
-	if (success) *success = true;
+        if (success) *success = true;
         return NULL;
     }
@@ -545,5 +582,5 @@
     if (!status) {
         psTrace("psModules.camera", 5, "Failed to find %s in argument list", argname);
-	if (success) *success = true;
+        if (success) *success = true;
         return NULL;
     }
@@ -679,5 +716,5 @@
         // don't free the file here: it is left on config->files
         // to be used optionally by pmFPAfileDefineFromDetDB (or others)
-	if (success) *success = true;
+        if (success) *success = true;
         return NULL;
     }
@@ -908,4 +945,34 @@
 }
 
+pmFPAfile *pmFPAfileDefineSkycell(const pmConfig *config, pmFPA *fpa, const char *filename)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(filename, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(config->cameraName, NULL);
+    PS_ASSERT_STRING_NON_EMPTY(config->formatName, NULL);
+
+    pmFPAfile *file;                    // The new file
+
+    if (config->cameraName[0] == '_' &&
+        strcmp(config->cameraName + strlen(config->cameraName) - 8, "-SKYCELL") == 0) {
+        // The input camera is already a skycell
+        file = fpaFileDefineOutput(config, fpa, filename, config->cameraName, "SKYCELL");
+    } else {
+        // Find the correct camera configuration
+        psString cameraName = NULL;         // Name of the new (automatically-generated) camera configuration
+        psStringAppend(&cameraName, "_%s-SKYCELL", config->cameraName);
+
+        file = fpaFileDefineOutput(config, fpa, filename, cameraName, "SKYCELL");
+        psFree(cameraName);
+    }
+
+    // Ensure everything is written out at the appropriate level
+    file->fileLevel = PM_FPA_LEVEL_FPA;
+    file->dataLevel = PM_FPA_LEVEL_FPA;
+    file->freeLevel = PM_FPA_LEVEL_FPA;
+
+    return file;
+}
+
 pmFPAfile *pmFPAfileDefineChipMosaic(const pmConfig *config, pmFPA *src, const char *filename)
 {
@@ -916,66 +983,21 @@
     PS_ASSERT_STRING_NON_EMPTY(config->formatName, NULL);
 
+    pmFPAfile *file;                    // The new file
     if (config->cameraName[0] == '_' &&
         strcmp(config->cameraName + strlen(config->cameraName) - 5, "-CHIP") == 0) {
         // The input camera has already been mosaicked to this level
-        pmFPAfile *file = pmFPAfileDefineOutput(config, NULL, filename);
-        psFree (file->camera);
-        psFree (file->format);
-        psFree (file->fpa);
-        file->camera = psMemIncrRefCounter(config->camera);
-        file->format = psMemIncrRefCounter(config->format);
-        file->fpa = pmFPAConstruct(file->camera);
-        return file;
-    }
-
-    // Find the correct camera configuration
-    bool mdok;                          // Status of MD lookup
-    psMetadata *cameras = psMetadataLookupMetadata(&mdok, config->site, "CAMERAS"); // Camera configurations
-    if (!mdok || !cameras) {
-        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find CAMERAS in the site configuration.\n");
-        return NULL;
-    }
-    psString name = NULL;               // Name of the new (automatically-generated) camera configuration
-    psStringAppend(&name, "_%s-CHIP", config->cameraName);
-    psMetadata *camera = psMetadataLookupMetadata(&mdok, cameras, name); // Camera configuration of interest
-    if (!mdok || !camera) {
-        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find automatically generated "
-                "camera configuration %s in site configuration.\n", name);
-        psFree(name);
-        return NULL;
-    }
-    psFree(name);
-
-    // Need to look up the format of the same name, but under the mosaic camera
-    psMetadata *formats = psMetadataLookupMetadata(&mdok, camera, "FORMATS"); // The FORMATS
-    if (!mdok || !formats) {
-        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find FORMATS in camera configuration %s.\n",
-                config->cameraName);
-        return NULL;
-    }
-    psMetadata *format = psMetadataLookupMetadata(&mdok, formats, config->formatName); // The format
-    if (!mdok || !format) {
-        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find camera format %s within camera %s.\n",
-                config->formatName, config->cameraName);
-        return NULL;
-    }
-
-    pmFPA *fpa = pmFPAConstruct(camera);
-    if (!fpa) {
-        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate mosaicked camera.\n");
-        return NULL;
-    }
-    pmFPAfile *file = pmFPAfileDefineOutput(config, fpa, filename);
-    if (!file) {
-        psErrorStackPrint(stderr, "file %s not defined\n", filename);
-        return NULL;
-    }
+        file = fpaFileDefineOutput(config, NULL, filename, config->cameraName, config->formatName);
+    } else {
+        psString cameraName = NULL;         // Name of the new (automatically-generated) camera configuration
+        psStringAppend(&cameraName, "_%s-CHIP", config->cameraName);
+
+        // Find the correct camera configuration
+        file = fpaFileDefineOutput(config, NULL, filename, cameraName, config->formatName);
+        psFree(cameraName);
+    }
+
     file->src = src; // inherit output elements from this source pmFPA
+
     file->mosaicLevel = PM_FPA_LEVEL_CHIP; // don't do any I/O on this at a lower level
-
-    psFree (file->format);
-    file->format = psMemIncrRefCounter(format);
-
-    psFree(fpa);
 
     return file;
@@ -989,74 +1011,29 @@
     PS_ASSERT_STRING_NON_EMPTY(config->cameraName, NULL);
 
+    pmFPAfile *file;                    // The new file
     if (config->cameraName[0] == '_' &&
             strcmp(config->cameraName + strlen(config->cameraName) - 4 , "-FPA") == 0) {
         // The input camera has already been mosaicked to this level
-        pmFPAfile *file = pmFPAfileDefineOutput(config, NULL, filename);
-        psFree (file->camera);
-        psFree (file->format);
-        psFree (file->fpa);
-        file->camera = psMemIncrRefCounter(config->camera);
-        file->format = psMemIncrRefCounter(config->format);
-        file->fpa = pmFPAConstruct(file->camera);
-        return file;
-    }
-
-    // Find the correct camera configuration
-    bool mdok;                          // Status of MD lookup
-    psMetadata *cameras = psMetadataLookupMetadata(&mdok, config->site, "CAMERAS"); // Camera configurations
-    if (!mdok || !cameras) {
-        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find CAMERAS in the site configuration.\n");
-        return NULL;
-    }
-    psString original;                  // Name of the original camera configuration
-    psString name = NULL;               // Name of the new (automatically-generated) camera configuration
-    if (config->cameraName[0] == '_' &&
+        file = fpaFileDefineOutput(config, NULL, filename, config->cameraName, config->formatName);
+    } else {
+
+        psString original;                  // Name of the original camera configuration
+        if (config->cameraName[0] == '_' &&
             strcmp(config->cameraName + strlen(config->cameraName) - 5 , "-CHIP") == 0) {
-        // It's a chip mosaic; we need to get the original name
-        original = psStringNCopy(config->cameraName + 1, strlen(config->cameraName) - 6);
-    } else {
-        original = psMemIncrRefCounter(config->cameraName);
-    }
-    psStringAppend(&name, "_%s-FPA", original);
-    psFree(original);
-    psMetadata *camera = psMetadataLookupMetadata(&mdok, cameras, name); // Camera configuration of interest
-    if (!mdok || !camera) {
-        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find automatically generated "
-                "camera configuration %s in site configuration.\n", name);
-        psFree(name);
-        return NULL;
-    }
-    psFree(name);
-
-    // Need to look up the format of the same name, but under the mosaic camera
-    psMetadata *formats = psMetadataLookupMetadata(&mdok, camera, "FORMATS"); // The FORMATS
-    if (!mdok || !formats) {
-        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find FORMATS in camera configuration %s.\n",
-                config->cameraName);
-        return NULL;
-    }
-    psMetadata *format = psMetadataLookupMetadata(&mdok, formats, config->formatName); // The format
-    if (!mdok || !format) {
-        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find camera format %s within camera %s.\n",
-                config->formatName, config->cameraName);
-        return NULL;
-    }
-
-    pmFPA *fpa = pmFPAConstruct(camera);
-    if (!fpa) {
-        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate mosaicked camera.\n");
-        return NULL;
-    }
-    pmFPAfile *file = pmFPAfileDefineOutput(config, fpa, filename);
-    if (!file) {
-        psErrorStackPrint(stderr, "file %s not defined\n", filename);
-        return NULL;
-    }
+            // It's a chip mosaic; we need to get the original name
+            original = psStringNCopy(config->cameraName + 1, strlen(config->cameraName) - 6);
+        } else {
+            original = psMemIncrRefCounter(config->cameraName);
+        }
+        psString cameraName = NULL;
+        psStringAppend(&cameraName, "_%s-FPA", original);
+
+        file = fpaFileDefineOutput(config, NULL, filename, cameraName, config->formatName);
+        psFree(cameraName);
+    }
+
     file->src = src; // inherit output elements from this source pmFPA
+
     file->mosaicLevel = PM_FPA_LEVEL_FPA; // don't do any I/O on this at a lower level
-    psFree (file->format);
-    file->format = psMemIncrRefCounter(format);
-
-    psFree(fpa);
 
     return file;
Index: /trunk/psModules/src/camera/pmFPAfileDefine.h
===================================================================
--- /trunk/psModules/src/camera/pmFPAfileDefine.h	(revision 13703)
+++ /trunk/psModules/src/camera/pmFPAfileDefine.h	(revision 13704)
@@ -4,6 +4,6 @@
  * @author EAM, IfA
  *
- * @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
- * @date $Date: 2007-05-24 02:38:07 $
+ * @version $Revision: 1.12 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-06-08 00:31:50 $
  * Copyright 2004-2005 Institute for Astronomy, University of Hawaii
  */
@@ -66,4 +66,14 @@
 pmFPAfile *pmFPAfileDefineNewCamera (const pmConfig *config, const char *filename);
 
+/// Create a new output pmFPAfile for a skycell of the default camera
+///
+/// The new pmFPAfile is inserted into the config->files metadata, freed and returned; so that the user does
+/// not have to (and should not!) free the result.
+pmFPAfile *pmFPAfileDefineSkycell(const pmConfig *config, ///< Configuration data
+                                  pmFPA *fpa, ///< FPA to which to bind
+                                  const char *filename ///< Output (root) filename
+    );
+
+
 /// Create a new output pmFPAfile based upon a chip mosaic of an existing FPA
 ///
Index: /trunk/psModules/src/config/pmConfig.c
===================================================================
--- /trunk/psModules/src/config/pmConfig.c	(revision 13703)
+++ /trunk/psModules/src/config/pmConfig.c	(revision 13704)
@@ -4,6 +4,6 @@
  *  @author EAM (IfA)
  *
- *  @version $Revision: 1.94 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2007-05-11 23:28:15 $
+ *  @version $Revision: 1.95 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-06-08 00:31:50 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -533,4 +533,12 @@
                                   "Camera specified on command line", config->camera);
 
+            if (!pmConfigCameraSkycellVersion(config->site, cameraFile)) {
+                psError(PS_ERR_UNKNOWN, false,
+                        "Unable to generate skycell versions of specified camera %s.\n",
+                        cameraFile);
+                psFree(config);
+                return NULL;
+            }
+
             if (!pmConfigCameraMosaickedVersions(config->site, cameraFile)) {
                 psError(PS_ERR_UNKNOWN, false,
@@ -570,4 +578,9 @@
         psFree(iter);
 
+        if (!pmConfigCameraSkycellVersionsAll(config->site)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to generate skycell versions of cameras.\n");
+            psFree(config);
+            return NULL;
+        }
         if (!pmConfigCameraMosaickedVersionsAll(config->site)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to generate mosaicked versions of cameras.\n");
Index: /trunk/psModules/src/config/pmConfigCamera.c
===================================================================
--- /trunk/psModules/src/config/pmConfigCamera.c	(revision 13703)
+++ /trunk/psModules/src/config/pmConfigCamera.c	(revision 13704)
@@ -5,5 +5,5 @@
 #include <stdio.h>
 #include <string.h>
-#include <strings.h>		/* for strn?casecmp */
+#include <strings.h>            /* for strn?casecmp */
 #include <pslib.h>
 
@@ -11,4 +11,5 @@
 #include "pmFPA.h"
 #include "pmFPALevel.h"
+#include "pmVersion.h"
 #include "pmConcepts.h"
 #include "pmConfigCamera.h"
@@ -21,4 +22,226 @@
 static void removeCellConceptsSources(psMetadata *source);
 static void removeChipConceptsSources(psMetadata *source);
+
+// Generate the skycell version of a named camera configuration
+bool pmConfigCameraSkycellVersion(psMetadata *site, // The site configuration
+                           const char *name // Name of the un-mosaicked camera
+                           )
+{
+    PS_ASSERT_METADATA_NON_NULL(site, false);
+    PS_ASSERT_STRING_NON_EMPTY(name, false);
+
+    bool mdok;                          // Status of MD lookup
+    psMetadata *cameras = psMetadataLookupMetadata(&mdok, site, "CAMERAS"); // List of cameras
+    if (!mdok || !cameras) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find CAMERAS in the site configuration.\n");
+        return false;
+    }
+    if (!pmConfigGenerateSkycellVersion(cameras, cameras, name)) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to build skycell camera description for %s\n", name);
+        return false;
+    }
+    return true;
+}
+
+
+bool pmConfigCameraSkycellVersionsAll(psMetadata *site)
+{
+    PS_ASSERT_METADATA_NON_NULL(site, false);
+
+    bool mdok;                          // Status of MD lookup
+    psMetadata *cameras = psMetadataLookupMetadata(&mdok, site, "CAMERAS"); // List of cameras
+    if (!mdok || !cameras) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find CAMERAS in the site configuration.\n");
+        return false;
+    }
+
+    psMetadataIterator *camerasIter = psMetadataIteratorAlloc(cameras, PS_LIST_HEAD, NULL); // Iterator
+    psMetadataItem *camerasItem = NULL; // Item from iteration
+    psMetadata *new = psMetadataAlloc();// New cameras to add
+    while ((camerasItem = psMetadataGetAndIncrement(camerasIter))) {
+        assert(camerasItem->type == PS_DATA_METADATA); // Only metadata are allowed here!
+        if (!pmConfigGenerateSkycellVersion(cameras, new, camerasItem->name)) {
+            psError(PS_ERR_UNKNOWN, true, "Failed to build skycell camera description for %s\n",
+                    camerasItem->name);
+            return false;
+        }
+    }
+    psFree(camerasIter);
+
+    // Now put the new cameras at the top of the list of cameras, so they get recognised first
+    // Note: going from the top, and putting everything to the top as we get there, so that the last one on
+    // goes to the top.  This preserves the original order of the cameras, putting the skycell versions
+    // before the originals.
+    camerasIter = psMetadataIteratorAlloc(new, PS_LIST_HEAD, NULL); // Iterator
+    while ((camerasItem = psMetadataGetAndIncrement(camerasIter))) {
+        psMetadataAddItem(cameras, camerasItem, PS_LIST_HEAD, 0);
+    }
+    psFree(camerasIter);
+    psFree(new);
+
+    return true;
+}
+
+// Generate a skycell version of a camera configuration
+bool pmConfigGenerateSkycellVersion(psMetadata *oldCameras, // Old list of camera configurations
+                                    psMetadata *newCameras, // New list of camera configurations
+                                    const char *name // Name of original camera configuration
+                                    )
+{
+    assert(oldCameras);
+    assert(newCameras);
+    assert(name);
+
+    // See if the old one is there
+    psMetadata *camera = psMetadataLookupMetadata(NULL, oldCameras, name); // The camera configuration
+    if (!camera) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Can't find camera to be skycelled in camera list.");
+        return false;
+    }
+
+    // See if the new one is already there
+    psString newName = NULL;       // Name of skycelled camera
+    psStringAppend(&newName, "_%s-SKYCELL", name);
+    if (psMetadataLookup(oldCameras, newName)) {
+        return true;
+    }
+
+    psMetadata *new = psMetadataCopy(NULL, camera); // Copy of the camera description
+
+    // Fix the FPA description to contain a single chip with single cell
+    {
+        psMetadata *fpa = psMetadataAlloc();// The FPA description
+        psMetadataAddStr(fpa, PS_LIST_HEAD, "SkyChip", 0, "Single chip with single cell", "SkyCell");
+        psMetadataAddMetadata(new, PS_LIST_TAIL, "FPA", PS_META_REPLACE, "Description of FPA hierarchy", fpa);
+        psFree(fpa);
+    }
+
+    // Clear out the formats, replace them with the One True Format
+    psMetadata *formats = psMetadataLookupMetadata(NULL, new, "FORMATS"); // The list of formats
+    if (!formats) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Can't find FORMATS within camera configuration.");
+        psFree(new);
+        return false;
+    }
+    while (psListLength(formats->list) > 0) {
+        psMetadataRemoveIndex(formats, PS_LIST_HEAD);
+    }
+    psMetadata *format = psMetadataAlloc(); // The One True Format
+
+    {
+        psMetadata *rule = psMetadataAlloc(); // The RULE --- how to recognise the camera
+        psMetadataAddStr(rule, PS_LIST_TAIL, "PSCAMERA", 0, "Camera name", name);
+        psMetadataAddStr(rule, PS_LIST_TAIL, "PSFORMAT", 0, "Camera format", "SKYCELL");
+        psString version = psModulesVersion();
+        psMetadataAddStr(rule, PS_LIST_TAIL, "ORIGIN", 0, "File origin", version);
+        psFree(version);
+        psMetadataAddMetadata(format, PS_LIST_TAIL, "RULE", 0, "How to recognise this type of file", rule);
+        psFree(rule);
+    }
+
+    {
+        psMetadata *file = psMetadataAlloc(); // The FILE --- how to read the data
+        psMetadataAddStr(file, PS_LIST_TAIL, "PHU", 0, "What level the FITS file represents", "FPA");
+        psMetadataAddStr(file, PS_LIST_TAIL, "EXTENSIONS", 0, "What level the extensions represent", "NONE");
+        psMetadataAddStr(file, PS_LIST_TAIL, "FPA.NAME", 0, "PHU keyword for unique identifier", "FPA.NAME");
+        psMetadataAddMetadata(format, PS_LIST_TAIL, "FILE", 0, "How to read this type of file", file);
+        psFree(file);
+    }
+
+    psMetadataAddStr(format, PS_LIST_TAIL, "CONTENTS", 0, "What's in this type of file",
+                     "SkyChip:SkyCell:_skycell");
+
+    {
+        psMetadata *cells = psMetadataAlloc(); // The CELLS --- how to read the cells
+        psMetadata *skycell = psMetadataAlloc(); // How to read the skycell
+        psMetadataAddStr(skycell, PS_LIST_TAIL, "CELL.TRIMSEC", 0, "Trim section", "CELL.TRIMSEC");
+        psMetadataAddStr(skycell, PS_LIST_TAIL, "CELL.BIASSEC", 0, "Bias section", "CELL.BIASSEC");
+        psMetadataAddStr(skycell, PS_LIST_TAIL, "CELL.TRIMSEC.SOURCE", 0, "Source for trim section",
+                         "HEADER");
+        psMetadataAddStr(skycell, PS_LIST_TAIL, "CELL.BIASSEC.SOURCE", 0, "Source for bias section",
+                         "HEADER");
+        psMetadataAddMetadata(cells, PS_LIST_TAIL, "_skycell", 0, "Skycell specification", skycell);
+        psFree(skycell);
+        psMetadataAddMetadata(format, PS_LIST_TAIL, "CELLS", 0, "How to read the cells", cells);
+        psFree(cells);
+    }
+
+    // Stuffing all concepts into the header, by their PS concept name (e.g., "FPA.AIRMASS").
+    // (HIERARCH will take care of the long names, implemented in psLib.)
+    // Some people may not like this, but it's quick and easy and will do for now.
+    // An alternative may be provided later.
+    {
+        psMetadata *translation = psMetadataAlloc(); // The TRANSLATION --- how to read the FITS headers
+
+        psList *concepts;               // List of concepts for each level
+        psListIterator *iter;           // Iterator for concepts
+        psString name;                  // Concept name, from iteration
+
+        concepts = pmConceptsList(PM_FPA_LEVEL_FPA); // FPA-level concepts
+        iter = psListIteratorAlloc(concepts, PS_LIST_HEAD, false);
+        while ((name = psListGetAndIncrement(iter))) {
+            psMetadataAddStr(translation, PS_LIST_TAIL, name, 0, NULL, name);
+        }
+        psFree(iter);
+        psFree(concepts);
+
+        concepts = pmConceptsList(PM_FPA_LEVEL_CHIP);
+        iter = psListIteratorAlloc(concepts, PS_LIST_HEAD, false);
+        while ((name = psListGetAndIncrement(iter))) {
+            psMetadataAddStr(translation, PS_LIST_TAIL, name, 0, NULL, name);
+        }
+        psFree(iter);
+        psFree(concepts);
+
+        concepts = pmConceptsList(PM_FPA_LEVEL_CELL);
+        iter = psListIteratorAlloc(concepts, PS_LIST_HEAD, false);
+        while ((name = psListGetAndIncrement(iter))) {
+            // We've done CELL.BIASSEC and CELL.TRIMSEC under CELLS, above.
+            if (strcmp(name, "CELL.BIASSEC") != 0 && strcmp(name, "CELL.TRIMSEC") != 0) {
+                psMetadataAddStr(translation, PS_LIST_TAIL, name, 0, NULL, name);
+            }
+        }
+        psFree(iter);
+        psFree(concepts);
+
+        psMetadataAddMetadata(format, PS_LIST_TAIL, "TRANSLATION", 0, "How to translate the FITS headers",
+                              translation);
+        psFree(translation);
+    }
+
+    {
+        psMetadata *defaults = psMetadataAlloc(); // Default values for concepts
+        psMetadataAddMetadata(format, PS_LIST_TAIL, "DEFAULTS", 0, "Default values for concepts", defaults);
+        psFree(defaults);
+    }
+
+    {
+        psMetadata *database = psMetadataAlloc(); // Database values for concepts
+        psMetadataAddMetadata(format, PS_LIST_TAIL, "DATABASE", 0, "Database values for concepts", database);
+        psFree(database);
+    }
+
+    {
+        psMetadata *conceptFormats = psMetadataAlloc(); // Format peculiarities for various concepts
+        // These are the only essential formats
+        psMetadataAddStr(conceptFormats, PS_LIST_TAIL, "FPA.RA", 0, "Units for RA", "HOURS");
+        psMetadataAddStr(conceptFormats, PS_LIST_TAIL, "FPA.DEC", 0, "Units for RA", "DEGREES");
+        psMetadataAddMetadata(format, PS_LIST_TAIL, "FORMATS", 0, "Formats for various concepts",
+                              conceptFormats);
+        psFree(conceptFormats);
+    }
+
+    psMetadataAddMetadata(formats, PS_LIST_TAIL, "SKYCELL", 0, "The One True Format for skycells", format);
+    psFree(format);
+
+    // New camera MUST go to the head of the metadata, so that it will be recognised first
+    // The old camera doesn't contain the PSCAMERA and PSFORMAT headers, so it will match anything!
+    psMetadataAddMetadata(newCameras, PS_LIST_HEAD, newName, PS_META_REPLACE,
+                          "Automatically generated", new);
+    psFree(newName);
+    psFree(new);
+
+    return true;
+}
 
 // Generate the Chip and FPA mosaicked version of a named camera configuration
@@ -66,11 +289,11 @@
         assert(camerasItem->type == PS_DATA_METADATA); // Only metadata are allowed here!
         if (!pmConfigGenerateMosaickedVersion(cameras, new, camerasItem->name, PM_FPA_LEVEL_CHIP)) {
-	    psError(PS_ERR_UNKNOWN, true, "Failed to build Chip mosaic camera description for %s\n", camerasItem->name);
-	    return false;
-	}
+            psError(PS_ERR_UNKNOWN, true, "Failed to build Chip mosaic camera description for %s\n", camerasItem->name);
+            return false;
+        }
         if (!pmConfigGenerateMosaickedVersion(cameras, new, camerasItem->name, PM_FPA_LEVEL_FPA)) {
-	    psError(PS_ERR_UNKNOWN, true, "Failed to build FPA mosaic camera description for %s\n", camerasItem->name);
-	    return false;
-	}
+            psError(PS_ERR_UNKNOWN, true, "Failed to build FPA mosaic camera description for %s\n", camerasItem->name);
+            return false;
+        }
     }
     psFree(camerasIter);
@@ -92,7 +315,7 @@
 // Generate a mosaicked version of a camera configuration
 bool pmConfigGenerateMosaickedVersion(psMetadata *oldCameras, // Old list of camera configurations
-				      psMetadata *newCameras, // New list of camera configurations
-				      const char *name, // Name of original camera configuration
-				      pmFPALevel mosaicLevel // Level to which we are mosaicking
+                                      psMetadata *newCameras, // New list of camera configurations
+                                      const char *name, // Name of original camera configuration
+                                      pmFPALevel mosaicLevel // Level to which we are mosaicking
     )
 {
@@ -101,4 +324,9 @@
     assert(name);
     assert(mosaicLevel == PM_FPA_LEVEL_CHIP || mosaicLevel == PM_FPA_LEVEL_FPA);
+
+    // Don't generate a mosaicked version of a skycell
+    if (name[0] == '_' && strlen(name) > 9 && strcmp(name + strlen(name) - 8, "-SKYCELL") == 0) {
+        return true;
+    }
 
     // See if the old one is there
Index: /trunk/psModules/src/config/pmConfigCamera.h
===================================================================
--- /trunk/psModules/src/config/pmConfigCamera.h	(revision 13703)
+++ /trunk/psModules/src/config/pmConfigCamera.h	(revision 13704)
@@ -1,10 +1,10 @@
 /*  @file pmConfigCamera.h
  *  @brief Camera Configuration functions
- * 
+ *
  *  @author Paul Price, IfA
  *  @author Eugene Magnier, IfA
- * 
- *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2007-03-30 21:12:56 $
+ *
+ *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-06-08 00:31:50 $
  *  Copyright 2005-2006 Institute for Astronomy, University of Hawaii
  */
@@ -16,9 +16,25 @@
 /// @{
 
+// Generate a skycell version of a camera configuration
+bool pmConfigGenerateSkycellVersion(psMetadata *oldCameras, // Old list of camera configurations
+                                    psMetadata *newCameras, // New list of camera configurations
+                                    const char *name // Name of original camera configuration
+    );
+
+/// Generate the skycell version of a particular camera configuration
+bool pmConfigCameraSkycellVersion(psMetadata *site, // The site configuration
+                           const char *name // Name of the un-mosaicked camera
+    );
+
+
+/// Generate skycell versions of all the camera configurations
+bool pmConfigCameraSkycellVersionsAll(psMetadata *site // Site configuration
+    );
+
 // Generate a mosaicked version of a camera configuration
 bool pmConfigGenerateMosaickedVersion(psMetadata *oldCameras, // Old list of camera configurations
-				      psMetadata *newCameras, // New list of camera configurations
-				      const char *name, // Name of original camera configuration
-				      pmFPALevel mosaicLevel // Level to which we are mosaicking
+                                      psMetadata *newCameras, // New list of camera configurations
+                                      const char *name, // Name of original camera configuration
+                                      pmFPALevel mosaicLevel // Level to which we are mosaicking
     );
 
