Index: /trunk/ppstamp/src/.cvsignore
===================================================================
--- /trunk/ppstamp/src/.cvsignore	(revision 15362)
+++ /trunk/ppstamp/src/.cvsignore	(revision 15363)
@@ -6,2 +6,4 @@
 config.h.in
 stamp-h1
+ppstampErrorCodes.c
+ppstampErrorCodes.h
Index: /trunk/ppstamp/src/Makefile.am
===================================================================
--- /trunk/ppstamp/src/Makefile.am	(revision 15362)
+++ /trunk/ppstamp/src/Makefile.am	(revision 15363)
@@ -18,4 +18,5 @@
         ppstampErrorCodes.c \
 	ppstampMakeStamp.c \
+	ppstampMosaic.c \
 	ppstampOptions.c \
 	ppstampParseCamera.c \
Index: /trunk/ppstamp/src/ppstamp.c
===================================================================
--- /trunk/ppstamp/src/ppstamp.c	(revision 15362)
+++ /trunk/ppstamp/src/ppstamp.c	(revision 15363)
@@ -7,15 +7,10 @@
 int main(int argc, char **argv)
 {
-    ppstampOptions *options;
+    ppstampOptions *options = NULL;
+    int exitCode;
 
     psTimerStart(TIMERNAME);
-
     psLibInit(NULL);
-
-    // Parse the configuration and arguments
-    // Open the input image(s)
-    // note usage is called on error or request by ppstampArguments
-    // TODO is the cleanup really necessary since we're exiting anyways?
-    // (It's useful to know about memory leaks)
+    ppstampErrorRegister();
 
     pmConfig *config = ppstampArguments(argc, argv, &options);
@@ -26,5 +21,4 @@
     }
 
-    // define recipe options
     // define the active I/O files
     if (!ppstampParseCamera(config)) {
@@ -35,6 +29,8 @@
 
     // find the pixels that we need to copy, setup the output image
-    if (!ppstampMakeStamp(config, options)) {
-        exit(PS_EXIT_UNKNOWN_ERROR);
+    if (ppstampMakeStamp(config, options)) {
+        exitCode = 0;
+    } else {
+        exitCode = PS_EXIT_DATA_ERROR;
     }
 
@@ -44,4 +40,4 @@
     ppstampCleanup(config, options);
 
-    return PS_EXIT_SUCCESS;
+    return exitCode;
 }
Index: /trunk/ppstamp/src/ppstamp.h
===================================================================
--- /trunk/ppstamp/src/ppstamp.h	(revision 15362)
+++ /trunk/ppstamp/src/ppstamp.h	(revision 15363)
@@ -15,4 +15,5 @@
 #include "psastro.h"
 #include "ppStats.h"
+#include "ppstampErrorCodes.h"
 
 #define TIMERNAME "ppstamp"             // Name of timer
@@ -59,4 +60,5 @@
 
 bool ppstampMakeStamp(pmConfig *config, ppstampOptions *);
+pmFPAfile * ppstampBuildMosaic(pmConfig *config, pmFPAfile *input, pmFPAview *view);
 
 // free memory, check for leaks
@@ -70,6 +72,5 @@
 
 /// Update the metadata with version information for all dependencies
-void ppstampVersionMetadata(psMetadata *metadata ///< Metadata to update with version information
-    );
+void ppstampVersionMetadata(psMetadata *metadata, ppstampOptions *options);
 
 psRegion *ppstampCellRegion(const pmCell *cell);
Index: /trunk/ppstamp/src/ppstampArguments.c
===================================================================
--- /trunk/ppstamp/src/ppstampArguments.c	(revision 15362)
+++ /trunk/ppstamp/src/ppstampArguments.c	(revision 15363)
@@ -6,8 +6,6 @@
 #include "ohana.h"
 
-static bool get2Angles(int argnum, int *pArgc, char **argv, bool bothDegrees, double *p1, double *p2);
-static bool get2Ints(int argnum, int *pArgc, char **argv, int *p1, int *p2);
-
-// XXX This usage output is kind of busy
+static bool get2Angles(int argnum, int *pArgc, char **argv, bool bothDegrees, bool makePositive, double *p1, double *p2);
+static bool get2Ints(int argnum, int *pArgc, char **argv, bool makePositive, int *p1, int *p2);
 
 static void usage (void)
@@ -15,14 +13,12 @@
     fprintf(stderr, "\n");
 
-    fprintf(stderr, "USAGE: ppstamp -celcenter RA DEC -arcrange dRA dDEC [-file INPUT.fits] [-list INPUT.txt] OUTPUT\n");
-    fprintf(stderr, "       ppstamp -celcenter RA DEC -celrange dRA dDEC [-file INPUT.fits] [-list INPUT.txt] OUTPUT\n");
-    fprintf(stderr, "       ppstamp -celcenter RA DEC -pixrange dx dy    [-file INPUT.fits] [-list INPUT.txt] OUTPUT\n");
-    fprintf(stderr, "       ppstamp -pixcenter x y    -pixrange dx dy    -chip chipname [-file INPUT.fits] [-list INPUT.txt] OUTPUT\n");
-    fprintf(stderr, "       ppstamp -pixcenter x y    -arcrange dRA dDEC -chip chipname [-file INPUT.fits] [-list INPUT.txt] OUTPUT\n");
-    fprintf(stderr, "       ppstamp -pixcenter x y    -celrange dRA dDEC -chip chipname [-file INPUT.fits] [-list INPUT.txt] OUTPUT\n");
-    fprintf(stderr, "\n");
-
-    fprintf(stderr, "celcenter & celrange may be specified in HH:MM:SS DD:MM:SS or as 2 values in decimal degrees\n");
-    fprintf(stderr, "arcrange may be specfied as 2 values DD:MM:SS DD:MM:SS or decimal degrees\n");
+    fprintf(stderr, "USAGE: ppstamp -skycenter RA DEC -pixrange dx dy    [-file INPUT.fits] [-list INPUT.txt] OUTPUT\n");
+    fprintf(stderr, "       ppstamp -skycenter RA DEC -arcrange dRA dDEC [-file INPUT.fits] [-list INPUT.txt] OUTPUT\n");
+    fprintf(stderr, "       ppstamp -pixcenter x y    -pixrange dx dy    [-chip chipname] [-file INPUT.fits] [-list INPUT.txt] OUTPUT\n");
+    fprintf(stderr, "       ppstamp -pixcenter x y    -arcrange dRA dDEC [-chip chipname] [-file INPUT.fits] [-list INPUT.txt] OUTPUT\n");
+    fprintf(stderr, "\n");
+
+    fprintf(stderr, "-skycenter is specified as HH:MM:SS DD:MM:SS or decimal degrees\n");
+    fprintf(stderr, "-arcrange values are seconds of arc\n");
     fprintf(stderr, "\n");
     fprintf(stderr, "Optional arguments:\n");
@@ -55,6 +51,6 @@
     pmConfig *config = pmConfigRead(&argc, argv, NULL);
     if (config == NULL) {
-        psErrorStackPrint(stderr, "Can't find site configuration!\n");
-        exit(EXIT_FAILURE);
+        psError(PPSTAMP_ERR_CONFIG, false, "Can't read site configuration!\n");
+        return(NULL);
     }
 
@@ -67,13 +63,16 @@
         psArgumentRemove(argnum, &argc, argv);
 
-        if (!get2Ints(argnum, &argc, argv, &options->centerX, &options->centerY)) {
-            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "invalid pixcenter specification\n");
-            usage();
-        }
-    }
-
-    if ((argnum = psArgumentGet(argc, argv, "-celcenter"))) {
+        if (!get2Ints(argnum, &argc, argv, false, &options->centerX, &options->centerY)) {
+            psError(PPSTAMP_ERR_ARGUMENTS, true, "invalid pixcenter specification: %s %s\n",
+                argv[argnum], argv[argnum+1]);
+            usage();
+        }
+        psArgumentRemove(argnum, &argc, argv);
+        psArgumentRemove(argnum, &argc, argv);
+    }
+
+    if ((argnum = psArgumentGet(argc, argv, "-skycenter"))) {
         if (gotCenter) {
-            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "can't specify both -pixcenter and -celcenter\n");;
+            psError(PPSTAMP_ERR_ARGUMENTS, true, "can't specify both -pixcenter and -skycenter\n");;
             usage();
         }
@@ -81,7 +80,11 @@
 
         double raDeg, decDeg;
-        if (!get2Angles(argnum, &argc, argv, false, &raDeg, &decDeg)) {
-            usage();
-        }
+        if (!get2Angles(argnum, &argc, argv, false, false, &raDeg, &decDeg)) {
+            psError(PPSTAMP_ERR_ARGUMENTS, true, "invalid skycenter specification: %s %s\n",
+                argv[argnum], argv[argnum+1]);
+            usage();
+        }
+        psArgumentRemove(argnum, &argc, argv);
+        psArgumentRemove(argnum, &argc, argv);
         options->centerRA  = DEG_TO_RAD(raDeg);
         options->centerDEC = DEG_TO_RAD(decDeg);
@@ -91,5 +94,5 @@
 
     if (!gotCenter) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "must specify center of region of interest\n");
+        psError(PPSTAMP_ERR_ARGUMENTS, true, "must specify center of region of interest\n");
         usage();
     }
@@ -99,12 +102,16 @@
         options->celestialRange = false;
         psArgumentRemove(argnum, &argc, argv);
-        if (!get2Ints(argnum, &argc, argv, &options->dX, &options->dY)) {
-            usage();
-        }
+        if (!get2Ints(argnum, &argc, argv, true, &options->dX, &options->dY)) {
+            psError(PPSTAMP_ERR_ARGUMENTS, true, "invalid pixrange specification: %s %s\n",
+                argv[argnum], argv[argnum+1]);
+            usage();
+        }
+        psArgumentRemove(argnum, &argc, argv);
+        psArgumentRemove(argnum, &argc, argv);
     }
 
     if ((argnum = psArgumentGet(argc, argv, "-arcrange"))) {
         if (gotRange) {
-            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "specify only one of -pixrange or -arcrange or -celrange\n");;
+            psError(PPSTAMP_ERR_ARGUMENTS, true, "specify only one of -pixrange or -arcrange\n");;
             usage();
         }
@@ -113,30 +120,35 @@
         options->celestialRange = true;
 
+        // arcrange values are seconds of arc
+        options->dRA  = DEG_TO_RAD(atof(argv[argnum]) * 3600.);
+        options->dDEC = DEG_TO_RAD(atof(argv[argnum+1]) * 3600.);
+
+        psArgumentRemove(argnum, &argc, argv);
+        psArgumentRemove(argnum, &argc, argv);
+    }
+    // I'm leaving in the -celrange option (HH:MM:SS DD:MM:SS), but not publicizing it
+    if ((argnum = psArgumentGet(argc, argv, "-celrange"))) {
+        if (gotRange) {
+            psError(PPSTAMP_ERR_ARGUMENTS, true, "specify one of -pixrange, -arcrange, or -celrange\n");;
+            usage();
+        }
+        psArgumentRemove(argnum, &argc, argv);
+        gotRange = true;
+        options->celestialRange = true;
+
         double deg1, deg2;
-        if (!get2Angles(argnum, &argc, argv, true, &deg1, &deg2)) {
-            usage();
-        }
+        if (!get2Angles(argnum, &argc, argv, false, true, &deg1, &deg2)) {
+            psError(PPSTAMP_ERR_ARGUMENTS, true, "invalid celrange specification: %s %s\n",
+                argv[argnum], argv[argnum+1]);
+            usage();
+        }
+        psArgumentRemove(argnum, &argc, argv);
+        psArgumentRemove(argnum, &argc, argv);
         options->dRA = DEG_TO_RAD(deg1);
         options->dDEC = DEG_TO_RAD(deg2);
     }
-    if ((argnum = psArgumentGet(argc, argv, "-celrange"))) {
-        if (gotRange) {
-            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "specify one of -pixrange or -arcrange or -celrange\n");;
-            usage();
-        }
-        psArgumentRemove(argnum, &argc, argv);
-        gotRange = true;
-        options->celestialRange = true;
-
-        double deg1, deg2;
-        if (!get2Angles(argnum, &argc, argv, false, &deg1, &deg2)) {
-            usage();
-        }
-        options->dRA = DEG_TO_RAD(deg1);
-        options->dDEC = DEG_TO_RAD(deg2);
-    }
 
     if (!gotRange) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "must specify range\n");
+        psError(PPSTAMP_ERR_ARGUMENTS, true, "must specify range\n");
         usage();
     }
@@ -151,16 +163,18 @@
     bool status = pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT", "-file", "-list");
     if (!status) {
-        // TODO: shall we print a specific message?
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "must specify INPUT\n");
-        usage ();
+        psError(PPSTAMP_ERR_ARGUMENTS, true, "must specify INPUT\n");
+        usage();
     }
 
     // finally the output file
-    if (argc != 2) {
-        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "must specify OUTPUT\n");
-        usage ();
-    }
-
-    // Add the input and output images (which remain on the command-line) to the arguments list
+    if (argc < 2) {
+        psError(PPSTAMP_ERR_ARGUMENTS, true, "must specify OUTPUT\n");
+        usage();
+    } else if (argc > 2) {
+        psError(PPSTAMP_ERR_ARGUMENTS, true, "unknown argument(s)\n");
+        usage();
+    }
+
+    // Add the output image (which remains on the command-line) to the arguments list
     psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Name of the output image", argv[1]);
 
@@ -173,4 +187,5 @@
 {
     char *p = string;
+
     if ((*p == '+') || (*p == '-')) {
         p++;
@@ -179,5 +194,5 @@
 }
 
-static bool get2Ints(int argnum, int *pArgc, char **argv, int *p1, int *p2)
+static bool get2Ints(int argnum, int *pArgc, char **argv, bool makePositive, int *p1, int *p2)
 {
     if (*pArgc < 2) {
@@ -188,19 +203,21 @@
         return false;
     }
-
     *p1 = atoi(argv[argnum]);
-    psArgumentRemove(argnum, pArgc, argv);
-
-    if (!validNumber(argv[argnum])) {
+
+    if (!validNumber(argv[argnum+1])) {
         return false;
     }
-
-    *p2 = atoi(argv[argnum]);
-    psArgumentRemove(argnum, pArgc, argv);
+    *p2 = atoi(argv[argnum+1]);
+
+    if (makePositive) {
+        *p1 = abs(*p1);
+        *p2 = abs(*p2);
+    }
 
     return true;
 }
 
-static bool get2Angles(int argnum, int *pArgc, char **argv, bool bothInDegrees, double *p1, double *p2)
+static bool get2Angles(int argnum, int *pArgc, char **argv, bool bothInDegrees, bool makePositive,
+    double *p1, double *p2)
 {
     bool rval;
@@ -221,6 +238,8 @@
     }
 
-    psArgumentRemove(argnum, pArgc, argv);
-    psArgumentRemove(argnum, pArgc, argv);
+    if (rval && makePositive) {
+        *p1 = abs(*p1);
+        *p2 = abs(*p2);
+    }
 
     return rval;
Index: /trunk/ppstamp/src/ppstampCleanup.c
===================================================================
--- /trunk/ppstamp/src/ppstampCleanup.c	(revision 15362)
+++ /trunk/ppstamp/src/ppstampCleanup.c	(revision 15363)
@@ -20,5 +20,5 @@
 
     // psMemCheckLeaks (0, NULL, stderr, false);
-    fprintf (stderr, "Found %d leaks in %s\n", psMemCheckLeaks (0, NULL, NULL, false), "ppstamp");
+    // fprintf (stderr, "Found %d leaks in %s\n", psMemCheckLeaks (0, NULL, NULL, false), "ppstamp");
 
     return;
Index: /trunk/ppstamp/src/ppstampErrorCodes.h.in
===================================================================
--- /trunk/ppstamp/src/ppstampErrorCodes.h.in	(revision 15362)
+++ /trunk/ppstamp/src/ppstampErrorCodes.h.in	(revision 15363)
@@ -9,10 +9,10 @@
  */
 typedef enum {
-    PPSTAMP_ERR_BASE = 600,
+    PPSTAMP_ERR_BASE = 650,
     PPSTAMP_ERR_${ErrorCode},
     PPSTAMP_ERR_NERROR
-} pswarpErrorCode;
+} ppstampErrorCode;
 
-void pswarpErrorRegister(void);
+void ppstampErrorRegister(void);
 
 #endif
Index: /trunk/ppstamp/src/ppstampMakeStamp.c
===================================================================
--- /trunk/ppstamp/src/ppstampMakeStamp.c	(revision 15362)
+++ /trunk/ppstamp/src/ppstampMakeStamp.c	(revision 15363)
@@ -16,4 +16,6 @@
 } ppstampOverlap;
 
+static void skyToChip(pmAstromObj *pt, pmFPA *fpa, pmChip *chip);
+
 // convert the input chip's transforms to the output
 static bool convertWCS(pmHDU *outHDU, pmFPA *outFPA, pmChip *outChip, pmChip *inChip, psRegion *roi)
@@ -24,8 +26,9 @@
 
     outChip->toFPA   = psPlaneTransformSetCenter(NULL, inChip->toFPA, roi->x0, roi->y0);
+
     outChip->fromFPA = psPlaneTransformInvert(NULL, outChip->toFPA, *roi, 50);
 
     if (!pmAstromWriteWCS(outHDU->header, outFPA, outChip, WCS_NONLIN_TOL)) {
-        psError(PS_ERR_UNKNOWN, false, "Failed to write WCS\n");
+        psError(PS_ERR_UNKNOWN, false, "Failed to write WCS to output\n");
         return false;
     }
@@ -34,5 +37,5 @@
 }
 
-static bool copyMetadata(pmFPAfile *output, pmFPAfile *input, pmChip *inChip, pmCell *inCell, psRegion *roi)
+static bool copyMetadata(pmFPAfile *output, pmFPAfile *input, pmChip *inChip, ppstampOptions *options)
 {
     pmChip    *outChip;
@@ -44,8 +47,11 @@
     psFree(view);
 
-    // XXX we probably should copy some concepts from the chip (skipping those that don't apply)
+    // copy data from the input chip header to the output.
+    // since some of the keywords might be duplicated we may not want to copy both
+
+    // copy the fpa concepts
     outChip->parent->concepts = psMetadataCopy(outChip->parent->concepts, inChip->parent->concepts);
 
-    pmHDU *inHDU  = pmHDUGetHighest(input->fpa, inChip, NULL);
+    pmHDU *inHDU  = pmHDUFromChip(inChip);
     pmHDU *outHDU = pmHDUGetHighest(output->fpa, outChip, NULL);
 
@@ -56,25 +62,75 @@
     }
 
-    // steal the input fpa's transforms
-    output->fpa->toTPA = input->fpa->toTPA;
-    output->fpa->fromTPA = input->fpa->fromTPA;
-    output->fpa->toSky = input->fpa->toSky;
-
-    // drop the references
-    input->fpa->toTPA = 0;
-    input->fpa->fromTPA = 0;
-    input->fpa->toSky = 0;
-
-    if (!convertWCS(outHDU, output->fpa, outChip, inChip, roi)) {
-        return false;
-    }
+    // If input had WCS convert it for stamp
+    if (input->fpa->toSky) {
+        // steal the input fpa's transforms
+        output->fpa->toTPA   = input->fpa->toTPA;
+        output->fpa->fromTPA = input->fpa->fromTPA;
+        output->fpa->toSky   = input->fpa->toSky;
+
+        // drop the references
+        input->fpa->toTPA   = 0;
+        input->fpa->fromTPA = 0;
+        input->fpa->toSky   = 0;
+
+        if (!convertWCS(outHDU, output->fpa, outChip, inChip, &options->roi)) {
+            return false;
+        }
+    } else {
+        psWarning("No WCS present in input\n");
+    }
+
+    ppstampVersionMetadata(outHDU->header, options);
 
     return true;
 }
 
+#ifdef notdef
+// update the ROI to account for any change in the coordinate system in the
+// mosaic process
+// THERE isn't any change. This is something I was trying to make the megacam
+// transforms come out correctly.
+static bool updateROI(ppstampOptions *options, pmFPAfile *mosaic, pmChip *mosaicChip)
+{
+    // set up the astrometry
+    pmHDU *hdu = pmHDUFromChip(mosaicChip);
+    PS_ASSERT_PTR_NON_NULL(hdu, 1)
+    PS_ASSERT_PTR_NON_NULL(hdu->header, 1)
+
+
+    if (!pmAstromReadWCS(mosaic->fpa, mosaicChip, hdu->header, 1.0)) {
+        psError(PS_ERR_UNKNOWN, false, "Failed to Read WCS from mosaic, cannot proceed\n");
+        return false;
+    }
+    pmAstromObj *center = pmAstromObjAlloc();
+
+    center->sky->r = options->centerRA;
+    center->sky->d = options->centerDEC;
+    center->sky->rErr = 0;
+    center->sky->dErr = 0;
+
+    skyToChip(center, mosaic->fpa, mosaicChip);
+
+    int width = options->dX;
+    int height = options->dY;
+
+    fprintf(stderr, "ROI before: %s\n", psRegionToString(options->roi));
+
+    options->roi.x0 = center->chip->x - width/2;
+    options->roi.x1 = options->roi.x0 + width;
+    options->roi.y0 = center->chip->y - height/2;
+    options->roi.y1 = options->roi.y0 + height;
+
+    fprintf(stderr, "ROI after:  %s\n", psRegionToString(options->roi));
+
+
+    return true;
+}
+#endif
+
 // Build the postage stamp output file
 
 static bool makeStamp(pmConfig *config, ppstampOptions *options, pmFPAfile *input, 
-                pmChip *inChip, pmCell *inCell, pmFPAview *view)
+                pmChip *inChip, pmFPAview *view)
 {
     int status = false;
@@ -91,18 +147,31 @@
     outview->chip = 0;
     outview->cell = 0;
-    outview->readout = 0;
     pmCell *target =  pmFPAviewThisCell(outview, output->fpa); // Target cell
     psFree(outview);
     outview = NULL;
 
-    //    psMetadataPrint(stderr, inChip->concepts, 0);
-    //   psMetadataPrint(stderr, inCell->concepts, 0);
-    // these default to zero would that be ok?
+    //   psMetadataPrint(stderr, inChip->concepts, 0);
+    
+    // These default to zero. would that be ok?
     psMetadataAddS32(target->concepts, PS_LIST_TAIL, "CELL.XBIN", PS_META_REPLACE, "Binning in x", 1);
     psMetadataAddS32(target->concepts, PS_LIST_TAIL, "CELL.YBIN", PS_META_REPLACE, "Binning in y", 1);
 
+
+    // we extract our postage stamp from a mosaic so that
+    // pmChipMosaic can handle the tricky bits of parity, binning, etc.
+    pmFPAfile *mosaic = ppstampBuildMosaic(config, input, view);
+    if (mosaic == NULL) {
+        return false;
+    }
+
+    // one cell one chip
+    pmFPAview *mosaicView = pmFPAviewAlloc(0);
+    mosaicView->chip = 0;
+    mosaicView->cell = 0;
+
     pmReadout *readout;
-    while ((readout = pmFPAviewNextReadout (view, input->fpa, 1)) != NULL) {
+    while ((readout = pmFPAviewNextReadout (mosaicView, mosaic->fpa, 1)) != NULL) {
         if (!readout->data_exists) {
+            psError(PS_ERR_UNKNOWN, false, "no data in mosaic readout!\n");
             continue;
         }
@@ -113,5 +182,5 @@
             break;
         }
-
+    
         psImage *subsetImage = psImageSubset(readout->image, options->roi);
         if (subsetImage) {
@@ -120,14 +189,16 @@
             psFree(subsetImage);
             if (outReadout->image) {
-                // The image has inherited the source's col0 and row0. We don't want this in
-                // our output so override it.
+                // The image has inherited the subset's col0 and row0. We don't want this in
+                // our output so override the values.
                 // XXX put this into a function in psImage
-                // (What I really needed was a function to "create a copy of this portion of an image")
+                // (What I really needed was a function that does
+                //     "create a copy of this portion of an image and return it as a new image with
+                //      origin 0,0 ")
                 P_PSIMAGE_SET_COL0(outReadout->image, 0);
                 P_PSIMAGE_SET_ROW0(outReadout->image, 0);
 
                 outReadout->data_exists = true;
-                outReadout->parent->data_exists = true;         // cell
-                outReadout->parent->parent->data_exists = true; // chip
+                outReadout->parent->data_exists = true;
+                outReadout->parent->parent->data_exists = true;
                 status = true;
             } else {
@@ -142,6 +213,16 @@
     }
 
+    // XXXX remove this or make writing the mosaic to a file a valid option
+    static bool writeMosaic = false;
+    if (writeMosaic) {
+        mosaic->save = true;
+        mosaicView->cell = -1;
+        pmFPAfileIOChecks(config, mosaicView, PM_FPA_AFTER);
+    }
+
+    psFree(mosaicView);
+
     if (status) {
-        status = copyMetadata(output, input, inChip, inCell, &options->roi);
+        status = copyMetadata(output, input, inChip, options);
     }
     return status;
@@ -181,5 +262,5 @@
 static void skyToChip(pmAstromObj *pt, pmFPA *fpa, pmChip *chip)
 {
-    // convert from sky to FP
+    // convert from sky to TP to FP
     psProject(pt->TP, pt->sky, fpa->toSky);
     psPlaneTransformApply(pt->FP, fpa->fromTPA, pt->TP);
@@ -190,5 +271,7 @@
 static void chipToSky(pmAstromObj *pt, pmFPA *fpa, pmChip *chip)
 {
+    // chip to FP
     psPlaneTransformApply(pt->FP, chip->toFPA, pt->chip);
+    // FP to TP to sky
     psPlaneTransformApply(pt->TP, fpa->toTPA, pt->FP);
     psDeproject(pt->sky, pt->TP, fpa->toSky);
@@ -208,4 +291,6 @@
 }
 
+// For roi width and height given in sky coordinates find a bounding box in chip coordinates
+// that encloses the requested range
 static void findBoundingBox(ppstampOptions *options, pmFPA *fpa, pmChip *chip, pmAstromObj *center)
 {
@@ -249,7 +334,15 @@
 
     psFree(pt);
-}
-
-
+
+    // save the width and height in case we need them later
+    options->dX = options->roi.x1 - options->roi.x0;
+    options->dY = options->roi.y1 - options->roi.y0;
+}
+
+#ifdef notdef
+// I'm not using this any more. We mosaic the chip before extracting the pixels
+// so we don't need to deal with cells at this level.
+
+// findCell
 // find cell on given chip that contains the region of interest
 // return the status of the overlap and if the cell completely contains the ROI 
@@ -263,5 +356,5 @@
     view->cell = -1;
     while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) {
-        psRegion *cellExtent = ppstampCellRegion(cell);
+        psRegion *cellExtent = pmCellExtent(cell);
         if (regionContainsPoint(cellExtent, center->chip)) {
             if (regionContainsRegion(cellExtent, &options->roi)) {
@@ -288,16 +381,21 @@
     return PPSTAMP_OFF;
 }
+#endif
+
+
 
 // findROI
 // calculate the region of interest in chip coordinates and determine whether that region
-// whether the region of interest is completely contained in a cell on a given chip.
+// is completely contained on a single chip
 
 static ppstampOverlap findROI(ppstampOptions *options, pmFPAview *view, pmFPAfile *input, pmChip *chip,
-                       pmAstromObj *center, pmCell **ppCell)
+                       pmAstromObj *center)
 {
     psString chipName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME");
-    psRegion    *chipExtent = ppstampChipRegion(chip);
+    psRegion    *chipBounds = ppstampChipRegion(chip);
     bool        onChip = false;
     ppstampOverlap   returnval = PPSTAMP_OFF;
+
+//    fprintf(stderr, "ppstampChipBounds: %s\n", psRegionToString(*chipBounds));
 
     // set up the astrometry
@@ -307,7 +405,11 @@
 
     if (!pmAstromReadWCS(input->fpa, chip, hdu->header, 1.0)) {
-        psError(PS_ERR_UNKNOWN, false, "Failed to Read WCS\n");
-        psFree(chipExtent);
-        return PPSTAMP_ERROR;
+        // we can live without WCS if the ROI is specified in pixels
+
+        if (options->celestialCenter || options->celestialRange) {
+            psError(PPSTAMP_ERR_DATA, false, "Failed to Read WCS, cannot proceed\n");
+            psFree(chipBounds);
+            return PPSTAMP_ERROR;
+        }
     }
 
@@ -320,8 +422,7 @@
 
         skyToChip(center, input->fpa, chip);
-        psPlaneTransformApply(center->chip, chip->fromFPA, center->FP);
-
-        if (regionContainsPoint(chipExtent, center->chip)) {
-            psLogMsg("ppstampMakeStamp", 3, "Found center (%f %f) on chip: %s\n", 
+
+        if (regionContainsPoint(chipBounds, center->chip)) {
+            psLogMsg("ppstampMakeStamp", 2, "Found center (%f %f) on chip: %s\n", 
                 center->chip->x, center->chip->y, chipName);
             onChip = true;
@@ -332,5 +433,5 @@
         // If no chip name was specified, select this one (the first one that had data)
         if ((options->chipName == NULL) || !strcmp(chipName, options->chipName)) {
-            psLogMsg("ppstampMakeStamp", 3, "Center on chip: %s\n", chipName);
+            psLogMsg("ppstampMakeStamp", 2, "Center on chip: %s\n", chipName);
             center->chip->x = options->centerX;
             center->chip->y = options->centerY;
@@ -355,15 +456,19 @@
         }
 
-        if (regionContainsRegion(chipExtent, &options->roi)) {
-            returnval = findCell(view, options, input, center, ppCell);
+
+        if (regionContainsRegion(chipBounds, &options->roi)) {
+            // returnval = findCell(view, options, input, center, ppCell);
+            psLogMsg("ppstampMakeStamp", 2, "ROI contained on: %s\n", chipName);
+            returnval = PPSTAMP_ON;
         } else {
             fprintf(stderr, "Partial Overlap chip %s\n", chipName);
             fprintf(stderr, "ROI:         %s\n", psRegionToString(options->roi));
-            fprintf(stderr, "Chip Extent: %s\n", psRegionToString(*chipExtent));
+            fprintf(stderr, "Chip Extent: %s\n", psRegionToString(*chipBounds));
 
             returnval = PPSTAMP_PARTIALLY_ON;
-        }
-    }
-    psFree(chipExtent);
+
+        }
+    }
+    psFree(chipBounds);
 
     return returnval;
@@ -374,4 +479,5 @@
     bool        status = false;
     bool        returnval = false;;
+    bool        foundOverlap = false;
     pmAstromObj *center = pmAstromObjAlloc();
 
@@ -408,20 +514,29 @@
         }
 
-        pmCell *cell;
-        ppstampOverlap overlap = findROI(options, view, input, chip, center, &cell);
-
-        if (overlap == PPSTAMP_ON) {
-
-            returnval = makeStamp(config, options, input, chip, cell, view);
-
+        ppstampOverlap overlap = findROI(options, view, input, chip, center);
+
+        switch (overlap) {
+        case PPSTAMP_OFF:
+            // keep looking
+            break;
+        case PPSTAMP_ON:
+            returnval = makeStamp(config, options, input, chip, view);
             allDone = true;
-	} else if (overlap == PPSTAMP_PARTIALLY_ON) {
-            psError(PS_ERR_UNKNOWN, false, "Region of interest must be confined to a single cell\n");
+            foundOverlap = true;
+            break;
+	case PPSTAMP_PARTIALLY_ON:
+            psError(PS_ERR_UNKNOWN, false, "Region of interest must be confined to a single chip\n");
             // XXX: perhaps print a helpful message about what coordinates would be valid
             returnval = false;
             allDone = true;
-        } else if (overlap == PPSTAMP_ERROR) {
+            foundOverlap = true;
+            break;
+        case PPSTAMP_ERROR:
             returnval = false;
             allDone = true;
+            break;
+        default:
+            psError(PS_ERR_PROGRAMMING, false, "findROI returned unexpected value %d\n", overlap);
+            break;
         }
 
@@ -430,18 +545,5 @@
         if (allDone) {
             view->chip = -1;
-            view->cell = -1;
-            break;
-        } else {
-            // Remove the transformations from the fpa so that they will be 
-            // recalculated from scratch for the next chip.
-            // If I don't do this the calculation of the astrometry parameters for the output file
-            // don't come out correctly. See the code on the false side of the test
-            // if (fpa->toSky == NULL) in the function pmAstromWCStoFPA
-            psFree(input->fpa->fromTPA);
-            psFree(input->fpa->toTPA);
-            psFree(input->fpa->toSky);
-            input->fpa->fromTPA = NULL;
-            input->fpa->toTPA = NULL;
-            input->fpa->toSky = NULL;
+            break;
         }
     } 
@@ -451,4 +553,8 @@
     psFree(view);
 
+    if (!foundOverlap) {
+        fprintf(stderr, "ROI not found in input\n");
+    }
+
     return returnval;
 }
Index: /trunk/ppstamp/src/ppstampMosaic.c
===================================================================
--- /trunk/ppstamp/src/ppstampMosaic.c	(revision 15363)
+++ /trunk/ppstamp/src/ppstampMosaic.c	(revision 15363)
@@ -0,0 +1,41 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ppstamp.h"
+
+// Mosaic the cells of a given chip
+
+
+pmFPAfile *ppstampBuildMosaic(pmConfig *config, pmFPAfile *input, pmFPAview *view)
+{
+    bool    status;
+
+    pmFPAfile *mosaic =  psMetadataLookupPtr(&status, config->files, "PPSTAMP.CHIP");
+    if (!status)  {
+        psErrorStackPrint(stderr, "can't find mosaic i/o file\n");
+        exit(EXIT_FAILURE);
+    }
+
+    pmFPAview *mosaicView = pmFPAviewAlloc(0);
+
+    mosaicView->chip = 0;
+    pmChip  *mChip  = pmFPAviewThisChip(mosaicView, mosaic->fpa);
+    pmChip  *inChip = pmFPAviewThisChip(view, input->fpa);
+    if (!mChip->hdu && !mChip->parent->hdu) {
+        const char *name = psMetadataLookupStr(&status, input->fpa->concepts, "FPA.NAME"); // Name of FPA
+        pmFPAAddSourceFromView(mosaic->fpa, name, mosaicView, mosaic->format);
+    }
+    psFree(mosaicView);
+
+    psMaskType blankMask = pmConfigMask("BLANK", config);
+
+    status = pmChipMosaic(mChip, inChip, true, blankMask);
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, false, "chip mosaic failed\n");
+        return NULL;
+    }
+
+    return mosaic;
+}
+
Index: /trunk/ppstamp/src/ppstampRegion.c
===================================================================
--- /trunk/ppstamp/src/ppstampRegion.c	(revision 15362)
+++ /trunk/ppstamp/src/ppstampRegion.c	(revision 15363)
@@ -21,6 +21,15 @@
     // pmReadoutExtent ignores the col0, row0 of the readout
 
-    int col0 = image->col0;
-    int row0 = image->row0;
+    int col0 = 0;
+    int row0 = 0;
+   
+#ifdef notdef
+    if (image->parent) {
+        // This is wrong. I'm trying to deal with the strange megacam 'spliced' images
+        // which appear to have inconsistant CELL.X0 and readout.col0
+        col0 = image->col0 - image->parent->col0;
+        row0 = image->row0 - image->parent->row0;
+    }
+#endif
 
     return psRegionAlloc(col0, col0 + image->numCols,
@@ -44,6 +53,41 @@
         psFree(roExtent);
     }
-    // Unlike pmCellExtent we don't add in cell.x0, cell.y0. The appropriate offset
-    // was included in the roExtent by ppstampReadoutRegion
+
+    bool mdok;                          // Status of MD lookup
+    int cellX0 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.X0"); // Cell x offset
+    if (!mdok) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find CELL.X0.\n");
+        psFree(cellExtent);
+        return NULL;
+    }
+
+    int cellY0 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.Y0"); // Cell y offset
+    if (!mdok) {
+        psError(PS_ERR_UNKNOWN, true, "Unable to find CELL.Y0.\n");
+        psFree(cellExtent);
+        return NULL;
+    }
+    int xParity = psMetadataLookupS32(NULL, cell->concepts, "CELL.XPARITY");
+    int yParity = psMetadataLookupS32(NULL, cell->concepts, "CELL.YPARITY");
+
+    if (xParity >= 0) {
+        cellExtent->x0 += cellX0;
+        cellExtent->x1 += cellX0;
+    } else {
+        float x0 = cellX0 - cellExtent->x1;
+        float x1 = cellX0 - cellExtent->x0;
+        cellExtent->x0 = x0;
+        cellExtent->x1 = x1;
+    }
+
+    if (yParity >= 0) {
+        cellExtent->y0 += cellY0;
+        cellExtent->y1 += cellY0;
+    } else {
+        float y0 = cellY0 - cellExtent->y1;
+        float y1 = cellY0 - cellExtent->y0;
+        cellExtent->y0 = y0;
+        cellExtent->y1 = y1;
+    }
 
     return cellExtent;
Index: /trunk/ppstamp/src/ppstampVersion.c
===================================================================
--- /trunk/ppstamp/src/ppstampVersion.c	(revision 15362)
+++ /trunk/ppstamp/src/ppstampVersion.c	(revision 15363)
@@ -24,5 +24,5 @@
 
 
-void ppstampVersionMetadata(psMetadata *metadata)
+void ppstampVersionMetadata(psMetadata *metadata, ppstampOptions *options)
 {
     PS_ASSERT_METADATA_NON_NULL(metadata,);
@@ -30,9 +30,4 @@
     psString pslib = psLibVersionLong();// psLib version
     psString psmodules = psModulesVersionLong(); // psModules version
-#ifdef notdef
-    psString psphot = psphotVersionLong(); // psphot version
-    psString psastro = psastroVersionLong(); // psastro version
-    psString ppStats = ppStatsVersionLong(); // ppStats version
-#endif
     psString ppstamp = ppstampVersionLong(); // ppstamp version
 
@@ -47,19 +42,13 @@
     psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, pslib, "");
     psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, psmodules, "");
-#ifdef notdef
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, psphot, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, psastro, "");
-    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, ppStats, "");
-#endif
     psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, ppstamp, "");
+    psString roi = NULL;
+    psStringAppend(&roi, "ppstamp: region of interest: %s", psRegionToString(options->roi));
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY",  PS_META_DUPLICATE_OK, roi, "");
 
+    psFree(roi);
     psFree(head);
     psFree(pslib);
     psFree(psmodules);
-#ifdef notdef
-    psFree(psphot);
-    psFree(psastro);
-    psFree(ppStats);
-#endif
     psFree(ppstamp);
 
