Index: /trunk/psModules/src/config/pmConfig.c
===================================================================
--- /trunk/psModules/src/config/pmConfig.c	(revision 5515)
+++ /trunk/psModules/src/config/pmConfig.c	(revision 5516)
@@ -3,6 +3,6 @@
  *  @author PAP, IfA
  *
- *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-10-20 23:06:24 $
+ *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-11-15 20:09:03 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -11,13 +11,9 @@
 #include <stdio.h>
 #include <string.h>
-#include <assert.h>
 #include "pslib.h"
-//#include "psAdditionals.h"
 #include "pmConfig.h"
 
 #define PS_SITE "PS_SITE"               // Name of the environment variable containing the site config file
-#define PS_DEFAULT_SITE "ipprc.config"     // Default site config file
-
-
+#define PS_DEFAULT_SITE "ipprc.config"  // Default site config file
 
 /** readConfig
@@ -32,5 +28,6 @@
     const char *description)            // Description of file
 {
-    int numBadLines = 0;  // Number of bad lines in config file
+    int numBadLines = 0;
+
     psLogMsg(__func__, PS_LOG_INFO, "Loading %s configuration from file %s\n",
              description, name);
@@ -56,4 +53,6 @@
 must take precedence override the values set here.  This must be, somehow,
 coded.
+ 
+XXX: Must load camera and recipe configuration if specified in the command line.
  *****************************************************************************/
 bool pmConfigRead(
@@ -119,4 +118,5 @@
     // file and store in psMetadata struct site.
     //
+
     if (!readConfig(site, siteName, "site")) {
         return false;
@@ -249,4 +249,16 @@
     }
 
+    //
+    // Allow command line options to override defaults for logging.
+    // XXX: Is it appropriate to use the ArgVerbosity function for this?
+    //   A: it removes the options from the command line.
+    //   B: will the pmConfigRead function always be called on initialization.
+    //
+    psS32 saveLogLevel = psLogGetLevel();
+    psArgumentVerbosity(argc, argv);
+    // XXX: substitute the string for the default log level for "2".
+    if (2 == psLogGetLevel()) {
+        psLogSetLevel(saveLogLevel);
+    }
     return(true);
 }
@@ -266,6 +278,6 @@
     // Apply the rules
     psMetadataIterator *ruleIter = psMetadataIteratorAlloc(rule, PS_LIST_HEAD, NULL); // Rule iterator
-    psMetadataItem *ruleItem = NULL; // Item from the metadata
-    bool match = true;   // Does it match?
+    psMetadataItem *ruleItem = NULL;    // Item from the metadata
+    bool match = true;                  // Does it match?
     while ((ruleItem = psMetadataGetAndIncrement(ruleIter)) && match) {
         // Check for the existence of the rule
@@ -389,9 +401,9 @@
     const char *recipeName)
 {
-    assert(camera);
-    assert(recipeName);
-
-    psMetadata *recipe = NULL; // Recipe to read
-    bool mdok = true;   // Status of MD lookup
+    PS_ASSERT_PTR_NON_NULL(camera, false);
+    PS_ASSERT_PTR_NON_NULL(recipeName, false);
+
+    psMetadata *recipe = NULL;          // Recipe to read
+    bool mdok = true;                   // Status of MD lookup
     psMetadata *recipes = psMetadataLookupMD(&mdok, camera, "RECIPES"); // The list of recipes
     if (! mdok || ! recipes) {
Index: /trunk/psModules/src/detrend/pmFlatField.c
===================================================================
--- /trunk/psModules/src/detrend/pmFlatField.c	(revision 5515)
+++ /trunk/psModules/src/detrend/pmFlatField.c	(revision 5516)
@@ -18,6 +18,6 @@
  *  @author Ross Harman, MHPCC
  *
- *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-10-20 23:06:24 $
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-11-15 20:09:03 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -36,4 +36,5 @@
 #include "pmMaskBadPixels.h"
 #include "pmFlatFieldErrors.h"
+#include "pmSubtractBias.h"
 
 
@@ -42,6 +43,20 @@
     const pmReadout *flat)
 {
+    // XXX: Use the proper image and readout asserts.
+    PS_ASSERT_PTR_NON_NULL(in, true);
+    PS_ASSERT_PTR_NON_NULL(in->image, false);
+    PS_ASSERT_PTR_NON_NULL(in->mask, false);
+    PS_ASSERT_PTR_NON_NULL(flat, false);
+    PS_ASSERT_PTR_NON_NULL(flat->image, false);
+    if (in == NULL)
+        printf("XXX: NULL\n");
+
     // XXX: Not sure if this is correct.  Must consult with IfA.
     PS_ASSERT_PTR_NON_NULL(in->mask, false);
+
+    PS_WARN_PTR_NON_NULL(in->parent);
+    if (in->parent != NULL) {
+        PS_WARN_PTR_NON_NULL(in->parent->concepts);
+    }
     int i = 0;
     int j = 0;
@@ -51,37 +66,25 @@
     psElemType flatType;
     psElemType maskType;
-    psImage *inImage = NULL;
     psImage *inMask = NULL;
     psImage *flatImage = NULL;
 
-
-    // Check for nulls
-    if (in == NULL) {
-        return true;       // Readout might 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;
+    //
+    // Determine trimmed image from metadata.
+    //
+    psImage *trimmedImg = p_psDetermineTrimmedImage(in);
     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 = in->mask;
 
     // Check input image and its mask are not larger than flat image
 
-    if (inImage->numRows>flatImage->numRows || inImage->numCols>flatImage->numCols) {
+    if (trimmedImg == NULL)
+        printf("XXX: 00\n");
+    if (flatImage == NULL)
+        printf("XXX 01\n");
+
+    if (trimmedImg->numRows>flatImage->numRows || trimmedImg->numCols>flatImage->numCols) {
         psError( PS_ERR_BAD_PARAMETER_SIZE, true,
                  PS_ERRORTEXT_pmFlatField_SIZE_INPUT_IMAGE,
-                 inImage->numRows, inImage->numCols, flatImage->numRows, flatImage->numCols);
+                 trimmedImg->numRows, trimmedImg->numCols, flatImage->numRows, flatImage->numCols);
         return false;
     }
@@ -94,6 +97,6 @@
 
     // Determine total offset based on image offset with chip offset
-    totOffCol = inImage->col0 + in->col0;
-    totOffRow = inImage->row0 + in->row0;
+    totOffCol = trimmedImg->col0 + in->col0;
+    totOffRow = trimmedImg->row0 + in->row0;
 
     // Check that offsets are within image limits
@@ -103,8 +106,8 @@
                  totOffRow, totOffCol, flatImage->numRows, flatImage->numCols);
         return false;
-    } else if(totOffRow>=inImage->numRows || totOffCol>=inImage->numCols) {
+    } else if(totOffRow>=trimmedImg->numRows || totOffCol>=trimmedImg->numCols) {
         psError( PS_ERR_BAD_PARAMETER_SIZE, true,
                  PS_ERRORTEXT_pmFlatField_OFFSET_INPUT_IMAGE,
-                 totOffRow, totOffCol, inImage->numRows, inImage->numCols);
+                 totOffRow, totOffCol, trimmedImg->numRows, trimmedImg->numCols);
         return false;
     } else if(totOffRow>=inMask->numRows || totOffCol>=inMask->numCols) {
@@ -116,5 +119,5 @@
 
     // Check for incorrect types
-    inType = inImage->type.type;
+    inType = trimmedImg->type.type;
     flatType = flatImage->type.type;
     maskType = inMask->type.type;
@@ -145,6 +148,6 @@
 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++) {                                                      \
+    for(j = totOffRow; j < trimmedImg->numRows; j++) {                                                          \
+        for(i = totOffCol; i < trimmedImg->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 */           \
@@ -154,9 +157,9 @@
         }                                                                                                    \
     }                                                                                                        \
-    for(j = totOffRow; j < inImage->numRows; j++) {                                                          \
-        for(i = totOffCol; i < inImage->numCols; i++) {                                                      \
+    for(j = totOffRow; j < trimmedImg->numRows; j++) {                                                          \
+        for(i = totOffCol; i < trimmedImg->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];                                      \
+                trimmedImg->data.TYPE[j][i] /= flatImage->data.TYPE[j][i];                                      \
             }                                                                                                \
         }                                                                                                    \
Index: /trunk/psModules/src/detrend/pmMaskBadPixels.c
===================================================================
--- /trunk/psModules/src/detrend/pmMaskBadPixels.c	(revision 5515)
+++ /trunk/psModules/src/detrend/pmMaskBadPixels.c	(revision 5516)
@@ -19,6 +19,6 @@
  *  @author Ross Harman, MHPCC
  *
- *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-09-28 20:43:52 $
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-11-15 20:09:03 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -35,8 +35,88 @@
 #include "pmMaskBadPixels.h"
 #include "pmMaskBadPixelsErrors.h"
+#include "pmSubtractBias.h"
+
+/******************************************************************************
+GrowPixel(inMask, row, col, radius, maskVal): This private routine takes an
+input image mask and a pixel location, then sets (logical or) all pixels with
+parameter radius if that pixel to maskVal parameter.
+ *****************************************************************************/
+psBool GrowPixel(
+    psImage *inMask,
+    psS32 col,
+    psS32 row,
+    psS32 radius,
+    psU32 maskVal)
+{
+    psS32 rowMin = PS_MAX(row-radius, 0);
+    psS32 rowMax = PS_MIN(row+radius+1, inMask->numRows);
+    psS32 colMin = PS_MAX(col-radius, 0);
+    psS32 colMax = PS_MIN(col+radius+1, inMask->numCols);
+    psF32 squareRadius = PS_SQR(radius);
+
+
+    for(psS32 i=rowMin; i<rowMax; i++) {
+        for(psS32 j=colMin; j<colMax; j++) {
+            // Old code:
+            //            psF32 rRound = 0.5 + sqrtf((psF32) (((col-j)*(col-j)) + ((row-i)*(row-i))));
+            //            if(rRound <= radius) {
+            psF32 squareDis = (psF32) (((col-j)*(col-j)) + ((row-i)*(row-i)));
+            if (squareDis <= squareRadius) {
+                inMask->data.U8[i][j] |= maskVal;
+            }
+        }
+    }
+    return(true);
+}
+
+/******************************************************************************
+GetRadius(inImg, col, row, sat, growVal, grow): This private routine takes an
+input image and pixel location and determines what radius that pixel must grow
+by.
+ 
+//XXX: Inline this or macro it for speed.
+ *****************************************************************************/
+static psS32 GetRadius(
+    psImage *inImg,
+    psS32 col,
+    psS32 row,
+    psF32 sat,
+    psU32 growVal,
+    psS32 grow)
+{
+    psS32 growRadius = 0;
+    if (inImg->type.type == PS_TYPE_F32) {
+        if(inImg->data.F32[row][col] == growVal) {
+            growRadius = grow;
+        }
+        if (inImg->data.F32[row][col] > sat) {
+            growRadius = PS_MAX(growRadius+1, 2);
+        }
+    } else if (inImg->type.type == PS_TYPE_S32) {
+        if(inImg->data.S32[row][col] == growVal) {
+            growRadius = grow;
+        }
+        if (inImg->data.S32[row][col] > sat) {
+            growRadius = PS_MAX(growRadius+1, 2);
+        }
+    } else if (inImg->type.type == PS_TYPE_U16) {
+        if(inImg->data.U16[row][col] == growVal) {
+            growRadius = grow;
+        }
+        if (inImg->data.U16[row][col] > sat) {
+            growRadius = PS_MAX(growRadius+1, 2);
+        }
+    } else {
+        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
+                 PS_ERRORTEXT_pmMaskBadPixels_TYPE_MASK_IMAGE,
+                 inImg->type.type);
+    }
+    return(growRadius);
+}
+
 
 bool pmMaskBadPixels(
     pmReadout *in,
-    const psImage *mask,
+    const pmReadout *mask,
     unsigned int maskVal,
     float sat,
@@ -44,69 +124,57 @@
     int grow)
 {
+    // XXX: Review this code then put all asserts at the top.
+    PS_ASSERT_PTR_NON_NULL(in, true);
+    PS_ASSERT_PTR_NON_NULL(in->image, false);
+    PS_WARN_PTR_NON_NULL(in->parent);
+    if (in->parent != NULL) {
+        PS_WARN_PTR_NON_NULL(in->parent->concepts);
+    }
+    PS_ASSERT_PTR_NON_NULL(mask, false);
     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));
+    //
+    // Determine trimmed image from metadata.
+    //
+    psImage *trimmedImg = p_psDetermineTrimmedImage(in);
+    if(in->mask == NULL) {
+        in->mask = psImageAlloc(trimmedImg->numCols, trimmedImg->numRows, PS_TYPE_MASK);
+        PS_IMAGE_SET_U8(in->mask, 0);
     }
     inMask = in->mask;
 
     // Check input image and its mask are not larger than mask
-    if(inImage->numRows > mask->numRows || inImage->numCols > mask->numCols) {
+    if(trimmedImg->numRows > mask->image->numRows || trimmedImg->numCols > mask->image->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) {
+                 trimmedImg->numRows, trimmedImg->numCols, mask->image->numRows, mask->image->numCols);
+        return false;
+    } else if(inMask->numRows > mask->image->numRows || inMask->numCols > mask->image->numCols) {
         psError( PS_ERR_BAD_PARAMETER_SIZE, true,
                  PS_ERRORTEXT_pmMaskBadPixels_SIZE_MASK_IMAGE,
-                 inMask->numRows, inMask->numCols, mask->numRows, mask->numCols);
+                 inMask->numRows, inMask->numCols, mask->image->numRows, mask->image->numCols);
         return false;
     }
 
     // Determine total offset based on image offset with chip offset
-    totOffCol = inImage->col0 + in->col0;
-    totOffRow = inImage->row0 + in->row0;
+    totOffCol = trimmedImg->col0 + in->col0;
+    totOffRow = trimmedImg->row0 + in->row0;
 
     // Check that offsets are within image limits
-    if(totOffRow>=mask->numRows || totOffCol>=mask->numCols) {
+    if(totOffRow>=mask->image->numRows || totOffCol>=mask->image->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) {
+                 totOffRow, totOffCol, mask->image->numRows, mask->image->numCols);
+        return false;
+    } else if(totOffRow>=trimmedImg->numRows || totOffCol>=trimmedImg->numCols) {
         psError( PS_ERR_BAD_PARAMETER_SIZE, true,
                  PS_ERRORTEXT_pmMaskBadPixels_OFFSET_INPUT_IMAGE,
-                 totOffRow, totOffCol, inImage->numRows, inImage->numCols);
+                 totOffRow, totOffCol, trimmedImg->numRows, trimmedImg->numCols);
         return false;
     } else if(totOffRow>=inMask->numRows || totOffCol>=inMask->numCols) {
@@ -118,6 +186,6 @@
 
     // Check for incorrect types
-    inType = inImage->type.type;
-    maskType = mask->type.type;
+    inType = trimmedImg->type.type;
+    maskType = mask->image->type.type;
     if(PS_IS_PSELEMTYPE_COMPLEX(inType)) {
         psError( PS_ERR_BAD_PARAMETER_TYPE, true,
@@ -132,57 +200,34 @@
     }
 
-    // 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;
+    //
+    // This is the main loop which examines each pixel and masks pixels if
+    //    A: The mask matches the maskValue
+    //    B: The pixel is larger than the saturation value
+    //    C: The pixel equals the grow value (in which case a circle is masked)
+    //
+    for(i=totOffRow; i<trimmedImg->numRows; i++) {
+        for(j=totOffCol; j<trimmedImg->numCols; j++) {
+            //
+            // A: Pixels which satisfy maskVal shall be masked
+            //
+            if (mask->image->data.U8[i][j] == maskVal) {
+                in->mask->data.U8[i][j] |= maskVal;
+            }
+
+            //
+            // We first determine how much we need to grow by and store this in
+            // growRadius.
+            //
+            psS32 growRadius = GetRadius(trimmedImg, j, i, sat, growVal, grow);
+
+            //
+            // Grow the pixel mask if necessary.
+            //
+            if (growRadius != 0) {
+                GrowPixel(in->mask, j, i, growRadius, maskVal);
+            }
+        }
+    }
+
+    return true;
 }
Index: /trunk/psModules/src/detrend/pmMaskBadPixels.h
===================================================================
--- /trunk/psModules/src/detrend/pmMaskBadPixels.h	(revision 5515)
+++ /trunk/psModules/src/detrend/pmMaskBadPixels.h	(revision 5516)
@@ -19,6 +19,6 @@
  *  @author Ross Harman, MHPCC
  *
- *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-09-28 20:43:52 $
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-11-15 20:09:03 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -36,4 +36,5 @@
 } pmMaskValue;
 
+// XXX: Use PS_MIN, PS_MAX
 /** Macro to find maximum of two numbers */
 #define MAX(A,B)((A)>=(B)?(A):(B))
@@ -53,5 +54,5 @@
 bool pmMaskBadPixels(
     pmReadout *in,          ///< Readout containing input image data.
-    const psImage *mask,    ///< Mask data to be added to readout mask data.
+    const pmReadout *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.
Index: /trunk/psModules/src/detrend/pmNonLinear.c
===================================================================
--- /trunk/psModules/src/detrend/pmNonLinear.c	(revision 5515)
+++ /trunk/psModules/src/detrend/pmNonLinear.c	(revision 5516)
@@ -5,6 +5,6 @@
  *  @author GLG, MHPCC
  *
- *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-10-20 23:06:24 $
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-11-15 20:09:03 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -22,4 +22,5 @@
 
 #include "pmNonLinear.h"
+#include "pmSubtractBias.h"
 
 /******************************************************************************
@@ -37,11 +38,17 @@
     PS_ASSERT_IMAGE_TYPE(inputReadout->image, PS_TYPE_F32, NULL);
     PS_ASSERT_PTR_NON_NULL(input1DPoly, NULL);
+    PS_WARN_PTR_NON_NULL(inputReadout->parent);
+    if (inputReadout->parent != NULL) {
+        PS_WARN_PTR_NON_NULL(inputReadout->parent->concepts);
+    }
+    //
+    // Determine trimmed image from metadata.
+    //
+    psImage *trimmedImg = p_psDetermineTrimmedImage(inputReadout);
 
-    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]);
+    for (psS32 i=0;i<trimmedImg->numRows;i++) {
+        for (psS32 j=0;j<trimmedImg->numCols;j++) {
+            trimmedImg->data.F32[i][j] = psPolynomial1DEval(input1DPoly,
+                                         trimmedImg->data.F32[i][j]);
         }
     }
@@ -68,4 +75,13 @@
     PS_ASSERT_PTR_NON_NULL(inputReadout->image,NULL);
     PS_ASSERT_IMAGE_TYPE(inputReadout->image, PS_TYPE_F32, NULL);
+    PS_WARN_PTR_NON_NULL(inputReadout->parent);
+    if (inputReadout->parent != NULL) {
+        PS_WARN_PTR_NON_NULL(inputReadout->parent->concepts);
+    }
+    //
+    // Determine trimmed image from metadata.
+    //
+    psImage *trimmedImg = p_psDetermineTrimmedImage(inputReadout);
+
     psLookupTable *tmpLT = psLookupTableAlloc(filename, "%f %f", 0);
     psS32 numLines = psLookupTableRead(tmpLT);
@@ -75,9 +91,9 @@
                  "WARNING: Lookup Table is too small.  Returning original pmReadout.\n");
     } else {
-        for (psS32 i=0;i<inputReadout->image->numRows;i++) {
-            for (psS32 j=0;j<inputReadout->image->numCols;j++) {
-                psF64 tmpD = psLookupTableInterpolate(tmpLT, inputReadout->image->data.F32[i][j], 1);
+        for (psS32 i=0;i<trimmedImg->numRows;i++) {
+            for (psS32 j=0;j<trimmedImg->numCols;j++) {
+                psF64 tmpD = psLookupTableInterpolate(tmpLT, trimmedImg->data.F32[i][j], 1);
                 if (!isnan(tmpD)) {
-                    inputReadout->image->data.F32[i][j] = tmpD;
+                    trimmedImg->data.F32[i][j] = tmpD;
                 } else {
                     numPixels++;
Index: /trunk/psModules/src/imcombine/pmReadoutCombine.c
===================================================================
--- /trunk/psModules/src/imcombine/pmReadoutCombine.c	(revision 5515)
+++ /trunk/psModules/src/imcombine/pmReadoutCombine.c	(revision 5516)
@@ -5,6 +5,6 @@
  *  @author GLG, MHPCC
  *
- *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-09-28 20:43:52 $
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-11-15 20:09:03 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -374,2 +374,89 @@
     return(output);
 }
+
+
+/* This function measures the robust median at each of the minimum and maximum
+ * coordinates and determines the difference and mean of the two values. The size
+ * of the box used to make the measurement at each point is specified by the
+ * configuration variable FRINGE_SQUARE_RADIUS. From the collection of
+ * differences, the robust median is calculated, and returned as part of the
+ * fringe statistics. For each fringe point, the values of delta and midValue are
+ * also assigned and available to the user on return.
+ */
+
+psStats *pmFringeStats(
+    psArray *fringePoints,
+    psImage *image,
+    psMetadata *config)
+{
+    PS_ASSERT_PTR_NON_NULL(fringePoints, NULL);
+    //    for (psS32 i = 0 ; i < fringePoints->n ; i++) {
+    //        if (!psMemCheckFringePoint((pmFringePoint *) fringePoints->data[i])) {
+    //            psError(PS_ERR_UNKNOWN, true, "fringePoints->data[%d] is not of type pmFringePoint.\n");
+    //            return(NULL);
+    //        }
+    //    }
+    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(image, NULL);
+    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    psBool rc;
+    psF32 frSquareRadius = psMetadataLookupF32(&rc, config, "FRINGE_SQUARE_RADIUS");
+    if (!rc) {
+        psError(PS_ERR_UNKNOWN, true, "Could not determing the fringe radius from the metadata.\n");
+    }
+
+    psRegion minRegion;
+    psRegion maxRegion;
+    psStats *minStats = psStatsAlloc(PS_STAT_ROBUST_MEAN);
+    psStats *maxStats = psStatsAlloc(PS_STAT_ROBUST_MEAN);
+    psStats *diffStats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN);
+    psVector *diffs = psVectorAlloc(fringePoints->n, PS_TYPE_F32);
+
+    //
+    // Loop through each fringe point.  Determine the robust mean around
+    // the min and max for that fringe point.  Save the difference between
+    // those two numbers in psVector diffs.
+    //
+    // XXX: Ensure you have the radius correct.  (add 1 to x1 and y1?)
+    //
+    for (psS32 i = 0 ; i < fringePoints->n ; i++) {
+        pmFringePoint *fp = (pmFringePoint *) fringePoints->data[i];
+        minRegion.x0 = fp->xMin - frSquareRadius;
+        minRegion.x1 = fp->xMin + frSquareRadius;
+        minRegion.y0 = fp->yMin - frSquareRadius;
+        minRegion.y1 = fp->yMin + frSquareRadius;
+        psImage *minSubImage = psImageSubset(image, minRegion);
+        minStats = psImageStats(minStats, minSubImage, NULL, 0);
+
+        maxRegion.x0 = fp->xMax - frSquareRadius;
+        maxRegion.x1 = fp->xMax + frSquareRadius;
+        maxRegion.y0 = fp->yMax - frSquareRadius;
+        maxRegion.y1 = fp->yMax + frSquareRadius;
+        psImage *maxSubImage = psImageSubset(image, maxRegion);
+        maxStats = psImageStats(maxStats, maxSubImage, NULL, 0);
+
+        if ((minStats == NULL) || (maxStats == NULL)) {
+            psError(PS_ERR_UNKNOWN, true, "Could not determine robust mean on subimage.\n");
+            psFree(minStats);
+            psFree(maxStats);
+            return(NULL);
+        }
+
+        fp->midValue = 0.5 * (maxStats->robustMean + minStats->robustMean);
+        fp->delta = maxStats->robustMean - minStats->robustMean;
+        diffs->data.F32[i] = fp->delta;
+    }
+    psFree(minStats);
+    psFree(maxStats);
+
+    diffStats = psVectorStats(diffStats, diffs, NULL, NULL, 0);
+    psFree(diffs);
+    if (diffStats == NULL) {
+        psError(PS_ERR_UNKNOWN, true, "Could not determine robust median of the differences.\n");
+        return(NULL);
+    }
+    return(diffStats);
+}
+
Index: /trunk/psModules/src/imcombine/pmReadoutCombine.h
===================================================================
--- /trunk/psModules/src/imcombine/pmReadoutCombine.h	(revision 5515)
+++ /trunk/psModules/src/imcombine/pmReadoutCombine.h	(revision 5516)
@@ -5,6 +5,6 @@
  *  @author GLG, MHPCC
  *
- *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-09-28 20:43:52 $
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-11-15 20:09:03 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -44,3 +44,31 @@
                           float readnoise);
 
+/**
+ *
+ * This function measures the robust median at each of the minimum and maximum
+ * coordinates and determines the difference and mean of the two values. The size
+ * of the box used to make the measurement at each point is specified by the
+ * configuration variable FRINGE_SQUARE_RADIUS. From the collection of
+ * differences, the robust median is calculated, and returned as part of the
+ * fringe statistics. For each fringe point, the values of delta and midValue are
+ * also assigned and available to the user on return.
+ *
+ */
+psStats *pmFringeStats(
+    psArray *fringePoints,
+    psImage *image,
+    psMetadata *config
+);
+
+typedef struct
+{
+    psF64 xMin;
+    psF64 yMin;
+    psF64 xMax;
+    psF64 yMax;
+    psF64 delta;
+    psF64 midValue;
+}
+pmFringePoint;
+
 #endif
Index: /trunk/psModules/src/imsubtract/pmSubtractBias.c
===================================================================
--- /trunk/psModules/src/imsubtract/pmSubtractBias.c	(revision 5515)
+++ /trunk/psModules/src/imsubtract/pmSubtractBias.c	(revision 5516)
@@ -6,22 +6,28 @@
  *  @author GLG, MHPCC
  *
- *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-10-20 23:06:24 $
+ *  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-11-15 20:09:03 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
  *
  */
-
+/*****************************************************************************/
+/* INCLUDE FILES                                                             */
+/*****************************************************************************/
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include "pslib.h"
 #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
-
+/*****************************************************************************/
+/* DEFINE STATEMENTS                                                         */
+/*****************************************************************************/
 // XXX: put these in psConstants.h
-void PS_POLY1D_PRINT(psPolynomial1D *poly)
+void PS_POLY1D_PRINT(
+    psPolynomial1D *poly)
 {
     printf("-------------- PS_POLY1D_PRINT() --------------\n");
@@ -51,35 +57,42 @@
 }\
 
-/******************************************************************************
-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.
+/*****************************************************************************/
+/* TYPE DEFINITIONS                                                          */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* GLOBAL VARIABLES                                                          */
+/*****************************************************************************/
+psS32 currentId = 0;                // XXX: remove
+psS32 memLeaks = 0;                 // XXX: remove
+//PRINT_MEMLEAKS(8); XXX
+/*****************************************************************************/
+/* FILE STATIC VARIABLES                                                     */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - LOCAL                                           */
+/*****************************************************************************/
+
+/******************************************************************************
+psSubtractFrame(): this routine will take as input the pmReadout for the input
+image and a pmReadout for the bias image.  The bias image is subtracted in
+place from the input image.  We assume that sizes and types are checked
+elsewhere.
+ 
+XXX: Verify that the image and readout offsets are being used the right way.
+ 
+XXX: Ensure that it does the correct thing with image size.
 *****************************************************************************/
-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++) {
+static pmReadout *SubtractFrame(
+    pmReadout *in,
+    const pmReadout *bias)
+{
+    // XXX: When did the ->row0 and ->col0 offsets get coded?
+    for (psS32 i=0;i<in->image->numRows;i++) {
+        for (psS32 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])|=
@@ -92,13 +105,57 @@
 }
 
+
+/******************************************************************************
+psSubtractDarkFrame(): this routine will take as input the pmReadout for the
+input image and a pmReadout for the dark image.  The dark image is scaled and
+subtracted in place from the input image.
+ 
+XXX: Verify that the image and readout offsets are being used the right way.
+ 
+XXX: Ensure that it does the correct thing with image size.
+*****************************************************************************/
+static pmReadout *SubtractDarkFrame(
+    pmReadout *in,
+    const pmReadout *dark,
+    psF32 scale)
+{
+    // XXX: When did the ->row0 and ->col0 offsets get coded?
+    if (fabs(scale) > FLT_EPSILON) {
+        for (psS32 i=0;i<in->image->numRows;i++) {
+            for (psS32 j=0;j<in->image->numCols;j++) {
+                in->image->data.F32[i][j]-=
+                    (scale * dark->image->data.F32[i+in->row0-dark->row0][j+in->col0-dark->col0]);
+
+                if ((in->mask != NULL) && (dark->mask != NULL)) {
+                    (in->mask->data.U8[i][j])|=
+                        dark->mask->data.U8[i+in->row0-dark->row0][j+in->col0-dark->col0];
+                }
+            }
+        }
+    } else {
+        for (psS32 i=0;i<in->image->numRows;i++) {
+            for (psS32 j=0;j<in->image->numCols;j++) {
+                in->image->data.F32[i][j]-=
+                    dark->image->data.F32[i+in->row0-dark->row0][j+in->col0-dark->col0];
+
+                if ((in->mask != NULL) && (dark->mask != NULL)) {
+                    (in->mask->data.U8[i][j])|=
+                        dark->mask->data.U8[i+in->row0-dark->row0][j+in->col0-dark->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)
+XXX: Is there a psLib function for this?
+ *****************************************************************************/
+static psImage *ImageSubtractScalar(
+    psImage *image,
+    psF32 scalar)
 {
     for (psS32 i=0;i<image->numRows;i++) {
@@ -164,5 +221,5 @@
 
     if (numOptions == 0) {
-        psError(PS_ERR_UNKNOWN,true, "No statistics options have been specified.\n");
+        psError(PS_ERR_UNKNOWN,true, "No allowable statistics options have been specified.\n");
     }
     if (numOptions != 1) {
@@ -173,5 +230,88 @@
 }
 
-
+/******************************************************************************
+Polynomial1DCopy(): This private function copies the members of the existing
+psPolynomial1D "in" into the existing psPolynomial1D "out".  The previous
+members of the existing psPolynomial1D "out" are psFree'ed.
+ *****************************************************************************/
+static psBool Polynomial1DCopy(
+    psPolynomial1D *out,
+    psPolynomial1D *in)
+{
+    psFree(out->coeff);
+    psFree(out->coeffErr);
+    psFree(out->mask);
+
+    out->type = in->type;
+    out->nX = in->nX;
+
+    out->coeff = (psF64 *) psAlloc((in->nX + 1) * sizeof(psF64));
+    // XXX: use memcpy
+    for (psS32 i = 0 ; i < (in->nX + 1) ; i++) {
+        out->coeff[i] = in->coeff[i];
+    }
+
+    out->coeffErr = (psF64 *) psAlloc((in->nX + 1) * sizeof(psF64));
+    // XXX: use memcpy
+    for (psS32 i = 0 ; i < (in->nX + 1) ; i++) {
+        out->coeffErr[i] = in->coeffErr[i];
+    }
+
+    out->mask = (psMaskType *) psAlloc((in->nX + 1) * sizeof(psMaskType));
+    // XXX: use memcpy
+    for (psS32 i = 0 ; i < (in->nX + 1) ; i++) {
+        out->mask[i] = in->mask[i];
+    }
+
+    return(true);
+}
+
+/******************************************************************************
+Polynomial1DDup(): This private function duplicates and then returns the input
+psPolynomial1D "in".
+ *****************************************************************************/
+static psPolynomial1D *Polynomial1DDup(
+    psPolynomial1D *in)
+{
+    psPolynomial1D *out = psPolynomial1DAlloc(in->nX, in->type);
+    Polynomial1DCopy(out, in);
+    return(out);
+}
+
+
+/******************************************************************************
+SplineCopy(): This private function copies the members of the existing
+psSpline in into the existing psSpline out.
+ *****************************************************************************/
+static psBool SplineCopy(
+    psSpline1D *out,
+    psSpline1D *in)
+{
+    PS_ASSERT_PTR_NON_NULL(out, false);
+    PS_ASSERT_PTR_NON_NULL(in, false);
+
+    for (psS32 i = 0 ; i < out->n ; i++) {
+        psFree(out->spline[i]);
+    }
+    psFree(out->spline);
+    psFree(out->knots);
+    psFree(out->p_psDeriv2);
+
+    out->n = in->n;
+    out->spline = (psPolynomial1D **) psAlloc(in->n * sizeof(psPolynomial1D *));
+    for (psS32 i = 0 ; i < in->n ; i++) {
+        out->spline[i] = Polynomial1DDup(in->spline[i]);
+    }
+
+    out->knots = psVectorCopy(out->knots, in->knots, in->knots->type.type);
+
+    out->p_psDeriv2 = (psF32 *) psAlloc((in->n + 1) * sizeof(psF32));
+    // XXX: use memcpy
+    for (psS32 i = 0 ; i < (in->n + 1) ; i++) {
+        out->p_psDeriv2[i] = in->p_psDeriv2[i];
+    }
+
+    return(true);
+}
 
 /******************************************************************************
@@ -181,16 +321,14 @@
     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)
+The resulting spline or polynomial is set in the fitSpec argument.
+ *****************************************************************************/
+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) {
@@ -205,21 +343,16 @@
     //
     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;
+        return(psVectorCopy(newVec, overscanVector, PS_TYPE_F32));
+    }
     psF32 x;
-    psS32 i;
 
     if (fit == PM_FIT_POLYNOMIAL) {
         // Fit a polynomial to the old overscan vector.
-        myPoly = (psPolynomial1D *) fitSpec;
+        psPolynomial1D *myPoly = (psPolynomial1D *) fitSpec;
         PS_ASSERT_POLY_NON_NULL(myPoly, NULL);
+        PS_ASSERT_POLY1D(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");
+            psError(PS_ERR_UNKNOWN, false, "Could not fit a polynomial to the psVector.\n");
             return(NULL);
         }
@@ -228,17 +361,9 @@
         // 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++) {
+        for (psS32 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;
-        // XXX: Does it make any sense to have a psSpline argument?
-        if (mySpline == NULL) {
-            mustFreeSpline = 1;
-        }
-
         //
         // NOTE: Since the X arg in the psVectorFitSpline1D() function is NULL,
@@ -246,26 +371,26 @@
         // properly when doing the spline eval.
         //
-        //        mySpline = psVectorFitSpline1D(mySpline, NULL, overscanVector, NULL);
-        mySpline = psVectorFitSpline1D(NULL, overscanVector);
+        psSpline1D *mySpline = psVectorFitSpline1D(NULL, overscanVector);
         if (mySpline == NULL) {
-            psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector()(2): Could not fit a spline to the psVector.\n");
+            psError(PS_ERR_UNKNOWN, false, "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
+        // of the old vector, use the fitted spline to determine the
         // interpolated value at that point, and set the new vector.
-        for (i=0;i<n;i++) {
+        for (psS32 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);
-
-
+
+        psSpline1D *ptrSpline = (psSpline1D *) fitSpec;
+        if (ptrSpline != NULL) {
+            // Copy the resulting spline fit into ptrSpline.
+            PS_ASSERT_SPLINE(ptrSpline, NULL);
+            SplineCopy(ptrSpline, mySpline);
+        }
+        psFree(mySpline);
     } else {
         psError(PS_ERR_UNKNOWN, true, "unknown fit type.  Returning NULL.\n");
@@ -280,5 +405,754 @@
 
 /******************************************************************************
-XXX: The SDRS does not specify type support.  F32 is implemented here.
+ *****************************************************************************/
+static psS32 GetOverscanSize(
+    psImage *inImg,
+    pmOverscanAxis overScanAxis)
+{
+    if (overScanAxis == PM_OVERSCAN_ROWS) {
+        return(inImg->numCols);
+    } else if (overScanAxis == PM_OVERSCAN_COLUMNS) {
+        return(inImg->numRows);
+    } else if (overScanAxis == PM_OVERSCAN_ALL) {
+        return(1);
+    }
+    return(0);
+}
+
+/******************************************************************************
+GetOverscanAxis(in) this private routine determines the appropiate overscan
+axis from the parent cell metadata.
+ 
+XXX: Verify the READDIR corresponds with my overscan axis.
+ *****************************************************************************/
+static pmOverscanAxis GetOverscanAxis(pmReadout *in)
+{
+    psBool rc;
+    if ((in->parent != NULL) && (in->parent->concepts)) {
+        psS32 dir = psMetadataLookupS32(&rc, in->parent->concepts, "CELL.READDIR");
+        if (rc == true) {
+            if (dir == 1) {
+                return(PM_OVERSCAN_ROWS);
+            } else if (dir == 2) {
+                return(PM_OVERSCAN_COLUMNS);
+            } else if (dir == 3) {
+                return(PM_OVERSCAN_ALL);
+            }
+        }
+    }
+
+    psLogMsg(__func__, PS_LOG_WARN,
+             "WARNING: pmSubtractBias.(): could not determine CELL.READDIR from in->parent metadata.  Setting overscan axis to PM_OVERSCAN_NONE.\n");
+    return(PM_OVERSCAN_NONE);
+}
+
+/******************************************************************************
+psListLength(list): determine the length of a psList.
+ 
+XXX: Put this elsewhere.
+ *****************************************************************************/
+static psS32 psListLength(
+    psList *list)
+{
+    psS32 length = 0;
+    psListElem *tmpElem = (psListElem *) list->head;
+    while (NULL != tmpElem) {
+        tmpElem = tmpElem->next;
+        length++;
+    }
+    return(length);
+}
+
+/******************************************************************************
+Note: this isn't needed anymore as of psModule SDRS 12-09.
+ *****************************************************************************/
+static psBool OverscanReducePixel(
+    psImage *in,
+    psList *bias,
+    psStats *myStats)
+{
+    PS_ASSERT_PTR_NON_NULL(in, NULL);
+    PS_ASSERT_PTR_NON_NULL(bias, NULL);
+    PS_ASSERT_PTR_NON_NULL(bias->head, NULL);
+    PS_ASSERT_PTR_NON_NULL(myStats, NULL);
+
+    // Allocate a psVector with one element per overscan image.
+    psS32 numOverscanImages = psListLength(bias);
+    psVector *statsAll = psVectorAlloc(numOverscanImages, PS_TYPE_F32);
+    psListElem *tmpOverscan = (psListElem *) bias->head;
+    psS32 i = 0;
+    psF64 statValue;
+    //
+    // We loop through each overscan image, calculating the specified
+    // statistic on that image.
+    //
+    while (NULL != tmpOverscan) {
+        psImage *myOverscanImage = (psImage *) tmpOverscan->data;
+
+        PS_ASSERT_IMAGE_TYPE(myOverscanImage, PS_TYPE_F32, NULL);
+        myStats = psImageStats(myStats, myOverscanImage, NULL, (psMaskType)0xffffffff);
+        if (myStats == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "psImageStats(): could not perform requested statistical operation.  Returning in image.\n");
+            psFree(statsAll);
+            return(false);
+        }
+        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");
+            psFree(statsAll);
+            return(false);
+        }
+        statsAll->data.F32[i] = statValue;
+        i++;
+        tmpOverscan = tmpOverscan->next;
+    }
+
+    //
+    // We reduce the individual stats for each overscan image to
+    // a single psF32.
+    //
+    myStats = psVectorStats(myStats, statsAll, NULL, NULL, 0);
+    if (myStats == NULL) {
+        psError(PS_ERR_UNKNOWN, false, "psImageStats(): could not perform requested statistical operation.  Returning in image.\n");
+        psFree(statsAll);
+        return(false);
+    }
+    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");
+        psFree(statsAll);
+        return(false);
+    }
+
+    //
+    // Subtract the result and return.
+    //
+    ImageSubtractScalar(in, statValue);
+    psFree(statsAll);
+    return(in);
+}
+
+/******************************************************************************
+ReduceOverscanImageToCol(overscanImage, myStats): This private routine reduces
+a single psImage to a column by combining all pixels from each row into a
+single pixel via requested statistic in myStats.
+ *****************************************************************************/
+static psVector *ReduceOverscanImageToCol(
+    psImage *overscanImage,
+    psStats *myStats)
+{
+    psF64 statValue;
+    psVector *tmpRow = psVectorAlloc(overscanImage->numCols, PS_TYPE_F32);
+    psVector *tmpCol = psVectorAlloc(overscanImage->numRows, PS_TYPE_F32);
+
+    //
+    // For each row, we store all pixels in that row into a temporary psVector,
+    // then we run psVectorStats() on that vector.
+    //
+    for (psS32 i=0;i<overscanImage->numRows;i++) {
+        for (psS32 j=0;j<overscanImage->numCols;j++) {
+            tmpRow->data.F32[j] = overscanImage->data.F32[i][j];
+        }
+
+        psStats *rc = psVectorStats(myStats, tmpRow, NULL, NULL, 0);
+        if (rc == NULL) {
+            psError(PS_ERR_UNKNOWN, true, "psVectorStats() could not perform requested statistical operation.  Returning in image.\n");
+            return(NULL);
+        }
+
+        if (false ==  p_psGetStatValue(rc, &statValue)) {
+            psError(PS_ERR_UNKNOWN, true, "p_psGetStatValue() could not determine result from requested statistical operation.  Returning in image.\n");
+            return(NULL);
+        }
+
+        tmpCol->data.F32[i] = (psF32) statValue;
+    }
+    psFree(tmpRow);
+
+    return(tmpCol);
+}
+
+/******************************************************************************
+ReduceOverscanImageToCol(overscanImage, myStats): This private routine reduces
+a single psImage to a row by combining all pixels from each column into a
+single pixel via requested statistic in myStats.
+ *****************************************************************************/
+static psVector *ReduceOverscanImageToRow(
+    psImage *overscanImage,
+    psStats *myStats)
+{
+    psF64 statValue;
+    psVector *tmpRow = psVectorAlloc(overscanImage->numCols, PS_TYPE_F32);
+    psVector *tmpCol = psVectorAlloc(overscanImage->numRows, PS_TYPE_F32);
+
+    //
+    // For each column, we store all pixels in that column into a temporary psVector,
+    // then we run psVectorStats() on that vector.
+    //
+    for (psS32 i=0;i<overscanImage->numCols;i++) {
+        for (psS32 j=0;j<overscanImage->numRows;j++) {
+            tmpCol->data.F32[j] = overscanImage->data.F32[j][i];
+        }
+
+        psStats *rc = psVectorStats(myStats, tmpCol, NULL, NULL, 0);
+        if (rc == NULL) {
+            psError(PS_ERR_UNKNOWN, true, "psVectorStats() could not perform requested statistical operation.  Returning in image.\n");
+            return(NULL);
+        }
+
+        if (false ==  p_psGetStatValue(rc, &statValue)) {
+            psError(PS_ERR_UNKNOWN, true, "p_psGetStatValue() could not determine result from requested statistical operation.  Returning in image.\n");
+            return(NULL);
+        }
+
+        tmpRow->data.F32[i] = (psF32) statValue;
+    }
+    psFree(tmpCol);
+
+    return(tmpRow);
+}
+
+/******************************************************************************
+OverscanReduce(vecSize, bias, myStats): This private routine takes a psList of
+overscan images (in bias) and reduces them to a single psVector via the
+specified psStats struct.  The vector is then scaled to the length or the
+row/column in inImg.
+ *****************************************************************************/
+static psVector* OverscanReduce(
+    psImage *inImg,
+    pmOverscanAxis overScanAxis,
+    psList *bias,
+    void *fitSpec,
+    pmFit fit,
+    psStats *myStats)
+{
+    if ((overScanAxis != PM_OVERSCAN_ROWS) && (overScanAxis != PM_OVERSCAN_COLUMNS)) {
+        psError(PS_ERR_UNKNOWN, true, "overScanAxis must be PM_OVERSCAN_ROWS or PM_OVERSCAN_COLUMNS\n");
+        return(NULL);
+    }
+    PS_ASSERT_PTR_NON_NULL(inImg, NULL);
+    PS_ASSERT_PTR_NON_NULL(bias, NULL);
+    PS_ASSERT_PTR_NON_NULL(bias->head, NULL);
+    PS_ASSERT_PTR_NON_NULL(myStats, NULL);
+    //
+    // Allocate a psVector for the output of this routine.
+    //
+    psS32 vecSize = GetOverscanSize(inImg, overScanAxis);
+    psVector *overscanVector = psVectorAlloc(vecSize, PS_TYPE_F32);
+
+    //
+    // Allocate an array of psVectors with one psVector per element of the
+    // final oversan column vector.  These psVectors will be used with
+    // psStats to reduce the multiple elements from each overscan column
+    // vector to a single final column vector.
+    //
+    psS32 numOverscanImages = psListLength(bias);
+    psVector **overscanVectors = (psVector **) psAlloc(numOverscanImages * sizeof(psVector *));
+    for (psS32 i = 0 ; i < numOverscanImages ; i++) {
+        overscanVectors[i] = NULL;
+    }
+
+    //
+    // We iterate through the list of overscan images.  For each image,
+    // we reduce it to a single column or row.  Save the overscan vector
+    // in overscanVectors[].
+    //
+    psListElem *tmpOverscan = (psListElem *) bias->head;
+    psS32 overscanID = 0;
+    while (tmpOverscan != NULL) {
+        psImage *tmpOverscanImage = (psImage *) tmpOverscan->data;
+        if (overScanAxis == PM_OVERSCAN_ROWS) {
+            overscanVectors[overscanID] = ReduceOverscanImageToRow(tmpOverscanImage, myStats);
+        } else if (overScanAxis == PM_OVERSCAN_COLUMNS) {
+            overscanVectors[overscanID] = ReduceOverscanImageToCol(tmpOverscanImage, myStats);
+        }
+
+        tmpOverscan = tmpOverscan->next;
+        overscanID++;
+    }
+
+    //
+    // For each overscan vector, if necessary, we scale that column or
+    // row to vecSize.  Note: we should have already ensured that the
+    // fit is poly or spline.
+    //
+    for (psS32 i = 0 ; i < numOverscanImages ; i++) {
+        psVector *tmpOverscanVector = overscanVectors[i];
+
+        if (tmpOverscanVector->n != vecSize) {
+            overscanVectors[i] = ScaleOverscanVector(tmpOverscanVector, vecSize, fitSpec, fit);
+            if (overscanVectors[i] == NULL) {
+                psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector(): could not scale the overscan vector.\n");
+                for (psS32 i = 0 ; i < numOverscanImages ; i++) {
+                    psFree(overscanVectors[i]);
+                }
+                psFree(overscanVectors);
+                psFree(tmpOverscanVector);
+                return(NULL);
+            }
+            psFree(tmpOverscanVector);
+        }
+    }
+
+    //
+    // We collect all elements in the overscan vectors for the various
+    // overscan images into a single psVector (tmpVec).  Then we call
+    // psStats on that vector to determine the final values for the
+    // overscan vector.
+    //
+    psVector *tmpVec = psVectorAlloc(numOverscanImages, PS_TYPE_F32);
+    psF64 statValue;
+    for (psS32 i = 0 ; i < vecSize ; i++) {
+        // Collect the i-th elements from each overscan vector into a single vector.
+        for (psS32 j = 0 ; j < numOverscanImages ; j++) {
+            tmpVec->data.F32[j] = overscanVectors[j]->data.F32[i];
+        }
+
+        if (NULL == psVectorStats(myStats, tmpVec, NULL, NULL, 0)) {
+            psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning in image.\n");
+            for (psS32 i = 0 ; i < numOverscanImages ; i++) {
+                psFree(overscanVectors[i]);
+            }
+            psFree(overscanVectors);
+            psFree(tmpVec);
+            return(NULL);
+        }
+        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");
+            for (psS32 i = 0 ; i < numOverscanImages ; i++) {
+                psFree(overscanVectors[i]);
+            }
+            psFree(overscanVectors);
+            psFree(tmpVec);
+            return(NULL);
+        }
+
+        overscanVector->data.F32[i] = (psF32) statValue;
+    }
+
+    //
+    // We're done.  Free the intermediate overscan vectors.
+    //
+    psFree(tmpVec);
+    for (psS32 i = 0 ; i < numOverscanImages ; i++) {
+        psFree(overscanVectors[i]);
+    }
+    psFree(overscanVectors);
+
+    //
+    // Return the computed overscanVector
+    //
+    return(overscanVector);
+}
+
+/******************************************************************************
+RebinOverscanVector(overscanVector, nBinOrig, myStats): this private routine
+takes groups of nBinOrig elements in the input vector, combines them into a
+single pixel via myStats and psVectorStats(), and then outputs a vector of
+those pixels.
+ *****************************************************************************/
+static psS32 RebinOverscanVector(
+    psVector *overscanVector,
+    psS32 nBinOrig,
+    psStats *myStats)
+{
+    psF64 statValue;
+    psS32 nBin;
+    if ((nBinOrig > 1) && (nBinOrig < overscanVector->n)) {
+        psS32 numBins = 1+((overscanVector->n)/nBinOrig);
+        psVector *myBin = psVectorAlloc(numBins, PS_TYPE_F32);
+        psVector *binVec = psVectorAlloc(nBinOrig, PS_TYPE_F32);
+
+        for (psS32 i=0;i<numBins;i++) {
+            for(psS32 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(-1);
+            }
+            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(-1);
+            }
+            myBin->data.F32[i] = statValue;
+        }
+
+        // Change the effective size of overscanVector.
+        overscanVector->n = numBins;
+        for (psS32 i=0;i<numBins;i++) {
+            overscanVector->data.F32[i] = myBin->data.F32[i];
+        }
+        psFree(binVec);
+        psFree(myBin);
+        nBin = nBinOrig;
+    } else {
+        nBin = 1;
+    }
+
+    return(nBin);
+}
+
+/******************************************************************************
+FitOverscanVectorAndUnbin(inImg, overscanVector, overScanAxis, fitSpec, fit,
+nBin):  this private routine fits a psPolynomial or psSpline to the overscan
+vector.  It then creates a new vector, with a size determined by the input
+image, evaluates the psPolynomial or psSpline at each element in that vector,
+then returns that vector.
+ *****************************************************************************/
+static psVector *FitOverscanVectorAndUnbin(
+    psImage *inImg,
+    psVector *overscanVector,
+    pmOverscanAxis overScanAxis,
+    void *fitSpec,
+    pmFit fit,
+    psS32 nBin)
+{
+    psPolynomial1D* myPoly = NULL;
+    psSpline1D *mySpline = NULL;
+    //
+    // Fit a polynomial or spline to the overscan vector.
+    //
+    if (fit == PM_FIT_POLYNOMIAL) {
+        myPoly = (psPolynomial1D *) fitSpec;
+        PS_ASSERT_POLY_NON_NULL(myPoly, NULL);
+        PS_ASSERT_POLY1D(myPoly, NULL);
+        myPoly = psVectorFitPolynomial1D(myPoly, NULL, 0, overscanVector, NULL, NULL);
+        if (myPoly == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "Could not fit a polynomial to overscan vector.  Returning NULL.\n");
+            return(NULL);
+        }
+    } else if (fit == PM_FIT_SPLINE) {
+        mySpline = psVectorFitSpline1D(NULL, overscanVector);
+        if (mySpline == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "Could not fit a spline to overscan vector.  Returning NULL.\n");
+            return(NULL);
+        }
+        if (fitSpec != NULL) {
+            // Copy the resulting spline fit into fitSpec.
+            psSpline1D *ptrSpline = (psSpline1D *) fitSpec;
+            PS_ASSERT_SPLINE(ptrSpline, NULL);
+            SplineCopy(ptrSpline, mySpline);
+        }
+    }
+
+    //
+    // Evaluate the poly/spline at each pixel in the overscan row/column.
+    //
+    psS32 vecSize = GetOverscanSize(inImg, overScanAxis);
+    psVector *newVec = psVectorAlloc(vecSize, PS_TYPE_F32);
+    if ((nBin > 1) && (nBin < overscanVector->n)) {
+        for (psS32 i = 0 ; i < vecSize ; i++) {
+            if (fit == PM_FIT_POLYNOMIAL) {
+                newVec->data.F32[i] = psPolynomial1DEval(myPoly, ((psF32) i) / ((psF32) nBin));
+            } else if (fit == PM_FIT_SPLINE) {
+                newVec->data.F32[i] = psSpline1DEval(mySpline, ((psF32) i) / ((psF32) nBin));
+            }
+        }
+    } else {
+        for (psS32 i = 0 ; i < vecSize ; i++) {
+            if (fit == PM_FIT_POLYNOMIAL) {
+                newVec->data.F32[i] = psPolynomial1DEval(myPoly, (psF32) i);
+            } else if (fit == PM_FIT_SPLINE) {
+                newVec->data.F32[i] = psSpline1DEval(mySpline, (psF32) i);
+            }
+        }
+    }
+
+    psFree(mySpline);
+    psFree(overscanVector);
+    return(newVec);
+}
+
+
+
+/******************************************************************************
+UnbinOverscanVector(inImg, overscanVector, overScanAxis, nBin):  this private
+routine takes a psVector overscanVector that was previously binned by a factor
+of nBin, and then expands it to its original size, duplicated elements nBin
+times for each element in the input vector overscanVector.
+ *****************************************************************************/
+static psVector *UnbinOverscanVector(
+    psImage *inImg,
+    psVector *overscanVector,
+    pmOverscanAxis overScanAxis,
+    psS32 nBin)
+{
+    psS32 vecSize;
+
+    if (overScanAxis == PM_OVERSCAN_ROWS) {
+        vecSize = inImg->numCols;
+    } else if (overScanAxis == PM_OVERSCAN_COLUMNS) {
+        vecSize = inImg->numRows;
+    }
+
+    psVector *newVec = psVectorAlloc(vecSize, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < vecSize ; i++) {
+        newVec->data.F32[i] = overscanVector->data.F32[i/nBin];
+    }
+
+    psFree(overscanVector);
+    return(newVec);
+}
+
+
+/******************************************************************************
+SubtractVectorFromImage(inImg, overscanVector, overScanAxis):  this private
+routine subtracts the overscanVector column-wise or row-wise from inImg.
+ *****************************************************************************/
+static psImage *SubtractVectorFromImage(
+    psImage *inImg,
+    psVector *overscanVector,
+    pmOverscanAxis overScanAxis)
+{
+    //
+    // Subtract overscan vector row-wise from the image.
+    //
+    if (overScanAxis == PM_OVERSCAN_ROWS) {
+        for (psS32 i=0;i<inImg->numCols;i++) {
+            for (psS32 j=0;j<inImg->numRows;j++) {
+                inImg->data.F32[j][i]-= overscanVector->data.F32[i];
+            }
+        }
+    }
+
+    //
+    // Subtract overscan vector column-wise from the image.
+    //
+    if (overScanAxis == PM_OVERSCAN_COLUMNS) {
+        for (psS32 i=0;i<inImg->numRows;i++) {
+            for (psS32 j=0;j<inImg->numCols;j++) {
+                inImg->data.F32[i][j]-= overscanVector->data.F32[i];
+            }
+        }
+    }
+
+    return(inImg);
+}
+
+
+
+typedef enum {
+    PM_ERROR_NO_SUBTRACTION,
+    PM_WARNING_NO_SUBTRACTION,
+    PM_ERROR_NO_BIAS_SUBTRACT,
+    PM_WARNING_NO_BIAS_SUBTRACT,
+    PM_ERROR_NO_DARK_SUBTRACT,
+    PM_WARNING_NO_DARK_SUBTRACT,
+    PM_OKAY
+} pmSubtractBiasAssertStatus;
+/******************************************************************************
+AssertCodeOverscan(....) this private routine verifies that the various input
+parameters to pmSubtractBias() are correct for overscan subtraction.
+ *****************************************************************************/
+pmSubtractBiasAssertStatus AssertCodeOverscan(
+    pmReadout *in,
+    void *fitSpec,
+    pmFit fit,
+    bool overscan,
+    psStats *stat,
+    int nBinOrig,
+    const pmReadout *bias,
+    const pmReadout *dark)
+{
+
+    PS_ASSERT_READOUT_NON_NULL(in, PM_ERROR_NO_SUBTRACTION);
+    PS_ASSERT_READOUT_NON_EMPTY(in, PM_ERROR_NO_SUBTRACTION);
+    PS_ASSERT_READOUT_TYPE(in, PS_TYPE_F32, PM_ERROR_NO_SUBTRACTION);
+    PS_WARN_PTR_NON_NULL(in->parent);
+    if (in->parent != NULL) {
+        PS_WARN_PTR_NON_NULL(in->parent->concepts);
+    }
+
+    if (overscan == true) {
+        pmOverscanAxis overScanAxis = GetOverscanAxis(in);
+        PS_ASSERT_PTR_NON_NULL(stat, PM_ERROR_NO_SUBTRACTION);
+        PS_ASSERT_PTR_NON_NULL(in->bias, PM_ERROR_NO_SUBTRACTION);
+        PS_ASSERT_PTR_NON_NULL(in->bias->head, PM_ERROR_NO_SUBTRACTION);
+        //
+        // Check the type, size of each bias image.
+        //
+        psListElem *tmpOverscan = (psListElem *) in->bias->head;
+        psS32 numOverscans = 0;
+        while (NULL != tmpOverscan) {
+            numOverscans++;
+            psImage *myOverscanImage = (psImage *) tmpOverscan->data;
+            PS_ASSERT_IMAGE_TYPE(myOverscanImage, PS_TYPE_F32, PM_ERROR_NO_SUBTRACTION);
+            // XXX: Get this right with the rows and columns.
+            if (overScanAxis == PM_OVERSCAN_ROWS) {
+                if (myOverscanImage->numRows != in->image->numRows) {
+                    psLogMsg(__func__, PS_LOG_WARN,
+                             "WARNING: pmSubtractBias.(): overscan image (# %d) has %d rows, input image has %d rows\n",
+                             numOverscans, myOverscanImage->numCols, in->image->numRows);
+                    if (fit == PM_FIT_NONE) {
+                        psError(PS_ERR_UNKNOWN, true, "Don't know how to scale the overscan vectors.  Set fit to PM_FIT_POLYNOMIAL or PM_FIT_SPLINE.\n");
+                        return(PM_ERROR_NO_SUBTRACTION);
+                    }
+                }
+            } else if (overScanAxis == PM_OVERSCAN_COLUMNS) {
+                if (myOverscanImage->numCols != in->image->numCols) {
+                    psLogMsg(__func__, PS_LOG_WARN,
+                             "WARNING: pmSubtractBias.(): overscan image (# %d) has %d columns, input image has %d columns\n",
+                             numOverscans, myOverscanImage->numCols, in->image->numCols);
+                    if (fit == PM_FIT_NONE) {
+                        psError(PS_ERR_UNKNOWN, true, "Don't know how to scale the overscan vectors.  Set fit to PM_FIT_POLYNOMIAL or PM_FIT_SPLINE.\n");
+                        return(PM_ERROR_NO_SUBTRACTION);
+                    }
+                }
+            } else if (overScanAxis != PM_OVERSCAN_ALL) {
+                psError(PS_ERR_UNKNOWN, true, "Must specify and overscan axis.\n");
+                return(PM_ERROR_NO_SUBTRACTION);
+            }
+            tmpOverscan = tmpOverscan->next;
+        }
+    } else {
+        if (fit != PM_FIT_NONE) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: pmSubtractBias.(): overscan is FALSE and fit is not PM_FIT_NONE.\n");
+            return(PM_WARNING_NO_SUBTRACTION);
+        }
+    }
+
+    // XXX: I do not like the following spec since it's useless to specify
+    // a psSpline as the fitSpec.
+    if (0) {
+        if ((fitSpec == NULL) &&
+                ((fit != PM_FIT_NONE) || (overscan == true))) {
+            psError(PS_ERR_UNKNOWN, true, "fitSpec is NULL and fit is not PM_FIT_NONE or overscan is TRUE.\n");
+            return(PM_ERROR_NO_SUBTRACTION);
+        }
+    }
+
+    return(PM_OKAY);
+}
+
+/******************************************************************************
+AssertCodeBias(....) this private routine verifies that the various input
+parameters to pmSubtractBias() are correct for bias subtraction.
+ *****************************************************************************/
+static pmSubtractBiasAssertStatus AssertCodeBias(
+    pmReadout *in,
+    void *fitSpec,
+    pmFit fit,
+    bool overscan,
+    psStats *stat,
+    int nBinOrig,
+    const pmReadout *bias,
+    const pmReadout *dark)
+{
+    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(PM_ERROR_NO_BIAS_SUBTRACT);
+    }
+    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(PM_ERROR_NO_BIAS_SUBTRACT);
+    }
+
+    if (bias != NULL) {
+        PS_ASSERT_READOUT_NON_EMPTY(bias, PM_ERROR_NO_BIAS_SUBTRACT);
+        PS_ASSERT_READOUT_TYPE(bias, PS_TYPE_F32, PM_ERROR_NO_DARK_SUBTRACT);
+    }
+    return(PM_OKAY);
+}
+
+/******************************************************************************
+AssertCodeDark(....) this private routine verifies that the various input
+parameters to pmSubtractBias() are correct for dark subtraction.
+ *****************************************************************************/
+pmSubtractBiasAssertStatus AssertCodeDark(
+    pmReadout *in,
+    void *fitSpec,
+    pmFit fit,
+    bool overscan,
+    psStats *stat,
+    int nBinOrig,
+    const pmReadout *bias,
+    const pmReadout *dark)
+{
+    if ((in->image->numRows + in->row0 - dark->row0) > dark->image->numRows) {
+        psError(PS_ERR_UNKNOWN, true, "dark image does not have enough rows.  Returning in image\n");
+        return(PM_ERROR_NO_DARK_SUBTRACT);
+    }
+    if ((in->image->numCols + in->col0 - dark->col0) > dark->image->numCols) {
+        psError(PS_ERR_UNKNOWN, true, "dark image does not have enough columns.  Returning in image\n");
+        return(PM_ERROR_NO_DARK_SUBTRACT);
+    }
+
+    if (dark != NULL) {
+        PS_ASSERT_READOUT_NON_EMPTY(dark, PM_ERROR_NO_DARK_SUBTRACT);
+        PS_ASSERT_READOUT_TYPE(dark, PS_TYPE_F32, PM_ERROR_NO_DARK_SUBTRACT);
+    }
+    return(PM_OKAY);
+}
+
+/******************************************************************************
+p_psDetermineTrimmedImage(): global routine: determines the region of the
+input pmReadout which will be operated on by the various detrend modules.  It
+does a metadata fetch on "CELL.TRIMSEC" for the parent cell of the pmReadout.
+ 
+Use it this way:
+    PS_WARN_PTR_NON_NULL(in->parent);
+    if (in->parent != NULL) {
+        PS_WARN_PTR_NON_NULL(in->parent->concepts);
+    }
+    //
+    // Determine trimmed image from metadata.
+    //
+    psImage *trimmedImg = p_psDetermineTrimmedImage(in);
+ 
+XXX: Create a pmUtils.c file and put this routine there.
+ *****************************************************************************/
+psImage *p_psDetermineTrimmedImage(pmReadout *in)
+{
+    if ((in->parent == NULL) || (in->parent->concepts == NULL)) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: could not determine CELL.TRIMSEC from parent cell Metadata (NULL).\n");
+        return(in->image);
+    }
+
+    psBool rc = false;
+    psImage *trimmedImg = NULL;
+    psRegion *trimRegion = psMetadataLookupPtr(&rc, in->parent->concepts,
+                           "CELL.TRIMSEC");
+    if (rc == false) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: could not determine CELL.TRIMSEC from parent cell Metadata.\n");
+        trimmedImg = in->image;
+    } else {
+        trimmedImg = psImageSubset(in->image, *trimRegion);
+    }
+
+    return(trimmedImg);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+pmSubtractBias(....): see SDRS for complete specification.
+ 
+XXX: Code and assert type support: U16, S32, F32.
+XXX: Add trace messages.
  *****************************************************************************/
 pmReadout *pmSubtractBias(
@@ -290,397 +1164,122 @@
     int nBin,
     const pmReadout *bias,
-    const pmReadout *dark
-)
+    const pmReadout *dark)
 {
     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");
+    //
+    // Check input parameters, generate warnings and errors.
+    //
+    if (PM_OKAY != AssertCodeOverscan(in, fitSpec, fit, overscan, stat, nBin, bias, dark)) {
         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");
+    //
+    // Determine trimmed image from metadata.
+    //
+    psImage *trimmedImg = p_psDetermineTrimmedImage(in);
+
+    //
+    // Subtract overscan frames if necessary.
+    //
+    if (overscan == true) {
+        pmOverscanAxis overScanAxis = GetOverscanAxis(in);
+
+        //
+        //  Create a psStats data structure and determine the highest
+        //  priority stats option.
+        //
+        psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        if (stat != NULL) {
+            myStats->options = GenNewStatOptions(stat);
+        }
+
+        //
+        // Reduce overscan images to a single pixel, then subtract.
+        // This code is no longer required as of SDRS 12-09.
+        //
+        if (overScanAxis == PM_OVERSCAN_ALL) {
+            if (false == OverscanReducePixel(trimmedImg, in->bias, myStats)) {
                 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");
+            psFree(myStats);
+        } else {
+            //
+            // Reduce the overscan images to a single overscan vector.
+            //
+            psVector *overscanVector = OverscanReduce(in->image, overScanAxis,
+                                       in->bias, fitSpec,
+                                       fit, myStats);
+            if (overscanVector == NULL) {
+                psError(PS_ERR_UNKNOWN, false, "Could not reduce overscan images to a single overscan vector.  Returning in image\n");
+                psFree(myStats);
                 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");
+
+            //
+            // Rebin the overscan vector if necessary.
+            //
+            psS32 newBin = RebinOverscanVector(overscanVector, nBin, myStats);
+            if (newBin < 0) {
+                psError(PS_ERR_UNKNOWN, false, "Could rebin the overscan vector.  Returning in image\n");
+                psFree(myStats);
+                return(in);
+            }
+
+            //
+            // If necessary, fit a psPolynomial or psSpline to the overscan vector.
+            // Then, unbin the overscan vector to appropriate length for the in image.
+            //
+            if ((fit == PM_FIT_POLYNOMIAL) || (fit == PM_FIT_SPLINE)) {
+                overscanVector = FitOverscanVectorAndUnbin(trimmedImg, overscanVector, overScanAxis, fitSpec, fit, newBin);
+                if (overscanVector == NULL) {
+                    psError(PS_ERR_UNKNOWN, false, "Could not fit the polynomial or spline to the overscan vector.  Returning in image\n");
+                    psFree(myStats);
                     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);
+            } else {
+                overscanVector = UnbinOverscanVector(trimmedImg, overscanVector, overScanAxis, newBin);
+            }
+
+            //
+            // Subtract the overscan vector from the input image.
+            //
+            SubtractVectorFromImage(trimmedImg, overscanVector, overScanAxis);
+            psFree(myStats);
+            psFree(overscanVector);
+        }
+    }
+
+    //
+    // Perform bias subtraction if necessary.
+    //
+    if (bias != NULL) {
+        if (PM_OKAY == AssertCodeBias(in, fitSpec, fit, overscan, stat, nBin, bias, dark)) {
+            SubtractFrame(in, bias);
+        }
+    }
+
+    //
+    // Perform dark subtraction if necessary.
+    //
+    if (dark != NULL) {
+        if (PM_OKAY == AssertCodeDark(in, fitSpec, fit, overscan, stat, nBin, bias, dark)) {
+            psBool rc;
+            psF32 scale = 0.0;
+            if (in->parent != NULL) {
+                scale = psMetadataLookupS32(&rc, in->parent->concepts, "CELL.DARKTIME");
+                if (rc == false) {
+                    psLogMsg(__func__, PS_LOG_WARN,
+                             "WARNING: pmSubtractBias.(): could not determine CELL.FARKTIME from in->parent metadata.\n");
                 }
-                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) {
-                // XXX: This makes no sense
-                // XXX: must free mySpline?
-                mySpline = (psSpline1D *) fitSpec;
-                mySpline = psVectorFitSpline1D(NULL, overscanVector);
-                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;
-    }
-
+            }
+            SubtractDarkFrame(in, dark, scale);
+        }
+    }
+
+    //
+    // All done.
+    //
     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 5515)
+++ /trunk/psModules/src/imsubtract/pmSubtractBias.h	(revision 5516)
@@ -6,6 +6,6 @@
  *  @author GLG, MHPCC
  *
- *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-10-20 23:06:24 $
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-11-15 20:09:03 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -47,8 +47,19 @@
     const pmReadout *bias,              ///< A possibly NULL bias pmReadout which is to be subtracted
     const pmReadout *dark               ///< A possibly NULL bias pmReadout which is to be subtracted
-)
+);
 // OLD: remove this
 //    const psList *overscans,      ///< A psList of overscan images
 //    pmOverscanAxis overScanAxis,  ///< Defines how overscans are applied
 
+/******************************************************************************
+DetermineTrimmedImageg() This private routine determines the region of the
+input pmReadout which will be operated on by the various detrend modules.  It
+does a metadata fetch on "CELL.TRIMSEC" for the parent cell of the pmReadout.
+ 
+XXX: Consider making a pmUtils.c file and put this routine there.
+ *****************************************************************************/
+psImage *p_psDetermineTrimmedImage(
+    pmReadout *in
+);
+
 #endif
Index: /trunk/psModules/src/imsubtract/pmSubtractSky.c
===================================================================
--- /trunk/psModules/src/imsubtract/pmSubtractSky.c	(revision 5515)
+++ /trunk/psModules/src/imsubtract/pmSubtractSky.c	(revision 5516)
@@ -6,6 +6,6 @@
  *  @author GLG, MHPCC
  *
- *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-10-12 21:02:04 $
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-11-15 20:09:03 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -18,6 +18,10 @@
 #include<math.h>
 #include "pslib.h"
-#include "psConstants.h"
 #include "pmSubtractSky.h"
+
+// XXX: Get rid of the.  Create pmUtils.h
+psImage *p_psDetermineTrimmedImage(
+    pmReadout *in
+);
 
 /******************************************************************************
@@ -152,4 +156,6 @@
  
 XXX: Use your brain and figure out the analytical expression.
+ 
+XXX: Why isn't it simply (xOrder+1) * (yOrder+1)?
  *****************************************************************************/
 static psS32 CalculatePolyTerms(psS32 xOrder, psS32 yOrder)
@@ -170,8 +176,9 @@
         }
     }
-
     psTrace("SubtractSky.CalculatePolyTerms", 4,
             "Exiting CalculatePolyTerms(%d, %d) -> %d\n", xOrder, yOrder, localPolyTerms);
     return(localPolyTerms);
+
+    //    return((xOrder+1) * (yOrder+1));
 }
 
@@ -283,7 +290,8 @@
 XXX: Different trace message facilities in use here.
  *****************************************************************************/
-static psPolynomial2D *ImageFitPolynomial(psPolynomial2D *myPoly,
-        psImage *dataImage,
-        psImage *maskImage)
+static psPolynomial2D *ImageFitPolynomial(
+    psPolynomial2D *myPoly,
+    psImage *dataImage,
+    psImage *maskImage)
 {
     psTrace("SubtractSky.ImageFitPolynomial", 4,
@@ -471,4 +479,8 @@
     PS_ASSERT_READOUT_NON_EMPTY(in, NULL);
     PS_ASSERT_READOUT_TYPE(in, PS_TYPE_F32, NULL);
+    PS_WARN_PTR_NON_NULL(in->parent);
+    if (in->parent != NULL) {
+        PS_WARN_PTR_NON_NULL(in->parent->concepts);
+    }
     psTrace(".psModule.pmSubtractSky", 4,
             "---- pmSubtractSky() begin ----\n");
@@ -492,5 +504,10 @@
         return(in);
     }
-    psImage *origImage = in->image;
+
+    //
+    // Determine trimmed image from metadata.
+    //
+
+    psImage *trimmedImg = p_psDetermineTrimmedImage(in);
     psImage *binnedImage = NULL;
     psPolynomial2D *myPoly = NULL;
@@ -539,5 +556,5 @@
         // No binning is required here.  Simply create a copy of the image
         // and a mask.
-        binnedImage = psImageCopy(binnedImage, origImage, PS_TYPE_F32);
+        binnedImage = psImageCopy(binnedImage, trimmedImg, PS_TYPE_F32);
         if (binnedImage == NULL) {
             psError(PS_ERR_UNKNOWN, false, "psImageCopy() returned NULL.  Returning in image.\n");
@@ -559,5 +576,5 @@
         }
     } else {
-        binnedImage = psImageRebin(NULL, origImage, in->mask, 0, binFactor, stats);
+        binnedImage = psImageRebin(NULL, trimmedImg, in->mask, 0, binFactor, stats);
         if (binnedImage == NULL) {
             psError(PS_ERR_UNKNOWN, false, "psImageRebin() returned NULL.  Returning in image.\n");
@@ -677,12 +694,12 @@
     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];
+        for (psS32 row = 0; row < trimmedImg->numRows ; row++) {
+            for (psS32 col = 0; col < trimmedImg->numCols ; col++) {
+                trimmedImg->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++) {
+        for (psS32 row = 0; row < trimmedImg->numRows ; row++) {
+            for (psS32 col = 0; col < trimmedImg->numCols ; col++) {
                 // We calculate the F32 value of the pixel coordinates in the
                 // binned image and then use a pixel interpolation routine to
@@ -700,5 +717,5 @@
                                      binnedImage, binColF64, binRowF64,
                                      NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
-                origImage->data.F32[row][col]-= binPixel;
+                trimmedImg->data.F32[row][col]-= binPixel;
 
                 psTrace(".psModule.pmSubtractSky", 8,
Index: /trunk/psModules/test/detrend/tst_pmFlatField.c
===================================================================
--- /trunk/psModules/test/detrend/tst_pmFlatField.c	(revision 5515)
+++ /trunk/psModules/test/detrend/tst_pmFlatField.c	(revision 5516)
@@ -21,6 +21,8 @@
  *  @author Ross Harman, MHPCC
  *
- *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-10-20 23:06:24 $
+ *  XXX: I added the CELL.TRIMSEC region code but there are not tests for it.
+ *
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-11-15 20:09:03 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
Index: /trunk/psModules/test/detrend/tst_pmMaskBadPixels.c
===================================================================
--- /trunk/psModules/test/detrend/tst_pmMaskBadPixels.c	(revision 5515)
+++ /trunk/psModules/test/detrend/tst_pmMaskBadPixels.c	(revision 5516)
@@ -17,6 +17,6 @@
  *  @author Ross Harman, MHPCC
  *
- *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-09-28 20:42:52 $
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-11-15 20:09:03 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -32,12 +32,12 @@
 printf(STRING);                                                                                              \
 printf("\n");                                                                                                \
-for(int i=IMAGE->numRows-1; i>-1; i--) {                                                                     \
-    for(int j=0; j<IMAGE->numCols; j++) {                                                                    \
-        if(PS_IS_PSELEMTYPE_COMPLEX(IMAGE->type.type)) {                                                     \
-            printf("%f+%fi ", creal(IMAGE->data.TYPE[i][j]), cimag(IMAGE->data.TYPE[i][j]));                 \
-        } else if(PS_IS_PSELEMTYPE_INT(IMAGE->type.type)) {                                                  \
-            printf("%d", (int)IMAGE->data.TYPE[i][j]);                                                       \
+for(int i=(IMAGE)->numRows-1; i>-1; i--) {                                                                     \
+    for(int j=0; j<(IMAGE)->numCols; j++) {                                                                    \
+        if(PS_IS_PSELEMTYPE_COMPLEX((IMAGE)->type.type)) {                                                     \
+            printf("%f+%fi ", creal((IMAGE)->data.TYPE[i][j]), cimag((IMAGE)->data.TYPE[i][j]));                 \
+        } else if(PS_IS_PSELEMTYPE_INT((IMAGE)->type.type)) {                                                  \
+            printf("%d", (int)(IMAGE)->data.TYPE[i][j]);                                                       \
         } else {                                                                                             \
-            printf("%f", (double)IMAGE->data.TYPE[i][j]);                                                    \
+            printf("%f", (double)(IMAGE)->data.TYPE[i][j]);                                                    \
         }                                                                                                    \
     }                                                                                                        \
@@ -48,11 +48,10 @@
 
 #define CREATE_AND_SET_IMAGE(NAME,TYPE,VALUE,NROWS,NCOLS)                                                    \
-psImage *NAME = (psImage*)psImageAlloc(NCOLS,NROWS,PS_TYPE_##TYPE);                                          \
-for(int i=0; i<NAME->numRows; i++) {                                                                         \
-    for(int j=0; j<NAME->numCols; j++) {                                                                     \
-        NAME->data.TYPE[i][j] = VALUE;                                                                       \
+(NAME) = (psImage*)psImageAlloc(NCOLS,NROWS,PS_TYPE_##TYPE);                                             \
+for(int i=0; i<(NAME)->numRows; i++) {                                                                         \
+    for(int j=0; j<(NAME)->numCols; j++) {                                                                     \
+        (NAME)->data.TYPE[i][j] = VALUE;                                                                       \
     }                                                                                                        \
 }
-
 
 static int testMaskBadPixels1(void);
@@ -80,8 +79,13 @@
                               {testMaskBadPixels9, 885, "pmMaskBadPixels - Attempt to use offset greater than input image", 0, false},
                               {testMaskBadPixels10, 885, "pmMaskBadPixels - Attempt to use complex input image", 0, false},
-                              {testMaskBadPixels11, 885, "pmMaskBadPixels - Attempt to use non-mask type mask image", 0, false},
+                              {testMaskBadPixels11, 885, "pmMaskBadPixels - Attempt to use non-mask type mask image", 0, false        },
                               {NULL}
                           };
 
+/*
+    #define PS_TYPE_MASK PS_TYPE_U8
+    #define PS_TYPE_MASK_DATA U8
+    #define PS_TYPE_MASK_NAME "psU8"
+*/
 
 int main(int argc, char* argv[])
@@ -92,39 +96,66 @@
 
 
+#define NUM_ROWS 50
+#define NUM_COLS 50
+#define DEFAULT_IMAGE_VAL 0.0
+#define DEFAULT_MASK_VAL 0
+#define MASK_VAL 1
+#define SAT_VAL  100.0
+#define GROW_VAL 1
+#define GROW_RAD 10
 int testMaskBadPixels1( void )
 {
+    //
     // Test A - Create mask based on maskVal argument
-    CREATE_AND_SET_IMAGE(inImage1,F64,0,50,50);
-    CREATE_AND_SET_IMAGE(mask1,U8,0,50,50)
-    //    pmReadout *inReadout = pmReadoutAlloc(0, 0, inImage1);
-    pmReadout *inReadout = pmReadoutAlloc(NULL);
-    inReadout->row0 = 0;
-    inReadout->col0 = 0;
-    inReadout->image = inImage1;
-    mask1->data.PS_TYPE_MASK_DATA[24][24]=1;
-    PRINT_MATRIX(mask1, U8, "Data mask:");
-    pmMaskBadPixels(inReadout, mask1, 1, 100.0, 0, 0);
+    //
+
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(inReadout->image, F32, DEFAULT_IMAGE_VAL, NUM_ROWS, NUM_COLS);
+    inReadout->image->row0 = 0;
+    inReadout->image->col0 = 0;
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+
+    pmReadout *maskReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(maskReadout->image, U8, DEFAULT_MASK_VAL, NUM_ROWS, NUM_COLS)
+    maskReadout->image->data.PS_TYPE_MASK_DATA[NUM_ROWS/2][NUM_COLS/2]=1;
+    maskReadout->image->row0 = 0;
+    maskReadout->image->col0 = 0;
+
+    PRINT_MATRIX(maskReadout->image, U8, "Data mask:");
+
+    pmMaskBadPixels(inReadout, maskReadout, MASK_VAL, SAT_VAL, GROW_VAL, GROW_RAD);
+    PRINT_MATRIX(inReadout->mask, PS_TYPE_MASK_DATA, "Resulting mask:");
+
+    psFree(inReadout);
+    psFree(maskReadout);
+
+    return 0;
+}
+
+int testMaskBadPixels2( void )
+{
+    //
+    // Test B - Create mask based on saturation argument
+    //
+
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(inReadout->image, F32, DEFAULT_IMAGE_VAL, NUM_ROWS, NUM_COLS);
+    inReadout->image->row0 = 0;
+    inReadout->image->col0 = 0;
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+    inReadout->image->data.F32[NUM_ROWS/2][NUM_COLS/2] = 150.0;
+
+    pmReadout *maskReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(maskReadout->image, U8, DEFAULT_MASK_VAL, NUM_ROWS, NUM_COLS)
+
+    //PS_IMAGE_PRINT_F32(inReadout->image);
+    PRINT_MATRIX(maskReadout->image, U8, "Data mask:");
+    pmMaskBadPixels(inReadout, maskReadout, MASK_VAL, SAT_VAL, GROW_VAL, GROW_RAD);
     PRINT_MATRIX(inReadout->mask, U8, "Resulting mask:");
-    psFree(mask1);
-    psFree(inReadout);
-
-    return 0;
-}
-
-int testMaskBadPixels2( void )
-{
-    // Test B - Create mask based on saturation argument
-    CREATE_AND_SET_IMAGE(inImage2,F64,150.0,50,50);
-    CREATE_AND_SET_IMAGE(mask2,U8,0,50,50)
-    //    pmReadout *inReadout2 = pmReadoutAlloc(0, 0, inImage2);
-    pmReadout *inReadout2 = pmReadoutAlloc(NULL);
-    inReadout2->row0 = 0;
-    inReadout2->col0 = 0;
-    inReadout2->image = inImage2;
-    PRINT_MATRIX(mask2, U8, "Data mask:");
-    pmMaskBadPixels(inReadout2, mask2, 0, 100.0, 0, 0);
-    PRINT_MATRIX(inReadout2->mask, U8, "Resulting mask:");
-    psFree(mask2);
-    psFree(inReadout2);
+
+    psFree(inReadout);
+    psFree(maskReadout);
 
     return 0;
@@ -133,20 +164,28 @@
 int testMaskBadPixels3( void )
 {
+    //
     // Test C - Create mask based on growVal and grow arguments
-    CREATE_AND_SET_IMAGE(inImage3,F64,50.0,50,50);
-    CREATE_AND_SET_IMAGE(mask3,U8,0,50,50)
-    //    pmReadout *inReadout3 = pmReadoutAlloc(0, 0, inImage3);
-    pmReadout *inReadout3 = pmReadoutAlloc(NULL);
-    inReadout3->row0 = 0;
-    inReadout3->col0 = 0;
-    inReadout3->image = inImage3;
-    mask3->data.PS_TYPE_MASK_DATA[24][24]=1;
-    mask3->data.PS_TYPE_MASK_DATA[4][3]=1;
-    mask3->data.PS_TYPE_MASK_DATA[4][46]=1;
-    PRINT_MATRIX(mask3, U8, "Data mask:");
-    pmMaskBadPixels(inReadout3, mask3, 0, 100.0, 1, 10);
-    PRINT_MATRIX(inReadout3->mask, U8, "Resulting mask:");
-    psFree(mask3);
-    psFree(inReadout3);
+    //
+
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(inReadout->image, F32, DEFAULT_IMAGE_VAL, NUM_ROWS, NUM_COLS);
+    inReadout->image->row0 = 0;
+    inReadout->image->col0 = 0;
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+    inReadout->image->data.F32[NUM_ROWS/2][NUM_COLS/2]=GROW_VAL;
+    inReadout->image->data.F32[NUM_ROWS/4][NUM_COLS/4]=GROW_VAL;
+    inReadout->image->data.F32[NUM_ROWS/4][NUM_COLS-(NUM_COLS/4)]=GROW_VAL;
+
+    pmReadout *maskReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(maskReadout->image, U8, DEFAULT_MASK_VAL, NUM_ROWS, NUM_COLS);
+
+    PRINT_MATRIX(maskReadout->image, U8, "Data mask:");
+    //PS_IMAGE_PRINT_F32(inReadout->image);
+    pmMaskBadPixels(inReadout, maskReadout, MASK_VAL, SAT_VAL, GROW_VAL, GROW_RAD);
+    PRINT_MATRIX(inReadout->mask, U8, "Resulting mask:");
+
+    psFree(inReadout);
+    psFree(maskReadout);
 
     return 0;
@@ -155,20 +194,25 @@
 int testMaskBadPixels4( void )
 {
+    //
     // Test D - Auto Create mask based on maskVal argument
-    CREATE_AND_SET_IMAGE(inImage4,F64,50.0,50,50);
-    CREATE_AND_SET_IMAGE(mask4,U8,0,50,50)
-    CREATE_AND_SET_IMAGE(mask4i,U8,0,50,50)
-    //    pmReadout *inReadout4 = pmReadoutAlloc(0, 0, inImage4);
-    pmReadout *inReadout4 = pmReadoutAlloc(NULL);
-    inReadout4->row0 = 0;
-    inReadout4->col0 = 0;
-    inReadout4->image = inImage4;
-    inReadout4->mask = mask4i;
-    mask4->data.PS_TYPE_MASK_DATA[24][24]=1;
-    PRINT_MATRIX(mask4, U8, "Data mask:");
-    pmMaskBadPixels(inReadout4, mask4, 0, 100.0, 1, 10);
-    PRINT_MATRIX(inReadout4->mask, U8, "Resulting mask:");
-    psFree(mask4);
-    psFree(inReadout4);
+    //
+
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(inReadout->image, F32, DEFAULT_IMAGE_VAL, NUM_ROWS, NUM_COLS);
+    inReadout->image->row0 = 0;
+    inReadout->image->col0 = 0;
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+
+    pmReadout *maskReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(maskReadout->image, U8, DEFAULT_MASK_VAL, NUM_ROWS, NUM_COLS)
+    maskReadout->image->data.PS_TYPE_MASK_DATA[NUM_ROWS/2][NUM_COLS/2]=1;
+
+    PRINT_MATRIX(maskReadout->image, U8, "Data mask:");
+    pmMaskBadPixels(inReadout, maskReadout, MASK_VAL, SAT_VAL, GROW_VAL, GROW_RAD);
+    PRINT_MATRIX(inReadout->mask, U8, "Resulting mask:");
+
+    psFree(inReadout);
+    psFree(maskReadout);
 
     return 0;
@@ -177,13 +221,17 @@
 int testMaskBadPixels5( void )
 {
+    //
     // Test E - Attempt to use null mask
-    CREATE_AND_SET_IMAGE(inImage5,F64,50.0,50,50);
-    //    pmReadout *inReadout5 = pmReadoutAlloc(0, 0, inImage5);
-    pmReadout *inReadout5 = pmReadoutAlloc(NULL);
-    inReadout5->row0 = 0;
-    inReadout5->col0 = 0;
-    inReadout5->image = inImage5;
-    pmMaskBadPixels(inReadout5, NULL, 0, 100.0, 1, 10);
-    psFree(inReadout5);
+    //
+
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(inReadout->image, F32, DEFAULT_IMAGE_VAL, NUM_ROWS, NUM_COLS);
+    inReadout->image->row0 = 0;
+    inReadout->image->col0 = 0;
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+
+    pmMaskBadPixels(inReadout, NULL, MASK_VAL, SAT_VAL, GROW_VAL, GROW_RAD);
+    psFree(inReadout);
 
     return 0;
@@ -192,13 +240,19 @@
 int testMaskBadPixels6( void )
 {
+    //
     // Test F - Attempt tp use null input image
-    CREATE_AND_SET_IMAGE(mask6,U8,0,50,50)
-    //    pmReadout *inReadout6 = pmReadoutAlloc(0, 0, NULL);
-    pmReadout *inReadout6 = pmReadoutAlloc(NULL);
-    inReadout6->row0 = 0;
-    inReadout6->col0 = 0;
-    inReadout6->mask = mask6;
-    pmMaskBadPixels(inReadout6, mask6, 0, 100.0, 1, 10);
-    psFree(inReadout6);
+    //
+
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+
+    pmReadout *maskReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(maskReadout->image, U8, DEFAULT_MASK_VAL, NUM_ROWS, NUM_COLS)
+
+    pmMaskBadPixels(inReadout, maskReadout, MASK_VAL, SAT_VAL, GROW_VAL, GROW_RAD);
+
+    psFree(inReadout);
+    psFree(maskReadout);
 
     return 0;
@@ -207,17 +261,22 @@
 int testMaskBadPixels7( void )
 {
+    //
     // Test G - Attempt to use input image bigger than mask
-    CREATE_AND_SET_IMAGE(inImage7,F64,0.0,60,60);
-    CREATE_AND_SET_IMAGE(mask7,U8,0,50,50)
-    CREATE_AND_SET_IMAGE(mask7i,U8,0,50,50)
-    //    pmReadout *inReadout7 = pmReadoutAlloc(0, 0, inImage7);
-    pmReadout *inReadout7 = pmReadoutAlloc(NULL);
-    inReadout7->row0 = 0;
-    inReadout7->col0 = 0;
-    inReadout7->image = inImage7;
-    inReadout7->mask = mask7i;
-    pmMaskBadPixels(inReadout7, mask7, 0, 100.0, 1, 10);
-    psFree(mask7);
-    psFree(inReadout7);
+    //
+
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(inReadout->image, F32, DEFAULT_IMAGE_VAL, NUM_ROWS+10, NUM_COLS+10);
+    inReadout->image->row0 = 0;
+    inReadout->image->col0 = 0;
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+
+    pmReadout *maskReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(maskReadout->image, U8, DEFAULT_MASK_VAL, NUM_ROWS, NUM_COLS)
+
+    pmMaskBadPixels(inReadout, maskReadout, MASK_VAL, SAT_VAL, GROW_VAL, GROW_RAD);
+
+    psFree(inReadout);
+    psFree(maskReadout);
 
     return 0;
@@ -226,17 +285,22 @@
 int testMaskBadPixels8( void )
 {
-    // Test H - Attempt to use input image mask bigger than mask
-    CREATE_AND_SET_IMAGE(inImage8,F64,0.0,50,50);
-    CREATE_AND_SET_IMAGE(mask8,U8,0,50,50)
-    CREATE_AND_SET_IMAGE(mask8i,U8,0,60,60)
-    //    pmReadout *inReadout8 = pmReadoutAlloc(0, 0, inImage8);
-    pmReadout *inReadout8 = pmReadoutAlloc(NULL);
-    inReadout8->row0 = 0;
-    inReadout8->col0 = 0;
-    inReadout8->image = inImage8;
-    inReadout8->mask = mask8i;
-    pmMaskBadPixels(inReadout8, mask8, 0, 100.0, 1, 10);
-    psFree(mask8);
-    psFree(inReadout8);
+    //
+    // Test H - Attempt to use mask bigger than image
+    //
+
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(inReadout->image, F32, DEFAULT_IMAGE_VAL, NUM_ROWS, NUM_COLS);
+    inReadout->image->row0 = 0;
+    inReadout->image->col0 = 0;
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+
+    pmReadout *maskReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(maskReadout->image, U8, DEFAULT_MASK_VAL, NUM_ROWS+10, NUM_COLS+10)
+
+    pmMaskBadPixels(inReadout, maskReadout, MASK_VAL, SAT_VAL, GROW_VAL, GROW_RAD);
+
+    psFree(inReadout);
+    psFree(maskReadout);
 
     return 0;
@@ -245,19 +309,23 @@
 int testMaskBadPixels9( void )
 {
+    //
     // Test I - Attempt to use offset greater than input image
-    CREATE_AND_SET_IMAGE(inImage9,F64,0.0,50,50);
-    CREATE_AND_SET_IMAGE(mask9,U8,0,50,50)
-    CREATE_AND_SET_IMAGE(mask9i,U8,0,50,50)
-    //    pmReadout *inReadout9 = pmReadoutAlloc(0, 0, inImage9);
-    pmReadout *inReadout9 = pmReadoutAlloc(NULL);
-    inReadout9->image = inImage9;
-    inReadout9->mask = mask9i;
-    inReadout9->row0 = 0;
-    inReadout9->col0 = 0;
-    *(int*)&inReadout9->col0 = 150;
-    *(int*)&inReadout9->row0 = 150;
-    pmMaskBadPixels(inReadout9, mask9, 0, 100.0, 1, 10);
-    psFree(mask9);
-    psFree(inReadout9);
+    //
+
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(inReadout->image, F32, DEFAULT_IMAGE_VAL, NUM_ROWS, NUM_COLS);
+    inReadout->image->row0 = 0;
+    inReadout->image->col0 = 0;
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+    *(int*)&inReadout->image->col0 = 150;
+    *(int*)&inReadout->image->row0 = 150;
+
+    pmReadout *maskReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(maskReadout->image, U8, DEFAULT_MASK_VAL, NUM_ROWS, NUM_COLS)
+    pmMaskBadPixels(inReadout, maskReadout, MASK_VAL, SAT_VAL, GROW_VAL, GROW_RAD);
+
+    psFree(inReadout);
+    psFree(maskReadout);
 
     return 0;
@@ -266,17 +334,22 @@
 int testMaskBadPixels10( void )
 {
+    //
     // Test J - Attempt to use complex input image
-    CREATE_AND_SET_IMAGE(inImage10,C64,50.0,50,50);
-    CREATE_AND_SET_IMAGE(mask10,U8,0,50,50)
-    CREATE_AND_SET_IMAGE(mask10i,U8,0,50,50)
-    //    pmReadout *inReadout10 = pmReadoutAlloc(0, 0, inImage10);
-    pmReadout *inReadout10 = pmReadoutAlloc(NULL);
-    inReadout10->row0 = 0;
-    inReadout10->col0 = 0;
-    inReadout10->image = inImage10;
-    inReadout10->mask = mask10i;
-    pmMaskBadPixels(inReadout10, mask10, 0, 100.0, 1, 10);
-    psFree(mask10);
-    psFree(inReadout10);
+    //
+
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(inReadout->image, C64, DEFAULT_IMAGE_VAL, NUM_ROWS, NUM_COLS);
+    inReadout->image->row0 = 0;
+    inReadout->image->col0 = 0;
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+
+    pmReadout *maskReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(maskReadout->image, U8, DEFAULT_MASK_VAL, NUM_ROWS, NUM_COLS)
+
+    pmMaskBadPixels(inReadout, maskReadout, MASK_VAL, SAT_VAL, GROW_VAL, GROW_RAD);
+
+    psFree(inReadout);
+    psFree(maskReadout);
 
     return 0;
@@ -285,19 +358,24 @@
 int testMaskBadPixels11( void )
 {
-    // Test K - Attempt to use non-mask type mask image
-    CREATE_AND_SET_IMAGE(inImage11,F64,50.0,50,50);
-    CREATE_AND_SET_IMAGE(mask11,F64,0,50,50)
-    CREATE_AND_SET_IMAGE(mask11i,U8,0,50,50)
-    //    pmReadout *inReadout11 = pmReadoutAlloc(0, 0, inImage11);
-    pmReadout *inReadout11 = pmReadoutAlloc(NULL);
-    inReadout11->row0 = 0;
-    inReadout11->col0 = 0;
-    inReadout11->image = inImage11;
-    inReadout11->mask = mask11i;
-    pmMaskBadPixels(inReadout11, mask11, 0, 100.0, 1, 10);
-    psFree(mask11);
-    psFree(inReadout11);
-
-    return 0;
-}
-
+    //
+    // Test K - Attempt to use mask image with wrong data type.
+    //
+
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(inReadout->image, F32, DEFAULT_IMAGE_VAL, NUM_ROWS, NUM_COLS);
+    inReadout->image->row0 = 0;
+    inReadout->image->col0 = 0;
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+
+    pmReadout *maskReadout = pmReadoutAlloc(NULL);
+    CREATE_AND_SET_IMAGE(maskReadout->image, F64, DEFAULT_MASK_VAL, NUM_ROWS, NUM_COLS)
+
+    pmMaskBadPixels(inReadout, maskReadout, MASK_VAL, SAT_VAL, GROW_VAL, GROW_RAD);
+
+    psFree(inReadout);
+    psFree(maskReadout);
+
+    return 0;
+}
+
Index: /trunk/psModules/test/imsubtract/tst_pmSubtractBias.c
===================================================================
--- /trunk/psModules/test/imsubtract/tst_pmSubtractBias.c	(revision 5515)
+++ /trunk/psModules/test/imsubtract/tst_pmSubtractBias.c	(revision 5516)
@@ -3,12 +3,21 @@
  *  @brief Contains the tests for pmSubtractBias.c:
  *
- * test00: This code will subtract full bias frames from the input image.
- * test01: Multiple overscan regions, calculate a scalar statistic for
- *  each, then subtract from the input image.
- * test02: Calculate a column overscan vector and subtract it from each
- *  column in the input image.
- * test03: Calculate a row overscan vector and subtract it from each
+ * test00a: This code will subtract full bias frames from the input image.
+ * XXX: Must test:
+ *  Various image offsets.
+ *  Various image size combinations.
+ *  Various data types for the bias and input images.
+ *  Ensure code works when CELL.TRIMSEC is not set.
+ * test00b: This code will subtract full dark frames from the input image.
+ * XXX: Must test:
+ *  Various image offsets.
+ *  Various image size combinations.
+ *  Various data types for the bias and input images.
+ *  Code properly determines CELL.DARKTIME from cell metadata.
+ *  Ensure code works when CELL.DARKTIME is not set.
+ *  Ensure code works when CELL.TRIMSEC is not set.
+ *  test03: Calculate a row overscan vector and subtract it from each
  *  row in the input image.
- * test04: 
+ * test05: 
  *
  *  @author GLG, MHPCC
@@ -16,6 +25,6 @@
  *  XXX: Memory leaks are not being detected.
  *
- *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-09-29 21:57:31 $
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-11-15 20:09:03 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -25,17 +34,19 @@
 #include "pslib.h"
 #include "pmSubtractBias.h"
-static int test00(void);
-static int test01(void);
-static int test02(void);
-static int test03(void);
-static int test04(void);
-static int testX(void);
+static int test00a(void);
+static int test00b(void);
+//static int test01(void);
+//static int test02(void);
+//static int test03(void);
+//static int test04(void);
+static int test05(void);
 testDescription tests[] = {
-                              {test00, 000, "pmSubtractBias", 0, true},
-                              {test01, 000, "pmSubtractBias", 0, true},
-                              {test02, 000, "pmSubtractBias", 0, true},
-                              {test03, 000, "pmSubtractBias", 0, false},
-                              {test04, 000, "pmSubtractBias", 0, true},
-                              {testX,  000, "pmSubtractBias", 0, true},
+                              {test00a, 000, "doSubtractBiasFullFrame", 0, false},
+                              {test00b, 000, "doSubtractDarkFullFrame", 0, false},
+                              //                              {test01, 000, "pmSubtractBias", 0, true},
+                              //                              {test02, 000, "pmSubtractBias", 0, true},
+                              //                              {test03, 000, "pmSubtractBias", 0, false},
+                              //                              {test04, 000, "pmSubtractBias", 0, true},
+                              {test05, 000, "pmSubtractBias", 0, false},
                               {NULL}
                           };
@@ -45,4 +56,16 @@
 {
     psLogSetFormat("HLNM");
+
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("spline1DFree", 0);
+    psTraceSetLevel("calculateSecondDerivs", 0);
+    psTraceSetLevel("vectorBinDisectF32", 0);
+    psTraceSetLevel("vectorBinDisectF64", 0);
+    psTraceSetLevel("p_psVectorBinDisect", 0);
+    psTraceSetLevel("psSpline1DAlloc", 0);
+    psTraceSetLevel("psVectorFitSpline1D", 0);
+    psTraceSetLevel("psSpline1DEval", 0);
+    psTraceSetLevel("psSpline1DEvalVector", 0);
+
     return !runTestSuite(stderr, "Test Point Driver", tests, argc, argv);
 }
@@ -50,4 +73,7 @@
 #define NUM_ROWS 8
 #define NUM_COLS 8
+#define MAX_HEADER_MSG_LENGTH 1000
+#define POLYNOMIAL_FIT_ORDER 2
+#define NUM_OVERSCANS 2
 /******************************************************************************
 doSubtractBiasFullFrame(): a sample pmReadout as well as a bias image are
@@ -63,6 +89,4 @@
     psImage *tmpImage1 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     psImage *tmpImage2 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
-    //    pmReadout *myReadout = pmReadoutAlloc(numCols, numRows, tmpImage1);
-    //    pmReadout *myBias = pmReadoutAlloc(numCols, numRows, tmpImage2);
     pmReadout *myReadout = pmReadoutAlloc(NULL);
     pmReadout *myBias = pmReadoutAlloc(NULL);
@@ -70,6 +94,7 @@
     myBias->image = tmpImage2;
 
-
-    printPositiveTestHeader(stdout, "pmSubtractBias", "doSubtractBiasFullFrame");
+    char *HeaderMessageStr = (char *) psAlloc(MAX_HEADER_MSG_LENGTH);
+    sprintf(HeaderMessageStr, "doSubtractBiasFullFrame(%d, %d)", numRows, numCols);
+    printPositiveTestHeader(stdout, "pmSubtractBias", HeaderMessageStr);
     for (i=0;i<numRows;i++) {
         for (j=0;j<numCols;j++) {
@@ -79,6 +104,6 @@
     }
 
-    myReadout = pmSubtractBias(myReadout, NULL, NULL, PM_OVERSCAN_NONE, NULL,
-                               0, PM_FIT_NONE, myBias);
+    myReadout = pmSubtractBias(myReadout, NULL, PM_FIT_NONE, false,
+                               NULL, 0, myBias, NULL);
 
     for (i=0;i<numRows;i++) {
@@ -96,10 +121,11 @@
     psFree(myReadout);
     psFree(myBias);
-    printFooter(stdout, "pmSubtractBias", "doSubtractBiasFullFrame", true);
+    printFooter(stdout, "pmSubtractBias", HeaderMessageStr, true);
+    psFree(HeaderMessageStr);
     return(testStatus);
 }
 
 
-int test00( void )
+int test00a( void )
 {
     int testStatus = 0;
@@ -112,10 +138,10 @@
 }
 
+
 /******************************************************************************
-doSubtractFullOverscans(): a sample pmReadout as well as several overscan
-images of the same size are created.  The overscan images are then subtracted
-from the pmReadout.
+doSubtractDarkFullFrame(): a sample pmReadout as well as a dark image are
+created and the dark image is subtracted from the pmReadout.
  *****************************************************************************/
-int doSubtractFullOverscans(int numCols, int numRows)
+int doSubtractDarkFullFrame(int numCols, int numRows)
 {
     int i;
@@ -126,33 +152,25 @@
     psImage *tmpImage1 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
     psImage *tmpImage2 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
-    psImage *tmpImage3 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
-    psImage *tmpImage4 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
-    //    pmReadout *myReadout = pmReadoutAlloc(numCols, numRows, tmpImage1);
     pmReadout *myReadout = pmReadoutAlloc(NULL);
+    pmReadout *myDark = pmReadoutAlloc(NULL);
     myReadout->image = tmpImage1;
-
-
-    printPositiveTestHeader(stdout, "pmSubtractBias", "doSubtractFullOverscans");
-    psList *list;
-    psStats *stat = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
-
+    myDark->image = tmpImage2;
+
+    char *HeaderMessageStr = (char *) psAlloc(MAX_HEADER_MSG_LENGTH);
+    sprintf(HeaderMessageStr, "doSubtractDarkFullFrame(%d, %d)", numRows, numCols);
+    printPositiveTestHeader(stdout, "pmSubtractBias", HeaderMessageStr);
     for (i=0;i<numRows;i++) {
         for (j=0;j<numCols;j++) {
             myReadout->image->data.F32[i][j] = (float) (i + j);
-            tmpImage2->data.F32[i][j] = 3.0;
-            tmpImage3->data.F32[i][j] = 4.0;
-            tmpImage4->data.F32[i][j] = 5.0;
-        }
-    }
-    list = psListAlloc(tmpImage2);
-    psListAdd(list, PS_LIST_HEAD, tmpImage3);
-    psListAdd(list, PS_LIST_HEAD, tmpImage4);
-
-    myReadout = pmSubtractBias(myReadout, NULL, list, PM_OVERSCAN_ALL, stat,
-                               0, PM_FIT_NONE, NULL);
+            myDark->image->data.F32[i][j] = 1.0;
+        }
+    }
+
+    myReadout = pmSubtractBias(myReadout, NULL, PM_FIT_NONE, false,
+                               NULL, 0, NULL, myDark);
 
     for (i=0;i<numRows;i++) {
         for (j=0;j<numCols;j++) {
-            expect = ((float) (i + j)) - 12.0;
+            expect = ((float) (i + j)) - 1.0;
             actual = myReadout->image->data.F32[i][j];
             if (FLT_EPSILON < fabs(expect - actual)) {
@@ -165,385 +183,24 @@
 
     psFree(myReadout);
-    psFree(tmpImage2);
-    psFree(tmpImage3);
-    psFree(tmpImage4);
-    psFree(stat);
-    psFree(list);
-
-    printFooter(stdout, "pmSubtractBias", "doSubtractFullOverscans", true);
+    psFree(myDark);
+    printFooter(stdout, "pmSubtractBias", HeaderMessageStr, true);
+    psFree(HeaderMessageStr);
     return(testStatus);
 }
 
-int test01( void )
+
+int test00b( void )
 {
     int testStatus = 0;
 
-    testStatus |= doSubtractFullOverscans(1, 1);
-    testStatus |= doSubtractFullOverscans(1, NUM_ROWS);
-    testStatus |= doSubtractFullOverscans(NUM_COLS, 1);
-    testStatus |= doSubtractFullOverscans(NUM_COLS, NUM_ROWS);
-
+    testStatus |= doSubtractDarkFullFrame(1, 1);
+    testStatus |= doSubtractDarkFullFrame(NUM_COLS, 1);
+    testStatus |= doSubtractDarkFullFrame(1, NUM_ROWS);
+    testStatus |= doSubtractDarkFullFrame(NUM_COLS, NUM_ROWS);
     return(testStatus);
 }
 
-/******************************************************************************
-doSubtractFullOverscans(): a sample pmReadout as well as several overscan
-images of the same size are created.  The overscan images are collected
-pixel-by-pixel then subtracted column-wise from the pmReadout.
- *****************************************************************************/
-int doSubtractFullOverscanColumns(int numCols, int numRows)
-{
-    int i;
-    int j;
-    float actual;
-    float expect;
-    int testStatus = 0;
-    psImage *tmpImage1 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
-    psImage *tmpImage2 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
-    psImage *tmpImage3 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
-    psImage *tmpImage4 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
-    pmReadout *myReadout = pmReadoutAlloc(NULL);
-    myReadout->image = tmpImage1;
-    psList *list;
-    psStats *stat = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
-
-    for (i=0;i<numRows;i++) {
-        for (j=0;j<numCols;j++) {
-            myReadout->image->data.F32[i][j] = (float) (i + j);
-            tmpImage2->data.F32[i][j] = 3.0;
-            tmpImage3->data.F32[i][j] = 4.0;
-            tmpImage4->data.F32[i][j] = 5.0;
-        }
-    }
-    list = psListAlloc(tmpImage2);
-    psListAdd(list, PS_LIST_HEAD, tmpImage3);
-    psListAdd(list, PS_LIST_HEAD, tmpImage4);
-
-    printPositiveTestHeader(stdout, "pmSubtractBias", "Column Overscans");
-
-    myReadout = pmSubtractBias(myReadout, NULL, list, PM_OVERSCAN_COLUMNS, stat,
-                               0, PM_FIT_NONE, NULL);
-
-    for (i=0;i<numRows;i++) {
-        for (j=0;j<numCols;j++) {
-            expect = ((float) (i + j)) - 12.0;
-            actual = myReadout->image->data.F32[i][j];
-            if (FLT_EPSILON < fabs(expect - actual)) {
-                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
-                testStatus = 1;
-            }
-        }
-    }
-
-
-    psFree(myReadout);
-    psFree(tmpImage2);
-    psFree(tmpImage3);
-    psFree(tmpImage4);
-    psFree(stat);
-    psFree(list);
-
-    printFooter(stdout, "pmSubtractBias", "Column Overscans", true);
-    return(testStatus);
-}
-/******************************************************************************
-doSubtractFullOverscans(): a sample pmReadout as well as several overscan
-images of the same size are created.  The overscan images are collected
-pixel-by-pixel then subtracted column-wise from the pmReadout.
- *****************************************************************************/
-int doSubtractFullOverscanColumnsPoly(int numCols, int numRows)
-{
-    int i;
-    int j;
-    float actual;
-    float expect;
-    int testStatus = 0;
-    psImage *tmpImage1 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
-    psImage *tmpImage2 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
-    psImage *tmpImage3 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
-    psImage *tmpImage4 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
-    pmReadout *myReadout = pmReadoutAlloc(NULL);
-    myReadout->image = tmpImage1;
-    psList *list;
-    psStats *stat = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
-    psPolynomial1D *myPoly = psPolynomial1DAlloc(1, PS_POLYNOMIAL_ORD);
-
-    for (i=0;i<numRows;i++) {
-        for (j=0;j<numCols;j++) {
-            myReadout->image->data.F32[i][j] = (float) (i + j);
-            tmpImage2->data.F32[i][j] = 3.0;
-            tmpImage3->data.F32[i][j] = 4.0;
-            tmpImage4->data.F32[i][j] = 5.0;
-        }
-    }
-    list = psListAlloc(tmpImage2);
-    psListAdd(list, PS_LIST_HEAD, tmpImage3);
-    psListAdd(list, PS_LIST_HEAD, tmpImage4);
-
-    printPositiveTestHeader(stdout, "pmSubtractBias", "Column Overscans");
-
-    myReadout = pmSubtractBias(myReadout, myPoly, list, PM_OVERSCAN_COLUMNS, stat,
-                               0, PM_FIT_POLYNOMIAL, NULL);
-
-    for (i=0;i<numRows;i++) {
-        for (j=0;j<numCols;j++) {
-            expect = ((float) (i + j)) - 12.0;
-            actual = myReadout->image->data.F32[i][j];
-            if (FLT_EPSILON < fabs(expect - actual)) {
-                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
-                testStatus = 1;
-            }
-        }
-    }
-
-
-    psFree(myReadout);
-    psFree(tmpImage2);
-    psFree(tmpImage3);
-    psFree(tmpImage4);
-    psFree(stat);
-    psFree(list);
-    psFree(myPoly);
-
-    printFooter(stdout, "pmSubtractBias", "Column Overscans", true);
-    return(testStatus);
-}
-/******************************************************************************
-doSubtractFullOverscansSmall(): a sample pmReadout as well as several overscan
-images of smaller size are created.  The overscan images are collected
-pixel-by-pixel then subtracted column-wise from the pmReadout.
- *****************************************************************************/
-int doSubtractFullOverscanColumnsSmall(int numCols, int numRows)
-{
-    int i;
-    int j;
-    float actual;
-    float expect;
-    int testStatus = 0;
-    psImage *tmpImage1 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
-    pmReadout *myReadout = pmReadoutAlloc(NULL);
-    myReadout->image = tmpImage1;
-    psImage *tmpImage2 = psImageAlloc(numCols/2, numRows/2, PS_TYPE_F32);
-    psImage *tmpImage3 = psImageAlloc(numCols/2, numRows/2, PS_TYPE_F32);
-    psImage *tmpImage4 = psImageAlloc(numCols/2, numRows/2, PS_TYPE_F32);
-    psList *list;
-    psStats *stat = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
-
-    for (i=0;i<numRows;i++) {
-        for (j=0;j<numCols;j++) {
-            myReadout->image->data.F32[i][j] = (float) (i + j);
-        }
-    }
-    for (i=0;i<numRows/2;i++) {
-        for (j=0;j<numCols/2;j++) {
-            tmpImage2->data.F32[i][j] = 3.0;
-            tmpImage3->data.F32[i][j] = 4.0;
-            tmpImage4->data.F32[i][j] = 5.0;
-        }
-    }
-    list = psListAlloc(tmpImage2);
-    psListAdd(list, PS_LIST_HEAD, tmpImage3);
-    psListAdd(list, PS_LIST_HEAD, tmpImage4);
-
-    printPositiveTestHeader(stdout, "pmSubtractBias", "Column Overscans");
-
-    myReadout = pmSubtractBias(myReadout, NULL, list, PM_OVERSCAN_COLUMNS, stat,
-                               0, PM_FIT_POLYNOMIAL, NULL);
-
-    for (i=0;i<numRows;i++) {
-        for (j=0;j<numCols;j++) {
-            expect = ((float) (i + j)) - 12.0;
-            actual = myReadout->image->data.F32[i][j];
-            if (FLT_EPSILON < fabs(expect - actual)) {
-                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
-                testStatus = 1;
-            }
-        }
-    }
-
-
-    psFree(myReadout);
-    psFree(tmpImage2);
-    psFree(tmpImage3);
-    psFree(tmpImage4);
-    psFree(stat);
-    psFree(list);
-
-    printFooter(stdout, "pmSubtractBias", "Column Overscans", true);
-    return(testStatus);
-}
-
-int test02( void )
-{
-    int testStatus = 0;
-
-    testStatus |= doSubtractFullOverscanColumns(1, 1);
-    testStatus |= doSubtractFullOverscanColumns(1, NUM_ROWS);
-    testStatus |= doSubtractFullOverscanColumns(NUM_COLS, 1);
-    testStatus |= doSubtractFullOverscanColumns(NUM_COLS, NUM_ROWS);
-    /* These tests do not make sense until the SDRS is clarified.
-        testStatus |= doSubtractFullOverscanColumnsSmall(1, 1);
-        testStatus |= doSubtractFullOverscanColumnsSmall(1, NUM_ROWS);
-        testStatus |= doSubtractFullOverscanColumnsSmall(NUM_COLS, 1);
-        testStatus |= doSubtractFullOverscanColumnsSmall(NUM_COLS, NUM_ROWS);
-    */
-
-    return(testStatus);
-}
-
-/******************************************************************************
-doSubtractFullOverscans(): a sample pmReadout as well as several overscan
-images of the same size are created.  The overscan images are collected
-pixel-by-pixel then subtracted row-wise from the pmReadout.
- *****************************************************************************/
-int doSubtractFullOverscanRows(int numCols, int numRows)
-{
-    int i;
-    int j;
-    float actual;
-    float expect;
-    int testStatus = 0;
-    psImage *tmpImage1 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
-    psImage *tmpImage2 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
-    psImage *tmpImage3 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
-    psImage *tmpImage4 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
-    pmReadout *myReadout = pmReadoutAlloc(NULL);
-    myReadout->image = tmpImage1;
-    psList *list;
-    psStats *stat = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
-
-    for (i=0;i<numRows;i++) {
-        for (j=0;j<numCols;j++) {
-            myReadout->image->data.F32[i][j] = (float) (i + j);
-            tmpImage2->data.F32[i][j] = 3.0;
-            tmpImage3->data.F32[i][j] = 4.0;
-            tmpImage4->data.F32[i][j] = 5.0;
-        }
-    }
-    list = psListAlloc(tmpImage2);
-    psListAdd(list, PS_LIST_HEAD, tmpImage3);
-    psListAdd(list, PS_LIST_HEAD, tmpImage4);
-
-    printPositiveTestHeader(stdout, "pmSubtractBias", "Row Overscans");
-
-    myReadout = pmSubtractBias(myReadout, NULL, list, PM_OVERSCAN_ROWS, stat,
-                               0, PM_FIT_NONE, NULL);
-
-    for (i=0;i<numRows;i++) {
-        for (j=0;j<numCols;j++) {
-            expect = ((float) (i + j)) - 12.0;
-            actual = myReadout->image->data.F32[i][j];
-            if (FLT_EPSILON < fabs(expect - actual)) {
-                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
-                testStatus = 1;
-            }
-        }
-    }
-
-
-    psFree(myReadout);
-    psFree(tmpImage2);
-    psFree(tmpImage3);
-    psFree(tmpImage4);
-    psFree(stat);
-    psFree(list);
-
-    printFooter(stdout, "pmSubtractBias", "Row Overscans", true);
-    return(testStatus);
-}
-
-/******************************************************************************
-doSubtractFullOverscansSmall(): a sample pmReadout as well as several overscan
-images of smaller size are created.  The overscan images are collected
-pixel-by-pixel then subtracted row-wise from the pmReadout.
- *****************************************************************************/
-int doSubtractFullOverscanRowsSmall(int numCols, int numRows)
-{
-    int i;
-    int j;
-    float actual;
-    float expect;
-    int testStatus = 0;
-    psS32 OSnumRows = numRows-1;
-    if (OSnumRows == 0) {
-        OSnumRows = 1;
-    }
-    psS32 OSnumCols = numCols-1;
-    if (OSnumCols == 0) {
-        OSnumCols = 1;
-    }
-    psImage *tmpImage1 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
-    psImage *tmpImage2 = psImageAlloc(OSnumCols, OSnumRows, PS_TYPE_F32);
-    psImage *tmpImage3 = psImageAlloc(OSnumCols, OSnumRows, PS_TYPE_F32);
-    psImage *tmpImage4 = psImageAlloc(OSnumCols, OSnumRows, PS_TYPE_F32);
-    pmReadout *myReadout = pmReadoutAlloc(NULL);
-    myReadout->image = tmpImage1;
-    psList *list;
-    psStats *stat = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
-
-    for (i=0;i<numRows;i++) {
-        for (j=0;j<numCols;j++) {
-            myReadout->image->data.F32[i][j] = (float) (i + j);
-        }
-    }
-    for (i=0;i<OSnumRows;i++) {
-        for (j=0;j<OSnumCols;j++) {
-            tmpImage2->data.F32[i][j] = 3.0;
-            tmpImage3->data.F32[i][j] = 4.0;
-            tmpImage4->data.F32[i][j] = 5.0;
-        }
-    }
-    list = psListAlloc(tmpImage2);
-    psListAdd(list, PS_LIST_HEAD, tmpImage3);
-    psListAdd(list, PS_LIST_HEAD, tmpImage4);
-
-    printPositiveTestHeader(stdout, "pmSubtractBias", "Row Overscans");
-
-    myReadout = pmSubtractBias(myReadout, NULL, list, PM_OVERSCAN_ROWS, stat,
-                               0, PM_FIT_SPLINE, NULL);
-
-    for (i=0;i<numRows;i++) {
-        for (j=0;j<numCols;j++) {
-            expect = ((float) (i + j)) - 12.0;
-            actual = myReadout->image->data.F32[i][j];
-            if (FLT_EPSILON < fabs(expect - actual)) {
-                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
-                testStatus = 1;
-            }
-        }
-    }
-
-
-    psFree(myReadout);
-    psFree(tmpImage2);
-    psFree(tmpImage3);
-    psFree(tmpImage4);
-    psFree(stat);
-    psFree(list);
-
-    printFooter(stdout, "pmSubtractBias", "Row Overscans", true);
-    return(testStatus);
-}
-
-
-// XXX: HEY
-int test03( void )
-{
-    int testStatus = 0;
-
-    //    testStatus |= doSubtractFullOverscanRows(1, 1);
-    //    testStatus |= doSubtractFullOverscanRows(1, NUM_ROWS);
-    //    testStatus |= doSubtractFullOverscanRows(NUM_COLS, 1);
-    //    testStatus |= doSubtractFullOverscanRows(NUM_COLS, NUM_ROWS);
-    //    testStatus |= doSubtractFullOverscanRowsSmall(1, 1);
-    //    testStatus |= doSubtractFullOverscanRowsSmall(1, NUM_ROWS);
-    //    testStatus |= doSubtractFullOverscanRowsSmall(NUM_COLS, 1);
-    testStatus |= doSubtractFullOverscanRowsSmall(NUM_COLS, NUM_ROWS);
-
-    return(testStatus);
-}
-
-
-
+
+/*
 int doSubtractOverscansTestInputCases(int numCols, int numRows)
 {
@@ -568,5 +225,5 @@
     myBias->image = tmpImage5;
     printPositiveTestHeader(stdout, "pmSubtractBias", "Testing input parameter error conditions");
-
+ 
     psImage *tmpImage5ShortRows = psImageAlloc(numCols, numRows-1, PS_TYPE_F32);
     pmReadout *myBiasShortRows = pmReadoutAlloc(NULL);
@@ -575,5 +232,5 @@
     pmReadout *myBiasShortCols = pmReadoutAlloc(NULL);
     myBiasShortCols->image = tmpImage5ShortCols;
-
+ 
     for (i=0;i<numRows;i++) {
         for (j=0;j<numCols;j++) {
@@ -588,5 +245,5 @@
     psListAdd(list, PS_LIST_HEAD, tmpImage3);
     psListAdd(list, PS_LIST_HEAD, tmpImage4);
-
+ 
     for (i=0;i<numRows-1;i++) {
         for (j=0;j<numCols-1;j++) {
@@ -609,6 +266,6 @@
         }
     }
-
-
+ 
+ 
     printf("------------------------------------------------------------------\n");
     printf("Calling pmSubtractBias() with NULL overscan list and PM_OVERSCAN_ALL.  Should generate error.\n");
@@ -618,5 +275,5 @@
         testStatus = false;
     }
-
+ 
     printf("------------------------------------------------------------------\n");
     printf("Calling pmSubtractBias() with NULL overscan list and PM_OVERSCAN_ROWS.  Should generate error.\n");
@@ -627,5 +284,5 @@
         psFree(rc);
     }
-
+ 
     printf("------------------------------------------------------------------\n");
     printf("Calling pmSubtractBias() with NULL overscan list and PM_OVERSCAN_COLUMNS.  Should generate error.\n");
@@ -636,9 +293,9 @@
         psFree(rc);
     }
-
+ 
     printf("------------------------------------------------------------------\n");
     printf("Calling pmSubtractBias() with non-NULL overscan list and PM_OVERSCAN_NONE.  Should generate warning.\n");
     rc = pmSubtractBias(myReadout, NULL, list, PM_OVERSCAN_NONE, stat, 0, PM_FIT_NONE, myBias);
-
+ 
     for (i=0;i<numRows;i++) {
         for (j=0;j<numCols;j++) {
@@ -649,11 +306,12 @@
                 testStatus = 1;
             }
-
+ 
             // Restore myReadout for next test.
             myReadout->image->data.F32[i][j] = (float) (i + j);
         }
     }
-
-    /* XXX: This does not seem to be a requirement.
+ 
+    // XXX: This does not seem to be a requirement.
+    if (0) {
         printf("------------------------------------------------------------------\n");
         printf("Calling pmSubtractBias() with NULL overscan list and PM_OVERSCAN_NONE.  Should generate warning.\n");
@@ -674,6 +332,6 @@
             }
         }
-    */
-
+    }
+ 
     printf("------------------------------------------------------------------\n");
     printf("Calling pmSubtractBias() with PM_OVERSCAN_NONE and PM_FIT_POLYNOMIAL.  Should generate Warning.\n");
@@ -687,11 +345,11 @@
                 testStatus = 1;
             }
-
+ 
             // Restore myReadout for next test.
             myReadout->image->data.F32[i][j] = (float) (i + j);
         }
     }
-
-
+ 
+ 
     printf("------------------------------------------------------------------\n");
     printf("Calling pmSubtractBias() with PM_OVERSCAN_ALL and PM_FIT_SPLINE.  Should generate Warning.\n");
@@ -702,5 +360,5 @@
         psFree(rc);
     }
-
+ 
     for (i=0;i<numRows;i++) {
         for (j=0;j<numCols;j++) {
@@ -711,11 +369,11 @@
                 testStatus = 1;
             }
-
+ 
             // Restore myReadout for next test.
             myReadout->image->data.F32[i][j] = (float) (i + j);
         }
     }
-
-
+ 
+ 
     printf("------------------------------------------------------------------\n");
     printf("Calling pmSubtractBias() with multiple stats->options.  Should generate Warning.\n");
@@ -734,10 +392,10 @@
     }
     stat->options = PS_STAT_SAMPLE_MEAN;
-
+ 
     printf("------------------------------------------------------------------\n");
     printf("Calling pmSubtractBias() undersize overscans (PM_OVERSCAN_ROWS).  Should generate Warning.\n");
     rc = pmSubtractBias(myReadout, NULL, listShort, PM_OVERSCAN_ROWS, stat,
                         0, PM_FIT_NONE, NULL);
-    /*
+    if (0) {
         for (i=0;i<numRows;i++) {
             for (j=0;j<numCols;j++) {
@@ -750,11 +408,11 @@
             }
         }
-    */
-
+    }
+ 
     printf("------------------------------------------------------------------\n");
     printf("Calling pmSubtractBias() undersize overscans (PM_OVERSCAN_COLUMNS).  Should generate Warning.\n");
     rc = pmSubtractBias(myReadout, NULL, listShort, PM_OVERSCAN_COLUMNS, stat,
                         0, PM_FIT_NONE, NULL);
-    /*
+    if (0) {
         for (i=0;i<numRows;i++) {
             for (j=0;j<numCols;j++) {
@@ -767,6 +425,6 @@
             }
         }
-    */
-
+    }
+ 
     printf("------------------------------------------------------------------\n");
     printf("Calling pmSubtractBias() undersize bias image (short rows).  Should generate Error.\n");
@@ -778,6 +436,6 @@
         psFree(rc);
     }
-
-
+ 
+ 
     printf("------------------------------------------------------------------\n");
     printf("Calling pmSubtractBias() undersize bias image (short columns).  Should generate Error.\n");
@@ -789,5 +447,5 @@
         psFree(rc);
     }
-
+ 
     printf("------------------------------------------------------------------\n");
     printf("Calling pmSubtractBias() with bogus PM_FIT.  Should generate Error.\n");
@@ -799,5 +457,5 @@
         psFree(rc);
     }
-
+ 
     printf("------------------------------------------------------------------\n");
     printf("Calling pmSubtractBias() with bogus overScanAxis.  Should generate Error.\n");
@@ -809,6 +467,6 @@
         psFree(rc);
     }
-
-    /*
+ 
+    if (0) {
         for (i=0;i<numRows;i++) {
             for (j=0;j<numCols;j++) {
@@ -821,6 +479,6 @@
             }
         }
-    */
-
+    }
+ 
     printf("------------------------------------------------------------------\n");
     psFree(myReadout);
@@ -837,29 +495,32 @@
     psFree(list);
     psFree(listShort);
-
+ 
     printFooter(stdout, "pmSubtractBias", "Testing input parameter error conditions", true);
     return(testStatus);
 }
-
+ 
 int test04( void )
 {
     int testStatus = 0;
-
+ 
     testStatus |= doSubtractOverscansTestInputCases(NUM_COLS, NUM_ROWS);
     return(testStatus);
 }
+*/
 
 /******************************************************************************
-doSubtractFullOverscansSmall(): a sample pmReadout as well as several overscan
-images of smaller size are created.  The overscan images are collected
-pixel-by-pixel then subtracted column-wise from the pmReadout.
+doSubtractFullOverscanColumnsGeneric(): This is a general version of the
+bias subtraction tests which allows the various parameters to be specified
+as arguments.
  *****************************************************************************/
-int doSubtractFullOverscanColumnsGeneric(int imageNumCols,
-        int imageNumRows,
-        int overscanNumCols,
-        int overscanNumRows,
-        pmOverscanAxis overscanaxis,
-        pmFit fit,
-        psS32 nBin)
+int doSubtractFullOverscanColumnsGeneric(
+    int imageNumCols,
+    int imageNumRows,
+    int overscanNumCols,
+    int overscanNumRows,
+    int numOverscans,
+    pmOverscanAxis overscanaxis,
+    pmFit fit,
+    psS32 nBin)
 {
     int i;
@@ -869,73 +530,137 @@
     int testStatus = 0;
 
-    psImage *tmpImage1 = psImageAlloc(imageNumCols, imageNumRows, PS_TYPE_F32);
-    //    pmReadout *myReadout = pmReadoutAlloc(imageNumCols, imageNumRows, tmpImage1);
-    pmReadout *myReadout = pmReadoutAlloc(NULL);
-    myReadout->image = tmpImage1;
+    printPositiveTestHeader(stdout, "pmSubtractBias", "PUT COMMENT HERE");
+    printf("---- doSubtractFullOverscanColumnsGeneric() ----\n");
+    printf("    Image size: %d by %d\n", imageNumRows, imageNumCols);
+    printf("    Overscan size: %d by %d\n", overscanNumRows, overscanNumCols);
+    printf("    Total Overscans: %d\n", numOverscans);
+    printf("    Binning factor: %d\n", nBin);
+    if (overscanaxis == PM_OVERSCAN_ROWS)
+        printf("    Overscan axis: PM_OVERSCAN_ROWS\n");
+    if (overscanaxis == PM_OVERSCAN_COLUMNS)
+        printf("    Overscan axis: PM_OVERSCAN_COLUMNS\n");
+    if (overscanaxis == PM_OVERSCAN_ALL)
+        printf("    Overscan axis: PM_OVERSCAN_ALL\n");
+    if (overscanaxis == PM_OVERSCAN_NONE)
+        printf("    Overscan axis: PM_OVERSCAN_NONE\n");
+    if (fit == PM_FIT_NONE)
+        printf("    Fit type: PM_FIT_NONE\n");
+    if (fit == PM_FIT_POLYNOMIAL)
+        printf("    Fit type: PM_FIT_POLYNOMIAL\n");
+    if (fit == PM_FIT_SPLINE)
+        printf("    Fit type: PM_FIT_SPLINE\n");
+
+    //
+    // Create and initialize input image, FPA hierarchy.
+    //
+    const psMetadata *camera = psMetadataAlloc();
+    pmFPA* fpa = pmFPAAlloc(camera);
+
+    if (fpa == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmFPAAlloc returned a NULL.\n");
+        return 1;
+    }
+
+    pmChip *chip = pmChipAlloc(fpa, "ChipName");
+    if (chip == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmChipAlloc returned a NULL.\n");
+        return 2;
+    }
+
+    pmCell *cell = pmCellAlloc(chip, (psMetadata *) camera, "CellName");
+    if (cell == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "TEST ERROR: pmCellAlloc returned a NULL.\n");
+        return 3;
+    }
+
+    pmReadout *myReadout = pmReadoutAlloc(cell);
+    myReadout->image = psImageAlloc(imageNumCols, imageNumRows, PS_TYPE_F32);
+    for (i=0;i<myReadout->image->numRows;i++) {
+        for (j=0;j<myReadout->image->numCols;j++) {
+            myReadout->image->data.F32[i][j] = (float) (i + j);
+        }
+    }
+
+    //
+    // Set overscan axis in the metadata.
+    //
+    psBool rc;
+    if (overscanaxis == PM_OVERSCAN_ROWS) {
+        rc = psMetadataAddS32(myReadout->parent->concepts, PS_LIST_HEAD, "CELL.READDIR", 0, NULL, 1);
+    } else if (overscanaxis == PM_OVERSCAN_COLUMNS) {
+        rc = psMetadataAddS32(myReadout->parent->concepts, PS_LIST_HEAD, "CELL.READDIR", 0, NULL, 2);
+    } else if (overscanaxis == PM_OVERSCAN_ALL) {
+        rc = psMetadataAddS32(myReadout->parent->concepts, PS_LIST_HEAD, "CELL.READDIR", 0, NULL, 3);
+    } else if (overscanaxis == PM_OVERSCAN_NONE) {
+        rc = psMetadataAddS32(myReadout->parent->concepts, PS_LIST_HEAD, "CELL.READDIR", 0, NULL, 0);
+    }
+    if (rc == false) {
+        printf("TEST ERROR: Could not set CELL.READDIR metadata.\n");
+        testStatus = 1;
+    }
+
+    psStats *stat = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    psPolynomial1D *myPoly = psPolynomial1DAlloc(POLYNOMIAL_FIT_ORDER, PS_POLYNOMIAL_ORD);
+    psSpline1D *mySpline = NULL;
+
+
+    if (0) {
+        if (overscanNumRows <= 0) {
+            overscanNumRows = 1;
+        }
+        if (overscanNumCols <= 0) {
+            overscanNumCols = 1;
+        }
+    }
+    psF32 oAverage = 0.0;
+    myReadout->bias = NULL;
+    for (psS32 i = 0 ; i < numOverscans ; i++) {
+        psImage *tmpImage = psImageAlloc(overscanNumCols, overscanNumRows, PS_TYPE_F32);
+        psF32 oValue = (float) (i + 3);
+        PS_IMAGE_SET_F32(tmpImage, oValue);
+        oAverage += oValue;
+        if (myReadout->bias == NULL) {
+            myReadout->bias = psListAlloc(tmpImage);
+        } else {
+            psListAdd(myReadout->bias, PS_LIST_HEAD, tmpImage);
+        }
+    }
+    oAverage/= (psF32) numOverscans;
+
+
+    if (fit == PM_FIT_NONE) {
+        myReadout = pmSubtractBias(myReadout, NULL, PM_FIT_NONE, overscanaxis,
+                                   stat, nBin, NULL, NULL);
+    } else if (fit == PM_FIT_POLYNOMIAL) {
+        myReadout = pmSubtractBias(myReadout, myPoly, PM_FIT_POLYNOMIAL, overscanaxis,
+                                   stat, nBin, NULL, NULL);
+    } else if (fit == PM_FIT_SPLINE) {
+        myReadout = pmSubtractBias(myReadout, mySpline, PM_FIT_SPLINE, overscanaxis,
+                                   stat, nBin, NULL, NULL);
+    }
+
     for (i=0;i<imageNumRows;i++) {
         for (j=0;j<imageNumCols;j++) {
-            myReadout->image->data.F32[i][j] = (float) (i + j);
-        }
-    }
-
-    psStats *stat = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
-    psPolynomial1D *myPoly = psPolynomial1DAlloc(1, PS_POLYNOMIAL_ORD);
-    psSpline1D *mySpline = NULL;
-
-
-    /*
-        if (overscanNumRows <= 0) {
-            overscanNumRows = 1;
-        }
-        if (overscanNumCols <= 0) {
-            overscanNumCols = 1;
-        }
-    */
-    psImage *tmpImage2 = psImageAlloc(overscanNumCols, overscanNumRows, PS_TYPE_F32);
-    psImage *tmpImage3 = psImageAlloc(overscanNumCols, overscanNumRows, PS_TYPE_F32);
-    psImage *tmpImage4 = psImageAlloc(overscanNumCols, overscanNumRows, PS_TYPE_F32);
-    psList *list;
-    for (i=0;i<overscanNumRows;i++) {
-        for (j=0;j<overscanNumCols;j++) {
-            tmpImage2->data.F32[i][j] = 3.0;
-            tmpImage3->data.F32[i][j] = 4.0;
-            tmpImage4->data.F32[i][j] = 5.0;
-        }
-    }
-    list = psListAlloc(tmpImage2);
-    psListAdd(list, PS_LIST_HEAD, tmpImage3);
-    psListAdd(list, PS_LIST_HEAD, tmpImage4);
-
-    printPositiveTestHeader(stdout, "pmSubtractBias", "Column Overscans");
-
-    if (fit == PM_FIT_NONE) {
-        myReadout = pmSubtractBias(myReadout, NULL, list, overscanaxis, stat,
-                                   nBin, fit, NULL);
-    } else if (fit == PM_FIT_POLYNOMIAL) {
-        myReadout = pmSubtractBias(myReadout, myPoly, list, overscanaxis, stat,
-                                   nBin, fit, NULL);
-    } else if (fit == PM_FIT_SPLINE) {
-        myReadout = pmSubtractBias(myReadout, mySpline, list, overscanaxis, stat,
-                                   nBin, fit, NULL);
-    }
-
-    for (i=0;i<imageNumRows;i++) {
-        for (j=0;j<imageNumCols;j++) {
-            expect = ((float) (i + j)) - 12.0;
+            expect = ((float) (i + j)) - oAverage;
             actual = myReadout->image->data.F32[i][j];
             if (FLT_EPSILON < fabs(expect - actual)) {
                 printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
                 testStatus = 1;
+            } else {
+                //printf("COOL: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
             }
         }
     }
-
-
+    psFree(fpa);
+    psFree(chip);
+    psFree(cell);
+    psListElem *tmpElem = (psListElem *) myReadout->bias->head;
+    while (NULL != tmpElem) {
+        psFree((psImage *) tmpElem->data);
+        tmpElem = tmpElem->next;
+    }
     psFree(myReadout);
-    psFree(tmpImage2);
-    psFree(tmpImage3);
-    psFree(tmpImage4);
+    psFree(camera);
     psFree(stat);
-    psFree(list);
     psFree(myPoly);
     psFree(mySpline);
@@ -945,5 +670,47 @@
 }
 
-int testX( void )
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************
+test05a() The following combinations are tested here:
+ Overscan images are same size, no fit, bin factor is 1.
+ Overscan images are same size, no fit, bin factor is 2.
+ *****************************************************************************/
+int test05a(
+    psS32 imageNumCols,
+    psS32 imageNumRows,
+    psS32 overscanNumCols,
+    psS32 overscanNumRows)
 {
     int testStatus = 0;
@@ -955,16 +722,16 @@
     // Overscan images are same size, no fit, bin factor is 1.
     //
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS, NUM_ROWS,
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
                   PM_OVERSCAN_ALL,
                   PM_FIT_NONE, 1);
 
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS, NUM_ROWS,
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
                   PM_OVERSCAN_COLUMNS,
                   PM_FIT_NONE, 1);
 
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS, NUM_ROWS,
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
                   PM_OVERSCAN_ROWS,
                   PM_FIT_NONE, 1);
@@ -973,125 +740,351 @@
     // Overscan images are same size, no fit, bin factor is 2.
     //
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS, NUM_ROWS,
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
                   PM_OVERSCAN_ALL,
                   PM_FIT_NONE, 2);
 
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS, NUM_ROWS,
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
                   PM_OVERSCAN_COLUMNS,
                   PM_FIT_NONE, 2);
 
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS, NUM_ROWS,
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
                   PM_OVERSCAN_ROWS,
                   PM_FIT_NONE, 2);
-    //
-    // Overscan images are too small, spline fit, bin factor is 1.
-    //
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS-1, NUM_ROWS-1,
-                  PM_OVERSCAN_ALL,
-                  PM_FIT_SPLINE, 1);
-
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS-1, NUM_ROWS-1,
-                  PM_OVERSCAN_COLUMNS,
-                  PM_FIT_SPLINE, 1);
-
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS-1, NUM_ROWS-1,
-                  PM_OVERSCAN_ROWS,
-                  PM_FIT_SPLINE, 1);
-
-    //
-    // Overscan images are too small, spline fit, bin factor is 2.
-    //
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS-1, NUM_ROWS-1,
-                  PM_OVERSCAN_ALL,
-                  PM_FIT_SPLINE, 2);
-
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS-1, NUM_ROWS-1,
-                  PM_OVERSCAN_COLUMNS,
-                  PM_FIT_SPLINE, 2);
-
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS-1, NUM_ROWS-1,
-                  PM_OVERSCAN_ROWS,
-                  PM_FIT_SPLINE, 2);
-
-    //
-    // Overscan images are same size, spline fit, bin factor is 1.
-    //
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS, NUM_ROWS,
-                  PM_OVERSCAN_ALL,
-                  PM_FIT_SPLINE, 1);
-
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS, NUM_ROWS,
-                  PM_OVERSCAN_COLUMNS,
-                  PM_FIT_SPLINE, 1);
-
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS, NUM_ROWS,
-                  PM_OVERSCAN_ROWS,
-                  PM_FIT_SPLINE, 1);
-    //
-    // Overscan images are same size, spline fit, bin factor is 2.
-    //
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS, NUM_ROWS,
-                  PM_OVERSCAN_ALL,
-                  PM_FIT_SPLINE, 2);
-
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS, NUM_ROWS,
-                  PM_OVERSCAN_COLUMNS,
-                  PM_FIT_SPLINE, 2);
-
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS, NUM_ROWS,
-                  PM_OVERSCAN_ROWS,
-                  PM_FIT_SPLINE, 2);
-
-    //
-    // Overscan images are same size, polynomial fit, bin factor is 1.
-    //
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS, NUM_ROWS,
-                  PM_OVERSCAN_ALL,
-                  PM_FIT_POLYNOMIAL, 1);
-
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS, NUM_ROWS,
-                  PM_OVERSCAN_COLUMNS,
-                  PM_FIT_POLYNOMIAL, 1);
-
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS, NUM_ROWS,
-                  PM_OVERSCAN_ROWS,
-                  PM_FIT_POLYNOMIAL, 1);
-    //
-    // Overscan images are same size, polynomial fit, bin factor is 2.
-    //
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS, NUM_ROWS,
-                  PM_OVERSCAN_ALL,
-                  PM_FIT_POLYNOMIAL, 2);
-
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS, NUM_ROWS,
-                  PM_OVERSCAN_COLUMNS,
-                  PM_FIT_POLYNOMIAL, 2);
-
-    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
-                  NUM_COLS, NUM_ROWS,
-                  PM_OVERSCAN_ROWS,
-                  PM_FIT_POLYNOMIAL, 2);
 
     return(testStatus);
 }
 
+
+/******************************************************************************
+test05b() The following combinations are tested here:
+ Overscan images are too small, spline fit, bin factor is 1.
+ Overscan images are too small, spline fit, bin factor is 2.
+ Overscan images are same size, spline fit, bin factor is 1.
+ Overscan images are same size, spline fit, bin factor is 2.
+ Overscan images are too big,   spline fit, bin factor is 1.
+ Overscan images are too big,   spline fit, bin factor is 2.
+ A single overscan image of the same size, spline fit, bin factor is 1.
+ 
+ Overscan images are too small, polynomial fit, bin factor is 1.
+ Overscan images are too small, polynomial fit, bin factor is 2.
+ Overscan images are same size, polynomial fit, bin factor is 1.
+ Overscan images are same size, polynomial fit, bin factor is 2.
+ Overscan images are too big,   polynomial fit, bin factor is 1.
+ Overscan images are too big,   polynomial fit, bin factor is 2.
+ A single overscan image of the same size, polynomial fit, bin factor is 1.
+ 
+XXX: Must add M-by-N image size tests.
+ *****************************************************************************/
+int test05b(
+    psS32 imageNumCols,
+    psS32 imageNumRows,
+    psS32 overscanNumCols,
+    psS32 overscanNumRows)
+{
+    int testStatus = 0;
+
+    //
+    // Overscan images are too small, spline fit, bin factor is 1.
+    //
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_SPLINE, 1);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_SPLINE, 1);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_SPLINE, 1);
+
+    //
+    // Overscan images are too small, spline fit, bin factor is 2.
+    //
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_SPLINE, 2);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_SPLINE, 2);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_SPLINE, 2);
+
+
+    //
+    // Overscan images are same size, spline fit, bin factor is 1.
+    //
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_SPLINE, 1);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_SPLINE, 1);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_SPLINE, 1);
+
+    //
+    // Overscan images are same size, spline fit, bin factor is 2.
+    //
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_SPLINE, 2);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_SPLINE, 2);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_SPLINE, 2);
+
+    //
+    // Overscan images are too big, spline fit, bin factor is 1.
+    //
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_SPLINE, 1);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_SPLINE, 1);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_SPLINE, 1);
+
+    //
+    // Overscan images are too big, spline fit, bin factor is 2.
+    //
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_SPLINE, 2);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_SPLINE, 2);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2 , NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_SPLINE, 2);
+
+
+    //
+    // A single overscan image of the same size, spline fit, bin factor is 1.
+    //
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, 1,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_SPLINE, 1);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, 1,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_SPLINE, 1);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, 1,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_SPLINE, 1);
+
+
+    //
+    // Overscan images are too small, polynomial fit, bin factor is 1.
+    //
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    //
+    // Overscan images are too small, polynomial fit, bin factor is 2.
+    //
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_POLYNOMIAL, 2);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_POLYNOMIAL, 2);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols-2, overscanNumRows-2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_POLYNOMIAL, 2);
+
+
+    //
+    // Overscan images are same size, polynomial fit, bin factor is 1.
+    //
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    //
+    // Overscan images are same size, polynomial fit, bin factor is 2.
+    //
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_POLYNOMIAL, 2);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_POLYNOMIAL, 2);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_POLYNOMIAL, 2);
+
+    //
+    // Overscan images are too big, polynomial fit, bin factor is 1.
+    //
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    //
+    // Overscan images are too big, polynomial fit, bin factor is 2.
+    //
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_POLYNOMIAL, 2);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2, NUM_OVERSCANS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_POLYNOMIAL, 2);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols+2, overscanNumRows+2, NUM_OVERSCANS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_POLYNOMIAL, 2);
+
+    //
+    // A single overscan image of the same size, polynomial fit, bin factor is 1.
+    //
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, 1,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, 1,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(imageNumCols, imageNumRows,
+                  overscanNumCols, overscanNumRows, 1,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_POLYNOMIAL, 1);
+
+    return(testStatus);
+}
+
+
+
+#define LOW_COLS 3
+#define LOW_ROWS 3
+
+/******************************************************************************
+test05(): See test05a() and test05b().
+ 
+We run the tests in test05b() starting with all possible combinations of
+sizes.
+ 
+XXX: Must add M-by-N image size tests.
+ *****************************************************************************/
+int test05()
+{
+    int testStatus = 0;
+    testStatus = test05a(NUM_COLS, NUM_ROWS, NUM_COLS, NUM_ROWS);
+
+    //    testStatus|= test05b(LOW_COLS, LOW_ROWS, LOW_COLS, LOW_ROWS);
+    //    testStatus|= test05b(LOW_COLS, LOW_ROWS, LOW_COLS, NUM_ROWS);
+    //    testStatus|= test05b(LOW_COLS, LOW_ROWS, NUM_COLS, LOW_ROWS);
+    //    testStatus|= test05b(LOW_COLS, LOW_ROWS, NUM_COLS, NUM_ROWS);
+
+    //    testStatus|= test05b(LOW_COLS, NUM_ROWS, LOW_COLS, LOW_ROWS);
+    //    testStatus|= test05b(LOW_COLS, NUM_ROWS, LOW_COLS, NUM_ROWS);
+    //    testStatus|= test05b(LOW_COLS, NUM_ROWS, NUM_COLS, LOW_ROWS);
+    //    testStatus|= test05b(LOW_COLS, NUM_ROWS, NUM_COLS, NUM_ROWS);
+
+    //    testStatus|= test05b(NUM_COLS, LOW_ROWS, LOW_COLS, LOW_ROWS);
+    //    testStatus|= test05b(NUM_COLS, LOW_ROWS, LOW_COLS, NUM_ROWS);
+    //    testStatus|= test05b(NUM_COLS, LOW_ROWS, NUM_COLS, LOW_ROWS);
+    //    testStatus|= test05b(NUM_COLS, LOW_ROWS, NUM_COLS, NUM_ROWS);
+
+    //    testStatus|= test05b(NUM_COLS, NUM_ROWS, LOW_COLS, LOW_ROWS);
+    //    testStatus|= test05b(NUM_COLS, NUM_ROWS, LOW_COLS, NUM_ROWS);
+    //    testStatus|= test05b(NUM_COLS, NUM_ROWS, NUM_COLS, LOW_ROWS);
+    testStatus|= test05b(NUM_COLS, NUM_ROWS, NUM_COLS, NUM_ROWS);
+
+
+    return(testStatus);
+}
+
+
Index: /trunk/psModules/test/imsubtract/tst_pmSubtractSky.c
===================================================================
--- /trunk/psModules/test/imsubtract/tst_pmSubtractSky.c	(revision 5515)
+++ /trunk/psModules/test/imsubtract/tst_pmSubtractSky.c	(revision 5516)
@@ -7,12 +7,11 @@
  *  @author GLG, MHPCC
  *
- *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-09-28 20:42:52 $
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-11-15 20:09:03 $
+ *
+ *  XXX: I added the CELL.TRIMSEC region code but there are not tests for it.
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
  */
-
-
-
 #include "psTest.h"
 #include "pslib.h"
@@ -67,9 +66,14 @@
     myReadout = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_POLYNOMIAL,
                               binFactor, myStats, 10.0);
-    for (i=0;i<numRows;i++) {
-        for (j=0;j<numCols;j++) {
-            if (ERROR_TOLERANCE < fabs(myReadout->image->data.F32[i][j])) {
-                printf("TEST ERROR: image[%d][%d] is %f, should be 0.0\n", i, j, myReadout->image->data.F32[i][j]);
-                testStatus = 1;
+    if (myReadout == NULL) {
+        printf("TEST ERROR: pmSubtractSky() returned NULL.\n");
+        testStatus = 1;
+    } else {
+        for (i=0;i<numRows;i++) {
+            for (j=0;j<numCols;j++) {
+                if (ERROR_TOLERANCE < fabs(myReadout->image->data.F32[i][j])) {
+                    printf("TEST ERROR: image[%d][%d] is %f, should be 0.0\n", i, j, myReadout->image->data.F32[i][j]);
+                    testStatus = 1;
+                }
             }
         }
@@ -117,10 +121,15 @@
     myReadout = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_POLYNOMIAL,
                               binFactor, myStats, 2.0);
-    for (i=0;i<numRows;i++) {
-        for (j=0;j<numCols;j++) {
-            if (errorTolerance < fabs(myReadout->image->data.F32[i][j] - trueImage->data.F32[i][j])) {
-                printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j,
-                       myReadout->image->data.F32[i][j], trueImage->data.F32[i][j]);
-                testStatus = 1;
+    if (myReadout == NULL) {
+        printf("TEST ERROR: pmSubtractSky() returned NULL.\n");
+        testStatus = 1;
+    } else {
+        for (i=0;i<numRows;i++) {
+            for (j=0;j<numCols;j++) {
+                if (errorTolerance < fabs(myReadout->image->data.F32[i][j] - trueImage->data.F32[i][j])) {
+                    printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j,
+                           myReadout->image->data.F32[i][j], trueImage->data.F32[i][j]);
+                    testStatus = 1;
+                }
             }
         }
Index: /trunk/psModules/test/objects/tst_pmObjects01.c
===================================================================
--- /trunk/psModules/test/objects/tst_pmObjects01.c	(revision 5515)
+++ /trunk/psModules/test/objects/tst_pmObjects01.c	(revision 5516)
@@ -27,6 +27,6 @@
     most of psObjects.c is not tested
  *
- *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-10-20 23:06:24 $
+ *  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-11-15 20:09:03 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -1287,6 +1287,2 @@
 
 
-// this code will
-
-
-
