Index: /trunk/psModules/configure.ac
===================================================================
--- /trunk/psModules/configure.ac	(revision 5169)
+++ /trunk/psModules/configure.ac	(revision 5170)
@@ -37,4 +37,12 @@
 AC_SUBST([AM_CFLAGS])
 
+SRCPATH='${top_srcdir}/src'
+SRCDIRS="astrom camera config detrend imcombine imsubtract objects photom"
+SRCINC=`echo "${SRCDIRS=}" | ${SED} "s|\(\\w\+\)|-I\${SRCPATH=}/\1|g"`
+SRCSUBLIBS=`echo "${SRCDIRS=}" | ${SED} "s|\(\\w\+\)|\1/libpsmodule\1.la|g"`
+AC_SUBST([SRCSUBLIBS],${SRCSUBLIBS=})
+AC_SUBST([SRCDIRS],${SRCDIRS=})
+AC_SUBST([SRCINC],${SRCINC=})
+
 dnl doxygen -------------------------------------------------------------------
 
@@ -55,5 +63,5 @@
 [PSLIB_CONFIG=$withval])
 
-if test -z ${PSLIB_CONFIG} ; then 
+if test -z ${PSLIB_CONFIG} ; then
   PKG_CHECK_MODULES([PSLIB], [pslib >= 0.7.0])
 else
@@ -78,5 +86,21 @@
   Makefile
   src/Makefile
+  src/astrom/Makefile
+  src/camera/Makefile
+  src/config/Makefile
+  src/detrend/Makefile
+  src/imcombine/Makefile
+  src/imsubtract/Makefile
+  src/objects/Makefile
+  src/photom/Makefile
   test/Makefile
+  test/astrom/Makefile
+  test/camera/Makefile
+  test/config/Makefile
+  test/detrend/Makefile
+  test/imcombine/Makefile
+  test/imsubtract/Makefile
+  test/objects/Makefile
+  test/photom/Makefile
   Doxyfile
   psmodule-config
Index: /trunk/psModules/psmodule.kdevses
===================================================================
--- /trunk/psModules/psmodule.kdevses	(revision 5169)
+++ /trunk/psModules/psmodule.kdevses	(revision 5170)
@@ -2,29 +2,38 @@
 <!DOCTYPE KDevPrjSession>
 <KDevPrjSession>
- <DocsAndViews NumberOfDocuments="8" >
-  <Doc0 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/ps/psModule/src/Makefile.am" >
-   <View0 line="24" Type="Source" />
+ <DocsAndViews NumberOfDocuments="11" >
+  <Doc0 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/psModule/src/astrom/Makefile.am" >
+   <View0 line="0" Type="Source" />
   </Doc0>
-  <Doc1 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/ps/psModule/src/psAstrometry.h" >
-   <View0 line="402" Type="Source" />
+  <Doc1 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/psModule/configure.ac" >
+   <View0 line="41" Type="Source" />
   </Doc1>
-  <Doc2 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/ps/psModule/src/pmImageCombine.h" >
-   <View0 line="27" Type="Source" />
+  <Doc2 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/psModule/src/Makefile.am" >
+   <View0 line="3" Type="Source" />
   </Doc2>
-  <Doc3 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/ps/psModule/src/pmImageCombine.c" >
-   <View0 line="36" Type="Source" />
+  <Doc3 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/psModule/test/Makefile.am" >
+   <View0 line="13" Type="Source" />
   </Doc3>
-  <Doc4 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/ps/psModule/src/pmObjects.c" >
-   <View0 line="1464" Type="Source" />
+  <Doc4 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/psModule/test/astrom/Makefile.am" >
+   <View0 line="2" Type="Source" />
   </Doc4>
-  <Doc5 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/ps/psModule/src/pmObjects.h" >
-   <View0 line="261" Type="Source" />
+  <Doc5 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/psModule/test/camera/Makefile.am" >
+   <View0 line="3" Type="Source" />
   </Doc5>
-  <Doc6 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/ps/psModule/src/psAstrometry.c" >
-   <View0 line="26" Type="Source" />
+  <Doc6 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/psModule/test/config/Makefile.am" >
+   <View0 line="6" Type="Source" />
   </Doc6>
-  <Doc7 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/ps/psModule/test/tst_pmImageCombine.c" >
-   <View0 line="0" Type="Source" />
+  <Doc7 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/psModule/test/detrend/Makefile.am" >
+   <View0 line="4" Type="Source" />
   </Doc7>
+  <Doc8 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/psModule/test/objects/Makefile.am" >
+   <View0 line="18" Type="Source" />
+  </Doc8>
+  <Doc9 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/psModule/test/imcombine/Makefile.am" >
+   <View0 line="23" Type="Source" />
+  </Doc9>
+  <Doc10 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/psModule/test/imsubtract/Makefile.am" >
+   <View0 line="12" Type="Source" />
+  </Doc10>
  </DocsAndViews>
  <pluginList>
Index: /trunk/psModules/src/Makefile.am
===================================================================
--- /trunk/psModules/src/Makefile.am	(revision 5169)
+++ /trunk/psModules/src/Makefile.am	(revision 5170)
@@ -1,31 +1,8 @@
+SUBDIRS = $(SRCDIRS)
 lib_LTLIBRARIES = libpsmodule.la
 
-libpsmodule_la_CPPFLAGS = $(PSLIB_CFLAGS)
-libpsmodule_la_LDFLAGS  = -release $(PACKAGE_VERSION)
-libpsmodule_la_SOURCES  = pmFlatField.c \
-  pmImageCombine.c \
-  pmImageSubtract.c \
-  pmMaskBadPixels.c\
-  pmNonLinear.c \
-  pmObjects.c \
-  pmReadoutCombine.c \
-  pmSubtractBias.c \
-  pmSubtractSky.c \
-  pmAstrometry.c
-
-libpsmodule_ladir = $(includedir)
-libpsmodule_la_HEADERS = \
-  pmFlatFieldErrors.h \
-  pmMaskBadPixelsErrors.h \
-  pmFlatField.h \
-  pmImageCombine.h \
-  pmImageSubtract.h \
-  pmMaskBadPixels.h \
-  pmNonLinear.h \
-  pmObjects.h \
-  pmReadoutCombine.h \
-  pmSubtractBias.h \
-  pmSubtractSky.h \
-  pmAstrometry.h
+libpsmodule_la_SOURCES = dummy.c
+libpsmodule_la_CPPFLAGS = $(SRCINC)
+libpsmodule_la_LIBADD =  $(SRCSUBLIBS)
 
 EXTRA_DIST = psErrorCodes.dat
Index: /trunk/psModules/src/astrom/Makefile.am
===================================================================
--- /trunk/psModules/src/astrom/Makefile.am	(revision 5170)
+++ /trunk/psModules/src/astrom/Makefile.am	(revision 5170)
@@ -0,0 +1,9 @@
+noinst_LTLIBRARIES = libpsmoduleastrom.la
+
+libpsmoduleastrom_la_CPPFLAGS = $(SRCINC) $(PSMODULE_CFLAGS)
+libpsmoduleastrom_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+libpsmoduleastrom_la_SOURCES  = pmAstrometry.c
+
+psmoduleincludedir = $(includedir)
+psmoduleinclude_HEADERS = \
+  pmAstrometry.h
Index: /trunk/psModules/src/astrom/pmAstrometry.c
===================================================================
--- /trunk/psModules/src/astrom/pmAstrometry.c	(revision 5170)
+++ /trunk/psModules/src/astrom/pmAstrometry.c	(revision 5170)
@@ -0,0 +1,897 @@
+/** @file  psAstrometry.c
+*
+*  @brief This file defines the basic types for astronomical coordinate
+*  transformation
+*
+*  @ingroup AstroImage
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-09-28 20:43:52 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+/******************************************************************************/
+/*  INCLUDE FILES                                                             */
+/******************************************************************************/
+#include <string.h>
+#include <math.h>
+#include "pslib.h"
+#include "psDB.h"
+#include "pmAstrometry.h"
+
+/*****************************************************************************
+checkValidImageCoords(): this is a private function which simply determines if
+the supplied x,y coordinates are in the range for the supplied psImage.
+ 
+XXX: What about col0 and row0
+XXX: This should return a psBool.
+ *****************************************************************************/
+static psS32 checkValidImageCoords(double x,
+                                   double y,
+                                   psImage* tmpImage)
+{
+    PS_ASSERT_IMAGE_NON_NULL(tmpImage, 0);
+
+    if ((x < 0.0) || (x > (double)tmpImage->numCols) ||
+            (y < 0.0) || (y > (double)tmpImage->numRows)) {
+        return (0);
+    }
+
+    return (1);
+}
+
+/******************************************************************************
+XXX: Is this the correct way to free metadata?
+XXX: Is this the correct way to free database structs?
+ 
+ 
+ 
+XXX: The memory dereferencing is not quite right with these functions.  If I
+call the alloc functions with non-NULL pointers, there will be memory leaks.
+ *****************************************************************************/
+static void FPAFree(pmFPA *fpa)
+{
+    if (fpa != NULL) {
+        psFree(fpa->fromTangentPlane);
+        psFree(fpa->toTangentPlane);
+        psFree(fpa->projection);
+        psFree(fpa->concepts);
+        psFree(fpa->analysis);
+        psFree(fpa->camera);
+        //
+        // Set the parent to NULL in all fpa->chips before psFree(fpa->chips)
+        // in order to avoid memory reference counter problems.
+        //
+        for (psS32 i = 0 ; i < fpa->chips->n ; i++) {
+            pmChip *tmpChip = (pmChip *) fpa->chips->data[i];
+            tmpChip->parent = NULL;
+        }
+        psFree(fpa->chips);
+        psFree(fpa->header);
+        psFree(fpa->db);
+    }
+}
+
+static void chipFree(pmChip* chip)
+{
+    if (chip != NULL) {
+        psFree(chip->toFPA);
+        psFree(chip->fromFPA);
+        psFree(chip->concepts);
+        psFree(chip->analysis);
+        //
+        // Set the parent to NULL in all chip->cells before psFree(chip->cells)
+        // in order to avoid memory reference counter problems.
+        //
+        for (psS32 i = 0 ; i < chip->cells->n ; i++) {
+            pmCell *tmpCell = (pmCell *) chip->cells->data[i];
+            tmpCell->parent = NULL;
+        }
+        psFree(chip->cells);
+        psFree(chip->parent);
+        psFree(chip->extname);
+        psFree(chip->pixels);
+        psFree(chip->header);
+    }
+}
+
+static void cellFree(pmCell *cell)
+{
+    if (cell != NULL) {
+        psFree(cell->toChip);
+        psFree(cell->toFPA);
+        psFree(cell->toSky);
+        psFree(cell->concepts);
+        psFree(cell->analysis);
+        //
+        // Set the parent to NULL in all cell->readouts before psFree(cell->readouts)
+        // in order to avoid memory reference counter problems.
+        //
+        for (psS32 i = 0 ; i < cell->readouts->n ; i++) {
+            pmReadout *tmpReadout = (pmReadout *) cell->readouts->data[i];
+            tmpReadout->parent = NULL;
+        }
+        psFree(cell->readouts);
+        psFree(cell->parent);
+        psFree(cell->extname);
+        psFree(cell->pixels);
+        psFree(cell->header);
+    }
+}
+
+static void readoutFree(pmReadout *readout)
+{
+    if (readout != NULL) {
+        psFree(readout->image);
+        psFree(readout->mask);
+        psFree(readout->analysis);
+        psFree(readout->concepts);
+        psFree(readout->parent);
+    }
+}
+
+// XXX: Verify these default values for row0, col0, rowBins, colBins
+pmReadout *pmReadoutAlloc(pmCell *cell)
+{
+    pmReadout *tmpReadout = (pmReadout *) psAlloc(sizeof(pmReadout));
+
+    tmpReadout->col0 = -1;
+    tmpReadout->row0 = -1;
+    tmpReadout->colBins = -1;
+    tmpReadout->rowBins = -1;
+    tmpReadout->image = NULL;
+    tmpReadout->mask = NULL;
+    tmpReadout->analysis = psMetadataAlloc();
+    tmpReadout->concepts = psMetadataAlloc();
+    tmpReadout->parent = cell;
+    if (cell != NULL) {
+        cell->readouts = psArrayAdd(cell->readouts, 1, (psPtr) tmpReadout);
+    }
+    psMemSetDeallocator(tmpReadout, (psFreeFunc) readoutFree);
+    return(tmpReadout);
+}
+
+pmCell *pmCellAlloc(pmChip *chip)
+{
+    pmCell *tmpCell = (pmCell *) psAlloc(sizeof(pmCell));
+
+    tmpCell->col0 = -1;
+    tmpCell->row0 = -1;
+    tmpCell->toChip = NULL;
+    tmpCell->toFPA = NULL;
+    tmpCell->toSky = NULL;
+    tmpCell->analysis = psMetadataAlloc();
+    tmpCell->concepts = psMetadataAlloc();
+    tmpCell->readouts = psArrayAlloc(0);
+    tmpCell->parent = chip;
+    if (chip != NULL) {
+        chip->cells = psArrayAdd(chip->cells, 1, (psPtr) tmpCell);
+    }
+    tmpCell->valid = false;
+    tmpCell->extname = NULL;
+    tmpCell->pixels = NULL;
+    tmpCell->header = NULL;
+
+    psMemSetDeallocator(tmpCell, (psFreeFunc) cellFree);
+    return(tmpCell);
+}
+
+pmChip *pmChipAlloc(pmFPA *fpa)
+{
+    pmChip *tmpChip = (pmChip *) psAlloc(sizeof(pmChip));
+
+    tmpChip->col0 = -1;
+    tmpChip->row0 = -1;
+    tmpChip->toFPA = NULL;
+    tmpChip->fromFPA = NULL;
+    tmpChip->concepts = psMetadataAlloc();
+    tmpChip->analysis = psMetadataAlloc();
+    tmpChip->cells = psArrayAlloc(0);
+    tmpChip->parent = fpa;
+    if (fpa != NULL) {
+        fpa->chips = psArrayAdd(fpa->chips, 1, (psPtr) tmpChip);
+    }
+    tmpChip->valid = false;
+    tmpChip->extname = NULL;
+    tmpChip->pixels = NULL;
+    tmpChip->header = NULL;
+
+    psMemSetDeallocator(tmpChip, (psFreeFunc) chipFree);
+    return(tmpChip);
+}
+
+pmFPA *pmFPAAlloc(
+    const psMetadata *camera,
+    psDB *db)
+{
+    pmFPA *tmpFPA = (pmFPA *) psAlloc(sizeof(pmFPA));
+
+    tmpFPA->fromTangentPlane = NULL;
+    tmpFPA->toTangentPlane = NULL;
+    tmpFPA->projection = NULL;
+    tmpFPA->concepts = psMetadataAlloc();
+    tmpFPA->analysis = psMetadataAlloc();
+    tmpFPA->camera = camera;
+    psMemIncrRefCounter((psPtr) camera);
+    tmpFPA->chips = psArrayAlloc(0);
+    tmpFPA->header = NULL;
+    tmpFPA->db = db;
+    psMemIncrRefCounter((psPtr) db);
+
+    psMemSetDeallocator(tmpFPA, (psFreeFunc) FPAFree);
+    return(tmpFPA);
+}
+
+bool cellCheckParents(pmCell *cell)
+{
+    if (cell == NULL) {
+        return(true);
+    }
+    psBool flag = true;
+
+    for (psS32 i = 0 ; i < cell->readouts->n ; i++) {
+        pmReadout *tmpReadout = (pmReadout *) cell->readouts->data[i];
+        PS_ASSERT_PTR_NON_NULL(tmpReadout, false);
+        if (tmpReadout->parent != cell) {
+            tmpReadout->parent = cell;
+            flag = false;
+        }
+    }
+    return(flag);
+}
+
+bool chipCheckParents(pmChip *chip)
+{
+    if (chip == NULL) {
+        return(true);
+    }
+    psBool flag = true;
+
+    for (psS32 i = 0 ; i < chip->cells->n ; i++) {
+        pmCell *tmpCell = (pmCell *) chip->cells->data[i];
+        PS_ASSERT_PTR_NON_NULL(tmpCell, false);
+        if (tmpCell->parent != chip) {
+            tmpCell->parent = chip;
+            flag = false;
+        }
+
+        flag &= cellCheckParents(tmpCell);
+    }
+    return(flag);
+}
+
+bool pmFPACheckParents(pmFPA *fpa)
+{
+    if (fpa == NULL) {
+        return(true);
+    }
+    psBool flag = true;
+
+    for (psS32 i = 0 ; i < fpa->chips->n ; i++) {
+        pmChip *tmpChip = (pmChip *) fpa->chips->data[i];
+        PS_ASSERT_PTR_NON_NULL(tmpChip, false);
+        if (tmpChip->parent != fpa) {
+            tmpChip->parent = fpa;
+            flag = false;
+        }
+
+        flag &= chipCheckParents(tmpChip);
+    }
+    return(flag);
+}
+
+
+
+// HEY
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+
+pmCell* pmCellInFPA(const psPlane* fpaCoord,
+                    const pmFPA* FPA)
+{
+    PS_ASSERT_PTR_NON_NULL(fpaCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(FPA, NULL);
+
+    pmChip* tmpChip = NULL;
+    psPlane chipCoord;
+    pmCell* outCell = NULL;
+
+    // Determine which chip contains the fpaCoords.
+    tmpChip = pmChipInFPA(fpaCoord, FPA);
+    if (tmpChip == NULL) {
+        return(NULL);
+    }
+
+    // Convert to those chip coordinates.
+    psCoordFPAToChip(&chipCoord, fpaCoord, tmpChip);
+
+    // Determine which cell contains those chip coordinates.
+    outCell = pmCellInChip(&chipCoord, tmpChip);
+
+    return (outCell);
+}
+
+pmChip* pmChipInFPA(const psPlane* fpaCoord,
+                    const pmFPA* FPA)
+{
+    PS_ASSERT_PTR_NON_NULL(fpaCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(FPA, NULL);
+    PS_ASSERT_PTR_NON_NULL(FPA->chips, NULL);
+
+    psArray* chips = FPA->chips;
+    psS32 nChips = chips->n;
+    psPlane chipCoord;
+    pmCell *tmpCell = NULL;
+
+    // Loop through every chip in this FPA.  Convert the original FPA
+    // coordinates to chip coordinates for that chip.  Then, determine if any
+    // cells in that chip contain those chip coordinates.
+
+    for (psS32 i = 0; i < nChips; i++) {
+        pmChip* tmpChip = chips->data[i];
+        PS_ASSERT_PTR_NON_NULL(tmpChip, NULL);
+        PS_ASSERT_PTR_NON_NULL(tmpChip->fromFPA, NULL);
+
+        psPlaneTransformApply(&chipCoord, tmpChip->fromFPA, fpaCoord);
+
+        tmpCell = pmCellInChip(&chipCoord, tmpChip);
+        if (tmpCell != NULL) {
+            return(tmpChip);
+        }
+    }
+
+    // XXX: Print warning here?
+    return (NULL);
+}
+
+
+pmCell* pmCellInChip(const psPlane* chipCoord,
+                     const pmChip* chip)
+{
+    PS_ASSERT_PTR_NON_NULL(chipCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(chip, NULL);
+
+    psPlane cellCoord;
+    psArray* cells;
+
+    cells = chip->cells;
+    if (cells == NULL) {
+        return NULL;
+    }
+
+    // We loop over each cell in the chip.  We transform the chipCoord into
+    // a cellCoord for that cell and determine if that cellCoord is valid.
+    // If so, then we return that cell.
+
+    for (psS32 i = 0; i < cells->n; i++) {
+        pmCell* tmpCell = (pmCell* ) cells->data[i];
+        PS_ASSERT_PTR_NON_NULL(tmpCell, NULL);
+
+        psPlaneTransform *chipToCell = NULL;
+        if (1 != p_psIsProjectionLinear(tmpCell->toChip)) {
+            // XXX: Generate warning message.
+            // XXX: Can we use the following function to derive a transform?
+            // chipToCell = psPlaneTransformInvert(NULL, tmpCell->toChip, NULL, -1);
+        } else {
+            chipToCell = p_psPlaneTransformLinearInvert(tmpCell->toChip);
+        }
+
+        PS_ASSERT_PTR_NON_NULL(chipToCell, NULL);
+        psArray* readouts = tmpCell->readouts;
+
+        if (readouts != NULL) {
+            for (psS32 j = 0; j < readouts->n; j++) {
+                pmReadout* tmpReadout = readouts->data[j];
+                PS_ASSERT_READOUT_NON_NULL(tmpReadout, NULL);
+
+                psPlaneTransformApply(&cellCoord,
+                                      chipToCell,
+                                      chipCoord);
+
+                if (checkValidImageCoords(cellCoord.x,
+                                          cellCoord.y,
+                                          tmpReadout->image)) {
+                    return (tmpCell);
+                }
+            }
+        }
+        psFree(chipToCell);
+    }
+
+    return (NULL);
+}
+
+
+psPlane* psCoordCellToChip(psPlane* outCoord,
+                           const psPlane* inCoord,
+                           const pmCell* cell)
+{
+    PS_ASSERT_PTR_NON_NULL(inCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+
+    return (psPlaneTransformApply(outCoord, cell->toChip, inCoord));
+}
+
+
+psPlane* psCoordChipToFPA(psPlane* outCoord,
+                          const psPlane* inCoord,
+                          const pmChip* chip)
+{
+    PS_ASSERT_PTR_NON_NULL(inCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(chip, NULL);
+
+    return (psPlaneTransformApply(outCoord, chip->toFPA, inCoord));
+}
+
+
+
+psPlane* psCoordFPAToTP(psPlane* outCoord,
+                        const psPlane* inCoord,
+                        double color,
+                        double magnitude,
+                        const pmFPA* fpa)
+{
+    PS_ASSERT_PTR_NON_NULL(inCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+
+    return(psPlaneDistortApply(outCoord, fpa->toTangentPlane, inCoord,
+                               color, magnitude));
+}
+
+/*****************************************************************************
+XXX: What about units for the (x,y) coords?
+ *****************************************************************************/
+psSphere* psCoordTPToSky(psSphere* outSphere,
+                         const psPlane* tpCoord,
+                         const psProjection *projection
+                        )
+{
+    PS_ASSERT_PTR_NON_NULL(tpCoord, NULL);
+
+    if (outSphere == NULL) {
+        outSphere = (psSphere* ) psAlloc(sizeof(psSphere));
+    }
+
+    // XXX: this was done by a SLALIB call -- needs to be reimplemented
+    psWarning("Warning!  psCoordTPToSky functionality is no longer implemented");
+    // slaAopqk(tpCoord->x, tpCoord->y, (double*)grommit,
+    //         &AOB, &ZOB, &HOB, &outSphere->r, &outSphere->d);
+
+    return (outSphere);
+}
+
+
+
+psPlane* psCoordCellToFPA(psPlane* fpaCoord,
+                          const psPlane* cellCoord,
+                          const pmCell* cell)
+{
+    PS_ASSERT_PTR_NON_NULL(cellCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+
+    return (psPlaneTransformApply(fpaCoord, cell->toFPA, cellCoord));
+}
+
+
+
+psSphere* psCoordCellToSky(psSphere* skyCoord,
+                           const psPlane* cellCoord,
+                           double color,
+                           double magnitude,
+                           const pmCell* cell)
+{
+    PS_ASSERT_PTR_NON_NULL(cellCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->toFPA, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent->parent, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent->parent->toTangentPlane, NULL);
+    //    PS_ASSERT_PTR_NON_NULL(cell->parent->parent->exposure, NULL);
+
+    psPlane* fpaCoord = NULL;
+    psPlane* tpCoord = NULL;
+    pmFPA* parFPA = (cell->parent)->parent;
+    //    psGrommit* tmpGrommit = NULL;
+
+    // Convert the input cell coordinates to FPA coordinates.
+    fpaCoord = psPlaneTransformApply(fpaCoord, cell->toFPA, cellCoord);
+
+    // Convert the FPA coordinates to tangent plane Coordinates.
+    tpCoord = psPlaneDistortApply(tpCoord, parFPA->toTangentPlane,
+                                  fpaCoord, color, magnitude);
+
+    // Generate a grommit for this FPA.
+    //    tmpGrommit = psGrommitAlloc(parFPA->exposure);
+
+    // Convert the tangent plane Coordinates to sky coordinates.
+    //    skyCoord = psCoordTPToSky(skyCoord, tpCoord, tmpGrommit);
+
+    psFree(fpaCoord);
+    psFree(tpCoord);
+    //    psFree(tmpGrommit);
+
+    return(skyCoord);
+}
+
+
+psSphere* psCoordCellToSkyQuick(psSphere* outSphere,
+                                const psPlane* cellCoord,
+                                const pmCell* cell)
+{
+    PS_ASSERT_PTR_NON_NULL(cellCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->toSky, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent->parent, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent->parent->projection, NULL);
+
+    psLogMsg(__func__, PS_LOG_WARN,
+             "WARNING: psCoordCellToSkyQuick(): This function is not fully specified in the SDRS.  Returning NULL.\n");
+    return(NULL);
+
+    /*
+        if (cell->toSky) {
+            // XXX: Should we use toTP or toSky?
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: psCoordCellToSkyQuick(): The cell->toSky transform is ignored.  The cell->toTP transform is being used.");
+        }
+     
+        psPlane *tpCoord = NULL;
+        pmChip *chip = cell->parent;
+        pmFPA *FPA = chip->parent;
+        psProjectionType oldProjectionType;
+     
+        if (outSphere == NULL) {
+            outSphere = (psSphere* ) psAlloc(sizeof(psSphere));
+        }
+     
+        // Determine the tangent plane coordinates.
+        tpCoord = psPlaneTransformApply(NULL, cell->toTP, cellCoord);
+     
+        // Save the old projection type and set the new projection type to TAN.
+        oldProjectionType = FPA->projection->type;
+        FPA->projection->type = PS_PROJ_TAN;
+     
+        // Deproject the tangent plane coordinates a sphere.
+        outSphere = psDeproject(tpCoord, FPA->projection);
+     
+        // Restore old projection type.  Free memory.
+        FPA->projection->type = oldProjectionType;
+        psFree(tpCoord);
+     
+        return (outSphere);
+    */
+}
+
+
+/*****************************************************************************
+XXX: What about units for the (x,y) coords?
+ *****************************************************************************/
+psPlane* psCoordSkyToTP(psPlane* tpCoord,
+                        const psSphere* in,
+                        const psProjection *projection)
+{
+    PS_ASSERT_PTR_NON_NULL(in, NULL);
+    //    PS_ASSERT_PTR_NON_NULL(grommit, NULL);
+
+    // char* type = "RA";
+
+    if (tpCoord == NULL) {
+        tpCoord = (psPlane* ) psAlloc(sizeof(psPlane));
+    }
+
+    // XXX: this was done by a SLALIB call -- needs to be reimplemented
+    psWarning("Warning!  psCoordSkyToTP functionality is no longer implemented");
+    // slaOapqk(type, in->r, in->d, (double*)grommit, &tpCoord->x, &tpCoord->y);
+
+    return(tpCoord);
+}
+
+
+psPlane* psCoordTPToFPA(psPlane* fpaCoord,
+                        const psPlane* tpCoord,
+                        double color,
+                        double magnitude,
+                        const pmFPA* fpa)
+{
+    PS_ASSERT_PTR_NON_NULL(tpCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+    PS_ASSERT_PTR_NON_NULL(fpa->fromTangentPlane, NULL);
+
+    return (psPlaneDistortApply(fpaCoord, fpa->fromTangentPlane,
+                                tpCoord, color, magnitude));
+}
+
+
+psPlane* psCoordFPAToChip(psPlane* chipCoord,
+                          const psPlane* fpaCoord,
+                          const pmChip* chip)
+{
+    PS_ASSERT_PTR_NON_NULL(fpaCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(chip, NULL);
+    PS_ASSERT_PTR_NON_NULL(chip->fromFPA, NULL);
+
+    chipCoord = psPlaneTransformApply(chipCoord, chip->fromFPA, fpaCoord);
+    return(chipCoord);
+}
+
+
+psPlane* psCoordChipToCell(psPlane* cellCoord,
+                           const psPlane* chipCoord,
+                           const pmCell* cell)
+{
+    PS_ASSERT_PTR_NON_NULL(chipCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent, NULL);
+
+    pmCell *tmpCell = pmCellInChip(chipCoord, cell->parent);
+    PS_ASSERT_PTR_NON_NULL(tmpCell->toChip, NULL);
+    psPlaneTransform *tmpChipToCell = p_psPlaneTransformLinearInvert(tmpCell->toChip);
+    PS_ASSERT_PTR_NON_NULL(tmpChipToCell, NULL);
+    cellCoord = psPlaneTransformApply(cellCoord, tmpChipToCell, chipCoord);
+    psFree(tmpChipToCell);
+    return(cellCoord);
+}
+
+
+psPlane* psCoordSkyToCell(psPlane* cellCoord,
+                          const psSphere* skyCoord,
+                          float color,
+                          float magnitude,
+                          const pmCell* cell)
+{
+    PS_ASSERT_PTR_NON_NULL(skyCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent->parent, NULL);
+    //    PS_ASSERT_PTR_NON_NULL(cell->parent->parent->grommit, NULL);
+
+    pmChip *parChip = cell->parent;
+    pmFPA *parFPA = parChip->parent;
+
+    // Convert the skyCoords to tangent plane coords.
+    psPlane *tpCoord = psCoordSkyToTP(NULL, skyCoord, parFPA->projection);
+
+    // Convert the tangent plane coords to FPA coords.
+    psPlane *fpaCoord = psCoordTPToFPA(NULL, tpCoord, color, magnitude, parFPA);
+
+    // Convert the FPA coords to chip coords.
+    psPlane *chipCoord = psCoordFPAToChip(NULL, fpaCoord, parChip);
+
+    // Convert the chip coords to cell coords.
+    cellCoord = psCoordChipToCell(cellCoord, chipCoord, cell);
+
+    psFree(tpCoord);
+    psFree(fpaCoord);
+    psFree(chipCoord);
+
+    return (cellCoord);
+}
+
+
+psPlane* psCoordSkyToCellQuick(psPlane* cellCoord,
+                               const psSphere* skyCoord,
+                               const pmCell* cell)
+{
+    PS_ASSERT_PTR_NON_NULL(skyCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent->parent, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent->parent->projection, NULL);
+
+
+    psLogMsg(__func__, PS_LOG_WARN,
+             "WARNING: psCoordSkyToCellQuick(): This function is not fully specified in the SDRS.  Returning NULL.\n");
+    return(NULL);
+    /*
+     
+        if (cell->toSky) {
+            // XXX: Should we use toTP or toSky?
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: psCoordSkyToCellQuick: The cell->toSky transform is ignored.  The cell->toTP transform is being used.");
+        }
+     
+        psPlane *tpCoord = NULL;
+        pmChip *whichChip = cell->parent;
+        pmFPA  *whichFPA = whichChip->parent;
+        psProjectionType oldProjectionType;
+        psPlaneTransform *TPtoCell = NULL;
+     
+        // Save the old projection type and set the new projection type to TAN.
+        oldProjectionType = whichFPA->projection->type;
+        whichFPA->projection->type = PS_PROJ_TAN;
+     
+        if (cellCoord == NULL) {
+            cellCoord = (psPlane* ) psAlloc(sizeof(psPlane));
+        }
+     
+        tpCoord = psProject(skyCoord, whichFPA->projection);
+     
+        // generate an error if cell->toTP is not linear.
+        if (0 == p_psIsProjectionLinear(cell->toTP)) {
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psAstrometry_NONLINEAR_TRANSFORM,
+                    "cell to tangent plane");
+        }
+     
+        TPtoCell = p_psPlaneTransformLinearInvert(cell->toTP);
+        cellCoord = psPlaneTransformApply(cellCoord, TPtoCell, tpCoord);
+     
+        // Restore old projection type.  Free memory.
+        whichFPA->projection->type = oldProjectionType;
+        psFree(tpCoord);
+        return (cellCoord);
+    */
+}
+
+/*
+psMetadataItem* psMetadataLookup(
+    const psMetadata * md,             ///< Metadata collection to lookup meta!
+    const char * key                   ///< Name of metadata key.
+);
+*/
+
+// XXX: How should we handle errors?  What if psMetadataLookup() is NULL?
+psMetadataItem *pmReadoutGetConcept(pmReadout *readout, const char *concept)
+{
+    return(psMetadataLookup(readout->concepts, concept));
+}
+
+psMetadataItem *pmCellGetConcept(pmCell *cell, const char *concept)
+{
+    return(psMetadataLookup(cell->concepts, concept));
+}
+
+psMetadataItem *pmChipGetConcept(pmChip *chip, const char *concept)
+{
+    return(psMetadataLookup(chip->concepts, concept));
+}
+
+psMetadataItem *pmFPAGetConcept(pmFPA *fpa, const char *concept)
+{
+    return(psMetadataLookup(fpa->concepts, concept));
+}
+
+
+float pmFPAGetAirmass(pmFPA *fpa) // FPA.AIRMASS
+{
+    psMetadataItem *tmp = pmFPAGetConcept(fpa, "FPA.AIRMASS");
+    return((float) tmp->data.F32);
+}
+
+psString pmFPAGetFilter(pmFPA *fpa) // FPA.FILTER
+{
+    psMetadataItem *tmp = pmFPAGetConcept(fpa, "FPA.FILTER");
+    return((psString) tmp->data.V);
+}
+
+float pmFPAGetPosAngle(pmFPA *fpa) // FPA.POSANGLE
+{
+    psMetadataItem *tmp = pmFPAGetConcept(fpa, "FPA.POSANGLE");
+    return((float) tmp->data.F32);
+}
+
+double pmFPAGetRA(pmFPA *fpa) // FPA.RA
+{
+    psMetadataItem *tmp = pmFPAGetConcept(fpa, "FPA.RA");
+    return((float) tmp->data.F32);
+}
+
+double pmFPAGetDec(pmFPA *fpa) // FPA.DEC
+{
+    psMetadataItem *tmp = pmFPAGetConcept(fpa, "FPA.DEC");
+    return((float) tmp->data.F32);
+}
+
+psString pmFPAGetRADecSys(pmFPA *fpa) // FPA.RADECSYS
+{
+    psMetadataItem *tmp = pmFPAGetConcept(fpa, "FPA.RADECSYS");
+    return((psString) tmp->data.V);
+}
+
+psString pmFPAGetName(pmFPA *fpa) // FPA.NAME
+{
+    psMetadataItem *tmp = pmFPAGetConcept(fpa, "FPA.NAME");
+    return((psString) tmp->data.V);
+}
+
+psString pmChipGetName(pmChip *chip) // CHIP.NAME
+{
+    psMetadataItem *tmp = pmChipGetConcept(chip, "CHIP.NAME");
+    return((psString) tmp->data.V);
+}
+
+psString pmCellGetName(pmCell *cell) // CELL.NAME
+{
+    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.NAME");
+    return((psString) tmp->data.V);
+}
+
+psTime *pmCellGetTime(pmCell *cell) // CELL.TIME
+{
+    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.TIME");
+    return((psTime *) tmp->data.V);
+}
+
+psList *pmCellGetBiasSec(pmCell *cell) // CELL.BIASSEC
+{
+    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.BIASSEC");
+    return((psList *) tmp->data.list);
+}
+
+psRegion pmCellGetTrimSec(pmCell *cell) // CELL.TRIMSEC
+{
+    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.TRIMSEC");
+    return((psRegion) *((psRegion *) (tmp->data.V)));
+}
+
+float pmCellGetGain(pmCell *cell) // CELL.GAIN
+{
+    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.GAIN");
+    return((float) tmp->data.F32);
+}
+
+float pmCellGetReadNoise(pmCell *cell) // CELL.READNOISE
+{
+    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.READNOISE");
+    return((float) tmp->data.F32);
+}
+
+float pmCellGetSaturation(pmCell *cell) // CELL.SATURATION
+{
+    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.SATURATION");
+    return((float) tmp->data.F32);
+}
+
+float pmCellGetBad(pmCell *cell) // CELL.BAD
+{
+    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.BAD");
+    return((float) tmp->data.F32);
+}
+
+
+psPixelCoord pmCellGetBin(pmCell *cell) // CELL.BIN
+{
+    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.BIN");
+    return((psPixelCoord)  *((psPixelCoord *) (tmp->data.V)));
+}
+
+psPixelCoord pmCellGetParity(pmCell *cell) // CELL.PARITY
+{
+    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.PARITY");
+    return((psPixelCoord)  *((psPixelCoord *) (tmp->data.V)));
+}
+
+float pmReadoutGetExposure(pmReadout *readout) // READOUT.EXPOSURE
+{
+    psMetadataItem *tmp = pmReadoutGetConcept(readout, "READOUT.EXPOSURE");
+    return((float) tmp->data.F32);
+}
+
+float pmReadoutGetDarkTime(pmReadout *readout) // READOUT.DARKTIME
+{
+    psMetadataItem *tmp = pmReadoutGetConcept(readout, "READOUT.DARKTIME");
+    return((float) tmp->data.F32);
+}
+
+
+
+
+/*
+typedef struct
+{
+    float x0;
+}
+psJunk;
+ 
+psJunk *pmCellTmp(pmCell *cell) // CELL.TRIMSEC
+{
+    psMetadataItem *tmp;
+    return((psJunk *) tmp->data.V);
+}
+ 
+*/
Index: /trunk/psModules/src/astrom/pmAstrometry.h
===================================================================
--- /trunk/psModules/src/astrom/pmAstrometry.h	(revision 5170)
+++ /trunk/psModules/src/astrom/pmAstrometry.h	(revision 5170)
@@ -0,0 +1,455 @@
+/** @file  pmAstrometry.h
+*
+*  @brief This file defines the basic types for astronomical coordinate
+*  transformation
+*
+*  @ingroup AstroImage
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-09-28 20:43:52 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef PS_ASTROMETRY_H
+#define PS_ASTROMETRY_H
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "pslib.h"
+#include "psDB.h"
+
+/// @addtogroup AstroImage
+/// @{
+
+/** Focal plane data structure
+ * 
+ *  A focal plane consists of one or more chips (according to the number of
+ *  pieces of contiguous silicon). It contains metadata containers for the
+ *  concepts and analysis, a link to the parent, and pointers to the FITS header,
+ *  if that corresponds to this level (the FPA may be the PHU, but will not ever
+ *  contain pixels). For astrometry, it contains a transformation from the focal
+ *  plane to the tangent plane and the fixed pattern residuals. It is expected
+ *  that the transformation will consist of two 4D polynomials (i.e. a function
+ *  of two coordinates in position, the magnitude of the object, and the color of
+ *  the object) in order to correct for optical distortions and the effects of
+ *  the atmosphere; hence we think that it is prudent to include a reverse
+ *  transformation which will be derived from numerically inverting the forward
+ *  transformation.
+ *  
+ */
+typedef struct
+{
+    // Astrometric transformations
+    psPlaneDistort* fromTangentPlane;   ///< Transformation from tangent plane to focal plane
+    psPlaneDistort* toTangentPlane;     ///< Transformation from focal plane to tangent plane
+    psProjection *projection;           ///< Projection from tangent plane to sky
+    // Information
+    psMetadata *concepts;               ///< Cache for PS concepts
+    psMetadata *analysis;               ///< FPA-level analysis metadata
+    const psMetadata *camera;           ///< Camera configuration
+    psArray *chips;                     ///< The chips
+    // FITS data
+    psMetadata *header;                 ///< The FITS header, if it corresponds to this level
+    psDB *db;                           ///< Database handle
+}
+pmFPA;
+
+/** Chip data structure
+ *  
+ *  A chip consists of one or more cells (according to the number of amplifiers
+ *  on the device). The chip contains metadata containers for the concepts and
+ *  analysis, a link to the parent, and pointers to the pointers to the various
+ *  FITS data, if that corresponds to this level. For astrometry, in addition to
+ *  the rough positioning information, it contains a coordinate transform from
+ *  the chip to the focal plane. It is expected that this transform will consist
+ *  of two second-order 2D polynomials; hence we think that it is prudent to
+ *  include a reverse transformation which will be derived from numerically
+ *  inverting the forward transformation. A boolean indicates whether the chip is
+ *  of interest, allowing it to be excluded from analysis.
+ *  
+ */
+typedef struct
+{
+    // Offset specifying position on focal plane
+    int col0;                           ///< Offset from the left of FPA.
+    int row0;                           ///< Offset from the bottom of FPA.
+    // Astrometric transformations
+    psPlaneTransform* toFPA;            ///< Transformation from chip to FPA coordinates
+    psPlaneTransform* fromFPA;          ///< Transformation from FPA to chip coordinates
+    // Information
+    psMetadata *concepts;               ///< Cache for PS concepts
+    psMetadata *analysis;               ///< Chip-level analysis metadata
+    psArray *cells;                     ///< The cells (referred to by name)
+    pmFPA *parent;                      ///< Parent FPA
+    bool valid;                         ///< Do we bother about reading and working with this chip?
+    // FITS data
+    const char *extname;                ///< Extension name, if it corresponds to this level
+    psArray *pixels;                    ///< The pixel data, if it corresponds to this level
+    psMetadata *header;                 ///< The FITS header, if it corresponds to this level
+}
+pmChip;
+
+/** Cell data structure
+ *
+ *  A cell consists of one or more readouts.  It also contains a pointer to the
+ *  cell's metadata, and its parent chip.  On the astrometry side, it also
+ *  contains coordinate transforms from the cell to chip, from the cell to
+ *  focal-plane, as well as a "quick and dirty" tranform from the cell to
+ *  sky coordinates.
+ *
+ */
+typedef struct
+{
+    // Offset specifying position on chip
+    int col0;                           ///< Offset from the left of chip.
+    int row0;                           ///< Offset from the bottom of chip.
+    // Astrometric transformations
+    psPlaneTransform* toChip;           ///< Transformations from cell to chip coordinates
+    psPlaneTransform* toFPA;            ///< Transformations from cell to FPA coordinates
+    psPlaneTransform* toSky;            ///< Transformations from cell to sky coordinates
+    // Information
+    psMetadata *concepts;               ///< Cache for PS concepts
+    psMetadata *analysis;               ///< Cell-level analysis metadata
+    psArray *readouts;                  ///< The readouts (referred to by number)
+    pmChip *parent;                     ///< Parent chip
+    bool valid;                         ///< Do we bother about reading and working with this cell?
+    // FITS data
+    const char *extname;                ///< Extension name, if it corresponds to this level
+    psArray *pixels;                    ///< The pixel data, if it corresponds to this level
+    psMetadata *header;                 ///< The FITS header, if it corresponds to this level
+}
+pmCell;
+
+/** Readout data structure.
+ *
+ *  A readout is the result of a single read of a cell (or a portion thereof).
+ *  It contains the offset from the lower-left corner of the chip, in the case
+ *  that the CCD was windowed, as well as the binning factors and parity (if the
+ *  binning value is negative, then the parity is reversed). It also contains the
+ *  pixel data, metadata containers for the concepts and analysis, and a link to
+ *  the parent.
+ *
+ */
+typedef struct
+{
+    // Position on the cell
+    int col0;                           ///< Offset from the left of chip.
+    int row0;                           ///< Offset from the bottom of chip.
+    int colBins;                        ///< Amount of binning in x-dimension
+    int rowBins;                        ///< Amount of binning in y-dimension
+    // Information
+    psImage *image;                     ///< Imaging area of readout
+    // XXX: The following mask was removed from the pmReadout struct in recent SDRS
+    // versions.  However, I'm keeping it here since al ot of modules still require
+    // it.
+    psImage *mask;                      ///< Mask of input image
+    psMetadata *analysis;               ///< Readout-level analysis metadata
+    psMetadata *concepts;               ///< Cache for PS Concepts
+    pmCell *parent;                     ///< Parent cell
+}
+pmReadout;
+
+
+/** Allocates a pmReadout
+ *
+ *  The constructor shall make an empty pmReadout. If the parent cell is not
+ *  NULL, the parent link is made and the readout shall be placed in the
+ *  parents array of readouts. The metadata containers shall be allocated. All
+ *  other pointers in the structure shall be initialized to NULL.
+ *
+ *  @return pmReadout*    newly allocated pmReadout with all internal pointers set to NULL
+ */
+pmReadout *pmReadoutAlloc(
+    pmCell *cell                        ///< Parent cell
+);
+
+/** Allocates a pmCell
+ *
+ *  The constructor shall make an empty pmCell. If the parent chip is not NULL,
+ *  the parent link is made and the cell shall be placed in the parents array of
+ *  cells. The readouts array shall be allocated with a zero size, and the
+ *  metadata containers constructed. All other pointers in the structure shall be
+ *  initialized to NULL.
+ *
+ *  @return pmCell*    newly allocated pmCell
+ */
+pmCell *pmCellAlloc(
+    pmChip *chip                        ///< Parent chip
+);
+
+/** Allocates a pmChip
+ * 
+ *  The constructor shall make an empty pmChip. If the parent fpa is not NULL,
+ *  the parent link is made and the chip shall be placed in the parent's array
+ *  of chips. The cells array shall be allocated with a zero size, and the
+ *  metadata containers constructed. All other pointers in the structure shall be
+ *  initialized to NULL.
+ *
+ *  @return pmChip*    newly allocated pmChip
+ */
+pmChip *pmChipAlloc(pmFPA *fpa);
+
+/** Allocates a pmFPA
+ * 
+ *  The constructor shall make an empty pmFPA. The chips array shall be
+ *  allocated with a zero size, the camera and db pointers set to the values
+ *  provided, and the concepts metadata constructed. All other pointers in the
+ *  structure shall be initialized to NULL.
+ * 
+ */
+pmFPA *pmFPAAlloc(
+    const psMetadata *camera,           ///< Camera configuration
+    psDB *db                            ///< Database handle
+);
+
+
+/** Verify parent links.
+ * 
+ *  This function checks the validity of the parent links in the FPA hierarchy.
+ *  If a parent link is not set (or not set correctly), it is corrected, and the
+ *  function shall return false. If all the parent pointers were correct, the
+ *  function shall return true.
+ * 
+ */
+bool pmFPACheckParents(
+    pmFPA *fpa
+);
+
+
+
+/** FUNC DESC
+ * 
+ * 
+ * 
+ * 
+ */
+
+
+
+/*****************************************************************************
+    Old Stuff
+ *****************************************************************************/
+
+
+
+/** Find cooresponding cell for given FPA coordinate
+ *
+ *  @return pmCell*    the cell cooresponding to the coord in FPA
+ */
+pmCell* pmCellInFPA(
+    const psPlane* coord,              ///< the coordinate in FPA plane
+    const pmFPA* FPA                   ///< the FPA to search for the cell
+);
+
+
+/** Find cooresponding chip for given FPA coordinate
+ *
+ *  @return pmChip*    the chip cooresponding to coord
+ */
+pmChip* pmChipInFPA(
+    const psPlane* coord,              ///< the coordinate in FPA plane
+    const pmFPA* FPA                   ///< the FPA to search for the cell
+);
+
+
+/** Find cooresponding cell for given Chip coordinate
+ *
+ *  @return pmCell*    the cell cooresponding to coord
+ */
+pmCell* pmCellInChip(
+    const psPlane* coord,              ///< the coordinate in Chip plane
+    const pmChip* chip                 ///< the chip to search for the cell
+);
+
+
+/** Translate a cell coordinate into a chip coordinate
+ *
+ *  @return psPlane*    the resulting chip coordinate
+ */
+psPlane* psCoordCellToChip(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within Cell
+    const pmCell* cell                 ///< the Cell in interest
+);
+
+
+/** Translate a chip coordinate into a FPA coordinate
+ *
+ *  @return psPlane*    the resulting FPA coordinate
+ */
+psPlane* psCoordChipToFPA(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within Chip
+    const pmChip* chip                 ///< the chip in interest
+);
+
+
+/** Translate a FPA coordinate into a Tangent Plane coordinate
+ *
+ *  @return psPlane*    the resulting Tangent Plane coordinate
+ */
+psPlane* psCoordFPAToTP(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within FPA
+    double color,                      ///< Color of source
+    double magnitude,                  ///< Magnitude of source
+    const pmFPA* fpa                   ///< the FPA in interest
+);
+
+
+/** Translate a Tangent Plane coordinate into a Sky coordinate
+ *
+ *  @return psSphere*    the resulting Sky coordinate
+ */
+psSphere* psCoordTPToSky(
+    psSphere* out,                     ///< a sphere struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                ///< the coordinate within Tangent Plane
+    const psProjection *projection
+);
+
+/** Translate a cell coordinate into a FPA coordinate
+ *
+ *  @return psPlane*    the resulting FPA coordinate
+ */
+psPlane* psCoordCellToFPA(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within cell
+    const pmCell* cell                 ///< the cell in interest
+);
+
+
+/** Translate a cell coordinate into a Sky coordinate
+ *
+ *  @return psSphere*    the resulting Sky coordinate
+ */
+psSphere* psCoordCellToSky(
+    psSphere* out,                     ///< a sphere struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within cell
+    double color,                      ///< Color of source
+    double magnitude,                  ///< Magnitude of source
+    const pmCell* cell                 ///< the cell in interest
+);
+
+
+/** Translate a cell coordinate into a Sky coordinate using a 'quick and
+ *  dirty' method
+ *
+ *  @return psSphere*    the resulting Sky coordinate
+ */
+psSphere* psCoordCellToSkyQuick(
+    psSphere* out,                     ///< a sphere struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within cell
+    const pmCell* cell                 ///< the cell in interest
+);
+
+
+/** Translate a Sky coordinate into a Tangent Plane coordinate
+ *
+ *  @return psPlane*    the resulting Tangent Plane coordinate
+ */
+psPlane* psCoordSkyToTP(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psSphere* in,                ///< the sky coordinate
+    const psProjection *projection
+);
+
+/** Translate a Tangent Plane coordinate into a FPA coordinate
+ *
+ *  @return psPlane*    the resulting FPA coordinate
+ */
+psPlane* psCoordTPToFPA(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within tangent plane
+    double color,                      ///< Color of source
+    double magnitude,                  ///< Magnitude of source
+    const pmFPA* fpa                   ///< the FPA of interest
+);
+
+
+/** Translate a FPA coordinate into a chip coordinate
+ *
+ *  @return psPlane*    the resulting chip coordinate
+ */
+psPlane* psCoordFPAToChip(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the FPA coordinate
+    const pmChip* chip                 ///< the chip of interest
+);
+
+
+/** Translate a chip coordinate into a cell coordinate
+ *
+ *  @return psPlane*    the resulting cell coordinate
+ */
+psPlane* psCoordChipToCell(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the Chip coordinate
+    const pmCell* cell                 ///< the cell of interest
+);
+
+
+/** Translate a sky coordinate into a cell coordinate
+ *
+ *  @return psPlane*    the resulting cell coordinate
+ */
+psPlane* psCoordSkyToCell(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psSphere* in,                ///< the Sky coordinate
+    float color,                       ///< Color of source
+    float magnitude,                   ///< Magnitude of source
+    const pmCell* cell                 ///< the cell of interest
+);
+
+
+/** Translate a sky coordinate into a cell coordinate using a 'quick and
+ *  dirty' method
+ *
+ *  @return psPlane*    the resulting cell coordinate
+ */
+psPlane* psCoordSkyToCellQuick(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psSphere* in,                ///< the Sky coordinate
+    const pmCell* cell                 ///< the cell of interest
+);
+
+
+psMetadataItem *pmCellGetConcept(pmCell *cell, const char *concept);
+psMetadataItem *pmChipGetConcept(pmChip *chip, const char *concept);
+psMetadataItem *pmFPAGetConcept(pmFPA *fpa, const char *concept);
+
+/**
+ * 
+ *  We next specify a series of specific functions for concept lookups. These
+ *  will generally be what the user utilises, so the goal is to provide a simple
+ *  interface providing a single type back, so the user doesnt have to go to the
+ *  trouble of checking types, etc. These functions should employ the above three
+ *  general lookup functions and deal with the result appropriately.
+ *
+ */
+float pmFPAGetAirmass(pmFPA *fpa); // FPA.AIRMASS
+psString pmFPAGetFilter(pmFPA *fpa); // FPA.FILTER
+float pmFPAGetPosAngle(pmFPA *fpa); // FPA.POSANGLE
+double pmFPAGetRA(pmFPA *fpa); // FPA.RA
+double pmFPAGetDec(pmFPA *fpa); // FPA.DEC
+psString pmFPAGetRADecSys(pmFPA *fpa); // FPA.RADECSYS
+psString pmFPAGetName(pmFPA *fpa); // FPA.NAME
+psString pmChipGetName(pmChip *chip); // CHIP.NAME
+psString pmCellGetName(pmCell *cell); // CELL.NAME
+psTime *pmCellGetTime(pmCell *cell); // CELL.TIME
+psList *pmCellGetBiasSec(pmCell *cell); // CELL.BIASSEC
+psRegion pmCellGetTrimSec(pmCell *cell); // CELL.TRIMSEC
+float pmCellGetGain(pmCell *cell); // CELL.GAIN
+float pmCellGetReadNoise(pmCell *cell); // CELL.READNOISE
+float pmCellGetSaturation(pmCell *cell); // CELL.SATURATION
+float pmCellGetBad(pmCell *cell); // CELL.BAD
+psPixelCoord pmCellGetBin(pmCell *cell); // CELL.BIN
+psPixelCoord pmCellGetParity(pmCell *cell); // CELL.PARITY
+float pmReadoutGetExposure(pmReadout *readout); // READOUT.EXPOSURE
+float pmReadoutGetDarkTime(pmReadout *readout); // READOUT.DARKTIME
+
+
+
+#endif // #ifndef PS_ASTROMETRY_H
Index: /trunk/psModules/src/camera/Makefile.am
===================================================================
--- /trunk/psModules/src/camera/Makefile.am	(revision 5170)
+++ /trunk/psModules/src/camera/Makefile.am	(revision 5170)
@@ -0,0 +1,9 @@
+noinst_LTLIBRARIES = libpsmodulecamera.la
+
+libpsmodulecamera_la_CPPFLAGS = $(SRCINC) $(PSMODULE_CFLAGS)
+libpsmodulecamera_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+libpsmodulecamera_la_SOURCES  =
+
+psmoduleincludedir = $(includedir)
+psmoduleinclude_HEADERS =
+
Index: /trunk/psModules/src/config/Makefile.am
===================================================================
--- /trunk/psModules/src/config/Makefile.am	(revision 5170)
+++ /trunk/psModules/src/config/Makefile.am	(revision 5170)
@@ -0,0 +1,9 @@
+noinst_LTLIBRARIES = libpsmoduleconfig.la
+
+libpsmoduleconfig_la_CPPFLAGS = $(SRCINC) $(PSMODULE_CFLAGS)
+libpsmoduleconfig_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+libpsmoduleconfig_la_SOURCES  =
+
+psmoduleincludedir = $(includedir)
+psmoduleinclude_HEADERS =
+
Index: /trunk/psModules/src/detrend/Makefile.am
===================================================================
--- /trunk/psModules/src/detrend/Makefile.am	(revision 5170)
+++ /trunk/psModules/src/detrend/Makefile.am	(revision 5170)
@@ -0,0 +1,17 @@
+noinst_LTLIBRARIES = libpsmoduledetrend.la
+
+libpsmoduledetrend_la_CPPFLAGS = $(SRCINC) $(PSMODULE_CFLAGS)
+libpsmoduledetrend_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+libpsmoduledetrend_la_SOURCES  = pmFlatField.c \
+    pmMaskBadPixels.c \
+    pmNonLinear.c
+
+psmoduleincludedir = $(includedir)
+psmoduleinclude_HEADERS = \
+  pmFlatField.h \
+  pmFlatFieldErrors.h \
+  pmMaskBadPixelsErrors.h \
+  pmMaskBadPixels.h \
+  pmNonLinear.h
+
+EXTRA_DIST = pmFlatFieldErrors.dat pmMaskBadPixelsErrors.dat
Index: /trunk/psModules/src/detrend/pmFlatField.c
===================================================================
--- /trunk/psModules/src/detrend/pmFlatField.c	(revision 5170)
+++ /trunk/psModules/src/detrend/pmFlatField.c	(revision 5170)
@@ -0,0 +1,182 @@
+/** @file  pmFlatField.c
+ *
+ *  @brief Given an input image and a flat field image, pmFlatField shall divide the input image by the flat
+ *  field image.
+ *
+ *  The input image, in, and the flat field image, flat, need not be the same size, since the input image may
+ *  already have been trimmed (following overscan subtraction), but the function shall use the offsets in the
+ *  image (in->x0 and in->y0) to determine the appropriate offsets to obtain the correct pixel on the flat
+ *  field. In the event that the flat image is too small (i.e., pixels on the input image refer to pixels
+ *  outside the range of the flat image), the function shall generate an error. Pixels which are negative or
+ *  zero in the flat shall be masked in the input image with the value PM_MASK_FLAT. Negative pixels in the
+ *  flat may be set to zero so that they are treated identically to zeroes. Any pixels masked in the flat
+ *  shall be masked with corresponding values in the output. The function shall not normalize the flat; this
+ *  responsibility is left to the caller. This function is basically equivalent to a divide (with psImageOp),
+ *  but with care for the region that is divided, checking for negative pixels, and copying of the mask from
+ *  the flat to the output.
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include<stdio.h>
+#include<math.h>
+#include <string.h>
+
+#include "pslib.h"
+#include "pmFlatField.h"
+#include "pmMaskBadPixels.h"
+#include "pmFlatFieldErrors.h"
+
+
+bool pmFlatField(pmReadout *in, pmReadout *mask, const pmReadout *flat)
+{
+    // XXX: Not sure if this is correct.  Must consult with IfA.
+    PS_ASSERT_PTR_NON_NULL(mask, false);
+    int i = 0;
+    int j = 0;
+    int totOffCol = 0;
+    int totOffRow = 0;
+    psElemType inType;
+    psElemType flatType;
+    psElemType maskType;
+    psImage *inImage = NULL;
+    psImage *inMask = NULL;
+    psImage *flatImage = NULL;
+
+
+    // Check for nulls
+    if (in == NULL) {
+        return true;       // Readout may not have data in it
+    } else if(flat==NULL) {
+        psError( PS_ERR_BAD_PARAMETER_NULL, true,
+                 PS_ERRORTEXT_pmFlatField_NULL_FLAT_READOUT);
+        return false;
+    }
+
+    inImage = in->image;
+    flatImage = flat->image;
+    if (inImage == NULL) {
+        psError( PS_ERR_BAD_PARAMETER_NULL, true,
+                 PS_ERRORTEXT_pmFlatField_NULL_INPUT_IMAGE);
+        return false;
+    } else if(flatImage == NULL) {
+        psError( PS_ERR_BAD_PARAMETER_NULL, true,
+                 PS_ERRORTEXT_pmFlatField_NULL_FLAT_IMAGE);
+        return false;
+    }
+    inMask = mask->image;
+
+    // Check input image and its mask are not larger than flat image
+
+    if (inImage->numRows>flatImage->numRows || inImage->numCols>flatImage->numCols) {
+        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
+                 PS_ERRORTEXT_pmFlatField_SIZE_INPUT_IMAGE,
+                 inImage->numRows, inImage->numCols, flatImage->numRows, flatImage->numCols);
+        return false;
+    }
+    if (inMask->numRows > flatImage->numRows || inMask->numCols > flatImage->numCols) {
+        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
+                 PS_ERRORTEXT_pmFlatField_SIZE_MASK_IMAGE,
+                 inMask->numRows, inMask->numCols, flatImage->numRows, flatImage->numCols);
+        return false;
+    }
+
+    // Determine total offset based on image offset with chip offset
+    totOffCol = inImage->col0 + in->col0;
+    totOffRow = inImage->row0 + in->row0;
+
+    // Check that offsets are within image limits
+    if(totOffRow>=flatImage->numRows || totOffCol>=flatImage->numCols) {
+        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
+                 PS_ERRORTEXT_pmFlatField_OFFSET_FLAT_IMAGE,
+                 totOffRow, totOffCol, flatImage->numRows, flatImage->numCols);
+        return false;
+    } else if(totOffRow>=inImage->numRows || totOffCol>=inImage->numCols) {
+        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
+                 PS_ERRORTEXT_pmFlatField_OFFSET_INPUT_IMAGE,
+                 totOffRow, totOffCol, inImage->numRows, inImage->numCols);
+        return false;
+    } else if(totOffRow>=inMask->numRows || totOffCol>=inMask->numCols) {
+        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
+                 PS_ERRORTEXT_pmFlatField_OFFSET_MASK_IMAGE,
+                 totOffRow, totOffCol, inMask->numRows, inMask->numCols);
+        return false;
+    }
+
+    // Check for incorrect types
+    inType = inImage->type.type;
+    flatType = flatImage->type.type;
+    maskType = inMask->type.type;
+    if(PS_IS_PSELEMTYPE_COMPLEX(inType)) {
+        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
+                 PS_ERRORTEXT_pmFlatField_TYPE_INPUT_IMAGE,
+                 inType);
+        return false;
+    } else if(PS_IS_PSELEMTYPE_COMPLEX(flatType)) {
+        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
+                 PS_ERRORTEXT_pmFlatField_TYPE_FLAT_IMAGE,
+                 flatType);
+        return false;
+    } else if(maskType != PS_TYPE_MASK) {
+        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
+                 PS_ERRORTEXT_pmFlatField_TYPE_MASK_IMAGE,
+                 maskType);
+        return false;
+    } else if(inType != flatType) {
+        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
+                 PS_ERRORTEXT_pmFlatField_TYPE_MISMATCH,
+                 inType, flatType);
+        return false;
+    }
+
+    // Macro for all PS types
+    #define PM_FLAT_DIVISION(TYPE)                                                                           \
+case PS_TYPE_##TYPE:                                                                                         \
+    /* Per Eugene's request, use two sets of loops: first to fill mask, second to avoid div with bad pix */  \
+    for(j = totOffRow; j < inImage->numRows; j++) {                                                          \
+        for(i = totOffCol; i < inImage->numCols; i++) {                                                      \
+            if(flatImage->data.TYPE[j][i] <= 0.0) {                                                          \
+                /* Negative or zero flat pixels shall be masked in input image as  PM_MASK_FLAT */           \
+                inMask->data.PS_TYPE_MASK_DATA[j][i] |= PM_MASK_FLAT;                                        \
+                flatImage->data.TYPE[j][i] = 0.0;                                                            \
+            }                                                                                                \
+        }                                                                                                    \
+    }                                                                                                        \
+    for(j = totOffRow; j < inImage->numRows; j++) {                                                          \
+        for(i = totOffCol; i < inImage->numCols; i++) {                                                      \
+            if(!inMask->data.PS_TYPE_MASK_DATA[j][i]) {                                                      \
+                /* Module shall divide the input image by the flat-fielded image */                          \
+                inImage->data.TYPE[j][i] /= flatImage->data.TYPE[j][i];                                      \
+            }                                                                                                \
+        }                                                                                                    \
+    }                                                                                                        \
+    break;
+
+    switch(inType) {
+        PM_FLAT_DIVISION(U8);
+        PM_FLAT_DIVISION(U16);
+        PM_FLAT_DIVISION(U32);
+        PM_FLAT_DIVISION(U64);
+        PM_FLAT_DIVISION(S8);
+        PM_FLAT_DIVISION(S16);
+        PM_FLAT_DIVISION(S32);
+        PM_FLAT_DIVISION(S64);
+        PM_FLAT_DIVISION(F32);
+        PM_FLAT_DIVISION(F64);
+    default:
+        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
+                 PS_ERRORTEXT_pmFlatField_TYPE_UNSUPPORTED,
+                 inType);
+    }
+
+    return true;
+}
Index: /trunk/psModules/src/detrend/pmFlatField.h
===================================================================
--- /trunk/psModules/src/detrend/pmFlatField.h	(revision 5170)
+++ /trunk/psModules/src/detrend/pmFlatField.h	(revision 5170)
@@ -0,0 +1,42 @@
+/** @file  pmFlatField.h
+ *
+ *  @brief Given an input image and a flat field image, pmFlatField shall divide the input image by the flat
+ *  field image.
+ *
+ *  The input image, in, and the flat field image, flat, need not be the same size, since the input image may
+ *  already have been trimmed (following overscan subtraction), but the function shall use the offsets in the
+ *  image (in->x0 and in->y0) to determine the appropriate offsets to obtain the correct pixel on the flat
+ *  field. In the event that the flat image is too small (i.e., pixels on the input image refer to pixels
+ *  outside the range of the flat image), the function shall generate an error. Pixels which are negative or
+ *  zero in the flat shall be masked in the input image with the value PM_MASK_FLAT. Negative pixels in the
+ *  flat may be set to zero so that they are treated identically to zeroes. Any pixels masked in the flat
+ *  shall be masked with corresponding values in the output. The function shall not normalize the flat; this
+ *  responsibility is left to the caller. This function is basically equivalent to a divide (with psImageOp),
+ *  but with care for the region that is divided, checking for negative pixels, and copying of the mask from
+ *  the flat to the output.
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include "pslib.h"
+#include "pmAstrometry.h"
+
+
+/** Execute flat field module.
+ *
+ *  Given an input image and a flat-field image, pmFlatField shall divide the input image by the flat field
+ *  image.
+ *
+ *  @return  bool: True or false for success or failure
+ */
+bool pmFlatField(
+    pmReadout *in,          ///< Readout with input image
+    pmReadout *mask,        ///< Input image mask
+    const pmReadout *flat   ///< Readout with flat image
+);
+
Index: /trunk/psModules/src/detrend/pmFlatFieldErrors.dat
===================================================================
--- /trunk/psModules/src/detrend/pmFlatFieldErrors.dat	(revision 5170)
+++ /trunk/psModules/src/detrend/pmFlatFieldErrors.dat	(revision 5170)
@@ -0,0 +1,22 @@
+#
+#  This file is used to generate pmFlatFieldErrors.h content
+#
+#  Format is:
+#  ERRORNAME(one word)    ERRORTEXT
+#
+#  N.B. in code, the ERRORNAME appears as PS_ERRORTEXT_ERRORNAME
+####################################################################
+#
+pmFlatField_NULL_FLAT_READOUT          Null not allowed for flat readout.
+pmFlatField_NULL_INPUT_IMAGE           Null not allowed for input image.
+pmFlatField_NULL_FLAT_IMAGE            Null not allowed for flat image.
+pmFlatField_SIZE_INPUT_IMAGE           Input image size exceeds that of flat image: (%d, %d) vs (%d, %d)
+pmFlatField_SIZE_MASK_IMAGE            Input image mask size exceeds that of flat image: (%d, %d) vs (%d, %d)
+pmFlatField_OFFSET_FLAT_IMAGE          Total offset >= flat image size: (%d, %d) vs (%d, %d)
+pmFlatField_OFFSET_INPUT_IMAGE         Total offset >= input image: (%d, %d) vs (%d, %d)
+pmFlatField_OFFSET_MASK_IMAGE          Total offset >= input image mask: (%d, %d) vs (%d, %d)
+pmFlatField_TYPE_INPUT_IMAGE           Complex types not allowed for input image. Type: %d
+pmFlatField_TYPE_FLAT_IMAGE            Complex types not allowed for flat image. Type: %d
+pmFlatField_TYPE_MASK_IMAGE            Mask must be PS_TYPE_MASK type. Type: %d
+pmFlatField_TYPE_MISMATCH              Input and flat image types differ: (%d vs %d)
+pmFlatField_TYPE_UNSUPPORTED           Unsupported image datatype. Type: %d
Index: /trunk/psModules/src/detrend/pmFlatFieldErrors.h
===================================================================
--- /trunk/psModules/src/detrend/pmFlatFieldErrors.h	(revision 5170)
+++ /trunk/psModules/src/detrend/pmFlatFieldErrors.h	(revision 5170)
@@ -0,0 +1,47 @@
+/** @file  pmFlatFieldErrors.h
+ *
+ *  @brief Contains the error text for the flat field module
+ *
+ *  @ingroup ErrorHandling
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PM_FLATFIELD_ERRORS_H
+#define PM_FLATFIELD_ERRORS_H
+
+/* N.B., lines between '//~Start' and '//~End' are automatic generated from
+ * the template following the '//~Start'.  The template is used to generate
+ * the other lines by, for each error text in psDataManipErrors.dat, the following
+ * substitutions are made:
+ *     $1  The error text macro name (first word in the psFlatFieldErrors.h lines)
+ *     $2  The error text (rest of the line in psFlatFieldErrors.h)
+ *     $n  The order of the source line in psFlatFieldErrors.h (comments excluded)
+ *
+ * DO NOT EDIT THE LINES BETWEEN //~Start and //~End!  ANY CHANGES WILL BE OVERWRITTEN.
+ */
+
+#define PS_ERRORNAME_DOMAIN "psModule.src."
+
+//~Start #define PS_ERRORTEXT_$1 "$2"
+#define PS_ERRORTEXT_pmFlatField_NULL_FLAT_READOUT "Null not allowed for flat readout."
+#define PS_ERRORTEXT_pmFlatField_NULL_INPUT_IMAGE "Null not allowed for input image."
+#define PS_ERRORTEXT_pmFlatField_NULL_FLAT_IMAGE "Null not allowed for flat image."
+#define PS_ERRORTEXT_pmFlatField_SIZE_INPUT_IMAGE "Input image size exceeds that of flat image: (%d, %d) vs (%d, %d)"
+#define PS_ERRORTEXT_pmFlatField_SIZE_MASK_IMAGE "Input image mask size exceeds that of flat image: (%d, %d) vs (%d, %d)"
+#define PS_ERRORTEXT_pmFlatField_OFFSET_FLAT_IMAGE "Total offset >= flat image size: (%d, %d) vs (%d, %d)"
+#define PS_ERRORTEXT_pmFlatField_OFFSET_INPUT_IMAGE "Total offset >= input image: (%d, %d) vs (%d, %d)"
+#define PS_ERRORTEXT_pmFlatField_OFFSET_MASK_IMAGE "Total offset >= input image mask: (%d, %d) vs (%d, %d)"
+#define PS_ERRORTEXT_pmFlatField_TYPE_INPUT_IMAGE "Complex types not allowed for input image. Type: %d"
+#define PS_ERRORTEXT_pmFlatField_TYPE_FLAT_IMAGE "Complex types not allowed for flat image. Type: %d"
+#define PS_ERRORTEXT_pmFlatField_TYPE_MASK_IMAGE "Mask must be PS_TYPE_MASK type. Type: %d"
+#define PS_ERRORTEXT_pmFlatField_TYPE_MISMATCH "Input and flat image types differ: (%d vs %d)"
+#define PS_ERRORTEXT_pmFlatField_TYPE_UNSUPPORTED "Unsupported image datatype. Type: %d"
+//~End
+
+#endif
Index: /trunk/psModules/src/detrend/pmMaskBadPixels.c
===================================================================
--- /trunk/psModules/src/detrend/pmMaskBadPixels.c	(revision 5170)
+++ /trunk/psModules/src/detrend/pmMaskBadPixels.c	(revision 5170)
@@ -0,0 +1,188 @@
+/** @file  pmMaskBadPixels.c
+ *
+ *  @brief Given an input image, a bad pixel mask, a corresponding value in the bad pixel mask to mask, a
+ *  saturation level, and a growing radius, mask in the input image those pixels in the bad pixel mask that
+ *  match the value to mask.
+ *
+ *  Given an input image, in, a bad pixel mask, a corresponding value in the bad pixel mask to mask in the
+ * input image, maskVal, a saturation level, and a growing radius, pmMaskBadPixels shall mask in the input
+ * image those pixels in the bad pixel mask that match the value to mask. Note that the input image, in, is
+ * modified in-place. All pixels in the mask which satisfy the maskVal shall have their corresponding pixels
+ * masked in the input image, in. All pixels which satisfy the growVal shall have their corresponding
+ * pixels, along with all pixels within the grow radius masked. Pixels which have flux greater than sat shall
+ * also be masked, but not grown. Note that the input image, in, and the mask need not be the same size, since
+ * the input image may already have been trimmed (following overscan subtraction), but the function shall use
+ * the offsets in the image (in->x0 and in->y0) to determine the appropriate offsets to obtain the correct
+ * pixel on the mask. In the event that the mask image is too small (i.e., pixels on the input image refer to
+ * pixels outside the range of the mask image), the function shall generate an error.
+ 
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include<stdio.h>
+#include<math.h>
+#include<string.h>
+
+#include "pmMaskBadPixels.h"
+#include "pmMaskBadPixelsErrors.h"
+
+bool pmMaskBadPixels(
+    pmReadout *in,
+    const psImage *mask,
+    unsigned int maskVal,
+    float sat,
+    unsigned int growVal,
+    int grow)
+{
+    int i = 0;
+    int j = 0;
+    int jj = 0;
+    int ii = 0;
+    int rRound = 0;
+    int rowMin = 0;
+    int rowMax = 0;
+    int colMin = 0;
+    int colMax = 0;
+    int totOffCol = 0;
+    int totOffRow = 0;
+    float r = 0.0f;
+    psElemType inType;
+    psElemType maskType;
+    psImage *inImage = NULL;
+    psImage *inMask = NULL;
+
+
+    // Check for nulls
+    if (in == NULL) {
+        return true;   // Readout may not have data in it
+    } else if(mask==NULL) {
+        psError( PS_ERR_BAD_PARAMETER_NULL, true,
+                 PS_ERRORTEXT_pmMaskBadPixels_NULL_MASK_IMAGE);
+        return false;
+    }
+
+    inImage = in->image;
+    if (inImage == NULL) {
+        psError( PS_ERR_BAD_PARAMETER_NULL, true,
+                 PS_ERRORTEXT_pmMaskBadPixels_NULL_INPUT_IMAGE);
+        return false;
+    } else if(in->mask == NULL) {
+        in->mask = psImageAlloc(inImage->numCols, inImage->numRows, PS_TYPE_MASK);
+        memset(in->mask->data.V[0], 0, inImage->numCols*inImage->numRows*PSELEMTYPE_SIZEOF(PS_TYPE_MASK));
+    }
+    inMask = in->mask;
+
+    // Check input image and its mask are not larger than mask
+    if(inImage->numRows > mask->numRows || inImage->numCols > mask->numCols) {
+        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
+                 PS_ERRORTEXT_pmMaskBadPixels_SIZE_INPUT_IMAGE,
+                 inImage->numRows, inImage->numCols, mask->numRows, mask->numCols);
+        return false;
+    } else if(inMask->numRows>mask->numRows || inMask->numCols>mask->numCols) {
+        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
+                 PS_ERRORTEXT_pmMaskBadPixels_SIZE_MASK_IMAGE,
+                 inMask->numRows, inMask->numCols, mask->numRows, mask->numCols);
+        return false;
+    }
+
+    // Determine total offset based on image offset with chip offset
+    totOffCol = inImage->col0 + in->col0;
+    totOffRow = inImage->row0 + in->row0;
+
+    // Check that offsets are within image limits
+    if(totOffRow>=mask->numRows || totOffCol>=mask->numCols) {
+        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
+                 PS_ERRORTEXT_pmMaskBadPixels_OFFSET_MASK_IMAGE,
+                 totOffRow, totOffCol, mask->numRows, mask->numCols);
+        return false;
+    } else if(totOffRow>=inImage->numRows || totOffCol>=inImage->numCols) {
+        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
+                 PS_ERRORTEXT_pmMaskBadPixels_OFFSET_INPUT_IMAGE,
+                 totOffRow, totOffCol, inImage->numRows, inImage->numCols);
+        return false;
+    } else if(totOffRow>=inMask->numRows || totOffCol>=inMask->numCols) {
+        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
+                 PS_ERRORTEXT_pmMaskBadPixels_OFFSET_INPUT_IMAGE_MASK,
+                 totOffRow, totOffCol, inMask->numRows, inMask->numCols);
+        return false;
+    }
+
+    // Check for incorrect types
+    inType = inImage->type.type;
+    maskType = mask->type.type;
+    if(PS_IS_PSELEMTYPE_COMPLEX(inType)) {
+        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
+                 PS_ERRORTEXT_pmMaskBadPixels_TYPE_INPUT_IMAGE,
+                 inType);
+        return false;
+    } else if(maskType!=PS_TYPE_MASK) {
+        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
+                 PS_ERRORTEXT_pmMaskBadPixels_TYPE_MASK_IMAGE,
+                 maskType);
+        return false;
+    }
+
+    // Macro for all PS types
+    #define PM_BAD_PIXELS(TYPE)                                                                              \
+case PS_TYPE_##TYPE:                                                                                         \
+    for(j=totOffRow; j<inImage->numRows; j++) {                                                              \
+        for(i=totOffCol; i<inImage->numCols; i++) {                                                          \
+            \
+            /* Pixels with flux greater than sat shall be masked */                                          \
+            if(inImage->data.TYPE[j][i] > sat) {                                                             \
+                inMask->data.PS_TYPE_MASK_DATA[j][i] |= PM_MASK_SAT;                                         \
+            }                                                                                                \
+            \
+            /* Pixels which satisfy maskVal shall be masked */                                               \
+            inMask->data.PS_TYPE_MASK_DATA[j][i] |= (mask->data.PS_TYPE_MASK_DATA[j][i]&maskVal);            \
+            \
+            /* Pixels which satisfy growVal and within the grow radius shall be masked */                    \
+            if(mask->data.PS_TYPE_MASK_DATA[j][i] & growVal) {                                               \
+                rowMin = MAX(j-grow, 0);                                                                     \
+                rowMax = MIN(j+grow+1, inImage->numRows);                                                    \
+                colMin = MAX(i-grow, 0);                                                                     \
+                colMax = MIN(i+grow+1, inImage->numCols);                                                    \
+                for(jj=rowMin; jj<rowMax; jj++) {                                                            \
+                    for(ii=colMin; ii<colMax; ii++) {                                                        \
+                        r = sqrtf((ii-i)*(ii-i)+(jj-j)*(jj-j));                                              \
+                        rRound = r + 0.5;                                                                    \
+                        if(rRound <= grow) {                                                                 \
+                            inMask->data.PS_TYPE_MASK_DATA[jj][ii] |=                                        \
+                                    (mask->data.PS_TYPE_MASK_DATA[j][i]&growVal);                            \
+                        }                                                                                    \
+                    }                                                                                        \
+                }                                                                                            \
+            }                                                                                                \
+        }                                                                                                    \
+    }                                                                                                        \
+    break;
+
+    // Switch to call bad pixel masking macro defined above
+    switch(inType) {
+        PM_BAD_PIXELS(U8);
+        PM_BAD_PIXELS(U16);
+        PM_BAD_PIXELS(U32);
+        PM_BAD_PIXELS(U64);
+        PM_BAD_PIXELS(S8);
+        PM_BAD_PIXELS(S16);
+        PM_BAD_PIXELS(S32);
+        PM_BAD_PIXELS(S64);
+        PM_BAD_PIXELS(F32);
+        PM_BAD_PIXELS(F64);
+    default:
+        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
+                 PS_ERRORTEXT_pmMaskBadPixels_TYPE_UNSUPPORTED,
+                 inType);
+    }
+
+    return false;
+}
Index: /trunk/psModules/src/detrend/pmMaskBadPixels.h
===================================================================
--- /trunk/psModules/src/detrend/pmMaskBadPixels.h	(revision 5170)
+++ /trunk/psModules/src/detrend/pmMaskBadPixels.h	(revision 5170)
@@ -0,0 +1,61 @@
+/** @file  pmMaskBadPixels.h
+ *
+ *  @brief Given an input image, a bad pixel mask, a corresponding value in the bad pixel mask to mask, a
+ *  saturation level, and a growing radius, mask in the input image those pixels in the bad pixel mask that
+ *  match the value to mask.
+ *
+ *  Given an input image, in, a bad pixel mask, a corresponding value in the bad pixel mask to mask in the
+ *  input image, maskVal, a saturation level, and a growing radius, pmMaskBadPixels shall mask in the input
+ *  image those pixels in the bad pixel mask that match the value to mask. Note that the input image, in, is
+ *  modified in-place. All pixels in the mask which satisfy the maskVal shall have their corresponding pixels
+ *  masked in the input image, in. All pixels which satisfy the growVal shall have their corresponding
+ *  pixels, along with all pixels within the grow radius masked. Pixels which have flux greater than sat shall
+ *  also be masked, but not grown. Note that the input image, in, and the mask need not be the same size,
+ *  since the input image may already have been trimmed (following overscan subtraction), but the function
+ *  shall use the offsets in the image (in->x0 and in->y0) to determine the appropriate offsets to obtain the
+ *  correct pixel on the mask. In the event that the mask image is too small (i.e., pixels on the input image
+ *  refer to pixels outside the range of the mask image), the function shall generate an error.
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include "pslib.h"
+#include "pmAstrometry.h"
+
+/** Mask values */
+typedef enum {
+    PM_MASK_TRAP    = 0x0001,   ///< The pixel is a charge trap.
+    PM_MASK_BADCOL  = 0x0002,   ///< The pixel is a bad column.
+    PM_MASK_SAT     = 0x0004,   ///< The pixel is saturated.
+    PM_MASK_FLAT    = 0x0008    ///< The pixel is non-positive in the flat-field.
+} pmMaskValue;
+
+/** Macro to find maximum of two numbers */
+#define MAX(A,B)((A)>=(B)?(A):(B))
+
+/** Macro to find minimum of two numbers */
+#define MIN(A,B)((A)<=(B)?(A):(B))
+
+
+/** Execute bad pixels module.
+ *
+ *  Given an input image, a bad pixel mask, a corresponding value in the bad pixel mask to mask, a
+ *  saturation level, and a growing radius, mask in the input image those pixels in the bad pixel mask that
+ *  match the value to mask.
+ *
+ *  @return  bool: True or false for success or failure
+ */
+bool pmMaskBadPixels(
+    pmReadout *in,          ///< Readout containing input image data.
+    const psImage *mask,    ///< Mask data to be added to readout mask data.
+    unsigned int maskVal,   ///< Mask value to determine what to add to input mask.
+    float sat,              ///< Saturation limit to mask bad pixels.
+    unsigned int growVal,   ///< Mask data to determine if a circurlar area should be masked.
+    int grow                ///< Radius of mask to apply around pixel.
+);
+
Index: /trunk/psModules/src/detrend/pmMaskBadPixelsErrors.dat
===================================================================
--- /trunk/psModules/src/detrend/pmMaskBadPixelsErrors.dat	(revision 5170)
+++ /trunk/psModules/src/detrend/pmMaskBadPixelsErrors.dat	(revision 5170)
@@ -0,0 +1,20 @@
+#
+#  This file is used to generate pmMaskBadPixelsErrors.h content
+#
+#  Format is:
+#  ERRORNAME(one word)    ERRORTEXT
+#
+#  N.B. in code, the ERRORNAME appears as PS_ERRORTEXT_ERRORNAME
+####################################################################
+#
+pmMaskBadPixels_NULL_MASK_IMAGE            Null not allowed for mask image.
+pmMaskBadPixels_NULL_INPUT_IMAGE           Null not allowed for input image.
+pmMaskBadPixels_SIZE_INPUT_IMAGE           Input image size exceeds that of mask image: (%d, %d) vs (%d, %d)
+pmMaskBadPixels_SIZE_MASK_IMAGE            Input image mask size exceeds that of mask image: (%d, %d) vs (%d, %d)
+pmMaskBadPixels_OFFSET_MASK_IMAGE          Total offset >= mask image: (%d, %d) vs (%d, %d)
+pmMaskBadPixels_OFFSET_INPUT_IMAGE         Total offset >= input image: (%d, %d) vs (%d, %d)
+pmMaskBadPixels_OFFSET_INPUT_IMAGE_MASK    Total offset >= input image mask: (%d, %d) vs (%d, %d)
+pmMaskBadPixels_TYPE_INPUT_IMAGE           Complex types not allowed for input image. Type: %d
+pmMaskBadPixels_TYPE_MASK_IMAGE            Mask must be PS_TYPE_MASK type. Type: %d
+pmMaskBadPixels_TYPE_MISMATCH              Input and flat image types differ: (%d vs %d)
+pmMaskBadPixels_TYPE_UNSUPPORTED           Unsupported image datatype. Type: %d
Index: /trunk/psModules/src/detrend/pmMaskBadPixelsErrors.h
===================================================================
--- /trunk/psModules/src/detrend/pmMaskBadPixelsErrors.h	(revision 5170)
+++ /trunk/psModules/src/detrend/pmMaskBadPixelsErrors.h	(revision 5170)
@@ -0,0 +1,45 @@
+/** @file  pmMaskBadPixelsErrors.h
+ *
+ *  @brief Contains the error text for the mask bad pixels module
+ *
+ *  @ingroup ErrorHandling
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PM_FLATFIELD_ERRORS_H
+#define PM_FLATFIELD_ERRORS_H
+
+/* N.B., lines between '//~Start' and '//~End' are automatic generated from
+ * the template following the '//~Start'.  The template is used to generate
+ * the other lines by, for each error text in pmMaskBadPixelsErrors.dat, the following
+ * substitutions are made:
+ *     $1  The error text macro name (first word in the pmMaskBadPixelsErrors.h lines)
+ *     $2  The error text (rest of the line in pmMaskBadPixelsErrors.h)
+ *     $n  The order of the source line in pmMaskBadPixelsErrors.h (comments excluded)
+ * 
+ * DO NOT EDIT THE LINES BETWEEN //~Start and //~End!  ANY CHANGES WILL BE OVERWRITTEN.
+ */
+
+#define PS_ERRORNAME_DOMAIN "psModule.src."
+
+//~Start #define PS_ERRORTEXT_$1 "$2"
+#define PS_ERRORTEXT_pmMaskBadPixels_NULL_MASK_IMAGE "Null not allowed for mask image."
+#define PS_ERRORTEXT_pmMaskBadPixels_NULL_INPUT_IMAGE "Null not allowed for input image."
+#define PS_ERRORTEXT_pmMaskBadPixels_SIZE_INPUT_IMAGE "Input image size exceeds that of mask image: (%d, %d) vs (%d, %d)"
+#define PS_ERRORTEXT_pmMaskBadPixels_SIZE_MASK_IMAGE "Input image mask size exceeds that of mask image: (%d, %d) vs (%d, %d)"
+#define PS_ERRORTEXT_pmMaskBadPixels_OFFSET_MASK_IMAGE "Total offset >= mask image: (%d, %d) vs (%d, %d)"
+#define PS_ERRORTEXT_pmMaskBadPixels_OFFSET_INPUT_IMAGE "Total offset >= input image: (%d, %d) vs (%d, %d)"
+#define PS_ERRORTEXT_pmMaskBadPixels_OFFSET_INPUT_IMAGE_MASK "Total offset >= input image mask: (%d, %d) vs (%d, %d)"
+#define PS_ERRORTEXT_pmMaskBadPixels_TYPE_INPUT_IMAGE "Complex types not allowed for input image. Type: %d"
+#define PS_ERRORTEXT_pmMaskBadPixels_TYPE_MASK_IMAGE "Mask must be PS_TYPE_MASK type. Type: %d"
+#define PS_ERRORTEXT_pmMaskBadPixels_TYPE_MISMATCH "Input and flat image types differ: (%d vs %d)"
+#define PS_ERRORTEXT_pmMaskBadPixels_TYPE_UNSUPPORTED "Unsupported image datatype. Type: %d"
+//~End
+
+#endif
Index: /trunk/psModules/src/detrend/pmNonLinear.c
===================================================================
--- /trunk/psModules/src/detrend/pmNonLinear.c	(revision 5170)
+++ /trunk/psModules/src/detrend/pmNonLinear.c	(revision 5170)
@@ -0,0 +1,123 @@
+/** @file  pmNonLinear.c
+ *
+ *  Provides polynomial or table lookup non-linearity corrections to readouts.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ *  XXX: The SDR is silent about image types.  Only F32 was implemented.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include<stdio.h>
+#include<math.h>
+
+#include "pmNonLinear.h"
+
+/******************************************************************************
+pmNonLinearityLookup(): This routine will take an pmReadout image as input
+and a 1-D polynomial.  For each pixel in the input image, the polynomial will
+be evaluated at that pixels value, and the image pixel will then be set to
+that value.
+ *****************************************************************************/
+
+pmReadout *pmNonLinearityPolynomial(pmReadout *inputReadout,
+                                    const psPolynomial1D *input1DPoly)
+{
+    PS_ASSERT_PTR_NON_NULL(inputReadout, NULL);
+    PS_ASSERT_PTR_NON_NULL(inputReadout->image, NULL);
+    PS_ASSERT_IMAGE_TYPE(inputReadout->image, PS_TYPE_F32, NULL);
+    PS_ASSERT_PTR_NON_NULL(input1DPoly, NULL);
+
+    psS32 i;
+    psS32 j;
+
+    for (i=0;i<inputReadout->image->numRows;i++) {
+        for (j=0;j<inputReadout->image->numCols;j++) {
+            inputReadout->image->data.F32[i][j] = psPolynomial1DEval(input1DPoly, inputReadout->image->data.F32[i][j]);
+        }
+    }
+    return(inputReadout);
+}
+
+
+/******************************************************************************
+pmNonLinearityLookup(): This routine will take an pmReadout image as input
+and two input vectors, which constitute a lookup table.  For each pixel in
+the input image, that pixels value will be determined in the input vector
+inFluxe, and the corresponding value in outFlux.  The image pixel will then
+be set to the value from outFlux.
+ *****************************************************************************/
+pmReadout *pmNonLinearityLookup(pmReadout *inputReadout,
+                                const psVector *inFlux,
+                                const psVector *outFlux)
+{
+    PS_ASSERT_PTR_NON_NULL(inputReadout,NULL);
+    PS_ASSERT_PTR_NON_NULL(inputReadout->image,NULL);
+    PS_ASSERT_IMAGE_TYPE(inputReadout->image, PS_TYPE_F32, NULL);
+    PS_ASSERT_PTR_NON_NULL(inFlux,NULL);
+    if (inFlux->n < 2) {
+        psError(PS_ERR_UNKNOWN,true, "pmNonLinearityLookup(): input vector less than 2 elements.  Returning inputReadout image.");
+        return(inputReadout);
+    }
+    PS_ASSERT_PTR_NON_NULL(outFlux,NULL);
+    psS32 tableSize = inFlux->n;
+    if (inFlux->n != outFlux->n) {
+        tableSize = PS_MIN(inFlux->n, outFlux->n);
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: pmNonLinear.c: pmNonLinearityLookup(): input vectors have different sizes (%d, %d)\n", inFlux->n, outFlux->n);
+    }
+    PS_ASSERT_VECTOR_TYPE(inFlux, PS_TYPE_F32, NULL);
+    PS_ASSERT_VECTOR_TYPE(outFlux, PS_TYPE_F32, NULL);
+
+    psS32 i;
+    psS32 j;
+    psS32 binNum;
+    psScalar x;
+    psS32 numPixels = 0;
+    psF32 slope;
+
+    x.type.type = PS_TYPE_F32;
+    for (i=0;i<inputReadout->image->numRows;i++) {
+        for (j=0;j<inputReadout->image->numCols;j++) {
+            x.data.F32 = inputReadout->image->data.F32[i][j];
+            binNum = p_psVectorBinDisect((psVector *)inFlux, &x);
+
+            if (binNum == -2) {
+                // We get here if x is below the table lookup range.
+                inputReadout->image->data.F32[i][j] = outFlux->data.F32[0];
+                numPixels++;
+
+            } else if (binNum == -1) {
+                // We get here if x is above the table lookup range.
+                inputReadout->image->data.F32[i][j] = outFlux->data.F32[tableSize-1];
+                numPixels++;
+
+            } else if (binNum < -2) {
+                // We get here if there was some other problem.
+                psError(PS_ERR_UNKNOWN,true, "pmNonLinearityLookup(): Could not perform p_psVectorBinDisect().  Returning inputReadout image.");
+                return(inputReadout);
+                numPixels++;
+            } else {
+                // Perform linear interpolation.
+                slope = (outFlux->data.F32[binNum+1] - outFlux->data.F32[binNum]) /
+                        (inFlux->data.F32[binNum+1]  - inFlux->data.F32[binNum]);
+                inputReadout->image->data.F32[i][j] = outFlux->data.F32[binNum] +
+                                                      ((x.data.F32 - inFlux->data.F32[binNum]) * slope);
+            }
+        }
+    }
+    if (numPixels > 0) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: pmNonLinear.c: pmNonLinearityLookup(): %d pixels outside table.", numPixels);
+    }
+    return(inputReadout);
+}
Index: /trunk/psModules/src/detrend/pmNonLinear.h
===================================================================
--- /trunk/psModules/src/detrend/pmNonLinear.h	(revision 5170)
+++ /trunk/psModules/src/detrend/pmNonLinear.h	(revision 5170)
@@ -0,0 +1,27 @@
+/** @file  pmNonLinear.h
+ *
+ *  Provides polynomial or table lookup non-linearity corrections to readouts.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#if !defined(PM_NON_LINEAR_H)
+#define PM_NON_LINEAR_H
+
+#include "pslib.h"
+#include "pmAstrometry.h"
+
+pmReadout *pmNonLinearityPolynomial(pmReadout *in,
+                                    const psPolynomial1D *coeff);
+
+pmReadout *pmNonLinearityLookup(pmReadout *in,
+                                const psVector *inFlux,
+                                const psVector *outFlux);
+
+#endif
Index: /trunk/psModules/src/imcombine/Makefile.am
===================================================================
--- /trunk/psModules/src/imcombine/Makefile.am	(revision 5170)
+++ /trunk/psModules/src/imcombine/Makefile.am	(revision 5170)
@@ -0,0 +1,11 @@
+noinst_LTLIBRARIES = libpsmoduleimcombine.la
+
+libpsmoduleimcombine_la_CPPFLAGS = $(SRCINC) $(PSMODULE_CFLAGS)
+libpsmoduleimcombine_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+libpsmoduleimcombine_la_SOURCES  = pmImageCombine.c \
+    pmReadoutCombine.c
+
+psmoduleincludedir = $(includedir)
+psmoduleinclude_HEADERS = \
+    pmImageCombine.h \
+    pmReadoutCombine.h
Index: /trunk/psModules/src/imcombine/pmImageCombine.c
===================================================================
--- /trunk/psModules/src/imcombine/pmImageCombine.c	(revision 5170)
+++ /trunk/psModules/src/imcombine/pmImageCombine.c	(revision 5170)
@@ -0,0 +1,666 @@
+/** @file  pmImageCombine.c
+ *
+ *  This file will perform image combination of several images of the
+ *  same field, produce a list of questionable pixels, then tag some
+ *  of those pixels as cosmic rays.
+ *
+ *  @author Paul Price, IfA (original prototype)
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  XXX: pmRejectPixels() has a known bug with the pmImageTransform() call.
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <math.h>
+#include "pslib.h"
+
+//
+// The following macros define how big the initial pixel list will be, and
+// how much it should be incremented when realloc'ed.
+//
+#define PS_COMBINE_IMAGE_INITIAL_PIXEL_LIST_LENGTH 100
+#define PS_COMBINE_IMAGE_INITIAL_PIXEL_LIST_LENGTH_INC 100
+#define PS_COMBINE_IMAGE_MAX_QUESTIONABLE_PIXELS 1000
+/******************************************************************************
+pmCombineImages(combine, questionablePixels, images, errors, masks, maskVal,
+                pixels, numIter, sigmaClip, stats)
+ 
+XXX: Allocate a dummy psStats structure so that we don't destroy away its data.
+ *****************************************************************************/
+psImage *pmCombineImages(psImage *combine,              ///< Combined image (output)
+                         psArray **questionablePixels,  ///< Array of rejection masks
+                         const psArray *images,         ///< Array of input images
+                         const psArray *errors,         ///< Array of input error images
+                         const psArray *masks,          ///< Array of input masks
+                         psU32 maskVal,                 ///< Mask value
+                         const psPixels *pixels,        ///< Pixels to combine
+                         psS32 numIter,                 ///< Number of rejection iterations
+                         psF32 sigmaClip,               ///< Number of standard deviations at which to reject
+                         const psStats *stats           ///< Statistics to use in the combination
+                        )
+{
+
+    PS_ASSERT_PTR_NON_NULL(images, combine);
+    psU32 numImages = images->n;
+    psTrace("ImageCombine.pmCombineImages", 3, "Calling pmCombineImages(%d)\n", images->n);
+
+    if (errors != NULL) {
+        if (images->n != errors->n) {
+            psError(PS_ERR_UNKNOWN, true, "images and errors args must have same length (%d != %d)\n",
+                    images->n, errors->n);
+            return(combine);
+        }
+    }
+    if (masks != NULL) {
+        if (images->n != masks->n) {
+            psError(PS_ERR_UNKNOWN, true, "images and masks args must have same length (%d != %d)\n",
+                    images->n, masks->n);
+            return(combine);
+        }
+    }
+
+    psImage *tmpImg = (psImage *) images->data[0];
+    psU32 numRows = tmpImg->numRows;
+    psU32 numCols = tmpImg->numCols;
+
+    //
+    // Check that all images have the appropriate size and type.
+    //
+    for (psS32 i = 0 ; i < numImages ; i++) {
+        psImage *tmpDataImg = (psImage *) images->data[i];
+        PS_ASSERT_IMAGE_NON_NULL(tmpDataImg, combine);
+        PS_ASSERT_IMAGE_TYPE(tmpDataImg, PS_TYPE_F32, combine);
+        if ((tmpDataImg->numRows != numRows) || (tmpDataImg->numCols != numCols)) {
+            psError(PS_ERR_UNKNOWN, true, "image %d has size (%d, %d); should be (%d, %d).\n",
+                    i, tmpDataImg->numRows, tmpDataImg->numCols, numRows, numCols);
+        }
+
+        if (errors != NULL) {
+            psImage *tmpErrorImg = (psImage *) errors->data[i];
+            PS_ASSERT_IMAGE_NON_NULL(tmpErrorImg, combine);
+            PS_ASSERT_IMAGE_TYPE(tmpErrorImg, PS_TYPE_F32, combine);
+            PS_ASSERT_IMAGES_SIZE_EQUAL(tmpDataImg, tmpErrorImg, NULL);
+        }
+
+        if (masks != NULL) {
+            psImage *tmpMaskImg = (psImage *) masks->data[i];
+            PS_ASSERT_IMAGE_NON_NULL(tmpMaskImg, combine);
+            PS_ASSERT_IMAGE_TYPE(tmpMaskImg, PS_TYPE_U8, combine);
+            PS_ASSERT_IMAGES_SIZE_EQUAL(tmpDataImg, tmpMaskImg, NULL);
+        }
+    }
+    PS_ASSERT_PTR_NON_NULL(stats, combine);
+
+    // Allocate and initialize the combined image, if necessary.
+    if (combine == NULL) {
+        combine = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+        if (pixels != NULL) {
+            PS_IMAGE_SET_F32(combine, 0.0);
+        }
+    }
+
+    //
+    // Allocate the questionablePixels psArray, if necesssary, then create a psPixels
+    // struct for each image.
+    //
+    if (*questionablePixels == NULL) {
+        *questionablePixels = psArrayAlloc(numImages);
+    } else if ((*questionablePixels)->n != numImages) {
+        *questionablePixels = psArrayRealloc(*questionablePixels, numImages);
+    }
+    for (psS32 im = 0 ; im < numImages ; im++) {
+        psFree((*questionablePixels)->data[im]);
+        ((*questionablePixels)->data[im]) = (psPtr *) psPixelsAlloc(PS_COMBINE_IMAGE_INITIAL_PIXEL_LIST_LENGTH);
+        ((psPixels *) ((*questionablePixels)->data[im]))->n = 0;
+    }
+    //
+    // qpPtr is used to maintain a count of the questionable pixels for each image.
+    //
+    psVector *qpPtr = psVectorAlloc(numImages, PS_TYPE_S32);
+    PS_VECTOR_SET_S32(qpPtr, 0);
+
+    //
+    // Allocate the necessary psVectors for the call to psVectorStats().
+    // These vectors will be used whether we are combining a list of pixels,
+    // or every pixel in the input images.
+    //
+    psVector *pixelData = psVectorAlloc(numImages, PS_TYPE_F32);
+
+    psVector *pixelMask = NULL;
+    if (masks != NULL) {
+        pixelMask = psVectorAlloc(numImages, PS_TYPE_U8);
+        PS_VECTOR_SET_U8(pixelMask, 0);
+    }
+
+    psVector *pixelErrors = NULL;
+    if (errors != NULL) {
+        pixelErrors = psVectorAlloc(numImages, PS_TYPE_F32);
+        PS_VECTOR_SET_F32(pixelErrors, 1.0);
+    }
+
+    if (pixels != NULL) {
+        // Only those specified pixels should be combined.
+
+        psStats *stdevStats = psStatsAlloc(PS_STAT_SAMPLE_STDEV);
+
+        for (psS32 p = 0 ; p < pixels->n ; p++) {
+            // Must initialize the mask to 0 for every pixel.
+            PS_VECTOR_SET_U8(pixelMask, 0);
+            psS32 col = (pixels->data[p]).x;
+            psS32 row = (pixels->data[p]).y;
+
+            //
+            // Loop through each image, extract the pixel/mask/error data
+            // into psVectors.
+            //
+            for (psS32 im = 0 ; im < numImages ; im++) {
+                // Set the pixel data
+                pixelData->data.F32[im] =       ((psImage *) images->data[im])->data.F32[row][col];
+                // Set the pixel mask data, if necessary
+                if (masks != NULL) {
+                    pixelMask->data.U8[im] =   ((psImage *) masks->data[im])->data.F32[row][col];
+                }
+
+                // Set the pixel error data, if necessary
+                if (errors != NULL) {
+                    pixelErrors->data.F32[im] = ((psImage *) errors->data[im])->data.F32[row][col];
+                }
+            }
+
+            //
+            // Iterate on the pixels, rejecting outliers
+            //
+            for (psS32 iter = 0 ; iter < numIter ; iter++) {
+                // Combine all the pixels, using the specified stat.
+
+                stats = psVectorStats((psStats *) stats, pixelData, pixelErrors, pixelMask, maskVal);
+                psF64 combinedPixel;
+                psBool rc = p_psGetStatValue(stats, &combinedPixel);
+                if (rc != true) {
+                    psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not combine pixels (%d, %d) with the specified stat.\n", row, col);
+                }
+                if (iter == 0) {
+                    combine->data.F32[row][col] = (psF32) combinedPixel;
+                }
+
+                //
+                // Reject all pixels that lie more that sigmaClip standard deviations from
+                // the combined pixel value.
+                //
+                psS32 numRejects = 0;
+                for (psS32 im = 0 ; im < numImages ; im++) {
+                    stdevStats = psVectorStats(stdevStats, pixelData, pixelErrors, pixelMask, maskVal);
+                    psF64 stdev;
+                    psBool rc = p_psGetStatValue(stdevStats, &stdev);
+                    if (rc != true) {
+                        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not compute the standard deviation of pixel (%d, %d).\n", row, col);
+                        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not combine pixels (%d, %d) with the specified stat.\n", row, col);
+                    }
+
+                    if (!(pixelMask->data.U8[im] & maskVal)) {
+                        if (fabs(pixelData->data.F32[im]) >
+                                (fabs(sigmaClip * stdev) + fabs(combinedPixel))) {
+                            numRejects++;
+                            pixelMask->data.U8[im] = maskVal;
+                            //
+                            // XXX: These data structures indirections are getting complicated.
+                            //
+                            psS32 ptr = qpPtr->data.S32[im];
+                            psPixels *pixelListPtr = ((psPixels *) ((*questionablePixels)->data[im]));
+
+                            if (ptr >= pixelListPtr->nalloc) {
+                                (*questionablePixels)->data[im] =
+                                    (psPtr *) psPixelsRealloc(((psPixels *) ((*questionablePixels)->data[im])),
+                                                              ((((psPixels *) ((*questionablePixels)->data[im]))->nalloc) +
+                                                               PS_COMBINE_IMAGE_INITIAL_PIXEL_LIST_LENGTH_INC));
+                                // XXX: Can the realloc() fail?  Must we check for NULL?
+                            }
+                            ((psPixels *) ((*questionablePixels)->data[im]))->data[ptr].x = col;
+                            ((psPixels *) ((*questionablePixels)->data[im]))->data[ptr].y = row;
+                            (qpPtr->data.S32[im])++;
+                            // XXX: this pixel ->n increment is wierd
+                            ((psPixels *) ((*questionablePixels)->data[im]))->n = qpPtr->data.S32[im];
+                        }
+                    }
+                }
+
+                //
+                // If the number of rejected pixels is zero, then there's no point
+                // continuing the loop.
+                //
+                if (numRejects == 0) {
+                    break;
+                }
+                psS32 totalRejects = 0;
+                for (psS32 im = 0 ; im < numImages ; im++) {
+                    if (pixelMask->data.U8[im] & maskVal) {
+                        totalRejects++;
+                    }
+                }
+
+                //
+                // XXX: Is it possible to have all pixels rejected?  If so, we should
+                // exit the loop.
+                //
+                if (totalRejects == numImages) {
+                    break;
+                }
+            }
+        }
+        psFree(stdevStats);
+    } else {
+        //
+        // We get here if there is a NULL list of pixels to combine.
+        // Therefore, we combine all pixels in all images.
+        //
+
+        //
+        // Loop over all pixels in all images, set the appropriate data, mask,
+        // error vectors, call psVectorStats(), and set the result in the
+        // combine image.
+        //
+        for (psS32 row = 0 ; row < numRows ; row++) {
+            for (psS32 col = 0 ; col < numCols ; col++) {
+                for (psS32 im = 0 ; im < numImages ; im++) {
+                    // Set the pixel data
+                    pixelData->data.F32[im] =       ((psImage *) images->data[im])->data.F32[row][col];
+
+                    // Set the pixel mask data, if necessary
+                    if (masks != NULL) {
+                        pixelMask->data.U8[im] =   ((psImage *) masks->data[im])->data.F32[row][col];
+                    }
+
+                    // Set the pixel error data, if necessary
+                    if (errors != NULL) {
+                        pixelErrors->data.F32[im] = ((psImage *) errors->data[im])->data.F32[row][col];
+                    }
+
+                }
+                // Combine all the pixels, using the specified stat.
+                stats = psVectorStats((psStats *) stats, pixelData, pixelErrors, pixelMask, maskVal);
+                psF64 tmpF64;
+                psBool rc = p_psGetStatValue(stats, &tmpF64);
+                combine->data.F32[row][col] = (psF32) tmpF64;
+                if (rc != true) {
+                    psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not combine pixels (%d, %d) with the specified stat.\n", row, col);
+                }
+            }
+        }
+    }
+
+    psFree(pixelData);
+    psFree(pixelMask);
+    psFree(pixelErrors);
+    psFree(qpPtr);
+
+    psTrace("ImageCombine.pmCombineImages", 3, "Exiting pmCombineImages(%d)\n", images->n);
+    return(combine);
+}
+
+
+/******************************************************************************
+XXX: Directly from Paul Price
+ *****************************************************************************/
+static psF32 CalcGradient(
+    psImage *image,
+    psImage *imageMask,
+    psS32 x,
+    psS32 y
+)
+{
+    psTrace("ImageCombine.CalcGradient", 4, "Calling CalcGradient(%d, %d)\n", x, y);
+    int num = 0;
+    psVector *pixels = psVectorAlloc(8, PS_TYPE_F32); // Array of pixels
+    psVector *mask = psVectorAlloc(8, PS_TYPE_U8); // Corresponding mask
+
+    // Get limits
+    int xMin = PS_MAX(x - 1, 0);
+    int xMax = PS_MIN(x + 1, image->numCols - 1);
+    int yMin = PS_MAX(y - 1, 0);
+    int yMax = PS_MIN(y + 1, image->numRows - 1);
+    if (imageMask != NULL) {
+        for (int j = yMin; j <= yMax; j++) {
+            for (int i = xMin; i <= xMax; i++) {
+                if ((i != x) && (j != y) && (0 == imageMask->data.U8[j][i])) {
+                    pixels->data.F32[num] = image->data.F32[j][i];
+                    mask->data.U8[num] = 0;
+                    num++;
+                } else {
+                    mask->data.U8[num] = 1;
+                }
+            }
+        }
+    } else {
+        //
+        // This code is simply the previous loop without the imageMask.
+        // XXX: Consider restructuring this.
+        //
+        for (int j = yMin; j <= yMax; j++) {
+            for (int i = xMin; i <= xMax; i++) {
+                if ((i != x) && (j != y)) {
+                    pixels->data.F32[num] = image->data.F32[j][i];
+                    mask->data.U8[num] = 0;
+                    num++;
+                } else {
+                    mask->data.U8[num] = 1;
+                }
+            }
+        }
+    }
+
+    pixels->n = num;
+    mask->n = num;
+
+    // Get the median
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
+    (void)psVectorStats(stats, pixels, NULL, mask, 1);
+    float median = stats->sampleMedian;
+    psFree(stats);
+    psFree(pixels);
+    psFree(mask);
+
+    psTrace("ImageCombine.CalcGradient", 4, "Exiting CalcGradient(%d, %d)\n", x, y);
+    return(median / image->data.F32[y][x]);
+}
+
+/******************************************************************************
+DetermineRegion(image, myOutToIn): for a psImage and a psPlaneTransform to that
+image, this routine determines the size of the input image which maps to that
+image, and returns the result in a psRegion struct.
+ 
+XXX: Basically, this routine is only guaranteed to work if the transform is
+linear.
+ 
+XXX: Shouldn't this functionality be part of psImageTransform()?
+ *****************************************************************************/
+static psRegion DetermineRegion(psImage *image,
+                                psPlaneTransform *myOutToIn)
+{
+    psTrace("ImageCombine.DetermineRegion", 4, "Calling DetermineRegion()\n");
+    psRegion myRegion;
+    myRegion.x0 = PS_MAX_F32;
+    myRegion.x1 = PS_MIN_F32;
+    myRegion.y0 = PS_MAX_F32;
+    myRegion.y1 = PS_MIN_F32;
+    psPlane in;
+    psPlane out;
+
+    in.x = 0.0;
+    in.y = 0.0;
+
+    psPlaneTransformApply(&out, myOutToIn, &in);
+    if (out.x < myRegion.x0) {
+        myRegion.x0 = out.x;
+    }
+    if (out.x > myRegion.x1) {
+        myRegion.x1 = out.x;
+    }
+    if (out.y < myRegion.y0) {
+        myRegion.y0 = out.y;
+    }
+    if (out.y > myRegion.y1) {
+        myRegion.y1 = out.y;
+    }
+
+    in.x = (psF32) (image->numCols);
+    in.y = 0.0;
+    psPlaneTransformApply(&out, myOutToIn, &in);
+    if (out.x < myRegion.x0) {
+        myRegion.x0 = out.x;
+    }
+    if (out.x > myRegion.x1) {
+        myRegion.x1 = out.x;
+    }
+    if (out.y < myRegion.y0) {
+        myRegion.y0 = out.y;
+    }
+    if (out.y > myRegion.y1) {
+        myRegion.y1 = out.y;
+    }
+
+    in.x = (psF32) (image->numCols);
+    ;
+    in.y = 0.0;
+    psPlaneTransformApply(&out, myOutToIn, &in);
+    if (out.x < myRegion.x0) {
+        myRegion.x0 = out.x;
+    }
+    if (out.x > myRegion.x1) {
+        myRegion.x1 = out.x;
+    }
+    if (out.y < myRegion.y0) {
+        myRegion.y0 = out.y;
+    }
+    if (out.y > myRegion.y1) {
+        myRegion.y1 = out.y;
+    }
+
+    in.x = (psF32) (image->numCols);
+    in.y = (psF32) (image->numRows);
+    psPlaneTransformApply(&out, myOutToIn, &in);
+    if (out.x < myRegion.x0) {
+        myRegion.x0 = out.x;
+    }
+    if (out.x > myRegion.x1) {
+        myRegion.x1 = out.x;
+    }
+    if (out.y < myRegion.y0) {
+        myRegion.y0 = out.y;
+    }
+    if (out.y > myRegion.y1) {
+        myRegion.y1 = out.y;
+    }
+
+    psTrace("ImageCombine.DetermineRegion", 4, "Exiting DetermineRegion()\n");
+    return(myRegion);
+}
+
+/******************************************************************************
+XXX: Don't we have a psLib function for this?
+ *****************************************************************************/
+static psImage *ImageConvertF32(psImage *image)
+{
+    psTrace("ImageCombine.ImageConvertF32", 4, "Calling ImageConvertF32()\n");
+    psImage *imgF32 = psImageAlloc(image->numCols, image->numRows, PS_TYPE_F32);
+
+    for (psS32 i = 0 ; i < image->numRows ; i++) {
+        for (psS32 j = 0 ; j < image->numCols ; j++) {
+            imgF32->data.F32[i][j] = (psF32) image->data.U8[i][j];
+        }
+    }
+
+    psTrace("ImageCombine.ImageConvertF32", 4, "Exiting ImageConvertF32()\n");
+    return(imgF32);
+}
+
+
+//
+// The following macros define how big the initial pixel list will be, and
+// how much it should be incremented when realloc'ed.
+//
+#define PS_REJECT_PIXEL_INITIAL_PIXEL_LIST_LENGTH 100
+#define PS_REJECT_PIXEL_INITIAL_PIXEL_LIST_LENGTH_INC 100
+/******************************************************************************
+pmRejectPixels(images, errors, inToOut, outToIn, rejThreshold,
+gradLimit)
+ 
+XXX: Optimization: we don't need to transform the entire mask image.
+XXX: The inToOut and outToIn transforms are confusing.  Verify that what
+     I think they mean syncs with PWP.
+ *****************************************************************************/
+psArray *pmRejectPixels(
+    const psArray *images,              ///< Array of input images
+    const psArray *masks,               ///< Array of input image masks
+    const psArray *errors,              ///< The pixels which were rejected in the combination
+    const psArray *inToOut,             ///< Transformation from input to output system
+    const psArray *outToIn,             ///< Transformation from output to input system
+    psF32 rejThreshold,                 ///< Rejection threshold
+    psF32 gradLimit                     ///< Gradient limit
+)
+{
+    psTrace("ImageCombine.pmRejectPixels", 3, "Calling pmRejectPixels()\n");
+    PS_ASSERT_PTR_NON_NULL(images, NULL);
+    for (psS32 im = 0 ; im < images->n ; im++) {
+        psImage *tmpImage = (psImage *) images->data[im];
+        PS_ASSERT_IMAGE_NON_NULL(tmpImage, NULL);
+        PS_ASSERT_IMAGE_NON_EMPTY(tmpImage, NULL);
+        PS_ASSERT_IMAGE_TYPE(tmpImage, PS_TYPE_F32, NULL);
+        if (masks != NULL) {
+            PS_ASSERT_INT_EQUAL(images->n, masks->n, NULL);
+            psImage *tmpMask = (psImage *) masks->data[im];
+            PS_ASSERT_IMAGE_NON_NULL(tmpMask, NULL);
+            PS_ASSERT_IMAGE_NON_EMPTY(tmpMask, NULL);
+            PS_ASSERT_IMAGE_TYPE(tmpMask, PS_TYPE_F32, NULL);
+            PS_ASSERT_IMAGES_SIZE_EQUAL(tmpImage, tmpMask, NULL);
+        }
+        PS_ASSERT_IMAGES_SIZE_EQUAL(((psImage *) images->data[0]), tmpImage, NULL);
+    }
+    PS_ASSERT_PTR_NON_NULL(errors, NULL);
+    PS_ASSERT_PTR_NON_NULL(inToOut, NULL);
+    PS_ASSERT_PTR_NON_NULL(outToIn, NULL);
+    // Ensure that the psArray parameters have an element for each image.
+    psS32 numImages = images->n;
+    PS_ASSERT_INT_EQUAL(numImages, errors->n, NULL);
+    PS_ASSERT_INT_EQUAL(numImages, inToOut->n, NULL);
+    PS_ASSERT_INT_EQUAL(numImages, outToIn->n, NULL);
+
+    //
+    // Create the psArray of psPixelLists, one for each image, for rejected pixels.
+    //
+    psArray *rejects = psArrayAlloc(numImages);
+    for (psS32 im = 0 ; im < numImages ; im++) {
+        rejects->data[im] = (psPtr *) psPixelsAlloc(PS_REJECT_PIXEL_INITIAL_PIXEL_LIST_LENGTH);
+        psPixels *pixels = (psPixels *) rejects->data[im];
+        pixels->n = 0;
+    }
+    //
+    // rPtr is used to maintain a count of the questionable pixels for each image.
+    //
+    psVector *rPtr = psVectorAlloc(numImages, PS_TYPE_S32);
+    PS_VECTOR_SET_S32(rPtr, 0);
+
+    psS32 numCols = ((psImage *) images->data[0])->numCols;
+    psS32 numRows = ((psImage *) images->data[0])->numRows;
+    psRegion myRegion = psRegionSet(0, numCols-1, 0, numRows-1);
+    psU32 maskVal = 1;  // XXX: Is this appropriate?
+
+    psPlane *inCoords = psAlloc(sizeof(psPlane));
+    psPlane *outCoords = psAlloc(sizeof(psPlane));
+
+    for (psS32 im = 0 ; im < numImages ; im++) {
+        //
+        // Extract data from psArrays.
+        //
+        psPixels *pixelList = (psPixels *) errors->data[im];
+
+        psImage *currImage = (psImage *) images->data[im];
+        myRegion.x0 = 0;
+        myRegion.x1 = currImage->numCols;
+        myRegion.y0 = 0;
+        myRegion.y1 = currImage->numRows;
+        psPlaneTransform *myInToOut = (psPlaneTransform *) inToOut->data[im];
+        psPlaneTransform *myOutToIn = (psPlaneTransform *) outToIn->data[im];
+
+        //
+        // Create a psU8 mask image from the list of cosmic pixels.
+        //
+        psImage *maskImage = NULL;
+        maskImage = psPixelsToMask(maskImage, pixelList, myRegion, maskVal);
+        psImage *maskImageF32 = ImageConvertF32(maskImage);
+
+        //
+        // Transform that mask image into detector coordinate space
+        //
+        psRegion myRegionXForm = DetermineRegion(maskImageF32, myOutToIn);
+        psImage *transformedImage = psImageTransform(NULL, NULL, maskImageF32, NULL,
+                                    0, myOutToIn, myRegionXForm, NULL,
+                                    PS_INTERPOLATE_BILINEAR, 0);
+
+        //
+        // Loop over all cosmic pixels.  Transform their coords to detector space.
+        // If the value of the transformed mask is larger than rejThreshold, then
+        // this might be a cosmic ray pixel.  We then calculate the mean gradient
+        // in other images.
+        //
+        for (psS32 p = 0 ; p < pixelList->n ; p++) {
+            inCoords->x = 0.5 + (psF32) (pixelList->data[p]).x;
+            inCoords->y = 0.5 + (psF32) (pixelList->data[p]).y;
+            psPlaneTransformApply(outCoords, myInToOut, inCoords);
+            psF32 maskVal = (psF32) psImagePixelInterpolate(transformedImage, outCoords->x, outCoords->y,
+                            NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+            if (maskVal > rejThreshold) {
+
+                // This is a possible cosmic array pixel.  We must calculate the gradient
+                // at this location in all input images.
+                psF32 meanGrads = 0.0;
+                psS32 numGrads = 0;
+                //
+                // Loop through all other images, calculate their mean gradient.
+                //
+                for (psS32 otherImg = 0 ; otherImg < numImages ; otherImg++) {
+                    if (im != otherImg) {
+                        // Map the outCoords to inCoords that for otherImg space.
+                        psImage *tmpMask = NULL;
+                        if (masks != NULL) {
+                            tmpMask = masks->data[otherImg];
+                        }
+                        psPlaneTransformApply(inCoords,
+                                              (psPlaneTransform * )outToIn->data[otherImg],
+                                              outCoords);
+                        psS32 xPix = (int)(inCoords->x + 0.5);
+                        psS32 yPix = (int)(inCoords->y + 0.5);
+                        if ((xPix >= 0) && (xPix <= ((psImage*)(images->data[otherImg]))->numCols - 1) &&
+                                (yPix >= 0) && (yPix <= ((psImage*)(images->data[otherImg]))->numRows - 1)) {
+                            meanGrads += CalcGradient(images->data[otherImg], tmpMask, xPix, yPix);
+                            numGrads++;
+                        }
+                    }
+                }
+                if (numGrads > 0) {
+                    meanGrads /= (psF32) numGrads;
+                } else {
+                    // XXX: my idea.  Verify with PWP:
+                    meanGrads = 1.0 + gradLimit;
+                }
+
+                // XXX: The SDRS and the prototype code differ significantly here:
+                // if (CalcGradient(inputs->data[i], pixelList->data.x, pixelList->data.y) < (gradLimit * meanGrads)) {
+                if (meanGrads < gradLimit) {
+                    //
+                    // Add this to the list of questionable pixels.  We must ensure that the
+                    // pixelList is large enough; if not, we realloc()
+                    //
+                    psS32 ptr = rPtr->data.S32[im];
+                    psPixels *pixelListPtr = (psPixels *) rejects->data[im];
+                    if (ptr >= pixelListPtr->nalloc) {
+                        rejects->data[im] = (psPtr *) psPixelsRealloc(((psPixels *) rejects->data[im]),
+                                            ((((psPixels *) rejects->data[im])->nalloc) + PS_REJECT_PIXEL_INITIAL_PIXEL_LIST_LENGTH_INC));
+                        // XXX: Can the realloc() fail?  Must we check for NULL?
+                    }
+
+                    ((psPixels *) rejects->data[im])->data[ptr].x = (pixelList->data[p]).x;
+                    ((psPixels *) rejects->data[im])->data[ptr].y = (pixelList->data[p]).y;
+                    (rPtr->data.S32[im])++;
+                    // XXX: this pixel ->n increment is wierd
+                    (((psPixels *) rejects->data[im])->n)++;
+                }
+            }
+        }
+
+        psFree(maskImage);
+        psFree(maskImageF32);
+        psFree(transformedImage);
+    }
+
+    psFree(inCoords);
+    psFree(outCoords);
+    psTrace("ImageCombine.pmRejectPixels", 3, "Exiting pmRejectPixels()\n");
+    return(rejects);
+}
Index: /trunk/psModules/src/imcombine/pmImageCombine.h
===================================================================
--- /trunk/psModules/src/imcombine/pmImageCombine.h	(revision 5170)
+++ /trunk/psModules/src/imcombine/pmImageCombine.h	(revision 5170)
@@ -0,0 +1,50 @@
+/** @file  pmImageCombine.h
+ *
+ *  This file will perform image combination of several images of the
+ *  same field, produce a list of questionable pixels, then tag some
+ *  of those pixels as cosmic rays.
+ *
+ *  @author Paul Price, IfA (original prototype)
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#if !defined(PM_IMAGE_COMBINE_H)
+#define PM_IMAGE_COMBINE_H
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include<stdio.h>
+#include<math.h>
+#include "pslib.h"
+
+psImage *pmCombineImages(
+    psImage *combine,                   ///< Combined image (output)
+    psArray **questionablePixels,       ///< Array of rejection masks
+    const psArray *images,              ///< Array of input images
+    const psArray *errors,              ///< Array of input error images
+    const psArray *masks,               ///< Array of input masks
+    psU32 maskVal,                      ///< Mask value
+    const psPixels *pixels,             ///< Pixels to combine
+    psS32 numIter,                      ///< Number of rejection iterations
+    psF32 sigmaClip,                    ///< Number of standard deviations at which to reject
+    const psStats *stats                ///< Statistics to use in the combination
+);
+
+psArray *pmRejectPixels(
+    const psArray *images,              ///< Array of input images
+    const psArray *masks,               ///< Array of input image masks
+    const psArray *errors,              ///< The pixels which were rejected in the combination
+    const psArray *inToOut,             ///< Transformation from input to output system
+    const psArray *outToIn,             ///< Transformation from output to input system
+    psF32 rejThreshold,                 ///< Rejection threshold
+    psF32 gradLimit                     ///< Gradient limit
+);
+#endif
Index: /trunk/psModules/src/imcombine/pmReadoutCombine.c
===================================================================
--- /trunk/psModules/src/imcombine/pmReadoutCombine.c	(revision 5170)
+++ /trunk/psModules/src/imcombine/pmReadoutCombine.c	(revision 5170)
@@ -0,0 +1,375 @@
+/** @file  pmReadoutCombine.c
+ *
+ *  This file will contain a module which will combine multiple readout images.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include<stdio.h>
+#include<math.h>
+#include "pslib.h"
+#include "pmReadoutCombine.h"
+
+/******************************************************************************
+DetermineNumBits(data): This routine takes an enum psStatsOptions as an
+argument and returns the number of non-zero bits.
+ *****************************************************************************/
+static psS32 DetermineNumBits(psStatsOptions data)
+{
+    psS32 i;
+    psU64 tmpData = data;
+    psS32 numBits = 0;
+
+    for (i=0;i<(8 * sizeof(psStatsOptions));i++) {
+        if (0x0001 & tmpData) {
+            numBits++;
+        }
+        tmpData = tmpData >> 1;
+    }
+    return(numBits);
+}
+
+/******************************************************************************
+XXX: Must add support for S16 and S32 types.  F32 currently supported.
+ *****************************************************************************/
+psImage *pmReadoutCombine(psImage *output,
+                          const psList *inputs,
+                          psCombineParams *params,
+                          const psVector *zero,
+                          const psVector *scale,
+                          bool applyZeroScale,
+                          psF32 gain,
+                          psF32 readnoise)
+{
+    PS_ASSERT_PTR_NON_NULL(inputs, NULL);
+    PS_ASSERT_PTR_NON_NULL(params, NULL);
+    PS_ASSERT_PTR_NON_NULL(params->stats, NULL);
+    if (zero != NULL) {
+        PS_ASSERT_VECTOR_TYPE(zero, PS_TYPE_F32, NULL);
+        //        PS_ASSERT_VECTOR_TYPE_S16_S32_F32(zero, NULL);
+    }
+    if (scale != NULL) {
+        PS_ASSERT_VECTOR_TYPE(scale, PS_TYPE_F32, NULL);
+        //        PS_ASSERT_VECTOR_TYPE_S16_S32_F32(scale, NULL);
+    }
+    if ((zero != NULL) && (scale != NULL)) {
+        PS_ASSERT_VECTOR_TYPE_EQUAL(zero, scale, NULL);
+        // PS_ASSERT_VECTOR_TYPE_S16_S32_F32(scale, NULL);
+    }
+
+    psStats *stats = params->stats;
+    psS32 i;
+    psS32 j;
+    psS32 maxInputCols = 0;
+    psS32 maxInputRows = 0;
+    psS32 minInputCols = PS_MAX_S32;
+    psS32 minInputRows = PS_MAX_S32;
+    psListElem *tmpInput = NULL;
+    pmReadout *tmpReadout = NULL;
+    psS32 numInputs = 0;
+    psS32 tmpI;
+    psElemType outputType = PS_TYPE_F32;
+
+    if (1 < DetermineNumBits(params->stats->options)) {
+        psError(PS_ERR_UNKNOWN, true,
+                "Multiple statistical options have been requested.  Returning NULL.\n");
+        return(NULL);
+    }
+
+    //
+    // We step through each readout in the input image list.  If any readout is
+    // NULL, empty, or has the wrong type, we generate an error and return
+    // NULL.  We determine how big of an output image is needed to combine
+    // these input images.  We do this by taking the
+    //     max(readout->col0 + readout->numCols + image->col0 + image->numCols)
+    //     max(readout->row0 + readout->numRows + image->row0 + image->numRows)
+    // We then compare that to
+    //     output->col0 + output->numCols
+    //     output->row0 + output->numRows
+    // to determine if the output image actually stores that pixel.  A similar
+    // thing is done for the minimum row and column.
+    //
+    tmpInput = (psListElem *) inputs->head;
+    while (NULL != tmpInput) {
+        tmpReadout = (pmReadout *) tmpInput->data;
+        PS_ASSERT_READOUT_NON_NULL(tmpReadout, output);
+        PS_ASSERT_READOUT_NON_EMPTY(tmpReadout, output);
+        PS_ASSERT_READOUT_TYPE(tmpReadout, PS_TYPE_F32, output);
+
+        outputType = tmpReadout->image->type.type;
+
+        minInputRows = PS_MIN(minInputRows,
+                              (tmpReadout->row0 + tmpReadout->image->row0));
+        tmpI = tmpReadout->row0 +
+               tmpReadout->image->row0 +
+               tmpReadout->image->numRows;
+        maxInputRows = PS_MAX(maxInputRows, tmpI);
+
+        minInputCols = PS_MIN(minInputCols,
+                              (tmpReadout->col0 + tmpReadout->image->col0));
+        tmpI = tmpReadout->col0 +
+               tmpReadout->image->col0 +
+               tmpReadout->image->numCols;
+        maxInputCols = PS_MAX(maxInputCols, tmpI);
+        tmpInput = tmpInput->next;
+        numInputs++;
+    }
+
+    // We ensure that the zero vector is of the proper size.
+    if (zero != NULL) {
+        PS_ASSERT_VECTOR_TYPE(zero, PS_TYPE_F32, NULL);
+        if (numInputs > zero->n) {
+            psError(PS_ERR_UNKNOWN, true, "zero vector has incorrect size (%d).  Returning NULL.\n", zero->n);
+            return(NULL);
+        } else if (numInputs < zero->n) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: the zero vector too many elements (%d)\n", zero->n);
+        }
+    }
+
+    // We ensure that the scale vector is of the proper size.
+    if (scale != NULL) {
+        PS_ASSERT_VECTOR_TYPE(scale, PS_TYPE_F32, NULL);
+        if (numInputs > scale->n) {
+            psError(PS_ERR_UNKNOWN, true, "scale vector has incorrect size (%d).  Returning NULL.\n", scale->n);
+            return(NULL);
+        } else if (numInputs < scale->n) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: the scale vector has too many elements (%d)\n", scale->n);
+        }
+    }
+
+    // At this point, the following variables have been computed:
+    // maxInputRows: the largest input row value, in output image space.
+    // maxInputCols: the largest input column value, in output image space.
+    // minInputRows: the smallest input row value, in output image space.
+    // minInputCols: the smallest input column value, in output image space.
+    //
+    if (output == NULL) {
+        output = psImageAlloc(maxInputCols-minInputCols,
+                              maxInputRows-minInputRows, outputType);
+        *(psS32 *) &(output->col0) = minInputCols;
+        *(psS32 *) &(output->row0) = minInputRows;
+    } else {
+        PS_ASSERT_IMAGE_TYPE(output, PS_TYPE_F32, NULL);
+        if (((output->col0 + output->numCols) < maxInputCols) ||
+                ((output->row0 + output->numRows) < maxInputRows)) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "Output image (%d, %d) is too small to hold combined images.  Returning NULL.\n",
+                    output->row0 + output->numRows,
+                    output->col0 + output->numCols);
+            return(NULL);
+        }
+
+        if ((output->col0 > minInputCols) || (output->row0 > minInputRows)) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "Output image offset is larger then input image offset.  Returning NULL.\n");
+            return(NULL);
+        }
+    }
+
+    psVector *tmpPixels = psVectorAlloc(numInputs, PS_TYPE_F32);
+    psVector *tmpPixelErrors = psVectorAlloc(numInputs, PS_TYPE_F32);
+    psVector *tmpPixelMask = psVectorAlloc(numInputs, PS_TYPE_U8);
+    psVector *tmpPixelMaskNKeep = psVectorAlloc(numInputs, PS_TYPE_U8);
+    psVector *outRowLower = psVectorAlloc(numInputs, PS_TYPE_U32);
+    psVector *outRowUpper = psVectorAlloc(numInputs, PS_TYPE_U32);
+    psVector *outColLower = psVectorAlloc(numInputs, PS_TYPE_U32);
+    psVector *outColUpper = psVectorAlloc(numInputs, PS_TYPE_U32);
+    pmReadout **tmpReadouts = (pmReadout **) psAlloc(numInputs * sizeof(pmReadout *));
+
+    // For each input readout, we create a pointer to that readout in
+    // "tmpReadouts[]", and we store the min/max pixel indices for that
+    // readout, in output image coordinates, in the psVectors
+    // (outRowLower, outColLower, outRowUpper, outColUpper).
+    i = 0;
+    tmpInput = (psListElem *) inputs->head;
+    while (NULL != tmpInput) {
+        tmpReadouts[i] = (pmReadout *) tmpInput->data;
+        outRowLower->data.U32[i] = tmpReadouts[i]->row0 + tmpReadouts[i]->image->row0;
+        outColLower->data.U32[i] = tmpReadouts[i]->col0 + tmpReadouts[i]->image->col0;
+        outRowUpper->data.U32[i] = tmpReadouts[i]->row0 +
+                                   tmpReadouts[i]->image->row0 +
+                                   tmpReadouts[i]->image->numRows;
+        outColUpper->data.U32[i] = tmpReadouts[i]->col0 +
+                                   tmpReadouts[i]->image->col0 +
+                                   tmpReadouts[i]->image->numCols;
+        tmpInput = tmpInput->next;
+        i++;
+    }
+
+    // We loop through each pixel in the output image.  We loop through each
+    // input readout.  We determine if that output pixel is contained in the
+    // image from that readout.  If so, we save it in psVector tmpPixels.
+    // If not, we set a mask for that element in tmpPixels.  Then, we mask off
+    // pixels not between fracLow and fracHigh.  Then we call the vector
+    // stats routine on those pixels/mask.  Then we set the output pixel value
+    // to the result of the stats call.
+
+    for (i = output->row0; i < (output->row0 + output->numRows) ; i++) {
+        for (j = output->col0; j < (output->col0 + output->numCols) ; j++) {
+            for (psS32 r = 0; r < numInputs ; r++) {
+                //  printf("[%d][%d]: [%d][%d] to [%d][%d]\n", i, j, outRowLower->data.U32[r], outColLower->data.U32[r], outRowUpper->data.U32[r], outColUpper->data.U32[r]);
+                if ((outRowLower->data.U32[r] <= i) &&
+                        (outColLower->data.U32[r] <= j) &&
+                        (outRowUpper->data.U32[r] > i) &&
+                        (outColUpper->data.U32[r] > j)) {
+
+                    psS32 imageRow = i - (tmpReadouts[r]->row0 +
+                                          tmpReadouts[r]->image->row0);
+                    psS32 imageCol = j - (tmpReadouts[r]->col0 +
+                                          tmpReadouts[r]->image->col0);
+
+                    if ((NULL == tmpReadouts[r]->mask) ||
+                            !(params->maskVal && tmpReadouts[r]->mask->data.U8[imageRow][imageCol])) {
+                        tmpPixels->data.F32[r] = tmpReadouts[r]->image->data.F32[imageRow][imageCol];
+                        tmpPixelMask->data.U8[r] = 0;
+                    } else {
+                        tmpPixels->data.F32[r] = 0.0;
+                        tmpPixelMask->data.U8[r] = 1;
+                    }
+                } else {
+                    tmpPixels->data.F32[r] = 0.0;
+                    tmpPixelMask->data.U8[r] = 1;
+                }
+                // printf("readout[%d], image [%d][%d] is %f\n", r, i, j, tmpPixels->data.F32[r]);
+            }
+            // At this point, we have scanned all input readouts for this
+            // one output pixel.
+            //            for (psS32 r = 0; r < numInputs ; r++) printf("(0)tmpPixels->data.F32[%d] is %f\n", r, tmpPixels->data.F32[r]);
+
+            // Determine how many pixels lie between fracLow and fracHigh.
+            psS32 pixelCount = 0;
+            for (psS32 r = 0; r < numInputs ; r++) {
+                if (tmpPixelMask->data.U8[r] == 0) {
+                    if ((params->fracLow <= tmpPixels->data.F32[r]) &&
+                            (params->fracHigh >= tmpPixels->data.F32[r])) {
+                        pixelCount++;
+                    }
+                }
+            }
+
+            // If more than params->nKeep pixels lie between the valid range,
+            // then loop through the pixels, and mask away any pixels outside
+            // that range.
+            if (pixelCount >= params->nKeep) {
+                for (psS32 r = 0; r < numInputs ; r++) {
+                    if (tmpPixelMask->data.U8[r] == 0) {
+                        if ((params->fracLow <= tmpPixels->data.F32[r]) &&
+                                (params->fracHigh >= tmpPixels->data.F32[r])) {
+                            tmpPixelMaskNKeep->data.U8[r] = 0;
+                        } else {
+                            tmpPixelMaskNKeep->data.U8[r] = 1;
+                        }
+                    }
+                }
+            }
+
+            if ((gain > 0.0) && (readnoise >= 0.0)) {
+                psF32 x;
+                psF32 sigma;
+                if (applyZeroScale == true) {
+                    for (psS32 r = 0; r < numInputs ; r++) {
+                        if (zero != NULL) {
+                            x = zero->data.F32[r];
+                        } else {
+                            x = 0.0;
+                        }
+                        if (scale != NULL) {
+                            x+= tmpPixels->data.F32[r] * scale->data.F32[r];
+                        } else {
+                            x+= tmpPixels->data.F32[r];
+                        }
+                        sigma = sqrtf((readnoise*readnoise) + gain * x) / gain;
+
+                        tmpPixelErrors->data.F32[r] = sigma;
+                        tmpPixels->data.F32[r]= x;
+                    }
+                } else {
+                    for (psS32 r = 0; r < numInputs ; r++) {
+                        x= tmpPixels->data.F32[r];
+
+                        if (zero != NULL) {
+                            sigma = zero->data.F32[r];
+                        } else {
+                            sigma = 0.0;
+                        }
+                        if (scale != NULL) {
+                            sigma+= tmpPixels->data.F32[r] * scale->data.F32[r];
+                        } else {
+                            sigma+= tmpPixels->data.F32[r];
+                        }
+                        sigma = sqrtf((readnoise*readnoise) + (gain * sigma)) / gain;
+
+                        tmpPixelErrors->data.F32[r] = sigma;
+                        tmpPixels->data.F32[r]= x;
+                    }
+                }
+                // Calculate the specified statistic on the stack of pixels.
+                //                for (psS32 r = 0; r < numInputs ; r++) printf("(1)tmpPixels->data.F32[%d] is %f\n", r, tmpPixels->data.F32[r]);
+                psStats *rc = psVectorStats(stats,
+                                            tmpPixels,
+                                            tmpPixelErrors,
+                                            tmpPixelMaskNKeep,
+                                            1);
+                if (rc == NULL) {
+                    psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning NULL.\n");
+                    return(NULL);
+                }
+            } else {
+                if (scale != NULL) {
+                    for (psS32 r = 0; r < numInputs ; r++) {
+                        tmpPixels->data.F32[r]*= scale->data.F32[r];
+                    }
+                }
+
+                // We add the zero vector, if non-NULL.
+                if (zero != NULL) {
+                    for (psS32 r = 0; r < numInputs ; r++) {
+                        tmpPixels->data.F32[r]+= zero->data.F32[r];
+                    }
+                }
+
+                // Calculate the specified statistic on the stack of pixels.
+                //                for (psS32 r = 0; r < numInputs ; r++) printf("(2)tmpPixels->data.F32[%d] is %f\n", r, tmpPixels->data.F32[r]);
+                psStats *rc = psVectorStats(stats,
+                                            tmpPixels,
+                                            NULL,
+                                            tmpPixelMaskNKeep,
+                                            1);
+                if (rc == NULL) {
+                    psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning NULL.\n");
+                    return(NULL);
+                }
+            }
+
+
+            // Set the pixel value in the output image to the stat value.
+            double statValue;
+            if (!p_psGetStatValue(stats, &statValue)) {
+                psError(PS_ERR_UNKNOWN, true, "Could not determine stats value.  Returning NULL.\n");
+                return(NULL);
+            } else {
+                output->data.F32[i-output->row0][j-output->col0] = (psF32) statValue;
+            }
+        }
+    }
+
+    psFree(tmpPixels);
+    psFree(tmpPixelErrors);
+    psFree(tmpPixelMask);
+    psFree(tmpPixelMaskNKeep);
+    psFree(outRowLower);
+    psFree(outRowUpper);
+    psFree(outColLower);
+    psFree(outColUpper);
+    psFree(tmpReadouts);
+
+    return(output);
+}
Index: /trunk/psModules/src/imcombine/pmReadoutCombine.h
===================================================================
--- /trunk/psModules/src/imcombine/pmReadoutCombine.h	(revision 5170)
+++ /trunk/psModules/src/imcombine/pmReadoutCombine.h	(revision 5170)
@@ -0,0 +1,46 @@
+/** @file  pmReadoutCombine.h
+ *
+ *  This file will contain a module which will combine multiple readout images.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#if !defined(PM_READOUT_COMBINE_H)
+#define PM_READOUT_COMBINE_H
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include<stdio.h>
+#include<math.h>
+#include "pslib.h"
+#include "psConstants.h"
+#include "pmAstrometry.h"
+
+typedef struct
+{
+    psStats *stats;
+    unsigned int maskVal;
+    float fracHigh;
+    float fracLow;
+    int nKeep;
+}
+psCombineParams;
+
+psImage *pmReadoutCombine(psImage *output,
+                          const psList *inputs,
+                          psCombineParams *params,
+                          const psVector *zero,
+                          const psVector *scale,
+                          bool applyZeroScale,
+                          float gain,
+                          float readnoise);
+
+#endif
Index: /trunk/psModules/src/imsubtract/Makefile.am
===================================================================
--- /trunk/psModules/src/imsubtract/Makefile.am	(revision 5170)
+++ /trunk/psModules/src/imsubtract/Makefile.am	(revision 5170)
@@ -0,0 +1,13 @@
+noinst_LTLIBRARIES = libpsmoduleimsubtract.la
+
+libpsmoduleimsubtract_la_CPPFLAGS = $(SRCINC) $(PSMODULE_CFLAGS)
+libpsmoduleimsubtract_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+libpsmoduleimsubtract_la_SOURCES  = pmImageSubtract.c \
+    pmSubtractBias.c \
+    pmSubtractSky.c
+
+psmoduleincludedir = $(includedir)
+psmoduleinclude_HEADERS = \
+    pmImageSubtract.h \
+    pmSubtractBias.h \
+    pmSubtractSky.h
Index: /trunk/psModules/src/imsubtract/pmImageSubtract.c
===================================================================
--- /trunk/psModules/src/imsubtract/pmImageSubtract.c	(revision 5170)
+++ /trunk/psModules/src/imsubtract/pmImageSubtract.c	(revision 5170)
@@ -0,0 +1,1390 @@
+/** @file  ImageSubtract.c
+ *
+ *  This file will contain code which creates a set of kernel basis
+ *  functions, solves for their solution, and applies them to an image.
+ *
+ *  @author Paul Price, IfA (original prototype)
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ *   XXX: sync with IfA on this:
+ *   The (x, y) (row, col) issue is becoming a problem.  In this file, and I
+ *   think, the rest of psLib and psModules, the following conventions are used:
+ *
+ * 1) x will correspond to the column, and y will correspond to the row.
+ * 2) When used in function prototypes, the column (and hence x) appears
+ *    first.
+ * 3) When used to index 2-D arrays, obviously, the row (and hence, y)
+ *    appears first. (2 and 3 are the source of confusion).
+ * 4) When (u, v) are used in certain structures.
+ *  u corresponds to x
+ *  v corresponds to y
+ * 5) When element (a, b) is casually referred to in comments, or
+ *    documentation it is unclear where a is the row, or the column.
+ * 6) A convention on loop index variables (i, j) would be convenient.
+ *    Currently, sometimes i corresponds to the column (x),
+ *    usually it corresponds to the row (y).
+ *
+ *  XXX: The following variables are used an interpreted this way:
+ * kernelSize: Means that the actual kernel is a square (1 + 2 * kernelSize) per side.
+ * border: When accessing an image, a swath of pixels this wide is ignored.
+ * footprint: When accessing a stample, a square of pixels, footprint pixels per side,
+ *  are looked at.  We must ensure that (footprint+kernelSize) pixels exist
+ *  around the center.
+ */
+
+#include<stdio.h>
+#include<math.h>
+#include "pslib.h"
+#include "psConstants.h"
+#include "pmImageSubtract.h"
+
+/*******************************************************************************
+    Private alloc/free functions.
+XXX: It's not clear if the SubtractionKernels alloc/free functions are feasable.
+ ******************************************************************************/
+void p_pmStampFree(pmStamp *stamp)
+{
+    psFree(stamp->matrix);
+    psFree(stamp->vector);
+}
+
+pmStamp *p_pmStampAlloc(pmStampStatus status)
+{
+    pmStamp *stamp = (pmStamp*)psAlloc(sizeof(pmStamp));
+    stamp->x = 0;
+    stamp->p_xSize = 0;
+    stamp->y = 0;
+    stamp->p_ySize = 0;
+    stamp->matrix = NULL;
+    stamp->vector = NULL;
+    stamp->status = status;
+
+    psMemSetDeallocator(stamp, (psFreeFunc)p_pmStampFree);
+
+    return(stamp);
+}
+
+void p_pmSubtractionKernelsFree(psSubtractionKernels *kernels)
+{
+    psFree(kernels->u);
+    psFree(kernels->v);
+    psFree(kernels->sigma);
+    psFree(kernels->xOrder);
+    psFree(kernels->yOrder);
+    psFree(kernels->preCalc);
+
+    psFree(kernels);
+}
+
+psSubtractionKernels *p_pmSubtractionKernelsAlloc(int numBasisFunctions,
+        pmSubtractionKernelsType type)
+{
+    psSubtractionKernels *tmp = (psSubtractionKernels *) psAlloc(sizeof(psSubtractionKernels));
+
+    tmp->type = type;
+    psMemSetDeallocator(tmp, (psFreeFunc) p_pmSubtractionKernelsFree);
+    return(tmp);
+}
+
+/*******************************************************************************
+psSubtractionKernels struct.
+ ******************************************************************************/
+psSubtractionKernels *pmSubtractionKernelsAllocPOIS(int size,
+        int spatialOrder)
+{
+    psTrace("ImageSubtract.pmSubtractionKernelsAllocPOIS", 3,
+            "Calling pmSubtractionKernelsAllocPOIS(%d, %d)\n", size, spatialOrder);
+    PS_ASSERT_INT_POSITIVE(size, NULL);
+    PS_ASSERT_INT_POSITIVE(spatialOrder, NULL);
+    //
+    // Calculate the number of basis functions (nBF)
+    //
+    psS32 xKernelHalfSize = size;
+    psS32 yKernelHalfSize = size;
+    psS32 nBF = (2 * xKernelHalfSize + 1) *
+                (2 * yKernelHalfSize + 1) *
+                (spatialOrder + 1) *
+                (spatialOrder + 2) / 2;
+
+    //
+    // Generate the new psSubtractionKernels data structure:
+    //
+    psSubtractionKernels *tmp = (psSubtractionKernels *) psAlloc(sizeof(psSubtractionKernels));
+    tmp->type = PM_SUBTRACTION_KERNEL_POIS;
+    tmp->u = psVectorAlloc(nBF, PS_TYPE_F32);
+    tmp->v = psVectorAlloc(nBF, PS_TYPE_F32);
+    tmp->sigma = NULL;
+    tmp->xOrder = psVectorAlloc(nBF, PS_TYPE_F32);
+    tmp->yOrder = psVectorAlloc(nBF, PS_TYPE_F32);
+    tmp->subIndex = 0;
+    tmp->preCalc = NULL;
+    tmp->size = size;
+    tmp->spatialOrder = spatialOrder;
+
+    //
+    // This corresponds to the Kernel Basis Function with (u, v) = (0, 0)
+    //
+    // Put the (u,v) = (0,0) component right at the start of the list
+    // for convenience.
+    //
+    psS32 ptr = 0;
+    for (psS32 order = 0; order <= spatialOrder; order++) {
+        for (psS32 xOrder = 0; xOrder <= order; xOrder++) {
+            psS32 yOrder = order - xOrder;
+            tmp->u->data.F32[ptr] = 0;
+            tmp->v->data.F32[ptr] = 0;
+            tmp->xOrder->data.F32[ptr] = xOrder;
+            tmp->yOrder->data.F32[ptr] = yOrder;
+            ptr++;
+        }
+    }
+
+    //
+    // Iterate over (u,v).  Generate a set of kernels for each (u, v).
+    //
+    for (psS32 v = -yKernelHalfSize; v <= yKernelHalfSize; v++) {
+        for (psS32 u = -xKernelHalfSize; u <= xKernelHalfSize; u++) {
+            // Already did (u,v) = (0,0): it's at the start, so skip it now.
+            if ((u != 0) || (v != 0)) {
+                //
+                // Iterate over spatial order.  This loop creates the terms for
+                // x^xOrder * y^yOrder  such that (xOrder+yOrder) <= spatialOrder.
+                //
+                for (psS32 order = 0; order <= spatialOrder; order++) {
+                    for (psS32 xOrder = 0; xOrder <= order; xOrder++) {
+                        psS32 yOrder = order - xOrder;
+                        tmp->u->data.F32[ptr] = u;
+                        tmp->v->data.F32[ptr] = v;
+                        tmp->xOrder->data.F32[ptr] = xOrder;
+                        tmp->yOrder->data.F32[ptr] = yOrder;
+                        ptr++;
+                    }
+                }
+            }
+        }
+    }
+
+    psTrace("ImageSubtract.pmSubtractionKernelsAllocPOIS", 3,
+            "Exiting pmSubtractionKernelsAllocPOIS(%d, %d)\n", size, spatialOrder);
+    return(tmp);
+}
+
+/*******************************************************************************
+XXX: Get the types correct (u, v, xOrder, yOrder).
+ 
+XXX: Code review this.
+ ******************************************************************************/
+psSubtractionKernels *pmSubtractionKernelsAllocISIS(const psVector *sigmas,
+        const psVector *orders,
+        int size,
+        int spatialOrder)
+{
+    PS_ASSERT_VECTOR_NON_NULL(sigmas, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(orders, NULL);
+    psTrace("ImageSubtract.pmSubtractionKernelsAllocISIS", 3,
+            "Calling pmSubtractionKernelsAllocISIS(%d, %d, %d, %d)\n",
+            sigmas->n, orders->n, size, spatialOrder);
+    PS_ASSERT_INT_POSITIVE(size, NULL);
+    PS_ASSERT_INT_POSITIVE(spatialOrder, NULL);
+    PS_ASSERT_VECTOR_TYPE(sigmas, PS_TYPE_F32, NULL);
+    PS_ASSERT_VECTOR_TYPE(orders, PS_TYPE_S32, NULL);
+    //
+    // Calculate the number of basis functions (nBF).
+    //
+    psS32 numSigmas = sigmas->n;
+
+    // XXX: Get rid of the sigma loop?  We merely multiple nBF by numSigmas?
+    // XXX: Verify that all this is correct.
+    psS32 nBF = 0;
+    for (psS32 s = 0 ; s < numSigmas ; s++) {
+        for (psS32 o = 0 ; o < orders->n ; o++) {
+            nBF+=((orders->data.S32[o] + 1) * (orders->data.S32[o] + 2) / 2);
+        }
+    }
+    nBF*= ((spatialOrder + 1) * (spatialOrder + 2) / 2);
+
+    //
+    // Generate the new psSubtractionKernels data structure:
+    //
+    psSubtractionKernels *tmp = (psSubtractionKernels *) psAlloc(sizeof(psSubtractionKernels));
+    tmp->type = PM_SUBTRACTION_KERNEL_ISIS;
+    tmp->u = psVectorAlloc(nBF, PS_TYPE_F32);
+    tmp->v = psVectorAlloc(nBF, PS_TYPE_F32);
+    tmp->sigma = psVectorAlloc(nBF, PS_TYPE_F32);
+    tmp->xOrder = psVectorAlloc(nBF, PS_TYPE_F32);
+    tmp->yOrder = psVectorAlloc(nBF, PS_TYPE_F32);
+    tmp->subIndex = 0;
+    tmp->size = size;
+    tmp->spatialOrder = spatialOrder;
+    tmp->preCalc = psArrayAlloc(nBF);
+
+    //
+    // We loop through all combinations of sigmas and polynomial orders
+    // creating the kernel basis functions.
+    //
+    psS32 ptr = 0;
+    for (psS32 sigPtr = 0 ; sigPtr < numSigmas ; sigPtr++) {
+        tmp->sigma->data.F32[sigPtr] = sigmas->data.F32[sigPtr];
+        //
+        // (xOrderP, yOrderP) are the order of the polynomial that modify the
+        // gaussian in the kernel.  They correspond to (j, k) in equation (5)
+        // from the psModules SDRS.
+        //
+        for (psS32 o = 0 ; o < orders->n ; o++) {
+            for (psS32 orderP = 0 ; orderP <= orders->data.S32[o] ; orderP++) {
+                for (psS32 xOrderP = 0 ; xOrderP <= orderP ; xOrderP++) {
+                    psS32 yOrderP = orderP - xOrderP;
+
+                    psImage *currPreCalc = psImageAlloc(1 + (2 * size), 1 + (2 * size), PS_TYPE_F32);
+                    PS_IMAGE_SET_F32(currPreCalc, 0.0);
+                    psBool setPreCalc = true;
+                    //
+                    // We loop through all spatial orders.  Since they have no effect on
+                    // the preCalc images, we only calculate them once, and store pointers
+                    // in tmp->preCalc->data[ptr] for other spatial orders.
+                    //
+                    for (psS32 order = 0; order <= spatialOrder; order++) {
+                        for (psS32 orderXTerm = 0; orderXTerm <= order; orderXTerm++) {
+                            PS_ASSERT_INT_LESS_THAN(ptr, nBF, NULL);
+
+                            psS32 orderYTerm = order - orderXTerm;
+
+                            tmp->u->data.F32[ptr] = xOrderP;
+                            tmp->v->data.F32[ptr] = yOrderP;
+                            tmp->xOrder->data.F32[ptr] = orderXTerm;
+                            tmp->yOrder->data.F32[ptr] = orderYTerm;
+                            tmp->sigma->data.F32[ptr] = sigmas->data.F32[sigPtr];
+                            tmp->preCalc->data[ptr] = (psPtr *) currPreCalc;
+
+                            //
+                            // We calculate the preCalc image only the first time through
+                            // this loop.  Otherwise, we increment the memory reference
+                            // counter.
+                            //
+                            if (setPreCalc == true) {
+                                for (psS32 v = -size; v <= size; v++) {
+                                    for (psS32 u = -size; u <= size; u++) {
+                                        // Scale the (u,v) coordinates in kernel space to [-1.0:1.0].
+                                        psF32 uScaled = ((psF32) u) / ((psF32) size);
+                                        psF32 vScaled = ((psF32) v) / ((psF32) size);
+
+                                        // Compute the value of the kernel at location (u, v):
+                                        psF32 exponent = (PS_SQR(uScaled) + PS_SQR(vScaled)) /
+                                                         (2.0 * PS_SQR(sigmas->data.F32[sigPtr]));
+                                        currPreCalc->data.F32[v+size][u+size] =
+                                            exp(-exponent) *
+                                            pow(uScaled, orderXTerm) *
+                                            pow(vScaled, orderYTerm);
+                                    }
+                                }
+                                setPreCalc = false;
+                            } else {
+                                psMemIncrRefCounter(currPreCalc);
+                            }
+                            ptr++;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    psTrace("ImageSubtract.pmSubtractionKernelsAllocISIS", 3,
+            "Exiting pmSubtractionKernelsAllocISIS(%d, %d, %d, %d)\n",
+            sigmas->n, orders->n, size, spatialOrder);
+    return(tmp);
+}
+
+/*******************************************************************************
+pmSubtractionFindStamps(stamps, image, mask, maskVal, threshold, xNum, yNum, border)
+ 
+XXX: The SDRS and the proptotype code differ significantly.
+ Prototype: When a maximum pixel is found within a stamp, an area of size
+     2*footprint is searched around that pixel looking for masked pixels.
+ SDRS: none of that is required.
+ 
+XXX: Do we need to care about case where yNum/xNum does not evenly divide the
+nnumber of rows/columns in the image?
+ ******************************************************************************/
+psArray *pmSubtractionFindStamps(psArray *stamps,        ///< Output stamps, or NULL
+                                 const psImage *image,   ///< Image for which to find stamps
+                                 const psImage *mask,    ///< Mask
+                                 psU32 maskVal,          ///< Value for mask
+                                 psF32 threshold,        ///< Threshold for stamps in the image
+                                 psS32 xNum,             ///< Number of stamps in x
+                                 psS32 yNum,             ///< Number of stamps in y
+                                 psS32 border            ///< Border around image to ignore (should be size of kernel or larger)
+                                )
+{
+    psTrace("ImageSubtract.pmSubtractionFindStamps", 3,
+            "Calling pmSubtractionFindStamps(%d, %f, %d, %d, %d)\n",
+            maskVal, threshold, xNum, yNum, border);
+    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(image, NULL);
+    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
+    if (mask != NULL) {
+        PS_ASSERT_IMAGES_SIZE_EQUAL(image, mask, NULL);
+        PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_U8, NULL);
+    }
+    PS_ASSERT_INT_POSITIVE(xNum, NULL);
+    PS_ASSERT_INT_POSITIVE(yNum, NULL);
+    PS_ASSERT_INT_POSITIVE(border, NULL);
+    PS_ASSERT_INT_LARGER_THAN(image->numCols, xNum, NULL);
+    PS_ASSERT_INT_LARGER_THAN(image->numRows, yNum, NULL);
+    PS_ASSERT_INT_LARGER_THAN(image->numCols, (2 * border), NULL);
+    PS_ASSERT_INT_LARGER_THAN(image->numRows, (2 * border), NULL);
+
+    if (stamps != NULL) {
+        PS_ASSERT_INT_EQUAL(stamps->n, (xNum * yNum), NULL);
+        //
+        // Ensure that a pmStamp struct exists at each psArray location.
+        //
+        for (psS32 s = 0 ; s < (xNum * yNum) ; s++) {
+            if (NULL == stamps->data[s]) {
+                stamps->data[s] = (psPtr *) p_pmStampAlloc(PM_STAMP_REJECTED);
+            }
+        }
+    } else {
+        stamps = (psArray *) psArrayAlloc(xNum * yNum);
+        for (psS32 s = 0 ; s < (xNum * yNum) ; s++) {
+            stamps->data[s] = (psPtr *) p_pmStampAlloc(PM_STAMP_REJECTED);
+        }
+    }
+    psS32 numRows = image->numRows;
+    psS32 numCols = image->numCols;
+
+    //
+    // Iterate over the image sections
+    //
+    // XXX: Must handle cases where image size is not an even multiple of xNum or yNum
+    // they are currently ignored.
+    //
+    psS32 num = 0;
+    for (psS32 j = 0; j < yNum; j++) {
+        for (psS32 i = 0; i < xNum; i++) {
+            pmStamp *stamp = (pmStamp *) stamps->data[num];
+            //
+            // Only find a new stamp if we need to
+            //
+            if (stamp->status == PM_STAMP_REJECTED) {
+                //
+                // Find maximum non-masked value in the image section,
+                // but don't include a footprint around the edge
+                //
+                psF32 max = -INFINITY;
+                psS32 bestx = 0;
+                psS32 besty = 0;
+                //
+                // The following nested loop iterates over every pixel in the mask
+                // associated with this (i, j).  It ignores pixels within a
+                // border of pixels from the image edge.
+                //
+                // XXX: verify (numX, numY), then get rid of it.
+                //
+                psS32 numX = xNum;
+                psS32 numY = yNum;
+                psS32 yMin = border + j * (numCols - 2.0 * border) / numY;
+                psS32 yMax = (border + (j + 1) * (numCols - 2.0 * border) / numY) - 1;
+                psS32 xMin = border + i * (numRows - 2.0 * border) / numX;
+                psS32 xMax = (border + (i + 1) * (numRows - 2.0 * border) / numX) - 1;
+
+                if ((yMax >= image->numRows) ||
+                        (xMax >= image->numCols) ||
+                        (yMin < 0) ||
+                        (xMin < 0)) {
+                    // XXX: We skip this stamp since its borders extends beyond the image.
+                    // XXX: This is here mainly as a safeguard.  We need to redefine the above
+                    // min/max pixels calculation to ensure that all stamps are legitimate.
+
+                    stamp->x = -1;
+                    stamp->y = -1;
+                    stamp->status = PM_STAMP_NONE;
+                } else {
+                    stamp->p_xSize = 1 + (xMax - xMin);
+                    stamp->p_ySize = 1 + (yMax - yMin);
+                    stamp->p_xMin = xMin;
+                    stamp->p_xMax = xMax;
+                    stamp->p_yMin = yMin;
+                    stamp->p_yMax = yMax;
+
+                    for (psS32 y = yMin; y <= yMax ; y++) {
+                        for (psS32 x = xMin; x <= xMax ; x++) {
+                            // Determine if this pixel is larger than the max, and unmasked.
+                            if (image->data.F32[y][x] > max) {
+                                if ((mask == NULL) || !((mask->data.U8[y][x]) & maskVal)) {
+                                    max = image->data.F32[y][x];
+                                    bestx = x;
+                                    besty = y;
+                                }
+                            }
+                        }
+                    }
+
+                    //
+                    // If the max pixel is larger than the threshold, we keep this stamp.
+                    // Otherwise, mark the stamp as PM_STAMP_NONE
+                    //
+                    if (image->data.F32[besty][bestx] >= threshold) {
+                        stamp->x = bestx;
+                        stamp->y = besty;
+                        stamp->status = PM_STAMP_RECALC;
+                    } else {
+                        stamp->x = bestx;
+                        stamp->y = besty;
+                        stamp->status = PM_STAMP_NONE;
+                    }
+                }
+            }
+            num++;
+        }
+    }
+    psTrace("ImageSubtract.pmSubtractionFindStamps", 3,
+            "Exiting pmSubtractionFindStamps(%d, %f, %d, %d, %d)\n",
+            maskVal, threshold, xNum, yNum, border);
+    return(stamps);
+}
+
+/*******************************************************************************
+GenSpatialOrder(spatialOrder, x, y): generates and returns a psImage in which
+the [i][j] location is calculated as (x^i * y^j).
+ 
+XXX: Modify loop so that terms higher than spatialOrder are not computed.
+ 
+XXX: Modify this so that [i][j] location is calculated as (x^j * y^i)?
+ ******************************************************************************/
+static psImage *GenSpatialOrder(psS32 spatialOrder,
+                                psF32 x,
+                                psF32 y)
+{
+    psTrace("ImageSubtract.GenSpatialOrder", 4,
+            "Calling GenSpatialOrder(%d, %f, %f)\n", spatialOrder, x, y);
+
+    psImage *polyValues = psImageAlloc(spatialOrder+1, spatialOrder+1, PS_TYPE_F64);
+
+    psF64 xSum = 1.0;
+    psF64 ySum = 1.0;
+    for (psS32 i = 0; i < spatialOrder + 1; i++) {
+        ySum = xSum;
+        for (psS32 j = 0; j < spatialOrder + 1; j++) {
+            polyValues->data.F64[i][j] = ySum;
+            ySum*= y;
+        }
+        xSum*= x;
+    }
+
+    psTrace("ImageSubtract.GenSpatialOrder", 4,
+            "Exiting GenSpatialOrder(%d, %f, %f)\n", spatialOrder, x, y);
+
+    return(polyValues);
+}
+
+
+/*******************************************************************************
+IsisKernelConvolve(input, kernels, kernelID, col, row): This routine
+convolves a single kernel basis function with a pixel in an image.
+  ******************************************************************************/
+static psF32 IsisKernelConvolve(const psImage *input,
+                                const psSubtractionKernels *kernels,
+                                psS32 kernelID,
+                                psS32 col,
+                                psS32 row)
+{
+
+
+    psTrace("ImageSubtract.IsisKernelConvolve", 4,
+            "Calling IsisKernelConvolve(%d, %d, %d)\n", kernelID, col, row);
+    psS32 spatialOrder = kernels->spatialOrder;
+    psS32 kernelSize = kernels->size;
+    psS32 xOrder = (psS32) kernels->xOrder->data.F32[kernelID];
+    psS32 yOrder = (psS32) kernels->yOrder->data.F32[kernelID];
+    psF32 numColsHalf = 0.5 * (psF32) input->numCols;
+    psF32 numRowsHalf = 0.5 * (psF32) input->numRows;
+    psF32 imageX = (((psF32) col) - numColsHalf) / numColsHalf; // Normalised position
+    psF32 imageY = (((psF32) row) - numRowsHalf) / numRowsHalf; // Normalised position
+
+    psImage *polyValues = GenSpatialOrder(spatialOrder, imageX, imageY);
+
+    psF64 polyVal = polyValues->data.F64[yOrder][xOrder];
+
+    psImage *preCalc = (psImage *) kernels->preCalc->data[kernelID];
+
+    // XXX: Are the following asserts really necessary?
+    PS_ASSERT_INT_LARGER_THAN_OR_EQUAL(row-kernelSize, 0, NAN);
+    PS_ASSERT_INT_LESS_THAN(row+kernelSize, input->numRows, NAN);
+    PS_ASSERT_INT_LARGER_THAN_OR_EQUAL(col-kernelSize, 0, NAN);
+    PS_ASSERT_INT_LESS_THAN(col+kernelSize, input->numCols, NAN);
+    psF32 conv = 0.0;
+    for (psS32 yy = -kernelSize ; yy < kernelSize ; yy++) {
+        for (psS32 xx = -kernelSize ; xx < kernelSize ; xx++) {
+            conv += input->data.F32[yy+row][xx+col] *
+                    preCalc->data.F32[yy+kernelSize][xx+kernelSize] *
+                    polyVal;
+        }
+    }
+    psFree(polyValues);
+
+    psTrace("ImageSubtract.IsisKernelConvolve", 4,
+            "Exiting IsisKernelConvolve(%d, %d, %d)\n", kernelID, col, row);
+    return(conv);
+}
+
+/*******************************************************************************
+ConvolvePixelPois(input, mask, badStampMaskVal, solution, kernels, col, row):
+ 
+This routine takes a single pixel in the psImage input and convolves it with
+the set of kernel basis functions and their appropriate weights in solution.
+It returns the value of the convolved pixel.
+ 
+XXX: Static structure for polyValues?
+ ******************************************************************************/
+static psF32 ConvolvePixelPois(const psImage *input,
+                               const psImage *mask,
+                               psU32 badStampMaskVal,
+                               const psVector *solution,
+                               const psSubtractionKernels *kernels,
+                               psS32 col,
+                               psS32 row)
+{
+    psTrace("ImageSubtract.ConvolvePixelPois", 4,
+            "Calling ConvolvePixelPois(%d, %d)\n", col, row);
+    psS32 nBF = kernels->u->n;
+    psF32 numColsHalf = 0.5 * (psF32) input->numCols;
+    psF32 numRowsHalf = 0.5 * (psF32) input->numRows;
+    psF32 background = solution->data.F64[solution->n-1];
+    psS32 spatialOrder = kernels->spatialOrder;
+    psF32 conv = background; // Initial convolved value
+
+    if ((mask == NULL) || !(mask->data.U8[row][col] & badStampMaskVal)) {
+        psF32 imageX = (((psF32) col) - numColsHalf) / numColsHalf; // Normalised position
+        psF32 imageY = (((psF32) row) - numRowsHalf) / numRowsHalf; // Normalised position
+        psImage *polyValues = GenSpatialOrder(spatialOrder, imageX, imageY);
+
+        // Iterate over the kernel basis functions
+        for (psS32 k = 0; k < nBF; k++) {
+            psS32 u = (psS32) kernels->u->data.F32[k];
+            psS32 v = (psS32) kernels->v->data.F32[k];
+
+            // XXX: What's the story with this?
+            #if 0
+
+            psS32 xOrder = (psS32) kernels->xOrder->data.F32[k];
+            psS32 yOrder = (psS32) kernels->yOrder->data.F32[k];
+            psF64 polyVal = polyValues->data.F64[yOrder][xOrder];
+            #else
+
+            psF32 polyVal = 1.0;
+            #endif
+
+            // XXX: Why this?
+            if (k == 0) {
+                conv += solution->data.F64[k] * input->data.F32[row - v][col - u] * polyVal;
+            } else {
+                conv += solution->data.F64[k] *
+                        (input->data.F32[row - v][col - u] * polyVal - input->data.F32[row][col]);
+            }
+        }
+        psFree(polyValues);
+    }
+
+    psTrace("ImageSubtract.ConvolvePixelPois", 4,
+            "Exiting ConvolvePixelPois(%d, %d)\n", col, row);
+    return(conv);
+}
+
+
+
+/*******************************************************************************
+ConvolvePixelIsis(input, mask, badStampMaskVal, solution, kernels, col, row):
+ 
+This routine takes a single pixel in the psImage input and convolves it with
+the set of kernel basis functions and their appropriate weights in solution.
+It returns the value of the convolved pixel.
+ 
+XXX: Static structure for polyValues?
+ ******************************************************************************/
+static psF32 ConvolvePixelIsis(const psImage *input,
+                               const psImage *mask,
+                               psU32 badStampMaskVal,
+                               const psVector *solution,
+                               const psSubtractionKernels *kernels,
+                               psS32 col,
+                               psS32 row)
+{
+    psTrace("ImageSubtract.ConvolvePixelIsis", 4,
+            "Calling ConvolvePixelIsis(%d, %d)\n", col, row);
+    psF32 background = solution->data.F64[solution->n-1];
+    psF32 conv = background; // Initial convolved value
+
+    if ((mask == NULL) || !(mask->data.U8[row][col] & badStampMaskVal)) {
+        // Iterate over the kernel basis functions
+        for (psS32 k = 0; k < kernels->u->n; k++) {
+            conv += IsisKernelConvolve(input, kernels, k, col, row);
+        }
+    }
+
+    psTrace("ImageSubtract.ConvolvePixelIsis", 4,
+            "Exiting ConvolvePixelIsis(%d, %d)\n", col, row);
+    return(conv);
+}
+
+/*******************************************************************************
+ConvolveImage(input, mask, badStampMaskVal, solution, kernels): convolves an
+arbitrary image with either an ISIS or POIS set of kernel basis functions.
+ ******************************************************************************/
+static psImage *ConvolveImage(const psImage *input,
+                              const psImage *mask,
+                              psU32 badStampMaskVal,
+                              const psVector *solution,
+                              const psSubtractionKernels *kernels)
+{
+    psTrace("ImageSubtract.ConvolveImage", 4, "Calling ConvolveImage()\n");
+    PS_ASSERT_IMAGE_NON_NULL(input, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(input, NULL);
+    PS_ASSERT_IMAGE_TYPE(input, PS_TYPE_F32, NULL);
+    if (mask != NULL) {
+        PS_ASSERT_IMAGES_SIZE_EQUAL(input, mask, NULL);
+        PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_U8, NULL);
+    }
+    PS_ASSERT_VECTOR_NON_NULL(solution, NULL);
+    PS_ASSERT_VECTOR_TYPE(solution, PS_TYPE_F64, NULL);
+    PS_ASSERT_PTR_NON_NULL(kernels, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->v, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->xOrder, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->yOrder, NULL);
+    if (kernels->preCalc != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->preCalc, NULL);
+    } else {
+        if (kernels->type != PM_SUBTRACTION_KERNEL_POIS) {
+            psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                    "Unallowable operation: kernels->preCalc == NULL and kernels->type != PM_SUBTRACTION_KERNEL_POIS.\n");
+            return(NULL);
+        }
+    }
+    psS32 nBF = kernels->u->n;
+    PS_ASSERT_VECTOR_SIZE(solution, nBF+1, NULL);
+
+    psS32 numCols = input->numCols;
+    psS32 numRows = input->numRows;
+    psS32 kernelSize = kernels->size;
+
+    psImage *convolved = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+
+    for (psS32 y = kernelSize; y < numRows - kernelSize; y++) {
+        for (psS32 x = kernelSize; x < numCols - kernelSize; x++) {
+            if (kernels->type == PM_SUBTRACTION_KERNEL_POIS) {
+                convolved->data.F32[y][x] = ConvolvePixelPois(input, mask, badStampMaskVal,
+                                            solution, kernels, x, y);
+            } else if (kernels->type == PM_SUBTRACTION_KERNEL_ISIS) {
+                convolved->data.F32[y][x] = ConvolvePixelIsis(input, mask, badStampMaskVal,
+                                            solution, kernels, x, y);
+            } else {
+                psLogMsg(__func__, PS_LOG_WARN, "WARNING: unknown kernel type.  Returning NULL\n");
+                return(NULL);
+            }
+        }
+    }
+
+    //
+    // Pad the rest of the convolved image with 0.0
+    //
+    for (psS32 y = kernelSize; y < numRows - kernelSize; y++) {
+        for (psS32 x = 0; x < kernelSize; x++) {
+            convolved->data.F32[y][x] = 0.0;
+        }
+        for (psS32 x = numCols - kernelSize; x < numCols; x++) {
+            convolved->data.F32[y][x] = 0.0;
+        }
+    }
+    for (psS32 y = 0; y < kernelSize; y++) {
+        for (psS32 x = 0; x < numCols; x++) {
+            convolved->data.F32[y][x] = 0.0;
+        }
+    }
+    for (psS32 y = numRows - kernelSize; y < numRows; y++) {
+        for (psS32 x = 0; x < numCols; x++) {
+            convolved->data.F32[y][x] = 0.0;
+        }
+    }
+
+    psTrace("ImageSubtract.ConvolveImage", 4, "Exiting ConvolveImage()\n");
+    return convolved;
+}
+
+
+
+
+
+/*******************************************************************************
+XXX: We should assert that the (footprint, kernelSize, imageSize) stuff
+ensures that all data is accessed in bounds?
+ ******************************************************************************/
+bool pmSubtractionCalculateEquation(psArray *stamps,          ///< The stamps for which to calculate the equation,
+                                    const psImage *reference, ///< Reference image
+                                    const psImage *input,     ///< Input image
+                                    const psSubtractionKernels *kernels, ///< The kernel basis functions
+                                    psS32 footprint           ///< Half-size of region over which to calculate equation
+                                   )
+{
+    psTrace("ImageSubtract.pmSubtractionCalculateEquation", 3,
+            "Calling pmSubtractionCalculateEquation()\n");
+    PS_ASSERT_PTR_NON_NULL(stamps, false);
+    PS_ASSERT_IMAGE_NON_NULL(reference, false);
+    PS_ASSERT_IMAGE_NON_EMPTY(reference, false);
+    PS_ASSERT_IMAGE_NON_NULL(input, false);
+    PS_ASSERT_IMAGE_NON_EMPTY(input, false);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(reference, input, false);
+    PS_ASSERT_PTR_NON_NULL(kernels, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->v, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->xOrder, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->yOrder, false);
+    if (kernels->type == PM_SUBTRACTION_KERNEL_ISIS) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->preCalc, false);
+    }
+    psS32 kernelSize = kernels->size;
+    PS_ASSERT_INT_NONNEGATIVE(footprint, false);
+    //
+    // For each legitimate stamp, ensure that the footprint is small enough to perform
+    // the full calculation.
+    //
+    // XXX: Verify with IfA that this is a reasonable action.
+    //
+    for (psS32 s = 0; s < stamps->n; s++) {
+        pmStamp *stamp = (pmStamp *) stamps->data[s];
+        if (stamp->status == PM_STAMP_RECALC) {
+            // XXX: trace message
+            // printf("stamp %d (x, y) is (%d, %d).  Footprint is %d.  kernelSize is %d.\n", s, stamp->x, stamp->y, footprint, kernelSize);
+            if (((stamp->y - (footprint + kernelSize)) < 0) ||
+                    ((stamp->x - (footprint + kernelSize)) < 0) ||
+                    ((stamp->y + footprint + kernelSize) >= input->numRows) ||
+                    ((stamp->x + footprint + kernelSize) >= input->numCols)) {
+                stamp->status = PM_STAMP_NONE;
+                psLogMsg(__func__, PS_LOG_WARN,
+                         "WARNING: stamp %d will be ignored.  It exceeds image size: access columns (%d to %d) and rows (%d to %d)\n",
+                         s,
+                         stamp->x - (footprint + kernelSize),
+                         (stamp->x + footprint + kernelSize) - 1,
+                         stamp->y - (footprint + kernelSize),
+                         (stamp->y + footprint + kernelSize) - 1);
+            }
+        }
+    }
+
+    psS32 numHalfRows = reference->numRows;
+    psS32 numHalfCols = reference->numCols;
+    psS32 spatialOrder = kernels->spatialOrder;
+
+    //
+    // The numSolveParams incorporates the additional parameter for the
+    // background value, which we must solve for.
+    //
+    psS32 numKernels = kernels->u->n;
+    int numSolveParams = numKernels + 1;
+    int bgIndex = numKernels;        // Index in matrix for the background
+
+    //
+    // We iterate over each stamp, allocate the matrix and vectors if
+    // necessary, and then calculate those matrix/vectors.
+    //
+    for (psS32 s = 0; s < stamps->n; s++) {
+        pmStamp *stamp = (pmStamp *) stamps->data[s];
+        psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): stamp %d\n", s);
+        if (stamp->status == PM_STAMP_RECALC) {
+            psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): stamp %d: status is PM_STAMP_RECALC.\n", s);
+            psImage *stampMatrix = stamp->matrix;
+            psVector *stampVector = stamp->vector;
+
+            if (stampMatrix == NULL) {
+                stampMatrix = psImageAlloc(numSolveParams, numSolveParams, PS_TYPE_F64);
+                stamp->matrix = stampMatrix;
+            } else {
+                PS_ASSERT_IMAGE_TYPE(stampMatrix, PS_TYPE_F64, false);
+                PS_ASSERT_IMAGE_SIZE(stampMatrix, numSolveParams, numSolveParams, false);
+            }
+            PS_IMAGE_SET_F64(stampMatrix, 0.0);
+
+            if (stampVector == NULL) {
+                stampVector = psVectorAlloc(numSolveParams, PS_TYPE_F64);
+                stamp->vector = stampVector;
+            } else {
+                PS_ASSERT_VECTOR_TYPE(stampVector, PS_TYPE_F64, false);
+                PS_ASSERT_VECTOR_SIZE(stampVector, numSolveParams, false);
+            }
+            PS_VECTOR_SET_F64(stampVector, 0.0);
+            psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): stamp %d: allocate matrix and vector.\n", s);
+
+            //
+            // Evaluate the spatial-order polynomial.  The [i][j]-th element of
+            // the psImage polyValues will hold (x^i * y^j) for the stamp.  The
+            // (x, y) value are scaled to [-1:1]
+            //
+            psImage *polyValues = GenSpatialOrder(spatialOrder,
+                                                  ((psF64) (stamp->x - numHalfCols)) / ((psF64) numHalfCols),
+                                                  ((psF64) (stamp->y - numHalfRows)) / ((psF64) numHalfRows));
+
+            psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): stamp %d: generated spatial order terms.\n", s);
+
+            if (kernels->type == PM_SUBTRACTION_KERNEL_POIS) {
+                //
+                // Iterate over all pixels surrounding this stamp.
+                //
+                for (psS32 y = stamp->y - footprint; y < stamp->y + footprint; y++) {
+                    for (psS32 x = stamp->x - footprint; x < stamp->x + footprint; x++) {
+                        psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): pixel (%d, %d).\n", y, x);
+
+                        // The inverse of the noise, squared.
+                        psF32 invNoise2 = 1.0/reference->data.F32[y][x];
+
+                        //
+                        // Iterate over the first convolution */
+                        //
+                        for (psS32 k1 = 0; k1 < numKernels; k1++) {
+                            psS32 u1 = kernels->u->data.F32[k1];        // Offset in x
+                            psS32 v1 = kernels->v->data.F32[k1];        // Offset in y
+                            psS32 i1 = kernels->xOrder->data.F32[k1];   // Polynomial order in x
+                            psS32 j1 = kernels->yOrder->data.F32[k1];   // Polynomial order in y
+
+                            //
+                            // First convolution.  This will set the value for the stampVector.
+                            //
+                            // XXX: verify the [y-v2][x-u2] subscript.  This generated errors in
+                            // testing, depending on kernel size and footprint.
+                            //
+                            psF32 conv1 = polyValues->data.F64[j1][i1] * reference->data.F32[y - v1][x - u1];
+
+                            //
+                            // Assuming that the first kernel component is 0 order in x and y, and 0 offset
+                            // XXX: I don't understand this:
+                            //
+                            if (k1 != 0) {
+                                conv1 -= reference->data.F32[y][x];
+                            }
+
+                            //
+                            // Iterate over the second convolution
+                            //
+                            for (psS32 k2 = k1; k2 < numKernels; k2++) {
+                                psS32 u2 = (psS32) kernels->u->data.F32[k2];        // Offset in x
+                                psS32 v2 = (psS32) kernels->v->data.F32[k2];        // Offset in y
+                                psS32 i2 = (psS32) kernels->xOrder->data.F32[k2];   // Polynomial order in x
+                                psS32 j2 = (psS32) kernels->yOrder->data.F32[k2];   // Polynomial order in y
+                                //
+                                // XXX: verify the [y-v2][x-u2] subscript.  This generated errors in
+                                // testing, depending on kernel size and footprint.
+                                //
+                                // Second convolution
+                                //
+                                psF32 conv2 = polyValues->data.F64[j2][i2] *
+                                              reference->data.F32[y-v2][x-u2];
+                                //
+                                // Assuming that the first kernel component is 0 order in x and y, and 0 offset
+                                // XXX: I don't understand this:
+                                if (k2 != 0) {
+                                    //
+                                    conv2 -= reference->data.F32[y][x];
+                                }
+
+                                // Add into the matrix element
+                                stampMatrix->data.F64[k1][k2] += conv1 * conv2 * invNoise2;
+
+                            } // Iteration on second convolution
+
+                            // Add into the vector element
+                            stampVector->data.F64[k1] += input->data.F32[y][x] * conv1 * invNoise2;
+
+                            /* Background term */
+                            stampMatrix->data.F64[k1][bgIndex] += conv1 * invNoise2;
+
+                        } // Iteration on first convolution
+
+                        //
+                        // Background only terms.
+                        // XXX: understand this.
+                        //
+                        stampMatrix->data.F64[bgIndex][bgIndex] += invNoise2;
+                        stampVector->data.F64[bgIndex] += input->data.F32[y][x] * invNoise2;
+                    }
+                }
+            } else if (kernels->type == PM_SUBTRACTION_KERNEL_ISIS) {
+                //
+                // Iterate over all pixels surrounding this stamp.
+                //
+                // XXX: Why isn't there a polyValues term here?
+                //
+
+                for (psS32 y = stamp->y - footprint; y < stamp->y + footprint; y++) {
+                    for (psS32 x = stamp->x - footprint; x < stamp->x + footprint; x++) {
+                        psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): pixel (%d, %d).\n", y, x);
+                        psF32 invNoise2 = 1.0/reference->data.F32[y][x]; // The inverse of the noise, squared.
+
+                        for (psS32 k1 = 0; k1 < numKernels; k1++) {
+                            psF32 conv1 = IsisKernelConvolve(reference, kernels, k1, x, y);
+
+                            for (psS32 k2 = k1; k2 < numKernels; k2++) {
+                                //printf("(k1, k2) is (%d, %d)\n", k1, k2);
+                                psF32 conv2 = IsisKernelConvolve(reference, kernels, k2, x, y);
+                                stampMatrix->data.F64[k1][k2]+= conv1 * conv2 * invNoise2;
+                            }
+                            stampVector->data.F64[k1]+= input->data.F32[y][x] * conv1 * invNoise2;
+                            stampMatrix->data.F64[k1][bgIndex] += conv1 * invNoise2;
+                        }
+                        stampMatrix->data.F64[bgIndex][bgIndex] += invNoise2;
+                        stampVector->data.F64[bgIndex] += input->data.F32[y][x] * invNoise2;
+                    }
+                }
+            } else {
+                psLogMsg(__func__, PS_LOG_WARN, "WARNING: unknown kernel->type.\n");
+                return(false);
+            }
+            psFree(polyValues);
+
+            // XXX: Generate psTrace()
+            if (0) {
+                for (psS32 s = 0; s < stamps->n; s++) {
+                    pmStamp *stamp = (pmStamp *) stamps->data[s];
+                    if (stamp->status == PM_STAMP_RECALC) {
+                        psVector *stampVector = stamp->vector;
+                        printf("STAMP: stamp %d vector:\n", s);
+                        PS_VECTOR_PRINT_F64(stampVector);
+                    }
+                }
+            }
+
+            //
+            // Fill in other side of symmetric matrix
+            //
+            // XXX: understand this.
+            // XXX: Why aren't they using numSolveParams instead of numKernels?
+            // XXX: is this POIS specific?
+            //
+            for (psS32 k1 = 0; k1 < numKernels; k1++) {
+                for (psS32 k2 = 0; k2 < k1; k2++) {
+                    stampMatrix->data.F64[k1][k2] = stampMatrix->data.F64[k2][k1];
+                }
+                stampMatrix->data.F64[bgIndex][k1] = stampMatrix->data.F64[k1][bgIndex];
+            }
+
+            //
+            // XXX: Why aren't they using numSolveParams instead of numKernels?
+            // XXX: is this POIS specific?
+            //
+            #define XXX_CONFIG_PENALTY 1.0
+            for (psS32 k = 0; k < numKernels; k++)
+            {
+                psS32 u = kernels->u->data.F32[k];  // Offset in x
+                psS32 v = kernels->v->data.F32[k];  // Offset in y
+                stampMatrix->data.F64[k][k] += XXX_CONFIG_PENALTY * (psF32)(u*u + v*v);
+            }
+            stamp->status = PM_STAMP_USED;
+        } else {
+            // Stamp is ignored since it's not PM_STAMP_RECALC
+        }
+    }
+    psTrace("ImageSubtract.pmSubtractionCalculateEquation", 3,
+            "Exiting pmSubtractionCalculateEquation()\n");
+    return(true);
+}
+
+
+
+
+/*******************************************************************************
+ ******************************************************************************/
+psVector *pmSubtractionSolveEquation(psVector *solution, ///< Solution vector, or NULL
+                                     const psArray *stamps      ///< Array of stamps
+                                    )
+{
+    psTrace("ImageSubtract.pmSubtractionSolveEquation", 3,
+            "Calling pmSubtractionSolveEquation()\n");
+    PS_ASSERT_PTR_NON_NULL(stamps, NULL);
+    psS32 size = -1;
+    psS32 s = 0;
+
+    //
+    // Determine the size of the stamp vectors and matrix.
+    // We iterate until we find the first acceptable stamp.
+    //
+    while ((size == -1) && (s < stamps->n)) {
+        pmStamp *stamp = (pmStamp *) stamps->data[s];
+        PS_ASSERT_PTR_NON_NULL(stamp, NULL);
+        if (stamp->status == PM_STAMP_USED) {
+            size = ((pmStamp *) stamps->data[s])->vector->n;
+            PS_ASSERT_INT_POSITIVE(size, NULL);
+        }
+        s++;
+    }
+    if (size == -1) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: no acceptable stamps.  Returning NULL\n");
+        return(NULL);
+    }
+
+    if (solution != NULL) {
+        PS_ASSERT_VECTOR_TYPE(solution, PS_TYPE_F64, NULL);
+        PS_ASSERT_VECTOR_SIZE(solution, size, NULL);
+    } else {
+        solution = psVectorAlloc(size, PS_TYPE_F64);
+    }
+
+    //
+    // Create the solution matrix and vector.
+    //
+    // XXX: Test these functions with size=-1.  This caused seg faults during test.
+    //      This should be done in the psImage.c and psVector.c test files.  It
+    //      should never occur here.
+    //
+    psImage *sumMatrix = psImageAlloc(size, size, PS_TYPE_F64);
+    psVector *sumVector = psVectorAlloc(size, PS_TYPE_F64);
+    PS_VECTOR_SET_F64(sumVector, 0.0);
+    PS_IMAGE_SET_F64(sumMatrix, 0.0);
+
+    //
+    // Verify that all stamps have similar sizes.
+    // Compute the sum matrix and vector.
+    //
+    for (psS32 s = 0; s < stamps->n; s++) {
+        pmStamp *stamp = (pmStamp *) stamps->data[s];
+
+        if (stamp->status == PM_STAMP_USED) {
+            PS_ASSERT_INT_EQUAL(((pmStamp *) stamps->data[s])->vector->n, size, NULL);
+
+            psImage *stampMatrix = stamp->matrix;
+            psVector *stampVector = stamp->vector;
+            PS_ASSERT_VECTOR_TYPE(stampVector, PS_TYPE_F64, NULL);
+            PS_ASSERT_VECTOR_SIZE(stampVector, size, NULL);
+            PS_ASSERT_IMAGE_TYPE(stampMatrix, PS_TYPE_F64, NULL);
+            PS_ASSERT_IMAGE_SIZE(stampMatrix, size, size, NULL);
+
+            (void)psBinaryOp(sumMatrix, sumMatrix, "+", stampMatrix);
+            (void)psBinaryOp(sumVector, sumVector, "+", stampVector);
+        }
+    }
+    psVector *permutation = NULL;
+    // XXX: Check output from these routines.
+
+    // XXX: psTrace()
+    if (0) {
+        PS_IMAGE_PRINT_F64(sumMatrix);
+    }
+
+    psImage *luMatrix = psMatrixLUD(NULL, &permutation, sumMatrix);
+    if (luMatrix == NULL) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to LU-Decompose the matrix.\n");
+        psFree(sumMatrix);
+        psFree(sumVector);
+        psFree(luMatrix);
+        psFree(permutation);
+        return(NULL);
+    }
+    // XXX: psTrace()
+    if (0) {
+        PS_IMAGE_PRINT_F64(luMatrix);
+    }
+
+    solution = psMatrixLUSolve(solution, luMatrix, sumVector, permutation);
+    // XXX: psTrace()
+    // XXX: should we be checking for NAN's in the solution vector?
+    if (0) {
+        PS_VECTOR_PRINT_F64(solution);
+    }
+    if (solution == NULL) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to solve the matrix.\n");
+        psFree(sumMatrix);
+        psFree(sumVector);
+        psFree(luMatrix);
+        psFree(permutation);
+        return(NULL);
+    }
+
+    psFree(sumMatrix);
+    psFree(sumVector);
+    psFree(luMatrix);
+    psFree(permutation);
+
+    psTrace("ImageSubtract.pmSubtractionSolveEquation", 3,
+            "Exiting pmSubtractionSolveEquation()\n");
+    return(solution);
+}
+
+
+/*******************************************************************************
+ ******************************************************************************/
+static psVector *CalculateDeviations(psVector *deviations,
+                                     psArray *stamps,
+                                     psS32 footprint,
+                                     const psImage *refImage,
+                                     const psImage *inImage,
+                                     const psImage *mask,
+                                     psU32 badStampMaskVal,
+                                     const psSubtractionKernels *kernels,
+                                     const psVector *solution)
+{
+    psTrace("ImageSubtract.CalculateDeviations", 4,
+            "Calling CalculateDeviations()\n");
+    PS_ASSERT_PTR_NON_NULL(stamps, NULL);
+    if (deviations != NULL) {
+        PS_ASSERT_VECTOR_TYPE(deviations, PS_TYPE_F32, NULL);
+        PS_ASSERT_VECTORS_SIZE_EQUAL(deviations, stamps, NULL);
+    } else {
+        deviations = psVectorAlloc(stamps->n, PS_TYPE_F32);
+        // XXX: Probably not necessary.
+        PS_VECTOR_SET_F32(deviations, 0.0);
+    }
+    PS_ASSERT_IMAGE_NON_NULL(refImage, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(refImage, NULL);
+    PS_ASSERT_IMAGE_TYPE(refImage, PS_TYPE_F32, NULL);
+    PS_ASSERT_IMAGE_NON_NULL(inImage, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(inImage, NULL);
+    PS_ASSERT_IMAGE_TYPE(inImage, PS_TYPE_F32, NULL);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(refImage, inImage, NULL);
+    PS_ASSERT_IMAGE_NON_NULL(mask, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(mask, NULL);
+    PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_U8, NULL);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(refImage, mask, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(solution, NULL);
+    PS_ASSERT_VECTOR_TYPE(solution, PS_TYPE_F64, NULL);
+    PS_ASSERT_PTR_NON_NULL(kernels, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->v, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->xOrder, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->yOrder, NULL);
+    if (kernels->preCalc != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->preCalc, NULL);
+    } else {
+        if (kernels->type != PM_SUBTRACTION_KERNEL_POIS) {
+            psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                    "Unallowable operation: kernels->preCalc == NULL and kernels->type != PM_SUBTRACTION_KERNEL_POIS.\n");
+            return(NULL);
+        }
+    }
+    psS32 nBF = kernels->u->n;
+    PS_ASSERT_VECTOR_SIZE(solution, nBF+1, NULL);
+
+    psS32 kernelSize = kernels->size;
+    int xSize = footprint + kernelSize;
+    int ySize = footprint + kernelSize;
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN); // Statistics
+    psImage *subStamp = psImageAlloc(2 * xSize, 2 * ySize, PS_TYPE_F32); // Subtraction of stamp
+    for (psS32 s = 0; s < stamps->n; s++) {
+        pmStamp *stamp = stamps->data[s]; // The coordinates of the stamp of interest
+        psS32 x = stamp->x;               // Stamp x coord
+        psS32 y = stamp->y;               // Stamp y coord
+        if (stamp->status == PM_STAMP_USED) {
+
+            psRegion myReg = psRegionSet(x - xSize, x + xSize, y - ySize, y + ySize);
+            psImage *refStamp = psImageSubset((psImage *) refImage, myReg);
+            psImage *inStamp = psImageSubset((psImage *) inImage, myReg);
+            psImage *maskStamp = psImageSubset((psImage *) mask, myReg);
+            psImage *convRefStamp = ConvolveImage(refStamp, maskStamp, badStampMaskVal, solution, kernels);
+
+            // Calculate chi^2
+            (void)psBinaryOp(subStamp, inStamp, "-", convRefStamp);
+            (void)psBinaryOp(subStamp, subStamp, "/", inStamp);
+            (void)psBinaryOp(subStamp, subStamp, "*", subStamp);
+            myReg = psRegionSet(kernelSize, kernelSize + 2 * footprint,
+                                kernelSize, kernelSize + 2 * footprint);
+            psImage *subStampTrim = psImageSubset((psImage *) subStamp, myReg);
+            psImage *maskStampTrim = psImageSubset((psImage *) maskStamp, myReg);
+            psImageStats(stats, subStampTrim, maskStampTrim, badStampMaskVal);
+
+            deviations->data.F32[s] = stats->sampleMean * (psF32)footprint * (psF32)footprint * 4.0;
+            // XXX: Allocate and free these elsewhere.
+            psFree(refStamp);
+            psFree(inStamp);
+            psFree(maskStamp);
+            psFree(convRefStamp);
+            psFree(subStampTrim);
+            psFree(maskStampTrim);
+        }
+    }
+
+    psFree(stats);
+    psFree(subStamp);
+
+    psTrace("ImageSubtract.CalculateDeviations", 4,
+            "Exiting CalculateDeviations()\n");
+    return deviations;
+}
+
+/*******************************************************************************
+ ******************************************************************************/
+bool pmSubtractionRejectStamps(psArray *stamps,  ///< Array of stamps to check for rejection
+                               psImage *mask,  ///< Mask image
+                               psU32 badStampMaskVal, ///< Value to use in mask for bad stamp
+                               psS32 footprint,  ///< Region to mask if stamp is bad
+                               psF32 sigmaRej,  ///< Number of RMS deviations above zero at which to reject
+                               const psImage *refImage, ///< Reference image
+                               const psImage *inImage, ///< Input image
+                               const psVector *solution, ///< Solution vector
+                               const psSubtractionKernels *kernels ///< Array of kernel parameters
+                              )
+{
+    psTrace("ImageSubtract.pmSubtractionRejectStamps", 3,
+            "Calling pmSubtractionRejectStamps()\n");
+    PS_ASSERT_PTR_NON_NULL(stamps, false);
+    PS_ASSERT_IMAGE_NON_NULL(refImage, false);
+    PS_ASSERT_IMAGE_NON_EMPTY(refImage, false);
+    PS_ASSERT_IMAGE_TYPE(refImage, PS_TYPE_F32, false);
+    PS_ASSERT_IMAGE_NON_NULL(inImage, false);
+    PS_ASSERT_IMAGE_NON_EMPTY(inImage, false);
+    PS_ASSERT_IMAGE_TYPE(inImage, PS_TYPE_F32, false);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(refImage, inImage, false);
+    PS_ASSERT_IMAGE_NON_NULL(mask, false);
+    PS_ASSERT_IMAGE_NON_EMPTY(mask, false);
+    PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_U8, false);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(refImage, mask, false);
+    PS_ASSERT_VECTOR_NON_NULL(solution, false);
+    PS_ASSERT_VECTOR_TYPE(solution, PS_TYPE_F64, false);
+    PS_ASSERT_PTR_NON_NULL(kernels, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->v, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->xOrder, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->yOrder, false);
+    if (kernels->preCalc != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->preCalc, false);
+    } else {
+        if (kernels->type != PM_SUBTRACTION_KERNEL_POIS) {
+            psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                    "Unallowable operation: kernels->preCalc == NULL and kernels->type != PM_SUBTRACTION_KERNEL_POIS.\n");
+            return(false);
+        }
+    }
+
+    psS32 nBF = kernels->u->n;
+    PS_ASSERT_VECTOR_SIZE(solution, nBF+1, false);
+
+    psVector *deviations = CalculateDeviations(NULL,
+                           stamps,
+                           footprint,
+                           refImage,
+                           inImage,
+                           mask,
+                           badStampMaskVal,
+                           kernels,
+                           solution);
+    //
+    // Calculate the deviation from zero.
+    //
+    psF64 meanDev = 0.0;
+    psS32 numDev = 0;
+    for (psS32 i = 0; i < deviations->n; i++) {
+        pmStamp *stamp = stamps->data[i];
+        if (stamp->status == PM_STAMP_USED) {
+            meanDev += PS_SQR(deviations->data.F32[i]);
+            numDev++;
+        }
+    }
+    psF32 rmsDev = sqrtf(meanDev / (psF64)(numDev - 1));
+    psF32 limit = rmsDev * sigmaRej;
+
+    for (psS32 s = 0; s < stamps->n; s++) {
+        pmStamp *stamp = (pmStamp *) stamps->data[s];
+        if (stamp->status == PM_STAMP_USED && fabsf(deviations->data.F32[s]) > limit) {
+            // Mask out the stamp in the image so you don't find it again
+            for (psS32 y = stamp->y - footprint; y < stamp->y + footprint; y++) {
+                for (psS32 x = stamp->x - footprint; x < stamp->x + footprint; x++) {
+                    mask->data.U8[y][x] |= badStampMaskVal;
+                }
+            }
+
+            // Set stamp for replacement
+            stamp->x = 0;
+            stamp->y = 0;
+            stamp->status = PM_STAMP_REJECTED;
+        }
+    }
+
+    psFree(deviations);
+    psTrace("ImageSubtract.pmSubtractionRejectStamps", 3,
+            "Exiting pmSubtractionRejectStamps()\n");
+    return(true);
+}
+
+/*******************************************************************************
+ ******************************************************************************/
+psImage *pmSubtractionKernelImage(psImage *out,
+                                  const psVector *solution,
+                                  const psSubtractionKernels *kernels,
+                                  psF32 x,
+                                  psF32 y
+                                 )
+{
+    psTrace("ImageSubtract.pmSubtractionKernelImage", 3,
+            "Calling pmSubtractionKernelImage()\n");
+    PS_ASSERT_VECTOR_NON_NULL(solution, NULL);
+    PS_ASSERT_PTR_NON_NULL(kernels, NULL);
+    PS_ASSERT_FLOAT_WITHIN_RANGE(x, -1.0, 1.0, NULL);
+    PS_ASSERT_FLOAT_WITHIN_RANGE(y, -1.0, 1.0, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->v, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->xOrder, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->yOrder, NULL);
+    if (kernels->preCalc != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->preCalc, NULL);
+    } else {
+        if (kernels->type != PM_SUBTRACTION_KERNEL_POIS) {
+            psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                    "Unallowable operation: kernels->preCalc == NULL and kernels->type != PM_SUBTRACTION_KERNEL_POIS.\n");
+            return(NULL);
+        }
+    }
+    PS_ASSERT_INT_EQUAL(1+kernels->u->n, solution->n, NULL);
+
+    psS32 nBF = kernels->u->n;
+    psS32 spatialOrder = kernels->spatialOrder;
+    psS32 kernelSize = kernels->size;
+
+    if (out != NULL) {
+        if ((out->numCols < (1+2*kernelSize)) || (out->numRows < (1+2*kernelSize))) {
+            psLogMsg(__func__, PS_LOG_WARN, "WARNING: out image is not large enough.\n");
+            return(out);
+        }
+    } else {
+        out = psImageAlloc(1+2*kernelSize, 1+2*kernelSize, PS_TYPE_F32);
+    }
+    PS_IMAGE_SET_F32(out, 0.0);
+
+    //
+    // Generate the spatial-order polynomial.  The [i][j]-th element of
+    // the psImage polyValues will hold (x^i * y^j) for the stamp.
+    //
+    psImage *polyValues = GenSpatialOrder(spatialOrder, x, y);
+
+    // XXX: switch (i, j) so they correspond to (x, y).
+    if (kernels->type == PM_SUBTRACTION_KERNEL_ISIS) {
+        for (psS32 k = 0 ; k < nBF ; k++) {
+            psS32 xOrder = (psS32) kernels->xOrder->data.F32[k];
+            psS32 yOrder = (psS32) kernels->yOrder->data.F32[k];
+            psF64 polyVal = polyValues->data.F64[yOrder][xOrder];
+
+            // XXX: Verify that this is correct.
+            for (psS32 i = -kernelSize ; i <= kernelSize ; i++) {
+                for (psS32 j = -kernelSize ; j <= kernelSize ; j++) {
+                    psImage *preCalc = (psImage *) kernels->preCalc->data[k];
+                    out->data.F32[i+kernelSize][j+kernelSize]+=
+                        solution->data.F64[k] *
+                        preCalc->data.F32[i+kernelSize][j+kernelSize] *
+                        polyVal;
+                }
+            }
+        }
+    } else if (kernels->type == PM_SUBTRACTION_KERNEL_POIS) {
+        for (psS32 k = 0 ; k < nBF ; k++) {
+            // XXX: Why don't we have compilation warnings on type here (if
+            // we remove the (psS32) cast)?
+            psS32 u = (psS32) kernels->u->data.F32[k];
+            psS32 v = (psS32) kernels->v->data.F32[k];
+            psS32 xOrder = (psS32) kernels->xOrder->data.F32[k];
+            psS32 yOrder = (psS32) kernels->yOrder->data.F32[k];
+            // XXX: Verify that this is correct.
+
+            out->data.F32[kernelSize - v][kernelSize - u]+=
+                solution->data.F64[k] * polyValues->data.F64[yOrder][xOrder];
+        }
+    }
+    psFree(polyValues);
+
+    psTrace("ImageSubtract.pmSubtractionKernelImage", 3,
+            "Exiting pmSubtractionKernelImage()\n");
+    return(out);
+}
Index: /trunk/psModules/src/imsubtract/pmImageSubtract.h
===================================================================
--- /trunk/psModules/src/imsubtract/pmImageSubtract.h	(revision 5170)
+++ /trunk/psModules/src/imsubtract/pmImageSubtract.h	(revision 5170)
@@ -0,0 +1,127 @@
+/** @file  ImageSubtract.h
+ *
+ *  This file will contain code which creates a set of kernel basis
+ *  functions, solves for their solution, and applies them to an image.
+ *
+ *  @author Paul Price, IfA (original prototype)
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#if !defined(PM_IMAGE_COMBINE_H)
+#define PM_IMAGE_COMBINE_H
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include<stdio.h>
+#include<math.h>
+#include "pslib.h"
+#include "psConstants.h"
+
+typedef enum {
+    PM_SUBTRACTION_KERNEL_POIS,         ///< POIS kernel --- delta functions
+    PM_SUBTRACTION_KERNEL_ISIS          ///< ISIS kernel --- gaussians modified by polynomials
+} pmSubtractionKernelsType;
+
+typedef struct
+{
+    pmSubtractionKernelsType type;      ///< Type ofKernels --- allowing the use of multiple kernels
+    psVector *u, *v;                    ///< Offset (for POIS) or polynomial order (for ISIS)
+    psVector *sigma;                    ///< Width of Gaussian (for ISIS)
+    psVector *xOrder, *yOrder;          ///< Spatial Polynomial order (for all)
+    int subIndex;                       ///< Index of kernel to be subtracted to maintain flux conservation
+    psArray *preCalc;                   ///< Array of images containing pre-calculated kernel (to
+    ///< accelerate ISIS; don't use for POIS)
+    psS32 size;                         ///< The halfsize of the kernel
+    psS32 spatialOrder;                 ///< The spatial order of the kernels
+}
+psSubtractionKernels;
+
+psSubtractionKernels *pmSubtractionKernelsAllocPOIS(
+    int size,
+    int SpatialOrder
+);
+
+psSubtractionKernels *pmSubtractionKernelsAllocISIS(
+    const psVector *sigmas,
+    const psVector *orders,
+    int size,
+    int SpatialOrder
+);
+
+typedef enum {
+    PM_STAMP_INIT,                      ///< Initial state
+    PM_STAMP_USED,                      ///< Use this stamp
+    PM_STAMP_REJECTED,                  ///< This stamp has been rejected
+    PM_STAMP_RECALC,                    ///< Having been reset, this stamp is to be recalculated
+    PM_STAMP_NONE                       ///< No stamp in this region
+} pmStampStatus;
+
+typedef struct
+{
+    int x, y;                           ///< Position
+    int p_xSize;
+    int p_ySize;
+    int p_xMin;
+    int p_xMax;
+    int p_yMin;
+    int p_yMax;
+    psImage *matrix;                    ///< Associated matrix
+    psVector *vector;                   ///< Assoicated vector
+    pmStampStatus status;               ///< Status ofstamp
+}
+pmStamp;
+
+psArray *pmSubtractionFindStamps(
+    psArray *stamps,                    ///< Output stamps, or NULL
+    const psImage *image,               ///< Image for which to find stamps
+    const psImage *mask,                ///< Mask
+    psU32 maskVal,                      ///< Value for mask
+    psF32 threshold,                    ///< Threshold for stamps in the image
+    psS32 xNum,                         ///< Number of stamps in x
+    psS32 yNum,                         ///< Number of stamps in y
+    psS32 border                        ///< Border around image to ignore (should be size of kernel)
+);
+
+bool pmSubtractionCalculateEquation(
+    psArray *stamps,                    ///< The stamps for which to calculate the equation,
+    const psImage *reference,           ///< Reference image
+    const psImage *input,               ///< Input image
+    const psSubtractionKernels *kernels,///< The kernel basis functions
+    psS32 footprint                     ///< Half-size of region over which to calculate equation
+);
+
+
+psVector *pmSubtractionSolveEquation(
+    psVector *solution,                 ///< Solution vector, or NULL
+    const psArray *stamps               ///< Array of stamps
+);
+
+bool pmSubtractionRejectStamps(
+    psArray *stamps,                    ///< Array of stamps to check for rejection
+    psImage *mask,                      ///< Mask image
+    psU32 badStampMaskVal,              ///< Value to use in mask for bad stamp
+    psS32 footprint,                    ///< Region to mask if stamp is bad
+    psF32 sigmaRej,                     ///< Number of RMS deviations above zero at which to reject
+    const psImage *refImage,            ///< Reference image
+    const psImage *inImage,             ///< Input image
+    const psVector *solution,           ///< Solution vector
+    const psSubtractionKernels *kernels ///< Array of kernel parameters
+);
+
+psImage *pmSubtractionKernelImage(
+    psImage *out,
+    const psVector *solution,
+    const psSubtractionKernels *kernels,
+    psF32 x,
+    psF32 y
+);
+
+#endif
Index: /trunk/psModules/src/imsubtract/pmSubtractBias.c
===================================================================
--- /trunk/psModules/src/imsubtract/pmSubtractBias.c	(revision 5170)
+++ /trunk/psModules/src/imsubtract/pmSubtractBias.c	(revision 5170)
@@ -0,0 +1,682 @@
+/** @file  pmSubtractBias.c
+ *
+ *  This file will contain a module which will subtract the detector bias
+ *  in place from an input image.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "pmSubtractBias.h"
+
+#define PM_SUBTRACT_BIAS_POLYNOMIAL_ORDER 2
+#define PM_SUBTRACT_BIAS_SPLINE_ORDER 3
+
+// XXX: put these in psConstants.h
+void PS_POLY1D_PRINT(psPolynomial1D *poly)
+{
+    printf("-------------- PS_POLY1D_PRINT() --------------\n");
+    printf("poly->COOL_1D_n is %d\n", poly->COOL_1D_n);
+    for (psS32 i = 0 ; i < (1 + poly->COOL_1D_n) ; i++) {
+        printf("poly->coeff[%d] is %f\n", i, poly->coeff[i]);
+    }
+}
+
+void PS_PRINT_SPLINE(psSpline1D *mySpline)
+{
+    printf("-------------- PS_PRINT_SPLINE() --------------\n");
+    printf("mySpline->n is %d\n", mySpline->n);
+    for (psS32 i = 0 ; i < mySpline->n ; i++) {
+        PS_POLY1D_PRINT(mySpline->spline[i]);
+    }
+    PS_VECTOR_PRINT_F32(mySpline->knots);
+}
+
+#define PS_IMAGE_PRINT_F32_HIDEF(NAME) \
+printf("======== printing %s ========\n", #NAME); \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        printf("%.5f ", (NAME)->data.F32[i][j]); \
+    } \
+    printf("\n"); \
+}\
+
+/******************************************************************************
+psSubtractFrame(): this routine will take as input a readout for the input
+image and a readout for the bias image.  The bias image is subtracted in
+place from the input image.
+*****************************************************************************/
+static pmReadout *SubtractFrame(pmReadout *in,
+                                const pmReadout *bias)
+{
+    psS32 i;
+    psS32 j;
+
+    if (bias == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: pmSubtractBias.c: SubtractFrame(): bias frame is NULL.  Returning original image.\n");
+        return(in);
+    }
+
+
+    if ((in->image->numRows + in->row0 - bias->row0) > bias->image->numRows) {
+        psError(PS_ERR_UNKNOWN,true, "bias image does not have enough rows.  Returning in image\n");
+        return(in);
+    }
+    if ((in->image->numCols + in->col0 - bias->col0) > bias->image->numCols) {
+        psError(PS_ERR_UNKNOWN,true, "bias image does not have enough columns.  Returning in image\n");
+        return(in);
+    }
+
+    for (i=0;i<in->image->numRows;i++) {
+        for (j=0;j<in->image->numCols;j++) {
+            in->image->data.F32[i][j]-=
+                bias->image->data.F32[i+in->row0-bias->row0][j+in->col0-bias->col0];
+            if ((in->mask != NULL) && (bias->mask != NULL)) {
+                (in->mask->data.U8[i][j])|=
+                    bias->mask->data.U8[i+in->row0-bias->row0][j+in->col0-bias->col0];
+            }
+        }
+    }
+
+    return(in);
+}
+
+/******************************************************************************
+ImageSubtractScalar(): subtract a scalar from the input image.
+ 
+XXX: Use a psLib function for this.
+ 
+XXX: This should
+ *****************************************************************************/
+static psImage *ImageSubtractScalar(psImage *image,
+                                    psF32 scalar)
+{
+    for (psS32 i=0;i<image->numRows;i++) {
+        for (psS32 j=0;j<image->numCols;j++) {
+            image->data.F32[i][j]-= scalar;
+        }
+    }
+    return(image);
+}
+
+/******************************************************************************
+GenNewStatOptions(): this routine will take as input the options member of the
+stat data structure, determine if multiple options have been specified, issue
+a warning message if so, and return the highest priority option (according to
+the order of the if-statements in this code).  The higher priority options are
+listed lower in the code.
+ *****************************************************************************/
+static psStatsOptions GenNewStatOptions(const psStats *stat)
+{
+    psS32 numOptions = 0;
+    psStatsOptions opt = 0;
+
+    if (stat->options & PS_STAT_ROBUST_MODE) {
+        if (numOptions == 0) {
+            opt = PS_STAT_ROBUST_MODE;
+        }
+        numOptions++;
+    }
+
+    if (stat->options & PS_STAT_ROBUST_MEDIAN) {
+        if (numOptions == 0) {
+            opt = PS_STAT_ROBUST_MEDIAN;
+        }
+        numOptions++;
+    }
+
+    if (stat->options & PS_STAT_ROBUST_MEAN) {
+        if (numOptions == 0) {
+            opt = PS_STAT_ROBUST_MEAN;
+        }
+        numOptions++;
+    }
+
+    if (stat->options & PS_STAT_CLIPPED_MEAN) {
+        if (numOptions == 0) {
+            opt = PS_STAT_CLIPPED_MEAN;
+        }
+        numOptions++;
+    }
+
+    if (stat->options & PS_STAT_SAMPLE_MEDIAN) {
+        if (numOptions == 0) {
+            opt = PS_STAT_SAMPLE_MEDIAN;
+        }
+        numOptions++;
+    }
+
+    if (stat->options & PS_STAT_SAMPLE_MEAN) {
+        numOptions++;
+        opt = PS_STAT_SAMPLE_MEAN;
+    }
+
+
+    if (numOptions == 0) {
+        psError(PS_ERR_UNKNOWN,true, "No statistics options have been specified.\n");
+    }
+    if (numOptions != 1) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: pmSubtractBias.c: GenNewStatOptions(): Too many statistics options have been specified\n");
+    }
+    return(opt);
+}
+
+
+
+/******************************************************************************
+ScaleOverscanVector(): this routine takes as input an arbitrary vector,
+creates a new vector of length n, and fills the new vector with the
+interpolated values of the old vector.  The type of interpolation is:
+    PM_FIT_POLYNOMIAL: fit a polynomial to the entire input vector data.
+    PM_FIT_SPLINE: fit splines to the input vector data.
+XXX: Doesn't it make more sense to do polynomial interpolation on a few
+elements of the input vector, rather than fit a polynomial to the entire
+vector?
+ *****************************************************************************/
+static psVector *ScaleOverscanVector(psVector *overscanVector,
+                                     psS32 n,
+                                     void *fitSpec,
+                                     pmFit fit)
+{
+    psTrace(".psModule.pmSubtracBias.ScaleOverscanVector", 4,
+            "---- ScaleOverscanVector() begin (%d -> %d) ----\n", overscanVector->n, n);
+    //    PS_VECTOR_PRINT_F32(overscanVector);
+
+    if (NULL == overscanVector) {
+        return(overscanVector);
+    }
+
+    // Allocate the new vector.
+    psVector *newVec = psVectorAlloc(n, PS_TYPE_F32);
+
+    //
+    // If the new vector is the same size as the old, simply copy the data.
+    //
+    if (n == overscanVector->n) {
+        for (psS32 i = 0 ; i < n ; i++) {
+            newVec->data.F32[i] = overscanVector->data.F32[i];
+        }
+        return(newVec);
+    }
+    psPolynomial1D *myPoly;
+    psSpline1D *mySpline;
+    psF32 x;
+    psS32 i;
+
+    if (fit == PM_FIT_POLYNOMIAL) {
+        // Fit a polynomial to the old overscan vector.
+        myPoly = (psPolynomial1D *) fitSpec;
+        PS_ASSERT_POLY_NON_NULL(myPoly, NULL);
+        myPoly = psVectorFitPolynomial1D(myPoly, NULL, 0, overscanVector, NULL, NULL);
+        if (myPoly == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector()(1): Could not fit a polynomial to the psVector.\n");
+            return(NULL);
+        }
+
+        // For each element of the new vector, convert the x-ordinate to that
+        // of the old vector, use the fitted polynomial to determine the
+        // interpolated value at that point, and set the new vector.
+        for (i=0;i<n;i++) {
+            x = ((psF32) i) * ((psF32) overscanVector->n) / ((psF32) n);
+            newVec->data.F32[i] = psPolynomial1DEval(myPoly, x);
+        }
+    } else if (fit == PM_FIT_SPLINE) {
+        psS32 mustFreeSpline = 0;
+        // Fit a spline to the old overscan vector.
+        mySpline = (psSpline1D *) fitSpec;
+        if (mySpline == NULL) {
+            mustFreeSpline = 1;
+        }
+
+        //
+        // NOTE: Since the X arg in the psVectorFitSpline1D() function is NULL,
+        // splines enpoints will be from 0.0 to overscanVector->n-1.  Must scale
+        // properly when doing the spline eval.
+        //
+        mySpline = psVectorFitSpline1D(mySpline, NULL, overscanVector, NULL);
+        if (mySpline == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector()(2): Could not fit a spline to the psVector.\n");
+            return(NULL);
+        }
+        //        PS_PRINT_SPLINE(mySpline);
+
+        // For each element of the new vector, convert the x-ordinate to that
+        // of the old vector, use the fitted polynomial to determine the
+        // interpolated value at that point, and set the new vector.
+        for (i=0;i<n;i++) {
+            // Scale to [0 : overscanVector->n - 1]
+            x = ((psF32) i) * ((psF32) (overscanVector->n-1)) / ((psF32) n);
+            newVec->data.F32[i] = psSpline1DEval(mySpline, x);
+        }
+        if (mustFreeSpline ==1) {
+            psFree(mySpline);
+        }
+        //        PS_VECTOR_PRINT_F32(newVec);
+
+
+    } else {
+        psError(PS_ERR_UNKNOWN, true, "unknown fit type.  Returning NULL.\n");
+        psFree(newVec);
+        return(NULL);
+    }
+
+    psTrace(".psModule.pmSubtracBias.ScaleOverscanVector", 4,
+            "---- ScaleOverscanVector() exit ----\n");
+    return(newVec);
+}
+
+/******************************************************************************
+XXX: The SDRS does not specify type support.  F32 is implemented here.
+ *****************************************************************************/
+pmReadout *pmSubtractBias(pmReadout *in,
+                          void *fitSpec,
+                          const psList *overscans,
+                          pmOverscanAxis overScanAxis,
+                          psStats *stat,
+                          psS32 nBinOrig,
+                          pmFit fit,
+                          const pmReadout *bias)
+{
+    psTrace(".psModule.pmSubtracBias.pmSubtractBias", 4,
+            "---- pmSubtractBias() begin ----\n");
+    PS_ASSERT_READOUT_NON_NULL(in, NULL);
+    PS_ASSERT_READOUT_NON_EMPTY(in, NULL);
+    PS_ASSERT_READOUT_TYPE(in, PS_TYPE_F32, NULL);
+
+    //
+    // If the overscans != NULL, then check the type of each image.
+    //
+    if (overscans != NULL) {
+        psListElem *tmpOverscan = (psListElem *) overscans->head;
+        while (NULL != tmpOverscan) {
+            psImage *myOverscanImage = (psImage *) tmpOverscan->data;
+            PS_ASSERT_IMAGE_TYPE(myOverscanImage, PS_TYPE_F32, NULL);
+            tmpOverscan = tmpOverscan->next;
+        }
+    }
+
+    if ((overscans == NULL) && (overScanAxis != PM_OVERSCAN_NONE)) {
+        psError(PS_ERR_UNKNOWN,true, "(overscans == NULL) && (overScanAxis != PM_OVERSCAN_NONE).  Returning in image\n");
+        return(in);
+    }
+
+    // Check for an unallowable pmFit.
+    if ((fit != PM_OVERSCAN_NONE) &&
+            (fit != PM_OVERSCAN_ROWS) &&
+            (fit != PM_OVERSCAN_COLUMNS) &&
+            (fit != PM_OVERSCAN_ALL)) {
+        psError(PS_ERR_UNKNOWN, true, "fit is unallowable (%d).  Returning in image.\n", fit);
+        return(in);
+    }
+    // Check for an unallowable pmOverscanAxis.
+    if ((overScanAxis != PM_OVERSCAN_NONE) &&
+            (overScanAxis != PM_OVERSCAN_ROWS) &&
+            (overScanAxis != PM_OVERSCAN_COLUMNS) &&
+            (overScanAxis != PM_OVERSCAN_ALL)) {
+        psError(PS_ERR_UNKNOWN, true, "overScanAxis is unallowable (%d).  Returning in image.\n", overScanAxis);
+        return(in);
+    }
+    psS32 i;
+    psS32 j;
+    psS32 numBins = 0;
+    static psVector *overscanVector = NULL;
+    psVector *tmpRow = NULL;
+    psVector *tmpCol = NULL;
+    psVector *myBin = NULL;
+    psVector *binVec = NULL;
+    psListElem *tmpOverscan = NULL;
+    double statValue;
+    psImage *myOverscanImage = NULL;
+    psPolynomial1D *myPoly = NULL;
+    psSpline1D *mySpline = NULL;
+    psS32 nBin;
+
+    //
+    //  Create a static stats data structure and determine the highest
+    //  priority stats option.
+    //
+    static psStats *myStats = NULL;
+    if (myStats == NULL) {
+        myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        p_psMemSetPersistent(myStats, true);
+    }
+    if (stat != NULL) {
+        myStats->options = GenNewStatOptions(stat);
+    }
+
+
+    if (overScanAxis == PM_OVERSCAN_NONE) {
+        if (fit != PM_FIT_NONE) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: pmSubtractBias.(): overScanAxis equals NONE, and fit does not equal NONE.  Proceeding to full fram subtraction.\n");
+        }
+
+        if (overscans != NULL) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: pmSubtractBias.(): overScanAxis equals NONE and overscans does not equal NULL.  Proceeding to full fram subtraction.\n");
+        }
+        return(SubtractFrame(in, bias));
+    }
+
+    if ((overScanAxis == PM_OVERSCAN_ALL) && (fit != PM_FIT_NONE)) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: pmSubtractBias.(): overScanAxis equals ALL, and fit does not equal NONE.  Proceeding with the rest of the module.\n");
+    }
+
+
+    //
+    // We subtract each overscan region from the image data.
+    // If we get here we know that overscans != NULL.
+    //
+
+    if (overScanAxis == PM_OVERSCAN_ALL) {
+        tmpOverscan = (psListElem *) overscans->head;
+        while (NULL != tmpOverscan) {
+            myOverscanImage = (psImage *) tmpOverscan->data;
+
+            PS_ASSERT_IMAGE_TYPE(myOverscanImage, PS_TYPE_F32, NULL);
+            psStats *rc = psImageStats(myStats, myOverscanImage, NULL, (psMaskType)0xffffffff);
+            if (rc == NULL) {
+                psError(PS_ERR_UNKNOWN, false, "psImageStats(): could not perform requested statistical operation.  Returning in image.\n");
+                return(in);
+            }
+            if (false == p_psGetStatValue(myStats, &statValue)) {
+                psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
+                return(in);
+            }
+            ImageSubtractScalar(in->image, statValue);
+
+            tmpOverscan = tmpOverscan->next;
+        }
+        return(in);
+    }
+
+    // This check is redundant with above code.
+    if (!((overScanAxis == PM_OVERSCAN_ROWS) || (overScanAxis == PM_OVERSCAN_COLUMNS))) {
+        psError(PS_ERR_UNKNOWN, true, "overScanAxis is unallowable (%d).\nReturning in image.\n", overScanAxis);
+        return(in);
+    }
+
+    tmpOverscan = (psListElem *) overscans->head;
+    while (NULL != tmpOverscan) {
+        //        PS_IMAGE_PRINT_F32_HIDEF(in->image);
+        myOverscanImage = (psImage *) tmpOverscan->data;
+
+        if (overScanAxis == PM_OVERSCAN_ROWS) {
+            if (myOverscanImage->numCols != (in->image)->numCols) {
+                psLogMsg(__func__, PS_LOG_WARN,
+                         "WARNING: pmSubtractBias.(): overscan image has %d columns, input image has %d columns\n",
+                         myOverscanImage->numCols, in->image->numCols);
+            }
+
+            // We create a row vector and subtract this vector from image.
+            // XXX: Is there a better way to extract a psVector from a psImage without
+            // having to copy every element in that vector?
+            overscanVector = psVectorAlloc(myOverscanImage->numCols, PS_TYPE_F32);
+            for (i=0;i<overscanVector->n;i++) {
+                overscanVector->data.F32[i] = 0.0;
+            }
+            tmpRow = psVectorAlloc(myOverscanImage->numRows, PS_TYPE_F32);
+
+            // For each column of the input image, loop through every row,
+            // collect the pixel in that row, then performed the specified
+            // statistical op on those pixels.  Store this in overscanVector.
+            for (i=0;i<myOverscanImage->numCols;i++) {
+                for (j=0;j<myOverscanImage->numRows;j++) {
+                    tmpRow->data.F32[j] = myOverscanImage->data.F32[j][i];
+                }
+                psStats *rc = psVectorStats(myStats, tmpRow, NULL, NULL, 0);
+                if (rc == NULL) {
+                    psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning in image.\n");
+                    return(in);
+                }
+                if (false ==  p_psGetStatValue(rc, &statValue)) {
+                    psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
+                    return(in);
+                }
+                overscanVector->data.F32[i] = statValue;
+            }
+            psFree(tmpRow);
+
+            // Scale the overscan vector to the size of the input image.
+            if (overscanVector->n != in->image->numCols) {
+                if ((fit == PM_FIT_POLYNOMIAL) || (fit == PM_FIT_SPLINE)) {
+                    psVector *newVec = ScaleOverscanVector(overscanVector,
+                                                           in->image->numCols,
+                                                           fitSpec, fit);
+                    if (newVec == NULL) {
+                        psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector(): could not scale the overscan vector.  Returning in image.\n");
+                        return(in);
+                    }
+                    psFree(overscanVector);
+                    overscanVector = newVec;
+                } else {
+                    psError(PS_ERR_UNKNOWN, true, "Don't know how to scale the overscan vector.  Set fit to PM_FIT_SPLINE or PM_FIT_POLYNOMIAL.  Returning in image.\n");
+                    psFree(overscanVector);
+                    return(in);
+                }
+            }
+        }
+
+        if (overScanAxis == PM_OVERSCAN_COLUMNS) {
+            if (myOverscanImage->numRows != (in->image)->numRows) {
+                psLogMsg(__func__, PS_LOG_WARN,
+                         "WARNING: pmSubtractBias.(): overscan image has %d rows, input image has %d rows\n",
+                         myOverscanImage->numRows, in->image->numRows);
+            }
+
+            // We create a column vector and subtract this vector from image.
+            overscanVector = psVectorAlloc(myOverscanImage->numRows, PS_TYPE_F32);
+            for (i=0;i<overscanVector->n;i++) {
+                overscanVector->data.F32[i] = 0.0;
+            }
+            tmpCol = psVectorAlloc(myOverscanImage->numCols, PS_TYPE_F32);
+
+            // For each row of the input image, loop through every column,
+            // collect the pixel in that row, then performed the specified
+            // statistical op on those pixels.  Store this in overscanVector.
+            for (i=0;i<myOverscanImage->numRows;i++) {
+                for (j=0;j<myOverscanImage->numCols;j++) {
+                    tmpCol->data.F32[j] = myOverscanImage->data.F32[i][j];
+                }
+                psStats *rc = psVectorStats(myStats, tmpCol, NULL, NULL, 0);
+                if (rc == NULL) {
+                    psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning in image.\n");
+                    return(in);
+                }
+                if (false ==  p_psGetStatValue(rc, &statValue)) {
+                    psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
+                    return(in);
+                }
+                overscanVector->data.F32[i] = statValue;
+            }
+            psFree(tmpCol);
+
+            // Scale the overscan vector to the size of the input image.
+            if (overscanVector->n != in->image->numRows) {
+                if ((fit == PM_FIT_POLYNOMIAL) || (fit == PM_FIT_SPLINE)) {
+                    psVector *newVec = ScaleOverscanVector(overscanVector,
+                                                           in->image->numRows,
+                                                           fitSpec, fit);
+                    if (newVec == NULL) {
+                        psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector(): could not scale the overscan vector.  Returning in image.\n");
+                        return(in);
+                    }
+                    psFree(overscanVector);
+                    overscanVector = newVec;
+                } else {
+                    psError(PS_ERR_UNKNOWN, true, "Don't know how to scale the overscan vector.  Set fit to PM_FIT_SPLINE or PM_FIT_POLYNOMIAL.  Returning in image.\n");
+                    psFree(overscanVector);
+                    return(in);
+                }
+            }
+        }
+
+        //
+        // Re-bin the overscan vector (change its length).
+        //
+        // Only if nBinOrig > 1.
+        if ((nBinOrig > 1) && (nBinOrig < overscanVector->n)) {
+            numBins = 1+((overscanVector->n)/nBinOrig);
+            myBin = psVectorAlloc(numBins, PS_TYPE_F32);
+            binVec = psVectorAlloc(nBinOrig, PS_TYPE_F32);
+
+            for (i=0;i<numBins;i++) {
+                for(j=0;j<nBinOrig;j++) {
+                    if (overscanVector->n > ((i*nBinOrig)+j)) {
+                        binVec->data.F32[j] = overscanVector->data.F32[(i*nBinOrig)+j];
+                    } else {
+                        // XXX: we get here if nBinOrig does not evenly divide
+                        // the overscanVector vector.  This is the last bin.  Should
+                        // we change the binVec->n to acknowledge that?
+                        binVec->n = j;
+                    }
+                }
+                psStats *rc = psVectorStats(myStats, binVec, NULL, NULL, 0);
+                if (rc == NULL) {
+                    psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning in image.\n");
+                    return(in);
+                }
+                if (false ==  p_psGetStatValue(rc, &statValue)) {
+                    psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
+                    return(in);
+                }
+                myBin->data.F32[i] = statValue;
+            }
+
+            // Change the effective size of overscanVector.
+            overscanVector->n = numBins;
+            for (i=0;i<numBins;i++) {
+                overscanVector->data.F32[i] = myBin->data.F32[i];
+            }
+            psFree(binVec);
+            psFree(myBin);
+            nBin = nBinOrig;
+        } else {
+            nBin = 1;
+        }
+
+        // At this point the number of data points in overscanVector should be
+        // equal to the number of rows/columns (whatever is appropriate) in the
+        // image divided by numBins.
+        //
+
+
+        //
+        // This doesn't seem right.  The only way to do a spline fit is if,
+        // by SDRS requirements, fitSpec is not-NULL>  But in order for it
+        // to be non-NULL, someone must have called psSpline1DAlloc() with
+        // the min, max, and number of splines.
+        //
+        if (!((fitSpec == NULL) || (fit == PM_FIT_NONE))) {
+            //
+            // Fit a polynomial or spline to the overscan vector.
+            //
+            if (fit == PM_FIT_POLYNOMIAL) {
+                myPoly = (psPolynomial1D *) fitSpec;
+                myPoly = psVectorFitPolynomial1D(myPoly, NULL, 0, overscanVector, NULL, NULL);
+                if (myPoly == NULL) {
+                    psError(PS_ERR_UNKNOWN, false, "(3) Could not fit a polynomial to overscan vector.  Returning in image.\n");
+                    psFree(overscanVector);
+                    return(in);
+                }
+            } else if (fit == PM_FIT_SPLINE) {
+                mySpline = (psSpline1D *) fitSpec;
+                mySpline = psVectorFitSpline1D(mySpline, NULL, overscanVector, NULL);
+                if (mySpline == NULL) {
+                    psError(PS_ERR_UNKNOWN, false, "Could not fit a spline to overscan vector.  Returning in image.\n");
+                    psFree(overscanVector);
+                    return(in);
+                }
+            }
+
+            //
+            // Subtract fitted overscan vector row-wise from the image.
+            //
+            if (overScanAxis == PM_OVERSCAN_ROWS) {
+                for (i=0;i<(in->image)->numCols;i++) {
+                    psF32 tmpF32 = 0.0;
+                    if (fit == PM_FIT_POLYNOMIAL) {
+                        tmpF32 = psPolynomial1DEval(myPoly, ((psF32) i) / ((psF32) nBin));
+                    } else if (fit == PM_FIT_SPLINE) {
+                        tmpF32 = psSpline1DEval(mySpline, ((psF32) i) / ((psF32) nBin));
+                    }
+                    for (j=0;j<(in->image)->numRows;j++) {
+                        (in->image)->data.F32[j][i]-= tmpF32;
+                    }
+                }
+            }
+
+            //
+            // Subtract fitted overscan vector column-wise from the image.
+            //
+            if (overScanAxis == PM_OVERSCAN_COLUMNS) {
+                for (i=0;i<(in->image)->numRows;i++) {
+                    psF32 tmpF32 = 0.0;
+                    if (fit == PM_FIT_POLYNOMIAL) {
+                        tmpF32 = psPolynomial1DEval(myPoly, ((psF32) i) / ((psF32) nBin));
+                    } else if (fit == PM_FIT_SPLINE) {
+                        tmpF32 = psSpline1DEval(mySpline, ((psF32) i) / ((psF32) nBin));
+                    }
+
+                    for (j=0;j<(in->image)->numCols;j++) {
+                        (in->image)->data.F32[i][j]-= tmpF32;
+                    }
+                }
+            }
+        } else {
+            //
+            // If we get here, then no polynomials were fit to the overscan
+            // vector.  We simply subtract it, taking into account binning,
+            // from the image.
+            //
+
+            //
+            // Subtract overscan vector row-wise from the image.
+            //
+            if (overScanAxis == PM_OVERSCAN_ROWS) {
+                for (i=0;i<(in->image)->numCols;i++) {
+                    for (j=0;j<(in->image)->numRows;j++) {
+                        (in->image)->data.F32[j][i]-= overscanVector->data.F32[i/nBin];
+                    }
+                }
+            }
+
+            //
+            // Subtract overscan vector column-wise from the image.
+            //
+            if (overScanAxis == PM_OVERSCAN_COLUMNS) {
+                for (i=0;i<(in->image)->numRows;i++) {
+                    for (j=0;j<(in->image)->numCols;j++) {
+                        (in->image)->data.F32[i][j]-= overscanVector->data.F32[i/nBin];
+                    }
+                }
+            }
+        }
+
+        psFree(overscanVector);
+
+        tmpOverscan = tmpOverscan->next;
+    }
+
+    psTrace(".psModule.pmSubtracBias.pmSubtractBias", 4,
+            "---- pmSubtractBias() exit ----\n");
+
+    if (bias != NULL) {
+        return(SubtractFrame(in, bias));
+    }
+    return(in);
+}
+
+
Index: /trunk/psModules/src/imsubtract/pmSubtractBias.h
===================================================================
--- /trunk/psModules/src/imsubtract/pmSubtractBias.h	(revision 5170)
+++ /trunk/psModules/src/imsubtract/pmSubtractBias.h	(revision 5170)
@@ -0,0 +1,49 @@
+/** @file  pmSubtractBias.h
+ *
+ *  This file will contain a module which will subtract the detector bias
+ *  in place from an input image.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#if !defined(PM_SUBTRACT_BIAS_H)
+#define PM_SUBTRACT_BIAS_H
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include<stdio.h>
+#include<math.h>
+#include "pslib.h"
+#include "pmAstrometry.h"
+
+typedef enum {
+    PM_OVERSCAN_NONE,                         ///< No overscan subtraction
+    PM_OVERSCAN_ROWS,                         ///< Subtract rows
+    PM_OVERSCAN_COLUMNS,                      ///< Subtract columns
+    PM_OVERSCAN_ALL                           ///< Subtract the statistic of all pixels in overscan region
+} pmOverscanAxis;
+
+typedef enum {
+    PM_FIT_NONE,                              ///< No fit
+    PM_FIT_POLYNOMIAL,                        ///< Fit polynomial
+    PM_FIT_SPLINE                             ///< Fit cubic splines
+} pmFit;
+
+pmReadout *pmSubtractBias(pmReadout *in,                ///< The input pmReadout image
+                          void *fitSpec,                ///< A polynomial or spline, defining the fit type.
+                          const psList *overscans,      ///< A psList of overscan images
+                          pmOverscanAxis overScanAxis,  ///< Defines how overscans are applied
+                          psStats *stat,                ///< The statistic to be used in combining overscan data
+                          int nBin,                     ///< The amount of binning to be done image pixels.
+                          pmFit fit,                    ///< PM_FIT_SPLINE, PM_FIT_POLYNOMIAL, or PM_FIT_NONE
+                          const pmReadout *bias);       ///< A possibly NULL bias pmReadout which is to be subtracted
+
+#endif
Index: /trunk/psModules/src/imsubtract/pmSubtractSky.c
===================================================================
--- /trunk/psModules/src/imsubtract/pmSubtractSky.c	(revision 5170)
+++ /trunk/psModules/src/imsubtract/pmSubtractSky.c	(revision 5170)
@@ -0,0 +1,720 @@
+/** @file  pmSubtractSky.c
+ *
+ *  This file will contain a module which will create a model of the
+ *  background sky and subtract that from the input image.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ *      
+ *
+ */
+
+#include<stdio.h>
+#include<math.h>
+#include "pslib.h"
+#include "psConstants.h"
+#include "pmSubtractSky.h"
+
+/******************************************************************************
+DetermineNumBits(data): This routine takes an enum psStatsOptions as an
+argument and returns the number of non-zero bits.
+ 
+XXX: This code is duplicated in the ReadoutCombine file.
+ *****************************************************************************/
+static psS32 DetermineNumBits(psStatsOptions data)
+{
+    psTrace("SubtractSky.DetermineNumBits", 4, "Calling DetermineNumBits(0x%x)\n", data);
+
+    psS32 i;
+    psU64 tmpData = data;
+    psS32 numBits = 0;
+
+    for (i=0;i<(8 * sizeof(psStatsOptions));i++) {
+        if (0x0001 & tmpData) {
+            numBits++;
+        }
+        tmpData = tmpData >> 1;
+    }
+
+    psTrace("SubtractSky.DetermineNumBits", 4,
+            "Calling DetermineNumBits(0x%x) -> %d\n", data, numBits);
+    return(numBits);
+}
+
+/******************************************************************************
+getHighestPriorityStatOption(statOptions): this routine takes as input a
+psStats->options with multiple options set and returns one with a single
+option set according to the precedence set in the SDRS.
+ *****************************************************************************/
+static psU64 getHighestPriorityStatOption(psU64 statOptions)
+{
+    psTrace("SubtractSky.getHighestPriorityStatOption", 4,
+            "Calling getHighestPriorityStatOption(0x%x)\n", statOptions);
+
+    if (statOptions & PS_STAT_SAMPLE_MEAN) {
+        return(PS_STAT_SAMPLE_MEAN);
+    } else if (statOptions & PS_STAT_SAMPLE_MEDIAN) {
+        return(PS_STAT_SAMPLE_MEDIAN);
+    } else if (statOptions & PS_STAT_CLIPPED_MEAN) {
+        return(PS_STAT_CLIPPED_MEAN);
+    } else if (statOptions & PS_STAT_ROBUST_MEAN) {
+        return(PS_STAT_ROBUST_MEAN);
+    } else if (statOptions & PS_STAT_ROBUST_MEDIAN) {
+        return(PS_STAT_ROBUST_MEDIAN);
+    } else if (statOptions & PS_STAT_ROBUST_MODE) {
+        return(PS_STAT_ROBUST_MODE);
+    }
+    psError(PS_ERR_UNKNOWN, true, "Unallowable option requested for statistically binning image pixels.\n");
+    return(-1);
+}
+
+/******************************************************************************
+psImage *binImage(origImage, binFactor, statOptions): This routine takes an
+input psImage and scales it smaller by a factor of binFactor.  The statistic
+used in combining input pixels is specified in statOptions.
+ 
+XXX: use static vectors for myStats, binVector and binMask.
+XXX: I coded this before I was aware of a psLib reBin function.  I don't
+use this function in this module.  I'm keeping it here in the event that
+requirements change and we might need a custom reBin function.
+ *****************************************************************************/
+/*
+static psImage *binImage(psImage *origImage,
+                         int binFactor,
+                         psStatsOptions statOptions)
+{
+    psTrace("SubtractSky.binImage", 4, "Calling binImage(%d)\n", binFactor);
+ 
+    if (binFactor <= 0) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: binImage(): binFactor is %d\n", binFactor);
+        return(origImage);
+    }
+    if (binFactor == 1) {
+        return(origImage);
+    }
+ 
+    psVector *binVector = psVectorAlloc(binFactor * binFactor, PS_TYPE_F32);
+    psVector *binMask = psVectorAlloc(binFactor * binFactor, PS_TYPE_U8);
+    psStats *myStats = psStatsAlloc(statOptions);
+ 
+    for (psS32 row = 0; row < origImage->numRows ; row+=binFactor) {
+        for (psS32 col = 0; col < origImage->numCols ; col+=binFactor) {
+            psS32 count = 0;
+            for (psS32 binRow = 0; binRow <= binFactor ; binRow++) {
+                for (psS32 binCol = 0; binCol <= binFactor ; binCol++) {
+                    if (((row + binRow) < origImage->numRows) &&
+                            ((col + binCol) < origImage->numCols)) {
+                        binVector->data.F32[count] =
+                            origImage->data.F32[row + binRow][col + binCol];
+                        binMask->data.U8[count] = 0;
+                    } else {
+                        binVector->data.F32[count] = 0.0;
+                        binMask->data.U8[count] = 1;
+                    }
+                    count++;
+                }
+            }
+            psStats *rc1 = psVectorStats(myStats, binVector, NULL, binMask, 1);
+            if (rc1 == NULL) {
+                psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning in image.\n");
+                return(origImage);
+            }
+            psF64 statValue;
+            psBool rc = p_psGetStatValue(rc1, &statValue);
+ 
+            if (rc == true) {
+                origImage->data.F32[row][col] = (psF32) statValue;
+            } else {
+                origImage->data.F32[row][col] = 0.0;
+                psLogMsg(__func__, PS_LOG_WARN,
+                         "WARNING: pmSubtractSky(), binImage(): p_psGetStatValue() was FALSE\n");
+            }
+        }
+    }
+    psFree(binVector);
+    psFree(binMask);
+    psFree(myStats);
+ 
+    psTrace("SubtractSky.binImage", 4, "Exiting binImage(%d)\n", binFactor);
+    return(origImage);
+}
+*/
+
+/******************************************************************************
+CalculatePolyTerms(xOrder, yOrder): this routine will calculate the number of
+coefficients (or terms) in a 2-D polynomial of order (xOrder, yOrder).
+ 
+XXX: Use your brain and figure out the analytical expression.
+ *****************************************************************************/
+static psS32 CalculatePolyTerms(psS32 xOrder, psS32 yOrder)
+{
+    psTrace("SubtractSky.CalculatePolyTerms", 4,
+            "Calling CalculatePolyTerms(%d, %d)\n", xOrder, yOrder);
+
+    psS32 maxOrder = PS_MAX(xOrder, yOrder);
+    psS32 localPolyTerms = 0;
+    psS32 order = 0;
+    psS32 num=0;
+
+    for (order=0;order<=maxOrder;order++) {
+        for (num=0;num<=order;num++) {
+            if (((order-num) <= xOrder) && (num <= yOrder)) {
+                localPolyTerms++;
+            }
+        }
+    }
+
+    psTrace("SubtractSky.CalculatePolyTerms", 4,
+            "Exiting CalculatePolyTerms(%d, %d) -> %d\n", xOrder, yOrder, localPolyTerms);
+    return(localPolyTerms);
+}
+
+/******************************************************************************
+buildPolyTerms(): this routine computes a 2-D array polyTerms[][] that holds
+terms for the polynomial that is used to model the sky background.  We use
+this array primarily for convenience in computations involving sky model
+polynomials.  It is defined as:
+    polyTerms[i][0] = the power to which X is raised in the i-th term of in an
+    poly-order sky background polynomial.
+ 
+    polyTerms[i][1] = the power to which Y is raised in the i-th term of in an
+    poly-order sky background polynomial.
+ *****************************************************************************/
+static psS32 **buildPolyTerms(psS32 xOrder, psS32 yOrder)
+{
+    psTrace("SubtractSky.buildPolyTerms", 4,
+            "Calling buildPolyTerms(%d, %d)\n", xOrder, yOrder);
+
+    psS32 i=0;
+    psS32 order = 0;
+    psS32 num=0;
+    psS32 localPolyTerms = CalculatePolyTerms(xOrder, yOrder);
+    psS32 maxOrder = PS_MAX(xOrder, yOrder);
+
+    // Create the data structure which we hold the xy order of each coeff.
+    psS32 **polyTerms = (psS32 **) psAlloc(localPolyTerms * sizeof(psS32 *));
+    for (i=0; i < localPolyTerms ; i++) {
+        polyTerms[i] = (psS32 *) psAlloc(2 * sizeof(psS32));
+    }
+
+    i=0;
+    // This code segment loops through each term i in the polynomial and
+    // calculates the power to which x/y are raised in that i-th term.
+    // We first do the 0-order terms, then the 1-order terms, etc.
+    for (order=0;order<=maxOrder;order++) {
+        for (num=0;num<=order;num++) {
+            if (((order-num) <= xOrder) && (num <= yOrder)) {
+                polyTerms[i][0] = order-num;
+                polyTerms[i][1] = num;
+                i++;
+            }
+        }
+    }
+
+    if (psTraceGetLevel(".psModule.pmSubtractSky.buildPolyTerms") >= 10) {
+        for (i=0; i < localPolyTerms ; i++) {
+            printf("x^%d * y^%d\n", polyTerms[i][0], polyTerms[i][1]);
+        }
+    }
+
+    psTrace("SubtractSky.buildPolyTerms", 4,
+            "Exiting buildPolyTerms(%d, %d)\n", xOrder, yOrder);
+    return(polyTerms);
+}
+
+/******************************************************************************
+This procedure calculates various combinations of powers of x and y and stores
+them in the data structure p_psPolySums[][].  After it completes:
+ 
+    p_psPolySums[i][j] == x^i * y^j
+ 
+XXX: Use a psImage for the p_psPolySums data structure?
+XXX: p_psPolySums: should this be a global?  Did you get the storage classifier
+     and name correct?
+XXX: Use variable size arrays for p_psPolySums[][].
+XXX: Must initialize p_psPolySums[][]?
+ *****************************************************************************/
+#define PS_MAX_POLYNOMIAL_ORDER 20
+
+psF64 p_psPolySums[PS_MAX_POLYNOMIAL_ORDER+1][PS_MAX_POLYNOMIAL_ORDER+1];
+static void buildSums(psF64 x,
+                      psF64 y,
+                      psS32 xOrder,
+                      psS32 yOrder)
+{
+    psTrace("SubtractSky.buildPolyTerms", 4,
+            "Calling buildPolyTerms(%d, %d)\n", xOrder, yOrder);
+
+    psS32 i = 0;
+    psS32 j = 0;
+    psF64 xSum = 0.0;
+    psF64 ySum = 0.0;
+
+    xSum = 1.0;
+    ySum = 1.0;
+    for(i=0;i<=xOrder;i++) {
+        ySum = xSum;
+        for(j=0;j<=yOrder;j++) {
+            p_psPolySums[i][j] = ySum;
+            ySum*= y;
+        }
+        xSum*= x;
+    }
+    psTrace("SubtractSky.buildPolyTerms", 4,
+            "Exiting buildPolyTerms(%d, %d)\n", xOrder, yOrder);
+}
+
+/******************************************************************************
+ImageFitPolynomial(myPoly, dataImage, maskImage): this private routine takes
+an input image along with a mask and fits a polynomial to it.  The degree of
+the polynomial is specified by input parameter myPoly, and need not be
+symmetrical in orders of X and Y.  The polynomial must be type
+PS_POLYNOMIAL_ORD.  If there are not enough rows or columns in the input image
+for the order of the polynomial, then that order is reduced.  The algorithm
+used in this routine is based on that of the pilot project ADD, but is not
+documented anywhere.
+ 
+XXX: Different trace message facilities in use here.
+ *****************************************************************************/
+static psPolynomial2D *ImageFitPolynomial(psPolynomial2D *myPoly,
+        psImage *dataImage,
+        psImage *maskImage)
+{
+    psTrace("SubtractSky.ImageFitPolynomial", 4,
+            "Calling ImageFitPolynomial()\n");
+    PS_ASSERT_POLY_NON_NULL(myPoly, NULL);
+    PS_ASSERT_POLY_TYPE(myPoly, PS_POLYNOMIAL_ORD, NULL);
+    PS_ASSERT_IMAGE_NON_NULL(dataImage, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(dataImage, NULL);
+    PS_ASSERT_IMAGE_TYPE(dataImage, PS_TYPE_F32, NULL);
+    PS_ASSERT_IMAGE_NON_NULL(maskImage, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(maskImage, NULL);
+    PS_ASSERT_IMAGE_TYPE(maskImage, PS_TYPE_U8, NULL);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(dataImage, maskImage, NULL);
+    psS32 oldPolyX = -1;
+    psS32 oldPolyY = -1;
+
+    // The matrix equations become singular if there are more powers of X
+    // in myPoly then there are rows of the image.  I think.  Similarly for
+    // powers of Y and columns.  So.  Here we reduce the complexity of the
+    // polynomial if there are not enough rows/columns in the input image.
+
+    if ((myPoly->COOL_2D_nX + 1) > dataImage->numRows) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: ImageFitPolynomial(): Reducing polynomial complexity in x-dimension.\n");
+        oldPolyX = myPoly->COOL_2D_nX;
+        myPoly->COOL_2D_nX = dataImage->numRows - 1;
+    }
+    if ((myPoly->COOL_2D_nY + 1) > dataImage->numCols) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: ImageFitPolynomial(): Reducing polynomial complexity in y-dimension.\n");
+        oldPolyY = myPoly->COOL_2D_nY;
+        myPoly->COOL_2D_nY = dataImage->numCols - 1;
+    }
+    psS32 i;
+    psS32 j;
+    psS32 x;
+    psS32 y;
+    psS32 aRow;
+    psS32 aCol;
+    psS32 **polyTerms = buildPolyTerms(myPoly->COOL_2D_nX, myPoly->COOL_2D_nY);
+    // We determine how many coefficients will be in the polynomial that we
+    // are fitting to this image.
+    psS32 localPolyTerms = CalculatePolyTerms(myPoly->COOL_2D_nX, myPoly->COOL_2D_nY);
+    psImage *A = psImageAlloc(localPolyTerms, localPolyTerms, PS_TYPE_F64);
+    psImage *Aout = psImageAlloc(localPolyTerms, localPolyTerms, PS_TYPE_F64);
+    psVector *B = psVectorAlloc(localPolyTerms, PS_TYPE_F64);
+    psVector *outPerm = NULL;
+
+    //
+    // Initialize A matrix and B vector.
+    //
+    PS_IMAGE_SET_F64(A, 0.0);
+    PS_VECTOR_SET_F64(B, 0.0);
+
+    //
+    // We build the A matrix and B vector.
+    //
+    for (x=0;x<dataImage->numRows;x++) {
+        for (y=0;y<dataImage->numCols;y++) {
+            if (maskImage->data.U8[x][y] == 0) {
+                buildSums((psF64) x, (psF64) y, myPoly->COOL_2D_nX, myPoly->COOL_2D_nY);
+
+                /************************************************************
+                This code dervies from equation (7) of the pilot ADD.  However,
+                it is not exactly the same in that the order of the polynomial
+                may be different in X And Y.
+
+                Equation (7) from the pilot ADD describes 16 linear equations.
+                The i-th equation is simply the partial derivative of the
+                sky background polynomial (1) w.r.t. to the i-th term in
+                that polynomial.  The i-th equation is stored in row i of
+                matrix A[][] (matrix A[][] has origin (1,1), not (0,0)).  To
+                compute A[i][j] we simply multiply the j-th term of the Sky
+                Background Polynomial (SBP) by the i-th term of SBP.
+                ************************************************************/
+                for (aRow=0;aRow<localPolyTerms;aRow++) {
+                    for (aCol=0;aCol<localPolyTerms;aCol++) {
+                        A->data.F64[aRow][aCol]+=
+                            (p_psPolySums[ polyTerms[aCol][0] ][ polyTerms[aCol][1] ] *
+                             p_psPolySums[ polyTerms[aRow][0] ][ polyTerms[aRow][1] ]);
+                    }
+                }
+                // Build the B[] vector, which is the right-hand side of (7).
+                for (i=0;i<localPolyTerms;i++) {
+                    B->data.F64[i]+= dataImage->data.F32[x][y] *
+                                     p_psPolySums[ polyTerms[i][0] ][ polyTerms[i][1] ];
+                }
+            }
+        }
+    }
+
+    if (psTraceGetLevel(".psModule.pmSubtractSky.ImageFitPolynomial") >= 8) {
+        for (aRow=0;aRow<localPolyTerms;aRow++) {
+            for (aCol=0;aCol<localPolyTerms;aCol++) {
+                printf("A[%d][%d] is %f\n", aRow, aCol,
+                       A->data.F64[aRow][aCol]);
+            }
+        }
+
+        for (i=0;i<=localPolyTerms;i++) {
+            printf("B[%d] is %f\n", i, B->data.F64[i]);
+        }
+    }
+
+    //
+    // Solve the matrix equations for the polynomial coefficients C.
+    // XXX: How do we know if these matrix operations were successful?
+    //
+    Aout = psMatrixLUD(Aout, &outPerm, A);
+    PS_ASSERT_IMAGE_NON_NULL(Aout, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(Aout, NULL);
+    psVector *C = psVectorAlloc(localPolyTerms, PS_TYPE_F64);
+    psMatrixLUSolve(C, Aout, B, outPerm);
+
+    //
+    // Set the appropriate coefficients in the myPoly structure.
+    //
+    for (i=0;i<localPolyTerms;i++) {
+        myPoly->coeff[ polyTerms[i][0] ][ polyTerms[i][1] ] = C->data.F64[i];
+        psTrace(".psModule.pmSubtractSky.ImageFitPolynomial", 6,
+                "myPoly->coeff[%d][%d] is %f\n", polyTerms[i][0], polyTerms[i][1], myPoly->coeff[ polyTerms[i][0] ][ polyTerms[i][1] ]);
+    }
+
+    //
+    // Free data structures that were allocated in this module.
+    //
+    for (i=0;i<localPolyTerms;i++) {
+        psFree(polyTerms[i]);
+    }
+    psFree(polyTerms);
+    psFree(A);
+    psFree(Aout);
+    psFree(B);
+    psFree(C);
+    psFree(outPerm);
+
+    //
+    // We restore the original size of the polynomial and set remaining
+    // coefficients to 0.0, if necessary.
+    //
+    // XXX: Verify this works after poly nOrder/nTerm change.
+    //
+    if (oldPolyX != -1) {
+        myPoly->COOL_2D_nX = oldPolyX;
+        for (i=oldPolyX ; i < (1 + myPoly->COOL_2D_nX) ; i++) {
+            for (j=0;j<(1 + myPoly->COOL_2D_nY) ; j++) {
+                myPoly->coeff[i][j] = 0.0;
+            }
+        }
+    }
+    if (oldPolyY != -1) {
+        myPoly->COOL_2D_nY = oldPolyY;
+        for (i=0 ; i < (1 + myPoly->COOL_2D_nX) ; i++) {
+            for (j=oldPolyY;j < (1 + myPoly->COOL_2D_nY) ; j++) {
+                myPoly->coeff[i][j] = 0.0;
+            }
+        }
+    }
+
+    psTrace("SubtractSky.ImageFitPolynomial", 4,
+            "Exiting ImageFitPolynomial()\n");
+    //    psTrace(".psModule.pmSubtractSky.ImageFitPolynomial", 4,
+    //            "---- ImageFitPolynomial() end successfully ----\n");
+    return(myPoly);
+}
+
+
+/******************************************************************************
+pmReadout pmSubtractSky():
+ 
+XXX: use static vectors for myStats, and the binned image
+ 
+XXX: The SDR is silent about types.  PS_TYPE_F32 is implemented here.
+ 
+XXX: Sync the psTrace message facilities.
+ *****************************************************************************/
+pmReadout *pmSubtractSky(pmReadout *in,
+                         void *fitSpec,
+                         psFit fit,
+                         psS32 binFactor,
+                         psStats *stats,
+                         psF32 clipSD)
+{
+    PS_ASSERT_READOUT_NON_NULL(in, NULL);
+    PS_ASSERT_READOUT_NON_EMPTY(in, NULL);
+    PS_ASSERT_READOUT_TYPE(in, PS_TYPE_F32, NULL);
+    psTrace(".psModule.pmSubtractSky", 4,
+            "---- pmSubtractSky() begin ----\n");
+
+    if ((fit != PM_FIT_NONE) &&
+            (fit != PM_FIT_POLYNOMIAL) &&
+            (fit != PM_FIT_SPLINE)) {
+        psError(PS_ERR_UNKNOWN, true, "psFit is unallowable (%d).  Returning in image.\n", fit);
+        return(in);
+    }
+
+    psStatsOptions statOptions = 0;
+
+    //
+    // Return the original input readout if the fit specs are poorly defined.
+    // No warning or error messages should be generated.
+    //
+    if ((fitSpec == NULL) ||
+            ((fit == PM_FIT_NONE) || (fit == PM_FIT_SPLINE))) {
+        //        psLogMsg(__func__, PS_LOG_WARN, "Fit specs are poorly defined.  Returning in image.\n");
+        return(in);
+    }
+    psImage *origImage = in->image;
+    psImage *binnedImage = NULL;
+    psPolynomial2D *myPoly = NULL;
+    psImage *binnedMaskImage = NULL;
+    psU32 oldStatOptions = 0;
+
+    //
+    // Determine which statistic to use when binning pixels, if any.
+    //
+    if (stats != NULL) {
+        statOptions = stats->options;
+        if (1 < DetermineNumBits(statOptions)) {
+            psLogMsg(__func__, PS_LOG_WARN, "WARNING: Multiple statistical options have been requested.\n");
+            statOptions = getHighestPriorityStatOption(statOptions);
+            if (statOptions == -1) {
+                psError(PS_ERR_UNKNOWN, true, "Not allowable stats->option was specified.  Returning in image.\n");
+                return(in);
+            }
+            // Save old input "stats" parameter.
+            oldStatOptions = stats->options;
+            stats->options = statOptions;
+        }
+        if (0 == DetermineNumBits(statOptions)) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: pmSubtractSky(): no stats->options was requested\n");
+        }
+    }
+
+    //
+    // Generate required warning messages.
+    //
+    if (binFactor <= 0) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: pmSubtractSky(): binFactor is %d\n", binFactor);
+    }
+    if (stats == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: pmSubtractSky(): input parameter stats is NULL\n");
+    }
+
+    //
+    // Bin the input image according to input parameters.
+    // Create a new binned image mask.
+    //
+    if ((binFactor <= 1) || (stats == NULL) || (0 == DetermineNumBits(statOptions))) {
+        // No binning is required here.  Simply create a copy of the image
+        // and a mask.
+        binnedImage = psImageCopy(binnedImage, origImage, PS_TYPE_F32);
+        if (binnedImage == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "psImageCopy() returned NULL.  Returning in image.\n");
+            return(in);
+        }
+
+        if (in->mask != NULL) {
+            binnedMaskImage = psImageCopy(binnedMaskImage, in->mask, PS_TYPE_U8);
+            if (binnedMaskImage == NULL) {
+                psError(PS_ERR_UNKNOWN, false, "psImageCopy() returned NULL.  Returning in image.\n");
+                psFree(binnedImage);
+                return(in);
+            }
+        } else {
+            binnedMaskImage = psImageAlloc(binnedImage->numCols,
+                                           binnedImage->numRows,
+                                           PS_TYPE_U8);
+            PS_IMAGE_SET_U8(binnedMaskImage, 0);
+        }
+    } else {
+        binnedImage = psImageRebin(NULL, origImage, in->mask, 0, binFactor, stats);
+        if (binnedImage == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "psImageRebin() returned NULL.  Returning in image.\n");
+            return(in);
+        }
+        binnedMaskImage = psImageAlloc(binnedImage->numCols,
+                                       binnedImage->numRows,
+                                       PS_TYPE_U8);
+        PS_IMAGE_SET_U8(binnedMaskImage, 0);
+    }
+    psTrace(".psModule.pmSubtractSky", 4,
+            "binnedImage size is (%d, %d)\n", binnedImage->numRows, binnedImage->numCols);
+
+    //
+    // Clip pixels that are outside the acceptable range.
+    //
+    if (clipSD <= 0.0) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: pmSubtractSky(): clipSD is %f\n", clipSD);
+    } else {
+        // Determine the mean and standard deviation of the binned image.
+        psF64 binnedMean;
+        psF64 binnedStdev;
+        psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        psStats *rc =  psImageStats(myStats, binnedImage, NULL, 0);
+        if (rc == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "psImageStats(): could not perform requested statistical operation.  Returning in image.\n");
+            return(in);
+        }
+        if (false == p_psGetStatValue(rc, &binnedMean)) {
+            psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine requested statistical operation.  Returning in image.\n");
+            return(in);
+        }
+        psTrace(".psModule.pmSubtractSky", 6,
+                "binned Mean is %f\n", binnedMean);
+
+        myStats->options = PS_STAT_SAMPLE_STDEV;
+        rc =  psImageStats(myStats, binnedImage, NULL, 0);
+        if (rc == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "psImageStats(): could not perform requested statistical operation.  Returning in image.\n");
+            return(in);
+        }
+        if (false == p_psGetStatValue(myStats, &binnedStdev)) {
+            psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine requested statistical operation.  Returning in image.\n");
+            return(in);
+        }
+        psFree(myStats);
+        psTrace(".psModule.pmSubtractSky", 6,
+                "binned StDev is %f\n", binnedStdev);
+
+        // Clip all pixels which are more than clipSD sigmas from the mean.
+        psTrace(".psModule.pmSubtractSky", 6,
+                "clipSD is %f\n", clipSD);
+
+        for (psS32 row = 0; row < binnedImage->numRows ; row++) {
+            for (psS32 col = 0; col < binnedImage->numCols ; col++) {
+                if (fabs(binnedImage->data.F32[row][col] - binnedMean) >
+                        (clipSD * binnedStdev)) {
+                    binnedMaskImage->data.U8[row][col] = 1;
+                }
+            }
+        }
+    }
+
+    //
+    // Fit the polynomial to the binned image
+    //
+    if (fit == PM_FIT_POLYNOMIAL) {
+        myPoly = (psPolynomial2D *) fitSpec;
+        PS_ASSERT_POLY_NON_NULL(myPoly, NULL);
+        PS_ASSERT_POLY_TYPE(myPoly, PS_POLYNOMIAL_ORD, NULL);
+
+        myPoly = ImageFitPolynomial(myPoly, binnedImage, binnedMaskImage);
+
+        if (myPoly != NULL) {
+            // Set the pixels in the binned image to that of the polynomial.
+            binnedImage = psImageEvalPolynomial(binnedImage, myPoly);
+            if (binnedImage == NULL) {
+                psError(PS_ERR_UNKNOWN, false, "psImageEvalPolynomial() returned NULL.  Returning in image.\n");
+                psFree(binnedMaskImage);
+                if (!((binFactor <= 1) || (stats == NULL))) {
+                    psFree(binnedImage);
+                }
+                if (oldStatOptions != 0) {
+                    stats->options = statOptions;
+                }
+                return(in);
+            }
+        } else {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: pmSubtractSky(): could not model sky with a polynomial.\n");
+            psFree(binnedMaskImage);
+            if (!((binFactor <= 1) || (stats == NULL))) {
+                psFree(binnedImage);
+            }
+            if (oldStatOptions != 0) {
+                stats->options = statOptions;
+            }
+            return(in);
+        }
+    } else {
+        // We shouldn't get here since we check this above.
+        psError(PS_ERR_UNKNOWN, true, "Unallowable fit type.  Returning in image.\n");
+        psFree(binnedMaskImage);
+        if (!((binFactor <= 1) || (stats == NULL))) {
+            psFree(binnedImage);
+        }
+        if (oldStatOptions != 0) {
+            stats->options = statOptions;
+        }
+        return(in);
+    }
+
+    //
+    //Subtract the polynomially fitted image from the original image
+    //
+    if (binFactor <= 1) {
+        // The binned image is the same size as the original image.
+        for (psS32 row = 0; row < origImage->numRows ; row++) {
+            for (psS32 col = 0; col < origImage->numCols ; col++) {
+                origImage->data.F32[row][col]-= binnedImage->data.F32[row][col];
+            }
+        }
+    } else {
+        for (psS32 row = 0; row < origImage->numRows ; row++) {
+            for (psS32 col = 0; col < origImage->numCols ; col++) {
+                // We calculate the F32 value of the pixel coordinates in the
+                // binned image and then use a pixel interpolation routine to
+                // determine the value of the pixel at that location.
+                psF32 binRowF64 = ((psF32) row) / ((psF32) binFactor);
+                psF32 binColF64 = ((psF32) col) / ((psF32) binFactor);
+
+                // We add 0.5 to the pixel locations since the pixel
+                // interpolation routine defines the location of pixel
+                // (i, j) as (i+0.5, j+0.5).
+                binRowF64+= 0.5;
+                binColF64+= 0.5;
+
+                psF32 binPixel = (psF32) psImagePixelInterpolate(
+                                     binnedImage, binColF64, binRowF64,
+                                     NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+                origImage->data.F32[row][col]-= binPixel;
+
+                psTrace(".psModule.pmSubtractSky", 8,
+                        "image[%d][%d] <--> binnedImage[%.2f][%.2f]: %f\n",
+                        row, col, binRowF64-0.5, binColF64-0.5, binPixel);
+            }
+        }
+
+    }
+    psFree(binnedMaskImage);
+    psFree(binnedImage);
+    if (oldStatOptions != 0) {
+        stats->options = statOptions;
+    }
+
+    psTrace(".psModule.pmSubtractSky", 4,
+            "---- pmSubtractSky() exit successfully ----\n");
+    return(in);
+}
Index: /trunk/psModules/src/imsubtract/pmSubtractSky.h
===================================================================
--- /trunk/psModules/src/imsubtract/pmSubtractSky.h	(revision 5170)
+++ /trunk/psModules/src/imsubtract/pmSubtractSky.h	(revision 5170)
@@ -0,0 +1,40 @@
+/** @file  pmSubtractSky.h
+ *
+ *  This file will contain a module which will create a model of the
+ *  background sky and subtract that from the input image.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#if !defined(PM_SUBTRACT_SKY_H)
+#define PM_SUBTRACT_SKY_H
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include<stdio.h>
+#include<math.h>
+#include "pslib.h"
+#include "pmAstrometry.h"
+
+// XXX: this is pmFit in pmSubtractBias.c, named psFit here.
+typedef enum {
+    PM_FIT_NONE,                              ///< No fit
+    PM_FIT_POLYNOMIAL,                        ///< Fit polynomial
+    PM_FIT_SPLINE                             ///< Fit cubic splines
+} psFit;
+
+pmReadout *pmSubtractSky(pmReadout *in,
+                         void *fitSpec,
+                         psFit fit,
+                         int binFactor,
+                         psStats *stats,
+                         float clipSD);
+#endif
Index: /trunk/psModules/src/objects/Makefile.am
===================================================================
--- /trunk/psModules/src/objects/Makefile.am	(revision 5170)
+++ /trunk/psModules/src/objects/Makefile.am	(revision 5170)
@@ -0,0 +1,9 @@
+noinst_LTLIBRARIES = libpsmoduleobjects.la
+
+libpsmoduleobjects_la_CPPFLAGS = $(SRCINC) $(PSMODULE_CFLAGS)
+libpsmoduleobjects_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+libpsmoduleobjects_la_SOURCES  = pmObjects.c
+
+psmoduleincludedir = $(includedir)
+psmoduleinclude_HEADERS = \
+  pmObjects.h
Index: /trunk/psModules/src/objects/pmObjects.c
===================================================================
--- /trunk/psModules/src/objects/pmObjects.c	(revision 5170)
+++ /trunk/psModules/src/objects/pmObjects.c	(revision 5170)
@@ -0,0 +1,2120 @@
+/** @file  pmObjects.c
+ *
+ *  This file will ...
+ *
+ *  @author GLG, MHPCC
+ *  @author EAM, IfA: significant modifications.
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include<stdio.h>
+#include<math.h>
+#include "pslib.h"
+#include "psConstants.h"
+#include "pmObjects.h"
+
+/******************************************************************************
+pmPeakAlloc(): Allocate the pmPeak data structure and set appropriate members.
+*****************************************************************************/
+pmPeak *pmPeakAlloc(psS32 x,
+                    psS32 y,
+                    psF32 counts,
+                    pmPeakType class)
+{
+    pmPeak *tmp = (pmPeak *) psAlloc(sizeof(pmPeak));
+    tmp->x = x;
+    tmp->y = y;
+    tmp->counts = counts;
+    tmp->class = class;
+
+    return(tmp);
+}
+
+/******************************************************************************
+pmMomentsAlloc(): Allocate the pmMoments structure and initialize the members
+to zero.
+*****************************************************************************/
+pmMoments *pmMomentsAlloc()
+{
+    pmMoments *tmp = (pmMoments *) psAlloc(sizeof(pmMoments));
+    tmp->x = 0.0;
+    tmp->y = 0.0;
+    tmp->Sx = 0.0;
+    tmp->Sx = 0.0;
+    tmp->Sxy = 0.0;
+    tmp->Sum = 0.0;
+    tmp->Peak = 0.0;
+    tmp->Sky = 0.0;
+    tmp->nPixels = 0;
+
+    return(tmp);
+}
+
+static void modelFree(pmModel *tmp)
+{
+    psFree(tmp->params);
+    psFree(tmp->dparams);
+}
+
+/******************************************************************************
+pmModelAlloc(): Allocate the pmModel structure, along with its parameters,
+and initialize the type member.  Initialize the params to 0.0.
+XXX EAM: changing params and dparams to psVector
+*****************************************************************************/
+pmModel *pmModelAlloc(pmModelType type)
+{
+    pmModel *tmp = (pmModel *) psAlloc(sizeof(pmModel));
+
+    tmp->type = type;
+    tmp->chisq = 0.0;
+    switch (type) {
+    case PS_MODEL_GAUSS:
+        tmp->params  = psVectorAlloc(7, PS_TYPE_F32);
+        tmp->dparams = psVectorAlloc(7, PS_TYPE_F32);
+        break;
+    case PS_MODEL_PGAUSS:
+        tmp->params  = psVectorAlloc(7, PS_TYPE_F32);
+        tmp->dparams = psVectorAlloc(7, PS_TYPE_F32);
+        break;
+    case PS_MODEL_TWIST_GAUSS:
+        tmp->params  = psVectorAlloc(11, PS_TYPE_F32);
+        tmp->dparams = psVectorAlloc(11, PS_TYPE_F32);
+        break;
+    case PS_MODEL_WAUSS:
+        tmp->params  = psVectorAlloc(9, PS_TYPE_F32);
+        tmp->dparams = psVectorAlloc(9, PS_TYPE_F32);
+        break;
+    case PS_MODEL_SERSIC:
+        tmp->params  = psVectorAlloc(8, PS_TYPE_F32);
+        tmp->dparams = psVectorAlloc(8, PS_TYPE_F32);
+        break;
+    case PS_MODEL_SERSIC_CORE:
+        tmp->params  = psVectorAlloc(12, PS_TYPE_F32);
+        tmp->dparams = psVectorAlloc(12, PS_TYPE_F32);
+        break;
+    default:
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return(NULL);
+    }
+
+    for (psS32 i = 0; i < tmp->params->n; i++) {
+        tmp->params->data.F32[i] = 0.0;
+        tmp->dparams->data.F32[i] = 0.0;
+    }
+
+    psMemSetDeallocator(tmp, (psFreeFunc) modelFree);
+    return(tmp);
+}
+
+/******************************************************************************
+XXX: We don't free pixels and mask since that caused a memory error.
+We might need to increase the reference counter and decrease it here.
+*****************************************************************************/
+static void sourceFree(pmSource *tmp)
+{
+    psFree(tmp->peak);
+    //    psFree(tmp->pixels);
+    //    psFree(tmp->mask);
+    psFree(tmp->moments);
+    psFree(tmp->modelPSF);
+    psFree(tmp->modelFLT);
+}
+
+/******************************************************************************
+pmSourceAlloc(): Allocate the pmSource structure and initialize its members
+to NULL.
+*****************************************************************************/
+pmSource *pmSourceAlloc()
+{
+    pmSource *tmp = (pmSource *) psAlloc(sizeof(pmSource));
+    tmp->peak = NULL;
+    tmp->pixels = NULL;
+    tmp->mask = NULL;
+    tmp->moments = NULL;
+    tmp->modelPSF = NULL;
+    tmp->modelFLT = NULL;
+    tmp->type = 0;
+    psMemSetDeallocator(tmp, (psFreeFunc) sourceFree);
+
+    return(tmp);
+}
+
+/******************************************************************************
+pmFindVectorPeaks(vector, threshold): Find all local peaks in the given vector
+above the given threshold.  Returns a vector of type PS_TYPE_U32 containing
+the location (x value) of all peaks.
+ 
+XXX: What types should be supported?  Only F32 is implemented.
+ 
+XXX: We currently step through the input vector twice; once to determine the
+size of the output vector, then to set the values of the output vector.
+Depending upon actual use, this may need to be optimized.
+*****************************************************************************/
+psVector *pmFindVectorPeaks(const psVector *vector,
+                            psF32 threshold)
+{
+    PS_ASSERT_VECTOR_NON_NULL(vector, NULL);
+    PS_ASSERT_VECTOR_NON_EMPTY(vector, NULL);
+    PS_ASSERT_VECTOR_TYPE(vector, PS_TYPE_F32, NULL);
+    int count = 0;
+    int n = vector->n;
+
+    //
+    // Special case: the input vector has a single element.
+    //
+    if (n == 1) {
+        psVector *tmpVector = NULL;
+        ;
+        if (vector->data.F32[0] > threshold) {
+            tmpVector = psVectorAlloc(1, PS_TYPE_U32);
+            tmpVector->data.U32[0] = 0;
+        } else {
+            tmpVector = psVectorAlloc(0, PS_TYPE_U32);
+        }
+        return(tmpVector);
+    }
+
+    //
+    // Determine if first pixel is a peak
+    //
+    if ((vector->data.F32[0] > vector->data.F32[1]) &&
+            (vector->data.F32[0] > threshold)) {
+        count++;
+    }
+
+    //
+    // Determine if interior pixels are peaks
+    //
+    for (psU32 i = 1; i < n-1 ; i++) {
+        if ((vector->data.F32[i] > vector->data.F32[i-1]) &&
+                (vector->data.F32[i] > vector->data.F32[i+1]) &&
+                (vector->data.F32[i] > threshold)) {
+            count++;
+        }
+    }
+
+    //
+    // Determine if last pixel is a peak
+    //
+    if ((vector->data.F32[n-1] > vector->data.F32[n-2]) &&
+            (vector->data.F32[n-1] > threshold)) {
+        count++;
+    }
+
+    //
+    // We know how many peaks exist, so we now allocate a psVector to store
+    // those peaks.
+    //
+    psVector *tmpVector = psVectorAlloc(count, PS_TYPE_U32);
+    count = 0;
+
+    //
+    // Determine if first pixel is a peak
+    //
+    if ((vector->data.F32[0] > vector->data.F32[1]) &&
+            (vector->data.F32[0] > threshold)) {
+        tmpVector->data.U32[count++] = 0;
+    }
+
+    //
+    // Determine if interior pixels are peaks
+    //
+    for (psU32 i = 1; i < (n-1) ; i++) {
+        if ((vector->data.F32[i] > vector->data.F32[i-1]) &&
+                (vector->data.F32[i] > vector->data.F32[i+1]) &&
+                (vector->data.F32[i] > threshold)) {
+            tmpVector->data.U32[count++] = i;
+        }
+    }
+
+    //
+    // Determine if last pixel is a peak
+    //
+    if ((vector->data.F32[n-1] > vector->data.F32[n-2]) &&
+            (vector->data.F32[n-1] > threshold)) {
+        tmpVector->data.U32[count++] = n-1;
+    }
+
+    return(tmpVector);
+}
+
+/******************************************************************************
+getRowVectorFromImage(): a private function which simply returns a
+psVector containing the specified row of data from the psImage.
+ 
+XXX: Is there a better way to do this?
+*****************************************************************************/
+static psVector *getRowVectorFromImage(psImage *image,
+                                       psU32 row)
+{
+    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
+    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
+
+    psVector *tmpVector = psVectorAlloc(image->numCols, PS_TYPE_F32);
+    for (psU32 col = 0; col < image->numCols ; col++) {
+        tmpVector->data.F32[col] = image->data.F32[row][col];
+    }
+    return(tmpVector);
+}
+
+/******************************************************************************
+myListAddPeak(): A private function which allocates a psArray, if the list
+argument is NULL, otherwise it adds the peak to that list.
+XXX EAM : changed the output to psArray
+XXX EAM : Switched row, col args
+XXX EAM : NOTE: this was changed in the call, so the new code is consistent
+*****************************************************************************/
+static psArray *myListAddPeak(psArray *list,
+                              psS32 row,
+                              psS32 col,
+                              psF32 counts,
+                              pmPeakType type)
+{
+    pmPeak *tmpPeak = pmPeakAlloc(col, row, counts, type);
+
+    if (list == NULL) {
+        list = psArrayAlloc(100);
+        list->n = 0;
+    }
+    psArrayAdd(list, 100, tmpPeak);
+
+    return(list);
+}
+
+/******************************************************************************
+pmFindImagePeaks(image, threshold): Find all local peaks in the given psImage
+above the given threshold.  Returns a psArray containing location (x/y value)
+of all peaks.
+ 
+XXX: I'm not convinced the peak type definition in the SDRS is mutually
+exclusive.  Some peaks can have multiple types.  Edges for sure.  Also, a
+digonal line with the same value at each point will have a peak for every
+point on that line.
+ 
+XXX: This does not work if image has either a single row, or a single column.
+ 
+XXX: In the output psArray elements, should we use the image row/column offsets?
+     Currently, we do not.
+ 
+XXX: Merge with CVS 1.20.  This had the proper code for images with a single
+row or column.
+*****************************************************************************/
+psArray *pmFindImagePeaks(const psImage *image,
+                          psF32 threshold)
+{
+    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
+    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
+    if ((image->numRows == 1) || (image->numCols == 1)) {
+        psError(PS_ERR_UNKNOWN, true, "Currently, input image must have at least 2 rows and 2 columns.");
+        return(NULL);
+    }
+    psVector *tmpRow = NULL;
+    psU32 col = 0;
+    psU32 row = 0;
+    psArray *list = NULL;
+
+    //
+    // Find peaks in row 0 only.
+    //
+    row = 0;
+    tmpRow = getRowVectorFromImage((psImage *) image, row);
+    psVector *row1 = pmFindVectorPeaks(tmpRow, threshold);
+
+    for (psU32 i = 0 ; i < row1->n ; i++ ) {
+        col = row1->data.U32[i];
+        //
+        // Determine if pixel (0,0) is a peak.
+        //
+        if (col == 0) {
+            if ( (image->data.F32[row][col] >  image->data.F32[row][col+1]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row+1][col]) &&
+                    (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
+
+                if (image->data.F32[row][col] > threshold) {
+                    list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
+                }
+            }
+        } else if (col < (image->numCols - 1)) {
+            if ( (image->data.F32[row][col] >= image->data.F32[row][col-1]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row][col+1]) &&
+                    (image->data.F32[row][col] >= image->data.F32[row+1][col-1]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row+1][col]) &&
+                    (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
+                if (image->data.F32[row][col] > threshold) {
+                    list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
+                }
+            }
+
+        } else if (col == (image->numCols - 1)) {
+            if ( (image->data.F32[row][col] >= image->data.F32[row][col-1]) &&
+                    (image->data.F32[row][col] > image->data.F32[row+1][col]) &&
+                    (image->data.F32[row][col] >= image->data.F32[row+1][col-1])) {
+                if (image->data.F32[row][col] > threshold) {
+                    list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
+                }
+            }
+
+        } else {
+            psError(PS_ERR_UNKNOWN, true, "peak specified valid column range.");
+        }
+    }
+
+    //
+    // Exit if this image has a single row.
+    //
+    if (image->numRows == 1) {
+        return(list);
+    }
+
+    //
+    // Find peaks in interior rows only.
+    //
+    for (row = 1 ; row < (image->numRows - 1) ; row++) {
+        tmpRow = getRowVectorFromImage((psImage *) image, row);
+        row1 = pmFindVectorPeaks(tmpRow, threshold);
+
+        // Step through all local peaks in this row.
+        for (psU32 i = 0 ; i < row1->n ; i++ ) {
+            pmPeakType myType = PM_PEAK_UNDEF;
+            col = row1->data.U32[i];
+
+            if (col == 0) {
+                // If col==0, then we can not read col-1 pixels
+                if ((image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row][col+1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row+1][col]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
+                    myType = PM_PEAK_EDGE;
+                    list = myListAddPeak(list, row, col, image->data.F32[row][col], myType);
+                }
+            } else if (col < (image->numCols - 1)) {
+                // This is an interior pixel
+                if ((image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
+                        (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
+                        (image->data.F32[row][col] > image->data.F32[row][col-1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row][col+1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row+1][col-1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row+1][col]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
+                    if (image->data.F32[row][col] > threshold) {
+                        if ((image->data.F32[row][col] > image->data.F32[row-1][col-1]) &&
+                                (image->data.F32[row][col] > image->data.F32[row-1][col]) &&
+                                (image->data.F32[row][col] > image->data.F32[row-1][col+1]) &&
+                                (image->data.F32[row][col] > image->data.F32[row][col-1]) &&
+                                (image->data.F32[row][col] > image->data.F32[row][col+1]) &&
+                                (image->data.F32[row][col] > image->data.F32[row+1][col-1]) &&
+                                (image->data.F32[row][col] > image->data.F32[row+1][col]) &&
+                                (image->data.F32[row][col] > image->data.F32[row+1][col+1])) {
+                            myType = PM_PEAK_LONE;
+                        }
+
+                        if ((image->data.F32[row][col] == image->data.F32[row-1][col-1]) ||
+                                (image->data.F32[row][col] == image->data.F32[row-1][col]) ||
+                                (image->data.F32[row][col] == image->data.F32[row-1][col+1]) ||
+                                (image->data.F32[row][col] == image->data.F32[row][col-1]) ||
+                                (image->data.F32[row][col] == image->data.F32[row][col+1]) ||
+                                (image->data.F32[row][col] == image->data.F32[row+1][col-1]) ||
+                                (image->data.F32[row][col] == image->data.F32[row+1][col]) ||
+                                (image->data.F32[row][col] == image->data.F32[row+1][col+1])) {
+                            myType = PM_PEAK_FLAT;
+                        }
+
+                        list = myListAddPeak(list, row, col, image->data.F32[row][col], myType);
+                    }
+                }
+            } else if (col == (image->numCols - 1)) {
+                // If col==numCols - 1, then we can not read col+1 pixels
+                if ((image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
+                        (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
+                        (image->data.F32[row][col] > image->data.F32[row][col-1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row][col+1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row+1][col-1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row+1][col])) {
+                    myType = PM_PEAK_EDGE;
+                    list = myListAddPeak(list, row, col, image->data.F32[row][col], myType);
+                }
+            } else {
+                psError(PS_ERR_UNKNOWN, true, "peak specified valid column range.");
+            }
+
+        }
+    }
+
+    //
+    // Find peaks in the last row only.
+    //
+    row = image->numRows - 1;
+    tmpRow = getRowVectorFromImage((psImage *) image, row);
+    row1 = pmFindVectorPeaks(tmpRow, threshold);
+    for (psU32 i = 0 ; i < row1->n ; i++ ) {
+        col = row1->data.U32[i];
+        if (col == 0) {
+            if ( (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
+                    (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row][col+1])) {
+                if (image->data.F32[row][col] > threshold) {
+                    list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
+                }
+            }
+        } else if (col < (image->numCols - 1)) {
+            if ( (image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
+                    (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row][col-1]) &&
+                    (image->data.F32[row][col] >= image->data.F32[row][col+1])) {
+                if (image->data.F32[row][col] > threshold) {
+                    list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
+                }
+            }
+
+        } else if (col == (image->numCols - 1)) {
+            if ( (image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row][col-1])) {
+                if (image->data.F32[row][col] > threshold) {
+                    list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
+                }
+            }
+        } else {
+            psError(PS_ERR_UNKNOWN, true, "peak specified valid colum range.");
+        }
+    }
+    return(list);
+}
+
+// XXX: Macro this.
+static bool isItInThisRegion(const psRegion valid,
+                             psS32 x,
+                             psS32 y)
+{
+
+    if ((x >= valid.x0) &&
+            (x <= valid.x1) &&
+            (y >= valid.y0) &&
+            (y <= valid.y1)) {
+        return(true);
+    }
+
+    return(false);
+}
+
+
+/******************************************************************************
+psCullPeaks(peaks, maxValue, valid): eliminate peaks from the psArray that have
+a peak value above the given maximum, or fall outside the valid region.
+ 
+XXX: Should the sky value be used when comparing the maximum?
+ 
+XXX: warning message if valid is NULL?
+ 
+XXX: changed API to create a NEW output psArray (should change name as well)
+*****************************************************************************/
+psList *pmCullPeaks(psList *peaks,
+                    psF32 maxValue,
+                    const psRegion valid)
+{
+    PS_ASSERT_PTR_NON_NULL(peaks, NULL);
+    //    PS_ASSERT_PTR_NON_NULL(valid, NULL);
+
+    psListElem *tmpListElem = (psListElem *) peaks->head;
+    psS32 indexNum = 0;
+
+    //    printf("pmCullPeaks(): list size is %d\n", peaks->size);
+    while (tmpListElem != NULL) {
+        pmPeak *tmpPeak = (pmPeak *) tmpListElem->data;
+        if ((tmpPeak->counts > maxValue) ||
+                (true == isItInThisRegion(valid, tmpPeak->x, tmpPeak->y))) {
+            psListRemoveData(peaks, (psPtr) tmpPeak);
+        }
+
+        indexNum++;
+        tmpListElem = tmpListElem->next;
+    }
+
+    return(peaks);
+}
+
+// XXX EAM: I changed this to return a new, subset array
+//          rather than alter the existing one
+psArray *pmPeaksSubset(psArray *peaks, psF32 maxValue, const psRegion valid)
+{
+    PS_ASSERT_PTR_NON_NULL(peaks, NULL);
+
+    psArray *output = psArrayAlloc (200);
+    output->n = 0;
+
+    psTrace (".pmObjects.pmCullPeaks", 3, "list size is %d\n", peaks->n);
+
+    for (int i = 0; i < peaks->n; i++) {
+        pmPeak *tmpPeak = (pmPeak *) peaks->data[i];
+        if (tmpPeak->counts > maxValue)
+            continue;
+        if (isItInThisRegion(valid, tmpPeak->x, tmpPeak->y))
+            continue;
+        psArrayAdd (output, 200, tmpPeak);
+    }
+    return(output);
+}
+
+/******************************************************************************
+pmSource *pmSourceLocalSky(image, peak, innerRadius, outerRadius): this
+routine creates a new pmSource data structure and sets the following members:
+    ->pmPeak
+    ->pmMoments->sky
+ 
+The sky value is set from the pixels in the square annulus surrounding the
+peak pixel.
+ 
+We simply create a subSet image and mask the inner pixels, then call
+psImageStats on that subImage+mask.
+ 
+XXX: The subImage has width of 1+2*outerRadius.  Verify with IfA.
+ 
+XXX: Use static data structures for:
+     subImage
+     subImageMask
+     myStats
+ 
+XXX: ensure that the inner and out radius fit in the actual image.  Should
+     we generate an error, or warning?  Currently an error.
+ 
+XXX: Sync with IfA on whether the peak x/y coords are data structure coords,
+     or they use the image row/column offsets.
+ 
+XXX: Should we simply set pmSource->peak = peak?  If so, should we increase
+the reference counter?  Or, should we copy the data structure?
+ 
+XXX: Currently the subimage always has an even number of rows/columns.  Is
+     this correct?  Since there is a center pixel, maybe it should have an
+     odd number of rows/columns.
+ 
+XXX: Use psTrace() for the print statements.
+ 
+XXX: Don't use separate structs for the subimage and mask.  Use the source->
+     members.
+*****************************************************************************/
+pmSource *pmSourceLocalSky(const psImage *image,
+                           const pmPeak *peak,
+                           psStatsOptions statsOptions,
+                           psF32 innerRadius,
+                           psF32 outerRadius)
+{
+    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
+    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
+    PS_ASSERT_PTR_NON_NULL(peak, NULL);
+    PS_FLOAT_COMPARE(0.0, innerRadius, NULL);
+    PS_FLOAT_COMPARE(innerRadius, outerRadius, NULL);
+    psS32 innerRadiusS32 = (psS32) innerRadius;
+    psS32 outerRadiusS32 = (psS32) outerRadius;
+
+    //
+    // We define variables for code readability.
+    //
+    // XXX: Since the peak->xy coords are in image, not subImage coords,
+    // these variables should be renamed for clarity (imageCenterRow, etc).
+    //
+    // peak->x,y is guaranteed to be on image
+    psS32 SubImageCenterRow = peak->y;
+    psS32 SubImageCenterCol = peak->x;
+
+    // XXX EAM : I added this code to stay on the image. So did George
+    psS32 SubImageStartRow  = PS_MAX(0, SubImageCenterRow - outerRadiusS32);
+    psS32 SubImageEndRow    = PS_MIN(image->numRows - 1, SubImageCenterRow + outerRadiusS32);
+    psS32 SubImageStartCol  = PS_MAX(0, SubImageCenterCol - outerRadiusS32);
+    psS32 SubImageEndCol    = PS_MIN(image->numCols - 1, SubImageCenterCol + outerRadiusS32);
+    // AnulusWidth == number of pixels width in the annulus.  We add one since
+    // the pixels at the inner AND outher radius are included.
+    psS32 AnulusWidth = 1 + (outerRadiusS32 - innerRadiusS32);
+    // Example: assume an outer/inner radius of 20/10.  Then the subimage
+    // should have width/length of 40.  An 18-by-18 interior region will
+    // be masked.
+    //    printf("pmSourceLocalSky(): innerRadiusS32 is %d\n", innerRadiusS32);
+    //    printf("pmSourceLocalSky(): outerRadiusS32 is %d\n", outerRadiusS32);
+    //    printf("pmSourceLocalSky(): AnulusWidth is %d\n", AnulusWidth);
+
+    // XXX EAM : these tests should not be needed: we can never hit this error because of above
+    # if (1)
+
+        if (SubImageStartRow < 0) {
+            psError(PS_ERR_UNKNOWN, true, "Sub image startRow is outside image boundaries (%d).\n",
+                    SubImageStartRow);
+            return(NULL);
+        }
+    if (SubImageEndRow >= image->numRows) {
+        psError(PS_ERR_UNKNOWN, true, "Sub image endRow is outside image boundaries (%d).\n",
+                SubImageEndRow);
+        return(NULL);
+    }
+    if (SubImageStartCol < 0) {
+        psError(PS_ERR_UNKNOWN, true, "Sub image startCol is outside image boundaries (%d).\n",
+                SubImageStartCol);
+        return(NULL);
+    }
+    if (SubImageEndCol >= image->numCols) {
+        psError(PS_ERR_UNKNOWN, true, "Sub image endCol is outside image boundaries (%d).\n",
+                SubImageEndCol);
+        return(NULL);
+    }
+    # endif
+
+    //
+    // Grab a subimage of the original image of size (2 * outerRadius).
+    //
+    // XXX: Must fix for new psImageSubset
+    //    psImage *subImage = psImageSubset((psImage *) image,
+    //                                      SubImageStartCol,
+    //                                      SubImageStartRow,
+    //                                      SubImageEndCol,
+    //                                      SubImageEndRow);
+    //    printf("pmSourceLocalSky: subimage width/length is (%d, %d)\n", subImage->numCols, subImage->numRows);
+    psRegion tmpRegion = psRegionSet(SubImageStartCol,
+                                     SubImageEndCol,
+                                     SubImageStartRow,
+                                     SubImageEndRow);
+    psImage *subImage = psImageSubset((psImage *) image, tmpRegion);
+
+    psImage *subImageMask = psImageAlloc(subImage->numCols,
+                                         subImage->numRows,
+                                         PS_TYPE_U8);
+
+    //
+    // Loop through the subimage mask, initialize mask to 0.
+    //
+    for (psS32 row = 0 ; row < subImageMask->numRows; row++) {
+        for (psS32 col = 0 ; col < subImageMask->numCols; col++) {
+            subImageMask->data.U8[row][col] = 0;
+        }
+    }
+
+    //
+    // Loop through the subimage, mask off pixels in the inner square.
+    // XXX this uses a static mask value of 1
+    //
+    for (psS32 row = AnulusWidth; row <= (subImageMask->numRows - AnulusWidth) - 1; row++) {
+        for (psS32 col = AnulusWidth; col <= (subImageMask->numCols - AnulusWidth) - 1; col++) {
+            subImageMask->data.U8[row][col] = 1;
+        }
+    }
+
+
+    //    for (psS32 row = 0 ; row < subImage->numRows; row++) {
+    //        for (psS32 col = 0 ; col < subImage->numCols; col++) {
+    //            printf("(%d) ", subImageMask->data.U8[row][col]);
+    //        }
+    //        printf("\n");
+    //    }
+
+    //
+    // Allocate the myStats structure, then call psImageStats(), which will
+    // calculate the specified statistic.
+    //
+    psStats *myStats = psStatsAlloc(statsOptions);
+    myStats = psImageStats(myStats, subImage, subImageMask, 1);
+
+    //
+    // Create the output mySource, and set appropriate members.
+    //
+    pmSource *mySource = pmSourceAlloc();
+    mySource->peak = (pmPeak *) peak;
+    mySource->moments = pmMomentsAlloc();
+    psF64 tmpF64;
+    p_psGetStatValue(myStats, &tmpF64);
+    mySource->moments->Sky = (psF32) tmpF64;
+    mySource->pixels = subImage;
+    mySource->mask = subImageMask;
+
+    //
+    // Free things.  XXX: This should be static memory.
+    //
+    psFree(myStats);
+
+    return(mySource);
+}
+
+/******************************************************************************
+bool checkRadius(*peak, radius, x, y): private function which simply
+determines if the (x, y) point is within the radius of the specified peak.
+ 
+XXX: macro this for performance.
+*****************************************************************************/
+static bool checkRadius(pmPeak *peak,
+                        psF32 radius,
+                        psS32 x,
+                        psS32 y)
+{
+    if (PS_SQR(radius) >= (psF32) (PS_SQR(x - peak->x) + PS_SQR(y - peak->y))) {
+        return(true);
+    }
+
+    return(false);
+}
+
+/******************************************************************************
+bool checkRadius2(): private function which simply determines if the (x, y)
+point is within the radius of the specified peak.
+ 
+XXX: macro this for performance.
+XXX: this is rather inefficient - at least compute and compare against radius^2
+*****************************************************************************/
+static bool checkRadius2(psF32 xCenter,
+                         psF32 yCenter,
+                         psF32 radius,
+                         psF32 x,
+                         psF32 y)
+{
+    /// XXX EAM should compare with hypot (x,y) for speed
+    if ((PS_SQR(x - xCenter) + PS_SQR(y - yCenter)) < PS_SQR(radius)) {
+        return(true);
+    }
+
+    return(false);
+}
+
+/******************************************************************************
+pmSourceMoments(source, radius): this function takes a subImage defined in the
+pmSource data structure, along with the peak location, and determines the
+various moments associated with that peak.
+ 
+Requires the following to have been created:
+    pmSource
+    pmSource->peak
+    pmSource->pixels
+ 
+XXX: The peak calculations are done in image coords, not subImage coords.
+ 
+XXX: mask values?
+*****************************************************************************/
+pmSource *pmSourceMoments(pmSource *source,
+                          psF32 radius)
+{
+    PS_ASSERT_PTR_NON_NULL(source, NULL);
+    PS_ASSERT_PTR_NON_NULL(source->peak, NULL);
+    PS_ASSERT_PTR_NON_NULL(source->pixels, NULL);
+    PS_FLOAT_COMPARE(0.0, radius, NULL);
+
+    //
+    // XXX: Verify the setting for sky if source->moments == NULL.
+    //
+    psF32 sky = 0.0;
+    if (source->moments == NULL) {
+        source->moments = pmMomentsAlloc();
+    } else {
+        sky = source->moments->Sky;
+    }
+
+    //
+    // Sum = SUM (z - sky)
+    // X1  = SUM (x - xc)*(z - sky)
+    // X2  = SUM (x - xc)^2 * (z - sky)
+    // XY  = SUM (x - xc)*(y - yc)*(z - sky)
+    //
+    psF32 Sum = 0.0;
+    psF32 peakPixel = -PS_MAX_F32;
+    psS32 numPixels = 0;
+    psF32 X1 = 0.0;
+    psF32 Y1 = 0.0;
+    psF32 X2 = 0.0;
+    psF32 Y2 = 0.0;
+    psF32 XY = 0.0;
+    psF32 x = 0;
+    psF32 y = 0;
+    //
+    // XXX why do I get different results for these two methods of finding Sx?
+    // XXX Sx, Sy would be better measured if we clip pixels close to sky
+    // XXX Sx, Sy can still be imaginary, so we probably need to keep Sx^2?
+    // We loop through all pixels in this subimage (source->pixels), and for each
+    // pixel that is not masked, AND within the radius of the peak pixel, we
+    // proceed with the moments calculation.  need to do two loops for a
+    // numerically stable result.  first loop: get the sums.
+    //
+    for (psS32 row = 0; row < source->pixels->numRows ; row++) {
+        for (psS32 col = 0; col < source->pixels->numCols ; col++) {
+            if ((source->mask != NULL) && (source->mask->data.U8[row][col] != 0)) {
+                psS32 imgColCoord = col + source->pixels->col0;
+                psS32 imgRowCoord = row + source->pixels->row0;
+                if (checkRadius(source->peak,
+                                radius,
+                                imgColCoord,
+                                imgRowCoord)) {
+                    psF32 xDiff = (psF32) (imgColCoord - source->peak->x);
+                    psF32 yDiff = (psF32) (imgRowCoord - source->peak->y);
+                    psF32 pDiff = source->pixels->data.F32[row][col] - sky;
+
+                    Sum+= pDiff;
+                    X1+= xDiff * pDiff;
+                    Y1+= yDiff * pDiff;
+                    XY+= xDiff * yDiff * pDiff;
+
+                    X2+= PS_SQR(xDiff) * pDiff;
+                    Y2+= PS_SQR(yDiff) * pDiff;
+
+                    if (source->pixels->data.F32[row][col] > peakPixel) {
+                        peakPixel = source->pixels->data.F32[row][col];
+                    }
+                    numPixels++;
+                }
+            }
+        }
+    }
+
+    //
+    // first moment X  = X1/Sum + xc
+    // second moment X = sqrt (X2/Sum - (X1/Sum)^2)
+    // Sxy             = XY / Sum
+    //
+    x = X1/Sum;
+    y = Y1/Sum;
+    source->moments->x = x + ((psF32) source->peak->x);
+    source->moments->y = y + ((psF32) source->peak->y);
+
+    source->moments->Sxy = XY/Sum;
+    source->moments->Sum = Sum;
+    source->moments->Peak = peakPixel;
+    source->moments->nPixels = numPixels;
+
+    // XXX EAM : these values can be negative, so we need to limit the range
+    source->moments->Sx = sqrt(PS_MAX(X2/Sum - PS_SQR(x), 0));
+    source->moments->Sy = sqrt(PS_MAX(Y2/Sum - PS_SQR(y), 0));
+    return(source);
+
+    // XXX EAM : the following code should be the same as above, but it is not very stable: ignore it
+    # if (0)
+        //
+        // second loop: get the difference sums
+        //
+        X2 = Y2 = 0;
+    for (psS32 row = 0; row < source->pixels->numRows ; row++) {
+        for (psS32 col = 0; col < source->pixels->numCols ; col++) {
+            if ((source->mask != NULL) && (source->mask->data.U8[row][col] != 0)) {
+                psS32 imgColCoord = col + source->pixels->col0;
+                psS32 imgRowCoord = row + source->pixels->row0;
+                if (checkRadius(source->peak,
+                                radius,
+                                imgColCoord,
+                                imgRowCoord)) {
+                    psF32 xDiff = (psF32) (imgColCoord - source->peak->x);
+                    psF32 yDiff = (psF32) (imgRowCoord - source->peak->y);
+                    psF32 pDiff = source->pixels->data.F32[row][col] - sky;
+
+                    Sum+= pDiff;
+                    X2+= PS_SQR(xDiff - x) * pDiff;
+                    Y2+= PS_SQR(yDiff - y) * pDiff;
+                }
+            }
+        }
+    }
+
+    //
+    // second moment X = sqrt (X2/Sum)
+    //
+    source->moments->Sx = (X2/Sum);
+    source->moments->Sy = (Y2/Sum);
+    return(source);
+    # endif
+}
+
+// XXX EAM : I used
+int pmComparePeakAscend (const void **a, const void **b)
+{
+    pmPeak *A = *(pmPeak **)a;
+    pmPeak *B = *(pmPeak **)b;
+
+    psF32 diff;
+
+    diff = A->counts - B->counts;
+    if (diff < FLT_EPSILON)
+        return (-1);
+    if (diff > FLT_EPSILON)
+        return (+1);
+    return (0);
+}
+
+int pmComparePeakDescend (const void **a, const void **b)
+{
+    pmPeak *A = *(pmPeak **)a;
+    pmPeak *B = *(pmPeak **)b;
+
+    psF32 diff;
+
+    diff = A->counts - B->counts;
+    if (diff < FLT_EPSILON)
+        return (+1);
+    if (diff > FLT_EPSILON)
+        return (-1);
+    return (0);
+}
+
+/******************************************************************************
+pmSourceRoughClass(source, metadata): make a guess at the source
+classification.
+ 
+XXX: This is not useable code, as of the release date.  There remains a fair
+bit of coding to be completed.
+ 
+XXX: The sigX and sigY stuff in the SDRS is unclear.
+ 
+XXX: How can this function ever return FALSE?
+*****************************************************************************/
+
+# define NPIX 10
+# define SCALE 0.1
+
+// XXX I am ignore memory freeing issues (EAM)
+bool pmSourceRoughClass(psArray *sources, psMetadata *metadata)
+{
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+    PS_ASSERT_PTR_NON_NULL(metadata, false);
+    psBool rc = true;
+    psArray *peaks  = NULL;
+    psF32 clumpX = 0.0;
+    psF32 clumpDX = 0.0;
+    psF32 clumpY = 0.0;
+    psF32 clumpDY = 0.0;
+
+    // find the sigmaX, sigmaY clump
+    {
+        psStats *stats  = NULL;
+        psImage *splane = NULL;
+        int binX, binY;
+
+        // construct a sigma-plane image
+        splane = psImageAlloc (NPIX/SCALE, NPIX/SCALE, PS_TYPE_F32);
+
+        // place the sources in the sigma-plane image (ignore 0,0 values?)
+        for (psS32 i = 0 ; i < sources->n ; i++)
+        {
+            pmSource *tmpSrc = (pmSource *) sources->data[i];
+            PS_ASSERT_PTR_NON_NULL(tmpSrc, false); // just skip this one?
+            PS_ASSERT_PTR_NON_NULL(tmpSrc->moments, false); // just skip this one?
+
+            // Sx,Sy are limited at 0.  a peak at 0,0 is artificial
+            if ((fabs(tmpSrc->moments->Sx) < FLT_EPSILON) && (fabs(tmpSrc->moments->Sy) < FLT_EPSILON)) {
+                continue;
+            }
+
+            // for the moment, force splane dimensions to be 10x10 image pix
+            binX = tmpSrc->moments->Sx/SCALE;
+            if (binX < 0)
+                continue;
+            if (binX >= splane->numCols)
+                continue;
+
+            binY = tmpSrc->moments->Sy/SCALE;
+            if (binY < 0)
+                continue;
+            if (binY >= splane->numRows)
+                continue;
+
+            splane->data.F32[binY][binX] += 1.0;
+        }
+
+        // find the peak in this image
+        stats = psStatsAlloc (PS_STAT_MAX);
+        stats = psImageStats (stats, splane, NULL, 0);
+        peaks = pmFindImagePeaks (splane, stats[0].max / 2);
+        psTrace (".pmObjects.pmSourceRoughClass", 2, "clump threshold is %f\n", stats[0].max/2);
+
+    }
+
+    // measure statistics on Sx, Sy if Sx, Sy within range of clump
+    {
+        pmPeak *clump;
+        psF32 minSx, maxSx;
+        psF32 minSy, maxSy;
+        psVector *tmpSx = NULL;
+        psVector *tmpSy = NULL;
+        psStats *stats  = NULL;
+
+        // XXX EAM : this lets us takes the single highest peak
+        psArraySort (peaks, pmComparePeakDescend);
+        clump = peaks->data[0];
+        psTrace (".pmObjects.pmSourceRoughClass", 2, "clump is at %d, %d\n", clump->x, clump->y);
+
+        // define section window for clump
+        minSx = clump->x * SCALE - 0.2;
+        maxSx = clump->x * SCALE + 0.2;
+        minSy = clump->y * SCALE - 0.2;
+        maxSy = clump->y * SCALE + 0.2;
+
+        tmpSx = psVectorAlloc (sources->n, PS_TYPE_F32);
+        tmpSy = psVectorAlloc (sources->n, PS_TYPE_F32);
+        tmpSx->n = 0;
+        tmpSy->n = 0;
+
+        // XXX clip sources based on flux?
+        // create vectors with Sx, Sy values in window
+        for (psS32 i = 0 ; i < sources->n ; i++)
+        {
+            pmSource *tmpSrc = (pmSource *) sources->data[i];
+
+            if (tmpSrc->moments->Sx < minSx)
+                continue;
+            if (tmpSrc->moments->Sx > maxSx)
+                continue;
+            if (tmpSrc->moments->Sy < minSy)
+                continue;
+            if (tmpSrc->moments->Sy > maxSy)
+                continue;
+            tmpSx->data.F32[tmpSx->n] = tmpSrc->moments->Sx;
+            tmpSy->data.F32[tmpSy->n] = tmpSrc->moments->Sy;
+            tmpSx->n++;
+            tmpSy->n++;
+            if (tmpSx->n == tmpSx->nalloc) {
+                psVectorRealloc (tmpSx, tmpSx->nalloc + 100);
+                psVectorRealloc (tmpSy, tmpSy->nalloc + 100);
+            }
+        }
+
+        // measures stats of Sx, Sy
+        stats = psStatsAlloc (PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV);
+
+        stats = psVectorStats (stats, tmpSx, NULL, NULL, 0);
+        clumpX  = stats->clippedMean;
+        clumpDX = stats->clippedStdev;
+
+        stats = psVectorStats (stats, tmpSy, NULL, NULL, 0);
+        clumpY  = stats->clippedMean;
+        clumpDY = stats->clippedStdev;
+
+        psTrace (".pmObjects.pmSourceRoughClass", 2, "clump  X,  Y: %f, %f\n", clumpX, clumpY);
+        psTrace (".pmObjects.pmSourceRoughClass", 2, "clump DX, DY: %f, %f\n", clumpDX, clumpDY);
+        // these values should be pushed on the metadata somewhere
+    }
+
+    int Nsat   = 0;
+    int Ngal   = 0;
+    int Nfaint = 0;
+    int Nstar  = 0;
+    int Npsf   = 0;
+    int Ncr    = 0;
+    psVector *starsn = psVectorAlloc (sources->n, PS_TYPE_F32);
+    starsn->n = 0;
+
+    // XXX allow clump size to be scaled relative to sigmas?
+    // make rough IDs based on clumpX,Y,DX,DY
+    for (psS32 i = 0 ; i < sources->n ; i++) {
+
+        pmSource *tmpSrc = (pmSource *) sources->data[i];
+
+        tmpSrc->peak->class = 0;
+
+        psF32 sigX = tmpSrc->moments->Sx;
+        psF32 sigY = tmpSrc->moments->Sy;
+
+        // check return status value (do these exist?)
+        bool status;
+        psF32 RDNOISE  = psMetadataLookupF32 (&status, metadata, "RDNOISE");
+        psF32 GAIN     = psMetadataLookupF32 (&status, metadata, "GAIN");
+        psF32 SATURATE = psMetadataLookupF32 (&status, metadata, "SATURATE");
+
+        psF32 PSF_SN_LIM   = psMetadataLookupF32 (&status, metadata, "PSF_SN_LIM");
+        psF32 FAINT_SN_LIM = psMetadataLookupF32 (&status, metadata, "FAINT_SN_LIM");
+
+        // saturated object (star or single pixel not distinguished)
+        if (tmpSrc->moments->Peak > SATURATE) {
+            tmpSrc->type |= PS_SOURCE_SATURATED;
+            Nsat ++;
+            continue;
+        }
+
+        // too small to be stellar
+        if ((sigX < (clumpX - clumpDX)) || (sigY < (clumpY - clumpDY))) {
+            tmpSrc->type |= PS_SOURCE_DEFECT;
+            Ncr ++;
+            continue;
+        }
+
+        // possible galaxy
+        if ((sigX > (clumpX + clumpDX)) || (sigY > (clumpY + clumpDY))) {
+            tmpSrc->type |= PS_SOURCE_GALAXY;
+            Ngal ++;
+            continue;
+        }
+
+        // the rest are probable stellar objects
+        psF32 S  = tmpSrc->moments->Sum;
+        psF32 A  = M_PI * sigX * sigY;
+        psF32 B  = tmpSrc->moments->Sky;
+        psF32 RT = sqrtf(S + (A * B) + (A * PS_SQR(RDNOISE) / sqrtf(GAIN)));
+        psF32 SN = (S * sqrtf(GAIN) / RT);
+
+        starsn->data.F32[starsn->n] = SN;
+        starsn->n ++;
+        Nstar ++;
+
+        // faint star
+        if (SN < FAINT_SN_LIM) {
+            tmpSrc->type |= PS_SOURCE_FAINTSTAR;
+            Nfaint ++;
+            continue;
+        }
+
+        // PSF star
+        if (SN > PSF_SN_LIM) {
+            tmpSrc->type |= PS_SOURCE_PSFSTAR;
+            Npsf ++;
+            continue;
+        }
+
+        // random type of star
+        tmpSrc->type |= PS_SOURCE_OTHER;
+    }
+
+    {
+        psStats *stats  = NULL;
+        stats = psStatsAlloc (PS_STAT_MIN | PS_STAT_MAX);
+        stats = psVectorStats (stats, starsn, NULL, NULL, 0);
+        psLogMsg ("pmObjects", 3, "SN range: %f - %f\n", stats[0].min, stats[0].max);
+    }
+
+    psTrace (".pmObjects.pmSourceRoughClass", 2, "Nstar:  %3d\n", Nstar);
+    psTrace (".pmObjects.pmSourceRoughClass", 2, "Npsf:   %3d\n", Npsf);
+    psTrace (".pmObjects.pmSourceRoughClass", 2, "Nfaint: %3d\n", Nfaint);
+    psTrace (".pmObjects.pmSourceRoughClass", 2, "Ngal:   %3d\n", Ngal);
+    psTrace (".pmObjects.pmSourceRoughClass", 2, "Nsat:   %3d\n", Nsat);
+    psTrace (".pmObjects.pmSourceRoughClass", 2, "Ncr:    %3d\n", Ncr);
+
+    return(rc);
+}
+
+/******************************************************************************
+pmSourceSetPixelsCircle(source, image, radius)
+ 
+XXX: Why boolean output?
+ 
+XXX: Why are we checking source->moments for NULL?  Should the circle be
+     centered on the centroid or the peak?
+ 
+XXX: The circle will have a diameter of (1+radius).  This is different from
+     the pmSourceSetLocal() function.
+*****************************************************************************/
+bool pmSourceSetPixelsCircle(pmSource *source,
+                             const psImage *image,
+                             psF32 radius)
+{
+    PS_ASSERT_IMAGE_NON_NULL(image, false);
+    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, false);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_PTR_NON_NULL(source->moments, false);
+    // PS_ASSERT_PTR_NON_NULL(source->peak, false);
+    PS_FLOAT_COMPARE(0.0, radius, false);
+
+    //
+    // We define variables for code readability.
+    //
+    // XXX: Since the peak->xy coords are in image, not subImage coords,
+    // these variables should be renamed for clarity (imageCenterRow, etc).
+    //
+    psS32 radiusS32 = (psS32) radius;
+    psS32 SubImageCenterRow = source->peak->y;
+    psS32 SubImageCenterCol = source->peak->x;
+    // XXX EAM : for the circle to stay on the image
+    psS32 SubImageStartRow  = PS_MAX (0, SubImageCenterRow - radiusS32);
+    psS32 SubImageEndRow    = PS_MIN (image->numRows - 1, SubImageCenterRow + radiusS32);
+    psS32 SubImageStartCol  = PS_MAX (0, SubImageCenterCol - radiusS32);
+    psS32 SubImageEndCol    = PS_MIN (image->numCols - 1, SubImageCenterCol + radiusS32);
+
+    // XXX EAM : this should not be needed: we can never hit this error
+    # if (1)
+
+        if (SubImageStartRow < 0) {
+            psError(PS_ERR_UNKNOWN, true, "Sub image startRow is outside image boundaries (%d).\n",
+                    SubImageStartRow);
+            return(false);
+        }
+    if (SubImageEndRow >= image->numRows) {
+        psError(PS_ERR_UNKNOWN, true, "Sub image endRow is outside image boundaries (%d).\n",
+                SubImageEndRow);
+        return(false);
+    }
+    if (SubImageStartCol < 0) {
+        psError(PS_ERR_UNKNOWN, true, "Sub image startCol is outside image boundaries (%d).\n",
+                SubImageStartCol);
+        return(false);
+    }
+    if (SubImageEndCol >= image->numCols) {
+        psError(PS_ERR_UNKNOWN, true, "Sub image endCol is outside image boundaries (%d).\n",
+                SubImageEndCol);
+        return(false);
+    }
+    # endif
+
+    // XXX: Must recycle image.
+    // XXX EAM: this message reflects a programming error we know about.
+    //          i am setting it to a trace message which we can take out
+    if (source->pixels != NULL) {
+        psTrace (".psModule.pmObjects.pmSourceSetPixelsCircle", 4,
+                 "WARNING: pmSourceSetPixelsCircle(): image->pixels not NULL.  Freeing and reallocating.\n");
+        psFree(source->pixels);
+    }
+    // XXX: Must fix this.  psImageSubset() has different parameters in latest CVS.
+    //    source->pixels = psImageSubset((psImage *) image,
+    //                                   SubImageStartCol,
+    //                                   SubImageStartRow,
+    //                                   SubImageEndCol,
+    //                                   SubImageEndRow);
+
+    // XXX: Must recycle image.
+    if (source->mask != NULL) {
+        psFree(source->mask);
+    }
+    source->mask = psImageAlloc(source->pixels->numCols,
+                                source->pixels->numRows,
+                                PS_TYPE_F32);
+
+    //
+    // Loop through the subimage mask, initialize mask to 0 or 1.
+    // XXX EAM: valid pixels should have 0, not 1
+    for (psS32 row = 0 ; row < source->mask->numRows; row++) {
+        for (psS32 col = 0 ; col < source->mask->numCols; col++) {
+
+            if (checkRadius2((psF32) radiusS32,
+                             (psF32) radiusS32,
+                             radius,
+                             (psF32) col,
+                             (psF32) row)) {
+                source->mask->data.U8[row][col] = 0;
+            } else {
+                source->mask->data.U8[row][col] = 1;
+            }
+        }
+    }
+    return(true);
+}
+
+/******************************************************************************
+pmSourceModelGuess(source, image, model): This function allocates a new
+pmModel structure and stores it in the pmSource data structure specified in
+the argument list.  The model type is specified in the argument list.  The
+params array in that pmModel structure are allocated, and then set to the
+appropriate values.  This function returns true if everything was successful.
+ 
+XXX: Many of the initial parameters are set to 0.0 since I don't know what
+the appropiate initial guesses are.
+ 
+XXX: The image argument is redundant.
+ 
+XXX: Many parameters are based on the src->moments structure, which is in
+image, not subImage coords.  Therefore, the calls to the model evaluation
+functions will be in image, not subImage coords.  Remember this.
+ 
+XXX: The source->models member used to be allocated here.  Now I assume
+->modelPSF should be allocated
+*****************************************************************************/
+bool pmSourceModelGuess(pmSource *source,
+                        const psImage *image,
+                        pmModelType model)
+{
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_PTR_NON_NULL(source->moments, false);
+    PS_ASSERT_PTR_NON_NULL(source->peak, false);
+    PS_ASSERT_IMAGE_NON_NULL(image, false);
+    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, false);
+    if (source->modelPSF != NULL) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: source->modelPSF was non-NULL; calling psFree(source->modelPSF).\n");
+        psFree(source->modelPSF);
+    }
+    if (!((model == PS_MODEL_GAUSS) ||
+            (model == PS_MODEL_PGAUSS) ||
+            (model == PS_MODEL_WAUSS) ||
+            (model == PS_MODEL_TWIST_GAUSS) ||
+            (model == PS_MODEL_SERSIC) ||
+            (model == PS_MODEL_SERSIC_CORE))) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined psModelType");
+        return(false);
+    }
+
+    source->modelPSF = pmModelAlloc(model);
+
+    psVector *params = source->modelPSF->params;
+
+    switch (model) {
+    case PS_MODEL_GAUSS:
+        params->data.F32[0] = source->moments->Sky;
+        params->data.F32[1] = source->peak->counts - source->moments->Sky;
+        params->data.F32[2] = source->moments->x;
+        params->data.F32[3] = source->moments->y;
+        params->data.F32[4] = sqrt(2.0) / source->moments->Sx;
+        params->data.F32[5] = sqrt(2.0) / source->moments->Sy;
+        params->data.F32[6] = source->moments->Sxy;
+        return(true);
+
+    case PS_MODEL_PGAUSS:
+        params->data.F32[0] = source->moments->Sky;
+        params->data.F32[1] = source->peak->counts - source->moments->Sky;
+        params->data.F32[2] = source->moments->x;
+        params->data.F32[3] = source->moments->y;
+        params->data.F32[4] = sqrt(2.0) / source->moments->Sx;
+        params->data.F32[5] = sqrt(2.0) / source->moments->Sy;
+        params->data.F32[6] = source->moments->Sxy;
+        return(true);
+
+    case PS_MODEL_WAUSS:
+        params->data.F32[0] = source->moments->Sky;
+        params->data.F32[1] = source->peak->counts - source->moments->Sky;
+        params->data.F32[2] = source->moments->x;
+        params->data.F32[3] = source->moments->y;
+        params->data.F32[4] = sqrt(2.0) / source->moments->Sx;
+        params->data.F32[5] = sqrt(2.0) / source->moments->Sy;
+        params->data.F32[6] = source->moments->Sxy;
+        // XXX: What are these?
+        // source->modelPSF->params[7] = B2;
+        // source->modelPSF->params[8] = B3;
+        return(true);
+
+        // XXX EAM : I might drop this model (or rather, replace it)
+    case PS_MODEL_TWIST_GAUSS:
+        params->data.F32[0] = source->moments->Sky;
+        params->data.F32[1] = source->peak->counts - source->moments->Sky;
+        params->data.F32[2] = source->moments->x;
+        params->data.F32[3] = source->moments->y;
+        // XXX: What are these?
+        // params->data.F32[4] = SxInner;
+        // params->data.F32[5] = SyInner;
+        // params->data.F32[6] = SxyInner;
+        // params->data.F32[7] = SxOuter;
+        // params->data.F32[8] = SyOuter;
+        // params->data.F32[9] = SxyOuter;
+        // params->data.F32[10] = N;
+        return(true);
+
+    case PS_MODEL_SERSIC:
+        params->data.F32[0] = source->moments->Sky;
+        params->data.F32[1] = source->peak->counts - source->moments->Sky;
+        params->data.F32[2] = source->moments->x;
+        params->data.F32[3] = source->moments->y;
+        params->data.F32[4] = sqrt(2.0) / source->moments->Sx;
+        params->data.F32[5] = sqrt(2.0) / source->moments->Sy;
+        params->data.F32[6] = source->moments->Sxy;
+        // XXX: What are these?
+        //params->data.F32[7] = Nexp;
+        return(true);
+
+    case PS_MODEL_SERSIC_CORE:
+        params->data.F32[0] = source->moments->Sky;
+        params->data.F32[1] = source->peak->counts - source->moments->Sky;
+        params->data.F32[2] = source->moments->x;
+        params->data.F32[3] = source->moments->y;
+        // XXX: What are these?
+        // params->data.F32[4] SxInner;
+        // params->data.F32[5] SyInner;
+        // params->data.F32[6] SxyInner;
+        // params->data.F32[7] Zd;
+        // params->data.F32[8] SxOuter;
+        // params->data.F32[9] SyOuter;
+        // params->data.F32[10] = SxyOuter;
+        // params->data.F32[11] = Nexp;
+        return(true);
+
+    default:
+        psError(PS_ERR_UNKNOWN, true, "Undefined psModelType");
+        return(false);
+    }
+}
+
+/******************************************************************************
+evalModel(source, level, row): a private function which evaluates the
+source->modelPSF function at the specified coords.  The coords are subImage, not
+image coords.
+ 
+NOTE: The coords are in subImage source->pixel coords, not image coords.
+ 
+XXX: reverse order of row,col args?
+ 
+XXX: rename all coords in this file such that their name defines whether
+the coords is in subImage or image space.
+ 
+XXX: This should probably be a public pmModules function.
+ 
+XXX: Use static vectors for x.
+ 
+XXX: Figure out if it's (row, col) or (col, row) for the model functions.
+ 
+XXX: For a while, the first psVectorAlloc() was generating a seg fault during
+testing.  Try to reproduce that and debug.
+*****************************************************************************/
+static psF32 evalModel(pmSource *src,
+                       psU32 row,
+                       psU32 col)
+{
+    PS_ASSERT_PTR_NON_NULL(src, false);
+    PS_ASSERT_PTR_NON_NULL(src->modelPSF, false);
+    PS_ASSERT_PTR_NON_NULL(src->modelPSF->params, false);
+
+    // XXX: The following step will not be necessary if the modelPSF->params
+    // member is a psVector.  Suggest to IfA.
+
+    // XXX EAM: done: modelPSF->params is now a vector
+    psVector *params = src->modelPSF->params;
+
+    //
+    // Allocate the x coordinate structure and convert row/col to image space.
+    //
+    psVector *x = psVectorAlloc(2, PS_TYPE_F32);
+    x->data.F32[0] = (psF32) (col + src->pixels->col0);
+    x->data.F32[1] = (psF32) (row + src->pixels->row0);
+    psF32 tmpF;
+
+    switch (src->modelPSF->type) {
+    case PS_MODEL_GAUSS:
+        tmpF = pmMinLM_Gauss2D(NULL, params, x);
+        break;
+    case PS_MODEL_PGAUSS:
+        tmpF = pmMinLM_PsuedoGauss2D(NULL, params, x);
+        break;
+    case PS_MODEL_TWIST_GAUSS:
+        tmpF = pmMinLM_TwistGauss2D(NULL, params, x);
+        break;
+    case PS_MODEL_WAUSS:
+        tmpF = pmMinLM_Wauss2D(NULL, params, x);
+        break;
+    case PS_MODEL_SERSIC:
+        tmpF = pmMinLM_Sersic(NULL, params, x);
+        break;
+    case PS_MODEL_SERSIC_CORE:
+        tmpF = pmMinLM_SersicCore(NULL, params, x);
+        break;
+    default:
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return(NAN);
+    }
+    psFree(x);
+    return(tmpF);
+}
+
+/******************************************************************************
+findValue(source, level, row, col, dir): a private function which determines
+the column coordinate of the model function which has the value "level".  If
+dir equals 0, then you loop leftwards from the peak pixel, otherwise,
+rightwards.
+ 
+XXX: reverse order of row,col args?
+ 
+XXX: Input row/col are in image coords.
+ 
+XXX: The result is returned in image coords.
+*****************************************************************************/
+static psF32 findValue(pmSource *source,
+                       psF32 level,
+                       psU32 row,
+                       psU32 col,
+                       psU32 dir)
+{
+    //
+    // Convert coords to subImage space.
+    //
+    psU32 subRow = row - source->pixels->row0;
+    psU32 subCol = col - source->pixels->col0;
+
+    // Ensure that the starting column is allowable.
+    if (!((0 <= subCol) && (subCol < source->pixels->numCols))) {
+        psError(PS_ERR_UNKNOWN, true, "Starting column outside subImage range");
+        return(NAN);
+    }
+    if (!((0 <= subRow) && (subRow < source->pixels->numRows))) {
+        psError(PS_ERR_UNKNOWN, true, "Starting row outside subImage range");
+        return(NAN);
+    }
+
+    psF32 oldValue = evalModel(source, subRow, subCol);
+    if (oldValue == level) {
+        return(((psF32) (subCol + source->pixels->col0)));
+    }
+
+    //
+    // We define variables incr and lastColumn so that we can use the same loop
+    // whether we are stepping leftwards, or rightwards.
+    //
+    psS32 incr;
+    psS32 lastColumn;
+    if (dir == 0) {
+        incr = -1;
+        lastColumn = -1;
+    } else {
+        incr = 1;
+        lastColumn = source->pixels->numCols;
+    }
+    subCol+=incr;
+
+    while (subCol != lastColumn) {
+        psF32 newValue = evalModel(source, subRow, subCol);
+        if (oldValue == level) {
+            return((psF32) (subCol + source->pixels->col0));
+        }
+
+        if ((newValue <= level) && (level <= oldValue)) {
+            // This is simple linear interpolation.
+            return( ((psF32) (subCol + source->pixels->col0)) + ((psF32) incr) * ((level - newValue) / (oldValue - newValue)) );
+        }
+
+        if ((oldValue <= level) && (level <= newValue)) {
+            // This is simple linear interpolation.
+            return( ((psF32) (subCol + source->pixels->col0)) + ((psF32) incr) * ((level - oldValue) / (newValue - oldValue)) );
+        }
+
+        subCol+=incr;
+    }
+
+    return(NAN);
+}
+
+/******************************************************************************
+pmSourceContour(src, img, level, mode): For an input subImage, and model, this
+routine returns a psArray of coordinates that evaluate to the specified level.
+ 
+XXX: Probably should remove the "image" argument.
+XXX: What type should the output coordinate vectors consist of?  col,row?
+XXX: Why a pmArray output?
+XXX: doex x,y correspond with col,row or row/col?
+XXX: What is mode?
+XXX: The top, bottom of the contour is not correctly determined.
+*****************************************************************************/
+psArray *pmSourceContour(pmSource *source,
+                         const psImage *image,
+                         psF32 level,
+                         pmContourType mode)
+{
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_PTR_NON_NULL(image, false);
+    PS_ASSERT_PTR_NON_NULL(source->moments, false);
+    PS_ASSERT_PTR_NON_NULL(source->peak, false);
+    PS_ASSERT_PTR_NON_NULL(source->pixels, false);
+    PS_ASSERT_PTR_NON_NULL(source->modelPSF, false);
+
+    //
+    // Allocate data for x/y pairs.
+    //
+    psVector *xVec = psVectorAlloc(2 * source->pixels->numRows, PS_TYPE_F32);
+    psVector *yVec = psVectorAlloc(2 * source->pixels->numRows, PS_TYPE_F32);
+
+    //
+    // Start at the row with peak pixel, then decrement.
+    //
+    psS32 col = source->peak->x;
+    for (psS32 row = source->peak->y; row>= 0 ; row--) {
+        // XXX: yVec contain no real information.  Do we really need it?
+        yVec->data.F32[row] = (psF32) (source->pixels->row0 + row);
+        yVec->data.F32[row+yVec->n] = (psF32) (source->pixels->row0 + row);
+
+        // Starting at peak pixel, search leftwards for the column intercept.
+        psF32 leftIntercept = findValue(source, level, row, col, 0);
+        if (isnan(leftIntercept)) {
+            psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
+            psFree(xVec);
+            psFree(yVec);
+            return(NULL);
+            //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
+        }
+        xVec->data.F32[row] = ((psF32) source->pixels->col0) + leftIntercept;
+
+        // Starting at peak pixel, search rightwards for the column intercept.
+
+        psF32 rightIntercept = findValue(source, level, row, col, 1);
+        if (isnan(rightIntercept)) {
+            psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
+            psFree(xVec);
+            psFree(yVec);
+            return(NULL);
+            //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
+        }
+        //printf("The intercepts are (%.2f, %.2f)\n", leftIntercept, rightIntercept);
+        xVec->data.F32[row+xVec->n] = ((psF32) source->pixels->col0) + rightIntercept;
+
+        // Set starting column for next row
+        col = (psS32) ((leftIntercept + rightIntercept) / 2.0);
+    }
+    //
+    // Start at the row (+1) with peak pixel, then increment.
+    //
+    col = source->peak->x;
+    for (psS32 row = 1 + source->peak->y; row < source->pixels->numRows ; row++) {
+        // XXX: yVec contain no real information.  Do we really need it?
+        yVec->data.F32[row] = (psF32) (source->pixels->row0 + row);
+        yVec->data.F32[row+yVec->n] = (psF32) (source->pixels->row0 + row);
+
+        // Starting at peak pixel, search leftwards for the column intercept.
+        psF32 leftIntercept = findValue(source, level, row, col, 0);
+        if (isnan(leftIntercept)) {
+            psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
+            psFree(xVec);
+            psFree(yVec);
+            return(NULL);
+            //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
+        }
+        xVec->data.F32[row] = ((psF32) source->pixels->col0) + leftIntercept;
+
+        // Starting at peak pixel, search rightwards for the column intercept.
+        psF32 rightIntercept = findValue(source, level, row, col, 1);
+        if (isnan(rightIntercept)) {
+            psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
+            psFree(xVec);
+            psFree(yVec);
+            return(NULL);
+            //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
+        }
+        xVec->data.F32[row+xVec->n] = ((psF32) source->pixels->col0) + rightIntercept;
+
+        // Set starting column for next row
+        col = (psS32) ((leftIntercept + rightIntercept) / 2.0);
+    }
+
+    //
+    // Allocate an array for result, store coord vectors there.
+    //
+    psArray *tmpArray = psArrayAlloc(2);
+    tmpArray->data[0] = (psPtr *) yVec;
+    tmpArray->data[1] = (psPtr *) xVec;
+    return(tmpArray);
+}
+
+#if 0
+static psVector *minLM_Gauss2D_Vec(psImage *deriv, psVector *params, psArray *x);
+static psVector *minLM_PsuedoGauss2D_Vec(psImage *deriv, psVector *params, psArray *x);
+static psVector *minLM_Wauss2D_Vec(psImage *deriv, psVector *params, psArray *x);
+static psVector *minLM_TwistGauss2D_Vec(psImage *deriv, psVector *params, psArray *x);
+static psVector *minLM_Sersic_Vec(psImage *deriv, psVector *params, psArray *x);
+static psVector *minLM_SersicCore_Vec(psImage *deriv, psVector *params, psArray *x);
+#endif
+
+// XXX EAM : these are better starting values, but should be available from metadata?
+#define PM_SOURCE_FIT_MODEL_NUM_ITERATIONS 20
+#define PM_SOURCE_FIT_MODEL_TOLERANCE 0.1
+/******************************************************************************
+pmSourceFitModel(source, image): must create the appropiate arguments to the
+LM minimization routines for the various p_pmMinLM_XXXXXX_Vec() functions.
+ 
+XXX: should there be a mask value?
+XXX: Probably should remove the "image" argument.
+*****************************************************************************/
+bool pmSourceFitModel(pmSource *source,
+                      const psImage *image)
+{
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_PTR_NON_NULL(source->moments, false);
+    PS_ASSERT_PTR_NON_NULL(source->peak, false);
+    PS_ASSERT_PTR_NON_NULL(source->pixels, false);
+    PS_ASSERT_PTR_NON_NULL(source->modelPSF, false);
+    PS_ASSERT_IMAGE_NON_NULL(image, false);
+    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, false);
+    psBool rc;
+
+    // find the number of valid pixels
+    // XXX EAM : this loop and the loop below could just be one pass
+    //           using the psArrayAdd and psVectorExtend functions
+    psS32 count = 0;
+    for (psS32 i = 0; i < source->pixels->numRows; i++) {
+        for (psS32 j = 0; j < source->pixels->numCols; j++) {
+            if (source->mask->data.U8[i][j] == 0) {
+                count++;
+            }
+        }
+    }
+
+    // construct the coordinate and value entries
+    psArray *x = psArrayAlloc(count);
+    psVector *y = psVectorAlloc(count, PS_TYPE_F32);
+    psVector *yErr = psVectorAlloc(count, PS_TYPE_F32);
+    psS32 tmpCnt = 0;
+    for (psS32 i = 0; i < source->pixels->numRows; i++) {
+        for (psS32 j = 0; j < source->pixels->numCols; j++) {
+            if (source->mask->data.U8[i][j] == 0) {
+                psVector *coord = psVectorAlloc(2, PS_TYPE_F32);
+                // XXX: Convert i/j to image space:
+                // XXX EAM: coord order is (x,y) == (col,row)
+                coord->data.F32[0] = (psF32) (j + source->pixels->col0);
+                coord->data.F32[1] = (psF32) (i + source->pixels->row0);
+                x->data[tmpCnt] = (psPtr *) coord;
+                y->data.F32[tmpCnt] = source->pixels->data.F32[i][j];
+
+                // XXX EAM : this is approximate: need to apply the gain and rdnoise
+                yErr->data.F32[tmpCnt] = sqrt(PS_MAX(1, source->pixels->data.F32[i][j]));
+                tmpCnt++;
+            }
+        }
+    }
+
+    psMinimization *myMin = psMinimizationAlloc(PM_SOURCE_FIT_MODEL_NUM_ITERATIONS,
+                            PM_SOURCE_FIT_MODEL_TOLERANCE);
+
+    psVector *params = source->modelPSF->params;
+
+    switch (source->modelPSF->type) {
+    case PS_MODEL_GAUSS:
+        rc = psMinimizeLMChi2(myMin, NULL, params, NULL, x, y, yErr, pmMinLM_Gauss2D);
+        break;
+    case PS_MODEL_PGAUSS:
+        rc = psMinimizeLMChi2(myMin, NULL, params, NULL, x, y, yErr, pmMinLM_PsuedoGauss2D);
+        break;
+    case PS_MODEL_TWIST_GAUSS:
+        rc = psMinimizeLMChi2(myMin, NULL, params, NULL, x, y, yErr, pmMinLM_Wauss2D);
+        break;
+    case PS_MODEL_WAUSS:
+        rc = psMinimizeLMChi2(myMin, NULL, params, NULL, x, y, yErr, pmMinLM_TwistGauss2D);
+        break;
+    case PS_MODEL_SERSIC:
+        rc = psMinimizeLMChi2(myMin, NULL, params, NULL, x, y, yErr, pmMinLM_Sersic);
+        break;
+    case PS_MODEL_SERSIC_CORE:
+        rc = psMinimizeLMChi2(myMin, NULL, params, NULL, x, y, yErr, pmMinLM_SersicCore);
+        break;
+    default:
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        rc = false;
+    }
+    // XXX EAM: we need to do something (give an error?) if rc is false
+    // XXX EAM: save the resulting chisq, nDOF, nIter
+    source->modelPSF->chisq = myMin->value;
+    source->modelPSF->nDOF  = y->n - params->n;
+    source->modelPSF->nIter = myMin->iter;
+
+    psFree(x);
+    psFree(y);
+    psFree(myMin);
+    return(rc);
+}
+
+static bool sourceAddOrSubModel(psImage *image,
+                                pmSource *src,
+                                bool center,
+                                psS32 flag)
+{
+    PS_ASSERT_PTR_NON_NULL(src, false);
+    PS_ASSERT_PTR_NON_NULL(src->moments, false);
+    PS_ASSERT_PTR_NON_NULL(src->peak, false);
+    PS_ASSERT_PTR_NON_NULL(src->pixels, false);
+    PS_ASSERT_PTR_NON_NULL(src->modelPSF, false);
+    PS_ASSERT_IMAGE_NON_NULL(image, false);
+    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, false);
+
+    psVector *x = psVectorAlloc(2, PS_TYPE_F32);
+    psVector *params = src->modelPSF->params;
+
+    for (psS32 i = 0; i < src->pixels->numRows; i++) {
+        for (psS32 j = 0; j < src->pixels->numCols; j++) {
+            psF32 pixelValue;
+            // XXX: Should you be adding the pixels for the entire subImage,
+            // or a radius of pixels around it?
+
+            // Convert i/j to imace coord space:
+            // XXX: Make sure you have col/row order correct.
+            psS32 imageRow = i + src->pixels->row0;
+            psS32 imageCol = j + src->pixels->col0;
+
+            x->data.F32[0] = (float) imageCol;
+            x->data.F32[1] = (float) imageRow;
+            switch (src->modelPSF->type) {
+            case PS_MODEL_GAUSS:
+                pixelValue = pmMinLM_Gauss2D(NULL, params, x);
+                break;
+            case PS_MODEL_PGAUSS:
+                pixelValue = pmMinLM_PsuedoGauss2D(NULL, params, x);
+                break;
+            case PS_MODEL_TWIST_GAUSS:
+                pixelValue = pmMinLM_TwistGauss2D(NULL, params, x);
+                break;
+            case PS_MODEL_WAUSS:
+                pixelValue = pmMinLM_Wauss2D(NULL, params, x);
+                break;
+            case PS_MODEL_SERSIC:
+                pixelValue = pmMinLM_Sersic(NULL, params, x);
+                break;
+            case PS_MODEL_SERSIC_CORE:
+                pixelValue = pmMinLM_SersicCore(NULL, params, x);
+                break;
+            default:
+                psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+                psFree(x);
+                return(false);
+            }
+            if (flag == 1) {
+                pixelValue = -pixelValue;
+            }
+
+            // XXX: Must figure out how to calculate the image coordinates and
+            // how to use the boolean "center" flag.
+
+            image->data.F32[imageRow][imageCol]+= pixelValue;
+        }
+    }
+    psFree(x);
+    return(true);
+}
+
+
+
+/******************************************************************************
+ *****************************************************************************/
+bool pmSourceAddModel(psImage *image,
+                      pmSource *src,
+                      bool center)
+{
+    return(sourceAddOrSubModel(image, src, center, 0));
+}
+
+/******************************************************************************
+ *****************************************************************************/
+bool pmSourceSubModel(psImage *image,
+                      pmSource *src,
+                      bool center)
+{
+    return(sourceAddOrSubModel(image, src, center, 1));
+}
+
+
+// XXX: Put this is psConstants.h
+#define PS_VECTOR_CHECK_SIZE(VEC1, N, RVAL) \
+if (VEC1->n != N) { \
+    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
+            "psVector %s has size %d, should be %d.", \
+            #VEC1, VEC1->n, N); \
+    return(RVAL); \
+}
+
+
+/**
+   all of these object representation functions have the same form : func(*deriv, *params, *x)
+ 
+   the argument "x" contains a single "x,y" coordinate pair.  The function computes the object
+   model, based on the parameters in "params" at the x,y point specified by *x, and returns the value.
+   The derivatives are also caculated and returned in the "deriv" argument.  parameter error checking is
+   skipped because speed is most important.
+**/
+
+/******************************************************************************
+    params->data.F32[0] = So;
+    params->data.F32[1] = Zo;
+    params->data.F32[2] = Xo;
+    params->data.F32[3] = Yo;
+    params->data.F32[4] = sqrt(2.0) / SigmaX;
+    params->data.F32[5] = sqrt(2.0) / SigmaY;
+    params->data.F32[6] = Sxy;
+*****************************************************************************/
+float pmMinLM_Gauss2D(
+    psVector *deriv,
+    const psVector *params,
+    const psVector *x)
+{
+    PS_ASSERT_VECTOR_NON_NULL(params, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(x, NAN);
+    psF32 X  = x->data.F32[0] - params->data.F32[2];
+    psF32 Y  = x->data.F32[1] - params->data.F32[3];
+    psF32 px = params->data.F32[4]*X;
+    psF32 py = params->data.F32[5]*Y;
+    psF32 z  = 0.5*PS_SQR(px) + 0.5*PS_SQR(py) + params->data.F32[6]*X*Y;
+    psF32 r  = exp(-z);
+    psF32 q  = params->data.F32[1]*r;
+    psF32 f  = q + params->data.F32[0];
+
+    if (deriv != NULL) {
+        deriv->data.F32[0] = +1.0;
+        deriv->data.F32[1] = +r;
+        deriv->data.F32[2] = q*(2*px*params->data.F32[4] + params->data.F32[6]*Y);
+        deriv->data.F32[3] = q*(2*py*params->data.F32[5] + params->data.F32[6]*X);
+        deriv->data.F32[4] = -2.0*q*px*X;
+        deriv->data.F32[5] = -2.0*q*py*Y;
+        deriv->data.F32[6] = -q*X*Y;
+    }
+    return(f);
+}
+
+/******************************************************************************
+    params->data.F32[0] = So;
+    params->data.F32[1] = Zo;
+    params->data.F32[2] = Xo;
+    params->data.F32[3] = Yo;
+    params->data.F32[4] = sqrt(2) / SigmaX;
+    params->data.F32[5] = sqrt(2) / SigmaY;
+    params->data.F32[6] = Sxy;
+*****************************************************************************/
+float pmMinLM_PsuedoGauss2D(
+    psVector *deriv,
+    const psVector *params,
+    const psVector *x)
+{
+    PS_ASSERT_VECTOR_NON_NULL(params, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(x, NAN);
+    psF32 X  = x->data.F32[0] - params->data.F32[2];
+    psF32 Y  = x->data.F32[1] - params->data.F32[3];
+    psF32 px = params->data.F32[4]*X;
+    psF32 py = params->data.F32[5]*Y;
+    psF32 z  = 0.5*PS_SQR(px) + 0.5*PS_SQR(py) + params->data.F32[6]*X*Y;
+    psF32 t  = 1 + z + 0.5*z*z;
+    psF32 r  = 1.0 / (t*(1 + z/3)); /* exp (-Z) */
+    psF32 f  = params->data.F32[1]*r + params->data.F32[0];
+
+    if (deriv != NULL) {
+        // note difference from a pure gaussian: q = params->data.F32[1]*r
+        psF32 q = params->data.F32[1]*r*r*t;
+        deriv->data.F32[0] = +1.0;
+        deriv->data.F32[1] = +r;
+        deriv->data.F32[2] = q*(2.0*px*params->data.F32[4] + params->data.F32[6]*Y);
+        deriv->data.F32[3] = q*(2.0*py*params->data.F32[5] + params->data.F32[6]*X);
+        deriv->data.F32[4] = -2.0*q*px*X;
+        deriv->data.F32[5] = -2.0*q*py*Y;
+        deriv->data.F32[6] = -q*X*Y;
+    }
+    return(f);
+}
+
+/******************************************************************************
+    params->data.F32[0] = So;
+    params->data.F32[1] = Zo;
+    params->data.F32[2] = Xo;
+    params->data.F32[3] = Yo;
+    params->data.F32[4] = Sx;
+    params->data.F32[5] = Sy;
+    params->data.F32[6] = Sxy;
+    params->data.F32[7] = B2;
+    params->data.F32[8] = B3;
+*****************************************************************************/
+float pmMinLM_Wauss2D(
+    psVector *deriv,
+    const psVector *params,
+    const psVector *x)
+{
+    PS_ASSERT_VECTOR_NON_NULL(params, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(x, NAN);
+    psF32 X = x->data.F32[0] - params->data.F32[2];
+    psF32 Y = x->data.F32[1] - params->data.F32[2];
+    psF32 px = params->data.F32[4]*X;
+    psF32 py = params->data.F32[5]*Y;
+    psF32 z = 0.5*PS_SQR(px) + 0.5*PS_SQR(py) + params->data.F32[6]*X*Y;
+    psF32 t = 0.5*z*z*(1.0 + params->data.F32[8]*z/3.0);
+    psF32 r = 1.0 / (1.0 + z + params->data.F32[7]*t); /* exp (-Z) */
+    psF32 f = params->data.F32[1]*r + params->data.F32[0];
+
+    if (deriv != NULL) {
+        // note difference from gaussian: q = params->data.F32[1]*r
+        psF32 q = params->data.F32[1]*r*r*(1.0 + params->data.F32[7]*z*(1.0 + params->data.F32[8]*z/2.0));
+        deriv->data.F32[0] = +1.0;
+        deriv->data.F32[1] = +r;
+        deriv->data.F32[2] = q*(2.0*px*params->data.F32[4] + params->data.F32[6]*Y);
+        deriv->data.F32[3] = q*(2.0*py*params->data.F32[5] + params->data.F32[6]*X);
+        deriv->data.F32[4] = -2.0*q*px*X;
+        deriv->data.F32[5] = -2.0*q*py*Y;
+        deriv->data.F32[6] = -q*X*Y;
+        deriv->data.F32[7] = -100.0*params->data.F32[1]*r*r*t;
+        deriv->data.F32[8] = -100.0*params->data.F32[1]*r*r*params->data.F32[7]*(z*z*z)/6.0;
+        // The values of 100 dampen the swing of params->data.F32[7,8] */
+    }
+    return(f);
+}
+
+// XXX: What should these be?
+#define FFACTOR 1.0
+#define FSCALE 1.0
+/******************************************************************************
+    params->data.F32[0] = So;
+    params->data.F32[1] = Zo;
+    params->data.F32[2] = Xo;
+    params->data.F32[3] = Yo;
+    params->data.F32[4] = SxInner;
+    params->data.F32[5] = SyInner;
+    params->data.F32[6] = SxyInner;
+    params->data.F32[7] = SxOuter;
+    params->data.F32[8] = SyOuter;
+    params->data.F32[9] = SxyOuter;
+    params->data.F32[10] = N;
+*****************************************************************************/
+float pmMinLM_TwistGauss2D(
+    psVector *deriv,
+    const psVector *params,
+    const psVector *x)
+{
+    PS_ASSERT_VECTOR_NON_NULL(params, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(x, NAN);
+    psF32 X = x->data.F32[0] - params->data.F32[2];
+    psF32 Y = x->data.F32[1] - params->data.F32[3];
+    psF32 px1 = params->data.F32[4]*X;
+    psF32 py1 = params->data.F32[5]*Y;
+    psF32 px2 = params->data.F32[7]*X;
+    psF32 py2 = params->data.F32[8]*Y;
+    psF32 z1 = 0.5*PS_SQR(px1) + 0.5*PS_SQR(py1) + params->data.F32[4]*X*Y;
+    psF32 z2 = 0.5*PS_SQR(px2) + 0.5*PS_SQR(py2) + params->data.F32[9]*X*Y;
+    psF32 r = 1.0 / (1.0 + z1 + pow(z2,params->data.F32[10]));
+
+    psF32 f = params->data.F32[5]*r + params->data.F32[6];
+
+    if (deriv != NULL) {
+        psF32 q1 = params->data.F32[5]*PS_SQR(r);
+        psF32 q2 = params->data.F32[5]*PS_SQR(r)*params->data.F32[10]*pow(z2,(params->data.F32[10]-1.0));
+        deriv->data.F32[0] = +1.0;
+        deriv->data.F32[1] = +r;
+        deriv->data.F32[2] = q1*(2.0*px1*params->data.F32[4] + params->data.F32[6]*Y) + q2*(2*px2*params->data.F32[7] + params->data.F32[9]*Y);
+        deriv->data.F32[3] = q1*(2.0*py1*params->data.F32[5] + params->data.F32[6]*X) + q2*(2*py2*params->data.F32[8] + params->data.F32[9]*X);
+
+        // These fudge factors impede the growth of params->data.F32[4] beyond
+        // params->data.F32[7].
+        psF32 f1 = fabs(params->data.F32[7]) / fabs(params->data.F32[4]);
+        psF32 f2 = (f1 < FSCALE) ? 1.0 : FFACTOR*(f1 - FSCALE) + 1.0;
+        deriv->data.F32[4] = -2.0*q1*px1*X*f2;
+
+        // These fudge factors impede the growth of params->data.F32[5] beyond
+        // params->data.F32[8].
+        f1 = fabs(params->data.F32[8]) / fabs(params->data.F32[5]);
+        f2 = (f1 < FSCALE) ? 1.0 : FFACTOR*(f1 - FSCALE) + 1.0;
+        deriv->data.F32[5] = -2.0*q1*py1*Y*f2;
+        deriv->data.F32[6] = -q1*X*Y;
+        deriv->data.F32[7] = -2.0*q2*px2*X;
+        deriv->data.F32[8] = -2.0*q2*py2*Y;
+        deriv->data.F32[9] = -q2*X*Y;
+        deriv->data.F32[10] = -q1*log(z2);
+    }
+
+    return(f);
+}
+
+/******************************************************************************
+    float Sersic()
+    params->data.F32[0] = So;
+    params->data.F32[1] = Zo;
+    params->data.F32[2] = Xo;
+    params->data.F32[3] = Yo;
+    params->data.F32[4] = Sx;
+    params->data.F32[5] = Sy;
+    params->data.F32[6] = Sxy;
+    params->data.F32[7] = Nexp;
+*****************************************************************************/
+float pmMinLM_Sersic(
+    psVector *deriv,
+    const psVector *params,
+    const psVector *x)
+{
+    PS_ASSERT_VECTOR_NON_NULL(params, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(x, NAN);
+    psError(PS_ERR_UNKNOWN, true, "This function is not implemented yet.");
+    return(0.0);
+}
+
+/******************************************************************************
+    float SersicBulge()
+    params->data.F32[0] So;
+    params->data.F32[1] Zo;
+    params->data.F32[2] Xo;
+    params->data.F32[3] Yo;
+    params->data.F32[4] SxInner;
+    params->data.F32[5] SyInner;
+    params->data.F32[6] SxyInner;
+    params->data.F32[7] Zd;
+    params->data.F32[8] SxOuter;
+    params->data.F32[9] SyOuter;
+    params->data.F32[10] = SxyOuter;
+    params->data.F32[11] = Nexp;
+*****************************************************************************/
+float pmMinLM_SersicCore(
+    psVector *deriv,
+    const psVector *params,
+    const psVector *x)
+{
+    PS_ASSERT_VECTOR_NON_NULL(params, NAN);
+    PS_ASSERT_VECTOR_NON_NULL(x, NAN);
+    psError(PS_ERR_UNKNOWN, true, "This function is not implemented yet.");
+    return(0.0);
+}
Index: /trunk/psModules/src/objects/pmObjects.h
===================================================================
--- /trunk/psModules/src/objects/pmObjects.h	(revision 5170)
+++ /trunk/psModules/src/objects/pmObjects.h	(revision 5170)
@@ -0,0 +1,397 @@
+/** @file  pmObjects.h
+ *
+ *  This file will ...
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#if !defined(PM_OBJECTS_H)
+#define PM_OBJECTS_H
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include<stdio.h>
+#include<math.h>
+#include "pslib.h"
+
+/** pmPeakType
+ * 
+ *  A peak pixel may have several features which may be determined when the
+ *  peak is found or measured. These are specified by the pmPeakType enum.
+ *  PM_PEAK_LONE represents a single pixel which is higher than its 8 immediate
+ *  neighbors.  The PM_PEAK_EDGE represents a peak pixel which touching the image
+ *  edge. The PM_PEAK_FLAT represents a peak pixel which has more than a specific
+ *  number of neighbors at the same value, within some tolarence:
+ * 
+ */
+typedef enum {
+    PM_PEAK_LONE,                       ///< Isolated peak.
+    PM_PEAK_EDGE,                       ///< Peak on edge.
+    PM_PEAK_FLAT,                       ///< Peak has equal-value neighbors.
+    PM_PEAK_UNDEF                       ///< Undefined.
+} pmPeakType;
+
+/** pmPeak data structure
+ *  
+ */
+typedef struct
+{
+    int x;                              ///< X-coordinate of peak pixel.
+    int y;                              ///< Y-coordinate of peak pixel.
+    float counts;                       ///< Value of peak pixel (above sky?).
+    pmPeakType class;                   ///< Description of peak.
+}
+pmPeak;
+
+/** pmMoments data structure
+ *  
+ */
+typedef struct
+{
+    float x;                            ///< X-coord of centroid.
+    float y;                            ///< Y-coord of centroid.
+    float Sx;                           ///< x-second moment.
+    float Sy;                           ///< y-second moment.
+    float Sxy;                          ///< xy cross moment.
+    float Sum;                          ///< Pixel sum above sky (background).
+    float Peak;                         ///< Peak counts above sky.
+    float Sky;                          ///< Sky level (background).
+    int nPixels;                        ///< Number of pixels used.
+}
+pmMoments;
+
+/** pmModelType enumeration
+ *  
+ */
+typedef enum {
+    PS_MODEL_GAUSS,                     ///< Regular 2-D Gaussian
+    PS_MODEL_PGAUSS,                    ///< Psuedo 2-D Gaussian
+    PS_MODEL_TWIST_GAUSS,               ///< 2-D Twisted Gaussian
+    PS_MODEL_WAUSS,                     ///< 2-D Waussian
+    PS_MODEL_SERSIC,                    ///< Sersic
+    PS_MODEL_SERSIC_CORE,               ///< Sersic Core
+    PS_MODEL_UNDEFINED                  ///< Undefined
+} pmModelType;
+
+/** pmModel data structure
+ *  
+ */
+// XXX: The SDRS has the "type" member of type psS32.
+typedef struct
+{
+    pmModelType type;                   ///< Model to be used.
+    psVector *params;                   ///< Paramater values.
+    psVector *dparams;                  ///< Parameter errors.
+    float chisq;                        ///< Fit chi-squared.
+    int nDOF;                           ///< number of degrees of freedom
+    int nIter;                          ///< number of iterations to reach min
+}
+pmModel;
+
+/** pmSourceType enumeration
+ *  
+ *  
+ *  
+ */
+typedef enum {
+    PS_SOURCE_PSFSTAR,
+    PS_SOURCE_GALAXY,
+    PS_SOURCE_DEFECT,
+    PS_SOURCE_SATURATED,
+    PS_SOURCE_SATSTAR,
+    PS_SOURCE_FAINTSTAR,
+    PS_SOURCE_BRIGHTSTAR,
+    PS_SOURCE_OTHER
+} pmSourceType;
+
+/** pmSource data structure
+ *  
+ *  This source has the capacity for several types of measurements. The
+ *  simplest measurement of a source is the location and flux of the peak pixel
+ *  associated with the source:
+ *  
+ */
+typedef struct
+{
+    pmPeak *peak;                       ///< Description of peak pixel.
+    psImage *pixels;                    ///< Rectangular region including object pixels.
+    psImage *mask;                      ///< Mask which marks pixels associated with objects.
+    pmMoments *moments;                 ///< Basic moments measure for the object.
+    pmModel *modelPSF;                  ///< PSF model parameters and type
+    pmModel *modelFLT;                  ///< FLT model parameters and type
+    pmSourceType type;                  ///< Best identification of object.
+}
+pmSource;
+
+/** pmPeak data structure
+ *  
+ *  
+ *  
+ */
+typedef struct
+{
+    psS32 type;                         ///< PSF Model in use
+    psArray *params;                    ///< Model parameters (psPolynomial2D)
+    psF32 chisq;                        ///< PSF goodness statistic
+    psS32 nPSFstars;                    ///< number of stars used to measure PSF
+}
+pmPSF;
+
+
+
+pmPeak *pmPeakAlloc(
+    int x,                              ///< Row-coordinate in image space
+    int y,                              ///< Col-coordinate in image space
+    float counts,                       ///< The value of the peak pixel
+    pmPeakType class                    ///< The type of peak pixel
+);
+
+pmMoments *pmMomentsAlloc();
+pmModel *pmModelAlloc(pmModelType type);
+pmSource *pmSourceAlloc();
+
+/******************************************************************************
+pmFindVectorPeaks(vector, threshold): Find all local peaks in the given vector
+above the given threshold.  Returns a vector of type PS_TYPE_U32 containing
+the location (x value) of all peaks.
+ *****************************************************************************/
+psVector *pmFindVectorPeaks(
+    const psVector *vector,             ///< The input vector (float)
+    float threshold                     ///< Threshold above which to find a peak
+);
+
+/******************************************************************************
+pmFindImagePeaks(image, threshold): Find all local peaks in the given psImage
+above the given threshold.  Returns a psList containing the location (x/y
+value) of all peaks.
+ *****************************************************************************/
+psArray *pmFindImagePeaks(
+    const psImage *image,               ///< The input image where peaks will be found (float)
+    float threshold                     ///< Threshold above which to find a peak
+);
+
+/******************************************************************************
+psCullPeaks(peaks, maxValue, valid): eliminate peaks from the psList that have
+a peak value above the given maximum, or fall outside the valid region.
+ *****************************************************************************/
+psList *pmCullPeaks(
+    psList *peaks,                      ///< The psList of peaks to be culled
+    float maxValue,                     ///< Cull peaks above this value
+    const psRegion valid               ///< Cull peaks otside this psRegion
+);
+
+/******************************************************************************
+pmSource *pmSourceLocalSky(image, peak, innerRadius, outerRadius):
+ 
+ *****************************************************************************/
+pmSource *pmSourceLocalSky(
+    const psImage *image,               ///< The input image (float)
+    const pmPeak *peak,                 ///< The peak for which the psSource struct is created.
+    psStatsOptions statsOptions,        ///< The statistic used in calculating the background sky
+    float innerRadius,                  ///< The inner radius of the suqare annulus for calculating sky
+    float outerRadius                   ///< The outer radius of the suqare annulus for calculating sky
+);
+
+/******************************************************************************
+ *****************************************************************************/
+pmSource *pmSourceMoments(
+    pmSource *source,                   ///< The input pmSource for which moments will be computed
+    float radius                        ///< Use a circle of pixels around the peak
+);
+
+/******************************************************************************
+pmSourceRoughClass(pmArray *source, psMetaDeta *metadata): make a guess at the
+source classification.
+ *****************************************************************************/
+bool pmSourceRoughClass(
+    psArray *source,                    ///< The input pmSource
+    psMetadata *metadata                ///< Contains classification parameters
+);
+/******************************************************************************
+pmSourceSetPixelCircle(source, image, radius)
+ *****************************************************************************/
+bool pmSourceSetPixelsCircle(
+    pmSource *source,                   ///< The input pmSource
+    const psImage *image,               ///< The input image (float)
+    float radius                        ///< The radius of the circle
+);
+
+/******************************************************************************
+ *****************************************************************************/
+bool pmSourceModelGuess(
+    pmSource *source,                   ///< The input pmSource
+    const psImage *image,               ///< The input image (float)
+    pmModelType model                   ///< The type of model to be created.
+);
+
+/******************************************************************************
+ *****************************************************************************/
+typedef enum {
+    PS_CONTOUR_CRUDE,
+} pmContourType;
+
+psArray *pmSourceContour(
+    pmSource *source,                   ///< The input pmSource
+    const psImage *image,               ///< The input image (float) (this arg should be removed)
+    float level,                        ///< The level of the contour
+    pmContourType mode                  ///< Currently this must be PS_CONTOUR_CRUDE
+);
+
+/******************************************************************************
+ *****************************************************************************/
+bool pmSourceFitModel(
+    pmSource *source,                   ///< The input pmSource
+    const psImage *image                ///< The input image (float)
+);
+
+/******************************************************************************
+ *****************************************************************************/
+bool pmSourceAddModel(
+    psImage *image,                     ///< The opuut image (float)
+    pmSource *source,                   ///< The input pmSource
+    bool center                         ///< A boolean flag that determines whether pixels are centered
+);
+
+/******************************************************************************
+ *****************************************************************************/
+bool pmSourceSubModel(
+    psImage *image,                     ///< The output image (float)
+    pmSource *source,                   ///< The input pmSource
+    bool center                         ///< A boolean flag that determines whether pixels are centered
+);
+
+/******************************************************************************
+XXX: Why only *x argument?
+XXX EAM: psMinimizeLMChi2Func returns psF64, not float
+ *****************************************************************************/
+float pmMinLM_Gauss2D(
+    psVector *deriv,                    ///< A possibly-NULL structure for the output derivatives
+    const psVector *params,             ///< A psVector which holds the parameters of this function
+    const psVector *x                   ///< A psVector which holds the row/col coordinate
+);
+
+/******************************************************************************
+ *****************************************************************************/
+float pmMinLM_PsuedoGauss2D(
+    psVector *deriv,                    ///< A possibly-NULL structure for the output derivatives
+    const psVector *params,             ///< A psVector which holds the parameters of this function
+    const psVector *x                   ///< A psVector which holds the row/col coordinate
+);
+
+/******************************************************************************
+ *****************************************************************************/
+float pmMinLM_Wauss2D(
+    psVector *deriv,                    ///< A possibly-NULL structure for the output derivatives
+    const psVector *params,             ///< A psVector which holds the parameters of this function
+    const psVector *x                   ///< A psVector which holds the row/col coordinate
+);
+
+/******************************************************************************
+ *****************************************************************************/
+float pmMinLM_TwistGauss2D(
+    psVector *deriv,                    ///< A possibly-NULL structure for the output derivatives
+    const psVector *params,             ///< A psVector which holds the parameters of this function
+    const psVector *x                   ///< A psVector which holds the row/col coordinate
+);
+
+/******************************************************************************
+ *****************************************************************************/
+float pmMinLM_Sersic(
+    psVector *deriv,                    ///< A possibly-NULL structure for the output derivatives
+    const psVector *params,             ///< A psVector which holds the parameters of this function
+    const psVector *x                   ///< A psVector which holds the row/col coordinate
+);
+
+/******************************************************************************
+ *****************************************************************************/
+float pmMinLM_SersicCore(
+    psVector *deriv,                    ///< A possibly-NULL structure for the output derivatives
+    const psVector *params,             ///< A psVector which holds the parameters of this function
+    const psVector *x                   ///< A psVector which holds the row/col coordinate
+);
+
+/******************************************************************************
+ *****************************************************************************/
+float pmMinLM_PsuedoSersic(
+    psVector *deriv,                    ///< A possibly-NULL structure for the output derivatives
+    const psVector *params,             ///< A psVector which holds the parameters of this function
+    const psVector *x                   ///< A psVector which holds the row/col coordinate
+);
+
+
+/**
+ * 
+ *  The object model functions are defined to allow for the flexible addition
+ *  of new object models. Every object model, with parameters represented by
+ *  pmModel, has an associated set of functions which provide necessary support
+ *  operations. A set of abstract functions allow the programmer to select the
+ *  approriate function or property for a specific named object model.
+ * 
+ */
+
+/**
+ * 
+ *  This function is the model chi-square minimization function for this model.
+ * 
+ */
+typedef psMinimizeLMChi2Func pmModelFunc;
+
+
+/**
+ * 
+ * This function returns the integrated flux for the given model parameters.
+ */
+typedef psF64 (*pmModelFlux)(const psVector *params);
+
+
+/**
+ * 
+ *  This function provides the model guess parameters based on the details of
+ *   the given source.
+ * 
+ */
+typedef bool (*pmModelGuessFunc)(pmModel *model, pmSource *source);
+
+
+/**
+ * 
+ *  This function constructs the PSF model for the given source based on the
+ *  supplied psf and the FLT model for the object.
+ * 
+ */
+typedef bool (*pmModelFromPSFFunc)(pmModel *modelPSF, pmModel *modelFLT, pmPSF *psf);
+
+
+/**
+ * 
+ *  This function returns the radius at which the given model and parameters
+ *  achieves the given flux.
+ * 
+ */
+typedef psF64 (*pmModelRadius)(const psVector *params, double flux);
+
+
+/**
+ * 
+ *  Each of the function types above has a corresponding function which returns
+ *  the function given the model type:
+ * 
+ */
+pmModelFunc pmModelFunc_GetFunction (pmModelType type);
+pmModelFlux pmModelFlux_GetFunction (pmModelType type);
+pmModelGuessFunc pmModelGuessFunc_GetFunction (pmModelType type);
+pmModelFromPSFFunc pmModelFromPSFFunc_GetFunction (pmModelType type);
+pmModelRadius pmModelRadius_GetFunction (pmModelType type);
+psS32 pmModelParameterCount(pmModelType type);
+psS32 pmModelSetType(char *name);
+char *pmModelGetType(pmModelType type);
+
+#endif
Index: /trunk/psModules/src/photom/Makefile.am
===================================================================
--- /trunk/psModules/src/photom/Makefile.am	(revision 5170)
+++ /trunk/psModules/src/photom/Makefile.am	(revision 5170)
@@ -0,0 +1,9 @@
+noinst_LTLIBRARIES = libpsmodulephotom.la
+
+libpsmodulephotom_la_CPPFLAGS = $(SRCINC) $(PSMODULE_CFLAGS)
+libpsmodulephotom_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+libpsmodulephotom_la_SOURCES  =
+
+psmoduleincludedir = $(includedir)
+psmoduleinclude_HEADERS =
+
Index: unk/psModules/src/pmAstrometry.c
===================================================================
--- /trunk/psModules/src/pmAstrometry.c	(revision 5169)
+++ 	(revision )
@@ -1,897 +1,0 @@
-/** @file  psAstrometry.c
-*
-*  @brief This file defines the basic types for astronomical coordinate
-*  transformation
-*
-*  @ingroup AstroImage
-*
-*  @author GLG, MHPCC
-*
-*  @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2005-09-14 19:21:41 $
-*
-*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
-*/
-
-/******************************************************************************/
-/*  INCLUDE FILES                                                             */
-/******************************************************************************/
-#include <string.h>
-#include <math.h>
-#include "pslib.h"
-#include "psDB.h"
-#include "pmAstrometry.h"
-
-/*****************************************************************************
-checkValidImageCoords(): this is a private function which simply determines if
-the supplied x,y coordinates are in the range for the supplied psImage.
- 
-XXX: What about col0 and row0
-XXX: This should return a psBool.
- *****************************************************************************/
-static psS32 checkValidImageCoords(double x,
-                                   double y,
-                                   psImage* tmpImage)
-{
-    PS_ASSERT_IMAGE_NON_NULL(tmpImage, 0);
-
-    if ((x < 0.0) || (x > (double)tmpImage->numCols) ||
-            (y < 0.0) || (y > (double)tmpImage->numRows)) {
-        return (0);
-    }
-
-    return (1);
-}
-
-/******************************************************************************
-XXX: Is this the correct way to free metadata?
-XXX: Is this the correct way to free database structs?
- 
- 
- 
-XXX: The memory dereferencing is not quite right with these functions.  If I
-call the alloc functions with non-NULL pointers, there will be memory leaks.
- *****************************************************************************/
-static void FPAFree(pmFPA *fpa)
-{
-    if (fpa != NULL) {
-        psFree(fpa->fromTangentPlane);
-        psFree(fpa->toTangentPlane);
-        psFree(fpa->projection);
-        psFree(fpa->concepts);
-        psFree(fpa->analysis);
-        psFree(fpa->camera);
-        //
-        // Set the parent to NULL in all fpa->chips before psFree(fpa->chips)
-        // in order to avoid memory reference counter problems.
-        //
-        for (psS32 i = 0 ; i < fpa->chips->n ; i++) {
-            pmChip *tmpChip = (pmChip *) fpa->chips->data[i];
-            tmpChip->parent = NULL;
-        }
-        psFree(fpa->chips);
-        psFree(fpa->header);
-        psFree(fpa->db);
-    }
-}
-
-static void chipFree(pmChip* chip)
-{
-    if (chip != NULL) {
-        psFree(chip->toFPA);
-        psFree(chip->fromFPA);
-        psFree(chip->concepts);
-        psFree(chip->analysis);
-        //
-        // Set the parent to NULL in all chip->cells before psFree(chip->cells)
-        // in order to avoid memory reference counter problems.
-        //
-        for (psS32 i = 0 ; i < chip->cells->n ; i++) {
-            pmCell *tmpCell = (pmCell *) chip->cells->data[i];
-            tmpCell->parent = NULL;
-        }
-        psFree(chip->cells);
-        psFree(chip->parent);
-        psFree(chip->extname);
-        psFree(chip->pixels);
-        psFree(chip->header);
-    }
-}
-
-static void cellFree(pmCell *cell)
-{
-    if (cell != NULL) {
-        psFree(cell->toChip);
-        psFree(cell->toFPA);
-        psFree(cell->toSky);
-        psFree(cell->concepts);
-        psFree(cell->analysis);
-        //
-        // Set the parent to NULL in all cell->readouts before psFree(cell->readouts)
-        // in order to avoid memory reference counter problems.
-        //
-        for (psS32 i = 0 ; i < cell->readouts->n ; i++) {
-            pmReadout *tmpReadout = (pmReadout *) cell->readouts->data[i];
-            tmpReadout->parent = NULL;
-        }
-        psFree(cell->readouts);
-        psFree(cell->parent);
-        psFree(cell->extname);
-        psFree(cell->pixels);
-        psFree(cell->header);
-    }
-}
-
-static void readoutFree(pmReadout *readout)
-{
-    if (readout != NULL) {
-        psFree(readout->image);
-        psFree(readout->mask);
-        psFree(readout->analysis);
-        psFree(readout->concepts);
-        psFree(readout->parent);
-    }
-}
-
-// XXX: Verify these default values for row0, col0, rowBins, colBins
-pmReadout *pmReadoutAlloc(pmCell *cell)
-{
-    pmReadout *tmpReadout = (pmReadout *) psAlloc(sizeof(pmReadout));
-
-    tmpReadout->col0 = -1;
-    tmpReadout->row0 = -1;
-    tmpReadout->colBins = -1;
-    tmpReadout->rowBins = -1;
-    tmpReadout->image = NULL;
-    tmpReadout->mask = NULL;
-    tmpReadout->analysis = psMetadataAlloc();
-    tmpReadout->concepts = psMetadataAlloc();
-    tmpReadout->parent = cell;
-    if (cell != NULL) {
-        cell->readouts = psArrayAdd(cell->readouts, 1, (psPtr) tmpReadout);
-    }
-    psMemSetDeallocator(tmpReadout, (psFreeFunc) readoutFree);
-    return(tmpReadout);
-}
-
-pmCell *pmCellAlloc(pmChip *chip)
-{
-    pmCell *tmpCell = (pmCell *) psAlloc(sizeof(pmCell));
-
-    tmpCell->col0 = -1;
-    tmpCell->row0 = -1;
-    tmpCell->toChip = NULL;
-    tmpCell->toFPA = NULL;
-    tmpCell->toSky = NULL;
-    tmpCell->analysis = psMetadataAlloc();
-    tmpCell->concepts = psMetadataAlloc();
-    tmpCell->readouts = psArrayAlloc(0);
-    tmpCell->parent = chip;
-    if (chip != NULL) {
-        chip->cells = psArrayAdd(chip->cells, 1, (psPtr) tmpCell);
-    }
-    tmpCell->valid = false;
-    tmpCell->extname = NULL;
-    tmpCell->pixels = NULL;
-    tmpCell->header = NULL;
-
-    psMemSetDeallocator(tmpCell, (psFreeFunc) cellFree);
-    return(tmpCell);
-}
-
-pmChip *pmChipAlloc(pmFPA *fpa)
-{
-    pmChip *tmpChip = (pmChip *) psAlloc(sizeof(pmChip));
-
-    tmpChip->col0 = -1;
-    tmpChip->row0 = -1;
-    tmpChip->toFPA = NULL;
-    tmpChip->fromFPA = NULL;
-    tmpChip->concepts = psMetadataAlloc();
-    tmpChip->analysis = psMetadataAlloc();
-    tmpChip->cells = psArrayAlloc(0);
-    tmpChip->parent = fpa;
-    if (fpa != NULL) {
-        fpa->chips = psArrayAdd(fpa->chips, 1, (psPtr) tmpChip);
-    }
-    tmpChip->valid = false;
-    tmpChip->extname = NULL;
-    tmpChip->pixels = NULL;
-    tmpChip->header = NULL;
-
-    psMemSetDeallocator(tmpChip, (psFreeFunc) chipFree);
-    return(tmpChip);
-}
-
-pmFPA *pmFPAAlloc(
-    const psMetadata *camera,
-    psDB *db)
-{
-    pmFPA *tmpFPA = (pmFPA *) psAlloc(sizeof(pmFPA));
-
-    tmpFPA->fromTangentPlane = NULL;
-    tmpFPA->toTangentPlane = NULL;
-    tmpFPA->projection = NULL;
-    tmpFPA->concepts = psMetadataAlloc();
-    tmpFPA->analysis = psMetadataAlloc();
-    tmpFPA->camera = camera;
-    psMemIncrRefCounter((psPtr) camera);
-    tmpFPA->chips = psArrayAlloc(0);
-    tmpFPA->header = NULL;
-    tmpFPA->db = db;
-    psMemIncrRefCounter((psPtr) db);
-
-    psMemSetDeallocator(tmpFPA, (psFreeFunc) FPAFree);
-    return(tmpFPA);
-}
-
-bool cellCheckParents(pmCell *cell)
-{
-    if (cell == NULL) {
-        return(true);
-    }
-    psBool flag = true;
-
-    for (psS32 i = 0 ; i < cell->readouts->n ; i++) {
-        pmReadout *tmpReadout = (pmReadout *) cell->readouts->data[i];
-        PS_ASSERT_PTR_NON_NULL(tmpReadout, false);
-        if (tmpReadout->parent != cell) {
-            tmpReadout->parent = cell;
-            flag = false;
-        }
-    }
-    return(flag);
-}
-
-bool chipCheckParents(pmChip *chip)
-{
-    if (chip == NULL) {
-        return(true);
-    }
-    psBool flag = true;
-
-    for (psS32 i = 0 ; i < chip->cells->n ; i++) {
-        pmCell *tmpCell = (pmCell *) chip->cells->data[i];
-        PS_ASSERT_PTR_NON_NULL(tmpCell, false);
-        if (tmpCell->parent != chip) {
-            tmpCell->parent = chip;
-            flag = false;
-        }
-
-        flag &= cellCheckParents(tmpCell);
-    }
-    return(flag);
-}
-
-bool pmFPACheckParents(pmFPA *fpa)
-{
-    if (fpa == NULL) {
-        return(true);
-    }
-    psBool flag = true;
-
-    for (psS32 i = 0 ; i < fpa->chips->n ; i++) {
-        pmChip *tmpChip = (pmChip *) fpa->chips->data[i];
-        PS_ASSERT_PTR_NON_NULL(tmpChip, false);
-        if (tmpChip->parent != fpa) {
-            tmpChip->parent = fpa;
-            flag = false;
-        }
-
-        flag &= chipCheckParents(tmpChip);
-    }
-    return(flag);
-}
-
-
-
-// HEY
-/*****************************************************************************/
-/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
-/*****************************************************************************/
-
-pmCell* pmCellInFPA(const psPlane* fpaCoord,
-                    const pmFPA* FPA)
-{
-    PS_ASSERT_PTR_NON_NULL(fpaCoord, NULL);
-    PS_ASSERT_PTR_NON_NULL(FPA, NULL);
-
-    pmChip* tmpChip = NULL;
-    psPlane chipCoord;
-    pmCell* outCell = NULL;
-
-    // Determine which chip contains the fpaCoords.
-    tmpChip = pmChipInFPA(fpaCoord, FPA);
-    if (tmpChip == NULL) {
-        return(NULL);
-    }
-
-    // Convert to those chip coordinates.
-    psCoordFPAToChip(&chipCoord, fpaCoord, tmpChip);
-
-    // Determine which cell contains those chip coordinates.
-    outCell = pmCellInChip(&chipCoord, tmpChip);
-
-    return (outCell);
-}
-
-pmChip* pmChipInFPA(const psPlane* fpaCoord,
-                    const pmFPA* FPA)
-{
-    PS_ASSERT_PTR_NON_NULL(fpaCoord, NULL);
-    PS_ASSERT_PTR_NON_NULL(FPA, NULL);
-    PS_ASSERT_PTR_NON_NULL(FPA->chips, NULL);
-
-    psArray* chips = FPA->chips;
-    psS32 nChips = chips->n;
-    psPlane chipCoord;
-    pmCell *tmpCell = NULL;
-
-    // Loop through every chip in this FPA.  Convert the original FPA
-    // coordinates to chip coordinates for that chip.  Then, determine if any
-    // cells in that chip contain those chip coordinates.
-
-    for (psS32 i = 0; i < nChips; i++) {
-        pmChip* tmpChip = chips->data[i];
-        PS_ASSERT_PTR_NON_NULL(tmpChip, NULL);
-        PS_ASSERT_PTR_NON_NULL(tmpChip->fromFPA, NULL);
-
-        psPlaneTransformApply(&chipCoord, tmpChip->fromFPA, fpaCoord);
-
-        tmpCell = pmCellInChip(&chipCoord, tmpChip);
-        if (tmpCell != NULL) {
-            return(tmpChip);
-        }
-    }
-
-    // XXX: Print warning here?
-    return (NULL);
-}
-
-
-pmCell* pmCellInChip(const psPlane* chipCoord,
-                     const pmChip* chip)
-{
-    PS_ASSERT_PTR_NON_NULL(chipCoord, NULL);
-    PS_ASSERT_PTR_NON_NULL(chip, NULL);
-
-    psPlane cellCoord;
-    psArray* cells;
-
-    cells = chip->cells;
-    if (cells == NULL) {
-        return NULL;
-    }
-
-    // We loop over each cell in the chip.  We transform the chipCoord into
-    // a cellCoord for that cell and determine if that cellCoord is valid.
-    // If so, then we return that cell.
-
-    for (psS32 i = 0; i < cells->n; i++) {
-        pmCell* tmpCell = (pmCell* ) cells->data[i];
-        PS_ASSERT_PTR_NON_NULL(tmpCell, NULL);
-
-        psPlaneTransform *chipToCell = NULL;
-        if (1 != p_psIsProjectionLinear(tmpCell->toChip)) {
-            // XXX: Generate warning message.
-            // XXX: Can we use the following function to derive a transform?
-            // chipToCell = psPlaneTransformInvert(NULL, tmpCell->toChip, NULL, -1);
-        } else {
-            chipToCell = p_psPlaneTransformLinearInvert(tmpCell->toChip);
-        }
-
-        PS_ASSERT_PTR_NON_NULL(chipToCell, NULL);
-        psArray* readouts = tmpCell->readouts;
-
-        if (readouts != NULL) {
-            for (psS32 j = 0; j < readouts->n; j++) {
-                pmReadout* tmpReadout = readouts->data[j];
-                PS_ASSERT_READOUT_NON_NULL(tmpReadout, NULL);
-
-                psPlaneTransformApply(&cellCoord,
-                                      chipToCell,
-                                      chipCoord);
-
-                if (checkValidImageCoords(cellCoord.x,
-                                          cellCoord.y,
-                                          tmpReadout->image)) {
-                    return (tmpCell);
-                }
-            }
-        }
-        psFree(chipToCell);
-    }
-
-    return (NULL);
-}
-
-
-psPlane* psCoordCellToChip(psPlane* outCoord,
-                           const psPlane* inCoord,
-                           const pmCell* cell)
-{
-    PS_ASSERT_PTR_NON_NULL(inCoord, NULL);
-    PS_ASSERT_PTR_NON_NULL(cell, NULL);
-
-    return (psPlaneTransformApply(outCoord, cell->toChip, inCoord));
-}
-
-
-psPlane* psCoordChipToFPA(psPlane* outCoord,
-                          const psPlane* inCoord,
-                          const pmChip* chip)
-{
-    PS_ASSERT_PTR_NON_NULL(inCoord, NULL);
-    PS_ASSERT_PTR_NON_NULL(chip, NULL);
-
-    return (psPlaneTransformApply(outCoord, chip->toFPA, inCoord));
-}
-
-
-
-psPlane* psCoordFPAToTP(psPlane* outCoord,
-                        const psPlane* inCoord,
-                        double color,
-                        double magnitude,
-                        const pmFPA* fpa)
-{
-    PS_ASSERT_PTR_NON_NULL(inCoord, NULL);
-    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
-
-    return(psPlaneDistortApply(outCoord, fpa->toTangentPlane, inCoord,
-                               color, magnitude));
-}
-
-/*****************************************************************************
-XXX: What about units for the (x,y) coords?
- *****************************************************************************/
-psSphere* psCoordTPToSky(psSphere* outSphere,
-                         const psPlane* tpCoord,
-                         const psProjection *projection
-                        )
-{
-    PS_ASSERT_PTR_NON_NULL(tpCoord, NULL);
-
-    if (outSphere == NULL) {
-        outSphere = (psSphere* ) psAlloc(sizeof(psSphere));
-    }
-
-    // XXX: this was done by a SLALIB call -- needs to be reimplemented
-    psWarning("Warning!  psCoordTPToSky functionality is no longer implemented");
-    // slaAopqk(tpCoord->x, tpCoord->y, (double*)grommit,
-    //         &AOB, &ZOB, &HOB, &outSphere->r, &outSphere->d);
-
-    return (outSphere);
-}
-
-
-
-psPlane* psCoordCellToFPA(psPlane* fpaCoord,
-                          const psPlane* cellCoord,
-                          const pmCell* cell)
-{
-    PS_ASSERT_PTR_NON_NULL(cellCoord, NULL);
-    PS_ASSERT_PTR_NON_NULL(cell, NULL);
-
-    return (psPlaneTransformApply(fpaCoord, cell->toFPA, cellCoord));
-}
-
-
-
-psSphere* psCoordCellToSky(psSphere* skyCoord,
-                           const psPlane* cellCoord,
-                           double color,
-                           double magnitude,
-                           const pmCell* cell)
-{
-    PS_ASSERT_PTR_NON_NULL(cellCoord, NULL);
-    PS_ASSERT_PTR_NON_NULL(cell, NULL);
-    PS_ASSERT_PTR_NON_NULL(cell->toFPA, NULL);
-    PS_ASSERT_PTR_NON_NULL(cell->parent, NULL);
-    PS_ASSERT_PTR_NON_NULL(cell->parent->parent, NULL);
-    PS_ASSERT_PTR_NON_NULL(cell->parent->parent->toTangentPlane, NULL);
-    //    PS_ASSERT_PTR_NON_NULL(cell->parent->parent->exposure, NULL);
-
-    psPlane* fpaCoord = NULL;
-    psPlane* tpCoord = NULL;
-    pmFPA* parFPA = (cell->parent)->parent;
-    //    psGrommit* tmpGrommit = NULL;
-
-    // Convert the input cell coordinates to FPA coordinates.
-    fpaCoord = psPlaneTransformApply(fpaCoord, cell->toFPA, cellCoord);
-
-    // Convert the FPA coordinates to tangent plane Coordinates.
-    tpCoord = psPlaneDistortApply(tpCoord, parFPA->toTangentPlane,
-                                  fpaCoord, color, magnitude);
-
-    // Generate a grommit for this FPA.
-    //    tmpGrommit = psGrommitAlloc(parFPA->exposure);
-
-    // Convert the tangent plane Coordinates to sky coordinates.
-    //    skyCoord = psCoordTPToSky(skyCoord, tpCoord, tmpGrommit);
-
-    psFree(fpaCoord);
-    psFree(tpCoord);
-    //    psFree(tmpGrommit);
-
-    return(skyCoord);
-}
-
-
-psSphere* psCoordCellToSkyQuick(psSphere* outSphere,
-                                const psPlane* cellCoord,
-                                const pmCell* cell)
-{
-    PS_ASSERT_PTR_NON_NULL(cellCoord, NULL);
-    PS_ASSERT_PTR_NON_NULL(cell, NULL);
-    PS_ASSERT_PTR_NON_NULL(cell->toSky, NULL);
-    PS_ASSERT_PTR_NON_NULL(cell->parent, NULL);
-    PS_ASSERT_PTR_NON_NULL(cell->parent->parent, NULL);
-    PS_ASSERT_PTR_NON_NULL(cell->parent->parent->projection, NULL);
-
-    psLogMsg(__func__, PS_LOG_WARN,
-             "WARNING: psCoordCellToSkyQuick(): This function is not fully specified in the SDRS.  Returning NULL.\n");
-    return(NULL);
-
-    /*
-        if (cell->toSky) {
-            // XXX: Should we use toTP or toSky?
-            psLogMsg(__func__, PS_LOG_WARN,
-                     "WARNING: psCoordCellToSkyQuick(): The cell->toSky transform is ignored.  The cell->toTP transform is being used.");
-        }
-     
-        psPlane *tpCoord = NULL;
-        pmChip *chip = cell->parent;
-        pmFPA *FPA = chip->parent;
-        psProjectionType oldProjectionType;
-     
-        if (outSphere == NULL) {
-            outSphere = (psSphere* ) psAlloc(sizeof(psSphere));
-        }
-     
-        // Determine the tangent plane coordinates.
-        tpCoord = psPlaneTransformApply(NULL, cell->toTP, cellCoord);
-     
-        // Save the old projection type and set the new projection type to TAN.
-        oldProjectionType = FPA->projection->type;
-        FPA->projection->type = PS_PROJ_TAN;
-     
-        // Deproject the tangent plane coordinates a sphere.
-        outSphere = psDeproject(tpCoord, FPA->projection);
-     
-        // Restore old projection type.  Free memory.
-        FPA->projection->type = oldProjectionType;
-        psFree(tpCoord);
-     
-        return (outSphere);
-    */
-}
-
-
-/*****************************************************************************
-XXX: What about units for the (x,y) coords?
- *****************************************************************************/
-psPlane* psCoordSkyToTP(psPlane* tpCoord,
-                        const psSphere* in,
-                        const psProjection *projection)
-{
-    PS_ASSERT_PTR_NON_NULL(in, NULL);
-    //    PS_ASSERT_PTR_NON_NULL(grommit, NULL);
-
-    // char* type = "RA";
-
-    if (tpCoord == NULL) {
-        tpCoord = (psPlane* ) psAlloc(sizeof(psPlane));
-    }
-
-    // XXX: this was done by a SLALIB call -- needs to be reimplemented
-    psWarning("Warning!  psCoordSkyToTP functionality is no longer implemented");
-    // slaOapqk(type, in->r, in->d, (double*)grommit, &tpCoord->x, &tpCoord->y);
-
-    return(tpCoord);
-}
-
-
-psPlane* psCoordTPToFPA(psPlane* fpaCoord,
-                        const psPlane* tpCoord,
-                        double color,
-                        double magnitude,
-                        const pmFPA* fpa)
-{
-    PS_ASSERT_PTR_NON_NULL(tpCoord, NULL);
-    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
-    PS_ASSERT_PTR_NON_NULL(fpa->fromTangentPlane, NULL);
-
-    return (psPlaneDistortApply(fpaCoord, fpa->fromTangentPlane,
-                                tpCoord, color, magnitude));
-}
-
-
-psPlane* psCoordFPAToChip(psPlane* chipCoord,
-                          const psPlane* fpaCoord,
-                          const pmChip* chip)
-{
-    PS_ASSERT_PTR_NON_NULL(fpaCoord, NULL);
-    PS_ASSERT_PTR_NON_NULL(chip, NULL);
-    PS_ASSERT_PTR_NON_NULL(chip->fromFPA, NULL);
-
-    chipCoord = psPlaneTransformApply(chipCoord, chip->fromFPA, fpaCoord);
-    return(chipCoord);
-}
-
-
-psPlane* psCoordChipToCell(psPlane* cellCoord,
-                           const psPlane* chipCoord,
-                           const pmCell* cell)
-{
-    PS_ASSERT_PTR_NON_NULL(chipCoord, NULL);
-    PS_ASSERT_PTR_NON_NULL(cell, NULL);
-    PS_ASSERT_PTR_NON_NULL(cell->parent, NULL);
-
-    pmCell *tmpCell = pmCellInChip(chipCoord, cell->parent);
-    PS_ASSERT_PTR_NON_NULL(tmpCell->toChip, NULL);
-    psPlaneTransform *tmpChipToCell = p_psPlaneTransformLinearInvert(tmpCell->toChip);
-    PS_ASSERT_PTR_NON_NULL(tmpChipToCell, NULL);
-    cellCoord = psPlaneTransformApply(cellCoord, tmpChipToCell, chipCoord);
-    psFree(tmpChipToCell);
-    return(cellCoord);
-}
-
-
-psPlane* psCoordSkyToCell(psPlane* cellCoord,
-                          const psSphere* skyCoord,
-                          float color,
-                          float magnitude,
-                          const pmCell* cell)
-{
-    PS_ASSERT_PTR_NON_NULL(skyCoord, NULL);
-    PS_ASSERT_PTR_NON_NULL(cell, NULL);
-    PS_ASSERT_PTR_NON_NULL(cell->parent, NULL);
-    PS_ASSERT_PTR_NON_NULL(cell->parent->parent, NULL);
-    //    PS_ASSERT_PTR_NON_NULL(cell->parent->parent->grommit, NULL);
-
-    pmChip *parChip = cell->parent;
-    pmFPA *parFPA = parChip->parent;
-
-    // Convert the skyCoords to tangent plane coords.
-    psPlane *tpCoord = psCoordSkyToTP(NULL, skyCoord, parFPA->projection);
-
-    // Convert the tangent plane coords to FPA coords.
-    psPlane *fpaCoord = psCoordTPToFPA(NULL, tpCoord, color, magnitude, parFPA);
-
-    // Convert the FPA coords to chip coords.
-    psPlane *chipCoord = psCoordFPAToChip(NULL, fpaCoord, parChip);
-
-    // Convert the chip coords to cell coords.
-    cellCoord = psCoordChipToCell(cellCoord, chipCoord, cell);
-
-    psFree(tpCoord);
-    psFree(fpaCoord);
-    psFree(chipCoord);
-
-    return (cellCoord);
-}
-
-
-psPlane* psCoordSkyToCellQuick(psPlane* cellCoord,
-                               const psSphere* skyCoord,
-                               const pmCell* cell)
-{
-    PS_ASSERT_PTR_NON_NULL(skyCoord, NULL);
-    PS_ASSERT_PTR_NON_NULL(cell, NULL);
-    PS_ASSERT_PTR_NON_NULL(cell->parent, NULL);
-    PS_ASSERT_PTR_NON_NULL(cell->parent->parent, NULL);
-    PS_ASSERT_PTR_NON_NULL(cell->parent->parent->projection, NULL);
-
-
-    psLogMsg(__func__, PS_LOG_WARN,
-             "WARNING: psCoordSkyToCellQuick(): This function is not fully specified in the SDRS.  Returning NULL.\n");
-    return(NULL);
-    /*
-     
-        if (cell->toSky) {
-            // XXX: Should we use toTP or toSky?
-            psLogMsg(__func__, PS_LOG_WARN,
-                     "WARNING: psCoordSkyToCellQuick: The cell->toSky transform is ignored.  The cell->toTP transform is being used.");
-        }
-     
-        psPlane *tpCoord = NULL;
-        pmChip *whichChip = cell->parent;
-        pmFPA  *whichFPA = whichChip->parent;
-        psProjectionType oldProjectionType;
-        psPlaneTransform *TPtoCell = NULL;
-     
-        // Save the old projection type and set the new projection type to TAN.
-        oldProjectionType = whichFPA->projection->type;
-        whichFPA->projection->type = PS_PROJ_TAN;
-     
-        if (cellCoord == NULL) {
-            cellCoord = (psPlane* ) psAlloc(sizeof(psPlane));
-        }
-     
-        tpCoord = psProject(skyCoord, whichFPA->projection);
-     
-        // generate an error if cell->toTP is not linear.
-        if (0 == p_psIsProjectionLinear(cell->toTP)) {
-            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
-                    PS_ERRORTEXT_psAstrometry_NONLINEAR_TRANSFORM,
-                    "cell to tangent plane");
-        }
-     
-        TPtoCell = p_psPlaneTransformLinearInvert(cell->toTP);
-        cellCoord = psPlaneTransformApply(cellCoord, TPtoCell, tpCoord);
-     
-        // Restore old projection type.  Free memory.
-        whichFPA->projection->type = oldProjectionType;
-        psFree(tpCoord);
-        return (cellCoord);
-    */
-}
-
-/*
-psMetadataItem* psMetadataLookup(
-    const psMetadata * md,             ///< Metadata collection to lookup meta!
-    const char * key                   ///< Name of metadata key.
-);
-*/
-
-// XXX: How should we handle errors?  What if psMetadataLookup() is NULL?
-psMetadataItem *pmReadoutGetConcept(pmReadout *readout, const char *concept)
-{
-    return(psMetadataLookup(readout->concepts, concept));
-}
-
-psMetadataItem *pmCellGetConcept(pmCell *cell, const char *concept)
-{
-    return(psMetadataLookup(cell->concepts, concept));
-}
-
-psMetadataItem *pmChipGetConcept(pmChip *chip, const char *concept)
-{
-    return(psMetadataLookup(chip->concepts, concept));
-}
-
-psMetadataItem *pmFPAGetConcept(pmFPA *fpa, const char *concept)
-{
-    return(psMetadataLookup(fpa->concepts, concept));
-}
-
-
-float pmFPAGetAirmass(pmFPA *fpa) // FPA.AIRMASS
-{
-    psMetadataItem *tmp = pmFPAGetConcept(fpa, "FPA.AIRMASS");
-    return((float) tmp->data.F32);
-}
-
-psString pmFPAGetFilter(pmFPA *fpa) // FPA.FILTER
-{
-    psMetadataItem *tmp = pmFPAGetConcept(fpa, "FPA.FILTER");
-    return((psString) tmp->data.V);
-}
-
-float pmFPAGetPosAngle(pmFPA *fpa) // FPA.POSANGLE
-{
-    psMetadataItem *tmp = pmFPAGetConcept(fpa, "FPA.POSANGLE");
-    return((float) tmp->data.F32);
-}
-
-double pmFPAGetRA(pmFPA *fpa) // FPA.RA
-{
-    psMetadataItem *tmp = pmFPAGetConcept(fpa, "FPA.RA");
-    return((float) tmp->data.F32);
-}
-
-double pmFPAGetDec(pmFPA *fpa) // FPA.DEC
-{
-    psMetadataItem *tmp = pmFPAGetConcept(fpa, "FPA.DEC");
-    return((float) tmp->data.F32);
-}
-
-psString pmFPAGetRADecSys(pmFPA *fpa) // FPA.RADECSYS
-{
-    psMetadataItem *tmp = pmFPAGetConcept(fpa, "FPA.RADECSYS");
-    return((psString) tmp->data.V);
-}
-
-psString pmFPAGetName(pmFPA *fpa) // FPA.NAME
-{
-    psMetadataItem *tmp = pmFPAGetConcept(fpa, "FPA.NAME");
-    return((psString) tmp->data.V);
-}
-
-psString pmChipGetName(pmChip *chip) // CHIP.NAME
-{
-    psMetadataItem *tmp = pmChipGetConcept(chip, "CHIP.NAME");
-    return((psString) tmp->data.V);
-}
-
-psString pmCellGetName(pmCell *cell) // CELL.NAME
-{
-    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.NAME");
-    return((psString) tmp->data.V);
-}
-
-psTime *pmCellGetTime(pmCell *cell) // CELL.TIME
-{
-    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.TIME");
-    return((psTime *) tmp->data.V);
-}
-
-psList *pmCellGetBiasSec(pmCell *cell) // CELL.BIASSEC
-{
-    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.BIASSEC");
-    return((psList *) tmp->data.list);
-}
-
-psRegion pmCellGetTrimSec(pmCell *cell) // CELL.TRIMSEC
-{
-    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.TRIMSEC");
-    return((psRegion) *((psRegion *) (tmp->data.V)));
-}
-
-float pmCellGetGain(pmCell *cell) // CELL.GAIN
-{
-    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.GAIN");
-    return((float) tmp->data.F32);
-}
-
-float pmCellGetReadNoise(pmCell *cell) // CELL.READNOISE
-{
-    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.READNOISE");
-    return((float) tmp->data.F32);
-}
-
-float pmCellGetSaturation(pmCell *cell) // CELL.SATURATION
-{
-    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.SATURATION");
-    return((float) tmp->data.F32);
-}
-
-float pmCellGetBad(pmCell *cell) // CELL.BAD
-{
-    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.BAD");
-    return((float) tmp->data.F32);
-}
-
-
-psPixelCoord pmCellGetBin(pmCell *cell) // CELL.BIN
-{
-    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.BIN");
-    return((psPixelCoord)  *((psPixelCoord *) (tmp->data.V)));
-}
-
-psPixelCoord pmCellGetParity(pmCell *cell) // CELL.PARITY
-{
-    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.PARITY");
-    return((psPixelCoord)  *((psPixelCoord *) (tmp->data.V)));
-}
-
-float pmReadoutGetExposure(pmReadout *readout) // READOUT.EXPOSURE
-{
-    psMetadataItem *tmp = pmReadoutGetConcept(readout, "READOUT.EXPOSURE");
-    return((float) tmp->data.F32);
-}
-
-float pmReadoutGetDarkTime(pmReadout *readout) // READOUT.DARKTIME
-{
-    psMetadataItem *tmp = pmReadoutGetConcept(readout, "READOUT.DARKTIME");
-    return((float) tmp->data.F32);
-}
-
-
-
-
-/*
-typedef struct
-{
-    float x0;
-}
-psJunk;
- 
-psJunk *pmCellTmp(pmCell *cell) // CELL.TRIMSEC
-{
-    psMetadataItem *tmp;
-    return((psJunk *) tmp->data.V);
-}
- 
-*/
Index: unk/psModules/src/pmAstrometry.h
===================================================================
--- /trunk/psModules/src/pmAstrometry.h	(revision 5169)
+++ 	(revision )
@@ -1,455 +1,0 @@
-/** @file  pmAstrometry.h
-*
-*  @brief This file defines the basic types for astronomical coordinate
-*  transformation
-*
-*  @ingroup AstroImage
-*
-*  @author GLG, MHPCC
-*
-*  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2005-09-11 22:25:39 $
-*
-*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
-*/
-
-#ifndef PS_ASTROMETRY_H
-#define PS_ASTROMETRY_H
-#if HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include "pslib.h"
-#include "psDB.h"
-
-/// @addtogroup AstroImage
-/// @{
-
-/** Focal plane data structure
- * 
- *  A focal plane consists of one or more chips (according to the number of
- *  pieces of contiguous silicon). It contains metadata containers for the
- *  concepts and analysis, a link to the parent, and pointers to the FITS header,
- *  if that corresponds to this level (the FPA may be the PHU, but will not ever
- *  contain pixels). For astrometry, it contains a transformation from the focal
- *  plane to the tangent plane and the fixed pattern residuals. It is expected
- *  that the transformation will consist of two 4D polynomials (i.e. a function
- *  of two coordinates in position, the magnitude of the object, and the color of
- *  the object) in order to correct for optical distortions and the effects of
- *  the atmosphere; hence we think that it is prudent to include a reverse
- *  transformation which will be derived from numerically inverting the forward
- *  transformation.
- *  
- */
-typedef struct
-{
-    // Astrometric transformations
-    psPlaneDistort* fromTangentPlane;   ///< Transformation from tangent plane to focal plane
-    psPlaneDistort* toTangentPlane;     ///< Transformation from focal plane to tangent plane
-    psProjection *projection;           ///< Projection from tangent plane to sky
-    // Information
-    psMetadata *concepts;               ///< Cache for PS concepts
-    psMetadata *analysis;               ///< FPA-level analysis metadata
-    const psMetadata *camera;           ///< Camera configuration
-    psArray *chips;                     ///< The chips
-    // FITS data
-    psMetadata *header;                 ///< The FITS header, if it corresponds to this level
-    psDB *db;                           ///< Database handle
-}
-pmFPA;
-
-/** Chip data structure
- *  
- *  A chip consists of one or more cells (according to the number of amplifiers
- *  on the device). The chip contains metadata containers for the concepts and
- *  analysis, a link to the parent, and pointers to the pointers to the various
- *  FITS data, if that corresponds to this level. For astrometry, in addition to
- *  the rough positioning information, it contains a coordinate transform from
- *  the chip to the focal plane. It is expected that this transform will consist
- *  of two second-order 2D polynomials; hence we think that it is prudent to
- *  include a reverse transformation which will be derived from numerically
- *  inverting the forward transformation. A boolean indicates whether the chip is
- *  of interest, allowing it to be excluded from analysis.
- *  
- */
-typedef struct
-{
-    // Offset specifying position on focal plane
-    int col0;                           ///< Offset from the left of FPA.
-    int row0;                           ///< Offset from the bottom of FPA.
-    // Astrometric transformations
-    psPlaneTransform* toFPA;            ///< Transformation from chip to FPA coordinates
-    psPlaneTransform* fromFPA;          ///< Transformation from FPA to chip coordinates
-    // Information
-    psMetadata *concepts;               ///< Cache for PS concepts
-    psMetadata *analysis;               ///< Chip-level analysis metadata
-    psArray *cells;                     ///< The cells (referred to by name)
-    pmFPA *parent;                      ///< Parent FPA
-    bool valid;                         ///< Do we bother about reading and working with this chip?
-    // FITS data
-    const char *extname;                ///< Extension name, if it corresponds to this level
-    psArray *pixels;                    ///< The pixel data, if it corresponds to this level
-    psMetadata *header;                 ///< The FITS header, if it corresponds to this level
-}
-pmChip;
-
-/** Cell data structure
- *
- *  A cell consists of one or more readouts.  It also contains a pointer to the
- *  cell's metadata, and its parent chip.  On the astrometry side, it also
- *  contains coordinate transforms from the cell to chip, from the cell to
- *  focal-plane, as well as a "quick and dirty" tranform from the cell to
- *  sky coordinates.
- *
- */
-typedef struct
-{
-    // Offset specifying position on chip
-    int col0;                           ///< Offset from the left of chip.
-    int row0;                           ///< Offset from the bottom of chip.
-    // Astrometric transformations
-    psPlaneTransform* toChip;           ///< Transformations from cell to chip coordinates
-    psPlaneTransform* toFPA;            ///< Transformations from cell to FPA coordinates
-    psPlaneTransform* toSky;            ///< Transformations from cell to sky coordinates
-    // Information
-    psMetadata *concepts;               ///< Cache for PS concepts
-    psMetadata *analysis;               ///< Cell-level analysis metadata
-    psArray *readouts;                  ///< The readouts (referred to by number)
-    pmChip *parent;                     ///< Parent chip
-    bool valid;                         ///< Do we bother about reading and working with this cell?
-    // FITS data
-    const char *extname;                ///< Extension name, if it corresponds to this level
-    psArray *pixels;                    ///< The pixel data, if it corresponds to this level
-    psMetadata *header;                 ///< The FITS header, if it corresponds to this level
-}
-pmCell;
-
-/** Readout data structure.
- *
- *  A readout is the result of a single read of a cell (or a portion thereof).
- *  It contains the offset from the lower-left corner of the chip, in the case
- *  that the CCD was windowed, as well as the binning factors and parity (if the
- *  binning value is negative, then the parity is reversed). It also contains the
- *  pixel data, metadata containers for the concepts and analysis, and a link to
- *  the parent.
- *
- */
-typedef struct
-{
-    // Position on the cell
-    int col0;                           ///< Offset from the left of chip.
-    int row0;                           ///< Offset from the bottom of chip.
-    int colBins;                        ///< Amount of binning in x-dimension
-    int rowBins;                        ///< Amount of binning in y-dimension
-    // Information
-    psImage *image;                     ///< Imaging area of readout
-    // XXX: The following mask was removed from the pmReadout struct in recent SDRS
-    // versions.  However, I'm keeping it here since al ot of modules still require
-    // it.
-    psImage *mask;                      ///< Mask of input image
-    psMetadata *analysis;               ///< Readout-level analysis metadata
-    psMetadata *concepts;               ///< Cache for PS Concepts
-    pmCell *parent;                     ///< Parent cell
-}
-pmReadout;
-
-
-/** Allocates a pmReadout
- *
- *  The constructor shall make an empty pmReadout. If the parent cell is not
- *  NULL, the parent link is made and the readout shall be placed in the
- *  parents array of readouts. The metadata containers shall be allocated. All
- *  other pointers in the structure shall be initialized to NULL.
- *
- *  @return pmReadout*    newly allocated pmReadout with all internal pointers set to NULL
- */
-pmReadout *pmReadoutAlloc(
-    pmCell *cell                        ///< Parent cell
-);
-
-/** Allocates a pmCell
- *
- *  The constructor shall make an empty pmCell. If the parent chip is not NULL,
- *  the parent link is made and the cell shall be placed in the parents array of
- *  cells. The readouts array shall be allocated with a zero size, and the
- *  metadata containers constructed. All other pointers in the structure shall be
- *  initialized to NULL.
- *
- *  @return pmCell*    newly allocated pmCell
- */
-pmCell *pmCellAlloc(
-    pmChip *chip                        ///< Parent chip
-);
-
-/** Allocates a pmChip
- * 
- *  The constructor shall make an empty pmChip. If the parent fpa is not NULL,
- *  the parent link is made and the chip shall be placed in the parent's array
- *  of chips. The cells array shall be allocated with a zero size, and the
- *  metadata containers constructed. All other pointers in the structure shall be
- *  initialized to NULL.
- *
- *  @return pmChip*    newly allocated pmChip
- */
-pmChip *pmChipAlloc(pmFPA *fpa);
-
-/** Allocates a pmFPA
- * 
- *  The constructor shall make an empty pmFPA. The chips array shall be
- *  allocated with a zero size, the camera and db pointers set to the values
- *  provided, and the concepts metadata constructed. All other pointers in the
- *  structure shall be initialized to NULL.
- * 
- */
-pmFPA *pmFPAAlloc(
-    const psMetadata *camera,           ///< Camera configuration
-    psDB *db                            ///< Database handle
-);
-
-
-/** Verify parent links.
- * 
- *  This function checks the validity of the parent links in the FPA hierarchy.
- *  If a parent link is not set (or not set correctly), it is corrected, and the
- *  function shall return false. If all the parent pointers were correct, the
- *  function shall return true.
- * 
- */
-bool pmFPACheckParents(
-    pmFPA *fpa
-);
-
-
-
-/** FUNC DESC
- * 
- * 
- * 
- * 
- */
-
-
-
-/*****************************************************************************
-    Old Stuff
- *****************************************************************************/
-
-
-
-/** Find cooresponding cell for given FPA coordinate
- *
- *  @return pmCell*    the cell cooresponding to the coord in FPA
- */
-pmCell* pmCellInFPA(
-    const psPlane* coord,              ///< the coordinate in FPA plane
-    const pmFPA* FPA                   ///< the FPA to search for the cell
-);
-
-
-/** Find cooresponding chip for given FPA coordinate
- *
- *  @return pmChip*    the chip cooresponding to coord
- */
-pmChip* pmChipInFPA(
-    const psPlane* coord,              ///< the coordinate in FPA plane
-    const pmFPA* FPA                   ///< the FPA to search for the cell
-);
-
-
-/** Find cooresponding cell for given Chip coordinate
- *
- *  @return pmCell*    the cell cooresponding to coord
- */
-pmCell* pmCellInChip(
-    const psPlane* coord,              ///< the coordinate in Chip plane
-    const pmChip* chip                 ///< the chip to search for the cell
-);
-
-
-/** Translate a cell coordinate into a chip coordinate
- *
- *  @return psPlane*    the resulting chip coordinate
- */
-psPlane* psCoordCellToChip(
-    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
-    const psPlane* in,                 ///< the coordinate within Cell
-    const pmCell* cell                 ///< the Cell in interest
-);
-
-
-/** Translate a chip coordinate into a FPA coordinate
- *
- *  @return psPlane*    the resulting FPA coordinate
- */
-psPlane* psCoordChipToFPA(
-    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
-    const psPlane* in,                 ///< the coordinate within Chip
-    const pmChip* chip                 ///< the chip in interest
-);
-
-
-/** Translate a FPA coordinate into a Tangent Plane coordinate
- *
- *  @return psPlane*    the resulting Tangent Plane coordinate
- */
-psPlane* psCoordFPAToTP(
-    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
-    const psPlane* in,                 ///< the coordinate within FPA
-    double color,                      ///< Color of source
-    double magnitude,                  ///< Magnitude of source
-    const pmFPA* fpa                   ///< the FPA in interest
-);
-
-
-/** Translate a Tangent Plane coordinate into a Sky coordinate
- *
- *  @return psSphere*    the resulting Sky coordinate
- */
-psSphere* psCoordTPToSky(
-    psSphere* out,                     ///< a sphere struct to recycle. If NULL, a new struct is created
-    const psPlane* in,                ///< the coordinate within Tangent Plane
-    const psProjection *projection
-);
-
-/** Translate a cell coordinate into a FPA coordinate
- *
- *  @return psPlane*    the resulting FPA coordinate
- */
-psPlane* psCoordCellToFPA(
-    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
-    const psPlane* in,                 ///< the coordinate within cell
-    const pmCell* cell                 ///< the cell in interest
-);
-
-
-/** Translate a cell coordinate into a Sky coordinate
- *
- *  @return psSphere*    the resulting Sky coordinate
- */
-psSphere* psCoordCellToSky(
-    psSphere* out,                     ///< a sphere struct to recycle. If NULL, a new struct is created
-    const psPlane* in,                 ///< the coordinate within cell
-    double color,                      ///< Color of source
-    double magnitude,                  ///< Magnitude of source
-    const pmCell* cell                 ///< the cell in interest
-);
-
-
-/** Translate a cell coordinate into a Sky coordinate using a 'quick and
- *  dirty' method
- *
- *  @return psSphere*    the resulting Sky coordinate
- */
-psSphere* psCoordCellToSkyQuick(
-    psSphere* out,                     ///< a sphere struct to recycle. If NULL, a new struct is created
-    const psPlane* in,                 ///< the coordinate within cell
-    const pmCell* cell                 ///< the cell in interest
-);
-
-
-/** Translate a Sky coordinate into a Tangent Plane coordinate
- *
- *  @return psPlane*    the resulting Tangent Plane coordinate
- */
-psPlane* psCoordSkyToTP(
-    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
-    const psSphere* in,                ///< the sky coordinate
-    const psProjection *projection
-);
-
-/** Translate a Tangent Plane coordinate into a FPA coordinate
- *
- *  @return psPlane*    the resulting FPA coordinate
- */
-psPlane* psCoordTPToFPA(
-    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
-    const psPlane* in,                 ///< the coordinate within tangent plane
-    double color,                      ///< Color of source
-    double magnitude,                  ///< Magnitude of source
-    const pmFPA* fpa                   ///< the FPA of interest
-);
-
-
-/** Translate a FPA coordinate into a chip coordinate
- *
- *  @return psPlane*    the resulting chip coordinate
- */
-psPlane* psCoordFPAToChip(
-    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
-    const psPlane* in,                 ///< the FPA coordinate
-    const pmChip* chip                 ///< the chip of interest
-);
-
-
-/** Translate a chip coordinate into a cell coordinate
- *
- *  @return psPlane*    the resulting cell coordinate
- */
-psPlane* psCoordChipToCell(
-    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
-    const psPlane* in,                 ///< the Chip coordinate
-    const pmCell* cell                 ///< the cell of interest
-);
-
-
-/** Translate a sky coordinate into a cell coordinate
- *
- *  @return psPlane*    the resulting cell coordinate
- */
-psPlane* psCoordSkyToCell(
-    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
-    const psSphere* in,                ///< the Sky coordinate
-    float color,                       ///< Color of source
-    float magnitude,                   ///< Magnitude of source
-    const pmCell* cell                 ///< the cell of interest
-);
-
-
-/** Translate a sky coordinate into a cell coordinate using a 'quick and
- *  dirty' method
- *
- *  @return psPlane*    the resulting cell coordinate
- */
-psPlane* psCoordSkyToCellQuick(
-    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
-    const psSphere* in,                ///< the Sky coordinate
-    const pmCell* cell                 ///< the cell of interest
-);
-
-
-psMetadataItem *pmCellGetConcept(pmCell *cell, const char *concept);
-psMetadataItem *pmChipGetConcept(pmChip *chip, const char *concept);
-psMetadataItem *pmFPAGetConcept(pmFPA *fpa, const char *concept);
-
-/**
- * 
- *  We next specify a series of specific functions for concept lookups. These
- *  will generally be what the user utilises, so the goal is to provide a simple
- *  interface providing a single type back, so the user doesnt have to go to the
- *  trouble of checking types, etc. These functions should employ the above three
- *  general lookup functions and deal with the result appropriately.
- *
- */
-float pmFPAGetAirmass(pmFPA *fpa); // FPA.AIRMASS
-psString pmFPAGetFilter(pmFPA *fpa); // FPA.FILTER
-float pmFPAGetPosAngle(pmFPA *fpa); // FPA.POSANGLE
-double pmFPAGetRA(pmFPA *fpa); // FPA.RA
-double pmFPAGetDec(pmFPA *fpa); // FPA.DEC
-psString pmFPAGetRADecSys(pmFPA *fpa); // FPA.RADECSYS
-psString pmFPAGetName(pmFPA *fpa); // FPA.NAME
-psString pmChipGetName(pmChip *chip); // CHIP.NAME
-psString pmCellGetName(pmCell *cell); // CELL.NAME
-psTime *pmCellGetTime(pmCell *cell); // CELL.TIME
-psList *pmCellGetBiasSec(pmCell *cell); // CELL.BIASSEC
-psRegion pmCellGetTrimSec(pmCell *cell); // CELL.TRIMSEC
-float pmCellGetGain(pmCell *cell); // CELL.GAIN
-float pmCellGetReadNoise(pmCell *cell); // CELL.READNOISE
-float pmCellGetSaturation(pmCell *cell); // CELL.SATURATION
-float pmCellGetBad(pmCell *cell); // CELL.BAD
-psPixelCoord pmCellGetBin(pmCell *cell); // CELL.BIN
-psPixelCoord pmCellGetParity(pmCell *cell); // CELL.PARITY
-float pmReadoutGetExposure(pmReadout *readout); // READOUT.EXPOSURE
-float pmReadoutGetDarkTime(pmReadout *readout); // READOUT.DARKTIME
-
-
-
-#endif // #ifndef PS_ASTROMETRY_H
Index: unk/psModules/src/pmFlatField.c
===================================================================
--- /trunk/psModules/src/pmFlatField.c	(revision 5169)
+++ 	(revision )
@@ -1,182 +1,0 @@
-/** @file  pmFlatField.c
- *
- *  @brief Given an input image and a flat field image, pmFlatField shall divide the input image by the flat
- *  field image.
- *
- *  The input image, in, and the flat field image, flat, need not be the same size, since the input image may
- *  already have been trimmed (following overscan subtraction), but the function shall use the offsets in the
- *  image (in->x0 and in->y0) to determine the appropriate offsets to obtain the correct pixel on the flat
- *  field. In the event that the flat image is too small (i.e., pixels on the input image refer to pixels
- *  outside the range of the flat image), the function shall generate an error. Pixels which are negative or
- *  zero in the flat shall be masked in the input image with the value PM_MASK_FLAT. Negative pixels in the
- *  flat may be set to zero so that they are treated identically to zeroes. Any pixels masked in the flat
- *  shall be masked with corresponding values in the output. The function shall not normalize the flat; this
- *  responsibility is left to the caller. This function is basically equivalent to a divide (with psImageOp),
- *  but with care for the region that is divided, checking for negative pixels, and copying of the mask from
- *  the flat to the output.
- *
- *  @author Ross Harman, MHPCC
- *
- *  @version $Revision: 1.19 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-09-12 20:38:25 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- */
-
-#if HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include<stdio.h>
-#include<math.h>
-#include <string.h>
-
-#include "pslib.h"
-#include "pmFlatField.h"
-#include "pmMaskBadPixels.h"
-#include "pmFlatFieldErrors.h"
-
-
-bool pmFlatField(pmReadout *in, pmReadout *mask, const pmReadout *flat)
-{
-    // XXX: Not sure if this is correct.  Must consult with IfA.
-    PS_ASSERT_PTR_NON_NULL(mask, false);
-    int i = 0;
-    int j = 0;
-    int totOffCol = 0;
-    int totOffRow = 0;
-    psElemType inType;
-    psElemType flatType;
-    psElemType maskType;
-    psImage *inImage = NULL;
-    psImage *inMask = NULL;
-    psImage *flatImage = NULL;
-
-
-    // Check for nulls
-    if (in == NULL) {
-        return true;       // Readout may not have data in it
-    } else if(flat==NULL) {
-        psError( PS_ERR_BAD_PARAMETER_NULL, true,
-                 PS_ERRORTEXT_pmFlatField_NULL_FLAT_READOUT);
-        return false;
-    }
-
-    inImage = in->image;
-    flatImage = flat->image;
-    if (inImage == NULL) {
-        psError( PS_ERR_BAD_PARAMETER_NULL, true,
-                 PS_ERRORTEXT_pmFlatField_NULL_INPUT_IMAGE);
-        return false;
-    } else if(flatImage == NULL) {
-        psError( PS_ERR_BAD_PARAMETER_NULL, true,
-                 PS_ERRORTEXT_pmFlatField_NULL_FLAT_IMAGE);
-        return false;
-    }
-    inMask = mask->image;
-
-    // Check input image and its mask are not larger than flat image
-
-    if (inImage->numRows>flatImage->numRows || inImage->numCols>flatImage->numCols) {
-        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
-                 PS_ERRORTEXT_pmFlatField_SIZE_INPUT_IMAGE,
-                 inImage->numRows, inImage->numCols, flatImage->numRows, flatImage->numCols);
-        return false;
-    }
-    if (inMask->numRows > flatImage->numRows || inMask->numCols > flatImage->numCols) {
-        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
-                 PS_ERRORTEXT_pmFlatField_SIZE_MASK_IMAGE,
-                 inMask->numRows, inMask->numCols, flatImage->numRows, flatImage->numCols);
-        return false;
-    }
-
-    // Determine total offset based on image offset with chip offset
-    totOffCol = inImage->col0 + in->col0;
-    totOffRow = inImage->row0 + in->row0;
-
-    // Check that offsets are within image limits
-    if(totOffRow>=flatImage->numRows || totOffCol>=flatImage->numCols) {
-        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
-                 PS_ERRORTEXT_pmFlatField_OFFSET_FLAT_IMAGE,
-                 totOffRow, totOffCol, flatImage->numRows, flatImage->numCols);
-        return false;
-    } else if(totOffRow>=inImage->numRows || totOffCol>=inImage->numCols) {
-        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
-                 PS_ERRORTEXT_pmFlatField_OFFSET_INPUT_IMAGE,
-                 totOffRow, totOffCol, inImage->numRows, inImage->numCols);
-        return false;
-    } else if(totOffRow>=inMask->numRows || totOffCol>=inMask->numCols) {
-        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
-                 PS_ERRORTEXT_pmFlatField_OFFSET_MASK_IMAGE,
-                 totOffRow, totOffCol, inMask->numRows, inMask->numCols);
-        return false;
-    }
-
-    // Check for incorrect types
-    inType = inImage->type.type;
-    flatType = flatImage->type.type;
-    maskType = inMask->type.type;
-    if(PS_IS_PSELEMTYPE_COMPLEX(inType)) {
-        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
-                 PS_ERRORTEXT_pmFlatField_TYPE_INPUT_IMAGE,
-                 inType);
-        return false;
-    } else if(PS_IS_PSELEMTYPE_COMPLEX(flatType)) {
-        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
-                 PS_ERRORTEXT_pmFlatField_TYPE_FLAT_IMAGE,
-                 flatType);
-        return false;
-    } else if(maskType != PS_TYPE_MASK) {
-        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
-                 PS_ERRORTEXT_pmFlatField_TYPE_MASK_IMAGE,
-                 maskType);
-        return false;
-    } else if(inType != flatType) {
-        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
-                 PS_ERRORTEXT_pmFlatField_TYPE_MISMATCH,
-                 inType, flatType);
-        return false;
-    }
-
-    // Macro for all PS types
-    #define PM_FLAT_DIVISION(TYPE)                                                                           \
-case PS_TYPE_##TYPE:                                                                                         \
-    /* Per Eugene's request, use two sets of loops: first to fill mask, second to avoid div with bad pix */  \
-    for(j = totOffRow; j < inImage->numRows; j++) {                                                          \
-        for(i = totOffCol; i < inImage->numCols; i++) {                                                      \
-            if(flatImage->data.TYPE[j][i] <= 0.0) {                                                          \
-                /* Negative or zero flat pixels shall be masked in input image as  PM_MASK_FLAT */           \
-                inMask->data.PS_TYPE_MASK_DATA[j][i] |= PM_MASK_FLAT;                                        \
-                flatImage->data.TYPE[j][i] = 0.0;                                                            \
-            }                                                                                                \
-        }                                                                                                    \
-    }                                                                                                        \
-    for(j = totOffRow; j < inImage->numRows; j++) {                                                          \
-        for(i = totOffCol; i < inImage->numCols; i++) {                                                      \
-            if(!inMask->data.PS_TYPE_MASK_DATA[j][i]) {                                                      \
-                /* Module shall divide the input image by the flat-fielded image */                          \
-                inImage->data.TYPE[j][i] /= flatImage->data.TYPE[j][i];                                      \
-            }                                                                                                \
-        }                                                                                                    \
-    }                                                                                                        \
-    break;
-
-    switch(inType) {
-        PM_FLAT_DIVISION(U8);
-        PM_FLAT_DIVISION(U16);
-        PM_FLAT_DIVISION(U32);
-        PM_FLAT_DIVISION(U64);
-        PM_FLAT_DIVISION(S8);
-        PM_FLAT_DIVISION(S16);
-        PM_FLAT_DIVISION(S32);
-        PM_FLAT_DIVISION(S64);
-        PM_FLAT_DIVISION(F32);
-        PM_FLAT_DIVISION(F64);
-    default:
-        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
-                 PS_ERRORTEXT_pmFlatField_TYPE_UNSUPPORTED,
-                 inType);
-    }
-
-    return true;
-}
Index: unk/psModules/src/pmFlatField.h
===================================================================
--- /trunk/psModules/src/pmFlatField.h	(revision 5169)
+++ 	(revision )
@@ -1,42 +1,0 @@
-/** @file  pmFlatField.h
- *
- *  @brief Given an input image and a flat field image, pmFlatField shall divide the input image by the flat
- *  field image.
- *
- *  The input image, in, and the flat field image, flat, need not be the same size, since the input image may
- *  already have been trimmed (following overscan subtraction), but the function shall use the offsets in the
- *  image (in->x0 and in->y0) to determine the appropriate offsets to obtain the correct pixel on the flat
- *  field. In the event that the flat image is too small (i.e., pixels on the input image refer to pixels
- *  outside the range of the flat image), the function shall generate an error. Pixels which are negative or
- *  zero in the flat shall be masked in the input image with the value PM_MASK_FLAT. Negative pixels in the
- *  flat may be set to zero so that they are treated identically to zeroes. Any pixels masked in the flat
- *  shall be masked with corresponding values in the output. The function shall not normalize the flat; this
- *  responsibility is left to the caller. This function is basically equivalent to a divide (with psImageOp),
- *  but with care for the region that is divided, checking for negative pixels, and copying of the mask from
- *  the flat to the output.
- *
- *  @author Ross Harman, MHPCC
- *
- *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-08-16 01:10:34 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- */
-
-#include "pslib.h"
-#include "pmAstrometry.h"
-
-
-/** Execute flat field module.
- *
- *  Given an input image and a flat-field image, pmFlatField shall divide the input image by the flat field
- *  image.
- *
- *  @return  bool: True or false for success or failure
- */
-bool pmFlatField(
-    pmReadout *in,          ///< Readout with input image
-    pmReadout *mask,        ///< Input image mask
-    const pmReadout *flat   ///< Readout with flat image
-);
-
Index: unk/psModules/src/pmFlatFieldErrors.dat
===================================================================
--- /trunk/psModules/src/pmFlatFieldErrors.dat	(revision 5169)
+++ 	(revision )
@@ -1,22 +1,0 @@
-#
-#  This file is used to generate pmFlatFieldErrors.h content
-#
-#  Format is:
-#  ERRORNAME(one word)    ERRORTEXT
-#
-#  N.B. in code, the ERRORNAME appears as PS_ERRORTEXT_ERRORNAME
-####################################################################
-#
-pmFlatField_NULL_FLAT_READOUT          Null not allowed for flat readout.
-pmFlatField_NULL_INPUT_IMAGE           Null not allowed for input image.
-pmFlatField_NULL_FLAT_IMAGE            Null not allowed for flat image.
-pmFlatField_SIZE_INPUT_IMAGE           Input image size exceeds that of flat image: (%d, %d) vs (%d, %d)
-pmFlatField_SIZE_MASK_IMAGE            Input image mask size exceeds that of flat image: (%d, %d) vs (%d, %d)
-pmFlatField_OFFSET_FLAT_IMAGE          Total offset >= flat image size: (%d, %d) vs (%d, %d)
-pmFlatField_OFFSET_INPUT_IMAGE         Total offset >= input image: (%d, %d) vs (%d, %d)
-pmFlatField_OFFSET_MASK_IMAGE          Total offset >= input image mask: (%d, %d) vs (%d, %d)
-pmFlatField_TYPE_INPUT_IMAGE           Complex types not allowed for input image. Type: %d
-pmFlatField_TYPE_FLAT_IMAGE            Complex types not allowed for flat image. Type: %d
-pmFlatField_TYPE_MASK_IMAGE            Mask must be PS_TYPE_MASK type. Type: %d
-pmFlatField_TYPE_MISMATCH              Input and flat image types differ: (%d vs %d)
-pmFlatField_TYPE_UNSUPPORTED           Unsupported image datatype. Type: %d
Index: unk/psModules/src/pmFlatFieldErrors.h
===================================================================
--- /trunk/psModules/src/pmFlatFieldErrors.h	(revision 5169)
+++ 	(revision )
@@ -1,47 +1,0 @@
-/** @file  pmFlatFieldErrors.h
- *
- *  @brief Contains the error text for the flat field module
- *
- *  @ingroup ErrorHandling
- *
- *  @author Ross Harman, MHPCC
- *
- *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2004-10-14 18:15:44 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- */
-
-#ifndef PM_FLATFIELD_ERRORS_H
-#define PM_FLATFIELD_ERRORS_H
-
-/* N.B., lines between '//~Start' and '//~End' are automatic generated from
- * the template following the '//~Start'.  The template is used to generate
- * the other lines by, for each error text in psDataManipErrors.dat, the following
- * substitutions are made:
- *     $1  The error text macro name (first word in the psFlatFieldErrors.h lines)
- *     $2  The error text (rest of the line in psFlatFieldErrors.h)
- *     $n  The order of the source line in psFlatFieldErrors.h (comments excluded)
- *
- * DO NOT EDIT THE LINES BETWEEN //~Start and //~End!  ANY CHANGES WILL BE OVERWRITTEN.
- */
-
-#define PS_ERRORNAME_DOMAIN "psModule.src."
-
-//~Start #define PS_ERRORTEXT_$1 "$2"
-#define PS_ERRORTEXT_pmFlatField_NULL_FLAT_READOUT "Null not allowed for flat readout."
-#define PS_ERRORTEXT_pmFlatField_NULL_INPUT_IMAGE "Null not allowed for input image."
-#define PS_ERRORTEXT_pmFlatField_NULL_FLAT_IMAGE "Null not allowed for flat image."
-#define PS_ERRORTEXT_pmFlatField_SIZE_INPUT_IMAGE "Input image size exceeds that of flat image: (%d, %d) vs (%d, %d)"
-#define PS_ERRORTEXT_pmFlatField_SIZE_MASK_IMAGE "Input image mask size exceeds that of flat image: (%d, %d) vs (%d, %d)"
-#define PS_ERRORTEXT_pmFlatField_OFFSET_FLAT_IMAGE "Total offset >= flat image size: (%d, %d) vs (%d, %d)"
-#define PS_ERRORTEXT_pmFlatField_OFFSET_INPUT_IMAGE "Total offset >= input image: (%d, %d) vs (%d, %d)"
-#define PS_ERRORTEXT_pmFlatField_OFFSET_MASK_IMAGE "Total offset >= input image mask: (%d, %d) vs (%d, %d)"
-#define PS_ERRORTEXT_pmFlatField_TYPE_INPUT_IMAGE "Complex types not allowed for input image. Type: %d"
-#define PS_ERRORTEXT_pmFlatField_TYPE_FLAT_IMAGE "Complex types not allowed for flat image. Type: %d"
-#define PS_ERRORTEXT_pmFlatField_TYPE_MASK_IMAGE "Mask must be PS_TYPE_MASK type. Type: %d"
-#define PS_ERRORTEXT_pmFlatField_TYPE_MISMATCH "Input and flat image types differ: (%d vs %d)"
-#define PS_ERRORTEXT_pmFlatField_TYPE_UNSUPPORTED "Unsupported image datatype. Type: %d"
-//~End
-
-#endif
Index: unk/psModules/src/pmImageCombine.c
===================================================================
--- /trunk/psModules/src/pmImageCombine.c	(revision 5169)
+++ 	(revision )
@@ -1,666 +1,0 @@
-/** @file  pmImageCombine.c
- *
- *  This file will perform image combination of several images of the
- *  same field, produce a list of questionable pixels, then tag some
- *  of those pixels as cosmic rays.
- *
- *  @author Paul Price, IfA (original prototype)
- *  @author GLG, MHPCC
- *
- *  @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-09-11 22:25:39 $
- *
- *  XXX: pmRejectPixels() has a known bug with the pmImageTransform() call.
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- */
-
-#include <config.h>
-#include <stdio.h>
-#include <math.h>
-#include "pslib.h"
-
-//
-// The following macros define how big the initial pixel list will be, and
-// how much it should be incremented when realloc'ed.
-//
-#define PS_COMBINE_IMAGE_INITIAL_PIXEL_LIST_LENGTH 100
-#define PS_COMBINE_IMAGE_INITIAL_PIXEL_LIST_LENGTH_INC 100
-#define PS_COMBINE_IMAGE_MAX_QUESTIONABLE_PIXELS 1000
-/******************************************************************************
-pmCombineImages(combine, questionablePixels, images, errors, masks, maskVal,
-                pixels, numIter, sigmaClip, stats)
- 
-XXX: Allocate a dummy psStats structure so that we don't destroy away its data.
- *****************************************************************************/
-psImage *pmCombineImages(psImage *combine,              ///< Combined image (output)
-                         psArray **questionablePixels,  ///< Array of rejection masks
-                         const psArray *images,         ///< Array of input images
-                         const psArray *errors,         ///< Array of input error images
-                         const psArray *masks,          ///< Array of input masks
-                         psU32 maskVal,                 ///< Mask value
-                         const psPixels *pixels,        ///< Pixels to combine
-                         psS32 numIter,                 ///< Number of rejection iterations
-                         psF32 sigmaClip,               ///< Number of standard deviations at which to reject
-                         const psStats *stats           ///< Statistics to use in the combination
-                        )
-{
-
-    PS_ASSERT_PTR_NON_NULL(images, combine);
-    psU32 numImages = images->n;
-    psTrace("ImageCombine.pmCombineImages", 3, "Calling pmCombineImages(%d)\n", images->n);
-
-    if (errors != NULL) {
-        if (images->n != errors->n) {
-            psError(PS_ERR_UNKNOWN, true, "images and errors args must have same length (%d != %d)\n",
-                    images->n, errors->n);
-            return(combine);
-        }
-    }
-    if (masks != NULL) {
-        if (images->n != masks->n) {
-            psError(PS_ERR_UNKNOWN, true, "images and masks args must have same length (%d != %d)\n",
-                    images->n, masks->n);
-            return(combine);
-        }
-    }
-
-    psImage *tmpImg = (psImage *) images->data[0];
-    psU32 numRows = tmpImg->numRows;
-    psU32 numCols = tmpImg->numCols;
-
-    //
-    // Check that all images have the appropriate size and type.
-    //
-    for (psS32 i = 0 ; i < numImages ; i++) {
-        psImage *tmpDataImg = (psImage *) images->data[i];
-        PS_ASSERT_IMAGE_NON_NULL(tmpDataImg, combine);
-        PS_ASSERT_IMAGE_TYPE(tmpDataImg, PS_TYPE_F32, combine);
-        if ((tmpDataImg->numRows != numRows) || (tmpDataImg->numCols != numCols)) {
-            psError(PS_ERR_UNKNOWN, true, "image %d has size (%d, %d); should be (%d, %d).\n",
-                    i, tmpDataImg->numRows, tmpDataImg->numCols, numRows, numCols);
-        }
-
-        if (errors != NULL) {
-            psImage *tmpErrorImg = (psImage *) errors->data[i];
-            PS_ASSERT_IMAGE_NON_NULL(tmpErrorImg, combine);
-            PS_ASSERT_IMAGE_TYPE(tmpErrorImg, PS_TYPE_F32, combine);
-            PS_ASSERT_IMAGES_SIZE_EQUAL(tmpDataImg, tmpErrorImg, NULL);
-        }
-
-        if (masks != NULL) {
-            psImage *tmpMaskImg = (psImage *) masks->data[i];
-            PS_ASSERT_IMAGE_NON_NULL(tmpMaskImg, combine);
-            PS_ASSERT_IMAGE_TYPE(tmpMaskImg, PS_TYPE_U8, combine);
-            PS_ASSERT_IMAGES_SIZE_EQUAL(tmpDataImg, tmpMaskImg, NULL);
-        }
-    }
-    PS_ASSERT_PTR_NON_NULL(stats, combine);
-
-    // Allocate and initialize the combined image, if necessary.
-    if (combine == NULL) {
-        combine = psImageAlloc(numCols, numRows, PS_TYPE_F32);
-        if (pixels != NULL) {
-            PS_IMAGE_SET_F32(combine, 0.0);
-        }
-    }
-
-    //
-    // Allocate the questionablePixels psArray, if necesssary, then create a psPixels
-    // struct for each image.
-    //
-    if (*questionablePixels == NULL) {
-        *questionablePixels = psArrayAlloc(numImages);
-    } else if ((*questionablePixels)->n != numImages) {
-        *questionablePixels = psArrayRealloc(*questionablePixels, numImages);
-    }
-    for (psS32 im = 0 ; im < numImages ; im++) {
-        psFree((*questionablePixels)->data[im]);
-        ((*questionablePixels)->data[im]) = (psPtr *) psPixelsAlloc(PS_COMBINE_IMAGE_INITIAL_PIXEL_LIST_LENGTH);
-        ((psPixels *) ((*questionablePixels)->data[im]))->n = 0;
-    }
-    //
-    // qpPtr is used to maintain a count of the questionable pixels for each image.
-    //
-    psVector *qpPtr = psVectorAlloc(numImages, PS_TYPE_S32);
-    PS_VECTOR_SET_S32(qpPtr, 0);
-
-    //
-    // Allocate the necessary psVectors for the call to psVectorStats().
-    // These vectors will be used whether we are combining a list of pixels,
-    // or every pixel in the input images.
-    //
-    psVector *pixelData = psVectorAlloc(numImages, PS_TYPE_F32);
-
-    psVector *pixelMask = NULL;
-    if (masks != NULL) {
-        pixelMask = psVectorAlloc(numImages, PS_TYPE_U8);
-        PS_VECTOR_SET_U8(pixelMask, 0);
-    }
-
-    psVector *pixelErrors = NULL;
-    if (errors != NULL) {
-        pixelErrors = psVectorAlloc(numImages, PS_TYPE_F32);
-        PS_VECTOR_SET_F32(pixelErrors, 1.0);
-    }
-
-    if (pixels != NULL) {
-        // Only those specified pixels should be combined.
-
-        psStats *stdevStats = psStatsAlloc(PS_STAT_SAMPLE_STDEV);
-
-        for (psS32 p = 0 ; p < pixels->n ; p++) {
-            // Must initialize the mask to 0 for every pixel.
-            PS_VECTOR_SET_U8(pixelMask, 0);
-            psS32 col = (pixels->data[p]).x;
-            psS32 row = (pixels->data[p]).y;
-
-            //
-            // Loop through each image, extract the pixel/mask/error data
-            // into psVectors.
-            //
-            for (psS32 im = 0 ; im < numImages ; im++) {
-                // Set the pixel data
-                pixelData->data.F32[im] =       ((psImage *) images->data[im])->data.F32[row][col];
-                // Set the pixel mask data, if necessary
-                if (masks != NULL) {
-                    pixelMask->data.U8[im] =   ((psImage *) masks->data[im])->data.F32[row][col];
-                }
-
-                // Set the pixel error data, if necessary
-                if (errors != NULL) {
-                    pixelErrors->data.F32[im] = ((psImage *) errors->data[im])->data.F32[row][col];
-                }
-            }
-
-            //
-            // Iterate on the pixels, rejecting outliers
-            //
-            for (psS32 iter = 0 ; iter < numIter ; iter++) {
-                // Combine all the pixels, using the specified stat.
-
-                stats = psVectorStats((psStats *) stats, pixelData, pixelErrors, pixelMask, maskVal);
-                psF64 combinedPixel;
-                psBool rc = p_psGetStatValue(stats, &combinedPixel);
-                if (rc != true) {
-                    psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not combine pixels (%d, %d) with the specified stat.\n", row, col);
-                }
-                if (iter == 0) {
-                    combine->data.F32[row][col] = (psF32) combinedPixel;
-                }
-
-                //
-                // Reject all pixels that lie more that sigmaClip standard deviations from
-                // the combined pixel value.
-                //
-                psS32 numRejects = 0;
-                for (psS32 im = 0 ; im < numImages ; im++) {
-                    stdevStats = psVectorStats(stdevStats, pixelData, pixelErrors, pixelMask, maskVal);
-                    psF64 stdev;
-                    psBool rc = p_psGetStatValue(stdevStats, &stdev);
-                    if (rc != true) {
-                        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not compute the standard deviation of pixel (%d, %d).\n", row, col);
-                        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not combine pixels (%d, %d) with the specified stat.\n", row, col);
-                    }
-
-                    if (!(pixelMask->data.U8[im] & maskVal)) {
-                        if (fabs(pixelData->data.F32[im]) >
-                                (fabs(sigmaClip * stdev) + fabs(combinedPixel))) {
-                            numRejects++;
-                            pixelMask->data.U8[im] = maskVal;
-                            //
-                            // XXX: These data structures indirections are getting complicated.
-                            //
-                            psS32 ptr = qpPtr->data.S32[im];
-                            psPixels *pixelListPtr = ((psPixels *) ((*questionablePixels)->data[im]));
-
-                            if (ptr >= pixelListPtr->nalloc) {
-                                (*questionablePixels)->data[im] =
-                                    (psPtr *) psPixelsRealloc(((psPixels *) ((*questionablePixels)->data[im])),
-                                                              ((((psPixels *) ((*questionablePixels)->data[im]))->nalloc) +
-                                                               PS_COMBINE_IMAGE_INITIAL_PIXEL_LIST_LENGTH_INC));
-                                // XXX: Can the realloc() fail?  Must we check for NULL?
-                            }
-                            ((psPixels *) ((*questionablePixels)->data[im]))->data[ptr].x = col;
-                            ((psPixels *) ((*questionablePixels)->data[im]))->data[ptr].y = row;
-                            (qpPtr->data.S32[im])++;
-                            // XXX: this pixel ->n increment is wierd
-                            ((psPixels *) ((*questionablePixels)->data[im]))->n = qpPtr->data.S32[im];
-                        }
-                    }
-                }
-
-                //
-                // If the number of rejected pixels is zero, then there's no point
-                // continuing the loop.
-                //
-                if (numRejects == 0) {
-                    break;
-                }
-                psS32 totalRejects = 0;
-                for (psS32 im = 0 ; im < numImages ; im++) {
-                    if (pixelMask->data.U8[im] & maskVal) {
-                        totalRejects++;
-                    }
-                }
-
-                //
-                // XXX: Is it possible to have all pixels rejected?  If so, we should
-                // exit the loop.
-                //
-                if (totalRejects == numImages) {
-                    break;
-                }
-            }
-        }
-        psFree(stdevStats);
-    } else {
-        //
-        // We get here if there is a NULL list of pixels to combine.
-        // Therefore, we combine all pixels in all images.
-        //
-
-        //
-        // Loop over all pixels in all images, set the appropriate data, mask,
-        // error vectors, call psVectorStats(), and set the result in the
-        // combine image.
-        //
-        for (psS32 row = 0 ; row < numRows ; row++) {
-            for (psS32 col = 0 ; col < numCols ; col++) {
-                for (psS32 im = 0 ; im < numImages ; im++) {
-                    // Set the pixel data
-                    pixelData->data.F32[im] =       ((psImage *) images->data[im])->data.F32[row][col];
-
-                    // Set the pixel mask data, if necessary
-                    if (masks != NULL) {
-                        pixelMask->data.U8[im] =   ((psImage *) masks->data[im])->data.F32[row][col];
-                    }
-
-                    // Set the pixel error data, if necessary
-                    if (errors != NULL) {
-                        pixelErrors->data.F32[im] = ((psImage *) errors->data[im])->data.F32[row][col];
-                    }
-
-                }
-                // Combine all the pixels, using the specified stat.
-                stats = psVectorStats((psStats *) stats, pixelData, pixelErrors, pixelMask, maskVal);
-                psF64 tmpF64;
-                psBool rc = p_psGetStatValue(stats, &tmpF64);
-                combine->data.F32[row][col] = (psF32) tmpF64;
-                if (rc != true) {
-                    psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not combine pixels (%d, %d) with the specified stat.\n", row, col);
-                }
-            }
-        }
-    }
-
-    psFree(pixelData);
-    psFree(pixelMask);
-    psFree(pixelErrors);
-    psFree(qpPtr);
-
-    psTrace("ImageCombine.pmCombineImages", 3, "Exiting pmCombineImages(%d)\n", images->n);
-    return(combine);
-}
-
-
-/******************************************************************************
-XXX: Directly from Paul Price
- *****************************************************************************/
-static psF32 CalcGradient(
-    psImage *image,
-    psImage *imageMask,
-    psS32 x,
-    psS32 y
-)
-{
-    psTrace("ImageCombine.CalcGradient", 4, "Calling CalcGradient(%d, %d)\n", x, y);
-    int num = 0;
-    psVector *pixels = psVectorAlloc(8, PS_TYPE_F32); // Array of pixels
-    psVector *mask = psVectorAlloc(8, PS_TYPE_U8); // Corresponding mask
-
-    // Get limits
-    int xMin = PS_MAX(x - 1, 0);
-    int xMax = PS_MIN(x + 1, image->numCols - 1);
-    int yMin = PS_MAX(y - 1, 0);
-    int yMax = PS_MIN(y + 1, image->numRows - 1);
-    if (imageMask != NULL) {
-        for (int j = yMin; j <= yMax; j++) {
-            for (int i = xMin; i <= xMax; i++) {
-                if ((i != x) && (j != y) && (0 == imageMask->data.U8[j][i])) {
-                    pixels->data.F32[num] = image->data.F32[j][i];
-                    mask->data.U8[num] = 0;
-                    num++;
-                } else {
-                    mask->data.U8[num] = 1;
-                }
-            }
-        }
-    } else {
-        //
-        // This code is simply the previous loop without the imageMask.
-        // XXX: Consider restructuring this.
-        //
-        for (int j = yMin; j <= yMax; j++) {
-            for (int i = xMin; i <= xMax; i++) {
-                if ((i != x) && (j != y)) {
-                    pixels->data.F32[num] = image->data.F32[j][i];
-                    mask->data.U8[num] = 0;
-                    num++;
-                } else {
-                    mask->data.U8[num] = 1;
-                }
-            }
-        }
-    }
-
-    pixels->n = num;
-    mask->n = num;
-
-    // Get the median
-    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
-    (void)psVectorStats(stats, pixels, NULL, mask, 1);
-    float median = stats->sampleMedian;
-    psFree(stats);
-    psFree(pixels);
-    psFree(mask);
-
-    psTrace("ImageCombine.CalcGradient", 4, "Exiting CalcGradient(%d, %d)\n", x, y);
-    return(median / image->data.F32[y][x]);
-}
-
-/******************************************************************************
-DetermineRegion(image, myOutToIn): for a psImage and a psPlaneTransform to that
-image, this routine determines the size of the input image which maps to that
-image, and returns the result in a psRegion struct.
- 
-XXX: Basically, this routine is only guaranteed to work if the transform is
-linear.
- 
-XXX: Shouldn't this functionality be part of psImageTransform()?
- *****************************************************************************/
-static psRegion DetermineRegion(psImage *image,
-                                psPlaneTransform *myOutToIn)
-{
-    psTrace("ImageCombine.DetermineRegion", 4, "Calling DetermineRegion()\n");
-    psRegion myRegion;
-    myRegion.x0 = PS_MAX_F32;
-    myRegion.x1 = PS_MIN_F32;
-    myRegion.y0 = PS_MAX_F32;
-    myRegion.y1 = PS_MIN_F32;
-    psPlane in;
-    psPlane out;
-
-    in.x = 0.0;
-    in.y = 0.0;
-
-    psPlaneTransformApply(&out, myOutToIn, &in);
-    if (out.x < myRegion.x0) {
-        myRegion.x0 = out.x;
-    }
-    if (out.x > myRegion.x1) {
-        myRegion.x1 = out.x;
-    }
-    if (out.y < myRegion.y0) {
-        myRegion.y0 = out.y;
-    }
-    if (out.y > myRegion.y1) {
-        myRegion.y1 = out.y;
-    }
-
-    in.x = (psF32) (image->numCols);
-    in.y = 0.0;
-    psPlaneTransformApply(&out, myOutToIn, &in);
-    if (out.x < myRegion.x0) {
-        myRegion.x0 = out.x;
-    }
-    if (out.x > myRegion.x1) {
-        myRegion.x1 = out.x;
-    }
-    if (out.y < myRegion.y0) {
-        myRegion.y0 = out.y;
-    }
-    if (out.y > myRegion.y1) {
-        myRegion.y1 = out.y;
-    }
-
-    in.x = (psF32) (image->numCols);
-    ;
-    in.y = 0.0;
-    psPlaneTransformApply(&out, myOutToIn, &in);
-    if (out.x < myRegion.x0) {
-        myRegion.x0 = out.x;
-    }
-    if (out.x > myRegion.x1) {
-        myRegion.x1 = out.x;
-    }
-    if (out.y < myRegion.y0) {
-        myRegion.y0 = out.y;
-    }
-    if (out.y > myRegion.y1) {
-        myRegion.y1 = out.y;
-    }
-
-    in.x = (psF32) (image->numCols);
-    in.y = (psF32) (image->numRows);
-    psPlaneTransformApply(&out, myOutToIn, &in);
-    if (out.x < myRegion.x0) {
-        myRegion.x0 = out.x;
-    }
-    if (out.x > myRegion.x1) {
-        myRegion.x1 = out.x;
-    }
-    if (out.y < myRegion.y0) {
-        myRegion.y0 = out.y;
-    }
-    if (out.y > myRegion.y1) {
-        myRegion.y1 = out.y;
-    }
-
-    psTrace("ImageCombine.DetermineRegion", 4, "Exiting DetermineRegion()\n");
-    return(myRegion);
-}
-
-/******************************************************************************
-XXX: Don't we have a psLib function for this?
- *****************************************************************************/
-static psImage *ImageConvertF32(psImage *image)
-{
-    psTrace("ImageCombine.ImageConvertF32", 4, "Calling ImageConvertF32()\n");
-    psImage *imgF32 = psImageAlloc(image->numCols, image->numRows, PS_TYPE_F32);
-
-    for (psS32 i = 0 ; i < image->numRows ; i++) {
-        for (psS32 j = 0 ; j < image->numCols ; j++) {
-            imgF32->data.F32[i][j] = (psF32) image->data.U8[i][j];
-        }
-    }
-
-    psTrace("ImageCombine.ImageConvertF32", 4, "Exiting ImageConvertF32()\n");
-    return(imgF32);
-}
-
-
-//
-// The following macros define how big the initial pixel list will be, and
-// how much it should be incremented when realloc'ed.
-//
-#define PS_REJECT_PIXEL_INITIAL_PIXEL_LIST_LENGTH 100
-#define PS_REJECT_PIXEL_INITIAL_PIXEL_LIST_LENGTH_INC 100
-/******************************************************************************
-pmRejectPixels(images, errors, inToOut, outToIn, rejThreshold,
-gradLimit)
- 
-XXX: Optimization: we don't need to transform the entire mask image.
-XXX: The inToOut and outToIn transforms are confusing.  Verify that what
-     I think they mean syncs with PWP.
- *****************************************************************************/
-psArray *pmRejectPixels(
-    const psArray *images,              ///< Array of input images
-    const psArray *masks,               ///< Array of input image masks
-    const psArray *errors,              ///< The pixels which were rejected in the combination
-    const psArray *inToOut,             ///< Transformation from input to output system
-    const psArray *outToIn,             ///< Transformation from output to input system
-    psF32 rejThreshold,                 ///< Rejection threshold
-    psF32 gradLimit                     ///< Gradient limit
-)
-{
-    psTrace("ImageCombine.pmRejectPixels", 3, "Calling pmRejectPixels()\n");
-    PS_ASSERT_PTR_NON_NULL(images, NULL);
-    for (psS32 im = 0 ; im < images->n ; im++) {
-        psImage *tmpImage = (psImage *) images->data[im];
-        PS_ASSERT_IMAGE_NON_NULL(tmpImage, NULL);
-        PS_ASSERT_IMAGE_NON_EMPTY(tmpImage, NULL);
-        PS_ASSERT_IMAGE_TYPE(tmpImage, PS_TYPE_F32, NULL);
-        if (masks != NULL) {
-            PS_ASSERT_INT_EQUAL(images->n, masks->n, NULL);
-            psImage *tmpMask = (psImage *) masks->data[im];
-            PS_ASSERT_IMAGE_NON_NULL(tmpMask, NULL);
-            PS_ASSERT_IMAGE_NON_EMPTY(tmpMask, NULL);
-            PS_ASSERT_IMAGE_TYPE(tmpMask, PS_TYPE_F32, NULL);
-            PS_ASSERT_IMAGES_SIZE_EQUAL(tmpImage, tmpMask, NULL);
-        }
-        PS_ASSERT_IMAGES_SIZE_EQUAL(((psImage *) images->data[0]), tmpImage, NULL);
-    }
-    PS_ASSERT_PTR_NON_NULL(errors, NULL);
-    PS_ASSERT_PTR_NON_NULL(inToOut, NULL);
-    PS_ASSERT_PTR_NON_NULL(outToIn, NULL);
-    // Ensure that the psArray parameters have an element for each image.
-    psS32 numImages = images->n;
-    PS_ASSERT_INT_EQUAL(numImages, errors->n, NULL);
-    PS_ASSERT_INT_EQUAL(numImages, inToOut->n, NULL);
-    PS_ASSERT_INT_EQUAL(numImages, outToIn->n, NULL);
-
-    //
-    // Create the psArray of psPixelLists, one for each image, for rejected pixels.
-    //
-    psArray *rejects = psArrayAlloc(numImages);
-    for (psS32 im = 0 ; im < numImages ; im++) {
-        rejects->data[im] = (psPtr *) psPixelsAlloc(PS_REJECT_PIXEL_INITIAL_PIXEL_LIST_LENGTH);
-        psPixels *pixels = (psPixels *) rejects->data[im];
-        pixels->n = 0;
-    }
-    //
-    // rPtr is used to maintain a count of the questionable pixels for each image.
-    //
-    psVector *rPtr = psVectorAlloc(numImages, PS_TYPE_S32);
-    PS_VECTOR_SET_S32(rPtr, 0);
-
-    psS32 numCols = ((psImage *) images->data[0])->numCols;
-    psS32 numRows = ((psImage *) images->data[0])->numRows;
-    psRegion myRegion = psRegionSet(0, numCols-1, 0, numRows-1);
-    psU32 maskVal = 1;  // XXX: Is this appropriate?
-
-    psPlane *inCoords = psAlloc(sizeof(psPlane));
-    psPlane *outCoords = psAlloc(sizeof(psPlane));
-
-    for (psS32 im = 0 ; im < numImages ; im++) {
-        //
-        // Extract data from psArrays.
-        //
-        psPixels *pixelList = (psPixels *) errors->data[im];
-
-        psImage *currImage = (psImage *) images->data[im];
-        myRegion.x0 = 0;
-        myRegion.x1 = currImage->numCols;
-        myRegion.y0 = 0;
-        myRegion.y1 = currImage->numRows;
-        psPlaneTransform *myInToOut = (psPlaneTransform *) inToOut->data[im];
-        psPlaneTransform *myOutToIn = (psPlaneTransform *) outToIn->data[im];
-
-        //
-        // Create a psU8 mask image from the list of cosmic pixels.
-        //
-        psImage *maskImage = NULL;
-        maskImage = psPixelsToMask(maskImage, pixelList, myRegion, maskVal);
-        psImage *maskImageF32 = ImageConvertF32(maskImage);
-
-        //
-        // Transform that mask image into detector coordinate space
-        //
-        psRegion myRegionXForm = DetermineRegion(maskImageF32, myOutToIn);
-        psImage *transformedImage = psImageTransform(NULL, NULL, maskImageF32, NULL,
-                                    0, myOutToIn, myRegionXForm, NULL,
-                                    PS_INTERPOLATE_BILINEAR, 0);
-
-        //
-        // Loop over all cosmic pixels.  Transform their coords to detector space.
-        // If the value of the transformed mask is larger than rejThreshold, then
-        // this might be a cosmic ray pixel.  We then calculate the mean gradient
-        // in other images.
-        //
-        for (psS32 p = 0 ; p < pixelList->n ; p++) {
-            inCoords->x = 0.5 + (psF32) (pixelList->data[p]).x;
-            inCoords->y = 0.5 + (psF32) (pixelList->data[p]).y;
-            psPlaneTransformApply(outCoords, myInToOut, inCoords);
-            psF32 maskVal = (psF32) psImagePixelInterpolate(transformedImage, outCoords->x, outCoords->y,
-                            NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
-            if (maskVal > rejThreshold) {
-
-                // This is a possible cosmic array pixel.  We must calculate the gradient
-                // at this location in all input images.
-                psF32 meanGrads = 0.0;
-                psS32 numGrads = 0;
-                //
-                // Loop through all other images, calculate their mean gradient.
-                //
-                for (psS32 otherImg = 0 ; otherImg < numImages ; otherImg++) {
-                    if (im != otherImg) {
-                        // Map the outCoords to inCoords that for otherImg space.
-                        psImage *tmpMask = NULL;
-                        if (masks != NULL) {
-                            tmpMask = masks->data[otherImg];
-                        }
-                        psPlaneTransformApply(inCoords,
-                                              (psPlaneTransform * )outToIn->data[otherImg],
-                                              outCoords);
-                        psS32 xPix = (int)(inCoords->x + 0.5);
-                        psS32 yPix = (int)(inCoords->y + 0.5);
-                        if ((xPix >= 0) && (xPix <= ((psImage*)(images->data[otherImg]))->numCols - 1) &&
-                                (yPix >= 0) && (yPix <= ((psImage*)(images->data[otherImg]))->numRows - 1)) {
-                            meanGrads += CalcGradient(images->data[otherImg], tmpMask, xPix, yPix);
-                            numGrads++;
-                        }
-                    }
-                }
-                if (numGrads > 0) {
-                    meanGrads /= (psF32) numGrads;
-                } else {
-                    // XXX: my idea.  Verify with PWP:
-                    meanGrads = 1.0 + gradLimit;
-                }
-
-                // XXX: The SDRS and the prototype code differ significantly here:
-                // if (CalcGradient(inputs->data[i], pixelList->data.x, pixelList->data.y) < (gradLimit * meanGrads)) {
-                if (meanGrads < gradLimit) {
-                    //
-                    // Add this to the list of questionable pixels.  We must ensure that the
-                    // pixelList is large enough; if not, we realloc()
-                    //
-                    psS32 ptr = rPtr->data.S32[im];
-                    psPixels *pixelListPtr = (psPixels *) rejects->data[im];
-                    if (ptr >= pixelListPtr->nalloc) {
-                        rejects->data[im] = (psPtr *) psPixelsRealloc(((psPixels *) rejects->data[im]),
-                                            ((((psPixels *) rejects->data[im])->nalloc) + PS_REJECT_PIXEL_INITIAL_PIXEL_LIST_LENGTH_INC));
-                        // XXX: Can the realloc() fail?  Must we check for NULL?
-                    }
-
-                    ((psPixels *) rejects->data[im])->data[ptr].x = (pixelList->data[p]).x;
-                    ((psPixels *) rejects->data[im])->data[ptr].y = (pixelList->data[p]).y;
-                    (rPtr->data.S32[im])++;
-                    // XXX: this pixel ->n increment is wierd
-                    (((psPixels *) rejects->data[im])->n)++;
-                }
-            }
-        }
-
-        psFree(maskImage);
-        psFree(maskImageF32);
-        psFree(transformedImage);
-    }
-
-    psFree(inCoords);
-    psFree(outCoords);
-    psTrace("ImageCombine.pmRejectPixels", 3, "Exiting pmRejectPixels()\n");
-    return(rejects);
-}
Index: unk/psModules/src/pmImageCombine.h
===================================================================
--- /trunk/psModules/src/pmImageCombine.h	(revision 5169)
+++ 	(revision )
@@ -1,50 +1,0 @@
-/** @file  pmImageCombine.h
- *
- *  This file will perform image combination of several images of the
- *  same field, produce a list of questionable pixels, then tag some
- *  of those pixels as cosmic rays.
- *
- *  @author Paul Price, IfA (original prototype)
- *  @author GLG, MHPCC
- *
- *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-09-11 22:25:39 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- */
-
-#if !defined(PM_IMAGE_COMBINE_H)
-#define PM_IMAGE_COMBINE_H
-
-#if HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include<stdio.h>
-#include<math.h>
-#include "pslib.h"
-
-psImage *pmCombineImages(
-    psImage *combine,                   ///< Combined image (output)
-    psArray **questionablePixels,       ///< Array of rejection masks
-    const psArray *images,              ///< Array of input images
-    const psArray *errors,              ///< Array of input error images
-    const psArray *masks,               ///< Array of input masks
-    psU32 maskVal,                      ///< Mask value
-    const psPixels *pixels,             ///< Pixels to combine
-    psS32 numIter,                      ///< Number of rejection iterations
-    psF32 sigmaClip,                    ///< Number of standard deviations at which to reject
-    const psStats *stats                ///< Statistics to use in the combination
-);
-
-psArray *pmRejectPixels(
-    const psArray *images,              ///< Array of input images
-    const psArray *masks,               ///< Array of input image masks
-    const psArray *errors,              ///< The pixels which were rejected in the combination
-    const psArray *inToOut,             ///< Transformation from input to output system
-    const psArray *outToIn,             ///< Transformation from output to input system
-    psF32 rejThreshold,                 ///< Rejection threshold
-    psF32 gradLimit                     ///< Gradient limit
-);
-#endif
Index: unk/psModules/src/pmImageSubtract.c
===================================================================
--- /trunk/psModules/src/pmImageSubtract.c	(revision 5169)
+++ 	(revision )
@@ -1,1390 +1,0 @@
-/** @file  ImageSubtract.c
- *
- *  This file will contain code which creates a set of kernel basis
- *  functions, solves for their solution, and applies them to an image.
- *
- *  @author Paul Price, IfA (original prototype)
- *  @author GLG, MHPCC
- *
- *  @version $Revision: 1.15 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-09-12 22:33:17 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- *   XXX: sync with IfA on this:
- *   The (x, y) (row, col) issue is becoming a problem.  In this file, and I
- *   think, the rest of psLib and psModules, the following conventions are used:
- *
- * 1) x will correspond to the column, and y will correspond to the row.
- * 2) When used in function prototypes, the column (and hence x) appears
- *    first.
- * 3) When used to index 2-D arrays, obviously, the row (and hence, y)
- *    appears first. (2 and 3 are the source of confusion).
- * 4) When (u, v) are used in certain structures.
- *  u corresponds to x
- *  v corresponds to y
- * 5) When element (a, b) is casually referred to in comments, or
- *    documentation it is unclear where a is the row, or the column.
- * 6) A convention on loop index variables (i, j) would be convenient.
- *    Currently, sometimes i corresponds to the column (x),
- *    usually it corresponds to the row (y).
- *
- *  XXX: The following variables are used an interpreted this way:
- * kernelSize: Means that the actual kernel is a square (1 + 2 * kernelSize) per side.
- * border: When accessing an image, a swath of pixels this wide is ignored.
- * footprint: When accessing a stample, a square of pixels, footprint pixels per side,
- *  are looked at.  We must ensure that (footprint+kernelSize) pixels exist
- *  around the center.
- */
-
-#include<stdio.h>
-#include<math.h>
-#include "pslib.h"
-#include "psConstants.h"
-#include "pmImageSubtract.h"
-
-/*******************************************************************************
-    Private alloc/free functions.
-XXX: It's not clear if the SubtractionKernels alloc/free functions are feasable.
- ******************************************************************************/
-void p_pmStampFree(pmStamp *stamp)
-{
-    psFree(stamp->matrix);
-    psFree(stamp->vector);
-}
-
-pmStamp *p_pmStampAlloc(pmStampStatus status)
-{
-    pmStamp *stamp = (pmStamp*)psAlloc(sizeof(pmStamp));
-    stamp->x = 0;
-    stamp->p_xSize = 0;
-    stamp->y = 0;
-    stamp->p_ySize = 0;
-    stamp->matrix = NULL;
-    stamp->vector = NULL;
-    stamp->status = status;
-
-    psMemSetDeallocator(stamp, (psFreeFunc)p_pmStampFree);
-
-    return(stamp);
-}
-
-void p_pmSubtractionKernelsFree(psSubtractionKernels *kernels)
-{
-    psFree(kernels->u);
-    psFree(kernels->v);
-    psFree(kernels->sigma);
-    psFree(kernels->xOrder);
-    psFree(kernels->yOrder);
-    psFree(kernels->preCalc);
-
-    psFree(kernels);
-}
-
-psSubtractionKernels *p_pmSubtractionKernelsAlloc(int numBasisFunctions,
-        pmSubtractionKernelsType type)
-{
-    psSubtractionKernels *tmp = (psSubtractionKernels *) psAlloc(sizeof(psSubtractionKernels));
-
-    tmp->type = type;
-    psMemSetDeallocator(tmp, (psFreeFunc) p_pmSubtractionKernelsFree);
-    return(tmp);
-}
-
-/*******************************************************************************
-psSubtractionKernels struct.
- ******************************************************************************/
-psSubtractionKernels *pmSubtractionKernelsAllocPOIS(int size,
-        int spatialOrder)
-{
-    psTrace("ImageSubtract.pmSubtractionKernelsAllocPOIS", 3,
-            "Calling pmSubtractionKernelsAllocPOIS(%d, %d)\n", size, spatialOrder);
-    PS_ASSERT_INT_POSITIVE(size, NULL);
-    PS_ASSERT_INT_POSITIVE(spatialOrder, NULL);
-    //
-    // Calculate the number of basis functions (nBF)
-    //
-    psS32 xKernelHalfSize = size;
-    psS32 yKernelHalfSize = size;
-    psS32 nBF = (2 * xKernelHalfSize + 1) *
-                (2 * yKernelHalfSize + 1) *
-                (spatialOrder + 1) *
-                (spatialOrder + 2) / 2;
-
-    //
-    // Generate the new psSubtractionKernels data structure:
-    //
-    psSubtractionKernels *tmp = (psSubtractionKernels *) psAlloc(sizeof(psSubtractionKernels));
-    tmp->type = PM_SUBTRACTION_KERNEL_POIS;
-    tmp->u = psVectorAlloc(nBF, PS_TYPE_F32);
-    tmp->v = psVectorAlloc(nBF, PS_TYPE_F32);
-    tmp->sigma = NULL;
-    tmp->xOrder = psVectorAlloc(nBF, PS_TYPE_F32);
-    tmp->yOrder = psVectorAlloc(nBF, PS_TYPE_F32);
-    tmp->subIndex = 0;
-    tmp->preCalc = NULL;
-    tmp->size = size;
-    tmp->spatialOrder = spatialOrder;
-
-    //
-    // This corresponds to the Kernel Basis Function with (u, v) = (0, 0)
-    //
-    // Put the (u,v) = (0,0) component right at the start of the list
-    // for convenience.
-    //
-    psS32 ptr = 0;
-    for (psS32 order = 0; order <= spatialOrder; order++) {
-        for (psS32 xOrder = 0; xOrder <= order; xOrder++) {
-            psS32 yOrder = order - xOrder;
-            tmp->u->data.F32[ptr] = 0;
-            tmp->v->data.F32[ptr] = 0;
-            tmp->xOrder->data.F32[ptr] = xOrder;
-            tmp->yOrder->data.F32[ptr] = yOrder;
-            ptr++;
-        }
-    }
-
-    //
-    // Iterate over (u,v).  Generate a set of kernels for each (u, v).
-    //
-    for (psS32 v = -yKernelHalfSize; v <= yKernelHalfSize; v++) {
-        for (psS32 u = -xKernelHalfSize; u <= xKernelHalfSize; u++) {
-            // Already did (u,v) = (0,0): it's at the start, so skip it now.
-            if ((u != 0) || (v != 0)) {
-                //
-                // Iterate over spatial order.  This loop creates the terms for
-                // x^xOrder * y^yOrder  such that (xOrder+yOrder) <= spatialOrder.
-                //
-                for (psS32 order = 0; order <= spatialOrder; order++) {
-                    for (psS32 xOrder = 0; xOrder <= order; xOrder++) {
-                        psS32 yOrder = order - xOrder;
-                        tmp->u->data.F32[ptr] = u;
-                        tmp->v->data.F32[ptr] = v;
-                        tmp->xOrder->data.F32[ptr] = xOrder;
-                        tmp->yOrder->data.F32[ptr] = yOrder;
-                        ptr++;
-                    }
-                }
-            }
-        }
-    }
-
-    psTrace("ImageSubtract.pmSubtractionKernelsAllocPOIS", 3,
-            "Exiting pmSubtractionKernelsAllocPOIS(%d, %d)\n", size, spatialOrder);
-    return(tmp);
-}
-
-/*******************************************************************************
-XXX: Get the types correct (u, v, xOrder, yOrder).
- 
-XXX: Code review this.
- ******************************************************************************/
-psSubtractionKernels *pmSubtractionKernelsAllocISIS(const psVector *sigmas,
-        const psVector *orders,
-        int size,
-        int spatialOrder)
-{
-    PS_ASSERT_VECTOR_NON_NULL(sigmas, NULL);
-    PS_ASSERT_VECTOR_NON_NULL(orders, NULL);
-    psTrace("ImageSubtract.pmSubtractionKernelsAllocISIS", 3,
-            "Calling pmSubtractionKernelsAllocISIS(%d, %d, %d, %d)\n",
-            sigmas->n, orders->n, size, spatialOrder);
-    PS_ASSERT_INT_POSITIVE(size, NULL);
-    PS_ASSERT_INT_POSITIVE(spatialOrder, NULL);
-    PS_ASSERT_VECTOR_TYPE(sigmas, PS_TYPE_F32, NULL);
-    PS_ASSERT_VECTOR_TYPE(orders, PS_TYPE_S32, NULL);
-    //
-    // Calculate the number of basis functions (nBF).
-    //
-    psS32 numSigmas = sigmas->n;
-
-    // XXX: Get rid of the sigma loop?  We merely multiple nBF by numSigmas?
-    // XXX: Verify that all this is correct.
-    psS32 nBF = 0;
-    for (psS32 s = 0 ; s < numSigmas ; s++) {
-        for (psS32 o = 0 ; o < orders->n ; o++) {
-            nBF+=((orders->data.S32[o] + 1) * (orders->data.S32[o] + 2) / 2);
-        }
-    }
-    nBF*= ((spatialOrder + 1) * (spatialOrder + 2) / 2);
-
-    //
-    // Generate the new psSubtractionKernels data structure:
-    //
-    psSubtractionKernels *tmp = (psSubtractionKernels *) psAlloc(sizeof(psSubtractionKernels));
-    tmp->type = PM_SUBTRACTION_KERNEL_ISIS;
-    tmp->u = psVectorAlloc(nBF, PS_TYPE_F32);
-    tmp->v = psVectorAlloc(nBF, PS_TYPE_F32);
-    tmp->sigma = psVectorAlloc(nBF, PS_TYPE_F32);
-    tmp->xOrder = psVectorAlloc(nBF, PS_TYPE_F32);
-    tmp->yOrder = psVectorAlloc(nBF, PS_TYPE_F32);
-    tmp->subIndex = 0;
-    tmp->size = size;
-    tmp->spatialOrder = spatialOrder;
-    tmp->preCalc = psArrayAlloc(nBF);
-
-    //
-    // We loop through all combinations of sigmas and polynomial orders
-    // creating the kernel basis functions.
-    //
-    psS32 ptr = 0;
-    for (psS32 sigPtr = 0 ; sigPtr < numSigmas ; sigPtr++) {
-        tmp->sigma->data.F32[sigPtr] = sigmas->data.F32[sigPtr];
-        //
-        // (xOrderP, yOrderP) are the order of the polynomial that modify the
-        // gaussian in the kernel.  They correspond to (j, k) in equation (5)
-        // from the psModules SDRS.
-        //
-        for (psS32 o = 0 ; o < orders->n ; o++) {
-            for (psS32 orderP = 0 ; orderP <= orders->data.S32[o] ; orderP++) {
-                for (psS32 xOrderP = 0 ; xOrderP <= orderP ; xOrderP++) {
-                    psS32 yOrderP = orderP - xOrderP;
-
-                    psImage *currPreCalc = psImageAlloc(1 + (2 * size), 1 + (2 * size), PS_TYPE_F32);
-                    PS_IMAGE_SET_F32(currPreCalc, 0.0);
-                    psBool setPreCalc = true;
-                    //
-                    // We loop through all spatial orders.  Since they have no effect on
-                    // the preCalc images, we only calculate them once, and store pointers
-                    // in tmp->preCalc->data[ptr] for other spatial orders.
-                    //
-                    for (psS32 order = 0; order <= spatialOrder; order++) {
-                        for (psS32 orderXTerm = 0; orderXTerm <= order; orderXTerm++) {
-                            PS_ASSERT_INT_LESS_THAN(ptr, nBF, NULL);
-
-                            psS32 orderYTerm = order - orderXTerm;
-
-                            tmp->u->data.F32[ptr] = xOrderP;
-                            tmp->v->data.F32[ptr] = yOrderP;
-                            tmp->xOrder->data.F32[ptr] = orderXTerm;
-                            tmp->yOrder->data.F32[ptr] = orderYTerm;
-                            tmp->sigma->data.F32[ptr] = sigmas->data.F32[sigPtr];
-                            tmp->preCalc->data[ptr] = (psPtr *) currPreCalc;
-
-                            //
-                            // We calculate the preCalc image only the first time through
-                            // this loop.  Otherwise, we increment the memory reference
-                            // counter.
-                            //
-                            if (setPreCalc == true) {
-                                for (psS32 v = -size; v <= size; v++) {
-                                    for (psS32 u = -size; u <= size; u++) {
-                                        // Scale the (u,v) coordinates in kernel space to [-1.0:1.0].
-                                        psF32 uScaled = ((psF32) u) / ((psF32) size);
-                                        psF32 vScaled = ((psF32) v) / ((psF32) size);
-
-                                        // Compute the value of the kernel at location (u, v):
-                                        psF32 exponent = (PS_SQR(uScaled) + PS_SQR(vScaled)) /
-                                                         (2.0 * PS_SQR(sigmas->data.F32[sigPtr]));
-                                        currPreCalc->data.F32[v+size][u+size] =
-                                            exp(-exponent) *
-                                            pow(uScaled, orderXTerm) *
-                                            pow(vScaled, orderYTerm);
-                                    }
-                                }
-                                setPreCalc = false;
-                            } else {
-                                psMemIncrRefCounter(currPreCalc);
-                            }
-                            ptr++;
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    psTrace("ImageSubtract.pmSubtractionKernelsAllocISIS", 3,
-            "Exiting pmSubtractionKernelsAllocISIS(%d, %d, %d, %d)\n",
-            sigmas->n, orders->n, size, spatialOrder);
-    return(tmp);
-}
-
-/*******************************************************************************
-pmSubtractionFindStamps(stamps, image, mask, maskVal, threshold, xNum, yNum, border)
- 
-XXX: The SDRS and the proptotype code differ significantly.
- Prototype: When a maximum pixel is found within a stamp, an area of size
-     2*footprint is searched around that pixel looking for masked pixels.
- SDRS: none of that is required.
- 
-XXX: Do we need to care about case where yNum/xNum does not evenly divide the
-nnumber of rows/columns in the image?
- ******************************************************************************/
-psArray *pmSubtractionFindStamps(psArray *stamps,        ///< Output stamps, or NULL
-                                 const psImage *image,   ///< Image for which to find stamps
-                                 const psImage *mask,    ///< Mask
-                                 psU32 maskVal,          ///< Value for mask
-                                 psF32 threshold,        ///< Threshold for stamps in the image
-                                 psS32 xNum,             ///< Number of stamps in x
-                                 psS32 yNum,             ///< Number of stamps in y
-                                 psS32 border            ///< Border around image to ignore (should be size of kernel or larger)
-                                )
-{
-    psTrace("ImageSubtract.pmSubtractionFindStamps", 3,
-            "Calling pmSubtractionFindStamps(%d, %f, %d, %d, %d)\n",
-            maskVal, threshold, xNum, yNum, border);
-    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
-    PS_ASSERT_IMAGE_NON_EMPTY(image, NULL);
-    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
-    if (mask != NULL) {
-        PS_ASSERT_IMAGES_SIZE_EQUAL(image, mask, NULL);
-        PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_U8, NULL);
-    }
-    PS_ASSERT_INT_POSITIVE(xNum, NULL);
-    PS_ASSERT_INT_POSITIVE(yNum, NULL);
-    PS_ASSERT_INT_POSITIVE(border, NULL);
-    PS_ASSERT_INT_LARGER_THAN(image->numCols, xNum, NULL);
-    PS_ASSERT_INT_LARGER_THAN(image->numRows, yNum, NULL);
-    PS_ASSERT_INT_LARGER_THAN(image->numCols, (2 * border), NULL);
-    PS_ASSERT_INT_LARGER_THAN(image->numRows, (2 * border), NULL);
-
-    if (stamps != NULL) {
-        PS_ASSERT_INT_EQUAL(stamps->n, (xNum * yNum), NULL);
-        //
-        // Ensure that a pmStamp struct exists at each psArray location.
-        //
-        for (psS32 s = 0 ; s < (xNum * yNum) ; s++) {
-            if (NULL == stamps->data[s]) {
-                stamps->data[s] = (psPtr *) p_pmStampAlloc(PM_STAMP_REJECTED);
-            }
-        }
-    } else {
-        stamps = (psArray *) psArrayAlloc(xNum * yNum);
-        for (psS32 s = 0 ; s < (xNum * yNum) ; s++) {
-            stamps->data[s] = (psPtr *) p_pmStampAlloc(PM_STAMP_REJECTED);
-        }
-    }
-    psS32 numRows = image->numRows;
-    psS32 numCols = image->numCols;
-
-    //
-    // Iterate over the image sections
-    //
-    // XXX: Must handle cases where image size is not an even multiple of xNum or yNum
-    // they are currently ignored.
-    //
-    psS32 num = 0;
-    for (psS32 j = 0; j < yNum; j++) {
-        for (psS32 i = 0; i < xNum; i++) {
-            pmStamp *stamp = (pmStamp *) stamps->data[num];
-            //
-            // Only find a new stamp if we need to
-            //
-            if (stamp->status == PM_STAMP_REJECTED) {
-                //
-                // Find maximum non-masked value in the image section,
-                // but don't include a footprint around the edge
-                //
-                psF32 max = -INFINITY;
-                psS32 bestx = 0;
-                psS32 besty = 0;
-                //
-                // The following nested loop iterates over every pixel in the mask
-                // associated with this (i, j).  It ignores pixels within a
-                // border of pixels from the image edge.
-                //
-                // XXX: verify (numX, numY), then get rid of it.
-                //
-                psS32 numX = xNum;
-                psS32 numY = yNum;
-                psS32 yMin = border + j * (numCols - 2.0 * border) / numY;
-                psS32 yMax = (border + (j + 1) * (numCols - 2.0 * border) / numY) - 1;
-                psS32 xMin = border + i * (numRows - 2.0 * border) / numX;
-                psS32 xMax = (border + (i + 1) * (numRows - 2.0 * border) / numX) - 1;
-
-                if ((yMax >= image->numRows) ||
-                        (xMax >= image->numCols) ||
-                        (yMin < 0) ||
-                        (xMin < 0)) {
-                    // XXX: We skip this stamp since its borders extends beyond the image.
-                    // XXX: This is here mainly as a safeguard.  We need to redefine the above
-                    // min/max pixels calculation to ensure that all stamps are legitimate.
-
-                    stamp->x = -1;
-                    stamp->y = -1;
-                    stamp->status = PM_STAMP_NONE;
-                } else {
-                    stamp->p_xSize = 1 + (xMax - xMin);
-                    stamp->p_ySize = 1 + (yMax - yMin);
-                    stamp->p_xMin = xMin;
-                    stamp->p_xMax = xMax;
-                    stamp->p_yMin = yMin;
-                    stamp->p_yMax = yMax;
-
-                    for (psS32 y = yMin; y <= yMax ; y++) {
-                        for (psS32 x = xMin; x <= xMax ; x++) {
-                            // Determine if this pixel is larger than the max, and unmasked.
-                            if (image->data.F32[y][x] > max) {
-                                if ((mask == NULL) || !((mask->data.U8[y][x]) & maskVal)) {
-                                    max = image->data.F32[y][x];
-                                    bestx = x;
-                                    besty = y;
-                                }
-                            }
-                        }
-                    }
-
-                    //
-                    // If the max pixel is larger than the threshold, we keep this stamp.
-                    // Otherwise, mark the stamp as PM_STAMP_NONE
-                    //
-                    if (image->data.F32[besty][bestx] >= threshold) {
-                        stamp->x = bestx;
-                        stamp->y = besty;
-                        stamp->status = PM_STAMP_RECALC;
-                    } else {
-                        stamp->x = bestx;
-                        stamp->y = besty;
-                        stamp->status = PM_STAMP_NONE;
-                    }
-                }
-            }
-            num++;
-        }
-    }
-    psTrace("ImageSubtract.pmSubtractionFindStamps", 3,
-            "Exiting pmSubtractionFindStamps(%d, %f, %d, %d, %d)\n",
-            maskVal, threshold, xNum, yNum, border);
-    return(stamps);
-}
-
-/*******************************************************************************
-GenSpatialOrder(spatialOrder, x, y): generates and returns a psImage in which
-the [i][j] location is calculated as (x^i * y^j).
- 
-XXX: Modify loop so that terms higher than spatialOrder are not computed.
- 
-XXX: Modify this so that [i][j] location is calculated as (x^j * y^i)?
- ******************************************************************************/
-static psImage *GenSpatialOrder(psS32 spatialOrder,
-                                psF32 x,
-                                psF32 y)
-{
-    psTrace("ImageSubtract.GenSpatialOrder", 4,
-            "Calling GenSpatialOrder(%d, %f, %f)\n", spatialOrder, x, y);
-
-    psImage *polyValues = psImageAlloc(spatialOrder+1, spatialOrder+1, PS_TYPE_F64);
-
-    psF64 xSum = 1.0;
-    psF64 ySum = 1.0;
-    for (psS32 i = 0; i < spatialOrder + 1; i++) {
-        ySum = xSum;
-        for (psS32 j = 0; j < spatialOrder + 1; j++) {
-            polyValues->data.F64[i][j] = ySum;
-            ySum*= y;
-        }
-        xSum*= x;
-    }
-
-    psTrace("ImageSubtract.GenSpatialOrder", 4,
-            "Exiting GenSpatialOrder(%d, %f, %f)\n", spatialOrder, x, y);
-
-    return(polyValues);
-}
-
-
-/*******************************************************************************
-IsisKernelConvolve(input, kernels, kernelID, col, row): This routine
-convolves a single kernel basis function with a pixel in an image.
-  ******************************************************************************/
-static psF32 IsisKernelConvolve(const psImage *input,
-                                const psSubtractionKernels *kernels,
-                                psS32 kernelID,
-                                psS32 col,
-                                psS32 row)
-{
-
-
-    psTrace("ImageSubtract.IsisKernelConvolve", 4,
-            "Calling IsisKernelConvolve(%d, %d, %d)\n", kernelID, col, row);
-    psS32 spatialOrder = kernels->spatialOrder;
-    psS32 kernelSize = kernels->size;
-    psS32 xOrder = (psS32) kernels->xOrder->data.F32[kernelID];
-    psS32 yOrder = (psS32) kernels->yOrder->data.F32[kernelID];
-    psF32 numColsHalf = 0.5 * (psF32) input->numCols;
-    psF32 numRowsHalf = 0.5 * (psF32) input->numRows;
-    psF32 imageX = (((psF32) col) - numColsHalf) / numColsHalf; // Normalised position
-    psF32 imageY = (((psF32) row) - numRowsHalf) / numRowsHalf; // Normalised position
-
-    psImage *polyValues = GenSpatialOrder(spatialOrder, imageX, imageY);
-
-    psF64 polyVal = polyValues->data.F64[yOrder][xOrder];
-
-    psImage *preCalc = (psImage *) kernels->preCalc->data[kernelID];
-
-    // XXX: Are the following asserts really necessary?
-    PS_ASSERT_INT_LARGER_THAN_OR_EQUAL(row-kernelSize, 0, NAN);
-    PS_ASSERT_INT_LESS_THAN(row+kernelSize, input->numRows, NAN);
-    PS_ASSERT_INT_LARGER_THAN_OR_EQUAL(col-kernelSize, 0, NAN);
-    PS_ASSERT_INT_LESS_THAN(col+kernelSize, input->numCols, NAN);
-    psF32 conv = 0.0;
-    for (psS32 yy = -kernelSize ; yy < kernelSize ; yy++) {
-        for (psS32 xx = -kernelSize ; xx < kernelSize ; xx++) {
-            conv += input->data.F32[yy+row][xx+col] *
-                    preCalc->data.F32[yy+kernelSize][xx+kernelSize] *
-                    polyVal;
-        }
-    }
-    psFree(polyValues);
-
-    psTrace("ImageSubtract.IsisKernelConvolve", 4,
-            "Exiting IsisKernelConvolve(%d, %d, %d)\n", kernelID, col, row);
-    return(conv);
-}
-
-/*******************************************************************************
-ConvolvePixelPois(input, mask, badStampMaskVal, solution, kernels, col, row):
- 
-This routine takes a single pixel in the psImage input and convolves it with
-the set of kernel basis functions and their appropriate weights in solution.
-It returns the value of the convolved pixel.
- 
-XXX: Static structure for polyValues?
- ******************************************************************************/
-static psF32 ConvolvePixelPois(const psImage *input,
-                               const psImage *mask,
-                               psU32 badStampMaskVal,
-                               const psVector *solution,
-                               const psSubtractionKernels *kernels,
-                               psS32 col,
-                               psS32 row)
-{
-    psTrace("ImageSubtract.ConvolvePixelPois", 4,
-            "Calling ConvolvePixelPois(%d, %d)\n", col, row);
-    psS32 nBF = kernels->u->n;
-    psF32 numColsHalf = 0.5 * (psF32) input->numCols;
-    psF32 numRowsHalf = 0.5 * (psF32) input->numRows;
-    psF32 background = solution->data.F64[solution->n-1];
-    psS32 spatialOrder = kernels->spatialOrder;
-    psF32 conv = background; // Initial convolved value
-
-    if ((mask == NULL) || !(mask->data.U8[row][col] & badStampMaskVal)) {
-        psF32 imageX = (((psF32) col) - numColsHalf) / numColsHalf; // Normalised position
-        psF32 imageY = (((psF32) row) - numRowsHalf) / numRowsHalf; // Normalised position
-        psImage *polyValues = GenSpatialOrder(spatialOrder, imageX, imageY);
-
-        // Iterate over the kernel basis functions
-        for (psS32 k = 0; k < nBF; k++) {
-            psS32 u = (psS32) kernels->u->data.F32[k];
-            psS32 v = (psS32) kernels->v->data.F32[k];
-
-            // XXX: What's the story with this?
-            #if 0
-
-            psS32 xOrder = (psS32) kernels->xOrder->data.F32[k];
-            psS32 yOrder = (psS32) kernels->yOrder->data.F32[k];
-            psF64 polyVal = polyValues->data.F64[yOrder][xOrder];
-            #else
-
-            psF32 polyVal = 1.0;
-            #endif
-
-            // XXX: Why this?
-            if (k == 0) {
-                conv += solution->data.F64[k] * input->data.F32[row - v][col - u] * polyVal;
-            } else {
-                conv += solution->data.F64[k] *
-                        (input->data.F32[row - v][col - u] * polyVal - input->data.F32[row][col]);
-            }
-        }
-        psFree(polyValues);
-    }
-
-    psTrace("ImageSubtract.ConvolvePixelPois", 4,
-            "Exiting ConvolvePixelPois(%d, %d)\n", col, row);
-    return(conv);
-}
-
-
-
-/*******************************************************************************
-ConvolvePixelIsis(input, mask, badStampMaskVal, solution, kernels, col, row):
- 
-This routine takes a single pixel in the psImage input and convolves it with
-the set of kernel basis functions and their appropriate weights in solution.
-It returns the value of the convolved pixel.
- 
-XXX: Static structure for polyValues?
- ******************************************************************************/
-static psF32 ConvolvePixelIsis(const psImage *input,
-                               const psImage *mask,
-                               psU32 badStampMaskVal,
-                               const psVector *solution,
-                               const psSubtractionKernels *kernels,
-                               psS32 col,
-                               psS32 row)
-{
-    psTrace("ImageSubtract.ConvolvePixelIsis", 4,
-            "Calling ConvolvePixelIsis(%d, %d)\n", col, row);
-    psF32 background = solution->data.F64[solution->n-1];
-    psF32 conv = background; // Initial convolved value
-
-    if ((mask == NULL) || !(mask->data.U8[row][col] & badStampMaskVal)) {
-        // Iterate over the kernel basis functions
-        for (psS32 k = 0; k < kernels->u->n; k++) {
-            conv += IsisKernelConvolve(input, kernels, k, col, row);
-        }
-    }
-
-    psTrace("ImageSubtract.ConvolvePixelIsis", 4,
-            "Exiting ConvolvePixelIsis(%d, %d)\n", col, row);
-    return(conv);
-}
-
-/*******************************************************************************
-ConvolveImage(input, mask, badStampMaskVal, solution, kernels): convolves an
-arbitrary image with either an ISIS or POIS set of kernel basis functions.
- ******************************************************************************/
-static psImage *ConvolveImage(const psImage *input,
-                              const psImage *mask,
-                              psU32 badStampMaskVal,
-                              const psVector *solution,
-                              const psSubtractionKernels *kernels)
-{
-    psTrace("ImageSubtract.ConvolveImage", 4, "Calling ConvolveImage()\n");
-    PS_ASSERT_IMAGE_NON_NULL(input, NULL);
-    PS_ASSERT_IMAGE_NON_EMPTY(input, NULL);
-    PS_ASSERT_IMAGE_TYPE(input, PS_TYPE_F32, NULL);
-    if (mask != NULL) {
-        PS_ASSERT_IMAGES_SIZE_EQUAL(input, mask, NULL);
-        PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_U8, NULL);
-    }
-    PS_ASSERT_VECTOR_NON_NULL(solution, NULL);
-    PS_ASSERT_VECTOR_TYPE(solution, PS_TYPE_F64, NULL);
-    PS_ASSERT_PTR_NON_NULL(kernels, NULL);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->v, NULL);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->xOrder, NULL);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->yOrder, NULL);
-    if (kernels->preCalc != NULL) {
-        PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->preCalc, NULL);
-    } else {
-        if (kernels->type != PM_SUBTRACTION_KERNEL_POIS) {
-            psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                    "Unallowable operation: kernels->preCalc == NULL and kernels->type != PM_SUBTRACTION_KERNEL_POIS.\n");
-            return(NULL);
-        }
-    }
-    psS32 nBF = kernels->u->n;
-    PS_ASSERT_VECTOR_SIZE(solution, nBF+1, NULL);
-
-    psS32 numCols = input->numCols;
-    psS32 numRows = input->numRows;
-    psS32 kernelSize = kernels->size;
-
-    psImage *convolved = psImageAlloc(numCols, numRows, PS_TYPE_F32);
-
-    for (psS32 y = kernelSize; y < numRows - kernelSize; y++) {
-        for (psS32 x = kernelSize; x < numCols - kernelSize; x++) {
-            if (kernels->type == PM_SUBTRACTION_KERNEL_POIS) {
-                convolved->data.F32[y][x] = ConvolvePixelPois(input, mask, badStampMaskVal,
-                                            solution, kernels, x, y);
-            } else if (kernels->type == PM_SUBTRACTION_KERNEL_ISIS) {
-                convolved->data.F32[y][x] = ConvolvePixelIsis(input, mask, badStampMaskVal,
-                                            solution, kernels, x, y);
-            } else {
-                psLogMsg(__func__, PS_LOG_WARN, "WARNING: unknown kernel type.  Returning NULL\n");
-                return(NULL);
-            }
-        }
-    }
-
-    //
-    // Pad the rest of the convolved image with 0.0
-    //
-    for (psS32 y = kernelSize; y < numRows - kernelSize; y++) {
-        for (psS32 x = 0; x < kernelSize; x++) {
-            convolved->data.F32[y][x] = 0.0;
-        }
-        for (psS32 x = numCols - kernelSize; x < numCols; x++) {
-            convolved->data.F32[y][x] = 0.0;
-        }
-    }
-    for (psS32 y = 0; y < kernelSize; y++) {
-        for (psS32 x = 0; x < numCols; x++) {
-            convolved->data.F32[y][x] = 0.0;
-        }
-    }
-    for (psS32 y = numRows - kernelSize; y < numRows; y++) {
-        for (psS32 x = 0; x < numCols; x++) {
-            convolved->data.F32[y][x] = 0.0;
-        }
-    }
-
-    psTrace("ImageSubtract.ConvolveImage", 4, "Exiting ConvolveImage()\n");
-    return convolved;
-}
-
-
-
-
-
-/*******************************************************************************
-XXX: We should assert that the (footprint, kernelSize, imageSize) stuff
-ensures that all data is accessed in bounds?
- ******************************************************************************/
-bool pmSubtractionCalculateEquation(psArray *stamps,          ///< The stamps for which to calculate the equation,
-                                    const psImage *reference, ///< Reference image
-                                    const psImage *input,     ///< Input image
-                                    const psSubtractionKernels *kernels, ///< The kernel basis functions
-                                    psS32 footprint           ///< Half-size of region over which to calculate equation
-                                   )
-{
-    psTrace("ImageSubtract.pmSubtractionCalculateEquation", 3,
-            "Calling pmSubtractionCalculateEquation()\n");
-    PS_ASSERT_PTR_NON_NULL(stamps, false);
-    PS_ASSERT_IMAGE_NON_NULL(reference, false);
-    PS_ASSERT_IMAGE_NON_EMPTY(reference, false);
-    PS_ASSERT_IMAGE_NON_NULL(input, false);
-    PS_ASSERT_IMAGE_NON_EMPTY(input, false);
-    PS_ASSERT_IMAGES_SIZE_EQUAL(reference, input, false);
-    PS_ASSERT_PTR_NON_NULL(kernels, false);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->v, false);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->xOrder, false);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->yOrder, false);
-    if (kernels->type == PM_SUBTRACTION_KERNEL_ISIS) {
-        PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->preCalc, false);
-    }
-    psS32 kernelSize = kernels->size;
-    PS_ASSERT_INT_NONNEGATIVE(footprint, false);
-    //
-    // For each legitimate stamp, ensure that the footprint is small enough to perform
-    // the full calculation.
-    //
-    // XXX: Verify with IfA that this is a reasonable action.
-    //
-    for (psS32 s = 0; s < stamps->n; s++) {
-        pmStamp *stamp = (pmStamp *) stamps->data[s];
-        if (stamp->status == PM_STAMP_RECALC) {
-            // XXX: trace message
-            // printf("stamp %d (x, y) is (%d, %d).  Footprint is %d.  kernelSize is %d.\n", s, stamp->x, stamp->y, footprint, kernelSize);
-            if (((stamp->y - (footprint + kernelSize)) < 0) ||
-                    ((stamp->x - (footprint + kernelSize)) < 0) ||
-                    ((stamp->y + footprint + kernelSize) >= input->numRows) ||
-                    ((stamp->x + footprint + kernelSize) >= input->numCols)) {
-                stamp->status = PM_STAMP_NONE;
-                psLogMsg(__func__, PS_LOG_WARN,
-                         "WARNING: stamp %d will be ignored.  It exceeds image size: access columns (%d to %d) and rows (%d to %d)\n",
-                         s,
-                         stamp->x - (footprint + kernelSize),
-                         (stamp->x + footprint + kernelSize) - 1,
-                         stamp->y - (footprint + kernelSize),
-                         (stamp->y + footprint + kernelSize) - 1);
-            }
-        }
-    }
-
-    psS32 numHalfRows = reference->numRows;
-    psS32 numHalfCols = reference->numCols;
-    psS32 spatialOrder = kernels->spatialOrder;
-
-    //
-    // The numSolveParams incorporates the additional parameter for the
-    // background value, which we must solve for.
-    //
-    psS32 numKernels = kernels->u->n;
-    int numSolveParams = numKernels + 1;
-    int bgIndex = numKernels;        // Index in matrix for the background
-
-    //
-    // We iterate over each stamp, allocate the matrix and vectors if
-    // necessary, and then calculate those matrix/vectors.
-    //
-    for (psS32 s = 0; s < stamps->n; s++) {
-        pmStamp *stamp = (pmStamp *) stamps->data[s];
-        psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): stamp %d\n", s);
-        if (stamp->status == PM_STAMP_RECALC) {
-            psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): stamp %d: status is PM_STAMP_RECALC.\n", s);
-            psImage *stampMatrix = stamp->matrix;
-            psVector *stampVector = stamp->vector;
-
-            if (stampMatrix == NULL) {
-                stampMatrix = psImageAlloc(numSolveParams, numSolveParams, PS_TYPE_F64);
-                stamp->matrix = stampMatrix;
-            } else {
-                PS_ASSERT_IMAGE_TYPE(stampMatrix, PS_TYPE_F64, false);
-                PS_ASSERT_IMAGE_SIZE(stampMatrix, numSolveParams, numSolveParams, false);
-            }
-            PS_IMAGE_SET_F64(stampMatrix, 0.0);
-
-            if (stampVector == NULL) {
-                stampVector = psVectorAlloc(numSolveParams, PS_TYPE_F64);
-                stamp->vector = stampVector;
-            } else {
-                PS_ASSERT_VECTOR_TYPE(stampVector, PS_TYPE_F64, false);
-                PS_ASSERT_VECTOR_SIZE(stampVector, numSolveParams, false);
-            }
-            PS_VECTOR_SET_F64(stampVector, 0.0);
-            psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): stamp %d: allocate matrix and vector.\n", s);
-
-            //
-            // Evaluate the spatial-order polynomial.  The [i][j]-th element of
-            // the psImage polyValues will hold (x^i * y^j) for the stamp.  The
-            // (x, y) value are scaled to [-1:1]
-            //
-            psImage *polyValues = GenSpatialOrder(spatialOrder,
-                                                  ((psF64) (stamp->x - numHalfCols)) / ((psF64) numHalfCols),
-                                                  ((psF64) (stamp->y - numHalfRows)) / ((psF64) numHalfRows));
-
-            psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): stamp %d: generated spatial order terms.\n", s);
-
-            if (kernels->type == PM_SUBTRACTION_KERNEL_POIS) {
-                //
-                // Iterate over all pixels surrounding this stamp.
-                //
-                for (psS32 y = stamp->y - footprint; y < stamp->y + footprint; y++) {
-                    for (psS32 x = stamp->x - footprint; x < stamp->x + footprint; x++) {
-                        psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): pixel (%d, %d).\n", y, x);
-
-                        // The inverse of the noise, squared.
-                        psF32 invNoise2 = 1.0/reference->data.F32[y][x];
-
-                        //
-                        // Iterate over the first convolution */
-                        //
-                        for (psS32 k1 = 0; k1 < numKernels; k1++) {
-                            psS32 u1 = kernels->u->data.F32[k1];        // Offset in x
-                            psS32 v1 = kernels->v->data.F32[k1];        // Offset in y
-                            psS32 i1 = kernels->xOrder->data.F32[k1];   // Polynomial order in x
-                            psS32 j1 = kernels->yOrder->data.F32[k1];   // Polynomial order in y
-
-                            //
-                            // First convolution.  This will set the value for the stampVector.
-                            //
-                            // XXX: verify the [y-v2][x-u2] subscript.  This generated errors in
-                            // testing, depending on kernel size and footprint.
-                            //
-                            psF32 conv1 = polyValues->data.F64[j1][i1] * reference->data.F32[y - v1][x - u1];
-
-                            //
-                            // Assuming that the first kernel component is 0 order in x and y, and 0 offset
-                            // XXX: I don't understand this:
-                            //
-                            if (k1 != 0) {
-                                conv1 -= reference->data.F32[y][x];
-                            }
-
-                            //
-                            // Iterate over the second convolution
-                            //
-                            for (psS32 k2 = k1; k2 < numKernels; k2++) {
-                                psS32 u2 = (psS32) kernels->u->data.F32[k2];        // Offset in x
-                                psS32 v2 = (psS32) kernels->v->data.F32[k2];        // Offset in y
-                                psS32 i2 = (psS32) kernels->xOrder->data.F32[k2];   // Polynomial order in x
-                                psS32 j2 = (psS32) kernels->yOrder->data.F32[k2];   // Polynomial order in y
-                                //
-                                // XXX: verify the [y-v2][x-u2] subscript.  This generated errors in
-                                // testing, depending on kernel size and footprint.
-                                //
-                                // Second convolution
-                                //
-                                psF32 conv2 = polyValues->data.F64[j2][i2] *
-                                              reference->data.F32[y-v2][x-u2];
-                                //
-                                // Assuming that the first kernel component is 0 order in x and y, and 0 offset
-                                // XXX: I don't understand this:
-                                if (k2 != 0) {
-                                    //
-                                    conv2 -= reference->data.F32[y][x];
-                                }
-
-                                // Add into the matrix element
-                                stampMatrix->data.F64[k1][k2] += conv1 * conv2 * invNoise2;
-
-                            } // Iteration on second convolution
-
-                            // Add into the vector element
-                            stampVector->data.F64[k1] += input->data.F32[y][x] * conv1 * invNoise2;
-
-                            /* Background term */
-                            stampMatrix->data.F64[k1][bgIndex] += conv1 * invNoise2;
-
-                        } // Iteration on first convolution
-
-                        //
-                        // Background only terms.
-                        // XXX: understand this.
-                        //
-                        stampMatrix->data.F64[bgIndex][bgIndex] += invNoise2;
-                        stampVector->data.F64[bgIndex] += input->data.F32[y][x] * invNoise2;
-                    }
-                }
-            } else if (kernels->type == PM_SUBTRACTION_KERNEL_ISIS) {
-                //
-                // Iterate over all pixels surrounding this stamp.
-                //
-                // XXX: Why isn't there a polyValues term here?
-                //
-
-                for (psS32 y = stamp->y - footprint; y < stamp->y + footprint; y++) {
-                    for (psS32 x = stamp->x - footprint; x < stamp->x + footprint; x++) {
-                        psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): pixel (%d, %d).\n", y, x);
-                        psF32 invNoise2 = 1.0/reference->data.F32[y][x]; // The inverse of the noise, squared.
-
-                        for (psS32 k1 = 0; k1 < numKernels; k1++) {
-                            psF32 conv1 = IsisKernelConvolve(reference, kernels, k1, x, y);
-
-                            for (psS32 k2 = k1; k2 < numKernels; k2++) {
-                                //printf("(k1, k2) is (%d, %d)\n", k1, k2);
-                                psF32 conv2 = IsisKernelConvolve(reference, kernels, k2, x, y);
-                                stampMatrix->data.F64[k1][k2]+= conv1 * conv2 * invNoise2;
-                            }
-                            stampVector->data.F64[k1]+= input->data.F32[y][x] * conv1 * invNoise2;
-                            stampMatrix->data.F64[k1][bgIndex] += conv1 * invNoise2;
-                        }
-                        stampMatrix->data.F64[bgIndex][bgIndex] += invNoise2;
-                        stampVector->data.F64[bgIndex] += input->data.F32[y][x] * invNoise2;
-                    }
-                }
-            } else {
-                psLogMsg(__func__, PS_LOG_WARN, "WARNING: unknown kernel->type.\n");
-                return(false);
-            }
-            psFree(polyValues);
-
-            // XXX: Generate psTrace()
-            if (0) {
-                for (psS32 s = 0; s < stamps->n; s++) {
-                    pmStamp *stamp = (pmStamp *) stamps->data[s];
-                    if (stamp->status == PM_STAMP_RECALC) {
-                        psVector *stampVector = stamp->vector;
-                        printf("STAMP: stamp %d vector:\n", s);
-                        PS_VECTOR_PRINT_F64(stampVector);
-                    }
-                }
-            }
-
-            //
-            // Fill in other side of symmetric matrix
-            //
-            // XXX: understand this.
-            // XXX: Why aren't they using numSolveParams instead of numKernels?
-            // XXX: is this POIS specific?
-            //
-            for (psS32 k1 = 0; k1 < numKernels; k1++) {
-                for (psS32 k2 = 0; k2 < k1; k2++) {
-                    stampMatrix->data.F64[k1][k2] = stampMatrix->data.F64[k2][k1];
-                }
-                stampMatrix->data.F64[bgIndex][k1] = stampMatrix->data.F64[k1][bgIndex];
-            }
-
-            //
-            // XXX: Why aren't they using numSolveParams instead of numKernels?
-            // XXX: is this POIS specific?
-            //
-            #define XXX_CONFIG_PENALTY 1.0
-            for (psS32 k = 0; k < numKernels; k++)
-            {
-                psS32 u = kernels->u->data.F32[k];  // Offset in x
-                psS32 v = kernels->v->data.F32[k];  // Offset in y
-                stampMatrix->data.F64[k][k] += XXX_CONFIG_PENALTY * (psF32)(u*u + v*v);
-            }
-            stamp->status = PM_STAMP_USED;
-        } else {
-            // Stamp is ignored since it's not PM_STAMP_RECALC
-        }
-    }
-    psTrace("ImageSubtract.pmSubtractionCalculateEquation", 3,
-            "Exiting pmSubtractionCalculateEquation()\n");
-    return(true);
-}
-
-
-
-
-/*******************************************************************************
- ******************************************************************************/
-psVector *pmSubtractionSolveEquation(psVector *solution, ///< Solution vector, or NULL
-                                     const psArray *stamps      ///< Array of stamps
-                                    )
-{
-    psTrace("ImageSubtract.pmSubtractionSolveEquation", 3,
-            "Calling pmSubtractionSolveEquation()\n");
-    PS_ASSERT_PTR_NON_NULL(stamps, NULL);
-    psS32 size = -1;
-    psS32 s = 0;
-
-    //
-    // Determine the size of the stamp vectors and matrix.
-    // We iterate until we find the first acceptable stamp.
-    //
-    while ((size == -1) && (s < stamps->n)) {
-        pmStamp *stamp = (pmStamp *) stamps->data[s];
-        PS_ASSERT_PTR_NON_NULL(stamp, NULL);
-        if (stamp->status == PM_STAMP_USED) {
-            size = ((pmStamp *) stamps->data[s])->vector->n;
-            PS_ASSERT_INT_POSITIVE(size, NULL);
-        }
-        s++;
-    }
-    if (size == -1) {
-        psLogMsg(__func__, PS_LOG_WARN, "WARNING: no acceptable stamps.  Returning NULL\n");
-        return(NULL);
-    }
-
-    if (solution != NULL) {
-        PS_ASSERT_VECTOR_TYPE(solution, PS_TYPE_F64, NULL);
-        PS_ASSERT_VECTOR_SIZE(solution, size, NULL);
-    } else {
-        solution = psVectorAlloc(size, PS_TYPE_F64);
-    }
-
-    //
-    // Create the solution matrix and vector.
-    //
-    // XXX: Test these functions with size=-1.  This caused seg faults during test.
-    //      This should be done in the psImage.c and psVector.c test files.  It
-    //      should never occur here.
-    //
-    psImage *sumMatrix = psImageAlloc(size, size, PS_TYPE_F64);
-    psVector *sumVector = psVectorAlloc(size, PS_TYPE_F64);
-    PS_VECTOR_SET_F64(sumVector, 0.0);
-    PS_IMAGE_SET_F64(sumMatrix, 0.0);
-
-    //
-    // Verify that all stamps have similar sizes.
-    // Compute the sum matrix and vector.
-    //
-    for (psS32 s = 0; s < stamps->n; s++) {
-        pmStamp *stamp = (pmStamp *) stamps->data[s];
-
-        if (stamp->status == PM_STAMP_USED) {
-            PS_ASSERT_INT_EQUAL(((pmStamp *) stamps->data[s])->vector->n, size, NULL);
-
-            psImage *stampMatrix = stamp->matrix;
-            psVector *stampVector = stamp->vector;
-            PS_ASSERT_VECTOR_TYPE(stampVector, PS_TYPE_F64, NULL);
-            PS_ASSERT_VECTOR_SIZE(stampVector, size, NULL);
-            PS_ASSERT_IMAGE_TYPE(stampMatrix, PS_TYPE_F64, NULL);
-            PS_ASSERT_IMAGE_SIZE(stampMatrix, size, size, NULL);
-
-            (void)psBinaryOp(sumMatrix, sumMatrix, "+", stampMatrix);
-            (void)psBinaryOp(sumVector, sumVector, "+", stampVector);
-        }
-    }
-    psVector *permutation = NULL;
-    // XXX: Check output from these routines.
-
-    // XXX: psTrace()
-    if (0) {
-        PS_IMAGE_PRINT_F64(sumMatrix);
-    }
-
-    psImage *luMatrix = psMatrixLUD(NULL, &permutation, sumMatrix);
-    if (luMatrix == NULL) {
-        psError(PS_ERR_UNKNOWN, true, "Failed to LU-Decompose the matrix.\n");
-        psFree(sumMatrix);
-        psFree(sumVector);
-        psFree(luMatrix);
-        psFree(permutation);
-        return(NULL);
-    }
-    // XXX: psTrace()
-    if (0) {
-        PS_IMAGE_PRINT_F64(luMatrix);
-    }
-
-    solution = psMatrixLUSolve(solution, luMatrix, sumVector, permutation);
-    // XXX: psTrace()
-    // XXX: should we be checking for NAN's in the solution vector?
-    if (0) {
-        PS_VECTOR_PRINT_F64(solution);
-    }
-    if (solution == NULL) {
-        psError(PS_ERR_UNKNOWN, true, "Failed to solve the matrix.\n");
-        psFree(sumMatrix);
-        psFree(sumVector);
-        psFree(luMatrix);
-        psFree(permutation);
-        return(NULL);
-    }
-
-    psFree(sumMatrix);
-    psFree(sumVector);
-    psFree(luMatrix);
-    psFree(permutation);
-
-    psTrace("ImageSubtract.pmSubtractionSolveEquation", 3,
-            "Exiting pmSubtractionSolveEquation()\n");
-    return(solution);
-}
-
-
-/*******************************************************************************
- ******************************************************************************/
-static psVector *CalculateDeviations(psVector *deviations,
-                                     psArray *stamps,
-                                     psS32 footprint,
-                                     const psImage *refImage,
-                                     const psImage *inImage,
-                                     const psImage *mask,
-                                     psU32 badStampMaskVal,
-                                     const psSubtractionKernels *kernels,
-                                     const psVector *solution)
-{
-    psTrace("ImageSubtract.CalculateDeviations", 4,
-            "Calling CalculateDeviations()\n");
-    PS_ASSERT_PTR_NON_NULL(stamps, NULL);
-    if (deviations != NULL) {
-        PS_ASSERT_VECTOR_TYPE(deviations, PS_TYPE_F32, NULL);
-        PS_ASSERT_VECTORS_SIZE_EQUAL(deviations, stamps, NULL);
-    } else {
-        deviations = psVectorAlloc(stamps->n, PS_TYPE_F32);
-        // XXX: Probably not necessary.
-        PS_VECTOR_SET_F32(deviations, 0.0);
-    }
-    PS_ASSERT_IMAGE_NON_NULL(refImage, NULL);
-    PS_ASSERT_IMAGE_NON_EMPTY(refImage, NULL);
-    PS_ASSERT_IMAGE_TYPE(refImage, PS_TYPE_F32, NULL);
-    PS_ASSERT_IMAGE_NON_NULL(inImage, NULL);
-    PS_ASSERT_IMAGE_NON_EMPTY(inImage, NULL);
-    PS_ASSERT_IMAGE_TYPE(inImage, PS_TYPE_F32, NULL);
-    PS_ASSERT_IMAGES_SIZE_EQUAL(refImage, inImage, NULL);
-    PS_ASSERT_IMAGE_NON_NULL(mask, NULL);
-    PS_ASSERT_IMAGE_NON_EMPTY(mask, NULL);
-    PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_U8, NULL);
-    PS_ASSERT_IMAGES_SIZE_EQUAL(refImage, mask, NULL);
-    PS_ASSERT_VECTOR_NON_NULL(solution, NULL);
-    PS_ASSERT_VECTOR_TYPE(solution, PS_TYPE_F64, NULL);
-    PS_ASSERT_PTR_NON_NULL(kernels, NULL);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->v, NULL);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->xOrder, NULL);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->yOrder, NULL);
-    if (kernels->preCalc != NULL) {
-        PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->preCalc, NULL);
-    } else {
-        if (kernels->type != PM_SUBTRACTION_KERNEL_POIS) {
-            psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                    "Unallowable operation: kernels->preCalc == NULL and kernels->type != PM_SUBTRACTION_KERNEL_POIS.\n");
-            return(NULL);
-        }
-    }
-    psS32 nBF = kernels->u->n;
-    PS_ASSERT_VECTOR_SIZE(solution, nBF+1, NULL);
-
-    psS32 kernelSize = kernels->size;
-    int xSize = footprint + kernelSize;
-    int ySize = footprint + kernelSize;
-    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN); // Statistics
-    psImage *subStamp = psImageAlloc(2 * xSize, 2 * ySize, PS_TYPE_F32); // Subtraction of stamp
-    for (psS32 s = 0; s < stamps->n; s++) {
-        pmStamp *stamp = stamps->data[s]; // The coordinates of the stamp of interest
-        psS32 x = stamp->x;               // Stamp x coord
-        psS32 y = stamp->y;               // Stamp y coord
-        if (stamp->status == PM_STAMP_USED) {
-
-            psRegion myReg = psRegionSet(x - xSize, x + xSize, y - ySize, y + ySize);
-            psImage *refStamp = psImageSubset((psImage *) refImage, myReg);
-            psImage *inStamp = psImageSubset((psImage *) inImage, myReg);
-            psImage *maskStamp = psImageSubset((psImage *) mask, myReg);
-            psImage *convRefStamp = ConvolveImage(refStamp, maskStamp, badStampMaskVal, solution, kernels);
-
-            // Calculate chi^2
-            (void)psBinaryOp(subStamp, inStamp, "-", convRefStamp);
-            (void)psBinaryOp(subStamp, subStamp, "/", inStamp);
-            (void)psBinaryOp(subStamp, subStamp, "*", subStamp);
-            myReg = psRegionSet(kernelSize, kernelSize + 2 * footprint,
-                                kernelSize, kernelSize + 2 * footprint);
-            psImage *subStampTrim = psImageSubset((psImage *) subStamp, myReg);
-            psImage *maskStampTrim = psImageSubset((psImage *) maskStamp, myReg);
-            psImageStats(stats, subStampTrim, maskStampTrim, badStampMaskVal);
-
-            deviations->data.F32[s] = stats->sampleMean * (psF32)footprint * (psF32)footprint * 4.0;
-            // XXX: Allocate and free these elsewhere.
-            psFree(refStamp);
-            psFree(inStamp);
-            psFree(maskStamp);
-            psFree(convRefStamp);
-            psFree(subStampTrim);
-            psFree(maskStampTrim);
-        }
-    }
-
-    psFree(stats);
-    psFree(subStamp);
-
-    psTrace("ImageSubtract.CalculateDeviations", 4,
-            "Exiting CalculateDeviations()\n");
-    return deviations;
-}
-
-/*******************************************************************************
- ******************************************************************************/
-bool pmSubtractionRejectStamps(psArray *stamps,  ///< Array of stamps to check for rejection
-                               psImage *mask,  ///< Mask image
-                               psU32 badStampMaskVal, ///< Value to use in mask for bad stamp
-                               psS32 footprint,  ///< Region to mask if stamp is bad
-                               psF32 sigmaRej,  ///< Number of RMS deviations above zero at which to reject
-                               const psImage *refImage, ///< Reference image
-                               const psImage *inImage, ///< Input image
-                               const psVector *solution, ///< Solution vector
-                               const psSubtractionKernels *kernels ///< Array of kernel parameters
-                              )
-{
-    psTrace("ImageSubtract.pmSubtractionRejectStamps", 3,
-            "Calling pmSubtractionRejectStamps()\n");
-    PS_ASSERT_PTR_NON_NULL(stamps, false);
-    PS_ASSERT_IMAGE_NON_NULL(refImage, false);
-    PS_ASSERT_IMAGE_NON_EMPTY(refImage, false);
-    PS_ASSERT_IMAGE_TYPE(refImage, PS_TYPE_F32, false);
-    PS_ASSERT_IMAGE_NON_NULL(inImage, false);
-    PS_ASSERT_IMAGE_NON_EMPTY(inImage, false);
-    PS_ASSERT_IMAGE_TYPE(inImage, PS_TYPE_F32, false);
-    PS_ASSERT_IMAGES_SIZE_EQUAL(refImage, inImage, false);
-    PS_ASSERT_IMAGE_NON_NULL(mask, false);
-    PS_ASSERT_IMAGE_NON_EMPTY(mask, false);
-    PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_U8, false);
-    PS_ASSERT_IMAGES_SIZE_EQUAL(refImage, mask, false);
-    PS_ASSERT_VECTOR_NON_NULL(solution, false);
-    PS_ASSERT_VECTOR_TYPE(solution, PS_TYPE_F64, false);
-    PS_ASSERT_PTR_NON_NULL(kernels, false);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->v, false);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->xOrder, false);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->yOrder, false);
-    if (kernels->preCalc != NULL) {
-        PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->preCalc, false);
-    } else {
-        if (kernels->type != PM_SUBTRACTION_KERNEL_POIS) {
-            psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                    "Unallowable operation: kernels->preCalc == NULL and kernels->type != PM_SUBTRACTION_KERNEL_POIS.\n");
-            return(false);
-        }
-    }
-
-    psS32 nBF = kernels->u->n;
-    PS_ASSERT_VECTOR_SIZE(solution, nBF+1, false);
-
-    psVector *deviations = CalculateDeviations(NULL,
-                           stamps,
-                           footprint,
-                           refImage,
-                           inImage,
-                           mask,
-                           badStampMaskVal,
-                           kernels,
-                           solution);
-    //
-    // Calculate the deviation from zero.
-    //
-    psF64 meanDev = 0.0;
-    psS32 numDev = 0;
-    for (psS32 i = 0; i < deviations->n; i++) {
-        pmStamp *stamp = stamps->data[i];
-        if (stamp->status == PM_STAMP_USED) {
-            meanDev += PS_SQR(deviations->data.F32[i]);
-            numDev++;
-        }
-    }
-    psF32 rmsDev = sqrtf(meanDev / (psF64)(numDev - 1));
-    psF32 limit = rmsDev * sigmaRej;
-
-    for (psS32 s = 0; s < stamps->n; s++) {
-        pmStamp *stamp = (pmStamp *) stamps->data[s];
-        if (stamp->status == PM_STAMP_USED && fabsf(deviations->data.F32[s]) > limit) {
-            // Mask out the stamp in the image so you don't find it again
-            for (psS32 y = stamp->y - footprint; y < stamp->y + footprint; y++) {
-                for (psS32 x = stamp->x - footprint; x < stamp->x + footprint; x++) {
-                    mask->data.U8[y][x] |= badStampMaskVal;
-                }
-            }
-
-            // Set stamp for replacement
-            stamp->x = 0;
-            stamp->y = 0;
-            stamp->status = PM_STAMP_REJECTED;
-        }
-    }
-
-    psFree(deviations);
-    psTrace("ImageSubtract.pmSubtractionRejectStamps", 3,
-            "Exiting pmSubtractionRejectStamps()\n");
-    return(true);
-}
-
-/*******************************************************************************
- ******************************************************************************/
-psImage *pmSubtractionKernelImage(psImage *out,
-                                  const psVector *solution,
-                                  const psSubtractionKernels *kernels,
-                                  psF32 x,
-                                  psF32 y
-                                 )
-{
-    psTrace("ImageSubtract.pmSubtractionKernelImage", 3,
-            "Calling pmSubtractionKernelImage()\n");
-    PS_ASSERT_VECTOR_NON_NULL(solution, NULL);
-    PS_ASSERT_PTR_NON_NULL(kernels, NULL);
-    PS_ASSERT_FLOAT_WITHIN_RANGE(x, -1.0, 1.0, NULL);
-    PS_ASSERT_FLOAT_WITHIN_RANGE(y, -1.0, 1.0, NULL);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->v, NULL);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->xOrder, NULL);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->yOrder, NULL);
-    if (kernels->preCalc != NULL) {
-        PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->preCalc, NULL);
-    } else {
-        if (kernels->type != PM_SUBTRACTION_KERNEL_POIS) {
-            psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                    "Unallowable operation: kernels->preCalc == NULL and kernels->type != PM_SUBTRACTION_KERNEL_POIS.\n");
-            return(NULL);
-        }
-    }
-    PS_ASSERT_INT_EQUAL(1+kernels->u->n, solution->n, NULL);
-
-    psS32 nBF = kernels->u->n;
-    psS32 spatialOrder = kernels->spatialOrder;
-    psS32 kernelSize = kernels->size;
-
-    if (out != NULL) {
-        if ((out->numCols < (1+2*kernelSize)) || (out->numRows < (1+2*kernelSize))) {
-            psLogMsg(__func__, PS_LOG_WARN, "WARNING: out image is not large enough.\n");
-            return(out);
-        }
-    } else {
-        out = psImageAlloc(1+2*kernelSize, 1+2*kernelSize, PS_TYPE_F32);
-    }
-    PS_IMAGE_SET_F32(out, 0.0);
-
-    //
-    // Generate the spatial-order polynomial.  The [i][j]-th element of
-    // the psImage polyValues will hold (x^i * y^j) for the stamp.
-    //
-    psImage *polyValues = GenSpatialOrder(spatialOrder, x, y);
-
-    // XXX: switch (i, j) so they correspond to (x, y).
-    if (kernels->type == PM_SUBTRACTION_KERNEL_ISIS) {
-        for (psS32 k = 0 ; k < nBF ; k++) {
-            psS32 xOrder = (psS32) kernels->xOrder->data.F32[k];
-            psS32 yOrder = (psS32) kernels->yOrder->data.F32[k];
-            psF64 polyVal = polyValues->data.F64[yOrder][xOrder];
-
-            // XXX: Verify that this is correct.
-            for (psS32 i = -kernelSize ; i <= kernelSize ; i++) {
-                for (psS32 j = -kernelSize ; j <= kernelSize ; j++) {
-                    psImage *preCalc = (psImage *) kernels->preCalc->data[k];
-                    out->data.F32[i+kernelSize][j+kernelSize]+=
-                        solution->data.F64[k] *
-                        preCalc->data.F32[i+kernelSize][j+kernelSize] *
-                        polyVal;
-                }
-            }
-        }
-    } else if (kernels->type == PM_SUBTRACTION_KERNEL_POIS) {
-        for (psS32 k = 0 ; k < nBF ; k++) {
-            // XXX: Why don't we have compilation warnings on type here (if
-            // we remove the (psS32) cast)?
-            psS32 u = (psS32) kernels->u->data.F32[k];
-            psS32 v = (psS32) kernels->v->data.F32[k];
-            psS32 xOrder = (psS32) kernels->xOrder->data.F32[k];
-            psS32 yOrder = (psS32) kernels->yOrder->data.F32[k];
-            // XXX: Verify that this is correct.
-
-            out->data.F32[kernelSize - v][kernelSize - u]+=
-                solution->data.F64[k] * polyValues->data.F64[yOrder][xOrder];
-        }
-    }
-    psFree(polyValues);
-
-    psTrace("ImageSubtract.pmSubtractionKernelImage", 3,
-            "Exiting pmSubtractionKernelImage()\n");
-    return(out);
-}
Index: unk/psModules/src/pmImageSubtract.h
===================================================================
--- /trunk/psModules/src/pmImageSubtract.h	(revision 5169)
+++ 	(revision )
@@ -1,127 +1,0 @@
-/** @file  ImageSubtract.h
- *
- *  This file will contain code which creates a set of kernel basis
- *  functions, solves for their solution, and applies them to an image.
- *
- *  @author Paul Price, IfA (original prototype)
- *  @author GLG, MHPCC
- *
- *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-09-11 22:25:39 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- */
-
-#if !defined(PM_IMAGE_COMBINE_H)
-#define PM_IMAGE_COMBINE_H
-
-#if HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include<stdio.h>
-#include<math.h>
-#include "pslib.h"
-#include "psConstants.h"
-
-typedef enum {
-    PM_SUBTRACTION_KERNEL_POIS,         ///< POIS kernel --- delta functions
-    PM_SUBTRACTION_KERNEL_ISIS          ///< ISIS kernel --- gaussians modified by polynomials
-} pmSubtractionKernelsType;
-
-typedef struct
-{
-    pmSubtractionKernelsType type;      ///< Type ofKernels --- allowing the use of multiple kernels
-    psVector *u, *v;                    ///< Offset (for POIS) or polynomial order (for ISIS)
-    psVector *sigma;                    ///< Width of Gaussian (for ISIS)
-    psVector *xOrder, *yOrder;          ///< Spatial Polynomial order (for all)
-    int subIndex;                       ///< Index of kernel to be subtracted to maintain flux conservation
-    psArray *preCalc;                   ///< Array of images containing pre-calculated kernel (to
-    ///< accelerate ISIS; don't use for POIS)
-    psS32 size;                         ///< The halfsize of the kernel
-    psS32 spatialOrder;                 ///< The spatial order of the kernels
-}
-psSubtractionKernels;
-
-psSubtractionKernels *pmSubtractionKernelsAllocPOIS(
-    int size,
-    int SpatialOrder
-);
-
-psSubtractionKernels *pmSubtractionKernelsAllocISIS(
-    const psVector *sigmas,
-    const psVector *orders,
-    int size,
-    int SpatialOrder
-);
-
-typedef enum {
-    PM_STAMP_INIT,                      ///< Initial state
-    PM_STAMP_USED,                      ///< Use this stamp
-    PM_STAMP_REJECTED,                  ///< This stamp has been rejected
-    PM_STAMP_RECALC,                    ///< Having been reset, this stamp is to be recalculated
-    PM_STAMP_NONE                       ///< No stamp in this region
-} pmStampStatus;
-
-typedef struct
-{
-    int x, y;                           ///< Position
-    int p_xSize;
-    int p_ySize;
-    int p_xMin;
-    int p_xMax;
-    int p_yMin;
-    int p_yMax;
-    psImage *matrix;                    ///< Associated matrix
-    psVector *vector;                   ///< Assoicated vector
-    pmStampStatus status;               ///< Status ofstamp
-}
-pmStamp;
-
-psArray *pmSubtractionFindStamps(
-    psArray *stamps,                    ///< Output stamps, or NULL
-    const psImage *image,               ///< Image for which to find stamps
-    const psImage *mask,                ///< Mask
-    psU32 maskVal,                      ///< Value for mask
-    psF32 threshold,                    ///< Threshold for stamps in the image
-    psS32 xNum,                         ///< Number of stamps in x
-    psS32 yNum,                         ///< Number of stamps in y
-    psS32 border                        ///< Border around image to ignore (should be size of kernel)
-);
-
-bool pmSubtractionCalculateEquation(
-    psArray *stamps,                    ///< The stamps for which to calculate the equation,
-    const psImage *reference,           ///< Reference image
-    const psImage *input,               ///< Input image
-    const psSubtractionKernels *kernels,///< The kernel basis functions
-    psS32 footprint                     ///< Half-size of region over which to calculate equation
-);
-
-
-psVector *pmSubtractionSolveEquation(
-    psVector *solution,                 ///< Solution vector, or NULL
-    const psArray *stamps               ///< Array of stamps
-);
-
-bool pmSubtractionRejectStamps(
-    psArray *stamps,                    ///< Array of stamps to check for rejection
-    psImage *mask,                      ///< Mask image
-    psU32 badStampMaskVal,              ///< Value to use in mask for bad stamp
-    psS32 footprint,                    ///< Region to mask if stamp is bad
-    psF32 sigmaRej,                     ///< Number of RMS deviations above zero at which to reject
-    const psImage *refImage,            ///< Reference image
-    const psImage *inImage,             ///< Input image
-    const psVector *solution,           ///< Solution vector
-    const psSubtractionKernels *kernels ///< Array of kernel parameters
-);
-
-psImage *pmSubtractionKernelImage(
-    psImage *out,
-    const psVector *solution,
-    const psSubtractionKernels *kernels,
-    psF32 x,
-    psF32 y
-);
-
-#endif
Index: unk/psModules/src/pmMaskBadPixels.c
===================================================================
--- /trunk/psModules/src/pmMaskBadPixels.c	(revision 5169)
+++ 	(revision )
@@ -1,188 +1,0 @@
-/** @file  pmMaskBadPixels.c
- *
- *  @brief Given an input image, a bad pixel mask, a corresponding value in the bad pixel mask to mask, a
- *  saturation level, and a growing radius, mask in the input image those pixels in the bad pixel mask that
- *  match the value to mask.
- *
- *  Given an input image, in, a bad pixel mask, a corresponding value in the bad pixel mask to mask in the
- * input image, maskVal, a saturation level, and a growing radius, pmMaskBadPixels shall mask in the input
- * image those pixels in the bad pixel mask that match the value to mask. Note that the input image, in, is
- * modified in-place. All pixels in the mask which satisfy the maskVal shall have their corresponding pixels
- * masked in the input image, in. All pixels which satisfy the growVal shall have their corresponding
- * pixels, along with all pixels within the grow radius masked. Pixels which have flux greater than sat shall
- * also be masked, but not grown. Note that the input image, in, and the mask need not be the same size, since
- * the input image may already have been trimmed (following overscan subtraction), but the function shall use
- * the offsets in the image (in->x0 and in->y0) to determine the appropriate offsets to obtain the correct
- * pixel on the mask. In the event that the mask image is too small (i.e., pixels on the input image refer to
- * pixels outside the range of the mask image), the function shall generate an error.
- 
- *  @author Ross Harman, MHPCC
- *
- *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-09-12 20:51:10 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- */
-
-#if HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include<stdio.h>
-#include<math.h>
-#include<string.h>
-
-#include "pmMaskBadPixels.h"
-#include "pmMaskBadPixelsErrors.h"
-
-bool pmMaskBadPixels(
-    pmReadout *in,
-    const psImage *mask,
-    unsigned int maskVal,
-    float sat,
-    unsigned int growVal,
-    int grow)
-{
-    int i = 0;
-    int j = 0;
-    int jj = 0;
-    int ii = 0;
-    int rRound = 0;
-    int rowMin = 0;
-    int rowMax = 0;
-    int colMin = 0;
-    int colMax = 0;
-    int totOffCol = 0;
-    int totOffRow = 0;
-    float r = 0.0f;
-    psElemType inType;
-    psElemType maskType;
-    psImage *inImage = NULL;
-    psImage *inMask = NULL;
-
-
-    // Check for nulls
-    if (in == NULL) {
-        return true;   // Readout may not have data in it
-    } else if(mask==NULL) {
-        psError( PS_ERR_BAD_PARAMETER_NULL, true,
-                 PS_ERRORTEXT_pmMaskBadPixels_NULL_MASK_IMAGE);
-        return false;
-    }
-
-    inImage = in->image;
-    if (inImage == NULL) {
-        psError( PS_ERR_BAD_PARAMETER_NULL, true,
-                 PS_ERRORTEXT_pmMaskBadPixels_NULL_INPUT_IMAGE);
-        return false;
-    } else if(in->mask == NULL) {
-        in->mask = psImageAlloc(inImage->numCols, inImage->numRows, PS_TYPE_MASK);
-        memset(in->mask->data.V[0], 0, inImage->numCols*inImage->numRows*PSELEMTYPE_SIZEOF(PS_TYPE_MASK));
-    }
-    inMask = in->mask;
-
-    // Check input image and its mask are not larger than mask
-    if(inImage->numRows > mask->numRows || inImage->numCols > mask->numCols) {
-        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
-                 PS_ERRORTEXT_pmMaskBadPixels_SIZE_INPUT_IMAGE,
-                 inImage->numRows, inImage->numCols, mask->numRows, mask->numCols);
-        return false;
-    } else if(inMask->numRows>mask->numRows || inMask->numCols>mask->numCols) {
-        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
-                 PS_ERRORTEXT_pmMaskBadPixels_SIZE_MASK_IMAGE,
-                 inMask->numRows, inMask->numCols, mask->numRows, mask->numCols);
-        return false;
-    }
-
-    // Determine total offset based on image offset with chip offset
-    totOffCol = inImage->col0 + in->col0;
-    totOffRow = inImage->row0 + in->row0;
-
-    // Check that offsets are within image limits
-    if(totOffRow>=mask->numRows || totOffCol>=mask->numCols) {
-        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
-                 PS_ERRORTEXT_pmMaskBadPixels_OFFSET_MASK_IMAGE,
-                 totOffRow, totOffCol, mask->numRows, mask->numCols);
-        return false;
-    } else if(totOffRow>=inImage->numRows || totOffCol>=inImage->numCols) {
-        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
-                 PS_ERRORTEXT_pmMaskBadPixels_OFFSET_INPUT_IMAGE,
-                 totOffRow, totOffCol, inImage->numRows, inImage->numCols);
-        return false;
-    } else if(totOffRow>=inMask->numRows || totOffCol>=inMask->numCols) {
-        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
-                 PS_ERRORTEXT_pmMaskBadPixels_OFFSET_INPUT_IMAGE_MASK,
-                 totOffRow, totOffCol, inMask->numRows, inMask->numCols);
-        return false;
-    }
-
-    // Check for incorrect types
-    inType = inImage->type.type;
-    maskType = mask->type.type;
-    if(PS_IS_PSELEMTYPE_COMPLEX(inType)) {
-        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
-                 PS_ERRORTEXT_pmMaskBadPixels_TYPE_INPUT_IMAGE,
-                 inType);
-        return false;
-    } else if(maskType!=PS_TYPE_MASK) {
-        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
-                 PS_ERRORTEXT_pmMaskBadPixels_TYPE_MASK_IMAGE,
-                 maskType);
-        return false;
-    }
-
-    // Macro for all PS types
-    #define PM_BAD_PIXELS(TYPE)                                                                              \
-case PS_TYPE_##TYPE:                                                                                         \
-    for(j=totOffRow; j<inImage->numRows; j++) {                                                              \
-        for(i=totOffCol; i<inImage->numCols; i++) {                                                          \
-            \
-            /* Pixels with flux greater than sat shall be masked */                                          \
-            if(inImage->data.TYPE[j][i] > sat) {                                                             \
-                inMask->data.PS_TYPE_MASK_DATA[j][i] |= PM_MASK_SAT;                                         \
-            }                                                                                                \
-            \
-            /* Pixels which satisfy maskVal shall be masked */                                               \
-            inMask->data.PS_TYPE_MASK_DATA[j][i] |= (mask->data.PS_TYPE_MASK_DATA[j][i]&maskVal);            \
-            \
-            /* Pixels which satisfy growVal and within the grow radius shall be masked */                    \
-            if(mask->data.PS_TYPE_MASK_DATA[j][i] & growVal) {                                               \
-                rowMin = MAX(j-grow, 0);                                                                     \
-                rowMax = MIN(j+grow+1, inImage->numRows);                                                    \
-                colMin = MAX(i-grow, 0);                                                                     \
-                colMax = MIN(i+grow+1, inImage->numCols);                                                    \
-                for(jj=rowMin; jj<rowMax; jj++) {                                                            \
-                    for(ii=colMin; ii<colMax; ii++) {                                                        \
-                        r = sqrtf((ii-i)*(ii-i)+(jj-j)*(jj-j));                                              \
-                        rRound = r + 0.5;                                                                    \
-                        if(rRound <= grow) {                                                                 \
-                            inMask->data.PS_TYPE_MASK_DATA[jj][ii] |=                                        \
-                                    (mask->data.PS_TYPE_MASK_DATA[j][i]&growVal);                            \
-                        }                                                                                    \
-                    }                                                                                        \
-                }                                                                                            \
-            }                                                                                                \
-        }                                                                                                    \
-    }                                                                                                        \
-    break;
-
-    // Switch to call bad pixel masking macro defined above
-    switch(inType) {
-        PM_BAD_PIXELS(U8);
-        PM_BAD_PIXELS(U16);
-        PM_BAD_PIXELS(U32);
-        PM_BAD_PIXELS(U64);
-        PM_BAD_PIXELS(S8);
-        PM_BAD_PIXELS(S16);
-        PM_BAD_PIXELS(S32);
-        PM_BAD_PIXELS(S64);
-        PM_BAD_PIXELS(F32);
-        PM_BAD_PIXELS(F64);
-    default:
-        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
-                 PS_ERRORTEXT_pmMaskBadPixels_TYPE_UNSUPPORTED,
-                 inType);
-    }
-
-    return false;
-}
Index: unk/psModules/src/pmMaskBadPixels.h
===================================================================
--- /trunk/psModules/src/pmMaskBadPixels.h	(revision 5169)
+++ 	(revision )
@@ -1,61 +1,0 @@
-/** @file  pmMaskBadPixels.h
- *
- *  @brief Given an input image, a bad pixel mask, a corresponding value in the bad pixel mask to mask, a
- *  saturation level, and a growing radius, mask in the input image those pixels in the bad pixel mask that
- *  match the value to mask.
- *
- *  Given an input image, in, a bad pixel mask, a corresponding value in the bad pixel mask to mask in the
- *  input image, maskVal, a saturation level, and a growing radius, pmMaskBadPixels shall mask in the input
- *  image those pixels in the bad pixel mask that match the value to mask. Note that the input image, in, is
- *  modified in-place. All pixels in the mask which satisfy the maskVal shall have their corresponding pixels
- *  masked in the input image, in. All pixels which satisfy the growVal shall have their corresponding
- *  pixels, along with all pixels within the grow radius masked. Pixels which have flux greater than sat shall
- *  also be masked, but not grown. Note that the input image, in, and the mask need not be the same size,
- *  since the input image may already have been trimmed (following overscan subtraction), but the function
- *  shall use the offsets in the image (in->x0 and in->y0) to determine the appropriate offsets to obtain the
- *  correct pixel on the mask. In the event that the mask image is too small (i.e., pixels on the input image
- *  refer to pixels outside the range of the mask image), the function shall generate an error.
- *
- *  @author Ross Harman, MHPCC
- *
- *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-08-16 01:10:34 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- */
-
-#include "pslib.h"
-#include "pmAstrometry.h"
-
-/** Mask values */
-typedef enum {
-    PM_MASK_TRAP    = 0x0001,   ///< The pixel is a charge trap.
-    PM_MASK_BADCOL  = 0x0002,   ///< The pixel is a bad column.
-    PM_MASK_SAT     = 0x0004,   ///< The pixel is saturated.
-    PM_MASK_FLAT    = 0x0008    ///< The pixel is non-positive in the flat-field.
-} pmMaskValue;
-
-/** Macro to find maximum of two numbers */
-#define MAX(A,B)((A)>=(B)?(A):(B))
-
-/** Macro to find minimum of two numbers */
-#define MIN(A,B)((A)<=(B)?(A):(B))
-
-
-/** Execute bad pixels module.
- *
- *  Given an input image, a bad pixel mask, a corresponding value in the bad pixel mask to mask, a
- *  saturation level, and a growing radius, mask in the input image those pixels in the bad pixel mask that
- *  match the value to mask.
- *
- *  @return  bool: True or false for success or failure
- */
-bool pmMaskBadPixels(
-    pmReadout *in,          ///< Readout containing input image data.
-    const psImage *mask,    ///< Mask data to be added to readout mask data.
-    unsigned int maskVal,   ///< Mask value to determine what to add to input mask.
-    float sat,              ///< Saturation limit to mask bad pixels.
-    unsigned int growVal,   ///< Mask data to determine if a circurlar area should be masked.
-    int grow                ///< Radius of mask to apply around pixel.
-);
-
Index: unk/psModules/src/pmMaskBadPixelsErrors.dat
===================================================================
--- /trunk/psModules/src/pmMaskBadPixelsErrors.dat	(revision 5169)
+++ 	(revision )
@@ -1,20 +1,0 @@
-#
-#  This file is used to generate pmMaskBadPixelsErrors.h content
-#
-#  Format is:
-#  ERRORNAME(one word)    ERRORTEXT
-#
-#  N.B. in code, the ERRORNAME appears as PS_ERRORTEXT_ERRORNAME
-####################################################################
-#
-pmMaskBadPixels_NULL_MASK_IMAGE            Null not allowed for mask image.
-pmMaskBadPixels_NULL_INPUT_IMAGE           Null not allowed for input image.
-pmMaskBadPixels_SIZE_INPUT_IMAGE           Input image size exceeds that of mask image: (%d, %d) vs (%d, %d)
-pmMaskBadPixels_SIZE_MASK_IMAGE            Input image mask size exceeds that of mask image: (%d, %d) vs (%d, %d)
-pmMaskBadPixels_OFFSET_MASK_IMAGE          Total offset >= mask image: (%d, %d) vs (%d, %d)
-pmMaskBadPixels_OFFSET_INPUT_IMAGE         Total offset >= input image: (%d, %d) vs (%d, %d)
-pmMaskBadPixels_OFFSET_INPUT_IMAGE_MASK    Total offset >= input image mask: (%d, %d) vs (%d, %d)
-pmMaskBadPixels_TYPE_INPUT_IMAGE           Complex types not allowed for input image. Type: %d
-pmMaskBadPixels_TYPE_MASK_IMAGE            Mask must be PS_TYPE_MASK type. Type: %d
-pmMaskBadPixels_TYPE_MISMATCH              Input and flat image types differ: (%d vs %d)
-pmMaskBadPixels_TYPE_UNSUPPORTED           Unsupported image datatype. Type: %d
Index: unk/psModules/src/pmMaskBadPixelsErrors.h
===================================================================
--- /trunk/psModules/src/pmMaskBadPixelsErrors.h	(revision 5169)
+++ 	(revision )
@@ -1,45 +1,0 @@
-/** @file  pmMaskBadPixelsErrors.h
- *
- *  @brief Contains the error text for the mask bad pixels module
- *
- *  @ingroup ErrorHandling
- *
- *  @author Ross Harman, MHPCC
- *
- *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2004-09-24 18:00:28 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- */
-
-#ifndef PM_FLATFIELD_ERRORS_H
-#define PM_FLATFIELD_ERRORS_H
-
-/* N.B., lines between '//~Start' and '//~End' are automatic generated from
- * the template following the '//~Start'.  The template is used to generate
- * the other lines by, for each error text in pmMaskBadPixelsErrors.dat, the following
- * substitutions are made:
- *     $1  The error text macro name (first word in the pmMaskBadPixelsErrors.h lines)
- *     $2  The error text (rest of the line in pmMaskBadPixelsErrors.h)
- *     $n  The order of the source line in pmMaskBadPixelsErrors.h (comments excluded)
- * 
- * DO NOT EDIT THE LINES BETWEEN //~Start and //~End!  ANY CHANGES WILL BE OVERWRITTEN.
- */
-
-#define PS_ERRORNAME_DOMAIN "psModule.src."
-
-//~Start #define PS_ERRORTEXT_$1 "$2"
-#define PS_ERRORTEXT_pmMaskBadPixels_NULL_MASK_IMAGE "Null not allowed for mask image."
-#define PS_ERRORTEXT_pmMaskBadPixels_NULL_INPUT_IMAGE "Null not allowed for input image."
-#define PS_ERRORTEXT_pmMaskBadPixels_SIZE_INPUT_IMAGE "Input image size exceeds that of mask image: (%d, %d) vs (%d, %d)"
-#define PS_ERRORTEXT_pmMaskBadPixels_SIZE_MASK_IMAGE "Input image mask size exceeds that of mask image: (%d, %d) vs (%d, %d)"
-#define PS_ERRORTEXT_pmMaskBadPixels_OFFSET_MASK_IMAGE "Total offset >= mask image: (%d, %d) vs (%d, %d)"
-#define PS_ERRORTEXT_pmMaskBadPixels_OFFSET_INPUT_IMAGE "Total offset >= input image: (%d, %d) vs (%d, %d)"
-#define PS_ERRORTEXT_pmMaskBadPixels_OFFSET_INPUT_IMAGE_MASK "Total offset >= input image mask: (%d, %d) vs (%d, %d)"
-#define PS_ERRORTEXT_pmMaskBadPixels_TYPE_INPUT_IMAGE "Complex types not allowed for input image. Type: %d"
-#define PS_ERRORTEXT_pmMaskBadPixels_TYPE_MASK_IMAGE "Mask must be PS_TYPE_MASK type. Type: %d"
-#define PS_ERRORTEXT_pmMaskBadPixels_TYPE_MISMATCH "Input and flat image types differ: (%d vs %d)"
-#define PS_ERRORTEXT_pmMaskBadPixels_TYPE_UNSUPPORTED "Unsupported image datatype. Type: %d"
-//~End
-
-#endif
Index: unk/psModules/src/pmNonLinear.c
===================================================================
--- /trunk/psModules/src/pmNonLinear.c	(revision 5169)
+++ 	(revision )
@@ -1,123 +1,0 @@
-/** @file  pmNonLinear.c
- *
- *  Provides polynomial or table lookup non-linearity corrections to readouts.
- *
- *  @author GLG, MHPCC
- *
- *  @version $Revision: 1.12 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-08-16 01:10:34 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- *  XXX: The SDR is silent about image types.  Only F32 was implemented.
- *
- */
-
-#if HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include<stdio.h>
-#include<math.h>
-
-#include "pmNonLinear.h"
-
-/******************************************************************************
-pmNonLinearityLookup(): This routine will take an pmReadout image as input
-and a 1-D polynomial.  For each pixel in the input image, the polynomial will
-be evaluated at that pixels value, and the image pixel will then be set to
-that value.
- *****************************************************************************/
-
-pmReadout *pmNonLinearityPolynomial(pmReadout *inputReadout,
-                                    const psPolynomial1D *input1DPoly)
-{
-    PS_ASSERT_PTR_NON_NULL(inputReadout, NULL);
-    PS_ASSERT_PTR_NON_NULL(inputReadout->image, NULL);
-    PS_ASSERT_IMAGE_TYPE(inputReadout->image, PS_TYPE_F32, NULL);
-    PS_ASSERT_PTR_NON_NULL(input1DPoly, NULL);
-
-    psS32 i;
-    psS32 j;
-
-    for (i=0;i<inputReadout->image->numRows;i++) {
-        for (j=0;j<inputReadout->image->numCols;j++) {
-            inputReadout->image->data.F32[i][j] = psPolynomial1DEval(input1DPoly, inputReadout->image->data.F32[i][j]);
-        }
-    }
-    return(inputReadout);
-}
-
-
-/******************************************************************************
-pmNonLinearityLookup(): This routine will take an pmReadout image as input
-and two input vectors, which constitute a lookup table.  For each pixel in
-the input image, that pixels value will be determined in the input vector
-inFluxe, and the corresponding value in outFlux.  The image pixel will then
-be set to the value from outFlux.
- *****************************************************************************/
-pmReadout *pmNonLinearityLookup(pmReadout *inputReadout,
-                                const psVector *inFlux,
-                                const psVector *outFlux)
-{
-    PS_ASSERT_PTR_NON_NULL(inputReadout,NULL);
-    PS_ASSERT_PTR_NON_NULL(inputReadout->image,NULL);
-    PS_ASSERT_IMAGE_TYPE(inputReadout->image, PS_TYPE_F32, NULL);
-    PS_ASSERT_PTR_NON_NULL(inFlux,NULL);
-    if (inFlux->n < 2) {
-        psError(PS_ERR_UNKNOWN,true, "pmNonLinearityLookup(): input vector less than 2 elements.  Returning inputReadout image.");
-        return(inputReadout);
-    }
-    PS_ASSERT_PTR_NON_NULL(outFlux,NULL);
-    psS32 tableSize = inFlux->n;
-    if (inFlux->n != outFlux->n) {
-        tableSize = PS_MIN(inFlux->n, outFlux->n);
-        psLogMsg(__func__, PS_LOG_WARN,
-                 "WARNING: pmNonLinear.c: pmNonLinearityLookup(): input vectors have different sizes (%d, %d)\n", inFlux->n, outFlux->n);
-    }
-    PS_ASSERT_VECTOR_TYPE(inFlux, PS_TYPE_F32, NULL);
-    PS_ASSERT_VECTOR_TYPE(outFlux, PS_TYPE_F32, NULL);
-
-    psS32 i;
-    psS32 j;
-    psS32 binNum;
-    psScalar x;
-    psS32 numPixels = 0;
-    psF32 slope;
-
-    x.type.type = PS_TYPE_F32;
-    for (i=0;i<inputReadout->image->numRows;i++) {
-        for (j=0;j<inputReadout->image->numCols;j++) {
-            x.data.F32 = inputReadout->image->data.F32[i][j];
-            binNum = p_psVectorBinDisect((psVector *)inFlux, &x);
-
-            if (binNum == -2) {
-                // We get here if x is below the table lookup range.
-                inputReadout->image->data.F32[i][j] = outFlux->data.F32[0];
-                numPixels++;
-
-            } else if (binNum == -1) {
-                // We get here if x is above the table lookup range.
-                inputReadout->image->data.F32[i][j] = outFlux->data.F32[tableSize-1];
-                numPixels++;
-
-            } else if (binNum < -2) {
-                // We get here if there was some other problem.
-                psError(PS_ERR_UNKNOWN,true, "pmNonLinearityLookup(): Could not perform p_psVectorBinDisect().  Returning inputReadout image.");
-                return(inputReadout);
-                numPixels++;
-            } else {
-                // Perform linear interpolation.
-                slope = (outFlux->data.F32[binNum+1] - outFlux->data.F32[binNum]) /
-                        (inFlux->data.F32[binNum+1]  - inFlux->data.F32[binNum]);
-                inputReadout->image->data.F32[i][j] = outFlux->data.F32[binNum] +
-                                                      ((x.data.F32 - inFlux->data.F32[binNum]) * slope);
-            }
-        }
-    }
-    if (numPixels > 0) {
-        psLogMsg(__func__, PS_LOG_WARN,
-                 "WARNING: pmNonLinear.c: pmNonLinearityLookup(): %d pixels outside table.", numPixels);
-    }
-    return(inputReadout);
-}
Index: unk/psModules/src/pmNonLinear.h
===================================================================
--- /trunk/psModules/src/pmNonLinear.h	(revision 5169)
+++ 	(revision )
@@ -1,27 +1,0 @@
-/** @file  pmNonLinear.h
- *
- *  Provides polynomial or table lookup non-linearity corrections to readouts.
- *
- *  @author GLG, MHPCC
- *
- *  @version $Revision: 1.7 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-08-16 01:10:34 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- */
-
-#if !defined(PM_NON_LINEAR_H)
-#define PM_NON_LINEAR_H
-
-#include "pslib.h"
-#include "pmAstrometry.h"
-
-pmReadout *pmNonLinearityPolynomial(pmReadout *in,
-                                    const psPolynomial1D *coeff);
-
-pmReadout *pmNonLinearityLookup(pmReadout *in,
-                                const psVector *inFlux,
-                                const psVector *outFlux);
-
-#endif
Index: unk/psModules/src/pmObjects.c
===================================================================
--- /trunk/psModules/src/pmObjects.c	(revision 5169)
+++ 	(revision )
@@ -1,2120 +1,0 @@
-/** @file  pmObjects.c
- *
- *  This file will ...
- *
- *  @author GLG, MHPCC
- *  @author EAM, IfA: significant modifications.
- *
- *  @version $Revision: 1.33 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-09-12 22:33:17 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- */
-
-#include<stdio.h>
-#include<math.h>
-#include "pslib.h"
-#include "psConstants.h"
-#include "pmObjects.h"
-
-/******************************************************************************
-pmPeakAlloc(): Allocate the pmPeak data structure and set appropriate members.
-*****************************************************************************/
-pmPeak *pmPeakAlloc(psS32 x,
-                    psS32 y,
-                    psF32 counts,
-                    pmPeakType class)
-{
-    pmPeak *tmp = (pmPeak *) psAlloc(sizeof(pmPeak));
-    tmp->x = x;
-    tmp->y = y;
-    tmp->counts = counts;
-    tmp->class = class;
-
-    return(tmp);
-}
-
-/******************************************************************************
-pmMomentsAlloc(): Allocate the pmMoments structure and initialize the members
-to zero.
-*****************************************************************************/
-pmMoments *pmMomentsAlloc()
-{
-    pmMoments *tmp = (pmMoments *) psAlloc(sizeof(pmMoments));
-    tmp->x = 0.0;
-    tmp->y = 0.0;
-    tmp->Sx = 0.0;
-    tmp->Sx = 0.0;
-    tmp->Sxy = 0.0;
-    tmp->Sum = 0.0;
-    tmp->Peak = 0.0;
-    tmp->Sky = 0.0;
-    tmp->nPixels = 0;
-
-    return(tmp);
-}
-
-static void modelFree(pmModel *tmp)
-{
-    psFree(tmp->params);
-    psFree(tmp->dparams);
-}
-
-/******************************************************************************
-pmModelAlloc(): Allocate the pmModel structure, along with its parameters,
-and initialize the type member.  Initialize the params to 0.0.
-XXX EAM: changing params and dparams to psVector
-*****************************************************************************/
-pmModel *pmModelAlloc(pmModelType type)
-{
-    pmModel *tmp = (pmModel *) psAlloc(sizeof(pmModel));
-
-    tmp->type = type;
-    tmp->chisq = 0.0;
-    switch (type) {
-    case PS_MODEL_GAUSS:
-        tmp->params  = psVectorAlloc(7, PS_TYPE_F32);
-        tmp->dparams = psVectorAlloc(7, PS_TYPE_F32);
-        break;
-    case PS_MODEL_PGAUSS:
-        tmp->params  = psVectorAlloc(7, PS_TYPE_F32);
-        tmp->dparams = psVectorAlloc(7, PS_TYPE_F32);
-        break;
-    case PS_MODEL_TWIST_GAUSS:
-        tmp->params  = psVectorAlloc(11, PS_TYPE_F32);
-        tmp->dparams = psVectorAlloc(11, PS_TYPE_F32);
-        break;
-    case PS_MODEL_WAUSS:
-        tmp->params  = psVectorAlloc(9, PS_TYPE_F32);
-        tmp->dparams = psVectorAlloc(9, PS_TYPE_F32);
-        break;
-    case PS_MODEL_SERSIC:
-        tmp->params  = psVectorAlloc(8, PS_TYPE_F32);
-        tmp->dparams = psVectorAlloc(8, PS_TYPE_F32);
-        break;
-    case PS_MODEL_SERSIC_CORE:
-        tmp->params  = psVectorAlloc(12, PS_TYPE_F32);
-        tmp->dparams = psVectorAlloc(12, PS_TYPE_F32);
-        break;
-    default:
-        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
-        return(NULL);
-    }
-
-    for (psS32 i = 0; i < tmp->params->n; i++) {
-        tmp->params->data.F32[i] = 0.0;
-        tmp->dparams->data.F32[i] = 0.0;
-    }
-
-    psMemSetDeallocator(tmp, (psFreeFunc) modelFree);
-    return(tmp);
-}
-
-/******************************************************************************
-XXX: We don't free pixels and mask since that caused a memory error.
-We might need to increase the reference counter and decrease it here.
-*****************************************************************************/
-static void sourceFree(pmSource *tmp)
-{
-    psFree(tmp->peak);
-    //    psFree(tmp->pixels);
-    //    psFree(tmp->mask);
-    psFree(tmp->moments);
-    psFree(tmp->modelPSF);
-    psFree(tmp->modelFLT);
-}
-
-/******************************************************************************
-pmSourceAlloc(): Allocate the pmSource structure and initialize its members
-to NULL.
-*****************************************************************************/
-pmSource *pmSourceAlloc()
-{
-    pmSource *tmp = (pmSource *) psAlloc(sizeof(pmSource));
-    tmp->peak = NULL;
-    tmp->pixels = NULL;
-    tmp->mask = NULL;
-    tmp->moments = NULL;
-    tmp->modelPSF = NULL;
-    tmp->modelFLT = NULL;
-    tmp->type = 0;
-    psMemSetDeallocator(tmp, (psFreeFunc) sourceFree);
-
-    return(tmp);
-}
-
-/******************************************************************************
-pmFindVectorPeaks(vector, threshold): Find all local peaks in the given vector
-above the given threshold.  Returns a vector of type PS_TYPE_U32 containing
-the location (x value) of all peaks.
- 
-XXX: What types should be supported?  Only F32 is implemented.
- 
-XXX: We currently step through the input vector twice; once to determine the
-size of the output vector, then to set the values of the output vector.
-Depending upon actual use, this may need to be optimized.
-*****************************************************************************/
-psVector *pmFindVectorPeaks(const psVector *vector,
-                            psF32 threshold)
-{
-    PS_ASSERT_VECTOR_NON_NULL(vector, NULL);
-    PS_ASSERT_VECTOR_NON_EMPTY(vector, NULL);
-    PS_ASSERT_VECTOR_TYPE(vector, PS_TYPE_F32, NULL);
-    int count = 0;
-    int n = vector->n;
-
-    //
-    // Special case: the input vector has a single element.
-    //
-    if (n == 1) {
-        psVector *tmpVector = NULL;
-        ;
-        if (vector->data.F32[0] > threshold) {
-            tmpVector = psVectorAlloc(1, PS_TYPE_U32);
-            tmpVector->data.U32[0] = 0;
-        } else {
-            tmpVector = psVectorAlloc(0, PS_TYPE_U32);
-        }
-        return(tmpVector);
-    }
-
-    //
-    // Determine if first pixel is a peak
-    //
-    if ((vector->data.F32[0] > vector->data.F32[1]) &&
-            (vector->data.F32[0] > threshold)) {
-        count++;
-    }
-
-    //
-    // Determine if interior pixels are peaks
-    //
-    for (psU32 i = 1; i < n-1 ; i++) {
-        if ((vector->data.F32[i] > vector->data.F32[i-1]) &&
-                (vector->data.F32[i] > vector->data.F32[i+1]) &&
-                (vector->data.F32[i] > threshold)) {
-            count++;
-        }
-    }
-
-    //
-    // Determine if last pixel is a peak
-    //
-    if ((vector->data.F32[n-1] > vector->data.F32[n-2]) &&
-            (vector->data.F32[n-1] > threshold)) {
-        count++;
-    }
-
-    //
-    // We know how many peaks exist, so we now allocate a psVector to store
-    // those peaks.
-    //
-    psVector *tmpVector = psVectorAlloc(count, PS_TYPE_U32);
-    count = 0;
-
-    //
-    // Determine if first pixel is a peak
-    //
-    if ((vector->data.F32[0] > vector->data.F32[1]) &&
-            (vector->data.F32[0] > threshold)) {
-        tmpVector->data.U32[count++] = 0;
-    }
-
-    //
-    // Determine if interior pixels are peaks
-    //
-    for (psU32 i = 1; i < (n-1) ; i++) {
-        if ((vector->data.F32[i] > vector->data.F32[i-1]) &&
-                (vector->data.F32[i] > vector->data.F32[i+1]) &&
-                (vector->data.F32[i] > threshold)) {
-            tmpVector->data.U32[count++] = i;
-        }
-    }
-
-    //
-    // Determine if last pixel is a peak
-    //
-    if ((vector->data.F32[n-1] > vector->data.F32[n-2]) &&
-            (vector->data.F32[n-1] > threshold)) {
-        tmpVector->data.U32[count++] = n-1;
-    }
-
-    return(tmpVector);
-}
-
-/******************************************************************************
-getRowVectorFromImage(): a private function which simply returns a
-psVector containing the specified row of data from the psImage.
- 
-XXX: Is there a better way to do this?
-*****************************************************************************/
-static psVector *getRowVectorFromImage(psImage *image,
-                                       psU32 row)
-{
-    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
-    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
-
-    psVector *tmpVector = psVectorAlloc(image->numCols, PS_TYPE_F32);
-    for (psU32 col = 0; col < image->numCols ; col++) {
-        tmpVector->data.F32[col] = image->data.F32[row][col];
-    }
-    return(tmpVector);
-}
-
-/******************************************************************************
-myListAddPeak(): A private function which allocates a psArray, if the list
-argument is NULL, otherwise it adds the peak to that list.
-XXX EAM : changed the output to psArray
-XXX EAM : Switched row, col args
-XXX EAM : NOTE: this was changed in the call, so the new code is consistent
-*****************************************************************************/
-static psArray *myListAddPeak(psArray *list,
-                              psS32 row,
-                              psS32 col,
-                              psF32 counts,
-                              pmPeakType type)
-{
-    pmPeak *tmpPeak = pmPeakAlloc(col, row, counts, type);
-
-    if (list == NULL) {
-        list = psArrayAlloc(100);
-        list->n = 0;
-    }
-    psArrayAdd(list, 100, tmpPeak);
-
-    return(list);
-}
-
-/******************************************************************************
-pmFindImagePeaks(image, threshold): Find all local peaks in the given psImage
-above the given threshold.  Returns a psArray containing location (x/y value)
-of all peaks.
- 
-XXX: I'm not convinced the peak type definition in the SDRS is mutually
-exclusive.  Some peaks can have multiple types.  Edges for sure.  Also, a
-digonal line with the same value at each point will have a peak for every
-point on that line.
- 
-XXX: This does not work if image has either a single row, or a single column.
- 
-XXX: In the output psArray elements, should we use the image row/column offsets?
-     Currently, we do not.
- 
-XXX: Merge with CVS 1.20.  This had the proper code for images with a single
-row or column.
-*****************************************************************************/
-psArray *pmFindImagePeaks(const psImage *image,
-                          psF32 threshold)
-{
-    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
-    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
-    if ((image->numRows == 1) || (image->numCols == 1)) {
-        psError(PS_ERR_UNKNOWN, true, "Currently, input image must have at least 2 rows and 2 columns.");
-        return(NULL);
-    }
-    psVector *tmpRow = NULL;
-    psU32 col = 0;
-    psU32 row = 0;
-    psArray *list = NULL;
-
-    //
-    // Find peaks in row 0 only.
-    //
-    row = 0;
-    tmpRow = getRowVectorFromImage((psImage *) image, row);
-    psVector *row1 = pmFindVectorPeaks(tmpRow, threshold);
-
-    for (psU32 i = 0 ; i < row1->n ; i++ ) {
-        col = row1->data.U32[i];
-        //
-        // Determine if pixel (0,0) is a peak.
-        //
-        if (col == 0) {
-            if ( (image->data.F32[row][col] >  image->data.F32[row][col+1]) &&
-                    (image->data.F32[row][col] >  image->data.F32[row+1][col]) &&
-                    (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
-
-                if (image->data.F32[row][col] > threshold) {
-                    list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
-                }
-            }
-        } else if (col < (image->numCols - 1)) {
-            if ( (image->data.F32[row][col] >= image->data.F32[row][col-1]) &&
-                    (image->data.F32[row][col] >  image->data.F32[row][col+1]) &&
-                    (image->data.F32[row][col] >= image->data.F32[row+1][col-1]) &&
-                    (image->data.F32[row][col] >  image->data.F32[row+1][col]) &&
-                    (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
-                if (image->data.F32[row][col] > threshold) {
-                    list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
-                }
-            }
-
-        } else if (col == (image->numCols - 1)) {
-            if ( (image->data.F32[row][col] >= image->data.F32[row][col-1]) &&
-                    (image->data.F32[row][col] > image->data.F32[row+1][col]) &&
-                    (image->data.F32[row][col] >= image->data.F32[row+1][col-1])) {
-                if (image->data.F32[row][col] > threshold) {
-                    list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
-                }
-            }
-
-        } else {
-            psError(PS_ERR_UNKNOWN, true, "peak specified valid column range.");
-        }
-    }
-
-    //
-    // Exit if this image has a single row.
-    //
-    if (image->numRows == 1) {
-        return(list);
-    }
-
-    //
-    // Find peaks in interior rows only.
-    //
-    for (row = 1 ; row < (image->numRows - 1) ; row++) {
-        tmpRow = getRowVectorFromImage((psImage *) image, row);
-        row1 = pmFindVectorPeaks(tmpRow, threshold);
-
-        // Step through all local peaks in this row.
-        for (psU32 i = 0 ; i < row1->n ; i++ ) {
-            pmPeakType myType = PM_PEAK_UNDEF;
-            col = row1->data.U32[i];
-
-            if (col == 0) {
-                // If col==0, then we can not read col-1 pixels
-                if ((image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
-                        (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
-                        (image->data.F32[row][col] >= image->data.F32[row][col+1]) &&
-                        (image->data.F32[row][col] >= image->data.F32[row+1][col]) &&
-                        (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
-                    myType = PM_PEAK_EDGE;
-                    list = myListAddPeak(list, row, col, image->data.F32[row][col], myType);
-                }
-            } else if (col < (image->numCols - 1)) {
-                // This is an interior pixel
-                if ((image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
-                        (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
-                        (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
-                        (image->data.F32[row][col] > image->data.F32[row][col-1]) &&
-                        (image->data.F32[row][col] >= image->data.F32[row][col+1]) &&
-                        (image->data.F32[row][col] >= image->data.F32[row+1][col-1]) &&
-                        (image->data.F32[row][col] >= image->data.F32[row+1][col]) &&
-                        (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
-                    if (image->data.F32[row][col] > threshold) {
-                        if ((image->data.F32[row][col] > image->data.F32[row-1][col-1]) &&
-                                (image->data.F32[row][col] > image->data.F32[row-1][col]) &&
-                                (image->data.F32[row][col] > image->data.F32[row-1][col+1]) &&
-                                (image->data.F32[row][col] > image->data.F32[row][col-1]) &&
-                                (image->data.F32[row][col] > image->data.F32[row][col+1]) &&
-                                (image->data.F32[row][col] > image->data.F32[row+1][col-1]) &&
-                                (image->data.F32[row][col] > image->data.F32[row+1][col]) &&
-                                (image->data.F32[row][col] > image->data.F32[row+1][col+1])) {
-                            myType = PM_PEAK_LONE;
-                        }
-
-                        if ((image->data.F32[row][col] == image->data.F32[row-1][col-1]) ||
-                                (image->data.F32[row][col] == image->data.F32[row-1][col]) ||
-                                (image->data.F32[row][col] == image->data.F32[row-1][col+1]) ||
-                                (image->data.F32[row][col] == image->data.F32[row][col-1]) ||
-                                (image->data.F32[row][col] == image->data.F32[row][col+1]) ||
-                                (image->data.F32[row][col] == image->data.F32[row+1][col-1]) ||
-                                (image->data.F32[row][col] == image->data.F32[row+1][col]) ||
-                                (image->data.F32[row][col] == image->data.F32[row+1][col+1])) {
-                            myType = PM_PEAK_FLAT;
-                        }
-
-                        list = myListAddPeak(list, row, col, image->data.F32[row][col], myType);
-                    }
-                }
-            } else if (col == (image->numCols - 1)) {
-                // If col==numCols - 1, then we can not read col+1 pixels
-                if ((image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
-                        (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
-                        (image->data.F32[row][col] > image->data.F32[row][col-1]) &&
-                        (image->data.F32[row][col] >= image->data.F32[row][col+1]) &&
-                        (image->data.F32[row][col] >= image->data.F32[row+1][col-1]) &&
-                        (image->data.F32[row][col] >= image->data.F32[row+1][col])) {
-                    myType = PM_PEAK_EDGE;
-                    list = myListAddPeak(list, row, col, image->data.F32[row][col], myType);
-                }
-            } else {
-                psError(PS_ERR_UNKNOWN, true, "peak specified valid column range.");
-            }
-
-        }
-    }
-
-    //
-    // Find peaks in the last row only.
-    //
-    row = image->numRows - 1;
-    tmpRow = getRowVectorFromImage((psImage *) image, row);
-    row1 = pmFindVectorPeaks(tmpRow, threshold);
-    for (psU32 i = 0 ; i < row1->n ; i++ ) {
-        col = row1->data.U32[i];
-        if (col == 0) {
-            if ( (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
-                    (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
-                    (image->data.F32[row][col] >  image->data.F32[row][col+1])) {
-                if (image->data.F32[row][col] > threshold) {
-                    list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
-                }
-            }
-        } else if (col < (image->numCols - 1)) {
-            if ( (image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
-                    (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
-                    (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
-                    (image->data.F32[row][col] >  image->data.F32[row][col-1]) &&
-                    (image->data.F32[row][col] >= image->data.F32[row][col+1])) {
-                if (image->data.F32[row][col] > threshold) {
-                    list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
-                }
-            }
-
-        } else if (col == (image->numCols - 1)) {
-            if ( (image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
-                    (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
-                    (image->data.F32[row][col] >  image->data.F32[row][col-1])) {
-                if (image->data.F32[row][col] > threshold) {
-                    list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
-                }
-            }
-        } else {
-            psError(PS_ERR_UNKNOWN, true, "peak specified valid colum range.");
-        }
-    }
-    return(list);
-}
-
-// XXX: Macro this.
-static bool isItInThisRegion(const psRegion valid,
-                             psS32 x,
-                             psS32 y)
-{
-
-    if ((x >= valid.x0) &&
-            (x <= valid.x1) &&
-            (y >= valid.y0) &&
-            (y <= valid.y1)) {
-        return(true);
-    }
-
-    return(false);
-}
-
-
-/******************************************************************************
-psCullPeaks(peaks, maxValue, valid): eliminate peaks from the psArray that have
-a peak value above the given maximum, or fall outside the valid region.
- 
-XXX: Should the sky value be used when comparing the maximum?
- 
-XXX: warning message if valid is NULL?
- 
-XXX: changed API to create a NEW output psArray (should change name as well)
-*****************************************************************************/
-psList *pmCullPeaks(psList *peaks,
-                    psF32 maxValue,
-                    const psRegion valid)
-{
-    PS_ASSERT_PTR_NON_NULL(peaks, NULL);
-    //    PS_ASSERT_PTR_NON_NULL(valid, NULL);
-
-    psListElem *tmpListElem = (psListElem *) peaks->head;
-    psS32 indexNum = 0;
-
-    //    printf("pmCullPeaks(): list size is %d\n", peaks->size);
-    while (tmpListElem != NULL) {
-        pmPeak *tmpPeak = (pmPeak *) tmpListElem->data;
-        if ((tmpPeak->counts > maxValue) ||
-                (true == isItInThisRegion(valid, tmpPeak->x, tmpPeak->y))) {
-            psListRemoveData(peaks, (psPtr) tmpPeak);
-        }
-
-        indexNum++;
-        tmpListElem = tmpListElem->next;
-    }
-
-    return(peaks);
-}
-
-// XXX EAM: I changed this to return a new, subset array
-//          rather than alter the existing one
-psArray *pmPeaksSubset(psArray *peaks, psF32 maxValue, const psRegion valid)
-{
-    PS_ASSERT_PTR_NON_NULL(peaks, NULL);
-
-    psArray *output = psArrayAlloc (200);
-    output->n = 0;
-
-    psTrace (".pmObjects.pmCullPeaks", 3, "list size is %d\n", peaks->n);
-
-    for (int i = 0; i < peaks->n; i++) {
-        pmPeak *tmpPeak = (pmPeak *) peaks->data[i];
-        if (tmpPeak->counts > maxValue)
-            continue;
-        if (isItInThisRegion(valid, tmpPeak->x, tmpPeak->y))
-            continue;
-        psArrayAdd (output, 200, tmpPeak);
-    }
-    return(output);
-}
-
-/******************************************************************************
-pmSource *pmSourceLocalSky(image, peak, innerRadius, outerRadius): this
-routine creates a new pmSource data structure and sets the following members:
-    ->pmPeak
-    ->pmMoments->sky
- 
-The sky value is set from the pixels in the square annulus surrounding the
-peak pixel.
- 
-We simply create a subSet image and mask the inner pixels, then call
-psImageStats on that subImage+mask.
- 
-XXX: The subImage has width of 1+2*outerRadius.  Verify with IfA.
- 
-XXX: Use static data structures for:
-     subImage
-     subImageMask
-     myStats
- 
-XXX: ensure that the inner and out radius fit in the actual image.  Should
-     we generate an error, or warning?  Currently an error.
- 
-XXX: Sync with IfA on whether the peak x/y coords are data structure coords,
-     or they use the image row/column offsets.
- 
-XXX: Should we simply set pmSource->peak = peak?  If so, should we increase
-the reference counter?  Or, should we copy the data structure?
- 
-XXX: Currently the subimage always has an even number of rows/columns.  Is
-     this correct?  Since there is a center pixel, maybe it should have an
-     odd number of rows/columns.
- 
-XXX: Use psTrace() for the print statements.
- 
-XXX: Don't use separate structs for the subimage and mask.  Use the source->
-     members.
-*****************************************************************************/
-pmSource *pmSourceLocalSky(const psImage *image,
-                           const pmPeak *peak,
-                           psStatsOptions statsOptions,
-                           psF32 innerRadius,
-                           psF32 outerRadius)
-{
-    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
-    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
-    PS_ASSERT_PTR_NON_NULL(peak, NULL);
-    PS_FLOAT_COMPARE(0.0, innerRadius, NULL);
-    PS_FLOAT_COMPARE(innerRadius, outerRadius, NULL);
-    psS32 innerRadiusS32 = (psS32) innerRadius;
-    psS32 outerRadiusS32 = (psS32) outerRadius;
-
-    //
-    // We define variables for code readability.
-    //
-    // XXX: Since the peak->xy coords are in image, not subImage coords,
-    // these variables should be renamed for clarity (imageCenterRow, etc).
-    //
-    // peak->x,y is guaranteed to be on image
-    psS32 SubImageCenterRow = peak->y;
-    psS32 SubImageCenterCol = peak->x;
-
-    // XXX EAM : I added this code to stay on the image. So did George
-    psS32 SubImageStartRow  = PS_MAX(0, SubImageCenterRow - outerRadiusS32);
-    psS32 SubImageEndRow    = PS_MIN(image->numRows - 1, SubImageCenterRow + outerRadiusS32);
-    psS32 SubImageStartCol  = PS_MAX(0, SubImageCenterCol - outerRadiusS32);
-    psS32 SubImageEndCol    = PS_MIN(image->numCols - 1, SubImageCenterCol + outerRadiusS32);
-    // AnulusWidth == number of pixels width in the annulus.  We add one since
-    // the pixels at the inner AND outher radius are included.
-    psS32 AnulusWidth = 1 + (outerRadiusS32 - innerRadiusS32);
-    // Example: assume an outer/inner radius of 20/10.  Then the subimage
-    // should have width/length of 40.  An 18-by-18 interior region will
-    // be masked.
-    //    printf("pmSourceLocalSky(): innerRadiusS32 is %d\n", innerRadiusS32);
-    //    printf("pmSourceLocalSky(): outerRadiusS32 is %d\n", outerRadiusS32);
-    //    printf("pmSourceLocalSky(): AnulusWidth is %d\n", AnulusWidth);
-
-    // XXX EAM : these tests should not be needed: we can never hit this error because of above
-    # if (1)
-
-        if (SubImageStartRow < 0) {
-            psError(PS_ERR_UNKNOWN, true, "Sub image startRow is outside image boundaries (%d).\n",
-                    SubImageStartRow);
-            return(NULL);
-        }
-    if (SubImageEndRow >= image->numRows) {
-        psError(PS_ERR_UNKNOWN, true, "Sub image endRow is outside image boundaries (%d).\n",
-                SubImageEndRow);
-        return(NULL);
-    }
-    if (SubImageStartCol < 0) {
-        psError(PS_ERR_UNKNOWN, true, "Sub image startCol is outside image boundaries (%d).\n",
-                SubImageStartCol);
-        return(NULL);
-    }
-    if (SubImageEndCol >= image->numCols) {
-        psError(PS_ERR_UNKNOWN, true, "Sub image endCol is outside image boundaries (%d).\n",
-                SubImageEndCol);
-        return(NULL);
-    }
-    # endif
-
-    //
-    // Grab a subimage of the original image of size (2 * outerRadius).
-    //
-    // XXX: Must fix for new psImageSubset
-    //    psImage *subImage = psImageSubset((psImage *) image,
-    //                                      SubImageStartCol,
-    //                                      SubImageStartRow,
-    //                                      SubImageEndCol,
-    //                                      SubImageEndRow);
-    //    printf("pmSourceLocalSky: subimage width/length is (%d, %d)\n", subImage->numCols, subImage->numRows);
-    psRegion tmpRegion = psRegionSet(SubImageStartCol,
-                                     SubImageEndCol,
-                                     SubImageStartRow,
-                                     SubImageEndRow);
-    psImage *subImage = psImageSubset((psImage *) image, tmpRegion);
-
-    psImage *subImageMask = psImageAlloc(subImage->numCols,
-                                         subImage->numRows,
-                                         PS_TYPE_U8);
-
-    //
-    // Loop through the subimage mask, initialize mask to 0.
-    //
-    for (psS32 row = 0 ; row < subImageMask->numRows; row++) {
-        for (psS32 col = 0 ; col < subImageMask->numCols; col++) {
-            subImageMask->data.U8[row][col] = 0;
-        }
-    }
-
-    //
-    // Loop through the subimage, mask off pixels in the inner square.
-    // XXX this uses a static mask value of 1
-    //
-    for (psS32 row = AnulusWidth; row <= (subImageMask->numRows - AnulusWidth) - 1; row++) {
-        for (psS32 col = AnulusWidth; col <= (subImageMask->numCols - AnulusWidth) - 1; col++) {
-            subImageMask->data.U8[row][col] = 1;
-        }
-    }
-
-
-    //    for (psS32 row = 0 ; row < subImage->numRows; row++) {
-    //        for (psS32 col = 0 ; col < subImage->numCols; col++) {
-    //            printf("(%d) ", subImageMask->data.U8[row][col]);
-    //        }
-    //        printf("\n");
-    //    }
-
-    //
-    // Allocate the myStats structure, then call psImageStats(), which will
-    // calculate the specified statistic.
-    //
-    psStats *myStats = psStatsAlloc(statsOptions);
-    myStats = psImageStats(myStats, subImage, subImageMask, 1);
-
-    //
-    // Create the output mySource, and set appropriate members.
-    //
-    pmSource *mySource = pmSourceAlloc();
-    mySource->peak = (pmPeak *) peak;
-    mySource->moments = pmMomentsAlloc();
-    psF64 tmpF64;
-    p_psGetStatValue(myStats, &tmpF64);
-    mySource->moments->Sky = (psF32) tmpF64;
-    mySource->pixels = subImage;
-    mySource->mask = subImageMask;
-
-    //
-    // Free things.  XXX: This should be static memory.
-    //
-    psFree(myStats);
-
-    return(mySource);
-}
-
-/******************************************************************************
-bool checkRadius(*peak, radius, x, y): private function which simply
-determines if the (x, y) point is within the radius of the specified peak.
- 
-XXX: macro this for performance.
-*****************************************************************************/
-static bool checkRadius(pmPeak *peak,
-                        psF32 radius,
-                        psS32 x,
-                        psS32 y)
-{
-    if (PS_SQR(radius) >= (psF32) (PS_SQR(x - peak->x) + PS_SQR(y - peak->y))) {
-        return(true);
-    }
-
-    return(false);
-}
-
-/******************************************************************************
-bool checkRadius2(): private function which simply determines if the (x, y)
-point is within the radius of the specified peak.
- 
-XXX: macro this for performance.
-XXX: this is rather inefficient - at least compute and compare against radius^2
-*****************************************************************************/
-static bool checkRadius2(psF32 xCenter,
-                         psF32 yCenter,
-                         psF32 radius,
-                         psF32 x,
-                         psF32 y)
-{
-    /// XXX EAM should compare with hypot (x,y) for speed
-    if ((PS_SQR(x - xCenter) + PS_SQR(y - yCenter)) < PS_SQR(radius)) {
-        return(true);
-    }
-
-    return(false);
-}
-
-/******************************************************************************
-pmSourceMoments(source, radius): this function takes a subImage defined in the
-pmSource data structure, along with the peak location, and determines the
-various moments associated with that peak.
- 
-Requires the following to have been created:
-    pmSource
-    pmSource->peak
-    pmSource->pixels
- 
-XXX: The peak calculations are done in image coords, not subImage coords.
- 
-XXX: mask values?
-*****************************************************************************/
-pmSource *pmSourceMoments(pmSource *source,
-                          psF32 radius)
-{
-    PS_ASSERT_PTR_NON_NULL(source, NULL);
-    PS_ASSERT_PTR_NON_NULL(source->peak, NULL);
-    PS_ASSERT_PTR_NON_NULL(source->pixels, NULL);
-    PS_FLOAT_COMPARE(0.0, radius, NULL);
-
-    //
-    // XXX: Verify the setting for sky if source->moments == NULL.
-    //
-    psF32 sky = 0.0;
-    if (source->moments == NULL) {
-        source->moments = pmMomentsAlloc();
-    } else {
-        sky = source->moments->Sky;
-    }
-
-    //
-    // Sum = SUM (z - sky)
-    // X1  = SUM (x - xc)*(z - sky)
-    // X2  = SUM (x - xc)^2 * (z - sky)
-    // XY  = SUM (x - xc)*(y - yc)*(z - sky)
-    //
-    psF32 Sum = 0.0;
-    psF32 peakPixel = -PS_MAX_F32;
-    psS32 numPixels = 0;
-    psF32 X1 = 0.0;
-    psF32 Y1 = 0.0;
-    psF32 X2 = 0.0;
-    psF32 Y2 = 0.0;
-    psF32 XY = 0.0;
-    psF32 x = 0;
-    psF32 y = 0;
-    //
-    // XXX why do I get different results for these two methods of finding Sx?
-    // XXX Sx, Sy would be better measured if we clip pixels close to sky
-    // XXX Sx, Sy can still be imaginary, so we probably need to keep Sx^2?
-    // We loop through all pixels in this subimage (source->pixels), and for each
-    // pixel that is not masked, AND within the radius of the peak pixel, we
-    // proceed with the moments calculation.  need to do two loops for a
-    // numerically stable result.  first loop: get the sums.
-    //
-    for (psS32 row = 0; row < source->pixels->numRows ; row++) {
-        for (psS32 col = 0; col < source->pixels->numCols ; col++) {
-            if ((source->mask != NULL) && (source->mask->data.U8[row][col] != 0)) {
-                psS32 imgColCoord = col + source->pixels->col0;
-                psS32 imgRowCoord = row + source->pixels->row0;
-                if (checkRadius(source->peak,
-                                radius,
-                                imgColCoord,
-                                imgRowCoord)) {
-                    psF32 xDiff = (psF32) (imgColCoord - source->peak->x);
-                    psF32 yDiff = (psF32) (imgRowCoord - source->peak->y);
-                    psF32 pDiff = source->pixels->data.F32[row][col] - sky;
-
-                    Sum+= pDiff;
-                    X1+= xDiff * pDiff;
-                    Y1+= yDiff * pDiff;
-                    XY+= xDiff * yDiff * pDiff;
-
-                    X2+= PS_SQR(xDiff) * pDiff;
-                    Y2+= PS_SQR(yDiff) * pDiff;
-
-                    if (source->pixels->data.F32[row][col] > peakPixel) {
-                        peakPixel = source->pixels->data.F32[row][col];
-                    }
-                    numPixels++;
-                }
-            }
-        }
-    }
-
-    //
-    // first moment X  = X1/Sum + xc
-    // second moment X = sqrt (X2/Sum - (X1/Sum)^2)
-    // Sxy             = XY / Sum
-    //
-    x = X1/Sum;
-    y = Y1/Sum;
-    source->moments->x = x + ((psF32) source->peak->x);
-    source->moments->y = y + ((psF32) source->peak->y);
-
-    source->moments->Sxy = XY/Sum;
-    source->moments->Sum = Sum;
-    source->moments->Peak = peakPixel;
-    source->moments->nPixels = numPixels;
-
-    // XXX EAM : these values can be negative, so we need to limit the range
-    source->moments->Sx = sqrt(PS_MAX(X2/Sum - PS_SQR(x), 0));
-    source->moments->Sy = sqrt(PS_MAX(Y2/Sum - PS_SQR(y), 0));
-    return(source);
-
-    // XXX EAM : the following code should be the same as above, but it is not very stable: ignore it
-    # if (0)
-        //
-        // second loop: get the difference sums
-        //
-        X2 = Y2 = 0;
-    for (psS32 row = 0; row < source->pixels->numRows ; row++) {
-        for (psS32 col = 0; col < source->pixels->numCols ; col++) {
-            if ((source->mask != NULL) && (source->mask->data.U8[row][col] != 0)) {
-                psS32 imgColCoord = col + source->pixels->col0;
-                psS32 imgRowCoord = row + source->pixels->row0;
-                if (checkRadius(source->peak,
-                                radius,
-                                imgColCoord,
-                                imgRowCoord)) {
-                    psF32 xDiff = (psF32) (imgColCoord - source->peak->x);
-                    psF32 yDiff = (psF32) (imgRowCoord - source->peak->y);
-                    psF32 pDiff = source->pixels->data.F32[row][col] - sky;
-
-                    Sum+= pDiff;
-                    X2+= PS_SQR(xDiff - x) * pDiff;
-                    Y2+= PS_SQR(yDiff - y) * pDiff;
-                }
-            }
-        }
-    }
-
-    //
-    // second moment X = sqrt (X2/Sum)
-    //
-    source->moments->Sx = (X2/Sum);
-    source->moments->Sy = (Y2/Sum);
-    return(source);
-    # endif
-}
-
-// XXX EAM : I used
-int pmComparePeakAscend (const void **a, const void **b)
-{
-    pmPeak *A = *(pmPeak **)a;
-    pmPeak *B = *(pmPeak **)b;
-
-    psF32 diff;
-
-    diff = A->counts - B->counts;
-    if (diff < FLT_EPSILON)
-        return (-1);
-    if (diff > FLT_EPSILON)
-        return (+1);
-    return (0);
-}
-
-int pmComparePeakDescend (const void **a, const void **b)
-{
-    pmPeak *A = *(pmPeak **)a;
-    pmPeak *B = *(pmPeak **)b;
-
-    psF32 diff;
-
-    diff = A->counts - B->counts;
-    if (diff < FLT_EPSILON)
-        return (+1);
-    if (diff > FLT_EPSILON)
-        return (-1);
-    return (0);
-}
-
-/******************************************************************************
-pmSourceRoughClass(source, metadata): make a guess at the source
-classification.
- 
-XXX: This is not useable code, as of the release date.  There remains a fair
-bit of coding to be completed.
- 
-XXX: The sigX and sigY stuff in the SDRS is unclear.
- 
-XXX: How can this function ever return FALSE?
-*****************************************************************************/
-
-# define NPIX 10
-# define SCALE 0.1
-
-// XXX I am ignore memory freeing issues (EAM)
-bool pmSourceRoughClass(psArray *sources, psMetadata *metadata)
-{
-    PS_ASSERT_PTR_NON_NULL(sources, false);
-    PS_ASSERT_PTR_NON_NULL(metadata, false);
-    psBool rc = true;
-    psArray *peaks  = NULL;
-    psF32 clumpX = 0.0;
-    psF32 clumpDX = 0.0;
-    psF32 clumpY = 0.0;
-    psF32 clumpDY = 0.0;
-
-    // find the sigmaX, sigmaY clump
-    {
-        psStats *stats  = NULL;
-        psImage *splane = NULL;
-        int binX, binY;
-
-        // construct a sigma-plane image
-        splane = psImageAlloc (NPIX/SCALE, NPIX/SCALE, PS_TYPE_F32);
-
-        // place the sources in the sigma-plane image (ignore 0,0 values?)
-        for (psS32 i = 0 ; i < sources->n ; i++)
-        {
-            pmSource *tmpSrc = (pmSource *) sources->data[i];
-            PS_ASSERT_PTR_NON_NULL(tmpSrc, false); // just skip this one?
-            PS_ASSERT_PTR_NON_NULL(tmpSrc->moments, false); // just skip this one?
-
-            // Sx,Sy are limited at 0.  a peak at 0,0 is artificial
-            if ((fabs(tmpSrc->moments->Sx) < FLT_EPSILON) && (fabs(tmpSrc->moments->Sy) < FLT_EPSILON)) {
-                continue;
-            }
-
-            // for the moment, force splane dimensions to be 10x10 image pix
-            binX = tmpSrc->moments->Sx/SCALE;
-            if (binX < 0)
-                continue;
-            if (binX >= splane->numCols)
-                continue;
-
-            binY = tmpSrc->moments->Sy/SCALE;
-            if (binY < 0)
-                continue;
-            if (binY >= splane->numRows)
-                continue;
-
-            splane->data.F32[binY][binX] += 1.0;
-        }
-
-        // find the peak in this image
-        stats = psStatsAlloc (PS_STAT_MAX);
-        stats = psImageStats (stats, splane, NULL, 0);
-        peaks = pmFindImagePeaks (splane, stats[0].max / 2);
-        psTrace (".pmObjects.pmSourceRoughClass", 2, "clump threshold is %f\n", stats[0].max/2);
-
-    }
-
-    // measure statistics on Sx, Sy if Sx, Sy within range of clump
-    {
-        pmPeak *clump;
-        psF32 minSx, maxSx;
-        psF32 minSy, maxSy;
-        psVector *tmpSx = NULL;
-        psVector *tmpSy = NULL;
-        psStats *stats  = NULL;
-
-        // XXX EAM : this lets us takes the single highest peak
-        psArraySort (peaks, pmComparePeakDescend);
-        clump = peaks->data[0];
-        psTrace (".pmObjects.pmSourceRoughClass", 2, "clump is at %d, %d\n", clump->x, clump->y);
-
-        // define section window for clump
-        minSx = clump->x * SCALE - 0.2;
-        maxSx = clump->x * SCALE + 0.2;
-        minSy = clump->y * SCALE - 0.2;
-        maxSy = clump->y * SCALE + 0.2;
-
-        tmpSx = psVectorAlloc (sources->n, PS_TYPE_F32);
-        tmpSy = psVectorAlloc (sources->n, PS_TYPE_F32);
-        tmpSx->n = 0;
-        tmpSy->n = 0;
-
-        // XXX clip sources based on flux?
-        // create vectors with Sx, Sy values in window
-        for (psS32 i = 0 ; i < sources->n ; i++)
-        {
-            pmSource *tmpSrc = (pmSource *) sources->data[i];
-
-            if (tmpSrc->moments->Sx < minSx)
-                continue;
-            if (tmpSrc->moments->Sx > maxSx)
-                continue;
-            if (tmpSrc->moments->Sy < minSy)
-                continue;
-            if (tmpSrc->moments->Sy > maxSy)
-                continue;
-            tmpSx->data.F32[tmpSx->n] = tmpSrc->moments->Sx;
-            tmpSy->data.F32[tmpSy->n] = tmpSrc->moments->Sy;
-            tmpSx->n++;
-            tmpSy->n++;
-            if (tmpSx->n == tmpSx->nalloc) {
-                psVectorRealloc (tmpSx, tmpSx->nalloc + 100);
-                psVectorRealloc (tmpSy, tmpSy->nalloc + 100);
-            }
-        }
-
-        // measures stats of Sx, Sy
-        stats = psStatsAlloc (PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV);
-
-        stats = psVectorStats (stats, tmpSx, NULL, NULL, 0);
-        clumpX  = stats->clippedMean;
-        clumpDX = stats->clippedStdev;
-
-        stats = psVectorStats (stats, tmpSy, NULL, NULL, 0);
-        clumpY  = stats->clippedMean;
-        clumpDY = stats->clippedStdev;
-
-        psTrace (".pmObjects.pmSourceRoughClass", 2, "clump  X,  Y: %f, %f\n", clumpX, clumpY);
-        psTrace (".pmObjects.pmSourceRoughClass", 2, "clump DX, DY: %f, %f\n", clumpDX, clumpDY);
-        // these values should be pushed on the metadata somewhere
-    }
-
-    int Nsat   = 0;
-    int Ngal   = 0;
-    int Nfaint = 0;
-    int Nstar  = 0;
-    int Npsf   = 0;
-    int Ncr    = 0;
-    psVector *starsn = psVectorAlloc (sources->n, PS_TYPE_F32);
-    starsn->n = 0;
-
-    // XXX allow clump size to be scaled relative to sigmas?
-    // make rough IDs based on clumpX,Y,DX,DY
-    for (psS32 i = 0 ; i < sources->n ; i++) {
-
-        pmSource *tmpSrc = (pmSource *) sources->data[i];
-
-        tmpSrc->peak->class = 0;
-
-        psF32 sigX = tmpSrc->moments->Sx;
-        psF32 sigY = tmpSrc->moments->Sy;
-
-        // check return status value (do these exist?)
-        bool status;
-        psF32 RDNOISE  = psMetadataLookupF32 (&status, metadata, "RDNOISE");
-        psF32 GAIN     = psMetadataLookupF32 (&status, metadata, "GAIN");
-        psF32 SATURATE = psMetadataLookupF32 (&status, metadata, "SATURATE");
-
-        psF32 PSF_SN_LIM   = psMetadataLookupF32 (&status, metadata, "PSF_SN_LIM");
-        psF32 FAINT_SN_LIM = psMetadataLookupF32 (&status, metadata, "FAINT_SN_LIM");
-
-        // saturated object (star or single pixel not distinguished)
-        if (tmpSrc->moments->Peak > SATURATE) {
-            tmpSrc->type |= PS_SOURCE_SATURATED;
-            Nsat ++;
-            continue;
-        }
-
-        // too small to be stellar
-        if ((sigX < (clumpX - clumpDX)) || (sigY < (clumpY - clumpDY))) {
-            tmpSrc->type |= PS_SOURCE_DEFECT;
-            Ncr ++;
-            continue;
-        }
-
-        // possible galaxy
-        if ((sigX > (clumpX + clumpDX)) || (sigY > (clumpY + clumpDY))) {
-            tmpSrc->type |= PS_SOURCE_GALAXY;
-            Ngal ++;
-            continue;
-        }
-
-        // the rest are probable stellar objects
-        psF32 S  = tmpSrc->moments->Sum;
-        psF32 A  = M_PI * sigX * sigY;
-        psF32 B  = tmpSrc->moments->Sky;
-        psF32 RT = sqrtf(S + (A * B) + (A * PS_SQR(RDNOISE) / sqrtf(GAIN)));
-        psF32 SN = (S * sqrtf(GAIN) / RT);
-
-        starsn->data.F32[starsn->n] = SN;
-        starsn->n ++;
-        Nstar ++;
-
-        // faint star
-        if (SN < FAINT_SN_LIM) {
-            tmpSrc->type |= PS_SOURCE_FAINTSTAR;
-            Nfaint ++;
-            continue;
-        }
-
-        // PSF star
-        if (SN > PSF_SN_LIM) {
-            tmpSrc->type |= PS_SOURCE_PSFSTAR;
-            Npsf ++;
-            continue;
-        }
-
-        // random type of star
-        tmpSrc->type |= PS_SOURCE_OTHER;
-    }
-
-    {
-        psStats *stats  = NULL;
-        stats = psStatsAlloc (PS_STAT_MIN | PS_STAT_MAX);
-        stats = psVectorStats (stats, starsn, NULL, NULL, 0);
-        psLogMsg ("pmObjects", 3, "SN range: %f - %f\n", stats[0].min, stats[0].max);
-    }
-
-    psTrace (".pmObjects.pmSourceRoughClass", 2, "Nstar:  %3d\n", Nstar);
-    psTrace (".pmObjects.pmSourceRoughClass", 2, "Npsf:   %3d\n", Npsf);
-    psTrace (".pmObjects.pmSourceRoughClass", 2, "Nfaint: %3d\n", Nfaint);
-    psTrace (".pmObjects.pmSourceRoughClass", 2, "Ngal:   %3d\n", Ngal);
-    psTrace (".pmObjects.pmSourceRoughClass", 2, "Nsat:   %3d\n", Nsat);
-    psTrace (".pmObjects.pmSourceRoughClass", 2, "Ncr:    %3d\n", Ncr);
-
-    return(rc);
-}
-
-/******************************************************************************
-pmSourceSetPixelsCircle(source, image, radius)
- 
-XXX: Why boolean output?
- 
-XXX: Why are we checking source->moments for NULL?  Should the circle be
-     centered on the centroid or the peak?
- 
-XXX: The circle will have a diameter of (1+radius).  This is different from
-     the pmSourceSetLocal() function.
-*****************************************************************************/
-bool pmSourceSetPixelsCircle(pmSource *source,
-                             const psImage *image,
-                             psF32 radius)
-{
-    PS_ASSERT_IMAGE_NON_NULL(image, false);
-    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, false);
-    PS_ASSERT_PTR_NON_NULL(source, false);
-    PS_ASSERT_PTR_NON_NULL(source->moments, false);
-    // PS_ASSERT_PTR_NON_NULL(source->peak, false);
-    PS_FLOAT_COMPARE(0.0, radius, false);
-
-    //
-    // We define variables for code readability.
-    //
-    // XXX: Since the peak->xy coords are in image, not subImage coords,
-    // these variables should be renamed for clarity (imageCenterRow, etc).
-    //
-    psS32 radiusS32 = (psS32) radius;
-    psS32 SubImageCenterRow = source->peak->y;
-    psS32 SubImageCenterCol = source->peak->x;
-    // XXX EAM : for the circle to stay on the image
-    psS32 SubImageStartRow  = PS_MAX (0, SubImageCenterRow - radiusS32);
-    psS32 SubImageEndRow    = PS_MIN (image->numRows - 1, SubImageCenterRow + radiusS32);
-    psS32 SubImageStartCol  = PS_MAX (0, SubImageCenterCol - radiusS32);
-    psS32 SubImageEndCol    = PS_MIN (image->numCols - 1, SubImageCenterCol + radiusS32);
-
-    // XXX EAM : this should not be needed: we can never hit this error
-    # if (1)
-
-        if (SubImageStartRow < 0) {
-            psError(PS_ERR_UNKNOWN, true, "Sub image startRow is outside image boundaries (%d).\n",
-                    SubImageStartRow);
-            return(false);
-        }
-    if (SubImageEndRow >= image->numRows) {
-        psError(PS_ERR_UNKNOWN, true, "Sub image endRow is outside image boundaries (%d).\n",
-                SubImageEndRow);
-        return(false);
-    }
-    if (SubImageStartCol < 0) {
-        psError(PS_ERR_UNKNOWN, true, "Sub image startCol is outside image boundaries (%d).\n",
-                SubImageStartCol);
-        return(false);
-    }
-    if (SubImageEndCol >= image->numCols) {
-        psError(PS_ERR_UNKNOWN, true, "Sub image endCol is outside image boundaries (%d).\n",
-                SubImageEndCol);
-        return(false);
-    }
-    # endif
-
-    // XXX: Must recycle image.
-    // XXX EAM: this message reflects a programming error we know about.
-    //          i am setting it to a trace message which we can take out
-    if (source->pixels != NULL) {
-        psTrace (".psModule.pmObjects.pmSourceSetPixelsCircle", 4,
-                 "WARNING: pmSourceSetPixelsCircle(): image->pixels not NULL.  Freeing and reallocating.\n");
-        psFree(source->pixels);
-    }
-    // XXX: Must fix this.  psImageSubset() has different parameters in latest CVS.
-    //    source->pixels = psImageSubset((psImage *) image,
-    //                                   SubImageStartCol,
-    //                                   SubImageStartRow,
-    //                                   SubImageEndCol,
-    //                                   SubImageEndRow);
-
-    // XXX: Must recycle image.
-    if (source->mask != NULL) {
-        psFree(source->mask);
-    }
-    source->mask = psImageAlloc(source->pixels->numCols,
-                                source->pixels->numRows,
-                                PS_TYPE_F32);
-
-    //
-    // Loop through the subimage mask, initialize mask to 0 or 1.
-    // XXX EAM: valid pixels should have 0, not 1
-    for (psS32 row = 0 ; row < source->mask->numRows; row++) {
-        for (psS32 col = 0 ; col < source->mask->numCols; col++) {
-
-            if (checkRadius2((psF32) radiusS32,
-                             (psF32) radiusS32,
-                             radius,
-                             (psF32) col,
-                             (psF32) row)) {
-                source->mask->data.U8[row][col] = 0;
-            } else {
-                source->mask->data.U8[row][col] = 1;
-            }
-        }
-    }
-    return(true);
-}
-
-/******************************************************************************
-pmSourceModelGuess(source, image, model): This function allocates a new
-pmModel structure and stores it in the pmSource data structure specified in
-the argument list.  The model type is specified in the argument list.  The
-params array in that pmModel structure are allocated, and then set to the
-appropriate values.  This function returns true if everything was successful.
- 
-XXX: Many of the initial parameters are set to 0.0 since I don't know what
-the appropiate initial guesses are.
- 
-XXX: The image argument is redundant.
- 
-XXX: Many parameters are based on the src->moments structure, which is in
-image, not subImage coords.  Therefore, the calls to the model evaluation
-functions will be in image, not subImage coords.  Remember this.
- 
-XXX: The source->models member used to be allocated here.  Now I assume
-->modelPSF should be allocated
-*****************************************************************************/
-bool pmSourceModelGuess(pmSource *source,
-                        const psImage *image,
-                        pmModelType model)
-{
-    PS_ASSERT_PTR_NON_NULL(source, false);
-    PS_ASSERT_PTR_NON_NULL(source->moments, false);
-    PS_ASSERT_PTR_NON_NULL(source->peak, false);
-    PS_ASSERT_IMAGE_NON_NULL(image, false);
-    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, false);
-    if (source->modelPSF != NULL) {
-        psLogMsg(__func__, PS_LOG_WARN, "WARNING: source->modelPSF was non-NULL; calling psFree(source->modelPSF).\n");
-        psFree(source->modelPSF);
-    }
-    if (!((model == PS_MODEL_GAUSS) ||
-            (model == PS_MODEL_PGAUSS) ||
-            (model == PS_MODEL_WAUSS) ||
-            (model == PS_MODEL_TWIST_GAUSS) ||
-            (model == PS_MODEL_SERSIC) ||
-            (model == PS_MODEL_SERSIC_CORE))) {
-        psError(PS_ERR_UNKNOWN, true, "Undefined psModelType");
-        return(false);
-    }
-
-    source->modelPSF = pmModelAlloc(model);
-
-    psVector *params = source->modelPSF->params;
-
-    switch (model) {
-    case PS_MODEL_GAUSS:
-        params->data.F32[0] = source->moments->Sky;
-        params->data.F32[1] = source->peak->counts - source->moments->Sky;
-        params->data.F32[2] = source->moments->x;
-        params->data.F32[3] = source->moments->y;
-        params->data.F32[4] = sqrt(2.0) / source->moments->Sx;
-        params->data.F32[5] = sqrt(2.0) / source->moments->Sy;
-        params->data.F32[6] = source->moments->Sxy;
-        return(true);
-
-    case PS_MODEL_PGAUSS:
-        params->data.F32[0] = source->moments->Sky;
-        params->data.F32[1] = source->peak->counts - source->moments->Sky;
-        params->data.F32[2] = source->moments->x;
-        params->data.F32[3] = source->moments->y;
-        params->data.F32[4] = sqrt(2.0) / source->moments->Sx;
-        params->data.F32[5] = sqrt(2.0) / source->moments->Sy;
-        params->data.F32[6] = source->moments->Sxy;
-        return(true);
-
-    case PS_MODEL_WAUSS:
-        params->data.F32[0] = source->moments->Sky;
-        params->data.F32[1] = source->peak->counts - source->moments->Sky;
-        params->data.F32[2] = source->moments->x;
-        params->data.F32[3] = source->moments->y;
-        params->data.F32[4] = sqrt(2.0) / source->moments->Sx;
-        params->data.F32[5] = sqrt(2.0) / source->moments->Sy;
-        params->data.F32[6] = source->moments->Sxy;
-        // XXX: What are these?
-        // source->modelPSF->params[7] = B2;
-        // source->modelPSF->params[8] = B3;
-        return(true);
-
-        // XXX EAM : I might drop this model (or rather, replace it)
-    case PS_MODEL_TWIST_GAUSS:
-        params->data.F32[0] = source->moments->Sky;
-        params->data.F32[1] = source->peak->counts - source->moments->Sky;
-        params->data.F32[2] = source->moments->x;
-        params->data.F32[3] = source->moments->y;
-        // XXX: What are these?
-        // params->data.F32[4] = SxInner;
-        // params->data.F32[5] = SyInner;
-        // params->data.F32[6] = SxyInner;
-        // params->data.F32[7] = SxOuter;
-        // params->data.F32[8] = SyOuter;
-        // params->data.F32[9] = SxyOuter;
-        // params->data.F32[10] = N;
-        return(true);
-
-    case PS_MODEL_SERSIC:
-        params->data.F32[0] = source->moments->Sky;
-        params->data.F32[1] = source->peak->counts - source->moments->Sky;
-        params->data.F32[2] = source->moments->x;
-        params->data.F32[3] = source->moments->y;
-        params->data.F32[4] = sqrt(2.0) / source->moments->Sx;
-        params->data.F32[5] = sqrt(2.0) / source->moments->Sy;
-        params->data.F32[6] = source->moments->Sxy;
-        // XXX: What are these?
-        //params->data.F32[7] = Nexp;
-        return(true);
-
-    case PS_MODEL_SERSIC_CORE:
-        params->data.F32[0] = source->moments->Sky;
-        params->data.F32[1] = source->peak->counts - source->moments->Sky;
-        params->data.F32[2] = source->moments->x;
-        params->data.F32[3] = source->moments->y;
-        // XXX: What are these?
-        // params->data.F32[4] SxInner;
-        // params->data.F32[5] SyInner;
-        // params->data.F32[6] SxyInner;
-        // params->data.F32[7] Zd;
-        // params->data.F32[8] SxOuter;
-        // params->data.F32[9] SyOuter;
-        // params->data.F32[10] = SxyOuter;
-        // params->data.F32[11] = Nexp;
-        return(true);
-
-    default:
-        psError(PS_ERR_UNKNOWN, true, "Undefined psModelType");
-        return(false);
-    }
-}
-
-/******************************************************************************
-evalModel(source, level, row): a private function which evaluates the
-source->modelPSF function at the specified coords.  The coords are subImage, not
-image coords.
- 
-NOTE: The coords are in subImage source->pixel coords, not image coords.
- 
-XXX: reverse order of row,col args?
- 
-XXX: rename all coords in this file such that their name defines whether
-the coords is in subImage or image space.
- 
-XXX: This should probably be a public pmModules function.
- 
-XXX: Use static vectors for x.
- 
-XXX: Figure out if it's (row, col) or (col, row) for the model functions.
- 
-XXX: For a while, the first psVectorAlloc() was generating a seg fault during
-testing.  Try to reproduce that and debug.
-*****************************************************************************/
-static psF32 evalModel(pmSource *src,
-                       psU32 row,
-                       psU32 col)
-{
-    PS_ASSERT_PTR_NON_NULL(src, false);
-    PS_ASSERT_PTR_NON_NULL(src->modelPSF, false);
-    PS_ASSERT_PTR_NON_NULL(src->modelPSF->params, false);
-
-    // XXX: The following step will not be necessary if the modelPSF->params
-    // member is a psVector.  Suggest to IfA.
-
-    // XXX EAM: done: modelPSF->params is now a vector
-    psVector *params = src->modelPSF->params;
-
-    //
-    // Allocate the x coordinate structure and convert row/col to image space.
-    //
-    psVector *x = psVectorAlloc(2, PS_TYPE_F32);
-    x->data.F32[0] = (psF32) (col + src->pixels->col0);
-    x->data.F32[1] = (psF32) (row + src->pixels->row0);
-    psF32 tmpF;
-
-    switch (src->modelPSF->type) {
-    case PS_MODEL_GAUSS:
-        tmpF = pmMinLM_Gauss2D(NULL, params, x);
-        break;
-    case PS_MODEL_PGAUSS:
-        tmpF = pmMinLM_PsuedoGauss2D(NULL, params, x);
-        break;
-    case PS_MODEL_TWIST_GAUSS:
-        tmpF = pmMinLM_TwistGauss2D(NULL, params, x);
-        break;
-    case PS_MODEL_WAUSS:
-        tmpF = pmMinLM_Wauss2D(NULL, params, x);
-        break;
-    case PS_MODEL_SERSIC:
-        tmpF = pmMinLM_Sersic(NULL, params, x);
-        break;
-    case PS_MODEL_SERSIC_CORE:
-        tmpF = pmMinLM_SersicCore(NULL, params, x);
-        break;
-    default:
-        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
-        return(NAN);
-    }
-    psFree(x);
-    return(tmpF);
-}
-
-/******************************************************************************
-findValue(source, level, row, col, dir): a private function which determines
-the column coordinate of the model function which has the value "level".  If
-dir equals 0, then you loop leftwards from the peak pixel, otherwise,
-rightwards.
- 
-XXX: reverse order of row,col args?
- 
-XXX: Input row/col are in image coords.
- 
-XXX: The result is returned in image coords.
-*****************************************************************************/
-static psF32 findValue(pmSource *source,
-                       psF32 level,
-                       psU32 row,
-                       psU32 col,
-                       psU32 dir)
-{
-    //
-    // Convert coords to subImage space.
-    //
-    psU32 subRow = row - source->pixels->row0;
-    psU32 subCol = col - source->pixels->col0;
-
-    // Ensure that the starting column is allowable.
-    if (!((0 <= subCol) && (subCol < source->pixels->numCols))) {
-        psError(PS_ERR_UNKNOWN, true, "Starting column outside subImage range");
-        return(NAN);
-    }
-    if (!((0 <= subRow) && (subRow < source->pixels->numRows))) {
-        psError(PS_ERR_UNKNOWN, true, "Starting row outside subImage range");
-        return(NAN);
-    }
-
-    psF32 oldValue = evalModel(source, subRow, subCol);
-    if (oldValue == level) {
-        return(((psF32) (subCol + source->pixels->col0)));
-    }
-
-    //
-    // We define variables incr and lastColumn so that we can use the same loop
-    // whether we are stepping leftwards, or rightwards.
-    //
-    psS32 incr;
-    psS32 lastColumn;
-    if (dir == 0) {
-        incr = -1;
-        lastColumn = -1;
-    } else {
-        incr = 1;
-        lastColumn = source->pixels->numCols;
-    }
-    subCol+=incr;
-
-    while (subCol != lastColumn) {
-        psF32 newValue = evalModel(source, subRow, subCol);
-        if (oldValue == level) {
-            return((psF32) (subCol + source->pixels->col0));
-        }
-
-        if ((newValue <= level) && (level <= oldValue)) {
-            // This is simple linear interpolation.
-            return( ((psF32) (subCol + source->pixels->col0)) + ((psF32) incr) * ((level - newValue) / (oldValue - newValue)) );
-        }
-
-        if ((oldValue <= level) && (level <= newValue)) {
-            // This is simple linear interpolation.
-            return( ((psF32) (subCol + source->pixels->col0)) + ((psF32) incr) * ((level - oldValue) / (newValue - oldValue)) );
-        }
-
-        subCol+=incr;
-    }
-
-    return(NAN);
-}
-
-/******************************************************************************
-pmSourceContour(src, img, level, mode): For an input subImage, and model, this
-routine returns a psArray of coordinates that evaluate to the specified level.
- 
-XXX: Probably should remove the "image" argument.
-XXX: What type should the output coordinate vectors consist of?  col,row?
-XXX: Why a pmArray output?
-XXX: doex x,y correspond with col,row or row/col?
-XXX: What is mode?
-XXX: The top, bottom of the contour is not correctly determined.
-*****************************************************************************/
-psArray *pmSourceContour(pmSource *source,
-                         const psImage *image,
-                         psF32 level,
-                         pmContourType mode)
-{
-    PS_ASSERT_PTR_NON_NULL(source, false);
-    PS_ASSERT_PTR_NON_NULL(image, false);
-    PS_ASSERT_PTR_NON_NULL(source->moments, false);
-    PS_ASSERT_PTR_NON_NULL(source->peak, false);
-    PS_ASSERT_PTR_NON_NULL(source->pixels, false);
-    PS_ASSERT_PTR_NON_NULL(source->modelPSF, false);
-
-    //
-    // Allocate data for x/y pairs.
-    //
-    psVector *xVec = psVectorAlloc(2 * source->pixels->numRows, PS_TYPE_F32);
-    psVector *yVec = psVectorAlloc(2 * source->pixels->numRows, PS_TYPE_F32);
-
-    //
-    // Start at the row with peak pixel, then decrement.
-    //
-    psS32 col = source->peak->x;
-    for (psS32 row = source->peak->y; row>= 0 ; row--) {
-        // XXX: yVec contain no real information.  Do we really need it?
-        yVec->data.F32[row] = (psF32) (source->pixels->row0 + row);
-        yVec->data.F32[row+yVec->n] = (psF32) (source->pixels->row0 + row);
-
-        // Starting at peak pixel, search leftwards for the column intercept.
-        psF32 leftIntercept = findValue(source, level, row, col, 0);
-        if (isnan(leftIntercept)) {
-            psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
-            psFree(xVec);
-            psFree(yVec);
-            return(NULL);
-            //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
-        }
-        xVec->data.F32[row] = ((psF32) source->pixels->col0) + leftIntercept;
-
-        // Starting at peak pixel, search rightwards for the column intercept.
-
-        psF32 rightIntercept = findValue(source, level, row, col, 1);
-        if (isnan(rightIntercept)) {
-            psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
-            psFree(xVec);
-            psFree(yVec);
-            return(NULL);
-            //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
-        }
-        //printf("The intercepts are (%.2f, %.2f)\n", leftIntercept, rightIntercept);
-        xVec->data.F32[row+xVec->n] = ((psF32) source->pixels->col0) + rightIntercept;
-
-        // Set starting column for next row
-        col = (psS32) ((leftIntercept + rightIntercept) / 2.0);
-    }
-    //
-    // Start at the row (+1) with peak pixel, then increment.
-    //
-    col = source->peak->x;
-    for (psS32 row = 1 + source->peak->y; row < source->pixels->numRows ; row++) {
-        // XXX: yVec contain no real information.  Do we really need it?
-        yVec->data.F32[row] = (psF32) (source->pixels->row0 + row);
-        yVec->data.F32[row+yVec->n] = (psF32) (source->pixels->row0 + row);
-
-        // Starting at peak pixel, search leftwards for the column intercept.
-        psF32 leftIntercept = findValue(source, level, row, col, 0);
-        if (isnan(leftIntercept)) {
-            psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
-            psFree(xVec);
-            psFree(yVec);
-            return(NULL);
-            //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
-        }
-        xVec->data.F32[row] = ((psF32) source->pixels->col0) + leftIntercept;
-
-        // Starting at peak pixel, search rightwards for the column intercept.
-        psF32 rightIntercept = findValue(source, level, row, col, 1);
-        if (isnan(rightIntercept)) {
-            psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
-            psFree(xVec);
-            psFree(yVec);
-            return(NULL);
-            //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
-        }
-        xVec->data.F32[row+xVec->n] = ((psF32) source->pixels->col0) + rightIntercept;
-
-        // Set starting column for next row
-        col = (psS32) ((leftIntercept + rightIntercept) / 2.0);
-    }
-
-    //
-    // Allocate an array for result, store coord vectors there.
-    //
-    psArray *tmpArray = psArrayAlloc(2);
-    tmpArray->data[0] = (psPtr *) yVec;
-    tmpArray->data[1] = (psPtr *) xVec;
-    return(tmpArray);
-}
-
-#if 0
-static psVector *minLM_Gauss2D_Vec(psImage *deriv, psVector *params, psArray *x);
-static psVector *minLM_PsuedoGauss2D_Vec(psImage *deriv, psVector *params, psArray *x);
-static psVector *minLM_Wauss2D_Vec(psImage *deriv, psVector *params, psArray *x);
-static psVector *minLM_TwistGauss2D_Vec(psImage *deriv, psVector *params, psArray *x);
-static psVector *minLM_Sersic_Vec(psImage *deriv, psVector *params, psArray *x);
-static psVector *minLM_SersicCore_Vec(psImage *deriv, psVector *params, psArray *x);
-#endif
-
-// XXX EAM : these are better starting values, but should be available from metadata?
-#define PM_SOURCE_FIT_MODEL_NUM_ITERATIONS 20
-#define PM_SOURCE_FIT_MODEL_TOLERANCE 0.1
-/******************************************************************************
-pmSourceFitModel(source, image): must create the appropiate arguments to the
-LM minimization routines for the various p_pmMinLM_XXXXXX_Vec() functions.
- 
-XXX: should there be a mask value?
-XXX: Probably should remove the "image" argument.
-*****************************************************************************/
-bool pmSourceFitModel(pmSource *source,
-                      const psImage *image)
-{
-    PS_ASSERT_PTR_NON_NULL(source, false);
-    PS_ASSERT_PTR_NON_NULL(source->moments, false);
-    PS_ASSERT_PTR_NON_NULL(source->peak, false);
-    PS_ASSERT_PTR_NON_NULL(source->pixels, false);
-    PS_ASSERT_PTR_NON_NULL(source->modelPSF, false);
-    PS_ASSERT_IMAGE_NON_NULL(image, false);
-    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, false);
-    psBool rc;
-
-    // find the number of valid pixels
-    // XXX EAM : this loop and the loop below could just be one pass
-    //           using the psArrayAdd and psVectorExtend functions
-    psS32 count = 0;
-    for (psS32 i = 0; i < source->pixels->numRows; i++) {
-        for (psS32 j = 0; j < source->pixels->numCols; j++) {
-            if (source->mask->data.U8[i][j] == 0) {
-                count++;
-            }
-        }
-    }
-
-    // construct the coordinate and value entries
-    psArray *x = psArrayAlloc(count);
-    psVector *y = psVectorAlloc(count, PS_TYPE_F32);
-    psVector *yErr = psVectorAlloc(count, PS_TYPE_F32);
-    psS32 tmpCnt = 0;
-    for (psS32 i = 0; i < source->pixels->numRows; i++) {
-        for (psS32 j = 0; j < source->pixels->numCols; j++) {
-            if (source->mask->data.U8[i][j] == 0) {
-                psVector *coord = psVectorAlloc(2, PS_TYPE_F32);
-                // XXX: Convert i/j to image space:
-                // XXX EAM: coord order is (x,y) == (col,row)
-                coord->data.F32[0] = (psF32) (j + source->pixels->col0);
-                coord->data.F32[1] = (psF32) (i + source->pixels->row0);
-                x->data[tmpCnt] = (psPtr *) coord;
-                y->data.F32[tmpCnt] = source->pixels->data.F32[i][j];
-
-                // XXX EAM : this is approximate: need to apply the gain and rdnoise
-                yErr->data.F32[tmpCnt] = sqrt(PS_MAX(1, source->pixels->data.F32[i][j]));
-                tmpCnt++;
-            }
-        }
-    }
-
-    psMinimization *myMin = psMinimizationAlloc(PM_SOURCE_FIT_MODEL_NUM_ITERATIONS,
-                            PM_SOURCE_FIT_MODEL_TOLERANCE);
-
-    psVector *params = source->modelPSF->params;
-
-    switch (source->modelPSF->type) {
-    case PS_MODEL_GAUSS:
-        rc = psMinimizeLMChi2(myMin, NULL, params, NULL, x, y, yErr, pmMinLM_Gauss2D);
-        break;
-    case PS_MODEL_PGAUSS:
-        rc = psMinimizeLMChi2(myMin, NULL, params, NULL, x, y, yErr, pmMinLM_PsuedoGauss2D);
-        break;
-    case PS_MODEL_TWIST_GAUSS:
-        rc = psMinimizeLMChi2(myMin, NULL, params, NULL, x, y, yErr, pmMinLM_Wauss2D);
-        break;
-    case PS_MODEL_WAUSS:
-        rc = psMinimizeLMChi2(myMin, NULL, params, NULL, x, y, yErr, pmMinLM_TwistGauss2D);
-        break;
-    case PS_MODEL_SERSIC:
-        rc = psMinimizeLMChi2(myMin, NULL, params, NULL, x, y, yErr, pmMinLM_Sersic);
-        break;
-    case PS_MODEL_SERSIC_CORE:
-        rc = psMinimizeLMChi2(myMin, NULL, params, NULL, x, y, yErr, pmMinLM_SersicCore);
-        break;
-    default:
-        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
-        rc = false;
-    }
-    // XXX EAM: we need to do something (give an error?) if rc is false
-    // XXX EAM: save the resulting chisq, nDOF, nIter
-    source->modelPSF->chisq = myMin->value;
-    source->modelPSF->nDOF  = y->n - params->n;
-    source->modelPSF->nIter = myMin->iter;
-
-    psFree(x);
-    psFree(y);
-    psFree(myMin);
-    return(rc);
-}
-
-static bool sourceAddOrSubModel(psImage *image,
-                                pmSource *src,
-                                bool center,
-                                psS32 flag)
-{
-    PS_ASSERT_PTR_NON_NULL(src, false);
-    PS_ASSERT_PTR_NON_NULL(src->moments, false);
-    PS_ASSERT_PTR_NON_NULL(src->peak, false);
-    PS_ASSERT_PTR_NON_NULL(src->pixels, false);
-    PS_ASSERT_PTR_NON_NULL(src->modelPSF, false);
-    PS_ASSERT_IMAGE_NON_NULL(image, false);
-    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, false);
-
-    psVector *x = psVectorAlloc(2, PS_TYPE_F32);
-    psVector *params = src->modelPSF->params;
-
-    for (psS32 i = 0; i < src->pixels->numRows; i++) {
-        for (psS32 j = 0; j < src->pixels->numCols; j++) {
-            psF32 pixelValue;
-            // XXX: Should you be adding the pixels for the entire subImage,
-            // or a radius of pixels around it?
-
-            // Convert i/j to imace coord space:
-            // XXX: Make sure you have col/row order correct.
-            psS32 imageRow = i + src->pixels->row0;
-            psS32 imageCol = j + src->pixels->col0;
-
-            x->data.F32[0] = (float) imageCol;
-            x->data.F32[1] = (float) imageRow;
-            switch (src->modelPSF->type) {
-            case PS_MODEL_GAUSS:
-                pixelValue = pmMinLM_Gauss2D(NULL, params, x);
-                break;
-            case PS_MODEL_PGAUSS:
-                pixelValue = pmMinLM_PsuedoGauss2D(NULL, params, x);
-                break;
-            case PS_MODEL_TWIST_GAUSS:
-                pixelValue = pmMinLM_TwistGauss2D(NULL, params, x);
-                break;
-            case PS_MODEL_WAUSS:
-                pixelValue = pmMinLM_Wauss2D(NULL, params, x);
-                break;
-            case PS_MODEL_SERSIC:
-                pixelValue = pmMinLM_Sersic(NULL, params, x);
-                break;
-            case PS_MODEL_SERSIC_CORE:
-                pixelValue = pmMinLM_SersicCore(NULL, params, x);
-                break;
-            default:
-                psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
-                psFree(x);
-                return(false);
-            }
-            if (flag == 1) {
-                pixelValue = -pixelValue;
-            }
-
-            // XXX: Must figure out how to calculate the image coordinates and
-            // how to use the boolean "center" flag.
-
-            image->data.F32[imageRow][imageCol]+= pixelValue;
-        }
-    }
-    psFree(x);
-    return(true);
-}
-
-
-
-/******************************************************************************
- *****************************************************************************/
-bool pmSourceAddModel(psImage *image,
-                      pmSource *src,
-                      bool center)
-{
-    return(sourceAddOrSubModel(image, src, center, 0));
-}
-
-/******************************************************************************
- *****************************************************************************/
-bool pmSourceSubModel(psImage *image,
-                      pmSource *src,
-                      bool center)
-{
-    return(sourceAddOrSubModel(image, src, center, 1));
-}
-
-
-// XXX: Put this is psConstants.h
-#define PS_VECTOR_CHECK_SIZE(VEC1, N, RVAL) \
-if (VEC1->n != N) { \
-    psError(PS_ERR_BAD_PARAMETER_SIZE, true, \
-            "psVector %s has size %d, should be %d.", \
-            #VEC1, VEC1->n, N); \
-    return(RVAL); \
-}
-
-
-/**
-   all of these object representation functions have the same form : func(*deriv, *params, *x)
- 
-   the argument "x" contains a single "x,y" coordinate pair.  The function computes the object
-   model, based on the parameters in "params" at the x,y point specified by *x, and returns the value.
-   The derivatives are also caculated and returned in the "deriv" argument.  parameter error checking is
-   skipped because speed is most important.
-**/
-
-/******************************************************************************
-    params->data.F32[0] = So;
-    params->data.F32[1] = Zo;
-    params->data.F32[2] = Xo;
-    params->data.F32[3] = Yo;
-    params->data.F32[4] = sqrt(2.0) / SigmaX;
-    params->data.F32[5] = sqrt(2.0) / SigmaY;
-    params->data.F32[6] = Sxy;
-*****************************************************************************/
-float pmMinLM_Gauss2D(
-    psVector *deriv,
-    const psVector *params,
-    const psVector *x)
-{
-    PS_ASSERT_VECTOR_NON_NULL(params, NAN);
-    PS_ASSERT_VECTOR_NON_NULL(x, NAN);
-    psF32 X  = x->data.F32[0] - params->data.F32[2];
-    psF32 Y  = x->data.F32[1] - params->data.F32[3];
-    psF32 px = params->data.F32[4]*X;
-    psF32 py = params->data.F32[5]*Y;
-    psF32 z  = 0.5*PS_SQR(px) + 0.5*PS_SQR(py) + params->data.F32[6]*X*Y;
-    psF32 r  = exp(-z);
-    psF32 q  = params->data.F32[1]*r;
-    psF32 f  = q + params->data.F32[0];
-
-    if (deriv != NULL) {
-        deriv->data.F32[0] = +1.0;
-        deriv->data.F32[1] = +r;
-        deriv->data.F32[2] = q*(2*px*params->data.F32[4] + params->data.F32[6]*Y);
-        deriv->data.F32[3] = q*(2*py*params->data.F32[5] + params->data.F32[6]*X);
-        deriv->data.F32[4] = -2.0*q*px*X;
-        deriv->data.F32[5] = -2.0*q*py*Y;
-        deriv->data.F32[6] = -q*X*Y;
-    }
-    return(f);
-}
-
-/******************************************************************************
-    params->data.F32[0] = So;
-    params->data.F32[1] = Zo;
-    params->data.F32[2] = Xo;
-    params->data.F32[3] = Yo;
-    params->data.F32[4] = sqrt(2) / SigmaX;
-    params->data.F32[5] = sqrt(2) / SigmaY;
-    params->data.F32[6] = Sxy;
-*****************************************************************************/
-float pmMinLM_PsuedoGauss2D(
-    psVector *deriv,
-    const psVector *params,
-    const psVector *x)
-{
-    PS_ASSERT_VECTOR_NON_NULL(params, NAN);
-    PS_ASSERT_VECTOR_NON_NULL(x, NAN);
-    psF32 X  = x->data.F32[0] - params->data.F32[2];
-    psF32 Y  = x->data.F32[1] - params->data.F32[3];
-    psF32 px = params->data.F32[4]*X;
-    psF32 py = params->data.F32[5]*Y;
-    psF32 z  = 0.5*PS_SQR(px) + 0.5*PS_SQR(py) + params->data.F32[6]*X*Y;
-    psF32 t  = 1 + z + 0.5*z*z;
-    psF32 r  = 1.0 / (t*(1 + z/3)); /* exp (-Z) */
-    psF32 f  = params->data.F32[1]*r + params->data.F32[0];
-
-    if (deriv != NULL) {
-        // note difference from a pure gaussian: q = params->data.F32[1]*r
-        psF32 q = params->data.F32[1]*r*r*t;
-        deriv->data.F32[0] = +1.0;
-        deriv->data.F32[1] = +r;
-        deriv->data.F32[2] = q*(2.0*px*params->data.F32[4] + params->data.F32[6]*Y);
-        deriv->data.F32[3] = q*(2.0*py*params->data.F32[5] + params->data.F32[6]*X);
-        deriv->data.F32[4] = -2.0*q*px*X;
-        deriv->data.F32[5] = -2.0*q*py*Y;
-        deriv->data.F32[6] = -q*X*Y;
-    }
-    return(f);
-}
-
-/******************************************************************************
-    params->data.F32[0] = So;
-    params->data.F32[1] = Zo;
-    params->data.F32[2] = Xo;
-    params->data.F32[3] = Yo;
-    params->data.F32[4] = Sx;
-    params->data.F32[5] = Sy;
-    params->data.F32[6] = Sxy;
-    params->data.F32[7] = B2;
-    params->data.F32[8] = B3;
-*****************************************************************************/
-float pmMinLM_Wauss2D(
-    psVector *deriv,
-    const psVector *params,
-    const psVector *x)
-{
-    PS_ASSERT_VECTOR_NON_NULL(params, NAN);
-    PS_ASSERT_VECTOR_NON_NULL(x, NAN);
-    psF32 X = x->data.F32[0] - params->data.F32[2];
-    psF32 Y = x->data.F32[1] - params->data.F32[2];
-    psF32 px = params->data.F32[4]*X;
-    psF32 py = params->data.F32[5]*Y;
-    psF32 z = 0.5*PS_SQR(px) + 0.5*PS_SQR(py) + params->data.F32[6]*X*Y;
-    psF32 t = 0.5*z*z*(1.0 + params->data.F32[8]*z/3.0);
-    psF32 r = 1.0 / (1.0 + z + params->data.F32[7]*t); /* exp (-Z) */
-    psF32 f = params->data.F32[1]*r + params->data.F32[0];
-
-    if (deriv != NULL) {
-        // note difference from gaussian: q = params->data.F32[1]*r
-        psF32 q = params->data.F32[1]*r*r*(1.0 + params->data.F32[7]*z*(1.0 + params->data.F32[8]*z/2.0));
-        deriv->data.F32[0] = +1.0;
-        deriv->data.F32[1] = +r;
-        deriv->data.F32[2] = q*(2.0*px*params->data.F32[4] + params->data.F32[6]*Y);
-        deriv->data.F32[3] = q*(2.0*py*params->data.F32[5] + params->data.F32[6]*X);
-        deriv->data.F32[4] = -2.0*q*px*X;
-        deriv->data.F32[5] = -2.0*q*py*Y;
-        deriv->data.F32[6] = -q*X*Y;
-        deriv->data.F32[7] = -100.0*params->data.F32[1]*r*r*t;
-        deriv->data.F32[8] = -100.0*params->data.F32[1]*r*r*params->data.F32[7]*(z*z*z)/6.0;
-        // The values of 100 dampen the swing of params->data.F32[7,8] */
-    }
-    return(f);
-}
-
-// XXX: What should these be?
-#define FFACTOR 1.0
-#define FSCALE 1.0
-/******************************************************************************
-    params->data.F32[0] = So;
-    params->data.F32[1] = Zo;
-    params->data.F32[2] = Xo;
-    params->data.F32[3] = Yo;
-    params->data.F32[4] = SxInner;
-    params->data.F32[5] = SyInner;
-    params->data.F32[6] = SxyInner;
-    params->data.F32[7] = SxOuter;
-    params->data.F32[8] = SyOuter;
-    params->data.F32[9] = SxyOuter;
-    params->data.F32[10] = N;
-*****************************************************************************/
-float pmMinLM_TwistGauss2D(
-    psVector *deriv,
-    const psVector *params,
-    const psVector *x)
-{
-    PS_ASSERT_VECTOR_NON_NULL(params, NAN);
-    PS_ASSERT_VECTOR_NON_NULL(x, NAN);
-    psF32 X = x->data.F32[0] - params->data.F32[2];
-    psF32 Y = x->data.F32[1] - params->data.F32[3];
-    psF32 px1 = params->data.F32[4]*X;
-    psF32 py1 = params->data.F32[5]*Y;
-    psF32 px2 = params->data.F32[7]*X;
-    psF32 py2 = params->data.F32[8]*Y;
-    psF32 z1 = 0.5*PS_SQR(px1) + 0.5*PS_SQR(py1) + params->data.F32[4]*X*Y;
-    psF32 z2 = 0.5*PS_SQR(px2) + 0.5*PS_SQR(py2) + params->data.F32[9]*X*Y;
-    psF32 r = 1.0 / (1.0 + z1 + pow(z2,params->data.F32[10]));
-
-    psF32 f = params->data.F32[5]*r + params->data.F32[6];
-
-    if (deriv != NULL) {
-        psF32 q1 = params->data.F32[5]*PS_SQR(r);
-        psF32 q2 = params->data.F32[5]*PS_SQR(r)*params->data.F32[10]*pow(z2,(params->data.F32[10]-1.0));
-        deriv->data.F32[0] = +1.0;
-        deriv->data.F32[1] = +r;
-        deriv->data.F32[2] = q1*(2.0*px1*params->data.F32[4] + params->data.F32[6]*Y) + q2*(2*px2*params->data.F32[7] + params->data.F32[9]*Y);
-        deriv->data.F32[3] = q1*(2.0*py1*params->data.F32[5] + params->data.F32[6]*X) + q2*(2*py2*params->data.F32[8] + params->data.F32[9]*X);
-
-        // These fudge factors impede the growth of params->data.F32[4] beyond
-        // params->data.F32[7].
-        psF32 f1 = fabs(params->data.F32[7]) / fabs(params->data.F32[4]);
-        psF32 f2 = (f1 < FSCALE) ? 1.0 : FFACTOR*(f1 - FSCALE) + 1.0;
-        deriv->data.F32[4] = -2.0*q1*px1*X*f2;
-
-        // These fudge factors impede the growth of params->data.F32[5] beyond
-        // params->data.F32[8].
-        f1 = fabs(params->data.F32[8]) / fabs(params->data.F32[5]);
-        f2 = (f1 < FSCALE) ? 1.0 : FFACTOR*(f1 - FSCALE) + 1.0;
-        deriv->data.F32[5] = -2.0*q1*py1*Y*f2;
-        deriv->data.F32[6] = -q1*X*Y;
-        deriv->data.F32[7] = -2.0*q2*px2*X;
-        deriv->data.F32[8] = -2.0*q2*py2*Y;
-        deriv->data.F32[9] = -q2*X*Y;
-        deriv->data.F32[10] = -q1*log(z2);
-    }
-
-    return(f);
-}
-
-/******************************************************************************
-    float Sersic()
-    params->data.F32[0] = So;
-    params->data.F32[1] = Zo;
-    params->data.F32[2] = Xo;
-    params->data.F32[3] = Yo;
-    params->data.F32[4] = Sx;
-    params->data.F32[5] = Sy;
-    params->data.F32[6] = Sxy;
-    params->data.F32[7] = Nexp;
-*****************************************************************************/
-float pmMinLM_Sersic(
-    psVector *deriv,
-    const psVector *params,
-    const psVector *x)
-{
-    PS_ASSERT_VECTOR_NON_NULL(params, NAN);
-    PS_ASSERT_VECTOR_NON_NULL(x, NAN);
-    psError(PS_ERR_UNKNOWN, true, "This function is not implemented yet.");
-    return(0.0);
-}
-
-/******************************************************************************
-    float SersicBulge()
-    params->data.F32[0] So;
-    params->data.F32[1] Zo;
-    params->data.F32[2] Xo;
-    params->data.F32[3] Yo;
-    params->data.F32[4] SxInner;
-    params->data.F32[5] SyInner;
-    params->data.F32[6] SxyInner;
-    params->data.F32[7] Zd;
-    params->data.F32[8] SxOuter;
-    params->data.F32[9] SyOuter;
-    params->data.F32[10] = SxyOuter;
-    params->data.F32[11] = Nexp;
-*****************************************************************************/
-float pmMinLM_SersicCore(
-    psVector *deriv,
-    const psVector *params,
-    const psVector *x)
-{
-    PS_ASSERT_VECTOR_NON_NULL(params, NAN);
-    PS_ASSERT_VECTOR_NON_NULL(x, NAN);
-    psError(PS_ERR_UNKNOWN, true, "This function is not implemented yet.");
-    return(0.0);
-}
Index: unk/psModules/src/pmObjects.h
===================================================================
--- /trunk/psModules/src/pmObjects.h	(revision 5169)
+++ 	(revision )
@@ -1,397 +1,0 @@
-/** @file  pmObjects.h
- *
- *  This file will ...
- *
- *  @author GLG, MHPCC
- *
- *  @version $Revision: 1.15 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-09-11 22:25:39 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- */
-
-#if !defined(PM_OBJECTS_H)
-#define PM_OBJECTS_H
-
-#if HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include<stdio.h>
-#include<math.h>
-#include "pslib.h"
-
-/** pmPeakType
- * 
- *  A peak pixel may have several features which may be determined when the
- *  peak is found or measured. These are specified by the pmPeakType enum.
- *  PM_PEAK_LONE represents a single pixel which is higher than its 8 immediate
- *  neighbors.  The PM_PEAK_EDGE represents a peak pixel which touching the image
- *  edge. The PM_PEAK_FLAT represents a peak pixel which has more than a specific
- *  number of neighbors at the same value, within some tolarence:
- * 
- */
-typedef enum {
-    PM_PEAK_LONE,                       ///< Isolated peak.
-    PM_PEAK_EDGE,                       ///< Peak on edge.
-    PM_PEAK_FLAT,                       ///< Peak has equal-value neighbors.
-    PM_PEAK_UNDEF                       ///< Undefined.
-} pmPeakType;
-
-/** pmPeak data structure
- *  
- */
-typedef struct
-{
-    int x;                              ///< X-coordinate of peak pixel.
-    int y;                              ///< Y-coordinate of peak pixel.
-    float counts;                       ///< Value of peak pixel (above sky?).
-    pmPeakType class;                   ///< Description of peak.
-}
-pmPeak;
-
-/** pmMoments data structure
- *  
- */
-typedef struct
-{
-    float x;                            ///< X-coord of centroid.
-    float y;                            ///< Y-coord of centroid.
-    float Sx;                           ///< x-second moment.
-    float Sy;                           ///< y-second moment.
-    float Sxy;                          ///< xy cross moment.
-    float Sum;                          ///< Pixel sum above sky (background).
-    float Peak;                         ///< Peak counts above sky.
-    float Sky;                          ///< Sky level (background).
-    int nPixels;                        ///< Number of pixels used.
-}
-pmMoments;
-
-/** pmModelType enumeration
- *  
- */
-typedef enum {
-    PS_MODEL_GAUSS,                     ///< Regular 2-D Gaussian
-    PS_MODEL_PGAUSS,                    ///< Psuedo 2-D Gaussian
-    PS_MODEL_TWIST_GAUSS,               ///< 2-D Twisted Gaussian
-    PS_MODEL_WAUSS,                     ///< 2-D Waussian
-    PS_MODEL_SERSIC,                    ///< Sersic
-    PS_MODEL_SERSIC_CORE,               ///< Sersic Core
-    PS_MODEL_UNDEFINED                  ///< Undefined
-} pmModelType;
-
-/** pmModel data structure
- *  
- */
-// XXX: The SDRS has the "type" member of type psS32.
-typedef struct
-{
-    pmModelType type;                   ///< Model to be used.
-    psVector *params;                   ///< Paramater values.
-    psVector *dparams;                  ///< Parameter errors.
-    float chisq;                        ///< Fit chi-squared.
-    int nDOF;                           ///< number of degrees of freedom
-    int nIter;                          ///< number of iterations to reach min
-}
-pmModel;
-
-/** pmSourceType enumeration
- *  
- *  
- *  
- */
-typedef enum {
-    PS_SOURCE_PSFSTAR,
-    PS_SOURCE_GALAXY,
-    PS_SOURCE_DEFECT,
-    PS_SOURCE_SATURATED,
-    PS_SOURCE_SATSTAR,
-    PS_SOURCE_FAINTSTAR,
-    PS_SOURCE_BRIGHTSTAR,
-    PS_SOURCE_OTHER
-} pmSourceType;
-
-/** pmSource data structure
- *  
- *  This source has the capacity for several types of measurements. The
- *  simplest measurement of a source is the location and flux of the peak pixel
- *  associated with the source:
- *  
- */
-typedef struct
-{
-    pmPeak *peak;                       ///< Description of peak pixel.
-    psImage *pixels;                    ///< Rectangular region including object pixels.
-    psImage *mask;                      ///< Mask which marks pixels associated with objects.
-    pmMoments *moments;                 ///< Basic moments measure for the object.
-    pmModel *modelPSF;                  ///< PSF model parameters and type
-    pmModel *modelFLT;                  ///< FLT model parameters and type
-    pmSourceType type;                  ///< Best identification of object.
-}
-pmSource;
-
-/** pmPeak data structure
- *  
- *  
- *  
- */
-typedef struct
-{
-    psS32 type;                         ///< PSF Model in use
-    psArray *params;                    ///< Model parameters (psPolynomial2D)
-    psF32 chisq;                        ///< PSF goodness statistic
-    psS32 nPSFstars;                    ///< number of stars used to measure PSF
-}
-pmPSF;
-
-
-
-pmPeak *pmPeakAlloc(
-    int x,                              ///< Row-coordinate in image space
-    int y,                              ///< Col-coordinate in image space
-    float counts,                       ///< The value of the peak pixel
-    pmPeakType class                    ///< The type of peak pixel
-);
-
-pmMoments *pmMomentsAlloc();
-pmModel *pmModelAlloc(pmModelType type);
-pmSource *pmSourceAlloc();
-
-/******************************************************************************
-pmFindVectorPeaks(vector, threshold): Find all local peaks in the given vector
-above the given threshold.  Returns a vector of type PS_TYPE_U32 containing
-the location (x value) of all peaks.
- *****************************************************************************/
-psVector *pmFindVectorPeaks(
-    const psVector *vector,             ///< The input vector (float)
-    float threshold                     ///< Threshold above which to find a peak
-);
-
-/******************************************************************************
-pmFindImagePeaks(image, threshold): Find all local peaks in the given psImage
-above the given threshold.  Returns a psList containing the location (x/y
-value) of all peaks.
- *****************************************************************************/
-psArray *pmFindImagePeaks(
-    const psImage *image,               ///< The input image where peaks will be found (float)
-    float threshold                     ///< Threshold above which to find a peak
-);
-
-/******************************************************************************
-psCullPeaks(peaks, maxValue, valid): eliminate peaks from the psList that have
-a peak value above the given maximum, or fall outside the valid region.
- *****************************************************************************/
-psList *pmCullPeaks(
-    psList *peaks,                      ///< The psList of peaks to be culled
-    float maxValue,                     ///< Cull peaks above this value
-    const psRegion valid               ///< Cull peaks otside this psRegion
-);
-
-/******************************************************************************
-pmSource *pmSourceLocalSky(image, peak, innerRadius, outerRadius):
- 
- *****************************************************************************/
-pmSource *pmSourceLocalSky(
-    const psImage *image,               ///< The input image (float)
-    const pmPeak *peak,                 ///< The peak for which the psSource struct is created.
-    psStatsOptions statsOptions,        ///< The statistic used in calculating the background sky
-    float innerRadius,                  ///< The inner radius of the suqare annulus for calculating sky
-    float outerRadius                   ///< The outer radius of the suqare annulus for calculating sky
-);
-
-/******************************************************************************
- *****************************************************************************/
-pmSource *pmSourceMoments(
-    pmSource *source,                   ///< The input pmSource for which moments will be computed
-    float radius                        ///< Use a circle of pixels around the peak
-);
-
-/******************************************************************************
-pmSourceRoughClass(pmArray *source, psMetaDeta *metadata): make a guess at the
-source classification.
- *****************************************************************************/
-bool pmSourceRoughClass(
-    psArray *source,                    ///< The input pmSource
-    psMetadata *metadata                ///< Contains classification parameters
-);
-/******************************************************************************
-pmSourceSetPixelCircle(source, image, radius)
- *****************************************************************************/
-bool pmSourceSetPixelsCircle(
-    pmSource *source,                   ///< The input pmSource
-    const psImage *image,               ///< The input image (float)
-    float radius                        ///< The radius of the circle
-);
-
-/******************************************************************************
- *****************************************************************************/
-bool pmSourceModelGuess(
-    pmSource *source,                   ///< The input pmSource
-    const psImage *image,               ///< The input image (float)
-    pmModelType model                   ///< The type of model to be created.
-);
-
-/******************************************************************************
- *****************************************************************************/
-typedef enum {
-    PS_CONTOUR_CRUDE,
-} pmContourType;
-
-psArray *pmSourceContour(
-    pmSource *source,                   ///< The input pmSource
-    const psImage *image,               ///< The input image (float) (this arg should be removed)
-    float level,                        ///< The level of the contour
-    pmContourType mode                  ///< Currently this must be PS_CONTOUR_CRUDE
-);
-
-/******************************************************************************
- *****************************************************************************/
-bool pmSourceFitModel(
-    pmSource *source,                   ///< The input pmSource
-    const psImage *image                ///< The input image (float)
-);
-
-/******************************************************************************
- *****************************************************************************/
-bool pmSourceAddModel(
-    psImage *image,                     ///< The opuut image (float)
-    pmSource *source,                   ///< The input pmSource
-    bool center                         ///< A boolean flag that determines whether pixels are centered
-);
-
-/******************************************************************************
- *****************************************************************************/
-bool pmSourceSubModel(
-    psImage *image,                     ///< The output image (float)
-    pmSource *source,                   ///< The input pmSource
-    bool center                         ///< A boolean flag that determines whether pixels are centered
-);
-
-/******************************************************************************
-XXX: Why only *x argument?
-XXX EAM: psMinimizeLMChi2Func returns psF64, not float
- *****************************************************************************/
-float pmMinLM_Gauss2D(
-    psVector *deriv,                    ///< A possibly-NULL structure for the output derivatives
-    const psVector *params,             ///< A psVector which holds the parameters of this function
-    const psVector *x                   ///< A psVector which holds the row/col coordinate
-);
-
-/******************************************************************************
- *****************************************************************************/
-float pmMinLM_PsuedoGauss2D(
-    psVector *deriv,                    ///< A possibly-NULL structure for the output derivatives
-    const psVector *params,             ///< A psVector which holds the parameters of this function
-    const psVector *x                   ///< A psVector which holds the row/col coordinate
-);
-
-/******************************************************************************
- *****************************************************************************/
-float pmMinLM_Wauss2D(
-    psVector *deriv,                    ///< A possibly-NULL structure for the output derivatives
-    const psVector *params,             ///< A psVector which holds the parameters of this function
-    const psVector *x                   ///< A psVector which holds the row/col coordinate
-);
-
-/******************************************************************************
- *****************************************************************************/
-float pmMinLM_TwistGauss2D(
-    psVector *deriv,                    ///< A possibly-NULL structure for the output derivatives
-    const psVector *params,             ///< A psVector which holds the parameters of this function
-    const psVector *x                   ///< A psVector which holds the row/col coordinate
-);
-
-/******************************************************************************
- *****************************************************************************/
-float pmMinLM_Sersic(
-    psVector *deriv,                    ///< A possibly-NULL structure for the output derivatives
-    const psVector *params,             ///< A psVector which holds the parameters of this function
-    const psVector *x                   ///< A psVector which holds the row/col coordinate
-);
-
-/******************************************************************************
- *****************************************************************************/
-float pmMinLM_SersicCore(
-    psVector *deriv,                    ///< A possibly-NULL structure for the output derivatives
-    const psVector *params,             ///< A psVector which holds the parameters of this function
-    const psVector *x                   ///< A psVector which holds the row/col coordinate
-);
-
-/******************************************************************************
- *****************************************************************************/
-float pmMinLM_PsuedoSersic(
-    psVector *deriv,                    ///< A possibly-NULL structure for the output derivatives
-    const psVector *params,             ///< A psVector which holds the parameters of this function
-    const psVector *x                   ///< A psVector which holds the row/col coordinate
-);
-
-
-/**
- * 
- *  The object model functions are defined to allow for the flexible addition
- *  of new object models. Every object model, with parameters represented by
- *  pmModel, has an associated set of functions which provide necessary support
- *  operations. A set of abstract functions allow the programmer to select the
- *  approriate function or property for a specific named object model.
- * 
- */
-
-/**
- * 
- *  This function is the model chi-square minimization function for this model.
- * 
- */
-typedef psMinimizeLMChi2Func pmModelFunc;
-
-
-/**
- * 
- * This function returns the integrated flux for the given model parameters.
- */
-typedef psF64 (*pmModelFlux)(const psVector *params);
-
-
-/**
- * 
- *  This function provides the model guess parameters based on the details of
- *   the given source.
- * 
- */
-typedef bool (*pmModelGuessFunc)(pmModel *model, pmSource *source);
-
-
-/**
- * 
- *  This function constructs the PSF model for the given source based on the
- *  supplied psf and the FLT model for the object.
- * 
- */
-typedef bool (*pmModelFromPSFFunc)(pmModel *modelPSF, pmModel *modelFLT, pmPSF *psf);
-
-
-/**
- * 
- *  This function returns the radius at which the given model and parameters
- *  achieves the given flux.
- * 
- */
-typedef psF64 (*pmModelRadius)(const psVector *params, double flux);
-
-
-/**
- * 
- *  Each of the function types above has a corresponding function which returns
- *  the function given the model type:
- * 
- */
-pmModelFunc pmModelFunc_GetFunction (pmModelType type);
-pmModelFlux pmModelFlux_GetFunction (pmModelType type);
-pmModelGuessFunc pmModelGuessFunc_GetFunction (pmModelType type);
-pmModelFromPSFFunc pmModelFromPSFFunc_GetFunction (pmModelType type);
-pmModelRadius pmModelRadius_GetFunction (pmModelType type);
-psS32 pmModelParameterCount(pmModelType type);
-psS32 pmModelSetType(char *name);
-char *pmModelGetType(pmModelType type);
-
-#endif
Index: unk/psModules/src/pmReadoutCombine.c
===================================================================
--- /trunk/psModules/src/pmReadoutCombine.c	(revision 5169)
+++ 	(revision )
@@ -1,375 +1,0 @@
-/** @file  pmReadoutCombine.c
- *
- *  This file will contain a module which will combine multiple readout images.
- *
- *  @author GLG, MHPCC
- *
- *  @version $Revision: 1.26 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-09-11 22:25:39 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- */
-
-#include<stdio.h>
-#include<math.h>
-#include "pslib.h"
-#include "pmReadoutCombine.h"
-
-/******************************************************************************
-DetermineNumBits(data): This routine takes an enum psStatsOptions as an
-argument and returns the number of non-zero bits.
- *****************************************************************************/
-static psS32 DetermineNumBits(psStatsOptions data)
-{
-    psS32 i;
-    psU64 tmpData = data;
-    psS32 numBits = 0;
-
-    for (i=0;i<(8 * sizeof(psStatsOptions));i++) {
-        if (0x0001 & tmpData) {
-            numBits++;
-        }
-        tmpData = tmpData >> 1;
-    }
-    return(numBits);
-}
-
-/******************************************************************************
-XXX: Must add support for S16 and S32 types.  F32 currently supported.
- *****************************************************************************/
-psImage *pmReadoutCombine(psImage *output,
-                          const psList *inputs,
-                          psCombineParams *params,
-                          const psVector *zero,
-                          const psVector *scale,
-                          bool applyZeroScale,
-                          psF32 gain,
-                          psF32 readnoise)
-{
-    PS_ASSERT_PTR_NON_NULL(inputs, NULL);
-    PS_ASSERT_PTR_NON_NULL(params, NULL);
-    PS_ASSERT_PTR_NON_NULL(params->stats, NULL);
-    if (zero != NULL) {
-        PS_ASSERT_VECTOR_TYPE(zero, PS_TYPE_F32, NULL);
-        //        PS_ASSERT_VECTOR_TYPE_S16_S32_F32(zero, NULL);
-    }
-    if (scale != NULL) {
-        PS_ASSERT_VECTOR_TYPE(scale, PS_TYPE_F32, NULL);
-        //        PS_ASSERT_VECTOR_TYPE_S16_S32_F32(scale, NULL);
-    }
-    if ((zero != NULL) && (scale != NULL)) {
-        PS_ASSERT_VECTOR_TYPE_EQUAL(zero, scale, NULL);
-        // PS_ASSERT_VECTOR_TYPE_S16_S32_F32(scale, NULL);
-    }
-
-    psStats *stats = params->stats;
-    psS32 i;
-    psS32 j;
-    psS32 maxInputCols = 0;
-    psS32 maxInputRows = 0;
-    psS32 minInputCols = PS_MAX_S32;
-    psS32 minInputRows = PS_MAX_S32;
-    psListElem *tmpInput = NULL;
-    pmReadout *tmpReadout = NULL;
-    psS32 numInputs = 0;
-    psS32 tmpI;
-    psElemType outputType = PS_TYPE_F32;
-
-    if (1 < DetermineNumBits(params->stats->options)) {
-        psError(PS_ERR_UNKNOWN, true,
-                "Multiple statistical options have been requested.  Returning NULL.\n");
-        return(NULL);
-    }
-
-    //
-    // We step through each readout in the input image list.  If any readout is
-    // NULL, empty, or has the wrong type, we generate an error and return
-    // NULL.  We determine how big of an output image is needed to combine
-    // these input images.  We do this by taking the
-    //     max(readout->col0 + readout->numCols + image->col0 + image->numCols)
-    //     max(readout->row0 + readout->numRows + image->row0 + image->numRows)
-    // We then compare that to
-    //     output->col0 + output->numCols
-    //     output->row0 + output->numRows
-    // to determine if the output image actually stores that pixel.  A similar
-    // thing is done for the minimum row and column.
-    //
-    tmpInput = (psListElem *) inputs->head;
-    while (NULL != tmpInput) {
-        tmpReadout = (pmReadout *) tmpInput->data;
-        PS_ASSERT_READOUT_NON_NULL(tmpReadout, output);
-        PS_ASSERT_READOUT_NON_EMPTY(tmpReadout, output);
-        PS_ASSERT_READOUT_TYPE(tmpReadout, PS_TYPE_F32, output);
-
-        outputType = tmpReadout->image->type.type;
-
-        minInputRows = PS_MIN(minInputRows,
-                              (tmpReadout->row0 + tmpReadout->image->row0));
-        tmpI = tmpReadout->row0 +
-               tmpReadout->image->row0 +
-               tmpReadout->image->numRows;
-        maxInputRows = PS_MAX(maxInputRows, tmpI);
-
-        minInputCols = PS_MIN(minInputCols,
-                              (tmpReadout->col0 + tmpReadout->image->col0));
-        tmpI = tmpReadout->col0 +
-               tmpReadout->image->col0 +
-               tmpReadout->image->numCols;
-        maxInputCols = PS_MAX(maxInputCols, tmpI);
-        tmpInput = tmpInput->next;
-        numInputs++;
-    }
-
-    // We ensure that the zero vector is of the proper size.
-    if (zero != NULL) {
-        PS_ASSERT_VECTOR_TYPE(zero, PS_TYPE_F32, NULL);
-        if (numInputs > zero->n) {
-            psError(PS_ERR_UNKNOWN, true, "zero vector has incorrect size (%d).  Returning NULL.\n", zero->n);
-            return(NULL);
-        } else if (numInputs < zero->n) {
-            psLogMsg(__func__, PS_LOG_WARN,
-                     "WARNING: the zero vector too many elements (%d)\n", zero->n);
-        }
-    }
-
-    // We ensure that the scale vector is of the proper size.
-    if (scale != NULL) {
-        PS_ASSERT_VECTOR_TYPE(scale, PS_TYPE_F32, NULL);
-        if (numInputs > scale->n) {
-            psError(PS_ERR_UNKNOWN, true, "scale vector has incorrect size (%d).  Returning NULL.\n", scale->n);
-            return(NULL);
-        } else if (numInputs < scale->n) {
-            psLogMsg(__func__, PS_LOG_WARN,
-                     "WARNING: the scale vector has too many elements (%d)\n", scale->n);
-        }
-    }
-
-    // At this point, the following variables have been computed:
-    // maxInputRows: the largest input row value, in output image space.
-    // maxInputCols: the largest input column value, in output image space.
-    // minInputRows: the smallest input row value, in output image space.
-    // minInputCols: the smallest input column value, in output image space.
-    //
-    if (output == NULL) {
-        output = psImageAlloc(maxInputCols-minInputCols,
-                              maxInputRows-minInputRows, outputType);
-        *(psS32 *) &(output->col0) = minInputCols;
-        *(psS32 *) &(output->row0) = minInputRows;
-    } else {
-        PS_ASSERT_IMAGE_TYPE(output, PS_TYPE_F32, NULL);
-        if (((output->col0 + output->numCols) < maxInputCols) ||
-                ((output->row0 + output->numRows) < maxInputRows)) {
-            psError(PS_ERR_UNKNOWN, true,
-                    "Output image (%d, %d) is too small to hold combined images.  Returning NULL.\n",
-                    output->row0 + output->numRows,
-                    output->col0 + output->numCols);
-            return(NULL);
-        }
-
-        if ((output->col0 > minInputCols) || (output->row0 > minInputRows)) {
-            psError(PS_ERR_UNKNOWN, true,
-                    "Output image offset is larger then input image offset.  Returning NULL.\n");
-            return(NULL);
-        }
-    }
-
-    psVector *tmpPixels = psVectorAlloc(numInputs, PS_TYPE_F32);
-    psVector *tmpPixelErrors = psVectorAlloc(numInputs, PS_TYPE_F32);
-    psVector *tmpPixelMask = psVectorAlloc(numInputs, PS_TYPE_U8);
-    psVector *tmpPixelMaskNKeep = psVectorAlloc(numInputs, PS_TYPE_U8);
-    psVector *outRowLower = psVectorAlloc(numInputs, PS_TYPE_U32);
-    psVector *outRowUpper = psVectorAlloc(numInputs, PS_TYPE_U32);
-    psVector *outColLower = psVectorAlloc(numInputs, PS_TYPE_U32);
-    psVector *outColUpper = psVectorAlloc(numInputs, PS_TYPE_U32);
-    pmReadout **tmpReadouts = (pmReadout **) psAlloc(numInputs * sizeof(pmReadout *));
-
-    // For each input readout, we create a pointer to that readout in
-    // "tmpReadouts[]", and we store the min/max pixel indices for that
-    // readout, in output image coordinates, in the psVectors
-    // (outRowLower, outColLower, outRowUpper, outColUpper).
-    i = 0;
-    tmpInput = (psListElem *) inputs->head;
-    while (NULL != tmpInput) {
-        tmpReadouts[i] = (pmReadout *) tmpInput->data;
-        outRowLower->data.U32[i] = tmpReadouts[i]->row0 + tmpReadouts[i]->image->row0;
-        outColLower->data.U32[i] = tmpReadouts[i]->col0 + tmpReadouts[i]->image->col0;
-        outRowUpper->data.U32[i] = tmpReadouts[i]->row0 +
-                                   tmpReadouts[i]->image->row0 +
-                                   tmpReadouts[i]->image->numRows;
-        outColUpper->data.U32[i] = tmpReadouts[i]->col0 +
-                                   tmpReadouts[i]->image->col0 +
-                                   tmpReadouts[i]->image->numCols;
-        tmpInput = tmpInput->next;
-        i++;
-    }
-
-    // We loop through each pixel in the output image.  We loop through each
-    // input readout.  We determine if that output pixel is contained in the
-    // image from that readout.  If so, we save it in psVector tmpPixels.
-    // If not, we set a mask for that element in tmpPixels.  Then, we mask off
-    // pixels not between fracLow and fracHigh.  Then we call the vector
-    // stats routine on those pixels/mask.  Then we set the output pixel value
-    // to the result of the stats call.
-
-    for (i = output->row0; i < (output->row0 + output->numRows) ; i++) {
-        for (j = output->col0; j < (output->col0 + output->numCols) ; j++) {
-            for (psS32 r = 0; r < numInputs ; r++) {
-                //  printf("[%d][%d]: [%d][%d] to [%d][%d]\n", i, j, outRowLower->data.U32[r], outColLower->data.U32[r], outRowUpper->data.U32[r], outColUpper->data.U32[r]);
-                if ((outRowLower->data.U32[r] <= i) &&
-                        (outColLower->data.U32[r] <= j) &&
-                        (outRowUpper->data.U32[r] > i) &&
-                        (outColUpper->data.U32[r] > j)) {
-
-                    psS32 imageRow = i - (tmpReadouts[r]->row0 +
-                                          tmpReadouts[r]->image->row0);
-                    psS32 imageCol = j - (tmpReadouts[r]->col0 +
-                                          tmpReadouts[r]->image->col0);
-
-                    if ((NULL == tmpReadouts[r]->mask) ||
-                            !(params->maskVal && tmpReadouts[r]->mask->data.U8[imageRow][imageCol])) {
-                        tmpPixels->data.F32[r] = tmpReadouts[r]->image->data.F32[imageRow][imageCol];
-                        tmpPixelMask->data.U8[r] = 0;
-                    } else {
-                        tmpPixels->data.F32[r] = 0.0;
-                        tmpPixelMask->data.U8[r] = 1;
-                    }
-                } else {
-                    tmpPixels->data.F32[r] = 0.0;
-                    tmpPixelMask->data.U8[r] = 1;
-                }
-                // printf("readout[%d], image [%d][%d] is %f\n", r, i, j, tmpPixels->data.F32[r]);
-            }
-            // At this point, we have scanned all input readouts for this
-            // one output pixel.
-            //            for (psS32 r = 0; r < numInputs ; r++) printf("(0)tmpPixels->data.F32[%d] is %f\n", r, tmpPixels->data.F32[r]);
-
-            // Determine how many pixels lie between fracLow and fracHigh.
-            psS32 pixelCount = 0;
-            for (psS32 r = 0; r < numInputs ; r++) {
-                if (tmpPixelMask->data.U8[r] == 0) {
-                    if ((params->fracLow <= tmpPixels->data.F32[r]) &&
-                            (params->fracHigh >= tmpPixels->data.F32[r])) {
-                        pixelCount++;
-                    }
-                }
-            }
-
-            // If more than params->nKeep pixels lie between the valid range,
-            // then loop through the pixels, and mask away any pixels outside
-            // that range.
-            if (pixelCount >= params->nKeep) {
-                for (psS32 r = 0; r < numInputs ; r++) {
-                    if (tmpPixelMask->data.U8[r] == 0) {
-                        if ((params->fracLow <= tmpPixels->data.F32[r]) &&
-                                (params->fracHigh >= tmpPixels->data.F32[r])) {
-                            tmpPixelMaskNKeep->data.U8[r] = 0;
-                        } else {
-                            tmpPixelMaskNKeep->data.U8[r] = 1;
-                        }
-                    }
-                }
-            }
-
-            if ((gain > 0.0) && (readnoise >= 0.0)) {
-                psF32 x;
-                psF32 sigma;
-                if (applyZeroScale == true) {
-                    for (psS32 r = 0; r < numInputs ; r++) {
-                        if (zero != NULL) {
-                            x = zero->data.F32[r];
-                        } else {
-                            x = 0.0;
-                        }
-                        if (scale != NULL) {
-                            x+= tmpPixels->data.F32[r] * scale->data.F32[r];
-                        } else {
-                            x+= tmpPixels->data.F32[r];
-                        }
-                        sigma = sqrtf((readnoise*readnoise) + gain * x) / gain;
-
-                        tmpPixelErrors->data.F32[r] = sigma;
-                        tmpPixels->data.F32[r]= x;
-                    }
-                } else {
-                    for (psS32 r = 0; r < numInputs ; r++) {
-                        x= tmpPixels->data.F32[r];
-
-                        if (zero != NULL) {
-                            sigma = zero->data.F32[r];
-                        } else {
-                            sigma = 0.0;
-                        }
-                        if (scale != NULL) {
-                            sigma+= tmpPixels->data.F32[r] * scale->data.F32[r];
-                        } else {
-                            sigma+= tmpPixels->data.F32[r];
-                        }
-                        sigma = sqrtf((readnoise*readnoise) + (gain * sigma)) / gain;
-
-                        tmpPixelErrors->data.F32[r] = sigma;
-                        tmpPixels->data.F32[r]= x;
-                    }
-                }
-                // Calculate the specified statistic on the stack of pixels.
-                //                for (psS32 r = 0; r < numInputs ; r++) printf("(1)tmpPixels->data.F32[%d] is %f\n", r, tmpPixels->data.F32[r]);
-                psStats *rc = psVectorStats(stats,
-                                            tmpPixels,
-                                            tmpPixelErrors,
-                                            tmpPixelMaskNKeep,
-                                            1);
-                if (rc == NULL) {
-                    psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning NULL.\n");
-                    return(NULL);
-                }
-            } else {
-                if (scale != NULL) {
-                    for (psS32 r = 0; r < numInputs ; r++) {
-                        tmpPixels->data.F32[r]*= scale->data.F32[r];
-                    }
-                }
-
-                // We add the zero vector, if non-NULL.
-                if (zero != NULL) {
-                    for (psS32 r = 0; r < numInputs ; r++) {
-                        tmpPixels->data.F32[r]+= zero->data.F32[r];
-                    }
-                }
-
-                // Calculate the specified statistic on the stack of pixels.
-                //                for (psS32 r = 0; r < numInputs ; r++) printf("(2)tmpPixels->data.F32[%d] is %f\n", r, tmpPixels->data.F32[r]);
-                psStats *rc = psVectorStats(stats,
-                                            tmpPixels,
-                                            NULL,
-                                            tmpPixelMaskNKeep,
-                                            1);
-                if (rc == NULL) {
-                    psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning NULL.\n");
-                    return(NULL);
-                }
-            }
-
-
-            // Set the pixel value in the output image to the stat value.
-            double statValue;
-            if (!p_psGetStatValue(stats, &statValue)) {
-                psError(PS_ERR_UNKNOWN, true, "Could not determine stats value.  Returning NULL.\n");
-                return(NULL);
-            } else {
-                output->data.F32[i-output->row0][j-output->col0] = (psF32) statValue;
-            }
-        }
-    }
-
-    psFree(tmpPixels);
-    psFree(tmpPixelErrors);
-    psFree(tmpPixelMask);
-    psFree(tmpPixelMaskNKeep);
-    psFree(outRowLower);
-    psFree(outRowUpper);
-    psFree(outColLower);
-    psFree(outColUpper);
-    psFree(tmpReadouts);
-
-    return(output);
-}
Index: unk/psModules/src/pmReadoutCombine.h
===================================================================
--- /trunk/psModules/src/pmReadoutCombine.h	(revision 5169)
+++ 	(revision )
@@ -1,46 +1,0 @@
-/** @file  pmReadoutCombine.h
- *
- *  This file will contain a module which will combine multiple readout images.
- *
- *  @author GLG, MHPCC
- *
- *  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-08-11 00:03:51 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- */
-
-#if !defined(PM_READOUT_COMBINE_H)
-#define PM_READOUT_COMBINE_H
-
-#if HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include<stdio.h>
-#include<math.h>
-#include "pslib.h"
-#include "psConstants.h"
-#include "pmAstrometry.h"
-
-typedef struct
-{
-    psStats *stats;
-    unsigned int maskVal;
-    float fracHigh;
-    float fracLow;
-    int nKeep;
-}
-psCombineParams;
-
-psImage *pmReadoutCombine(psImage *output,
-                          const psList *inputs,
-                          psCombineParams *params,
-                          const psVector *zero,
-                          const psVector *scale,
-                          bool applyZeroScale,
-                          float gain,
-                          float readnoise);
-
-#endif
Index: unk/psModules/src/pmSubtractBias.c
===================================================================
--- /trunk/psModules/src/pmSubtractBias.c	(revision 5169)
+++ 	(revision )
@@ -1,682 +1,0 @@
-/** @file  pmSubtractBias.c
- *
- *  This file will contain a module which will subtract the detector bias
- *  in place from an input image.
- *
- *  @author GLG, MHPCC
- *
- *  @version $Revision: 1.38 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-09-23 01:55:30 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- */
-
-#if HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "pmSubtractBias.h"
-
-#define PM_SUBTRACT_BIAS_POLYNOMIAL_ORDER 2
-#define PM_SUBTRACT_BIAS_SPLINE_ORDER 3
-
-// XXX: put these in psConstants.h
-void PS_POLY1D_PRINT(psPolynomial1D *poly)
-{
-    printf("-------------- PS_POLY1D_PRINT() --------------\n");
-    printf("poly->COOL_1D_n is %d\n", poly->COOL_1D_n);
-    for (psS32 i = 0 ; i < (1 + poly->COOL_1D_n) ; i++) {
-        printf("poly->coeff[%d] is %f\n", i, poly->coeff[i]);
-    }
-}
-
-void PS_PRINT_SPLINE(psSpline1D *mySpline)
-{
-    printf("-------------- PS_PRINT_SPLINE() --------------\n");
-    printf("mySpline->n is %d\n", mySpline->n);
-    for (psS32 i = 0 ; i < mySpline->n ; i++) {
-        PS_POLY1D_PRINT(mySpline->spline[i]);
-    }
-    PS_VECTOR_PRINT_F32(mySpline->knots);
-}
-
-#define PS_IMAGE_PRINT_F32_HIDEF(NAME) \
-printf("======== printing %s ========\n", #NAME); \
-for (int i = 0 ; i < (NAME)->numRows ; i++) { \
-    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
-        printf("%.5f ", (NAME)->data.F32[i][j]); \
-    } \
-    printf("\n"); \
-}\
-
-/******************************************************************************
-psSubtractFrame(): this routine will take as input a readout for the input
-image and a readout for the bias image.  The bias image is subtracted in
-place from the input image.
-*****************************************************************************/
-static pmReadout *SubtractFrame(pmReadout *in,
-                                const pmReadout *bias)
-{
-    psS32 i;
-    psS32 j;
-
-    if (bias == NULL) {
-        psLogMsg(__func__, PS_LOG_WARN,
-                 "WARNING: pmSubtractBias.c: SubtractFrame(): bias frame is NULL.  Returning original image.\n");
-        return(in);
-    }
-
-
-    if ((in->image->numRows + in->row0 - bias->row0) > bias->image->numRows) {
-        psError(PS_ERR_UNKNOWN,true, "bias image does not have enough rows.  Returning in image\n");
-        return(in);
-    }
-    if ((in->image->numCols + in->col0 - bias->col0) > bias->image->numCols) {
-        psError(PS_ERR_UNKNOWN,true, "bias image does not have enough columns.  Returning in image\n");
-        return(in);
-    }
-
-    for (i=0;i<in->image->numRows;i++) {
-        for (j=0;j<in->image->numCols;j++) {
-            in->image->data.F32[i][j]-=
-                bias->image->data.F32[i+in->row0-bias->row0][j+in->col0-bias->col0];
-            if ((in->mask != NULL) && (bias->mask != NULL)) {
-                (in->mask->data.U8[i][j])|=
-                    bias->mask->data.U8[i+in->row0-bias->row0][j+in->col0-bias->col0];
-            }
-        }
-    }
-
-    return(in);
-}
-
-/******************************************************************************
-ImageSubtractScalar(): subtract a scalar from the input image.
- 
-XXX: Use a psLib function for this.
- 
-XXX: This should
- *****************************************************************************/
-static psImage *ImageSubtractScalar(psImage *image,
-                                    psF32 scalar)
-{
-    for (psS32 i=0;i<image->numRows;i++) {
-        for (psS32 j=0;j<image->numCols;j++) {
-            image->data.F32[i][j]-= scalar;
-        }
-    }
-    return(image);
-}
-
-/******************************************************************************
-GenNewStatOptions(): this routine will take as input the options member of the
-stat data structure, determine if multiple options have been specified, issue
-a warning message if so, and return the highest priority option (according to
-the order of the if-statements in this code).  The higher priority options are
-listed lower in the code.
- *****************************************************************************/
-static psStatsOptions GenNewStatOptions(const psStats *stat)
-{
-    psS32 numOptions = 0;
-    psStatsOptions opt = 0;
-
-    if (stat->options & PS_STAT_ROBUST_MODE) {
-        if (numOptions == 0) {
-            opt = PS_STAT_ROBUST_MODE;
-        }
-        numOptions++;
-    }
-
-    if (stat->options & PS_STAT_ROBUST_MEDIAN) {
-        if (numOptions == 0) {
-            opt = PS_STAT_ROBUST_MEDIAN;
-        }
-        numOptions++;
-    }
-
-    if (stat->options & PS_STAT_ROBUST_MEAN) {
-        if (numOptions == 0) {
-            opt = PS_STAT_ROBUST_MEAN;
-        }
-        numOptions++;
-    }
-
-    if (stat->options & PS_STAT_CLIPPED_MEAN) {
-        if (numOptions == 0) {
-            opt = PS_STAT_CLIPPED_MEAN;
-        }
-        numOptions++;
-    }
-
-    if (stat->options & PS_STAT_SAMPLE_MEDIAN) {
-        if (numOptions == 0) {
-            opt = PS_STAT_SAMPLE_MEDIAN;
-        }
-        numOptions++;
-    }
-
-    if (stat->options & PS_STAT_SAMPLE_MEAN) {
-        numOptions++;
-        opt = PS_STAT_SAMPLE_MEAN;
-    }
-
-
-    if (numOptions == 0) {
-        psError(PS_ERR_UNKNOWN,true, "No statistics options have been specified.\n");
-    }
-    if (numOptions != 1) {
-        psLogMsg(__func__, PS_LOG_WARN,
-                 "WARNING: pmSubtractBias.c: GenNewStatOptions(): Too many statistics options have been specified\n");
-    }
-    return(opt);
-}
-
-
-
-/******************************************************************************
-ScaleOverscanVector(): this routine takes as input an arbitrary vector,
-creates a new vector of length n, and fills the new vector with the
-interpolated values of the old vector.  The type of interpolation is:
-    PM_FIT_POLYNOMIAL: fit a polynomial to the entire input vector data.
-    PM_FIT_SPLINE: fit splines to the input vector data.
-XXX: Doesn't it make more sense to do polynomial interpolation on a few
-elements of the input vector, rather than fit a polynomial to the entire
-vector?
- *****************************************************************************/
-static psVector *ScaleOverscanVector(psVector *overscanVector,
-                                     psS32 n,
-                                     void *fitSpec,
-                                     pmFit fit)
-{
-    psTrace(".psModule.pmSubtracBias.ScaleOverscanVector", 4,
-            "---- ScaleOverscanVector() begin (%d -> %d) ----\n", overscanVector->n, n);
-    //    PS_VECTOR_PRINT_F32(overscanVector);
-
-    if (NULL == overscanVector) {
-        return(overscanVector);
-    }
-
-    // Allocate the new vector.
-    psVector *newVec = psVectorAlloc(n, PS_TYPE_F32);
-
-    //
-    // If the new vector is the same size as the old, simply copy the data.
-    //
-    if (n == overscanVector->n) {
-        for (psS32 i = 0 ; i < n ; i++) {
-            newVec->data.F32[i] = overscanVector->data.F32[i];
-        }
-        return(newVec);
-    }
-    psPolynomial1D *myPoly;
-    psSpline1D *mySpline;
-    psF32 x;
-    psS32 i;
-
-    if (fit == PM_FIT_POLYNOMIAL) {
-        // Fit a polynomial to the old overscan vector.
-        myPoly = (psPolynomial1D *) fitSpec;
-        PS_ASSERT_POLY_NON_NULL(myPoly, NULL);
-        myPoly = psVectorFitPolynomial1D(myPoly, NULL, 0, overscanVector, NULL, NULL);
-        if (myPoly == NULL) {
-            psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector()(1): Could not fit a polynomial to the psVector.\n");
-            return(NULL);
-        }
-
-        // For each element of the new vector, convert the x-ordinate to that
-        // of the old vector, use the fitted polynomial to determine the
-        // interpolated value at that point, and set the new vector.
-        for (i=0;i<n;i++) {
-            x = ((psF32) i) * ((psF32) overscanVector->n) / ((psF32) n);
-            newVec->data.F32[i] = psPolynomial1DEval(myPoly, x);
-        }
-    } else if (fit == PM_FIT_SPLINE) {
-        psS32 mustFreeSpline = 0;
-        // Fit a spline to the old overscan vector.
-        mySpline = (psSpline1D *) fitSpec;
-        if (mySpline == NULL) {
-            mustFreeSpline = 1;
-        }
-
-        //
-        // NOTE: Since the X arg in the psVectorFitSpline1D() function is NULL,
-        // splines enpoints will be from 0.0 to overscanVector->n-1.  Must scale
-        // properly when doing the spline eval.
-        //
-        mySpline = psVectorFitSpline1D(mySpline, NULL, overscanVector, NULL);
-        if (mySpline == NULL) {
-            psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector()(2): Could not fit a spline to the psVector.\n");
-            return(NULL);
-        }
-        //        PS_PRINT_SPLINE(mySpline);
-
-        // For each element of the new vector, convert the x-ordinate to that
-        // of the old vector, use the fitted polynomial to determine the
-        // interpolated value at that point, and set the new vector.
-        for (i=0;i<n;i++) {
-            // Scale to [0 : overscanVector->n - 1]
-            x = ((psF32) i) * ((psF32) (overscanVector->n-1)) / ((psF32) n);
-            newVec->data.F32[i] = psSpline1DEval(mySpline, x);
-        }
-        if (mustFreeSpline ==1) {
-            psFree(mySpline);
-        }
-        //        PS_VECTOR_PRINT_F32(newVec);
-
-
-    } else {
-        psError(PS_ERR_UNKNOWN, true, "unknown fit type.  Returning NULL.\n");
-        psFree(newVec);
-        return(NULL);
-    }
-
-    psTrace(".psModule.pmSubtracBias.ScaleOverscanVector", 4,
-            "---- ScaleOverscanVector() exit ----\n");
-    return(newVec);
-}
-
-/******************************************************************************
-XXX: The SDRS does not specify type support.  F32 is implemented here.
- *****************************************************************************/
-pmReadout *pmSubtractBias(pmReadout *in,
-                          void *fitSpec,
-                          const psList *overscans,
-                          pmOverscanAxis overScanAxis,
-                          psStats *stat,
-                          psS32 nBinOrig,
-                          pmFit fit,
-                          const pmReadout *bias)
-{
-    psTrace(".psModule.pmSubtracBias.pmSubtractBias", 4,
-            "---- pmSubtractBias() begin ----\n");
-    PS_ASSERT_READOUT_NON_NULL(in, NULL);
-    PS_ASSERT_READOUT_NON_EMPTY(in, NULL);
-    PS_ASSERT_READOUT_TYPE(in, PS_TYPE_F32, NULL);
-
-    //
-    // If the overscans != NULL, then check the type of each image.
-    //
-    if (overscans != NULL) {
-        psListElem *tmpOverscan = (psListElem *) overscans->head;
-        while (NULL != tmpOverscan) {
-            psImage *myOverscanImage = (psImage *) tmpOverscan->data;
-            PS_ASSERT_IMAGE_TYPE(myOverscanImage, PS_TYPE_F32, NULL);
-            tmpOverscan = tmpOverscan->next;
-        }
-    }
-
-    if ((overscans == NULL) && (overScanAxis != PM_OVERSCAN_NONE)) {
-        psError(PS_ERR_UNKNOWN,true, "(overscans == NULL) && (overScanAxis != PM_OVERSCAN_NONE).  Returning in image\n");
-        return(in);
-    }
-
-    // Check for an unallowable pmFit.
-    if ((fit != PM_OVERSCAN_NONE) &&
-            (fit != PM_OVERSCAN_ROWS) &&
-            (fit != PM_OVERSCAN_COLUMNS) &&
-            (fit != PM_OVERSCAN_ALL)) {
-        psError(PS_ERR_UNKNOWN, true, "fit is unallowable (%d).  Returning in image.\n", fit);
-        return(in);
-    }
-    // Check for an unallowable pmOverscanAxis.
-    if ((overScanAxis != PM_OVERSCAN_NONE) &&
-            (overScanAxis != PM_OVERSCAN_ROWS) &&
-            (overScanAxis != PM_OVERSCAN_COLUMNS) &&
-            (overScanAxis != PM_OVERSCAN_ALL)) {
-        psError(PS_ERR_UNKNOWN, true, "overScanAxis is unallowable (%d).  Returning in image.\n", overScanAxis);
-        return(in);
-    }
-    psS32 i;
-    psS32 j;
-    psS32 numBins = 0;
-    static psVector *overscanVector = NULL;
-    psVector *tmpRow = NULL;
-    psVector *tmpCol = NULL;
-    psVector *myBin = NULL;
-    psVector *binVec = NULL;
-    psListElem *tmpOverscan = NULL;
-    double statValue;
-    psImage *myOverscanImage = NULL;
-    psPolynomial1D *myPoly = NULL;
-    psSpline1D *mySpline = NULL;
-    psS32 nBin;
-
-    //
-    //  Create a static stats data structure and determine the highest
-    //  priority stats option.
-    //
-    static psStats *myStats = NULL;
-    if (myStats == NULL) {
-        myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
-        p_psMemSetPersistent(myStats, true);
-    }
-    if (stat != NULL) {
-        myStats->options = GenNewStatOptions(stat);
-    }
-
-
-    if (overScanAxis == PM_OVERSCAN_NONE) {
-        if (fit != PM_FIT_NONE) {
-            psLogMsg(__func__, PS_LOG_WARN,
-                     "WARNING: pmSubtractBias.(): overScanAxis equals NONE, and fit does not equal NONE.  Proceeding to full fram subtraction.\n");
-        }
-
-        if (overscans != NULL) {
-            psLogMsg(__func__, PS_LOG_WARN,
-                     "WARNING: pmSubtractBias.(): overScanAxis equals NONE and overscans does not equal NULL.  Proceeding to full fram subtraction.\n");
-        }
-        return(SubtractFrame(in, bias));
-    }
-
-    if ((overScanAxis == PM_OVERSCAN_ALL) && (fit != PM_FIT_NONE)) {
-        psLogMsg(__func__, PS_LOG_WARN,
-                 "WARNING: pmSubtractBias.(): overScanAxis equals ALL, and fit does not equal NONE.  Proceeding with the rest of the module.\n");
-    }
-
-
-    //
-    // We subtract each overscan region from the image data.
-    // If we get here we know that overscans != NULL.
-    //
-
-    if (overScanAxis == PM_OVERSCAN_ALL) {
-        tmpOverscan = (psListElem *) overscans->head;
-        while (NULL != tmpOverscan) {
-            myOverscanImage = (psImage *) tmpOverscan->data;
-
-            PS_ASSERT_IMAGE_TYPE(myOverscanImage, PS_TYPE_F32, NULL);
-            psStats *rc = psImageStats(myStats, myOverscanImage, NULL, (psMaskType)0xffffffff);
-            if (rc == NULL) {
-                psError(PS_ERR_UNKNOWN, false, "psImageStats(): could not perform requested statistical operation.  Returning in image.\n");
-                return(in);
-            }
-            if (false == p_psGetStatValue(myStats, &statValue)) {
-                psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
-                return(in);
-            }
-            ImageSubtractScalar(in->image, statValue);
-
-            tmpOverscan = tmpOverscan->next;
-        }
-        return(in);
-    }
-
-    // This check is redundant with above code.
-    if (!((overScanAxis == PM_OVERSCAN_ROWS) || (overScanAxis == PM_OVERSCAN_COLUMNS))) {
-        psError(PS_ERR_UNKNOWN, true, "overScanAxis is unallowable (%d).\nReturning in image.\n", overScanAxis);
-        return(in);
-    }
-
-    tmpOverscan = (psListElem *) overscans->head;
-    while (NULL != tmpOverscan) {
-        //        PS_IMAGE_PRINT_F32_HIDEF(in->image);
-        myOverscanImage = (psImage *) tmpOverscan->data;
-
-        if (overScanAxis == PM_OVERSCAN_ROWS) {
-            if (myOverscanImage->numCols != (in->image)->numCols) {
-                psLogMsg(__func__, PS_LOG_WARN,
-                         "WARNING: pmSubtractBias.(): overscan image has %d columns, input image has %d columns\n",
-                         myOverscanImage->numCols, in->image->numCols);
-            }
-
-            // We create a row vector and subtract this vector from image.
-            // XXX: Is there a better way to extract a psVector from a psImage without
-            // having to copy every element in that vector?
-            overscanVector = psVectorAlloc(myOverscanImage->numCols, PS_TYPE_F32);
-            for (i=0;i<overscanVector->n;i++) {
-                overscanVector->data.F32[i] = 0.0;
-            }
-            tmpRow = psVectorAlloc(myOverscanImage->numRows, PS_TYPE_F32);
-
-            // For each column of the input image, loop through every row,
-            // collect the pixel in that row, then performed the specified
-            // statistical op on those pixels.  Store this in overscanVector.
-            for (i=0;i<myOverscanImage->numCols;i++) {
-                for (j=0;j<myOverscanImage->numRows;j++) {
-                    tmpRow->data.F32[j] = myOverscanImage->data.F32[j][i];
-                }
-                psStats *rc = psVectorStats(myStats, tmpRow, NULL, NULL, 0);
-                if (rc == NULL) {
-                    psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning in image.\n");
-                    return(in);
-                }
-                if (false ==  p_psGetStatValue(rc, &statValue)) {
-                    psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
-                    return(in);
-                }
-                overscanVector->data.F32[i] = statValue;
-            }
-            psFree(tmpRow);
-
-            // Scale the overscan vector to the size of the input image.
-            if (overscanVector->n != in->image->numCols) {
-                if ((fit == PM_FIT_POLYNOMIAL) || (fit == PM_FIT_SPLINE)) {
-                    psVector *newVec = ScaleOverscanVector(overscanVector,
-                                                           in->image->numCols,
-                                                           fitSpec, fit);
-                    if (newVec == NULL) {
-                        psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector(): could not scale the overscan vector.  Returning in image.\n");
-                        return(in);
-                    }
-                    psFree(overscanVector);
-                    overscanVector = newVec;
-                } else {
-                    psError(PS_ERR_UNKNOWN, true, "Don't know how to scale the overscan vector.  Set fit to PM_FIT_SPLINE or PM_FIT_POLYNOMIAL.  Returning in image.\n");
-                    psFree(overscanVector);
-                    return(in);
-                }
-            }
-        }
-
-        if (overScanAxis == PM_OVERSCAN_COLUMNS) {
-            if (myOverscanImage->numRows != (in->image)->numRows) {
-                psLogMsg(__func__, PS_LOG_WARN,
-                         "WARNING: pmSubtractBias.(): overscan image has %d rows, input image has %d rows\n",
-                         myOverscanImage->numRows, in->image->numRows);
-            }
-
-            // We create a column vector and subtract this vector from image.
-            overscanVector = psVectorAlloc(myOverscanImage->numRows, PS_TYPE_F32);
-            for (i=0;i<overscanVector->n;i++) {
-                overscanVector->data.F32[i] = 0.0;
-            }
-            tmpCol = psVectorAlloc(myOverscanImage->numCols, PS_TYPE_F32);
-
-            // For each row of the input image, loop through every column,
-            // collect the pixel in that row, then performed the specified
-            // statistical op on those pixels.  Store this in overscanVector.
-            for (i=0;i<myOverscanImage->numRows;i++) {
-                for (j=0;j<myOverscanImage->numCols;j++) {
-                    tmpCol->data.F32[j] = myOverscanImage->data.F32[i][j];
-                }
-                psStats *rc = psVectorStats(myStats, tmpCol, NULL, NULL, 0);
-                if (rc == NULL) {
-                    psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning in image.\n");
-                    return(in);
-                }
-                if (false ==  p_psGetStatValue(rc, &statValue)) {
-                    psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
-                    return(in);
-                }
-                overscanVector->data.F32[i] = statValue;
-            }
-            psFree(tmpCol);
-
-            // Scale the overscan vector to the size of the input image.
-            if (overscanVector->n != in->image->numRows) {
-                if ((fit == PM_FIT_POLYNOMIAL) || (fit == PM_FIT_SPLINE)) {
-                    psVector *newVec = ScaleOverscanVector(overscanVector,
-                                                           in->image->numRows,
-                                                           fitSpec, fit);
-                    if (newVec == NULL) {
-                        psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector(): could not scale the overscan vector.  Returning in image.\n");
-                        return(in);
-                    }
-                    psFree(overscanVector);
-                    overscanVector = newVec;
-                } else {
-                    psError(PS_ERR_UNKNOWN, true, "Don't know how to scale the overscan vector.  Set fit to PM_FIT_SPLINE or PM_FIT_POLYNOMIAL.  Returning in image.\n");
-                    psFree(overscanVector);
-                    return(in);
-                }
-            }
-        }
-
-        //
-        // Re-bin the overscan vector (change its length).
-        //
-        // Only if nBinOrig > 1.
-        if ((nBinOrig > 1) && (nBinOrig < overscanVector->n)) {
-            numBins = 1+((overscanVector->n)/nBinOrig);
-            myBin = psVectorAlloc(numBins, PS_TYPE_F32);
-            binVec = psVectorAlloc(nBinOrig, PS_TYPE_F32);
-
-            for (i=0;i<numBins;i++) {
-                for(j=0;j<nBinOrig;j++) {
-                    if (overscanVector->n > ((i*nBinOrig)+j)) {
-                        binVec->data.F32[j] = overscanVector->data.F32[(i*nBinOrig)+j];
-                    } else {
-                        // XXX: we get here if nBinOrig does not evenly divide
-                        // the overscanVector vector.  This is the last bin.  Should
-                        // we change the binVec->n to acknowledge that?
-                        binVec->n = j;
-                    }
-                }
-                psStats *rc = psVectorStats(myStats, binVec, NULL, NULL, 0);
-                if (rc == NULL) {
-                    psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning in image.\n");
-                    return(in);
-                }
-                if (false ==  p_psGetStatValue(rc, &statValue)) {
-                    psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
-                    return(in);
-                }
-                myBin->data.F32[i] = statValue;
-            }
-
-            // Change the effective size of overscanVector.
-            overscanVector->n = numBins;
-            for (i=0;i<numBins;i++) {
-                overscanVector->data.F32[i] = myBin->data.F32[i];
-            }
-            psFree(binVec);
-            psFree(myBin);
-            nBin = nBinOrig;
-        } else {
-            nBin = 1;
-        }
-
-        // At this point the number of data points in overscanVector should be
-        // equal to the number of rows/columns (whatever is appropriate) in the
-        // image divided by numBins.
-        //
-
-
-        //
-        // This doesn't seem right.  The only way to do a spline fit is if,
-        // by SDRS requirements, fitSpec is not-NULL>  But in order for it
-        // to be non-NULL, someone must have called psSpline1DAlloc() with
-        // the min, max, and number of splines.
-        //
-        if (!((fitSpec == NULL) || (fit == PM_FIT_NONE))) {
-            //
-            // Fit a polynomial or spline to the overscan vector.
-            //
-            if (fit == PM_FIT_POLYNOMIAL) {
-                myPoly = (psPolynomial1D *) fitSpec;
-                myPoly = psVectorFitPolynomial1D(myPoly, NULL, 0, overscanVector, NULL, NULL);
-                if (myPoly == NULL) {
-                    psError(PS_ERR_UNKNOWN, false, "(3) Could not fit a polynomial to overscan vector.  Returning in image.\n");
-                    psFree(overscanVector);
-                    return(in);
-                }
-            } else if (fit == PM_FIT_SPLINE) {
-                mySpline = (psSpline1D *) fitSpec;
-                mySpline = psVectorFitSpline1D(mySpline, NULL, overscanVector, NULL);
-                if (mySpline == NULL) {
-                    psError(PS_ERR_UNKNOWN, false, "Could not fit a spline to overscan vector.  Returning in image.\n");
-                    psFree(overscanVector);
-                    return(in);
-                }
-            }
-
-            //
-            // Subtract fitted overscan vector row-wise from the image.
-            //
-            if (overScanAxis == PM_OVERSCAN_ROWS) {
-                for (i=0;i<(in->image)->numCols;i++) {
-                    psF32 tmpF32 = 0.0;
-                    if (fit == PM_FIT_POLYNOMIAL) {
-                        tmpF32 = psPolynomial1DEval(myPoly, ((psF32) i) / ((psF32) nBin));
-                    } else if (fit == PM_FIT_SPLINE) {
-                        tmpF32 = psSpline1DEval(mySpline, ((psF32) i) / ((psF32) nBin));
-                    }
-                    for (j=0;j<(in->image)->numRows;j++) {
-                        (in->image)->data.F32[j][i]-= tmpF32;
-                    }
-                }
-            }
-
-            //
-            // Subtract fitted overscan vector column-wise from the image.
-            //
-            if (overScanAxis == PM_OVERSCAN_COLUMNS) {
-                for (i=0;i<(in->image)->numRows;i++) {
-                    psF32 tmpF32 = 0.0;
-                    if (fit == PM_FIT_POLYNOMIAL) {
-                        tmpF32 = psPolynomial1DEval(myPoly, ((psF32) i) / ((psF32) nBin));
-                    } else if (fit == PM_FIT_SPLINE) {
-                        tmpF32 = psSpline1DEval(mySpline, ((psF32) i) / ((psF32) nBin));
-                    }
-
-                    for (j=0;j<(in->image)->numCols;j++) {
-                        (in->image)->data.F32[i][j]-= tmpF32;
-                    }
-                }
-            }
-        } else {
-            //
-            // If we get here, then no polynomials were fit to the overscan
-            // vector.  We simply subtract it, taking into account binning,
-            // from the image.
-            //
-
-            //
-            // Subtract overscan vector row-wise from the image.
-            //
-            if (overScanAxis == PM_OVERSCAN_ROWS) {
-                for (i=0;i<(in->image)->numCols;i++) {
-                    for (j=0;j<(in->image)->numRows;j++) {
-                        (in->image)->data.F32[j][i]-= overscanVector->data.F32[i/nBin];
-                    }
-                }
-            }
-
-            //
-            // Subtract overscan vector column-wise from the image.
-            //
-            if (overScanAxis == PM_OVERSCAN_COLUMNS) {
-                for (i=0;i<(in->image)->numRows;i++) {
-                    for (j=0;j<(in->image)->numCols;j++) {
-                        (in->image)->data.F32[i][j]-= overscanVector->data.F32[i/nBin];
-                    }
-                }
-            }
-        }
-
-        psFree(overscanVector);
-
-        tmpOverscan = tmpOverscan->next;
-    }
-
-    psTrace(".psModule.pmSubtracBias.pmSubtractBias", 4,
-            "---- pmSubtractBias() exit ----\n");
-
-    if (bias != NULL) {
-        return(SubtractFrame(in, bias));
-    }
-    return(in);
-}
-
-
Index: unk/psModules/src/pmSubtractBias.h
===================================================================
--- /trunk/psModules/src/pmSubtractBias.h	(revision 5169)
+++ 	(revision )
@@ -1,49 +1,0 @@
-/** @file  pmSubtractBias.h
- *
- *  This file will contain a module which will subtract the detector bias
- *  in place from an input image.
- *
- *  @author GLG, MHPCC
- *
- *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-08-16 01:10:34 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- */
-
-#if !defined(PM_SUBTRACT_BIAS_H)
-#define PM_SUBTRACT_BIAS_H
-
-#if HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include<stdio.h>
-#include<math.h>
-#include "pslib.h"
-#include "pmAstrometry.h"
-
-typedef enum {
-    PM_OVERSCAN_NONE,                         ///< No overscan subtraction
-    PM_OVERSCAN_ROWS,                         ///< Subtract rows
-    PM_OVERSCAN_COLUMNS,                      ///< Subtract columns
-    PM_OVERSCAN_ALL                           ///< Subtract the statistic of all pixels in overscan region
-} pmOverscanAxis;
-
-typedef enum {
-    PM_FIT_NONE,                              ///< No fit
-    PM_FIT_POLYNOMIAL,                        ///< Fit polynomial
-    PM_FIT_SPLINE                             ///< Fit cubic splines
-} pmFit;
-
-pmReadout *pmSubtractBias(pmReadout *in,                ///< The input pmReadout image
-                          void *fitSpec,                ///< A polynomial or spline, defining the fit type.
-                          const psList *overscans,      ///< A psList of overscan images
-                          pmOverscanAxis overScanAxis,  ///< Defines how overscans are applied
-                          psStats *stat,                ///< The statistic to be used in combining overscan data
-                          int nBin,                     ///< The amount of binning to be done image pixels.
-                          pmFit fit,                    ///< PM_FIT_SPLINE, PM_FIT_POLYNOMIAL, or PM_FIT_NONE
-                          const pmReadout *bias);       ///< A possibly NULL bias pmReadout which is to be subtracted
-
-#endif
