Index: /branches/eam_branches/20091201/psModules/src/astrom/pmAstrometryWCS.c
===================================================================
--- /branches/eam_branches/20091201/psModules/src/astrom/pmAstrometryWCS.c	(revision 26878)
+++ /branches/eam_branches/20091201/psModules/src/astrom/pmAstrometryWCS.c	(revision 26879)
@@ -378,5 +378,5 @@
     // XXX make it optional to write out CDi_j terms, or other versions
     // apply CDELT1,2 (degrees / pixel) to yield PCi,j terms of order unity
-    if (!(wcs->wcsCDkeys)) {
+    if (!wcs->wcsCDkeys) { 
 
       double cdelt1 = wcs->cdelt1;
@@ -419,6 +419,5 @@
         psMetadataRemoveKey(header, "CD2_2");
       }
-    }
-    if (wcs->wcsCDkeys) {
+    } else {
 
       psMetadataAddF64 (header, PS_LIST_TAIL, "CD1_1", PS_META_REPLACE, "", wcs->trans->x->coeff[1][0]);
@@ -845,13 +844,13 @@
     int k=0;
     for (int j=0; j<nSamples; j++) {
-        double y = j * deltaY / nSamples;
+        double y = bounds->y0 + (j * deltaY / nSamples);
         for (int i=0; i<nSamples; i++) {
             psPlane *s = psPlaneAlloc();
-            s->x = i * deltaX / nSamples;
+            s->x = bounds->x0 + (i * deltaX / nSamples);
             s->y = y;
             psArraySet(src, k, s);
             psPlane *d = psPlaneTransformApply(NULL, trans, s);
             psArraySet(dst, k, d);
-            psFree(s);
+            psFree(s);  // drop our refs to s and d
             psFree(d);
             ++k;
@@ -869,4 +868,5 @@
     // compare the computed coordintes from this transform with the original
     psPlane *new = psPlaneAlloc();
+    printf("   i     chip_x  fpa_x     fpa_x_fit     dx         chip_y    fpa_y     fpa_y_fit     dy     dx > 0.5 || dy > 0.5\n");
     for (int i=0; i<psArrayLength(dst); i++) {
         psPlane *d = (psPlane *) psArrayGet(dst, i);
@@ -875,5 +875,9 @@
         new = psPlaneTransformApply(new, newTrans, s);
 
-        printf("%4d %f %f\n", i, 100.*(new->x - d->x)/d->x, 100.*(new->y - d->y)/d->y);
+        double xerr = new->x - d->x;
+        double yerr = new->y - d->y;
+        bool bigerr = (fabs(xerr) > .5) || (fabs(yerr) > .5);
+        printf("%4d %9.2f %9.2f %9.2f %9.2f     %9.2f %9.2f %9.2f %9.2f   %s\n"
+        , i, s->x, new->x, d->x, xerr, s->y, new->y, d->y, yerr, bigerr ? "BIGERR" : "");
     }
     psFree(new);
@@ -891,10 +895,43 @@
     psRegion    *chipBounds = pmChipPixels(chip);
 
-    psPlaneTransform *newToFPA = linearFitToTransform(chip->toFPA, chipBounds);
+#ifdef TESTING_CAMRUN_14728
+    chipBounds->y0 = 1874.;
+    chipBounds->x1 = 2387.;
+#endif
+
+    // First combine the chip to FPA and FPA to TPA into a single transformation
+    psPlaneTransform *chipToTPA = psPlaneTransformCombine(NULL, chip->toFPA, fpa->toTPA, *chipBounds, 50);
+    if (!chipToTPA) {
+        psFree(chipBounds);
+        psError(PS_ERR_UNKNOWN, false, "failed to create chipToTPA");
+        return false;
+    }
+
+    // Next do a linear fit
+    psPlaneTransform *newToFPA = linearFitToTransform(chipToTPA, chipBounds);
+    psFree(chipToTPA);
     if (!newToFPA) {
         psFree(chipBounds);
-        psError(PS_ERR_UNKNOWN, false, "linear fit for toFPA failed");
-        return false;
-    }
+        psError(PS_ERR_UNKNOWN, false, "linear fit of chip to TPA transform failed");
+        return false;
+    }
+
+    psPlaneTransform *newFromFPA = psPlaneTransformInvert(NULL, newToFPA, *chipBounds, 50);
+    psFree(chipBounds);
+    if (!newFromFPA) {
+        psFree(newToFPA);
+        psError(PS_ERR_UNKNOWN, false, "inversion of fit of chip to TPA transform failed");
+        return false;
+    }
+
+    // Success. Now set the fpa's toTPA and fromTPA to identity and replace the chip's transforms.
+
+    psPlaneTransform *newToTPA   = psPlaneTransformIdentity(1);
+    psFree(fpa->toTPA);
+    fpa->toTPA = newToTPA;
+
+    psPlaneTransform *newFromTPA = psPlaneTransformIdentity(1);
+    psFree(fpa->fromTPA);
+    fpa->fromTPA = newFromTPA;
 
     psFree(chip->toFPA);
@@ -902,48 +939,8 @@
 
     psFree(chip->fromFPA);
-    chip->fromFPA = psPlaneTransformInvert(NULL, chip->toFPA, *chipBounds, 50);
-    if (!chip->fromFPA) {
-        psError(PS_ERR_UNKNOWN, false, "failed to invert linear fit for toFPA");
-        return false;
-    }
-
-    psPlane *chip0 = psPlaneAlloc();
-    chip0->x = 0;
-    chip0->y = 0;
-    psPlane *chip1 = psPlaneAlloc();
-    chip1->x = chipBounds->x1;
-    chip1->y = chipBounds->y1;
-
-    // compute bounding region for fpa
-    psPlane *fpa0 = psPlaneTransformApply(NULL, newToFPA, chip0);
-    psPlane *fpa1 = psPlaneTransformApply(NULL, newToFPA, chip1);
-
-    psRegion *fpaBounds = psRegionAlloc(fpa0->x, fpa1->x, fpa0->y, fpa1->y);
-    psFree(chip0);
-    psFree(chip1);
-    psFree(fpa0);
-    psFree(fpa1);
-
-    psPlaneTransform *newToTPA = linearFitToTransform(fpa->toTPA, fpaBounds);
-    if (!newToTPA) {
-        psError(PS_ERR_UNKNOWN, false, "failed to perform linear fit to toTPA");
-        psFree(fpaBounds);
-        return false;
-    }
-    psFree(fpa->toTPA);
-    fpa->toTPA = newToTPA;
-
-    // XXX: is this region ok?
-    psFree(fpa->fromTPA);
-    fpa->fromTPA = psPlaneTransformInvert(NULL, fpa->toTPA, *fpaBounds, 50);
-    if (!fpa->fromTPA) {
-        psError(PS_ERR_UNKNOWN, false, "failed to invert linear fit to toTPA");
-        return false;
-    }
-
+    chip->fromFPA = newFromFPA;
+
+    // Finally change the type for the projection.
     fpa->toSky->type = PS_PROJ_TAN;
-
-    psFree(chipBounds);
-    psFree(fpaBounds);
 
     return true;
Index: /branches/eam_branches/20091201/psModules/src/camera/pmFPA.c
===================================================================
--- /branches/eam_branches/20091201/psModules/src/camera/pmFPA.c	(revision 26878)
+++ /branches/eam_branches/20091201/psModules/src/camera/pmFPA.c	(revision 26879)
@@ -378,4 +378,5 @@
     tmpFPA->toTPA = NULL;
     tmpFPA->toSky = NULL;
+    tmpFPA->wcsCDkeys = false;
 
     tmpFPA->analysis = psMetadataAlloc();
Index: /branches/eam_branches/20091201/psModules/src/camera/pmReadoutFake.c
===================================================================
--- /branches/eam_branches/20091201/psModules/src/camera/pmReadoutFake.c	(revision 26878)
+++ /branches/eam_branches/20091201/psModules/src/camera/pmReadoutFake.c	(revision 26879)
@@ -23,11 +23,16 @@
 #include "pmSourceUtils.h"
 #include "pmModelUtils.h"
+#include "pmSourceGroups.h"
 
 #include "pmReadoutFake.h"
 
-#define MODEL_TYPE "PS_MODEL_RGAUSS"    // Type of model to use
 #define MAX_AXIS_RATIO 20.0             // Maximum axis ratio for PSF model
 #define MODEL_MASK (PM_MODEL_STATUS_NONCONVERGE | PM_MODEL_STATUS_OFFIMAGE | \
                     PM_MODEL_STATUS_BADARGS | PM_MODEL_STATUS_LIMITS) // Mask to apply to models
+
+
+static bool threaded = false;           // Running threaded?
+
+
 
 
@@ -47,4 +52,186 @@
     }
     return pmPSF_AxesToModel(params, axes);
+}
+
+/// Generate fake sources on a readout
+static bool readoutFake(pmReadout *readout, // Readout of interest
+                        const pmSourceGroups *groups, // Source groups
+                        const psVector *x,        // x coordinates
+                        const psVector *y,        // y coordinates
+                        const psVector *mag,      // Magnitudes
+                        const psVector *xOffset,  // Offsets in x
+                        const psVector *yOffset,  // Offsets in y
+                        const pmPSF *psf,         // PSF
+                        float minFlux,            // Minimum flux
+                        float radius,             // Minimum radius
+                        bool circularise,         // Circularise PSF?
+                        bool normalisePeak,       // Normalise sources for peak?
+                        int groupIndex,           // Group index
+                        int cellIndex             // Cell index
+                        )
+{
+    psArray *cells = groups->groups->data[groupIndex]; // Cells in group
+    psVector *cellSources = cells->data[cellIndex];    // Sources in cell
+
+    for (int i = 0; i < cellSources->n; i++) {
+        int index = cellSources->data.S32[i];                       // Index for source of interest
+        float flux = powf(10.0, -0.4 * mag->data.F32[index]);       // Flux of source
+        float xSrc = x->data.F32[index], ySrc = y->data.F32[index]; // Coordinates of source
+
+        if (normalisePeak) {
+            // Normalise flux
+            pmModel *normModel = pmModelFromPSFforXY(psf, xSrc, ySrc, 1.0); // Model for normalisation
+            if (!normModel || (normModel->flags & MODEL_MASK)) {
+                psFree(normModel);
+                continue;
+            }
+            // check that all params are valid:
+            bool validParams = true;
+            for (int j = 0; validParams && (j < normModel->params->n); j++) {
+                switch (j) {
+                  case PM_PAR_SKY:
+                  case PM_PAR_I0:
+                  case PM_PAR_XPOS:
+                  case PM_PAR_YPOS:
+                    continue;
+                  default:
+                    if (!isfinite(normModel->params->data.F32[j])) {
+                        validParams = false;
+                    }
+                }
+            }
+            if (!validParams) {
+                psFree(normModel);
+                continue;
+            }
+            if (circularise && !circulariseModel(normModel)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to circularise PSF model.");
+                psFree(normModel);
+                return false;
+            }
+
+            flux /= normModel->modelFlux(normModel->params);
+            psFree(normModel);
+        }
+
+        pmModel *fakeModel = pmModelFromPSFforXY(psf, xSrc, ySrc, flux);
+        if (!fakeModel || (fakeModel->flags & MODEL_MASK)) {
+            psFree(fakeModel);
+            continue;
+        }
+        // check that all params are valid:
+        bool validParams = true;
+        for (int j = 0; validParams && (j < fakeModel->params->n); j++) {
+            switch (j) {
+              case PM_PAR_SKY:
+              case PM_PAR_I0:
+              case PM_PAR_XPOS:
+              case PM_PAR_YPOS:
+                continue;
+              default:
+                if (!isfinite(fakeModel->params->data.F32[j])) {
+                    validParams = false;
+                }
+            }
+        }
+        if (!validParams) {
+            psFree(fakeModel);
+            continue;
+        }
+        if (circularise && !circulariseModel(fakeModel)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to circularise PSF model.");
+            psFree(fakeModel);
+            return false;
+        }
+
+        psTrace("psModules.camera", 10, "Adding source at %f,%f with flux %f\n",
+                fakeModel->params->data.F32[PM_PAR_XPOS], fakeModel->params->data.F32[PM_PAR_YPOS],
+                fakeModel->params->data.F32[PM_PAR_I0]);
+
+        pmSource *fakeSource = pmSourceAlloc(); // Fake source to generate
+        fakeSource->peak = pmPeakAlloc(xSrc, ySrc, fakeModel->params->data.F32[PM_PAR_I0], PM_PEAK_LONE);
+        float fakeRadius = 1.0;         // Radius of fake source
+        if (isfinite(minFlux)) {
+            fakeRadius = PS_MAX(fakeRadius, fakeModel->modelRadius(fakeModel->params, minFlux));
+        }
+        if (radius > 0) {
+            fakeRadius = PS_MAX(fakeRadius, radius);
+        }
+
+        if (xOffset) {
+            if (!pmSourceDefinePixels(fakeSource, readout, xSrc + xOffset->data.S32[index],
+                                      ySrc + yOffset->data.S32[index], fakeRadius)) {
+                psErrorClear();
+                continue;
+            }
+            if (!pmModelAddWithOffset(fakeSource->pixels, NULL, fakeModel, PM_MODEL_OP_FULL, 0,
+                                      xOffset->data.S32[index], yOffset->data.S32[index])) {
+                psErrorClear();
+                continue;
+            }
+        } else {
+            if (!pmSourceDefinePixels(fakeSource, readout, xSrc, ySrc, fakeRadius)) {
+                psErrorClear();
+                continue;
+            }
+            if (!pmModelAdd(fakeSource->pixels, NULL, fakeModel, PM_MODEL_OP_FULL, 0)) {
+                psErrorClear();
+                continue;
+            }
+        }
+        psFree(fakeSource);
+        psFree(fakeModel);
+    }
+
+    return true;
+}
+
+/// Thread job for readoutFake()
+static bool readoutFakeThread(psThreadJob *job)
+{
+    PS_ASSERT_THREAD_JOB_NON_NULL(job, false);
+
+    psArray *args = job->args;          // Arguments
+
+    pmReadout *readout = args->data[0];     // Readout of interest
+    const pmSourceGroups *groups = args->data[1]; // Source groups
+    const psVector *x = args->data[2];        // x coordinates
+    const psVector *y = args->data[3];        // y coordinates
+    const psVector *mag = args->data[4];      // Magnitudes
+    const psVector *xOffset = args->data[5];  // Offsets in x
+    const psVector *yOffset = args->data[6];  // Offsets in y
+    const pmPSF *psf = args->data[7];         // PSF
+    float minFlux = PS_SCALAR_VALUE(args->data[8], F32); // Minimum flux
+    float radius = PS_SCALAR_VALUE(args->data[9], F32);  // Minimum radius
+    bool circularise = PS_SCALAR_VALUE(args->data[10], U8); // Circularise PSF?
+    bool normalisePeak = PS_SCALAR_VALUE(args->data[11], U8); // Normalise for peak?
+    int groupIndex = PS_SCALAR_VALUE(args->data[12], S32); // Group index
+    int cellIndex = PS_SCALAR_VALUE(args->data[13], S32);  // Cell index
+
+    return readoutFake(readout, groups, x, y, mag, xOffset, yOffset, psf, minFlux, radius, circularise,
+                       normalisePeak, groupIndex, cellIndex);
+}
+
+
+bool pmReadoutFakeThreads(bool new)
+{
+    bool old = threaded;                // Old status, to return
+
+    if (!old && new) {
+        threaded = true;
+
+        {
+            psThreadTask *task = psThreadTaskAlloc("PSMODULES_READOUT_FAKE", 14);
+            task->function = &readoutFakeThread;
+            psThreadTaskAdd(task);
+            psFree(task);
+        }
+
+    } else if (old && !new) {
+        threaded = false;
+        psThreadTaskRemove("PSMODULES_READOUT_FAKE");
+    }
+
+    return old;
 }
 
@@ -86,102 +273,73 @@
     psImageInit(readout->image, 0);
 
-    for (long i = 0; i < numSources; i++) {
-        float flux = powf(10.0, -0.4 * mag->data.F32[i]); // Flux of source
-        float xSrc = x->data.F32[i], ySrc = y->data.F32[i]; // Coordinates of source
-
-        if (normalisePeak) {
-            // Normalise flux
-            pmModel *normModel = pmModelFromPSFforXY(psf, xSrc, ySrc, 1.0); // Model for normalisation
-            if (!normModel || (normModel->flags & MODEL_MASK)) {
-                psFree(normModel);
-                continue;
-            }
-	    // check that all params are valid:
-	    bool validParams = true;
-	    for (int n = 0; validParams && (n < normModel->params->n); n++) {
-		if (n == PM_PAR_SKY) continue;
-		if (n == PM_PAR_I0) continue;
-		if (n == PM_PAR_XPOS) continue;
-		if (n == PM_PAR_YPOS) continue;
-		if (!isfinite(normModel->params->data.F32[n])) validParams = false;
-	    }
-	    if (!validParams) {
-                psFree(normModel);
-		continue;
-	    }		
-            if (circularise && !circulariseModel(normModel)) {
-                psError(PS_ERR_UNKNOWN, false, "Unable to circularise PSF model.");
-                psFree(normModel);
+    int numThreads = threaded ? psThreadPoolSize() : 0; // Number of threads
+    pmSourceGroups *groups = pmSourceGroupsFromVectors(readout, x, y, numThreads); // Groups of sources
+    if (!groups) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to generate source groups");
+        return false;
+    }
+
+    if (threaded) {
+        for (int i = 0; i < groups->groups->n; i++) {
+            psArray *cells = groups->groups->data[i]; // Cell with sources
+            for (int j = 0; j < cells->n; j++) {
+                psThreadJob *job = psThreadJobAlloc("PSMODULES_READOUT_FAKE");
+                psArray *args = job->args;
+                psArrayAdd(args, 1, readout);
+                psArrayAdd(args, 1, groups);
+                // Casting away const to add to array
+                psArrayAdd(args, 1, (psVector*)x);
+                psArrayAdd(args, 1, (psVector*)y);
+                psArrayAdd(args, 1, (psVector*)mag);
+                psArrayAdd(args, 1, (psVector*)xOffset);
+                psArrayAdd(args, 1, (psVector*)yOffset);
+                psArrayAdd(args, 1, (pmPSF*)psf);
+                PS_ARRAY_ADD_SCALAR(args, minFlux, PS_TYPE_F32);
+                PS_ARRAY_ADD_SCALAR(args, radius, PS_TYPE_S32);
+                PS_ARRAY_ADD_SCALAR(args, circularise, PS_TYPE_U8);
+                PS_ARRAY_ADD_SCALAR(args, normalisePeak, PS_TYPE_U8);
+                PS_ARRAY_ADD_SCALAR(args, i, PS_TYPE_S32);
+                PS_ARRAY_ADD_SCALAR(args, j, PS_TYPE_S32);
+
+                if (!psThreadJobAddPending(job)) {
+                    psFree(job);
+                    psFree(groups);
+                    return false;
+                }
+                psFree(job);
+            }
+            if (!psThreadPoolWait(true)) {
+                psError(PS_ERR_UNKNOWN, false, "Error waiting for threads.");
+                psFree(groups);
                 return false;
             }
-
-            flux /= normModel->modelFlux(normModel->params);
-            psFree(normModel);
-        }
-
-        pmModel *fakeModel = pmModelFromPSFforXY(psf, xSrc, ySrc, flux);
-        if (!fakeModel || (fakeModel->flags & MODEL_MASK)) {
-            psFree(fakeModel);
-            continue;
-        }
-	// check that all params are valid:
-	bool validParams = true;
-	for (int n = 0; validParams && (n < fakeModel->params->n); n++) {
-	    if (n == PM_PAR_SKY) continue;
-	    if (n == PM_PAR_I0) continue;
-	    if (n == PM_PAR_XPOS) continue;
-	    if (n == PM_PAR_YPOS) continue;
-	    if (!isfinite(fakeModel->params->data.F32[n])) validParams = false;
-	}
-	if (!validParams) {
-	    psFree(fakeModel);
-	    continue;
-	}		
-        if (circularise && !circulariseModel(fakeModel)) {
-            psError(PS_ERR_UNKNOWN, false, "Unable to circularise PSF model.");
-            psFree(fakeModel);
-            return false;
-        }
-
-        psTrace("psModules.camera", 10, "Adding source at %f,%f with flux %f\n",
-                fakeModel->params->data.F32[PM_PAR_XPOS], fakeModel->params->data.F32[PM_PAR_YPOS],
-                fakeModel->params->data.F32[PM_PAR_I0]);
-
-        pmSource *fakeSource = pmSourceAlloc(); // Fake source to generate
-        fakeSource->peak = pmPeakAlloc(xSrc, ySrc, fakeModel->params->data.F32[PM_PAR_I0], PM_PEAK_LONE);
-        float fakeRadius = 1.0;         // Radius of fake source
-        if (isfinite(minFlux)) {
-            fakeRadius = PS_MAX(fakeRadius, fakeModel->modelRadius(fakeModel->params, minFlux));
-        }
-        if (radius > 0) {
-            fakeRadius = PS_MAX(fakeRadius, radius);
-        }
-
-        if (xOffset) {
-            if (!pmSourceDefinePixels(fakeSource, readout, xSrc + xOffset->data.S32[i],
-                                      ySrc + yOffset->data.S32[i], fakeRadius)) {
-                psErrorClear();
-                continue;
-            }
-            if (!pmModelAddWithOffset(fakeSource->pixels, NULL, fakeModel, PM_MODEL_OP_FULL, 0,
-                                      xOffset->data.S32[i], yOffset->data.S32[i])) {
-                psErrorClear();
-                continue;
-            }
-        } else {
-            if (!pmSourceDefinePixels(fakeSource, readout, xSrc, ySrc, fakeRadius)) {
-                psErrorClear();
-                continue;
-            }
-            if (!pmModelAdd(fakeSource->pixels, NULL, fakeModel, PM_MODEL_OP_FULL, 0)) {
-                psErrorClear();
-                continue;
-            }
-        }
-        psFree(fakeSource);
-        psFree(fakeModel);
+        }
+    } else if (!readoutFake(readout, groups, x, y, mag, xOffset, yOffset, psf, minFlux, radius, circularise,
+                            normalisePeak, 0, 0)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to generate fake sources on readout");
+        psFree(groups);
+        return false;
+    }
+
+    psFree(groups);
+
+// Set a concept value
+#define CONCEPT_SET_S32(CONCEPTS, NAME, OLD, NEW) { \
+        psMetadataItem *item = psMetadataLookup(CONCEPTS, NAME); \
+        psAssert(item->type == PS_TYPE_S32, "Incorrect type: %x", item->type); \
+        if (item->data.S32 == OLD) { \
+            item->data.S32 = NEW; \
+        } \
+    }
+
+    if (readout->parent) {
+        CONCEPT_SET_S32(readout->parent->concepts, "CELL.XPARITY", 0, 1);
+        CONCEPT_SET_S32(readout->parent->concepts, "CELL.YPARITY", 0, 1);
+        CONCEPT_SET_S32(readout->parent->concepts, "CELL.XBIN", 0, 1);
+        CONCEPT_SET_S32(readout->parent->concepts, "CELL.YBIN", 0, 1);
     }
 
     return true;
+
 }
 
Index: /branches/eam_branches/20091201/psModules/src/camera/pmReadoutFake.h
===================================================================
--- /branches/eam_branches/20091201/psModules/src/camera/pmReadoutFake.h	(revision 26878)
+++ /branches/eam_branches/20091201/psModules/src/camera/pmReadoutFake.h	(revision 26879)
@@ -12,4 +12,11 @@
 #include <pmPSF.h>
 #include <pmSourceMasks.h>
+
+/// Set threading
+///
+/// Returns old threading state
+bool pmReadoutFakeThreads(
+    bool new                            // New threading state
+    );
 
 /// Generate a fake readout from vectors
Index: /branches/eam_branches/20091201/psModules/src/config/pmConfig.c
===================================================================
--- /branches/eam_branches/20091201/psModules/src/config/pmConfig.c	(revision 26878)
+++ /branches/eam_branches/20091201/psModules/src/config/pmConfig.c	(revision 26879)
@@ -47,4 +47,5 @@
 
 static bool checkPath(const char *filename, bool create, bool trunc);
+static psString resolveConfigFile(const char *name);
 
 bool pmConfigReadParamsSet(bool newReadCameraConfig)
@@ -447,5 +448,5 @@
             psWarning("-ipprc command-line switch provided without the required filename --- ignored.\n");
         } else {
-            configFile = psStringCopy(argv[argNum]);
+            configFile = resolveConfigFile(argv[argNum]);
             psArgumentRemove(argNum, argc, argv);
         }
@@ -1834,2 +1835,41 @@
     return false;
 }
+
+static psString resolveConfigFile(const char *nameArg)
+{
+    // if config file name is nebulous path resolve it
+    // otherwise just return a copy of the argument
+    if (strncasecmp(nameArg, "neb://", strlen("neb://"))) {
+        return psStringCopy(nameArg);
+    }
+
+#ifdef HAVE_NEBCLIENT
+    char *neb_server = getenv("NEB_SERVER");
+
+    // if env isn't set, check the config system
+    if (!neb_server) {
+        psError(PM_ERR_CONFIG, true, "NEB_SERVER environment variable must be set in order to resolve config file.");
+            return NULL;
+    }
+
+    nebServer *server = nebServerAlloc(neb_server);
+    if (!server) {
+        psError(PM_ERR_SYS, true, "failed to create a nebServer object.");
+        return NULL;
+    }
+
+    char *nebfile = nebFind(server, nameArg);
+    nebServerFree(server);
+    if (!nebfile) {
+        // object does not exist
+        psError(PM_ERR_SYS, true, "failed to resolve nebulous path: %s.", nameArg);
+        return NULL;
+    }
+    // XXX: do I need to free nebfile?
+
+    return psStringCopy(nebfile);
+#else
+    psError(PM_ERR_PROG, true, "psModules was compiled without nebulous support.");
+    return NULL;
+#endif // ifdef HAVE_NEBCLIENT
+}
Index: /branches/eam_branches/20091201/psModules/src/config/pmConfigCamera.c
===================================================================
--- /branches/eam_branches/20091201/psModules/src/config/pmConfigCamera.c	(revision 26878)
+++ /branches/eam_branches/20091201/psModules/src/config/pmConfigCamera.c	(revision 26879)
@@ -149,5 +149,5 @@
     camerasIter = psMetadataIteratorAlloc(new, PS_LIST_HEAD, NULL); // Iterator
     while ((camerasItem = psMetadataGetAndIncrement(camerasIter))) {
-        psMetadataAddItem(cameras, camerasItem, PS_LIST_HEAD, 0);
+        psMetadataAddItem(cameras, camerasItem, PS_LIST_HEAD, PS_META_REPLACE);
     }
     psFree(camerasIter);
@@ -210,7 +210,12 @@
 
     // See if the new one is already there
-    psString newName = NULL;       // Name of skycelled camera
-    psStringAppend(&newName, "_%s-SKYCELL", name);
-    if (psMetadataLookup(oldCameras, newName)) {
+    psString newName = pmConfigCameraSkycellName(name); // Name of skycelled camera
+    bool mdok;                       // Status of MD lookup
+    psMetadata *oldCam = psMetadataLookupMetadata(&mdok, oldCameras, newName); // Existing camera configuration
+    if (mdok && oldCam) {
+        // Ensure new camera goes to the head of the metadata, so that it will be recognised first
+        // The old camera doesn't contain the PSMOSAIC header, so it will match anything!
+        psTrace("psModules.config", 6, "Camera configuration for %s exists, so moving to the front.", newName);
+        psMetadataAddMetadata(newCameras, PS_LIST_HEAD, newName, PS_META_REPLACE, NULL, oldCam);
         psFree(newName);
         return true;
@@ -366,4 +371,5 @@
     // 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!
+    psTrace("psModules.config", 6, "Generated new camera configuration for %s.", newName);
     psMetadataAddMetadata(newCameras, PS_LIST_HEAD, newName, PS_META_REPLACE,
                           "Automatically generated", new);
@@ -436,5 +442,5 @@
     camerasIter = psMetadataIteratorAlloc(new, PS_LIST_HEAD, NULL); // Iterator
     while ((camerasItem = psMetadataGetAndIncrement(camerasIter))) {
-        psMetadataAddItem(cameras, camerasItem, PS_LIST_HEAD, 0);
+        psMetadataAddItem(cameras, camerasItem, PS_LIST_HEAD, PS_META_REPLACE);
     }
     psFree(camerasIter);
@@ -469,7 +475,13 @@
 
     // See if the new one is already there
-    psString newName = NULL;       // Name of mosaicked camera
-    psStringAppend(&newName, "_%s-%s", name, mosaicLevel == PM_FPA_LEVEL_CHIP ? "CHIP" : "FPA");
-    if (psMetadataLookup(oldCameras, newName)) {
+    psString newName = mosaicLevel == PM_FPA_LEVEL_CHIP ? pmConfigCameraChipName(name) :
+        pmConfigCameraFPAName(name); // Name of mosaicked camera
+    bool mdok;                       // Status of MD lookup
+    psMetadata *oldCam = psMetadataLookupMetadata(&mdok, oldCameras, newName); // Existing camera configuration
+    if (mdok && oldCam) {
+        // Ensure new camera goes to the head of the metadata, so that it will be recognised first
+        // The old camera doesn't contain the PSMOSAIC header, so it will match anything!
+        psTrace("psModules.config", 6, "Camera configuration for %s exists, so moving to the front.", newName);
+        psMetadataAddMetadata(newCameras, PS_LIST_HEAD, newName, PS_META_REPLACE, NULL, oldCam);
         psFree(newName);
         return true;
@@ -477,5 +489,4 @@
 
     psMetadata *new = psMetadataCopy(NULL, camera); // Copy of the camera description
-    bool mdok;                          // Status of MD lookups
 
     // ** Fix up the contents of the FPA description to match the mosaicked camera **
@@ -849,4 +860,5 @@
     // New camera MUST go to the head of the metadata, so that it will be recognised first
     // The old camera doesn't contain the PSMOSAIC header, so it will match anything!
+    psTrace("psModules.config", 6, "Generated new camera configuration for %s.", newName);
     psMetadataAddMetadata(newCameras, PS_LIST_HEAD, newName, PS_META_REPLACE,
                           "Automatically generated", new);
Index: /branches/eam_branches/20091201/psModules/src/config/pmConfigRun.c
===================================================================
--- /branches/eam_branches/20091201/psModules/src/config/pmConfigRun.c	(revision 26878)
+++ /branches/eam_branches/20091201/psModules/src/config/pmConfigRun.c	(revision 26879)
@@ -160,5 +160,5 @@
     psArray *files = configRunFileGet(config, name, "FILES.INPUT"); // Files from RUN metadata
     if (!files) {
-        configRunFileGet(config, name, "FILES.OUTPUT");
+        files = configRunFileGet(config, name, "FILES.OUTPUT");
     }
 
Index: /branches/eam_branches/20091201/psModules/src/objects/pmSourceGroups.c
===================================================================
--- /branches/eam_branches/20091201/psModules/src/objects/pmSourceGroups.c	(revision 26878)
+++ /branches/eam_branches/20091201/psModules/src/objects/pmSourceGroups.c	(revision 26879)
@@ -186,4 +186,5 @@
             cellSources->data.S32[i] = i;
         }
+	cellSources->n = numSources;
     } else {
         for (int i = 0; i < numSources; i++) {
Index: /branches/eam_branches/20091201/psModules/src/objects/pmSourceIO.c
===================================================================
--- /branches/eam_branches/20091201/psModules/src/objects/pmSourceIO.c	(revision 26878)
+++ /branches/eam_branches/20091201/psModules/src/objects/pmSourceIO.c	(revision 26879)
@@ -960,5 +960,5 @@
         if (hdu->header == NULL) {
             // if the IMAGE header does not exist, we have no data for this view
-            if (!psFitsMoveExtName (file->fits, headname)) {
+            if (!psFitsMoveExtNameClean (file->fits, headname)) {
                 readout->data_exists = false;
                 psFree (headname);
