Index: /tags/ipp-1-X/rel8_2/psModules/.cvsignore
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/.cvsignore	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/.cvsignore	(revision 22115)
@@ -0,0 +1,27 @@
+autom4te.cache
+Makefile
+config.log
+config.status
+libtool
+Makefile.in
+aclocal.m4
+configure
+Doxyfile
+DoxygenLog
+docs
+man
+psmodule-config
+psmodule.pc
+bin
+include
+lib
+config.guess
+config.sub
+depcomp
+install-sh
+ltmain.sh
+missing
+psmodule.kdevelop.pcs
+psmodule-*.tar.gz
+psmodule-*.tar.bz2
+compile
Index: /tags/ipp-1-X/rel8_2/psModules/AUTHORS
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/AUTHORS	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/AUTHORS	(revision 22115)
@@ -0,0 +1,1 @@
+Maui High Performance Computing Center, University of Hawai'i
Index: /tags/ipp-1-X/rel8_2/psModules/COPYING
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/COPYING	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/COPYING	(revision 22115)
@@ -0,0 +1,6 @@
+(C) 2004 University of Hawai'i
+
+Permission to copy/distribute to be governed by:
+    Institute for Astronomy
+    2680 Woodlawn Drive
+    Honolulu, HI 96822-1897
Index: /tags/ipp-1-X/rel8_2/psModules/ChangeLog
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/ChangeLog	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/ChangeLog	(revision 22115)
@@ -0,0 +1,1 @@
+ 
Index: /tags/ipp-1-X/rel8_2/psModules/Doxyfile.in
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/Doxyfile.in	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/Doxyfile.in	(revision 22115)
@@ -0,0 +1,1079 @@
+# Doxyfile 1.3.4
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = "Pan-STARRS Module Library"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER         = @VERSION@
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = @prefix@/docs/psmodule
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch,
+# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en
+# (Japanese with English messages), Korean, Norwegian, Polish, Portuguese,
+# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# This tag can be used to specify the encoding used in the generated output.
+# The encoding is not always determined by the language that is chosen,
+# but also whether or not the output is meant for Windows or non-Windows users.
+# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES
+# forces the Windows encoding (this is the default for the Windows binary),
+# whereas setting the tag to NO uses a Unix-style encoding (the default for
+# all platforms other than Windows).
+
+USE_WINDOWS_ENCODING   = NO
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC    = YES
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited
+# members of a class in the documentation of that class as if those members were
+# ordinary class members. Constructors, destructors and assignment operators of
+# the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. It is allowed to use relative paths in the argument list.
+
+STRIP_FROM_PATH        =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like the Qt-style comments (thus requiring an
+# explict @brief command for a brief description.
+
+JAVADOC_AUTOBRIEF      = YES
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the DETAILS_AT_TOP tag is set to YES then Doxygen
+# will output the detailed description near the top, like JavaDoc.
+# If set to NO, the detailed description appears after the member
+# documentation.
+
+DETAILS_AT_TOP         = YES
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# reimplements.
+
+INHERIT_DOCS           = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources
+# only. Doxygen will then generate output that is more tailored for Java.
+# For instance, namespaces will be presented as packages, qualified scopes
+# will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING            = NO
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
+# documentation blocks found inside the body of a function. 
+# If set to NO (the default) these blocks will be appended to the 
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower-case letters. If set to YES upper-case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put a list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting 
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consists of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
+# potential errors in the documentation, such as not documenting some 
+# parameters in a documented function, or documenting parameters that 
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = DoxygenLog
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT                  = src
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp
+# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc
+
+FILE_PATTERNS          = *.h *.dox
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories 
+# that are symbolic links (a Unix filesystem feature) are excluded from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories.
+
+EXCLUDE_PATTERNS       = *_wrap.c
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = 
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             = 
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.
+
+INPUT_FILTER           = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources.
+
+SOURCE_BROWSER         = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = NO
+
+# If the REFERENCED_BY_RELATION tag is set to YES (the default) 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = YES
+
+# If the REFERENCES_RELATION tag is set to YES (the default) 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = YES
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index 
+# of all compounds will be generated. Enable this if the project 
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX     = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER            = 
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet
+
+HTML_STYLESHEET        = 
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output dir.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = YES
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 10
+
+# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
+# generated containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
+# probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW      = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = YES
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = letter
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = times
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
+# include the index chapters (such as File Index, Compound Index, etc.) 
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimised for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assigments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = YES
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_XML           = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
+# generate a Perl module file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
+# nicely formatted so it can be parsed by a human reader.  This is useful 
+# if you want to understand what is going on.  On the other hand, if this 
+# tag is set to NO the size of the Perl module output will be much smaller 
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file 
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
+# This is useful so different doxyrules.make files included by the same 
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_PREDEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed.
+
+PREDEFINED             = DOXYGEN
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse the 
+# parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. 
+# Optionally an initial location of the external documentation 
+# can be added for each tagfile. The format of a tag file without 
+# this location is as follows: 
+#   TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows: 
+#   TAGFILES = file1=loc1 "file2 = loc2" ... 
+# where "loc1" and "loc2" can be relative or absolute paths or 
+# URLs. If a location is present for each tag, the installdox tool 
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen 
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = @PERL@
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or
+# super classes. Setting the tag to NO turns the diagrams off. Note that this 
+# option is superceded by the HAVE_DOT option below. This is only a fallback. It is 
+# recommended to install and use dot, since it yields more powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = YES
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
+# collaboration diagrams in a style similiar to the OMG's Unified Modeling 
+# Language.
+
+UML_LOOK               = YES
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will 
+# generate a call dependency graph for every global function or class method. 
+# Note that enabling this option will significantly increase the time of a run. 
+# So in most cases it will be better to enable call graphs for selected 
+# functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found on the path.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_WIDTH    = 1024
+
+# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height 
+# (in pixels) of the graphs generated by dot. If a graph becomes larger than 
+# this value, doxygen will try to truncate the graph, so that it fits within 
+# the specified constraint. Beware that most browsers cannot cope with very 
+# large images.
+
+MAX_DOT_GRAPH_HEIGHT   = 1024
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
+# graphs generated by dot. A depth value of 3 means that only nodes reachable 
+# from the root by following a path via at most 3 edges will be shown. Nodes that 
+# lay further from the root node will be omitted. Note that setting this option to 
+# 1 or 2 may greatly reduce the computation time needed for large code bases. Also 
+# note that a graph may be further truncated if the graph's image dimensions are 
+# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). 
+# If 0 is used for the depth value (the default), the graph is not depth-constrained.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermediate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Configuration::addtions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
+
Index: /tags/ipp-1-X/rel8_2/psModules/INSTALL
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/INSTALL	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/INSTALL	(revision 22115)
@@ -0,0 +1,167 @@
+Basic Installation
+==================
+
+   These are generic installation instructions.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, a file
+`config.cache' that saves the results of its tests to speed up
+reconfiguring, and a file `config.log' containing compiler output
+(useful mainly for debugging `configure').
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If at some point `config.cache'
+contains results you don't want to keep, you may remove or edit it.
+
+   The file `configure.in' is used to create `configure' by a program
+called `autoconf'.  You only need `configure.in' if you want to change
+it or regenerate `configure' using a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.  If you're
+     using `csh' on an old version of System V, you might need to type
+     `sh ./configure' instead to prevent `csh' from trying to execute
+     `configure' itself.
+
+     Running `configure' takes a while.  While running, it prints some
+     messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Type `make install' to install the programs and any data files and
+     documentation.
+
+  4. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  
+
+Compilers and Options
+=====================
+
+   Some systems require unusual options for compilation or linking that
+the `configure' script does not know about.  You can give `configure'
+initial values for variables by setting them in the environment.  Using
+a Bourne-compatible shell, you can do that on the command line like
+this:
+     CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
+
+Or on systems that have the `env' program, you can do it like this:
+     env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
+
+Compiling For Multiple Architectures
+====================================
+
+   You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   If you have to use a `make' that does not supports the `VPATH'
+variable, you have to compile the package for one architecture at a time
+in the source code directory.  After you have installed the package for
+one architecture, use `make distclean' before reconfiguring for another
+architecture.
+
+Installation Names
+==================
+
+   By default, `make install' will install the package's files in
+`/usr/local/bin', `/usr/local/man', etc.  You can specify an
+installation prefix other than `/usr/local' by giving `configure' the
+option `--prefix=PATH'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+give `configure' the option `--exec-prefix=PATH', the package will use
+PATH as the prefix for installing programs and libraries.
+Documentation and other data files will still use the regular prefix.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+   Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+   There may be some features `configure' can not figure out
+automatically, but needs to determine by the type of host the package
+will run on.  Usually `configure' can figure that out, but if it prints
+a message saying it can not guess the host type, give it the
+`--host=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name with three fields:
+     CPU-COMPANY-SYSTEM
+
+See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the host type.
+
+   If you are building compiler tools for cross-compiling, you can also
+use the `--target=TYPE' option to select the type of system they will
+produce code for and the `--build=TYPE' option to select the type of
+system on which you are compiling the package.
+
+Sharing Defaults
+================
+
+   If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Operation Controls
+==================
+
+   `configure' recognizes the following options to control how it
+operates.
+
+`--cache-file=FILE'
+     Use and save the results of the tests in FILE instead of
+     `./config.cache'.  Set FILE to `/dev/null' to disable caching, for
+     debugging `configure'.
+
+`--help'
+     Print a summary of the options to `configure', and exit.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`--version'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`configure' also accepts some other, not widely useful, options.
+
Index: /tags/ipp-1-X/rel8_2/psModules/Makefile.am
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/Makefile.am	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/Makefile.am	(revision 22115)
@@ -0,0 +1,30 @@
+SUBDIRS = src test
+
+bin_SCRIPTS = psmodule-config
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA= psmodule.pc
+
+EXTRA_DIST = Doxyfile.in psmodule-config.in psmodule.pc.in
+
+if DOXYGEN
+
+docs: Doxyfile $(prefix)/docs/psmodule $(mandir)/man3
+	doxygen Doxyfile
+	mv -f $(prefix)/docs/psmodule/man/man3/* $(mandir)/man3
+	rm -rf $(prefix)/docs/psmodule/man
+
+$(prefix)/docs/psmodule:
+	mkdir -p $(prefix)/docs/psmodule
+
+$(mandir)/man3:
+	mkdir -p $(mandir)/man3
+
+uninstall-hook:
+	rm -rf $(prefix)/docs/psmodule/*
+
+CLEANFILES = $(prefix)/docs/psmodule/*
+
+endif
+
+test: check
Index: /tags/ipp-1-X/rel8_2/psModules/NEWS
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/NEWS	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/NEWS	(revision 22115)
@@ -0,0 +1,1 @@
+ 
Index: /tags/ipp-1-X/rel8_2/psModules/README
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/README	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/README	(revision 22115)
@@ -0,0 +1,1 @@
+
Index: /tags/ipp-1-X/rel8_2/psModules/TODO
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/TODO	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/TODO	(revision 22115)
@@ -0,0 +1,1 @@
+ 
Index: /tags/ipp-1-X/rel8_2/psModules/autogen.sh
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/autogen.sh	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/autogen.sh	(revision 22115)
@@ -0,0 +1,106 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=psmodule
+TEST_TYPE=-f
+FILE=psmodule.pc.in
+
+DIE=0
+
+if [ "`which glibtoolize 2> /dev/null`" != "" ]
+ then LIBTOOLIZE=glibtoolize
+ else LIBTOOLIZE=libtoolize
+fi
+
+ACLOCAL=aclocal
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $LIBTOOlIZE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+        DIE=1
+}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOlIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /tags/ipp-1-X/rel8_2/psModules/configure.ac
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/configure.ac	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/configure.ac	(revision 22115)
@@ -0,0 +1,102 @@
+AC_PREREQ(2.59)
+
+AC_INIT([psmodule],[0.8.2],[http://pan-starrs.ifa.hawaii.edu/bugzilla])
+AC_CONFIG_SRCDIR([psmodule.pc.in])
+
+AM_INIT_AUTOMAKE([1.7 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+dnl otherise AC_PROG_CC will default CFLAGS to "-g -02"
+if test -z ${CFLAGS} ; then
+  CFLAGS="-g -O0"
+fi
+AC_SUBST([CFLAGS])
+
+AC_LANG(C)
+AC_PROG_CC
+dnl FIXME document why GNU_SOURCE is being set
+AC_GNU_SOURCE
+AC_PROG_INSTALL
+AM_PROG_LIBTOOL
+
+AC_PREFIX_DEFAULT([`pwd`])
+
+dnl cflags --------------------------------------------------------------------
+dnl FIXME document why -std=gnu99 is being set
+
+AC_MSG_CHECKING([C99 flag])
+if test "$GCC" = "yes" ; then
+  AM_CFLAGS="-std=gnu99"
+  AC_MSG_RESULT([-std=gnu99])
+else
+  AM_CFLAGS="-std=c99"
+  AC_MSG_RESULT([-std=c99])
+fi
+AM_CFLAGS="${AM_CFLAGS=} -Wall -Werror"
+AC_SUBST([AM_CFLAGS])
+
+SRCPATH='${top_srcdir}/src'
+SRCDIRS="astrom camera config detrend imcombine imsubtract objects photom"
+SRCINC=`echo "${SRCDIRS=}" | ${SED} "s|\(\\w\+\)|-I\${SRCPATH=}/\1|g"`
+SRCSUBLIBS=`echo "${SRCDIRS=}" | ${SED} "s|\(\\w\+\)|\1/libpsmodule\1.la|g"`
+AC_SUBST([SRCSUBLIBS],${SRCSUBLIBS=})
+AC_SUBST([SRCDIRS],${SRCDIRS=})
+AC_SUBST([SRCINC],${SRCINC=})
+
+dnl doxygen -------------------------------------------------------------------
+
+AC_CHECK_PROG([doxygen], [doxygen], [true], [false])
+AM_CONDITIONAL(DOXYGEN, test x$doxygen = xtrue)
+
+dnl pslib ---------------------------------------------------------------------
+AC_ARG_WITH(pslib-config,
+[  --with-pslib-config=FILE  Specify location of psLib-config script],
+[PSLIB_CONFIG=$withval])
+
+if test -z ${PSLIB_CONFIG} ; then
+  PKG_CHECK_MODULES([PSLIB], [pslib >= 0.8.0])
+else
+  AC_CHECK_FILE($PSLIB_CONFIG,[],
+    [AC_MSG_ERROR([psLib is required.  If not in path, use --with-pslib-config to specify pslib-config script location.])])
+  AC_MSG_CHECKING([PSLIB_CFLAGS])
+  PSLIB_CFLAGS="`${PSLIB_CONFIG} --cflags`"
+  AC_MSG_RESULT([${PSLIB_CFLAGS}])
+  AC_MSG_CHECKING([PSLIB_LIBS])
+  PSLIB_LIBS="`${PSLIB_CONFIG} --libs`"
+  AC_MSG_RESULT([${PSLIB_LIBS}])
+fi
+
+PSMODULE_CFLAGS="${PSMODULE_CFLAGS=} ${PSLIB_CFLAGS}"
+PSMODULE_LIBS="${PSMODULE_CFLAGS=} ${PSLIB_LIBS}"
+dnl ---------------------------------------------------------------------------
+
+AC_SUBST([PSMODULE_CFLAGS])
+AC_SUBST([PSMODULE_LIBS])
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+  src/astrom/Makefile
+  src/camera/Makefile
+  src/config/Makefile
+  src/detrend/Makefile
+  src/imcombine/Makefile
+  src/imsubtract/Makefile
+  src/objects/Makefile
+  src/photom/Makefile
+  test/Makefile
+  test/astrom/Makefile
+  test/camera/Makefile
+  test/config/Makefile
+  test/detrend/Makefile
+  test/imcombine/Makefile
+  test/imsubtract/Makefile
+  test/objects/Makefile
+  test/photom/Makefile
+  Doxyfile
+  psmodule-config
+  psmodule.pc
+])
+
+AC_OUTPUT
Index: /tags/ipp-1-X/rel8_2/psModules/psmodule-config.in
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/psmodule-config.in	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/psmodule-config.in	(revision 22115)
@@ -0,0 +1,76 @@
+#! /bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+usage()
+{
+    cat <<EOF
+Usage: pslib-config [OPTION]
+
+Known values for OPTION are:
+
+  --prefix		print psLib installation prefix
+  --libs		print library linking information
+  --cflags		print pre-processor and compiler flags
+  --help		display this help and exit
+  --version		output version information
+
+EOF
+
+    exit $1
+}
+
+if test $# -eq 0; then
+    usage 1
+fi
+
+cflags=false
+libs=false
+
+while test $# -gt 0; do
+    case "$1" in
+    -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+    *) optarg= ;;
+    esac
+
+    case "$1" in
+    --prefix=*)
+	prefix=$optarg
+	;;
+
+    --prefix)
+	echo $prefix
+	;;
+
+    --version)
+	echo @VERSION@
+	exit 0
+	;;
+
+    --help)
+	usage 0
+	;;
+
+    --cflags)
+       	echo -I${includedir} @PSMODULE_CFLAGS@
+       	;;
+
+    --libs)
+       	echo -L${libdir} -lpsmodule @PSMODULE_LIBS@
+       	;;
+
+    --deps)
+       	echo @LDFLAGS@
+       	;;
+    *)
+	usage
+	exit 1
+	;;
+    esac
+    shift
+done
+
+exit 0
Index: /tags/ipp-1-X/rel8_2/psModules/psmodule.kdevelop
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/psmodule.kdevelop	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/psmodule.kdevelop	(revision 22115)
@@ -0,0 +1,180 @@
+<?xml version = '1.0'?>
+<kdevelop>
+  <general>
+    <author>Robert Daniel DeSonia</author>
+    <email>desonia@mhpcc.hawaii.edu</email>
+    <version>0.1</version>
+    <projectmanagement>KDevAutoProject</projectmanagement>
+    <primarylanguage>C</primarylanguage>
+    <keywords>
+      <keyword>C</keyword>
+      <keyword>Code</keyword>
+    </keywords>
+    <projectdirectory>.</projectdirectory>
+    <absoluteprojectpath>false</absoluteprojectpath>
+    <description/>
+    <ignoreparts/>
+    <secondaryLanguages/>
+  </general>
+  <kdevautoproject>
+    <general>
+      <activetarget>src/psmodule</activetarget>
+      <useconfiguration>debug</useconfiguration>
+    </general>
+    <run>
+      <mainprogram>src/tst_pmFlatField</mainprogram>
+      <terminal>true</terminal>
+      <directoryradio>executable</directoryradio>
+      <customdirectory>/</customdirectory>
+      <programargs/>
+      <autocompile>true</autocompile>
+      <envvars/>
+    </run>
+    <configurations>
+      <optimized>
+        <builddir>optimized</builddir>
+        <ccompiler>kdevgccoptions</ccompiler>
+        <cxxcompiler>kdevgppoptions</cxxcompiler>
+        <f77compiler>kdevg77options</f77compiler>
+        <cflags>-O2 -g0</cflags>
+      </optimized>
+      <debug>
+        <configargs>--enable-debug=full</configargs>
+        <builddir>debug</builddir>
+        <ccompiler>kdevgccoptions</ccompiler>
+        <cxxcompiler>kdevgppoptions</cxxcompiler>
+        <f77compiler>kdevg77options</f77compiler>
+        <cflags>-O0 -g3</cflags>
+      </debug>
+    </configurations>
+    <make>
+      <envvars>
+        <envvar value="1" name="WANT_AUTOCONF_2_5" />
+        <envvar value="1" name="WANT_AUTOMAKE_1_6" />
+      </envvars>
+      <abortonerror>false</abortonerror>
+      <numberofjobs>1</numberofjobs>
+      <dontact>false</dontact>
+      <makebin/>
+    </make>
+  </kdevautoproject>
+  <kdevdoctreeview>
+    <ignoretocs>
+      <toc>ada</toc>
+      <toc>ada_bugs_gcc</toc>
+      <toc>bash</toc>
+      <toc>bash_bugs</toc>
+      <toc>clanlib</toc>
+      <toc>w3c-dom-level2-html</toc>
+      <toc>fortran_bugs_gcc</toc>
+      <toc>gnome1</toc>
+      <toc>gnustep</toc>
+      <toc>gtk</toc>
+      <toc>gtk_bugs</toc>
+      <toc>haskell</toc>
+      <toc>haskell_bugs_ghc</toc>
+      <toc>java_bugs_gcc</toc>
+      <toc>java_bugs_sun</toc>
+      <toc>kde2book</toc>
+      <toc>libstdc++</toc>
+      <toc>opengl</toc>
+      <toc>pascal_bugs_fp</toc>
+      <toc>php</toc>
+      <toc>php_bugs</toc>
+      <toc>perl</toc>
+      <toc>perl_bugs</toc>
+      <toc>python</toc>
+      <toc>python_bugs</toc>
+      <toc>qt-kdev3</toc>
+      <toc>ruby</toc>
+      <toc>ruby_bugs</toc>
+      <toc>sdl</toc>
+      <toc>stl</toc>
+      <toc>w3c-svg</toc>
+      <toc>sw</toc>
+      <toc>w3c-uaag10</toc>
+      <toc>wxwidgets_bugs</toc>
+    </ignoretocs>
+    <ignoreqt_xml>
+      <toc>Guide to the Qt Translation Tools</toc>
+      <toc>Qt Assistant Manual</toc>
+      <toc>Qt Designer Manual</toc>
+      <toc>Qt Reference Documentation</toc>
+      <toc>qmake User Guide</toc>
+    </ignoreqt_xml>
+    <ignoredoxygen>
+      <toc>KDE Libraries (Doxygen)</toc>
+    </ignoredoxygen>
+    <projectdoc>
+      <userdocDir>html/</userdocDir>
+      <apidocDir>html/</apidocDir>
+    </projectdoc>
+    <ignorekdocs/>
+    <ignoredevhelp/>
+  </kdevdoctreeview>
+  <kdevfilecreate>
+    <filetypes/>
+    <useglobaltypes>
+      <type ext="c" />
+      <type ext="h" />
+    </useglobaltypes>
+  </kdevfilecreate>
+  <kdevfileview>
+    <groups>
+      <group pattern="*.h" name="Header files" />
+      <group pattern="*.c" name="Source files" />
+      <hidenonprojectfiles>false</hidenonprojectfiles>
+      <hidenonlocation>false</hidenonlocation>
+    </groups>
+    <tree>
+      <hidepatterns>*.o,*.lo,CVS</hidepatterns>
+      <hidenonprojectfiles>false</hidenonprojectfiles>
+      <showvcsfields>false</showvcsfields>
+    </tree>
+  </kdevfileview>
+  <kdevcppsupport>
+    <references/>
+    <codecompletion>
+      <includeGlobalFunctions>true</includeGlobalFunctions>
+      <includeTypes>true</includeTypes>
+      <includeEnums>true</includeEnums>
+      <includeTypedefs>false</includeTypedefs>
+      <automaticCodeCompletion>true</automaticCodeCompletion>
+      <automaticArgumentsHint>true</automaticArgumentsHint>
+      <automaticHeaderCompletion>true</automaticHeaderCompletion>
+      <codeCompletionDelay>250</codeCompletionDelay>
+      <argumentsHintDelay>400</argumentsHintDelay>
+      <headerCompletionDelay>250</headerCompletionDelay>
+    </codecompletion>
+  </kdevcppsupport>
+  <cppsupportpart>
+    <filetemplates>
+      <interfacesuffix>.h</interfacesuffix>
+      <implementationsuffix>.cpp</implementationsuffix>
+    </filetemplates>
+  </cppsupportpart>
+  <kdevdebugger>
+    <general>
+      <programargs/>
+      <gdbpath/>
+      <dbgshell/>
+      <configGdbScript/>
+      <runShellScript/>
+      <runGdbScript/>
+      <breakonloadinglibs>true</breakonloadinglibs>
+      <separatetty>false</separatetty>
+      <floatingtoolbar>false</floatingtoolbar>
+    </general>
+    <display>
+      <staticmembers>false</staticmembers>
+      <demanglenames>true</demanglenames>
+    </display>
+  </kdevdebugger>
+  <kdevcvsservice>
+    <recursivewhenupdate>true</recursivewhenupdate>
+    <prunedirswhenupdate>true</prunedirswhenupdate>
+    <createdirswhenupdate>true</createdirswhenupdate>
+    <recursivewhencommitremove>true</recursivewhencommitremove>
+    <revertoptions>-C</revertoptions>
+  </kdevcvsservice>
+</kdevelop>
Index: /tags/ipp-1-X/rel8_2/psModules/psmodule.kdevses
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/psmodule.kdevses	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/psmodule.kdevses	(revision 22115)
@@ -0,0 +1,56 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<!DOCTYPE KDevPrjSession>
+<KDevPrjSession>
+ <DocsAndViews NumberOfDocuments="11" >
+  <Doc0 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/psModule/src/astrom/Makefile.am" >
+   <View0 line="0" Type="Source" />
+  </Doc0>
+  <Doc1 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/psModule/configure.ac" >
+   <View0 line="41" Type="Source" />
+  </Doc1>
+  <Doc2 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/psModule/src/Makefile.am" >
+   <View0 line="3" Type="Source" />
+  </Doc2>
+  <Doc3 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/psModule/test/Makefile.am" >
+   <View0 line="13" Type="Source" />
+  </Doc3>
+  <Doc4 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/psModule/test/astrom/Makefile.am" >
+   <View0 line="2" Type="Source" />
+  </Doc4>
+  <Doc5 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/psModule/test/camera/Makefile.am" >
+   <View0 line="3" Type="Source" />
+  </Doc5>
+  <Doc6 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/psModule/test/config/Makefile.am" >
+   <View0 line="6" Type="Source" />
+  </Doc6>
+  <Doc7 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/psModule/test/detrend/Makefile.am" >
+   <View0 line="4" Type="Source" />
+  </Doc7>
+  <Doc8 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/psModule/test/objects/Makefile.am" >
+   <View0 line="18" Type="Source" />
+  </Doc8>
+  <Doc9 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/psModule/test/imcombine/Makefile.am" >
+   <View0 line="23" Type="Source" />
+  </Doc9>
+  <Doc10 NumberOfViews="1" URL="file:/home/drobbin/panstarrs/psModule/test/imsubtract/Makefile.am" >
+   <View0 line="12" Type="Source" />
+  </Doc10>
+ </DocsAndViews>
+ <pluginList>
+  <kdevbookmarks>
+   <bookmarks/>
+  </kdevbookmarks>
+  <kdevsubversion>
+   <subversion recurseresolve="1" recurserelocate="1" recursemerge="1" recursecommit="1" base="" recursepropget="1" recurseswitch="1" recurseupdate="1" recursepropset="1" recursediff="1" recurserevert="1" forcemove="1" recursecheckout="1" forceremove="1" recurseadd="1" recurseproplist="1" forcemerge="1" />
+  </kdevsubversion>
+  <kdevvalgrind>
+   <executable path="" params="" />
+   <valgrind path="" params="" />
+   <calltree path="" params="" />
+   <kcachegrind path="" />
+  </kdevvalgrind>
+  <kdevdebugger>
+   <breakpointList/>
+  </kdevdebugger>
+ </pluginList>
+</KDevPrjSession>
Index: /tags/ipp-1-X/rel8_2/psModules/psmodule.pc.in
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/psmodule.pc.in	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/psmodule.pc.in	(revision 22115)
@@ -0,0 +1,10 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: psmodule
+Description: Pan-STARRS Module Library
+Version: @VERSION@
+Libs: -L${libdir} -lpsmodule @PSMODULE_LIBS@
+Cflags: -I${includedir} @PSMODULE_CFLAGS@
Index: /tags/ipp-1-X/rel8_2/psModules/src/.cvsignore
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/.cvsignore	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/.cvsignore	(revision 22115)
@@ -0,0 +1,9 @@
+.deps
+.libs
+Makefile
+config.h
+stamp-h1
+Makefile.in
+config.h.in
+*.la
+*.lo
Index: /tags/ipp-1-X/rel8_2/psModules/src/Makefile.am
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/Makefile.am	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/Makefile.am	(revision 22115)
@@ -0,0 +1,8 @@
+SUBDIRS = $(SRCDIRS)
+lib_LTLIBRARIES = libpsmodule.la
+
+libpsmodule_la_SOURCES = dummy.c
+libpsmodule_la_CPPFLAGS = $(SRCINC)
+libpsmodule_la_LIBADD =  $(SRCSUBLIBS)
+
+EXTRA_DIST = psErrorCodes.dat
Index: /tags/ipp-1-X/rel8_2/psModules/src/astrom/.cvsignore
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/astrom/.cvsignore	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/astrom/.cvsignore	(revision 22115)
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
Index: /tags/ipp-1-X/rel8_2/psModules/src/astrom/Makefile.am
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/astrom/Makefile.am	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/astrom/Makefile.am	(revision 22115)
@@ -0,0 +1,9 @@
+noinst_LTLIBRARIES = libpsmoduleastrom.la
+
+libpsmoduleastrom_la_CPPFLAGS = $(SRCINC) $(PSMODULE_CFLAGS)
+libpsmoduleastrom_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+libpsmoduleastrom_la_SOURCES  = pmAstrometry.c
+
+psmoduleincludedir = $(includedir)
+psmoduleinclude_HEADERS = \
+  pmAstrometry.h
Index: /tags/ipp-1-X/rel8_2/psModules/src/astrom/pmAstrometry.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/astrom/pmAstrometry.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/astrom/pmAstrometry.c	(revision 22115)
@@ -0,0 +1,897 @@
+/** @file  psAstrometry.c
+*
+*  @brief This file defines the basic types for astronomical coordinate
+*  transformation
+*
+*  @ingroup AstroImage
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-09-28 20:43:52 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+/******************************************************************************/
+/*  INCLUDE FILES                                                             */
+/******************************************************************************/
+#include <string.h>
+#include <math.h>
+#include "pslib.h"
+#include "psDB.h"
+#include "pmAstrometry.h"
+
+/*****************************************************************************
+checkValidImageCoords(): this is a private function which simply determines if
+the supplied x,y coordinates are in the range for the supplied psImage.
+ 
+XXX: What about col0 and row0
+XXX: This should return a psBool.
+ *****************************************************************************/
+static psS32 checkValidImageCoords(double x,
+                                   double y,
+                                   psImage* tmpImage)
+{
+    PS_ASSERT_IMAGE_NON_NULL(tmpImage, 0);
+
+    if ((x < 0.0) || (x > (double)tmpImage->numCols) ||
+            (y < 0.0) || (y > (double)tmpImage->numRows)) {
+        return (0);
+    }
+
+    return (1);
+}
+
+/******************************************************************************
+XXX: Is this the correct way to free metadata?
+XXX: Is this the correct way to free database structs?
+ 
+ 
+ 
+XXX: The memory dereferencing is not quite right with these functions.  If I
+call the alloc functions with non-NULL pointers, there will be memory leaks.
+ *****************************************************************************/
+static void FPAFree(pmFPA *fpa)
+{
+    if (fpa != NULL) {
+        psFree(fpa->fromTangentPlane);
+        psFree(fpa->toTangentPlane);
+        psFree(fpa->projection);
+        psFree(fpa->concepts);
+        psFree(fpa->analysis);
+        psFree(fpa->camera);
+        //
+        // Set the parent to NULL in all fpa->chips before psFree(fpa->chips)
+        // in order to avoid memory reference counter problems.
+        //
+        for (psS32 i = 0 ; i < fpa->chips->n ; i++) {
+            pmChip *tmpChip = (pmChip *) fpa->chips->data[i];
+            tmpChip->parent = NULL;
+        }
+        psFree(fpa->chips);
+        psFree(fpa->header);
+        psFree(fpa->db);
+    }
+}
+
+static void chipFree(pmChip* chip)
+{
+    if (chip != NULL) {
+        psFree(chip->toFPA);
+        psFree(chip->fromFPA);
+        psFree(chip->concepts);
+        psFree(chip->analysis);
+        //
+        // Set the parent to NULL in all chip->cells before psFree(chip->cells)
+        // in order to avoid memory reference counter problems.
+        //
+        for (psS32 i = 0 ; i < chip->cells->n ; i++) {
+            pmCell *tmpCell = (pmCell *) chip->cells->data[i];
+            tmpCell->parent = NULL;
+        }
+        psFree(chip->cells);
+        psFree(chip->parent);
+        psFree(chip->extname);
+        psFree(chip->pixels);
+        psFree(chip->header);
+    }
+}
+
+static void cellFree(pmCell *cell)
+{
+    if (cell != NULL) {
+        psFree(cell->toChip);
+        psFree(cell->toFPA);
+        psFree(cell->toSky);
+        psFree(cell->concepts);
+        psFree(cell->analysis);
+        //
+        // Set the parent to NULL in all cell->readouts before psFree(cell->readouts)
+        // in order to avoid memory reference counter problems.
+        //
+        for (psS32 i = 0 ; i < cell->readouts->n ; i++) {
+            pmReadout *tmpReadout = (pmReadout *) cell->readouts->data[i];
+            tmpReadout->parent = NULL;
+        }
+        psFree(cell->readouts);
+        psFree(cell->parent);
+        psFree(cell->extname);
+        psFree(cell->pixels);
+        psFree(cell->header);
+    }
+}
+
+static void readoutFree(pmReadout *readout)
+{
+    if (readout != NULL) {
+        psFree(readout->image);
+        psFree(readout->mask);
+        psFree(readout->analysis);
+        psFree(readout->concepts);
+        psFree(readout->parent);
+    }
+}
+
+// XXX: Verify these default values for row0, col0, rowBins, colBins
+pmReadout *pmReadoutAlloc(pmCell *cell)
+{
+    pmReadout *tmpReadout = (pmReadout *) psAlloc(sizeof(pmReadout));
+
+    tmpReadout->col0 = -1;
+    tmpReadout->row0 = -1;
+    tmpReadout->colBins = -1;
+    tmpReadout->rowBins = -1;
+    tmpReadout->image = NULL;
+    tmpReadout->mask = NULL;
+    tmpReadout->analysis = psMetadataAlloc();
+    tmpReadout->concepts = psMetadataAlloc();
+    tmpReadout->parent = cell;
+    if (cell != NULL) {
+        cell->readouts = psArrayAdd(cell->readouts, 1, (psPtr) tmpReadout);
+    }
+    psMemSetDeallocator(tmpReadout, (psFreeFunc) readoutFree);
+    return(tmpReadout);
+}
+
+pmCell *pmCellAlloc(pmChip *chip)
+{
+    pmCell *tmpCell = (pmCell *) psAlloc(sizeof(pmCell));
+
+    tmpCell->col0 = -1;
+    tmpCell->row0 = -1;
+    tmpCell->toChip = NULL;
+    tmpCell->toFPA = NULL;
+    tmpCell->toSky = NULL;
+    tmpCell->analysis = psMetadataAlloc();
+    tmpCell->concepts = psMetadataAlloc();
+    tmpCell->readouts = psArrayAlloc(0);
+    tmpCell->parent = chip;
+    if (chip != NULL) {
+        chip->cells = psArrayAdd(chip->cells, 1, (psPtr) tmpCell);
+    }
+    tmpCell->valid = false;
+    tmpCell->extname = NULL;
+    tmpCell->pixels = NULL;
+    tmpCell->header = NULL;
+
+    psMemSetDeallocator(tmpCell, (psFreeFunc) cellFree);
+    return(tmpCell);
+}
+
+pmChip *pmChipAlloc(pmFPA *fpa)
+{
+    pmChip *tmpChip = (pmChip *) psAlloc(sizeof(pmChip));
+
+    tmpChip->col0 = -1;
+    tmpChip->row0 = -1;
+    tmpChip->toFPA = NULL;
+    tmpChip->fromFPA = NULL;
+    tmpChip->concepts = psMetadataAlloc();
+    tmpChip->analysis = psMetadataAlloc();
+    tmpChip->cells = psArrayAlloc(0);
+    tmpChip->parent = fpa;
+    if (fpa != NULL) {
+        fpa->chips = psArrayAdd(fpa->chips, 1, (psPtr) tmpChip);
+    }
+    tmpChip->valid = false;
+    tmpChip->extname = NULL;
+    tmpChip->pixels = NULL;
+    tmpChip->header = NULL;
+
+    psMemSetDeallocator(tmpChip, (psFreeFunc) chipFree);
+    return(tmpChip);
+}
+
+pmFPA *pmFPAAlloc(
+    const psMetadata *camera,
+    psDB *db)
+{
+    pmFPA *tmpFPA = (pmFPA *) psAlloc(sizeof(pmFPA));
+
+    tmpFPA->fromTangentPlane = NULL;
+    tmpFPA->toTangentPlane = NULL;
+    tmpFPA->projection = NULL;
+    tmpFPA->concepts = psMetadataAlloc();
+    tmpFPA->analysis = psMetadataAlloc();
+    tmpFPA->camera = camera;
+    psMemIncrRefCounter((psPtr) camera);
+    tmpFPA->chips = psArrayAlloc(0);
+    tmpFPA->header = NULL;
+    tmpFPA->db = db;
+    psMemIncrRefCounter((psPtr) db);
+
+    psMemSetDeallocator(tmpFPA, (psFreeFunc) FPAFree);
+    return(tmpFPA);
+}
+
+bool cellCheckParents(pmCell *cell)
+{
+    if (cell == NULL) {
+        return(true);
+    }
+    psBool flag = true;
+
+    for (psS32 i = 0 ; i < cell->readouts->n ; i++) {
+        pmReadout *tmpReadout = (pmReadout *) cell->readouts->data[i];
+        PS_ASSERT_PTR_NON_NULL(tmpReadout, false);
+        if (tmpReadout->parent != cell) {
+            tmpReadout->parent = cell;
+            flag = false;
+        }
+    }
+    return(flag);
+}
+
+bool chipCheckParents(pmChip *chip)
+{
+    if (chip == NULL) {
+        return(true);
+    }
+    psBool flag = true;
+
+    for (psS32 i = 0 ; i < chip->cells->n ; i++) {
+        pmCell *tmpCell = (pmCell *) chip->cells->data[i];
+        PS_ASSERT_PTR_NON_NULL(tmpCell, false);
+        if (tmpCell->parent != chip) {
+            tmpCell->parent = chip;
+            flag = false;
+        }
+
+        flag &= cellCheckParents(tmpCell);
+    }
+    return(flag);
+}
+
+bool pmFPACheckParents(pmFPA *fpa)
+{
+    if (fpa == NULL) {
+        return(true);
+    }
+    psBool flag = true;
+
+    for (psS32 i = 0 ; i < fpa->chips->n ; i++) {
+        pmChip *tmpChip = (pmChip *) fpa->chips->data[i];
+        PS_ASSERT_PTR_NON_NULL(tmpChip, false);
+        if (tmpChip->parent != fpa) {
+            tmpChip->parent = fpa;
+            flag = false;
+        }
+
+        flag &= chipCheckParents(tmpChip);
+    }
+    return(flag);
+}
+
+
+
+// HEY
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+
+pmCell* pmCellInFPA(const psPlane* fpaCoord,
+                    const pmFPA* FPA)
+{
+    PS_ASSERT_PTR_NON_NULL(fpaCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(FPA, NULL);
+
+    pmChip* tmpChip = NULL;
+    psPlane chipCoord;
+    pmCell* outCell = NULL;
+
+    // Determine which chip contains the fpaCoords.
+    tmpChip = pmChipInFPA(fpaCoord, FPA);
+    if (tmpChip == NULL) {
+        return(NULL);
+    }
+
+    // Convert to those chip coordinates.
+    psCoordFPAToChip(&chipCoord, fpaCoord, tmpChip);
+
+    // Determine which cell contains those chip coordinates.
+    outCell = pmCellInChip(&chipCoord, tmpChip);
+
+    return (outCell);
+}
+
+pmChip* pmChipInFPA(const psPlane* fpaCoord,
+                    const pmFPA* FPA)
+{
+    PS_ASSERT_PTR_NON_NULL(fpaCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(FPA, NULL);
+    PS_ASSERT_PTR_NON_NULL(FPA->chips, NULL);
+
+    psArray* chips = FPA->chips;
+    psS32 nChips = chips->n;
+    psPlane chipCoord;
+    pmCell *tmpCell = NULL;
+
+    // Loop through every chip in this FPA.  Convert the original FPA
+    // coordinates to chip coordinates for that chip.  Then, determine if any
+    // cells in that chip contain those chip coordinates.
+
+    for (psS32 i = 0; i < nChips; i++) {
+        pmChip* tmpChip = chips->data[i];
+        PS_ASSERT_PTR_NON_NULL(tmpChip, NULL);
+        PS_ASSERT_PTR_NON_NULL(tmpChip->fromFPA, NULL);
+
+        psPlaneTransformApply(&chipCoord, tmpChip->fromFPA, fpaCoord);
+
+        tmpCell = pmCellInChip(&chipCoord, tmpChip);
+        if (tmpCell != NULL) {
+            return(tmpChip);
+        }
+    }
+
+    // XXX: Print warning here?
+    return (NULL);
+}
+
+
+pmCell* pmCellInChip(const psPlane* chipCoord,
+                     const pmChip* chip)
+{
+    PS_ASSERT_PTR_NON_NULL(chipCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(chip, NULL);
+
+    psPlane cellCoord;
+    psArray* cells;
+
+    cells = chip->cells;
+    if (cells == NULL) {
+        return NULL;
+    }
+
+    // We loop over each cell in the chip.  We transform the chipCoord into
+    // a cellCoord for that cell and determine if that cellCoord is valid.
+    // If so, then we return that cell.
+
+    for (psS32 i = 0; i < cells->n; i++) {
+        pmCell* tmpCell = (pmCell* ) cells->data[i];
+        PS_ASSERT_PTR_NON_NULL(tmpCell, NULL);
+
+        psPlaneTransform *chipToCell = NULL;
+        if (1 != p_psIsProjectionLinear(tmpCell->toChip)) {
+            // XXX: Generate warning message.
+            // XXX: Can we use the following function to derive a transform?
+            // chipToCell = psPlaneTransformInvert(NULL, tmpCell->toChip, NULL, -1);
+        } else {
+            chipToCell = p_psPlaneTransformLinearInvert(tmpCell->toChip);
+        }
+
+        PS_ASSERT_PTR_NON_NULL(chipToCell, NULL);
+        psArray* readouts = tmpCell->readouts;
+
+        if (readouts != NULL) {
+            for (psS32 j = 0; j < readouts->n; j++) {
+                pmReadout* tmpReadout = readouts->data[j];
+                PS_ASSERT_READOUT_NON_NULL(tmpReadout, NULL);
+
+                psPlaneTransformApply(&cellCoord,
+                                      chipToCell,
+                                      chipCoord);
+
+                if (checkValidImageCoords(cellCoord.x,
+                                          cellCoord.y,
+                                          tmpReadout->image)) {
+                    return (tmpCell);
+                }
+            }
+        }
+        psFree(chipToCell);
+    }
+
+    return (NULL);
+}
+
+
+psPlane* psCoordCellToChip(psPlane* outCoord,
+                           const psPlane* inCoord,
+                           const pmCell* cell)
+{
+    PS_ASSERT_PTR_NON_NULL(inCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+
+    return (psPlaneTransformApply(outCoord, cell->toChip, inCoord));
+}
+
+
+psPlane* psCoordChipToFPA(psPlane* outCoord,
+                          const psPlane* inCoord,
+                          const pmChip* chip)
+{
+    PS_ASSERT_PTR_NON_NULL(inCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(chip, NULL);
+
+    return (psPlaneTransformApply(outCoord, chip->toFPA, inCoord));
+}
+
+
+
+psPlane* psCoordFPAToTP(psPlane* outCoord,
+                        const psPlane* inCoord,
+                        double color,
+                        double magnitude,
+                        const pmFPA* fpa)
+{
+    PS_ASSERT_PTR_NON_NULL(inCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+
+    return(psPlaneDistortApply(outCoord, fpa->toTangentPlane, inCoord,
+                               color, magnitude));
+}
+
+/*****************************************************************************
+XXX: What about units for the (x,y) coords?
+ *****************************************************************************/
+psSphere* psCoordTPToSky(psSphere* outSphere,
+                         const psPlane* tpCoord,
+                         const psProjection *projection
+                        )
+{
+    PS_ASSERT_PTR_NON_NULL(tpCoord, NULL);
+
+    if (outSphere == NULL) {
+        outSphere = (psSphere* ) psAlloc(sizeof(psSphere));
+    }
+
+    // XXX: this was done by a SLALIB call -- needs to be reimplemented
+    psWarning("Warning!  psCoordTPToSky functionality is no longer implemented");
+    // slaAopqk(tpCoord->x, tpCoord->y, (double*)grommit,
+    //         &AOB, &ZOB, &HOB, &outSphere->r, &outSphere->d);
+
+    return (outSphere);
+}
+
+
+
+psPlane* psCoordCellToFPA(psPlane* fpaCoord,
+                          const psPlane* cellCoord,
+                          const pmCell* cell)
+{
+    PS_ASSERT_PTR_NON_NULL(cellCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+
+    return (psPlaneTransformApply(fpaCoord, cell->toFPA, cellCoord));
+}
+
+
+
+psSphere* psCoordCellToSky(psSphere* skyCoord,
+                           const psPlane* cellCoord,
+                           double color,
+                           double magnitude,
+                           const pmCell* cell)
+{
+    PS_ASSERT_PTR_NON_NULL(cellCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->toFPA, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent->parent, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent->parent->toTangentPlane, NULL);
+    //    PS_ASSERT_PTR_NON_NULL(cell->parent->parent->exposure, NULL);
+
+    psPlane* fpaCoord = NULL;
+    psPlane* tpCoord = NULL;
+    pmFPA* parFPA = (cell->parent)->parent;
+    //    psGrommit* tmpGrommit = NULL;
+
+    // Convert the input cell coordinates to FPA coordinates.
+    fpaCoord = psPlaneTransformApply(fpaCoord, cell->toFPA, cellCoord);
+
+    // Convert the FPA coordinates to tangent plane Coordinates.
+    tpCoord = psPlaneDistortApply(tpCoord, parFPA->toTangentPlane,
+                                  fpaCoord, color, magnitude);
+
+    // Generate a grommit for this FPA.
+    //    tmpGrommit = psGrommitAlloc(parFPA->exposure);
+
+    // Convert the tangent plane Coordinates to sky coordinates.
+    //    skyCoord = psCoordTPToSky(skyCoord, tpCoord, tmpGrommit);
+
+    psFree(fpaCoord);
+    psFree(tpCoord);
+    //    psFree(tmpGrommit);
+
+    return(skyCoord);
+}
+
+
+psSphere* psCoordCellToSkyQuick(psSphere* outSphere,
+                                const psPlane* cellCoord,
+                                const pmCell* cell)
+{
+    PS_ASSERT_PTR_NON_NULL(cellCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->toSky, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent->parent, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent->parent->projection, NULL);
+
+    psLogMsg(__func__, PS_LOG_WARN,
+             "WARNING: psCoordCellToSkyQuick(): This function is not fully specified in the SDRS.  Returning NULL.\n");
+    return(NULL);
+
+    /*
+        if (cell->toSky) {
+            // XXX: Should we use toTP or toSky?
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: psCoordCellToSkyQuick(): The cell->toSky transform is ignored.  The cell->toTP transform is being used.");
+        }
+     
+        psPlane *tpCoord = NULL;
+        pmChip *chip = cell->parent;
+        pmFPA *FPA = chip->parent;
+        psProjectionType oldProjectionType;
+     
+        if (outSphere == NULL) {
+            outSphere = (psSphere* ) psAlloc(sizeof(psSphere));
+        }
+     
+        // Determine the tangent plane coordinates.
+        tpCoord = psPlaneTransformApply(NULL, cell->toTP, cellCoord);
+     
+        // Save the old projection type and set the new projection type to TAN.
+        oldProjectionType = FPA->projection->type;
+        FPA->projection->type = PS_PROJ_TAN;
+     
+        // Deproject the tangent plane coordinates a sphere.
+        outSphere = psDeproject(tpCoord, FPA->projection);
+     
+        // Restore old projection type.  Free memory.
+        FPA->projection->type = oldProjectionType;
+        psFree(tpCoord);
+     
+        return (outSphere);
+    */
+}
+
+
+/*****************************************************************************
+XXX: What about units for the (x,y) coords?
+ *****************************************************************************/
+psPlane* psCoordSkyToTP(psPlane* tpCoord,
+                        const psSphere* in,
+                        const psProjection *projection)
+{
+    PS_ASSERT_PTR_NON_NULL(in, NULL);
+    //    PS_ASSERT_PTR_NON_NULL(grommit, NULL);
+
+    // char* type = "RA";
+
+    if (tpCoord == NULL) {
+        tpCoord = (psPlane* ) psAlloc(sizeof(psPlane));
+    }
+
+    // XXX: this was done by a SLALIB call -- needs to be reimplemented
+    psWarning("Warning!  psCoordSkyToTP functionality is no longer implemented");
+    // slaOapqk(type, in->r, in->d, (double*)grommit, &tpCoord->x, &tpCoord->y);
+
+    return(tpCoord);
+}
+
+
+psPlane* psCoordTPToFPA(psPlane* fpaCoord,
+                        const psPlane* tpCoord,
+                        double color,
+                        double magnitude,
+                        const pmFPA* fpa)
+{
+    PS_ASSERT_PTR_NON_NULL(tpCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(fpa, NULL);
+    PS_ASSERT_PTR_NON_NULL(fpa->fromTangentPlane, NULL);
+
+    return (psPlaneDistortApply(fpaCoord, fpa->fromTangentPlane,
+                                tpCoord, color, magnitude));
+}
+
+
+psPlane* psCoordFPAToChip(psPlane* chipCoord,
+                          const psPlane* fpaCoord,
+                          const pmChip* chip)
+{
+    PS_ASSERT_PTR_NON_NULL(fpaCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(chip, NULL);
+    PS_ASSERT_PTR_NON_NULL(chip->fromFPA, NULL);
+
+    chipCoord = psPlaneTransformApply(chipCoord, chip->fromFPA, fpaCoord);
+    return(chipCoord);
+}
+
+
+psPlane* psCoordChipToCell(psPlane* cellCoord,
+                           const psPlane* chipCoord,
+                           const pmCell* cell)
+{
+    PS_ASSERT_PTR_NON_NULL(chipCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent, NULL);
+
+    pmCell *tmpCell = pmCellInChip(chipCoord, cell->parent);
+    PS_ASSERT_PTR_NON_NULL(tmpCell->toChip, NULL);
+    psPlaneTransform *tmpChipToCell = p_psPlaneTransformLinearInvert(tmpCell->toChip);
+    PS_ASSERT_PTR_NON_NULL(tmpChipToCell, NULL);
+    cellCoord = psPlaneTransformApply(cellCoord, tmpChipToCell, chipCoord);
+    psFree(tmpChipToCell);
+    return(cellCoord);
+}
+
+
+psPlane* psCoordSkyToCell(psPlane* cellCoord,
+                          const psSphere* skyCoord,
+                          float color,
+                          float magnitude,
+                          const pmCell* cell)
+{
+    PS_ASSERT_PTR_NON_NULL(skyCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent->parent, NULL);
+    //    PS_ASSERT_PTR_NON_NULL(cell->parent->parent->grommit, NULL);
+
+    pmChip *parChip = cell->parent;
+    pmFPA *parFPA = parChip->parent;
+
+    // Convert the skyCoords to tangent plane coords.
+    psPlane *tpCoord = psCoordSkyToTP(NULL, skyCoord, parFPA->projection);
+
+    // Convert the tangent plane coords to FPA coords.
+    psPlane *fpaCoord = psCoordTPToFPA(NULL, tpCoord, color, magnitude, parFPA);
+
+    // Convert the FPA coords to chip coords.
+    psPlane *chipCoord = psCoordFPAToChip(NULL, fpaCoord, parChip);
+
+    // Convert the chip coords to cell coords.
+    cellCoord = psCoordChipToCell(cellCoord, chipCoord, cell);
+
+    psFree(tpCoord);
+    psFree(fpaCoord);
+    psFree(chipCoord);
+
+    return (cellCoord);
+}
+
+
+psPlane* psCoordSkyToCellQuick(psPlane* cellCoord,
+                               const psSphere* skyCoord,
+                               const pmCell* cell)
+{
+    PS_ASSERT_PTR_NON_NULL(skyCoord, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent->parent, NULL);
+    PS_ASSERT_PTR_NON_NULL(cell->parent->parent->projection, NULL);
+
+
+    psLogMsg(__func__, PS_LOG_WARN,
+             "WARNING: psCoordSkyToCellQuick(): This function is not fully specified in the SDRS.  Returning NULL.\n");
+    return(NULL);
+    /*
+     
+        if (cell->toSky) {
+            // XXX: Should we use toTP or toSky?
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: psCoordSkyToCellQuick: The cell->toSky transform is ignored.  The cell->toTP transform is being used.");
+        }
+     
+        psPlane *tpCoord = NULL;
+        pmChip *whichChip = cell->parent;
+        pmFPA  *whichFPA = whichChip->parent;
+        psProjectionType oldProjectionType;
+        psPlaneTransform *TPtoCell = NULL;
+     
+        // Save the old projection type and set the new projection type to TAN.
+        oldProjectionType = whichFPA->projection->type;
+        whichFPA->projection->type = PS_PROJ_TAN;
+     
+        if (cellCoord == NULL) {
+            cellCoord = (psPlane* ) psAlloc(sizeof(psPlane));
+        }
+     
+        tpCoord = psProject(skyCoord, whichFPA->projection);
+     
+        // generate an error if cell->toTP is not linear.
+        if (0 == p_psIsProjectionLinear(cell->toTP)) {
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psAstrometry_NONLINEAR_TRANSFORM,
+                    "cell to tangent plane");
+        }
+     
+        TPtoCell = p_psPlaneTransformLinearInvert(cell->toTP);
+        cellCoord = psPlaneTransformApply(cellCoord, TPtoCell, tpCoord);
+     
+        // Restore old projection type.  Free memory.
+        whichFPA->projection->type = oldProjectionType;
+        psFree(tpCoord);
+        return (cellCoord);
+    */
+}
+
+/*
+psMetadataItem* psMetadataLookup(
+    const psMetadata * md,             ///< Metadata collection to lookup meta!
+    const char * key                   ///< Name of metadata key.
+);
+*/
+
+// XXX: How should we handle errors?  What if psMetadataLookup() is NULL?
+psMetadataItem *pmReadoutGetConcept(pmReadout *readout, const char *concept)
+{
+    return(psMetadataLookup(readout->concepts, concept));
+}
+
+psMetadataItem *pmCellGetConcept(pmCell *cell, const char *concept)
+{
+    return(psMetadataLookup(cell->concepts, concept));
+}
+
+psMetadataItem *pmChipGetConcept(pmChip *chip, const char *concept)
+{
+    return(psMetadataLookup(chip->concepts, concept));
+}
+
+psMetadataItem *pmFPAGetConcept(pmFPA *fpa, const char *concept)
+{
+    return(psMetadataLookup(fpa->concepts, concept));
+}
+
+
+float pmFPAGetAirmass(pmFPA *fpa) // FPA.AIRMASS
+{
+    psMetadataItem *tmp = pmFPAGetConcept(fpa, "FPA.AIRMASS");
+    return((float) tmp->data.F32);
+}
+
+psString pmFPAGetFilter(pmFPA *fpa) // FPA.FILTER
+{
+    psMetadataItem *tmp = pmFPAGetConcept(fpa, "FPA.FILTER");
+    return((psString) tmp->data.V);
+}
+
+float pmFPAGetPosAngle(pmFPA *fpa) // FPA.POSANGLE
+{
+    psMetadataItem *tmp = pmFPAGetConcept(fpa, "FPA.POSANGLE");
+    return((float) tmp->data.F32);
+}
+
+double pmFPAGetRA(pmFPA *fpa) // FPA.RA
+{
+    psMetadataItem *tmp = pmFPAGetConcept(fpa, "FPA.RA");
+    return((float) tmp->data.F32);
+}
+
+double pmFPAGetDec(pmFPA *fpa) // FPA.DEC
+{
+    psMetadataItem *tmp = pmFPAGetConcept(fpa, "FPA.DEC");
+    return((float) tmp->data.F32);
+}
+
+psString pmFPAGetRADecSys(pmFPA *fpa) // FPA.RADECSYS
+{
+    psMetadataItem *tmp = pmFPAGetConcept(fpa, "FPA.RADECSYS");
+    return((psString) tmp->data.V);
+}
+
+psString pmFPAGetName(pmFPA *fpa) // FPA.NAME
+{
+    psMetadataItem *tmp = pmFPAGetConcept(fpa, "FPA.NAME");
+    return((psString) tmp->data.V);
+}
+
+psString pmChipGetName(pmChip *chip) // CHIP.NAME
+{
+    psMetadataItem *tmp = pmChipGetConcept(chip, "CHIP.NAME");
+    return((psString) tmp->data.V);
+}
+
+psString pmCellGetName(pmCell *cell) // CELL.NAME
+{
+    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.NAME");
+    return((psString) tmp->data.V);
+}
+
+psTime *pmCellGetTime(pmCell *cell) // CELL.TIME
+{
+    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.TIME");
+    return((psTime *) tmp->data.V);
+}
+
+psList *pmCellGetBiasSec(pmCell *cell) // CELL.BIASSEC
+{
+    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.BIASSEC");
+    return((psList *) tmp->data.list);
+}
+
+psRegion pmCellGetTrimSec(pmCell *cell) // CELL.TRIMSEC
+{
+    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.TRIMSEC");
+    return((psRegion) *((psRegion *) (tmp->data.V)));
+}
+
+float pmCellGetGain(pmCell *cell) // CELL.GAIN
+{
+    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.GAIN");
+    return((float) tmp->data.F32);
+}
+
+float pmCellGetReadNoise(pmCell *cell) // CELL.READNOISE
+{
+    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.READNOISE");
+    return((float) tmp->data.F32);
+}
+
+float pmCellGetSaturation(pmCell *cell) // CELL.SATURATION
+{
+    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.SATURATION");
+    return((float) tmp->data.F32);
+}
+
+float pmCellGetBad(pmCell *cell) // CELL.BAD
+{
+    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.BAD");
+    return((float) tmp->data.F32);
+}
+
+
+psPixelCoord pmCellGetBin(pmCell *cell) // CELL.BIN
+{
+    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.BIN");
+    return((psPixelCoord)  *((psPixelCoord *) (tmp->data.V)));
+}
+
+psPixelCoord pmCellGetParity(pmCell *cell) // CELL.PARITY
+{
+    psMetadataItem *tmp = pmCellGetConcept(cell, "CELL.PARITY");
+    return((psPixelCoord)  *((psPixelCoord *) (tmp->data.V)));
+}
+
+float pmReadoutGetExposure(pmReadout *readout) // READOUT.EXPOSURE
+{
+    psMetadataItem *tmp = pmReadoutGetConcept(readout, "READOUT.EXPOSURE");
+    return((float) tmp->data.F32);
+}
+
+float pmReadoutGetDarkTime(pmReadout *readout) // READOUT.DARKTIME
+{
+    psMetadataItem *tmp = pmReadoutGetConcept(readout, "READOUT.DARKTIME");
+    return((float) tmp->data.F32);
+}
+
+
+
+
+/*
+typedef struct
+{
+    float x0;
+}
+psJunk;
+ 
+psJunk *pmCellTmp(pmCell *cell) // CELL.TRIMSEC
+{
+    psMetadataItem *tmp;
+    return((psJunk *) tmp->data.V);
+}
+ 
+*/
Index: /tags/ipp-1-X/rel8_2/psModules/src/astrom/pmAstrometry.h
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/astrom/pmAstrometry.h	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/astrom/pmAstrometry.h	(revision 22115)
@@ -0,0 +1,455 @@
+/** @file  pmAstrometry.h
+*
+*  @brief This file defines the basic types for astronomical coordinate
+*  transformation
+*
+*  @ingroup AstroImage
+*
+*  @author GLG, MHPCC
+*
+*  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+*  @date $Date: 2005-09-28 20:43:52 $
+*
+*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+*/
+
+#ifndef PS_ASTROMETRY_H
+#define PS_ASTROMETRY_H
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "pslib.h"
+#include "psDB.h"
+
+/// @addtogroup AstroImage
+/// @{
+
+/** Focal plane data structure
+ * 
+ *  A focal plane consists of one or more chips (according to the number of
+ *  pieces of contiguous silicon). It contains metadata containers for the
+ *  concepts and analysis, a link to the parent, and pointers to the FITS header,
+ *  if that corresponds to this level (the FPA may be the PHU, but will not ever
+ *  contain pixels). For astrometry, it contains a transformation from the focal
+ *  plane to the tangent plane and the fixed pattern residuals. It is expected
+ *  that the transformation will consist of two 4D polynomials (i.e. a function
+ *  of two coordinates in position, the magnitude of the object, and the color of
+ *  the object) in order to correct for optical distortions and the effects of
+ *  the atmosphere; hence we think that it is prudent to include a reverse
+ *  transformation which will be derived from numerically inverting the forward
+ *  transformation.
+ *  
+ */
+typedef struct
+{
+    // Astrometric transformations
+    psPlaneDistort* fromTangentPlane;   ///< Transformation from tangent plane to focal plane
+    psPlaneDistort* toTangentPlane;     ///< Transformation from focal plane to tangent plane
+    psProjection *projection;           ///< Projection from tangent plane to sky
+    // Information
+    psMetadata *concepts;               ///< Cache for PS concepts
+    psMetadata *analysis;               ///< FPA-level analysis metadata
+    const psMetadata *camera;           ///< Camera configuration
+    psArray *chips;                     ///< The chips
+    // FITS data
+    psMetadata *header;                 ///< The FITS header, if it corresponds to this level
+    psDB *db;                           ///< Database handle
+}
+pmFPA;
+
+/** Chip data structure
+ *  
+ *  A chip consists of one or more cells (according to the number of amplifiers
+ *  on the device). The chip contains metadata containers for the concepts and
+ *  analysis, a link to the parent, and pointers to the pointers to the various
+ *  FITS data, if that corresponds to this level. For astrometry, in addition to
+ *  the rough positioning information, it contains a coordinate transform from
+ *  the chip to the focal plane. It is expected that this transform will consist
+ *  of two second-order 2D polynomials; hence we think that it is prudent to
+ *  include a reverse transformation which will be derived from numerically
+ *  inverting the forward transformation. A boolean indicates whether the chip is
+ *  of interest, allowing it to be excluded from analysis.
+ *  
+ */
+typedef struct
+{
+    // Offset specifying position on focal plane
+    int col0;                           ///< Offset from the left of FPA.
+    int row0;                           ///< Offset from the bottom of FPA.
+    // Astrometric transformations
+    psPlaneTransform* toFPA;            ///< Transformation from chip to FPA coordinates
+    psPlaneTransform* fromFPA;          ///< Transformation from FPA to chip coordinates
+    // Information
+    psMetadata *concepts;               ///< Cache for PS concepts
+    psMetadata *analysis;               ///< Chip-level analysis metadata
+    psArray *cells;                     ///< The cells (referred to by name)
+    pmFPA *parent;                      ///< Parent FPA
+    bool valid;                         ///< Do we bother about reading and working with this chip?
+    // FITS data
+    const char *extname;                ///< Extension name, if it corresponds to this level
+    psArray *pixels;                    ///< The pixel data, if it corresponds to this level
+    psMetadata *header;                 ///< The FITS header, if it corresponds to this level
+}
+pmChip;
+
+/** Cell data structure
+ *
+ *  A cell consists of one or more readouts.  It also contains a pointer to the
+ *  cell's metadata, and its parent chip.  On the astrometry side, it also
+ *  contains coordinate transforms from the cell to chip, from the cell to
+ *  focal-plane, as well as a "quick and dirty" tranform from the cell to
+ *  sky coordinates.
+ *
+ */
+typedef struct
+{
+    // Offset specifying position on chip
+    int col0;                           ///< Offset from the left of chip.
+    int row0;                           ///< Offset from the bottom of chip.
+    // Astrometric transformations
+    psPlaneTransform* toChip;           ///< Transformations from cell to chip coordinates
+    psPlaneTransform* toFPA;            ///< Transformations from cell to FPA coordinates
+    psPlaneTransform* toSky;            ///< Transformations from cell to sky coordinates
+    // Information
+    psMetadata *concepts;               ///< Cache for PS concepts
+    psMetadata *analysis;               ///< Cell-level analysis metadata
+    psArray *readouts;                  ///< The readouts (referred to by number)
+    pmChip *parent;                     ///< Parent chip
+    bool valid;                         ///< Do we bother about reading and working with this cell?
+    // FITS data
+    const char *extname;                ///< Extension name, if it corresponds to this level
+    psArray *pixels;                    ///< The pixel data, if it corresponds to this level
+    psMetadata *header;                 ///< The FITS header, if it corresponds to this level
+}
+pmCell;
+
+/** Readout data structure.
+ *
+ *  A readout is the result of a single read of a cell (or a portion thereof).
+ *  It contains the offset from the lower-left corner of the chip, in the case
+ *  that the CCD was windowed, as well as the binning factors and parity (if the
+ *  binning value is negative, then the parity is reversed). It also contains the
+ *  pixel data, metadata containers for the concepts and analysis, and a link to
+ *  the parent.
+ *
+ */
+typedef struct
+{
+    // Position on the cell
+    int col0;                           ///< Offset from the left of chip.
+    int row0;                           ///< Offset from the bottom of chip.
+    int colBins;                        ///< Amount of binning in x-dimension
+    int rowBins;                        ///< Amount of binning in y-dimension
+    // Information
+    psImage *image;                     ///< Imaging area of readout
+    // XXX: The following mask was removed from the pmReadout struct in recent SDRS
+    // versions.  However, I'm keeping it here since al ot of modules still require
+    // it.
+    psImage *mask;                      ///< Mask of input image
+    psMetadata *analysis;               ///< Readout-level analysis metadata
+    psMetadata *concepts;               ///< Cache for PS Concepts
+    pmCell *parent;                     ///< Parent cell
+}
+pmReadout;
+
+
+/** Allocates a pmReadout
+ *
+ *  The constructor shall make an empty pmReadout. If the parent cell is not
+ *  NULL, the parent link is made and the readout shall be placed in the
+ *  parents array of readouts. The metadata containers shall be allocated. All
+ *  other pointers in the structure shall be initialized to NULL.
+ *
+ *  @return pmReadout*    newly allocated pmReadout with all internal pointers set to NULL
+ */
+pmReadout *pmReadoutAlloc(
+    pmCell *cell                        ///< Parent cell
+);
+
+/** Allocates a pmCell
+ *
+ *  The constructor shall make an empty pmCell. If the parent chip is not NULL,
+ *  the parent link is made and the cell shall be placed in the parents array of
+ *  cells. The readouts array shall be allocated with a zero size, and the
+ *  metadata containers constructed. All other pointers in the structure shall be
+ *  initialized to NULL.
+ *
+ *  @return pmCell*    newly allocated pmCell
+ */
+pmCell *pmCellAlloc(
+    pmChip *chip                        ///< Parent chip
+);
+
+/** Allocates a pmChip
+ * 
+ *  The constructor shall make an empty pmChip. If the parent fpa is not NULL,
+ *  the parent link is made and the chip shall be placed in the parent's array
+ *  of chips. The cells array shall be allocated with a zero size, and the
+ *  metadata containers constructed. All other pointers in the structure shall be
+ *  initialized to NULL.
+ *
+ *  @return pmChip*    newly allocated pmChip
+ */
+pmChip *pmChipAlloc(pmFPA *fpa);
+
+/** Allocates a pmFPA
+ * 
+ *  The constructor shall make an empty pmFPA. The chips array shall be
+ *  allocated with a zero size, the camera and db pointers set to the values
+ *  provided, and the concepts metadata constructed. All other pointers in the
+ *  structure shall be initialized to NULL.
+ * 
+ */
+pmFPA *pmFPAAlloc(
+    const psMetadata *camera,           ///< Camera configuration
+    psDB *db                            ///< Database handle
+);
+
+
+/** Verify parent links.
+ * 
+ *  This function checks the validity of the parent links in the FPA hierarchy.
+ *  If a parent link is not set (or not set correctly), it is corrected, and the
+ *  function shall return false. If all the parent pointers were correct, the
+ *  function shall return true.
+ * 
+ */
+bool pmFPACheckParents(
+    pmFPA *fpa
+);
+
+
+
+/** FUNC DESC
+ * 
+ * 
+ * 
+ * 
+ */
+
+
+
+/*****************************************************************************
+    Old Stuff
+ *****************************************************************************/
+
+
+
+/** Find cooresponding cell for given FPA coordinate
+ *
+ *  @return pmCell*    the cell cooresponding to the coord in FPA
+ */
+pmCell* pmCellInFPA(
+    const psPlane* coord,              ///< the coordinate in FPA plane
+    const pmFPA* FPA                   ///< the FPA to search for the cell
+);
+
+
+/** Find cooresponding chip for given FPA coordinate
+ *
+ *  @return pmChip*    the chip cooresponding to coord
+ */
+pmChip* pmChipInFPA(
+    const psPlane* coord,              ///< the coordinate in FPA plane
+    const pmFPA* FPA                   ///< the FPA to search for the cell
+);
+
+
+/** Find cooresponding cell for given Chip coordinate
+ *
+ *  @return pmCell*    the cell cooresponding to coord
+ */
+pmCell* pmCellInChip(
+    const psPlane* coord,              ///< the coordinate in Chip plane
+    const pmChip* chip                 ///< the chip to search for the cell
+);
+
+
+/** Translate a cell coordinate into a chip coordinate
+ *
+ *  @return psPlane*    the resulting chip coordinate
+ */
+psPlane* psCoordCellToChip(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within Cell
+    const pmCell* cell                 ///< the Cell in interest
+);
+
+
+/** Translate a chip coordinate into a FPA coordinate
+ *
+ *  @return psPlane*    the resulting FPA coordinate
+ */
+psPlane* psCoordChipToFPA(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within Chip
+    const pmChip* chip                 ///< the chip in interest
+);
+
+
+/** Translate a FPA coordinate into a Tangent Plane coordinate
+ *
+ *  @return psPlane*    the resulting Tangent Plane coordinate
+ */
+psPlane* psCoordFPAToTP(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within FPA
+    double color,                      ///< Color of source
+    double magnitude,                  ///< Magnitude of source
+    const pmFPA* fpa                   ///< the FPA in interest
+);
+
+
+/** Translate a Tangent Plane coordinate into a Sky coordinate
+ *
+ *  @return psSphere*    the resulting Sky coordinate
+ */
+psSphere* psCoordTPToSky(
+    psSphere* out,                     ///< a sphere struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                ///< the coordinate within Tangent Plane
+    const psProjection *projection
+);
+
+/** Translate a cell coordinate into a FPA coordinate
+ *
+ *  @return psPlane*    the resulting FPA coordinate
+ */
+psPlane* psCoordCellToFPA(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within cell
+    const pmCell* cell                 ///< the cell in interest
+);
+
+
+/** Translate a cell coordinate into a Sky coordinate
+ *
+ *  @return psSphere*    the resulting Sky coordinate
+ */
+psSphere* psCoordCellToSky(
+    psSphere* out,                     ///< a sphere struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within cell
+    double color,                      ///< Color of source
+    double magnitude,                  ///< Magnitude of source
+    const pmCell* cell                 ///< the cell in interest
+);
+
+
+/** Translate a cell coordinate into a Sky coordinate using a 'quick and
+ *  dirty' method
+ *
+ *  @return psSphere*    the resulting Sky coordinate
+ */
+psSphere* psCoordCellToSkyQuick(
+    psSphere* out,                     ///< a sphere struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within cell
+    const pmCell* cell                 ///< the cell in interest
+);
+
+
+/** Translate a Sky coordinate into a Tangent Plane coordinate
+ *
+ *  @return psPlane*    the resulting Tangent Plane coordinate
+ */
+psPlane* psCoordSkyToTP(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psSphere* in,                ///< the sky coordinate
+    const psProjection *projection
+);
+
+/** Translate a Tangent Plane coordinate into a FPA coordinate
+ *
+ *  @return psPlane*    the resulting FPA coordinate
+ */
+psPlane* psCoordTPToFPA(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the coordinate within tangent plane
+    double color,                      ///< Color of source
+    double magnitude,                  ///< Magnitude of source
+    const pmFPA* fpa                   ///< the FPA of interest
+);
+
+
+/** Translate a FPA coordinate into a chip coordinate
+ *
+ *  @return psPlane*    the resulting chip coordinate
+ */
+psPlane* psCoordFPAToChip(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the FPA coordinate
+    const pmChip* chip                 ///< the chip of interest
+);
+
+
+/** Translate a chip coordinate into a cell coordinate
+ *
+ *  @return psPlane*    the resulting cell coordinate
+ */
+psPlane* psCoordChipToCell(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psPlane* in,                 ///< the Chip coordinate
+    const pmCell* cell                 ///< the cell of interest
+);
+
+
+/** Translate a sky coordinate into a cell coordinate
+ *
+ *  @return psPlane*    the resulting cell coordinate
+ */
+psPlane* psCoordSkyToCell(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psSphere* in,                ///< the Sky coordinate
+    float color,                       ///< Color of source
+    float magnitude,                   ///< Magnitude of source
+    const pmCell* cell                 ///< the cell of interest
+);
+
+
+/** Translate a sky coordinate into a cell coordinate using a 'quick and
+ *  dirty' method
+ *
+ *  @return psPlane*    the resulting cell coordinate
+ */
+psPlane* psCoordSkyToCellQuick(
+    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
+    const psSphere* in,                ///< the Sky coordinate
+    const pmCell* cell                 ///< the cell of interest
+);
+
+
+psMetadataItem *pmCellGetConcept(pmCell *cell, const char *concept);
+psMetadataItem *pmChipGetConcept(pmChip *chip, const char *concept);
+psMetadataItem *pmFPAGetConcept(pmFPA *fpa, const char *concept);
+
+/**
+ * 
+ *  We next specify a series of specific functions for concept lookups. These
+ *  will generally be what the user utilises, so the goal is to provide a simple
+ *  interface providing a single type back, so the user doesnt have to go to the
+ *  trouble of checking types, etc. These functions should employ the above three
+ *  general lookup functions and deal with the result appropriately.
+ *
+ */
+float pmFPAGetAirmass(pmFPA *fpa); // FPA.AIRMASS
+psString pmFPAGetFilter(pmFPA *fpa); // FPA.FILTER
+float pmFPAGetPosAngle(pmFPA *fpa); // FPA.POSANGLE
+double pmFPAGetRA(pmFPA *fpa); // FPA.RA
+double pmFPAGetDec(pmFPA *fpa); // FPA.DEC
+psString pmFPAGetRADecSys(pmFPA *fpa); // FPA.RADECSYS
+psString pmFPAGetName(pmFPA *fpa); // FPA.NAME
+psString pmChipGetName(pmChip *chip); // CHIP.NAME
+psString pmCellGetName(pmCell *cell); // CELL.NAME
+psTime *pmCellGetTime(pmCell *cell); // CELL.TIME
+psList *pmCellGetBiasSec(pmCell *cell); // CELL.BIASSEC
+psRegion pmCellGetTrimSec(pmCell *cell); // CELL.TRIMSEC
+float pmCellGetGain(pmCell *cell); // CELL.GAIN
+float pmCellGetReadNoise(pmCell *cell); // CELL.READNOISE
+float pmCellGetSaturation(pmCell *cell); // CELL.SATURATION
+float pmCellGetBad(pmCell *cell); // CELL.BAD
+psPixelCoord pmCellGetBin(pmCell *cell); // CELL.BIN
+psPixelCoord pmCellGetParity(pmCell *cell); // CELL.PARITY
+float pmReadoutGetExposure(pmReadout *readout); // READOUT.EXPOSURE
+float pmReadoutGetDarkTime(pmReadout *readout); // READOUT.DARKTIME
+
+
+
+#endif // #ifndef PS_ASTROMETRY_H
Index: /tags/ipp-1-X/rel8_2/psModules/src/camera/.cvsignore
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/camera/.cvsignore	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/camera/.cvsignore	(revision 22115)
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
Index: /tags/ipp-1-X/rel8_2/psModules/src/camera/Makefile.am
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/camera/Makefile.am	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/camera/Makefile.am	(revision 22115)
@@ -0,0 +1,9 @@
+noinst_LTLIBRARIES = libpsmodulecamera.la
+
+libpsmodulecamera_la_CPPFLAGS = $(SRCINC) $(PSMODULE_CFLAGS)
+libpsmodulecamera_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+libpsmodulecamera_la_SOURCES  =
+
+psmoduleincludedir = $(includedir)
+psmoduleinclude_HEADERS =
+
Index: /tags/ipp-1-X/rel8_2/psModules/src/config/.cvsignore
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/config/.cvsignore	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/config/.cvsignore	(revision 22115)
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
Index: /tags/ipp-1-X/rel8_2/psModules/src/config/Makefile.am
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/config/Makefile.am	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/config/Makefile.am	(revision 22115)
@@ -0,0 +1,9 @@
+noinst_LTLIBRARIES = libpsmoduleconfig.la
+
+libpsmoduleconfig_la_CPPFLAGS = $(SRCINC) $(PSMODULE_CFLAGS)
+libpsmoduleconfig_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+libpsmoduleconfig_la_SOURCES  =
+
+psmoduleincludedir = $(includedir)
+psmoduleinclude_HEADERS =
+
Index: /tags/ipp-1-X/rel8_2/psModules/src/detrend/.cvsignore
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/detrend/.cvsignore	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/detrend/.cvsignore	(revision 22115)
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
Index: /tags/ipp-1-X/rel8_2/psModules/src/detrend/Makefile.am
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/detrend/Makefile.am	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/detrend/Makefile.am	(revision 22115)
@@ -0,0 +1,17 @@
+noinst_LTLIBRARIES = libpsmoduledetrend.la
+
+libpsmoduledetrend_la_CPPFLAGS = $(SRCINC) $(PSMODULE_CFLAGS)
+libpsmoduledetrend_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+libpsmoduledetrend_la_SOURCES  = pmFlatField.c \
+    pmMaskBadPixels.c \
+    pmNonLinear.c
+
+psmoduleincludedir = $(includedir)
+psmoduleinclude_HEADERS = \
+  pmFlatField.h \
+  pmFlatFieldErrors.h \
+  pmMaskBadPixelsErrors.h \
+  pmMaskBadPixels.h \
+  pmNonLinear.h
+
+EXTRA_DIST = pmFlatFieldErrors.dat pmMaskBadPixelsErrors.dat
Index: /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmFlatField.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmFlatField.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmFlatField.c	(revision 22115)
@@ -0,0 +1,182 @@
+/** @file  pmFlatField.c
+ *
+ *  @brief Given an input image and a flat field image, pmFlatField shall divide the input image by the flat
+ *  field image.
+ *
+ *  The input image, in, and the flat field image, flat, need not be the same size, since the input image may
+ *  already have been trimmed (following overscan subtraction), but the function shall use the offsets in the
+ *  image (in->x0 and in->y0) to determine the appropriate offsets to obtain the correct pixel on the flat
+ *  field. In the event that the flat image is too small (i.e., pixels on the input image refer to pixels
+ *  outside the range of the flat image), the function shall generate an error. Pixels which are negative or
+ *  zero in the flat shall be masked in the input image with the value PM_MASK_FLAT. Negative pixels in the
+ *  flat may be set to zero so that they are treated identically to zeroes. Any pixels masked in the flat
+ *  shall be masked with corresponding values in the output. The function shall not normalize the flat; this
+ *  responsibility is left to the caller. This function is basically equivalent to a divide (with psImageOp),
+ *  but with care for the region that is divided, checking for negative pixels, and copying of the mask from
+ *  the flat to the output.
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include<stdio.h>
+#include<math.h>
+#include <string.h>
+
+#include "pslib.h"
+#include "pmFlatField.h"
+#include "pmMaskBadPixels.h"
+#include "pmFlatFieldErrors.h"
+
+
+bool pmFlatField(pmReadout *in, pmReadout *mask, const pmReadout *flat)
+{
+    // XXX: Not sure if this is correct.  Must consult with IfA.
+    PS_ASSERT_PTR_NON_NULL(mask, false);
+    int i = 0;
+    int j = 0;
+    int totOffCol = 0;
+    int totOffRow = 0;
+    psElemType inType;
+    psElemType flatType;
+    psElemType maskType;
+    psImage *inImage = NULL;
+    psImage *inMask = NULL;
+    psImage *flatImage = NULL;
+
+
+    // Check for nulls
+    if (in == NULL) {
+        return true;       // Readout may not have data in it
+    } else if(flat==NULL) {
+        psError( PS_ERR_BAD_PARAMETER_NULL, true,
+                 PS_ERRORTEXT_pmFlatField_NULL_FLAT_READOUT);
+        return false;
+    }
+
+    inImage = in->image;
+    flatImage = flat->image;
+    if (inImage == NULL) {
+        psError( PS_ERR_BAD_PARAMETER_NULL, true,
+                 PS_ERRORTEXT_pmFlatField_NULL_INPUT_IMAGE);
+        return false;
+    } else if(flatImage == NULL) {
+        psError( PS_ERR_BAD_PARAMETER_NULL, true,
+                 PS_ERRORTEXT_pmFlatField_NULL_FLAT_IMAGE);
+        return false;
+    }
+    inMask = mask->image;
+
+    // Check input image and its mask are not larger than flat image
+
+    if (inImage->numRows>flatImage->numRows || inImage->numCols>flatImage->numCols) {
+        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
+                 PS_ERRORTEXT_pmFlatField_SIZE_INPUT_IMAGE,
+                 inImage->numRows, inImage->numCols, flatImage->numRows, flatImage->numCols);
+        return false;
+    }
+    if (inMask->numRows > flatImage->numRows || inMask->numCols > flatImage->numCols) {
+        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
+                 PS_ERRORTEXT_pmFlatField_SIZE_MASK_IMAGE,
+                 inMask->numRows, inMask->numCols, flatImage->numRows, flatImage->numCols);
+        return false;
+    }
+
+    // Determine total offset based on image offset with chip offset
+    totOffCol = inImage->col0 + in->col0;
+    totOffRow = inImage->row0 + in->row0;
+
+    // Check that offsets are within image limits
+    if(totOffRow>=flatImage->numRows || totOffCol>=flatImage->numCols) {
+        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
+                 PS_ERRORTEXT_pmFlatField_OFFSET_FLAT_IMAGE,
+                 totOffRow, totOffCol, flatImage->numRows, flatImage->numCols);
+        return false;
+    } else if(totOffRow>=inImage->numRows || totOffCol>=inImage->numCols) {
+        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
+                 PS_ERRORTEXT_pmFlatField_OFFSET_INPUT_IMAGE,
+                 totOffRow, totOffCol, inImage->numRows, inImage->numCols);
+        return false;
+    } else if(totOffRow>=inMask->numRows || totOffCol>=inMask->numCols) {
+        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
+                 PS_ERRORTEXT_pmFlatField_OFFSET_MASK_IMAGE,
+                 totOffRow, totOffCol, inMask->numRows, inMask->numCols);
+        return false;
+    }
+
+    // Check for incorrect types
+    inType = inImage->type.type;
+    flatType = flatImage->type.type;
+    maskType = inMask->type.type;
+    if(PS_IS_PSELEMTYPE_COMPLEX(inType)) {
+        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
+                 PS_ERRORTEXT_pmFlatField_TYPE_INPUT_IMAGE,
+                 inType);
+        return false;
+    } else if(PS_IS_PSELEMTYPE_COMPLEX(flatType)) {
+        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
+                 PS_ERRORTEXT_pmFlatField_TYPE_FLAT_IMAGE,
+                 flatType);
+        return false;
+    } else if(maskType != PS_TYPE_MASK) {
+        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
+                 PS_ERRORTEXT_pmFlatField_TYPE_MASK_IMAGE,
+                 maskType);
+        return false;
+    } else if(inType != flatType) {
+        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
+                 PS_ERRORTEXT_pmFlatField_TYPE_MISMATCH,
+                 inType, flatType);
+        return false;
+    }
+
+    // Macro for all PS types
+    #define PM_FLAT_DIVISION(TYPE)                                                                           \
+case PS_TYPE_##TYPE:                                                                                         \
+    /* Per Eugene's request, use two sets of loops: first to fill mask, second to avoid div with bad pix */  \
+    for(j = totOffRow; j < inImage->numRows; j++) {                                                          \
+        for(i = totOffCol; i < inImage->numCols; i++) {                                                      \
+            if(flatImage->data.TYPE[j][i] <= 0.0) {                                                          \
+                /* Negative or zero flat pixels shall be masked in input image as  PM_MASK_FLAT */           \
+                inMask->data.PS_TYPE_MASK_DATA[j][i] |= PM_MASK_FLAT;                                        \
+                flatImage->data.TYPE[j][i] = 0.0;                                                            \
+            }                                                                                                \
+        }                                                                                                    \
+    }                                                                                                        \
+    for(j = totOffRow; j < inImage->numRows; j++) {                                                          \
+        for(i = totOffCol; i < inImage->numCols; i++) {                                                      \
+            if(!inMask->data.PS_TYPE_MASK_DATA[j][i]) {                                                      \
+                /* Module shall divide the input image by the flat-fielded image */                          \
+                inImage->data.TYPE[j][i] /= flatImage->data.TYPE[j][i];                                      \
+            }                                                                                                \
+        }                                                                                                    \
+    }                                                                                                        \
+    break;
+
+    switch(inType) {
+        PM_FLAT_DIVISION(U8);
+        PM_FLAT_DIVISION(U16);
+        PM_FLAT_DIVISION(U32);
+        PM_FLAT_DIVISION(U64);
+        PM_FLAT_DIVISION(S8);
+        PM_FLAT_DIVISION(S16);
+        PM_FLAT_DIVISION(S32);
+        PM_FLAT_DIVISION(S64);
+        PM_FLAT_DIVISION(F32);
+        PM_FLAT_DIVISION(F64);
+    default:
+        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
+                 PS_ERRORTEXT_pmFlatField_TYPE_UNSUPPORTED,
+                 inType);
+    }
+
+    return true;
+}
Index: /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmFlatField.h
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmFlatField.h	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmFlatField.h	(revision 22115)
@@ -0,0 +1,42 @@
+/** @file  pmFlatField.h
+ *
+ *  @brief Given an input image and a flat field image, pmFlatField shall divide the input image by the flat
+ *  field image.
+ *
+ *  The input image, in, and the flat field image, flat, need not be the same size, since the input image may
+ *  already have been trimmed (following overscan subtraction), but the function shall use the offsets in the
+ *  image (in->x0 and in->y0) to determine the appropriate offsets to obtain the correct pixel on the flat
+ *  field. In the event that the flat image is too small (i.e., pixels on the input image refer to pixels
+ *  outside the range of the flat image), the function shall generate an error. Pixels which are negative or
+ *  zero in the flat shall be masked in the input image with the value PM_MASK_FLAT. Negative pixels in the
+ *  flat may be set to zero so that they are treated identically to zeroes. Any pixels masked in the flat
+ *  shall be masked with corresponding values in the output. The function shall not normalize the flat; this
+ *  responsibility is left to the caller. This function is basically equivalent to a divide (with psImageOp),
+ *  but with care for the region that is divided, checking for negative pixels, and copying of the mask from
+ *  the flat to the output.
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include "pslib.h"
+#include "pmAstrometry.h"
+
+
+/** Execute flat field module.
+ *
+ *  Given an input image and a flat-field image, pmFlatField shall divide the input image by the flat field
+ *  image.
+ *
+ *  @return  bool: True or false for success or failure
+ */
+bool pmFlatField(
+    pmReadout *in,          ///< Readout with input image
+    pmReadout *mask,        ///< Input image mask
+    const pmReadout *flat   ///< Readout with flat image
+);
+
Index: /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmFlatFieldErrors.dat
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmFlatFieldErrors.dat	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmFlatFieldErrors.dat	(revision 22115)
@@ -0,0 +1,22 @@
+#
+#  This file is used to generate pmFlatFieldErrors.h content
+#
+#  Format is:
+#  ERRORNAME(one word)    ERRORTEXT
+#
+#  N.B. in code, the ERRORNAME appears as PS_ERRORTEXT_ERRORNAME
+####################################################################
+#
+pmFlatField_NULL_FLAT_READOUT          Null not allowed for flat readout.
+pmFlatField_NULL_INPUT_IMAGE           Null not allowed for input image.
+pmFlatField_NULL_FLAT_IMAGE            Null not allowed for flat image.
+pmFlatField_SIZE_INPUT_IMAGE           Input image size exceeds that of flat image: (%d, %d) vs (%d, %d)
+pmFlatField_SIZE_MASK_IMAGE            Input image mask size exceeds that of flat image: (%d, %d) vs (%d, %d)
+pmFlatField_OFFSET_FLAT_IMAGE          Total offset >= flat image size: (%d, %d) vs (%d, %d)
+pmFlatField_OFFSET_INPUT_IMAGE         Total offset >= input image: (%d, %d) vs (%d, %d)
+pmFlatField_OFFSET_MASK_IMAGE          Total offset >= input image mask: (%d, %d) vs (%d, %d)
+pmFlatField_TYPE_INPUT_IMAGE           Complex types not allowed for input image. Type: %d
+pmFlatField_TYPE_FLAT_IMAGE            Complex types not allowed for flat image. Type: %d
+pmFlatField_TYPE_MASK_IMAGE            Mask must be PS_TYPE_MASK type. Type: %d
+pmFlatField_TYPE_MISMATCH              Input and flat image types differ: (%d vs %d)
+pmFlatField_TYPE_UNSUPPORTED           Unsupported image datatype. Type: %d
Index: /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmFlatFieldErrors.h
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmFlatFieldErrors.h	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmFlatFieldErrors.h	(revision 22115)
@@ -0,0 +1,47 @@
+/** @file  pmFlatFieldErrors.h
+ *
+ *  @brief Contains the error text for the flat field module
+ *
+ *  @ingroup ErrorHandling
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PM_FLATFIELD_ERRORS_H
+#define PM_FLATFIELD_ERRORS_H
+
+/* N.B., lines between '//~Start' and '//~End' are automatic generated from
+ * the template following the '//~Start'.  The template is used to generate
+ * the other lines by, for each error text in psDataManipErrors.dat, the following
+ * substitutions are made:
+ *     $1  The error text macro name (first word in the psFlatFieldErrors.h lines)
+ *     $2  The error text (rest of the line in psFlatFieldErrors.h)
+ *     $n  The order of the source line in psFlatFieldErrors.h (comments excluded)
+ *
+ * DO NOT EDIT THE LINES BETWEEN //~Start and //~End!  ANY CHANGES WILL BE OVERWRITTEN.
+ */
+
+#define PS_ERRORNAME_DOMAIN "psModule.src."
+
+//~Start #define PS_ERRORTEXT_$1 "$2"
+#define PS_ERRORTEXT_pmFlatField_NULL_FLAT_READOUT "Null not allowed for flat readout."
+#define PS_ERRORTEXT_pmFlatField_NULL_INPUT_IMAGE "Null not allowed for input image."
+#define PS_ERRORTEXT_pmFlatField_NULL_FLAT_IMAGE "Null not allowed for flat image."
+#define PS_ERRORTEXT_pmFlatField_SIZE_INPUT_IMAGE "Input image size exceeds that of flat image: (%d, %d) vs (%d, %d)"
+#define PS_ERRORTEXT_pmFlatField_SIZE_MASK_IMAGE "Input image mask size exceeds that of flat image: (%d, %d) vs (%d, %d)"
+#define PS_ERRORTEXT_pmFlatField_OFFSET_FLAT_IMAGE "Total offset >= flat image size: (%d, %d) vs (%d, %d)"
+#define PS_ERRORTEXT_pmFlatField_OFFSET_INPUT_IMAGE "Total offset >= input image: (%d, %d) vs (%d, %d)"
+#define PS_ERRORTEXT_pmFlatField_OFFSET_MASK_IMAGE "Total offset >= input image mask: (%d, %d) vs (%d, %d)"
+#define PS_ERRORTEXT_pmFlatField_TYPE_INPUT_IMAGE "Complex types not allowed for input image. Type: %d"
+#define PS_ERRORTEXT_pmFlatField_TYPE_FLAT_IMAGE "Complex types not allowed for flat image. Type: %d"
+#define PS_ERRORTEXT_pmFlatField_TYPE_MASK_IMAGE "Mask must be PS_TYPE_MASK type. Type: %d"
+#define PS_ERRORTEXT_pmFlatField_TYPE_MISMATCH "Input and flat image types differ: (%d vs %d)"
+#define PS_ERRORTEXT_pmFlatField_TYPE_UNSUPPORTED "Unsupported image datatype. Type: %d"
+//~End
+
+#endif
Index: /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmMaskBadPixels.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmMaskBadPixels.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmMaskBadPixels.c	(revision 22115)
@@ -0,0 +1,188 @@
+/** @file  pmMaskBadPixels.c
+ *
+ *  @brief Given an input image, a bad pixel mask, a corresponding value in the bad pixel mask to mask, a
+ *  saturation level, and a growing radius, mask in the input image those pixels in the bad pixel mask that
+ *  match the value to mask.
+ *
+ *  Given an input image, in, a bad pixel mask, a corresponding value in the bad pixel mask to mask in the
+ * input image, maskVal, a saturation level, and a growing radius, pmMaskBadPixels shall mask in the input
+ * image those pixels in the bad pixel mask that match the value to mask. Note that the input image, in, is
+ * modified in-place. All pixels in the mask which satisfy the maskVal shall have their corresponding pixels
+ * masked in the input image, in. All pixels which satisfy the growVal shall have their corresponding
+ * pixels, along with all pixels within the grow radius masked. Pixels which have flux greater than sat shall
+ * also be masked, but not grown. Note that the input image, in, and the mask need not be the same size, since
+ * the input image may already have been trimmed (following overscan subtraction), but the function shall use
+ * the offsets in the image (in->x0 and in->y0) to determine the appropriate offsets to obtain the correct
+ * pixel on the mask. In the event that the mask image is too small (i.e., pixels on the input image refer to
+ * pixels outside the range of the mask image), the function shall generate an error.
+ 
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include<stdio.h>
+#include<math.h>
+#include<string.h>
+
+#include "pmMaskBadPixels.h"
+#include "pmMaskBadPixelsErrors.h"
+
+bool pmMaskBadPixels(
+    pmReadout *in,
+    const psImage *mask,
+    unsigned int maskVal,
+    float sat,
+    unsigned int growVal,
+    int grow)
+{
+    int i = 0;
+    int j = 0;
+    int jj = 0;
+    int ii = 0;
+    int rRound = 0;
+    int rowMin = 0;
+    int rowMax = 0;
+    int colMin = 0;
+    int colMax = 0;
+    int totOffCol = 0;
+    int totOffRow = 0;
+    float r = 0.0f;
+    psElemType inType;
+    psElemType maskType;
+    psImage *inImage = NULL;
+    psImage *inMask = NULL;
+
+
+    // Check for nulls
+    if (in == NULL) {
+        return true;   // Readout may not have data in it
+    } else if(mask==NULL) {
+        psError( PS_ERR_BAD_PARAMETER_NULL, true,
+                 PS_ERRORTEXT_pmMaskBadPixels_NULL_MASK_IMAGE);
+        return false;
+    }
+
+    inImage = in->image;
+    if (inImage == NULL) {
+        psError( PS_ERR_BAD_PARAMETER_NULL, true,
+                 PS_ERRORTEXT_pmMaskBadPixels_NULL_INPUT_IMAGE);
+        return false;
+    } else if(in->mask == NULL) {
+        in->mask = psImageAlloc(inImage->numCols, inImage->numRows, PS_TYPE_MASK);
+        memset(in->mask->data.V[0], 0, inImage->numCols*inImage->numRows*PSELEMTYPE_SIZEOF(PS_TYPE_MASK));
+    }
+    inMask = in->mask;
+
+    // Check input image and its mask are not larger than mask
+    if(inImage->numRows > mask->numRows || inImage->numCols > mask->numCols) {
+        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
+                 PS_ERRORTEXT_pmMaskBadPixels_SIZE_INPUT_IMAGE,
+                 inImage->numRows, inImage->numCols, mask->numRows, mask->numCols);
+        return false;
+    } else if(inMask->numRows>mask->numRows || inMask->numCols>mask->numCols) {
+        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
+                 PS_ERRORTEXT_pmMaskBadPixels_SIZE_MASK_IMAGE,
+                 inMask->numRows, inMask->numCols, mask->numRows, mask->numCols);
+        return false;
+    }
+
+    // Determine total offset based on image offset with chip offset
+    totOffCol = inImage->col0 + in->col0;
+    totOffRow = inImage->row0 + in->row0;
+
+    // Check that offsets are within image limits
+    if(totOffRow>=mask->numRows || totOffCol>=mask->numCols) {
+        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
+                 PS_ERRORTEXT_pmMaskBadPixels_OFFSET_MASK_IMAGE,
+                 totOffRow, totOffCol, mask->numRows, mask->numCols);
+        return false;
+    } else if(totOffRow>=inImage->numRows || totOffCol>=inImage->numCols) {
+        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
+                 PS_ERRORTEXT_pmMaskBadPixels_OFFSET_INPUT_IMAGE,
+                 totOffRow, totOffCol, inImage->numRows, inImage->numCols);
+        return false;
+    } else if(totOffRow>=inMask->numRows || totOffCol>=inMask->numCols) {
+        psError( PS_ERR_BAD_PARAMETER_SIZE, true,
+                 PS_ERRORTEXT_pmMaskBadPixels_OFFSET_INPUT_IMAGE_MASK,
+                 totOffRow, totOffCol, inMask->numRows, inMask->numCols);
+        return false;
+    }
+
+    // Check for incorrect types
+    inType = inImage->type.type;
+    maskType = mask->type.type;
+    if(PS_IS_PSELEMTYPE_COMPLEX(inType)) {
+        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
+                 PS_ERRORTEXT_pmMaskBadPixels_TYPE_INPUT_IMAGE,
+                 inType);
+        return false;
+    } else if(maskType!=PS_TYPE_MASK) {
+        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
+                 PS_ERRORTEXT_pmMaskBadPixels_TYPE_MASK_IMAGE,
+                 maskType);
+        return false;
+    }
+
+    // Macro for all PS types
+    #define PM_BAD_PIXELS(TYPE)                                                                              \
+case PS_TYPE_##TYPE:                                                                                         \
+    for(j=totOffRow; j<inImage->numRows; j++) {                                                              \
+        for(i=totOffCol; i<inImage->numCols; i++) {                                                          \
+            \
+            /* Pixels with flux greater than sat shall be masked */                                          \
+            if(inImage->data.TYPE[j][i] > sat) {                                                             \
+                inMask->data.PS_TYPE_MASK_DATA[j][i] |= PM_MASK_SAT;                                         \
+            }                                                                                                \
+            \
+            /* Pixels which satisfy maskVal shall be masked */                                               \
+            inMask->data.PS_TYPE_MASK_DATA[j][i] |= (mask->data.PS_TYPE_MASK_DATA[j][i]&maskVal);            \
+            \
+            /* Pixels which satisfy growVal and within the grow radius shall be masked */                    \
+            if(mask->data.PS_TYPE_MASK_DATA[j][i] & growVal) {                                               \
+                rowMin = MAX(j-grow, 0);                                                                     \
+                rowMax = MIN(j+grow+1, inImage->numRows);                                                    \
+                colMin = MAX(i-grow, 0);                                                                     \
+                colMax = MIN(i+grow+1, inImage->numCols);                                                    \
+                for(jj=rowMin; jj<rowMax; jj++) {                                                            \
+                    for(ii=colMin; ii<colMax; ii++) {                                                        \
+                        r = sqrtf((ii-i)*(ii-i)+(jj-j)*(jj-j));                                              \
+                        rRound = r + 0.5;                                                                    \
+                        if(rRound <= grow) {                                                                 \
+                            inMask->data.PS_TYPE_MASK_DATA[jj][ii] |=                                        \
+                                    (mask->data.PS_TYPE_MASK_DATA[j][i]&growVal);                            \
+                        }                                                                                    \
+                    }                                                                                        \
+                }                                                                                            \
+            }                                                                                                \
+        }                                                                                                    \
+    }                                                                                                        \
+    break;
+
+    // Switch to call bad pixel masking macro defined above
+    switch(inType) {
+        PM_BAD_PIXELS(U8);
+        PM_BAD_PIXELS(U16);
+        PM_BAD_PIXELS(U32);
+        PM_BAD_PIXELS(U64);
+        PM_BAD_PIXELS(S8);
+        PM_BAD_PIXELS(S16);
+        PM_BAD_PIXELS(S32);
+        PM_BAD_PIXELS(S64);
+        PM_BAD_PIXELS(F32);
+        PM_BAD_PIXELS(F64);
+    default:
+        psError( PS_ERR_BAD_PARAMETER_TYPE, true,
+                 PS_ERRORTEXT_pmMaskBadPixels_TYPE_UNSUPPORTED,
+                 inType);
+    }
+
+    return false;
+}
Index: /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmMaskBadPixels.h
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmMaskBadPixels.h	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmMaskBadPixels.h	(revision 22115)
@@ -0,0 +1,61 @@
+/** @file  pmMaskBadPixels.h
+ *
+ *  @brief Given an input image, a bad pixel mask, a corresponding value in the bad pixel mask to mask, a
+ *  saturation level, and a growing radius, mask in the input image those pixels in the bad pixel mask that
+ *  match the value to mask.
+ *
+ *  Given an input image, in, a bad pixel mask, a corresponding value in the bad pixel mask to mask in the
+ *  input image, maskVal, a saturation level, and a growing radius, pmMaskBadPixels shall mask in the input
+ *  image those pixels in the bad pixel mask that match the value to mask. Note that the input image, in, is
+ *  modified in-place. All pixels in the mask which satisfy the maskVal shall have their corresponding pixels
+ *  masked in the input image, in. All pixels which satisfy the growVal shall have their corresponding
+ *  pixels, along with all pixels within the grow radius masked. Pixels which have flux greater than sat shall
+ *  also be masked, but not grown. Note that the input image, in, and the mask need not be the same size,
+ *  since the input image may already have been trimmed (following overscan subtraction), but the function
+ *  shall use the offsets in the image (in->x0 and in->y0) to determine the appropriate offsets to obtain the
+ *  correct pixel on the mask. In the event that the mask image is too small (i.e., pixels on the input image
+ *  refer to pixels outside the range of the mask image), the function shall generate an error.
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include "pslib.h"
+#include "pmAstrometry.h"
+
+/** Mask values */
+typedef enum {
+    PM_MASK_TRAP    = 0x0001,   ///< The pixel is a charge trap.
+    PM_MASK_BADCOL  = 0x0002,   ///< The pixel is a bad column.
+    PM_MASK_SAT     = 0x0004,   ///< The pixel is saturated.
+    PM_MASK_FLAT    = 0x0008    ///< The pixel is non-positive in the flat-field.
+} pmMaskValue;
+
+/** Macro to find maximum of two numbers */
+#define MAX(A,B)((A)>=(B)?(A):(B))
+
+/** Macro to find minimum of two numbers */
+#define MIN(A,B)((A)<=(B)?(A):(B))
+
+
+/** Execute bad pixels module.
+ *
+ *  Given an input image, a bad pixel mask, a corresponding value in the bad pixel mask to mask, a
+ *  saturation level, and a growing radius, mask in the input image those pixels in the bad pixel mask that
+ *  match the value to mask.
+ *
+ *  @return  bool: True or false for success or failure
+ */
+bool pmMaskBadPixels(
+    pmReadout *in,          ///< Readout containing input image data.
+    const psImage *mask,    ///< Mask data to be added to readout mask data.
+    unsigned int maskVal,   ///< Mask value to determine what to add to input mask.
+    float sat,              ///< Saturation limit to mask bad pixels.
+    unsigned int growVal,   ///< Mask data to determine if a circurlar area should be masked.
+    int grow                ///< Radius of mask to apply around pixel.
+);
+
Index: /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmMaskBadPixelsErrors.dat
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmMaskBadPixelsErrors.dat	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmMaskBadPixelsErrors.dat	(revision 22115)
@@ -0,0 +1,20 @@
+#
+#  This file is used to generate pmMaskBadPixelsErrors.h content
+#
+#  Format is:
+#  ERRORNAME(one word)    ERRORTEXT
+#
+#  N.B. in code, the ERRORNAME appears as PS_ERRORTEXT_ERRORNAME
+####################################################################
+#
+pmMaskBadPixels_NULL_MASK_IMAGE            Null not allowed for mask image.
+pmMaskBadPixels_NULL_INPUT_IMAGE           Null not allowed for input image.
+pmMaskBadPixels_SIZE_INPUT_IMAGE           Input image size exceeds that of mask image: (%d, %d) vs (%d, %d)
+pmMaskBadPixels_SIZE_MASK_IMAGE            Input image mask size exceeds that of mask image: (%d, %d) vs (%d, %d)
+pmMaskBadPixels_OFFSET_MASK_IMAGE          Total offset >= mask image: (%d, %d) vs (%d, %d)
+pmMaskBadPixels_OFFSET_INPUT_IMAGE         Total offset >= input image: (%d, %d) vs (%d, %d)
+pmMaskBadPixels_OFFSET_INPUT_IMAGE_MASK    Total offset >= input image mask: (%d, %d) vs (%d, %d)
+pmMaskBadPixels_TYPE_INPUT_IMAGE           Complex types not allowed for input image. Type: %d
+pmMaskBadPixels_TYPE_MASK_IMAGE            Mask must be PS_TYPE_MASK type. Type: %d
+pmMaskBadPixels_TYPE_MISMATCH              Input and flat image types differ: (%d vs %d)
+pmMaskBadPixels_TYPE_UNSUPPORTED           Unsupported image datatype. Type: %d
Index: /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmMaskBadPixelsErrors.h
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmMaskBadPixelsErrors.h	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmMaskBadPixelsErrors.h	(revision 22115)
@@ -0,0 +1,45 @@
+/** @file  pmMaskBadPixelsErrors.h
+ *
+ *  @brief Contains the error text for the mask bad pixels module
+ *
+ *  @ingroup ErrorHandling
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PM_FLATFIELD_ERRORS_H
+#define PM_FLATFIELD_ERRORS_H
+
+/* N.B., lines between '//~Start' and '//~End' are automatic generated from
+ * the template following the '//~Start'.  The template is used to generate
+ * the other lines by, for each error text in pmMaskBadPixelsErrors.dat, the following
+ * substitutions are made:
+ *     $1  The error text macro name (first word in the pmMaskBadPixelsErrors.h lines)
+ *     $2  The error text (rest of the line in pmMaskBadPixelsErrors.h)
+ *     $n  The order of the source line in pmMaskBadPixelsErrors.h (comments excluded)
+ * 
+ * DO NOT EDIT THE LINES BETWEEN //~Start and //~End!  ANY CHANGES WILL BE OVERWRITTEN.
+ */
+
+#define PS_ERRORNAME_DOMAIN "psModule.src."
+
+//~Start #define PS_ERRORTEXT_$1 "$2"
+#define PS_ERRORTEXT_pmMaskBadPixels_NULL_MASK_IMAGE "Null not allowed for mask image."
+#define PS_ERRORTEXT_pmMaskBadPixels_NULL_INPUT_IMAGE "Null not allowed for input image."
+#define PS_ERRORTEXT_pmMaskBadPixels_SIZE_INPUT_IMAGE "Input image size exceeds that of mask image: (%d, %d) vs (%d, %d)"
+#define PS_ERRORTEXT_pmMaskBadPixels_SIZE_MASK_IMAGE "Input image mask size exceeds that of mask image: (%d, %d) vs (%d, %d)"
+#define PS_ERRORTEXT_pmMaskBadPixels_OFFSET_MASK_IMAGE "Total offset >= mask image: (%d, %d) vs (%d, %d)"
+#define PS_ERRORTEXT_pmMaskBadPixels_OFFSET_INPUT_IMAGE "Total offset >= input image: (%d, %d) vs (%d, %d)"
+#define PS_ERRORTEXT_pmMaskBadPixels_OFFSET_INPUT_IMAGE_MASK "Total offset >= input image mask: (%d, %d) vs (%d, %d)"
+#define PS_ERRORTEXT_pmMaskBadPixels_TYPE_INPUT_IMAGE "Complex types not allowed for input image. Type: %d"
+#define PS_ERRORTEXT_pmMaskBadPixels_TYPE_MASK_IMAGE "Mask must be PS_TYPE_MASK type. Type: %d"
+#define PS_ERRORTEXT_pmMaskBadPixels_TYPE_MISMATCH "Input and flat image types differ: (%d vs %d)"
+#define PS_ERRORTEXT_pmMaskBadPixels_TYPE_UNSUPPORTED "Unsupported image datatype. Type: %d"
+//~End
+
+#endif
Index: /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmNonLinear.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmNonLinear.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmNonLinear.c	(revision 22115)
@@ -0,0 +1,123 @@
+/** @file  pmNonLinear.c
+ *
+ *  Provides polynomial or table lookup non-linearity corrections to readouts.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ *  XXX: The SDR is silent about image types.  Only F32 was implemented.
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include<stdio.h>
+#include<math.h>
+
+#include "pmNonLinear.h"
+
+/******************************************************************************
+pmNonLinearityLookup(): This routine will take an pmReadout image as input
+and a 1-D polynomial.  For each pixel in the input image, the polynomial will
+be evaluated at that pixels value, and the image pixel will then be set to
+that value.
+ *****************************************************************************/
+
+pmReadout *pmNonLinearityPolynomial(pmReadout *inputReadout,
+                                    const psPolynomial1D *input1DPoly)
+{
+    PS_ASSERT_PTR_NON_NULL(inputReadout, NULL);
+    PS_ASSERT_PTR_NON_NULL(inputReadout->image, NULL);
+    PS_ASSERT_IMAGE_TYPE(inputReadout->image, PS_TYPE_F32, NULL);
+    PS_ASSERT_PTR_NON_NULL(input1DPoly, NULL);
+
+    psS32 i;
+    psS32 j;
+
+    for (i=0;i<inputReadout->image->numRows;i++) {
+        for (j=0;j<inputReadout->image->numCols;j++) {
+            inputReadout->image->data.F32[i][j] = psPolynomial1DEval(input1DPoly, inputReadout->image->data.F32[i][j]);
+        }
+    }
+    return(inputReadout);
+}
+
+
+/******************************************************************************
+pmNonLinearityLookup(): This routine will take an pmReadout image as input
+and two input vectors, which constitute a lookup table.  For each pixel in
+the input image, that pixels value will be determined in the input vector
+inFluxe, and the corresponding value in outFlux.  The image pixel will then
+be set to the value from outFlux.
+ *****************************************************************************/
+pmReadout *pmNonLinearityLookup(pmReadout *inputReadout,
+                                const psVector *inFlux,
+                                const psVector *outFlux)
+{
+    PS_ASSERT_PTR_NON_NULL(inputReadout,NULL);
+    PS_ASSERT_PTR_NON_NULL(inputReadout->image,NULL);
+    PS_ASSERT_IMAGE_TYPE(inputReadout->image, PS_TYPE_F32, NULL);
+    PS_ASSERT_PTR_NON_NULL(inFlux,NULL);
+    if (inFlux->n < 2) {
+        psError(PS_ERR_UNKNOWN,true, "pmNonLinearityLookup(): input vector less than 2 elements.  Returning inputReadout image.");
+        return(inputReadout);
+    }
+    PS_ASSERT_PTR_NON_NULL(outFlux,NULL);
+    psS32 tableSize = inFlux->n;
+    if (inFlux->n != outFlux->n) {
+        tableSize = PS_MIN(inFlux->n, outFlux->n);
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: pmNonLinear.c: pmNonLinearityLookup(): input vectors have different sizes (%d, %d)\n", inFlux->n, outFlux->n);
+    }
+    PS_ASSERT_VECTOR_TYPE(inFlux, PS_TYPE_F32, NULL);
+    PS_ASSERT_VECTOR_TYPE(outFlux, PS_TYPE_F32, NULL);
+
+    psS32 i;
+    psS32 j;
+    psS32 binNum;
+    psScalar x;
+    psS32 numPixels = 0;
+    psF32 slope;
+
+    x.type.type = PS_TYPE_F32;
+    for (i=0;i<inputReadout->image->numRows;i++) {
+        for (j=0;j<inputReadout->image->numCols;j++) {
+            x.data.F32 = inputReadout->image->data.F32[i][j];
+            binNum = p_psVectorBinDisect((psVector *)inFlux, &x);
+
+            if (binNum == -2) {
+                // We get here if x is below the table lookup range.
+                inputReadout->image->data.F32[i][j] = outFlux->data.F32[0];
+                numPixels++;
+
+            } else if (binNum == -1) {
+                // We get here if x is above the table lookup range.
+                inputReadout->image->data.F32[i][j] = outFlux->data.F32[tableSize-1];
+                numPixels++;
+
+            } else if (binNum < -2) {
+                // We get here if there was some other problem.
+                psError(PS_ERR_UNKNOWN,true, "pmNonLinearityLookup(): Could not perform p_psVectorBinDisect().  Returning inputReadout image.");
+                return(inputReadout);
+                numPixels++;
+            } else {
+                // Perform linear interpolation.
+                slope = (outFlux->data.F32[binNum+1] - outFlux->data.F32[binNum]) /
+                        (inFlux->data.F32[binNum+1]  - inFlux->data.F32[binNum]);
+                inputReadout->image->data.F32[i][j] = outFlux->data.F32[binNum] +
+                                                      ((x.data.F32 - inFlux->data.F32[binNum]) * slope);
+            }
+        }
+    }
+    if (numPixels > 0) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: pmNonLinear.c: pmNonLinearityLookup(): %d pixels outside table.", numPixels);
+    }
+    return(inputReadout);
+}
Index: /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmNonLinear.h
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmNonLinear.h	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/detrend/pmNonLinear.h	(revision 22115)
@@ -0,0 +1,27 @@
+/** @file  pmNonLinear.h
+ *
+ *  Provides polynomial or table lookup non-linearity corrections to readouts.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#if !defined(PM_NON_LINEAR_H)
+#define PM_NON_LINEAR_H
+
+#include "pslib.h"
+#include "pmAstrometry.h"
+
+pmReadout *pmNonLinearityPolynomial(pmReadout *in,
+                                    const psPolynomial1D *coeff);
+
+pmReadout *pmNonLinearityLookup(pmReadout *in,
+                                const psVector *inFlux,
+                                const psVector *outFlux);
+
+#endif
Index: /tags/ipp-1-X/rel8_2/psModules/src/imcombine/.cvsignore
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/imcombine/.cvsignore	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/imcombine/.cvsignore	(revision 22115)
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
Index: /tags/ipp-1-X/rel8_2/psModules/src/imcombine/Makefile.am
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/imcombine/Makefile.am	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/imcombine/Makefile.am	(revision 22115)
@@ -0,0 +1,11 @@
+noinst_LTLIBRARIES = libpsmoduleimcombine.la
+
+libpsmoduleimcombine_la_CPPFLAGS = $(SRCINC) $(PSMODULE_CFLAGS)
+libpsmoduleimcombine_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+libpsmoduleimcombine_la_SOURCES  = pmImageCombine.c \
+    pmReadoutCombine.c
+
+psmoduleincludedir = $(includedir)
+psmoduleinclude_HEADERS = \
+    pmImageCombine.h \
+    pmReadoutCombine.h
Index: /tags/ipp-1-X/rel8_2/psModules/src/imcombine/pmImageCombine.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/imcombine/pmImageCombine.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/imcombine/pmImageCombine.c	(revision 22115)
@@ -0,0 +1,666 @@
+/** @file  pmImageCombine.c
+ *
+ *  This file will perform image combination of several images of the
+ *  same field, produce a list of questionable pixels, then tag some
+ *  of those pixels as cosmic rays.
+ *
+ *  @author Paul Price, IfA (original prototype)
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  XXX: pmRejectPixels() has a known bug with the pmImageTransform() call.
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <math.h>
+#include "pslib.h"
+
+//
+// The following macros define how big the initial pixel list will be, and
+// how much it should be incremented when realloc'ed.
+//
+#define PS_COMBINE_IMAGE_INITIAL_PIXEL_LIST_LENGTH 100
+#define PS_COMBINE_IMAGE_INITIAL_PIXEL_LIST_LENGTH_INC 100
+#define PS_COMBINE_IMAGE_MAX_QUESTIONABLE_PIXELS 1000
+/******************************************************************************
+pmCombineImages(combine, questionablePixels, images, errors, masks, maskVal,
+                pixels, numIter, sigmaClip, stats)
+ 
+XXX: Allocate a dummy psStats structure so that we don't destroy away its data.
+ *****************************************************************************/
+psImage *pmCombineImages(psImage *combine,              ///< Combined image (output)
+                         psArray **questionablePixels,  ///< Array of rejection masks
+                         const psArray *images,         ///< Array of input images
+                         const psArray *errors,         ///< Array of input error images
+                         const psArray *masks,          ///< Array of input masks
+                         psU32 maskVal,                 ///< Mask value
+                         const psPixels *pixels,        ///< Pixels to combine
+                         psS32 numIter,                 ///< Number of rejection iterations
+                         psF32 sigmaClip,               ///< Number of standard deviations at which to reject
+                         const psStats *stats           ///< Statistics to use in the combination
+                        )
+{
+
+    PS_ASSERT_PTR_NON_NULL(images, combine);
+    psU32 numImages = images->n;
+    psTrace("ImageCombine.pmCombineImages", 3, "Calling pmCombineImages(%d)\n", images->n);
+
+    if (errors != NULL) {
+        if (images->n != errors->n) {
+            psError(PS_ERR_UNKNOWN, true, "images and errors args must have same length (%d != %d)\n",
+                    images->n, errors->n);
+            return(combine);
+        }
+    }
+    if (masks != NULL) {
+        if (images->n != masks->n) {
+            psError(PS_ERR_UNKNOWN, true, "images and masks args must have same length (%d != %d)\n",
+                    images->n, masks->n);
+            return(combine);
+        }
+    }
+
+    psImage *tmpImg = (psImage *) images->data[0];
+    psU32 numRows = tmpImg->numRows;
+    psU32 numCols = tmpImg->numCols;
+
+    //
+    // Check that all images have the appropriate size and type.
+    //
+    for (psS32 i = 0 ; i < numImages ; i++) {
+        psImage *tmpDataImg = (psImage *) images->data[i];
+        PS_ASSERT_IMAGE_NON_NULL(tmpDataImg, combine);
+        PS_ASSERT_IMAGE_TYPE(tmpDataImg, PS_TYPE_F32, combine);
+        if ((tmpDataImg->numRows != numRows) || (tmpDataImg->numCols != numCols)) {
+            psError(PS_ERR_UNKNOWN, true, "image %d has size (%d, %d); should be (%d, %d).\n",
+                    i, tmpDataImg->numRows, tmpDataImg->numCols, numRows, numCols);
+        }
+
+        if (errors != NULL) {
+            psImage *tmpErrorImg = (psImage *) errors->data[i];
+            PS_ASSERT_IMAGE_NON_NULL(tmpErrorImg, combine);
+            PS_ASSERT_IMAGE_TYPE(tmpErrorImg, PS_TYPE_F32, combine);
+            PS_ASSERT_IMAGES_SIZE_EQUAL(tmpDataImg, tmpErrorImg, NULL);
+        }
+
+        if (masks != NULL) {
+            psImage *tmpMaskImg = (psImage *) masks->data[i];
+            PS_ASSERT_IMAGE_NON_NULL(tmpMaskImg, combine);
+            PS_ASSERT_IMAGE_TYPE(tmpMaskImg, PS_TYPE_U8, combine);
+            PS_ASSERT_IMAGES_SIZE_EQUAL(tmpDataImg, tmpMaskImg, NULL);
+        }
+    }
+    PS_ASSERT_PTR_NON_NULL(stats, combine);
+
+    // Allocate and initialize the combined image, if necessary.
+    if (combine == NULL) {
+        combine = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+        if (pixels != NULL) {
+            PS_IMAGE_SET_F32(combine, 0.0);
+        }
+    }
+
+    //
+    // Allocate the questionablePixels psArray, if necesssary, then create a psPixels
+    // struct for each image.
+    //
+    if (*questionablePixels == NULL) {
+        *questionablePixels = psArrayAlloc(numImages);
+    } else if ((*questionablePixels)->n != numImages) {
+        *questionablePixels = psArrayRealloc(*questionablePixels, numImages);
+    }
+    for (psS32 im = 0 ; im < numImages ; im++) {
+        psFree((*questionablePixels)->data[im]);
+        ((*questionablePixels)->data[im]) = (psPtr *) psPixelsAlloc(PS_COMBINE_IMAGE_INITIAL_PIXEL_LIST_LENGTH);
+        ((psPixels *) ((*questionablePixels)->data[im]))->n = 0;
+    }
+    //
+    // qpPtr is used to maintain a count of the questionable pixels for each image.
+    //
+    psVector *qpPtr = psVectorAlloc(numImages, PS_TYPE_S32);
+    PS_VECTOR_SET_S32(qpPtr, 0);
+
+    //
+    // Allocate the necessary psVectors for the call to psVectorStats().
+    // These vectors will be used whether we are combining a list of pixels,
+    // or every pixel in the input images.
+    //
+    psVector *pixelData = psVectorAlloc(numImages, PS_TYPE_F32);
+
+    psVector *pixelMask = NULL;
+    if (masks != NULL) {
+        pixelMask = psVectorAlloc(numImages, PS_TYPE_U8);
+        PS_VECTOR_SET_U8(pixelMask, 0);
+    }
+
+    psVector *pixelErrors = NULL;
+    if (errors != NULL) {
+        pixelErrors = psVectorAlloc(numImages, PS_TYPE_F32);
+        PS_VECTOR_SET_F32(pixelErrors, 1.0);
+    }
+
+    if (pixels != NULL) {
+        // Only those specified pixels should be combined.
+
+        psStats *stdevStats = psStatsAlloc(PS_STAT_SAMPLE_STDEV);
+
+        for (psS32 p = 0 ; p < pixels->n ; p++) {
+            // Must initialize the mask to 0 for every pixel.
+            PS_VECTOR_SET_U8(pixelMask, 0);
+            psS32 col = (pixels->data[p]).x;
+            psS32 row = (pixels->data[p]).y;
+
+            //
+            // Loop through each image, extract the pixel/mask/error data
+            // into psVectors.
+            //
+            for (psS32 im = 0 ; im < numImages ; im++) {
+                // Set the pixel data
+                pixelData->data.F32[im] =       ((psImage *) images->data[im])->data.F32[row][col];
+                // Set the pixel mask data, if necessary
+                if (masks != NULL) {
+                    pixelMask->data.U8[im] =   ((psImage *) masks->data[im])->data.F32[row][col];
+                }
+
+                // Set the pixel error data, if necessary
+                if (errors != NULL) {
+                    pixelErrors->data.F32[im] = ((psImage *) errors->data[im])->data.F32[row][col];
+                }
+            }
+
+            //
+            // Iterate on the pixels, rejecting outliers
+            //
+            for (psS32 iter = 0 ; iter < numIter ; iter++) {
+                // Combine all the pixels, using the specified stat.
+
+                stats = psVectorStats((psStats *) stats, pixelData, pixelErrors, pixelMask, maskVal);
+                psF64 combinedPixel;
+                psBool rc = p_psGetStatValue(stats, &combinedPixel);
+                if (rc != true) {
+                    psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not combine pixels (%d, %d) with the specified stat.\n", row, col);
+                }
+                if (iter == 0) {
+                    combine->data.F32[row][col] = (psF32) combinedPixel;
+                }
+
+                //
+                // Reject all pixels that lie more that sigmaClip standard deviations from
+                // the combined pixel value.
+                //
+                psS32 numRejects = 0;
+                for (psS32 im = 0 ; im < numImages ; im++) {
+                    stdevStats = psVectorStats(stdevStats, pixelData, pixelErrors, pixelMask, maskVal);
+                    psF64 stdev;
+                    psBool rc = p_psGetStatValue(stdevStats, &stdev);
+                    if (rc != true) {
+                        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not compute the standard deviation of pixel (%d, %d).\n", row, col);
+                        psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not combine pixels (%d, %d) with the specified stat.\n", row, col);
+                    }
+
+                    if (!(pixelMask->data.U8[im] & maskVal)) {
+                        if (fabs(pixelData->data.F32[im]) >
+                                (fabs(sigmaClip * stdev) + fabs(combinedPixel))) {
+                            numRejects++;
+                            pixelMask->data.U8[im] = maskVal;
+                            //
+                            // XXX: These data structures indirections are getting complicated.
+                            //
+                            psS32 ptr = qpPtr->data.S32[im];
+                            psPixels *pixelListPtr = ((psPixels *) ((*questionablePixels)->data[im]));
+
+                            if (ptr >= pixelListPtr->nalloc) {
+                                (*questionablePixels)->data[im] =
+                                    (psPtr *) psPixelsRealloc(((psPixels *) ((*questionablePixels)->data[im])),
+                                                              ((((psPixels *) ((*questionablePixels)->data[im]))->nalloc) +
+                                                               PS_COMBINE_IMAGE_INITIAL_PIXEL_LIST_LENGTH_INC));
+                                // XXX: Can the realloc() fail?  Must we check for NULL?
+                            }
+                            ((psPixels *) ((*questionablePixels)->data[im]))->data[ptr].x = col;
+                            ((psPixels *) ((*questionablePixels)->data[im]))->data[ptr].y = row;
+                            (qpPtr->data.S32[im])++;
+                            // XXX: this pixel ->n increment is wierd
+                            ((psPixels *) ((*questionablePixels)->data[im]))->n = qpPtr->data.S32[im];
+                        }
+                    }
+                }
+
+                //
+                // If the number of rejected pixels is zero, then there's no point
+                // continuing the loop.
+                //
+                if (numRejects == 0) {
+                    break;
+                }
+                psS32 totalRejects = 0;
+                for (psS32 im = 0 ; im < numImages ; im++) {
+                    if (pixelMask->data.U8[im] & maskVal) {
+                        totalRejects++;
+                    }
+                }
+
+                //
+                // XXX: Is it possible to have all pixels rejected?  If so, we should
+                // exit the loop.
+                //
+                if (totalRejects == numImages) {
+                    break;
+                }
+            }
+        }
+        psFree(stdevStats);
+    } else {
+        //
+        // We get here if there is a NULL list of pixels to combine.
+        // Therefore, we combine all pixels in all images.
+        //
+
+        //
+        // Loop over all pixels in all images, set the appropriate data, mask,
+        // error vectors, call psVectorStats(), and set the result in the
+        // combine image.
+        //
+        for (psS32 row = 0 ; row < numRows ; row++) {
+            for (psS32 col = 0 ; col < numCols ; col++) {
+                for (psS32 im = 0 ; im < numImages ; im++) {
+                    // Set the pixel data
+                    pixelData->data.F32[im] =       ((psImage *) images->data[im])->data.F32[row][col];
+
+                    // Set the pixel mask data, if necessary
+                    if (masks != NULL) {
+                        pixelMask->data.U8[im] =   ((psImage *) masks->data[im])->data.F32[row][col];
+                    }
+
+                    // Set the pixel error data, if necessary
+                    if (errors != NULL) {
+                        pixelErrors->data.F32[im] = ((psImage *) errors->data[im])->data.F32[row][col];
+                    }
+
+                }
+                // Combine all the pixels, using the specified stat.
+                stats = psVectorStats((psStats *) stats, pixelData, pixelErrors, pixelMask, maskVal);
+                psF64 tmpF64;
+                psBool rc = p_psGetStatValue(stats, &tmpF64);
+                combine->data.F32[row][col] = (psF32) tmpF64;
+                if (rc != true) {
+                    psLogMsg(__func__, PS_LOG_WARN, "WARNING: could not combine pixels (%d, %d) with the specified stat.\n", row, col);
+                }
+            }
+        }
+    }
+
+    psFree(pixelData);
+    psFree(pixelMask);
+    psFree(pixelErrors);
+    psFree(qpPtr);
+
+    psTrace("ImageCombine.pmCombineImages", 3, "Exiting pmCombineImages(%d)\n", images->n);
+    return(combine);
+}
+
+
+/******************************************************************************
+XXX: Directly from Paul Price
+ *****************************************************************************/
+static psF32 CalcGradient(
+    psImage *image,
+    psImage *imageMask,
+    psS32 x,
+    psS32 y
+)
+{
+    psTrace("ImageCombine.CalcGradient", 4, "Calling CalcGradient(%d, %d)\n", x, y);
+    int num = 0;
+    psVector *pixels = psVectorAlloc(8, PS_TYPE_F32); // Array of pixels
+    psVector *mask = psVectorAlloc(8, PS_TYPE_U8); // Corresponding mask
+
+    // Get limits
+    int xMin = PS_MAX(x - 1, 0);
+    int xMax = PS_MIN(x + 1, image->numCols - 1);
+    int yMin = PS_MAX(y - 1, 0);
+    int yMax = PS_MIN(y + 1, image->numRows - 1);
+    if (imageMask != NULL) {
+        for (int j = yMin; j <= yMax; j++) {
+            for (int i = xMin; i <= xMax; i++) {
+                if ((i != x) && (j != y) && (0 == imageMask->data.U8[j][i])) {
+                    pixels->data.F32[num] = image->data.F32[j][i];
+                    mask->data.U8[num] = 0;
+                    num++;
+                } else {
+                    mask->data.U8[num] = 1;
+                }
+            }
+        }
+    } else {
+        //
+        // This code is simply the previous loop without the imageMask.
+        // XXX: Consider restructuring this.
+        //
+        for (int j = yMin; j <= yMax; j++) {
+            for (int i = xMin; i <= xMax; i++) {
+                if ((i != x) && (j != y)) {
+                    pixels->data.F32[num] = image->data.F32[j][i];
+                    mask->data.U8[num] = 0;
+                    num++;
+                } else {
+                    mask->data.U8[num] = 1;
+                }
+            }
+        }
+    }
+
+    pixels->n = num;
+    mask->n = num;
+
+    // Get the median
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
+    (void)psVectorStats(stats, pixels, NULL, mask, 1);
+    float median = stats->sampleMedian;
+    psFree(stats);
+    psFree(pixels);
+    psFree(mask);
+
+    psTrace("ImageCombine.CalcGradient", 4, "Exiting CalcGradient(%d, %d)\n", x, y);
+    return(median / image->data.F32[y][x]);
+}
+
+/******************************************************************************
+DetermineRegion(image, myOutToIn): for a psImage and a psPlaneTransform to that
+image, this routine determines the size of the input image which maps to that
+image, and returns the result in a psRegion struct.
+ 
+XXX: Basically, this routine is only guaranteed to work if the transform is
+linear.
+ 
+XXX: Shouldn't this functionality be part of psImageTransform()?
+ *****************************************************************************/
+static psRegion DetermineRegion(psImage *image,
+                                psPlaneTransform *myOutToIn)
+{
+    psTrace("ImageCombine.DetermineRegion", 4, "Calling DetermineRegion()\n");
+    psRegion myRegion;
+    myRegion.x0 = PS_MAX_F32;
+    myRegion.x1 = PS_MIN_F32;
+    myRegion.y0 = PS_MAX_F32;
+    myRegion.y1 = PS_MIN_F32;
+    psPlane in;
+    psPlane out;
+
+    in.x = 0.0;
+    in.y = 0.0;
+
+    psPlaneTransformApply(&out, myOutToIn, &in);
+    if (out.x < myRegion.x0) {
+        myRegion.x0 = out.x;
+    }
+    if (out.x > myRegion.x1) {
+        myRegion.x1 = out.x;
+    }
+    if (out.y < myRegion.y0) {
+        myRegion.y0 = out.y;
+    }
+    if (out.y > myRegion.y1) {
+        myRegion.y1 = out.y;
+    }
+
+    in.x = (psF32) (image->numCols);
+    in.y = 0.0;
+    psPlaneTransformApply(&out, myOutToIn, &in);
+    if (out.x < myRegion.x0) {
+        myRegion.x0 = out.x;
+    }
+    if (out.x > myRegion.x1) {
+        myRegion.x1 = out.x;
+    }
+    if (out.y < myRegion.y0) {
+        myRegion.y0 = out.y;
+    }
+    if (out.y > myRegion.y1) {
+        myRegion.y1 = out.y;
+    }
+
+    in.x = (psF32) (image->numCols);
+    ;
+    in.y = 0.0;
+    psPlaneTransformApply(&out, myOutToIn, &in);
+    if (out.x < myRegion.x0) {
+        myRegion.x0 = out.x;
+    }
+    if (out.x > myRegion.x1) {
+        myRegion.x1 = out.x;
+    }
+    if (out.y < myRegion.y0) {
+        myRegion.y0 = out.y;
+    }
+    if (out.y > myRegion.y1) {
+        myRegion.y1 = out.y;
+    }
+
+    in.x = (psF32) (image->numCols);
+    in.y = (psF32) (image->numRows);
+    psPlaneTransformApply(&out, myOutToIn, &in);
+    if (out.x < myRegion.x0) {
+        myRegion.x0 = out.x;
+    }
+    if (out.x > myRegion.x1) {
+        myRegion.x1 = out.x;
+    }
+    if (out.y < myRegion.y0) {
+        myRegion.y0 = out.y;
+    }
+    if (out.y > myRegion.y1) {
+        myRegion.y1 = out.y;
+    }
+
+    psTrace("ImageCombine.DetermineRegion", 4, "Exiting DetermineRegion()\n");
+    return(myRegion);
+}
+
+/******************************************************************************
+XXX: Don't we have a psLib function for this?
+ *****************************************************************************/
+static psImage *ImageConvertF32(psImage *image)
+{
+    psTrace("ImageCombine.ImageConvertF32", 4, "Calling ImageConvertF32()\n");
+    psImage *imgF32 = psImageAlloc(image->numCols, image->numRows, PS_TYPE_F32);
+
+    for (psS32 i = 0 ; i < image->numRows ; i++) {
+        for (psS32 j = 0 ; j < image->numCols ; j++) {
+            imgF32->data.F32[i][j] = (psF32) image->data.U8[i][j];
+        }
+    }
+
+    psTrace("ImageCombine.ImageConvertF32", 4, "Exiting ImageConvertF32()\n");
+    return(imgF32);
+}
+
+
+//
+// The following macros define how big the initial pixel list will be, and
+// how much it should be incremented when realloc'ed.
+//
+#define PS_REJECT_PIXEL_INITIAL_PIXEL_LIST_LENGTH 100
+#define PS_REJECT_PIXEL_INITIAL_PIXEL_LIST_LENGTH_INC 100
+/******************************************************************************
+pmRejectPixels(images, errors, inToOut, outToIn, rejThreshold,
+gradLimit)
+ 
+XXX: Optimization: we don't need to transform the entire mask image.
+XXX: The inToOut and outToIn transforms are confusing.  Verify that what
+     I think they mean syncs with PWP.
+ *****************************************************************************/
+psArray *pmRejectPixels(
+    const psArray *images,              ///< Array of input images
+    const psArray *masks,               ///< Array of input image masks
+    const psArray *errors,              ///< The pixels which were rejected in the combination
+    const psArray *inToOut,             ///< Transformation from input to output system
+    const psArray *outToIn,             ///< Transformation from output to input system
+    psF32 rejThreshold,                 ///< Rejection threshold
+    psF32 gradLimit                     ///< Gradient limit
+)
+{
+    psTrace("ImageCombine.pmRejectPixels", 3, "Calling pmRejectPixels()\n");
+    PS_ASSERT_PTR_NON_NULL(images, NULL);
+    for (psS32 im = 0 ; im < images->n ; im++) {
+        psImage *tmpImage = (psImage *) images->data[im];
+        PS_ASSERT_IMAGE_NON_NULL(tmpImage, NULL);
+        PS_ASSERT_IMAGE_NON_EMPTY(tmpImage, NULL);
+        PS_ASSERT_IMAGE_TYPE(tmpImage, PS_TYPE_F32, NULL);
+        if (masks != NULL) {
+            PS_ASSERT_INT_EQUAL(images->n, masks->n, NULL);
+            psImage *tmpMask = (psImage *) masks->data[im];
+            PS_ASSERT_IMAGE_NON_NULL(tmpMask, NULL);
+            PS_ASSERT_IMAGE_NON_EMPTY(tmpMask, NULL);
+            PS_ASSERT_IMAGE_TYPE(tmpMask, PS_TYPE_F32, NULL);
+            PS_ASSERT_IMAGES_SIZE_EQUAL(tmpImage, tmpMask, NULL);
+        }
+        PS_ASSERT_IMAGES_SIZE_EQUAL(((psImage *) images->data[0]), tmpImage, NULL);
+    }
+    PS_ASSERT_PTR_NON_NULL(errors, NULL);
+    PS_ASSERT_PTR_NON_NULL(inToOut, NULL);
+    PS_ASSERT_PTR_NON_NULL(outToIn, NULL);
+    // Ensure that the psArray parameters have an element for each image.
+    psS32 numImages = images->n;
+    PS_ASSERT_INT_EQUAL(numImages, errors->n, NULL);
+    PS_ASSERT_INT_EQUAL(numImages, inToOut->n, NULL);
+    PS_ASSERT_INT_EQUAL(numImages, outToIn->n, NULL);
+
+    //
+    // Create the psArray of psPixelLists, one for each image, for rejected pixels.
+    //
+    psArray *rejects = psArrayAlloc(numImages);
+    for (psS32 im = 0 ; im < numImages ; im++) {
+        rejects->data[im] = (psPtr *) psPixelsAlloc(PS_REJECT_PIXEL_INITIAL_PIXEL_LIST_LENGTH);
+        psPixels *pixels = (psPixels *) rejects->data[im];
+        pixels->n = 0;
+    }
+    //
+    // rPtr is used to maintain a count of the questionable pixels for each image.
+    //
+    psVector *rPtr = psVectorAlloc(numImages, PS_TYPE_S32);
+    PS_VECTOR_SET_S32(rPtr, 0);
+
+    psS32 numCols = ((psImage *) images->data[0])->numCols;
+    psS32 numRows = ((psImage *) images->data[0])->numRows;
+    psRegion myRegion = psRegionSet(0, numCols-1, 0, numRows-1);
+    psU32 maskVal = 1;  // XXX: Is this appropriate?
+
+    psPlane *inCoords = psAlloc(sizeof(psPlane));
+    psPlane *outCoords = psAlloc(sizeof(psPlane));
+
+    for (psS32 im = 0 ; im < numImages ; im++) {
+        //
+        // Extract data from psArrays.
+        //
+        psPixels *pixelList = (psPixels *) errors->data[im];
+
+        psImage *currImage = (psImage *) images->data[im];
+        myRegion.x0 = 0;
+        myRegion.x1 = currImage->numCols;
+        myRegion.y0 = 0;
+        myRegion.y1 = currImage->numRows;
+        psPlaneTransform *myInToOut = (psPlaneTransform *) inToOut->data[im];
+        psPlaneTransform *myOutToIn = (psPlaneTransform *) outToIn->data[im];
+
+        //
+        // Create a psU8 mask image from the list of cosmic pixels.
+        //
+        psImage *maskImage = NULL;
+        maskImage = psPixelsToMask(maskImage, pixelList, myRegion, maskVal);
+        psImage *maskImageF32 = ImageConvertF32(maskImage);
+
+        //
+        // Transform that mask image into detector coordinate space
+        //
+        psRegion myRegionXForm = DetermineRegion(maskImageF32, myOutToIn);
+        psImage *transformedImage = psImageTransform(NULL, NULL, maskImageF32, NULL,
+                                    0, myOutToIn, myRegionXForm, NULL,
+                                    PS_INTERPOLATE_BILINEAR, 0);
+
+        //
+        // Loop over all cosmic pixels.  Transform their coords to detector space.
+        // If the value of the transformed mask is larger than rejThreshold, then
+        // this might be a cosmic ray pixel.  We then calculate the mean gradient
+        // in other images.
+        //
+        for (psS32 p = 0 ; p < pixelList->n ; p++) {
+            inCoords->x = 0.5 + (psF32) (pixelList->data[p]).x;
+            inCoords->y = 0.5 + (psF32) (pixelList->data[p]).y;
+            psPlaneTransformApply(outCoords, myInToOut, inCoords);
+            psF32 maskVal = (psF32) psImagePixelInterpolate(transformedImage, outCoords->x, outCoords->y,
+                            NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+            if (maskVal > rejThreshold) {
+
+                // This is a possible cosmic array pixel.  We must calculate the gradient
+                // at this location in all input images.
+                psF32 meanGrads = 0.0;
+                psS32 numGrads = 0;
+                //
+                // Loop through all other images, calculate their mean gradient.
+                //
+                for (psS32 otherImg = 0 ; otherImg < numImages ; otherImg++) {
+                    if (im != otherImg) {
+                        // Map the outCoords to inCoords that for otherImg space.
+                        psImage *tmpMask = NULL;
+                        if (masks != NULL) {
+                            tmpMask = masks->data[otherImg];
+                        }
+                        psPlaneTransformApply(inCoords,
+                                              (psPlaneTransform * )outToIn->data[otherImg],
+                                              outCoords);
+                        psS32 xPix = (int)(inCoords->x + 0.5);
+                        psS32 yPix = (int)(inCoords->y + 0.5);
+                        if ((xPix >= 0) && (xPix <= ((psImage*)(images->data[otherImg]))->numCols - 1) &&
+                                (yPix >= 0) && (yPix <= ((psImage*)(images->data[otherImg]))->numRows - 1)) {
+                            meanGrads += CalcGradient(images->data[otherImg], tmpMask, xPix, yPix);
+                            numGrads++;
+                        }
+                    }
+                }
+                if (numGrads > 0) {
+                    meanGrads /= (psF32) numGrads;
+                } else {
+                    // XXX: my idea.  Verify with PWP:
+                    meanGrads = 1.0 + gradLimit;
+                }
+
+                // XXX: The SDRS and the prototype code differ significantly here:
+                // if (CalcGradient(inputs->data[i], pixelList->data.x, pixelList->data.y) < (gradLimit * meanGrads)) {
+                if (meanGrads < gradLimit) {
+                    //
+                    // Add this to the list of questionable pixels.  We must ensure that the
+                    // pixelList is large enough; if not, we realloc()
+                    //
+                    psS32 ptr = rPtr->data.S32[im];
+                    psPixels *pixelListPtr = (psPixels *) rejects->data[im];
+                    if (ptr >= pixelListPtr->nalloc) {
+                        rejects->data[im] = (psPtr *) psPixelsRealloc(((psPixels *) rejects->data[im]),
+                                            ((((psPixels *) rejects->data[im])->nalloc) + PS_REJECT_PIXEL_INITIAL_PIXEL_LIST_LENGTH_INC));
+                        // XXX: Can the realloc() fail?  Must we check for NULL?
+                    }
+
+                    ((psPixels *) rejects->data[im])->data[ptr].x = (pixelList->data[p]).x;
+                    ((psPixels *) rejects->data[im])->data[ptr].y = (pixelList->data[p]).y;
+                    (rPtr->data.S32[im])++;
+                    // XXX: this pixel ->n increment is wierd
+                    (((psPixels *) rejects->data[im])->n)++;
+                }
+            }
+        }
+
+        psFree(maskImage);
+        psFree(maskImageF32);
+        psFree(transformedImage);
+    }
+
+    psFree(inCoords);
+    psFree(outCoords);
+    psTrace("ImageCombine.pmRejectPixels", 3, "Exiting pmRejectPixels()\n");
+    return(rejects);
+}
Index: /tags/ipp-1-X/rel8_2/psModules/src/imcombine/pmImageCombine.h
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/imcombine/pmImageCombine.h	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/imcombine/pmImageCombine.h	(revision 22115)
@@ -0,0 +1,50 @@
+/** @file  pmImageCombine.h
+ *
+ *  This file will perform image combination of several images of the
+ *  same field, produce a list of questionable pixels, then tag some
+ *  of those pixels as cosmic rays.
+ *
+ *  @author Paul Price, IfA (original prototype)
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#if !defined(PM_IMAGE_COMBINE_H)
+#define PM_IMAGE_COMBINE_H
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include<stdio.h>
+#include<math.h>
+#include "pslib.h"
+
+psImage *pmCombineImages(
+    psImage *combine,                   ///< Combined image (output)
+    psArray **questionablePixels,       ///< Array of rejection masks
+    const psArray *images,              ///< Array of input images
+    const psArray *errors,              ///< Array of input error images
+    const psArray *masks,               ///< Array of input masks
+    psU32 maskVal,                      ///< Mask value
+    const psPixels *pixels,             ///< Pixels to combine
+    psS32 numIter,                      ///< Number of rejection iterations
+    psF32 sigmaClip,                    ///< Number of standard deviations at which to reject
+    const psStats *stats                ///< Statistics to use in the combination
+);
+
+psArray *pmRejectPixels(
+    const psArray *images,              ///< Array of input images
+    const psArray *masks,               ///< Array of input image masks
+    const psArray *errors,              ///< The pixels which were rejected in the combination
+    const psArray *inToOut,             ///< Transformation from input to output system
+    const psArray *outToIn,             ///< Transformation from output to input system
+    psF32 rejThreshold,                 ///< Rejection threshold
+    psF32 gradLimit                     ///< Gradient limit
+);
+#endif
Index: /tags/ipp-1-X/rel8_2/psModules/src/imcombine/pmReadoutCombine.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/imcombine/pmReadoutCombine.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/imcombine/pmReadoutCombine.c	(revision 22115)
@@ -0,0 +1,375 @@
+/** @file  pmReadoutCombine.c
+ *
+ *  This file will contain a module which will combine multiple readout images.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include<stdio.h>
+#include<math.h>
+#include "pslib.h"
+#include "pmReadoutCombine.h"
+
+/******************************************************************************
+DetermineNumBits(data): This routine takes an enum psStatsOptions as an
+argument and returns the number of non-zero bits.
+ *****************************************************************************/
+static psS32 DetermineNumBits(psStatsOptions data)
+{
+    psS32 i;
+    psU64 tmpData = data;
+    psS32 numBits = 0;
+
+    for (i=0;i<(8 * sizeof(psStatsOptions));i++) {
+        if (0x0001 & tmpData) {
+            numBits++;
+        }
+        tmpData = tmpData >> 1;
+    }
+    return(numBits);
+}
+
+/******************************************************************************
+XXX: Must add support for S16 and S32 types.  F32 currently supported.
+ *****************************************************************************/
+psImage *pmReadoutCombine(psImage *output,
+                          const psList *inputs,
+                          psCombineParams *params,
+                          const psVector *zero,
+                          const psVector *scale,
+                          bool applyZeroScale,
+                          psF32 gain,
+                          psF32 readnoise)
+{
+    PS_ASSERT_PTR_NON_NULL(inputs, NULL);
+    PS_ASSERT_PTR_NON_NULL(params, NULL);
+    PS_ASSERT_PTR_NON_NULL(params->stats, NULL);
+    if (zero != NULL) {
+        PS_ASSERT_VECTOR_TYPE(zero, PS_TYPE_F32, NULL);
+        //        PS_ASSERT_VECTOR_TYPE_S16_S32_F32(zero, NULL);
+    }
+    if (scale != NULL) {
+        PS_ASSERT_VECTOR_TYPE(scale, PS_TYPE_F32, NULL);
+        //        PS_ASSERT_VECTOR_TYPE_S16_S32_F32(scale, NULL);
+    }
+    if ((zero != NULL) && (scale != NULL)) {
+        PS_ASSERT_VECTOR_TYPE_EQUAL(zero, scale, NULL);
+        // PS_ASSERT_VECTOR_TYPE_S16_S32_F32(scale, NULL);
+    }
+
+    psStats *stats = params->stats;
+    psS32 i;
+    psS32 j;
+    psS32 maxInputCols = 0;
+    psS32 maxInputRows = 0;
+    psS32 minInputCols = PS_MAX_S32;
+    psS32 minInputRows = PS_MAX_S32;
+    psListElem *tmpInput = NULL;
+    pmReadout *tmpReadout = NULL;
+    psS32 numInputs = 0;
+    psS32 tmpI;
+    psElemType outputType = PS_TYPE_F32;
+
+    if (1 < DetermineNumBits(params->stats->options)) {
+        psError(PS_ERR_UNKNOWN, true,
+                "Multiple statistical options have been requested.  Returning NULL.\n");
+        return(NULL);
+    }
+
+    //
+    // We step through each readout in the input image list.  If any readout is
+    // NULL, empty, or has the wrong type, we generate an error and return
+    // NULL.  We determine how big of an output image is needed to combine
+    // these input images.  We do this by taking the
+    //     max(readout->col0 + readout->numCols + image->col0 + image->numCols)
+    //     max(readout->row0 + readout->numRows + image->row0 + image->numRows)
+    // We then compare that to
+    //     output->col0 + output->numCols
+    //     output->row0 + output->numRows
+    // to determine if the output image actually stores that pixel.  A similar
+    // thing is done for the minimum row and column.
+    //
+    tmpInput = (psListElem *) inputs->head;
+    while (NULL != tmpInput) {
+        tmpReadout = (pmReadout *) tmpInput->data;
+        PS_ASSERT_READOUT_NON_NULL(tmpReadout, output);
+        PS_ASSERT_READOUT_NON_EMPTY(tmpReadout, output);
+        PS_ASSERT_READOUT_TYPE(tmpReadout, PS_TYPE_F32, output);
+
+        outputType = tmpReadout->image->type.type;
+
+        minInputRows = PS_MIN(minInputRows,
+                              (tmpReadout->row0 + tmpReadout->image->row0));
+        tmpI = tmpReadout->row0 +
+               tmpReadout->image->row0 +
+               tmpReadout->image->numRows;
+        maxInputRows = PS_MAX(maxInputRows, tmpI);
+
+        minInputCols = PS_MIN(minInputCols,
+                              (tmpReadout->col0 + tmpReadout->image->col0));
+        tmpI = tmpReadout->col0 +
+               tmpReadout->image->col0 +
+               tmpReadout->image->numCols;
+        maxInputCols = PS_MAX(maxInputCols, tmpI);
+        tmpInput = tmpInput->next;
+        numInputs++;
+    }
+
+    // We ensure that the zero vector is of the proper size.
+    if (zero != NULL) {
+        PS_ASSERT_VECTOR_TYPE(zero, PS_TYPE_F32, NULL);
+        if (numInputs > zero->n) {
+            psError(PS_ERR_UNKNOWN, true, "zero vector has incorrect size (%d).  Returning NULL.\n", zero->n);
+            return(NULL);
+        } else if (numInputs < zero->n) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: the zero vector too many elements (%d)\n", zero->n);
+        }
+    }
+
+    // We ensure that the scale vector is of the proper size.
+    if (scale != NULL) {
+        PS_ASSERT_VECTOR_TYPE(scale, PS_TYPE_F32, NULL);
+        if (numInputs > scale->n) {
+            psError(PS_ERR_UNKNOWN, true, "scale vector has incorrect size (%d).  Returning NULL.\n", scale->n);
+            return(NULL);
+        } else if (numInputs < scale->n) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: the scale vector has too many elements (%d)\n", scale->n);
+        }
+    }
+
+    // At this point, the following variables have been computed:
+    // maxInputRows: the largest input row value, in output image space.
+    // maxInputCols: the largest input column value, in output image space.
+    // minInputRows: the smallest input row value, in output image space.
+    // minInputCols: the smallest input column value, in output image space.
+    //
+    if (output == NULL) {
+        output = psImageAlloc(maxInputCols-minInputCols,
+                              maxInputRows-minInputRows, outputType);
+        *(psS32 *) &(output->col0) = minInputCols;
+        *(psS32 *) &(output->row0) = minInputRows;
+    } else {
+        PS_ASSERT_IMAGE_TYPE(output, PS_TYPE_F32, NULL);
+        if (((output->col0 + output->numCols) < maxInputCols) ||
+                ((output->row0 + output->numRows) < maxInputRows)) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "Output image (%d, %d) is too small to hold combined images.  Returning NULL.\n",
+                    output->row0 + output->numRows,
+                    output->col0 + output->numCols);
+            return(NULL);
+        }
+
+        if ((output->col0 > minInputCols) || (output->row0 > minInputRows)) {
+            psError(PS_ERR_UNKNOWN, true,
+                    "Output image offset is larger then input image offset.  Returning NULL.\n");
+            return(NULL);
+        }
+    }
+
+    psVector *tmpPixels = psVectorAlloc(numInputs, PS_TYPE_F32);
+    psVector *tmpPixelErrors = psVectorAlloc(numInputs, PS_TYPE_F32);
+    psVector *tmpPixelMask = psVectorAlloc(numInputs, PS_TYPE_U8);
+    psVector *tmpPixelMaskNKeep = psVectorAlloc(numInputs, PS_TYPE_U8);
+    psVector *outRowLower = psVectorAlloc(numInputs, PS_TYPE_U32);
+    psVector *outRowUpper = psVectorAlloc(numInputs, PS_TYPE_U32);
+    psVector *outColLower = psVectorAlloc(numInputs, PS_TYPE_U32);
+    psVector *outColUpper = psVectorAlloc(numInputs, PS_TYPE_U32);
+    pmReadout **tmpReadouts = (pmReadout **) psAlloc(numInputs * sizeof(pmReadout *));
+
+    // For each input readout, we create a pointer to that readout in
+    // "tmpReadouts[]", and we store the min/max pixel indices for that
+    // readout, in output image coordinates, in the psVectors
+    // (outRowLower, outColLower, outRowUpper, outColUpper).
+    i = 0;
+    tmpInput = (psListElem *) inputs->head;
+    while (NULL != tmpInput) {
+        tmpReadouts[i] = (pmReadout *) tmpInput->data;
+        outRowLower->data.U32[i] = tmpReadouts[i]->row0 + tmpReadouts[i]->image->row0;
+        outColLower->data.U32[i] = tmpReadouts[i]->col0 + tmpReadouts[i]->image->col0;
+        outRowUpper->data.U32[i] = tmpReadouts[i]->row0 +
+                                   tmpReadouts[i]->image->row0 +
+                                   tmpReadouts[i]->image->numRows;
+        outColUpper->data.U32[i] = tmpReadouts[i]->col0 +
+                                   tmpReadouts[i]->image->col0 +
+                                   tmpReadouts[i]->image->numCols;
+        tmpInput = tmpInput->next;
+        i++;
+    }
+
+    // We loop through each pixel in the output image.  We loop through each
+    // input readout.  We determine if that output pixel is contained in the
+    // image from that readout.  If so, we save it in psVector tmpPixels.
+    // If not, we set a mask for that element in tmpPixels.  Then, we mask off
+    // pixels not between fracLow and fracHigh.  Then we call the vector
+    // stats routine on those pixels/mask.  Then we set the output pixel value
+    // to the result of the stats call.
+
+    for (i = output->row0; i < (output->row0 + output->numRows) ; i++) {
+        for (j = output->col0; j < (output->col0 + output->numCols) ; j++) {
+            for (psS32 r = 0; r < numInputs ; r++) {
+                //  printf("[%d][%d]: [%d][%d] to [%d][%d]\n", i, j, outRowLower->data.U32[r], outColLower->data.U32[r], outRowUpper->data.U32[r], outColUpper->data.U32[r]);
+                if ((outRowLower->data.U32[r] <= i) &&
+                        (outColLower->data.U32[r] <= j) &&
+                        (outRowUpper->data.U32[r] > i) &&
+                        (outColUpper->data.U32[r] > j)) {
+
+                    psS32 imageRow = i - (tmpReadouts[r]->row0 +
+                                          tmpReadouts[r]->image->row0);
+                    psS32 imageCol = j - (tmpReadouts[r]->col0 +
+                                          tmpReadouts[r]->image->col0);
+
+                    if ((NULL == tmpReadouts[r]->mask) ||
+                            !(params->maskVal && tmpReadouts[r]->mask->data.U8[imageRow][imageCol])) {
+                        tmpPixels->data.F32[r] = tmpReadouts[r]->image->data.F32[imageRow][imageCol];
+                        tmpPixelMask->data.U8[r] = 0;
+                    } else {
+                        tmpPixels->data.F32[r] = 0.0;
+                        tmpPixelMask->data.U8[r] = 1;
+                    }
+                } else {
+                    tmpPixels->data.F32[r] = 0.0;
+                    tmpPixelMask->data.U8[r] = 1;
+                }
+                // printf("readout[%d], image [%d][%d] is %f\n", r, i, j, tmpPixels->data.F32[r]);
+            }
+            // At this point, we have scanned all input readouts for this
+            // one output pixel.
+            //            for (psS32 r = 0; r < numInputs ; r++) printf("(0)tmpPixels->data.F32[%d] is %f\n", r, tmpPixels->data.F32[r]);
+
+            // Determine how many pixels lie between fracLow and fracHigh.
+            psS32 pixelCount = 0;
+            for (psS32 r = 0; r < numInputs ; r++) {
+                if (tmpPixelMask->data.U8[r] == 0) {
+                    if ((params->fracLow <= tmpPixels->data.F32[r]) &&
+                            (params->fracHigh >= tmpPixels->data.F32[r])) {
+                        pixelCount++;
+                    }
+                }
+            }
+
+            // If more than params->nKeep pixels lie between the valid range,
+            // then loop through the pixels, and mask away any pixels outside
+            // that range.
+            if (pixelCount >= params->nKeep) {
+                for (psS32 r = 0; r < numInputs ; r++) {
+                    if (tmpPixelMask->data.U8[r] == 0) {
+                        if ((params->fracLow <= tmpPixels->data.F32[r]) &&
+                                (params->fracHigh >= tmpPixels->data.F32[r])) {
+                            tmpPixelMaskNKeep->data.U8[r] = 0;
+                        } else {
+                            tmpPixelMaskNKeep->data.U8[r] = 1;
+                        }
+                    }
+                }
+            }
+
+            if ((gain > 0.0) && (readnoise >= 0.0)) {
+                psF32 x;
+                psF32 sigma;
+                if (applyZeroScale == true) {
+                    for (psS32 r = 0; r < numInputs ; r++) {
+                        if (zero != NULL) {
+                            x = zero->data.F32[r];
+                        } else {
+                            x = 0.0;
+                        }
+                        if (scale != NULL) {
+                            x+= tmpPixels->data.F32[r] * scale->data.F32[r];
+                        } else {
+                            x+= tmpPixels->data.F32[r];
+                        }
+                        sigma = sqrtf((readnoise*readnoise) + gain * x) / gain;
+
+                        tmpPixelErrors->data.F32[r] = sigma;
+                        tmpPixels->data.F32[r]= x;
+                    }
+                } else {
+                    for (psS32 r = 0; r < numInputs ; r++) {
+                        x= tmpPixels->data.F32[r];
+
+                        if (zero != NULL) {
+                            sigma = zero->data.F32[r];
+                        } else {
+                            sigma = 0.0;
+                        }
+                        if (scale != NULL) {
+                            sigma+= tmpPixels->data.F32[r] * scale->data.F32[r];
+                        } else {
+                            sigma+= tmpPixels->data.F32[r];
+                        }
+                        sigma = sqrtf((readnoise*readnoise) + (gain * sigma)) / gain;
+
+                        tmpPixelErrors->data.F32[r] = sigma;
+                        tmpPixels->data.F32[r]= x;
+                    }
+                }
+                // Calculate the specified statistic on the stack of pixels.
+                //                for (psS32 r = 0; r < numInputs ; r++) printf("(1)tmpPixels->data.F32[%d] is %f\n", r, tmpPixels->data.F32[r]);
+                psStats *rc = psVectorStats(stats,
+                                            tmpPixels,
+                                            tmpPixelErrors,
+                                            tmpPixelMaskNKeep,
+                                            1);
+                if (rc == NULL) {
+                    psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning NULL.\n");
+                    return(NULL);
+                }
+            } else {
+                if (scale != NULL) {
+                    for (psS32 r = 0; r < numInputs ; r++) {
+                        tmpPixels->data.F32[r]*= scale->data.F32[r];
+                    }
+                }
+
+                // We add the zero vector, if non-NULL.
+                if (zero != NULL) {
+                    for (psS32 r = 0; r < numInputs ; r++) {
+                        tmpPixels->data.F32[r]+= zero->data.F32[r];
+                    }
+                }
+
+                // Calculate the specified statistic on the stack of pixels.
+                //                for (psS32 r = 0; r < numInputs ; r++) printf("(2)tmpPixels->data.F32[%d] is %f\n", r, tmpPixels->data.F32[r]);
+                psStats *rc = psVectorStats(stats,
+                                            tmpPixels,
+                                            NULL,
+                                            tmpPixelMaskNKeep,
+                                            1);
+                if (rc == NULL) {
+                    psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning NULL.\n");
+                    return(NULL);
+                }
+            }
+
+
+            // Set the pixel value in the output image to the stat value.
+            double statValue;
+            if (!p_psGetStatValue(stats, &statValue)) {
+                psError(PS_ERR_UNKNOWN, true, "Could not determine stats value.  Returning NULL.\n");
+                return(NULL);
+            } else {
+                output->data.F32[i-output->row0][j-output->col0] = (psF32) statValue;
+            }
+        }
+    }
+
+    psFree(tmpPixels);
+    psFree(tmpPixelErrors);
+    psFree(tmpPixelMask);
+    psFree(tmpPixelMaskNKeep);
+    psFree(outRowLower);
+    psFree(outRowUpper);
+    psFree(outColLower);
+    psFree(outColUpper);
+    psFree(tmpReadouts);
+
+    return(output);
+}
Index: /tags/ipp-1-X/rel8_2/psModules/src/imcombine/pmReadoutCombine.h
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/imcombine/pmReadoutCombine.h	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/imcombine/pmReadoutCombine.h	(revision 22115)
@@ -0,0 +1,46 @@
+/** @file  pmReadoutCombine.h
+ *
+ *  This file will contain a module which will combine multiple readout images.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#if !defined(PM_READOUT_COMBINE_H)
+#define PM_READOUT_COMBINE_H
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include<stdio.h>
+#include<math.h>
+#include "pslib.h"
+#include "psConstants.h"
+#include "pmAstrometry.h"
+
+typedef struct
+{
+    psStats *stats;
+    unsigned int maskVal;
+    float fracHigh;
+    float fracLow;
+    int nKeep;
+}
+psCombineParams;
+
+psImage *pmReadoutCombine(psImage *output,
+                          const psList *inputs,
+                          psCombineParams *params,
+                          const psVector *zero,
+                          const psVector *scale,
+                          bool applyZeroScale,
+                          float gain,
+                          float readnoise);
+
+#endif
Index: /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/.cvsignore
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/.cvsignore	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/.cvsignore	(revision 22115)
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
Index: /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/Makefile.am
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/Makefile.am	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/Makefile.am	(revision 22115)
@@ -0,0 +1,13 @@
+noinst_LTLIBRARIES = libpsmoduleimsubtract.la
+
+libpsmoduleimsubtract_la_CPPFLAGS = $(SRCINC) $(PSMODULE_CFLAGS)
+libpsmoduleimsubtract_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+libpsmoduleimsubtract_la_SOURCES  = pmImageSubtract.c \
+    pmSubtractBias.c \
+    pmSubtractSky.c
+
+psmoduleincludedir = $(includedir)
+psmoduleinclude_HEADERS = \
+    pmImageSubtract.h \
+    pmSubtractBias.h \
+    pmSubtractSky.h
Index: /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/pmImageSubtract.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/pmImageSubtract.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/pmImageSubtract.c	(revision 22115)
@@ -0,0 +1,1390 @@
+/** @file  ImageSubtract.c
+ *
+ *  This file will contain code which creates a set of kernel basis
+ *  functions, solves for their solution, and applies them to an image.
+ *
+ *  @author Paul Price, IfA (original prototype)
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ *   XXX: sync with IfA on this:
+ *   The (x, y) (row, col) issue is becoming a problem.  In this file, and I
+ *   think, the rest of psLib and psModules, the following conventions are used:
+ *
+ * 1) x will correspond to the column, and y will correspond to the row.
+ * 2) When used in function prototypes, the column (and hence x) appears
+ *    first.
+ * 3) When used to index 2-D arrays, obviously, the row (and hence, y)
+ *    appears first. (2 and 3 are the source of confusion).
+ * 4) When (u, v) are used in certain structures.
+ *  u corresponds to x
+ *  v corresponds to y
+ * 5) When element (a, b) is casually referred to in comments, or
+ *    documentation it is unclear where a is the row, or the column.
+ * 6) A convention on loop index variables (i, j) would be convenient.
+ *    Currently, sometimes i corresponds to the column (x),
+ *    usually it corresponds to the row (y).
+ *
+ *  XXX: The following variables are used an interpreted this way:
+ * kernelSize: Means that the actual kernel is a square (1 + 2 * kernelSize) per side.
+ * border: When accessing an image, a swath of pixels this wide is ignored.
+ * footprint: When accessing a stample, a square of pixels, footprint pixels per side,
+ *  are looked at.  We must ensure that (footprint+kernelSize) pixels exist
+ *  around the center.
+ */
+
+#include<stdio.h>
+#include<math.h>
+#include "pslib.h"
+#include "psConstants.h"
+#include "pmImageSubtract.h"
+
+/*******************************************************************************
+    Private alloc/free functions.
+XXX: It's not clear if the SubtractionKernels alloc/free functions are feasable.
+ ******************************************************************************/
+void p_pmStampFree(pmStamp *stamp)
+{
+    psFree(stamp->matrix);
+    psFree(stamp->vector);
+}
+
+pmStamp *p_pmStampAlloc(pmStampStatus status)
+{
+    pmStamp *stamp = (pmStamp*)psAlloc(sizeof(pmStamp));
+    stamp->x = 0;
+    stamp->p_xSize = 0;
+    stamp->y = 0;
+    stamp->p_ySize = 0;
+    stamp->matrix = NULL;
+    stamp->vector = NULL;
+    stamp->status = status;
+
+    psMemSetDeallocator(stamp, (psFreeFunc)p_pmStampFree);
+
+    return(stamp);
+}
+
+void p_pmSubtractionKernelsFree(psSubtractionKernels *kernels)
+{
+    psFree(kernels->u);
+    psFree(kernels->v);
+    psFree(kernels->sigma);
+    psFree(kernels->xOrder);
+    psFree(kernels->yOrder);
+    psFree(kernels->preCalc);
+
+    psFree(kernels);
+}
+
+psSubtractionKernels *p_pmSubtractionKernelsAlloc(int numBasisFunctions,
+        pmSubtractionKernelsType type)
+{
+    psSubtractionKernels *tmp = (psSubtractionKernels *) psAlloc(sizeof(psSubtractionKernels));
+
+    tmp->type = type;
+    psMemSetDeallocator(tmp, (psFreeFunc) p_pmSubtractionKernelsFree);
+    return(tmp);
+}
+
+/*******************************************************************************
+psSubtractionKernels struct.
+ ******************************************************************************/
+psSubtractionKernels *pmSubtractionKernelsAllocPOIS(int size,
+        int spatialOrder)
+{
+    psTrace("ImageSubtract.pmSubtractionKernelsAllocPOIS", 3,
+            "Calling pmSubtractionKernelsAllocPOIS(%d, %d)\n", size, spatialOrder);
+    PS_ASSERT_INT_POSITIVE(size, NULL);
+    PS_ASSERT_INT_POSITIVE(spatialOrder, NULL);
+    //
+    // Calculate the number of basis functions (nBF)
+    //
+    psS32 xKernelHalfSize = size;
+    psS32 yKernelHalfSize = size;
+    psS32 nBF = (2 * xKernelHalfSize + 1) *
+                (2 * yKernelHalfSize + 1) *
+                (spatialOrder + 1) *
+                (spatialOrder + 2) / 2;
+
+    //
+    // Generate the new psSubtractionKernels data structure:
+    //
+    psSubtractionKernels *tmp = (psSubtractionKernels *) psAlloc(sizeof(psSubtractionKernels));
+    tmp->type = PM_SUBTRACTION_KERNEL_POIS;
+    tmp->u = psVectorAlloc(nBF, PS_TYPE_F32);
+    tmp->v = psVectorAlloc(nBF, PS_TYPE_F32);
+    tmp->sigma = NULL;
+    tmp->xOrder = psVectorAlloc(nBF, PS_TYPE_F32);
+    tmp->yOrder = psVectorAlloc(nBF, PS_TYPE_F32);
+    tmp->subIndex = 0;
+    tmp->preCalc = NULL;
+    tmp->size = size;
+    tmp->spatialOrder = spatialOrder;
+
+    //
+    // This corresponds to the Kernel Basis Function with (u, v) = (0, 0)
+    //
+    // Put the (u,v) = (0,0) component right at the start of the list
+    // for convenience.
+    //
+    psS32 ptr = 0;
+    for (psS32 order = 0; order <= spatialOrder; order++) {
+        for (psS32 xOrder = 0; xOrder <= order; xOrder++) {
+            psS32 yOrder = order - xOrder;
+            tmp->u->data.F32[ptr] = 0;
+            tmp->v->data.F32[ptr] = 0;
+            tmp->xOrder->data.F32[ptr] = xOrder;
+            tmp->yOrder->data.F32[ptr] = yOrder;
+            ptr++;
+        }
+    }
+
+    //
+    // Iterate over (u,v).  Generate a set of kernels for each (u, v).
+    //
+    for (psS32 v = -yKernelHalfSize; v <= yKernelHalfSize; v++) {
+        for (psS32 u = -xKernelHalfSize; u <= xKernelHalfSize; u++) {
+            // Already did (u,v) = (0,0): it's at the start, so skip it now.
+            if ((u != 0) || (v != 0)) {
+                //
+                // Iterate over spatial order.  This loop creates the terms for
+                // x^xOrder * y^yOrder  such that (xOrder+yOrder) <= spatialOrder.
+                //
+                for (psS32 order = 0; order <= spatialOrder; order++) {
+                    for (psS32 xOrder = 0; xOrder <= order; xOrder++) {
+                        psS32 yOrder = order - xOrder;
+                        tmp->u->data.F32[ptr] = u;
+                        tmp->v->data.F32[ptr] = v;
+                        tmp->xOrder->data.F32[ptr] = xOrder;
+                        tmp->yOrder->data.F32[ptr] = yOrder;
+                        ptr++;
+                    }
+                }
+            }
+        }
+    }
+
+    psTrace("ImageSubtract.pmSubtractionKernelsAllocPOIS", 3,
+            "Exiting pmSubtractionKernelsAllocPOIS(%d, %d)\n", size, spatialOrder);
+    return(tmp);
+}
+
+/*******************************************************************************
+XXX: Get the types correct (u, v, xOrder, yOrder).
+ 
+XXX: Code review this.
+ ******************************************************************************/
+psSubtractionKernels *pmSubtractionKernelsAllocISIS(const psVector *sigmas,
+        const psVector *orders,
+        int size,
+        int spatialOrder)
+{
+    PS_ASSERT_VECTOR_NON_NULL(sigmas, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(orders, NULL);
+    psTrace("ImageSubtract.pmSubtractionKernelsAllocISIS", 3,
+            "Calling pmSubtractionKernelsAllocISIS(%d, %d, %d, %d)\n",
+            sigmas->n, orders->n, size, spatialOrder);
+    PS_ASSERT_INT_POSITIVE(size, NULL);
+    PS_ASSERT_INT_POSITIVE(spatialOrder, NULL);
+    PS_ASSERT_VECTOR_TYPE(sigmas, PS_TYPE_F32, NULL);
+    PS_ASSERT_VECTOR_TYPE(orders, PS_TYPE_S32, NULL);
+    //
+    // Calculate the number of basis functions (nBF).
+    //
+    psS32 numSigmas = sigmas->n;
+
+    // XXX: Get rid of the sigma loop?  We merely multiple nBF by numSigmas?
+    // XXX: Verify that all this is correct.
+    psS32 nBF = 0;
+    for (psS32 s = 0 ; s < numSigmas ; s++) {
+        for (psS32 o = 0 ; o < orders->n ; o++) {
+            nBF+=((orders->data.S32[o] + 1) * (orders->data.S32[o] + 2) / 2);
+        }
+    }
+    nBF*= ((spatialOrder + 1) * (spatialOrder + 2) / 2);
+
+    //
+    // Generate the new psSubtractionKernels data structure:
+    //
+    psSubtractionKernels *tmp = (psSubtractionKernels *) psAlloc(sizeof(psSubtractionKernels));
+    tmp->type = PM_SUBTRACTION_KERNEL_ISIS;
+    tmp->u = psVectorAlloc(nBF, PS_TYPE_F32);
+    tmp->v = psVectorAlloc(nBF, PS_TYPE_F32);
+    tmp->sigma = psVectorAlloc(nBF, PS_TYPE_F32);
+    tmp->xOrder = psVectorAlloc(nBF, PS_TYPE_F32);
+    tmp->yOrder = psVectorAlloc(nBF, PS_TYPE_F32);
+    tmp->subIndex = 0;
+    tmp->size = size;
+    tmp->spatialOrder = spatialOrder;
+    tmp->preCalc = psArrayAlloc(nBF);
+
+    //
+    // We loop through all combinations of sigmas and polynomial orders
+    // creating the kernel basis functions.
+    //
+    psS32 ptr = 0;
+    for (psS32 sigPtr = 0 ; sigPtr < numSigmas ; sigPtr++) {
+        tmp->sigma->data.F32[sigPtr] = sigmas->data.F32[sigPtr];
+        //
+        // (xOrderP, yOrderP) are the order of the polynomial that modify the
+        // gaussian in the kernel.  They correspond to (j, k) in equation (5)
+        // from the psModules SDRS.
+        //
+        for (psS32 o = 0 ; o < orders->n ; o++) {
+            for (psS32 orderP = 0 ; orderP <= orders->data.S32[o] ; orderP++) {
+                for (psS32 xOrderP = 0 ; xOrderP <= orderP ; xOrderP++) {
+                    psS32 yOrderP = orderP - xOrderP;
+
+                    psImage *currPreCalc = psImageAlloc(1 + (2 * size), 1 + (2 * size), PS_TYPE_F32);
+                    PS_IMAGE_SET_F32(currPreCalc, 0.0);
+                    psBool setPreCalc = true;
+                    //
+                    // We loop through all spatial orders.  Since they have no effect on
+                    // the preCalc images, we only calculate them once, and store pointers
+                    // in tmp->preCalc->data[ptr] for other spatial orders.
+                    //
+                    for (psS32 order = 0; order <= spatialOrder; order++) {
+                        for (psS32 orderXTerm = 0; orderXTerm <= order; orderXTerm++) {
+                            PS_ASSERT_INT_LESS_THAN(ptr, nBF, NULL);
+
+                            psS32 orderYTerm = order - orderXTerm;
+
+                            tmp->u->data.F32[ptr] = xOrderP;
+                            tmp->v->data.F32[ptr] = yOrderP;
+                            tmp->xOrder->data.F32[ptr] = orderXTerm;
+                            tmp->yOrder->data.F32[ptr] = orderYTerm;
+                            tmp->sigma->data.F32[ptr] = sigmas->data.F32[sigPtr];
+                            tmp->preCalc->data[ptr] = (psPtr *) currPreCalc;
+
+                            //
+                            // We calculate the preCalc image only the first time through
+                            // this loop.  Otherwise, we increment the memory reference
+                            // counter.
+                            //
+                            if (setPreCalc == true) {
+                                for (psS32 v = -size; v <= size; v++) {
+                                    for (psS32 u = -size; u <= size; u++) {
+                                        // Scale the (u,v) coordinates in kernel space to [-1.0:1.0].
+                                        psF32 uScaled = ((psF32) u) / ((psF32) size);
+                                        psF32 vScaled = ((psF32) v) / ((psF32) size);
+
+                                        // Compute the value of the kernel at location (u, v):
+                                        psF32 exponent = (PS_SQR(uScaled) + PS_SQR(vScaled)) /
+                                                         (2.0 * PS_SQR(sigmas->data.F32[sigPtr]));
+                                        currPreCalc->data.F32[v+size][u+size] =
+                                            exp(-exponent) *
+                                            pow(uScaled, orderXTerm) *
+                                            pow(vScaled, orderYTerm);
+                                    }
+                                }
+                                setPreCalc = false;
+                            } else {
+                                psMemIncrRefCounter(currPreCalc);
+                            }
+                            ptr++;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    psTrace("ImageSubtract.pmSubtractionKernelsAllocISIS", 3,
+            "Exiting pmSubtractionKernelsAllocISIS(%d, %d, %d, %d)\n",
+            sigmas->n, orders->n, size, spatialOrder);
+    return(tmp);
+}
+
+/*******************************************************************************
+pmSubtractionFindStamps(stamps, image, mask, maskVal, threshold, xNum, yNum, border)
+ 
+XXX: The SDRS and the proptotype code differ significantly.
+ Prototype: When a maximum pixel is found within a stamp, an area of size
+     2*footprint is searched around that pixel looking for masked pixels.
+ SDRS: none of that is required.
+ 
+XXX: Do we need to care about case where yNum/xNum does not evenly divide the
+nnumber of rows/columns in the image?
+ ******************************************************************************/
+psArray *pmSubtractionFindStamps(psArray *stamps,        ///< Output stamps, or NULL
+                                 const psImage *image,   ///< Image for which to find stamps
+                                 const psImage *mask,    ///< Mask
+                                 psU32 maskVal,          ///< Value for mask
+                                 psF32 threshold,        ///< Threshold for stamps in the image
+                                 psS32 xNum,             ///< Number of stamps in x
+                                 psS32 yNum,             ///< Number of stamps in y
+                                 psS32 border            ///< Border around image to ignore (should be size of kernel or larger)
+                                )
+{
+    psTrace("ImageSubtract.pmSubtractionFindStamps", 3,
+            "Calling pmSubtractionFindStamps(%d, %f, %d, %d, %d)\n",
+            maskVal, threshold, xNum, yNum, border);
+    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(image, NULL);
+    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
+    if (mask != NULL) {
+        PS_ASSERT_IMAGES_SIZE_EQUAL(image, mask, NULL);
+        PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_U8, NULL);
+    }
+    PS_ASSERT_INT_POSITIVE(xNum, NULL);
+    PS_ASSERT_INT_POSITIVE(yNum, NULL);
+    PS_ASSERT_INT_POSITIVE(border, NULL);
+    PS_ASSERT_INT_LARGER_THAN(image->numCols, xNum, NULL);
+    PS_ASSERT_INT_LARGER_THAN(image->numRows, yNum, NULL);
+    PS_ASSERT_INT_LARGER_THAN(image->numCols, (2 * border), NULL);
+    PS_ASSERT_INT_LARGER_THAN(image->numRows, (2 * border), NULL);
+
+    if (stamps != NULL) {
+        PS_ASSERT_INT_EQUAL(stamps->n, (xNum * yNum), NULL);
+        //
+        // Ensure that a pmStamp struct exists at each psArray location.
+        //
+        for (psS32 s = 0 ; s < (xNum * yNum) ; s++) {
+            if (NULL == stamps->data[s]) {
+                stamps->data[s] = (psPtr *) p_pmStampAlloc(PM_STAMP_REJECTED);
+            }
+        }
+    } else {
+        stamps = (psArray *) psArrayAlloc(xNum * yNum);
+        for (psS32 s = 0 ; s < (xNum * yNum) ; s++) {
+            stamps->data[s] = (psPtr *) p_pmStampAlloc(PM_STAMP_REJECTED);
+        }
+    }
+    psS32 numRows = image->numRows;
+    psS32 numCols = image->numCols;
+
+    //
+    // Iterate over the image sections
+    //
+    // XXX: Must handle cases where image size is not an even multiple of xNum or yNum
+    // they are currently ignored.
+    //
+    psS32 num = 0;
+    for (psS32 j = 0; j < yNum; j++) {
+        for (psS32 i = 0; i < xNum; i++) {
+            pmStamp *stamp = (pmStamp *) stamps->data[num];
+            //
+            // Only find a new stamp if we need to
+            //
+            if (stamp->status == PM_STAMP_REJECTED) {
+                //
+                // Find maximum non-masked value in the image section,
+                // but don't include a footprint around the edge
+                //
+                psF32 max = -INFINITY;
+                psS32 bestx = 0;
+                psS32 besty = 0;
+                //
+                // The following nested loop iterates over every pixel in the mask
+                // associated with this (i, j).  It ignores pixels within a
+                // border of pixels from the image edge.
+                //
+                // XXX: verify (numX, numY), then get rid of it.
+                //
+                psS32 numX = xNum;
+                psS32 numY = yNum;
+                psS32 yMin = border + j * (numCols - 2.0 * border) / numY;
+                psS32 yMax = (border + (j + 1) * (numCols - 2.0 * border) / numY) - 1;
+                psS32 xMin = border + i * (numRows - 2.0 * border) / numX;
+                psS32 xMax = (border + (i + 1) * (numRows - 2.0 * border) / numX) - 1;
+
+                if ((yMax >= image->numRows) ||
+                        (xMax >= image->numCols) ||
+                        (yMin < 0) ||
+                        (xMin < 0)) {
+                    // XXX: We skip this stamp since its borders extends beyond the image.
+                    // XXX: This is here mainly as a safeguard.  We need to redefine the above
+                    // min/max pixels calculation to ensure that all stamps are legitimate.
+
+                    stamp->x = -1;
+                    stamp->y = -1;
+                    stamp->status = PM_STAMP_NONE;
+                } else {
+                    stamp->p_xSize = 1 + (xMax - xMin);
+                    stamp->p_ySize = 1 + (yMax - yMin);
+                    stamp->p_xMin = xMin;
+                    stamp->p_xMax = xMax;
+                    stamp->p_yMin = yMin;
+                    stamp->p_yMax = yMax;
+
+                    for (psS32 y = yMin; y <= yMax ; y++) {
+                        for (psS32 x = xMin; x <= xMax ; x++) {
+                            // Determine if this pixel is larger than the max, and unmasked.
+                            if (image->data.F32[y][x] > max) {
+                                if ((mask == NULL) || !((mask->data.U8[y][x]) & maskVal)) {
+                                    max = image->data.F32[y][x];
+                                    bestx = x;
+                                    besty = y;
+                                }
+                            }
+                        }
+                    }
+
+                    //
+                    // If the max pixel is larger than the threshold, we keep this stamp.
+                    // Otherwise, mark the stamp as PM_STAMP_NONE
+                    //
+                    if (image->data.F32[besty][bestx] >= threshold) {
+                        stamp->x = bestx;
+                        stamp->y = besty;
+                        stamp->status = PM_STAMP_RECALC;
+                    } else {
+                        stamp->x = bestx;
+                        stamp->y = besty;
+                        stamp->status = PM_STAMP_NONE;
+                    }
+                }
+            }
+            num++;
+        }
+    }
+    psTrace("ImageSubtract.pmSubtractionFindStamps", 3,
+            "Exiting pmSubtractionFindStamps(%d, %f, %d, %d, %d)\n",
+            maskVal, threshold, xNum, yNum, border);
+    return(stamps);
+}
+
+/*******************************************************************************
+GenSpatialOrder(spatialOrder, x, y): generates and returns a psImage in which
+the [i][j] location is calculated as (x^i * y^j).
+ 
+XXX: Modify loop so that terms higher than spatialOrder are not computed.
+ 
+XXX: Modify this so that [i][j] location is calculated as (x^j * y^i)?
+ ******************************************************************************/
+static psImage *GenSpatialOrder(psS32 spatialOrder,
+                                psF32 x,
+                                psF32 y)
+{
+    psTrace("ImageSubtract.GenSpatialOrder", 4,
+            "Calling GenSpatialOrder(%d, %f, %f)\n", spatialOrder, x, y);
+
+    psImage *polyValues = psImageAlloc(spatialOrder+1, spatialOrder+1, PS_TYPE_F64);
+
+    psF64 xSum = 1.0;
+    psF64 ySum = 1.0;
+    for (psS32 i = 0; i < spatialOrder + 1; i++) {
+        ySum = xSum;
+        for (psS32 j = 0; j < spatialOrder + 1; j++) {
+            polyValues->data.F64[i][j] = ySum;
+            ySum*= y;
+        }
+        xSum*= x;
+    }
+
+    psTrace("ImageSubtract.GenSpatialOrder", 4,
+            "Exiting GenSpatialOrder(%d, %f, %f)\n", spatialOrder, x, y);
+
+    return(polyValues);
+}
+
+
+/*******************************************************************************
+IsisKernelConvolve(input, kernels, kernelID, col, row): This routine
+convolves a single kernel basis function with a pixel in an image.
+  ******************************************************************************/
+static psF32 IsisKernelConvolve(const psImage *input,
+                                const psSubtractionKernels *kernels,
+                                psS32 kernelID,
+                                psS32 col,
+                                psS32 row)
+{
+
+
+    psTrace("ImageSubtract.IsisKernelConvolve", 4,
+            "Calling IsisKernelConvolve(%d, %d, %d)\n", kernelID, col, row);
+    psS32 spatialOrder = kernels->spatialOrder;
+    psS32 kernelSize = kernels->size;
+    psS32 xOrder = (psS32) kernels->xOrder->data.F32[kernelID];
+    psS32 yOrder = (psS32) kernels->yOrder->data.F32[kernelID];
+    psF32 numColsHalf = 0.5 * (psF32) input->numCols;
+    psF32 numRowsHalf = 0.5 * (psF32) input->numRows;
+    psF32 imageX = (((psF32) col) - numColsHalf) / numColsHalf; // Normalised position
+    psF32 imageY = (((psF32) row) - numRowsHalf) / numRowsHalf; // Normalised position
+
+    psImage *polyValues = GenSpatialOrder(spatialOrder, imageX, imageY);
+
+    psF64 polyVal = polyValues->data.F64[yOrder][xOrder];
+
+    psImage *preCalc = (psImage *) kernels->preCalc->data[kernelID];
+
+    // XXX: Are the following asserts really necessary?
+    PS_ASSERT_INT_LARGER_THAN_OR_EQUAL(row-kernelSize, 0, NAN);
+    PS_ASSERT_INT_LESS_THAN(row+kernelSize, input->numRows, NAN);
+    PS_ASSERT_INT_LARGER_THAN_OR_EQUAL(col-kernelSize, 0, NAN);
+    PS_ASSERT_INT_LESS_THAN(col+kernelSize, input->numCols, NAN);
+    psF32 conv = 0.0;
+    for (psS32 yy = -kernelSize ; yy < kernelSize ; yy++) {
+        for (psS32 xx = -kernelSize ; xx < kernelSize ; xx++) {
+            conv += input->data.F32[yy+row][xx+col] *
+                    preCalc->data.F32[yy+kernelSize][xx+kernelSize] *
+                    polyVal;
+        }
+    }
+    psFree(polyValues);
+
+    psTrace("ImageSubtract.IsisKernelConvolve", 4,
+            "Exiting IsisKernelConvolve(%d, %d, %d)\n", kernelID, col, row);
+    return(conv);
+}
+
+/*******************************************************************************
+ConvolvePixelPois(input, mask, badStampMaskVal, solution, kernels, col, row):
+ 
+This routine takes a single pixel in the psImage input and convolves it with
+the set of kernel basis functions and their appropriate weights in solution.
+It returns the value of the convolved pixel.
+ 
+XXX: Static structure for polyValues?
+ ******************************************************************************/
+static psF32 ConvolvePixelPois(const psImage *input,
+                               const psImage *mask,
+                               psU32 badStampMaskVal,
+                               const psVector *solution,
+                               const psSubtractionKernels *kernels,
+                               psS32 col,
+                               psS32 row)
+{
+    psTrace("ImageSubtract.ConvolvePixelPois", 4,
+            "Calling ConvolvePixelPois(%d, %d)\n", col, row);
+    psS32 nBF = kernels->u->n;
+    psF32 numColsHalf = 0.5 * (psF32) input->numCols;
+    psF32 numRowsHalf = 0.5 * (psF32) input->numRows;
+    psF32 background = solution->data.F64[solution->n-1];
+    psS32 spatialOrder = kernels->spatialOrder;
+    psF32 conv = background; // Initial convolved value
+
+    if ((mask == NULL) || !(mask->data.U8[row][col] & badStampMaskVal)) {
+        psF32 imageX = (((psF32) col) - numColsHalf) / numColsHalf; // Normalised position
+        psF32 imageY = (((psF32) row) - numRowsHalf) / numRowsHalf; // Normalised position
+        psImage *polyValues = GenSpatialOrder(spatialOrder, imageX, imageY);
+
+        // Iterate over the kernel basis functions
+        for (psS32 k = 0; k < nBF; k++) {
+            psS32 u = (psS32) kernels->u->data.F32[k];
+            psS32 v = (psS32) kernels->v->data.F32[k];
+
+            // XXX: What's the story with this?
+            #if 0
+
+            psS32 xOrder = (psS32) kernels->xOrder->data.F32[k];
+            psS32 yOrder = (psS32) kernels->yOrder->data.F32[k];
+            psF64 polyVal = polyValues->data.F64[yOrder][xOrder];
+            #else
+
+            psF32 polyVal = 1.0;
+            #endif
+
+            // XXX: Why this?
+            if (k == 0) {
+                conv += solution->data.F64[k] * input->data.F32[row - v][col - u] * polyVal;
+            } else {
+                conv += solution->data.F64[k] *
+                        (input->data.F32[row - v][col - u] * polyVal - input->data.F32[row][col]);
+            }
+        }
+        psFree(polyValues);
+    }
+
+    psTrace("ImageSubtract.ConvolvePixelPois", 4,
+            "Exiting ConvolvePixelPois(%d, %d)\n", col, row);
+    return(conv);
+}
+
+
+
+/*******************************************************************************
+ConvolvePixelIsis(input, mask, badStampMaskVal, solution, kernels, col, row):
+ 
+This routine takes a single pixel in the psImage input and convolves it with
+the set of kernel basis functions and their appropriate weights in solution.
+It returns the value of the convolved pixel.
+ 
+XXX: Static structure for polyValues?
+ ******************************************************************************/
+static psF32 ConvolvePixelIsis(const psImage *input,
+                               const psImage *mask,
+                               psU32 badStampMaskVal,
+                               const psVector *solution,
+                               const psSubtractionKernels *kernels,
+                               psS32 col,
+                               psS32 row)
+{
+    psTrace("ImageSubtract.ConvolvePixelIsis", 4,
+            "Calling ConvolvePixelIsis(%d, %d)\n", col, row);
+    psF32 background = solution->data.F64[solution->n-1];
+    psF32 conv = background; // Initial convolved value
+
+    if ((mask == NULL) || !(mask->data.U8[row][col] & badStampMaskVal)) {
+        // Iterate over the kernel basis functions
+        for (psS32 k = 0; k < kernels->u->n; k++) {
+            conv += IsisKernelConvolve(input, kernels, k, col, row);
+        }
+    }
+
+    psTrace("ImageSubtract.ConvolvePixelIsis", 4,
+            "Exiting ConvolvePixelIsis(%d, %d)\n", col, row);
+    return(conv);
+}
+
+/*******************************************************************************
+ConvolveImage(input, mask, badStampMaskVal, solution, kernels): convolves an
+arbitrary image with either an ISIS or POIS set of kernel basis functions.
+ ******************************************************************************/
+static psImage *ConvolveImage(const psImage *input,
+                              const psImage *mask,
+                              psU32 badStampMaskVal,
+                              const psVector *solution,
+                              const psSubtractionKernels *kernels)
+{
+    psTrace("ImageSubtract.ConvolveImage", 4, "Calling ConvolveImage()\n");
+    PS_ASSERT_IMAGE_NON_NULL(input, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(input, NULL);
+    PS_ASSERT_IMAGE_TYPE(input, PS_TYPE_F32, NULL);
+    if (mask != NULL) {
+        PS_ASSERT_IMAGES_SIZE_EQUAL(input, mask, NULL);
+        PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_U8, NULL);
+    }
+    PS_ASSERT_VECTOR_NON_NULL(solution, NULL);
+    PS_ASSERT_VECTOR_TYPE(solution, PS_TYPE_F64, NULL);
+    PS_ASSERT_PTR_NON_NULL(kernels, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->v, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->xOrder, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->yOrder, NULL);
+    if (kernels->preCalc != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->preCalc, NULL);
+    } else {
+        if (kernels->type != PM_SUBTRACTION_KERNEL_POIS) {
+            psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                    "Unallowable operation: kernels->preCalc == NULL and kernels->type != PM_SUBTRACTION_KERNEL_POIS.\n");
+            return(NULL);
+        }
+    }
+    psS32 nBF = kernels->u->n;
+    PS_ASSERT_VECTOR_SIZE(solution, nBF+1, NULL);
+
+    psS32 numCols = input->numCols;
+    psS32 numRows = input->numRows;
+    psS32 kernelSize = kernels->size;
+
+    psImage *convolved = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+
+    for (psS32 y = kernelSize; y < numRows - kernelSize; y++) {
+        for (psS32 x = kernelSize; x < numCols - kernelSize; x++) {
+            if (kernels->type == PM_SUBTRACTION_KERNEL_POIS) {
+                convolved->data.F32[y][x] = ConvolvePixelPois(input, mask, badStampMaskVal,
+                                            solution, kernels, x, y);
+            } else if (kernels->type == PM_SUBTRACTION_KERNEL_ISIS) {
+                convolved->data.F32[y][x] = ConvolvePixelIsis(input, mask, badStampMaskVal,
+                                            solution, kernels, x, y);
+            } else {
+                psLogMsg(__func__, PS_LOG_WARN, "WARNING: unknown kernel type.  Returning NULL\n");
+                return(NULL);
+            }
+        }
+    }
+
+    //
+    // Pad the rest of the convolved image with 0.0
+    //
+    for (psS32 y = kernelSize; y < numRows - kernelSize; y++) {
+        for (psS32 x = 0; x < kernelSize; x++) {
+            convolved->data.F32[y][x] = 0.0;
+        }
+        for (psS32 x = numCols - kernelSize; x < numCols; x++) {
+            convolved->data.F32[y][x] = 0.0;
+        }
+    }
+    for (psS32 y = 0; y < kernelSize; y++) {
+        for (psS32 x = 0; x < numCols; x++) {
+            convolved->data.F32[y][x] = 0.0;
+        }
+    }
+    for (psS32 y = numRows - kernelSize; y < numRows; y++) {
+        for (psS32 x = 0; x < numCols; x++) {
+            convolved->data.F32[y][x] = 0.0;
+        }
+    }
+
+    psTrace("ImageSubtract.ConvolveImage", 4, "Exiting ConvolveImage()\n");
+    return convolved;
+}
+
+
+
+
+
+/*******************************************************************************
+XXX: We should assert that the (footprint, kernelSize, imageSize) stuff
+ensures that all data is accessed in bounds?
+ ******************************************************************************/
+bool pmSubtractionCalculateEquation(psArray *stamps,          ///< The stamps for which to calculate the equation,
+                                    const psImage *reference, ///< Reference image
+                                    const psImage *input,     ///< Input image
+                                    const psSubtractionKernels *kernels, ///< The kernel basis functions
+                                    psS32 footprint           ///< Half-size of region over which to calculate equation
+                                   )
+{
+    psTrace("ImageSubtract.pmSubtractionCalculateEquation", 3,
+            "Calling pmSubtractionCalculateEquation()\n");
+    PS_ASSERT_PTR_NON_NULL(stamps, false);
+    PS_ASSERT_IMAGE_NON_NULL(reference, false);
+    PS_ASSERT_IMAGE_NON_EMPTY(reference, false);
+    PS_ASSERT_IMAGE_NON_NULL(input, false);
+    PS_ASSERT_IMAGE_NON_EMPTY(input, false);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(reference, input, false);
+    PS_ASSERT_PTR_NON_NULL(kernels, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->v, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->xOrder, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->yOrder, false);
+    if (kernels->type == PM_SUBTRACTION_KERNEL_ISIS) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->preCalc, false);
+    }
+    psS32 kernelSize = kernels->size;
+    PS_ASSERT_INT_NONNEGATIVE(footprint, false);
+    //
+    // For each legitimate stamp, ensure that the footprint is small enough to perform
+    // the full calculation.
+    //
+    // XXX: Verify with IfA that this is a reasonable action.
+    //
+    for (psS32 s = 0; s < stamps->n; s++) {
+        pmStamp *stamp = (pmStamp *) stamps->data[s];
+        if (stamp->status == PM_STAMP_RECALC) {
+            // XXX: trace message
+            // printf("stamp %d (x, y) is (%d, %d).  Footprint is %d.  kernelSize is %d.\n", s, stamp->x, stamp->y, footprint, kernelSize);
+            if (((stamp->y - (footprint + kernelSize)) < 0) ||
+                    ((stamp->x - (footprint + kernelSize)) < 0) ||
+                    ((stamp->y + footprint + kernelSize) >= input->numRows) ||
+                    ((stamp->x + footprint + kernelSize) >= input->numCols)) {
+                stamp->status = PM_STAMP_NONE;
+                psLogMsg(__func__, PS_LOG_WARN,
+                         "WARNING: stamp %d will be ignored.  It exceeds image size: access columns (%d to %d) and rows (%d to %d)\n",
+                         s,
+                         stamp->x - (footprint + kernelSize),
+                         (stamp->x + footprint + kernelSize) - 1,
+                         stamp->y - (footprint + kernelSize),
+                         (stamp->y + footprint + kernelSize) - 1);
+            }
+        }
+    }
+
+    psS32 numHalfRows = reference->numRows;
+    psS32 numHalfCols = reference->numCols;
+    psS32 spatialOrder = kernels->spatialOrder;
+
+    //
+    // The numSolveParams incorporates the additional parameter for the
+    // background value, which we must solve for.
+    //
+    psS32 numKernels = kernels->u->n;
+    int numSolveParams = numKernels + 1;
+    int bgIndex = numKernels;        // Index in matrix for the background
+
+    //
+    // We iterate over each stamp, allocate the matrix and vectors if
+    // necessary, and then calculate those matrix/vectors.
+    //
+    for (psS32 s = 0; s < stamps->n; s++) {
+        pmStamp *stamp = (pmStamp *) stamps->data[s];
+        psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): stamp %d\n", s);
+        if (stamp->status == PM_STAMP_RECALC) {
+            psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): stamp %d: status is PM_STAMP_RECALC.\n", s);
+            psImage *stampMatrix = stamp->matrix;
+            psVector *stampVector = stamp->vector;
+
+            if (stampMatrix == NULL) {
+                stampMatrix = psImageAlloc(numSolveParams, numSolveParams, PS_TYPE_F64);
+                stamp->matrix = stampMatrix;
+            } else {
+                PS_ASSERT_IMAGE_TYPE(stampMatrix, PS_TYPE_F64, false);
+                PS_ASSERT_IMAGE_SIZE(stampMatrix, numSolveParams, numSolveParams, false);
+            }
+            PS_IMAGE_SET_F64(stampMatrix, 0.0);
+
+            if (stampVector == NULL) {
+                stampVector = psVectorAlloc(numSolveParams, PS_TYPE_F64);
+                stamp->vector = stampVector;
+            } else {
+                PS_ASSERT_VECTOR_TYPE(stampVector, PS_TYPE_F64, false);
+                PS_ASSERT_VECTOR_SIZE(stampVector, numSolveParams, false);
+            }
+            PS_VECTOR_SET_F64(stampVector, 0.0);
+            psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): stamp %d: allocate matrix and vector.\n", s);
+
+            //
+            // Evaluate the spatial-order polynomial.  The [i][j]-th element of
+            // the psImage polyValues will hold (x^i * y^j) for the stamp.  The
+            // (x, y) value are scaled to [-1:1]
+            //
+            psImage *polyValues = GenSpatialOrder(spatialOrder,
+                                                  ((psF64) (stamp->x - numHalfCols)) / ((psF64) numHalfCols),
+                                                  ((psF64) (stamp->y - numHalfRows)) / ((psF64) numHalfRows));
+
+            psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): stamp %d: generated spatial order terms.\n", s);
+
+            if (kernels->type == PM_SUBTRACTION_KERNEL_POIS) {
+                //
+                // Iterate over all pixels surrounding this stamp.
+                //
+                for (psS32 y = stamp->y - footprint; y < stamp->y + footprint; y++) {
+                    for (psS32 x = stamp->x - footprint; x < stamp->x + footprint; x++) {
+                        psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): pixel (%d, %d).\n", y, x);
+
+                        // The inverse of the noise, squared.
+                        psF32 invNoise2 = 1.0/reference->data.F32[y][x];
+
+                        //
+                        // Iterate over the first convolution */
+                        //
+                        for (psS32 k1 = 0; k1 < numKernels; k1++) {
+                            psS32 u1 = kernels->u->data.F32[k1];        // Offset in x
+                            psS32 v1 = kernels->v->data.F32[k1];        // Offset in y
+                            psS32 i1 = kernels->xOrder->data.F32[k1];   // Polynomial order in x
+                            psS32 j1 = kernels->yOrder->data.F32[k1];   // Polynomial order in y
+
+                            //
+                            // First convolution.  This will set the value for the stampVector.
+                            //
+                            // XXX: verify the [y-v2][x-u2] subscript.  This generated errors in
+                            // testing, depending on kernel size and footprint.
+                            //
+                            psF32 conv1 = polyValues->data.F64[j1][i1] * reference->data.F32[y - v1][x - u1];
+
+                            //
+                            // Assuming that the first kernel component is 0 order in x and y, and 0 offset
+                            // XXX: I don't understand this:
+                            //
+                            if (k1 != 0) {
+                                conv1 -= reference->data.F32[y][x];
+                            }
+
+                            //
+                            // Iterate over the second convolution
+                            //
+                            for (psS32 k2 = k1; k2 < numKernels; k2++) {
+                                psS32 u2 = (psS32) kernels->u->data.F32[k2];        // Offset in x
+                                psS32 v2 = (psS32) kernels->v->data.F32[k2];        // Offset in y
+                                psS32 i2 = (psS32) kernels->xOrder->data.F32[k2];   // Polynomial order in x
+                                psS32 j2 = (psS32) kernels->yOrder->data.F32[k2];   // Polynomial order in y
+                                //
+                                // XXX: verify the [y-v2][x-u2] subscript.  This generated errors in
+                                // testing, depending on kernel size and footprint.
+                                //
+                                // Second convolution
+                                //
+                                psF32 conv2 = polyValues->data.F64[j2][i2] *
+                                              reference->data.F32[y-v2][x-u2];
+                                //
+                                // Assuming that the first kernel component is 0 order in x and y, and 0 offset
+                                // XXX: I don't understand this:
+                                if (k2 != 0) {
+                                    //
+                                    conv2 -= reference->data.F32[y][x];
+                                }
+
+                                // Add into the matrix element
+                                stampMatrix->data.F64[k1][k2] += conv1 * conv2 * invNoise2;
+
+                            } // Iteration on second convolution
+
+                            // Add into the vector element
+                            stampVector->data.F64[k1] += input->data.F32[y][x] * conv1 * invNoise2;
+
+                            /* Background term */
+                            stampMatrix->data.F64[k1][bgIndex] += conv1 * invNoise2;
+
+                        } // Iteration on first convolution
+
+                        //
+                        // Background only terms.
+                        // XXX: understand this.
+                        //
+                        stampMatrix->data.F64[bgIndex][bgIndex] += invNoise2;
+                        stampVector->data.F64[bgIndex] += input->data.F32[y][x] * invNoise2;
+                    }
+                }
+            } else if (kernels->type == PM_SUBTRACTION_KERNEL_ISIS) {
+                //
+                // Iterate over all pixels surrounding this stamp.
+                //
+                // XXX: Why isn't there a polyValues term here?
+                //
+
+                for (psS32 y = stamp->y - footprint; y < stamp->y + footprint; y++) {
+                    for (psS32 x = stamp->x - footprint; x < stamp->x + footprint; x++) {
+                        psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): pixel (%d, %d).\n", y, x);
+                        psF32 invNoise2 = 1.0/reference->data.F32[y][x]; // The inverse of the noise, squared.
+
+                        for (psS32 k1 = 0; k1 < numKernels; k1++) {
+                            psF32 conv1 = IsisKernelConvolve(reference, kernels, k1, x, y);
+
+                            for (psS32 k2 = k1; k2 < numKernels; k2++) {
+                                //printf("(k1, k2) is (%d, %d)\n", k1, k2);
+                                psF32 conv2 = IsisKernelConvolve(reference, kernels, k2, x, y);
+                                stampMatrix->data.F64[k1][k2]+= conv1 * conv2 * invNoise2;
+                            }
+                            stampVector->data.F64[k1]+= input->data.F32[y][x] * conv1 * invNoise2;
+                            stampMatrix->data.F64[k1][bgIndex] += conv1 * invNoise2;
+                        }
+                        stampMatrix->data.F64[bgIndex][bgIndex] += invNoise2;
+                        stampVector->data.F64[bgIndex] += input->data.F32[y][x] * invNoise2;
+                    }
+                }
+            } else {
+                psLogMsg(__func__, PS_LOG_WARN, "WARNING: unknown kernel->type.\n");
+                return(false);
+            }
+            psFree(polyValues);
+
+            // XXX: Generate psTrace()
+            if (0) {
+                for (psS32 s = 0; s < stamps->n; s++) {
+                    pmStamp *stamp = (pmStamp *) stamps->data[s];
+                    if (stamp->status == PM_STAMP_RECALC) {
+                        psVector *stampVector = stamp->vector;
+                        printf("STAMP: stamp %d vector:\n", s);
+                        PS_VECTOR_PRINT_F64(stampVector);
+                    }
+                }
+            }
+
+            //
+            // Fill in other side of symmetric matrix
+            //
+            // XXX: understand this.
+            // XXX: Why aren't they using numSolveParams instead of numKernels?
+            // XXX: is this POIS specific?
+            //
+            for (psS32 k1 = 0; k1 < numKernels; k1++) {
+                for (psS32 k2 = 0; k2 < k1; k2++) {
+                    stampMatrix->data.F64[k1][k2] = stampMatrix->data.F64[k2][k1];
+                }
+                stampMatrix->data.F64[bgIndex][k1] = stampMatrix->data.F64[k1][bgIndex];
+            }
+
+            //
+            // XXX: Why aren't they using numSolveParams instead of numKernels?
+            // XXX: is this POIS specific?
+            //
+            #define XXX_CONFIG_PENALTY 1.0
+            for (psS32 k = 0; k < numKernels; k++)
+            {
+                psS32 u = kernels->u->data.F32[k];  // Offset in x
+                psS32 v = kernels->v->data.F32[k];  // Offset in y
+                stampMatrix->data.F64[k][k] += XXX_CONFIG_PENALTY * (psF32)(u*u + v*v);
+            }
+            stamp->status = PM_STAMP_USED;
+        } else {
+            // Stamp is ignored since it's not PM_STAMP_RECALC
+        }
+    }
+    psTrace("ImageSubtract.pmSubtractionCalculateEquation", 3,
+            "Exiting pmSubtractionCalculateEquation()\n");
+    return(true);
+}
+
+
+
+
+/*******************************************************************************
+ ******************************************************************************/
+psVector *pmSubtractionSolveEquation(psVector *solution, ///< Solution vector, or NULL
+                                     const psArray *stamps      ///< Array of stamps
+                                    )
+{
+    psTrace("ImageSubtract.pmSubtractionSolveEquation", 3,
+            "Calling pmSubtractionSolveEquation()\n");
+    PS_ASSERT_PTR_NON_NULL(stamps, NULL);
+    psS32 size = -1;
+    psS32 s = 0;
+
+    //
+    // Determine the size of the stamp vectors and matrix.
+    // We iterate until we find the first acceptable stamp.
+    //
+    while ((size == -1) && (s < stamps->n)) {
+        pmStamp *stamp = (pmStamp *) stamps->data[s];
+        PS_ASSERT_PTR_NON_NULL(stamp, NULL);
+        if (stamp->status == PM_STAMP_USED) {
+            size = ((pmStamp *) stamps->data[s])->vector->n;
+            PS_ASSERT_INT_POSITIVE(size, NULL);
+        }
+        s++;
+    }
+    if (size == -1) {
+        psLogMsg(__func__, PS_LOG_WARN, "WARNING: no acceptable stamps.  Returning NULL\n");
+        return(NULL);
+    }
+
+    if (solution != NULL) {
+        PS_ASSERT_VECTOR_TYPE(solution, PS_TYPE_F64, NULL);
+        PS_ASSERT_VECTOR_SIZE(solution, size, NULL);
+    } else {
+        solution = psVectorAlloc(size, PS_TYPE_F64);
+    }
+
+    //
+    // Create the solution matrix and vector.
+    //
+    // XXX: Test these functions with size=-1.  This caused seg faults during test.
+    //      This should be done in the psImage.c and psVector.c test files.  It
+    //      should never occur here.
+    //
+    psImage *sumMatrix = psImageAlloc(size, size, PS_TYPE_F64);
+    psVector *sumVector = psVectorAlloc(size, PS_TYPE_F64);
+    PS_VECTOR_SET_F64(sumVector, 0.0);
+    PS_IMAGE_SET_F64(sumMatrix, 0.0);
+
+    //
+    // Verify that all stamps have similar sizes.
+    // Compute the sum matrix and vector.
+    //
+    for (psS32 s = 0; s < stamps->n; s++) {
+        pmStamp *stamp = (pmStamp *) stamps->data[s];
+
+        if (stamp->status == PM_STAMP_USED) {
+            PS_ASSERT_INT_EQUAL(((pmStamp *) stamps->data[s])->vector->n, size, NULL);
+
+            psImage *stampMatrix = stamp->matrix;
+            psVector *stampVector = stamp->vector;
+            PS_ASSERT_VECTOR_TYPE(stampVector, PS_TYPE_F64, NULL);
+            PS_ASSERT_VECTOR_SIZE(stampVector, size, NULL);
+            PS_ASSERT_IMAGE_TYPE(stampMatrix, PS_TYPE_F64, NULL);
+            PS_ASSERT_IMAGE_SIZE(stampMatrix, size, size, NULL);
+
+            (void)psBinaryOp(sumMatrix, sumMatrix, "+", stampMatrix);
+            (void)psBinaryOp(sumVector, sumVector, "+", stampVector);
+        }
+    }
+    psVector *permutation = NULL;
+    // XXX: Check output from these routines.
+
+    // XXX: psTrace()
+    if (0) {
+        PS_IMAGE_PRINT_F64(sumMatrix);
+    }
+
+    psImage *luMatrix = psMatrixLUD(NULL, &permutation, sumMatrix);
+    if (luMatrix == NULL) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to LU-Decompose the matrix.\n");
+        psFree(sumMatrix);
+        psFree(sumVector);
+        psFree(luMatrix);
+        psFree(permutation);
+        return(NULL);
+    }
+    // XXX: psTrace()
+    if (0) {
+        PS_IMAGE_PRINT_F64(luMatrix);
+    }
+
+    solution = psMatrixLUSolve(solution, luMatrix, sumVector, permutation);
+    // XXX: psTrace()
+    // XXX: should we be checking for NAN's in the solution vector?
+    if (0) {
+        PS_VECTOR_PRINT_F64(solution);
+    }
+    if (solution == NULL) {
+        psError(PS_ERR_UNKNOWN, true, "Failed to solve the matrix.\n");
+        psFree(sumMatrix);
+        psFree(sumVector);
+        psFree(luMatrix);
+        psFree(permutation);
+        return(NULL);
+    }
+
+    psFree(sumMatrix);
+    psFree(sumVector);
+    psFree(luMatrix);
+    psFree(permutation);
+
+    psTrace("ImageSubtract.pmSubtractionSolveEquation", 3,
+            "Exiting pmSubtractionSolveEquation()\n");
+    return(solution);
+}
+
+
+/*******************************************************************************
+ ******************************************************************************/
+static psVector *CalculateDeviations(psVector *deviations,
+                                     psArray *stamps,
+                                     psS32 footprint,
+                                     const psImage *refImage,
+                                     const psImage *inImage,
+                                     const psImage *mask,
+                                     psU32 badStampMaskVal,
+                                     const psSubtractionKernels *kernels,
+                                     const psVector *solution)
+{
+    psTrace("ImageSubtract.CalculateDeviations", 4,
+            "Calling CalculateDeviations()\n");
+    PS_ASSERT_PTR_NON_NULL(stamps, NULL);
+    if (deviations != NULL) {
+        PS_ASSERT_VECTOR_TYPE(deviations, PS_TYPE_F32, NULL);
+        PS_ASSERT_VECTORS_SIZE_EQUAL(deviations, stamps, NULL);
+    } else {
+        deviations = psVectorAlloc(stamps->n, PS_TYPE_F32);
+        // XXX: Probably not necessary.
+        PS_VECTOR_SET_F32(deviations, 0.0);
+    }
+    PS_ASSERT_IMAGE_NON_NULL(refImage, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(refImage, NULL);
+    PS_ASSERT_IMAGE_TYPE(refImage, PS_TYPE_F32, NULL);
+    PS_ASSERT_IMAGE_NON_NULL(inImage, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(inImage, NULL);
+    PS_ASSERT_IMAGE_TYPE(inImage, PS_TYPE_F32, NULL);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(refImage, inImage, NULL);
+    PS_ASSERT_IMAGE_NON_NULL(mask, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(mask, NULL);
+    PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_U8, NULL);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(refImage, mask, NULL);
+    PS_ASSERT_VECTOR_NON_NULL(solution, NULL);
+    PS_ASSERT_VECTOR_TYPE(solution, PS_TYPE_F64, NULL);
+    PS_ASSERT_PTR_NON_NULL(kernels, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->v, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->xOrder, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->yOrder, NULL);
+    if (kernels->preCalc != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->preCalc, NULL);
+    } else {
+        if (kernels->type != PM_SUBTRACTION_KERNEL_POIS) {
+            psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                    "Unallowable operation: kernels->preCalc == NULL and kernels->type != PM_SUBTRACTION_KERNEL_POIS.\n");
+            return(NULL);
+        }
+    }
+    psS32 nBF = kernels->u->n;
+    PS_ASSERT_VECTOR_SIZE(solution, nBF+1, NULL);
+
+    psS32 kernelSize = kernels->size;
+    int xSize = footprint + kernelSize;
+    int ySize = footprint + kernelSize;
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN); // Statistics
+    psImage *subStamp = psImageAlloc(2 * xSize, 2 * ySize, PS_TYPE_F32); // Subtraction of stamp
+    for (psS32 s = 0; s < stamps->n; s++) {
+        pmStamp *stamp = stamps->data[s]; // The coordinates of the stamp of interest
+        psS32 x = stamp->x;               // Stamp x coord
+        psS32 y = stamp->y;               // Stamp y coord
+        if (stamp->status == PM_STAMP_USED) {
+
+            psRegion myReg = psRegionSet(x - xSize, x + xSize, y - ySize, y + ySize);
+            psImage *refStamp = psImageSubset((psImage *) refImage, myReg);
+            psImage *inStamp = psImageSubset((psImage *) inImage, myReg);
+            psImage *maskStamp = psImageSubset((psImage *) mask, myReg);
+            psImage *convRefStamp = ConvolveImage(refStamp, maskStamp, badStampMaskVal, solution, kernels);
+
+            // Calculate chi^2
+            (void)psBinaryOp(subStamp, inStamp, "-", convRefStamp);
+            (void)psBinaryOp(subStamp, subStamp, "/", inStamp);
+            (void)psBinaryOp(subStamp, subStamp, "*", subStamp);
+            myReg = psRegionSet(kernelSize, kernelSize + 2 * footprint,
+                                kernelSize, kernelSize + 2 * footprint);
+            psImage *subStampTrim = psImageSubset((psImage *) subStamp, myReg);
+            psImage *maskStampTrim = psImageSubset((psImage *) maskStamp, myReg);
+            psImageStats(stats, subStampTrim, maskStampTrim, badStampMaskVal);
+
+            deviations->data.F32[s] = stats->sampleMean * (psF32)footprint * (psF32)footprint * 4.0;
+            // XXX: Allocate and free these elsewhere.
+            psFree(refStamp);
+            psFree(inStamp);
+            psFree(maskStamp);
+            psFree(convRefStamp);
+            psFree(subStampTrim);
+            psFree(maskStampTrim);
+        }
+    }
+
+    psFree(stats);
+    psFree(subStamp);
+
+    psTrace("ImageSubtract.CalculateDeviations", 4,
+            "Exiting CalculateDeviations()\n");
+    return deviations;
+}
+
+/*******************************************************************************
+ ******************************************************************************/
+bool pmSubtractionRejectStamps(psArray *stamps,  ///< Array of stamps to check for rejection
+                               psImage *mask,  ///< Mask image
+                               psU32 badStampMaskVal, ///< Value to use in mask for bad stamp
+                               psS32 footprint,  ///< Region to mask if stamp is bad
+                               psF32 sigmaRej,  ///< Number of RMS deviations above zero at which to reject
+                               const psImage *refImage, ///< Reference image
+                               const psImage *inImage, ///< Input image
+                               const psVector *solution, ///< Solution vector
+                               const psSubtractionKernels *kernels ///< Array of kernel parameters
+                              )
+{
+    psTrace("ImageSubtract.pmSubtractionRejectStamps", 3,
+            "Calling pmSubtractionRejectStamps()\n");
+    PS_ASSERT_PTR_NON_NULL(stamps, false);
+    PS_ASSERT_IMAGE_NON_NULL(refImage, false);
+    PS_ASSERT_IMAGE_NON_EMPTY(refImage, false);
+    PS_ASSERT_IMAGE_TYPE(refImage, PS_TYPE_F32, false);
+    PS_ASSERT_IMAGE_NON_NULL(inImage, false);
+    PS_ASSERT_IMAGE_NON_EMPTY(inImage, false);
+    PS_ASSERT_IMAGE_TYPE(inImage, PS_TYPE_F32, false);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(refImage, inImage, false);
+    PS_ASSERT_IMAGE_NON_NULL(mask, false);
+    PS_ASSERT_IMAGE_NON_EMPTY(mask, false);
+    PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_U8, false);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(refImage, mask, false);
+    PS_ASSERT_VECTOR_NON_NULL(solution, false);
+    PS_ASSERT_VECTOR_TYPE(solution, PS_TYPE_F64, false);
+    PS_ASSERT_PTR_NON_NULL(kernels, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->v, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->xOrder, false);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->yOrder, false);
+    if (kernels->preCalc != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->preCalc, false);
+    } else {
+        if (kernels->type != PM_SUBTRACTION_KERNEL_POIS) {
+            psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                    "Unallowable operation: kernels->preCalc == NULL and kernels->type != PM_SUBTRACTION_KERNEL_POIS.\n");
+            return(false);
+        }
+    }
+
+    psS32 nBF = kernels->u->n;
+    PS_ASSERT_VECTOR_SIZE(solution, nBF+1, false);
+
+    psVector *deviations = CalculateDeviations(NULL,
+                           stamps,
+                           footprint,
+                           refImage,
+                           inImage,
+                           mask,
+                           badStampMaskVal,
+                           kernels,
+                           solution);
+    //
+    // Calculate the deviation from zero.
+    //
+    psF64 meanDev = 0.0;
+    psS32 numDev = 0;
+    for (psS32 i = 0; i < deviations->n; i++) {
+        pmStamp *stamp = stamps->data[i];
+        if (stamp->status == PM_STAMP_USED) {
+            meanDev += PS_SQR(deviations->data.F32[i]);
+            numDev++;
+        }
+    }
+    psF32 rmsDev = sqrtf(meanDev / (psF64)(numDev - 1));
+    psF32 limit = rmsDev * sigmaRej;
+
+    for (psS32 s = 0; s < stamps->n; s++) {
+        pmStamp *stamp = (pmStamp *) stamps->data[s];
+        if (stamp->status == PM_STAMP_USED && fabsf(deviations->data.F32[s]) > limit) {
+            // Mask out the stamp in the image so you don't find it again
+            for (psS32 y = stamp->y - footprint; y < stamp->y + footprint; y++) {
+                for (psS32 x = stamp->x - footprint; x < stamp->x + footprint; x++) {
+                    mask->data.U8[y][x] |= badStampMaskVal;
+                }
+            }
+
+            // Set stamp for replacement
+            stamp->x = 0;
+            stamp->y = 0;
+            stamp->status = PM_STAMP_REJECTED;
+        }
+    }
+
+    psFree(deviations);
+    psTrace("ImageSubtract.pmSubtractionRejectStamps", 3,
+            "Exiting pmSubtractionRejectStamps()\n");
+    return(true);
+}
+
+/*******************************************************************************
+ ******************************************************************************/
+psImage *pmSubtractionKernelImage(psImage *out,
+                                  const psVector *solution,
+                                  const psSubtractionKernels *kernels,
+                                  psF32 x,
+                                  psF32 y
+                                 )
+{
+    psTrace("ImageSubtract.pmSubtractionKernelImage", 3,
+            "Calling pmSubtractionKernelImage()\n");
+    PS_ASSERT_VECTOR_NON_NULL(solution, NULL);
+    PS_ASSERT_PTR_NON_NULL(kernels, NULL);
+    PS_ASSERT_FLOAT_WITHIN_RANGE(x, -1.0, 1.0, NULL);
+    PS_ASSERT_FLOAT_WITHIN_RANGE(y, -1.0, 1.0, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->v, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->xOrder, NULL);
+    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->yOrder, NULL);
+    if (kernels->preCalc != NULL) {
+        PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->preCalc, NULL);
+    } else {
+        if (kernels->type != PM_SUBTRACTION_KERNEL_POIS) {
+            psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                    "Unallowable operation: kernels->preCalc == NULL and kernels->type != PM_SUBTRACTION_KERNEL_POIS.\n");
+            return(NULL);
+        }
+    }
+    PS_ASSERT_INT_EQUAL(1+kernels->u->n, solution->n, NULL);
+
+    psS32 nBF = kernels->u->n;
+    psS32 spatialOrder = kernels->spatialOrder;
+    psS32 kernelSize = kernels->size;
+
+    if (out != NULL) {
+        if ((out->numCols < (1+2*kernelSize)) || (out->numRows < (1+2*kernelSize))) {
+            psLogMsg(__func__, PS_LOG_WARN, "WARNING: out image is not large enough.\n");
+            return(out);
+        }
+    } else {
+        out = psImageAlloc(1+2*kernelSize, 1+2*kernelSize, PS_TYPE_F32);
+    }
+    PS_IMAGE_SET_F32(out, 0.0);
+
+    //
+    // Generate the spatial-order polynomial.  The [i][j]-th element of
+    // the psImage polyValues will hold (x^i * y^j) for the stamp.
+    //
+    psImage *polyValues = GenSpatialOrder(spatialOrder, x, y);
+
+    // XXX: switch (i, j) so they correspond to (x, y).
+    if (kernels->type == PM_SUBTRACTION_KERNEL_ISIS) {
+        for (psS32 k = 0 ; k < nBF ; k++) {
+            psS32 xOrder = (psS32) kernels->xOrder->data.F32[k];
+            psS32 yOrder = (psS32) kernels->yOrder->data.F32[k];
+            psF64 polyVal = polyValues->data.F64[yOrder][xOrder];
+
+            // XXX: Verify that this is correct.
+            for (psS32 i = -kernelSize ; i <= kernelSize ; i++) {
+                for (psS32 j = -kernelSize ; j <= kernelSize ; j++) {
+                    psImage *preCalc = (psImage *) kernels->preCalc->data[k];
+                    out->data.F32[i+kernelSize][j+kernelSize]+=
+                        solution->data.F64[k] *
+                        preCalc->data.F32[i+kernelSize][j+kernelSize] *
+                        polyVal;
+                }
+            }
+        }
+    } else if (kernels->type == PM_SUBTRACTION_KERNEL_POIS) {
+        for (psS32 k = 0 ; k < nBF ; k++) {
+            // XXX: Why don't we have compilation warnings on type here (if
+            // we remove the (psS32) cast)?
+            psS32 u = (psS32) kernels->u->data.F32[k];
+            psS32 v = (psS32) kernels->v->data.F32[k];
+            psS32 xOrder = (psS32) kernels->xOrder->data.F32[k];
+            psS32 yOrder = (psS32) kernels->yOrder->data.F32[k];
+            // XXX: Verify that this is correct.
+
+            out->data.F32[kernelSize - v][kernelSize - u]+=
+                solution->data.F64[k] * polyValues->data.F64[yOrder][xOrder];
+        }
+    }
+    psFree(polyValues);
+
+    psTrace("ImageSubtract.pmSubtractionKernelImage", 3,
+            "Exiting pmSubtractionKernelImage()\n");
+    return(out);
+}
Index: /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/pmImageSubtract.h
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/pmImageSubtract.h	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/pmImageSubtract.h	(revision 22115)
@@ -0,0 +1,127 @@
+/** @file  ImageSubtract.h
+ *
+ *  This file will contain code which creates a set of kernel basis
+ *  functions, solves for their solution, and applies them to an image.
+ *
+ *  @author Paul Price, IfA (original prototype)
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#if !defined(PM_IMAGE_COMBINE_H)
+#define PM_IMAGE_COMBINE_H
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include<stdio.h>
+#include<math.h>
+#include "pslib.h"
+#include "psConstants.h"
+
+typedef enum {
+    PM_SUBTRACTION_KERNEL_POIS,         ///< POIS kernel --- delta functions
+    PM_SUBTRACTION_KERNEL_ISIS          ///< ISIS kernel --- gaussians modified by polynomials
+} pmSubtractionKernelsType;
+
+typedef struct
+{
+    pmSubtractionKernelsType type;      ///< Type ofKernels --- allowing the use of multiple kernels
+    psVector *u, *v;                    ///< Offset (for POIS) or polynomial order (for ISIS)
+    psVector *sigma;                    ///< Width of Gaussian (for ISIS)
+    psVector *xOrder, *yOrder;          ///< Spatial Polynomial order (for all)
+    int subIndex;                       ///< Index of kernel to be subtracted to maintain flux conservation
+    psArray *preCalc;                   ///< Array of images containing pre-calculated kernel (to
+    ///< accelerate ISIS; don't use for POIS)
+    psS32 size;                         ///< The halfsize of the kernel
+    psS32 spatialOrder;                 ///< The spatial order of the kernels
+}
+psSubtractionKernels;
+
+psSubtractionKernels *pmSubtractionKernelsAllocPOIS(
+    int size,
+    int SpatialOrder
+);
+
+psSubtractionKernels *pmSubtractionKernelsAllocISIS(
+    const psVector *sigmas,
+    const psVector *orders,
+    int size,
+    int SpatialOrder
+);
+
+typedef enum {
+    PM_STAMP_INIT,                      ///< Initial state
+    PM_STAMP_USED,                      ///< Use this stamp
+    PM_STAMP_REJECTED,                  ///< This stamp has been rejected
+    PM_STAMP_RECALC,                    ///< Having been reset, this stamp is to be recalculated
+    PM_STAMP_NONE                       ///< No stamp in this region
+} pmStampStatus;
+
+typedef struct
+{
+    int x, y;                           ///< Position
+    int p_xSize;
+    int p_ySize;
+    int p_xMin;
+    int p_xMax;
+    int p_yMin;
+    int p_yMax;
+    psImage *matrix;                    ///< Associated matrix
+    psVector *vector;                   ///< Assoicated vector
+    pmStampStatus status;               ///< Status ofstamp
+}
+pmStamp;
+
+psArray *pmSubtractionFindStamps(
+    psArray *stamps,                    ///< Output stamps, or NULL
+    const psImage *image,               ///< Image for which to find stamps
+    const psImage *mask,                ///< Mask
+    psU32 maskVal,                      ///< Value for mask
+    psF32 threshold,                    ///< Threshold for stamps in the image
+    psS32 xNum,                         ///< Number of stamps in x
+    psS32 yNum,                         ///< Number of stamps in y
+    psS32 border                        ///< Border around image to ignore (should be size of kernel)
+);
+
+bool pmSubtractionCalculateEquation(
+    psArray *stamps,                    ///< The stamps for which to calculate the equation,
+    const psImage *reference,           ///< Reference image
+    const psImage *input,               ///< Input image
+    const psSubtractionKernels *kernels,///< The kernel basis functions
+    psS32 footprint                     ///< Half-size of region over which to calculate equation
+);
+
+
+psVector *pmSubtractionSolveEquation(
+    psVector *solution,                 ///< Solution vector, or NULL
+    const psArray *stamps               ///< Array of stamps
+);
+
+bool pmSubtractionRejectStamps(
+    psArray *stamps,                    ///< Array of stamps to check for rejection
+    psImage *mask,                      ///< Mask image
+    psU32 badStampMaskVal,              ///< Value to use in mask for bad stamp
+    psS32 footprint,                    ///< Region to mask if stamp is bad
+    psF32 sigmaRej,                     ///< Number of RMS deviations above zero at which to reject
+    const psImage *refImage,            ///< Reference image
+    const psImage *inImage,             ///< Input image
+    const psVector *solution,           ///< Solution vector
+    const psSubtractionKernels *kernels ///< Array of kernel parameters
+);
+
+psImage *pmSubtractionKernelImage(
+    psImage *out,
+    const psVector *solution,
+    const psSubtractionKernels *kernels,
+    psF32 x,
+    psF32 y
+);
+
+#endif
Index: /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/pmSubtractBias.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/pmSubtractBias.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/pmSubtractBias.c	(revision 22115)
@@ -0,0 +1,686 @@
+/** @file  pmSubtractBias.c
+ *
+ *  This file will contain a module which will subtract the detector bias
+ *  in place from an input image.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-10-12 21:02:04 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "pmSubtractBias.h"
+
+#define PM_SUBTRACT_BIAS_POLYNOMIAL_ORDER 2
+#define PM_SUBTRACT_BIAS_SPLINE_ORDER 3
+
+// XXX: put these in psConstants.h
+void PS_POLY1D_PRINT(psPolynomial1D *poly)
+{
+    printf("-------------- PS_POLY1D_PRINT() --------------\n");
+    printf("poly->nX is %d\n", poly->nX);
+    for (psS32 i = 0 ; i < (1 + poly->nX) ; i++) {
+        printf("poly->coeff[%d] is %f\n", i, poly->coeff[i]);
+    }
+}
+
+void PS_PRINT_SPLINE(psSpline1D *mySpline)
+{
+    printf("-------------- PS_PRINT_SPLINE() --------------\n");
+    printf("mySpline->n is %d\n", mySpline->n);
+    for (psS32 i = 0 ; i < mySpline->n ; i++) {
+        PS_POLY1D_PRINT(mySpline->spline[i]);
+    }
+    PS_VECTOR_PRINT_F32(mySpline->knots);
+}
+
+#define PS_IMAGE_PRINT_F32_HIDEF(NAME) \
+printf("======== printing %s ========\n", #NAME); \
+for (int i = 0 ; i < (NAME)->numRows ; i++) { \
+    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
+        printf("%.5f ", (NAME)->data.F32[i][j]); \
+    } \
+    printf("\n"); \
+}\
+
+/******************************************************************************
+psSubtractFrame(): this routine will take as input a readout for the input
+image and a readout for the bias image.  The bias image is subtracted in
+place from the input image.
+*****************************************************************************/
+static pmReadout *SubtractFrame(pmReadout *in,
+                                const pmReadout *bias)
+{
+    psS32 i;
+    psS32 j;
+
+    if (bias == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: pmSubtractBias.c: SubtractFrame(): bias frame is NULL.  Returning original image.\n");
+        return(in);
+    }
+
+
+    if ((in->image->numRows + in->row0 - bias->row0) > bias->image->numRows) {
+        psError(PS_ERR_UNKNOWN,true, "bias image does not have enough rows.  Returning in image\n");
+        return(in);
+    }
+    if ((in->image->numCols + in->col0 - bias->col0) > bias->image->numCols) {
+        psError(PS_ERR_UNKNOWN,true, "bias image does not have enough columns.  Returning in image\n");
+        return(in);
+    }
+
+    for (i=0;i<in->image->numRows;i++) {
+        for (j=0;j<in->image->numCols;j++) {
+            in->image->data.F32[i][j]-=
+                bias->image->data.F32[i+in->row0-bias->row0][j+in->col0-bias->col0];
+            if ((in->mask != NULL) && (bias->mask != NULL)) {
+                (in->mask->data.U8[i][j])|=
+                    bias->mask->data.U8[i+in->row0-bias->row0][j+in->col0-bias->col0];
+            }
+        }
+    }
+
+    return(in);
+}
+
+/******************************************************************************
+ImageSubtractScalar(): subtract a scalar from the input image.
+ 
+XXX: Use a psLib function for this.
+ 
+XXX: This should
+ *****************************************************************************/
+static psImage *ImageSubtractScalar(psImage *image,
+                                    psF32 scalar)
+{
+    for (psS32 i=0;i<image->numRows;i++) {
+        for (psS32 j=0;j<image->numCols;j++) {
+            image->data.F32[i][j]-= scalar;
+        }
+    }
+    return(image);
+}
+
+/******************************************************************************
+GenNewStatOptions(): this routine will take as input the options member of the
+stat data structure, determine if multiple options have been specified, issue
+a warning message if so, and return the highest priority option (according to
+the order of the if-statements in this code).  The higher priority options are
+listed lower in the code.
+ *****************************************************************************/
+static psStatsOptions GenNewStatOptions(const psStats *stat)
+{
+    psS32 numOptions = 0;
+    psStatsOptions opt = 0;
+
+    if (stat->options & PS_STAT_ROBUST_MODE) {
+        if (numOptions == 0) {
+            opt = PS_STAT_ROBUST_MODE;
+        }
+        numOptions++;
+    }
+
+    if (stat->options & PS_STAT_ROBUST_MEDIAN) {
+        if (numOptions == 0) {
+            opt = PS_STAT_ROBUST_MEDIAN;
+        }
+        numOptions++;
+    }
+
+    if (stat->options & PS_STAT_ROBUST_MEAN) {
+        if (numOptions == 0) {
+            opt = PS_STAT_ROBUST_MEAN;
+        }
+        numOptions++;
+    }
+
+    if (stat->options & PS_STAT_CLIPPED_MEAN) {
+        if (numOptions == 0) {
+            opt = PS_STAT_CLIPPED_MEAN;
+        }
+        numOptions++;
+    }
+
+    if (stat->options & PS_STAT_SAMPLE_MEDIAN) {
+        if (numOptions == 0) {
+            opt = PS_STAT_SAMPLE_MEDIAN;
+        }
+        numOptions++;
+    }
+
+    if (stat->options & PS_STAT_SAMPLE_MEAN) {
+        numOptions++;
+        opt = PS_STAT_SAMPLE_MEAN;
+    }
+
+
+    if (numOptions == 0) {
+        psError(PS_ERR_UNKNOWN,true, "No statistics options have been specified.\n");
+    }
+    if (numOptions != 1) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: pmSubtractBias.c: GenNewStatOptions(): Too many statistics options have been specified\n");
+    }
+    return(opt);
+}
+
+
+
+/******************************************************************************
+ScaleOverscanVector(): this routine takes as input an arbitrary vector,
+creates a new vector of length n, and fills the new vector with the
+interpolated values of the old vector.  The type of interpolation is:
+    PM_FIT_POLYNOMIAL: fit a polynomial to the entire input vector data.
+    PM_FIT_SPLINE: fit splines to the input vector data.
+XXX: Doesn't it make more sense to do polynomial interpolation on a few
+elements of the input vector, rather than fit a polynomial to the entire
+vector?
+ *****************************************************************************/
+static psVector *ScaleOverscanVector(psVector *overscanVector,
+                                     psS32 n,
+                                     void *fitSpec,
+                                     pmFit fit)
+{
+    psTrace(".psModule.pmSubtracBias.ScaleOverscanVector", 4,
+            "---- ScaleOverscanVector() begin (%d -> %d) ----\n", overscanVector->n, n);
+    //    PS_VECTOR_PRINT_F32(overscanVector);
+
+    if (NULL == overscanVector) {
+        return(overscanVector);
+    }
+
+    // Allocate the new vector.
+    psVector *newVec = psVectorAlloc(n, PS_TYPE_F32);
+
+    //
+    // If the new vector is the same size as the old, simply copy the data.
+    //
+    if (n == overscanVector->n) {
+        for (psS32 i = 0 ; i < n ; i++) {
+            newVec->data.F32[i] = overscanVector->data.F32[i];
+        }
+        return(newVec);
+    }
+    psPolynomial1D *myPoly;
+    psSpline1D *mySpline;
+    psF32 x;
+    psS32 i;
+
+    if (fit == PM_FIT_POLYNOMIAL) {
+        // Fit a polynomial to the old overscan vector.
+        myPoly = (psPolynomial1D *) fitSpec;
+        PS_ASSERT_POLY_NON_NULL(myPoly, NULL);
+        myPoly = psVectorFitPolynomial1D(myPoly, NULL, 0, overscanVector, NULL, NULL);
+        if (myPoly == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector()(1): Could not fit a polynomial to the psVector.\n");
+            return(NULL);
+        }
+
+        // For each element of the new vector, convert the x-ordinate to that
+        // of the old vector, use the fitted polynomial to determine the
+        // interpolated value at that point, and set the new vector.
+        for (i=0;i<n;i++) {
+            x = ((psF32) i) * ((psF32) overscanVector->n) / ((psF32) n);
+            newVec->data.F32[i] = psPolynomial1DEval(myPoly, x);
+        }
+    } else if (fit == PM_FIT_SPLINE) {
+        psS32 mustFreeSpline = 0;
+        // Fit a spline to the old overscan vector.
+        mySpline = (psSpline1D *) fitSpec;
+        // 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,
+        // splines endpoints will be from 0.0 to overscanVector->n-1.  Must scale
+        // properly when doing the spline eval.
+        //
+        //        mySpline = psVectorFitSpline1D(mySpline, NULL, overscanVector, NULL);
+        mySpline = psVectorFitSpline1D(NULL, overscanVector);
+        if (mySpline == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector()(2): Could not fit a spline to the psVector.\n");
+            return(NULL);
+        }
+        //        PS_PRINT_SPLINE(mySpline);
+
+        // For each element of the new vector, convert the x-ordinate to that
+        // of the old vector, use the fitted polynomial to determine the
+        // interpolated value at that point, and set the new vector.
+        for (i=0;i<n;i++) {
+            // Scale to [0 : overscanVector->n - 1]
+            x = ((psF32) i) * ((psF32) (overscanVector->n-1)) / ((psF32) n);
+            newVec->data.F32[i] = psSpline1DEval(mySpline, x);
+        }
+        if (mustFreeSpline ==1) {
+            psFree(mySpline);
+        }
+        //        PS_VECTOR_PRINT_F32(newVec);
+
+
+    } else {
+        psError(PS_ERR_UNKNOWN, true, "unknown fit type.  Returning NULL.\n");
+        psFree(newVec);
+        return(NULL);
+    }
+
+    psTrace(".psModule.pmSubtracBias.ScaleOverscanVector", 4,
+            "---- ScaleOverscanVector() exit ----\n");
+    return(newVec);
+}
+
+/******************************************************************************
+XXX: The SDRS does not specify type support.  F32 is implemented here.
+ *****************************************************************************/
+pmReadout *pmSubtractBias(pmReadout *in,
+                          void *fitSpec,
+                          const psList *overscans,
+                          pmOverscanAxis overScanAxis,
+                          psStats *stat,
+                          psS32 nBinOrig,
+                          pmFit fit,
+                          const pmReadout *bias)
+{
+    psTrace(".psModule.pmSubtracBias.pmSubtractBias", 4,
+            "---- pmSubtractBias() begin ----\n");
+    PS_ASSERT_READOUT_NON_NULL(in, NULL);
+    PS_ASSERT_READOUT_NON_EMPTY(in, NULL);
+    PS_ASSERT_READOUT_TYPE(in, PS_TYPE_F32, NULL);
+
+    //
+    // If the overscans != NULL, then check the type of each image.
+    //
+    if (overscans != NULL) {
+        psListElem *tmpOverscan = (psListElem *) overscans->head;
+        while (NULL != tmpOverscan) {
+            psImage *myOverscanImage = (psImage *) tmpOverscan->data;
+            PS_ASSERT_IMAGE_TYPE(myOverscanImage, PS_TYPE_F32, NULL);
+            tmpOverscan = tmpOverscan->next;
+        }
+    }
+
+    if ((overscans == NULL) && (overScanAxis != PM_OVERSCAN_NONE)) {
+        psError(PS_ERR_UNKNOWN,true, "(overscans == NULL) && (overScanAxis != PM_OVERSCAN_NONE).  Returning in image\n");
+        return(in);
+    }
+
+    // Check for an unallowable pmFit.
+    if ((fit != PM_OVERSCAN_NONE) &&
+            (fit != PM_OVERSCAN_ROWS) &&
+            (fit != PM_OVERSCAN_COLUMNS) &&
+            (fit != PM_OVERSCAN_ALL)) {
+        psError(PS_ERR_UNKNOWN, true, "fit is unallowable (%d).  Returning in image.\n", fit);
+        return(in);
+    }
+    // Check for an unallowable pmOverscanAxis.
+    if ((overScanAxis != PM_OVERSCAN_NONE) &&
+            (overScanAxis != PM_OVERSCAN_ROWS) &&
+            (overScanAxis != PM_OVERSCAN_COLUMNS) &&
+            (overScanAxis != PM_OVERSCAN_ALL)) {
+        psError(PS_ERR_UNKNOWN, true, "overScanAxis is unallowable (%d).  Returning in image.\n", overScanAxis);
+        return(in);
+    }
+    psS32 i;
+    psS32 j;
+    psS32 numBins = 0;
+    static psVector *overscanVector = NULL;
+    psVector *tmpRow = NULL;
+    psVector *tmpCol = NULL;
+    psVector *myBin = NULL;
+    psVector *binVec = NULL;
+    psListElem *tmpOverscan = NULL;
+    double statValue;
+    psImage *myOverscanImage = NULL;
+    psPolynomial1D *myPoly = NULL;
+    psSpline1D *mySpline = NULL;
+    psS32 nBin;
+
+    //
+    //  Create a static stats data structure and determine the highest
+    //  priority stats option.
+    //
+    static psStats *myStats = NULL;
+    if (myStats == NULL) {
+        myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        p_psMemSetPersistent(myStats, true);
+    }
+    if (stat != NULL) {
+        myStats->options = GenNewStatOptions(stat);
+    }
+
+
+    if (overScanAxis == PM_OVERSCAN_NONE) {
+        if (fit != PM_FIT_NONE) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: pmSubtractBias.(): overScanAxis equals NONE, and fit does not equal NONE.  Proceeding to full fram subtraction.\n");
+        }
+
+        if (overscans != NULL) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: pmSubtractBias.(): overScanAxis equals NONE and overscans does not equal NULL.  Proceeding to full fram subtraction.\n");
+        }
+        return(SubtractFrame(in, bias));
+    }
+
+    if ((overScanAxis == PM_OVERSCAN_ALL) && (fit != PM_FIT_NONE)) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: pmSubtractBias.(): overScanAxis equals ALL, and fit does not equal NONE.  Proceeding with the rest of the module.\n");
+    }
+
+
+    //
+    // We subtract each overscan region from the image data.
+    // If we get here we know that overscans != NULL.
+    //
+
+    if (overScanAxis == PM_OVERSCAN_ALL) {
+        tmpOverscan = (psListElem *) overscans->head;
+        while (NULL != tmpOverscan) {
+            myOverscanImage = (psImage *) tmpOverscan->data;
+
+            PS_ASSERT_IMAGE_TYPE(myOverscanImage, PS_TYPE_F32, NULL);
+            psStats *rc = psImageStats(myStats, myOverscanImage, NULL, (psMaskType)0xffffffff);
+            if (rc == NULL) {
+                psError(PS_ERR_UNKNOWN, false, "psImageStats(): could not perform requested statistical operation.  Returning in image.\n");
+                return(in);
+            }
+            if (false == p_psGetStatValue(myStats, &statValue)) {
+                psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
+                return(in);
+            }
+            ImageSubtractScalar(in->image, statValue);
+
+            tmpOverscan = tmpOverscan->next;
+        }
+        return(in);
+    }
+
+    // This check is redundant with above code.
+    if (!((overScanAxis == PM_OVERSCAN_ROWS) || (overScanAxis == PM_OVERSCAN_COLUMNS))) {
+        psError(PS_ERR_UNKNOWN, true, "overScanAxis is unallowable (%d).\nReturning in image.\n", overScanAxis);
+        return(in);
+    }
+
+    tmpOverscan = (psListElem *) overscans->head;
+    while (NULL != tmpOverscan) {
+        //        PS_IMAGE_PRINT_F32_HIDEF(in->image);
+        myOverscanImage = (psImage *) tmpOverscan->data;
+
+        if (overScanAxis == PM_OVERSCAN_ROWS) {
+            if (myOverscanImage->numCols != (in->image)->numCols) {
+                psLogMsg(__func__, PS_LOG_WARN,
+                         "WARNING: pmSubtractBias.(): overscan image has %d columns, input image has %d columns\n",
+                         myOverscanImage->numCols, in->image->numCols);
+            }
+
+            // We create a row vector and subtract this vector from image.
+            // XXX: Is there a better way to extract a psVector from a psImage without
+            // having to copy every element in that vector?
+            overscanVector = psVectorAlloc(myOverscanImage->numCols, PS_TYPE_F32);
+            for (i=0;i<overscanVector->n;i++) {
+                overscanVector->data.F32[i] = 0.0;
+            }
+            tmpRow = psVectorAlloc(myOverscanImage->numRows, PS_TYPE_F32);
+
+            // For each column of the input image, loop through every row,
+            // collect the pixel in that row, then performed the specified
+            // statistical op on those pixels.  Store this in overscanVector.
+            for (i=0;i<myOverscanImage->numCols;i++) {
+                for (j=0;j<myOverscanImage->numRows;j++) {
+                    tmpRow->data.F32[j] = myOverscanImage->data.F32[j][i];
+                }
+                psStats *rc = psVectorStats(myStats, tmpRow, NULL, NULL, 0);
+                if (rc == NULL) {
+                    psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning in image.\n");
+                    return(in);
+                }
+                if (false ==  p_psGetStatValue(rc, &statValue)) {
+                    psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
+                    return(in);
+                }
+                overscanVector->data.F32[i] = statValue;
+            }
+            psFree(tmpRow);
+
+            // Scale the overscan vector to the size of the input image.
+            if (overscanVector->n != in->image->numCols) {
+                if ((fit == PM_FIT_POLYNOMIAL) || (fit == PM_FIT_SPLINE)) {
+                    psVector *newVec = ScaleOverscanVector(overscanVector,
+                                                           in->image->numCols,
+                                                           fitSpec, fit);
+                    if (newVec == NULL) {
+                        psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector(): could not scale the overscan vector.  Returning in image.\n");
+                        return(in);
+                    }
+                    psFree(overscanVector);
+                    overscanVector = newVec;
+                } else {
+                    psError(PS_ERR_UNKNOWN, true, "Don't know how to scale the overscan vector.  Set fit to PM_FIT_SPLINE or PM_FIT_POLYNOMIAL.  Returning in image.\n");
+                    psFree(overscanVector);
+                    return(in);
+                }
+            }
+        }
+
+        if (overScanAxis == PM_OVERSCAN_COLUMNS) {
+            if (myOverscanImage->numRows != (in->image)->numRows) {
+                psLogMsg(__func__, PS_LOG_WARN,
+                         "WARNING: pmSubtractBias.(): overscan image has %d rows, input image has %d rows\n",
+                         myOverscanImage->numRows, in->image->numRows);
+            }
+
+            // We create a column vector and subtract this vector from image.
+            overscanVector = psVectorAlloc(myOverscanImage->numRows, PS_TYPE_F32);
+            for (i=0;i<overscanVector->n;i++) {
+                overscanVector->data.F32[i] = 0.0;
+            }
+            tmpCol = psVectorAlloc(myOverscanImage->numCols, PS_TYPE_F32);
+
+            // For each row of the input image, loop through every column,
+            // collect the pixel in that row, then performed the specified
+            // statistical op on those pixels.  Store this in overscanVector.
+            for (i=0;i<myOverscanImage->numRows;i++) {
+                for (j=0;j<myOverscanImage->numCols;j++) {
+                    tmpCol->data.F32[j] = myOverscanImage->data.F32[i][j];
+                }
+                psStats *rc = psVectorStats(myStats, tmpCol, NULL, NULL, 0);
+                if (rc == NULL) {
+                    psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning in image.\n");
+                    return(in);
+                }
+                if (false ==  p_psGetStatValue(rc, &statValue)) {
+                    psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
+                    return(in);
+                }
+                overscanVector->data.F32[i] = statValue;
+            }
+            psFree(tmpCol);
+
+            // Scale the overscan vector to the size of the input image.
+            if (overscanVector->n != in->image->numRows) {
+                if ((fit == PM_FIT_POLYNOMIAL) || (fit == PM_FIT_SPLINE)) {
+                    psVector *newVec = ScaleOverscanVector(overscanVector,
+                                                           in->image->numRows,
+                                                           fitSpec, fit);
+                    if (newVec == NULL) {
+                        psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector(): could not scale the overscan vector.  Returning in image.\n");
+                        return(in);
+                    }
+                    psFree(overscanVector);
+                    overscanVector = newVec;
+                } else {
+                    psError(PS_ERR_UNKNOWN, true, "Don't know how to scale the overscan vector.  Set fit to PM_FIT_SPLINE or PM_FIT_POLYNOMIAL.  Returning in image.\n");
+                    psFree(overscanVector);
+                    return(in);
+                }
+            }
+        }
+
+        //
+        // Re-bin the overscan vector (change its length).
+        //
+        // Only if nBinOrig > 1.
+        if ((nBinOrig > 1) && (nBinOrig < overscanVector->n)) {
+            numBins = 1+((overscanVector->n)/nBinOrig);
+            myBin = psVectorAlloc(numBins, PS_TYPE_F32);
+            binVec = psVectorAlloc(nBinOrig, PS_TYPE_F32);
+
+            for (i=0;i<numBins;i++) {
+                for(j=0;j<nBinOrig;j++) {
+                    if (overscanVector->n > ((i*nBinOrig)+j)) {
+                        binVec->data.F32[j] = overscanVector->data.F32[(i*nBinOrig)+j];
+                    } else {
+                        // XXX: we get here if nBinOrig does not evenly divide
+                        // the overscanVector vector.  This is the last bin.  Should
+                        // we change the binVec->n to acknowledge that?
+                        binVec->n = j;
+                    }
+                }
+                psStats *rc = psVectorStats(myStats, binVec, NULL, NULL, 0);
+                if (rc == NULL) {
+                    psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning in image.\n");
+                    return(in);
+                }
+                if (false ==  p_psGetStatValue(rc, &statValue)) {
+                    psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning in image.\n");
+                    return(in);
+                }
+                myBin->data.F32[i] = statValue;
+            }
+
+            // Change the effective size of overscanVector.
+            overscanVector->n = numBins;
+            for (i=0;i<numBins;i++) {
+                overscanVector->data.F32[i] = myBin->data.F32[i];
+            }
+            psFree(binVec);
+            psFree(myBin);
+            nBin = nBinOrig;
+        } else {
+            nBin = 1;
+        }
+
+        // At this point the number of data points in overscanVector should be
+        // equal to the number of rows/columns (whatever is appropriate) in the
+        // image divided by numBins.
+        //
+
+
+        //
+        // This doesn't seem right.  The only way to do a spline fit is if,
+        // by SDRS requirements, fitSpec is not-NULL>  But in order for it
+        // to be non-NULL, someone must have called psSpline1DAlloc() with
+        // the min, max, and number of splines.
+        //
+        if (!((fitSpec == NULL) || (fit == PM_FIT_NONE))) {
+            //
+            // Fit a polynomial or spline to the overscan vector.
+            //
+            if (fit == PM_FIT_POLYNOMIAL) {
+                myPoly = (psPolynomial1D *) fitSpec;
+                myPoly = psVectorFitPolynomial1D(myPoly, NULL, 0, overscanVector, NULL, NULL);
+                if (myPoly == NULL) {
+                    psError(PS_ERR_UNKNOWN, false, "(3) Could not fit a polynomial to overscan vector.  Returning in image.\n");
+                    psFree(overscanVector);
+                    return(in);
+                }
+            } else if (fit == PM_FIT_SPLINE) {
+                // 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;
+    }
+
+    psTrace(".psModule.pmSubtracBias.pmSubtractBias", 4,
+            "---- pmSubtractBias() exit ----\n");
+
+    if (bias != NULL) {
+        return(SubtractFrame(in, bias));
+    }
+    return(in);
+}
+
+
Index: /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/pmSubtractBias.h
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/pmSubtractBias.h	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/pmSubtractBias.h	(revision 22115)
@@ -0,0 +1,49 @@
+/** @file  pmSubtractBias.h
+ *
+ *  This file will contain a module which will subtract the detector bias
+ *  in place from an input image.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#if !defined(PM_SUBTRACT_BIAS_H)
+#define PM_SUBTRACT_BIAS_H
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include<stdio.h>
+#include<math.h>
+#include "pslib.h"
+#include "pmAstrometry.h"
+
+typedef enum {
+    PM_OVERSCAN_NONE,                         ///< No overscan subtraction
+    PM_OVERSCAN_ROWS,                         ///< Subtract rows
+    PM_OVERSCAN_COLUMNS,                      ///< Subtract columns
+    PM_OVERSCAN_ALL                           ///< Subtract the statistic of all pixels in overscan region
+} pmOverscanAxis;
+
+typedef enum {
+    PM_FIT_NONE,                              ///< No fit
+    PM_FIT_POLYNOMIAL,                        ///< Fit polynomial
+    PM_FIT_SPLINE                             ///< Fit cubic splines
+} pmFit;
+
+pmReadout *pmSubtractBias(pmReadout *in,                ///< The input pmReadout image
+                          void *fitSpec,                ///< A polynomial or spline, defining the fit type.
+                          const psList *overscans,      ///< A psList of overscan images
+                          pmOverscanAxis overScanAxis,  ///< Defines how overscans are applied
+                          psStats *stat,                ///< The statistic to be used in combining overscan data
+                          int nBin,                     ///< The amount of binning to be done image pixels.
+                          pmFit fit,                    ///< PM_FIT_SPLINE, PM_FIT_POLYNOMIAL, or PM_FIT_NONE
+                          const pmReadout *bias);       ///< A possibly NULL bias pmReadout which is to be subtracted
+
+#endif
Index: /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/pmSubtractSky.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/pmSubtractSky.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/pmSubtractSky.c	(revision 22115)
@@ -0,0 +1,720 @@
+/** @file  pmSubtractSky.c
+ *
+ *  This file will contain a module which will create a model of the
+ *  background sky and subtract that from the input image.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-10-12 21:02:04 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ *      
+ *
+ */
+
+#include<stdio.h>
+#include<math.h>
+#include "pslib.h"
+#include "psConstants.h"
+#include "pmSubtractSky.h"
+
+/******************************************************************************
+DetermineNumBits(data): This routine takes an enum psStatsOptions as an
+argument and returns the number of non-zero bits.
+ 
+XXX: This code is duplicated in the ReadoutCombine file.
+ *****************************************************************************/
+static psS32 DetermineNumBits(psStatsOptions data)
+{
+    psTrace("SubtractSky.DetermineNumBits", 4, "Calling DetermineNumBits(0x%x)\n", data);
+
+    psS32 i;
+    psU64 tmpData = data;
+    psS32 numBits = 0;
+
+    for (i=0;i<(8 * sizeof(psStatsOptions));i++) {
+        if (0x0001 & tmpData) {
+            numBits++;
+        }
+        tmpData = tmpData >> 1;
+    }
+
+    psTrace("SubtractSky.DetermineNumBits", 4,
+            "Calling DetermineNumBits(0x%x) -> %d\n", data, numBits);
+    return(numBits);
+}
+
+/******************************************************************************
+getHighestPriorityStatOption(statOptions): this routine takes as input a
+psStats->options with multiple options set and returns one with a single
+option set according to the precedence set in the SDRS.
+ *****************************************************************************/
+static psU64 getHighestPriorityStatOption(psU64 statOptions)
+{
+    psTrace("SubtractSky.getHighestPriorityStatOption", 4,
+            "Calling getHighestPriorityStatOption(0x%x)\n", statOptions);
+
+    if (statOptions & PS_STAT_SAMPLE_MEAN) {
+        return(PS_STAT_SAMPLE_MEAN);
+    } else if (statOptions & PS_STAT_SAMPLE_MEDIAN) {
+        return(PS_STAT_SAMPLE_MEDIAN);
+    } else if (statOptions & PS_STAT_CLIPPED_MEAN) {
+        return(PS_STAT_CLIPPED_MEAN);
+    } else if (statOptions & PS_STAT_ROBUST_MEAN) {
+        return(PS_STAT_ROBUST_MEAN);
+    } else if (statOptions & PS_STAT_ROBUST_MEDIAN) {
+        return(PS_STAT_ROBUST_MEDIAN);
+    } else if (statOptions & PS_STAT_ROBUST_MODE) {
+        return(PS_STAT_ROBUST_MODE);
+    }
+    psError(PS_ERR_UNKNOWN, true, "Unallowable option requested for statistically binning image pixels.\n");
+    return(-1);
+}
+
+/******************************************************************************
+psImage *binImage(origImage, binFactor, statOptions): This routine takes an
+input psImage and scales it smaller by a factor of binFactor.  The statistic
+used in combining input pixels is specified in statOptions.
+ 
+XXX: use static vectors for myStats, binVector and binMask.
+XXX: I coded this before I was aware of a psLib reBin function.  I don't
+use this function in this module.  I'm keeping it here in the event that
+requirements change and we might need a custom reBin function.
+ *****************************************************************************/
+/*
+static psImage *binImage(psImage *origImage,
+                         int binFactor,
+                         psStatsOptions statOptions)
+{
+    psTrace("SubtractSky.binImage", 4, "Calling binImage(%d)\n", binFactor);
+ 
+    if (binFactor <= 0) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: binImage(): binFactor is %d\n", binFactor);
+        return(origImage);
+    }
+    if (binFactor == 1) {
+        return(origImage);
+    }
+ 
+    psVector *binVector = psVectorAlloc(binFactor * binFactor, PS_TYPE_F32);
+    psVector *binMask = psVectorAlloc(binFactor * binFactor, PS_TYPE_U8);
+    psStats *myStats = psStatsAlloc(statOptions);
+ 
+    for (psS32 row = 0; row < origImage->numRows ; row+=binFactor) {
+        for (psS32 col = 0; col < origImage->numCols ; col+=binFactor) {
+            psS32 count = 0;
+            for (psS32 binRow = 0; binRow <= binFactor ; binRow++) {
+                for (psS32 binCol = 0; binCol <= binFactor ; binCol++) {
+                    if (((row + binRow) < origImage->numRows) &&
+                            ((col + binCol) < origImage->numCols)) {
+                        binVector->data.F32[count] =
+                            origImage->data.F32[row + binRow][col + binCol];
+                        binMask->data.U8[count] = 0;
+                    } else {
+                        binVector->data.F32[count] = 0.0;
+                        binMask->data.U8[count] = 1;
+                    }
+                    count++;
+                }
+            }
+            psStats *rc1 = psVectorStats(myStats, binVector, NULL, binMask, 1);
+            if (rc1 == NULL) {
+                psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning in image.\n");
+                return(origImage);
+            }
+            psF64 statValue;
+            psBool rc = p_psGetStatValue(rc1, &statValue);
+ 
+            if (rc == true) {
+                origImage->data.F32[row][col] = (psF32) statValue;
+            } else {
+                origImage->data.F32[row][col] = 0.0;
+                psLogMsg(__func__, PS_LOG_WARN,
+                         "WARNING: pmSubtractSky(), binImage(): p_psGetStatValue() was FALSE\n");
+            }
+        }
+    }
+    psFree(binVector);
+    psFree(binMask);
+    psFree(myStats);
+ 
+    psTrace("SubtractSky.binImage", 4, "Exiting binImage(%d)\n", binFactor);
+    return(origImage);
+}
+*/
+
+/******************************************************************************
+CalculatePolyTerms(xOrder, yOrder): this routine will calculate the number of
+coefficients (or terms) in a 2-D polynomial of order (xOrder, yOrder).
+ 
+XXX: Use your brain and figure out the analytical expression.
+ *****************************************************************************/
+static psS32 CalculatePolyTerms(psS32 xOrder, psS32 yOrder)
+{
+    psTrace("SubtractSky.CalculatePolyTerms", 4,
+            "Calling CalculatePolyTerms(%d, %d)\n", xOrder, yOrder);
+
+    psS32 maxOrder = PS_MAX(xOrder, yOrder);
+    psS32 localPolyTerms = 0;
+    psS32 order = 0;
+    psS32 num=0;
+
+    for (order=0;order<=maxOrder;order++) {
+        for (num=0;num<=order;num++) {
+            if (((order-num) <= xOrder) && (num <= yOrder)) {
+                localPolyTerms++;
+            }
+        }
+    }
+
+    psTrace("SubtractSky.CalculatePolyTerms", 4,
+            "Exiting CalculatePolyTerms(%d, %d) -> %d\n", xOrder, yOrder, localPolyTerms);
+    return(localPolyTerms);
+}
+
+/******************************************************************************
+buildPolyTerms(): this routine computes a 2-D array polyTerms[][] that holds
+terms for the polynomial that is used to model the sky background.  We use
+this array primarily for convenience in computations involving sky model
+polynomials.  It is defined as:
+    polyTerms[i][0] = the power to which X is raised in the i-th term of in an
+    poly-order sky background polynomial.
+ 
+    polyTerms[i][1] = the power to which Y is raised in the i-th term of in an
+    poly-order sky background polynomial.
+ *****************************************************************************/
+static psS32 **buildPolyTerms(psS32 xOrder, psS32 yOrder)
+{
+    psTrace("SubtractSky.buildPolyTerms", 4,
+            "Calling buildPolyTerms(%d, %d)\n", xOrder, yOrder);
+
+    psS32 i=0;
+    psS32 order = 0;
+    psS32 num=0;
+    psS32 localPolyTerms = CalculatePolyTerms(xOrder, yOrder);
+    psS32 maxOrder = PS_MAX(xOrder, yOrder);
+
+    // Create the data structure which we hold the xy order of each coeff.
+    psS32 **polyTerms = (psS32 **) psAlloc(localPolyTerms * sizeof(psS32 *));
+    for (i=0; i < localPolyTerms ; i++) {
+        polyTerms[i] = (psS32 *) psAlloc(2 * sizeof(psS32));
+    }
+
+    i=0;
+    // This code segment loops through each term i in the polynomial and
+    // calculates the power to which x/y are raised in that i-th term.
+    // We first do the 0-order terms, then the 1-order terms, etc.
+    for (order=0;order<=maxOrder;order++) {
+        for (num=0;num<=order;num++) {
+            if (((order-num) <= xOrder) && (num <= yOrder)) {
+                polyTerms[i][0] = order-num;
+                polyTerms[i][1] = num;
+                i++;
+            }
+        }
+    }
+
+    if (psTraceGetLevel(".psModule.pmSubtractSky.buildPolyTerms") >= 10) {
+        for (i=0; i < localPolyTerms ; i++) {
+            printf("x^%d * y^%d\n", polyTerms[i][0], polyTerms[i][1]);
+        }
+    }
+
+    psTrace("SubtractSky.buildPolyTerms", 4,
+            "Exiting buildPolyTerms(%d, %d)\n", xOrder, yOrder);
+    return(polyTerms);
+}
+
+/******************************************************************************
+This procedure calculates various combinations of powers of x and y and stores
+them in the data structure p_psPolySums[][].  After it completes:
+ 
+    p_psPolySums[i][j] == x^i * y^j
+ 
+XXX: Use a psImage for the p_psPolySums data structure?
+XXX: p_psPolySums: should this be a global?  Did you get the storage classifier
+     and name correct?
+XXX: Use variable size arrays for p_psPolySums[][].
+XXX: Must initialize p_psPolySums[][]?
+ *****************************************************************************/
+#define PS_MAX_POLYNOMIAL_ORDER 20
+
+psF64 p_psPolySums[PS_MAX_POLYNOMIAL_ORDER+1][PS_MAX_POLYNOMIAL_ORDER+1];
+static void buildSums(psF64 x,
+                      psF64 y,
+                      psS32 xOrder,
+                      psS32 yOrder)
+{
+    psTrace("SubtractSky.buildPolyTerms", 4,
+            "Calling buildPolyTerms(%d, %d)\n", xOrder, yOrder);
+
+    psS32 i = 0;
+    psS32 j = 0;
+    psF64 xSum = 0.0;
+    psF64 ySum = 0.0;
+
+    xSum = 1.0;
+    ySum = 1.0;
+    for(i=0;i<=xOrder;i++) {
+        ySum = xSum;
+        for(j=0;j<=yOrder;j++) {
+            p_psPolySums[i][j] = ySum;
+            ySum*= y;
+        }
+        xSum*= x;
+    }
+    psTrace("SubtractSky.buildPolyTerms", 4,
+            "Exiting buildPolyTerms(%d, %d)\n", xOrder, yOrder);
+}
+
+/******************************************************************************
+ImageFitPolynomial(myPoly, dataImage, maskImage): this private routine takes
+an input image along with a mask and fits a polynomial to it.  The degree of
+the polynomial is specified by input parameter myPoly, and need not be
+symmetrical in orders of X and Y.  The polynomial must be type
+PS_POLYNOMIAL_ORD.  If there are not enough rows or columns in the input image
+for the order of the polynomial, then that order is reduced.  The algorithm
+used in this routine is based on that of the pilot project ADD, but is not
+documented anywhere.
+ 
+XXX: Different trace message facilities in use here.
+ *****************************************************************************/
+static psPolynomial2D *ImageFitPolynomial(psPolynomial2D *myPoly,
+        psImage *dataImage,
+        psImage *maskImage)
+{
+    psTrace("SubtractSky.ImageFitPolynomial", 4,
+            "Calling ImageFitPolynomial()\n");
+    PS_ASSERT_POLY_NON_NULL(myPoly, NULL);
+    PS_ASSERT_POLY_TYPE(myPoly, PS_POLYNOMIAL_ORD, NULL);
+    PS_ASSERT_IMAGE_NON_NULL(dataImage, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(dataImage, NULL);
+    PS_ASSERT_IMAGE_TYPE(dataImage, PS_TYPE_F32, NULL);
+    PS_ASSERT_IMAGE_NON_NULL(maskImage, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(maskImage, NULL);
+    PS_ASSERT_IMAGE_TYPE(maskImage, PS_TYPE_U8, NULL);
+    PS_ASSERT_IMAGES_SIZE_EQUAL(dataImage, maskImage, NULL);
+    psS32 oldPolyX = -1;
+    psS32 oldPolyY = -1;
+
+    // The matrix equations become singular if there are more powers of X
+    // in myPoly then there are rows of the image.  I think.  Similarly for
+    // powers of Y and columns.  So.  Here we reduce the complexity of the
+    // polynomial if there are not enough rows/columns in the input image.
+
+    if ((myPoly->nX + 1) > dataImage->numRows) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: ImageFitPolynomial(): Reducing polynomial complexity in x-dimension.\n");
+        oldPolyX = myPoly->nX;
+        myPoly->nX = dataImage->numRows - 1;
+    }
+    if ((myPoly->nY + 1) > dataImage->numCols) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: ImageFitPolynomial(): Reducing polynomial complexity in y-dimension.\n");
+        oldPolyY = myPoly->nY;
+        myPoly->nY = dataImage->numCols - 1;
+    }
+    psS32 i;
+    psS32 j;
+    psS32 x;
+    psS32 y;
+    psS32 aRow;
+    psS32 aCol;
+    psS32 **polyTerms = buildPolyTerms(myPoly->nX, myPoly->nY);
+    // We determine how many coefficients will be in the polynomial that we
+    // are fitting to this image.
+    psS32 localPolyTerms = CalculatePolyTerms(myPoly->nX, myPoly->nY);
+    psImage *A = psImageAlloc(localPolyTerms, localPolyTerms, PS_TYPE_F64);
+    psImage *Aout = psImageAlloc(localPolyTerms, localPolyTerms, PS_TYPE_F64);
+    psVector *B = psVectorAlloc(localPolyTerms, PS_TYPE_F64);
+    psVector *outPerm = NULL;
+
+    //
+    // Initialize A matrix and B vector.
+    //
+    PS_IMAGE_SET_F64(A, 0.0);
+    PS_VECTOR_SET_F64(B, 0.0);
+
+    //
+    // We build the A matrix and B vector.
+    //
+    for (x=0;x<dataImage->numRows;x++) {
+        for (y=0;y<dataImage->numCols;y++) {
+            if (maskImage->data.U8[x][y] == 0) {
+                buildSums((psF64) x, (psF64) y, myPoly->nX, myPoly->nY);
+
+                /************************************************************
+                This code dervies from equation (7) of the pilot ADD.  However,
+                it is not exactly the same in that the order of the polynomial
+                may be different in X And Y.
+
+                Equation (7) from the pilot ADD describes 16 linear equations.
+                The i-th equation is simply the partial derivative of the
+                sky background polynomial (1) w.r.t. to the i-th term in
+                that polynomial.  The i-th equation is stored in row i of
+                matrix A[][] (matrix A[][] has origin (1,1), not (0,0)).  To
+                compute A[i][j] we simply multiply the j-th term of the Sky
+                Background Polynomial (SBP) by the i-th term of SBP.
+                ************************************************************/
+                for (aRow=0;aRow<localPolyTerms;aRow++) {
+                    for (aCol=0;aCol<localPolyTerms;aCol++) {
+                        A->data.F64[aRow][aCol]+=
+                            (p_psPolySums[ polyTerms[aCol][0] ][ polyTerms[aCol][1] ] *
+                             p_psPolySums[ polyTerms[aRow][0] ][ polyTerms[aRow][1] ]);
+                    }
+                }
+                // Build the B[] vector, which is the right-hand side of (7).
+                for (i=0;i<localPolyTerms;i++) {
+                    B->data.F64[i]+= dataImage->data.F32[x][y] *
+                                     p_psPolySums[ polyTerms[i][0] ][ polyTerms[i][1] ];
+                }
+            }
+        }
+    }
+
+    if (psTraceGetLevel(".psModule.pmSubtractSky.ImageFitPolynomial") >= 8) {
+        for (aRow=0;aRow<localPolyTerms;aRow++) {
+            for (aCol=0;aCol<localPolyTerms;aCol++) {
+                printf("A[%d][%d] is %f\n", aRow, aCol,
+                       A->data.F64[aRow][aCol]);
+            }
+        }
+
+        for (i=0;i<=localPolyTerms;i++) {
+            printf("B[%d] is %f\n", i, B->data.F64[i]);
+        }
+    }
+
+    //
+    // Solve the matrix equations for the polynomial coefficients C.
+    // XXX: How do we know if these matrix operations were successful?
+    //
+    Aout = psMatrixLUD(Aout, &outPerm, A);
+    PS_ASSERT_IMAGE_NON_NULL(Aout, NULL);
+    PS_ASSERT_IMAGE_NON_EMPTY(Aout, NULL);
+    psVector *C = psVectorAlloc(localPolyTerms, PS_TYPE_F64);
+    psMatrixLUSolve(C, Aout, B, outPerm);
+
+    //
+    // Set the appropriate coefficients in the myPoly structure.
+    //
+    for (i=0;i<localPolyTerms;i++) {
+        myPoly->coeff[ polyTerms[i][0] ][ polyTerms[i][1] ] = C->data.F64[i];
+        psTrace(".psModule.pmSubtractSky.ImageFitPolynomial", 6,
+                "myPoly->coeff[%d][%d] is %f\n", polyTerms[i][0], polyTerms[i][1], myPoly->coeff[ polyTerms[i][0] ][ polyTerms[i][1] ]);
+    }
+
+    //
+    // Free data structures that were allocated in this module.
+    //
+    for (i=0;i<localPolyTerms;i++) {
+        psFree(polyTerms[i]);
+    }
+    psFree(polyTerms);
+    psFree(A);
+    psFree(Aout);
+    psFree(B);
+    psFree(C);
+    psFree(outPerm);
+
+    //
+    // We restore the original size of the polynomial and set remaining
+    // coefficients to 0.0, if necessary.
+    //
+    // XXX: Verify this works after poly nOrder/nTerm change.
+    //
+    if (oldPolyX != -1) {
+        myPoly->nX = oldPolyX;
+        for (i=oldPolyX ; i < (1 + myPoly->nX) ; i++) {
+            for (j=0;j<(1 + myPoly->nY) ; j++) {
+                myPoly->coeff[i][j] = 0.0;
+            }
+        }
+    }
+    if (oldPolyY != -1) {
+        myPoly->nY = oldPolyY;
+        for (i=0 ; i < (1 + myPoly->nX) ; i++) {
+            for (j=oldPolyY;j < (1 + myPoly->nY) ; j++) {
+                myPoly->coeff[i][j] = 0.0;
+            }
+        }
+    }
+
+    psTrace("SubtractSky.ImageFitPolynomial", 4,
+            "Exiting ImageFitPolynomial()\n");
+    //    psTrace(".psModule.pmSubtractSky.ImageFitPolynomial", 4,
+    //            "---- ImageFitPolynomial() end successfully ----\n");
+    return(myPoly);
+}
+
+
+/******************************************************************************
+pmReadout pmSubtractSky():
+ 
+XXX: use static vectors for myStats, and the binned image
+ 
+XXX: The SDR is silent about types.  PS_TYPE_F32 is implemented here.
+ 
+XXX: Sync the psTrace message facilities.
+ *****************************************************************************/
+pmReadout *pmSubtractSky(pmReadout *in,
+                         void *fitSpec,
+                         psFit fit,
+                         psS32 binFactor,
+                         psStats *stats,
+                         psF32 clipSD)
+{
+    PS_ASSERT_READOUT_NON_NULL(in, NULL);
+    PS_ASSERT_READOUT_NON_EMPTY(in, NULL);
+    PS_ASSERT_READOUT_TYPE(in, PS_TYPE_F32, NULL);
+    psTrace(".psModule.pmSubtractSky", 4,
+            "---- pmSubtractSky() begin ----\n");
+
+    if ((fit != PM_FIT_NONE) &&
+            (fit != PM_FIT_POLYNOMIAL) &&
+            (fit != PM_FIT_SPLINE)) {
+        psError(PS_ERR_UNKNOWN, true, "psFit is unallowable (%d).  Returning in image.\n", fit);
+        return(in);
+    }
+
+    psStatsOptions statOptions = 0;
+
+    //
+    // Return the original input readout if the fit specs are poorly defined.
+    // No warning or error messages should be generated.
+    //
+    if ((fitSpec == NULL) ||
+            ((fit == PM_FIT_NONE) || (fit == PM_FIT_SPLINE))) {
+        //        psLogMsg(__func__, PS_LOG_WARN, "Fit specs are poorly defined.  Returning in image.\n");
+        return(in);
+    }
+    psImage *origImage = in->image;
+    psImage *binnedImage = NULL;
+    psPolynomial2D *myPoly = NULL;
+    psImage *binnedMaskImage = NULL;
+    psU32 oldStatOptions = 0;
+
+    //
+    // Determine which statistic to use when binning pixels, if any.
+    //
+    if (stats != NULL) {
+        statOptions = stats->options;
+        if (1 < DetermineNumBits(statOptions)) {
+            psLogMsg(__func__, PS_LOG_WARN, "WARNING: Multiple statistical options have been requested.\n");
+            statOptions = getHighestPriorityStatOption(statOptions);
+            if (statOptions == -1) {
+                psError(PS_ERR_UNKNOWN, true, "Not allowable stats->option was specified.  Returning in image.\n");
+                return(in);
+            }
+            // Save old input "stats" parameter.
+            oldStatOptions = stats->options;
+            stats->options = statOptions;
+        }
+        if (0 == DetermineNumBits(statOptions)) {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: pmSubtractSky(): no stats->options was requested\n");
+        }
+    }
+
+    //
+    // Generate required warning messages.
+    //
+    if (binFactor <= 0) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: pmSubtractSky(): binFactor is %d\n", binFactor);
+    }
+    if (stats == NULL) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: pmSubtractSky(): input parameter stats is NULL\n");
+    }
+
+    //
+    // Bin the input image according to input parameters.
+    // Create a new binned image mask.
+    //
+    if ((binFactor <= 1) || (stats == NULL) || (0 == DetermineNumBits(statOptions))) {
+        // No binning is required here.  Simply create a copy of the image
+        // and a mask.
+        binnedImage = psImageCopy(binnedImage, origImage, PS_TYPE_F32);
+        if (binnedImage == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "psImageCopy() returned NULL.  Returning in image.\n");
+            return(in);
+        }
+
+        if (in->mask != NULL) {
+            binnedMaskImage = psImageCopy(binnedMaskImage, in->mask, PS_TYPE_U8);
+            if (binnedMaskImage == NULL) {
+                psError(PS_ERR_UNKNOWN, false, "psImageCopy() returned NULL.  Returning in image.\n");
+                psFree(binnedImage);
+                return(in);
+            }
+        } else {
+            binnedMaskImage = psImageAlloc(binnedImage->numCols,
+                                           binnedImage->numRows,
+                                           PS_TYPE_U8);
+            PS_IMAGE_SET_U8(binnedMaskImage, 0);
+        }
+    } else {
+        binnedImage = psImageRebin(NULL, origImage, in->mask, 0, binFactor, stats);
+        if (binnedImage == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "psImageRebin() returned NULL.  Returning in image.\n");
+            return(in);
+        }
+        binnedMaskImage = psImageAlloc(binnedImage->numCols,
+                                       binnedImage->numRows,
+                                       PS_TYPE_U8);
+        PS_IMAGE_SET_U8(binnedMaskImage, 0);
+    }
+    psTrace(".psModule.pmSubtractSky", 4,
+            "binnedImage size is (%d, %d)\n", binnedImage->numRows, binnedImage->numCols);
+
+    //
+    // Clip pixels that are outside the acceptable range.
+    //
+    if (clipSD <= 0.0) {
+        psLogMsg(__func__, PS_LOG_WARN,
+                 "WARNING: pmSubtractSky(): clipSD is %f\n", clipSD);
+    } else {
+        // Determine the mean and standard deviation of the binned image.
+        psF64 binnedMean;
+        psF64 binnedStdev;
+        psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+        psStats *rc =  psImageStats(myStats, binnedImage, NULL, 0);
+        if (rc == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "psImageStats(): could not perform requested statistical operation.  Returning in image.\n");
+            return(in);
+        }
+        if (false == p_psGetStatValue(rc, &binnedMean)) {
+            psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine requested statistical operation.  Returning in image.\n");
+            return(in);
+        }
+        psTrace(".psModule.pmSubtractSky", 6,
+                "binned Mean is %f\n", binnedMean);
+
+        myStats->options = PS_STAT_SAMPLE_STDEV;
+        rc =  psImageStats(myStats, binnedImage, NULL, 0);
+        if (rc == NULL) {
+            psError(PS_ERR_UNKNOWN, false, "psImageStats(): could not perform requested statistical operation.  Returning in image.\n");
+            return(in);
+        }
+        if (false == p_psGetStatValue(myStats, &binnedStdev)) {
+            psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine requested statistical operation.  Returning in image.\n");
+            return(in);
+        }
+        psFree(myStats);
+        psTrace(".psModule.pmSubtractSky", 6,
+                "binned StDev is %f\n", binnedStdev);
+
+        // Clip all pixels which are more than clipSD sigmas from the mean.
+        psTrace(".psModule.pmSubtractSky", 6,
+                "clipSD is %f\n", clipSD);
+
+        for (psS32 row = 0; row < binnedImage->numRows ; row++) {
+            for (psS32 col = 0; col < binnedImage->numCols ; col++) {
+                if (fabs(binnedImage->data.F32[row][col] - binnedMean) >
+                        (clipSD * binnedStdev)) {
+                    binnedMaskImage->data.U8[row][col] = 1;
+                }
+            }
+        }
+    }
+
+    //
+    // Fit the polynomial to the binned image
+    //
+    if (fit == PM_FIT_POLYNOMIAL) {
+        myPoly = (psPolynomial2D *) fitSpec;
+        PS_ASSERT_POLY_NON_NULL(myPoly, NULL);
+        PS_ASSERT_POLY_TYPE(myPoly, PS_POLYNOMIAL_ORD, NULL);
+
+        myPoly = ImageFitPolynomial(myPoly, binnedImage, binnedMaskImage);
+
+        if (myPoly != NULL) {
+            // Set the pixels in the binned image to that of the polynomial.
+            binnedImage = psImageEvalPolynomial(binnedImage, myPoly);
+            if (binnedImage == NULL) {
+                psError(PS_ERR_UNKNOWN, false, "psImageEvalPolynomial() returned NULL.  Returning in image.\n");
+                psFree(binnedMaskImage);
+                if (!((binFactor <= 1) || (stats == NULL))) {
+                    psFree(binnedImage);
+                }
+                if (oldStatOptions != 0) {
+                    stats->options = statOptions;
+                }
+                return(in);
+            }
+        } else {
+            psLogMsg(__func__, PS_LOG_WARN,
+                     "WARNING: pmSubtractSky(): could not model sky with a polynomial.\n");
+            psFree(binnedMaskImage);
+            if (!((binFactor <= 1) || (stats == NULL))) {
+                psFree(binnedImage);
+            }
+            if (oldStatOptions != 0) {
+                stats->options = statOptions;
+            }
+            return(in);
+        }
+    } else {
+        // We shouldn't get here since we check this above.
+        psError(PS_ERR_UNKNOWN, true, "Unallowable fit type.  Returning in image.\n");
+        psFree(binnedMaskImage);
+        if (!((binFactor <= 1) || (stats == NULL))) {
+            psFree(binnedImage);
+        }
+        if (oldStatOptions != 0) {
+            stats->options = statOptions;
+        }
+        return(in);
+    }
+
+    //
+    //Subtract the polynomially fitted image from the original image
+    //
+    if (binFactor <= 1) {
+        // The binned image is the same size as the original image.
+        for (psS32 row = 0; row < origImage->numRows ; row++) {
+            for (psS32 col = 0; col < origImage->numCols ; col++) {
+                origImage->data.F32[row][col]-= binnedImage->data.F32[row][col];
+            }
+        }
+    } else {
+        for (psS32 row = 0; row < origImage->numRows ; row++) {
+            for (psS32 col = 0; col < origImage->numCols ; col++) {
+                // We calculate the F32 value of the pixel coordinates in the
+                // binned image and then use a pixel interpolation routine to
+                // determine the value of the pixel at that location.
+                psF32 binRowF64 = ((psF32) row) / ((psF32) binFactor);
+                psF32 binColF64 = ((psF32) col) / ((psF32) binFactor);
+
+                // We add 0.5 to the pixel locations since the pixel
+                // interpolation routine defines the location of pixel
+                // (i, j) as (i+0.5, j+0.5).
+                binRowF64+= 0.5;
+                binColF64+= 0.5;
+
+                psF32 binPixel = (psF32) psImagePixelInterpolate(
+                                     binnedImage, binColF64, binRowF64,
+                                     NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
+                origImage->data.F32[row][col]-= binPixel;
+
+                psTrace(".psModule.pmSubtractSky", 8,
+                        "image[%d][%d] <--> binnedImage[%.2f][%.2f]: %f\n",
+                        row, col, binRowF64-0.5, binColF64-0.5, binPixel);
+            }
+        }
+
+    }
+    psFree(binnedMaskImage);
+    psFree(binnedImage);
+    if (oldStatOptions != 0) {
+        stats->options = statOptions;
+    }
+
+    psTrace(".psModule.pmSubtractSky", 4,
+            "---- pmSubtractSky() exit successfully ----\n");
+    return(in);
+}
Index: /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/pmSubtractSky.h
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/pmSubtractSky.h	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/imsubtract/pmSubtractSky.h	(revision 22115)
@@ -0,0 +1,40 @@
+/** @file  pmSubtractSky.h
+ *
+ *  This file will contain a module which will create a model of the
+ *  background sky and subtract that from the input image.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:43:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#if !defined(PM_SUBTRACT_SKY_H)
+#define PM_SUBTRACT_SKY_H
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include<stdio.h>
+#include<math.h>
+#include "pslib.h"
+#include "pmAstrometry.h"
+
+// XXX: this is pmFit in pmSubtractBias.c, named psFit here.
+typedef enum {
+    PM_FIT_NONE,                              ///< No fit
+    PM_FIT_POLYNOMIAL,                        ///< Fit polynomial
+    PM_FIT_SPLINE                             ///< Fit cubic splines
+} psFit;
+
+pmReadout *pmSubtractSky(pmReadout *in,
+                         void *fitSpec,
+                         psFit fit,
+                         int binFactor,
+                         psStats *stats,
+                         float clipSD);
+#endif
Index: /tags/ipp-1-X/rel8_2/psModules/src/mainpage.dox
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/mainpage.dox	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/mainpage.dox	(revision 22115)
@@ -0,0 +1,130 @@
+/** @mainpage psModule Image Processing Library
+
+
+@section intro Introduction
+This library contains the Pan-STARRS Image Processing Pipeline (IPP) modules (psModule). These modules
+use the functionality of the Pan-STARRS Library (psLib) to perform more complex tasks associated with image
+processing. Modules were constructed to support each of the required processing stages and are listed according
+to the particular processing stage for which they will primarily be used. To preserve namespace, globally-visible
+structures and functions are prefixed with pm, an abbreviation for Pan-STARRS Modules.
+
+Modules in this release include:
+
+ - Astrometry (pmAstrometry)
+ - Subtract bias (pmSubtractBias)
+ - Correct for non-linearity (pmNonLinear)
+ - Flat-field (pmFlatField)
+ - Mask bad pixels (pmMaskBadPixels and pmMaskBadPixelsErrors)
+ - Image Combine (pmImageCombine)
+ - Image Subtract (pmImageSubtract)
+ - Combine readouts (pmReadoutCombine)
+ - Subtract sky (pmSubtractSky)
+ - Object detection (pmObjects)
+
+The installed code for psModule consists of header files and a binary library.
+
+@section extinstall Required external Libraries
+
+Before building psLib from source, several external software libraries must
+be installed. These include:
+ - Pan-STARRS Library (psLib)
+     - Available from the Maui High Performance Computiong Center (MHPCC) at https://mhpcc.pan-starrs.org/code/releases
+     - Compatibility tested with rel5
+     - Note, psLib itself has many external library dependencies.
+ - Doxygen Documentation System
+     - Only needed if code documentation is to be made
+     - Available at http://www.doxygen.org
+     - Compatibility tested with version 1.3.6
+     - The optional companion package, GraphViz (a.k.a., the 'dot' command), is recommended to enable header-file dependency diagrams
+
+We recommend using the particular versions listed as compatibility tested, as
+that is the only versions of the external libraries tested to work well with psLib
+and psModule.  Though it is quite possible that later versions of the libraries
+listed will also work, care must be taken when upgrading these libraries to verify
+that its functionality is compatible with the tested version.
+
+@section install How to Build from Source
+
+Tested versions of psModule are put into a tar file and can be downloaded from:
+
+https://mhpcc.pan-starrs.org/code/releases
+
+If one has a login account on mhpcc.pan-starrs.org, direct CVS access is
+possible.  Example of the commands required for direct CVS retrieval are
+as follows:
+<pre>
+$ cvs -d:ext:USERNAME@mhpcc.pan-starrs.org:/data/panstarrs/cvsroot co -r RELEASEBRANCH psModule
+</pre>
+where:
+  - USERNAME is your login name on the server
+  - RELEASEBRANCH is the desired release branch, e.g. rel7.
+
+
+@section build How to Build and Test the psModule Library.
+
+The psModule library and associated tests are made via the GNU autoconf/automake system.
+
+The source should build using the configure script in the psModule directory.  The
+recommended steps are:
+<pre>
+$ cd psModule
+$ ./configure
+$ make
+$ make check
+$ make install
+</pre>
+<i>Unless otherwise specified, the library is installed with PREFIX of the current directory.</i>
+
+If the code was retrieved from CVS, you will need to substitute 'autogen.sh' for 'configure' in
+above example.
+
+Other configuration options, such as location of external libraries, are also available.
+To get a list of options, type the following in the top psModule directory.
+<pre>
+$ configure --help
+</pre>
+A likely option needed is '--with-pslib-config', which specifies the location of
+the configuration script for psLib.  By default, configure searches for it using PATH, but that
+is not always sufficient.
+
+@section install How to Install
+
+To install the library using the prefix given in the configure step, execute in
+the top build directory:
+<pre>
+$ make install
+</pre>
+
+
+@section usage Building and Linking your code to the psModule library
+
+To assist the use of the library with your own code, a configuration tool is part
+of the psModule library package.  This tool, psmodule-config, is installed in the BIN
+directory, according to the options given to the configure script.
+
+The required CFLAG options for the compiler stage of code that uses psModule can be
+obtained via 'psmodule-config --cflags'.  This outputs the cc options that supplies
+include path(s) required to find the psModule headers.
+
+The required linking options, can be obtained via 'psmodule-config --libs'.  This
+outputs the ld options that supplies the library paths and files required to
+link to the psModule library.
+
+Note: psmodule-config usage above refers to the install locations of the library.  
+
+@section doc How to Create Code Documentation
+
+Both HTML and man page documentation may be generated from the inline
+documentation embedded in the code using the following commands:
+<pre>
+$ cd psModule
+$ make docs
+</pre>
+<i>This places documentation in PREFIX/docs.</i>
+
+Also, a prebuilt set of code documentation for both the releases and last
+CVS snapshot can be found at:
+
+https://mhpcc.pan-starrs.org/docs/
+
+*/
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/.cvsignore
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/.cvsignore	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/.cvsignore	(revision 22115)
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/Makefile.am
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/Makefile.am	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/Makefile.am	(revision 22115)
@@ -0,0 +1,28 @@
+noinst_LTLIBRARIES = libpsmoduleobjects.la
+
+libpsmoduleobjects_la_CPPFLAGS = $(SRCINC) $(PSMODULE_CFLAGS)
+libpsmoduleobjects_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+libpsmoduleobjects_la_SOURCES  = \
+    pmObjects.c \
+    pmPSF.c \
+    pmPSFtry.c \
+    pmModelGroup.c \
+    psModulesUtils.c \
+    psLibUtils.c \
+    psEllipse.c
+
+EXTRA_DIST = \
+	models/pmModel_GAUSS.c \
+	models/pmModel_PGAUSS.c \
+	models/pmModel_QGAUSS.c \
+	models/pmModel_SGAUSS.c 
+    
+psmoduleincludedir = $(includedir)
+psmoduleinclude_HEADERS = \
+    pmObjects.h \
+    pmPSF.h \
+    pmPSFtry.h \
+    pmModelGroup.h \
+    psModulesUtils.h \
+    psLibUtils.h \
+    psEllipse.h
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_GAUSS.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_GAUSS.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_GAUSS.c	(revision 22115)
@@ -0,0 +1,164 @@
+
+/******************************************************************************
+    params->data.F32[0] = So;
+    params->data.F32[1] = Zo;
+    params->data.F32[2] = Xo;
+    params->data.F32[3] = Yo;
+    params->data.F32[4] = sqrt(2.0) / SigmaX;
+    params->data.F32[5] = sqrt(2.0) / SigmaY;
+    params->data.F32[6] = Sxy;
+*****************************************************************************/
+
+psF32 pmModelFunc_GAUSS(psVector *deriv,
+                        const psVector *params,
+                        const psVector *x)
+{
+    psF32 X  = x->data.F32[0] - params->data.F32[2];
+    psF32 Y  = x->data.F32[1] - params->data.F32[3];
+    psF32 px = params->data.F32[4]*X;
+    psF32 py = params->data.F32[5]*Y;
+    psF32 z  = 0.5*PS_SQR(px) + 0.5*PS_SQR(py) + params->data.F32[6]*X*Y;
+    psF32 r  = exp(-z);
+    psF32 q  = params->data.F32[1]*r;
+    psF32 f  = q + params->data.F32[0];
+
+    if (deriv != NULL) {
+        deriv->data.F32[0] = +1.0;
+        deriv->data.F32[1] = +r;
+        deriv->data.F32[2] = q*(2*px*params->data.F32[4] + params->data.F32[6]*Y);
+        deriv->data.F32[3] = q*(2*py*params->data.F32[5] + params->data.F32[6]*X);
+        deriv->data.F32[4] = -2.0*q*px*X;
+        deriv->data.F32[5] = -2.0*q*py*Y;
+        deriv->data.F32[6] = -q*X*Y;
+    }
+    return(f);
+}
+
+psF64 pmModelFlux_GAUSS(const psVector *params)
+{
+    psF64 A1   = PS_SQR(params->data.F32[4]);
+    psF64 A2   = PS_SQR(params->data.F32[5]);
+    psF64 A3   = PS_SQR(params->data.F32[6]);
+    psF64 Area = 2.0 * M_PI / sqrt(A1*A2 - A3);
+    // Area is equivalent to 2 pi sigma^2
+
+    psF64 Flux = params->data.F32[1] * Area;
+
+    return(Flux);
+}
+
+// return the radius which yields the requested flux
+psF64 pmModelRadius_GAUSS  (const psVector *params, psF64 flux)
+{
+
+    if (flux <= 0)
+        return (1.0);
+    if (params->data.F32[1] <= 0)
+        return (1.0);
+    if (flux >= params->data.F32[1])
+        return (1.0);
+
+    psF64 sigma  = sqrt(2.0) * hypot (1.0 / params->data.F32[4], 1.0 / params->data.F32[5]);
+    psF64 radius = sigma * sqrt (2.0 * log(params->data.F32[1] / flux));
+    return (radius);
+}
+
+// define the parameter limits
+bool pmModelLimits_GAUSS (psVector **beta_lim, psVector **params_min, psVector **params_max)
+{
+
+    *beta_lim   = psVectorAlloc (7, PS_TYPE_F32);
+    *params_min = psVectorAlloc (7, PS_TYPE_F32);
+    *params_max = psVectorAlloc (7, PS_TYPE_F32);
+
+    beta_lim[0][0].data.F32[0] = 1000;
+    beta_lim[0][0].data.F32[1] = 10000;
+    beta_lim[0][0].data.F32[2] = 5;
+    beta_lim[0][0].data.F32[3] = 5;
+    beta_lim[0][0].data.F32[4] = 0.5;
+    beta_lim[0][0].data.F32[5] = 0.5;
+    beta_lim[0][0].data.F32[6] = 0.5;
+
+    params_min[0][0].data.F32[0] = -1000;
+    params_min[0][0].data.F32[1] = 0;
+    params_min[0][0].data.F32[2] = -100;
+    params_min[0][0].data.F32[3] = -100;
+    params_min[0][0].data.F32[4] = 0.01;
+    params_min[0][0].data.F32[5] = 0.01;
+    params_min[0][0].data.F32[6] = -5.0;
+
+    params_max[0][0].data.F32[0] = 1e5;
+    params_max[0][0].data.F32[1] = 1e6;
+    params_max[0][0].data.F32[2] = 1e4;  // this should be set by image dimensions!
+    params_max[0][0].data.F32[3] = 1e4;  // this should be set by image dimensions!
+    params_max[0][0].data.F32[4] = 2.0;
+    params_max[0][0].data.F32[5] = 2.0;
+    params_max[0][0].data.F32[6] = +5.0;
+
+    return (TRUE);
+}
+
+// make an initial guess for parameters
+bool pmModelGuess_GAUSS (pmModel *model, pmSource *source)
+{
+
+    pmMoments *moments = source->moments;
+    psF32     *params  = model->params->data.F32;
+
+    params[0] = moments->Sky;
+    params[1] = moments->Peak - moments->Sky;
+    params[2] = moments->x;
+    params[3] = moments->y;
+    params[4] = 1.2 / moments->Sx;
+    params[5] = 1.2 / moments->Sy;
+    params[6] = 0.0;
+    return(true);
+}
+
+// construct the PSF model from the FLT model and the psf
+bool pmModelFromPSF_GAUSS (pmModel *modelPSF, pmModel *modelFLT, pmPSF *psf)
+{
+
+    psF32 *out = modelPSF->params->data.F32;
+    psF32 *in  = modelFLT->params->data.F32;
+
+    out[0] = in[0];
+    out[1] = in[1];
+    out[2] = in[2];
+    out[3] = in[3];
+
+    for (int i = 4; i < 7; i++) {
+        psPolynomial2D *poly = psf->params->data[i-4];
+        // XXX: be wary of a bug here.  EAM had his own version of psPolynomial2DEval().
+        // I think the reason was that hewas using nX=xOrder.  Not sure.  I changed this
+        // without verifying.
+        // out[i] = Polynomial2DEval_EAM (poly, out[2], out[3]);
+        out[i] = psPolynomial2DEval(poly, out[2], out[3]);
+    }
+    return(true);
+}
+
+// check the status of the fitted model
+bool pmModelFitStatus_GAUSS (pmModel *model)
+{
+
+    psF32 dP;
+    bool  status;
+
+    psF32 *PAR  = model->params->data.F32;
+    psF32 *dPAR = model->dparams->data.F32;
+
+    dP = 0;
+    dP += PS_SQR(dPAR[4] / PAR[4]);
+    dP += PS_SQR(dPAR[5] / PAR[5]);
+    dP = sqrt (dP);
+
+    status = true;
+    status &= (dP < 0.5);
+    status &= (PAR[1] > 0);
+    status &= ((dPAR[1]/PAR[1]) < 0.5);
+
+    if (status)
+        return true;
+    return false;
+}
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_PGAUSS.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_PGAUSS.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_PGAUSS.c	(revision 22115)
@@ -0,0 +1,176 @@
+
+/******************************************************************************
+    params->data.F32[0] = So;
+    params->data.F32[1] = Zo;
+    params->data.F32[2] = Xo;
+    params->data.F32[3] = Yo;
+    params->data.F32[4] = sqrt(2.0) / SigmaX;
+    params->data.F32[5] = sqrt(2.0) / SigmaY;
+    params->data.F32[6] = Sxy;
+*****************************************************************************/
+
+psF32 pmModelFunc_PGAUSS(psVector *deriv,
+                         const psVector *params,
+                         const psVector *x)
+{
+    psF32 *PAR = params->data.F32;
+
+    psF32 X  = x->data.F32[0] - PAR[2];
+    psF32 Y  = x->data.F32[1] - PAR[3];
+    psF32 px = PAR[4]*X;
+    psF32 py = PAR[5]*Y;
+    psF32 z  = 0.5*PS_SQR(px) + 0.5*PS_SQR(py) + PAR[6]*X*Y;
+    psF32 t  = 1 + z + z*z/2.0;
+    psF32 r  = 1.0 / (t + z*z*z/6.0); /* exp (-Z) */
+    psF32 f  = PAR[1]*r + PAR[0];
+
+    if (deriv != NULL) {
+        // note difference from a pure gaussian: q = PAR[1]*r
+        psF32 q = PAR[1]*r*r*t;
+        deriv->data.F32[0] = +1.0;
+        deriv->data.F32[1] = +r;
+        deriv->data.F32[2] = q*(2.0*px*PAR[4] + PAR[6]*Y);
+        deriv->data.F32[3] = q*(2.0*py*PAR[5] + PAR[6]*X);
+        deriv->data.F32[4] = -2.0*q*px*X;
+        deriv->data.F32[5] = -2.0*q*py*Y;
+        deriv->data.F32[6] = -q*X*Y;
+    }
+    return(f);
+}
+
+bool pmModelLimits_PGAUSS (psVector **beta_lim, psVector **params_min, psVector **params_max)
+{
+
+    *beta_lim   = psVectorAlloc (7, PS_TYPE_F32);
+    *params_min = psVectorAlloc (7, PS_TYPE_F32);
+    *params_max = psVectorAlloc (7, PS_TYPE_F32);
+
+    beta_lim[0][0].data.F32[0] = 1000;
+    beta_lim[0][0].data.F32[1] = 10000;
+    beta_lim[0][0].data.F32[2] = 5;
+    beta_lim[0][0].data.F32[3] = 5;
+    beta_lim[0][0].data.F32[4] = 0.5;
+    beta_lim[0][0].data.F32[5] = 0.5;
+    beta_lim[0][0].data.F32[6] = 0.5;
+
+    params_min[0][0].data.F32[0] = -1000;
+    params_min[0][0].data.F32[1] = 0;
+    params_min[0][0].data.F32[2] = -100;
+    params_min[0][0].data.F32[3] = -100;
+    params_min[0][0].data.F32[4] = 0.01;
+    params_min[0][0].data.F32[5] = 0.01;
+    params_min[0][0].data.F32[6] = -5.0;
+
+    params_max[0][0].data.F32[0] = 1e5;
+    params_max[0][0].data.F32[1] = 1e6;
+    params_max[0][0].data.F32[2] = 1e4;  // this should be set by image dimensions!
+    params_max[0][0].data.F32[3] = 1e4;  // this should be set by image dimensions!
+    params_max[0][0].data.F32[4] = 2.0;
+    params_max[0][0].data.F32[5] = 2.0;
+    params_max[0][0].data.F32[6] = +5.0;
+
+    return (TRUE);
+}
+
+psF64 pmModelFlux_PGAUSS(const psVector *params)
+{
+    float f, norm, z;
+
+    psF32 *PAR = params->data.F32;
+
+    psF64 A1   = PS_SQR(PAR[4]);
+    psF64 A2   = PS_SQR(PAR[5]);
+    psF64 A3   = PS_SQR(PAR[6]);
+    psF64 Area = 2.0 * M_PI / sqrt(A1*A2 - A3);
+    // Area is equivalent to 2 pi sigma^2
+
+    // the area needs to be multiplied by the integral of f(z)
+    norm = 0.0;
+    for (z = 0.005; z < 50; z += 0.01) {
+        f = 1.0 / (1 + z + z*z/2 + z*z*z/6);
+        norm += f;
+    }
+    norm *= 0.01;
+
+    psF64 Flux = params->data.F32[1] * Area * norm;
+
+    return(Flux);
+}
+
+// define this function so it never returns Inf or NaN
+// return the radius which yields the requested flux
+psF64 pmModelRadius_PGAUSS  (const psVector *params, psF64 flux)
+{
+    if (flux <= 0)
+        return (1.0);
+    if (params->data.F32[1] <= 0)
+        return (1.0);
+    if (flux >= params->data.F32[1])
+        return (1.0);
+
+    psF64 sigma  = sqrt(2.0) * hypot (1.0 / params->data.F32[4], 1.0 / params->data.F32[5]);
+    psF64 radius = sigma * sqrt (2.0 * log(params->data.F32[1] / flux));
+    if (isnan(radius)) {
+        fprintf (stderr, "error in code\n");
+    }
+    return (radius);
+}
+
+bool pmModelGuess_PGAUSS (pmModel *model, pmSource *source)
+{
+
+    pmMoments *moments = source->moments;
+    psF32     *params  = model->params->data.F32;
+
+    params[0] = moments->Sky;
+    params[1] = moments->Peak - moments->Sky;
+    params[2] = moments->x;
+    params[3] = moments->y;
+    params[4] = 1.2 / moments->Sx;
+    params[5] = 1.2 / moments->Sy;
+    params[6] = 0.0;
+    return(true);
+}
+
+bool pmModelFromPSF_PGAUSS (pmModel *modelPSF, pmModel *modelFLT, pmPSF *psf)
+{
+
+    psF32 *out = modelPSF->params->data.F32;
+    psF32 *in  = modelFLT->params->data.F32;
+
+    out[0] = in[0];
+    out[1] = in[1];
+    out[2] = in[2];
+    out[3] = in[3];
+
+    for (int i = 4; i < 7; i++) {
+        psPolynomial2D *poly = psf->params->data[i-4];
+        // XXX: Verify this (from EAM change)
+        out[i] = psPolynomial2DEval(poly, out[2], out[3]);
+    }
+    return(true);
+}
+
+bool pmModelFitStatus_PGAUSS (pmModel *model)
+{
+
+    psF32 dP;
+    bool  status;
+
+    psF32 *PAR  = model->params->data.F32;
+    psF32 *dPAR = model->dparams->data.F32;
+
+    dP = 0;
+    dP += PS_SQR(dPAR[4] / PAR[4]);
+    dP += PS_SQR(dPAR[5] / PAR[5]);
+    dP = sqrt (dP);
+
+    status = true;
+    status &= (dP < 0.5);
+    status &= (PAR[1] > 0);
+    status &= ((dPAR[1]/PAR[1]) < 0.5);
+
+    if (status)
+        return true;
+    return false;
+}
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_QGAUSS.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_QGAUSS.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_QGAUSS.c	(revision 22115)
@@ -0,0 +1,203 @@
+
+/******************************************************************************
+    one component, two slopes:
+    1 / (1 + z^M + z^N)
+ 
+    params->data.F32[0] = So;
+    params->data.F32[1] = Zo;
+    params->data.F32[2] = Xo;
+    params->data.F32[3] = Yo;
+    params->data.F32[4] = sqrt(2.0) / SigmaX;
+    params->data.F32[5] = sqrt(2.0) / SigmaY;
+    params->data.F32[6] = Sxy;
+    params->data.F32[7] = 
+    params->data.F32[8] = 
+*****************************************************************************/
+
+psF32 pmModelFunc_QGAUSS(psVector *deriv,
+                         const psVector *params,
+                         const psVector *x)
+{
+    psF32 *PAR = params->data.F32;
+
+    psF32 X  = x->data.F32[0] - PAR[2];
+    psF32 Y  = x->data.F32[1] - PAR[3];
+    psF32 px = PAR[4]*X;
+    psF32 py = PAR[5]*Y;
+    psF32 z  = 0.5*PS_SQR(px) + 0.5*PS_SQR(py) + PAR[6]*X*Y;
+
+    psF32 r  = 1.0 / (1 + PAR[7]*z + pow(z, 2.25));
+    psF32 f  = PAR[1]*r + PAR[0];
+
+    if (deriv != NULL) {
+        // note difference from a pure gaussian: q = params->data.F32[1]*r
+        psF32 t = PAR[1]*r*r;
+        psF32 q = t*(PAR[7] + 2.25*pow(z, 1.25));
+
+        deriv->data.F32[0] = +1.0;
+        deriv->data.F32[1] = +r;
+        deriv->data.F32[2] = q*(2.0*px*PAR[4] + PAR[6]*Y);
+        deriv->data.F32[3] = q*(2.0*py*PAR[5] + PAR[6]*X);
+        deriv->data.F32[4] = -2.0*q*px*X;
+        deriv->data.F32[5] = -2.0*q*py*Y;
+        deriv->data.F32[6] = -q*X*Y;
+        deriv->data.F32[7] = -t*z;
+    }
+    return(f);
+}
+
+bool pmModelLimits_QGAUSS (psVector **beta_lim, psVector **params_min, psVector **params_max)
+{
+
+    *beta_lim   = psVectorAlloc (8, PS_TYPE_F32);
+    *params_min = psVectorAlloc (8, PS_TYPE_F32);
+    *params_max = psVectorAlloc (8, PS_TYPE_F32);
+
+    beta_lim[0][0].data.F32[0] = 1000;
+    beta_lim[0][0].data.F32[1] = 10000;
+    beta_lim[0][0].data.F32[2] = 5;
+    beta_lim[0][0].data.F32[3] = 5;
+    beta_lim[0][0].data.F32[4] = 0.5;
+    beta_lim[0][0].data.F32[5] = 0.5;
+    beta_lim[0][0].data.F32[6] = 0.5;
+    beta_lim[0][0].data.F32[7] = 0.5;
+
+    params_min[0][0].data.F32[0] = -1000;
+    params_min[0][0].data.F32[1] = 0;
+    params_min[0][0].data.F32[2] = -100;
+    params_min[0][0].data.F32[3] = -100;
+    params_min[0][0].data.F32[4] = 0.01;
+    params_min[0][0].data.F32[5] = 0.01;
+    params_min[0][0].data.F32[6] = -5.0;
+    params_min[0][0].data.F32[7] = 0.1;
+
+    params_max[0][0].data.F32[0] = 1e5;
+    params_max[0][0].data.F32[1] = 1e6;
+    params_max[0][0].data.F32[2] = 1e4;  // this should be set by image dimensions!
+    params_max[0][0].data.F32[3] = 1e4;  // this should be set by image dimensions!
+    params_max[0][0].data.F32[4] = 2.0;
+    params_max[0][0].data.F32[5] = 2.0;
+    params_max[0][0].data.F32[6] = +5.0;
+    params_max[0][0].data.F32[7] = 10.0;
+
+    return (TRUE);
+}
+
+bool pmModelGuess_QGAUSS (pmModel *model, pmSource *source)
+{
+
+    pmMoments *moments = source->moments;
+    pmPeak    *peak    = source->peak;
+    psF32     *params  = model->params->data.F32;
+
+    params[0] = moments->Sky;
+    params[1] = moments->Peak - moments->Sky;
+    params[2] = peak->x;
+    params[3] = peak->y;
+    params[4] = 1.2 / moments->Sx;
+    params[5] = 1.2 / moments->Sy;
+    params[6] = 0.0;
+    params[7] = 1.0;
+    return(true);
+}
+
+psF64 pmModelFlux_QGAUSS(const psVector *params)
+{
+    float f, norm, z;
+
+    psF32 *PAR = params->data.F32;
+
+    psF64 A1   = PS_SQR(PAR[4]);
+    psF64 A2   = PS_SQR(PAR[5]);
+    psF64 A3   = PS_SQR(PAR[6]);
+    psF64 Area = 2.0 * M_PI / sqrt(A1*A2 - A3);
+    // Area is equivalent to 2 pi sigma^2
+
+    // the area needs to be multiplied by the integral of f(z)
+    norm = 0.0;
+    for (z = 0.005; z < 50; z += 0.01) {
+        f = 1.0 / (1 + PAR[7]*z + pow(z, 2.25));
+        norm += f;
+    }
+    norm *= 0.01;
+
+    psF64 Flux = PAR[1] * Area * norm;
+
+    return(Flux);
+}
+
+// define this function so it never returns Inf or NaN
+// return the radius which yields the requested flux
+psF64 pmModelRadius_QGAUSS  (const psVector *params, psF64 flux)
+{
+    psF64 z, f;
+    psF32 *PAR = params->data.F32;
+
+    if (flux <= 0)
+        return (1.0);
+    if (PAR[1] <= 0)
+        return (1.0);
+    if (flux >= PAR[1])
+        return (1.0);
+
+    // if Sx == Sy, sigma = Sx == Sy
+    psF64 sigma = hypot (1.0 / PAR[4], 1.0 / PAR[5]) / sqrt(2.0);
+    psF64 dz = 1.0 / (2.0 * sigma*sigma);
+    psF64 limit = flux / PAR[1];
+
+    // we can do this much better with intelligent choices here
+    for (z = 0.0; z < 20.0; z += dz) {
+        f = 1.0 / (1 + PAR[7]*z + pow(z, 2.25));
+        if (f < limit)
+            break;
+    }
+    psF64 radius = sigma * sqrt (2.0 * z);
+    if (isnan(radius)) {
+        fprintf (stderr, "error in code\n");
+    }
+    return (radius);
+}
+
+bool pmModelFromPSF_QGAUSS (pmModel *modelPSF, pmModel *modelFLT, pmPSF *psf)
+{
+
+    psF32 *out = modelPSF->params->data.F32;
+    psF32 *in  = modelFLT->params->data.F32;
+
+    out[0] = in[0];
+    out[1] = in[1];
+    out[2] = in[2];
+    out[3] = in[3];
+
+    for (int i = 4; i < 8; i++) {
+        psPolynomial2D *poly = psf->params->data[i-4];
+        // XXX: Verify this (from EAM change)
+        //out[i] = Polynomial2DEval_EAM(poly, out[2], out[3]);
+        out[i] = psPolynomial2DEval(poly, out[2], out[3]);
+    }
+    return(true);
+}
+
+bool pmModelFitStatus_QGAUSS (pmModel *model)
+{
+
+    psF32 dP;
+    bool  status;
+
+    psF32 *PAR  = model->params->data.F32;
+    psF32 *dPAR = model->dparams->data.F32;
+
+    dP = 0;
+    dP += PS_SQR(dPAR[4] / PAR[4]);
+    dP += PS_SQR(dPAR[5] / PAR[5]);
+    dP = sqrt (dP);
+
+    status = true;
+    status &= (dP < 0.5);
+    status &= (PAR[1] > 0);
+    status &= ((dPAR[1]/PAR[1]) < 0.5);
+
+    if (status)
+        return true;
+    return false;
+}
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_RGAUSS.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_RGAUSS.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_RGAUSS.c	(revision 22115)
@@ -0,0 +1,157 @@
+
+/******************************************************************************
+    one component, two slopes:
+    1 / (1 + z + z^Npow)
+ 
+    params->data.F32[0] = So;
+    params->data.F32[1] = Zo;
+    params->data.F32[2] = Xo;
+    params->data.F32[3] = Yo;
+    params->data.F32[4] = 1 / SigmaX;
+    params->data.F32[5] = 1 / SigmaY;
+    params->data.F32[6] = Sxy;
+    params->data.F32[7] = Npow
+*****************************************************************************/
+
+psF64 psModelFunc_RGAUSS(psVector *deriv,
+                         const psVector *params,
+                         const psVector *x)
+{
+    psF32 *PAR = params->data.F32;
+
+    psF32 X  = x->data.F32[0] - PAR[2];
+    psF32 Y  = x->data.F32[1] - PAR[3];
+    psF32 px = PAR[4]*X;
+    psF32 py = PAR[5]*Y;
+    psF32 z  = 0.5*PS_SQR(px) + 0.5*PS_SQR(py) + PAR[6]*X*Y;
+
+    // psF32 FACT = 1 + 5*exp(-5*PAR[7]);
+
+    psF32 p  = pow(z, PAR[7] - 1.0);
+    psF32 r  = 1.0 / (1 + z + z*p);
+    psF32 f  = PAR[1]*r + PAR[0];
+
+    if (deriv != NULL) {
+        // note difference from a pure gaussian: q = params->data.F32[1]*r
+        psF32 t = PAR[1]*r*r;
+        psF32 q = t*(1 + PAR[7]*p);
+
+        deriv->data.F32[0] = +1.0;
+        deriv->data.F32[1] = +r;
+        deriv->data.F32[2] = q*(2.0*px*PAR[4] + PAR[6]*Y);
+        deriv->data.F32[3] = q*(2.0*py*PAR[5] + PAR[6]*X);
+        deriv->data.F32[4] = -q*px*X*2;
+        deriv->data.F32[5] = -q*py*Y*2;
+        deriv->data.F32[6] = -q*X*Y;
+        deriv->data.F32[7] = -5.0*t*log(z)*p*z;
+
+        // deriv->data.F32[4] = -1.8*q*px*X*2;
+        // deriv->data.F32[5] = -1.8*q*py*Y*2;
+        // deriv->data.F32[6] = -1.5*q*X*Y;
+        // deriv->data.F32[7] = -5.0*t*log(z)*p*z;
+    }
+    return(f);
+}
+
+psF64 psModelFlux_RGAUSS(const psVector *params)
+{
+    float f, norm, z;
+
+    psF32 *PAR = params->data.F32;
+
+    psF64 A1   = PS_SQR(PAR[4]);
+    psF64 A2   = PS_SQR(PAR[5]);
+    psF64 A3   = PS_SQR(PAR[6]);
+    psF64 Area = 2.0 * M_PI / sqrt(A1*A2 - A3);
+    // Area is equivalent to 2 pi sigma^2
+
+    // the area needs to be multiplied by the integral of f(z)
+    norm = 0.0;
+    for (z = 0.005; z < 50; z += 0.01) {
+        f = 1.0 / (1 + z + pow(z, PAR[7]));
+        norm += f;
+    }
+    norm *= 0.01;
+
+    psF64 Flux = params->data.F32[1] * Area * norm;
+
+    return(Flux);
+}
+
+// define this function so it never returns Inf or NaN
+// return the radius which yields the requested flux
+psF64 psModelRadius_RGAUSS  (const psVector *params, psF64 flux)
+{
+    psF64 z, f, p;
+    psF32 *PAR = params->data.F32;
+
+    if (flux <= 0)
+        return (1.0);
+    if (PAR[1] <= 0)
+        return (1.0);
+    if (flux >= PAR[1])
+        return (1.0);
+
+    // if Sx == Sy, sigma = Sx == Sy
+    psF64 sigma = hypot (1.0 / PAR[4], 1.0 / PAR[5]) / sqrt(2.0);
+    psF64 dz = 1.0 / (2.0 * sigma*sigma);
+    psF64 limit = flux / PAR[1];
+
+    // we can do this much better with intelligent choices here
+    for (z = 0.0; z < 20.0; z += dz) {
+        p = pow(z, PAR[7]);
+        f = 1.0 / (1 + z + p);
+        if (f < limit)
+            break;
+    }
+    psF64 radius = sigma * sqrt (2.0 * z);
+    if (isnan(radius)) {
+        fprintf (stderr, "error in code\n");
+    }
+    return (radius);
+}
+
+bool psModelGuess_RGAUSS (psModel *model, psSource *source)
+{
+
+    psVector *params = model->params;
+
+    EllipseAxes axes;
+    EllipseShape shape;
+    EllipseMoments moments;
+
+    moments.x2 = PS_SQR(source->moments->Sx);
+    moments.y2 = PS_SQR(source->moments->Sy);
+    moments.xy = source->moments->Sxy;
+
+    axes = EllipseMomentsToAxes(moments);
+    shape = EllipseAxesToShape(axes);
+
+    params->data.F32[0] = source->moments->Sky;
+    params->data.F32[1] = source->peak->counts - source->moments->Sky;
+    params->data.F32[2] = source->moments->x;
+    params->data.F32[3] = source->moments->y;
+    params->data.F32[4] = 1.0 / shape.sx;
+    params->data.F32[5] = 1.0 / shape.sy;
+    params->data.F32[6] = shape.sxy;
+    params->data.F32[7] = 2.0;
+    return(true);
+}
+
+bool psModelFromPSF_RGAUSS (psModel *modelPSF, psModel *modelFLT, pmPSF *psf)
+{
+
+    psF32 *out = modelPSF->params->data.F32;
+    psF32 *in  = modelFLT->params->data.F32;
+
+    out[0] = in[0];
+    out[1] = in[1];
+    out[2] = in[2];
+    out[3] = in[3];
+
+    for (int i = 4; i < 8; i++) {
+        psPolynomial2D *poly = psf->params->data[i-4];
+        out[i] = Polynomial2DEval (poly, out[2], out[3]);
+    }
+    return(true);
+}
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_SGAUSS.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_SGAUSS.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_SGAUSS.c	(revision 22115)
@@ -0,0 +1,357 @@
+
+
+/******************************************************************************
+    one component, two slopes:
+    1 / (1 + z^Npow + St*z^Ntide)
+ 
+    params->data.F32[0] = So;
+    params->data.F32[1] = Zo;
+    params->data.F32[2] = Xo;
+    params->data.F32[3] = Yo;
+    params->data.F32[4] = 1 / SigmaX;
+    params->data.F32[5] = 1 / SigmaY;
+    params->data.F32[6] = Sxy;
+    params->data.F32[7] = Npow
+    params->data.F32[8] = St
+*****************************************************************************/
+
+# define SQ(A)((A)*(A))
+psF64 psImageEllipseContour (EllipseAxes axes, double xc, double yc, psImage *image);
+psF64 p_psImageGetElementF64(psImage *a, int i, int j);
+
+psF32 pmModelFunc_SGAUSS(psVector *deriv,
+                         const psVector *params,
+                         const psVector *x)
+{
+    psF32 *PAR = params->data.F32;
+
+    psF32 X  = x->data.F32[0] - PAR[2];
+    psF32 Y  = x->data.F32[1] - PAR[3];
+    psF32 px = PAR[4]*X;
+    psF32 py = PAR[5]*Y;
+    psF32 z  = PS_MAX((0.5*PS_SQR(px) + 0.5*PS_SQR(py) + PAR[6]*X*Y), 1e-8);
+    // note that if z -> 0, dPAR[7] -> -inf
+    // also z^(PAR[7]-1) -> Inf
+
+    psF32 pr = z*PAR[8];
+    psF32 pr3 = pr*pr*pr;
+    psF32 p  = pow(z, PAR[7] - 1.0);
+    psF32 r  = 1.0 / (1 + z*p + pr*pr3);
+    psF32 f  = PAR[1]*r + PAR[0];
+
+    if (deriv != NULL) {
+        // note difference from a pure gaussian: q = params->data.F32[1]*r
+        psF32 t = PAR[1]*r*r;
+        psF32 q = t*(PAR[7]*p + 4*PAR[8]*pr3);
+
+        deriv->data.F32[0] = +1.0;
+        deriv->data.F32[1] = +r;
+        deriv->data.F32[2] = q*(2.0*px*PAR[4] + PAR[6]*Y);
+        deriv->data.F32[3] = q*(2.0*py*PAR[5] + PAR[6]*X);
+        deriv->data.F32[4] = -2.0*q*px*X;
+        deriv->data.F32[5] = -2.0*q*py*Y;
+        deriv->data.F32[6] = -q*X*Y;
+        deriv->data.F32[7] = -2*t*log(z)*z*p;
+        deriv->data.F32[8] = -2*t*4*z*pr3;
+    }
+    return(f);
+}
+
+bool pmModelLimits_SGAUSS (psVector **beta_lim, psVector **params_min, psVector **params_max)
+{
+
+    *beta_lim   = psVectorAlloc (9, PS_TYPE_F32);
+    *params_min = psVectorAlloc (9, PS_TYPE_F32);
+    *params_max = psVectorAlloc (9, PS_TYPE_F32);
+
+    beta_lim[0][0].data.F32[0] = 1000;
+    beta_lim[0][0].data.F32[1] = 10000;
+    beta_lim[0][0].data.F32[2] = 5;
+    beta_lim[0][0].data.F32[3] = 5;
+    beta_lim[0][0].data.F32[4] = 0.5;
+    beta_lim[0][0].data.F32[5] = 0.5;
+    beta_lim[0][0].data.F32[6] = 0.5;
+    beta_lim[0][0].data.F32[7] = 0.5;
+    beta_lim[0][0].data.F32[8] = 0.05;
+
+    params_min[0][0].data.F32[0] = -1000;
+    params_min[0][0].data.F32[1] = 0;
+    params_min[0][0].data.F32[2] = -100;
+    params_min[0][0].data.F32[3] = -100;
+    params_min[0][0].data.F32[4] = 0.01;
+    params_min[0][0].data.F32[5] = 0.01;
+    params_min[0][0].data.F32[6] = -5.0;
+    params_min[0][0].data.F32[7] = 0.5;
+    params_min[0][0].data.F32[8] = 0.001;
+
+    params_max[0][0].data.F32[0] = 1e5;
+    params_max[0][0].data.F32[1] = 1e6;
+    params_max[0][0].data.F32[2] = 1e4;  // this should be set by image dimensions!
+    params_max[0][0].data.F32[3] = 1e4;  // this should be set by image dimensions!
+    params_max[0][0].data.F32[4] = 2.0;
+    params_max[0][0].data.F32[5] = 2.0;
+    params_max[0][0].data.F32[6] = +3.0;
+    params_max[0][0].data.F32[7] = 5.0;
+    params_max[0][0].data.F32[8] = 0.5;
+
+    return (TRUE);
+}
+
+// measure the flux for the elliptical contour
+psF64 psImageEllipseContour (EllipseAxes axes, double xc, double yc, psImage *image)
+{
+
+    double t, dt, ct, st, xo, yo, value;
+    int N, Nt, x, y;
+
+    // choose dt to uniformly divide contour, with ~1 pix spacing at most
+    dt = asin (1 / axes.minor);
+    Nt = (int)(2*M_PI / dt) + 1;
+    dt = 2*M_PI / Nt;
+
+    ct = cos(axes.theta);
+    st = sin(axes.theta);
+    xo = xc - image->col0;
+    yo = yc - image->row0;
+
+    psVector *contour = psVectorAlloc (Nt, PS_TYPE_F32);
+    for (t = 0, N = 0; (t < 2*M_PI) && (N < Nt); t += dt) {
+        x = ct*axes.major*cos(t) + st*axes.minor*sin(t) + xo;
+        y = ct*axes.minor*sin(t) + st*axes.major*cos(t) + yo;
+        value = p_psImageGetElementF64(image, x, y);
+        if (isfinite(value)) {
+            contour->data.F32[N] = value;
+            N++;
+        }
+    }
+    contour->n = N;
+    // accept every pixel: double counting is not so problematic here...
+
+    psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+    psVectorStats (stats, contour, NULL, NULL, 0);
+    value = stats->sampleMedian;
+
+    psFree (stats);
+    psFree (contour);
+
+    return (value);
+}
+
+bool pmModelGuess_SGAUSS (pmModel *model, pmSource *source)
+{
+
+    pmMoments *sMoments = source->moments;
+    pmPeak    *peak     = source->peak;
+    psF32     *params   = model->params->data.F32;
+
+    EllipseAxes axes;
+    EllipseShape shape;
+    EllipseMoments moments;
+
+    moments.x2 = PS_SQR(sMoments->Sx);
+    moments.y2 = PS_SQR(sMoments->Sy);
+    moments.xy = sMoments->Sxy;
+
+    // solve the math to go from Moments To Shape
+    axes = EllipseMomentsToAxes(moments);
+    shape = EllipseAxesToShape(axes);
+
+    params[0] = sMoments->Sky;
+    params[1] = sMoments->Peak - sMoments->Sky;
+    params[2] = peak->x;
+    params[3] = peak->y;
+    params[4] = 1.0 / shape.sx;
+    params[5] = 1.0 / shape.sy;
+    params[6] = shape.sxy;
+
+    # if (0)
+
+        f1 = psImageEllipseContour (axes, peak->x, peak->y, source->pixels);
+    axes.major *= 2.0;
+    axes.minor *= 2.0;
+    f2 = psImageEllipseContour (axes, peak->x, peak->y, source->pixels);
+
+    if (f1 > f2) {
+        params[7] = PS_MIN (3.0, PS_MAX (0.5, log(2.0*(f1/f2) - 1.0) / log(2.0)));
+    } else {
+        params[7] = 0.5;
+    }
+    # endif
+
+    params[7] = 1.8;
+    params[8] = 0.1;
+
+
+    return(true);
+}
+
+// XXX EAM : test version using flux contours to guess slope
+bool pmModelGuess_SGAUSS_HARD (pmModel *model, pmSource *source)
+{
+
+    pmMoments *sMoments = source->moments;
+    pmPeak    *peak     = source->peak;
+    psF32     *params   = model->params->data.F32;
+    float f1, f2;
+
+    EllipseAxes axes;
+    EllipseShape shape;
+    EllipseMoments moments;
+
+    moments.x2 = PS_SQR(sMoments->Sx);
+    moments.y2 = PS_SQR(sMoments->Sy);
+    moments.xy = sMoments->Sxy;
+
+    // solve the math to go from Moments To Shape
+    axes = EllipseMomentsToAxes(moments);
+    shape = EllipseAxesToShape(axes);
+
+    params[0] = sMoments->Sky;
+    params[1] = sMoments->Peak - sMoments->Sky;
+    params[2] = peak->x;
+    params[3] = peak->y;
+    params[4] = 1.0 / shape.sx;
+    params[5] = 1.0 / shape.sy;
+    params[6] = shape.sxy;
+
+    f1 = psImageEllipseContour (axes, peak->x, peak->y, source->pixels);
+    axes.major *= 2.0;
+    axes.minor *= 2.0;
+    f2 = psImageEllipseContour (axes, peak->x, peak->y, source->pixels);
+
+    if (f1 > f2) {
+        params[7] = PS_MIN (3.0, PS_MAX (0.5, log(2.0*(f1/f2) - 1.0) / log(2.0)));
+    } else {
+        params[7] = 0.5;
+    }
+    params[8] = 0.1;
+
+    return(true);
+}
+
+psF64 pmModelFlux_SGAUSS(const psVector *params)
+{
+    float f, norm, z;
+
+    psF32 *PAR = params->data.F32;
+
+    psF64 A1   = PS_SQR(PAR[4]);
+    psF64 A2   = PS_SQR(PAR[5]);
+    psF64 A3   = PS_SQR(PAR[6]);
+    psF64 Area = 2.0 * M_PI / sqrt(A1*A2 - A3);
+    // Area is equivalent to 2 pi sigma^2
+
+    // the area needs to be multiplied by the integral of f(z)
+    norm = 0.0;
+    for (z = 0.005; z < 50; z += 0.01) {
+        psF32 pr = PAR[8]*z;
+        f = 1.0 / (1 + pow(z, PAR[7]) + SQ(SQ(pr)));
+        norm += f;
+    }
+    norm *= 0.01;
+
+    psF64 Flux = PAR[1] * Area * norm;
+
+    return(Flux);
+}
+
+// XXX need to define the radius along the major axis
+// define this function so it never returns Inf or NaN
+// return the radius which yields the requested flux
+psF64 pmModelRadius_SGAUSS  (const psVector *params, psF64 flux)
+{
+    psF64 r, z, pr, f;
+    psF32 *PAR = params->data.F32;
+
+    EllipseAxes axes;
+    EllipseShape shape;
+
+    if (flux <= 0)
+        return (1.0);
+    if (PAR[1] <= 0)
+        return (1.0);
+    if (flux >= PAR[1])
+        return (1.0);
+
+    // convert Sx,Sy,Sxy to major/minor axes
+    shape.sx = 1.0 / PAR[4];
+    shape.sy = 1.0 / PAR[5];
+    shape.sxy = PAR[6];
+
+    axes = EllipseShapeToAxes (shape);
+    psF64 dr = 1.0 / axes.major;
+    psF64 limit = flux / PAR[1];
+
+    // XXX : we can do this faster with an intelligent starting choice
+    for (r = 0.0; r < 20.0; r += dr) {
+        z = SQ(r);
+        pr = PAR[8]*z;
+        f = 1.0 / (1 + pow(z, PAR[7]) + SQ(SQ(pr)));
+        if (f < limit)
+            break;
+    }
+    psF64 radius = 2.0 * axes.major * sqrt (z);
+    if (isnan(radius)) {
+        fprintf (stderr, "error in code\n");
+    }
+    return (radius);
+}
+
+bool pmModelFromPSF_SGAUSS (pmModel *modelPSF, pmModel *modelFLT, pmPSF *psf)
+{
+
+    psF32 *out = modelPSF->params->data.F32;
+    psF32 *in  = modelFLT->params->data.F32;
+
+    out[0] = in[0];
+    out[1] = in[1];
+    out[2] = in[2];
+    out[3] = in[3];
+
+    for (int i = 4; i < 9; i++) {
+        psPolynomial2D *poly = psf->params->data[i-4];
+        // XXX: Verify this (from EAM change)
+        //out[i] = Polynomial2DEval_EAM(poly, out[2], out[3]);
+        out[i] = psPolynomial2DEval(poly, out[2], out[3]);
+    }
+    return(true);
+}
+
+bool pmModelFitStatus_SGAUSS (pmModel *model)
+{
+
+    psF32 dP;
+    bool  status;
+    EllipseAxes axes;
+    EllipseShape shape;
+
+    psF32 *PAR  = model->params->data.F32;
+    psF32 *dPAR = model->dparams->data.F32;
+
+    shape.sx = 1.0 / PAR[4];
+    shape.sy = 1.0 / PAR[5];
+    shape.sxy = PAR[6];
+
+    axes = EllipseShapeToAxes (shape);
+
+    dP = 0;
+    dP += PS_SQR(dPAR[4] / PAR[4]);
+    dP += PS_SQR(dPAR[5] / PAR[5]);
+    dP += PS_SQR(dPAR[7] / PAR[7]);
+    dP = sqrt (dP);
+
+    status = true;
+    status &= (dP < 0.5);
+    status &= (PAR[1] > 0);
+    status &= ((dPAR[1]/PAR[1]) < 0.5);
+    status &= (fabs(PAR[8]) < 0.5);
+    status &= (dPAR[8] < 0.1);
+    status &= (axes.major > 1.41);
+    status &= (axes.minor > 1.41);
+    status &= ((axes.major / axes.minor) < 5.0);
+    status &= (PAR[7] > 0.5);
+
+    if (status)
+        return true;
+    return false;
+}
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_TGAUSS.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_TGAUSS.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_TGAUSS.c	(revision 22115)
@@ -0,0 +1,114 @@
+
+/******************************************************************************
+    one component, two slopes:
+    1 / (1 + z^M + z^N)
+ 
+    params->data.F32[0] = So;
+    params->data.F32[1] = Zo;
+    params->data.F32[2] = Xo;
+    params->data.F32[3] = Yo;
+    params->data.F32[4] = sqrt(2.0) / SigmaX;
+    params->data.F32[5] = sqrt(2.0) / SigmaY;
+    params->data.F32[6] = Sxy;
+    params->data.F32[7] = 
+*****************************************************************************/
+
+psF64 psModelFunc_TGAUSS(psVector *deriv,
+                         const psVector *params,
+                         const psVector *x)
+{
+    psF32 *PAR = params->data.F32;
+
+    psF32 X  = x->data.F32[0] - PAR[2];
+    psF32 Y  = x->data.F32[1] - PAR[3];
+    psF32 px = PAR[4]*X;
+    psF32 py = PAR[5]*Y;
+    psF32 z  = 0.5*PS_SQR(px) + 0.5*PS_SQR(py) + PAR[6]*X*Y;
+
+    psF32 p  = pow(z, 1.2);
+    psF32 r  = 1.0 / (1 + z + PAR[7]*z*p);
+    psF32 f  = PAR[1]*r + PAR[0];
+
+    if (deriv != NULL) {
+        // note difference from a pure gaussian: q = params->data.F32[1]*r
+        psF32 t = PAR[1]*r*r;
+        psF32 q = t*(1 + PAR[7]*2.2*p);
+
+        deriv->data.F32[0] = +1.0;
+        deriv->data.F32[1] = +r;
+        deriv->data.F32[2] = q*(2.0*px*PAR[4] + PAR[6]*Y);
+        deriv->data.F32[3] = q*(2.0*py*PAR[5] + PAR[6]*X);
+        deriv->data.F32[4] = -2.0*q*px*X;
+        deriv->data.F32[5] = -2.0*q*py*Y;
+        deriv->data.F32[6] = -q*X*Y;
+        deriv->data.F32[7] = -t*z*p;
+    }
+    return(f);
+}
+
+psF64 psModelFlux_TGAUSS(const psVector *params)
+{
+    psF64 A1   = 1 / PS_SQR(params->data.F32[4]);
+    psF64 A2   = 1 / PS_SQR(params->data.F32[5]);
+    psF64 A3   = params->data.F32[6];
+    psF64 Area = 2.0 * M_PI / sqrt(A1*A2 - PS_SQR(A3));
+    // Area is equivalent to 2 pi sigma^2
+
+    psF64 Flux = params->data.F32[1] * Area;
+
+    return(Flux);
+}
+
+// define this function so it never returns Inf or NaN
+// return the radius which yields the requested flux
+psF64 psModelRadius_TGAUSS  (const psVector *params, psF64 flux)
+{
+    if (flux <= 0)
+        return (1.0);
+    if (params->data.F32[1] <= 0)
+        return (1.0);
+    if (flux >= params->data.F32[1])
+        return (1.0);
+
+    psF64 sigma  = sqrt(2.0) * hypot (1.0 / params->data.F32[4], 1.0 / params->data.F32[5]);
+    psF64 radius = sigma * sqrt (2.0 * log(params->data.F32[1] / flux));
+    if (isnan(radius)) {
+        fprintf (stderr, "error in code\n");
+    }
+    return (radius);
+}
+
+bool psModelGuess_TGAUSS (psModel *model, psSource *source)
+{
+
+    psVector *params = model->params;
+
+    params->data.F32[0] = source->moments->Sky;
+    params->data.F32[1] = source->peak->counts - source->moments->Sky;
+    params->data.F32[2] = source->moments->x;
+    params->data.F32[3] = source->moments->y;
+    params->data.F32[4] = 1.0/source->moments->Sx;
+    params->data.F32[5] = 1.0/source->moments->Sy;
+    // params->data.F32[6] = source->moments->Sxy;
+    params->data.F32[6] = 0.0;
+    params->data.F32[7] = 5.0;
+    return(true);
+}
+
+bool psModelFromPSF_TGAUSS (psModel *modelPSF, psModel *modelFLT, pmPSF *psf)
+{
+
+    psF32 *out = modelPSF->params->data.F32;
+    psF32 *in  = modelFLT->params->data.F32;
+
+    out[0] = in[0];
+    out[1] = in[1];
+    out[2] = in[2];
+    out[3] = in[3];
+
+    for (int i = 4; i < 8; i++) {
+        psPolynomial2D *poly = psf->params->data[i-4];
+        out[i] = Polynomial2DEval (poly, out[2], out[3]);
+    }
+    return(true);
+}
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_WAUSS.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_WAUSS.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_WAUSS.c	(revision 22115)
@@ -0,0 +1,105 @@
+
+/******************************************************************************
+    params->data.F32[0] = So;
+    params->data.F32[1] = Zo;
+    params->data.F32[2] = Xo;
+    params->data.F32[3] = Yo;
+    params->data.F32[4] = sqrt(2.0) / SigmaX;
+    params->data.F32[5] = sqrt(2.0) / SigmaY;
+    params->data.F32[6] = Sxy;
+*****************************************************************************/
+
+psF64 psModelFunc_WAUSS(psVector *deriv,
+                        const psVector *params,
+                        const psVector *x)
+{
+    psF32 X = x->data.F32[0] - params->data.F32[2];
+    psF32 Y = x->data.F32[1] - params->data.F32[2];
+    psF32 px = params->data.F32[4]*X;
+    psF32 py = params->data.F32[5]*Y;
+    psF32 z = 0.5*PS_SQR(px) + 0.5*PS_SQR(py) + params->data.F32[6]*X*Y;
+    psF32 t = 0.5*z*z*(1.0 + params->data.F32[8]*z/3.0);
+    psF32 r = 1.0 / (1.0 + z + params->data.F32[7]*t); /* exp (-Z) */
+    psF32 f = params->data.F32[1]*r + params->data.F32[0];
+
+    if (deriv != NULL) {
+        // note difference from gaussian: q = params->data.F32[1]*r
+        psF32 q = params->data.F32[1]*r*r*(1.0 + params->data.F32[7]*z*(1.0 + params->data.F32[8]*z/2.0));
+        deriv->data.F32[0] = +1.0;
+        deriv->data.F32[1] = +r;
+        deriv->data.F32[2] = q*(2.0*px*params->data.F32[4] + params->data.F32[6]*Y);
+        deriv->data.F32[3] = q*(2.0*py*params->data.F32[5] + params->data.F32[6]*X);
+        deriv->data.F32[4] = -2.0*q*px*X;
+        deriv->data.F32[5] = -2.0*q*py*Y;
+        deriv->data.F32[6] = -q*X*Y;
+        deriv->data.F32[7] = -100.0*params->data.F32[1]*r*r*t;
+        deriv->data.F32[8] = -100.0*params->data.F32[1]*r*r*params->data.F32[7]*(z*z*z)/6.0;
+        // The values of 100 dampen the swing of params->data.F32[7,8] */
+    }
+    return(f);
+}
+
+// this is probably wrong since it uses the gauss integral 2 pi sigma^2
+psF64 psModelFlux_WAUSS(const psVector *params)
+{
+    psF64 A1   = 1 / PS_SQR(params->data.F32[4]);
+    psF64 A2   = 1 / PS_SQR(params->data.F32[5]);
+    psF64 A3   = params->data.F32[6];
+    psF64 Area = 2.0 * M_PI / sqrt(A1*A2 - PS_SQR(A3));
+    // Area is equivalent to 2 pi sigma^2
+
+    psF64 Flux = params->data.F32[1] * Area;
+
+    return(Flux);
+}
+
+// return the radius which yields the requested flux
+psF64 psModelRadius_WAUSS  (const psVector *params, psF64 flux)
+{
+    if (flux <= 0)
+        return (1.0);
+    if (params->data.F32[1] <= 0)
+        return (1.0);
+    if (flux >= params->data.F32[1] <= 0)
+        return (1.0);
+
+    psF64 sigma  = sqrt(2.0) * hypot (1.0 / params->data.F32[4], 1.0 / params->data.F32[5]);
+    psF64 radius = sigma * sqrt (2.0 * log(params->data.F32[1] / flux));
+    return (radius);
+}
+
+bool psModelGuess_WAUSS (psModel *model, psSource *source)
+{
+
+    psVector *params = model->params;
+
+    params->data.F32[0] = source->moments->Sky;
+    params->data.F32[1] = source->peak->counts - source->moments->Sky;
+    params->data.F32[2] = source->moments->x;
+    params->data.F32[3] = source->moments->y;
+    params->data.F32[4] = sqrt(2.0) / source->moments->Sx;
+    params->data.F32[5] = sqrt(2.0) / source->moments->Sy;
+    params->data.F32[6] = source->moments->Sxy;
+    // XXX: What are these?
+    // params->data.F32[7] = B2;
+    // params->data.F32[8] = B3;
+    return(true);
+}
+
+bool psModelFromPSF_WAUSS (psModel *modelPSF, psModel *modelFLT, pmPSF *psf)
+{
+
+    psF32 *out = modelPSF->params->data.F32;
+    psF32 *in  = modelFLT->params->data.F32;
+
+    out[0] = in[0];
+    out[1] = in[1];
+    out[2] = in[2];
+    out[3] = in[3];
+
+    for (int i = 4; i < 9; i++) {
+        psPolynomial2D *poly = psf->params->data[i-4];
+        out[i] = Polynomial2DEval (poly, out[2], out[3]);
+    }
+    return(true);
+}
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_ZGAUSS.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_ZGAUSS.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/models/pmModel_ZGAUSS.c	(revision 22115)
@@ -0,0 +1,164 @@
+
+/******************************************************************************
+    one component, two slopes:
+    1 / (1 + z^Npow + PAR8*z^4)
+ 
+    params->data.F32[0] = So;
+    params->data.F32[1] = Zo;
+    params->data.F32[2] = Xo;
+    params->data.F32[3] = Yo;
+    params->data.F32[4] = 1 / SigmaX;
+    params->data.F32[5] = 1 / SigmaY;
+    params->data.F32[6] = Sxy;
+    params->data.F32[7] = Npow
+*****************************************************************************/
+
+# define SQ(A)((A)*(A))
+# define PAR8 0.1
+
+psF64 psModelFunc_ZGAUSS(psVector *deriv,
+                         const psVector *params,
+                         const psVector *x)
+{
+    psF32 *PAR = params->data.F32;
+
+    psF32 X  = x->data.F32[0] - PAR[2];
+    psF32 Y  = x->data.F32[1] - PAR[3];
+    psF32 px = PAR[4]*X;
+    psF32 py = PAR[5]*Y;
+    psF32 z  = 0.5*PS_SQR(px) + 0.5*PS_SQR(py) + PAR[6]*X*Y;
+
+    psF32 pr = PAR8*z;
+    psF32 p  = pow(z, PAR[7] - 1.0);
+    psF32 r  = 1.0 / (1 + z*p + SQ(SQ(pr)));
+    psF32 f  = PAR[1]*r + PAR[0];
+
+    if (deriv != NULL) {
+        // note difference from a pure gaussian: q = params->data.F32[1]*r
+        psF32 t = PAR[1]*r*r;
+        psF32 q = t*(PAR[7]*p + 4*PAR8*pr*pr*pr);
+
+        deriv->data.F32[0] = +1.0;
+        deriv->data.F32[1] = +r;
+        deriv->data.F32[2] = q*(2.0*px*PAR[4] + PAR[6]*Y);
+        deriv->data.F32[3] = q*(2.0*py*PAR[5] + PAR[6]*X);
+        deriv->data.F32[4] = -q*px*X;
+        deriv->data.F32[5] = -q*py*Y;
+        deriv->data.F32[6] = -q*X*Y;
+        deriv->data.F32[7] = -t*log(z)*z*p;
+    }
+    return(f);
+}
+
+psF64 psModelFlux_ZGAUSS(const psVector *params)
+{
+    float f, norm, z;
+
+    psF32 *PAR = params->data.F32;
+
+    psF64 A1   = PS_SQR(PAR[4]);
+    psF64 A2   = PS_SQR(PAR[5]);
+    psF64 A3   = PS_SQR(PAR[6]);
+    psF64 Area = 2.0 * M_PI / sqrt(A1*A2 - A3);
+    // Area is equivalent to 2 pi sigma^2
+
+    // the area needs to be multiplied by the integral of f(z)
+    norm = 0.0;
+    psF32 pr = PAR8*z;
+    for (z = 0.005; z < 50; z += 0.01) {
+        f = 1.0 / (1 + pow(z, PAR[7]) + SQ(SQ(pr)));
+        norm += f;
+    }
+    norm *= 0.01;
+
+    psF64 Flux = PAR[1] * Area * norm;
+
+    return(Flux);
+}
+
+// XXX need to define the radius along the major axis
+// define this function so it never returns Inf or NaN
+// return the radius which yields the requested flux
+psF64 psModelRadius_ZGAUSS  (const psVector *params, psF64 flux)
+{
+    psF64 r, z, pr, f;
+    psF32 *PAR = params->data.F32;
+
+    EllipseAxes axes;
+    EllipseShape shape;
+
+    if (flux <= 0)
+        return (1.0);
+    if (PAR[1] <= 0)
+        return (1.0);
+    if (flux >= PAR[1])
+        return (1.0);
+
+    // convert Sx,Sy,Sxy to major/minor axes
+    shape.sx = 1.0 / PAR[4];
+    shape.sy = 1.0 / PAR[5];
+    shape.sxy = PAR[6];
+
+    axes = EllipseShapeToAxes (shape);
+    psF64 dr = 1.0 / axes.major;
+    psF64 limit = flux / PAR[1];
+
+    // XXX : we can do this faster with an intelligent starting choice
+    for (r = 0.0; r < 20.0; r += dr) {
+        z = SQ(r);
+        pr = PAR8*z;
+        f = 1.0 / (1 + pow(z, PAR[7]) + SQ(SQ(pr)));
+        if (f < limit)
+            break;
+    }
+    psF64 radius = 2.0 * axes.major * sqrt (z);
+    if (isnan(radius)) {
+        fprintf (stderr, "error in code\n");
+    }
+    return (radius);
+}
+
+bool psModelGuess_ZGAUSS (psModel *model, psSource *source)
+{
+
+    psVector *params = model->params;
+
+    EllipseAxes axes;
+    EllipseShape shape;
+    EllipseMoments moments;
+
+    moments.x2 = PS_SQR(source->moments->Sx);
+    moments.y2 = PS_SQR(source->moments->Sy);
+    moments.xy = source->moments->Sxy;
+
+    axes = EllipseMomentsToAxes(moments);
+    shape = EllipseAxesToShape(axes);
+
+    params->data.F32[0] = source->moments->Sky;
+    params->data.F32[1] = source->peak->counts - source->moments->Sky;
+    params->data.F32[2] = source->moments->x;
+    params->data.F32[3] = source->moments->y;
+    params->data.F32[4] = 1.0 / shape.sx;
+    params->data.F32[5] = 1.0 / shape.sy;
+    params->data.F32[6] = shape.sxy;
+    params->data.F32[7] = 1.9;
+    return(true);
+}
+
+bool psModelFromPSF_ZGAUSS (psModel *modelPSF, psModel *modelFLT, pmPSF *psf)
+{
+
+    psF32 *out = modelPSF->params->data.F32;
+    psF32 *in  = modelFLT->params->data.F32;
+
+    out[0] = in[0];
+    out[1] = in[1];
+    out[2] = in[2];
+    out[3] = in[3];
+
+    for (int i = 4; i < 8; i++) {
+        psPolynomial2D *poly = psf->params->data[i-4];
+        out[i] = Polynomial2DEval (poly, out[2], out[3]);
+    }
+    return(true);
+}
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/pmModelGroup.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/pmModelGroup.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/pmModelGroup.c	(revision 22115)
@@ -0,0 +1,120 @@
+# include "pmModelGroup.h"
+
+double hypot(double x, double y);
+double sqrt (double x);
+
+#include "psEllipse.h"
+#include "models/pmModel_GAUSS.c"
+#include "models/pmModel_PGAUSS.c"
+#include "models/pmModel_QGAUSS.c"
+#include "models/pmModel_SGAUSS.c"
+
+static pmModelGroup models[] = {
+                                   {"PS_MODEL_GAUSS",        7, pmModelFunc_GAUSS,   pmModelFlux_GAUSS,   pmModelRadius_GAUSS,   pmModelLimits_GAUSS,   pmModelGuess_GAUSS,  pmModelFromPSF_GAUSS,  pmModelFitStatus_GAUSS},
+                                   {"PS_MODEL_PGAUSS",       7, pmModelFunc_PGAUSS,  pmModelFlux_PGAUSS,  pmModelRadius_PGAUSS,  pmModelLimits_PGAUSS,  pmModelGuess_PGAUSS, pmModelFromPSF_PGAUSS, pmModelFitStatus_PGAUSS},
+                                   {"PS_MODEL_QGAUSS",       8, pmModelFunc_QGAUSS,  pmModelFlux_QGAUSS,  pmModelRadius_QGAUSS,  pmModelLimits_QGAUSS,  pmModelGuess_QGAUSS, pmModelFromPSF_QGAUSS, pmModelFitStatus_QGAUSS},
+                                   {"PS_MODEL_SGAUSS",       9, pmModelFunc_SGAUSS,  pmModelFlux_SGAUSS,  pmModelRadius_SGAUSS,  pmModelLimits_SGAUSS,  pmModelGuess_SGAUSS, pmModelFromPSF_SGAUSS, pmModelFitStatus_SGAUSS},
+                               };
+
+pmModelFunc pmModelFunc_GetFunction (pmModelType type)
+{
+    int Nmodels = sizeof (models) / sizeof (pmModelGroup);
+    if ((type < 0) || (type >= Nmodels)) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return (NULL);
+    }
+    return (models[type].modelFunc);
+}
+
+pmModelFlux pmModelFlux_GetFunction (pmModelType type)
+{
+    int Nmodels = sizeof (models) / sizeof (pmModelGroup);
+    if ((type < 0) || (type >= Nmodels)) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return (NULL);
+    }
+    return (models[type].modelFlux);
+}
+
+pmModelRadius pmModelRadius_GetFunction (pmModelType type)
+{
+    int Nmodels = sizeof (models) / sizeof (pmModelGroup);
+    if ((type < 0) || (type >= Nmodels)) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return (NULL);
+    }
+    return (models[type].modelRadius);
+}
+
+pmModelLimits pmModelLimits_GetFunction (pmModelType type)
+{
+    int Nmodels = sizeof (models) / sizeof (pmModelGroup);
+    if ((type < 0) || (type >= Nmodels)) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return (NULL);
+    }
+    return (models[type].modelLimits);
+}
+
+pmModelGuessFunc pmModelGuessFunc_GetFunction (pmModelType type)
+{
+    int Nmodels = sizeof (models) / sizeof (pmModelGroup);
+    if ((type < 0) || (type >= Nmodels)) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return (NULL);
+    }
+    return (models[type].modelGuessFunc);
+}
+
+pmModelFitStatusFunc pmModelFitStatusFunc_GetFunction (pmModelType type)
+{
+    int Nmodels = sizeof (models) / sizeof (pmModelGroup);
+    if ((type < 0) || (type >= Nmodels)) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return (NULL);
+    }
+    return (models[type].modelFitStatusFunc);
+}
+
+pmModelFromPSFFunc pmModelFromPSFFunc_GetFunction (pmModelType type)
+{
+    int Nmodels = sizeof (models) / sizeof (pmModelGroup);
+    if ((type < 0) || (type >= Nmodels)) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return (NULL);
+    }
+    return (models[type].modelFromPSFFunc);
+}
+
+psS32 pmModelParameterCount (pmModelType type)
+{
+    int Nmodels = sizeof (models) / sizeof (pmModelGroup);
+    if ((type < 0) || (type >= Nmodels)) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return (0);
+    }
+    return (models[type].nParams);
+}
+
+psS32 pmModelSetType (char *name)
+{
+
+    int Nmodels = sizeof (models) / sizeof (pmModelGroup);
+    for (int i = 0; i < Nmodels; i++) {
+        if (!strcmp(models[i].name, name)) {
+            return (i);
+        }
+    }
+    return (-1);
+}
+
+char *pmModelGetType (pmModelType type)
+{
+
+    int Nmodels = sizeof (models) / sizeof (pmModelGroup);
+    if ((type < 0) || (type >= Nmodels)) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return (NULL);
+    }
+    return (models[type].name);
+}
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/pmModelGroup.h
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/pmModelGroup.h	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/pmModelGroup.h	(revision 22115)
@@ -0,0 +1,211 @@
+/** @file  pmModelGroup.h
+ *
+ *  The object model function types are defined to allow for the flexible addition
+ *  of new object models. Every object model, with parameters represented by
+ *  pmModel, has an associated set of functions which provide necessary support
+ *  operations. A set of abstract functions allow the programmer to select the
+ *  approriate function or property for a specific named object model.
+ * 
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-10-10 19:53:40 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ * 
+ */
+#include "pmObjects.h"
+#include "pmPSF.h"
+/**
+ * 
+ * This function returns the number of parameters used by the listed function.
+ * 
+ */
+int pmModelParameterCount(
+    pmModelType type                    ///< Add comment.
+);
+
+
+/**
+ * 
+ * This function returns the user-space model names for the specified model type.
+ * 
+ */
+char *pmModelGetType(
+    pmModelType type                    ///< Add comment.
+);
+
+
+/**
+ * 
+ * This function returns the internal model type code for the user-space model names.
+ * 
+ */
+pmModelType pmModelSetType(
+    char *name                          ///< Add comment.
+);
+
+
+#ifndef PM_MODEL_GROUP_H
+#define PM_MODEL_GROUP_H
+
+/**
+ * 
+ *  This function is the model chi-square minimization function for this model.
+ * 
+ */
+typedef psMinimizeLMChi2Func pmModelFunc;
+
+
+/**
+ * 
+ * This function returns the integrated flux for the given model parameters.
+ */
+typedef psF64 (*pmModelFlux)(const psVector *params);
+
+
+/**
+ * 
+ *  This function returns the radius at which the given model and parameters
+ *  achieves the given flux.
+ * 
+ */
+typedef psF64 (*pmModelRadius)(const psVector *params, double flux);
+
+/**
+ * 
+ *  This function sets the model parameter limits vectors for the given model
+ * 
+ */
+typedef bool (*pmModelLimits)(psVector **beta_lim, psVector **params_min, psVector **params_max);
+
+/**
+ * 
+ *  This function provides the model guess parameters based on the details of
+ *   the given source.
+ * 
+ */
+typedef bool (*pmModelGuessFunc)(pmModel *model, pmSource *source);
+
+
+/**
+ * 
+ *  This function constructs the PSF model for the given source based on the
+ *  supplied psf and the FLT model for the object.
+ * 
+ */
+typedef bool (*pmModelFromPSFFunc)(pmModel *modelPSF, pmModel *modelFLT, pmPSF *psf);
+
+/**
+ * 
+ *  This function returns the success / failure status of the given model fit
+ * 
+ */
+typedef bool (*pmModelFitStatusFunc)(pmModel *model);
+
+/**
+ * 
+ *  Each of the function types above has a corresponding function which returns
+ *  the function given the model type:
+ * 
+ */
+
+
+/**
+ * 
+ * pmModelFunc is the function used to determine the value of the model at a
+ * specific coordinate, and is the one used by psMinimizeLMChi2.
+ * 
+ */
+pmModelFunc          pmModelFunc_GetFunction (pmModelType type);
+
+
+/**
+ * 
+ * pmModelFlux returns the total integrated flux for the given input parameters.
+ * 
+ */
+pmModelFlux          pmModelFlux_GetFunction (pmModelType type);
+
+
+/**
+ * 
+ * pmModelRadius returns the scaling radius at which the flux of the model
+ * matches the specified flux. This presumes that the model is a function of an
+ * elliptical contour.
+ * 
+ */
+pmModelRadius        pmModelRadius_GetFunction (pmModelType type);
+
+
+/**
+ * 
+ * pmModelLimits sets the parameter limit vectors for the function.
+ * 
+ */
+pmModelLimits        pmModelLimits_GetFunction (pmModelType type);
+
+
+/**
+ * 
+ * pmModelGuessFunc generates an initial guess for the model based on the
+ * provided source statistics (moments and pixel values as needed).
+ * 
+ */
+pmModelGuessFunc     pmModelGuessFunc_GetFunction (pmModelType type);
+
+
+/**
+ * 
+ * pmModelFromPSFFunc takes as input a representation of the psf and a value
+ * for the model and fills in the PSF parameters of the model. The input
+ * primarily relies upon the centroid coordinates of the input model, though the
+ * normalization may potentially be used.
+ * 
+ */
+pmModelFromPSFFunc   pmModelFromPSFFunc_GetFunction (pmModelType type);
+
+
+/**
+ * 
+ * pmModelFitStatusFunc returns a true or false values based on the success or
+ * failure of a model fit.  The success is determined by quantities such as the
+ * chisq or the signal-to-noise.
+ * 
+ */
+pmModelFitStatusFunc pmModelFitStatusFunc_GetFunction (pmModelType type);
+
+
+
+
+/**
+ * 
+ * Every model instance belongs to a class of models, defined by the value of
+ * the pmModelType type entry. Various functions need access to information about
+ * each of the models. Some of this information varies from model to model, and
+ * may depend on the current parameter values or other data quantities. In order
+ * to keep the code from requiring the information about each model to be coded
+ * into the low-level fitting routines, we define a collection of functions which
+ * allow us to abstract this type of model-dependent information. These generic
+ * functions take the model type and return the corresponding function pointer
+ * for the specified model. Each model is defined by creating this collection of
+ * specific functions, and placing them in a single file for each model. We
+ * define the following structure to carry the collection of information about
+ * the models.
+ * 
+ */
+typedef struct
+{
+    char *name;
+    int nParams;
+    pmModelFunc          modelFunc;
+    pmModelFlux          modelFlux;
+    pmModelRadius        modelRadius;
+    pmModelLimits        modelLimits;
+    pmModelGuessFunc     modelGuessFunc;
+    pmModelFromPSFFunc   modelFromPSFFunc;
+    pmModelFitStatusFunc modelFitStatusFunc;
+}
+pmModelGroup;
+
+# endif
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/pmObjects.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/pmObjects.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/pmObjects.c	(revision 22115)
@@ -0,0 +1,1935 @@
+/** @file  pmObjects.c
+ *
+ *  This file will ...
+ *
+ *  @author GLG, MHPCC
+ *  @author EAM, IfA: significant modifications.
+ *
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-10-10 19:53:40 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include "pslib.h"
+#include "pmObjects.h"
+#include "pmModelGroup.h"
+/******************************************************************************
+pmPeakAlloc(): Allocate the pmPeak data structure and set appropriate members.
+*****************************************************************************/
+pmPeak *pmPeakAlloc(psS32 x,
+                    psS32 y,
+                    psF32 counts,
+                    pmPeakType class)
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+    pmPeak *tmp = (pmPeak *) psAlloc(sizeof(pmPeak));
+    tmp->x = x;
+    tmp->y = y;
+    tmp->counts = counts;
+    tmp->class = class;
+
+    psTrace(__func__, 3, "---- %s() end ----\n", __func__);
+    return(tmp);
+}
+
+/******************************************************************************
+pmMomentsAlloc(): Allocate the pmMoments structure and initialize the members
+to zero.
+*****************************************************************************/
+pmMoments *pmMomentsAlloc()
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+    pmMoments *tmp = (pmMoments *) psAlloc(sizeof(pmMoments));
+    tmp->x = 0.0;
+    tmp->y = 0.0;
+    tmp->Sx = 0.0;
+    tmp->Sy = 0.0;
+    tmp->Sxy = 0.0;
+    tmp->Sum = 0.0;
+    tmp->Peak = 0.0;
+    tmp->Sky = 0.0;
+    tmp->nPixels = 0;
+
+    psTrace(__func__, 3, "---- %s() end ----\n", __func__);
+    return(tmp);
+}
+
+static void modelFree(pmModel *tmp)
+{
+    psTrace(__func__, 4, "---- %s() begin ----\n", __func__);
+    psFree(tmp->params);
+    psFree(tmp->dparams);
+    psTrace(__func__, 4, "---- %s() end ----\n", __func__);
+}
+
+/******************************************************************************
+pmModelAlloc(): Allocate the pmModel structure, along with its parameters,
+and initialize the type member.  Initialize the params to 0.0.
+XXX EAM: simplifying code with pmModelParameterCount
+*****************************************************************************/
+pmModel *pmModelAlloc(pmModelType type)
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+    pmModel *tmp = (pmModel *) psAlloc(sizeof(pmModel));
+
+    tmp->type = type;
+    tmp->chisq = 0.0;
+    tmp->nIter = 0;
+    psS32 Nparams = pmModelParameterCount(type);
+    if (Nparams == 0) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return(NULL);
+    }
+
+    tmp->params  = psVectorAlloc(Nparams, PS_TYPE_F32);
+    tmp->dparams = psVectorAlloc(Nparams, PS_TYPE_F32);
+
+    for (psS32 i = 0; i < tmp->params->n; i++) {
+        tmp->params->data.F32[i] = 0.0;
+        tmp->dparams->data.F32[i] = 0.0;
+    }
+
+    psMemSetDeallocator(tmp, (psFreeFunc) modelFree);
+    psTrace(__func__, 3, "---- %s() end ----\n", __func__);
+    return(tmp);
+}
+
+/******************************************************************************
+XXX EAM : we can now free these pixels - memory ref is incremented now
+*****************************************************************************/
+static void sourceFree(pmSource *tmp)
+{
+    psTrace(__func__, 4, "---- %s() begin ----\n", __func__);
+    psFree(tmp->peak);
+    psFree(tmp->pixels);
+    psFree(tmp->weight);
+    psFree(tmp->mask);
+    psFree(tmp->moments);
+    psFree(tmp->modelPSF);
+    psFree(tmp->modelFLT);
+    psTrace(__func__, 4, "---- %s() end ----\n", __func__);
+}
+
+/******************************************************************************
+pmSourceAlloc(): Allocate the pmSource structure and initialize its members
+to NULL.
+*****************************************************************************/
+pmSource *pmSourceAlloc()
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+    pmSource *tmp = (pmSource *) psAlloc(sizeof(pmSource));
+    tmp->peak = NULL;
+    tmp->pixels = NULL;
+    tmp->weight = NULL;
+    tmp->mask = NULL;
+    tmp->moments = NULL;
+    tmp->modelPSF = NULL;
+    tmp->modelFLT = NULL;
+    tmp->type = 0;
+    psMemSetDeallocator(tmp, (psFreeFunc) sourceFree);
+
+    psTrace(__func__, 3, "---- %s() end ----\n", __func__);
+    return(tmp);
+}
+
+/******************************************************************************
+pmFindVectorPeaks(vector, threshold): Find all local peaks in the given vector
+above the given threshold.  Returns a vector of type PS_TYPE_U32 containing
+the location (x value) of all peaks.
+ 
+XXX: What types should be supported?  Only F32 is implemented.
+ 
+XXX: We currently step through the input vector twice; once to determine the
+size of the output vector, then to set the values of the output vector.
+Depending upon actual use, this may need to be optimized.
+*****************************************************************************/
+psVector *pmFindVectorPeaks(const psVector *vector,
+                            psF32 threshold)
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_VECTOR_NON_NULL(vector, NULL);
+    PS_ASSERT_VECTOR_NON_EMPTY(vector, NULL);
+    PS_ASSERT_VECTOR_TYPE(vector, PS_TYPE_F32, NULL);
+    int count = 0;
+    int n = vector->n;
+
+    //
+    // Special case: the input vector has a single element.
+    //
+    if (n == 1) {
+        psVector *tmpVector = NULL;
+        ;
+        if (vector->data.F32[0] > threshold) {
+            tmpVector = psVectorAlloc(1, PS_TYPE_U32);
+            tmpVector->data.U32[0] = 0;
+        } else {
+            tmpVector = psVectorAlloc(0, PS_TYPE_U32);
+        }
+        psTrace(__func__, 3, "---- %s() end ----\n", __func__);
+        return(tmpVector);
+    }
+
+    //
+    // Determine if first pixel is a peak
+    //
+    if ((vector->data.F32[0] > vector->data.F32[1]) &&
+            (vector->data.F32[0] > threshold)) {
+        count++;
+    }
+
+    //
+    // Determine if interior pixels are peaks
+    //
+    for (psU32 i = 1; i < n-1 ; i++) {
+        if ((vector->data.F32[i] > vector->data.F32[i-1]) &&
+                (vector->data.F32[i] > vector->data.F32[i+1]) &&
+                (vector->data.F32[i] > threshold)) {
+            count++;
+        }
+    }
+
+    //
+    // Determine if last pixel is a peak
+    //
+    if ((vector->data.F32[n-1] > vector->data.F32[n-2]) &&
+            (vector->data.F32[n-1] > threshold)) {
+        count++;
+    }
+
+    //
+    // We know how many peaks exist, so we now allocate a psVector to store
+    // those peaks.
+    //
+    psVector *tmpVector = psVectorAlloc(count, PS_TYPE_U32);
+    count = 0;
+
+    //
+    // Determine if first pixel is a peak
+    //
+    if ((vector->data.F32[0] > vector->data.F32[1]) &&
+            (vector->data.F32[0] > threshold)) {
+        tmpVector->data.U32[count++] = 0;
+    }
+
+    //
+    // Determine if interior pixels are peaks
+    //
+    for (psU32 i = 1; i < (n-1) ; i++) {
+        if ((vector->data.F32[i] > vector->data.F32[i-1]) &&
+                (vector->data.F32[i] > vector->data.F32[i+1]) &&
+                (vector->data.F32[i] > threshold)) {
+            tmpVector->data.U32[count++] = i;
+        }
+    }
+
+    //
+    // Determine if last pixel is a peak
+    //
+    if ((vector->data.F32[n-1] > vector->data.F32[n-2]) &&
+            (vector->data.F32[n-1] > threshold)) {
+        tmpVector->data.U32[count++] = n-1;
+    }
+
+    psTrace(__func__, 3, "---- %s() end ----\n", __func__);
+    return(tmpVector);
+}
+
+/******************************************************************************
+getRowVectorFromImage(): a private function which simply returns a
+psVector containing the specified row of data from the psImage.
+ 
+XXX: Is there a better way to do this?  
+XXX EAM: does this really need to alloc a new vector???
+*****************************************************************************/
+static psVector *getRowVectorFromImage(psImage *image,
+                                       psU32 row)
+{
+    psTrace(__func__, 4, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
+    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
+
+    psVector *tmpVector = psVectorAlloc(image->numCols, PS_TYPE_F32);
+    for (psU32 col = 0; col < image->numCols ; col++) {
+        tmpVector->data.F32[col] = image->data.F32[row][col];
+    }
+    psTrace(__func__, 4, "---- %s() end ----\n", __func__);
+    return(tmpVector);
+}
+
+/******************************************************************************
+myListAddPeak(): A private function which allocates a psArray, if the list
+argument is NULL, otherwise it adds the peak to that list.
+XXX EAM : changed the output to psArray
+XXX EAM : Switched row, col args
+XXX EAM : NOTE: this was changed in the call, so the new code is consistent
+*****************************************************************************/
+static psArray *myListAddPeak(psArray *list,
+                              psS32 row,
+                              psS32 col,
+                              psF32 counts,
+                              pmPeakType type)
+{
+    psTrace(__func__, 4, "---- %s() begin ----\n", __func__);
+    pmPeak *tmpPeak = pmPeakAlloc(col, row, counts, type);
+
+    if (list == NULL) {
+        list = psArrayAlloc(100);
+        list->n = 0;
+    }
+    psArrayAdd(list, 100, tmpPeak);
+    psFree (tmpPeak);
+    // XXX EAM : is this free appropriate?  (does psArrayAdd increment memory counter?)
+
+    psTrace(__func__, 4, "---- %s() end ----\n", __func__);
+    return(list);
+}
+
+/******************************************************************************
+pmFindImagePeaks(image, threshold): Find all local peaks in the given psImage
+above the given threshold.  Returns a psArray containing location (x/y value)
+of all peaks.
+ 
+XXX: I'm not convinced the peak type definition in the SDRS is mutually
+exclusive.  Some peaks can have multiple types.  Edges for sure.  Also, a
+digonal line with the same value at each point will have a peak for every
+point on that line.
+ 
+XXX: This does not work if image has either a single row, or a single column.
+ 
+XXX: In the output psArray elements, should we use the image row/column offsets?
+     Currently, we do not.
+ 
+XXX: Merge with CVS 1.20.  This had the proper code for images with a single
+row or column.
+*****************************************************************************/
+psArray *pmFindImagePeaks(const psImage *image,
+                          psF32 threshold)
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
+    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
+    if ((image->numRows == 1) || (image->numCols == 1)) {
+        psError(PS_ERR_UNKNOWN, true, "Currently, input image must have at least 2 rows and 2 columns.");
+        psTrace(__func__, 3, "---- %s(NULL) end ----\n", __func__);
+        return(NULL);
+    }
+    psVector *tmpRow = NULL;
+    psU32 col = 0;
+    psU32 row = 0;
+    psArray *list = NULL;
+
+    //
+    // Find peaks in row 0 only.
+    //
+    row = 0;
+    tmpRow = getRowVectorFromImage((psImage *) image, row);
+    psVector *row1 = pmFindVectorPeaks(tmpRow, threshold);
+
+    for (psU32 i = 0 ; i < row1->n ; i++ ) {
+        col = row1->data.U32[i];
+        //
+        // Determine if pixel (0,0) is a peak.
+        //
+        if (col == 0) {
+            if ( (image->data.F32[row][col] >  image->data.F32[row][col+1]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row+1][col]) &&
+                    (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
+
+                if (image->data.F32[row][col] > threshold) {
+                    list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
+                }
+            }
+        } else if (col < (image->numCols - 1)) {
+            if ( (image->data.F32[row][col] >= image->data.F32[row][col-1]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row][col+1]) &&
+                    (image->data.F32[row][col] >= image->data.F32[row+1][col-1]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row+1][col]) &&
+                    (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
+                if (image->data.F32[row][col] > threshold) {
+                    list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
+                }
+            }
+
+        } else if (col == (image->numCols - 1)) {
+            if ( (image->data.F32[row][col] >= image->data.F32[row][col-1]) &&
+                    (image->data.F32[row][col] > image->data.F32[row+1][col]) &&
+                    (image->data.F32[row][col] >= image->data.F32[row+1][col-1])) {
+                if (image->data.F32[row][col] > threshold) {
+                    list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
+                }
+            }
+
+        } else {
+            psError(PS_ERR_UNKNOWN, true, "peak specified valid column range.");
+        }
+    }
+    psFree (tmpRow);
+    psFree (row1);
+
+    //
+    // Exit if this image has a single row.
+    //
+    if (image->numRows == 1) {
+        psTrace(__func__, 3, "---- %s() end ----\n", __func__);
+        return(list);
+    }
+
+    //
+    // Find peaks in interior rows only.
+    //
+    for (row = 1 ; row < (image->numRows - 1) ; row++) {
+        tmpRow = getRowVectorFromImage((psImage *) image, row);
+        row1 = pmFindVectorPeaks(tmpRow, threshold);
+
+        // Step through all local peaks in this row.
+        for (psU32 i = 0 ; i < row1->n ; i++ ) {
+            pmPeakType myType = PM_PEAK_UNDEF;
+            col = row1->data.U32[i];
+
+            if (col == 0) {
+                // If col==0, then we can not read col-1 pixels
+                if ((image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row][col+1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row+1][col]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
+                    myType = PM_PEAK_EDGE;
+                    list = myListAddPeak(list, row, col, image->data.F32[row][col], myType);
+                }
+            } else if (col < (image->numCols - 1)) {
+                // This is an interior pixel
+                if ((image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
+                        (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
+                        (image->data.F32[row][col] > image->data.F32[row][col-1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row][col+1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row+1][col-1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row+1][col]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row+1][col+1])) {
+                    if (image->data.F32[row][col] > threshold) {
+                        if ((image->data.F32[row][col] > image->data.F32[row-1][col-1]) &&
+                                (image->data.F32[row][col] > image->data.F32[row-1][col]) &&
+                                (image->data.F32[row][col] > image->data.F32[row-1][col+1]) &&
+                                (image->data.F32[row][col] > image->data.F32[row][col-1]) &&
+                                (image->data.F32[row][col] > image->data.F32[row][col+1]) &&
+                                (image->data.F32[row][col] > image->data.F32[row+1][col-1]) &&
+                                (image->data.F32[row][col] > image->data.F32[row+1][col]) &&
+                                (image->data.F32[row][col] > image->data.F32[row+1][col+1])) {
+                            myType = PM_PEAK_LONE;
+                        }
+
+                        if ((image->data.F32[row][col] == image->data.F32[row-1][col-1]) ||
+                                (image->data.F32[row][col] == image->data.F32[row-1][col]) ||
+                                (image->data.F32[row][col] == image->data.F32[row-1][col+1]) ||
+                                (image->data.F32[row][col] == image->data.F32[row][col-1]) ||
+                                (image->data.F32[row][col] == image->data.F32[row][col+1]) ||
+                                (image->data.F32[row][col] == image->data.F32[row+1][col-1]) ||
+                                (image->data.F32[row][col] == image->data.F32[row+1][col]) ||
+                                (image->data.F32[row][col] == image->data.F32[row+1][col+1])) {
+                            myType = PM_PEAK_FLAT;
+                        }
+
+                        list = myListAddPeak(list, row, col, image->data.F32[row][col], myType);
+                    }
+                }
+            } else if (col == (image->numCols - 1)) {
+                // If col==numCols - 1, then we can not read col+1 pixels
+                if ((image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
+                        (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
+                        (image->data.F32[row][col] > image->data.F32[row][col-1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row][col+1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row+1][col-1]) &&
+                        (image->data.F32[row][col] >= image->data.F32[row+1][col])) {
+                    myType = PM_PEAK_EDGE;
+                    list = myListAddPeak(list, row, col, image->data.F32[row][col], myType);
+                }
+            } else {
+                psError(PS_ERR_UNKNOWN, true, "peak specified outside valid column range.");
+            }
+
+        }
+        psFree (tmpRow);
+        psFree (row1);
+    }
+
+    //
+    // Find peaks in the last row only.
+    //
+    row = image->numRows - 1;
+    tmpRow = getRowVectorFromImage((psImage *) image, row);
+    row1 = pmFindVectorPeaks(tmpRow, threshold);
+    for (psU32 i = 0 ; i < row1->n ; i++ ) {
+        col = row1->data.U32[i];
+        if (col == 0) {
+            if ( (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
+                    (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row][col+1])) {
+                if (image->data.F32[row][col] > threshold) {
+                    list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
+                }
+            }
+        } else if (col < (image->numCols - 1)) {
+            if ( (image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
+                    (image->data.F32[row][col] >= image->data.F32[row-1][col+1]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row][col-1]) &&
+                    (image->data.F32[row][col] >= image->data.F32[row][col+1])) {
+                if (image->data.F32[row][col] > threshold) {
+                    list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
+                }
+            }
+
+        } else if (col == (image->numCols - 1)) {
+            if ( (image->data.F32[row][col] >= image->data.F32[row-1][col-1]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row-1][col]) &&
+                    (image->data.F32[row][col] >  image->data.F32[row][col-1])) {
+                if (image->data.F32[row][col] > threshold) {
+                    list = myListAddPeak(list, row, col, image->data.F32[row][col], PM_PEAK_EDGE);
+                }
+            }
+        } else {
+            psError(PS_ERR_UNKNOWN, true, "peak specified outside valid column range.");
+        }
+    }
+    psFree (tmpRow);
+    psFree (row1);
+    psTrace(__func__, 3, "---- %s() end ----\n", __func__);
+    return(list);
+}
+
+// XXX: Macro this.
+static bool isItInThisRegion(const psRegion valid,
+                             psS32 x,
+                             psS32 y)
+{
+    psTrace(__func__, 4, "---- %s() begin ----\n", __func__);
+    if ((x >= valid.x0) &&
+            (x <= valid.x1) &&
+            (y >= valid.y0) &&
+            (y <= valid.y1)) {
+        psTrace(__func__, 4, "---- %s(true) end ----\n", __func__);
+        return(true);
+    }
+    psTrace(__func__, 4, "---- %s(false) end ----\n", __func__);
+    return(false);
+}
+
+
+/******************************************************************************
+psCullPeaks(peaks, maxValue, valid): eliminate peaks from the psArray that have
+a peak value above the given maximum, or fall outside the valid region.
+ 
+XXX: Should the sky value be used when comparing the maximum?
+ 
+XXX: warning message if valid is NULL?
+ 
+XXX: changed API to create a NEW output psArray (should change name as well)
+ 
+XXX: Do we free the psList elements of those culled peaks?
+ 
+XXX EAM : do we still need pmCullPeaks, or only pmPeaksSubset?
+*****************************************************************************/
+psList *pmCullPeaks(psList *peaks,
+                    psF32 maxValue,
+                    const psRegion valid)
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(peaks, NULL);
+
+    psListElem *tmpListElem = (psListElem *) peaks->head;
+    psS32 indexNum = 0;
+
+    //    printf("pmCullPeaks(): list size is %d\n", peaks->size);
+    while (tmpListElem != NULL) {
+        pmPeak *tmpPeak = (pmPeak *) tmpListElem->data;
+        if ((tmpPeak->counts > maxValue) ||
+                (true == isItInThisRegion(valid, tmpPeak->x, tmpPeak->y))) {
+            psListRemoveData(peaks, (psPtr) tmpPeak);
+        }
+
+        indexNum++;
+        tmpListElem = tmpListElem->next;
+    }
+
+    psTrace(__func__, 3, "---- %s() end ----\n", __func__);
+    return(peaks);
+}
+
+// XXX EAM: I changed this to return a new, subset array
+//          rather than alter the existing one
+// XXX: Fix the *valid pointer.
+psArray *pmPeaksSubset(
+    psArray *peaks,
+    psF32 maxValue,
+    const psRegion valid)
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(peaks, NULL);
+
+    psArray *output = psArrayAlloc (200);
+    output->n = 0;
+
+    psTrace (".pmObjects.pmCullPeaks", 3, "list size is %d\n", peaks->n);
+
+    for (int i = 0; i < peaks->n; i++) {
+        pmPeak *tmpPeak = (pmPeak *) peaks->data[i];
+        if (tmpPeak->counts > maxValue)
+            continue;
+        if (isItInThisRegion(valid, tmpPeak->x, tmpPeak->y))
+            continue;
+        psArrayAdd (output, 200, tmpPeak);
+    }
+    psTrace(__func__, 3, "---- %s() end ----\n", __func__);
+    return(output);
+}
+
+/******************************************************************************
+pmSource *pmSourceLocalSky(image, peak, innerRadius, outerRadius): this
+routine creates a new pmSource data structure and sets the following members:
+    ->pmPeak
+    ->pmMoments->sky
+ 
+The sky value is set from the pixels in the square annulus surrounding the
+peak pixel.
+ 
+We simply create a subSet image and mask the inner pixels, then call
+psImageStats on that subImage+mask.
+ 
+XXX: The subImage has width of 1+2*outerRadius.  Verify with IfA.
+ 
+XXX: Use static data structures for:
+     subImage
+     subImageMask
+     myStats
+ 
+XXX: ensure that the inner and out radius fit in the actual image.  Should
+     we generate an error, or warning?  Currently an error.
+ 
+XXX: Sync with IfA on whether the peak x/y coords are data structure coords,
+     or they use the image row/column offsets.
+ 
+XXX: Should we simply set pmSource->peak = peak?  If so, should we increase
+the reference counter?  Or, should we copy the data structure?
+ 
+XXX: Currently the subimage always has an even number of rows/columns.  Is
+     this correct?  Since there is a center pixel, maybe it should have an
+     odd number of rows/columns.
+ 
+XXX: Use psTrace() for the print statements.
+ 
+XXX: Don't use separate structs for the subimage and mask.  Use the source->
+     members.
+*****************************************************************************/
+
+
+
+
+
+
+
+
+
+bool pmSourceLocalSky(
+    pmSource *source,
+    psStatsOptions statsOptions,
+    psF32 Radius)
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_IMAGE_NON_NULL(source->pixels, false);
+    PS_ASSERT_IMAGE_NON_NULL(source->mask, false);
+    PS_ASSERT_PTR_NON_NULL(source->peak, false);
+    PS_ASSERT_INT_POSITIVE(Radius, false);
+    PS_ASSERT_INT_NONNEGATIVE(Radius, false);
+
+    psImage *image = source->pixels;
+    psImage *mask  = source->mask;
+    pmPeak *peak  = source->peak;
+    psRegion srcRegion;
+
+    srcRegion = psRegionForSquare(peak->x, peak->y, Radius);
+    srcRegion = psRegionForImage(mask, srcRegion);
+
+    psImageMaskRegion(mask, srcRegion, "OR", PSPHOT_MASK_MARKED);
+    psStats *myStats = psStatsAlloc(statsOptions);
+    myStats = psImageStats(myStats, image, mask, 0xff);
+    psImageMaskRegion(mask, srcRegion, "AND", ~PSPHOT_MASK_MARKED);
+
+    psF64 tmpF64;
+    p_psGetStatValue(myStats, &tmpF64);
+    psFree(myStats);
+
+    if (isnan(tmpF64)) {
+        psTrace(__func__, 3, "---- %s(false) end ----\n", __func__);
+        return(false);
+    }
+    source->moments = pmMomentsAlloc();
+    source->moments->Sky = (psF32) tmpF64;
+    psTrace(__func__, 3, "---- %s(true) end ----\n", __func__);
+    return (true);
+}
+
+/******************************************************************************
+bool checkRadius2(): private function which simply determines if the (x, y)
+point is within the radius of the specified peak.
+ 
+XXX: macro this for performance.
+XXX: this is rather inefficient - at least compute and compare against radius^2
+*****************************************************************************/
+static bool checkRadius2(psF32 xCenter,
+                         psF32 yCenter,
+                         psF32 radius,
+                         psF32 x,
+                         psF32 y)
+{
+    psTrace(__func__, 4, "---- %s() begin ----\n", __func__);
+    /// XXX EAM should compare with hypot (x,y) for speed
+    if ((PS_SQR(x - xCenter) + PS_SQR(y - yCenter)) < PS_SQR(radius)) {
+        return(true);
+    }
+
+    psTrace(__func__, 4, "---- %s(false) end ----\n", __func__);
+    return(false);
+}
+
+/******************************************************************************
+pmSourceMoments(source, radius): this function takes a subImage defined in the
+pmSource data structure, along with the peak location, and determines the
+various moments associated with that peak.
+ 
+Requires the following to have been created:
+    pmSource
+    pmSource->peak
+    pmSource->pixels
+    pmSource->weight
+    pmSource->mask
+ 
+XXX: The peak calculations are done in image coords, not subImage coords.
+ 
+XXX EAM : this version clips input pixels on S/N
+XXX EAM : this version returns false for several reasons
+*****************************************************************************/
+# define VALID_RADIUS(X,Y,RAD2) (((RAD2) >= (PS_SQR(X) + PS_SQR(Y))) ? 1 : 0)
+
+bool pmSourceMoments(pmSource *source,
+                     psF32 radius)
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_PTR_NON_NULL(source->peak, false);
+    PS_ASSERT_PTR_NON_NULL(source->pixels, false);
+    PS_ASSERT_PTR_NON_NULL(source->mask, false);
+    PS_ASSERT_FLOAT_LARGER_THAN(radius, 0.0, false);
+
+    //
+    // XXX: Verify the setting for sky if source->moments == NULL.
+    //
+    psF32 sky = 0.0;
+    if (source->moments == NULL) {
+        source->moments = pmMomentsAlloc();
+    } else {
+        sky = source->moments->Sky;
+    }
+
+    //
+    // Sum = SUM (z - sky)
+    // X1  = SUM (x - xc)*(z - sky)
+    // X2  = SUM (x - xc)^2 * (z - sky)
+    // XY  = SUM (x - xc)*(y - yc)*(z - sky)
+    //
+    psF32 peakPixel = -PS_MAX_F32;
+    psS32 numPixels = 0;
+    psF32 Sum = 0.0;
+    psF32 X1 = 0.0;
+    psF32 Y1 = 0.0;
+    psF32 X2 = 0.0;
+    psF32 Y2 = 0.0;
+    psF32 XY = 0.0;
+    psF32 x  = 0;
+    psF32 y  = 0;
+    psF32 R2 = PS_SQR(radius);
+
+    psF32 xPeak = source->peak->x;
+    psF32 yPeak = source->peak->y;
+
+    // XXX why do I get different results for these two methods of finding Sx?
+    // XXX Sx, Sy would be better measured if we clip pixels close to sky
+    // XXX Sx, Sy can still be imaginary, so we probably need to keep Sx^2?
+    // We loop through all pixels in this subimage (source->pixels), and for each
+    // pixel that is not masked, AND within the radius of the peak pixel, we
+    // proceed with the moments calculation.  need to do two loops for a
+    // numerically stable result.  first loop: get the sums.
+    // XXX EAM : mask == 0 is valid
+
+    for (psS32 row = 0; row < source->pixels->numRows ; row++) {
+        for (psS32 col = 0; col < source->pixels->numCols ; col++) {
+            if ((source->mask != NULL) && (source->mask->data.U8[row][col]))
+                continue;
+
+            psF32 xDiff = col + source->pixels->col0 - xPeak;
+            psF32 yDiff = row + source->pixels->row0 - yPeak;
+
+            // XXX EAM : calculate xDiff, yDiff up front;
+            //           radius is just a function of (xDiff, yDiff)
+            if (!VALID_RADIUS(xDiff, yDiff, R2))
+                continue;
+
+            psF32 pDiff = source->pixels->data.F32[row][col] - sky;
+
+            // XXX EAM : check for valid S/N in pixel
+            // XXX EAM : should this limit be user-defined?
+            if (pDiff / sqrt(source->weight->data.F32[row][col]) < 1)
+                continue;
+
+            Sum += pDiff;
+            X1  += xDiff * pDiff;
+            Y1  += yDiff * pDiff;
+            XY  += xDiff * yDiff * pDiff;
+
+            X2  += PS_SQR(xDiff) * pDiff;
+            Y2  += PS_SQR(yDiff) * pDiff;
+
+            peakPixel = PS_MAX (source->pixels->data.F32[row][col], peakPixel);
+            numPixels++;
+        }
+    }
+    // XXX EAM - the limit is a bit arbitrary.  make it user defined?
+    if ((numPixels < 3) || (Sum <= 0)) {
+        psTrace (".psModules.pmSourceMoments", 5, "no valid pixels for source\n");
+        psTrace(__func__, 3, "---- %s(false) end ----\n", __func__);
+        return (false);
+    }
+
+    psTrace (".psModules.pmSourceMoments", 5,
+             "sky: %f  Sum: %f  X1: %f  Y1: %f  X2: %f  Y2: %f  XY: %f  Npix: %d\n",
+             sky, Sum, X1, Y1, X2, Y2, XY, numPixels);
+
+    //
+    // first moment X  = X1/Sum + xc
+    // second moment X = sqrt (X2/Sum - (X1/Sum)^2)
+    // Sxy             = XY / Sum
+    //
+    x = X1/Sum;
+    y = Y1/Sum;
+    if ((fabs(x) > radius) || (fabs(y) > radius)) {
+        psTrace (".psModules.pmSourceMoments", 5,
+                 "large centroid swing; invalid peak %d, %d\n",
+                 source->peak->x, source->peak->y);
+        psTrace(__func__, 3, "---- %s(false) end ----\n", __func__);
+        return (false);
+    }
+
+    source->moments->x = x + xPeak;
+    source->moments->y = y + yPeak;
+
+    // XXX EAM : Sxy needs to have x*y subtracted
+    source->moments->Sxy = XY/Sum - x*y;
+    source->moments->Sum = Sum;
+    source->moments->Peak = peakPixel;
+    source->moments->nPixels = numPixels;
+
+    // XXX EAM : these values can be negative, so we need to limit the range
+    source->moments->Sx = sqrt(PS_MAX(X2/Sum - PS_SQR(x), 0));
+    source->moments->Sy = sqrt(PS_MAX(Y2/Sum - PS_SQR(y), 0));
+
+    psTrace (".psModules.pmSourceMoments", 4,
+             "sky: %f  Sum: %f  x: %f  y: %f  Sx: %f  Sy: %f  Sxy: %f\n",
+             sky, Sum, source->moments->x, source->moments->y,
+             source->moments->Sx, source->moments->Sy, source->moments->Sxy);
+
+    psTrace(__func__, 3, "---- %s(true) end ----\n", __func__);
+    return(true);
+}
+
+// XXX EAM : I used
+int pmComparePeakAscend (const void **a, const void **b)
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+    pmPeak *A = *(pmPeak **)a;
+    pmPeak *B = *(pmPeak **)b;
+
+    psF32 diff;
+
+    diff = A->counts - B->counts;
+    if (diff < FLT_EPSILON) {
+        psTrace(__func__, 3, "---- %s(-1) end ----\n", __func__);
+        return (-1);
+    } else if (diff > FLT_EPSILON) {
+        psTrace(__func__, 3, "---- %s(+1) end ----\n", __func__);
+        return (+1);
+    }
+    psTrace(__func__, 3, "---- %s(0) end ----\n", __func__);
+    return (0);
+}
+
+int pmComparePeakDescend (const void **a, const void **b)
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+    pmPeak *A = *(pmPeak **)a;
+    pmPeak *B = *(pmPeak **)b;
+
+    psF32 diff;
+
+    diff = A->counts - B->counts;
+    if (diff < FLT_EPSILON) {
+        psTrace(__func__, 3, "---- %s(+1) end ----\n", __func__);
+        return (+1);
+    } else if (diff > FLT_EPSILON) {
+        psTrace(__func__, 3, "---- %s(-1) end ----\n", __func__);
+        return (-1);
+    }
+    psTrace(__func__, 3, "---- %s(0) end ----\n", __func__);
+    return (0);
+}
+
+/******************************************************************************
+pmSourcePSFClump(source, metadata): Find the likely PSF clump in the 
+sigma-x, sigma-y plane. return 0,0 clump in case of error. 
+*****************************************************************************/
+
+// XXX EAM include a S/N cutoff in selecting the sources?
+pmPSFClump pmSourcePSFClump(psArray *sources, psMetadata *metadata)
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+
+    # define NPIX 10
+    # define SCALE 0.1
+
+    psArray *peaks  = NULL;
+    pmPSFClump emptyClump = {0.0, 0.0, 0.0, 0.0};
+    pmPSFClump psfClump = emptyClump;
+
+    PS_ASSERT_PTR_NON_NULL(sources, emptyClump);
+    PS_ASSERT_PTR_NON_NULL(metadata, emptyClump);
+
+    // find the sigmaX, sigmaY clump
+    {
+        psStats *stats  = NULL;
+        psImage *splane = NULL;
+        int binX, binY;
+
+        // construct a sigma-plane image
+        // psImageAlloc does zero the data
+        splane = psImageAlloc (NPIX/SCALE, NPIX/SCALE, PS_TYPE_F32);
+        for (int i = 0; i < splane->numRows; i++)
+        {
+            memset (splane->data.F32[i], 0, splane->numCols*sizeof(PS_TYPE_F32));
+        }
+
+        // place the sources in the sigma-plane image (ignore 0,0 values?)
+        for (psS32 i = 0 ; i < sources->n ; i++)
+        {
+            pmSource *tmpSrc = (pmSource *) sources->data[i];
+            if (tmpSrc == NULL) {
+                continue;
+            }
+            if (tmpSrc->moments == NULL) {
+                continue;
+            }
+
+            // Sx,Sy are limited at 0.  a peak at 0,0 is artificial
+            if ((fabs(tmpSrc->moments->Sx) < FLT_EPSILON) && (fabs(tmpSrc->moments->Sy) < FLT_EPSILON)) {
+                continue;
+            }
+
+            // for the moment, force splane dimensions to be 10x10 image pix
+            binX = tmpSrc->moments->Sx/SCALE;
+            if (binX < 0)
+                continue;
+            if (binX >= splane->numCols)
+                continue;
+
+            binY = tmpSrc->moments->Sy/SCALE;
+            if (binY < 0)
+                continue;
+            if (binY >= splane->numRows)
+                continue;
+
+            splane->data.F32[binY][binX] += 1.0;
+        }
+
+        // find the peak in this image
+        stats = psStatsAlloc (PS_STAT_MAX);
+        stats = psImageStats (stats, splane, NULL, 0);
+        peaks = pmFindImagePeaks (splane, stats[0].max / 2);
+        psTrace (".pmObjects.pmSourceRoughClass", 2, "clump threshold is %f\n", stats[0].max/2);
+
+        psFree (splane);
+        psFree (stats);
+
+    }
+    // XXX EAM : possible errors:
+    //           1) no peak in splane
+    //           2) no significant peak in splane
+
+    // measure statistics on Sx, Sy if Sx, Sy within range of clump
+    {
+        pmPeak *clump;
+        psF32 minSx, maxSx;
+        psF32 minSy, maxSy;
+        psVector *tmpSx = NULL;
+        psVector *tmpSy = NULL;
+        psStats *stats  = NULL;
+
+        // XXX EAM : this lets us takes the single highest peak
+        psArraySort (peaks, pmComparePeakDescend);
+        clump = peaks->data[0];
+        psTrace (".pmObjects.pmSourceRoughClass", 2, "clump is at %d, %d (%f)\n", clump->x, clump->y, clump->counts);
+
+        // define section window for clump
+        minSx = clump->x * SCALE - 0.2;
+        maxSx = clump->x * SCALE + 0.2;
+        minSy = clump->y * SCALE - 0.2;
+        maxSy = clump->y * SCALE + 0.2;
+
+        tmpSx = psVectorAlloc (sources->n, PS_TYPE_F32);
+        tmpSy = psVectorAlloc (sources->n, PS_TYPE_F32);
+        tmpSx->n = 0;
+        tmpSy->n = 0;
+
+        // XXX clip sources based on flux?
+        // create vectors with Sx, Sy values in window
+        for (psS32 i = 0 ; i < sources->n ; i++)
+        {
+            pmSource *tmpSrc = (pmSource *) sources->data[i];
+
+            if (tmpSrc->moments->Sx < minSx)
+                continue;
+            if (tmpSrc->moments->Sx > maxSx)
+                continue;
+            if (tmpSrc->moments->Sy < minSy)
+                continue;
+            if (tmpSrc->moments->Sy > maxSy)
+                continue;
+            tmpSx->data.F32[tmpSx->n] = tmpSrc->moments->Sx;
+            tmpSy->data.F32[tmpSy->n] = tmpSrc->moments->Sy;
+            tmpSx->n++;
+            tmpSy->n++;
+            if (tmpSx->n == tmpSx->nalloc) {
+                psVectorRealloc (tmpSx, tmpSx->nalloc + 100);
+                psVectorRealloc (tmpSy, tmpSy->nalloc + 100);
+            }
+        }
+
+        // measures stats of Sx, Sy
+        stats = psStatsAlloc (PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV);
+
+        stats = psVectorStats (stats, tmpSx, NULL, NULL, 0);
+        psfClump.X  = stats->clippedMean;
+        psfClump.dX = stats->clippedStdev;
+
+        stats = psVectorStats (stats, tmpSy, NULL, NULL, 0);
+        psfClump.Y  = stats->clippedMean;
+        psfClump.dY = stats->clippedStdev;
+
+        psTrace (".pmObjects.pmSourceRoughClass", 2, "clump  X,  Y: %f, %f\n", psfClump.X, psfClump.Y);
+        psTrace (".pmObjects.pmSourceRoughClass", 2, "clump DX, DY: %f, %f\n", psfClump.dX, psfClump.dY);
+        // these values should be pushed on the metadata somewhere
+
+        psFree (stats);
+        psFree (peaks);
+        psFree (tmpSx);
+        psFree (tmpSy);
+    }
+
+    psTrace(__func__, 3, "---- %s() end ----\n", __func__);
+    return (psfClump);
+}
+
+/******************************************************************************
+pmSourceRoughClass(source, metadata): make a guess at the source
+classification.
+ 
+XXX: push the clump info into the metadata?
+ 
+XXX: How can this function ever return FALSE?
+ 
+XXX EAM : add the saturated mask value to metadata 
+*****************************************************************************/
+
+bool pmSourceRoughClass(psArray *sources, psMetadata *metadata, pmPSFClump clump)
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+
+    psBool rc = true;
+
+    int Nsat     = 0;
+    int Ngal     = 0;
+    int Nstar    = 0;
+    int Npsf     = 0;
+    int Ncr      = 0;
+    int Nsatstar = 0;
+
+    psVector *starsn = psVectorAlloc (sources->n, PS_TYPE_F32);
+    starsn->n = 0;
+
+    // check return status value (do these exist?)
+    bool status;
+    psF32 RDNOISE    = psMetadataLookupF32 (&status, metadata, "RDNOISE");
+    psF32 GAIN       = psMetadataLookupF32 (&status, metadata, "GAIN");
+    psF32 PSF_SN_LIM = psMetadataLookupF32 (&status, metadata, "PSF_SN_LIM");
+    // psF32 SATURATE = psMetadataLookupF32 (&status, metadata, "SATURATE");
+
+    // XXX allow clump size to be scaled relative to sigmas?
+    // make rough IDs based on clumpX,Y,DX,DY
+    for (psS32 i = 0 ; i < sources->n ; i++) {
+
+        pmSource *tmpSrc = (pmSource *) sources->data[i];
+
+        tmpSrc->peak->class = 0;
+
+        psF32 sigX = tmpSrc->moments->Sx;
+        psF32 sigY = tmpSrc->moments->Sy;
+
+        // calculate and save signal-to-noise estimates
+        psF32 S  = tmpSrc->moments->Sum;
+        psF32 A  = 4 * M_PI * sigX * sigY;
+        psF32 B  = tmpSrc->moments->Sky;
+        psF32 RT = sqrt(S + (A * B) + (A * PS_SQR(RDNOISE) / sqrt(GAIN)));
+        psF32 SN = (S * sqrt(GAIN) / RT);
+        tmpSrc->moments->SN = SN;
+
+        // XXX EAM : can we use the value of SATURATE if mask is NULL?
+        //
+        // XXX: Must verify this region (the region argument was added to psImageCountPixelMask()
+        // after EAM wrote this code.
+        //
+        psRegion tmpRegion = psRegionSet(0, tmpSrc->mask->numCols, 0, tmpSrc->mask->numRows);
+        int Nsatpix = psImageCountPixelMask(tmpSrc->mask, tmpRegion, PSPHOT_MASK_SATURATED);
+
+        // saturated star (size consistent with PSF or larger)
+        // Nsigma should be user-configured parameter
+        bool big = (sigX > (clump.X - clump.dX)) && (sigY > (clump.Y - clump.dY));
+        if ((Nsatpix > 1) && big) {
+            tmpSrc->type |= PM_SOURCE_SATSTAR;
+            Nsatstar ++;
+            continue;
+        }
+
+        // saturated object (not a star, eg bleed trails, hot pixels)
+        if (Nsatpix > 1) {
+            tmpSrc->type |= PM_SOURCE_SATURATED;
+            Nsat ++;
+            continue;
+        }
+
+        // likely defect (too small to be stellar) (push out to 3 sigma)
+        // low S/N objects which are small are probably stellar
+        // only set candidate defects if
+        if ((sigX < 0.05) || (sigY < 0.05)) {
+            tmpSrc->type |= PM_SOURCE_DEFECT;
+            Ncr ++;
+            continue;
+        }
+
+        // likely unsaturated galaxy (too large to be stellar)
+        if ((sigX > (clump.X + 3*clump.dX)) || (sigY > (clump.Y + 3*clump.dY))) {
+            tmpSrc->type |= PM_SOURCE_GALAXY;
+            Ngal ++;
+            continue;
+        }
+
+        // the rest are probable stellar objects
+        starsn->data.F32[starsn->n] = SN;
+        starsn->n ++;
+        Nstar ++;
+
+        // PSF star (within 1.5 sigma of clump center
+        psF32 radius = hypot ((sigX-clump.X)/clump.dX, (sigY-clump.Y)/clump.dY);
+        if ((SN > PSF_SN_LIM) && (radius < 1.5)) {
+            tmpSrc->type |= PM_SOURCE_PSFSTAR;
+            Npsf ++;
+            continue;
+        }
+
+        // random type of star
+        tmpSrc->type |= PM_SOURCE_OTHER;
+    }
+
+    {
+        psStats *stats  = NULL;
+        stats = psStatsAlloc (PS_STAT_MIN | PS_STAT_MAX);
+        stats = psVectorStats (stats, starsn, NULL, NULL, 0);
+        psLogMsg ("pmObjects", 3, "SN range: %f - %f\n", stats[0].min, stats[0].max);
+        psFree (stats);
+        psFree (starsn);
+    }
+
+    psTrace (".pmObjects.pmSourceRoughClass", 2, "Nstar:    %3d\n", Nstar);
+    psTrace (".pmObjects.pmSourceRoughClass", 2, "Npsf:     %3d\n", Npsf);
+    psTrace (".pmObjects.pmSourceRoughClass", 2, "Ngal:     %3d\n", Ngal);
+    psTrace (".pmObjects.pmSourceRoughClass", 2, "Nsatstar: %3d\n", Nsatstar);
+    psTrace (".pmObjects.pmSourceRoughClass", 2, "Nsat:     %3d\n", Nsat);
+    psTrace (".pmObjects.pmSourceRoughClass", 2, "Ncr:      %3d\n", Ncr);
+
+    psTrace(__func__, 3, "---- %s(%d) end ----\n", __func__, rc);
+    return(rc);
+}
+
+/** pmSourceDefinePixels()
+ * 
+ * Define psImage subarrays for the source located at coordinates x,y on the
+ * image set defined by readout. The pixels defined by this operation consist of
+ * a square window (of full width 2Radius+1) centered on the pixel which contains
+ * the given coordinate, in the frame of the readout. The window is defined to
+ * have limits which are valid within the boundary of the readout image, thus if
+ * the radius would fall outside the image pixels, the subimage is truncated to
+ * only consist of valid pixels. If readout->mask or readout->weight are not
+ * NULL, matching subimages are defined for those images as well. This function
+ * fails if no valid pixels can be defined (x or y less than Radius, for
+ * example). This function should be used to define a region of interest around a
+ * source, including both source and sky pixels.
+ * 
+ * XXX: must code this.
+ * 
+ */
+bool pmSourceDefinePixels(
+    pmSource *mySource,                 ///< Add comment.
+    pmReadout *readout,                 ///< Add comment.
+    psF32 x,                            ///< Add comment.
+    psF32 y,                            ///< Add comment.
+    psF32 Radius)                       ///< Add comment.
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+    psLogMsg(__func__, PS_LOG_WARN, "WARNING: pmSourceDefinePixels() has not been implemented.  Returning FALSE.\n");
+    psTrace(__func__, 3, "---- %s() end ----\n", __func__);
+    return(false);
+}
+
+/******************************************************************************
+pmSourceSetPixelsCircle(source, image, radius)
+ 
+XXX: This was replaced by DefinePixels in SDRS.  Remove it.
+*****************************************************************************/
+bool pmSourceSetPixelsCircle(pmSource *source,
+                             const psImage *image,
+                             psF32 radius)
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_IMAGE_NON_NULL(image, false);
+    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, false);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_PTR_NON_NULL(source->moments, false);
+    PS_ASSERT_PTR_NON_NULL(source->peak, false);
+    PS_FLOAT_COMPARE(0.0, radius, false);
+
+    //
+    // We define variables for code readability.
+    //
+    // XXX: Since the peak->xy coords are in image, not subImage coords,
+    // these variables should be renamed for clarity (imageCenterRow, etc).
+    //
+    psS32 radiusS32 = (psS32) radius;
+    psS32 SubImageCenterRow = source->peak->y;
+    psS32 SubImageCenterCol = source->peak->x;
+    // XXX EAM : for the circle to stay on the image
+    // XXX EAM : EndRow is *exclusive* of pixel region (ie, last pixel + 1)
+    psS32 SubImageStartRow  = PS_MAX (0, SubImageCenterRow - radiusS32);
+    psS32 SubImageEndRow    = PS_MIN (image->numRows, SubImageCenterRow + radiusS32 + 1);
+    psS32 SubImageStartCol  = PS_MAX (0, SubImageCenterCol - radiusS32);
+    psS32 SubImageEndCol    = PS_MIN (image->numCols, SubImageCenterCol + radiusS32 + 1);
+
+    // XXX: Must recycle image.
+    // XXX EAM: this message reflects a programming error we know about.
+    //          i am setting it to a trace message which we can take out
+    if (source->pixels != NULL) {
+        psTrace (".psModule.pmObjects.pmSourceSetPixelsCircle", 4,
+                 "WARNING: pmSourceSetPixelsCircle(): image->pixels not NULL.  Freeing and reallocating.\n");
+        psFree(source->pixels);
+    }
+    source->pixels = psImageSubset((psImage *) image, psRegionSet(SubImageStartCol,
+                                   SubImageStartRow,
+                                   SubImageEndCol,
+                                   SubImageEndRow));
+
+    // XXX: Must recycle image.
+    if (source->mask != NULL) {
+        psFree(source->mask);
+    }
+    source->mask = psImageAlloc(source->pixels->numCols,
+                                source->pixels->numRows,
+                                PS_TYPE_U8); // XXX EAM : type was F32
+
+    //
+    // Loop through the subimage mask, initialize mask to 0 or 1.
+    // XXX EAM: valid pixels should have 0, not 1
+    for (psS32 row = 0 ; row < source->mask->numRows; row++) {
+        for (psS32 col = 0 ; col < source->mask->numCols; col++) {
+
+            if (checkRadius2((psF32) radiusS32,
+                             (psF32) radiusS32,
+                             radius,
+                             (psF32) col,
+                             (psF32) row)) {
+                source->mask->data.U8[row][col] = 0;
+            } else {
+                source->mask->data.U8[row][col] = 1;
+            }
+        }
+    }
+    psTrace(__func__, 3, "---- %s(true) end ----\n", __func__);
+    return(true);
+}
+
+/******************************************************************************
+pmSourceModelGuess(source, model): This function allocates a new
+pmModel structure based on the given modelType specified in the argument list.  
+The corresponding pmModelGuess function is returned, and used to 
+supply the values of the params array in the pmModel structure.  
+ 
+XXX: Many parameters are based on the src->moments structure, which is in
+image, not subImage coords.  Therefore, the calls to the model evaluation
+functions will be in image, not subImage coords.  Remember this.
+*****************************************************************************/
+pmModel *pmSourceModelGuess(pmSource *source,
+                            pmModelType modelType)
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(source->moments, false);
+    PS_ASSERT_PTR_NON_NULL(source->peak, false);
+
+    pmModel *model = pmModelAlloc(modelType);
+
+    pmModelGuessFunc modelGuessFunc = pmModelGuessFunc_GetFunction(modelType);
+    modelGuessFunc(model, source);
+    psTrace(__func__, 3, "---- %s() end ----\n", __func__);
+    return(model);
+}
+
+/******************************************************************************
+evalModel(source, level, row): a private function which evaluates the
+source->modelPSF function at the specified coords.  The coords are subImage, not
+image coords.
+ 
+NOTE: The coords are in subImage source->pixel coords, not image coords.
+ 
+XXX: reverse order of row,col args?
+ 
+XXX: rename all coords in this file such that their name defines whether
+the coords is in subImage or image space.
+ 
+XXX: This should probably be a public pmModules function.
+ 
+XXX: Use static vectors for x.
+ 
+XXX: Figure out if it's (row, col) or (col, row) for the model functions.
+ 
+XXX: For a while, the first psVectorAlloc() was generating a seg fault during
+testing.  Try to reproduce that and debug.
+*****************************************************************************/
+
+// XXX EAM : I have made this a public function
+// XXX EAM : this now uses a pmModel as the input
+// XXX EAM : it was using src->type to find the model, not model->type
+psF32 pmModelEval(pmModel *model, psImage *image, psS32 col, psS32 row)
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(image, false);
+    PS_ASSERT_PTR_NON_NULL(model, false);
+    PS_ASSERT_PTR_NON_NULL(model->params, false);
+
+    // Allocate the x coordinate structure and convert row/col to image space.
+    //
+    psVector *x = psVectorAlloc(2, PS_TYPE_F32);
+    x->data.F32[0] = (psF32) (col + image->col0);
+    x->data.F32[1] = (psF32) (row + image->row0);
+    psF32 tmpF;
+    pmModelFunc modelFunc;
+
+    modelFunc = pmModelFunc_GetFunction (model->type);
+    tmpF = modelFunc (NULL, model->params, x);
+    psFree(x);
+    psTrace(__func__, 3, "---- %s() end ----\n", __func__);
+    return(tmpF);
+}
+
+/******************************************************************************
+findValue(source, level, row, col, dir): a private function which determines
+the column coordinate of the model function which has the value "level".  If
+dir equals 0, then you loop leftwards from the peak pixel, otherwise,
+rightwards.
+ 
+XXX: reverse order of row,col args?
+ 
+XXX: Input row/col are in image coords.
+ 
+XXX: The result is returned in image coords.
+*****************************************************************************/
+static psF32 findValue(pmSource *source,
+                       psF32 level,
+                       psU32 row,
+                       psU32 col,
+                       psU32 dir)
+{
+    psTrace(__func__, 4, "---- %s() begin ----\n", __func__);
+    //
+    // Convert coords to subImage space.
+    //
+    psU32 subRow = row - source->pixels->row0;
+    psU32 subCol = col - source->pixels->col0;
+
+    // Ensure that the starting column is allowable.
+    if (!((0 <= subCol) && (subCol < source->pixels->numCols))) {
+        psError(PS_ERR_UNKNOWN, true, "Starting column outside subImage range");
+        psTrace(__func__, 4, "---- %s(NAN) end ----\n", __func__);
+        return(NAN);
+    }
+    if (!((0 <= subRow) && (subRow < source->pixels->numRows))) {
+        psTrace(__func__, 4, "---- %s(NAN) end ----\n", __func__);
+        psError(PS_ERR_UNKNOWN, true, "Starting row outside subImage range");
+        return(NAN);
+    }
+
+    // XXX EAM : i changed this to match pmModelEval above, but see
+    // XXX EAM   the note below in pmSourceContour
+    psF32 oldValue = pmModelEval(source->modelFLT, source->pixels, subCol, subRow);
+    if (oldValue == level) {
+        psTrace(__func__, 4, "---- %s() end ----\n", __func__);
+        return(((psF32) (subCol + source->pixels->col0)));
+    }
+
+    //
+    // We define variables incr and lastColumn so that we can use the same loop
+    // whether we are stepping leftwards, or rightwards.
+    //
+    psS32 incr;
+    psS32 lastColumn;
+    if (dir == 0) {
+        incr = -1;
+        lastColumn = -1;
+    } else {
+        incr = 1;
+        lastColumn = source->pixels->numCols;
+    }
+    subCol+=incr;
+
+    while (subCol != lastColumn) {
+        psF32 newValue = pmModelEval(source->modelFLT, source->pixels, subCol, subRow);
+        if (oldValue == level) {
+            psTrace(__func__, 4, "---- %s() end ----\n", __func__);
+            return((psF32) (subCol + source->pixels->col0));
+        }
+
+        if ((newValue <= level) && (level <= oldValue)) {
+            // This is simple linear interpolation.
+            psTrace(__func__, 4, "---- %s() end ----\n", __func__);
+            return( ((psF32) (subCol + source->pixels->col0)) + ((psF32) incr) * ((level - newValue) / (oldValue - newValue)) );
+        }
+
+        if ((oldValue <= level) && (level <= newValue)) {
+            // This is simple linear interpolation.
+            psTrace(__func__, 4, "---- %s() end ----\n", __func__);
+            return( ((psF32) (subCol + source->pixels->col0)) + ((psF32) incr) * ((level - oldValue) / (newValue - oldValue)) );
+        }
+
+        subCol+=incr;
+    }
+
+    psTrace(__func__, 4, "---- %s(NAN) end ----\n", __func__);
+    return(NAN);
+}
+
+/******************************************************************************
+pmSourceContour(src, img, level, mode): For an input subImage, and model, this
+routine returns a psArray of coordinates that evaluate to the specified level.
+ 
+XXX: Probably should remove the "image" argument.
+XXX: What type should the output coordinate vectors consist of?  col,row?
+XXX: Why a pmArray output?
+XXX: doex x,y correspond with col,row or row/col?
+XXX: What is mode?
+XXX: The top, bottom of the contour is not correctly determined.
+XXX EAM : this functions is using the model for the contour, but it should
+          be using only the image counts
+*****************************************************************************/
+psArray *pmSourceContour(pmSource *source,
+                         const psImage *image,
+                         psF32 level,
+                         pmContourType mode)
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_PTR_NON_NULL(image, false);
+    PS_ASSERT_PTR_NON_NULL(source->moments, false);
+    PS_ASSERT_PTR_NON_NULL(source->peak, false);
+    PS_ASSERT_PTR_NON_NULL(source->pixels, false);
+    PS_ASSERT_PTR_NON_NULL(source->modelFLT, false);
+    // XXX EAM : what is the purpose of modelPSF/modelFLT?
+
+    //
+    // Allocate data for x/y pairs.
+    //
+    psVector *xVec = psVectorAlloc(2 * source->pixels->numRows, PS_TYPE_F32);
+    psVector *yVec = psVectorAlloc(2 * source->pixels->numRows, PS_TYPE_F32);
+
+    //
+    // Start at the row with peak pixel, then decrement.
+    //
+    psS32 col = source->peak->x;
+    for (psS32 row = source->peak->y; row>= 0 ; row--) {
+        // XXX: yVec contain no real information.  Do we really need it?
+        yVec->data.F32[row] = (psF32) (source->pixels->row0 + row);
+        yVec->data.F32[row+yVec->n] = (psF32) (source->pixels->row0 + row);
+
+        // Starting at peak pixel, search leftwards for the column intercept.
+        psF32 leftIntercept = findValue(source, level, row, col, 0);
+        if (isnan(leftIntercept)) {
+            psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
+            psFree(xVec);
+            psFree(yVec);
+            psTrace(__func__, 3, "---- %s(NULL) end ----\n", __func__);
+            return(NULL);
+            //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
+        }
+        xVec->data.F32[row] = ((psF32) source->pixels->col0) + leftIntercept;
+
+        // Starting at peak pixel, search rightwards for the column intercept.
+
+        psF32 rightIntercept = findValue(source, level, row, col, 1);
+        if (isnan(rightIntercept)) {
+            psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
+            psFree(xVec);
+            psFree(yVec);
+            psTrace(__func__, 3, "---- %s(NULL) end ----\n", __func__);
+            return(NULL);
+            //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
+        }
+        psTrace(__func__, 4, "The intercepts are (%.2f, %.2f)\n", leftIntercept, rightIntercept);
+        xVec->data.F32[row+xVec->n] = ((psF32) source->pixels->col0) + rightIntercept;
+
+        // Set starting column for next row
+        col = (psS32) ((leftIntercept + rightIntercept) / 2.0);
+    }
+    //
+    // Start at the row (+1) with peak pixel, then increment.
+    //
+    col = source->peak->x;
+    for (psS32 row = 1 + source->peak->y; row < source->pixels->numRows ; row++) {
+        // XXX: yVec contain no real information.  Do we really need it?
+        yVec->data.F32[row] = (psF32) (source->pixels->row0 + row);
+        yVec->data.F32[row+yVec->n] = (psF32) (source->pixels->row0 + row);
+
+        // Starting at peak pixel, search leftwards for the column intercept.
+        psF32 leftIntercept = findValue(source, level, row, col, 0);
+        if (isnan(leftIntercept)) {
+            psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
+            psFree(xVec);
+            psFree(yVec);
+            psTrace(__func__, 3, "---- %s(NULL) end ----\n", __func__);
+            return(NULL);
+            //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
+        }
+        xVec->data.F32[row] = ((psF32) source->pixels->col0) + leftIntercept;
+
+        // Starting at peak pixel, search rightwards for the column intercept.
+        psF32 rightIntercept = findValue(source, level, row, col, 1);
+        if (isnan(rightIntercept)) {
+            psError(PS_ERR_UNKNOWN, true, "Could not find contour edge (NAN)");
+            psFree(xVec);
+            psFree(yVec);
+            psTrace(__func__, 3, "---- %s(NULL) end ----\n", __func__);
+            return(NULL);
+            //psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not find contour edge (NAN)\n");
+        }
+        xVec->data.F32[row+xVec->n] = ((psF32) source->pixels->col0) + rightIntercept;
+
+        // Set starting column for next row
+        col = (psS32) ((leftIntercept + rightIntercept) / 2.0);
+    }
+
+    //
+    // Allocate an array for result, store coord vectors there.
+    //
+    psArray *tmpArray = psArrayAlloc(2);
+    tmpArray->data[0] = (psPtr *) yVec;
+    tmpArray->data[1] = (psPtr *) xVec;
+    psTrace(__func__, 3, "---- %s() end ----\n", __func__);
+    return(tmpArray);
+}
+
+// XXX EAM : these are better starting values, but should be available from metadata?
+#define PM_SOURCE_FIT_MODEL_NUM_ITERATIONS 15
+#define PM_SOURCE_FIT_MODEL_TOLERANCE 0.1
+/******************************************************************************
+pmSourceFitModel(source, model): must create the appropiate arguments to the
+LM minimization routines for the various p_pmMinLM_XXXXXX_Vec() functions.
+ 
+XXX: should there be a mask value?
+XXX EAM : fit the specified model (not necessarily the one in source)
+*****************************************************************************/
+bool pmSourceFitModel_v5(pmSource *source,
+                         pmModel *model,
+                         const bool PSF)
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_PTR_NON_NULL(source->moments, false);
+    PS_ASSERT_PTR_NON_NULL(source->peak, false);
+    PS_ASSERT_PTR_NON_NULL(source->pixels, false);
+    PS_ASSERT_PTR_NON_NULL(source->mask, false);
+    PS_ASSERT_PTR_NON_NULL(source->weight, false);
+    psBool fitStatus = true;
+    psBool onPic     = true;
+    psBool rc        = true;
+
+    // XXX EAM : is it necessary for the mask & weight to exist?  the
+    //           tests below could be conditions (!NULL)
+
+    psVector *params = model->params;
+    psVector *dparams = model->dparams;
+    psVector *paramMask = NULL;
+
+    pmModelFunc modelFunc = pmModelFunc_GetFunction (model->type);
+
+    int nParams = PSF ? params->n - 4 : params->n;
+
+    // find the number of valid pixels
+    // XXX EAM : this loop and the loop below could just be one pass
+    //           using the psArrayAdd and psVectorExtend functions
+    psS32 count = 0;
+    for (psS32 i = 0; i < source->pixels->numRows; i++) {
+        for (psS32 j = 0; j < source->pixels->numCols; j++) {
+            if (source->mask->data.U8[i][j] == 0) {
+                count++;
+            }
+        }
+    }
+    if (count <  nParams + 1) {
+        psTrace (".pmObjects.pmSourceFitModel", 4, "insufficient valid pixels\n");
+        psTrace(__func__, 3, "---- %s(false) end ----\n", __func__);
+        return(false);
+    }
+
+    // construct the coordinate and value entries
+    psArray *x = psArrayAlloc(count);
+    psVector *y = psVectorAlloc(count, PS_TYPE_F32);
+    psVector *yErr = psVectorAlloc(count, PS_TYPE_F32);
+    psS32 tmpCnt = 0;
+    for (psS32 i = 0; i < source->pixels->numRows; i++) {
+        for (psS32 j = 0; j < source->pixels->numCols; j++) {
+            if (source->mask->data.U8[i][j] == 0) {
+                psVector *coord = psVectorAlloc(2, PS_TYPE_F32);
+                // XXX: Convert i/j to image space:
+                // XXX EAM: coord order is (x,y) == (col,row)
+                coord->data.F32[0] = (psF32) (j + source->pixels->col0);
+                coord->data.F32[1] = (psF32) (i + source->pixels->row0);
+                x->data[tmpCnt] = (psPtr *) coord;
+                y->data.F32[tmpCnt] = source->pixels->data.F32[i][j];
+                yErr->data.F32[tmpCnt] = sqrt (source->weight->data.F32[i][j]);
+                // XXX EAM : note the wasted effort: we carry dY^2, take sqrt(), then
+                //           the minimization function calculates sq()
+                tmpCnt++;
+            }
+        }
+    }
+
+    psMinimization *myMin = psMinimizationAlloc(PM_SOURCE_FIT_MODEL_NUM_ITERATIONS,
+                            PM_SOURCE_FIT_MODEL_TOLERANCE);
+
+    // PSF model only fits first 4 parameters, FLT model fits all
+    if (PSF) {
+        paramMask = psVectorAlloc (params->n, PS_TYPE_U8);
+        for (int i = 0; i < 4; i++) {
+            paramMask->data.U8[i] = 0;
+        }
+        for (int i = 4; i < paramMask->n; i++) {
+            paramMask->data.U8[i] = 1;
+        }
+    }
+
+    // XXX EAM : covar must be F64?
+    psImage *covar = psImageAlloc (params->n, params->n, PS_TYPE_F64);
+
+    psTrace (".pmObjects.pmSourceFitModel", 5, "fitting function\n");
+    fitStatus = psMinimizeLMChi2(myMin, covar, params, paramMask, x, y, yErr, modelFunc);
+    for (int i = 0; i < dparams->n; i++) {
+        if ((paramMask != NULL) && paramMask->data.U8[i])
+            continue;
+        dparams->data.F32[i] = sqrt(covar->data.F64[i][i]);
+    }
+
+    // XXX EAM: we need to do something (give an error?) if rc is false
+    // XXX EAM: psMinimizeLMChi2 does not check convergence
+
+    // XXX models can go insane: reject these
+    onPic &= (params->data.F32[2] >= source->pixels->col0);
+    onPic &= (params->data.F32[2] <  source->pixels->col0 + source->pixels->numCols);
+    onPic &= (params->data.F32[3] >= source->pixels->row0);
+    onPic &= (params->data.F32[3] <  source->pixels->row0 + source->pixels->numRows);
+
+    // XXX EAM: save the resulting chisq, nDOF, nIter
+    model->chisq = myMin->value;
+    model->nIter = myMin->iter;
+    model->nDOF  = y->n - nParams;
+
+    // XXX EAM get the Gauss-Newton distance for fixed model parameters
+    if (paramMask != NULL) {
+        psVector *delta = psVectorAlloc (params->n, PS_TYPE_F64);
+        psMinimizeGaussNewtonDelta (delta, params, NULL, x, y, yErr, modelFunc);
+        for (int i = 0; i < dparams->n; i++) {
+            if (!paramMask->data.U8[i])
+                continue;
+            dparams->data.F32[i] = delta->data.F64[i];
+        }
+        psFree (delta);
+    }
+
+    psFree(x);
+    psFree(y);
+    psFree(yErr);
+    psFree(myMin);
+    psFree(covar);
+    psFree(paramMask);
+
+    rc = (onPic && fitStatus);
+    psTrace(__func__, 3, "---- %s(%d) end ----\n", __func__, rc);
+    return(rc);
+}
+
+// XXX EAM : new version with parameter range limits and weight enhancement
+bool pmSourceFitModel (pmSource *source,
+                       pmModel *model,
+                       const bool PSF)
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_PTR_NON_NULL(source->moments, false);
+    PS_ASSERT_PTR_NON_NULL(source->peak, false);
+    PS_ASSERT_PTR_NON_NULL(source->pixels, false);
+    PS_ASSERT_PTR_NON_NULL(source->mask, false);
+    PS_ASSERT_PTR_NON_NULL(source->weight, false);
+
+    // XXX EAM : is it necessary for the mask & weight to exist?  the
+    //           tests below could be conditions (!NULL)
+
+    psBool fitStatus = true;
+    psBool onPic     = true;
+    psBool rc        = true;
+    psF32  Ro, ymodel;
+
+    psVector *params = model->params;
+    psVector *dparams = model->dparams;
+    psVector *paramMask = NULL;
+
+    pmModelFunc modelFunc = pmModelFunc_GetFunction (model->type);
+
+    // XXX EAM : I need to use the sky value to constrain the weight model
+    int nParams = PSF ? params->n - 4 : params->n;
+    psF32 So = params->data.F32[0];
+
+    // find the number of valid pixels
+    // XXX EAM : this loop and the loop below could just be one pass
+    //           using the psArrayAdd and psVectorExtend functions
+    psS32 count = 0;
+    for (psS32 i = 0; i < source->pixels->numRows; i++) {
+        for (psS32 j = 0; j < source->pixels->numCols; j++) {
+            if (source->mask->data.U8[i][j] == 0) {
+                count++;
+            }
+        }
+    }
+    if (count <  nParams + 1) {
+        psTrace (".pmObjects.pmSourceFitModel", 4, "insufficient valid pixels\n");
+        psTrace(__func__, 3, "---- %s(false) end ----\n", __func__);
+        return(false);
+    }
+
+    // construct the coordinate and value entries
+    psArray *x = psArrayAlloc(count);
+    psVector *y = psVectorAlloc(count, PS_TYPE_F32);
+    psVector *yErr = psVectorAlloc(count, PS_TYPE_F32);
+    psS32 tmpCnt = 0;
+    for (psS32 i = 0; i < source->pixels->numRows; i++) {
+        for (psS32 j = 0; j < source->pixels->numCols; j++) {
+            if (source->mask->data.U8[i][j] == 0) {
+                psVector *coord = psVectorAlloc(2, PS_TYPE_F32);
+                // XXX: Convert i/j to image space:
+                // XXX EAM: coord order is (x,y) == (col,row)
+                coord->data.F32[0] = (psF32) (j + source->pixels->col0);
+                coord->data.F32[1] = (psF32) (i + source->pixels->row0);
+                x->data[tmpCnt] = (psPtr *) coord;
+                y->data.F32[tmpCnt] = source->pixels->data.F32[i][j];
+
+                // compare observed flux to model flux to adjust weight
+                ymodel = modelFunc (NULL, model->params, coord);
+
+                // this test enhances the weight based on deviation from the model flux
+                Ro = 1.0 + fabs (y->data.F32[tmpCnt] - ymodel) / sqrt(PS_SQR(ymodel - So) + PS_SQR(So));
+
+                // psMinimizeLMChi2_EAM takes wt = 1/dY^2
+                if (source->weight->data.F32[i][j] == 0) {
+                    yErr->data.F32[tmpCnt] = 0.0;
+                } else {
+                    yErr->data.F32[tmpCnt] = 1.0 / (source->weight->data.F32[i][j] * Ro);
+                }
+                tmpCnt++;
+            }
+        }
+    }
+
+    psMinimization *myMin = psMinimizationAlloc(PM_SOURCE_FIT_MODEL_NUM_ITERATIONS,
+                            PM_SOURCE_FIT_MODEL_TOLERANCE);
+
+    // PSF model only fits first 4 parameters, FLT model fits all
+    if (PSF) {
+        paramMask = psVectorAlloc (params->n, PS_TYPE_U8);
+        for (int i = 0; i < 4; i++) {
+            paramMask->data.U8[i] = 0;
+        }
+        for (int i = 4; i < paramMask->n; i++) {
+            paramMask->data.U8[i] = 1;
+        }
+    }
+
+    // XXX EAM : I've added three types of parameter range checks
+    // XXX EAM : this requires my new psMinimization functions
+    pmModelLimits modelLimits = pmModelLimits_GetFunction (model->type);
+    psVector *beta_lim = NULL;
+    psVector *params_min = NULL;
+    psVector *params_max = NULL;
+
+    // XXX EAM : in this implementation, I pass in the limits with the covar matrix.
+    //           in the SDRS, I define a new psMinimization which will take these in
+    psImage *covar = psImageAlloc (params->n, 3, PS_TYPE_F64);
+    modelLimits (&beta_lim, &params_min, &params_max);
+    for (int i = 0; i < params->n; i++) {
+        covar->data.F64[0][i] = beta_lim->data.F32[i];
+        covar->data.F64[1][i] = params_min->data.F32[i];
+        covar->data.F64[2][i] = params_max->data.F32[i];
+    }
+
+    psTrace (".pmObjects.pmSourceFitModel", 5, "fitting function\n");
+    fitStatus = psMinimizeLMChi2(myMin, covar, params, paramMask, x, y, yErr, modelFunc);
+    for (int i = 0; i < dparams->n; i++) {
+        if ((paramMask != NULL) && paramMask->data.U8[i])
+            continue;
+        dparams->data.F32[i] = sqrt(covar->data.F64[i][i]);
+    }
+
+    // XXX EAM: we need to do something (give an error?) if rc is false
+    // XXX EAM: psMinimizeLMChi2 does not check convergence
+
+    // XXX models can go insane: reject these
+    onPic &= (params->data.F32[2] >= source->pixels->col0);
+    onPic &= (params->data.F32[2] <  source->pixels->col0 + source->pixels->numCols);
+    onPic &= (params->data.F32[3] >= source->pixels->row0);
+    onPic &= (params->data.F32[3] <  source->pixels->row0 + source->pixels->numRows);
+
+    // XXX EAM: save the resulting chisq, nDOF, nIter
+    model->chisq = myMin->value;
+    model->nIter = myMin->iter;
+    model->nDOF  = y->n - nParams;
+
+    // XXX EAM get the Gauss-Newton distance for fixed model parameters
+    if (paramMask != NULL) {
+        psVector *delta = psVectorAlloc (params->n, PS_TYPE_F64);
+        psMinimizeGaussNewtonDelta(delta, params, NULL, x, y, yErr, modelFunc);
+        for (int i = 0; i < dparams->n; i++) {
+            if (!paramMask->data.U8[i])
+                continue;
+            dparams->data.F32[i] = delta->data.F64[i];
+        }
+    }
+
+    psFree(paramMask);
+    psFree(x);
+    psFree(y);
+    psFree(myMin);
+
+    rc = (onPic && fitStatus);
+    psTrace(__func__, 3, "---- %s(%d) end ----\n", __func__, rc);
+    return(rc);
+}
+
+bool p_pmSourceAddOrSubModel(psImage *image,
+                             psImage *mask,
+                             pmModel *model,
+                             bool center,
+                             psS32 flag)
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+
+    PS_ASSERT_PTR_NON_NULL(model, false);
+    PS_ASSERT_IMAGE_NON_NULL(image, false);
+    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, false);
+
+    psVector *x = psVectorAlloc(2, PS_TYPE_F32);
+    psVector *params = model->params;
+    pmModelFunc modelFunc = pmModelFunc_GetFunction (model->type);
+    psS32 imageCol;
+    psS32 imageRow;
+
+    for (psS32 i = 0; i < image->numRows; i++) {
+        for (psS32 j = 0; j < image->numCols; j++) {
+            if ((mask != NULL) && mask->data.U8[i][j])
+                continue;
+            psF32 pixelValue;
+            // XXX: Should you be adding the pixels for the entire subImage,
+            // or a radius of pixels around it?
+
+            // Convert i/j to imace coord space:
+            // XXX: Make sure you have col/row order correct.
+            // XXX EAM : 'center' option changes this
+            // XXX EAM : i == numCols/2 -> x = model->params->data.F32[2]
+            if (center) {
+                imageCol = j - 0.5*image->numCols + model->params->data.F32[2];
+                imageRow = i - 0.5*image->numRows + model->params->data.F32[3];
+            } else {
+                imageCol = j + image->col0;
+                imageRow = i + image->row0;
+            }
+
+            x->data.F32[0] = (float) imageCol;
+            x->data.F32[1] = (float) imageRow;
+            pixelValue = modelFunc (NULL, params, x);
+            // fprintf (stderr, "%f %f  %d %d  %f\n", x->data.F32[0], x->data.F32[1], i, j, pixelValue);
+
+            if (flag == 1) {
+                pixelValue = -pixelValue;
+            }
+
+            // XXX: Must figure out how to calculate the image coordinates and
+            // how to use the boolean "center" flag.
+
+            image->data.F32[i][j]+= pixelValue;
+        }
+    }
+    psFree(x);
+    psTrace(__func__, 3, "---- %s(true) end ----\n", __func__);
+    return(true);
+}
+
+
+
+/******************************************************************************
+ *****************************************************************************/
+bool pmSourceAddModel(psImage *image,
+                      psImage *mask,
+                      pmModel *model,
+                      bool center)
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+    psBool rc = p_pmSourceAddOrSubModel(image, mask, model, center, 0);
+    psTrace(__func__, 3, "---- %s(%d) end ----\n", __func__, rc);
+    return(rc);
+}
+
+/******************************************************************************
+ *****************************************************************************/
+bool pmSourceSubModel(psImage *image,
+                      psImage *mask,
+                      pmModel *model,
+                      bool center)
+{
+    psTrace(__func__, 3, "---- %s() begin ----\n", __func__);
+    psBool rc = p_pmSourceAddOrSubModel(image, mask, model, center, 1);
+    psTrace(__func__, 3, "---- %s(%d) end ----\n", __func__, rc);
+    return(rc);
+}
+
+
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/pmObjects.h
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/pmObjects.h	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/pmObjects.h	(revision 22115)
@@ -0,0 +1,622 @@
+/** @file  pmObjects.h
+ *
+ * The process of finding, measuring, and classifying astronomical sources on
+ * images is one of the critical tasks of the IPP or any astronomical software
+ * system. This file will define structures and functions related to the task
+ * of source detection and measurement. The elements defined in this section 
+ * are generally low-level components which can be connected together to
+ * construct a complete object measurement suite.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-10-10 19:53:40 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+#if !defined(PM_OBJECTS_H)
+#define PM_OBJECTS_H
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <math.h>
+#include "pslib.h"
+#include "pmAstrometry.h"
+/**
+ * In the object analysis process, we will use specific mask values to mark the
+ * image pixels. The following structure defines the relevant mask values.
+ *
+ * XXX: This is probably a bad solution: we will want to set mask values
+ * outside of the PSPHOT code.  Perhaps we can set up a registered set of mask
+ * values with specific meanings that other functions can add to or define?
+ */
+enum {
+    PSPHOT_MASK_CLEAR     = 0x00,
+    PSPHOT_MASK_INVALID   = 0x01,
+    PSPHOT_MASK_SATURATED = 0x02,
+    PSPHOT_MASK_MARKED    = 0x08,
+} psphotMaskValues;
+
+
+/** pmPeakType
+ * 
+ *  A peak pixel may have several features which may be determined when the
+ *  peak is found or measured. These are specified by the pmPeakType enum.
+ *  PM_PEAK_LONE represents a single pixel which is higher than its 8 immediate
+ *  neighbors.  The PM_PEAK_EDGE represents a peak pixel which touching the image
+ *  edge. The PM_PEAK_FLAT represents a peak pixel which has more than a specific
+ *  number of neighbors at the same value, within some tolarence:
+ * 
+ */
+typedef enum {
+    PM_PEAK_LONE,                       ///< Isolated peak.
+    PM_PEAK_EDGE,                       ///< Peak on edge.
+    PM_PEAK_FLAT,                       ///< Peak has equal-value neighbors.
+    PM_PEAK_UNDEF                       ///< Undefined.
+} pmPeakType;
+
+
+/** pmPeak data structure
+ *  
+ *  A source has the capacity for several types of measurements. The
+ *  simplest measurement of a source is the location and flux of the peak pixel
+ *  associated with the source:
+ *  
+ */
+typedef struct
+{
+    int x;                              ///< X-coordinate of peak pixel.
+    int y;                              ///< Y-coordinate of peak pixel.
+    float counts;                       ///< Value of peak pixel (above sky?).
+    pmPeakType class;                   ///< Description of peak.
+}
+pmPeak;
+
+
+/** pmMoments data structure
+ *  
+ * One of the simplest measurements which can be made quickly for an object
+ * are the object moments. We specify a structure to carry the moment information
+ * for a specific source:
+ *  
+ */
+typedef struct
+{
+    float x;    ///< X-coord of centroid.
+    float y;    ///< Y-coord of centroid.
+    float Sx;    ///< x-second moment.
+    float Sy;    ///< y-second moment.
+    float Sxy;    ///< xy cross moment.
+    float Sum;    ///< Pixel sum above sky (background).
+    float Peak;    ///< Peak counts above sky.
+    float Sky;    ///< Sky level (background).
+    float SN;    ///< approx signal-to-noise
+    int nPixels;   ///< Number of pixels used.
+}
+pmMoments;
+
+
+/** pmPSFClump data structure
+ * 
+ * A collection of object moment measurements can be used to determine
+ * approximate object classes. The key to this analysis is the location and
+ * statistics (in the second-moment plane,
+ *  
+ */
+typedef struct
+{
+    float X;
+    float dX;
+    float Y;
+    float dY;
+}
+pmPSFClump;
+
+typedef int pmModelType;
+#define PS_MODEL_GAUSS 0
+#define PS_MODEL_PGAUSS 1
+#define PS_MODEL_QGAUSS 2
+#define PS_MODEL_SGAUSS 3
+
+
+/** pmModel data structure
+ * 
+ * Every source may have two types of models: a PSF model and a FLT (floating)
+ * model. The PSF model represents the best fit of the image PSF to the specific
+ * object. In this case, the PSF-dependent parameters are specified for the
+ * object by the PSF, not by the fit. The FLT model represents the best fit of
+ * the given model to the object, with all parameters floating in the fit.
+ *  
+ */
+typedef struct
+{
+    pmModelType type;   ///< Model to be used.
+    psVector *params;   ///< Paramater values.
+    psVector *dparams;   ///< Parameter errors.
+    float chisq;   ///< Fit chi-squared.
+    int nDOF;    ///< number of degrees of freedom
+    int nIter;    ///< number of iterations to reach min
+    float radius;   ///< fit radius actually used
+}
+pmModel;
+
+/** pmSourceType enumeration
+ * 
+ * A given source may be identified as most-likely to be one of several source
+ * types. The pmSource entry pmSourceType defines the current best-guess for this
+ * source.
+ * 
+ * XXX: The values given below are currently illustrative and will require
+ * some modification as the source classification code is developed. (TBD)
+ * 
+ */
+typedef enum {
+    PM_SOURCE_DEFECT,                   ///< a cosmic-ray
+    PM_SOURCE_SATURATED,                ///< random saturated pixels
+
+    PM_SOURCE_SATSTAR,                  ///< a saturated star
+    PM_SOURCE_PSFSTAR,                  ///< a PSF star
+    PM_SOURCE_GOODSTAR,                 ///< a good-quality star
+
+    PM_SOURCE_POOR_FIT_PSF,             ///< poor quality PSF fit
+    PM_SOURCE_FAIL_FIT_PSF,             ///< failed to get a good PSF fit
+    PM_SOURCE_FAINTSTAR,                ///< below S/N cutoff
+
+    PM_SOURCE_GALAXY,                   ///< an extended object (galaxy)
+    PM_SOURCE_FAINT_GALAXY,             ///< a galaxy below S/N cutoff
+    PM_SOURCE_DROP_GALAXY,              ///< ?
+    PM_SOURCE_FAIL_FIT_GAL,             ///< failed on the galaxy fit
+    PM_SOURCE_POOR_FIT_GAL,             ///< poor quality galaxy fit
+
+    PM_SOURCE_OTHER,                    ///< unidentified
+} pmSourceType;
+
+/** pmSource data structure
+ *  
+ *  This source has the capacity for several types of measurements. The
+ *  simplest measurement of a source is the location and flux of the peak pixel
+ *  associated with the source:
+ *  
+ */
+typedef struct
+{
+    pmPeak *peak;   ///< Description of peak pixel.
+    psImage *pixels;   ///< Rectangular region including object pixels.
+    psImage *weight;   ///< Image variance.
+    psImage *mask;   ///< Mask which marks pixels associated with objects.
+    pmMoments *moments;   ///< Basic moments measure for the object.
+    pmModel *modelPSF;   ///< PSF Model fit (parameters and type)
+    pmModel *modelFLT;   ///< FLT (floating) Model fit (parameters and type).
+    pmSourceType type;   ///< Best identification of object.
+}
+pmSource;
+
+
+/** pmPeakAlloc()
+ *
+ *  @return pmPeak*    newly allocated pmPeak with all internal pointers set to NULL
+ */
+pmPeak *pmPeakAlloc(
+    int x,    ///< Row-coordinate in image space
+    int y,    ///< Col-coordinate in image space
+    float counts,   ///< The value of the peak pixel
+    pmPeakType class   ///< The type of peak pixel
+);
+
+
+/** pmMomentsAlloc()
+ * 
+ */
+pmMoments *pmMomentsAlloc();
+
+
+/** pmModelAlloc()
+ * 
+ */
+pmModel *pmModelAlloc(pmModelType type);
+
+
+/** pmSourceAlloc()
+ * 
+ */
+pmSource  *pmSourceAlloc();
+
+
+/** pmFindVectorPeaks()
+ * 
+ * Find all local peaks in the given vector above the given threshold. A peak
+ * is defined as any element with a value greater than its two neighbors and with
+ * a value above the threshold. Two types of special cases must be addressed.
+ * Equal value elements: If an element has the same value as the following
+ * element, it is not considered a peak. If an element has the same value as the
+ * preceding element (but not the following), then it is considered a peak. Note
+ * that this rule (arbitrarily) identifies flat regions by their trailing edge.
+ * Edge cases: At start of the vector, the element must be higher than its
+ * neighbor. At the end of the vector, the element must be higher or equal to its
+ * neighbor. These two rules again places the peak associated with a flat region
+ * which touches the image edge at the image edge. The result of this function is
+ * a vector containing the coordinates (element number) of the detected peaks
+ * (type psU32).
+ * 
+ */
+psVector *pmFindVectorPeaks(
+    const psVector *vector,  ///< The input vector (float)
+    float threshold   ///< Threshold above which to find a peak
+);
+
+
+/** pmFindImagePeaks()
+ * 
+ * Find all local peaks in the given image above the given threshold. This
+ * function should find all row peaks using pmFindVectorPeaks, then test each row
+ * peak and exclude peaks which are not local peaks. A peak is a local peak if it
+ * has a higher value than all 8 neighbors. If the peak has the same value as its
+ * +y neighbor or +x neighbor, it is NOT a local peak. If any other neighbors
+ * have an equal value, the peak is considered a valid peak. Note two points:
+ * first, the +x neighbor condition is already enforced by pmFindVectorPeaks.
+ * Second, these rules have the effect of making flat-topped regions have single
+ * peaks at the (+x,+y) corner. When selecting the peaks, their type must also be
+ * set. The result of this function is an array of pmPeak entries.
+ * 
+ */
+psArray *pmFindImagePeaks(
+    const psImage *image,  ///< The input image where peaks will be found (float)
+    float threshold   ///< Threshold above which to find a peak
+);
+
+
+/** pmCullPeaks()
+ * 
+ * Eliminate peaks from the psList that have a peak value above the given
+ * maximum, or fall outside the valid region.
+ * 
+ */
+psList *pmCullPeaks(
+    psList *peaks,   ///< The psList of peaks to be culled
+    float maxValue,   ///< Cull peaks above this value
+    const psRegion valid                ///< Cull peaks otside this psRegion
+);
+
+
+/** pmPeaksSubset()
+ * 
+ * Create a new peaks array, removing certain types of peaks from the input
+ * array of peaks based on the given criteria. Peaks should be eliminated if they
+ * have a peak value above the given maximum value limit or if the fall outside
+ * the valid region.  The result of the function is a new array with a reduced
+ * number of peaks.
+ * 
+ */
+psArray *pmPeaksSubset(
+    psArray *peaks,                     ///< Add comment.
+    float maxvalue,                     ///< Add comment.
+    const psRegion valid                ///< Add comment.
+);
+
+
+/** pmSourceDefinePixels()
+ * 
+ * Define psImage subarrays for the source located at coordinates x,y on the
+ * image set defined by readout. The pixels defined by this operation consist of
+ * a square window (of full width 2Radius+1) centered on the pixel which contains
+ * the given coordinate, in the frame of the readout. The window is defined to
+ * have limits which are valid within the boundary of the readout image, thus if
+ * the radius would fall outside the image pixels, the subimage is truncated to
+ * only consist of valid pixels. If readout->mask or readout->weight are not
+ * NULL, matching subimages are defined for those images as well. This function
+ * fails if no valid pixels can be defined (x or y less than Radius, for
+ * example). This function should be used to define a region of interest around a
+ * source, including both source and sky pixels.
+ * 
+ * XXX: must code this.
+ * 
+ */
+// XXX: Uncommenting the pmReadout causes compile errors.
+bool pmSourceDefinePixels(
+    pmSource *mySource,                 ///< Add comment.
+    pmReadout *readout,                 ///< Add comment.
+    psF32 x,                            ///< Add comment.
+    psF32 y,                            ///< Add comment.
+    psF32 Radius                        ///< Add comment.
+);
+
+
+/** pmSourceLocalSky()
+ * 
+ * Measure the local sky in the vicinity of the given source. The Radius
+ * defines the square aperture in which the moments will be measured. This
+ * function assumes the source pixels have been defined, and that the value of
+ * Radius here is smaller than the value of Radius used to define the pixels. The
+ * annular region not contained within the radius defined here is used to measure
+ * the local background in the vicinity of the source. The local background
+ * measurement uses the specified statistic passed in via the statsOptions entry.
+ * This function allocates the pmMoments structure. The resulting sky is used to
+ * set the value of the pmMoments.sky element of the provided pmSource structure.
+ * 
+ */
+bool pmSourceLocalSky(
+    pmSource *source,   ///< The input image (float)
+    psStatsOptions statsOptions, ///< The statistic used in calculating the background sky
+    float Radius   ///< The inner radius of the square annulus to exclude
+);
+
+
+/** pmSourceMoments()
+ * 
+ * Measure source moments for the given source, using the value of
+ * source.moments.sky provided as the local background value and the peak
+ * coordinates as the initial source location. The resulting moment values are
+ * applied to the source.moments entry, and the source is returned. The moments
+ * are measured within the given circular radius of the source.peak coordinates.
+ * The return value indicates the success (TRUE) of the operation.
+ * 
+ */
+bool pmSourceMoments(
+    pmSource *source,   ///< The input pmSource for which moments will be computed
+    float radius   ///< Use a circle of pixels around the peak
+);
+
+
+/** pmSourcePSFClump()
+ * 
+ * We use the source moments to make an initial, approximate source
+ * classification, and as part of the information needed to build a PSF model for
+ * the image. As long as the PSF shape does not vary excessively across the
+ * image, the sources which are represented by a PSF (the start) will have very
+ * similar second moments. The function pmSourcePSFClump searches a collection of
+ * sources with measured moments for a group with moments which are all very
+ * similar. The function returns a pmPSFClump structure, representing the
+ * centroid and size of the clump in the sigma_x, sigma_y second-moment plane.
+ * 
+ * The goal is to identify and characterize the stellar clump within the
+ * sigma_x, sigma_y second-moment plane.  To do this, an image is constructed to
+ * represent this plane.  The units of sigma_x and sigma_y are in image pixels. A
+ * pixel in this analysis image represents 0.1 pixels in the input image. The
+ * dimensions of the image need only be 10 pixels. The peak pixel in this image
+ * (above a threshold of half of the image maximum) is found. The coordinates of
+ * this peak pixel represent the 2D mode of the sigma_x, sigma_y distribution.
+ * The sources with sigma_x, sigma_y within 0.2 pixels of this value are then
+ *  * used to calculate the median and standard deviation of the sigma_x, sigma_y
+ * values. These resulting values are returned via the pmPSFClump structure.
+ * 
+ * The return value indicates the success (TRUE) of the operation.
+ * 
+ * XXX: Limit the S/N of the candidate sources (part of Metadata)? (TBD).
+ * XXX: Save the clump parameters on the Metadata (TBD)
+ * 
+ */
+pmPSFClump pmSourcePSFClump(
+    psArray *source,   ///< The input pmSource
+    psMetadata *metadata  ///< Contains classification parameters
+);
+
+
+/** pmSourceRoughClass()
+ * 
+ * Based on the specified data values, make a guess at the source
+ * classification. The sources are provides as a psArray of pmSource entries.
+ * Definable parameters needed to make the classification are provided to the
+ * routine with the psMetadata structure. The rules (in SDRS) refer to values which
+ * can be extracted from the metadata using the given keywords. Except as noted,
+ * the data type for these parameters are psF32.
+ * 
+ */
+bool pmSourceRoughClass(
+    psArray *source,   ///< The input pmSource
+    psMetadata *metadata,  ///< Contains classification parameters
+    pmPSFClump clump   ///< Statistics about the PSF clump
+);
+
+
+/** pmSourceModelGuess()
+ * 
+ * Convert available data to an initial guess for the given model. This
+ * function allocates a pmModel entry for the pmSource structure based on the
+ * provided model selection. The method of defining the model parameter guesses
+ * are specified for each model below. The guess values are placed in the model
+ * parameters. The function returns TRUE on success or FALSE on failure.
+ * 
+ */
+pmModel *pmSourceModelGuess(
+    pmSource *source,   ///< The input pmSource
+    pmModelType model   ///< The type of model to be created.
+);
+
+
+/** pmContourType
+ * 
+ * Only one type is defined at present.
+ * 
+ */
+typedef enum {
+    PS_CONTOUR_CRUDE,
+    PS_CONTOUR_UNKNOWN01,
+    PS_CONTOUR_UNKNOWN02
+} pmContourType;
+
+
+/** pmSourceContour()
+ * 
+ * Find points in a contour for the given source at the given level. If type
+ * is PM_CONTOUR_CRUDE, the contour is found by starting at the source peak,
+ * running along each pixel row until the level is crossed, then interpolating to
+ * the level coordinate for that row. This is done for each row, with the
+ * starting point determined by the midpoint of the previous row, until the
+ * starting point has a value below the contour level. The returned contour
+ * consists of two vectors giving the x and y coordinates of the contour levels.
+ * This function may be used as part of the model guess inputs.  Other contour
+ * types may be specified in the future for more refined contours (TBD)
+ * 
+ */
+psArray *pmSourceContour(
+    pmSource *source,   ///< The input pmSource
+    const psImage *image,  ///< The input image (float) (this arg should be removed)
+    float level,   ///< The level of the contour
+    pmContourType mode   ///< Currently this must be PS_CONTOUR_CRUDE
+);
+
+
+/** pmSourceFitModel()
+ * 
+ * Fit the requested model to the specified source. The starting guess for the
+ * model is given by the input source.model parameter values. The pixels of
+ * interest are specified by the source.pixelsand source.maskentries. This
+ * function calls psMinimizeLMChi2() on the image data. The function returns TRUE
+ * on success or FALSE on failure.
+ * 
+ */
+bool pmSourceFitModel(
+    pmSource *source,   ///< The input pmSource
+    pmModel *model,   ///< model to be fitted
+    const bool PSF   ///< Treat model as PSF or FLT?
+);
+
+
+/** pmModelFitStatus()
+ * 
+ * This function wraps the call to the model-specific function returned by
+ * pmModelFitStatusFunc_GetFunction.  The model-specific function examines the
+ * model parameters, parameter errors, Chisq, S/N, and other parameters available
+ * from model to decide if the particular fit was successful or not.
+ * 
+ * XXX: Must code this.
+ * 
+ */
+bool pmModelFitStatus(
+    pmModel *model                      ///< Add comment.
+);
+
+
+/** pmSourceAddModel()
+ * 
+ * Add the given source model flux to/from the provided image. The boolean
+ * option center selects if the source is re-centered to the image center or if
+ * it is placed at its centroid location. The boolean option sky selects if the
+ * background sky is applied (TRUE) or not. The pixel range in the target image
+ * is at most the pixel range specified by the source.pixels image. The success
+ * status is returned.
+ * 
+ */
+bool pmSourceAddModel(
+    psImage *image,   ///< The output image (float)
+    psImage *mask,   ///< The image pixel mask (valid == 0)
+    pmModel *model,   ///< The input pmModel
+    bool center    ///< A boolean flag that determines whether pixels are centered
+);
+
+
+/** pmSourceSubModel()
+ * 
+ * Subtract the given source model flux to/from the provided image. The
+ * boolean option center selects if the source is re-centered to the image center
+ * or if it is placed at its centroid location. The boolean option sky selects if
+ * the background sky is applied (TRUE) or not. The pixel range in the target
+ * image is at most the pixel range specified by the source.pixels image. The
+ * success status is returned.
+ * 
+ */
+bool pmSourceSubModel(
+    psImage *image,   ///< The output image (float)
+    psImage *mask,   ///< The image pixel mask (valid == 0)
+    pmModel *model,   ///< The input pmModel
+    bool center    ///< A boolean flag that determines whether pixels are centered
+);
+
+
+/**
+ * 
+ * The function returns both the magnitude of the fit, defined as -2.5log(flux),
+ * where the flux is integrated under the model, theoretically from a radius of 0
+ * to infinity. In practice, we integrate the model beyond 50sigma.  The aperture magnitude is
+ * defined as -2.5log(flux) , where the flux is summed for all pixels which are
+ * not excluded by the aperture mask. The model flux is calculated by calling the
+ * model-specific function provided by pmModelFlux_GetFunction.
+ * 
+ * XXX: must code this.
+ * 
+ */
+bool pmSourcePhotometry(
+    float *fitMag,                      ///< integrated fit magnitude
+    float *obsMag,   ///< aperture flux magnitude
+    pmModel *model,                     ///< model used for photometry
+    psImage *image,                     ///< image pixels to be used
+    psImage *mask                       ///< mask of pixels to ignore
+);
+
+
+/**
+ * 
+ * This function converts the source classification into the closest available
+ * approximation to the Dophot classification scheme:
+ * 
+ * PM_SOURCE_DEFECT: 8
+ * PM_SOURCE_SATURATED: 8
+ * PM_SOURCE_SATSTAR: 10
+ * PM_SOURCE_PSFSTAR: 1
+ * PM_SOURCE_GOODSTAR: 1
+ * PM_SOURCE_POOR_FIT_PSF: 7
+ * PM_SOURCE_FAIL_FIT_PSF: 4
+ * PM_SOURCE_FAINTSTAR: 4
+ * PM_SOURCE_GALAXY: 2
+ * PM_SOURCE_FAINT_GALAXY: 2
+ * PM_SOURCE_DROP_GALAXY: 2
+ * PM_SOURCE_FAIL_FIT_GAL: 2
+ * PM_SOURCE_POOR_FIT_GAL: 2
+ * PM_SOURCE_OTHER: ?
+ * 
+ */
+int pmSourceDophotType(
+    pmSource *source                    ///< Add comment.
+);
+
+
+/** pmSourceSextractType()
+ * 
+ * This function converts the source classification into the closest available
+ * approximation to the Sextractor classification scheme. the correspondence is
+ * not yet defined (TBD) .
+ * 
+ * XXX: Must code this.
+ * 
+ */
+int pmSourceSextractType(
+    pmSource *source                    ///< Add comment.
+);
+
+/** pmSourceFitModel_v5()
+ * 
+ * Fit the requested model to the specified source. The starting guess for the
+ * model is given by the input source.model parameter values. The pixels of
+ * interest are specified by the source.pixelsand source.maskentries. This
+ * function calls psMinimizeLMChi2() on the image data. The function returns TRUE
+ * on success or FALSE on failure.
+ * 
+ */
+bool pmSourceFitModel_v5(
+    pmSource *source,   ///< The input pmSource
+    pmModel *model,   ///< model to be fitted
+    const bool PSF   ///< Treat model as PSF or FLT?
+);
+
+
+/** pmSourceFitModel_v7()
+ * 
+ * Fit the requested model to the specified source. The starting guess for the
+ * model is given by the input source.model parameter values. The pixels of
+ * interest are specified by the source.pixelsand source.maskentries. This
+ * function calls psMinimizeLMChi2() on the image data. The function returns TRUE
+ * on success or FALSE on failure.
+ * 
+ */
+bool pmSourceFitModel_v7(
+    pmSource *source,   ///< The input pmSource
+    pmModel *model,   ///< model to be fitted
+    const bool PSF   ///< Treat model as PSF or FLT?
+);
+
+#endif
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/pmPSF.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/pmPSF.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/pmPSF.c	(revision 22115)
@@ -0,0 +1,191 @@
+/** @file  pmPSF.c
+ *
+ * This file contains typedefs for the Point-Spread Function and prototypes
+ * for functions that calculate the PSF.
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-10-10 19:53:40 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+/*****************************************************************************/
+/* INCLUDE FILES                                                             */
+/*****************************************************************************/
+
+#include <pslib.h>
+#include "psLibUtils.h"
+#include "pmObjects.h"
+#include "pmPSF.h"
+#include "pmModelGroup.h"
+
+/*****************************************************************************/
+/* DEFINE STATEMENTS                                                         */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* TYPE DEFINITIONS                                                          */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* GLOBAL VARIABLES                                                          */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* FILE STATIC VARIABLES                                                     */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - LOCAL                                           */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* FUNCTION IMPLEMENTATION - PUBLIC                                          */
+/*****************************************************************************/
+
+
+
+/*****************************************************************************
+pmPSFFree(psf): function to free a pmPSF structure
+ *****************************************************************************/
+static void pmPSFFree (pmPSF *psf)
+{
+
+    if (psf == NULL)
+        return;
+
+    psFree (psf->params);
+    return;
+}
+
+
+
+/*****************************************************************************
+pmPSFAlloc (type): allocate a pmPSF.
+    NOTE: a PSF always has 4 parameters fewer than the equivalent model.
+      This is because those 4 parameters are
+ X-center
+ Y-center
+ ???: Sky background value?
+ ???: Zo?
+ *****************************************************************************/
+pmPSF *pmPSFAlloc (pmModelType type)
+{
+    int Nparams;
+
+    pmPSF *psf = (pmPSF *) psAlloc(sizeof(pmPSF));
+
+    psf->type     = type;
+    psf->chisq    = 0.0;
+    psf->ApResid  = 0.0;
+    psf->dApResid = 0.0;
+    psf->skyBias  = 0.0;
+
+    Nparams = pmModelParameterCount (type);
+    if (!Nparams) {
+        psError(PS_ERR_UNKNOWN, true, "Undefined pmModelType");
+        return(NULL);
+    }
+
+    psf->params = psArrayAlloc(Nparams - 4);
+    for (int i = 0; i < psf->params->n; i++) {
+        // XXX EAM : make this a user-defined value?
+        // XXX EAM : future version (0.7.0?) psf->params->data[i] = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, 1, 1);
+        psf->params->data[i] = psPolynomial2DAlloc(PS_POLYNOMIAL_ORD, 1, 1);
+    }
+
+    psMemSetDeallocator(psf, (psFreeFunc) pmPSFFree);
+    return(psf);
+}
+
+
+
+/*****************************************************************************
+pmPSFFromModels (*psf, *models, *mask): build a PSF from a collection of
+free-fitted models.  The PSF ignores the first 4 (independent) model
+parameters and constructs a polynomial fit to the remaining as a function of
+image coordinate.
+    input: an array of pmModels, pre-allocated psf
+Note: some of the array entries may be NULL (failed fits); ignore them.
+ *****************************************************************************/
+bool pmPSFFromModels (pmPSF *psf, psArray *models, psVector *mask)
+{
+
+    // construct the fit vectors from the collection of objects
+    // use the mask to ignore missing fits
+    psVector *x  = psVectorAlloc (models->n, PS_TYPE_F64);
+    psVector *y  = psVectorAlloc (models->n, PS_TYPE_F64);
+    psVector *z  = psVectorAlloc (models->n, PS_TYPE_F64);
+    psVector *dz = psVectorAlloc (models->n, PS_TYPE_F64);
+
+    for (int i = 0; i < models->n; i++) {
+        pmModel *model = models->data[i];
+        if (model == NULL)
+            continue;
+
+        // XXX EAM : this is fragile: x and y must be stored consistently at 2,3
+        // note that the data is provided in the F64 array
+        x->data.F64[i] = model->params->data.F32[2];
+        y->data.F64[i] = model->params->data.F32[3];
+    }
+
+    // we are doing a robust fit.  after each pass, we drop points which are
+    // more deviant than three sigma.
+    // mask is currently updated for each pass. this is inconsistent?
+
+    psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
+
+    for (int i = 0; i < psf->params->n; i++) {
+        for (int j = 0; j < models->n; j++) {
+            pmModel *model = models->data[j];
+            if (model == NULL)
+                continue;
+            z->data.F64[j] = model->params->data.F32[i + 4];
+            dz->data.F64[j] = 1;
+            // XXX EAM : need to use actual errors?
+            // XXX EAM : this is fragile: psf(Nparams) = model(Nparams) - 4
+        }
+
+        // XXX EAM : this is the expected API (cycle 7? cycle 8?)
+        psf->params->data[i] = psVectorClipFitPolynomial2D(psf->params->data[i], stats, mask, 0xff, z, dz, x, y);
+
+        // XXX EAM : drop this when above is implemented...
+        // psf->params->data[i] = RobustFit2D (psf->params->data[i], mask, x, y, z, dz);
+
+        // psTrace ("psphot.psftest", 3, "keeping %d of %d PSF candidates (PSF param %d)\n", Nkeep, mask->n, i);
+        // psPolynomial2DDump (psf->params->data[i]);
+    }
+
+    psFree (stats);
+    psFree (x);
+    psFree (y);
+    psFree (z);
+    psFree (dz);
+    return (true);
+}
+
+
+
+/*****************************************************************************
+pmModelFromPSF (*modelFLT, *psf):  use the model position parameters to
+construct a realization of the PSF model at the object coordinates
+ *****************************************************************************/
+pmModel *pmModelFromPSF (pmModel *modelFLT, pmPSF *psf)
+{
+
+    // need to define the relationship between the modelFLT and modelPSF ?
+
+    // find function used to set the model parameters
+    pmModelFromPSFFunc modelFromPSFFunc = pmModelFromPSFFunc_GetFunction (psf->type);
+
+    // allocate a new pmModel to hold the PSF version
+    pmModel *modelPSF = pmModelAlloc (psf->type);
+
+    // set model parameters for this source based on PSF information
+    modelFromPSFFunc (modelPSF, modelFLT, psf);
+
+    return (modelPSF);
+}
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/pmPSF.h
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/pmPSF.h	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/pmPSF.h	(revision 22115)
@@ -0,0 +1,87 @@
+/** @file  pmPSF.h
+ *
+ * This file contains typedefs for the Point-Spread Function and prototypes
+ * for functions that calculate the PSF.
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-10-10 19:53:40 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+# ifndef PM_PSF_H
+# define PM_PSF_H
+
+
+/** pmPSF data structure
+ * 
+ * It is useful to generate a model to define the point-spread-function which
+ * describes the flux distribution for unresolved sources in an image. In
+ * general, the PSF varies with position in the image. We allow any of the source
+ * models defined for the pmModel to represent the PSF. For a given source model,
+ * the 2D spatial variation of all of the source parameters, except the first
+ * four PSF-independent parameters, are represented as polynomial, stored in a
+ * psArray. The other elements of the structure define the quality of the PSF
+ * determination.
+ * 
+ */
+typedef struct
+{
+    pmModelType type;   ///< PSF Model in use
+    psArray *params;   ///< Model parameters (psPolynomial2D)
+    float chisq;   ///< PSF goodness statistic
+    float ApResid;                      ///< ???
+    float dApResid;                     ///< ???
+    float skyBias;                      ///< ???
+    int nPSFstars;   ///< number of stars used to measure PSF
+}
+pmPSF;
+
+
+/**
+ * 
+ * Allocator for the pmPSF structure.
+ * 
+ */
+pmPSF *pmPSFAlloc(
+    pmModelType type                    ///< Add comment
+);
+
+
+/**
+ * 
+ * This function takes a collection of pmModel fitted models from across a
+ * single image and builds a pmPSF representation of the PSF. The input array of
+ * model fits may consist of entries to be ignored (noted by a non-zero mask
+ * entry). The analysis of the models fits a 2D polynomial for each parameter to
+ * the collection of model parameters as a function of position (and
+ * normalization?). In this process, some of the input models may be marked as
+ * outliers and excluded from the fit. These elements will be marked with a
+ * specific mask value (1 == PSFTRY_MASK_OUTLIER).
+ * 
+ */
+bool pmPSFFromModels(
+    pmPSF *psf,                         ///< Add comment
+    psArray *models,                    ///< Add comment
+    psVector *mask                      ///< Add comment
+);
+
+
+/**
+ * 
+ * This function constructs a pmModel instance based on the pmPSF description
+ * of the PSF. The input is a pmModel with at least the values of the centroid
+ * coordinates (possibly normalization if this is needed) defined. The values of
+ * the PSF-dependent parameters are specified for the specific realization based
+ * on the coordinates of the object.
+ * 
+ */
+pmModel *pmModelFromPSF(
+    pmModel *model,                     ///< Add comment
+    pmPSF *psf                          ///< Add comment
+);
+
+# endif
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/pmPSFtry.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/pmPSFtry.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/pmPSFtry.c	(revision 22115)
@@ -0,0 +1,338 @@
+# include <pslib.h>
+# include "psLibUtils.h"
+# include "pmObjects.h"
+# include "psModulesUtils.h"
+# include "pmPSF.h"
+# include "pmPSFtry.h"
+# include "pmModelGroup.h"
+
+// ********  pmPSFtry functions  **************************************************
+// * pmPSFtry holds a single pmPSF model test, with the input sources, the freely
+// * fitted version of the model, the pmPSF fit to the fitted model parameters,
+// * and the PSF fits to the source. It also includes the statistics from the
+// * fits, both the individual sources, and the collection
+
+// free a pmPSFtry structure
+static void pmPSFtryFree (pmPSFtry *test)
+{
+
+    if (test == NULL)
+        return;
+
+    psFree (test->psf);
+    psFree (test->sources);
+    psFree (test->modelFLT);
+    psFree (test->modelPSF);
+    psFree (test->metric);
+    psFree (test->fitMag);
+    psFree (test->mask);
+    return;
+}
+
+// allocate a pmPSFtry based on the desired sources and the model (identified by name)
+pmPSFtry *pmPSFtryAlloc (psArray *sources, char *modelName)
+{
+
+    pmModelType type;
+
+    pmPSFtry *test = (pmPSFtry *) psAlloc(sizeof(pmPSFtry));
+
+    // XXX probably need to increment ref counter
+    type           = pmModelSetType (modelName);
+    test->psf      = pmPSFAlloc (type);
+    test->sources  = psMemCopy(sources);
+    test->modelFLT = psArrayAlloc (sources->n);
+    test->modelPSF = psArrayAlloc (sources->n);
+    test->metric   = psVectorAlloc (sources->n, PS_TYPE_F64);
+    test->fitMag   = psVectorAlloc (sources->n, PS_TYPE_F64);
+    test->mask     = psVectorAlloc (sources->n, PS_TYPE_U8);
+
+    for (int i = 0; i < test->modelFLT->n; i++) {
+        test->mask->data.U8[i]  = 0;
+        test->modelFLT->data[i] = NULL;
+        test->modelPSF->data[i] = NULL;
+        test->metric->data.F64[i] = 0;
+        test->fitMag->data.F64[i] = 0;
+    }
+
+    psMemSetDeallocator(test, (psFreeFunc) pmPSFtryFree);
+    return (test);
+}
+
+// build a pmPSFtry for the given model:
+// - fit each source with the free-floating model
+// - construct the pmPSF from the collection of models
+// - fit each source with the PSF-parameter models
+// - measure the pmPSF quality metric (dApResid)
+
+// sources used in for pmPSFtry may be masked by the analysis
+// mask values indicate the reason the source was rejected:
+
+pmPSFtry *pmPSFtryModel (psArray *sources, char *modelName, float RADIUS)
+{
+    bool status;
+    float obsMag;
+    float fitMag;
+    float x;
+    float y;
+    int Nflt = 0;
+    int Npsf = 0;
+
+    pmPSFtry *try
+    = pmPSFtryAlloc (sources, modelName);
+
+    // stage 1:  fit an independent model (freeModel) to all sources
+    psTimerStart ("fit");
+    for (int i = 0; i < try
+                ->sources->n; i++) {
+
+            pmSource *source = try
+                                   ->sources->data[i];
+            pmModel  *model  = pmSourceModelGuess (source, try
+                                                       ->psf->type);
+            x = source->peak->x;
+            y = source->peak->y;
+
+            // set temporary object mask and fit object
+            // fit model as FLT, not PSF
+            psImageKeepCircle (source->mask, x, y, RADIUS, "OR", PSPHOT_MASK_MARKED);
+            status = pmSourceFitModel (source, model, false);
+            psImageKeepCircle (source->mask, x, y, RADIUS, "AND", ~PSPHOT_MASK_MARKED);
+
+            // exclude the poor fits
+            if (!status) {
+                try
+                    ->mask->data.U8[i] = PSFTRY_MASK_FLT_FAIL;
+                psFree (model);
+                continue;
+            }
+            try
+                ->modelFLT->data[i] = model;
+            Nflt ++;
+        }
+    psLogMsg ("psphot.psftry", 4, "fit flt:   %f sec for %d sources\n", psTimerMark ("fit"), sources->n);
+    psTrace ("psphot.psftry", 3, "keeping %d of %d PSF candidates (FLT)\n", Nflt, sources->n);
+
+    // make this optional?
+    // DumpModelFits (try->modelFLT, "modelsFLT.dat");
+
+    // stage 2: construct a psf (pmPSF) from this collection of model fits
+    pmPSFFromModels (try
+                     ->psf, try
+                         ->modelFLT, try
+                             ->mask);
+
+    // stage 3: refit with fixed shape parameters
+    psTimerStart ("fit");
+    for (int i = 0; i < try
+                ->sources->n; i++) {
+            // masked for: bad model fit, outlier in parameters
+            if (try
+                    ->mask->data.U8[i] & PSFTRY_MASK_ALL) continue;
+
+            pmSource *source = try
+                                   ->sources->data[i];
+            pmModel  *modelFLT = try
+                                     ->modelFLT->data[i];
+
+            // set shape for this model based on PSF
+            pmModel *modelPSF = pmModelFromPSF (modelFLT, try
+                                                    ->psf);
+            x = source->peak->x;
+            y = source->peak->y;
+
+            psImageKeepCircle (source->mask, x, y, RADIUS, "OR", PSPHOT_MASK_MARKED);
+            status = pmSourceFitModel (source, modelPSF, true);
+
+            // skip poor fits
+            if (!status) {
+                try
+                    ->mask->data.U8[i] = PSFTRY_MASK_PSF_FAIL;
+                psFree (modelPSF);
+                goto next_source;
+            }
+
+            // otherwise, save the resulting model
+            try
+                ->modelPSF->data[i] = modelPSF;
+
+            // XXX : use a different aperture radius from the fit radius?
+            // XXX : use a different estimator for the local sky?
+            // XXX : pass 'source' as input?
+            if (!pmSourcePhotometry (&fitMag, &obsMag, modelPSF, source->pixels, source->mask)) {
+                try
+                    ->mask->data.U8[i] = PSFTRY_MASK_BAD_PHOT;
+                goto next_source;
+            }
+
+            try
+                ->metric->data.F64[i] = obsMag - fitMag;
+            try
+                ->fitMag->data.F64[i] = fitMag;
+            Npsf ++;
+
+next_source:
+            psImageKeepCircle (source->mask, x, y, RADIUS, "AND", ~PSPHOT_MASK_MARKED);
+
+        }
+    psLogMsg ("psphot.psftry", 4, "fit psf:   %f sec for %d sources\n", psTimerMark ("fit"), sources->n);
+    psTrace ("psphot.psftry", 3, "keeping %d of %d PSF candidates (PSF)\n", Npsf, sources->n);
+
+    // make this optional
+    // DumpModelFits (try->modelPSF, "modelsPSF.dat");
+
+    // XXX this function wants aperture radius for pmSourcePhotometry
+    pmPSFtryMetric (try
+                    , RADIUS);
+    psLogMsg ("psphot.pspsf", 3, "try model %s, ap-fit: %f +/- %f, sky bias: %f\n",
+              modelName, try
+                  ->psf->ApResid, try
+                      ->psf->dApResid, try
+                          ->psf->skyBias);
+
+    return (try
+           );
+}
+
+
+bool pmPSFtryMetric (pmPSFtry *try
+                     , float RADIUS)
+{
+
+    float dBin;
+    int   nKeep, nSkip;
+
+    // the measured (aperture - fit) magnitudes (dA == try->metric)
+    //   depend on both the true ap-fit (dAo) and the bias in the sky measurement:
+    //     dA = dAo + dsky/flux
+    //   where flux is the flux of the star
+    // we fit this trend to find the infinite flux aperture correction (dAo),
+    //   the nominal sky bias (dsky), and the error on dAo
+    // the values of dA are contaminated by stars with close neighbors in the aperture
+    //   we use an outlier rejection to avoid this bias
+
+    // rflux = ten(0.4*fitMag);
+    psVector *rflux = psVectorAlloc (try
+                                     ->sources->n, PS_TYPE_F64);
+    for (int i = 0; i < try
+                ->sources->n; i++) {
+            if (try
+                    ->mask->data.U8[i] & PSFTRY_MASK_ALL) continue;
+            rflux->data.F64[i] = pow(10.0, 0.4*try
+                                     ->fitMag->data.F64[i]);
+        }
+
+    // find min and max of (1/flux):
+    psStats *stats = psStatsAlloc (PS_STAT_MIN | PS_STAT_MAX);
+    psVectorStats (stats, rflux, NULL, try
+                       ->mask, PSFTRY_MASK_ALL);
+
+    // build binned versions of rflux, metric
+    dBin = (stats->max - stats->min) / 10.0;
+    psVector *rfBin = psVectorCreate(NULL, stats->min, stats->max, dBin, PS_TYPE_F64);
+    psVector *daBin = psVectorAlloc (rfBin->n, PS_TYPE_F64);
+    psVector *maskB = psVectorAlloc (rfBin->n, PS_TYPE_U8);
+    psFree (stats);
+
+    psTrace ("psphot.metricmodel", 3, "rflux max: %g, min: %g, delta: %g\n", stats->max, stats->min, dBin);
+
+    // group data in daBin bins, measure lower 50% mean
+    for (int i = 0; i < daBin->n; i++) {
+
+        psVector *tmp = psVectorAlloc (try
+                                       ->sources->n, PS_TYPE_F64);
+        tmp->n = 0;
+
+        // accumulate data within bin range
+        for (int j = 0; j < try
+                    ->sources->n; j++) {
+                // masked for: bad model fit, outlier in parameters
+                if (try
+                        ->mask->data.U8[j] & PSFTRY_MASK_ALL) continue;
+
+                // skip points with extreme dA values
+                if (fabs(try
+                         ->metric->data.F64[j]) > 0.5) continue;
+
+                // skip points outside of this bin
+                if (rflux->data.F64[j] < rfBin->data.F64[i] - 0.5*dBin)
+                    continue;
+                if (rflux->data.F64[j] > rfBin->data.F64[i] + 0.5*dBin)
+                    continue;
+
+                tmp->data.F64[tmp->n] = try
+                                            ->metric->data.F64[j];
+                tmp->n ++;
+            }
+
+        // is this a valid point?
+        maskB->data.U8[i] = 0;
+        if (tmp->n < 2) {
+            maskB->data.U8[i] = 1;
+            psFree (tmp);
+            continue;
+        }
+
+        // dA values are contaminated with low outliers
+        // measure statistics only on upper 50% of points
+        // this would be easier if we could sort in reverse:
+        //
+        // psVectorSort (tmp, tmp);
+        // tmp->n = 0.5*tmp->n;
+        // stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+        // psVectorStats (stats, tmp, NULL, NULL, 0);
+        // psTrace ("psphot.metricmodel", 4, "rfBin %d (%g): %d pts, %g\n", i, rfBin->data.F64[i], tmp->n, stats->sampleMedian);
+
+        psVectorSort (tmp, tmp);
+        nKeep = 0.5*tmp->n;
+        nSkip = tmp->n - nKeep;
+
+        psVector *tmp2 = psVectorAlloc (nKeep, PS_TYPE_F64);
+        for (int j = 0; j < tmp2->n; j++) {
+            tmp2->data.F64[j] = tmp->data.F64[j + nSkip];
+        }
+
+        stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+        psVectorStats (stats, tmp2, NULL, NULL, 0);
+        psTrace ("psphot.metricmodel", 4, "rfBin %d (%g): %d pts, %g\n", i, rfBin->data.F64[i], tmp->n, stats->sampleMedian);
+
+        daBin->data.F64[i] = stats->sampleMedian;
+
+        psFree (stats);
+        psFree (tmp);
+        psFree (tmp2);
+    }
+
+    // linear fit to rfBin, daBin
+    psPolynomial1D *poly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+
+    // XXX EAM : this is the intended API (cycle 7? cycle 8?)
+    poly = psVectorFitPolynomial1D(poly, maskB, 1, daBin, NULL, rfBin);
+
+    // XXX EAM : replace this when the above version is implemented
+    // poly = psVectorFitPolynomial1DOrd(poly, maskB, rfBin, daBin, NULL);
+
+    psVector *daBinFit = psPolynomial1DEvalVector (poly, rfBin);
+    psVector *daResid  = (psVector *) psBinaryOp (NULL, (void *) daBin, "-", (void *) daBinFit);
+
+    stats = psStatsAlloc (PS_STAT_CLIPPED_STDEV);
+    stats = psVectorStats (stats, daResid, NULL, maskB, 1);
+
+    try
+        ->psf->ApResid = poly->coeff[0];
+    try
+        ->psf->dApResid = stats->clippedStdev;
+    try
+        ->psf->skyBias = poly->coeff[1] / (M_PI * PS_SQR(RADIUS));
+
+    psFree (rflux);
+    psFree (rfBin);
+    psFree (daBin);
+    psFree (maskB);
+    psFree (daBinFit);
+    psFree (daResid);
+    psFree (poly);
+    psFree (stats);
+
+    return true;
+}
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/pmPSFtry.h
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/pmPSFtry.h	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/pmPSFtry.h	(revision 22115)
@@ -0,0 +1,112 @@
+/** @file  pmPSFtry.h
+ *
+ * This file contains code that allows the user to try to fit several
+ * PSF models to an image.
+ *
+ *  @author EAM, IfA
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-10-10 19:53:40 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ */
+
+# ifndef PM_PSF_TRY_H
+# define PM_PSF_TRY_H
+
+
+/**
+ * 
+ * This structure contains a pointer to the collection of sources which will
+ * be used to test the PSF model form. It lists the pmModelType type of model
+ * being tests, and contains an element to store the resulting psf
+ * representation. In addition, this structure carries the complete collection of
+ * FLT (floating parameter) and PSF (fixed parameter) model fits to each of the
+ * sources modelFLT and modelPSF. It also contains a mask which is set by the
+ * model fitting and psf fitting steps. For each model, the value of the quality
+ * metric is stored in the vector metric and the fitted instrumental magnitude is
+ * stored in fitMag. The quality metric for the PSF model is the aperture
+ * magnitude minus the fitted magnitude for each source. This collection of
+ * aperture residuals is examined in the analysis process, and a linear trend of
+ * the residual with the inverse object flux (ie, 100:4his structure contains a
+ * pointer to the collection of sources which will be used to test the PSF model
+ * form. It lists the pmModelType type of modmag) is fitted. The result of this
+ * fit is a measured sky bias (systematic error in the sky measured by the fits),
+ * an effective infinite-magnitude aperture correction (ApResid), and the scatter
+ * of the aperture correction for the ensemble of PSF stars (dApResid). The
+ * ultimate metric to intercompare multiple types of PSF models is the value of
+ * the aperture correction scatter.
+ * 
+ * XXX: There are many more members in the SDRS then in the prototype code.
+ * I stuck with the prototype code.
+ * 
+ * 
+ */
+typedef struct
+{
+    pmPSF      *psf;                    ///< Add comment.
+    psArray    *sources;                ///< pointers to the original sources
+    psArray    *modelFLT;               ///< model fits, floating parameters
+    psArray    *modelPSF;               ///< model fits, PSF parameters
+    psVector   *mask;                   ///< Add comment.
+    psVector   *metric;                 ///< Add comment.
+    psVector   *fitMag;                 ///< Add comment.
+}
+pmPSFtry;
+
+
+/** pmPSFtryMaskValues
+ * 
+ * The following datatype defines the masks used by the pmPSFtry analysis to
+ * identify sources which should or should not be included in the analysis.
+ * 
+ */
+enum {
+    PSFTRY_MASK_CLEAR    = 0x00,        ///< Add comment.
+    PSFTRY_MASK_OUTLIER  = 0x01,        ///< 1: outlier in psf polynomial fit (provided by psPolynomials)
+    PSFTRY_MASK_FLT_FAIL = 0x02,        ///< 2: flt model failed to converge
+    PSFTRY_MASK_PSF_FAIL = 0x04,        ///< 3: psf model failed to converge
+    PSFTRY_MASK_BAD_PHOT = 0x08,        ///< 4: invalid source photometry
+    PSFTRY_MASK_ALL      = 0x0f,        ///< Add comment.
+} pmPSFtryMaskValues;
+
+
+/** pmPSFtryAlloc()
+ * 
+ * Allocate a pmPSFtry data structure.
+ * 
+ */
+pmPSFtry *pmPSFtryAlloc(
+    psArray *stars,                     ///< Add comment.
+    char *modelName                     ///< Add comment.
+);
+
+
+/** pmPSFtryModel()
+ * 
+ * This function takes the input collection of sources and performs a complete
+ * analysis to determine a PSF model of the given type (specified by model name).
+ * The result is a pmPSFtry with the results of the analysis.
+ * 
+ */
+pmPSFtry *pmPSFtryModel(
+    psArray *sources,                   ///< Add comment.
+    char *modelName,                    ///< Add comment.
+    float radius                        ///< Add comment.
+);
+
+
+/** pmPSFtryMetric()
+ * 
+ * This function is used to measure the PSF model metric for the set of
+ * results contained in the pmPSFtry structure.
+ * 
+ */
+bool pmPSFtryMetric(
+    pmPSFtry *try
+    ,                      ///< Add comment.
+    float RADIUS                        ///< Add comment.
+);
+
+# endif
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/psEllipse.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/psEllipse.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/psEllipse.c	(revision 22115)
@@ -0,0 +1,62 @@
+# include "pslib.h"
+# include "psEllipse.h"
+
+EllipseAxes EllipseMomentsToAxes (EllipseMoments moments)
+{
+
+    EllipseAxes axes;
+
+    double f = sqrt (0.25*PS_SQR(moments.x2 - moments.y2) + PS_SQR(moments.xy));
+    if (f > (moments.x2 + moments.y2) / 2.0) {
+        f = 0.98*(moments.x2 + moments.y2) / 2.0;
+    }
+
+    axes.major = sqrt (0.5*(moments.x2 + moments.y2) + f);
+    axes.minor = sqrt (0.5*(moments.x2 + moments.y2) - f);
+    axes.theta = atan2 (2*moments.xy, moments.x2 - moments.y2) / 2;
+    // theta in radians
+
+    return (axes);
+}
+
+EllipseShape EllipseAxesToShape (EllipseAxes axes)
+{
+
+    EllipseShape shape;
+
+    double r1 = 1.0 / PS_SQR(axes.major) + 1.0 / PS_SQR(axes.minor);
+    double r2 = 1.0 / PS_SQR(axes.major) - 1.0 / PS_SQR(axes.minor);
+
+    double sxr = r1 + r2*cos(2*axes.theta);
+    double syr = r1 - r2*cos(2*axes.theta);
+
+    shape.sx = 1.0 / sqrt(sxr);
+    shape.sy = 1.0 / sqrt(syr);
+    shape.sxy = r2*sin(2*axes.theta);
+
+    return (shape);
+}
+
+EllipseAxes EllipseShapeToAxes (EllipseShape shape)
+{
+
+    EllipseAxes axes;
+
+    double f1 = 1.0 / PS_SQR(shape.sx) + 1.0 / PS_SQR(shape.sy);
+    double f2 = 1.0 / PS_SQR(shape.sx) - 1.0 / PS_SQR(shape.sy);
+
+    // force the axis ratio to be less than 10
+    double r1 = 0.5*0.95*sqrt (PS_SQR(f1) - PS_SQR(f2));
+
+    shape.sxy = PS_MIN(PS_MAX(shape.sxy, -r1), r1);
+
+    axes.theta = atan2 (-2.0*shape.sxy, f2) / 2.0;
+
+    double Ar = 0.25*f1 + 0.25*sqrt(PS_SQR(f2) + 4*PS_SQR(shape.sxy));
+    double Br = 0.25*f1 - 0.25*sqrt(PS_SQR(f2) + 4*PS_SQR(shape.sxy));
+
+    axes.minor = 1.0 / sqrt (Ar);
+    axes.major = 1.0 / sqrt (Br);
+
+    return (axes);
+}
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/psEllipse.h
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/psEllipse.h	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/psEllipse.h	(revision 22115)
@@ -0,0 +1,30 @@
+// strucures to define elliptical shape parameters
+typedef struct
+{
+    double major;
+    double minor;
+    double theta;
+}
+EllipseAxes;
+
+typedef struct
+{
+    double x2;
+    double y2;
+    double xy;
+}
+EllipseMoments;
+
+typedef struct
+{
+    double sx;
+    double sy;
+    double sxy;
+}
+EllipseShape;
+
+// conversions between elliptical shape representations
+EllipseAxes EllipseMomentsToAxes (EllipseMoments moments);
+EllipseShape EllipseAxesToShape (EllipseAxes axes);
+EllipseAxes EllipseShapeToAxes (EllipseShape shape);
+
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/psLibUtils.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/psLibUtils.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/psLibUtils.c	(revision 22115)
@@ -0,0 +1,509 @@
+# include <strings.h>  // for strncasecmp
+# include <pslib.h>
+# include "psLibUtils.h"
+
+// XXX EAM : this is NOT included in the C99 headers ??
+FILE *fdopen(int fildes, const char *mode);
+
+// XXX EAM : these utility functions should be added back into PSLib
+
+static psHash *timers = NULL;
+
+/* XXX: remove
+bool psTimerClear (char *name) {
+ 
+  bool status;
+ 
+  if (name == NULL) return false;
+ 
+  status = psHashRemove (timers, name);
+  return (status);
+}
+*/
+
+void psTimerFree ()
+{
+
+    psFree (timers);
+    p_psTimeFinalize();
+    return;
+}
+
+// start/restart a named timer
+bool psTimerStart (char *name)
+{
+
+    psTime *start;
+
+    if (timers == NULL)
+        timers = psHashAlloc (16);
+
+    start = psTimeGetNow (PS_TIME_UTC);
+    psHashAdd (timers, name, start);
+    psFree (start);
+    return (TRUE);
+}
+
+// get current elapsed time on named timer
+psF64 psTimerMark (char *name)
+{
+
+    psTime *start;
+    psTime *mark;
+    psF64   delta;
+
+    if (timers == NULL)
+        return (0);
+
+    start = psHashLookup (timers, name);
+    if (start == NULL)
+        return (0);
+
+    mark = psTimeGetNow (PS_TIME_UTC);
+    delta = psTimeDelta (mark, start);
+    psFree (mark);
+    // psFree (start); -- XXX is psHashLookup not psMemCopying?
+
+    return (delta);
+}
+
+/* XXX: remove
+// find the location of the specified argument
+int psArgumentGet (int argc, char **argv, char *arg) {
+ 
+    int i;
+ 
+    for (i = 0; i < argc; i++) {
+ if (!strcmp(argv[i], arg))
+     return (i);
+    }
+  
+    return ((int)NULL);
+}
+*/
+
+/* XXX: remove
+// remove the specified argument (by location)
+int psArgumentRemove (int N, int *argc, char **argv) {
+ 
+    int i;
+ 
+    if ((N != (int)NULL) && (N != 0)) {
+ (*argc)--;
+ for (i = N; i < *argc; i++) {
+     argv[i] = argv[i+1];
+ }
+    }
+ 
+    return (N);
+}
+*/
+
+// we have log levels 1 (Error), 2 (Warning), 3 (Info), 4 (Details), 5 (Minutiae)
+// 2 = default, -v = 3, -vv = 4, -vvv = 5
+psS32 psLogArguments (int *argc, char **argv)
+{
+
+    int N, level;
+
+    // default log level is 2
+    level = 2;
+
+    // set in order, so that -vvv overrides -vv overrides -v
+    if ((N = psArgumentGet (*argc, argv, "-v"))) {
+        psArgumentRemove (N, argc, argv);
+        level = 3;
+    }
+    if ((N = psArgumentGet (*argc, argv, "-vv"))) {
+        psArgumentRemove (N, argc, argv);
+        level = 4;
+    }
+    if ((N = psArgumentGet (*argc, argv, "-vvv"))) {
+        psArgumentRemove (N, argc, argv);
+        level = 5;
+    }
+
+    if ((N = psArgumentGet (*argc, argv, "-logfmt"))) {
+        if (*argc < N + 2) {
+            psAbort ("psLogArguments", "USAGE: -logfmt (format)");
+        }
+        psArgumentRemove (N, argc, argv);
+        psLogSetFormat (argv[N]); // XXX EAM : this function should return an error if the log format is invalid
+        psArgumentRemove (N, argc, argv);
+    }
+
+    // set the level, return the level
+    psLogSetLevel (level);
+    return (level);
+}
+
+// set trace levels by facility
+psS32 psTraceArguments (int *argc, char **argv)
+{
+
+    int N;
+
+    // argument format is: -trace (facil) (level)
+    while ((N = psArgumentGet (*argc, argv, "-trace"))) {
+        if (*argc < N + 3) {
+            psAbort ("psTraceArguments", "USAGE: -trace (facility) (level)");
+        }
+        psArgumentRemove (N, argc, argv);
+        psTraceSetLevel (argv[N], atoi(argv[N+1]));
+        psArgumentRemove (N, argc, argv);
+        psArgumentRemove (N, argc, argv);
+    }
+    if ((N = psArgumentGet (*argc, argv, "-trace-levels"))) {
+        psTracePrintLevels ();
+        exit (2);
+    }
+    return (TRUE);
+}
+
+# if (0)
+    // alternate implementation of this function from pmObjects.c
+    // now defined in psLib SDRS as psImageRow
+    psVector *psGetRowVectorFromImage(psImage *image,
+                                      psU32 row)
+{
+    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
+    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
+
+    psVector *tmpVector = psVectorAlloc(image->numCols, PS_TYPE_F32);
+    memcpy (tmpVector->data.F32, image->data.F32[row], image->numCols*sizeof(psF32));
+    return(tmpVector);
+}
+# endif
+
+// XXX EAM : this is now in psLib
+void psImageSmooth_EAM (psImage *image, float sigma, float Nsigma)
+{
+
+    int Nx, Ny, Npixel, Nrange;
+    float factor, g, s;
+    psVector *temp;
+
+    // relevant terms
+    Nrange = sigma*Nsigma + 0.5;
+    Npixel = 2*Nrange + 1;
+    factor = -0.5/(sigma*sigma);
+
+    Nx = image->numCols;
+    Ny = image->numRows;
+
+    // generate gaussian
+    psVector *gaussnorm = psVectorAlloc (Npixel, PS_TYPE_F32);
+    for (int i = -Nrange; i < Nrange + 1; i++) {
+        gaussnorm->data.F32[i+Nrange] = exp (factor*i*i);
+    }
+    psF32 *gauss = &gaussnorm->data.F32[Nrange];
+
+    // smooth in X direction
+    temp = psVectorAlloc (Nx, PS_TYPE_F32);
+    for (int j = 0; j < Ny; j++) {
+        psF32 *vi = image->data.F32[j];
+        psF32 *vo = temp->data.F32;
+        for (int i = 0; i < Nx; i++) {
+            g = s = 0;
+            for (int n = -Nrange; n < Nrange + 1; n++) {
+                if (i+n < 0)
+                    continue;
+                if (i+n >= Nx)
+                    continue;
+                s += gauss[n]*vi[i+n];
+                g += gauss[n];
+            }
+            vo[i] = s / g;
+        }
+        memcpy (image->data.F32[j], temp->data.F32, Nx*sizeof(psF32));
+    }
+    psFree (temp);
+
+    // smooth in Y direction
+    temp = psVectorAlloc (image->numRows, PS_TYPE_F32);
+    for (int i = 0; i < Nx; i++) {
+        psF32  *vo = temp->data.F32;
+        psF32 **vi = image->data.F32;
+        for (int j = 0; j < Ny; j++) {
+            g = s = 0;
+            for (int n = -Nrange; n < Nrange + 1; n++) {
+                if (j+n < 0)
+                    continue;
+                if (j+n >= Ny)
+                    continue;
+                s += gauss[n]*vi[j+n][i];
+                g += gauss[n];
+            }
+            vo[j] = s / g;
+        }
+        // replace temp in image
+        for (int j = 0; j < Ny; j++) {
+            vi[j][i] = vo[j];
+        }
+    }
+    psFree (temp);
+    psFree (gaussnorm);
+}
+
+bool psImageInit (psImage *image,...)
+{
+
+    va_list argp;
+    psU8  vU8;
+    psF32 vF32;
+    psF64 vF64;
+
+    if (image == NULL)
+        return (false);
+
+    va_start (argp, image);
+
+    switch (image->type.type) {
+    case PS_TYPE_U8:
+        vU8 = va_arg (argp, psU32);
+
+        for (int iy = 0; iy < image->numRows; iy++) {
+            for (int ix = 0; ix < image->numCols; ix++) {
+                image->data.U8[iy][ix] = vU8;
+            }
+        }
+        break;
+
+    case PS_TYPE_F32:
+        vF32 = va_arg (argp, psF64);
+
+        for (int iy = 0; iy < image->numRows; iy++) {
+            for (int ix = 0; ix < image->numCols; ix++) {
+                image->data.F32[iy][ix] = vF32;
+            }
+        }
+        return (true);
+
+    case PS_TYPE_F64:
+        vF64 = va_arg (argp, psF64);
+
+        for (int iy = 0; iy < image->numRows; iy++) {
+            for (int ix = 0; ix < image->numCols; ix++) {
+                image->data.F64[iy][ix] = vF64;
+            }
+        }
+        return (true);
+
+    default:
+        psAbort ("psphot.psUtils", "datatype %d not defined in psImageInit\n", image->type);
+        return (false);
+    }
+    return (false);
+}
+
+/* XXX: remove
+// count number of pixels with given mask value
+int psImageCountPixelMask (psImage *mask, psU8 value) 
+{
+    int Npixels = 0;
+  
+    for (int i = 0; i < mask->numRows; i++) {
+ for (int j = 0; j < mask->numCols; j++) {
+     if (mask->data.U8[i][j] & value) {
+  Npixels ++;
+     }
+ }
+    }
+    return (Npixels);
+}
+*/
+
+// define a square region centered on the given coordinate
+// XXX EAM : this is now in psLib
+# if (0)
+    psRegion *psRegionSquare (psF32 x, psF32 y, psF32 radius)
+{
+    psRegion *region;
+    region = psRegionAlloc (x - radius, x + radius + 1,
+                            y - radius, y + radius + 1);
+    return (region);
+}
+# endif
+
+// set actual region based on image parameters:
+// compensate for negative upper limits
+// XXX this is inconsistent: the coordindates should always be in the parent
+//     frame, which means the negative values should subtract from Nx,Ny of
+//     the parent, not the child.  but, we don't carry the dimensions of the
+//     parent in the psImage container.  for now, use the child Nx,Ny
+//     force range to be on this subimage
+// XXX EAM : this needs to be changed to use psRegion rather than psRegion*
+// XXX EAM : this is now in psLib
+# if (0)
+    psRegion *psRegionForImage (psRegion *out, psImage *image, psRegion *in)
+{
+
+    // x0,y0, x1,y1 are in *parent* units
+
+    if (out == NULL) {
+        out = psRegionAlloc(in->x0, in->x1, in->y0, in->y1);
+    } else {
+        *out = *in;
+    }
+    // XXX these are probably wrong (see above)
+    if (out->x1 <= 0) {
+        out->x1 = image->col0 + image->numCols + out->x1;
+    }
+    if (out->y1 <= 0) {
+        out->y1 = image->row0 + image->numRows + out->y1;
+    }
+
+    // force the lower-limits to be on the child
+    out->x0 = PS_MAX(image->col0, out->x0);
+    out->y0 = PS_MAX(image->row0, out->y0);
+
+    // force the upper-limits to be on the child
+    out->x1 = PS_MIN(image->col0 + image->numCols, out->x1);
+    out->y1 = PS_MIN(image->row0 + image->numRows, out->y1);
+    return (out);
+}
+# endif
+
+// mask the area contained by the region
+// the region is defined wrt the parent image
+// XXX EAM : this is now in psLib
+# if (0)
+    void psImageMaskRegion (psImage *image, psRegion *region, bool logical_and, int maskValue)
+{
+
+    for (int iy = 0; iy < image->numRows; iy++) {
+        for (int ix = 0; ix < image->numCols; ix++) {
+            if (ix + image->col0 <  region->x0)
+                continue;
+            if (ix + image->col0 >= region->x1)
+                continue;
+            if (iy + image->row0 <  region->y0)
+                continue;
+            if (iy + image->row0 >= region->y1)
+                continue;
+            if (logical_and) {
+                image->data.U8[iy][ix] &= maskValue;
+            } else {
+                image->data.U8[iy][ix] |= maskValue;
+            }
+        }
+    }
+}
+# endif
+
+// mask the area not contained by the region
+// the region is defined wrt the parent image
+// XXX EAM : this is now in psLib
+# if (0)
+    void psImageKeepRegion (psImage *image, psRegion *region, bool logical_and, int maskValue)
+{
+
+    for (int iy = 0; iy < image->numRows; iy++) {
+        for (int ix = 0; ix < image->numCols; ix++) {
+            if (ix + image->col0 <  region->x0)
+                goto maskit;
+            if (ix + image->col0 >= region->x1)
+                goto maskit;
+            if (iy + image->row0 <  region->y0)
+                goto maskit;
+            if (iy + image->row0 >= region->y1)
+                goto maskit;
+            continue;
+maskit:
+            if (logical_and) {
+                image->data.U8[iy][ix] &= maskValue;
+            } else {
+                image->data.U8[iy][ix] |= maskValue;
+            }
+        }
+    }
+}
+# endif
+
+// mask the area contained by the region
+// the region is defined wrt the parent image
+// XXX EAM : this is now in psLib
+# if (0)
+    void psImageMaskCircle (psImage *image, double x, double y, double radius, bool logical_and, int maskValue)
+{
+
+    double dx, dy, r2, R2;
+
+    R2 = PS_SQR(radius);
+
+    for (int iy = 0; iy < image->numRows; iy++) {
+        for (int ix = 0; ix < image->numCols; ix++) {
+            dx = ix + image->col0 - x;
+            dy = iy + image->row0 - y;
+            r2 = PS_SQR(dx) + PS_SQR(dy);
+            if (r2 > R2)
+                continue;
+            if (logical_and) {
+                image->data.U8[iy][ix] &= maskValue;
+            } else {
+                image->data.U8[iy][ix] |= maskValue;
+            }
+        }
+    }
+}
+# endif
+
+// mask the area contained by the region
+// the region is defined wrt the parent image
+// XXX EAM : this is now in psLib
+# if (0)
+    void psImageKeepCircle (psImage *image, double x, double y, double radius, bool logical_and, int maskValue)
+{
+
+    double dx, dy, r2, R2;
+
+    R2 = PS_SQR(radius);
+
+    for (int iy = 0; iy < image->numRows; iy++) {
+        for (int ix = 0; ix < image->numCols; ix++) {
+            dx = ix + image->col0 - x;
+            dy = iy + image->row0 - y;
+            r2 = PS_SQR(dx) + PS_SQR(dy);
+            if (r2 < R2)
+                continue;
+            if (logical_and) {
+                image->data.U8[iy][ix] &= maskValue;
+            } else {
+                image->data.U8[iy][ix] |= maskValue;
+            }
+        }
+    }
+}
+# endif
+
+/* XXX: remove
+psVector *psVectorCreate (double lower, double upper, double delta, psElemType type) {
+ 
+  int nBin = (upper - lower) / delta;
+ 
+  psVector *out = psVectorAlloc (nBin, type);
+  
+  for (int i = 0; i < nBin; i++) {
+    out->data.F64[i] = lower + i * delta;
+  }
+ 
+  return (out);
+}
+*/
+
+// XXX EAM a utility function
+bool p_psVectorPrintRow (int fd, psVector *a, char *name)
+{
+
+    FILE *f;
+    f = fdopen(fd, "a+");
+    fprintf (f, "vector: %s\n", name);
+
+    for (int i = 0; i < a[0].n; i++) {
+        fprintf (f, "%f  ", p_psVectorGetElementF64(a, i));
+    }
+    fprintf (f, "\n");
+    fclose(f);
+    return (true);
+}
+// XXX EAM is the use of fdopen here a good way to do this?
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/psLibUtils.h
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/psLibUtils.h	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/psLibUtils.h	(revision 22115)
@@ -0,0 +1,59 @@
+
+# ifndef PS_LIB_UTILS
+# define PS_LIB_UTILS
+
+// XXX EAM : psEllipse needs to be be included in psLib
+# include "psEllipse.h"
+
+// structure to carry a dynamic string
+typedef struct
+{
+    int NLINE;
+    int Nline;
+    char *line;
+}
+psLine;
+
+# define psMemCopy(A)(psMemIncrRefCounter((A)))
+
+// XXX EAM : my version using varience instead of stdev
+bool psMinimizeGaussNewtonDelta_EAM (psVector *delta,
+                                     const psVector *params,
+                                     const psVector *paramMask,
+                                     const psArray  *x,
+                                     const psVector *y,
+                                     const psVector *yErr,
+                                     psMinimizeLMChi2Func func);
+
+// minimize : using varience vs sigma and parameter limits
+psBool       p_psMinLM_GuessABP_EAM (psImage  *Alpha, psVector *Beta, psVector *Params, const psImage  *alpha, const psVector *beta, const psVector *params, const psVector *paramMask, const psVector *beta_lim, const psVector *params_min, const psVector *params_max, psF64 lambda);
+psBool       psMinimizeLMChi2_EAM(psMinimization *min, psImage *covar, psVector *params, const psVector *paramMask, const psArray *x, const psVector *y, const psVector *yErr, psMinimizeLMChi2Func func);
+psF64        p_psMinLM_dLinear (const psVector *Beta, const psVector *beta, psF64 lambda);
+
+// psLib extra utilities
+bool       psTimerStart (char *name);   // added to SDRS
+//XXX: I removed this: bool       psTimerClear (char *name);   // added to SDRS
+psF64       psTimerMark (char *name);    // added to SDRS
+void       psTimerFree ();              // added to SDRS (as psTimerStop)
+psS32       psLogArguments (int *argc, char **argv);   // added to SDRS (part of psArgumentVerbosity)
+psS32       psTraceArguments (int *argc, char **argv); // added to SDRS (part of psArgumentVerbosity)
+//XXX: remove: int      psArgumentGet (int argc, char **argv, char *arg); // added to SDRS
+//XXX: remove: int      psArgumentRemove (int N, int *argc, char **argv); // added to SDRS
+//XXX: remove: psVector    *psVectorCreate (double lower, double upper, double delta, psElemType type); // added to SDRS
+// psVector    *psGetRowVectorFromImage(psImage *image, psU32 row); // added to SDRS (as psImageRow)
+//XXX: remove: int          psImageCountPixelMask (psImage *mask, psU8 value); // added to SDRS
+
+// basic image functions
+bool         psImageInit (psImage *image,...); // added to SDRS (v.15)
+void      psImageSmooth_EAM (psImage *image, float sigma, float Nsigma); // added to SDRS (v.15)
+
+// psLine functions -- keep out for now?
+psLine      *psLineAlloc (int Nline);
+bool      psLineInit (psLine *line);
+bool      psLineAdd (psLine *line, char *format, ...);
+
+// not included in the .h file -- these are fairly lame, keep out?
+bool p_psVectorPrint (int fd, psVector *a, char *name);
+bool p_psVectorPrintRow (int fd, psVector *a, char *name);
+
+# endif
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/psModulesUtils.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/psModulesUtils.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/psModulesUtils.c	(revision 22115)
@@ -0,0 +1,95 @@
+# include "psModulesUtils.h"
+
+// XXXX: Must figure out the right names for these defines, then replace!
+#define PS_META_STR 0
+#define PS_META_F32 0
+
+
+
+// extract config informatin from config data or from image header, if specified
+// XXX EAM : this function should be replaced with Paul's image/header/metadata load scheme
+psF32 pmConfigLookupF32 (bool *status, psMetadata *config, psMetadata *header, char *name)
+{
+
+    char *source;
+    char *keyword;
+    psF32 value;
+    psMetadataItem *item;
+
+    // look for the entry in the config collection
+    item = psMetadataLookup (config, name);
+    if (item == NULL) {
+        psLogMsg ("pmConfigLookupF32", 2, "no key %s in config data, trying as header keyword", name);
+        value = psMetadataLookupF32 (status, header, name);
+        if (*status == false) {
+            psAbort ("pmConfigLookupF32", "no key %s in header", name);
+        }
+        *status = true;
+        return (value);
+    }
+
+    // I'm either expecting a string, with the name "HD:keyword"...
+    if (item->type == PS_META_STR) {
+        source = item->data.V;
+        if (!strncasecmp (source, "HD:", 3)) {
+            keyword = &source[3];
+            value = psMetadataLookupF32 (status, header, keyword);
+            if (*status == false) {
+                psAbort ("pmConfigLookupF32", "no key %s in config", name);
+            }
+            *status = true;
+            // psFree (item);
+            return (value);
+        }
+    }
+
+    //  ... or a value (F32?)
+    if (item->type == PS_META_F32) {
+        value = item->data.F32;
+        // psFree (item);
+        return (value);
+    }
+
+    *status = false;
+    psError(PS_ERR_UNKNOWN, true, "invalid item");
+    return (0);
+}
+
+bool pmModelFitStatus (pmModel *model)
+{
+
+    bool status;
+
+    pmModelFitStatusFunc statusFunc = pmModelFitStatusFunc_GetFunction (model->type);
+    status = statusFunc (model);
+
+    return (status);
+}
+
+bool pmSourcePhotometry (float *fitMag, float *obsMag, pmModel *model, psImage *image, psImage *mask)
+{
+
+    float obsSum = 0;
+    float fitSum = 0;
+    float sky = model->params->data.F32[0];
+
+    pmModelFlux modelFluxFunc = pmModelFlux_GetFunction (model->type);
+    fitSum = modelFluxFunc (model->params);
+
+    for (int ix = 0; ix < image->numCols; ix++) {
+        for (int iy = 0; iy < image->numRows; iy++) {
+            if (mask->data.U8[iy][ix])
+                continue;
+            obsSum += image->data.F32[iy][ix] - sky;
+        }
+    }
+    if (obsSum <= 0)
+        return false;
+    if (fitSum <= 0)
+        return false;
+
+    *fitMag = -2.5*log10(fitSum);
+    *obsMag = -2.5*log10(obsSum);
+    return (true);
+}
+
Index: /tags/ipp-1-X/rel8_2/psModules/src/objects/psModulesUtils.h
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/objects/psModulesUtils.h	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/objects/psModulesUtils.h	(revision 22115)
@@ -0,0 +1,20 @@
+
+#ifndef PS_MODULE_UTILS
+#define PS_MODULE_UTILS
+
+#include <strings.h>  // for strcasecmp
+#include <pslib.h>
+#include "psLibUtils.h"
+#include "pmObjects.h"
+#include "pmModelGroup.h"
+
+// psModule extra utilities
+// bool      pmSourceFitModel_EAM(pmSource *source, pmModel *model, const bool PSF);
+bool       pmModelFitStatus (pmModel *model);
+int      pmSourceDophotType (pmSource *source);
+bool      pmSourcePhotometry (float *fitMag, float *obsMag, pmModel *model, psImage *image, psImage *mask);
+
+// XXX: unify with paul's image/header/metadata functions
+psF32        pmConfigLookupF32 (bool *status, psMetadata *config, psMetadata *header, char *name);
+
+#endif
Index: /tags/ipp-1-X/rel8_2/psModules/src/parseErrorCodes.pl
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/parseErrorCodes.pl	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/parseErrorCodes.pl	(revision 22115)
@@ -0,0 +1,97 @@
+#!/usr/bin/perl
+
+# Provides functions for handling long command line options
+use Getopt::Long;
+
+my @ErrorCodes        = ();
+my @ErrorDescriptions = ();
+
+my $data = "psErrorCodes.dat";
+
+# Assign variables based on the presence of command line options to the script
+GetOptions(
+    "data=s"  => \$data,
+    "verbose" => \$verbose,
+    "help"    => \$help
+);
+
+if ($help) {
+    print "Usage: parseErrorCodes ", "[--data=$data] ", "[--help] ",
+      "[--verbose] filename [filename ...]\n\n";
+    exit(0);
+}
+
+print "Using data file '$data'\n" if $verbose;
+unless ( open( DATAFILE, "<", $data ) ) {
+    die "Can not open data file $data.";
+}
+
+print "Datafile:\n" if $verbose;
+while (<DATAFILE>) {
+    chop;
+    if (/^\s*\#/) {
+        print "C $_\n" if $verbose;
+    }
+    else {
+        if (/^\s*(\w+)\s+([\%\w].*)/) {
+            my $ErrorCode        = $1;
+            my $ErrorDescription = $2;
+            print "  $ErrorCode: '$ErrorDescription'\n" if $verbose;
+            push( @ErrorCodes,        $ErrorCode );
+            push( @ErrorDescriptions, $ErrorDescription );
+        } else {
+            print "I $_\n" if $verbose;
+        }
+    }
+}
+close(DATAFILE);
+
+my $found = $#ErrorCodes + 1;
+print "\nFound $found error codes.\n" if $verbose;
+
+foreach (@ARGV) {
+    my $filename = $_;
+    my @result   = ();
+
+    die "Failed to open input file"
+      if !open( INFILE, "<", $filename );
+
+    print "\nOutput File:\n" if $verbose;
+    while (<INFILE>) {
+        chop;
+        push( @result, $_ );
+        if (/^\s*\/\/~Start(.*)$/) {
+            $line = $1;
+            for ( $n = 0 ; $n < $found ; $n++ ) {
+                $_ = $line;
+                s/\$1/$ErrorCodes[$n]/g;
+                s/\$2/$ErrorDescriptions[$n]/g;
+                s/\$n/$n/g;
+                push( @result, $_ );
+                print "$_\n" if $verbose;
+            }
+
+            $break = 0;
+            while ( ( $break == 0 ) && ( $_ = <INFILE> ) ) {
+                if (/^\s*\/\/~End/) {
+                    $break = 1;
+                }
+            }
+            chop;
+            push( @result, $_ );
+        }
+    }
+
+    close(INFILE);
+
+    die "Failed to overwrite input file"
+      if !open( OUTFILE, ">", $filename );
+
+    foreach (@result) {
+        print OUTFILE "$_\n";
+        print "$_\n" if $verbose;
+    }
+
+    close(OUTFILE);
+
+}
Index: /tags/ipp-1-X/rel8_2/psModules/src/photom/.cvsignore
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/photom/.cvsignore	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/photom/.cvsignore	(revision 22115)
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
Index: /tags/ipp-1-X/rel8_2/psModules/src/photom/Makefile.am
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/photom/Makefile.am	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/photom/Makefile.am	(revision 22115)
@@ -0,0 +1,9 @@
+noinst_LTLIBRARIES = libpsmodulephotom.la
+
+libpsmodulephotom_la_CPPFLAGS = $(SRCINC) $(PSMODULE_CFLAGS)
+libpsmodulephotom_la_LDFLAGS  = -release $(PACKAGE_VERSION)
+libpsmodulephotom_la_SOURCES  =
+
+psmoduleincludedir = $(includedir)
+psmoduleinclude_HEADERS =
+
Index: /tags/ipp-1-X/rel8_2/psModules/src/psErrorCodes.dat
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/src/psErrorCodes.dat	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/src/psErrorCodes.dat	(revision 22115)
@@ -0,0 +1,18 @@
+#
+#  This file is used to generate psErrorCode.h content
+#
+# Format:
+#    ERROR_CLASS_NAME    ERROR_CLASS_DESCRIPTION
+#
+# Note: the ERROR_CLASS_NAME will appear in the code with a "PS_ERR_" prefix
+#
+UNKNOWN                        unknown error
+IO                             I/O error
+LOCATION_INVALID               specified location is unknown
+MEMORY_CORRUPTION              memory corruption detected
+MEMORY_DEREF_USAGE             dereferenced memory still used
+BAD_PARAMETER_VALUE            parameter is out-of-range
+BAD_PARAMETER_TYPE             parameter is of unsupported type
+BAD_PARAMETER_NULL             parameter is null
+BAD_PARAMETER_SIZE             size of parameter's data is outside of acceptable range.
+UNEXPECTED_NULL                unexpected NULL found
Index: /tags/ipp-1-X/rel8_2/psModules/templates/c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/templates/c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/templates/c	(revision 22115)
@@ -0,0 +1,11 @@
+/** @file  filename.c
+ *
+ *  @brief Contains the definitions for ...
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1.1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-09-10 19:01:33 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
Index: /tags/ipp-1-X/rel8_2/psModules/templates/h
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/templates/h	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/templates/h	(revision 22115)
@@ -0,0 +1,13 @@
+/** @file  filename.h
+ *
+ *  @brief Contains the definitions for ...
+ *
+ *  Detailed Description goes here.
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1.1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-09-10 19:01:33 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
Index: /tags/ipp-1-X/rel8_2/psModules/test/.cvsignore
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/.cvsignore	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/.cvsignore	(revision 22115)
@@ -0,0 +1,15 @@
+.deps
+.libs
+Makefile
+tst_pmFlatField
+tst_pmMaskBadPixels
+tst_pmNonLinear
+tst_pmSubtractBias
+temp
+Makefile.in
+tst_pmObjects01
+tst_pmReadoutCombine
+tst_pmSubtractSky
+tst_pmImageSubtract
+tst_pmImageCombine
+tst_pmAstrometry
Index: /tags/ipp-1-X/rel8_2/psModules/test/FullUnitTest
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/FullUnitTest	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/FullUnitTest	(revision 22115)
@@ -0,0 +1,360 @@
+#!/usr/bin/perl
+#
+#  This is a perl script test harness which will recursively search the
+#  directory tree looking for files named UnitTest and then execute
+#  the script
+#
+#  SYNOSIS :  FullUnitTest options arguements
+#
+#       where  options =
+#             --verbose     Display extra information to user
+#             --noverbose   Don't display extra information to user
+#             --recursiv e  Recursively run tests in directory tree
+#             --norecursive Test only the specified or current directory
+#             --silent      Don't display any information to user
+#             --nosilent    Display progress of script to user
+#
+#              arguements = directory(ies) to perform tests
+#
+#  RETURN : integer number of tests which failed
+#
+#  $Revision: 1.3 $  $Name: not supported by cvs2svn $
+#  $Date: 2005-04-12 21:51:00 $
+#
+#  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+#
+##############################################################################
+
+use FindBin qw($Bin);
+
+$runTest = "$Bin/runTest";
+
+# Provide functions for determining the pathname of current working directory
+use Cwd;
+
+# Provides functions for handling psS64 command line options
+use Getopt::Long;
+
+# Assign variables based on the presence of command line options to the script
+# The ! option allows for --nooption to be set to zero
+# (e.g. --noverbose --recursive causes $verbose=0 and $recursive=1)
+GetOptions(
+    "verbose!"   => \$verbose,
+    "recursive!" => \$recursive,
+    "silent!"    => \$silent,
+    "clean!"     => \$clean
+);
+
+# Check if both silent and verbose options are set and if so stop the script
+die "Can't specify both verbose and silent options." if ( $verbose && $silent );
+
+# Set up the PSLIB_ROOT environment variable if the user doesn't have
+if ( !$ENV{'PSLIB_ROOT'} ) {
+
+    # Use the directory directly above where FullUnitTest script resides
+    $PSLIB_ROOT = `cd ..;pwd`;
+
+    # Remove newline for the end of path returned
+    chomp($PSLIB_ROOT);
+
+    # Set the environment variable
+    $ENV{'PSLIB_ROOT'} = $PSLIB_ROOT;
+    print("PSLIB_ROOT not found: set to $PSLIB_ROOT.\n") if $verbose;
+}
+
+# add PSLIB_ROOT/lib to LD_LIBRARY_PATH and DYLD_LIBRARY_PATH environment
+# variables
+$ENV{'LD_LIBRARY_PATH'}   = "$ENV{'PSLIB_ROOT'}/src/.libs:$ENV{'LD_LIBRARY_PATH'}";
+$ENV{'DYLD_LIBRARY_PATH'} = "$ENV{'PSLIB_ROOT'}/src/.libs:$ENV{'DYLD_LIBRARY_PATH'}";
+
+# Initialize variables for counting the makes and test failures and the
+# total makes and tests performed
+$makeFailCount       = 0;
+$testpointFailCount  = 0;
+$testDriverFailCount = 0;
+$totalTestpoints     = 0;
+$totalTestDrivers    = 0;
+$totalMakes          = 0;
+
+# Initialize variable indicating how many arguements were passed
+$args = 0;
+
+# Loop through all the arguements passed to the script
+foreach (@ARGV) {
+
+    # Remove newline if there is one
+    chomp;
+
+    # Increment number of arguements found
+    $args++;
+
+    # Set variable to current working directory
+    $cwd = cwd;
+
+    # Change directory to the directory of the arguement
+    chdir($_);
+
+    # Check for the recursive option
+    if ($recursive) {
+
+        # Invoke subroutine to go to the lowest directory in tree
+        # starting at the specified directory
+        &worm($_);
+    }
+    else {
+
+        # Invoke subroutines to run the test at the specified directory
+        &makeTestDrivers($_);
+        &executeTestDrivers($_);
+    }
+
+    # Change directory back to the directory where FullUnitTest was invoked
+    chdir($cwd);
+}
+
+# Check if there were no arguements specified
+if ( $args == 0 ) {
+
+    # Display message to user that all directories under the current will be
+    # tested
+    print("Recursively testing current directory tree.\n") if $verbose;
+
+    # Invoke subroutine to go to recursively test each directory in tree
+    &worm( $ENV{"PWD"} );
+}
+
+# Check if there were any failures during make or testing
+if ( $makeFailCount > 0 || $testDriverFailCount > 0 ) {
+
+    # Display summary of failures
+    print("\nMake Failures = $makeFailCount out of $totalMakes");
+    print(
+"\nTest Driver Failures = $testDriverFailCount out of $totalTestDrivers\n"
+    );
+    print( "\nMakes that failed:\n  " . join( "\n  ", @makesFailed ) . "\n" )
+      if $makeFailCount;
+    print( "\nTests that failed:\n  " . join( "\n  ", @testsFailed ) . "\n" )
+      if $testDriverFailCount;
+}
+else {
+
+    # Display message of all makes and tests pass to user if silent option
+    # not specified
+    print(
+"\nAll $totalTestDrivers Test Drivers Passed with $totalTestpoints Testpoints.\n"
+      )
+      if ( !$silent );
+}
+
+# Exit with the number of tests that failed
+exit($testDriverFailCount);
+
+################################################################################
+#
+# SUBROUTINE: worm
+#
+#     Description:  This subroutine will perform the necessary unit tests be
+#                   calling makeTestDrivers and executeTestDrivers  subroutines
+#                   and then check each subdirectory below the base directory
+#                   recursively.
+#
+#     Parameter(s):  base directory to start testing
+#
+#     Return:  None
+#
+################################################################################
+
+sub worm {
+
+    # Assign local variable to input parameter
+    local ($base_dir) = @_;
+    local ( @files, $i );
+
+    # Invoke subroutine to make test driver in the base directory
+    &makeTestDrivers($base_dir);
+
+    # Invoke subroutine to execute tests in the base directory
+    &executeTestDrivers($base_dir);
+
+    # Create array of entries found in directory
+    @files = <*>;
+
+    # Loop through the file list looking for another directory that is not
+    # labelled CVS and recursively invoke subroutine worm
+    $i = 0;
+    while ( $files[$i] ) {
+
+        # Check for directory and directory not labelled CVS
+        if (   -d $files[$i]
+            && ( $files[$i] ne "CVS" )
+            && ( $files[$i] ne "temp" )
+            && ( $files[$i] ne "verified" ) )
+        {
+
+            # Change current directory to directory found
+            chdir( $files[$i] );
+
+            # Invoke subroutine worm again
+            &worm( $files[$i] );
+
+            # Change current directory back to parent
+            chdir("..");
+        }
+
+        # Increment file list index
+        $i++;
+    }
+}
+
+################################################################################
+#
+#  SUBROUTINE: makeTestDrivers
+#
+#      Description:  This subroutine will perform a make for all the necessary
+#                    test drivers.    This will occur in the specified
+#                    base directory which is passed as the only parameter.
+#
+#      Parameter(s):  base directory where make and test drivers are located
+#
+#      Return:  None
+#
+################################################################################
+
+sub makeTestDrivers {
+    local ($base_dir) = @_;
+    local ($pwd);
+
+    # Set variable pwd to current working directory
+    $pwd = cwd;
+
+    # Display message to user in which directory testing is taken place only
+    # if the silent command option was not specified
+    print("---- Entering $pwd ----\n") if ( !$silent );
+
+    # Check for the existence of a file name Makefile in current directory
+    if ( -e "Makefile" ) {
+
+        # Increment the total number of makes executed
+        $totalMakes++;
+
+        if ($clean) {
+
+            # Execute the make clean
+            `make clean`;
+
+        }
+
+        # Execute the make and save results
+        $_ = join( "\n|| ", split( "\n", "\n" . `make tests` ) );
+
+        # Check the output of make for return value != 0 or any of the
+        # following words: FAILED, FAULT, ERROR, Not found, SIGNAL
+        if ( $? != 0 ) {
+
+            # Display the errored output of make if silent option not enabled
+            print("$_\n") if ( !$silent );
+
+            # Display the make failed in the current directory
+            print("\nMake for $pwd Failed\n");
+
+            # Increment the number of makes that have failed
+            $makeFailCount++;
+
+            # Push the current working directory onto the list of failed
+            # make directories list
+            push( @makesFailed, $pwd );
+        }
+        else {
+
+            # Display the results of the successful make if verbose option set
+            print("$_\n") if $verbose;
+
+            # Display the make was successful if silent option not set
+            print("\nMake successful.\n") if ( !$silent );
+        }
+    }
+}
+
+################################################################################
+#
+#  SUBROUTINE: executeTestDrivers
+#
+#      Description:  This subroutine will execute all the necessary
+#                    test drivers.    This will occur in the specified
+#                    base directory which is passed as the only parameter.
+#
+#      Parameter(s):  base directory where make and test drivers are located
+#
+################################################################################
+
+sub executeTestDrivers {
+    local ($base_dir) = @_;
+    local ( @files, $j );
+    local ($pwd);
+    local ($exitValue) = 0;
+
+    # Set variable to pwd to current test directory
+    $pwd = cwd;
+
+    # Create a list of all elements in base directory
+    @files = <*>;
+
+    # Loop through all the items in the files array
+    $initialTest = 0;
+    $j           = 0;
+    while ( $files[$j] ) {
+
+        # Check that the item is not a directory and is executable and
+        # conforms to the test driver naming convention TST
+        if (   !( -d $files[$j] )
+            && ( -x $files[$j] )
+            && ( $files[$j] =~ /^TST/i || $files[$j] =~ /^ATST/i ) )
+        {
+
+            # Increment total number of test drivers executed
+            $totalTestDrivers++;
+
+            # Display message to user of which test driver is being executed
+            print("--- Executing test driver $files[$j]\n") if ( !$silent );
+
+            # Execute the test driver
+            if ($silent) {
+                system("$runTest --quiet ./$files[$j]");
+            } elsif ($verbose) {
+                system("$runTest --printpassfail ./$files[$j]");
+            } else {
+                system("$runTest ./$files[$j]");
+            }
+            $retVal = $?;
+
+            # Count testpoints
+            $testPattern = "\"\\*\\*\\*\\* TESTPOINT \\*\\*\\*\\*\"";
+            $totalPoints = `grep -c $testPattern temp/$files[$j].stdout`;
+            $totalPoints += `grep -c $testPattern temp/$files[$j].stderr`;
+            $failPoints =
+              `grep "> TESTPOINT FAILED" temp/$files[$j].stdout | wc -l`;
+            $failPoints +=
+              `grep "> TESTPOINT FAILED" temp/$files[$j].stderr | wc -l`;
+            $testpointFailCount += $failPoints;
+            $totalTestpoints    += $totalPoints;
+
+            # Check result of test driver
+            if ( $retVal != 0 )
+            {
+
+                # Display test driver failed
+                $failPoints++;
+                print(
+                    "Test driver: $files[$j] Failed (Return value $retVal).\n");
+
+                # Increment the total number of test failed
+                $testDriverFailCount++;
+
+                # Push the current working directory on the list of failed
+                push( @testsFailed, $pwd . "/" . $files[$j] );
+            }
+        }
+        $j++;
+    }
+}
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/Makefile.am
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/Makefile.am	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/Makefile.am	(revision 22115)
@@ -0,0 +1,17 @@
+# Makefile for psModule tests
+
+SUBDIRS = $(SRCDIRS)
+
+EXTRA_DIST = \
+	runTest \
+	FullUnitTest
+
+all:
+	chmod a+x $(srcdir)/FullUnitTest
+
+test: check
+
+tests: $(TESTS)
+
+CLEANFILES = $(TESTS)
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/astrom/.cvsignore
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/astrom/.cvsignore	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/astrom/.cvsignore	(revision 22115)
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+temp
+tst_pmAstrometry
Index: /tags/ipp-1-X/rel8_2/psModules/test/astrom/Makefile.am
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/astrom/Makefile.am	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/astrom/Makefile.am	(revision 22115)
@@ -0,0 +1,22 @@
+# Makefile for psModule tests
+
+AM_LDFLAGS = -L$(top_builddir)/src -lpsmodule $(PSMODULE_LIBS)
+AM_CFLAGS  = @AM_CFLAGS@ $(PSMODULE_CFLAGS) $(SRCINC)
+AM_CPPFLAGS = $(SRCINC) $(PSLIB_CFLAGS)
+
+TESTS = \
+    tst_pmAstrometry
+
+tst_pmAstrometry_SOURCES = tst_pmAstrometry.c
+
+check_PROGRAMS = $(TESTS)
+
+TESTS_ENVIRONMENT = perl $(top_srcdir)/test/runTest --verified=$(srcdir)/verified
+
+tests: $(TESTS)
+
+EXTRA_DIST = verified
+
+CLEANFILES = $(TESTS) temp/*
+
+test: check
Index: /tags/ipp-1-X/rel8_2/psModules/test/astrom/tst_pmAstrometry.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/astrom/tst_pmAstrometry.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/astrom/tst_pmAstrometry.c	(revision 22115)
@@ -0,0 +1,363 @@
+/** @file  tst_pmAstrometry.c
+ *
+ *  @brief Contains the tests for pmAstrometry.[ch].  Only the pmxxxAlloc()
+ *  and psFree() functionality are used here.
+ *
+ *  @author George Gusciora, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:42:51 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include "config.h"
+#include <math.h>
+#include <string.h>
+#include "psTest.h"
+#include "pslib_strict.h"
+#include "pmAstrometry.h"
+
+static psS32 testFPAAlloc(void);
+static psS32 testChipAlloc(void);
+static psS32 testCellAlloc(void);
+static psS32 testReadoutAlloc(void);
+
+testDescription tests[] = {
+                              {testFPAAlloc,739,"pmFPAAlloc",0,false},
+                              {testChipAlloc,740,"pmChipAlloc",0,false},
+                              {testCellAlloc,741,"pmCellAlloc",0,false},
+                              {testReadoutAlloc,742,"pmReadoutAlloc",0,false},
+                              {NULL}
+                          };
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetLevel(PS_LOG_INFO);
+    psLogSetFormat("HLNM");
+
+    return ! runTestSuite(stderr,"pmAstrometry",tests,argc,argv);
+}
+
+static psS32 testFPAAlloc(void)
+{
+    // XXX: Do something more with these arguments.
+    const psMetadata *camera = psMetadataAlloc();
+    psDB *db = NULL;
+    pmFPA* fpa = pmFPAAlloc(camera, db);
+
+    if (fpa == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "pmFPAAlloc returned a NULL.");
+        return 1;
+    }
+
+    if (fpa->fromTangentPlane != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "pmFPAAlloc did not set ->fromTangentPlane to NULL.");
+        return 2;
+    }
+
+    if (fpa->toTangentPlane != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "pmFPAAlloc did not set ->toTangentPlane to NULL.");
+        return 3;
+    }
+    if (fpa->projection != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "pmFPAAlloc did not set ->projection to NULL.");
+        return 4;
+    }
+
+    if (fpa->concepts == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "pmFPAAlloc did not set ->concepts.");
+        return 5;
+    }
+
+    if (fpa->analysis == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "pmFPAAlloc did not set ->analysis.");
+        return 6;
+    }
+
+    if (fpa->camera != camera) {
+        psLogMsg(__func__,PS_LOG_ERROR, "pmFPAAlloc did not set ->camera.");
+        return 7;
+    }
+
+    if (fpa->chips == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "pmFPAAlloc did not set ->chips.");
+        return 8;
+    }
+
+    if (fpa->header != NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "pmFPAAlloc did not set ->header to NULL.");
+        return 9;
+    }
+
+    if (fpa->db != db) {
+        psLogMsg(__func__,PS_LOG_ERROR, "pmFPAAlloc did not set ->db.");
+        return 10;
+    }
+
+    psFree(camera);
+    psFree(fpa);
+
+    return 0;
+}
+
+static psS32 testChipAlloc(void)
+{
+    const psMetadata *camera = psMetadataAlloc();
+    psDB *db = NULL;
+    pmFPA* fpa = pmFPAAlloc(camera, db);
+    if (fpa == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "pmFPAAlloc returned a NULL.");
+        return 1;
+    }
+
+    pmChip *chip = pmChipAlloc(fpa);
+    if (chip == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "pmChipAlloc returned a NULL.");
+        return 1;
+    }
+
+    if (chip->col0 != -1) {
+        psLogMsg(__func__, PS_LOG_ERROR, "chip->col0 set improperly.\n");
+        return 5;
+    }
+
+    if (chip->row0 != -1) {
+        psLogMsg(__func__, PS_LOG_ERROR, "chip->row0 set improperly.\n");
+        return 6;
+    }
+
+    if (chip->toFPA != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "chip->toChip set improperly.\n");
+        return 7;
+    }
+
+    if (chip->fromFPA != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "chip->toFPA set improperly.\n");
+        return 8;
+    }
+
+    if (chip->concepts == NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "chip->concepts set improperly.\n");
+        return 21;
+    }
+
+    if (chip->analysis == NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "chip->analysis set improperly.\n");
+        return 10;
+    }
+
+    if (chip->cells == NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "chip->cells set improperly.\n");
+        return 22;
+    }
+
+    if (chip->parent != fpa) {
+        psLogMsg(__func__, PS_LOG_ERROR, "chip->parent set improperly.\n");
+        return 23;
+    }
+
+    if (chip->valid != false) {
+        psLogMsg(__func__, PS_LOG_ERROR, "chip->valid set improperly.\n");
+        return 24;
+    }
+
+    if (chip->extname != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "chip->extname set improperly.\n");
+        return 25;
+    }
+
+    if (chip->pixels != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "chip->pixels set improperly.\n");
+        return 26;
+    }
+
+    if (chip->header != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "chip->header set improperly.\n");
+        return 27;
+    }
+
+    psFree(fpa);
+    psFree(chip);
+    psFree(camera);
+
+    return 0;
+}
+
+static psS32 testCellAlloc(void)
+{
+    psDB *db = NULL;
+    const psMetadata *camera = psMetadataAlloc();
+    pmFPA* fpa = pmFPAAlloc(camera, db);
+    if (fpa == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "pmFPAAlloc returned a NULL.n");
+        return 1;
+    }
+
+    pmChip *chip = pmChipAlloc(fpa);
+    if (chip == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "pmChipAlloc returned a NULL.n");
+        return 2;
+    }
+
+    pmCell *cell = pmCellAlloc(chip);
+    if (cell == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "pmCellAlloc returned a NULL.n");
+        return 3;
+    }
+
+    if (cell->col0 != -1) {
+        psLogMsg(__func__, PS_LOG_ERROR, "cell->col0 set improperly.\n");
+        return 5;
+    }
+
+    if (cell->row0 != -1) {
+        psLogMsg(__func__, PS_LOG_ERROR, "cell->row0 set improperly.\n");
+        return 6;
+    }
+
+    if (cell->toChip != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "cell->toChip set improperly.\n");
+        return 7;
+    }
+
+    if (cell->toFPA != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "cell->toFPA set improperly.\n");
+        return 8;
+    }
+
+    if (cell->toSky != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "cell->toSky set improperly.\n");
+        return 9;
+    }
+
+    if (cell->analysis == NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "cell->analysis set improperly.\n");
+        return 10;
+    }
+
+    if (cell->concepts == NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "cell->concepts set improperly.\n");
+        return 21;
+    }
+
+    if (cell->readouts == NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "cell->readouts set improperly.\n");
+        return 22;
+    }
+
+    if (cell->parent != chip) {
+        psLogMsg(__func__, PS_LOG_ERROR, "cell->parent set improperly.\n");
+        return 23;
+    }
+
+    if (cell->valid != false) {
+        psLogMsg(__func__, PS_LOG_ERROR, "cell->valid set improperly.\n");
+        return 24;
+    }
+
+    if (cell->extname != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "cell->extname set improperly.\n");
+        return 25;
+    }
+
+    if (cell->pixels != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "cell->pixels set improperly.\n");
+        return 26;
+    }
+
+    if (cell->header != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "cell->header set improperly.\n");
+        return 27;
+    }
+
+    psFree(fpa);
+    psFree(chip);
+    psFree(cell);
+    psFree(camera);
+
+    return 0;
+}
+
+static psS32 testReadoutAlloc(void)
+{
+    psDB *db = NULL;
+    const psMetadata *camera = psMetadataAlloc();
+    pmFPA* fpa = pmFPAAlloc(camera, db);
+
+    if (fpa == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "pmFPAAlloc returned a NULL.\n");
+        return 1;
+    }
+
+    pmChip *chip = pmChipAlloc(fpa);
+    if (chip == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "pmChipAlloc returned a NULL.\n");
+        return 2;
+    }
+
+    pmCell *cell = pmCellAlloc(chip);
+    if (cell == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "pmCellAlloc returned a NULL.\n");
+        return 3;
+    }
+
+    pmReadout *readout = pmReadoutAlloc(cell);
+    if (readout == NULL) {
+        psLogMsg(__func__,PS_LOG_ERROR, "pmReadoutAlloc returned a NULL.\n");
+        return 4;
+    }
+
+    if (readout->col0 != -1) {
+        psLogMsg(__func__, PS_LOG_ERROR, "pmReadout->col0 set improperly.\n");
+        return 5;
+    }
+
+    if (readout->row0 != -1) {
+        psLogMsg(__func__, PS_LOG_ERROR, "pmReadout->row0 set improperly.\n");
+        return 6;
+    }
+
+    if (readout->colBins != -1) {
+        psLogMsg(__func__, PS_LOG_ERROR, "pmReadout->colBins set improperly.\n");
+        return 7;
+    }
+
+    if (readout->rowBins != -1) {
+        psLogMsg(__func__, PS_LOG_ERROR, "pmReadout->colBins set improperly.\n");
+        return 8;
+    }
+
+    if (readout->image != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "pmReadout->image set improperly.\n");
+        return 9;
+    }
+
+    if (readout->mask != NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "pmReadout->mask set improperly.\n");
+        return 10;
+    }
+
+    if (readout->analysis == NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "pmReadout->analysis set improperly.\n");
+        return 11;
+    }
+
+    if (readout->concepts == NULL) {
+        psLogMsg(__func__, PS_LOG_ERROR, "pmReadout->concepts set improperly.\n");
+        return 12;
+    }
+
+    if (readout->parent != cell) {
+        psLogMsg(__func__, PS_LOG_ERROR, "pmReadout->parent set improperly.\n");
+        return 15;
+    }
+
+    psFree(fpa);
+    psFree(chip);
+    psFree(cell);
+    psFree(readout);
+    psFree(camera);
+
+    return 0;
+}
Index: /tags/ipp-1-X/rel8_2/psModules/test/astrom/verified/tst_pmAstrometry.stderr
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/astrom/verified/tst_pmAstrometry.stderr	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/astrom/verified/tst_pmAstrometry.stderr	(revision 22115)
@@ -0,0 +1,36 @@
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmAstrometry.c                                         *
+*            TestPoint: pmAstrometry{pmFPAAlloc}                                   *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmAstrometry{pmFPAAlloc} | tst_pmAstrometry.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmAstrometry.c                                         *
+*            TestPoint: pmAstrometry{pmChipAlloc}                                  *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmAstrometry{pmChipAlloc} | tst_pmAstrometry.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmAstrometry.c                                         *
+*            TestPoint: pmAstrometry{pmCellAlloc}                                  *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmAstrometry{pmCellAlloc} | tst_pmAstrometry.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmAstrometry.c                                         *
+*            TestPoint: pmAstrometry{pmReadoutAlloc}                               *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmAstrometry{pmReadoutAlloc} | tst_pmAstrometry.c)
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/camera/.cvsignore
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/camera/.cvsignore	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/camera/.cvsignore	(revision 22115)
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+temp
+tst_pmAstrometry
Index: /tags/ipp-1-X/rel8_2/psModules/test/camera/Makefile.am
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/camera/Makefile.am	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/camera/Makefile.am	(revision 22115)
@@ -0,0 +1,16 @@
+# Makefile for psModule tests
+
+AM_LDFLAGS = -L$(top_builddir)/src -lpsmodule $(PSMODULE_LIBS)
+AM_CFLAGS  = @AM_CFLAGS@ $(PSMODULE_CFLAGS) $(SRCINC)
+
+TESTS =
+
+check_PROGRAMS = $(TESTS)
+
+TESTS_ENVIRONMENT = perl $(top_srcdir)/test/runTest --verified=$(srcdir)/verified
+
+tests: $(TESTS)
+
+CLEANFILES = $(TESTS) temp/*
+
+test: check
Index: /tags/ipp-1-X/rel8_2/psModules/test/config/.cvsignore
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/config/.cvsignore	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/config/.cvsignore	(revision 22115)
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+temp
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/config/Makefile.am
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/config/Makefile.am	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/config/Makefile.am	(revision 22115)
@@ -0,0 +1,14 @@
+# Makefile for psModule tests
+
+AM_LDFLAGS = -L$(top_builddir)/src -lpsmodule $(PSMODULE_LIBS)
+AM_CFLAGS  = @AM_CFLAGS@ $(PSMODULE_CFLAGS) $(SRCINC)
+
+TESTS =
+
+check_PROGRAMS = $(TESTS)
+
+TESTS_ENVIRONMENT = perl $(top_srcdir)/test/runTest --verified=$(srcdir)/verified
+
+tests:
+
+test: check
Index: /tags/ipp-1-X/rel8_2/psModules/test/detrend/.cvsignore
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/detrend/.cvsignore	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/detrend/.cvsignore	(revision 22115)
@@ -0,0 +1,9 @@
+.deps
+.libs
+Makefile
+Makefile.in
+temp
+tst_pmFlatField
+tst_pmMaskBadPixels
+tst_pmNonLinear
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/detrend/Makefile.am
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/detrend/Makefile.am	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/detrend/Makefile.am	(revision 22115)
@@ -0,0 +1,26 @@
+# Makefile for psModule tests
+
+AM_LDFLAGS = -L$(top_builddir)/src -lpsmodule $(PSMODULE_LIBS)
+AM_CFLAGS  = @AM_CFLAGS@ $(PSMODULE_CFLAGS) $(SRCINC)
+AM_CPPFLAGS = $(SRCINC) $(PSLIB_CFLAGS)
+
+TESTS = \
+    tst_pmFlatField \
+    tst_pmMaskBadPixels \
+    tst_pmNonLinear
+
+tst_pmFlatField_SOURCES = tst_pmFlatField.c
+tst_pmMaskBadPixels_SOURCES = tst_pmMaskBadPixels.c
+tst_pmNonLinear_SOURCES = tst_pmNonLinear.c
+
+check_PROGRAMS = $(TESTS)
+
+TESTS_ENVIRONMENT = perl $(top_srcdir)/test/runTest --verified=$(srcdir)/verified
+
+tests: $(TESTS)
+
+EXTRA_DIST = verified
+
+CLEANFILES = $(TESTS) temp/*
+
+test: check
Index: /tags/ipp-1-X/rel8_2/psModules/test/detrend/tst_pmFlatField.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/detrend/tst_pmFlatField.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/detrend/tst_pmFlatField.c	(revision 22115)
@@ -0,0 +1,298 @@
+/** @file tst_pmFlatField.c
+ *
+ *  @brief Contains the tests for pmFlatField.c:
+ *
+ *    Test A - Divide input image by flat image
+ *    Test B - Mask flat image data
+ *    Test C - Mask flat image data starting with non-null mask
+ *    Test E - Attempt to use null input image
+ *    Test F - Attempt tp use null flat image
+ *    Test G - Attempt to use input image bigger than flat image
+ *    Test H - Attempt to use input image mask bigger than flat image
+ *    Test I - Attempt to use offset greater than input image
+ *    Test J - Attempt to use complex input image
+ *    Test K - Attempt to use complex flat image
+ *    Test L - Attempt to use non-equal input and flat image types
+ *    Test M - Attempt to use non-mask type mask image
+ *
+ * XXX: Added a mask argument to pmFlatField().  Must add tests.  For now, all
+ * masks are NULL.
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:42:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+
+#include "psTest.h"
+#include "pslib.h"
+#include "pmFlatField.h"
+
+
+#define PRINT_MATRIX(IMAGE,TYPE,STRING)                                                                      \
+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]);                                                      \
+        } else {                                                                                             \
+            printf("%f ", (double)IMAGE->data.TYPE[i][j]);                                                   \
+        }                                                                                                    \
+    }                                                                                                        \
+    printf("\n");                                                                                            \
+}                                                                                                            \
+printf("\n");
+
+
+#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;                                                                       \
+    }                                                                                                        \
+}
+
+
+static int testFlatField(void);
+
+
+testDescription tests[] = {
+                              {testFlatField, 753, "pmFlatField", 0, false},
+                              {NULL}
+                          };
+
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    return !runTestSuite(stderr, "Test Point Driver", tests, argc, argv);
+}
+
+
+int testFlatField( void )
+{
+    // Test A - Divide input image by flat image
+    printPositiveTestHeader(stdout, "pmFlatField", "Test A - Divide input image by flat image");
+    CREATE_AND_SET_IMAGE(inImage,F64,6.0,3,3)
+    pmReadout *inReadout = pmReadoutAlloc(NULL);
+    inReadout->image = inImage;
+    inReadout->row0 = 0;
+    inReadout->col0 = 0;
+    PRINT_MATRIX(inImage,F64,"Input image:");
+
+    CREATE_AND_SET_IMAGE(inMask,U8,0,3,3)
+    pmReadout *inMaskReadout = pmReadoutAlloc(NULL);
+    inMaskReadout->image = inMask;
+    inMaskReadout->row0 = 0;
+    inMaskReadout->col0 = 0;
+    PRINT_MATRIX(inMask,U8,"Input mask:");
+
+    CREATE_AND_SET_IMAGE(flatImage1,F64,2.0,3,3)
+    pmReadout *flatReadout = pmReadoutAlloc(NULL);
+    flatReadout->row0 = 0;
+    flatReadout->col0 = 0;
+    flatReadout->image = flatImage1;
+    PRINT_MATRIX(flatImage1,F64,"Flat image:");
+
+    if ( !pmFlatField(inReadout, inMaskReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test A - Returned false should be true");
+        return 1;
+    }
+    PRINT_MATRIX(inImage,F64, "Resulting image:");
+    printFooter(stdout, "pmFlatField", "Test A - Divide input image by flat image", true);
+    printf("\n\n\n");
+
+
+    // Test B - Mask flat image data
+    printPositiveTestHeader(stdout, "pmFlatField", "Test B - Mask flat image data");
+    PRINT_MATRIX(inImage, F64, "Input image:");
+    CREATE_AND_SET_IMAGE(flatImage2,F64,0.0,3,3)
+    PRINT_MATRIX(flatImage2, F64, "Flat image:");
+    flatReadout->image = flatImage2;
+    if ( !pmFlatField(inReadout, inMaskReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test B - Returned false should be true");
+        return 2;
+    }
+    PRINT_MATRIX(inMaskReadout->image, PS_TYPE_MASK_DATA, "Resulting mask:");
+    PRINT_MATRIX(inImage,F64,"Resulting image:");
+    printFooter(stdout, "pmFlatField", "Test B - Mask flat image data", true);
+    printf("\n\n\n");
+
+
+    // Test C - Mask flat image data starting with non-null mask
+    printPositiveTestHeader(stdout, "pmFlatField", "Test C - Mask flat image data starting with non-null mask");
+    PRINT_MATRIX(inImage, F64, "Input image:");
+    flatImage2->data.F64[0][0] = 3.0;
+    flatImage2->data.F64[0][1] = -3.0;
+    PRINT_MATRIX(flatImage2, F64, "Flat image:");
+    CREATE_AND_SET_IMAGE(mask1,U8,0,3,3);
+    psFree(inReadout->mask);
+    inReadout->mask = mask1;
+    if ( !pmFlatField(inReadout, inMaskReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test C - Returned false should be true");
+        return 3;
+    }
+    PRINT_MATRIX(flatImage2, F64, "Flat image out:");
+    PRINT_MATRIX(inReadout->mask, PS_TYPE_MASK_DATA,"Resulting mask:");
+    PRINT_MATRIX(inImage,F64,"Resulting image:");
+    printFooter(stdout, "pmFlatField", "Test C - Mask flat image data starting with non-null mask", true);
+    printf("\n\n\n");
+
+
+    // Test D - Attempt to use null flat readout
+    printNegativeTestHeader(stdout,"pmFlatField", "Test D - Attempt to use null flat readout",
+                            "Null not allowed for flat readout", 0);
+    if( pmFlatField(inReadout, inMaskReadout, NULL) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test D - Returned true should be false");
+        return 4;
+    }
+    printFooter(stdout, "pmFlatField", "Test D - Attempt to use null flat readout", true);
+    printf("\n\n\n");
+
+
+    // Test E - Attempt to use null input image
+    printNegativeTestHeader(stdout,"pmFlatField", "Test E - Attempt to use null input image",
+                            "Null not allowed for input image", 0);
+    psImage *temp = inReadout->image;
+    inReadout->image = NULL;
+    if ( pmFlatField(inReadout, inMaskReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test E - Returned true should be false" );
+        return 5;
+    }
+    inReadout->image = temp;
+    printFooter(stdout, "pmFlatField", "Test E - Attempt to use null input image", true);
+    printf("\n\n\n");
+
+
+    // Test F - Attempt tp use null flat image
+    printNegativeTestHeader(stdout,"pmFlatField", "Test F - Attempt tp use null flat image",
+                            "Null not allowed for flat image", 0);
+    temp = flatReadout->image;
+    flatReadout->image = NULL;
+    if ( pmFlatField(inReadout, inMaskReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test F - Returned true should be false" );
+        return 6;
+    }
+    flatReadout->image = temp;
+    printFooter(stdout, "pmFlatField", "Test F - Attempt tp use null flat image", true);
+    printf("\n\n\n");
+
+
+    // Test G - Attempt to use input image bigger than flat image
+    printNegativeTestHeader(stdout,"pmFlatField", "Test G - Attempt to use input image bigger than flat image",
+                            "Input image size exceeds that of flat image", 0);
+    CREATE_AND_SET_IMAGE(smallFlat,F64,0.0,2,2);
+    temp = flatReadout->image;
+    flatReadout->image = smallFlat;
+    if ( pmFlatField(inReadout, inMaskReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test G - Returned true should be false");
+        return 7;
+    }
+    flatReadout->image = temp;
+    printFooter(stdout, "pmFlatField", "Test G - Attempt to use input image bigger than flat image", true);
+    printf("\n\n\n");
+
+
+    // Test H - Attempt to use input image mask bigger than flat image
+    printNegativeTestHeader(stdout,"pmFlatField", "Test H - Attempt to use input image mask bigger than flat image",
+                            "Input image mask size exceeds that of flat image", 0);
+    CREATE_AND_SET_IMAGE(largeMask,F64,0.0,5,5);
+    temp = inReadout->mask;
+    inMaskReadout->image = largeMask;
+    if ( pmFlatField(inReadout, inMaskReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test H - Returned true should be false");
+        return 8;
+    }
+    inReadout->mask = temp;
+    printFooter(stdout, "pmFlatField", "Test H - Attempt to use input image mask bigger than flat image", true);
+    printf("\n\n\n");
+
+
+    // Test I - Attempt to use offset greater than input image
+    printNegativeTestHeader(stdout,"pmFlatField", "Test I - Attempt to use offset greater than input image",
+                            "Total offset >= input image size", 0);
+    *(int*)&inReadout->col0 = 50;
+    *(int*)&inReadout->row0 = 50;
+    if ( pmFlatField(inReadout, inMaskReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test I - Returned true should be false");
+        return 9;
+    }
+    *(int*)&inReadout->col0 = 0;
+    *(int*)&inReadout->row0 = 0;
+    printFooter(stdout, "pmFlatField", "Test I - Attempt to use offset greater than input image", true);
+    printf("\n\n\n");
+
+
+    // Test J - Attempt to use complex input image
+    printNegativeTestHeader(stdout,"pmFlatField", "Test J - Attempt to use complex input image",
+                            "Complex types not allowed for input image", 0);
+    *(psElemType* ) & inReadout->image->type.type = PS_TYPE_C64;
+    if ( pmFlatField(inReadout, inMaskReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test J - Returned true should be false");
+        return 10;
+    }
+    *(psElemType* ) & inReadout->image->type.type = PS_TYPE_F64;
+    printFooter(stdout, "pmFlatField", "Test J - Attempt to use complex input image", true);
+    printf("\n\n\n");
+
+
+    // Test K - Attempt to use complex flat image
+    printNegativeTestHeader(stdout,"pmFlatField", "Test K - Attempt to use complex flat image",
+                            "Complex types not allowed for flat image", 0);
+    *(psElemType* ) & flatReadout->image->type.type = PS_TYPE_C64;
+    if ( pmFlatField(inReadout, inMaskReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test K - Returned ture should be false");
+        return 11;
+    }
+    *(psElemType* ) & flatReadout->image->type.type = PS_TYPE_F64;
+    printFooter(stdout, "pmFlatField", "Test K - Attempt to use complex flat image", true);
+    printf("\n\n\n");
+
+
+    // Test L - Attempt to use non-equal input and flat image types
+    printNegativeTestHeader(stdout,"pmFlatField", "Test L - Attempt to use non-equal input and flat image types",
+                            "Input and flat image types differ", 0);
+    *(psElemType* ) & flatReadout->image->type.type = PS_TYPE_F32;
+    if ( pmFlatField(inReadout, inMaskReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test L - Returned true should be false");
+        return 12;
+    }
+    *(psElemType* ) & flatReadout->image->type.type = PS_TYPE_F64;
+    printFooter(stdout, "pmFlatField", "Test L - Attempt to use non-equal input and flat image types", true);
+    printf("\n\n\n");
+
+
+    // Test M - Attempt to use non-mask type mask image
+    printNegativeTestHeader(stdout,"pmFlatField", "Test M - Attempt to use non-mask type mask image",
+                            "Mask must be PS_TYPE_MASK type", 0);
+    *(psElemType* ) & inReadout->mask->type.type = PS_TYPE_F32;
+    if ( pmFlatField(inReadout, inMaskReadout, flatReadout) ) {
+        psError(PS_ERR_UNKNOWN,true,"Test M - Returned true should be false");
+        return 13;
+    }
+    *(psElemType* ) & inReadout->mask->type.type = PS_TYPE_MASK;
+    printFooter(stdout, "pmFlatField", "Test M - Attempt to use non-mask type mask image", true);
+    printf("\n\n\n");
+
+
+    // Free memory
+    psFree(inMask);
+    psFree(largeMask);
+    inMaskReadout->image = NULL;
+    psFree(inMaskReadout);
+    psFree(inReadout);
+    psFree(flatReadout);
+    //psFree(inImage);
+    psFree(flatImage1);
+    //psFree(flatImage1);
+    psFree(smallFlat);
+
+    return 0;
+}
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/detrend/tst_pmMaskBadPixels.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/detrend/tst_pmMaskBadPixels.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/detrend/tst_pmMaskBadPixels.c	(revision 22115)
@@ -0,0 +1,303 @@
+/** @file tst_pmMaskBadPixels.c
+ *
+ *  @brief Contains the tests for pmMaskBadPixels:
+ *
+ *    Test A - Create mask based on maskVal argument
+ *    Test B - Create mask based on saturation argument
+ *    Test C - Create mask based on growVal and grow arguments
+ *    Test D - Auto Create mask based on maskVal argument
+ *    Test E - Attempt to use null mask
+ *    Test F - Attempt tp use null input image
+ *    Test G - Attempt to use input image bigger than mask
+ *    Test H - Attempt to use input image mask bigger than mask
+ *    Test I - Attempt to use offset greater than input image
+ *    Test J - Attempt to use complex input image
+ *    Test K - Attempt to use non-mask type mask image
+ *
+ *  @author Ross Harman, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:42:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+
+#include "psTest.h"
+#include "pslib.h"
+#include "pmMaskBadPixels.h"
+
+
+#define PRINT_MATRIX(IMAGE,TYPE,STRING)                                                                      \
+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]);                                                       \
+        } else {                                                                                             \
+            printf("%f", (double)IMAGE->data.TYPE[i][j]);                                                    \
+        }                                                                                                    \
+    }                                                                                                        \
+    printf("\n");                                                                                            \
+}                                                                                                            \
+printf("\n");
+
+
+#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;                                                                       \
+    }                                                                                                        \
+}
+
+
+static int testMaskBadPixels1(void);
+static int testMaskBadPixels2(void);
+static int testMaskBadPixels3(void);
+static int testMaskBadPixels4(void);
+static int testMaskBadPixels5(void);
+static int testMaskBadPixels6(void);
+static int testMaskBadPixels7(void);
+static int testMaskBadPixels8(void);
+static int testMaskBadPixels9(void);
+static int testMaskBadPixels10(void);
+static int testMaskBadPixels11(void);
+
+
+testDescription tests[] = {
+                              {testMaskBadPixels1, 885, "pmMaskBadPixels - Create mask based on maskVal argument", 0, false},
+                              {testMaskBadPixels2, 885, "pmMaskBadPixels - Create mask based on saturation argument", 0, false},
+                              {testMaskBadPixels3, 885, "pmMaskBadPixels - Create mask based on growVal and grow arguments", 0, false},
+                              {testMaskBadPixels4, 885, "pmMaskBadPixels - Auto create mask based on maskVal argument", 0, false},
+                              {testMaskBadPixels5, 885, "pmMaskBadPixels - Attempt to use null mask", 0, false},
+                              {testMaskBadPixels6, 885, "pmMaskBadPixels - Attempt tp use null input image", 0, false},
+                              {testMaskBadPixels7, 885, "pmMaskBadPixels - Attempt to use input image bigger than mask", 0, false},
+                              {testMaskBadPixels8, 885, "pmMaskBadPixels - Attempt to use input image mask bigger than mask", 0, false},
+                              {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},
+                              {NULL}
+                          };
+
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    return !runTestSuite(stderr, "Test Point Driver", tests, argc, argv);
+}
+
+
+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);
+    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);
+
+    return 0;
+}
+
+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);
+
+    return 0;
+}
+
+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);
+
+    return 0;
+}
+
+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);
+
+    return 0;
+}
+
+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);
+
+    return 0;
+}
+
+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);
+
+    return 0;
+}
+
+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);
+
+    return 0;
+}
+
+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);
+
+    return 0;
+}
+
+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);
+
+    return 0;
+}
+
+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;
+}
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/detrend/tst_pmNonLinear.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/detrend/tst_pmNonLinear.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/detrend/tst_pmNonLinear.c	(revision 22115)
@@ -0,0 +1,511 @@
+/** @file tst_pmNonLinear.c
+ *
+ *  @brief Contains the tests for pmNonLinear.c:
+ *
+ * test00: This code will create a simple polynomial, and call
+ * pmNonLinearityPolynomial() for a variety of image sizes [(1, 1), (1,
+ * N), (N, 1), (N, N)].  
+ *
+ * test01: This code will create simple table lookup vectors, and call
+ * pmNonLinearityPolynomial() for a variety of image sizes [(1, 1), (1,
+ * N), (N, 1), (N, N)].  
+ *
+ * test02, test03: This code tests the functions with various unallowable
+ * input parameters (NULLS) and incorrect vector sizes.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:42:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include "psTest.h"
+#include "pslib.h"
+#include "pmNonLinear.h"
+static int test00(void);
+static int test01(void);
+static int test02(void);
+static int test03(void);
+testDescription tests[] = {
+                              {test00, 000, "pmNonLinearityPolynomial", true, false},
+                              {test01, 000, "pmNonLinearityLookup", true, false},
+                              {test02, 000, "pmNonLinearityPolynomial(): error/warning conditions", true, false},
+                              {test03, 000, "pmNonLinearityLookup(): error/warning conditions", true, false},
+                              {NULL}
+                          };
+
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    return !runTestSuite(stderr, "Test Point Driver", tests, argc, argv);
+}
+
+#define NUM_ROWS 8
+#define NUM_COLS 8
+int doNonLinearityPolynomialTest(int numCols, int numRows)
+{
+    int i;
+    int j;
+    float actual;
+    float expect;
+    int testStatus = true;
+    psImage *myImage = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    pmReadout *myReadout = pmReadoutAlloc(NULL);
+    myReadout->image = myImage;
+    psPolynomial1D *myPoly = psPolynomial1DAlloc(2, PS_POLYNOMIAL_ORD);
+    myPoly->coeff[1] = 1.0;
+
+    printPositiveTestHeader(stdout, "pmNonLinear", "doNonLinearityPolynomialTest");
+
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            myReadout->image->data.F32[i][j] = (float) (i + j);
+        }
+    }
+
+    myReadout = pmNonLinearityPolynomial(myReadout, myPoly);
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            expect = psPolynomial1DEval(myPoly, (float) (i + j));
+            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 = false;
+            }
+        }
+    }
+
+
+    psFree(myReadout);
+    psFree(myPoly);
+    printFooter(stdout, "pmNonLinear", "doNonLinearityPolynomialTest", true);
+    return(testStatus);
+}
+
+int test00( void )
+{
+    int testStatus = 0;
+
+    testStatus |= doNonLinearityPolynomialTest(1, 1);
+    testStatus |= doNonLinearityPolynomialTest(NUM_COLS, 1);
+    testStatus |= doNonLinearityPolynomialTest(1, NUM_ROWS);
+    testStatus |= doNonLinearityPolynomialTest(NUM_COLS, NUM_ROWS);
+
+    return(testStatus);
+}
+
+int doNonLinearityLookupTest(int numCols, int numRows)
+{
+    int i;
+    int j;
+    float actual;
+    float expect;
+    int testStatus = true;
+    int tableSize = PS_MAX(numCols, numRows)*2;
+    psImage *myImage = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    pmReadout *myReadout = pmReadoutAlloc(NULL);
+    myReadout->image = myImage;
+    psVector *in = psVectorAlloc(tableSize, PS_TYPE_F32);
+    psVector *out = psVectorAlloc(tableSize, PS_TYPE_F32);
+
+    printPositiveTestHeader(stdout, "pmNonLinear", "doNonLinearityLookupTest");
+    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<tableSize;i++) {
+        in->data.F32[i] = (float) i;
+        out->data.F32[i] = (float) (2 * i);
+    }
+
+    myReadout = pmNonLinearityLookup(myReadout, in, out);
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            expect = (float) (2 * (i + j));
+            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 = false;
+            }
+        }
+    }
+
+
+    psFree(myReadout);
+    psFree(in);
+    psFree(out);
+    printFooter(stdout, "pmNonLinear", "doNonLinearityLookupTest", true);
+    return(testStatus);
+}
+
+int test01( void )
+{
+    int testStatus = 0;
+
+    testStatus |= doNonLinearityLookupTest(1, 1);
+    testStatus |= doNonLinearityLookupTest(NUM_COLS, 1);
+    testStatus |= doNonLinearityLookupTest(1, NUM_ROWS);
+    testStatus |= doNonLinearityLookupTest (NUM_COLS, NUM_ROWS);
+
+    return(testStatus);
+}
+
+int test02( void )
+{
+    int i;
+    int j;
+    int testStatus = true;
+    psImage *myImage = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+    pmReadout *myReadout = pmReadoutAlloc(NULL);
+    pmReadout *rc = NULL;
+    myReadout->image = myImage;
+    psPolynomial1D *myPoly = psPolynomial1DAlloc(2, PS_POLYNOMIAL_ORD);
+    myPoly->coeff[1] = 1.0;
+
+    printPositiveTestHeader(stdout, "pmNonLinear", "Testing bad input parameter conditions.");
+    for (i=0;i<NUM_ROWS;i++) {
+        for (j=0;j<NUM_COLS;j++) {
+            myReadout->image->data.F32[i][j] = (float) (i + j);
+        }
+    }
+
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityPolynomial() with NULL input readout.  Should generate error, return NULL.\n");
+    rc = pmNonLinearityPolynomial(NULL, myPoly);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmNonLinearityPolynomial() returned a non-NULL pmReadout\n");
+        testStatus = false;
+    }
+
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityPolynomial() with NULL input readout->image.  Should generate error, return NULL.\n");
+    psImage *tmpImage = myReadout->image;
+    myReadout->image = NULL;
+    rc = pmNonLinearityPolynomial(myReadout, myPoly);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmNonLinearityPolynomial() returned a non-NULL pmReadout\n");
+        testStatus = false;
+    }
+    myReadout->image = tmpImage;
+
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityPolynomial() with NULL polynomial.  Should generate error, return NULL.\n");
+    rc = pmNonLinearityPolynomial(myReadout, NULL);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmNonLinearityPolynomial() returned a non-NULL pmReadout\n");
+        testStatus = false;
+    }
+
+    psFree(myReadout);
+    psFree(myPoly);
+    return(testStatus);
+}
+
+
+int test03Init(pmReadout *myReadout)
+{
+    for (psS32 i=0;i<NUM_ROWS;i++) {
+        for (psS32 j=0;j<NUM_COLS;j++) {
+            myReadout->image->data.F32[i][j] = (float) (i + j);
+        }
+    }
+    return(0);
+}
+
+int test03()
+{
+    int i;
+    int j;
+    int testStatus = true;
+    int tableSize = PS_MAX(NUM_COLS, NUM_ROWS)*3;
+    psImage *myImage = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+    pmReadout *myReadout = pmReadoutAlloc(NULL);
+    pmReadout *rc = NULL;
+    myReadout->image = myImage;
+    psVector *in = psVectorAlloc(tableSize, PS_TYPE_F32);
+    psVector *inOne = psVectorAlloc(1, PS_TYPE_F32);
+    psVector *inSmall = psVectorAlloc(tableSize-1, PS_TYPE_F32);
+    psVector *inBig = psVectorAlloc(tableSize+1, PS_TYPE_F32);
+    psVector *out = psVectorAlloc(tableSize, PS_TYPE_F32);
+    psVector *outOne = psVectorAlloc(1, PS_TYPE_F32);
+    psVector *outSmall = psVectorAlloc(tableSize-1, PS_TYPE_F32);
+    psVector *outBig = psVectorAlloc(tableSize+1, PS_TYPE_F32);
+
+    test03Init(myReadout);
+    for (i=0;i<tableSize;i++) {
+        in->data.F32[i] = (float) i;
+        out->data.F32[i] = (float) (2 * i);
+        inBig->data.F32[i] = (float) i;
+        outBig->data.F32[i] = (float) (2 * i);
+        if (i < tableSize-1) {
+            inSmall->data.F32[i] = (float) i;
+            outSmall->data.F32[i] = (float) (2 * i);
+        }
+    }
+    inBig->data.F32[tableSize] = (float) tableSize;
+    outBig->data.F32[tableSize] = (float) (2 * tableSize);
+    inOne->data.F32[0] = 0.0;
+    outOne->data.F32[0] = 0.0;
+
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityLookup() with NULL input pmReadout.  Should generate error, return NULL.\n");
+    rc = pmNonLinearityLookup(NULL, in, out);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmNonLinearityPolynomial() returned a non-NULL pmReadout\n");
+        testStatus = false;
+    }
+
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityLookup() with NULL input pmReadout->image.  Should generate error, return NULL.\n");
+    psImage *tmpImage = myReadout->image;
+    myReadout->image = NULL;
+    rc = pmNonLinearityLookup(myReadout, in, out);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmNonLinearityPolynomial() returned a non-NULL pmReadout\n");
+        testStatus = false;
+    }
+    myReadout->image = tmpImage;
+
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityLookup() with NULL inFlux psVector.  Should generate error, return NULL.\n");
+    rc = pmNonLinearityLookup(myReadout, NULL, out);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmNonLinearityPolynomial() returned a non-NULL pmReadout\n");
+        testStatus = false;
+    }
+
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityLookup() with NULL outFlux psVector.  Should generate error, return NULL.\n");
+    rc = pmNonLinearityLookup(myReadout, in, NULL);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmNonLinearityPolynomial() returned a non-NULL pmReadout\n");
+        testStatus = false;
+    }
+
+    test03Init(myReadout);
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityLookup() with size difference in inFlux/outFLux psVectors.  Should generate warning.\n");
+    rc = pmNonLinearityLookup(myReadout, in, outBig);
+    if (rc == NULL) {
+        printf("TEST ERROR: pmNonLinearityPolynomial() returned a NULL pmReadout\n");
+        testStatus = false;
+    }
+    for (i=0;i<NUM_ROWS;i++) {
+        for (j=0;j<NUM_COLS;j++) {
+            psF32 expect = (float) (2 * (i + j));
+            psF32 actual = rc->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 = false;
+            }
+        }
+    }
+
+
+    test03Init(myReadout);
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityLookup() with size difference in inFlux/outFLux psVectors.  Should generate warning.\n");
+    rc = pmNonLinearityLookup(myReadout, in, outSmall);
+    if (rc == NULL) {
+        printf("TEST ERROR: pmNonLinearityPolynomial() returned a NULL pmReadout\n");
+        testStatus = false;
+    }
+    for (i=0;i<NUM_ROWS;i++) {
+        for (j=0;j<NUM_COLS;j++) {
+            psF32 expect = (float) (2 * (i + j));
+            psF32 actual = rc->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 = false;
+            }
+        }
+    }
+
+    test03Init(myReadout);
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityLookup() with size difference in inFlux/outFLux psVectors.  Should generate warning.\n");
+    rc = pmNonLinearityLookup(myReadout, inSmall, out);
+    if (rc == NULL) {
+        printf("TEST ERROR: pmNonLinearityPolynomial() returned a NULL pmReadout\n");
+        testStatus = false;
+    }
+    for (i=0;i<NUM_ROWS;i++) {
+        for (j=0;j<NUM_COLS;j++) {
+            psF32 expect = (float) (2 * (i + j));
+            psF32 actual = rc->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 = false;
+            }
+        }
+    }
+
+    test03Init(myReadout);
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityLookup() with size difference in inFlux/outFLux psVectors.  Should generate warning.\n");
+    rc = pmNonLinearityLookup(myReadout, inBig, out);
+    if (rc == NULL) {
+        printf("TEST ERROR: pmNonLinearityPolynomial() returned a NULL pmReadout\n");
+        testStatus = false;
+    }
+    for (i=0;i<NUM_ROWS;i++) {
+        for (j=0;j<NUM_COLS;j++) {
+            psF32 expect = (float) (2 * (i + j));
+            psF32 actual = rc->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 = false;
+            }
+        }
+    }
+
+    test03Init(myReadout);
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityLookup() with size difference in inFlux/outFLux psVectors.  Should generate warning.\n");
+    rc = pmNonLinearityLookup(myReadout, inSmall, outBig);
+    if (rc == NULL) {
+        printf("TEST ERROR: pmNonLinearityPolynomial() returned a NULL pmReadout\n");
+        testStatus = false;
+    }
+    for (i=0;i<NUM_ROWS;i++) {
+        for (j=0;j<NUM_COLS;j++) {
+            psF32 expect = (float) (2 * (i + j));
+            psF32 actual = rc->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 = false;
+            }
+        }
+    }
+
+    test03Init(myReadout);
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityLookup() with size difference in inFlux/outFLux psVectors.  Should generate warning.\n");
+    rc = pmNonLinearityLookup(myReadout, inBig, outSmall);
+    if (rc == NULL) {
+        printf("TEST ERROR: pmNonLinearityPolynomial() returned a NULL pmReadout\n");
+        testStatus = false;
+    }
+    for (i=0;i<NUM_ROWS;i++) {
+        for (j=0;j<NUM_COLS;j++) {
+            psF32 expect = (float) (2 * (i + j));
+            psF32 actual = rc->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 = false;
+            }
+        }
+    }
+
+    test03Init(myReadout);
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityLookup() with inFlux psVector size 1.  Should generate error, return original pmReadout.\n");
+    rc = pmNonLinearityLookup(myReadout, inOne, out);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmNonLinearityPolynomial() did not return the original pmReadout\n");
+        testStatus = false;
+    }
+    for (i=0;i<NUM_ROWS;i++) {
+        for (j=0;j<NUM_COLS;j++) {
+            psF32 expect = (float) ((i + j));
+            psF32 actual = rc->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 = false;
+            }
+        }
+    }
+
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityLookup() with one pixels outside inFlux range.  Should generate warnings.\n");
+    test03Init(myReadout);
+    myReadout->image->data.F32[0][0] = -1;
+    rc = pmNonLinearityLookup(myReadout, in, out);
+    if (rc == NULL) {
+        printf("TEST ERROR: pmNonLinearityPolynomial() returned a NULL pmReadout\n");
+        testStatus = false;
+    }
+    for (i=0;i<NUM_ROWS;i++) {
+        for (j=0;j<NUM_COLS;j++) {
+            psF32 expect = (float) (2 * (i + j));
+            psF32 actual = rc->image->data.F32[i][j];
+            if(i==0 && j==0) {
+                if(actual != 0.0) {
+                    printf("TEST ERROR: image[%d][%d] is %f, should be %f\n",i,j,actual,0.0);
+                }
+            } else {
+                if (FLT_EPSILON < fabs(expect - actual)) {
+                    printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
+                    testStatus = false;
+                }
+            }
+        }
+    }
+
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityLookup() with one pixels outside inFlux range.  Should generate warnings.\n");
+    test03Init(myReadout);
+    myReadout->image->data.F32[NUM_ROWS-1][NUM_COLS-1] = 100;
+    rc = pmNonLinearityLookup(myReadout, in, out);
+    if (rc == NULL) {
+        printf("TEST ERROR: pmNonLinearityPolynomial() returned a NULL pmReadout\n");
+        testStatus = false;
+    }
+    for (i=0;i<NUM_ROWS;i++) {
+        for (j=0;j<NUM_COLS;j++) {
+            psF32 expect = (float) (2 * (i + j));
+            psF32 actual = rc->image->data.F32[i][j];
+            if(i==(NUM_ROWS-1) && j==(NUM_COLS-1)) {
+                if(actual != (tableSize-1)*2) {
+                    printf("TEST ERROR: image[%d][%d] is %f, should be %f\n",i,j,actual,(tableSize-1)*2.0);
+                }
+            } else {
+                if (FLT_EPSILON < fabs(expect - actual)) {
+                    printf("TEST ERROR: image[%d][%d] is %f, should be %f\n", i, j, actual, expect);
+                    testStatus = false;
+                }
+            }
+        }
+    }
+
+    test03Init(myReadout);
+    printf("------------------------------------------------------------\n");
+    printf("Calling pmNonLinearityLookup() with image values not in vector.\n");
+    myReadout->image->data.F32[0][0] = 0.5;
+    rc = pmNonLinearityLookup(myReadout, in, out);
+    if (rc == NULL) {
+        printf("TEST ERROR: pmNonLinearityPolynomial() returned a NULL pmReadout\n");
+        testStatus = false;
+    }
+    for (i=0;i<NUM_ROWS;i++) {
+        for (j=0;j<NUM_COLS;j++) {
+            psF32 expect = (float) (2 * (i + j));
+            if(i==0 && j==0) {
+                expect = 1.0;
+            }
+            psF32 actual = rc->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 = false;
+            }
+        }
+    }
+
+    psFree(myReadout);
+    psFree(in);
+    psFree(inOne);
+    psFree(inSmall);
+    psFree(inBig);
+    psFree(out);
+    psFree(outOne);
+    psFree(outSmall);
+    psFree(outBig);
+
+    printFooter(stdout, "pmNonLinear", "Testing bad input parameter conditions.", true);
+    return(testStatus);
+}
Index: /tags/ipp-1-X/rel8_2/psModules/test/detrend/verified/tst_pmFlatField.stderr
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/detrend/verified/tst_pmFlatField.stderr	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/detrend/verified/tst_pmFlatField.stderr	(revision 22115)
@@ -0,0 +1,29 @@
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmFlatField.c                                          *
+*            TestPoint: Test Point Driver{pmFlatField}                             *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<HOST>|E|pmFlatField (FILE:LINENO)
+    Null not allowed for flat readout.
+<HOST>|E|pmFlatField (FILE:LINENO)
+    Null not allowed for input image.
+<HOST>|E|pmFlatField (FILE:LINENO)
+    Null not allowed for flat image.
+<HOST>|E|pmFlatField (FILE:LINENO)
+    Input image size exceeds that of flat image: (3, 3) vs (2, 2)
+<HOST>|E|pmFlatField (FILE:LINENO)
+    Input image mask size exceeds that of flat image: (5, 5) vs (3, 3)
+<HOST>|E|pmFlatField (FILE:LINENO)
+    Input image mask size exceeds that of flat image: (5, 5) vs (3, 3)
+<HOST>|E|pmFlatField (FILE:LINENO)
+    Input image mask size exceeds that of flat image: (5, 5) vs (3, 3)
+<HOST>|E|pmFlatField (FILE:LINENO)
+    Input image mask size exceeds that of flat image: (5, 5) vs (3, 3)
+<HOST>|E|pmFlatField (FILE:LINENO)
+    Input image mask size exceeds that of flat image: (5, 5) vs (3, 3)
+<HOST>|E|pmFlatField (FILE:LINENO)
+    Input image mask size exceeds that of flat image: (5, 5) vs (3, 3)
+
+---> TESTPOINT PASSED (Test Point Driver{pmFlatField} | tst_pmFlatField.c)
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/detrend/verified/tst_pmFlatField.stdout
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/detrend/verified/tst_pmFlatField.stdout	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/detrend/verified/tst_pmFlatField.stdout	(revision 22115)
@@ -0,0 +1,241 @@
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmFlatField.c                                          *
+*            TestPoint: pmFlatField{Test A - Divide input image by flat image}     *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+Input image:
+6.000000 6.000000 6.000000 
+6.000000 6.000000 6.000000 
+6.000000 6.000000 6.000000 
+
+Input mask:
+0 0 0 
+0 0 0 
+0 0 0 
+
+Flat image:
+2.000000 2.000000 2.000000 
+2.000000 2.000000 2.000000 
+2.000000 2.000000 2.000000 
+
+Resulting image:
+3.000000 3.000000 3.000000 
+3.000000 3.000000 3.000000 
+3.000000 3.000000 3.000000 
+
+
+---> TESTPOINT PASSED (pmFlatField{Test A - Divide input image by flat image} | tst_pmFlatField.c)
+
+
+
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmFlatField.c                                          *
+*            TestPoint: pmFlatField{Test B - Mask flat image data}                 *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+Input image:
+3.000000 3.000000 3.000000 
+3.000000 3.000000 3.000000 
+3.000000 3.000000 3.000000 
+
+Flat image:
+0.000000 0.000000 0.000000 
+0.000000 0.000000 0.000000 
+0.000000 0.000000 0.000000 
+
+Resulting mask:
+8 8 8 
+8 8 8 
+8 8 8 
+
+Resulting image:
+3.000000 3.000000 3.000000 
+3.000000 3.000000 3.000000 
+3.000000 3.000000 3.000000 
+
+
+---> TESTPOINT PASSED (pmFlatField{Test B - Mask flat image data} | tst_pmFlatField.c)
+
+
+
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmFlatField.c                                          *
+*            TestPoint: pmFlatField{Test C - Mask flat image data starting with non-null mask} *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+Input image:
+3.000000 3.000000 3.000000 
+3.000000 3.000000 3.000000 
+3.000000 3.000000 3.000000 
+
+Flat image:
+0.000000 0.000000 0.000000 
+0.000000 0.000000 0.000000 
+3.000000 -3.000000 0.000000 
+
+Flat image out:
+0.000000 0.000000 0.000000 
+0.000000 0.000000 0.000000 
+3.000000 0.000000 0.000000 
+
+Resulting mask:
+0 0 0 
+0 0 0 
+0 0 0 
+
+Resulting image:
+3.000000 3.000000 3.000000 
+3.000000 3.000000 3.000000 
+3.000000 3.000000 3.000000 
+
+
+---> TESTPOINT PASSED (pmFlatField{Test C - Mask flat image data starting with non-null mask} | tst_pmFlatField.c)
+
+
+
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmFlatField.c                                          *
+*            TestPoint: pmFlatField{Test D - Attempt to use null flat readout}     *
+*             TestType: Negative                                                   *
+*    ExpectedErrorText: Null not allowed for flat readout                          *
+*  ExpectedStatusValue: 0                                                          *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmFlatField{Test D - Attempt to use null flat readout} | tst_pmFlatField.c)
+
+
+
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmFlatField.c                                          *
+*            TestPoint: pmFlatField{Test E - Attempt to use null input image}      *
+*             TestType: Negative                                                   *
+*    ExpectedErrorText: Null not allowed for input image                           *
+*  ExpectedStatusValue: 0                                                          *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmFlatField{Test E - Attempt to use null input image} | tst_pmFlatField.c)
+
+
+
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmFlatField.c                                          *
+*            TestPoint: pmFlatField{Test F - Attempt tp use null flat image}       *
+*             TestType: Negative                                                   *
+*    ExpectedErrorText: Null not allowed for flat image                            *
+*  ExpectedStatusValue: 0                                                          *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmFlatField{Test F - Attempt tp use null flat image} | tst_pmFlatField.c)
+
+
+
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmFlatField.c                                          *
+*            TestPoint: pmFlatField{Test G - Attempt to use input image bigger than flat image} *
+*             TestType: Negative                                                   *
+*    ExpectedErrorText: Input image size exceeds that of flat image                *
+*  ExpectedStatusValue: 0                                                          *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmFlatField{Test G - Attempt to use input image bigger than flat image} | tst_pmFlatField.c)
+
+
+
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmFlatField.c                                          *
+*            TestPoint: pmFlatField{Test H - Attempt to use input image mask bigger than flat image} *
+*             TestType: Negative                                                   *
+*    ExpectedErrorText: Input image mask size exceeds that of flat image           *
+*  ExpectedStatusValue: 0                                                          *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmFlatField{Test H - Attempt to use input image mask bigger than flat image} | tst_pmFlatField.c)
+
+
+
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmFlatField.c                                          *
+*            TestPoint: pmFlatField{Test I - Attempt to use offset greater than input image} *
+*             TestType: Negative                                                   *
+*    ExpectedErrorText: Total offset >= input image size                           *
+*  ExpectedStatusValue: 0                                                          *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmFlatField{Test I - Attempt to use offset greater than input image} | tst_pmFlatField.c)
+
+
+
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmFlatField.c                                          *
+*            TestPoint: pmFlatField{Test J - Attempt to use complex input image}   *
+*             TestType: Negative                                                   *
+*    ExpectedErrorText: Complex types not allowed for input image                  *
+*  ExpectedStatusValue: 0                                                          *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmFlatField{Test J - Attempt to use complex input image} | tst_pmFlatField.c)
+
+
+
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmFlatField.c                                          *
+*            TestPoint: pmFlatField{Test K - Attempt to use complex flat image}    *
+*             TestType: Negative                                                   *
+*    ExpectedErrorText: Complex types not allowed for flat image                   *
+*  ExpectedStatusValue: 0                                                          *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmFlatField{Test K - Attempt to use complex flat image} | tst_pmFlatField.c)
+
+
+
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmFlatField.c                                          *
+*            TestPoint: pmFlatField{Test L - Attempt to use non-equal input and flat image types} *
+*             TestType: Negative                                                   *
+*    ExpectedErrorText: Input and flat image types differ                          *
+*  ExpectedStatusValue: 0                                                          *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmFlatField{Test L - Attempt to use non-equal input and flat image types} | tst_pmFlatField.c)
+
+
+
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmFlatField.c                                          *
+*            TestPoint: pmFlatField{Test M - Attempt to use non-mask type mask image} *
+*             TestType: Negative                                                   *
+*    ExpectedErrorText: Mask must be PS_TYPE_MASK type                             *
+*  ExpectedStatusValue: 0                                                          *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmFlatField{Test M - Attempt to use non-mask type mask image} | tst_pmFlatField.c)
+
+
+
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/detrend/verified/tst_pmMaskBadPixels.stderr
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/detrend/verified/tst_pmMaskBadPixels.stderr	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/detrend/verified/tst_pmMaskBadPixels.stderr	(revision 22115)
@@ -0,0 +1,113 @@
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmMaskBadPixels.c                                      *
+*            TestPoint: Test Point Driver{pmMaskBadPixels - Create mask based on maskVal argument} *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (Test Point Driver{pmMaskBadPixels - Create mask based on maskVal argument} | tst_pmMaskBadPixels.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmMaskBadPixels.c                                      *
+*            TestPoint: Test Point Driver{pmMaskBadPixels - Create mask based on saturation argument} *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (Test Point Driver{pmMaskBadPixels - Create mask based on saturation argument} | tst_pmMaskBadPixels.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmMaskBadPixels.c                                      *
+*            TestPoint: Test Point Driver{pmMaskBadPixels - Create mask based on growVal and grow argum *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (Test Point Driver{pmMaskBadPixels - Create mask based on growVal and grow arguments} | tst_pmMaskBadPixels.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmMaskBadPixels.c                                      *
+*            TestPoint: Test Point Driver{pmMaskBadPixels - Auto create mask based on maskVal argument} *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (Test Point Driver{pmMaskBadPixels - Auto create mask based on maskVal argument} | tst_pmMaskBadPixels.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmMaskBadPixels.c                                      *
+*            TestPoint: Test Point Driver{pmMaskBadPixels - Attempt to use null mask} *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<HOST>|E|pmMaskBadPixels (FILE:LINENO)
+    Null not allowed for mask image.
+
+---> TESTPOINT PASSED (Test Point Driver{pmMaskBadPixels - Attempt to use null mask} | tst_pmMaskBadPixels.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmMaskBadPixels.c                                      *
+*            TestPoint: Test Point Driver{pmMaskBadPixels - Attempt tp use null input image} *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<HOST>|E|pmMaskBadPixels (FILE:LINENO)
+    Null not allowed for input image.
+
+---> TESTPOINT PASSED (Test Point Driver{pmMaskBadPixels - Attempt tp use null input image} | tst_pmMaskBadPixels.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmMaskBadPixels.c                                      *
+*            TestPoint: Test Point Driver{pmMaskBadPixels - Attempt to use input image bigger than mask *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<HOST>|E|pmMaskBadPixels (FILE:LINENO)
+    Input image size exceeds that of mask image: (60, 60) vs (50, 50)
+
+---> TESTPOINT PASSED (Test Point Driver{pmMaskBadPixels - Attempt to use input image bigger than mask} | tst_pmMaskBadPixels.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmMaskBadPixels.c                                      *
+*            TestPoint: Test Point Driver{pmMaskBadPixels - Attempt to use input image mask bigger than *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<HOST>|E|pmMaskBadPixels (FILE:LINENO)
+    Input image mask size exceeds that of mask image: (60, 60) vs (50, 50)
+
+---> TESTPOINT PASSED (Test Point Driver{pmMaskBadPixels - Attempt to use input image mask bigger than mask} | tst_pmMaskBadPixels.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmMaskBadPixels.c                                      *
+*            TestPoint: Test Point Driver{pmMaskBadPixels - Attempt to use offset greater than input im *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<HOST>|E|pmMaskBadPixels (FILE:LINENO)
+    Total offset >= mask image: (150, 150) vs (50, 50)
+
+---> TESTPOINT PASSED (Test Point Driver{pmMaskBadPixels - Attempt to use offset greater than input image} | tst_pmMaskBadPixels.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmMaskBadPixels.c                                      *
+*            TestPoint: Test Point Driver{pmMaskBadPixels - Attempt to use complex input image} *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<HOST>|E|pmMaskBadPixels (FILE:LINENO)
+    Complex types not allowed for input image. Type: 2064
+
+---> TESTPOINT PASSED (Test Point Driver{pmMaskBadPixels - Attempt to use complex input image} | tst_pmMaskBadPixels.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmMaskBadPixels.c                                      *
+*            TestPoint: Test Point Driver{pmMaskBadPixels - Attempt to use non-mask type mask image} *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<HOST>|E|pmMaskBadPixels (FILE:LINENO)
+    Mask must be PS_TYPE_MASK type. Type: 1032
+
+---> TESTPOINT PASSED (Test Point Driver{pmMaskBadPixels - Attempt to use non-mask type mask image} | tst_pmMaskBadPixels.c)
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/detrend/verified/tst_pmMaskBadPixels.stdout
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/detrend/verified/tst_pmMaskBadPixels.stdout	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/detrend/verified/tst_pmMaskBadPixels.stdout	(revision 22115)
@@ -0,0 +1,416 @@
+Data mask:
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000010000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+
+Resulting mask:
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000010000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+
+Data mask:
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+
+Resulting mask:
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+44444444444444444444444444444444444444444444444444
+
+Data mask:
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000010000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00010000000000000000000000000000000000000000001000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+
+Resulting mask:
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000011111110000000000000000000000
+00000000000000000001111111111100000000000000000000
+00000000000000000011111111111110000000000000000000
+00000000000000000111111111111111000000000000000000
+00000000000000001111111111111111100000000000000000
+00000000000000011111111111111111110000000000000000
+00000000000000011111111111111111110000000000000000
+00000000000000111111111111111111111000000000000000
+00000000000000111111111111111111111000000000000000
+00000000000000111111111111111111111000000000000000
+00000000000000111111111111111111111000000000000000
+00000000000000111111111111111111111000000000000000
+00000000000000111111111111111111111000000000000000
+00000000000000111111111111111111111000000000000000
+00000000000000011111111111111111110000000000000000
+00000000000000011111111111111111110000000000000000
+00000000000000001111111111111111100000000000000000
+00000000000000000111111111111111000000000000000000
+00000000000000000011111111111110000000000000000000
+00000000000000000001111111111100000000000000000000
+11111110000000000000011111110000000000000001111111
+11111111100000000000000000000000000000000111111111
+11111111110000000000000000000000000000001111111111
+11111111111000000000000000000000000000011111111111
+11111111111100000000000000000000000000111111111111
+11111111111110000000000000000000000001111111111111
+11111111111110000000000000000000000001111111111111
+11111111111111000000000000000000000011111111111111
+11111111111111000000000000000000000011111111111111
+11111111111111000000000000000000000011111111111111
+11111111111111000000000000000000000011111111111111
+11111111111111000000000000000000000011111111111111
+11111111111111000000000000000000000011111111111111
+11111111111111000000000000000000000011111111111111
+11111111111110000000000000000000000001111111111111
+
+Data mask:
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000010000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+
+Resulting mask:
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000011111110000000000000000000000
+00000000000000000001111111111100000000000000000000
+00000000000000000011111111111110000000000000000000
+00000000000000000111111111111111000000000000000000
+00000000000000001111111111111111100000000000000000
+00000000000000011111111111111111110000000000000000
+00000000000000011111111111111111110000000000000000
+00000000000000111111111111111111111000000000000000
+00000000000000111111111111111111111000000000000000
+00000000000000111111111111111111111000000000000000
+00000000000000111111111111111111111000000000000000
+00000000000000111111111111111111111000000000000000
+00000000000000111111111111111111111000000000000000
+00000000000000111111111111111111111000000000000000
+00000000000000011111111111111111110000000000000000
+00000000000000011111111111111111110000000000000000
+00000000000000001111111111111111100000000000000000
+00000000000000000111111111111111000000000000000000
+00000000000000000011111111111110000000000000000000
+00000000000000000001111111111100000000000000000000
+00000000000000000000011111110000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+00000000000000000000000000000000000000000000000000
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/detrend/verified/tst_pmNonLinear.stderr
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/detrend/verified/tst_pmNonLinear.stderr	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/detrend/verified/tst_pmNonLinear.stderr	(revision 22115)
@@ -0,0 +1,72 @@
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmNonLinear.c                                          *
+*            TestPoint: Test Point Driver{pmNonLinearityPolynomial}                *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (Test Point Driver{pmNonLinearityPolynomial} | tst_pmNonLinear.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmNonLinear.c                                          *
+*            TestPoint: Test Point Driver{pmNonLinearityLookup}                    *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (Test Point Driver{pmNonLinearityLookup} | tst_pmNonLinear.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmNonLinear.c                                          *
+*            TestPoint: Test Point Driver{pmNonLinearityPolynomial(): error/warning conditions} *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<HOST>|E|pmNonLinearityPolynomial (FILE:LINENO)
+    Unallowable operation: inputReadout is NULL.
+<HOST>|E|pmNonLinearityPolynomial (FILE:LINENO)
+    Unallowable operation: inputReadout->image is NULL.
+<HOST>|E|pmNonLinearityPolynomial (FILE:LINENO)
+    Unallowable operation: input1DPoly is NULL.
+
+---> TESTPOINT PASSED (Test Point Driver{pmNonLinearityPolynomial(): error/warning conditions} | tst_pmNonLinear.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmNonLinear.c                                          *
+*            TestPoint: Test Point Driver{pmNonLinearityLookup(): error/warning conditions} *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<HOST>|E|pmNonLinearityLookup (FILE:LINENO)
+    Unallowable operation: inputReadout is NULL.
+<HOST>|E|pmNonLinearityLookup (FILE:LINENO)
+    Unallowable operation: inputReadout->image is NULL.
+<HOST>|E|pmNonLinearityLookup (FILE:LINENO)
+    Unallowable operation: inFlux is NULL.
+<HOST>|E|pmNonLinearityLookup (FILE:LINENO)
+    Unallowable operation: outFlux is NULL.
+<HOST>|W|pmNonLinearityLookup
+    WARNING: pmNonLinear.c: pmNonLinearityLookup(): input vectors have different sizes (24, 25)
+<HOST>|W|pmNonLinearityLookup
+    WARNING: pmNonLinear.c: pmNonLinearityLookup(): input vectors have different sizes (24, 23)
+<HOST>|W|pmNonLinearityLookup
+    WARNING: pmNonLinear.c: pmNonLinearityLookup(): input vectors have different sizes (23, 24)
+<HOST>|W|pmNonLinearityLookup
+    WARNING: pmNonLinear.c: pmNonLinearityLookup(): input vectors have different sizes (25, 24)
+<HOST>|W|pmNonLinearityLookup
+    WARNING: pmNonLinear.c: pmNonLinearityLookup(): input vectors have different sizes (23, 25)
+<HOST>|W|pmNonLinearityLookup
+    WARNING: pmNonLinear.c: pmNonLinearityLookup(): input vectors have different sizes (25, 23)
+<HOST>|E|pmNonLinearityLookup (FILE:LINENO)
+    pmNonLinearityLookup(): input vector less than 2 elements.  Returning inputReadout image.
+<HOST>|W|vectorBinDisectF32
+    vectorBinDisectF32(): ordinate -1.000000 is outside vector range (0.000000 - 23.000000).
+<HOST>|W|pmNonLinearityLookup
+    WARNING: pmNonLinear.c: pmNonLinearityLookup(): 1 pixels outside table.
+<HOST>|W|vectorBinDisectF32
+    vectorBinDisectF32(): ordinate 100.000000 is outside vector range (0.000000 - 23.000000).
+<HOST>|W|pmNonLinearityLookup
+    WARNING: pmNonLinear.c: pmNonLinearityLookup(): 1 pixels outside table.
+
+---> TESTPOINT PASSED (Test Point Driver{pmNonLinearityLookup(): error/warning conditions} | tst_pmNonLinear.c)
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/detrend/verified/tst_pmNonLinear.stdout
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/detrend/verified/tst_pmNonLinear.stdout	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/detrend/verified/tst_pmNonLinear.stdout	(revision 22115)
@@ -0,0 +1,115 @@
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmNonLinear.c                                          *
+*            TestPoint: pmNonLinear{doNonLinearityPolynomialTest}                  *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmNonLinear{doNonLinearityPolynomialTest} | tst_pmNonLinear.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmNonLinear.c                                          *
+*            TestPoint: pmNonLinear{doNonLinearityPolynomialTest}                  *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmNonLinear{doNonLinearityPolynomialTest} | tst_pmNonLinear.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmNonLinear.c                                          *
+*            TestPoint: pmNonLinear{doNonLinearityPolynomialTest}                  *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmNonLinear{doNonLinearityPolynomialTest} | tst_pmNonLinear.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmNonLinear.c                                          *
+*            TestPoint: pmNonLinear{doNonLinearityPolynomialTest}                  *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmNonLinear{doNonLinearityPolynomialTest} | tst_pmNonLinear.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmNonLinear.c                                          *
+*            TestPoint: pmNonLinear{doNonLinearityLookupTest}                      *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmNonLinear{doNonLinearityLookupTest} | tst_pmNonLinear.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmNonLinear.c                                          *
+*            TestPoint: pmNonLinear{doNonLinearityLookupTest}                      *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmNonLinear{doNonLinearityLookupTest} | tst_pmNonLinear.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmNonLinear.c                                          *
+*            TestPoint: pmNonLinear{doNonLinearityLookupTest}                      *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmNonLinear{doNonLinearityLookupTest} | tst_pmNonLinear.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmNonLinear.c                                          *
+*            TestPoint: pmNonLinear{doNonLinearityLookupTest}                      *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmNonLinear{doNonLinearityLookupTest} | tst_pmNonLinear.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmNonLinear.c                                          *
+*            TestPoint: pmNonLinear{Testing bad input parameter conditions.}       *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+------------------------------------------------------------
+Calling pmNonLinearityPolynomial() with NULL input readout.  Should generate error, return NULL.
+------------------------------------------------------------
+Calling pmNonLinearityPolynomial() with NULL input readout->image.  Should generate error, return NULL.
+------------------------------------------------------------
+Calling pmNonLinearityPolynomial() with NULL polynomial.  Should generate error, return NULL.
+------------------------------------------------------------
+Calling pmNonLinearityLookup() with NULL input pmReadout.  Should generate error, return NULL.
+------------------------------------------------------------
+Calling pmNonLinearityLookup() with NULL input pmReadout->image.  Should generate error, return NULL.
+------------------------------------------------------------
+Calling pmNonLinearityLookup() with NULL inFlux psVector.  Should generate error, return NULL.
+------------------------------------------------------------
+Calling pmNonLinearityLookup() with NULL outFlux psVector.  Should generate error, return NULL.
+------------------------------------------------------------
+Calling pmNonLinearityLookup() with size difference in inFlux/outFLux psVectors.  Should generate warning.
+------------------------------------------------------------
+Calling pmNonLinearityLookup() with size difference in inFlux/outFLux psVectors.  Should generate warning.
+------------------------------------------------------------
+Calling pmNonLinearityLookup() with size difference in inFlux/outFLux psVectors.  Should generate warning.
+------------------------------------------------------------
+Calling pmNonLinearityLookup() with size difference in inFlux/outFLux psVectors.  Should generate warning.
+------------------------------------------------------------
+Calling pmNonLinearityLookup() with size difference in inFlux/outFLux psVectors.  Should generate warning.
+------------------------------------------------------------
+Calling pmNonLinearityLookup() with size difference in inFlux/outFLux psVectors.  Should generate warning.
+------------------------------------------------------------
+Calling pmNonLinearityLookup() with inFlux psVector size 1.  Should generate error, return original pmReadout.
+------------------------------------------------------------
+Calling pmNonLinearityLookup() with one pixels outside inFlux range.  Should generate warnings.
+------------------------------------------------------------
+Calling pmNonLinearityLookup() with one pixels outside inFlux range.  Should generate warnings.
+------------------------------------------------------------
+Calling pmNonLinearityLookup() with image values not in vector.
+
+---> TESTPOINT PASSED (pmNonLinear{Testing bad input parameter conditions.} | tst_pmNonLinear.c)
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/imcombine/.cvsignore
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/imcombine/.cvsignore	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/imcombine/.cvsignore	(revision 22115)
@@ -0,0 +1,7 @@
+.deps
+.libs
+Makefile
+Makefile.in
+temp
+tst_pmImageCombine
+tst_pmReadoutCombine
Index: /tags/ipp-1-X/rel8_2/psModules/test/imcombine/Makefile.am
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/imcombine/Makefile.am	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/imcombine/Makefile.am	(revision 22115)
@@ -0,0 +1,23 @@
+# Makefile for psModule tests
+
+AM_LDFLAGS = -L$(top_builddir)/src -lpsmodule $(PSMODULE_LIBS)
+AM_CFLAGS  = @AM_CFLAGS@ $(PSMODULE_CFLAGS) $(SRCINC)
+
+TESTS = \
+    tst_pmImageCombine \
+    tst_pmReadoutCombine
+
+tst_pmImageCombine_SOURCES = tst_pmImageCombine.c
+tst_pmReadoutCombine_SOURCES = tst_pmReadoutCombine.c
+
+check_PROGRAMS = $(TESTS)
+
+TESTS_ENVIRONMENT = perl $(top_srcdir)/test/runTest --verified=$(srcdir)/verified
+
+tests: $(TESTS)
+
+EXTRA_DIST = verified
+
+CLEANFILES = $(TESTS) temp/*
+
+test: check
Index: /tags/ipp-1-X/rel8_2/psModules/test/imcombine/tst_pmImageCombine.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/imcombine/tst_pmImageCombine.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/imcombine/tst_pmImageCombine.c	(revision 22115)
@@ -0,0 +1,353 @@
+/** @file tst_pmImageCombine.c
+ *
+ *  @brief Contains the tests for pmImageCombine.c:
+ *
+ *  test00: This code will test the various functions in the pmImageCombine.c file.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  XXX: Must verify the results internally.  Don't use stdout file.
+ *  XXX: Must test masks with pmRejectPixels()
+ *
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-10-10 21:58:34 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+#include "psTest.h"
+#include "pslib.h"
+#include "pmImageCombine.h"
+static int test00(void);
+testDescription tests[] = {
+                              {test00, 000, "pmCombineImages()", true, false},
+                              {NULL}
+                          };
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    return !runTestSuite(stderr, "Test Point Driver", tests, argc, argv);
+}
+
+psF32 genRanFloat(psF32 low,
+                  psF32 high)
+{
+    psF32 ran1 = (((psF32) (random() % 10000)) / 10000.0);
+    return(low + (ran1 * (high - low)));
+}
+
+psF32 genRanInt(psS32 low,
+                psS32 high)
+{
+    psF32 ran1 = (((psF32) (random() % 10000)) / 10000.0);
+    return((psS32) (low + (ran1 * (high - low))));
+}
+
+#define TST00_EXPANSION_FACTOR_X 1.0
+#define TST00_EXPANSION_FACTOR_Y 1.0
+#define TST00_OFFSET_X 0.0
+#define TST00_OFFSET_Y 0.0
+#define TST00_NUM_PIXELS 10
+#define TST00_MASK_VALUE 1
+#define TST00_NUM_ITERATIONS 4
+#define TST00_SIGMA_CLIP 1.0
+#define TST00_REJECTION_THRESHOLD 0.01
+#define TST00_GRADIENT_LIMIT 10.0
+/*******************************************************************************
+NOTE: This function returns FALSE if there were no errors.
+ ******************************************************************************/
+psBool testCombineImages(psS32 numRows,
+                         psS32 numCols,
+                         psS32 numImages)
+{
+    printf("Testing pmCombineImages(%d, %d, %d)\n", numRows, numCols, numImages);
+    bool testStatus = false;
+
+    psArray *images = psArrayAlloc(numImages);
+    psArray *errors = psArrayAlloc(numImages);
+    psArray *masks = psArrayAlloc(numImages);
+    for (psS32 i = 0 ; i < numImages ; i++) {
+        images->data[i] = (psPtr *) psImageAlloc(numCols, numRows, PS_TYPE_F32);
+        errors->data[i] = (psPtr *) psImageAlloc(numCols, numRows, PS_TYPE_F32);
+        masks->data[i] = (psPtr *) psImageAlloc(numCols, numRows, PS_TYPE_U8);
+        psImage *image = (psImage *) images->data[i];
+        psImage *error = (psImage *) errors->data[i];
+        psImage *mask = (psImage *) masks->data[i];
+        PS_IMAGE_SET_F32(image, 0.0);
+        PS_IMAGE_SET_F32(error, 1.0);
+        PS_IMAGE_SET_U8(mask, 0);
+
+        for (psS32 row = 0 ; row < numRows ; row++) {
+            for (psS32 col = 0 ; col < numCols ; col++) {
+                // Scale row/col to [-1.0:1.0]
+                psF32 rowScaled = ((psF32) (row - (numRows/2))) / ((psF32) (numRows/2));
+                psF32 colScaled = ((psF32) (col - (numCols/2))) / ((psF32) (numCols/2));
+                image->data.F32[row][col] = PS_SQR((2.0 - rowScaled) + (2.0 - colScaled)) + genRanFloat(0.0, 0.5);
+            }
+        }
+    }
+
+    //
+    // Same as above except the numImages is wrong
+    //
+    psArray *imagesLong = psArrayAlloc(numImages+1);
+    psArray *errorsLong = psArrayAlloc(numImages+1);
+    psArray *masksLong = psArrayAlloc(numImages+1);
+    for (psS32 i = 0 ; i < numImages+1 ; i++) {
+        imagesLong->data[i] = (psPtr *) psImageAlloc(numCols, numRows, PS_TYPE_F32);
+        errorsLong->data[i] = (psPtr *) psImageAlloc(numCols, numRows, PS_TYPE_F32);
+        masksLong->data[i] = (psPtr *) psImageAlloc(numCols, numRows, PS_TYPE_U8);
+        psImage *image = (psImage *) imagesLong->data[i];
+        psImage *error = (psImage *) errorsLong->data[i];
+        psImage *mask = (psImage *) masksLong->data[i];
+        PS_IMAGE_SET_F32(image, 0.0);
+        PS_IMAGE_SET_F32(error, 1.0);
+        PS_IMAGE_SET_U8(mask, 0);
+
+        for (psS32 row = 0 ; row < numRows ; row++) {
+            for (psS32 col = 0 ; col < numCols ; col++) {
+                // Scale row/col to [-1.0:1.0]
+                psF32 rowScaled = ((psF32) (row - (numRows/2))) / ((psF32) (numRows/2));
+                psF32 colScaled = ((psF32) (col - (numCols/2))) / ((psF32) (numCols/2));
+                image->data.F32[row][col] = PS_SQR((2.0 - rowScaled) + (2.0 - colScaled)) + genRanFloat(0.0, 0.5);
+            }
+        }
+    }
+
+    //
+    // Same as above except the type is wrong
+    //
+    psArray *imagesBadType = psArrayAlloc(numImages);
+    psArray *errorsBadType = psArrayAlloc(numImages);
+    psArray *masksBadType = psArrayAlloc(numImages);
+    for (psS32 i = 0 ; i < numImages ; i++) {
+        imagesBadType->data[i] = (psPtr *) psImageAlloc(numCols, numRows, PS_TYPE_F64);
+        errorsBadType->data[i] = (psPtr *) psImageAlloc(numCols, numRows, PS_TYPE_F64);
+        masksBadType->data[i] = (psPtr *) psImageAlloc(numCols, numRows, PS_TYPE_S8);
+        psImage *image = (psImage *) imagesBadType->data[i];
+        psImage *error = (psImage *) errorsBadType->data[i];
+        psImage *mask = (psImage *) masksBadType    ->data[i];
+        PS_IMAGE_SET_F32(image, 0.0);
+        PS_IMAGE_SET_F32(error, 1.0);
+        PS_IMAGE_SET_U8(mask, 0);
+
+        for (psS32 row = 0 ; row < numRows ; row++) {
+            for (psS32 col = 0 ; col < numCols ; col++) {
+                // Scale row/col to [-1.0:1.0]
+                psF32 rowScaled = ((psF32) (row - (numRows/2))) / ((psF32) (numRows/2));
+                psF32 colScaled = ((psF32) (col - (numCols/2))) / ((psF32) (numCols/2));
+                image->data.F32[row][col] = PS_SQR((2.0 - rowScaled) + (2.0 - colScaled)) + genRanFloat(0.0, 0.5);
+            }
+        }
+    }
+
+    psPixels *pixels = psPixelsAlloc(TST00_NUM_PIXELS);
+    for (psS32 p = 0 ; p < TST00_NUM_PIXELS ; p++) {
+        psS32 col =  genRanInt(0, numCols);
+        psS32 row =  genRanInt(0, numRows);
+        pixels->data[p].x = (psF32) col;
+        pixels->data[p].y = (psF32) row;
+        psS32 im = genRanInt(0, numImages-1);
+        psImage *image = (psImage *) images->data[im];
+        image->data.F32[row][col] += 100.0;
+        printf("Generating a bad pixel in image (%d) at (%d, %d)\n", im, row, col);
+    }
+
+    psArray *questionablePixels = NULL;
+    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a NULL images.  Should generate error, return NULL.\n");
+    psImage *outImg = pmCombineImages(NULL, &questionablePixels, NULL, errors,
+                                      masks, TST00_MASK_VALUE, pixels, TST00_NUM_ITERATIONS,
+                                      TST00_SIGMA_CLIP, stats);
+    if (outImg != NULL) {
+        printf("TEST ERROR: pmCombineImages() returned a non-NULL psImage.\n");
+        psFree(outImg);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a long images.  Should generate error, return NULL.\n");
+    outImg = pmCombineImages(NULL, &questionablePixels, imagesLong, errors,
+                             masks, TST00_MASK_VALUE, pixels, TST00_NUM_ITERATIONS,
+                             TST00_SIGMA_CLIP, stats);
+    if (outImg != NULL) {
+        printf("TEST ERROR: pmCombineImages() returned a non-NULL psImage.\n");
+        psFree(outImg);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a bad type images.  Should generate error, return NULL.\n");
+    outImg = pmCombineImages(NULL, &questionablePixels, imagesBadType, errors,
+                             masks, TST00_MASK_VALUE, pixels, TST00_NUM_ITERATIONS,
+                             TST00_SIGMA_CLIP, stats);
+    if (outImg != NULL) {
+        printf("TEST ERROR: pmCombineImages() returned a non-NULL psImage.\n");
+        psFree(outImg);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a long errors.  Should generate error, return NULL.\n");
+    outImg = pmCombineImages(NULL, &questionablePixels, images, errorsLong,
+                             masks, TST00_MASK_VALUE, pixels, TST00_NUM_ITERATIONS,
+                             TST00_SIGMA_CLIP, stats);
+    if (outImg != NULL) {
+        printf("TEST ERROR: pmCombineImages() returned a non-NULL psImage.\n");
+        psFree(outImg);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a bad type errors.  Should generate error, return NULL.\n");
+    outImg = pmCombineImages(NULL, &questionablePixels, images, errorsBadType,
+                             masks, TST00_MASK_VALUE, pixels, TST00_NUM_ITERATIONS,
+                             TST00_SIGMA_CLIP, stats);
+    if (outImg != NULL) {
+        printf("TEST ERROR: pmCombineImages() returned a non-NULL psImage.\n");
+        psFree(outImg);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a long masks.  Should generate error, return NULL.\n");
+    outImg = pmCombineImages(NULL, &questionablePixels, images, errors,
+                             masksLong, TST00_MASK_VALUE, pixels, TST00_NUM_ITERATIONS,
+                             TST00_SIGMA_CLIP, stats);
+    if (outImg != NULL) {
+        printf("TEST ERROR: pmCombineImages() returned a non-NULL psImage.\n");
+        psFree(outImg);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a bad type masks.  Should generate error, return NULL.\n");
+    outImg = pmCombineImages(NULL, &questionablePixels, images, errors,
+                             masksBadType, TST00_MASK_VALUE, pixels, TST00_NUM_ITERATIONS,
+                             TST00_SIGMA_CLIP, stats);
+    if (outImg != NULL) {
+        printf("TEST ERROR: pmCombineImages() returned a non-NULL psImage.\n");
+        psFree(outImg);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a NULL stats.  Should generate error, return NULL.\n");
+    outImg = pmCombineImages(NULL, &questionablePixels, images, errors,
+                             masks, TST00_MASK_VALUE, pixels, TST00_NUM_ITERATIONS,
+                             TST00_SIGMA_CLIP, NULL);
+    if (outImg != NULL) {
+        printf("TEST ERROR: pmCombineImages() returned a non-NULL psImage.\n");
+        psFree(outImg);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with acceptable data.  Should generate a psImage.\n");
+    outImg = pmCombineImages(NULL, &questionablePixels, images, errors,
+                             masks, TST00_MASK_VALUE, pixels, TST00_NUM_ITERATIONS,
+                             TST00_SIGMA_CLIP, stats);
+    if (outImg == NULL) {
+        printf("TEST ERROR: pmCombineImages() returned a NULL psImage.\n");
+        testStatus = true;
+    }
+    if (0) {
+        for (psS32 p = 0 ; p < TST00_NUM_PIXELS ; p++) {
+            psS32 col = (psS32) (pixels->data[p]).x;
+            psS32 row = (psS32) (pixels->data[p]).y;
+            printf("------------------------------------------\n");
+            printf("Pixel (%d, %d) in combined image is %f\n", row, col, outImg->data.F32[row][col]);
+            for (psS32 i = 0 ; i < numImages ; i++) {
+                psImage *image = (psImage *) images->data[i];
+                printf("Pixel (after combine) (%d, %d) in image (%d) is %f\n", row, col, i, image->data.F32[row][col]);
+            }
+        }
+    }
+
+    if (questionablePixels->n != numImages) {
+        printf("TEST ERROR: pmCombineImages(): questionablePixels->n was %ld, should have been %d\n",
+               questionablePixels->n, numImages);
+        testStatus = true;
+    } else {
+        // XXX: We should internally verify this with the pixels list, not merely use the stdout.
+        for (psS32 i = 0 ; i < questionablePixels->n ; i++) {
+            psPixels *myPixels = (psPixels *) questionablePixels->data[i];
+            for (psS32 p = 0 ; p < myPixels->n ; p++) {
+                printf("Image %d, questionable pixel %d is (%f %f)\n",
+                       i, p, myPixels->data[p].y, myPixels->data[p].x);
+            }
+        }
+    }
+
+    psArray *expandTransforms = psArrayAlloc(numImages);
+    psArray *contractTransforms = psArrayAlloc(numImages);
+    for (psS32 im = 0 ; im < numImages ; im++) {
+        psPlaneTransform *ptExpand = psPlaneTransformAlloc(2, 2);
+        ptExpand->x->coeff[0][0] = TST00_OFFSET_X;
+        ptExpand->x->coeff[1][0] = TST00_EXPANSION_FACTOR_X;
+        ptExpand->y->coeff[0][0] = TST00_OFFSET_Y;
+        ptExpand->y->coeff[0][1] = TST00_EXPANSION_FACTOR_Y;
+        expandTransforms->data[im] = (psPtr *) ptExpand;
+
+        psPlaneTransform *ptContract = psPlaneTransformAlloc(2, 2);
+        ptContract->x->coeff[0][0] = -TST00_OFFSET_X;
+        ptContract->x->coeff[1][0] = 1.0 / TST00_EXPANSION_FACTOR_X;
+        ptContract->y->coeff[0][0] = -TST00_OFFSET_Y;
+        ptContract->y->coeff[0][1] = 1.0 / TST00_EXPANSION_FACTOR_Y;
+        contractTransforms->data[im] = (psPtr *) ptContract;
+    }
+
+    //-------------------------------------------------------------------------
+    //
+    // XXX: psRejectPixels() has known bugs.  Specifically, in the psImageTransform() call.
+    // We exclude this from our tests.
+    //
+    printf("\n\n\nCalling pmRejectPixels() with acceptable data.  Should generate a psArray.\n");
+    psArray *pixelRejects = pmRejectPixels(images, NULL, questionablePixels, expandTransforms,
+                                           contractTransforms, TST00_REJECTION_THRESHOLD,
+                                           TST00_GRADIENT_LIMIT);
+    if (pixelRejects == NULL) {
+        printf("TEST ERROR: pmRejectPixels() returned a NULL psArray.\n");
+        testStatus = true;
+    } else {
+        // XXX: We should internally verify this with the pixels list, not merely use the stdout.
+        for (psS32 i = 0 ; i < pixelRejects->n ; i++) {
+            psPixels *myPixels = (psPixels *) pixelRejects->data[i];
+            printf("tst_pmImageCombine.c: Image %d had %ld rejects.\n", i, myPixels->n);
+
+            for (psS32 p = 0 ; p < myPixels->n ; p++) {
+                printf("Image %d, rejected pixel %d is (%f %f)\n", i, p,
+                       myPixels->data[p].y, myPixels->data[p].x);
+            }
+        }
+        psFree(pixelRejects);
+    }
+
+    psFree(images);
+    psFree(errors);
+    psFree(masks);
+    psFree(imagesLong);
+    psFree(errorsLong);
+    psFree(masksLong);
+    psFree(imagesBadType);
+    psFree(errorsBadType);
+    psFree(masksBadType);
+    psFree(pixels);
+    psFree(stats);
+
+    return(testStatus);
+}
+
+/*******************************************************************************
+NOTE: This function returns TRUE if there were no errors.
+ ******************************************************************************/
+int test00( void )
+{
+    bool testStatus = false;
+
+    testStatus|= testCombineImages(10, 10, 5);
+
+    return(!testStatus);
+}
Index: /tags/ipp-1-X/rel8_2/psModules/test/imcombine/tst_pmReadoutCombine.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/imcombine/tst_pmReadoutCombine.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/imcombine/tst_pmReadoutCombine.c	(revision 22115)
@@ -0,0 +1,452 @@
+/** @file tst_pmReadoutCombine.c
+ *
+ *  test00() This routine will test the basic functionality.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:42:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ *
+ *  XXX: Untested:
+ * S16, S32 types
+ * Multiple input readouts with varying sizes and offsets.
+ * params->fracLow and params->fracHigh
+ * params->nKeep
+ * (gain > 0.0) && (readnoise >= 0.0) (applyZeroScale == true)
+ * (gain > 0.0) && (readnoise >= 0.0) (applyZeroScale == false)
+ *
+ */
+
+#include "psTest.h"
+#include "pslib.h"
+#include "pmReadoutCombine.h"
+static int test00(void);
+static int test01(void);
+testDescription tests[] = {
+                              {test00, 000, "pmSubtractBias(): Basic readout combines with no image overlap", true, false},
+                              {test01, 000, "pmSubtractBias(): input parameter error conditions", true, false},
+                              {NULL}
+                          };
+
+#define NUM_READOUTS  10
+#define INPUT_NUM_ROWS 20
+#define INPUT_NUM_COLS 20
+#define VEC_ZERO 1.0
+#define VEC_SCALE 2.0
+
+psS32 VerifyTheOutput(psImage *output, psF32 expect)
+{
+    bool testStatus = true;
+
+    for (psS32 i = 0 ; i < output->numRows ; i++) {
+        for (psS32 j = 0 ; j < output->numCols ; j++) {
+            if (output->data.F32[i][j] != expect) {
+                printf("TEST ERROR: output[%d][%d] is %.2f, should be %f\n", i, j, output->data.F32[i][j], expect);
+                testStatus = false;
+            }
+        }
+    }
+    return(testStatus);
+}
+
+
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    return !runTestSuite(stderr, "Test Point Driver", tests, argc, argv);
+}
+
+/******************************************************************************
+simpleCombineNoOverlap(): this routine creates a list of NUM_READOUTS input
+readouts and calls pmReadoutCombine().
+ *****************************************************************************/
+int simpleCombineNoOverlap(psS32 numInputCols, psS32 numInputRows)
+{
+    int i;
+    int r;
+    psList *list = NULL;
+    int baseRowsReadout[NUM_READOUTS];
+    int baseColsReadout[NUM_READOUTS];
+    int baseRows[NUM_READOUTS];
+    int baseCols[NUM_READOUTS];
+    int numRows[NUM_READOUTS];
+    int numCols[NUM_READOUTS];
+    int minOutRow = 10000;
+    int minOutCol = 10000;
+    int maxOutRow = -1;
+    int maxOutCol = -1;
+    psImage *output = NULL;
+    psCombineParams *params = (psCombineParams *) psAlloc(sizeof(psCombineParams));
+    psVector *zero = psVectorAlloc(NUM_READOUTS, PS_TYPE_F32);
+    psVector *scale = psVectorAlloc(NUM_READOUTS, PS_TYPE_F32);
+    printPositiveTestHeader(stdout, "pmReadoutCombine", "simpleCombineNoOverlap");
+
+    for (i=0;i<NUM_READOUTS;i++) {
+        zero->data.F32[i] = VEC_ZERO;
+    }
+    for (i=0;i<NUM_READOUTS;i++) {
+        scale->data.F32[i] = VEC_SCALE;
+    }
+
+    params->stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    params->maskVal = 1;
+    params->fracLow = 0.0;
+    params->fracHigh = 10000.0;
+    params->nKeep = 0;
+
+    //
+    // Create a psList of psReadouts.  The pixels in readout r will all have the
+    // value r.
+    //
+    for (r=0;r<NUM_READOUTS;r++) {
+        baseRowsReadout[r] = r + 40;
+        baseColsReadout[r] = r + 42;
+        baseRows[r] = r;
+        baseCols[r] = r+2;
+        numRows[r] = 4 + (2 * r);
+        numCols[r] = 8 + (2 * r);
+
+        baseRowsReadout[r] = 0;
+        baseColsReadout[r] = 0;
+        baseRows[r] = 0;
+        baseCols[r] = 0;
+        numRows[r] = numInputRows;
+        numCols[r] = numInputCols;
+
+        psImage *tmpImage = psImageAlloc(numCols[r], numRows[r], PS_TYPE_F32);
+        PS_IMAGE_SET_F32(tmpImage, ((float) r));
+        *(int *) (& (tmpImage->row0)) = baseRows[r];
+        *(int *) (& (tmpImage->col0)) = baseCols[r];
+        pmReadout *tmpReadout = pmReadoutAlloc(NULL);
+        tmpReadout->row0 = 0;
+        tmpReadout->col0 = 0;
+        tmpReadout->image = tmpImage;
+
+        minOutRow = PS_MIN(minOutRow, (baseRowsReadout[r] + baseRows[r]));
+        minOutCol = PS_MIN(minOutCol, (baseColsReadout[r] + baseCols[r]));
+        maxOutRow = PS_MAX(maxOutRow, (baseRowsReadout[r] + baseRows[r] + numRows[r]));
+        maxOutCol = PS_MAX(maxOutCol, (baseColsReadout[r] + baseCols[r] + numCols[r]));
+
+        if (r == 0) {
+            list = psListAlloc(tmpReadout);
+        } else {
+            psListAdd(list, PS_LIST_HEAD, tmpReadout);
+        }
+    }
+    printf("tst_pmReadoutCombine(): (minOutRow, minOutCol) to (maxOutRow, maxOutCol) is (%d, %d) (%d, %d)\n",
+           minOutRow, minOutCol, maxOutRow, maxOutCol);
+
+    output = pmReadoutCombine(output, list, params, zero, scale, true, 0.0, 0.0);
+    psF32 NR = (psF32) NUM_READOUTS;
+    psF32 expectedPixel = ((NR/2.0) * (VEC_ZERO + (VEC_ZERO + VEC_SCALE * (NR - 1)))) / NR;
+
+    int testStatus = VerifyTheOutput(output, expectedPixel);
+
+    psFree(params->stats);
+    psFree(params);
+    psFree(output);
+    psFree(zero);
+    psFree(scale);
+
+    psListElem *tmpInput = (psListElem *) list->head;
+    while (NULL != tmpInput) {
+        pmReadout *tmpReadout = (pmReadout *) tmpInput->data;
+        psFree(tmpReadout);
+        tmpInput = tmpInput->next;
+    }
+    psFree(list);
+
+    printFooter(stdout, "pmReadoutCombine", "simpleCombineNoOverlap", true);
+    return(testStatus);
+}
+
+int test00( void )
+{
+    int testStatus = 0;
+
+    testStatus |= simpleCombineNoOverlap(1, 1);
+    testStatus |= simpleCombineNoOverlap(INPUT_NUM_COLS, 1);
+    testStatus |= simpleCombineNoOverlap(1, INPUT_NUM_ROWS);
+    testStatus |= simpleCombineNoOverlap(INPUT_NUM_COLS, INPUT_NUM_ROWS);
+
+    return(testStatus);
+}
+
+/******************************************************************************
+test01(): we simply call pmReadoutCombine() with a variety of erroneous input
+parameter combinations and verify that it behaves properly.
+ *****************************************************************************/
+int test01()
+{
+    int testStatus = true;
+    int i;
+    int r;
+    psList *list = NULL;
+    int baseRowsReadout[NUM_READOUTS];
+    int baseColsReadout[NUM_READOUTS];
+    int baseRows[NUM_READOUTS];
+    int baseCols[NUM_READOUTS];
+    int numRows[NUM_READOUTS];
+    int numCols[NUM_READOUTS];
+    int minOutRow = 10000;
+    int minOutCol = 10000;
+    int maxOutRow = -1;
+    int maxOutCol = -1;
+    psImage *output = NULL;
+    psImage *rc = NULL;
+    psCombineParams *params = (psCombineParams *) psAlloc(sizeof(psCombineParams));
+    psVector *zero = psVectorAlloc(NUM_READOUTS, PS_TYPE_F32);
+    psVector *zeroHalf = psVectorAlloc(NUM_READOUTS/2, PS_TYPE_F32);
+    psVector *zeroBig = psVectorAlloc(NUM_READOUTS+1, PS_TYPE_F32);
+    psVector *zeroF64 = psVectorAlloc(NUM_READOUTS, PS_TYPE_F64);
+    psVector *scale = psVectorAlloc(NUM_READOUTS, PS_TYPE_F32);
+    psVector *scaleHalf = psVectorAlloc(NUM_READOUTS/2, PS_TYPE_F32);
+    psVector *scaleBig = psVectorAlloc(NUM_READOUTS*2, PS_TYPE_F32);
+    psVector *scaleF64 = psVectorAlloc(NUM_READOUTS, PS_TYPE_F64);
+    for (i=0;i<NUM_READOUTS;i++) {
+        zero->data.F32[i] = 3.0;
+        zeroBig->data.F32[i] = 3.0;
+        zero->data.F32[i] = VEC_ZERO;
+        zeroBig->data.F32[i] = VEC_ZERO;
+    }
+    for (i=0;i<NUM_READOUTS;i++) {
+        scale->data.F32[i] = 6.0;
+        scaleBig->data.F32[i] = 6.0;
+        scale->data.F32[i] = VEC_SCALE;
+        scaleBig->data.F32[i] = VEC_SCALE;
+    }
+    printPositiveTestHeader(stdout, "pmReadoutCombine", "Testing bad input parameter conditions");
+
+    params->stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    params->maskVal = 1;
+    params->fracLow = 0.0;
+    params->fracHigh = 10000.0;
+    params->nKeep = 0;
+
+    for (r=0;r<NUM_READOUTS;r++) {
+        baseRowsReadout[r] = 0;
+        baseColsReadout[r] = 0;
+        baseRows[r] = 0;
+        baseCols[r] = 0;
+        numRows[r] = INPUT_NUM_ROWS;
+        numCols[r] = INPUT_NUM_COLS;
+
+        psImage *tmpImage = psImageAlloc(numCols[r], numRows[r], PS_TYPE_F32);
+        PS_IMAGE_SET_F32(tmpImage, ((float) r));
+        *(int *) (& (tmpImage->row0)) = baseRows[r];
+        *(int *) (& (tmpImage->col0)) = baseCols[r];
+        pmReadout *tmpReadout = pmReadoutAlloc(NULL);
+        tmpReadout->row0 = 0;
+        tmpReadout->col0 = 0;
+        tmpReadout->image = tmpImage;
+        minOutRow = PS_MIN(minOutRow, (baseRowsReadout[r] + baseRows[r]));
+        minOutCol = PS_MIN(minOutCol, (baseColsReadout[r] + baseCols[r]));
+        maxOutRow = PS_MAX(maxOutRow, (baseRowsReadout[r] + baseRows[r] + numRows[r]));
+        maxOutCol = PS_MAX(maxOutCol, (baseColsReadout[r] + baseCols[r] + numCols[r]));
+
+        if (r == 0) {
+            list = psListAlloc(tmpReadout);
+        } else {
+            psListAdd(list, PS_LIST_HEAD, tmpReadout);
+        }
+    }
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() with NULL zero vector.\n");
+    rc = pmReadoutCombine(NULL, list, params, NULL, scale, true, 0.0, 0.0);
+    if (rc != NULL) {
+        psF32 NR = (psF32) NUM_READOUTS;
+        psF32 expectedPixel = ((NR/2.0) * (0.0 + (0.0 + VEC_SCALE * (NR - 1)))) / NR;
+        if (false == VerifyTheOutput(rc, expectedPixel)) {
+            testStatus = false;
+        }
+
+        if (rc->type.type != scale->type.type) {
+            printf("TEST ERROR: output readout->image has incorrect type.\n");
+            testStatus = false;
+        }
+        psFree(rc);
+    } else {
+        printf("TEST ERROR: pmReadoutCombine() returned NULL\n");
+        testStatus = false;
+
+    }
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() with incorrect length zero vector (too small).  Should generate error.\n");
+    rc = pmReadoutCombine(NULL, list, params, zeroHalf, scale, true, 0.0, 0.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmReadoutCombine() did not return NULL\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() with incorrect type zero vector.  Should generate error.\n");
+    rc = pmReadoutCombine(NULL, list, params, zeroF64, scale, true, 0.0, 0.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmReadoutCombine() did not return NULL\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() with incorrect length zero vector (too big).  Should generate warning.\n");
+    rc = pmReadoutCombine(output, list, params, zeroBig, scale, true, 0.0, 0.0);
+    if (rc != NULL) {
+        psF32 NR = (psF32) NUM_READOUTS;
+        psF32 expectedPixel = ((NR/2.0) * (VEC_ZERO + (VEC_ZERO + VEC_SCALE * (NR - 1)))) / NR;
+
+        if (false == VerifyTheOutput(rc, expectedPixel)) {
+            testStatus = false;
+        }
+        psFree(rc);
+        rc = NULL;
+    } else {
+        printf("TEST ERROR: pmReadoutCombine() returned NULL\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() with NULL scale vector.\n");
+    rc = pmReadoutCombine(output, list, params, zero, NULL, true, 0.0, 0.0);
+
+    if (rc != NULL) {
+        psF32 NR = (psF32) NUM_READOUTS;
+        psF32 expectedPixel = ((NR/2.0) * (VEC_ZERO + (VEC_ZERO + 1.0 * (NR - 1)))) / NR;
+        if (false == VerifyTheOutput(rc, expectedPixel)) {
+            testStatus = false;
+        }
+
+        if (rc->type.type != scale->type.type) {
+            printf("TEST ERROR: output readout->image has incorrect type.\n");
+            testStatus = false;
+        }
+        psFree(rc);
+    } else {
+        printf("TEST ERROR: pmReadoutCombine() returned NULL\n");
+        testStatus = false;
+
+    }
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() with incorrect length scale vector (too small).  Should generate error.\n");
+    rc = pmReadoutCombine(output, list, params, zero, scaleHalf, true, 0.0, 0.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmReadoutCombine() did not return NULL\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() with incorrect type scale vector.  Should generate error.\n");
+    rc = pmReadoutCombine(output, list, params, zero, scaleF64, true, 0.0, 0.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmReadoutCombine() did not return NULL\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() with incorrect length scale vector (too big).  Should generate warning.\n");
+    rc = pmReadoutCombine(output, list, params, zero, scaleBig, true, 0.0, 0.0);
+    if (rc != NULL) {
+        psF32 NR = (psF32) NUM_READOUTS;
+        psF32 expectedPixel = ((NR/2.0) * (VEC_ZERO + (VEC_ZERO + VEC_SCALE * (NR - 1)))) / NR;
+
+        if (false == VerifyTheOutput(rc, expectedPixel)) {
+            testStatus = false;
+        }
+        psFree(rc);
+        rc = NULL;
+    } else {
+        printf("TEST ERROR: pmReadoutCombine() returned NULL\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() insufficient size output image.  Should generate error, return NULL.\n");
+    output = psImageAlloc(1, 1, PS_TYPE_F32);
+    rc = pmReadoutCombine(output, list, params, zero, scale, true, 0.0, 0.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmReadoutCombine() did not return NULL\n");
+        testStatus = false;
+    }
+    psFree(output);
+    output = NULL;
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() row0/col0 too large.  Should generate error, return NULL.\n");
+    output = psImageAlloc(1, 1, PS_TYPE_F32);
+    *(psS32*)&output->row0 = 10000;
+    *(psS32*)&output->col0 = 10000;
+    rc = pmReadoutCombine(output, list, params, zero, scale, true, 0.0, 0.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmReadoutCombine() did not return NULL\n");
+        testStatus = false;
+    }
+    psFree(output);
+    output = NULL;
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() with NULL input list.  Should generate error, return NULL.\n");
+    rc = pmReadoutCombine(output, NULL, params, zero, scale, true, 0.0, 0.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmReadoutCombine() did not return NULL\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("Calling pmReadoutCombine() with NULL params.  Should generate error, return NULL.\n");
+    rc = pmReadoutCombine(output, list, NULL, zero, scale, true, 0.0, 0.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmReadoutCombine() did not return NULL\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------\n");
+    psStatsOptions oldStatsOpts = params->stats->options |= PS_STAT_MIN;
+    printf("Calling pmReadoutCombine() with multiple stats->options.  Should generate error, return NULL.\n");
+    rc = pmReadoutCombine(output, list, params, zero, scale, true, 0.0, 0.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmReadoutCombine() did not return NULL\n");
+        testStatus = false;
+    }
+    params->stats->options = oldStatsOpts;
+
+    printf("----------------------------------------------------------------------------\n");
+    psStats *oldStats = params->stats;
+    printf("Calling pmReadoutCombine() with NULL param->stats.  Should generate error, return NULL.\n");
+    params->stats = NULL;
+    rc = pmReadoutCombine(output, list, params, zero, scale, true, 0.0, 0.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmReadoutCombine() did not return NULL\n");
+        testStatus = false;
+    }
+    params->stats = oldStats;
+
+    printf("----------------------------------------------------------------------------\n");
+    printf("============================================================================\n");
+
+    psFree(params->stats);
+    psFree(params);
+    //    psFree(output);
+    //    psFree(rc);
+    psFree(zero);
+    psFree(zeroHalf);
+    psFree(zeroBig);
+    psFree(zeroF64);
+    psFree(scale);
+    psFree(scaleHalf);
+    psFree(scaleBig);
+    psFree(scaleF64);
+    psListElem *tmpInput = (psListElem *) list->head;
+    while (NULL != tmpInput) {
+        pmReadout *tmpReadout = (pmReadout *) tmpInput->data;
+        psFree(tmpReadout);
+        tmpInput = tmpInput->next;
+    }
+    psFree(list);
+
+    printFooter(stdout, "pmReadoutCombine", "Testing bad input parameter conditions", true);
+    return(testStatus);
+}
Index: /tags/ipp-1-X/rel8_2/psModules/test/imcombine/verified/tst_pmImageCombine.stderr
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/imcombine/verified/tst_pmImageCombine.stderr	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/imcombine/verified/tst_pmImageCombine.stderr	(revision 22115)
@@ -0,0 +1,25 @@
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmImageCombine.c                                       *
+*            TestPoint: Test Point Driver{pmCombineImages()}                       *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<HOST>|E|pmCombineImages (FILE:LINENO)
+    Unallowable operation: images is NULL.
+<HOST>|E|pmCombineImages (FILE:LINENO)
+    images and errors args must have same length (6 != 5)
+<HOST>|E|pmCombineImages (FILE:LINENO)
+    Unallowable operation: psImage tmpDataImg has incorrect type.
+<HOST>|E|pmCombineImages (FILE:LINENO)
+    images and errors args must have same length (5 != 6)
+<HOST>|E|pmCombineImages (FILE:LINENO)
+    Unallowable operation: psImage tmpErrorImg has incorrect type.
+<HOST>|E|pmCombineImages (FILE:LINENO)
+    images and masks args must have same length (5 != 6)
+<HOST>|E|pmCombineImages (FILE:LINENO)
+    Unallowable operation: psImage tmpMaskImg has incorrect type.
+<HOST>|E|pmCombineImages (FILE:LINENO)
+    Unallowable operation: stats is NULL.
+
+---> TESTPOINT PASSED (Test Point Driver{pmCombineImages()} | tst_pmImageCombine.c)
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/imcombine/verified/tst_pmImageCombine.stdout
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/imcombine/verified/tst_pmImageCombine.stdout	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/imcombine/verified/tst_pmImageCombine.stdout	(revision 22115)
@@ -0,0 +1,47 @@
+Testing pmCombineImages(10, 10, 5)
+Generating a bad pixel in image (1) at (0, 8)
+Generating a bad pixel in image (3) at (7, 5)
+Generating a bad pixel in image (1) at (1, 2)
+Generating a bad pixel in image (2) at (9, 9)
+Generating a bad pixel in image (1) at (3, 9)
+Generating a bad pixel in image (2) at (0, 5)
+Generating a bad pixel in image (1) at (4, 5)
+Generating a bad pixel in image (1) at (3, 8)
+Generating a bad pixel in image (2) at (6, 2)
+Generating a bad pixel in image (2) at (3, 7)
+Calling with a NULL images.  Should generate error, return NULL.
+Calling with a long images.  Should generate error, return NULL.
+Calling with a bad type images.  Should generate error, return NULL.
+Calling with a long errors.  Should generate error, return NULL.
+Calling with a bad type errors.  Should generate error, return NULL.
+Calling with a long masks.  Should generate error, return NULL.
+Calling with a bad type masks.  Should generate error, return NULL.
+Calling with a NULL stats.  Should generate error, return NULL.
+Calling with acceptable data.  Should generate a psImage.
+Image 1, questionable pixel 0 is (0.000000 8.000000)
+Image 1, questionable pixel 1 is (1.000000 2.000000)
+Image 1, questionable pixel 2 is (3.000000 9.000000)
+Image 1, questionable pixel 3 is (4.000000 5.000000)
+Image 1, questionable pixel 4 is (3.000000 8.000000)
+Image 2, questionable pixel 0 is (9.000000 9.000000)
+Image 2, questionable pixel 1 is (0.000000 5.000000)
+Image 2, questionable pixel 2 is (6.000000 2.000000)
+Image 2, questionable pixel 3 is (3.000000 7.000000)
+Image 3, questionable pixel 0 is (7.000000 5.000000)
+
+
+
+Calling pmRejectPixels() with acceptable data.  Should generate a psArray.
+tst_pmImageCombine.c: Image 0 had 0 rejects.
+tst_pmImageCombine.c: Image 1 had 4 rejects.
+Image 1, rejected pixel 0 is (0.000000 8.000000)
+Image 1, rejected pixel 1 is (1.000000 2.000000)
+Image 1, rejected pixel 2 is (4.000000 5.000000)
+Image 1, rejected pixel 3 is (3.000000 8.000000)
+tst_pmImageCombine.c: Image 2 had 3 rejects.
+Image 2, rejected pixel 0 is (0.000000 5.000000)
+Image 2, rejected pixel 1 is (6.000000 2.000000)
+Image 2, rejected pixel 2 is (3.000000 7.000000)
+tst_pmImageCombine.c: Image 3 had 1 rejects.
+Image 3, rejected pixel 0 is (7.000000 5.000000)
+tst_pmImageCombine.c: Image 4 had 0 rejects.
Index: /tags/ipp-1-X/rel8_2/psModules/test/imcombine/verified/tst_pmReadoutCombine.stderr
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/imcombine/verified/tst_pmReadoutCombine.stderr	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/imcombine/verified/tst_pmReadoutCombine.stderr	(revision 22115)
@@ -0,0 +1,42 @@
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmReadoutCombine.c                                     *
+*            TestPoint: Test Point Driver{pmSubtractBias(): Basic readout combines with no image overla *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (Test Point Driver{pmSubtractBias(): Basic readout combines with no image overlap} | tst_pmReadoutCombine.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmReadoutCombine.c                                     *
+*            TestPoint: Test Point Driver{pmSubtractBias(): input parameter error conditions} *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<HOST>|E|pmReadoutCombine (FILE:LINENO)
+    zero vector has incorrect size (5).  Returning NULL.
+<HOST>|E|pmReadoutCombine (FILE:LINENO)
+    Unallowable operation: psVector zero has incorrect type.
+<HOST>|W|pmReadoutCombine
+    WARNING: the zero vector too many elements (11)
+<HOST>|E|pmReadoutCombine (FILE:LINENO)
+    scale vector has incorrect size (5).  Returning NULL.
+<HOST>|E|pmReadoutCombine (FILE:LINENO)
+    Unallowable operation: psVector scale has incorrect type.
+<HOST>|W|pmReadoutCombine
+    WARNING: the scale vector has too many elements (20)
+<HOST>|E|pmReadoutCombine (FILE:LINENO)
+    Output image (1, 1) is too small to hold combined images.  Returning NULL.
+<HOST>|E|pmReadoutCombine (FILE:LINENO)
+    Output image offset is larger then input image offset.  Returning NULL.
+<HOST>|E|pmReadoutCombine (FILE:LINENO)
+    Unallowable operation: inputs is NULL.
+<HOST>|E|pmReadoutCombine (FILE:LINENO)
+    Unallowable operation: params is NULL.
+<HOST>|E|pmReadoutCombine (FILE:LINENO)
+    Multiple statistical options have been requested.  Returning NULL.
+<HOST>|E|pmReadoutCombine (FILE:LINENO)
+    Unallowable operation: params->stats is NULL.
+
+---> TESTPOINT PASSED (Test Point Driver{pmSubtractBias(): input parameter error conditions} | tst_pmReadoutCombine.c)
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/imcombine/verified/tst_pmReadoutCombine.stdout
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/imcombine/verified/tst_pmReadoutCombine.stdout	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/imcombine/verified/tst_pmReadoutCombine.stdout	(revision 22115)
@@ -0,0 +1,79 @@
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmReadoutCombine.c                                     *
+*            TestPoint: pmReadoutCombine{simpleCombineNoOverlap}                   *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+tst_pmReadoutCombine(): (minOutRow, minOutCol) to (maxOutRow, maxOutCol) is (0, 0) (1, 1)
+
+---> TESTPOINT PASSED (pmReadoutCombine{simpleCombineNoOverlap} | tst_pmReadoutCombine.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmReadoutCombine.c                                     *
+*            TestPoint: pmReadoutCombine{simpleCombineNoOverlap}                   *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+tst_pmReadoutCombine(): (minOutRow, minOutCol) to (maxOutRow, maxOutCol) is (0, 0) (1, 20)
+
+---> TESTPOINT PASSED (pmReadoutCombine{simpleCombineNoOverlap} | tst_pmReadoutCombine.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmReadoutCombine.c                                     *
+*            TestPoint: pmReadoutCombine{simpleCombineNoOverlap}                   *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+tst_pmReadoutCombine(): (minOutRow, minOutCol) to (maxOutRow, maxOutCol) is (0, 0) (20, 1)
+
+---> TESTPOINT PASSED (pmReadoutCombine{simpleCombineNoOverlap} | tst_pmReadoutCombine.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmReadoutCombine.c                                     *
+*            TestPoint: pmReadoutCombine{simpleCombineNoOverlap}                   *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+tst_pmReadoutCombine(): (minOutRow, minOutCol) to (maxOutRow, maxOutCol) is (0, 0) (20, 20)
+
+---> TESTPOINT PASSED (pmReadoutCombine{simpleCombineNoOverlap} | tst_pmReadoutCombine.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmReadoutCombine.c                                     *
+*            TestPoint: pmReadoutCombine{Testing bad input parameter conditions}   *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+----------------------------------------------------------------------------
+Calling pmReadoutCombine() with NULL zero vector.
+----------------------------------------------------------------------------
+Calling pmReadoutCombine() with incorrect length zero vector (too small).  Should generate error.
+----------------------------------------------------------------------------
+Calling pmReadoutCombine() with incorrect type zero vector.  Should generate error.
+----------------------------------------------------------------------------
+Calling pmReadoutCombine() with incorrect length zero vector (too big).  Should generate warning.
+----------------------------------------------------------------------------
+Calling pmReadoutCombine() with NULL scale vector.
+----------------------------------------------------------------------------
+Calling pmReadoutCombine() with incorrect length scale vector (too small).  Should generate error.
+----------------------------------------------------------------------------
+Calling pmReadoutCombine() with incorrect type scale vector.  Should generate error.
+----------------------------------------------------------------------------
+Calling pmReadoutCombine() with incorrect length scale vector (too big).  Should generate warning.
+----------------------------------------------------------------------------
+Calling pmReadoutCombine() insufficient size output image.  Should generate error, return NULL.
+----------------------------------------------------------------------------
+Calling pmReadoutCombine() row0/col0 too large.  Should generate error, return NULL.
+----------------------------------------------------------------------------
+Calling pmReadoutCombine() with NULL input list.  Should generate error, return NULL.
+----------------------------------------------------------------------------
+Calling pmReadoutCombine() with NULL params.  Should generate error, return NULL.
+----------------------------------------------------------------------------
+Calling pmReadoutCombine() with multiple stats->options.  Should generate error, return NULL.
+----------------------------------------------------------------------------
+Calling pmReadoutCombine() with NULL param->stats.  Should generate error, return NULL.
+----------------------------------------------------------------------------
+============================================================================
+
+---> TESTPOINT PASSED (pmReadoutCombine{Testing bad input parameter conditions} | tst_pmReadoutCombine.c)
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/.cvsignore
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/.cvsignore	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/.cvsignore	(revision 22115)
@@ -0,0 +1,8 @@
+.deps
+.libs
+Makefile
+Makefile.in
+temp
+tst_pmImageSubtract
+tst_pmSubtractBias
+tst_pmSubtractSky
Index: /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/Makefile.am
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/Makefile.am	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/Makefile.am	(revision 22115)
@@ -0,0 +1,26 @@
+# Makefile for psModule tests
+
+AM_LDFLAGS = -L$(top_builddir)/src -lpsmodule $(PSMODULE_LIBS)
+AM_CFLAGS  = @AM_CFLAGS@ $(PSMODULE_CFLAGS) $(SRCINC)
+AM_CPPFLAGS = $(SRCINC) $(PSLIB_CFLAGS)
+
+TESTS = \
+    tst_pmImageSubtract \
+    tst_pmSubtractBias \
+    tst_pmSubtractSky
+
+tst_pmImageSubtract_SOURCES = tst_pmImageSubtract.c
+tst_pmSubtractBias_SOURCES = tst_pmSubtractBias.c
+tst_pmSubtractSky_SOURCES = tst_pmSubtractSky.c
+
+check_PROGRAMS = $(TESTS)
+
+TESTS_ENVIRONMENT = perl $(top_srcdir)/test/runTest --verified=$(srcdir)/verified
+
+tests: $(TESTS)
+
+EXTRA_DIST = verified
+
+CLEANFILES = $(TESTS) temp/*
+
+test: check
Index: /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/tst_pmImageSubtract.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/tst_pmImageSubtract.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/tst_pmImageSubtract.c	(revision 22115)
@@ -0,0 +1,843 @@
+/** @file tst_pmImageSubtract.c
+ *
+ *  @brief Contains the tests for pmImageSubtract.c:
+ *
+ *  test00: This code will test the various functions in pmObjects.c
+ *
+ *  @author GLG, MHPCC
+ *
+ *  XXX: Most test simply ensure that the functions can be called with allowable
+ *  data.  More work need to be done to verify the results.
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:42:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+#include "psTest.h"
+#include "pslib.h"
+#include "pmImageSubtract.h"
+#define ERROR_TOLERANCE 1.0
+static int test00(void);
+static int test01(void);
+static int test02(void);
+static int test03(void);
+testDescription tests[] = {
+                              {test00, 000, "pmSubtractionKernelsAllocPOIS()", true, false},
+                              {test01, 000, "pmSubtractionKernelsAllocISIS()", true, false},
+                              {test02, 000, "pmSubtractionFindStamps()", true, false},
+                              {test03, 000, "pmSubtractionCalculateEquation()", true, false},
+                              {NULL}
+                          };
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    return !runTestSuite(stderr, "Test Point Driver", tests, argc, argv);
+}
+
+
+/*******************************************************************************
+NOTE: This function returns FALSE if there were no errors.
+ 
+XXX: Call with various unallowable input parameters.
+ 
+XXX: Untested: we don't loop through the (u, v, xOrder, yOrder) psVectors and
+ensure that each value is set correctly.
+ ******************************************************************************/
+psBool testPOISAlloc(psS32 size,
+                     psS32 SpatialOrder)
+{
+    printf("Testing pmSubtractionKernelsAllocPOIS(%d, %d)\n", size, SpatialOrder);
+
+    bool testStatus = false;
+    psS32 nBasisFunctions = (2 * size + 1) * (2 * size + 1) * (SpatialOrder + 1) * (SpatialOrder + 2) / 2;
+
+    psSubtractionKernels *kernels = pmSubtractionKernelsAllocPOIS(size, SpatialOrder);
+    if (kernels == NULL) {
+        printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() returned a NULL psSubtractionKernels.\n");
+        testStatus = true;
+    } else {
+        if (kernels->type != PM_SUBTRACTION_KERNEL_POIS) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated the wrong kernels->type.\n");
+            testStatus = true;
+        }
+
+        if ((kernels->u == NULL) ||
+                (kernels->u->n != nBasisFunctions)) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated a NULL ->u member or\n");
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated a incorrect length ->u member.\n");
+            testStatus = true;
+        }
+
+        if ((kernels->v == NULL) ||
+                (kernels->v->n != nBasisFunctions)) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated a NULL ->v member or\n");
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated a incorrect length ->v member.\n");
+            testStatus = true;
+        }
+
+        if (kernels->sigma != NULL) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated a non-NULL ->sigma member.\n");
+            testStatus = true;
+        }
+
+        if ((kernels->xOrder == NULL) ||
+                (kernels->xOrder->n != nBasisFunctions)) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated a NULL ->xOrder member or\n");
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated a incorrect length ->xOrder member.\n");
+            testStatus = true;
+        }
+
+        if ((kernels->yOrder == NULL) ||
+                (kernels->yOrder->n != nBasisFunctions)) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated a NULL ->yOrder member or\n");
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated a incorrect length ->yOrder member.\n");
+            testStatus = true;
+        }
+
+        if (kernels->subIndex != 0) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated a non-zero ->subIndex member (%d).\n", kernels->subIndex);
+            testStatus = true;
+        }
+
+        psS32 i = kernels->subIndex;
+        if ((kernels->u->data.F32[i] != 0) ||
+                (kernels->v->data.F32[i] != 0)) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS(): the ->subIndex member points to a kernel with (%f, %f) (u, v) basis function.\n",
+                   kernels->u->data.F32[i], kernels->v->data.F32[i]);
+            testStatus = true;
+        }
+
+        if (kernels->preCalc != NULL) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated a non-NULL ->preCalc member.\n");
+            testStatus = true;
+        }
+
+        if (kernels->size != size) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated an incorrect ->size member (%d).\n", kernels->size);
+            testStatus = true;
+        }
+
+        if (kernels->spatialOrder != SpatialOrder) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocPOIS() generated an incorrect ->spatialOrder member (%d).\n", kernels->spatialOrder);
+            testStatus = true;
+        }
+    }
+    psFree(kernels);
+    return(testStatus);
+}
+
+/*******************************************************************************
+NOTE: This function returns TRUE if there were no errors.
+ ******************************************************************************/
+int test00( void )
+{
+    bool testStatus = false;
+
+    testStatus|= testPOISAlloc(1, 1);
+    testStatus|= testPOISAlloc(2, 3);
+    testStatus|= testPOISAlloc(3, 4);
+
+    return(!testStatus);
+}
+
+/*******************************************************************************
+NOTE: This function returns FALSE if there were no errors.
+ 
+XXX: Call with various unallowable input parameters.
+ 
+XXX: Untested: we don't loop through the (u, v, xOrder, yOrder) psVectors and
+ensure that each value is set correctly.  We don't ensure that he preCalc
+psImages are set correctly.
+ ******************************************************************************/
+psBool testISISAlloc(psS32 sigmaLength,
+                     psS32 orderLength,
+                     psS32 size,
+                     psS32 SpatialOrder)
+{
+    printf("Testing pmSubtractionKernelsAllocISIS(%d, %d, %d, %d)\n",
+           sigmaLength, orderLength, size, SpatialOrder);
+
+    psVector *sigmas = psVectorAlloc(sigmaLength, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < sigmas->n ; i++) {
+        sigmas->data.F32[i] = 1.0 + (psF32) i;
+    }
+    psVector *orders = psVectorAlloc(orderLength, PS_TYPE_S32);
+    for (psS32 i = 0 ; i < orders->n ; i++) {
+        orders->data.S32[i] = i + 2;
+    }
+
+    bool testStatus = false;
+    psS32 numSigmas = sigmas->n;
+    psS32 nBasisFunctions = 0;
+    for (psS32 s = 0 ; s < numSigmas ; s++) {
+        for (psS32 o = 0 ; o < orders->n ; o++) {
+            nBasisFunctions+= ((orders->data.S32[o] + 1) * (orders->data.S32[o] + 2) / 2);
+        }
+    }
+    nBasisFunctions*= ((SpatialOrder + 1) * (SpatialOrder + 2) / 2);
+
+    psSubtractionKernels *kernels = pmSubtractionKernelsAllocISIS(sigmas, orders, size, SpatialOrder);
+    if (kernels == NULL) {
+        printf("TEST ERROR: pmSubtractionKernelsAllocISIS() returned a NULL psSubtractionKernels.\n");
+        testStatus = true;
+    } else {
+        if (kernels->type != PM_SUBTRACTION_KERNEL_ISIS) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated the wrong kernels->type.\n");
+            testStatus = true;
+        }
+
+        if ((kernels->u == NULL) ||
+                (kernels->u->n != nBasisFunctions)) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a NULL ->u member or\n");
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a incorrect length ->u member.\n");
+            testStatus = true;
+        }
+
+        if ((kernels->v == NULL) ||
+                (kernels->v->n != nBasisFunctions)) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a NULL ->v member or\n");
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a incorrect length ->v member.\n");
+            testStatus = true;
+        }
+
+        if ((kernels->sigma == NULL) ||
+                (kernels->sigma->n != nBasisFunctions)) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a NULL ->sigma member or\n");
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a incorrect length ->sigma member.\n");
+            testStatus = true;
+        }
+
+        if ((kernels->xOrder == NULL) ||
+                (kernels->xOrder->n != nBasisFunctions)) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a NULL ->xOrder member or\n");
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a incorrect length ->xOrder member.\n");
+            testStatus = true;
+        }
+
+        if ((kernels->yOrder == NULL) ||
+                (kernels->yOrder->n != nBasisFunctions)) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a NULL ->yOrder member or\n");
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a incorrect length ->yOrder member.\n");
+            testStatus = true;
+        }
+
+        if (kernels->subIndex != 0) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a non-zero ->subIndex member (%d).\n", kernels->subIndex);
+            testStatus = true;
+        }
+
+        //
+        // Ensure that kernels->subIndex points to the correct kernel.
+        //
+        psS32 i = kernels->subIndex;
+        if ((kernels->u->data.F32[i] != 0.0) ||
+                (kernels->v->data.F32[i] != 0.0) ||
+                (kernels->xOrder->data.F32[i] != 0.0) ||
+                (kernels->yOrder->data.F32[i] != 0.0) ||
+                (kernels->sigma->data.F32[i] != sigmas->data.F32[0])) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS(): the ->subIndex member points to the wrong kernel.\n");
+            printf("TEST ERROR: (u, v, xOrder, yOrder, sigma) is (%f, %f, %f, %f, %f).\n",
+                   kernels->u->data.F32[i], kernels->v->data.F32[i],
+                   kernels->xOrder->data.F32[i], kernels->yOrder->data.F32[i],
+                   kernels->sigma->data.F32[i]);
+            testStatus = true;
+        }
+
+        //
+        // Ensure that the preCalc images are allocated correctly.
+        //
+        if ((kernels->preCalc == NULL) ||
+                (kernels->preCalc->n != nBasisFunctions)) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a NULL ->preCalc member or\n");
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated a incorrect length ->preCalc member.\n");
+            testStatus = true;
+        } else {
+            for (psS32 k = 0 ; k < kernels->u->n ; k++) {
+                psImage *kerImg = (psImage *) kernels->preCalc->data[k];
+                if (kerImg == NULL) {
+                    printf("TEST ERROR: the %d-th kernel preCalc image is NULL.\n", k);
+                    testStatus = true;
+                } else {
+                    if (kerImg->type.type != PS_TYPE_F32) {
+                        printf("TEST ERROR: preCalc image %d had ioncorrect type.\n", k);
+                        testStatus = true;
+                    }
+                    if ((kerImg->numRows != (1 + (2 * size))) ||
+                            (kerImg->numCols != (1 + (2 * size)))) {
+                        printf("TEST ERROR: preCalc image %d had incorrect size (%d, %d).\n", k,
+                               kerImg->numRows, kerImg->numCols);
+                        testStatus = true;
+                    }
+                }
+            }
+        }
+
+        if (kernels->size != size) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated an incorrect ->size member (%d).\n", kernels->size);
+            testStatus = true;
+        }
+
+        if (kernels->spatialOrder != SpatialOrder) {
+            printf("TEST ERROR: pmSubtractionKernelsAllocISIS() generated an incorrect ->spatialOrder member (%d).\n", kernels->spatialOrder);
+            testStatus = true;
+        }
+    }
+    psFree(sigmas);
+
+    psFree(kernels->u);
+    psFree(kernels->v);
+    psFree(kernels->sigma);
+    psFree(kernels->xOrder);
+    psFree(kernels->yOrder);
+    psFree(kernels->preCalc);
+    psFree(kernels);
+    psFree(orders);
+    return(testStatus);
+}
+
+/*******************************************************************************
+NOTE: This function returns TRUE if there were no errors.
+ ******************************************************************************/
+int test01( void )
+{
+    bool testStatus = false;
+
+    /*
+        testStatus|= testISISAlloc(1, 1, 1, 1);
+        testStatus|= testISISAlloc(2, 2, 2, 2);
+        testStatus|= testISISAlloc(2, 3, 4, 5);
+        testStatus|= testISISAlloc(3, 4, 5, 6);
+    */
+
+    return(!testStatus);
+}
+
+
+/*******************************************************************************
+NOTE: This function returns FALSE if there were no errors.
+ 
+XXX: Can we test to ensure that no stamps overlap?
+ 
+XXX: Test stamp alloc/dealloc functions.
+ ******************************************************************************/
+#define TST02_THRESHOLD 3.0
+#define TST02_MASK_VAL 1
+psBool testFindStamps(psS32 numCols,
+                      psS32 numRows,
+                      psS32 xNum,
+                      psS32 yNum,
+                      psS32 border)
+{
+    printf("Testing pmSubtractionFindStamps(%d, %d, %d, %d, %d)\n",
+           numCols, numRows, xNum, yNum, border);
+    bool testStatus = false;
+
+    // Create a test image and set a single pixel in the center of each stamp.
+    psImage *tstImg = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    int numStamps = 0;
+    PS_IMAGE_SET_F32(tstImg, 0.0);
+    for (psS32 j = 0; j < yNum; j++) {
+        for (psS32 i = 0; i < xNum; i++) {
+            psS32 yMin = border + j * (numRows - 2.0 * border) / yNum;
+            psS32 yMax = PS_MIN(numRows-1, (border + (j + 1) * (numRows - 2.0 * border) / yNum) - 1);
+            psS32 xMin = border + i * (numCols - 2.0 * border) / xNum;
+            psS32 xMax = PS_MIN(numCols-1, (border + (i + 1) * (numCols - 2.0 * border) / xNum) - 1);
+
+            tstImg->data.F32[(yMax+yMin)/2][(xMax+xMin)/2] = TST02_THRESHOLD + (psF32) (i + j);
+            numStamps++;
+        }
+    }
+    psImage *tmpMask= psImageAlloc(numCols, numRows, PS_TYPE_U8);
+    PS_IMAGE_SET_U8(tstImg, 0);
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a NULL psImage.  Should generate error, return NULL.\n");
+    psArray *stamps = pmSubtractionFindStamps(NULL, NULL, tmpMask, TST02_MASK_VAL,
+                      TST02_THRESHOLD, xNum, yNum,
+                      border);
+    if (stamps != NULL) {
+        printf("TEST ERROR: pmSubtractionFindStamps returned a non-NULL psArray.\n");
+        psFree(stamps);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a non-positive xNum.  Should generate error, return NULL.\n");
+    stamps = pmSubtractionFindStamps(NULL, tstImg, tmpMask, TST02_MASK_VAL,
+                                     TST02_THRESHOLD, 0, yNum,
+                                     border);
+    if (stamps != NULL) {
+        printf("TEST ERROR: pmSubtractionFindStamps returned a non-NULL psArray.\n");
+        psFree(stamps);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a non-positive yNum.  Should generate error, return NULL.\n");
+    stamps = pmSubtractionFindStamps(NULL, tstImg, tmpMask, TST02_MASK_VAL,
+                                     TST02_THRESHOLD, xNum, 0,
+                                     border);
+    if (stamps != NULL) {
+        printf("TEST ERROR: pmSubtractionFindStamps returned a non-NULL psArray.\n");
+        psFree(stamps);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with a non-positive border.  Should generate error, return NULL.\n");
+    stamps = pmSubtractionFindStamps(NULL, tstImg, tmpMask, TST02_MASK_VAL,
+                                     TST02_THRESHOLD, xNum, yNum,
+                                     0);
+    if (stamps != NULL) {
+        printf("TEST ERROR: pmSubtractionFindStamps returned a non-NULL psArray.\n");
+        psFree(stamps);
+        testStatus = true;
+    }
+
+    //-------------------------------------------------------------------------
+    printf("Calling with acceptable input parameters, non-NULL mask.\n");
+    stamps = pmSubtractionFindStamps(NULL, tstImg, tmpMask, TST02_MASK_VAL,
+                                     TST02_THRESHOLD, xNum, yNum,
+                                     border);
+    if (stamps == NULL) {
+        printf("TEST ERROR: pmSubtractionFindStamps returned a non-NULL psArray.\n");
+        testStatus = true;
+    } else {
+        if (stamps->n != numStamps) {
+            printf("TEST ERROR: %ld stamps were found, %d were expected.\n",
+                   stamps->n, numStamps);
+            testStatus = true;
+        }
+        for (psS32 s = 0 ; s < stamps->n ; s++) {
+            pmStamp *stamp = (pmStamp *) stamps->data[s];
+            psS32 row = stamp->y;
+            psS32 col = stamp->x;
+            // printf("Stamp %d at (%d, %d) has a value of %f\n", s, row, col, tstImg->data.F32[row][col]);
+            if (tstImg->data.F32[row][col] < TST02_THRESHOLD) {
+                if (stamp->status != PM_STAMP_NONE) {
+                    printf("TEST ERROR: stamp %d had peak value %f (below theshold) and the status was not set to PM_STAMP_NONE.\n",
+                           s, tstImg->data.F32[row][col]);
+                    testStatus = true;
+                }
+            } else {
+                if (stamp->status != PM_STAMP_RECALC) {
+                    printf("TEST ERROR: stamp %d had peak value %f (above theshold) and the status was not set to PM_STAMP_RECALC.\n",
+                           s, tstImg->data.F32[row][col]);
+                    testStatus = true;
+                }
+            }
+        }
+    }
+    psFree(stamps);
+
+    //-------------------------------------------------------------------------
+    printf("Calling with acceptable input parameters, NULL mask.\n");
+    stamps = pmSubtractionFindStamps(NULL, tstImg, NULL, TST02_MASK_VAL,
+                                     TST02_THRESHOLD, xNum, yNum,
+                                     border);
+    if (stamps == NULL) {
+        printf("TEST ERROR: pmSubtractionFindStamps returned a non-NULL psArray.\n");
+        testStatus = true;
+    } else {
+        if (stamps->n != numStamps) {
+            printf("TEST ERROR: %ld stamps were found, %d were expected.\n",
+                   stamps->n, numStamps);
+            testStatus = true;
+        }
+        for (psS32 s = 0 ; s < stamps->n ; s++) {
+            pmStamp *stamp = (pmStamp *) stamps->data[s];
+            psS32 row = stamp->y;
+            psS32 col = stamp->x;
+            //printf("Stamp %d at (%d, %d) has a value of %f\n", s, row, col, tstImg->data.F32[row][col]);
+            if (tstImg->data.F32[row][col] < TST02_THRESHOLD) {
+                if (stamp->status != PM_STAMP_NONE) {
+                    printf("TEST ERROR: stamp %d had peak value %f (below theshold) and the status was not set to PM_STAMP_NONE.\n",
+                           s, tstImg->data.F32[row][col]);
+                    testStatus = true;
+                }
+            } else {
+                if (stamp->status != PM_STAMP_RECALC) {
+                    printf("TEST ERROR: stamp %d had peak value %f (above theshold) and the status was not set to PM_STAMP_RECALC.\n",
+                           s, tstImg->data.F32[row][col]);
+                    testStatus = true;
+                }
+            }
+        }
+    }
+
+    psFree(tstImg);
+    psFree(tmpMask);
+    psFree(stamps);
+
+    return(testStatus);
+}
+
+
+
+/*******************************************************************************
+NOTE: This function returns TRUE if there were no errors.
+ ******************************************************************************/
+int test02( void )
+{
+    bool testStatus = false;
+
+    testStatus|= testFindStamps(100, 100, 2, 2, 2);
+    testStatus|= testFindStamps(100, 100, 10, 10, 2);
+
+    return(!testStatus);
+}
+
+
+psF32 genRanFloat(psF32 low,
+                  psF32 high)
+{
+    psF32 ran1 = (((psF32) (random() % 10000)) / 10000.0);
+    return(low + (ran1 * (high - low)));
+}
+
+//
+// XXX: POIS kernels are producing NANs if the image size is 20 or less.
+//
+// XXX: ISIS kernels are producing NANS if the TST03_ORDER_LENGTH is 2 or larger.
+//
+#define TST03_THRESHOLD  3.0
+#define TST03_MASK_VAL  1
+#define TST03_KERNEL_SIZE 2
+#define TST03_SPATIAL_ORDER 2
+#define TST03_ORDER_LENGTH 1
+#define TST03_SIGMA_LENGTH 1
+#define TST03_PSF_MAX  10.0
+#define TST03_BG 0.0
+#define TST03_IMAGE_SIZE 25
+#define TST03_NUM_COLS  TST03_IMAGE_SIZE
+#define TST03_NUM_ROWS  TST03_IMAGE_SIZE
+#define TST03_NUM_STAMPS 2
+#define TST03_NUM_STAMPS_COLS TST03_NUM_STAMPS
+#define TST03_NUM_STAMPS_ROWS TST03_NUM_STAMPS
+#define TST03_BORDER  TST03_KERNEL_SIZE
+//#define TST03_FOOTPRINT (((TST03_IMAGE_SIZE - (2 * TST03_BORDER)) / TST03_NUM_STAMPS) - TST03_KERNEL_SIZE)
+#define TST03_FOOTPRINT  4
+#define TST03_PSF_WIDTH  (TST03_FOOTPRINT/2 - 1)
+
+/*******************************************************************************
+This routine generates an object in the center of the stamp defined by the
+(xMin, xMax) and (yMin, yMax) boundary.
+ ******************************************************************************/
+psBool genObject(psImage *tstImg,
+                 psImage *refImg,
+                 psS32 xMin,
+                 psS32 xMax,
+                 psS32 yMin,
+                 psS32 yMax)
+{
+    if (((1 + 2 * TST03_PSF_WIDTH) > (xMax - xMin)) ||
+            ((1 + 2 * TST03_PSF_WIDTH) > (yMax - yMin))) {
+        printf("INCORRECT TEST CONFIGURATION: TST03_PSF_WIDTH is too big.\n");
+        printf("TST03_PSF_WIDTH is %d: (xMin - xMax) is %d\n", TST03_PSF_WIDTH, (xMax - xMin));
+        return(FALSE);
+    }
+    //
+    // This code basically creates a peak at the center of the stamp with
+    // a height of TST03_PSF_MAX
+    //
+    psS32 xCenter = (xMax + xMin) / 2;
+    psS32 yCenter = (yMax + yMin) / 2;
+    psF32 subImageWidth = 1.0 + sqrtf(PS_SQR(((psF32) ((yMax-yMin)/2))) + PS_SQR(((psF32) ((xMax-xMin)/2))));
+    for (psS32 y = yMin ; y <= yMax ; y++) {
+        for (psS32 x = xMin ; x <= xMax ; x++) {
+            psF32 dist = sqrtf(PS_SQR((psF32) (y - yCenter)) + PS_SQR((psF32) (x - xCenter)));
+            psF32 pixel = TST03_PSF_MAX * PS_SQR(((psF32) (subImageWidth - dist)) / ((psF32) subImageWidth));
+            if (pixel < 0.0) {
+                pixel = 0.0;
+            }
+            refImg->data.F32[y][x] = pixel + TST03_BG;
+            tstImg->data.F32[y][x] = pixel + TST03_BG;
+            // Add some noise for the test image.
+            tstImg->data.F32[y][x]+= genRanFloat(0.0, 1.0);
+        }
+    }
+
+    return(TRUE);
+}
+
+/*******************************************************************************
+NOTE: This function returns FALSE if there were no errors.
+ 
+XXX: We should use a larger variety of input parameter configurations.
+ 
+I test the following functions here (since they linearly rely on a set of data
+structures that the previoues ones generate):
+    pmSubtractionCalculateEquation()
+    pmSubtractionSolveEquation()
+    pmSubtractionRejectStamps()
+    pmSubtractionKernelImage()
+ ******************************************************************************/
+psBool testSubCalcEqu(psS32 numCols,
+                      psS32 numRows,
+                      psS32 xNum,
+                      psS32 yNum,
+                      psS32 border,
+                      pmSubtractionKernelsType KernelType)
+{
+    printf("Testing pmSubtractionCalculateEquation(): \n");
+    printf("    image size is (%d, %d)\n", numRows, numCols);
+    printf("    num stamps is (%d, %d).  Border is %d\n", yNum, xNum, border);
+    if (KernelType == PM_SUBTRACTION_KERNEL_POIS) {
+        printf("   kernel type is PM_SUBTRACTION_KERNEL_POIS.\n");
+    } else if (KernelType == PM_SUBTRACTION_KERNEL_ISIS) {
+        printf("   kernel type is PM_SUBTRACTION_KERNEL_ISIS.\n");
+    }
+    bool testStatus = false;
+
+    // Create a test image and set a single pixel in the center of each stamp.
+    psImage *tstImg = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    psImage *refImg = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    psImage *maskImg = psImageAlloc(numCols, numRows, PS_TYPE_U8);
+    PS_IMAGE_SET_F32(tstImg, 0.0);
+    PS_IMAGE_SET_F32(maskImg, 0);
+    for (psS32 j = 0; j < yNum; j++) {
+        for (psS32 i = 0; i < xNum; i++) {
+            psS32 yMin = border + j * (numRows - 2.0 * border) / yNum;
+            psS32 yMax = PS_MIN(numRows-1, (border + (j + 1) * (numRows - 2.0 * border) / yNum) - 1);
+            psS32 xMin = border + i * (numCols - 2.0 * border) / xNum;
+            psS32 xMax = PS_MIN(numCols-1, (border + (i + 1) * (numCols - 2.0 * border) / xNum) - 1);
+
+            genObject(tstImg, refImg, xMin, xMax, yMin, yMax);
+        }
+    }
+    printf("Generating stamps...\n");
+    psArray *stamps = pmSubtractionFindStamps(NULL, tstImg, NULL, TST03_MASK_VAL,
+                      TST03_THRESHOLD, xNum, yNum,
+                      border);
+
+    //
+    // PsVectors sigmas and orders are for ISIS kernels only.
+    //
+    psVector *sigmas = psVectorAlloc(TST03_SIGMA_LENGTH, PS_TYPE_F32);
+    for (psS32 i = 0 ; i < sigmas->n ; i++) {
+        sigmas->data.F32[i] = 1.0 + (psF32) i;
+    }
+    psVector *orders = psVectorAlloc(TST03_ORDER_LENGTH, PS_TYPE_S32);
+    for (psS32 i = 0 ; i < orders->n ; i++) {
+        orders->data.S32[i] = i + 2;
+    }
+
+    //
+    // Create the subtraction kernels
+    //
+    printf("Generating kernel basis functions...\n");
+    psSubtractionKernels *myKernels = NULL;
+    if (KernelType == PM_SUBTRACTION_KERNEL_POIS) {
+        myKernels = pmSubtractionKernelsAllocPOIS(TST03_KERNEL_SIZE, TST03_SPATIAL_ORDER);
+    } else if (KernelType == PM_SUBTRACTION_KERNEL_ISIS) {
+        myKernels = pmSubtractionKernelsAllocISIS(sigmas, orders, TST03_KERNEL_SIZE, TST03_SPATIAL_ORDER);
+    }
+
+    if ((stamps == NULL) ||
+            (myKernels == NULL)) {
+        printf("TEST ERROR: stamps or myKernels is NULL.\n");
+        testStatus = true;
+    } else {
+        //-------------------------------------------------------------------------
+        printf("Calling with a NULL psArray stamps.  Should generate error, return FALSE.\n");
+        psBool rc = pmSubtractionCalculateEquation(NULL, refImg, tstImg, myKernels, TST03_FOOTPRINT);
+        if (rc == TRUE) {
+            printf("TEST ERROR: pmSubtractionCalculateEquation() returned TRUE.\n");
+            testStatus = true;
+        }
+
+        //-------------------------------------------------------------------------
+        printf("Calling with a NULL reference images.  Should generate error, return FALSE.\n");
+        rc = pmSubtractionCalculateEquation(stamps, NULL, tstImg, myKernels, TST03_FOOTPRINT);
+        if (rc == TRUE) {
+            printf("TEST ERROR: pmSubtractionCalculateEquation() returned TRUE.\n");
+            testStatus = true;
+        }
+
+        //-------------------------------------------------------------------------
+        printf("Calling with a NULL input images.  Should generate error, return FALSE.\n");
+        rc = pmSubtractionCalculateEquation(stamps, refImg, NULL, myKernels, TST03_FOOTPRINT);
+        if (rc == TRUE) {
+            printf("TEST ERROR: pmSubtractionCalculateEquation() returned TRUE.\n");
+            testStatus = true;
+        }
+
+        //-------------------------------------------------------------------------
+        printf("Calling with a NULL kernel basis functions.  Should generate error, return FALSE.\n");
+        rc = pmSubtractionCalculateEquation(stamps, refImg, tstImg, NULL, TST03_FOOTPRINT);
+        if (rc == TRUE) {
+            printf("TEST ERROR: pmSubtractionCalculateEquation() returned TRUE.\n");
+            testStatus = true;
+        }
+
+        //-------------------------------------------------------------------------
+        printf("Calling with acceptable input parameters.  Should return TRUE.\n");
+
+        rc = pmSubtractionCalculateEquation(stamps, refImg, tstImg, myKernels, TST03_FOOTPRINT);
+        if (rc != TRUE) {
+            printf("TEST ERROR: pmSubtractionCalculateEquation() returned FALSE.\n");
+            testStatus = true;
+        } else {
+
+            if (0) {
+                for (psS32 s = 0 ; s < stamps->n ; s++) {
+                    printf("********************************* Stamp %d *********************************\n", s);
+                    pmStamp *stamp = (pmStamp *) stamps->data[s];
+                    if (stamp->vector != NULL) {
+                        PS_VECTOR_PRINT_F64(stamp->vector);
+                    }
+                    if (stamp->matrix != NULL) {
+                        printf("Stamp matrix size is (%d, %d)\n", stamp->matrix->numRows, stamp->matrix->numCols);
+                        PS_IMAGE_PRINT_F64(stamp->matrix);
+                    }
+                }
+            }
+
+
+            //-------------------------------------------------------------------------
+            printf("Calling pmSubtractionSolveEquation() with a NULL stamp argument.  Should generate error, return FALSE.\n");
+            psVector *solution = pmSubtractionSolveEquation(NULL, NULL);
+            if (solution != NULL) {
+                printf("TEST ERROR: pmSubtractionSolveEquation() returned non-NULL.\n");
+                testStatus = true;
+            }
+
+            //-------------------------------------------------------------------------
+            printf("Calling pmSubtractionSolveEquation() with acceptable input parameters.  Should return non-NULL.\n");
+            solution = pmSubtractionSolveEquation(NULL, stamps);
+            if (solution == NULL) {
+                printf("TEST ERROR: pmSubtractionSolveEquation() returned NULL.\n");
+                testStatus = true;
+            } else {
+                printf("The solution vector is:\n");
+                for (psS32 i = 0 ; i < solution->n ; i++) {
+                    printf("(%.2f) ", solution->data.F64[i]);
+                }
+                printf("\n");
+
+                //-------------------------------------------------------------------------
+                printf("Calling pmSubtractionRejectStamps() with acceptable input parameters.  Should return TRUE.\n");
+                rc = pmSubtractionRejectStamps(stamps, maskImg, 0xff, TST03_FOOTPRINT, 1.0, refImg,
+                                               tstImg, solution, myKernels);
+                if (rc != TRUE) {
+                    printf("TEST ERROR: pmSubtractionRejectStamps() returned FALSE.\n");
+                    testStatus = true;
+                } else {}
+
+                //-------------------------------------------------------------------------
+                printf("Calling pmSubtractionKernelImage() with NULL solution.  Should generate error, return NULL.\n");
+                psImage *kernelImg = pmSubtractionKernelImage(NULL, NULL, myKernels, 0.0, 0.0);
+                if (kernelImg != NULL) {
+                    printf("TEST ERROR: pmSubtractionKernelImage() returned non-NULL.\n");
+                    testStatus = true;
+                }
+                free(kernelImg);
+
+                //-------------------------------------------------------------------------
+                printf("Calling pmSubtractionKernelImage() with NULL kernels.  Should generate error, return NULL.\n");
+                kernelImg = pmSubtractionKernelImage(NULL, solution, NULL, 0.0, 0.0);
+                if (kernelImg != NULL) {
+                    printf("TEST ERROR: pmSubtractionKernelImage() returned non-NULL.\n");
+                    testStatus = true;
+                }
+                free(kernelImg);
+
+                //-------------------------------------------------------------------------
+                printf("Calling pmSubtractionKernelImage() unallowable x value.  Should generate error, return NULL.\n");
+                kernelImg = pmSubtractionKernelImage(NULL, solution, myKernels, -2.0, 0.0);
+                if (kernelImg != NULL) {
+                    printf("TEST ERROR: pmSubtractionKernelImage() returned non-NULL.\n");
+                    testStatus = true;
+                }
+                free(kernelImg);
+
+                //-------------------------------------------------------------------------
+                printf("Calling pmSubtractionKernelImage() unallowable x value.  Should generate error, return NULL.\n");
+                kernelImg = pmSubtractionKernelImage(NULL, solution, myKernels, 2.0, 0.0);
+                if (kernelImg != NULL) {
+                    printf("TEST ERROR: pmSubtractionKernelImage() returned non-NULL.\n");
+                    testStatus = true;
+                }
+                free(kernelImg);
+
+                //-------------------------------------------------------------------------
+                printf("Calling pmSubtractionKernelImage() unallowable y value.  Should generate error, return NULL.\n");
+                kernelImg = pmSubtractionKernelImage(NULL, solution, myKernels, 0.0, -2.0);
+                if (kernelImg != NULL) {
+                    printf("TEST ERROR: pmSubtractionKernelImage() returned non-NULL.\n");
+                    testStatus = true;
+                }
+                free(kernelImg);
+
+                //-------------------------------------------------------------------------
+                printf("Calling pmSubtractionKernelImage() unallowable y value.  Should generate error, return NULL.\n");
+                kernelImg = pmSubtractionKernelImage(NULL, solution, myKernels, 0.0, 2.0);
+                if (kernelImg != NULL) {
+                    printf("TEST ERROR: pmSubtractionKernelImage() returned non-NULL.\n");
+                    testStatus = true;
+                }
+                free(kernelImg);
+
+                //-------------------------------------------------------------------------
+                printf("Calling pmSubtractionKernelImage() with acceptable input parameters.  Should return a psImage.\n");
+                kernelImg = pmSubtractionKernelImage(NULL, solution, myKernels, 0.5, 0.5);
+                if (kernelImg == NULL) {
+                    printf("TEST ERROR: pmSubtractionKernelImage() returned NULL.\n");
+                    testStatus = true;
+                } else {
+                    for (psS32 row = 0 ; row < kernelImg->numRows; row++) {
+                        for (psS32 col = 0 ; col < kernelImg->numCols; col++) {
+                            printf("%f ", kernelImg->data.F32[row][col]);
+                        }
+                        printf("\n");
+                    }
+                }
+                free(kernelImg);
+
+                psFree(solution);
+            }
+        }
+    }
+    psFree(tstImg);
+    psFree(refImg);
+    psFree(stamps);
+    psFree(myKernels);
+    psFree(orders);
+    psFree(sigmas);
+
+    return(testStatus);
+}
+
+/*******************************************************************************
+NOTE: This function returns TRUE if there were no errors.
+ ******************************************************************************/
+int test03( void )
+{
+    bool testStatus = false;
+
+
+    srand(1995);
+    if (1)
+        testStatus|= testSubCalcEqu(TST03_NUM_COLS,
+                                    TST03_NUM_ROWS,
+                                    TST03_NUM_STAMPS_COLS,
+                                    TST03_NUM_STAMPS_ROWS,
+                                    TST03_BORDER,
+                                    PM_SUBTRACTION_KERNEL_POIS);
+
+
+    srand(1995);
+    if (1)
+        testStatus|= testSubCalcEqu(TST03_NUM_COLS,
+                                    TST03_NUM_ROWS,
+                                    TST03_NUM_STAMPS_COLS,
+                                    TST03_NUM_STAMPS_ROWS,
+                                    TST03_BORDER,
+                                    PM_SUBTRACTION_KERNEL_ISIS);
+
+
+
+    return(!testStatus);
+}
Index: /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/tst_pmSubtractBias.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/tst_pmSubtractBias.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/tst_pmSubtractBias.c	(revision 22115)
@@ -0,0 +1,1097 @@
+/** @file tst_pmSubtractBias.c
+ *
+ *  @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
+ *  row in the input image.
+ * test04: 
+ *
+ *  @author GLG, MHPCC
+ *
+ *  XXX: Memory leaks are not being detected.
+ *
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-29 21:57:31 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include "psTest.h"
+#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);
+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},
+                              {NULL}
+                          };
+
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    return !runTestSuite(stderr, "Test Point Driver", tests, argc, argv);
+}
+
+#define NUM_ROWS 8
+#define NUM_COLS 8
+/******************************************************************************
+doSubtractBiasFullFrame(): a sample pmReadout as well as a bias image are
+created and the bias image is subtracted from the pmReadout.
+ *****************************************************************************/
+int doSubtractBiasFullFrame(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);
+    //    pmReadout *myReadout = pmReadoutAlloc(numCols, numRows, tmpImage1);
+    //    pmReadout *myBias = pmReadoutAlloc(numCols, numRows, tmpImage2);
+    pmReadout *myReadout = pmReadoutAlloc(NULL);
+    pmReadout *myBias = pmReadoutAlloc(NULL);
+    myReadout->image = tmpImage1;
+    myBias->image = tmpImage2;
+
+
+    printPositiveTestHeader(stdout, "pmSubtractBias", "doSubtractBiasFullFrame");
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            myReadout->image->data.F32[i][j] = (float) (i + j);
+            myBias->image->data.F32[i][j] = 1.0;
+        }
+    }
+
+    myReadout = pmSubtractBias(myReadout, NULL, NULL, PM_OVERSCAN_NONE, NULL,
+                               0, PM_FIT_NONE, myBias);
+
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            expect = ((float) (i + j)) - 1.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(myBias);
+    printFooter(stdout, "pmSubtractBias", "doSubtractBiasFullFrame", true);
+    return(testStatus);
+}
+
+
+int test00( void )
+{
+    int testStatus = 0;
+
+    testStatus |= doSubtractBiasFullFrame(1, 1);
+    testStatus |= doSubtractBiasFullFrame(NUM_COLS, 1);
+    testStatus |= doSubtractBiasFullFrame(1, NUM_ROWS);
+    testStatus |= doSubtractBiasFullFrame(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 then subtracted
+from the pmReadout.
+ *****************************************************************************/
+int doSubtractFullOverscans(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(numCols, numRows, tmpImage1);
+    pmReadout *myReadout = pmReadoutAlloc(NULL);
+    myReadout->image = tmpImage1;
+
+
+    printPositiveTestHeader(stdout, "pmSubtractBias", "doSubtractFullOverscans");
+    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);
+
+    myReadout = pmSubtractBias(myReadout, NULL, list, PM_OVERSCAN_ALL, 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", "doSubtractFullOverscans", true);
+    return(testStatus);
+}
+
+int test01( void )
+{
+    int testStatus = 0;
+
+    testStatus |= doSubtractFullOverscans(1, 1);
+    testStatus |= doSubtractFullOverscans(1, NUM_ROWS);
+    testStatus |= doSubtractFullOverscans(NUM_COLS, 1);
+    testStatus |= doSubtractFullOverscans(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)
+{
+    int i;
+    int j;
+    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);
+    psImage *tmpImage2Short = psImageAlloc(numCols-1, numRows-1, PS_TYPE_F32);
+    psImage *tmpImage3Short = psImageAlloc(numCols-1, numRows-1, PS_TYPE_F32);
+    psImage *tmpImage4Short = psImageAlloc(numCols-1, numRows-1, PS_TYPE_F32);
+    pmReadout *myReadout = pmReadoutAlloc(NULL);
+    myReadout->image = tmpImage1;
+    pmReadout *rc = NULL;
+    psList *list;
+    psList *listShort;
+    psStats *stat = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    psImage *tmpImage5 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    pmReadout *myBias = pmReadoutAlloc(NULL);
+    myBias->image = tmpImage5;
+    printPositiveTestHeader(stdout, "pmSubtractBias", "Testing input parameter error conditions");
+
+    psImage *tmpImage5ShortRows = psImageAlloc(numCols, numRows-1, PS_TYPE_F32);
+    pmReadout *myBiasShortRows = pmReadoutAlloc(NULL);
+    myBiasShortRows->image = tmpImage5ShortRows;
+    psImage *tmpImage5ShortCols = psImageAlloc(numCols-1, numRows, PS_TYPE_F32);
+    pmReadout *myBiasShortCols = pmReadoutAlloc(NULL);
+    myBiasShortCols->image = tmpImage5ShortCols;
+
+    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;
+            myBias->image->data.F32[i][j] = 1.0;
+        }
+    }
+    list = psListAlloc(tmpImage2);
+    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++) {
+            tmpImage2Short->data.F32[i][j] = 3.0;
+            tmpImage3Short->data.F32[i][j] = 4.0;
+            tmpImage4Short->data.F32[i][j] = 5.0;
+        }
+    }
+    listShort = psListAlloc(tmpImage2Short);
+    psListAdd(listShort, PS_LIST_HEAD, tmpImage3Short);
+    psListAdd(listShort, PS_LIST_HEAD, tmpImage4Short);
+    for (i=0;i<numRows-1;i++) {
+        for (j=0;j<numCols;j++) {
+            myBiasShortRows->image->data.F32[i][j] = 1.0;
+        }
+    }
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols-1;j++) {
+            myBiasShortCols->image->data.F32[i][j] = 1.0;
+        }
+    }
+
+
+    printf("------------------------------------------------------------------\n");
+    printf("Calling pmSubtractBias() with NULL overscan list and PM_OVERSCAN_ALL.  Should generate error.\n");
+    rc = pmSubtractBias(myReadout, NULL, NULL, PM_OVERSCAN_ALL, stat, 0, PM_FIT_NONE, NULL);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractBias() did not return input pmReadout.\n");
+        testStatus = false;
+    }
+
+    printf("------------------------------------------------------------------\n");
+    printf("Calling pmSubtractBias() with NULL overscan list and PM_OVERSCAN_ROWS.  Should generate error.\n");
+    rc = pmSubtractBias(myReadout, NULL, NULL, PM_OVERSCAN_ROWS, stat, 0, PM_FIT_NONE, NULL);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractBias() did not return input pmReadout.\n");
+        testStatus = false;
+        psFree(rc);
+    }
+
+    printf("------------------------------------------------------------------\n");
+    printf("Calling pmSubtractBias() with NULL overscan list and PM_OVERSCAN_COLUMNS.  Should generate error.\n");
+    rc = pmSubtractBias(myReadout, NULL, NULL, PM_OVERSCAN_COLUMNS, stat, 0, PM_FIT_NONE, NULL);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractBias() did not return input pmReadout.\n");
+        testStatus = false;
+        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++) {
+            psF32 expect = ((float) (i + j)) - 1.0;
+            psF32 actual = rc->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;
+            }
+
+            // Restore myReadout for next test.
+            myReadout->image->data.F32[i][j] = (float) (i + j);
+        }
+    }
+
+    /* XXX: This does not seem to be a requirement.
+        printf("------------------------------------------------------------------\n");
+        printf("Calling pmSubtractBias() with NULL overscan list and PM_OVERSCAN_NONE.  Should generate warning.\n");
+        rc = pmSubtractBias(myReadout, NULL, NULL, PM_OVERSCAN_NONE, stat,
+                            0, PM_FIT_NONE, myBias);
+     
+        for (i=0;i<numRows;i++) {
+            for (j=0;j<numCols;j++) {
+                psF32 expect = ((float) (i + j)) - 1.0;
+                psF32 actual = rc->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;
+                }
+     
+                // Restore myReadout for next test.
+                myReadout->image->data.F32[i][j] = (float) (i + j);
+            }
+        }
+    */
+
+    printf("------------------------------------------------------------------\n");
+    printf("Calling pmSubtractBias() with PM_OVERSCAN_NONE and PM_FIT_POLYNOMIAL.  Should generate Warning.\n");
+    rc = pmSubtractBias(myReadout, NULL, NULL, PM_OVERSCAN_NONE, stat, 0, PM_FIT_POLYNOMIAL, myBias);
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            psF32 expect = ((float) (i + j)) - 1.0;
+            psF32 actual = rc->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;
+            }
+
+            // 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");
+    rc = pmSubtractBias(myReadout, NULL, NULL, PM_OVERSCAN_NONE, stat, 0, PM_FIT_SPLINE, myBias);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractBias() did not return input pmReadout.\n");
+        testStatus = false;
+        psFree(rc);
+    }
+
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            psF32 expect = ((float) (i + j)) - 1.0;
+            psF32 actual = rc->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;
+            }
+
+            // 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");
+    stat->options|= PS_STAT_SAMPLE_MEDIAN;
+    myReadout = pmSubtractBias(myReadout, NULL, list, PM_OVERSCAN_ALL, stat,
+                               0, PM_FIT_NONE, NULL);
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            psF32 expect = ((float) (i + j)) - 12.0;
+            psF32 actual = rc->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;
+            }
+        }
+    }
+    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);
+    /*
+        for (i=0;i<numRows;i++) {
+            for (j=0;j<numCols;j++) {
+                psF32 expect = ((float) (i + j)) - 12.0;
+                psF32 actual = rc->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;
+                }
+            }
+        }
+    */
+
+    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);
+    /*
+        for (i=0;i<numRows;i++) {
+            for (j=0;j<numCols;j++) {
+                psF32 expect = ((float) (i + j)) - 12.0;
+                psF32 actual = rc->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;
+                }
+            }
+        }
+    */
+
+    printf("------------------------------------------------------------------\n");
+    printf("Calling pmSubtractBias() undersize bias image (short rows).  Should generate Error.\n");
+    myReadout = pmSubtractBias(myReadout, NULL, NULL, PM_OVERSCAN_NONE, NULL,
+                               0, PM_FIT_NONE, myBiasShortRows);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractBias() did not return input pmReadout.\n");
+        testStatus = false;
+        psFree(rc);
+    }
+
+
+    printf("------------------------------------------------------------------\n");
+    printf("Calling pmSubtractBias() undersize bias image (short columns).  Should generate Error.\n");
+    myReadout = pmSubtractBias(myReadout, NULL, NULL, PM_OVERSCAN_NONE, NULL,
+                               0, PM_FIT_NONE, myBiasShortCols);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractBias() did not return input pmReadout.\n");
+        testStatus = false;
+        psFree(rc);
+    }
+
+    printf("------------------------------------------------------------------\n");
+    printf("Calling pmSubtractBias() with bogus PM_FIT.  Should generate Error.\n");
+    myReadout = pmSubtractBias(myReadout, NULL, list, PM_OVERSCAN_ROWS, stat,
+                               0, 54321, NULL);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractBias() did not return input pmReadout.\n");
+        testStatus = false;
+        psFree(rc);
+    }
+
+    printf("------------------------------------------------------------------\n");
+    printf("Calling pmSubtractBias() with bogus overScanAxis.  Should generate Error.\n");
+    myReadout = pmSubtractBias(myReadout, NULL, list, 54321, stat,
+                               0, PM_FIT_NONE, NULL);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractBias() did not return input pmReadout.\n");
+        testStatus = false;
+        psFree(rc);
+    }
+
+    /*
+        for (i=0;i<numRows;i++) {
+            for (j=0;j<numCols;j++) {
+                psF32 expect = ((float) (i + j)) - 12.0;
+                psF32 actual = rc->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;
+                }
+            }
+        }
+    */
+
+    printf("------------------------------------------------------------------\n");
+    psFree(myReadout);
+    psFree(tmpImage2);
+    psFree(tmpImage3);
+    psFree(tmpImage4);
+    psFree(tmpImage2Short);
+    psFree(tmpImage3Short);
+    psFree(tmpImage4Short);
+    psFree(myBias);
+    psFree(myBiasShortRows);
+    psFree(myBiasShortCols);
+    psFree(stat);
+    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.
+ *****************************************************************************/
+int doSubtractFullOverscanColumnsGeneric(int imageNumCols,
+        int imageNumRows,
+        int overscanNumCols,
+        int overscanNumRows,
+        pmOverscanAxis overscanaxis,
+        pmFit fit,
+        psS32 nBin)
+{
+    int i;
+    int j;
+    float actual;
+    float expect;
+    int testStatus = 0;
+
+    psImage *tmpImage1 = psImageAlloc(imageNumCols, imageNumRows, PS_TYPE_F32);
+    //    pmReadout *myReadout = pmReadoutAlloc(imageNumCols, imageNumRows, tmpImage1);
+    pmReadout *myReadout = pmReadoutAlloc(NULL);
+    myReadout->image = tmpImage1;
+    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;
+            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);
+    psFree(mySpline);
+
+    printFooter(stdout, "pmSubtractBias", "Column Overscans", true);
+    return(testStatus);
+}
+
+int testX( void )
+{
+    int testStatus = 0;
+
+    // imageNumCols, imageNumRows, overscanNumCols, overscanNumRows,
+    // overscanaxis, fit, nBin
+
+    //
+    // Overscan images are same size, no fit, bin factor is 1.
+    //
+    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
+                  NUM_COLS, NUM_ROWS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_NONE, 1);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
+                  NUM_COLS, NUM_ROWS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_NONE, 1);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
+                  NUM_COLS, NUM_ROWS,
+                  PM_OVERSCAN_ROWS,
+                  PM_FIT_NONE, 1);
+
+    //
+    // Overscan images are same size, no fit, bin factor is 2.
+    //
+    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
+                  NUM_COLS, NUM_ROWS,
+                  PM_OVERSCAN_ALL,
+                  PM_FIT_NONE, 2);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
+                  NUM_COLS, NUM_ROWS,
+                  PM_OVERSCAN_COLUMNS,
+                  PM_FIT_NONE, 2);
+
+    testStatus |= doSubtractFullOverscanColumnsGeneric(NUM_COLS, NUM_ROWS,
+                  NUM_COLS, NUM_ROWS,
+                  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);
+}
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/tst_pmSubtractSky.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/tst_pmSubtractSky.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/tst_pmSubtractSky.c	(revision 22115)
@@ -0,0 +1,366 @@
+/** @file tst_pmSubtractSky.c
+ *
+ *  @brief Contains the tests for pmSubtractSky.c:
+ *
+ * test00: This code will ...
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-09-28 20:42:52 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+
+
+#include "psTest.h"
+#include "pslib.h"
+#include "pmSubtractSky.h"
+#define NUM_ROWS 512
+#define NUM_COLS 512
+#define POLY_X_ORDER 3
+#define POLY_Y_ORDER 3
+#define ERROR_TOLERANCE 1.0
+#define OBJECT_INTENSITY 2000.0
+static int test00(void);
+static int test01(void);
+testDescription tests[] = {
+                              {test00, 000, "pmSubtractSky", 0, false},
+                              {test01, 000, "pmSubtractSky: warning, error messages", 0, false},
+                              {NULL}
+                          };
+
+float func(int i, int j)
+{
+    return((float) (i + j));
+}
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    return !runTestSuite(stderr, "Test Point Driver", tests, argc, argv);
+    //    test00();
+}
+
+/******************************************************************************
+ *****************************************************************************/
+int doSubtractSkySimple(int numCols, int numRows, int binFactor)
+{
+    int i;
+    int j;
+    int testStatus = 0;
+    psImage *tmpImageF32 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    //    pmReadout *myReadout = pmReadoutAlloc(numCols, numRows, tmpImageF32);
+    pmReadout *myReadout = pmReadoutAlloc(NULL);
+    myReadout->image = tmpImageF32;
+    psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    psPolynomial2D *myPoly = psPolynomial2DAlloc(POLY_X_ORDER, POLY_Y_ORDER, PS_POLYNOMIAL_ORD);
+
+    printPositiveTestHeader(stdout, "pmSubtractSky", "doSubtractSkySimple");
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            myReadout->image->data.F32[i][j] = func(i, j);
+        }
+    }
+
+    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;
+            }
+        }
+    }
+    psFree(myReadout);
+    psFree(myStats);
+    psFree(myPoly);
+    printFooter(stdout, "pmSubtractSky", "doSubtractSkySimple", true);
+    return(testStatus);
+}
+
+/******************************************************************************
+ *****************************************************************************/
+int doSubtractSkyWithObjects(int numCols, int numRows, int binFactor)
+{
+    int i;
+    int j;
+    int testStatus = 0;
+    psImage *tmpImageF32 = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    //    pmReadout *myReadout = pmReadoutAlloc(numCols, numRows, tmpImageF32);
+    pmReadout *myReadout = pmReadoutAlloc(NULL);
+    myReadout->image = tmpImageF32;
+    psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    psPolynomial2D *myPoly = psPolynomial2DAlloc(POLY_X_ORDER, POLY_Y_ORDER, PS_POLYNOMIAL_ORD);
+    psImage *trueImage = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    psF32 errorTolerance = ERROR_TOLERANCE * ((psF32) binFactor);
+
+    printPositiveTestHeader(stdout, "pmSubtractSky", "doSubtractSkyWithObjects");
+    for (i=0;i<numRows;i++) {
+        for (j=0;j<numCols;j++) {
+            myReadout->image->data.F32[i][j] = func(i, j);
+        }
+    }
+    // We insert a few bright spots in the image.
+    myReadout->image->data.F32[NUM_ROWS/4][NUM_COLS/4]+= OBJECT_INTENSITY;
+    myReadout->image->data.F32[NUM_ROWS/4][3*NUM_COLS/4]+= OBJECT_INTENSITY;
+    myReadout->image->data.F32[3*NUM_ROWS/4][NUM_COLS/4]+= OBJECT_INTENSITY;
+    myReadout->image->data.F32[3*NUM_ROWS/4][3*NUM_COLS/4]+= OBJECT_INTENSITY;
+    PS_IMAGE_SET_F32(trueImage, 0.0);
+    trueImage->data.F32[NUM_ROWS/4][NUM_COLS/4]+= OBJECT_INTENSITY;
+    trueImage->data.F32[NUM_ROWS/4][3*NUM_COLS/4]+= OBJECT_INTENSITY;
+    trueImage->data.F32[3*NUM_ROWS/4][NUM_COLS/4]+= OBJECT_INTENSITY;
+    trueImage->data.F32[3*NUM_ROWS/4][3*NUM_COLS/4]+= OBJECT_INTENSITY;
+
+    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;
+            }
+        }
+    }
+    psFree(myReadout);
+    psFree(myStats);
+    psFree(myPoly);
+    psFree(trueImage);
+    printFooter(stdout, "pmSubtractSky", "doSubtractSkyWithObjects", true);
+    return(testStatus);
+}
+
+
+int test00( void )
+{
+    int testStatus = 0;
+
+    psTraceSetLevel(".", 0);
+
+    // Bin Factor == 1
+    printf("doSubtractSkySimple(1, 1, 1)\n");
+    testStatus |= doSubtractSkySimple(1, 1, 1);
+    printf("doSubtractSkySimple(NUM_COLS, 1, 1)\n");
+    testStatus |= doSubtractSkySimple(NUM_COLS, 1, 1);
+
+    printf("doSubtractSkySimple(1, NUM_ROWS, 1)\n");
+    testStatus |= doSubtractSkySimple(1, NUM_ROWS, 1);
+    printf("doSubtractSkySimple(NUM_COLS, NUM_ROWS, 1)\n");
+    testStatus |= doSubtractSkySimple(NUM_COLS, NUM_ROWS, 1);
+
+    // Bin Factor == 2
+    printf("doSubtractSkySimple(1, 1, 2)\n");
+    testStatus |= doSubtractSkySimple(1, 1, 2);
+    printf("doSubtractSkySimple(NUM_COLS, 1, 2)\n");
+    testStatus |= doSubtractSkySimple(NUM_COLS, 1, 2);
+    printf("doSubtractSkySimple(1, NUM_ROWS, 2)\n");
+    testStatus |= doSubtractSkySimple(1, NUM_ROWS, 2);
+    printf("doSubtractSkySimple(NUM_COLS, NUM_ROWS, 2)\n");
+    testStatus |= doSubtractSkySimple(NUM_COLS, NUM_ROWS, 2);
+
+    printf("doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 1)\n");
+    testStatus |= doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 1);
+    printf("doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 2)\n");
+    testStatus |= doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 2);
+    printf("doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 4)\n");
+    testStatus |= doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 8);
+
+    printf("doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 8)\n");
+    testStatus |= doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 8);
+    printf("doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 16)\n");
+    testStatus |= doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 16);
+    printf("doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 32)\n");
+    testStatus |= doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 32);
+    printf("doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 64)\n");
+    testStatus |= doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 64);
+    printf("doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 128)\n");
+    testStatus |= doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 128);
+    printf("doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 256)\n");
+    testStatus |= doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 256);
+
+    return(testStatus);
+}
+
+#define NUM_ROWS_SMALL 16
+#define NUM_COLS_SMALL 16
+int test01( void )
+{
+    int testStatus = 0;
+    psS32 i;
+    psS32 j;
+    psImage *tmpImageF32 = psImageAlloc(NUM_COLS_SMALL, NUM_ROWS_SMALL, PS_TYPE_F32);
+    psImage *tmpImageF64 = psImageAlloc(NUM_COLS_SMALL, NUM_ROWS_SMALL, PS_TYPE_F64);
+    //    pmReadout *myReadout = pmReadoutAlloc(NUM_COLS_SMALL, NUM_ROWS_SMALL, tmpImageF32);
+    pmReadout *myReadout = pmReadoutAlloc(NULL);
+    myReadout->image = tmpImageF32;
+    pmReadout *rc = NULL;
+    psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+    psPolynomial2D *myPoly = psPolynomial2DAlloc(POLY_X_ORDER, POLY_Y_ORDER, PS_POLYNOMIAL_ORD);
+
+    printPositiveTestHeader(stdout, "pmSubtractSky", "Testing bad input parameter conditions.");
+    for (i=0;i<NUM_ROWS_SMALL;i++) {
+        for (j=0;j<NUM_COLS_SMALL;j++) {
+            myReadout->image->data.F32[i][j] = func(i, j);
+        }
+    }
+
+    printf("----------------------------------------------------------------\n");
+    printf("Calling pmSubtractSky() with NULL pmReadout.  Should error.\n\n");
+    rc = pmSubtractSky(NULL, (void *) myPoly, PM_FIT_POLYNOMIAL, 1, myStats, 2.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmSubtractSky() returned a non-NULL pmReadout\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------\n");
+    printf("Calling pmSubtractSky() with NULL pmReadout->image.  Should error.\n\n");
+    myReadout->image = NULL;
+    rc = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_POLYNOMIAL, 1, myStats, 2.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmSubtractSky() returned a non-NULL pmReadout\n");
+        testStatus = false;
+    }
+    myReadout->image = tmpImageF32;
+
+    printf("----------------------------------------------------------------\n");
+    printf("Calling pmSubtractSky() with PS_TYPE_F64 pmReadout->image.  Should error.\n\n");
+    myReadout->image = tmpImageF64;
+    rc = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_POLYNOMIAL, 1, myStats, 2.0);
+    if (rc != NULL) {
+        printf("TEST ERROR: pmSubtractSky() returned a non-NULL pmReadout\n");
+        testStatus = false;
+    }
+    myReadout->image = tmpImageF32;
+
+    printf("----------------------------------------------------------------\n");
+    printf("Calling pmSubtractSky() with NULL fitSpec.  Should return image, no ERROR, no WARNING.\n\n");
+    rc = pmSubtractSky(myReadout, NULL, PM_FIT_POLYNOMIAL, 1, myStats, 2.0);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractSky() returned something other than pmReadout\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------\n");
+    printf("Calling pmSubtractSky() with PM_FIT_NONE fit.  Should return image, no ERROR, no WARNING.\n\n");
+    rc = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_NONE, 1, myStats, 2.0);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractSky() returned something other than pmReadout\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------\n");
+    printf("Calling pmSubtractSky() with PM_FIT_SPLINE fit.  Should return image, no ERROR, no WARNING.\n\n");
+    rc = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_SPLINE, 1, myStats, 2.0);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractSky() returned something other than pmReadout\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------\n");
+    printf("Calling pmSubtractSky() with NULL myStats.  Should fit entire image, generate WARNING.\n\n");
+    rc = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_POLYNOMIAL, 1, NULL, 2.0);
+    for (i=0;i<NUM_ROWS_SMALL;i++) {
+        for (j=0;j<NUM_COLS_SMALL;j++) {
+            if (ERROR_TOLERANCE < fabs(rc->image->data.F32[i][j])) {
+                printf("TEST ERROR: image[%d][%d] is %f, should be 0.0\n", i, j,
+                       rc->image->data.F32[i][j]);
+                testStatus = false;
+            }
+        }
+    }
+
+    printf("----------------------------------------------------------------\n");
+    psU64 oldOptions = myStats->options;
+    myStats->options = 0;
+    printf("Calling pmSubtractSky() with no myStats->options specified.  Should fit entire image, generate WARNING.\n\n");
+    rc = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_POLYNOMIAL, 1, myStats, 2.0);
+    for (i=0;i<NUM_ROWS_SMALL;i++) {
+        for (j=0;j<NUM_COLS_SMALL;j++) {
+            if (ERROR_TOLERANCE < fabs(rc->image->data.F32[i][j])) {
+                printf("TEST ERROR: image[%d][%d] is %f, should be 0.0\n", i, j,
+                       rc->image->data.F32[i][j]);
+                testStatus = false;
+            }
+        }
+    }
+    myStats->options = oldOptions;
+
+    printf("----------------------------------------------------------------\n");
+    oldOptions = myStats->options;
+    myStats->options = PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_MEDIAN;
+    printf("Calling pmSubtractSky() with multiple myStats->options specified.  Should fit entire image, generate WARNING.\n\n");
+    rc = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_POLYNOMIAL, 1, myStats, 2.0);
+    for (i=0;i<NUM_ROWS_SMALL;i++) {
+        for (j=0;j<NUM_COLS_SMALL;j++) {
+            if (ERROR_TOLERANCE < fabs(rc->image->data.F32[i][j])) {
+                printf("TEST ERROR: image[%d][%d] is %f, should be 0.0\n", i, j,
+                       rc->image->data.F32[i][j]);
+                testStatus = false;
+            }
+        }
+    }
+    myStats->options = oldOptions;
+
+    printf("----------------------------------------------------------------\n");
+    printf("Calling pmSubtractSky() with 0 binFactor.  Should fit entire image, generate WARNING.\n\n");
+    rc = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_POLYNOMIAL, 0, myStats, 2.0);
+    for (i=0;i<NUM_ROWS_SMALL;i++) {
+        for (j=0;j<NUM_COLS_SMALL;j++) {
+            if (ERROR_TOLERANCE < fabs(rc->image->data.F32[i][j])) {
+                printf("TEST ERROR: image[%d][%d] is %f, should be 0.0\n", i, j,
+                       rc->image->data.F32[i][j]);
+                testStatus = false;
+            }
+        }
+    }
+
+    printf("----------------------------------------------------------------\n");
+    printf("Calling pmSubtractSky() with -1 binFactor.  Should fit entire image, generate WARNING.\n\n");
+    rc = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_POLYNOMIAL, -1, myStats, 2.0);
+    for (i=0;i<NUM_ROWS_SMALL;i++) {
+        for (j=0;j<NUM_COLS_SMALL;j++) {
+            if (ERROR_TOLERANCE < fabs(rc->image->data.F32[i][j])) {
+                printf("TEST ERROR: image[%d][%d] is %f, should be 0.0\n", i, j,
+                       rc->image->data.F32[i][j]);
+                testStatus = false;
+            }
+        }
+    }
+
+    printf("----------------------------------------------------------------\n");
+    printf("Calling pmSubtractSky() with -1.0 clipSD.  Should fit entire image, generate WARNING.\n\n");
+    rc = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_POLYNOMIAL, 1, myStats, -1.0);
+    for (i=0;i<NUM_ROWS_SMALL;i++) {
+        for (j=0;j<NUM_COLS_SMALL;j++) {
+            if (ERROR_TOLERANCE < fabs(rc->image->data.F32[i][j])) {
+                printf("TEST ERROR: image[%d][%d] is %f, should be 0.0\n", i, j,
+                       rc->image->data.F32[i][j]);
+                testStatus = false;
+            }
+        }
+    }
+
+    printf("----------------------------------------------------------------\n");
+    printf("Calling pmSubtractSky() with bogus psFit.  Should generate Error.\n\n");
+    rc = pmSubtractSky(myReadout, (void *) myPoly, 54321, 1, myStats, -1.0);
+    if (rc != myReadout) {
+        printf("TEST ERROR: pmSubtractSky() returned something other than pmReadout\n");
+        testStatus = false;
+    }
+
+
+    //    myReadout = pmSubtractSky(myReadout, (void *) myPoly, PM_FIT_POLYNOMIAL,
+    //                              1, myStats, 2.0);
+
+
+    printf("----------------------------------------------------------------\n");
+    psFree(myReadout);
+    psFree(myStats);
+    psFree(myPoly);
+    psFree(tmpImageF64);
+    printFooter(stdout, "pmSubtractSky", "Testing bad input parameter conditions.", true);
+    return(testStatus);
+}
Index: /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/verified/tst_pmImageSubtract.stderr
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/verified/tst_pmImageSubtract.stderr	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/verified/tst_pmImageSubtract.stderr	(revision 22115)
@@ -0,0 +1,96 @@
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmImageSubtract.c                                      *
+*            TestPoint: Test Point Driver{pmSubtractionKernelsAllocPOIS()}         *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (Test Point Driver{pmSubtractionKernelsAllocPOIS()} | tst_pmImageSubtract.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmImageSubtract.c                                      *
+*            TestPoint: Test Point Driver{pmSubtractionKernelsAllocISIS()}         *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (Test Point Driver{pmSubtractionKernelsAllocISIS()} | tst_pmImageSubtract.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmImageSubtract.c                                      *
+*            TestPoint: Test Point Driver{pmSubtractionFindStamps()}               *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<HOST>|E|pmSubtractionFindStamps (FILE:LINENO)
+    Unallowable operation: psImage image or its data is NULL.
+<HOST>|E|pmSubtractionFindStamps (FILE:LINENO)
+    Error: xNum is 0 or less.
+<HOST>|E|pmSubtractionFindStamps (FILE:LINENO)
+    Error: yNum is 0 or less.
+<HOST>|E|pmSubtractionFindStamps (FILE:LINENO)
+    Error: border is 0 or less.
+<HOST>|E|pmSubtractionFindStamps (FILE:LINENO)
+    Unallowable operation: psImage image or its data is NULL.
+<HOST>|E|pmSubtractionFindStamps (FILE:LINENO)
+    Error: xNum is 0 or less.
+<HOST>|E|pmSubtractionFindStamps (FILE:LINENO)
+    Error: yNum is 0 or less.
+<HOST>|E|pmSubtractionFindStamps (FILE:LINENO)
+    Error: border is 0 or less.
+
+---> TESTPOINT PASSED (Test Point Driver{pmSubtractionFindStamps()} | tst_pmImageSubtract.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmImageSubtract.c                                      *
+*            TestPoint: Test Point Driver{pmSubtractionCalculateEquation()}        *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<HOST>|E|pmSubtractionCalculateEquation (FILE:LINENO)
+    Unallowable operation: stamps is NULL.
+<HOST>|E|pmSubtractionCalculateEquation (FILE:LINENO)
+    Unallowable operation: psImage reference or its data is NULL.
+<HOST>|E|pmSubtractionCalculateEquation (FILE:LINENO)
+    Unallowable operation: psImage input or its data is NULL.
+<HOST>|E|pmSubtractionCalculateEquation (FILE:LINENO)
+    Unallowable operation: kernels is NULL.
+<HOST>|E|pmSubtractionSolveEquation (FILE:LINENO)
+    Unallowable operation: stamps is NULL.
+<HOST>|E|pmSubtractionKernelImage (FILE:LINENO)
+    Unallowable operation: psVector solution or its data is NULL.
+<HOST>|E|pmSubtractionKernelImage (FILE:LINENO)
+    Unallowable operation: kernels is NULL.
+<HOST>|E|pmSubtractionKernelImage (FILE:LINENO)
+    Error: x, -2.000000, is out of range.  Must be between -1.000000 and 1.000000.
+<HOST>|E|pmSubtractionKernelImage (FILE:LINENO)
+    Error: x, 2.000000, is out of range.  Must be between -1.000000 and 1.000000.
+<HOST>|E|pmSubtractionKernelImage (FILE:LINENO)
+    Error: y, -2.000000, is out of range.  Must be between -1.000000 and 1.000000.
+<HOST>|E|pmSubtractionKernelImage (FILE:LINENO)
+    Error: y, 2.000000, is out of range.  Must be between -1.000000 and 1.000000.
+<HOST>|E|pmSubtractionCalculateEquation (FILE:LINENO)
+    Unallowable operation: stamps is NULL.
+<HOST>|E|pmSubtractionCalculateEquation (FILE:LINENO)
+    Unallowable operation: psImage reference or its data is NULL.
+<HOST>|E|pmSubtractionCalculateEquation (FILE:LINENO)
+    Unallowable operation: psImage input or its data is NULL.
+<HOST>|E|pmSubtractionCalculateEquation (FILE:LINENO)
+    Unallowable operation: kernels is NULL.
+<HOST>|E|pmSubtractionSolveEquation (FILE:LINENO)
+    Unallowable operation: stamps is NULL.
+<HOST>|E|pmSubtractionKernelImage (FILE:LINENO)
+    Unallowable operation: psVector solution or its data is NULL.
+<HOST>|E|pmSubtractionKernelImage (FILE:LINENO)
+    Unallowable operation: kernels is NULL.
+<HOST>|E|pmSubtractionKernelImage (FILE:LINENO)
+    Error: x, -2.000000, is out of range.  Must be between -1.000000 and 1.000000.
+<HOST>|E|pmSubtractionKernelImage (FILE:LINENO)
+    Error: x, 2.000000, is out of range.  Must be between -1.000000 and 1.000000.
+<HOST>|E|pmSubtractionKernelImage (FILE:LINENO)
+    Error: y, -2.000000, is out of range.  Must be between -1.000000 and 1.000000.
+<HOST>|E|pmSubtractionKernelImage (FILE:LINENO)
+    Error: y, 2.000000, is out of range.  Must be between -1.000000 and 1.000000.
+
+---> TESTPOINT PASSED (Test Point Driver{pmSubtractionCalculateEquation()} | tst_pmImageSubtract.c)
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/verified/tst_pmImageSubtract.stdout
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/verified/tst_pmImageSubtract.stdout	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/verified/tst_pmImageSubtract.stdout	(revision 22115)
@@ -0,0 +1,73 @@
+Testing pmSubtractionKernelsAllocPOIS(1, 1)
+Testing pmSubtractionKernelsAllocPOIS(2, 3)
+Testing pmSubtractionKernelsAllocPOIS(3, 4)
+Testing pmSubtractionFindStamps(100, 100, 2, 2, 2)
+Calling with a NULL psImage.  Should generate error, return NULL.
+Calling with a non-positive xNum.  Should generate error, return NULL.
+Calling with a non-positive yNum.  Should generate error, return NULL.
+Calling with a non-positive border.  Should generate error, return NULL.
+Calling with acceptable input parameters, non-NULL mask.
+Calling with acceptable input parameters, NULL mask.
+Testing pmSubtractionFindStamps(100, 100, 10, 10, 2)
+Calling with a NULL psImage.  Should generate error, return NULL.
+Calling with a non-positive xNum.  Should generate error, return NULL.
+Calling with a non-positive yNum.  Should generate error, return NULL.
+Calling with a non-positive border.  Should generate error, return NULL.
+Calling with acceptable input parameters, non-NULL mask.
+Calling with acceptable input parameters, NULL mask.
+Testing pmSubtractionCalculateEquation(): 
+    image size is (25, 25)
+    num stamps is (2, 2).  Border is 2
+   kernel type is PM_SUBTRACTION_KERNEL_POIS.
+Generating stamps...
+Generating kernel basis functions...
+Calling with a NULL psArray stamps.  Should generate error, return FALSE.
+Calling with a NULL reference images.  Should generate error, return FALSE.
+Calling with a NULL input images.  Should generate error, return FALSE.
+Calling with a NULL kernel basis functions.  Should generate error, return FALSE.
+Calling with acceptable input parameters.  Should return TRUE.
+Calling pmSubtractionSolveEquation() with a NULL stamp argument.  Should generate error, return FALSE.
+Calling pmSubtractionSolveEquation() with acceptable input parameters.  Should return non-NULL.
+The solution vector is:
+(-3866492.76) (-520094.85) (-1277345.36) (-481569.41) (0.21) (-1182727.56) (0.01) (0.01) (-0.00) (-0.02) (-0.00) (0.00) (-0.01) (0.01) (-0.00) (-0.01) (0.00) (0.00) (0.02) (-0.01) (-0.01) (0.00) (0.01) (0.01) (-0.03) (0.01) (0.00) (-0.01) (0.00) (0.00) (0.01) (-0.01) (-0.02) (0.00) (0.01) (0.01) (-0.01) (0.01) (-0.00) (-0.01) (0.00) (0.01) (-0.02) (0.01) (0.02) (-0.01) (-0.00) (-0.01) (0.04) (-0.02) (-0.02) (0.01) (0.02) (0.01) (-0.03) (0.03) (0.02) (-0.02) (-0.01) (-0.01) (-0.01) (0.01) (0.00) (-0.01) (0.00) (0.00) (-0.02) (0.01) (0.01) (-0.01) (0.00) (-0.00) (-0.01) (-0.02) (0.01) (0.02) (0.02) (-0.01) (0.04) (0.00) (-0.01) (-0.01) (-0.02) (-0.00) (0.00) (-0.00) (-0.01) (0.00) (0.01) (0.01) (0.00) (-0.01) (-0.02) (0.02) (0.02) (0.02) (0.01) (-0.01) (-0.02) (0.01) (0.01) (0.02) (0.03) (-0.00) (-0.05) (-0.00) (0.01) (0.05) (-0.04) (0.03) (0.03) (-0.03) (-0.04) (-0.02) (-0.02) (-0.00) (0.00) (0.01) (0.00) (0.00) (-0.02) (0.01) (0.01) (-0.00) (-0.00) (-0.01) (-0.03) (0.02) (0.01) (-0.02) (-0.01) (-0.00) (0.01) (-0.01) (-0.02) (0.01) (0.00) (0.02) (-0.03) (0.01) (0.00) (-0.00) (-0.00) (0.00) (0.00) (-0.02) (-0.02) (0.02) (0.01) (0.02) (0.49) 
+Calling pmSubtractionRejectStamps() with acceptable input parameters.  Should return TRUE.
+Calling pmSubtractionKernelImage() with NULL solution.  Should generate error, return NULL.
+Calling pmSubtractionKernelImage() with NULL kernels.  Should generate error, return NULL.
+Calling pmSubtractionKernelImage() unallowable x value.  Should generate error, return NULL.
+Calling pmSubtractionKernelImage() unallowable x value.  Should generate error, return NULL.
+Calling pmSubtractionKernelImage() unallowable y value.  Should generate error, return NULL.
+Calling pmSubtractionKernelImage() unallowable y value.  Should generate error, return NULL.
+Calling pmSubtractionKernelImage() with acceptable input parameters.  Should return a psImage.
+-0.001170 -0.025330 0.003882 -0.024851 -0.014656 
+-0.019844 -0.032570 0.014897 0.002807 0.000729 
+0.002328 0.031599 -5181287.500000 -0.004433 -0.013596 
+-0.008172 -0.016571 0.033990 -0.012052 -0.002317 
+0.010558 -0.020875 0.011541 -0.006648 0.006954 
+Testing pmSubtractionCalculateEquation(): 
+    image size is (25, 25)
+    num stamps is (2, 2).  Border is 2
+   kernel type is PM_SUBTRACTION_KERNEL_ISIS.
+Generating stamps...
+Generating kernel basis functions...
+Calling with a NULL psArray stamps.  Should generate error, return FALSE.
+Calling with a NULL reference images.  Should generate error, return FALSE.
+Calling with a NULL input images.  Should generate error, return FALSE.
+Calling with a NULL kernel basis functions.  Should generate error, return FALSE.
+Calling with acceptable input parameters.  Should return TRUE.
+Calling pmSubtractionSolveEquation() with a NULL stamp argument.  Should generate error, return FALSE.
+Calling pmSubtractionSolveEquation() with acceptable input parameters.  Should return non-NULL.
+The solution vector is:
+(0.09) (-0.01) (-0.01) (0.00) (-0.00) (-0.00) (0.00) (-0.00) (0.00) (0.00) (-0.00) (0.00) (0.00) (-0.00) (0.00) (0.00) (-0.00) (0.00) (0.00) (-0.00) (0.00) (0.00) (-0.00) (0.00) (0.00) (-0.00) (0.00) (0.00) (-0.00) (0.00) (0.00) (-0.00) (0.00) (0.00) (-0.00) (0.00) (0.55) 
+Calling pmSubtractionRejectStamps() with acceptable input parameters.  Should return TRUE.
+Calling pmSubtractionKernelImage() with NULL solution.  Should generate error, return NULL.
+Calling pmSubtractionKernelImage() with NULL kernels.  Should generate error, return NULL.
+Calling pmSubtractionKernelImage() unallowable x value.  Should generate error, return NULL.
+Calling pmSubtractionKernelImage() unallowable x value.  Should generate error, return NULL.
+Calling pmSubtractionKernelImage() unallowable y value.  Should generate error, return NULL.
+Calling pmSubtractionKernelImage() unallowable y value.  Should generate error, return NULL.
+Calling pmSubtractionKernelImage() with acceptable input parameters.  Should return a psImage.
+0.031614 0.045997 0.052122 0.045997 0.031614 
+0.045997 0.066926 0.075837 0.066926 0.045997 
+0.052122 0.075837 0.085934 0.075837 0.052122 
+0.045997 0.066926 0.075837 0.066926 0.045997 
+0.031614 0.045997 0.052122 0.045997 0.031614 
Index: /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/verified/tst_pmSubtractBias.stderr
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/verified/tst_pmSubtractBias.stderr	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/verified/tst_pmSubtractBias.stderr	(revision 22115)
@@ -0,0 +1,15 @@
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmSubtractBias.c                                       *
+*            TestPoint: Test Point Driver{pmSubtractBias}                          *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<HOST>|W|pmSubtractBias
+    WARNING: pmSubtractBias.(): overscan image has 7 columns, input image has 8 columns
+<HOST>|W|pmSubtractBias
+    WARNING: pmSubtractBias.(): overscan image has 7 columns, input image has 8 columns
+<HOST>|W|pmSubtractBias
+    WARNING: pmSubtractBias.(): overscan image has 7 columns, input image has 8 columns
+
+---> TESTPOINT PASSED (Test Point Driver{pmSubtractBias} | tst_pmSubtractBias.c)
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/verified/tst_pmSubtractBias.stdout
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/verified/tst_pmSubtractBias.stdout	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/verified/tst_pmSubtractBias.stdout	(revision 22115)
@@ -0,0 +1,9 @@
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmSubtractBias.c                                       *
+*            TestPoint: pmSubtractBias{Row Overscans}                              *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmSubtractBias{Row Overscans} | tst_pmSubtractBias.c)
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/verified/tst_pmSubtractSky.stderr
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/verified/tst_pmSubtractSky.stderr	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/verified/tst_pmSubtractSky.stderr	(revision 22115)
@@ -0,0 +1,62 @@
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmSubtractSky.c                                        *
+*            TestPoint: Test Point Driver{pmSubtractSky}                           *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<HOST>|W|p_psVectorSampleStdev
+    WARNING: p_psVectorSampleStdev(): only one valid psVector elements (1).  Setting stats->sampleStdev = 0.0.
+<HOST>|W|ImageFitPolynomial
+    WARNING: ImageFitPolynomial(): Reducing polynomial complexity in x-dimension.
+<HOST>|W|ImageFitPolynomial
+    WARNING: ImageFitPolynomial(): Reducing polynomial complexity in y-dimension.
+<HOST>|W|ImageFitPolynomial
+    WARNING: ImageFitPolynomial(): Reducing polynomial complexity in x-dimension.
+<HOST>|W|ImageFitPolynomial
+    WARNING: ImageFitPolynomial(): Reducing polynomial complexity in y-dimension.
+<HOST>|W|p_psVectorSampleStdev
+    WARNING: p_psVectorSampleStdev(): only one valid psVector elements (1).  Setting stats->sampleStdev = 0.0.
+<HOST>|W|ImageFitPolynomial
+    WARNING: ImageFitPolynomial(): Reducing polynomial complexity in x-dimension.
+<HOST>|W|ImageFitPolynomial
+    WARNING: ImageFitPolynomial(): Reducing polynomial complexity in y-dimension.
+<HOST>|W|ImageFitPolynomial
+    WARNING: ImageFitPolynomial(): Reducing polynomial complexity in x-dimension.
+<HOST>|W|ImageFitPolynomial
+    WARNING: ImageFitPolynomial(): Reducing polynomial complexity in y-dimension.
+<HOST>|W|ImageFitPolynomial
+    WARNING: ImageFitPolynomial(): Reducing polynomial complexity in x-dimension.
+<HOST>|W|ImageFitPolynomial
+    WARNING: ImageFitPolynomial(): Reducing polynomial complexity in y-dimension.
+
+---> TESTPOINT PASSED (Test Point Driver{pmSubtractSky} | tst_pmSubtractSky.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmSubtractSky.c                                        *
+*            TestPoint: Test Point Driver{pmSubtractSky: warning, error messages}  *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<HOST>|E|pmSubtractSky (FILE:LINENO)
+    Unallowable operation: psReadout in or its data is NULL.
+<HOST>|E|pmSubtractSky (FILE:LINENO)
+    Unallowable operation: psReadout in or its data is NULL.
+<HOST>|E|pmSubtractSky (FILE:LINENO)
+    Unallowable operation: psImage in has incorrect type.
+<HOST>|W|pmSubtractSky
+    WARNING: pmSubtractSky(): input parameter stats is NULL
+<HOST>|W|pmSubtractSky
+    WARNING: pmSubtractSky(): no stats->options was requested
+<HOST>|W|pmSubtractSky
+    WARNING: Multiple statistical options have been requested.
+<HOST>|W|pmSubtractSky
+    WARNING: pmSubtractSky(): binFactor is 0
+<HOST>|W|pmSubtractSky
+    WARNING: pmSubtractSky(): binFactor is -1
+<HOST>|W|pmSubtractSky
+    WARNING: pmSubtractSky(): clipSD is -1.000000
+<HOST>|E|pmSubtractSky (FILE:LINENO)
+    psFit is unallowable (54321).  Returning in image.
+
+---> TESTPOINT PASSED (Test Point Driver{pmSubtractSky: warning, error messages} | tst_pmSubtractSky.c)
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/verified/tst_pmSubtractSky.stdout
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/verified/tst_pmSubtractSky.stdout	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/imsubtract/verified/tst_pmSubtractSky.stdout	(revision 22115)
@@ -0,0 +1,219 @@
+doSubtractSkySimple(1, 1, 1)
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmSubtractSky.c                                        *
+*            TestPoint: pmSubtractSky{doSubtractSkySimple}                         *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmSubtractSky{doSubtractSkySimple} | tst_pmSubtractSky.c)
+
+doSubtractSkySimple(NUM_COLS, 1, 1)
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmSubtractSky.c                                        *
+*            TestPoint: pmSubtractSky{doSubtractSkySimple}                         *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmSubtractSky{doSubtractSkySimple} | tst_pmSubtractSky.c)
+
+doSubtractSkySimple(1, NUM_ROWS, 1)
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmSubtractSky.c                                        *
+*            TestPoint: pmSubtractSky{doSubtractSkySimple}                         *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmSubtractSky{doSubtractSkySimple} | tst_pmSubtractSky.c)
+
+doSubtractSkySimple(NUM_COLS, NUM_ROWS, 1)
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmSubtractSky.c                                        *
+*            TestPoint: pmSubtractSky{doSubtractSkySimple}                         *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmSubtractSky{doSubtractSkySimple} | tst_pmSubtractSky.c)
+
+doSubtractSkySimple(1, 1, 2)
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmSubtractSky.c                                        *
+*            TestPoint: pmSubtractSky{doSubtractSkySimple}                         *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmSubtractSky{doSubtractSkySimple} | tst_pmSubtractSky.c)
+
+doSubtractSkySimple(NUM_COLS, 1, 2)
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmSubtractSky.c                                        *
+*            TestPoint: pmSubtractSky{doSubtractSkySimple}                         *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmSubtractSky{doSubtractSkySimple} | tst_pmSubtractSky.c)
+
+doSubtractSkySimple(1, NUM_ROWS, 2)
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmSubtractSky.c                                        *
+*            TestPoint: pmSubtractSky{doSubtractSkySimple}                         *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmSubtractSky{doSubtractSkySimple} | tst_pmSubtractSky.c)
+
+doSubtractSkySimple(NUM_COLS, NUM_ROWS, 2)
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmSubtractSky.c                                        *
+*            TestPoint: pmSubtractSky{doSubtractSkySimple}                         *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmSubtractSky{doSubtractSkySimple} | tst_pmSubtractSky.c)
+
+doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 1)
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmSubtractSky.c                                        *
+*            TestPoint: pmSubtractSky{doSubtractSkyWithObjects}                    *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmSubtractSky{doSubtractSkyWithObjects} | tst_pmSubtractSky.c)
+
+doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 2)
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmSubtractSky.c                                        *
+*            TestPoint: pmSubtractSky{doSubtractSkyWithObjects}                    *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmSubtractSky{doSubtractSkyWithObjects} | tst_pmSubtractSky.c)
+
+doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 4)
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmSubtractSky.c                                        *
+*            TestPoint: pmSubtractSky{doSubtractSkyWithObjects}                    *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmSubtractSky{doSubtractSkyWithObjects} | tst_pmSubtractSky.c)
+
+doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 8)
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmSubtractSky.c                                        *
+*            TestPoint: pmSubtractSky{doSubtractSkyWithObjects}                    *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmSubtractSky{doSubtractSkyWithObjects} | tst_pmSubtractSky.c)
+
+doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 16)
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmSubtractSky.c                                        *
+*            TestPoint: pmSubtractSky{doSubtractSkyWithObjects}                    *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmSubtractSky{doSubtractSkyWithObjects} | tst_pmSubtractSky.c)
+
+doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 32)
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmSubtractSky.c                                        *
+*            TestPoint: pmSubtractSky{doSubtractSkyWithObjects}                    *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmSubtractSky{doSubtractSkyWithObjects} | tst_pmSubtractSky.c)
+
+doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 64)
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmSubtractSky.c                                        *
+*            TestPoint: pmSubtractSky{doSubtractSkyWithObjects}                    *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmSubtractSky{doSubtractSkyWithObjects} | tst_pmSubtractSky.c)
+
+doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 128)
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmSubtractSky.c                                        *
+*            TestPoint: pmSubtractSky{doSubtractSkyWithObjects}                    *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmSubtractSky{doSubtractSkyWithObjects} | tst_pmSubtractSky.c)
+
+doSubtractSkyWithObjects(NUM_COLS, NUM_ROWS, 256)
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmSubtractSky.c                                        *
+*            TestPoint: pmSubtractSky{doSubtractSkyWithObjects}                    *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (pmSubtractSky{doSubtractSkyWithObjects} | tst_pmSubtractSky.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmSubtractSky.c                                        *
+*            TestPoint: pmSubtractSky{Testing bad input parameter conditions.}     *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+----------------------------------------------------------------
+Calling pmSubtractSky() with NULL pmReadout.  Should error.
+
+----------------------------------------------------------------
+Calling pmSubtractSky() with NULL pmReadout->image.  Should error.
+
+----------------------------------------------------------------
+Calling pmSubtractSky() with PS_TYPE_F64 pmReadout->image.  Should error.
+
+----------------------------------------------------------------
+Calling pmSubtractSky() with NULL fitSpec.  Should return image, no ERROR, no WARNING.
+
+----------------------------------------------------------------
+Calling pmSubtractSky() with PM_FIT_NONE fit.  Should return image, no ERROR, no WARNING.
+
+----------------------------------------------------------------
+Calling pmSubtractSky() with PM_FIT_SPLINE fit.  Should return image, no ERROR, no WARNING.
+
+----------------------------------------------------------------
+Calling pmSubtractSky() with NULL myStats.  Should fit entire image, generate WARNING.
+
+----------------------------------------------------------------
+Calling pmSubtractSky() with no myStats->options specified.  Should fit entire image, generate WARNING.
+
+----------------------------------------------------------------
+Calling pmSubtractSky() with multiple myStats->options specified.  Should fit entire image, generate WARNING.
+
+----------------------------------------------------------------
+Calling pmSubtractSky() with 0 binFactor.  Should fit entire image, generate WARNING.
+
+----------------------------------------------------------------
+Calling pmSubtractSky() with -1 binFactor.  Should fit entire image, generate WARNING.
+
+----------------------------------------------------------------
+Calling pmSubtractSky() with -1.0 clipSD.  Should fit entire image, generate WARNING.
+
+----------------------------------------------------------------
+Calling pmSubtractSky() with bogus psFit.  Should generate Error.
+
+----------------------------------------------------------------
+
+---> TESTPOINT PASSED (pmSubtractSky{Testing bad input parameter conditions.} | tst_pmSubtractSky.c)
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/objects/.cvsignore
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/objects/.cvsignore	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/objects/.cvsignore	(revision 22115)
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+temp
+tst_pmObjects01
Index: /tags/ipp-1-X/rel8_2/psModules/test/objects/Makefile.am
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/objects/Makefile.am	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/objects/Makefile.am	(revision 22115)
@@ -0,0 +1,21 @@
+# Makefile for psModule tests
+
+AM_LDFLAGS = -L$(top_builddir)/src -lpsmodule $(PSMODULE_LIBS)
+AM_CFLAGS  = @AM_CFLAGS@ $(PSMODULE_CFLAGS) $(SRCINC)
+
+TESTS = \
+    tst_pmObjects01
+
+tst_pmObjects01_SOURCES = tst_pmObjects01.c
+
+check_PROGRAMS = $(TESTS)
+
+TESTS_ENVIRONMENT = perl $(top_srcdir)/test/runTest --verified=$(srcdir)/verified
+
+tests: $(TESTS)
+
+EXTRA_DIST = verified
+
+CLEANFILES = $(TESTS) temp/*
+
+test: check
Index: /tags/ipp-1-X/rel8_2/psModules/test/objects/tst_pmObjects01.c
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/objects/tst_pmObjects01.c	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/objects/tst_pmObjects01.c	(revision 22115)
@@ -0,0 +1,980 @@
+/** @file tst_pmFindObjects.c
+ *
+ *  @brief Contains the tests for pmSubtractSky.c:
+ *
+ * test00: This code will ...
+ *
+ *  @author GLG, MHPCC
+ *
+ * XXX: Must test
+ *       pmSourceRoughClass
+ *
+ * XXX: Must test output results for many other functions.
+ *
+ * XXX: There are many cases where row/col can be switched in the code.
+ * We must test that here by using non-square images.  All tests
+ * in this file should be run with non-square images.
+ *
+ * XXX: Memory leaks are not being caught.  If I allocated a psVector in these functions
+ * and never deallocate, no error is generated.
+Fully Tested:
+    pmPeakAlloc()
+    pmMomentsAlloc()
+Weakly Tested:
+    pmSourceMoments()
+ 
+ *
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2005-10-10 21:55:42 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+#include "psTest.h"
+#include "pslib.h"
+#include "pmObjects.h"
+#include "pmModelGroup.h"
+
+#define NUM_ROWS 10
+#define NUM_COLS 10
+#define ERROR_TOLERANCE 1.0
+#define ERROR_TOL 0.001
+static int test00(void);
+static int test01(void);
+static int test02(void);
+static int test03(void);
+static int test04(void);
+static int test05(void);
+//static int test06(void);
+//static int test07(void);
+/*
+static int test08(void);
+static int test09(void);
+static int test15(void);
+static int test16(void);
+static int test20(void);
+*/
+testDescription tests[] = {
+                              {test00, 000, "pmObjects: structure allocators and deallocators", true, false},
+                              {test01, 001, "pmObjects: psFindVectorPeaks()", true, false},
+                              {test02, 001, "pmObjects: psFindImagePeaks()", true, false},
+                              {test03, 001, "pmObjects: pmCullPeaks()", true, false},
+                              {test04, 001, "pmObjects: pmSourceLocalSky()", true, false},
+                              {test05, 001, "pmObjects: pmSourceMoments()", true, false},
+                              //                              {test06, 001, "pmObjects: pmSourceSetPixelsCircle()", true, false},
+                              //                              {test07, 001, "pmObjects: pmMin()", true, false},
+                              /*
+                                                            {test08, 001, "pmObjects: pmSourceModelGuess()", true, false},
+                                                            {test09, 001, "pmObjects: pmSourceContour()", true, false},
+                                                            {test15, 001, "pmObjects: pmSourceAddModel()", true, false},
+                                                            {test16, 001, "pmObjects: pmSourceSubModel()", true, false},
+                                                            {test20, 001, "pmObjects: pmSourceSubModel()", true, false},
+                              */
+                              {NULL}
+                          };
+
+int main(int argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    //
+    // We include the function names here in psTraceSetLevel() commands for
+    // debugging convenience.  There is no guarantee that this list of functions
+    // is complete.
+    //
+    psTraceSetLevel(".", 0);
+    psTraceSetLevel("pmPeakAlloc", 0);
+    psTraceSetLevel("pmMomentsAlloc", 0);
+    psTraceSetLevel("modelFree", 0);
+    psTraceSetLevel("pmModelAlloc", 0);
+    psTraceSetLevel("sourceFree", 0);
+    psTraceSetLevel("pmSourceAlloc", 0);
+    psTraceSetLevel("pmFindVectorPeaks", 0);
+    psTraceSetLevel("getRowVectorFromImage", 0);
+    psTraceSetLevel("myListAddPeak", 0);
+    psTraceSetLevel("pmFindImagePeaks", 0);
+    psTraceSetLevel("isItInThisRegion", 0);
+    psTraceSetLevel("pmCullPeaks", 0);
+    psTraceSetLevel("pmPeaksSubset", 0);
+    psTraceSetLevel("pmSourceLocalSky", 0);
+    psTraceSetLevel("checkRadius2", 0);
+    psTraceSetLevel("pmSourceMoments", 0);
+    psTraceSetLevel("pmComparePeakAscend", 0);
+    psTraceSetLevel("pmComparePeakDescend", 0);
+    psTraceSetLevel("pmSourcePSFClump", 0);
+    psTraceSetLevel("pmSourceRoughClass", 0);
+    psTraceSetLevel("pmSourceDefinePixels", 0);
+    psTraceSetLevel("pmSourceModelGuess", 0);
+    psTraceSetLevel("pmModelEval", 0);
+    psTraceSetLevel("findValue", 0);
+    psTraceSetLevel("pmSourceContour", 0);
+    psTraceSetLevel("pmSourceFitModel_v5", 0);
+    psTraceSetLevel("pmSourceFitModel", 0);
+    psTraceSetLevel("p_pmSourceAddOrSubModel", 0);
+    psTraceSetLevel("pmSourceAddModel", 0);
+    psTraceSetLevel("pmSourceSubModel", 0);
+
+    return !runTestSuite(stderr, "Test Point Driver", tests, argc, argv);
+}
+
+/******************************************************************************
+test00(): Test the various allocators and deallocators.
+ *****************************************************************************/
+int test00( void )
+{
+    bool testStatus = true;
+    psTraceSetLevel(".", 0);
+
+    printf("Testing pmPeakAlloc()...\n");
+    pmPeak *tmpPeak = pmPeakAlloc(1, 2, 3.0, PM_PEAK_LONE);
+    if (tmpPeak == NULL) {
+        printf("TEST ERROR: pmPeakAlloc() returned a NULL pmPeak\n");
+        testStatus = false;
+    } else {
+        if (tmpPeak->x != 1) {
+            printf("TEST ERROR: pmPeakAlloc() improperly set pmPeak->x\n");
+            testStatus = false;
+        }
+        if (tmpPeak->y != 2) {
+            printf("TEST ERROR: pmPeakAlloc() improperly set pmPeak->y\n");
+            testStatus = false;
+        }
+        if (tmpPeak->counts != 3.0) {
+            printf("TEST ERROR: pmPeakAlloc() improperly set pmPeak->counts\n");
+            testStatus = false;
+        }
+        if (tmpPeak->class != PM_PEAK_LONE) {
+            printf("TEST ERROR: pmPeakAlloc() improperly set pmPeak->class\n");
+            testStatus = false;
+        }
+    }
+    psFree(tmpPeak);
+
+    printf("Testing pmMomentsAlloc()...\n");
+    pmMoments *tmpMoments = pmMomentsAlloc();
+    if (tmpMoments == NULL) {
+        printf("TEST ERROR: pmMomentsAlloc() returned a NULL pmMoments\n");
+        testStatus = false;
+    } else {
+        if ((fabs(tmpMoments->x-0.0) > ERROR_TOL) ||
+                (fabs(tmpMoments->y-0.0) > ERROR_TOL) ||
+                (fabs(tmpMoments->Sx-0.0) > ERROR_TOL) ||
+                (fabs(tmpMoments->Sy-0.0) > ERROR_TOL) ||
+                (fabs(tmpMoments->Sxy-0.0) > ERROR_TOL) ||
+                (fabs(tmpMoments->Sum-0.0) > ERROR_TOL) ||
+                (fabs(tmpMoments->Peak-0.0) > ERROR_TOL) ||
+                (fabs(tmpMoments->Sky-0.0) > ERROR_TOL) ||
+                (tmpMoments->nPixels != 0)) {
+            printf("TEST ERROR: pmMomentsAlloc() did not properly initialize the pmMoments structure.\n");
+            printf("    tmpMoments->x is %f\n", tmpMoments->x);
+            printf("    tmpMoments->y is %f\n", tmpMoments->y);
+            printf("    tmpMoments->Sx is %f\n", tmpMoments->Sx);
+            printf("    tmpMoments->Sy is %f\n", tmpMoments->Sy);
+            printf("    tmpMoments->Sxy is %f\n", tmpMoments->Sxy);
+            printf("    tmpMoments->Sum is %f\n", tmpMoments->Sum);
+            printf("    tmpMoments->Peak is %f\n", tmpMoments->Peak);
+            printf("    tmpMoments->Sky is %f\n", tmpMoments->Sky);
+            printf("    tmp    Moments->nPixels is %d\n", tmpMoments->nPixels);
+            testStatus = false;
+        }
+    }
+    psFree(tmpMoments);
+
+
+    //
+    // Loop through each type of model
+    //
+    psS32 i = 0;
+    while (0 != pmModelParameterCount(i)) {
+        printf("Testing pmModelAlloc(%s)...\n", pmModelGetType(0));
+        pmModel *tmpModel = pmModelAlloc(i);
+        if (tmpModel == NULL) {
+            printf("TEST ERROR: pmModelAlloc(%s) returned a NULL pmModel\n", pmModelGetType(0));
+            testStatus = false;
+        } else {
+
+            /* XXX: Should we test that the members were set correctly?
+                        if ((tmpModel->params->n != 7) || (tmpModel->dparams->n != 7)) {
+                            printf("TEST ERROR: pmModelAlloc(PS_MODEL_GAUSS) allocated an incorrect number of params (%ld, %ld)\n",
+                                   tmpModel->params->n, tmpModel->dparams->n);
+                            testStatus = false;
+                        } else {
+                            for (psS32 i = 0 ; i < 7 ; i++) {
+                                if ((tmpModel->params->data.F32[i] != 0.0) ||
+                                        (tmpModel->dparams->data.F32[i] != 0.0)) {
+                                    printf("TEST ERROR: pmModelAlloc(PS_MODEL_GAUSS) did not ininitialize the params/dparams array to 0.0.\n");
+                                    testStatus = false;
+                                }
+                            }
+                        }
+            */
+        }
+        psFree(tmpModel);
+        i++;
+    }
+
+
+    pmSource *tmpSource = pmSourceAlloc();
+    if (tmpSource == NULL) {
+        printf("TEST ERROR: pmSourceAlloc() returned a NULL pmSource\n");
+        testStatus = false;
+    }
+    psFree(tmpSource);
+
+    return(testStatus);
+}
+
+/******************************************************************************
+test01(): we first test pmFindVectorPeaks() with a variety of bad input
+parameters.  Then we test it with a simple vector both 1- and multi-elements.
+ *****************************************************************************/
+#define TST01_VECTOR_LENGTH 10
+bool test_pmFindVectorPeaks(int n)
+{
+    bool testStatus = true;
+    psVector *inData = psVectorAlloc(n, PS_TYPE_F32);
+    psVector *outData = NULL;
+
+    printf("-------------- Calling test_pmFindVectorPeaks on an %d size vector. --------------\n", n);
+    //
+    // Test first pixel peak.
+    //
+    printf("Test pmFindVectorPeaks() with a first-element peak.\n");
+    for (psS32 i = 0 ; i < n ; i++) {
+        inData->data.F32[i] = (float) (n-i);
+    }
+    inData->data.F32[0] = (float) n;
+
+    outData= pmFindVectorPeaks(inData, 0.0);
+    if (outData == NULL) {
+        printf("TEST ERROR: pmFindVectorPeaks returned a NULL psVector.\n");
+        testStatus = false;
+    } else {
+        if (outData->n != 1) {
+            printf("TEST ERROR: outData->n is %ld\n", outData->n);
+            testStatus = false;
+        }
+        if (outData->data.U32[0] != 0) {
+            printf("TEST ERROR: Did not find peak at element 0.\n");
+            testStatus = false;
+        }
+        psFree(outData);
+    }
+
+    //
+    // Test first pixel peak, large threshold
+    //
+    printf("Test pmFindVectorPeaks() with a first-element peak, large threshold.\n");
+    for (psS32 i = 0 ; i < n ; i++) {
+        inData->data.F32[i] = (float) (n-i);
+    }
+    inData->data.F32[0] = (float) n;
+
+    outData= pmFindVectorPeaks(inData, (float) (n*n));
+    if (outData == NULL) {
+        printf("TEST ERROR: pmFindVectorPeaks returned a NULL psVector.\n");
+        testStatus = false;
+    } else {
+
+        if (outData->n != 0) {
+            printf("TEST ERROR: outData->n is %ld\n", outData->n);
+            testStatus = false;
+        }
+        psFree(outData);
+
+        // Skip remaining tests if the input vector has length 1.
+        if (n == 1) {
+            psFree(inData);
+            return(testStatus);
+        }
+    }
+
+    //
+    // Test last pixel peak.
+    //
+    printf("Test pmFindVectorPeaks() with a last-element peak.\n");
+    for (psS32 i = 0 ; i < n ; i++) {
+        inData->data.F32[i] = (float) (i);
+    }
+    inData->data.F32[n-1] = (float) n;
+
+    outData= pmFindVectorPeaks(inData, 0.0);
+    if (outData == NULL) {
+        printf("TEST ERROR: pmFindVectorPeaks returned a NULL psVector.\n");
+        testStatus = false;
+    } else {
+
+        if (outData->n != 1) {
+            printf("TEST ERROR: outData->n is %ld\n", outData->n);
+            testStatus = false;
+        }
+        if (outData->data.U32[0] != n-1) {
+            printf("TEST ERROR: Did not find peak at element %d.\n", n-1);
+            testStatus = false;
+        }
+        psFree(outData);
+    }
+
+    //
+    // Test last pixel peak, large threshold.
+    //
+    printf("Test pmFindVectorPeaks() with a last-element peak, large threshold.\n");
+    for (psS32 i = 0 ; i < n ; i++) {
+        inData->data.F32[i] = (float) (i);
+    }
+    inData->data.F32[n-1] = (float) n;
+
+    outData= pmFindVectorPeaks(inData, (float) (n*n));
+    if (outData == NULL) {
+        printf("TEST ERROR: pmFindVectorPeaks returned a NULL psVector.\n");
+        testStatus = false;
+    } else {
+
+        if (outData->n != 0) {
+            printf("TEST ERROR: outData->n is %ld\n", outData->n);
+            testStatus = false;
+        }
+        psFree(outData);
+    }
+
+    //
+    // Test interior peaks.
+    // Set all even number elements to be peaks.
+    //
+    printf("Test pmFindVectorPeaks() with all even-numbered elements peak.\n");
+    for (psS32 i = 0 ; i < n ; i++) {
+        if (0 == i%2) {
+            inData->data.F32[i] = (float) (2 * i);
+        } else {
+            inData->data.F32[i] = (float) (i);
+        }
+    }
+    inData->data.F32[0] = (float) n;
+
+
+    outData= pmFindVectorPeaks(inData, 0.0);
+    if (outData == NULL) {
+        printf("TEST ERROR: pmFindVectorPeaks returned a NULL psVector.\n");
+        testStatus = false;
+    } else {
+
+        if (outData->n != n/2) {
+            printf("TEST ERROR: outData->n is %ld\n", outData->n);
+            testStatus = false;
+        }
+
+        for (psS32 i = 0 ; i < outData->n ; i++) {
+            if (outData->data.U32[i] != (2 * i)) {
+                printf("TEST ERROR: the %d-th peak is element number %d\n", i, outData->data.U32[i]);
+                testStatus = false;
+            }
+        }
+        psFree(outData);
+    }
+
+    //
+    // Test interior peaks, with threshold = n*n.
+    // Should generate an empty output psVector.
+    //
+    printf("Test pmFindVectorPeaks() with all even-numbered elements peak, large threshold.\n");
+    outData= pmFindVectorPeaks(inData, (float) (n*n));
+    if (outData == NULL) {
+        printf("TEST ERROR: pmFindVectorPeaks returned a NULL psVector.\n");
+        testStatus = false;
+    } else {
+
+        if (outData->n != 0) {
+            printf("TEST ERROR: outData->n is %ld\n", outData->n);
+            testStatus = false;
+        }
+        psFree(outData);
+    }
+
+    psFree(inData);
+    return(testStatus);
+}
+
+int test01( void )
+{
+    bool testStatus = true;
+    psVector *tmpVec = NULL;
+    psVector *tmpVecF64 = psVectorAlloc(TST01_VECTOR_LENGTH, PS_TYPE_F64);
+    psVector *tmpVecEmpty = psVectorAlloc(0, PS_TYPE_F32);
+
+    psTraceSetLevel(".", 0);
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmFindVectorPeaks with NULL psVector.  Should generate error and return NULL.\n");
+    tmpVec = pmFindVectorPeaks(NULL, 0.0);
+    if (tmpVec != NULL) {
+        printf("TEST ERROR: pmFindVectorPeaks() returned a non-NULL psVector.\n");
+        testStatus = false;
+        psFree(tmpVec);
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmFindVectorPeaks with empty psVector.  Should generate error and return NULL.\n");
+    tmpVec = pmFindVectorPeaks(tmpVecEmpty, 0.0);
+    if (tmpVec != NULL) {
+        printf("TEST ERROR: pmFindVectorPeaks() returned a non-NULL psVector.\n");
+        testStatus = false;
+        psFree(tmpVec);
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmFindVectorPeaks with PS_TYPE_F64 psVector.  Should generate error and return NULL.\n");
+    tmpVec = pmFindVectorPeaks(tmpVecF64, 0.0);
+    if (tmpVec != NULL) {
+        printf("TEST ERROR: pmFindVectorPeaks() returned a non-NULL psVector.\n");
+        testStatus = false;
+        psFree(tmpVec);
+    }
+    testStatus&= test_pmFindVectorPeaks(1);
+    testStatus&= test_pmFindVectorPeaks(TST01_VECTOR_LENGTH);
+
+    psFree(tmpVecF64);
+    psFree(tmpVecEmpty);
+    return(testStatus);
+}
+
+/******************************************************************************
+test02():
+// XXX: Must test flat peaks.
+// XXX: test 1-by-n and n-by-1 images.
+ *****************************************************************************/
+#define TST02_NUM_ROWS 5
+#define TST02_NUM_COLS 5
+bool test_pmFindImagePeaks(int numRows, int numCols)
+{
+    printf("-------------- Calling test_pmFindImagePeaks on an %d-by-%d image. --------------\n", numRows, numCols);
+    //    if ((numRows < 4) || (numCols < 4)) {
+    //        printf("WARNING: Don't call this test with a smaller than 4-by-4 image.\n");
+    //        return(true);
+    //    }
+    bool testStatus = true;
+    psImage *inData = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+    psArray *outData = NULL;
+
+    //
+    // Initialize test image.
+    //
+    for (psS32 i = 0 ; i < numRows ; i++) {
+        for (psS32 j = 0 ; j < numCols ; j++) {
+            inData->data.F32[i][j] = PS_SQR(i - numRows/2) + PS_SQR(j-numCols/2);
+        }
+    }
+    //
+    // Set corner and center pixels as peaks.
+    //
+    inData->data.F32[0][0] = PS_SQR(numRows) + PS_SQR(numCols);
+    inData->data.F32[0][numCols-1] = PS_SQR(numRows) + PS_SQR(numCols);
+    inData->data.F32[numRows-1][0] = PS_SQR(numRows) + PS_SQR(numCols);
+    inData->data.F32[numRows-1][numCols-1] = PS_SQR(numRows) + PS_SQR(numCols);
+    inData->data.F32[numRows/2][numCols/2] = PS_SQR(numRows) + PS_SQR(numCols);
+
+    //
+    // Print image.
+    //
+    for (psS32 i = 0 ; i < numRows ; i++) {
+        for (psS32 j = 0 ; j < numCols ; j++) {
+            printf("(%.1f) ", inData->data.F32[i][j]);
+        }
+        printf("\n");
+    }
+
+    //
+    // Call pmFindImagePeaks() with a threshold of 0.0.
+    //
+    outData = pmFindImagePeaks(inData, 0.0);
+
+    if (outData == NULL) {
+        printf("TEST ERROR: pmFindImagePeaks returned a NULL psList.\n");
+        testStatus = false;
+    } else {
+        psS32 expectedNumPeaks;
+        if ((numRows == 1) && (numCols == 1)) {
+            expectedNumPeaks = 1;
+        } else if ((numRows == 1) || (numCols == 1)) {
+            expectedNumPeaks = 3;
+        } else {
+            expectedNumPeaks = 5;
+        }
+        if (outData->n != expectedNumPeaks) {
+            printf("TEST ERROR: pmFindImagePeaks found %ld peaks (should be %d)\n", outData->n, expectedNumPeaks);
+            testStatus = false;
+        }
+
+        // HEY: verify
+        for (psS32 i = 0 ; i < outData->n ; i++) {
+            pmPeak *tmpPeak = (pmPeak *) outData->data[i];
+            if (((tmpPeak->x == 0) && (tmpPeak->y == 0)) ||
+                    ((tmpPeak->x == 0) && (tmpPeak->y == numRows-1)) ||
+                    ((tmpPeak->x == numCols-1) && (tmpPeak->y == 0)) ||
+                    ((tmpPeak->x == numCols-1) && (tmpPeak->y == numRows-1))) {
+                if (!((tmpPeak->class & PM_PEAK_LONE) || (tmpPeak->class & PM_PEAK_EDGE))) {
+                    printf("TEST ERROR: (0) peak at (%d, %d) (%f) ->class set improperly (0x%x).",
+                           tmpPeak->y, tmpPeak->x, tmpPeak->counts, tmpPeak->class);
+                    printf(" should be (0x%x or 0x%x).\n", PM_PEAK_LONE, PM_PEAK_EDGE);
+                    testStatus = false;
+                }
+            } else if ((tmpPeak->x == numCols/2) && (tmpPeak->y == numRows/2)) {
+                if (tmpPeak->class != PM_PEAK_LONE) {
+                    printf("TEST ERROR: (1) peak at (%d, %d) (%f) ->class set improperly (0x%x).\n",
+                           tmpPeak->y, tmpPeak->x, tmpPeak->counts, tmpPeak->class);
+                    printf(" should be (0x%x).\n", PM_PEAK_LONE);
+                    testStatus = false;
+                }
+            } else {
+                printf("TEST ERROR: Peak at (%d, %d) (%f)\n", tmpPeak->y, tmpPeak->x, tmpPeak->counts);
+                testStatus = false;
+            }
+        }
+    }
+
+    psFree(inData);
+    psFree(outData);
+    return(testStatus);
+}
+
+int test02( void )
+{
+    bool testStatus = true;
+    psArray *tmpArray = NULL;
+    psImage *tmpImageF64 = psImageAlloc(TST02_NUM_ROWS, TST02_NUM_COLS, PS_TYPE_F64);
+    psImage *tmpImageEmpty = psImageAlloc(0, 0, PS_TYPE_F32);
+
+    psTraceSetLevel(".", 0);
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmFindImagePeaks with NULL psImage.  Should generate error and return NULL.\n");
+    tmpArray = pmFindImagePeaks(NULL, 0.0);
+    if (tmpArray != NULL) {
+        printf("TEST ERROR: pmFindImagePeaks() returned a non-NULL psImage.\n");
+        testStatus = false;
+        psFree(tmpArray);
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmFindImagePeaks with empty psImage.  Should generate error and return NULL.\n");
+    tmpArray = pmFindImagePeaks(tmpImageEmpty, 0.0);
+    if (tmpArray != NULL) {
+        printf("TEST ERROR: pmFindImagePeaks() returned a non-NULL psImage.\n");
+        testStatus = false;
+        psFree(tmpArray);
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmFindImagePeaks with PS_TYPE_F64 psImage.  Should generate error and return NULL.\n");
+    tmpArray = pmFindImagePeaks(tmpImageF64, 0.0);
+    if (tmpArray != NULL) {
+        printf("TEST ERROR: pmFindImagePeaks() returned a non-NULL psImage.\n");
+        testStatus = false;
+        psFree(tmpArray);
+    }
+    printf("----------------------------------------------------------------------------------\n");
+    //    testStatus&= test_pmFindImagePeaks(1, 1);
+    //    testStatus&= test_pmFindImagePeaks(2, 5);
+    //    testStatus&= test_pmFindImagePeaks(5, 2);
+    // HEY: add code for small images
+    //    testStatus&= test_pmFindImagePeaks(1, 1);
+    //    testStatus&= test_pmFindImagePeaks(1, 8);
+    //    testStatus&= test_pmFindImagePeaks(8, 1);
+    testStatus&= test_pmFindImagePeaks(TST02_NUM_ROWS,   TST02_NUM_COLS);
+    testStatus&= test_pmFindImagePeaks(2*TST02_NUM_ROWS, TST02_NUM_COLS);
+    testStatus&= test_pmFindImagePeaks(TST02_NUM_ROWS,   2*TST02_NUM_COLS);
+
+
+    psFree(tmpImageF64);
+    psFree(tmpImageEmpty);
+    return(testStatus);
+}
+
+/******************************************************************************
+test03(): We first test pmCullPeaks() with various NULL and unallowable input
+parameters.  Then we generate a list of peaks and test that pmCullPeaks()
+removes them correctly.
+ *****************************************************************************/
+int test03( void )
+{
+    bool testStatus = true;
+    psImage *imgData = psImageAlloc(NUM_COLS, NUM_ROWS, PS_TYPE_F32);
+    psArray *outData = NULL;
+
+    /* XXX: Modify for new pmCullPeaks()
+            printf("----------------------------------------------------------------------------------\n");
+            printf("Calling pmCullPeaks with NULL psList.  Should generate error and return NULL.\n");
+            outData = pmCullPeaks(NULL, 0.0, NULL);
+            if (outData != NULL) {
+                printf("TEST ERROR: pmCulPeaks() returned a non-NULL psList.\n");
+                testStatus = false;
+        }
+    */
+
+    //
+    // Set peaks in input image.  All even-column and even-row pixels are
+    // set non-zero, all other pixels are set to zero.
+    //
+    psS32 numPeaksOrig = 0;
+    for (psS32 i = 0 ; i < NUM_ROWS ; i++) {
+        for (psS32 j = 0 ; j < NUM_COLS ; j++) {
+            if ((0 == i%2) && (0 == j%2)) {
+                imgData->data.F32[i][j] = (float) (i + 10);
+                numPeaksOrig++;
+            } else {
+                imgData->data.F32[i][j] = 0.0;
+            }
+        }
+    }
+    for (psS32 i = 0 ; i < NUM_ROWS ; i++) {
+        for (psS32 j = 0 ; j < NUM_COLS ; j++) {
+            printf("(%.1f) ", imgData->data.F32[i][j]);
+        }
+        printf("\n");
+    }
+    printf("Set %d peaks\n", numPeaksOrig);
+
+    //
+    // Call pmCullPeaks() with HUGE maxValue and NULL psRegion.  Should not
+    // remove any peaks.
+    //
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmCullPeaks with large maxValue and NULL psRegion.\n");
+    outData = pmFindImagePeaks(imgData, 0.0);
+    /* XXX: Modify for new pmCullPeaks
+        outData = pmCullPeaks(outData, PS_MAX_F32, NULL);
+
+        if (outData == NULL) {
+            printf("TEST ERROR: pmCullPeaks() returned a non-NULL psList.\n");
+            testStatus = false;
+            return(testStatus);
+        }
+        if (outData->n != numPeaksOrig) {
+            printf("TEST ERROR (0): pmCullPeaks incorrectly removed peaks\n");
+            printf("The pmCullPeaks() output had %d peaks, should have had %d peaks.\n", outData->n, numPeaksOrig);
+            testStatus = false;
+        }
+    */
+    psFree(outData);
+
+    //
+    // Call pmCullPeaks() with TINY maxValue and NULL psRegion.  Should
+    // remove all peaks.
+    //
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmCullPeaks with tiny maxValue and NULL psRegion.\n");
+    outData = pmFindImagePeaks(imgData, 0.0);
+    printf("pmFindImagePeaks found %ld peaks\n", outData->n);
+    /* XXX: Modify for new pmCullPeaks
+        outData = pmCullPeaks(outData, 0.0, NULL);
+
+        if (outData == NULL) {
+            printf("TEST ERROR: pmCullPeaks() returned a non-NULL psList.\n");
+            testStatus = false;
+            return(testStatus);
+        }
+        if (outData->n != 0) {
+            printf("TEST ERROR (1): pmCullPeaks incorrectly removed peaks\n");
+            printf("The pmCullPeaks() output had %d peaks, should have had %d peaks.\n", outData->n, 0);
+            testStatus = false;
+        }
+        psFree(outData);
+    */
+
+    //
+    // Call pmCullPeaks() with HUGE maxValue and disjoint psRegion.  Should
+    // not remove any peaks.
+    //
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmCullPeaks with large maxValue and disjoint psRegion.\n");
+    outData = pmFindImagePeaks(imgData, 0.0);
+    printf("pmFindImagePeaks found %ld peaks\n", outData->n);
+    psRegion tmpRegion = psRegionSet(10000.0, 20000.0, 10000.0, 20000.0);
+
+    /* XXX: Modify for new pmCullPeaks
+        outData = pmCullPeaks(outData, PS_MAX_F32, tmpRegion);
+
+        if (outData == NULL) {
+            printf("TEST ERROR: pmCullPeaks() returned a non-NULL psList.\n");
+            testStatus = false;
+            return(testStatus);
+        }
+        if (outData->n != numPeaksOrig) {
+            printf("TEST ERROR (2): pmCullPeaks incorrectly removed peaks\n");
+            printf("The pmCullPeaks() output had %d peaks, should have had %d peaks.\n", outData->n, numPeaksOrig);
+            testStatus = false;
+        }
+    */
+    psFree(outData);
+
+    //
+    // Call pmCullPeaks() with HUGE maxValue and non-disjoint psRegion.  Should
+    // remove all peaks.
+    //
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmCullPeaks with large maxValue and non-disjoint psRegion.\n");
+    outData = pmFindImagePeaks(imgData, 0.0);
+    printf("pmFindImagePeaks found %ld peaks\n", outData->n);
+    tmpRegion = psRegionSet(-PS_MAX_F32, PS_MAX_F32, -PS_MAX_F32, PS_MAX_F32);
+    /* XXX: Modify for new pmCullPeaks
+        outData = pmCullPeaks(outData, PS_MAX_F32, tmpRegion);
+
+        if (outData == NULL) {
+            printf("TEST ERROR: pmCullPeaks() returned a non-NULL psList.\n");
+            testStatus = false;
+            return(testStatus);
+        }
+        if (outData->n != 0) {
+            printf("TEST ERROR (3): pmCullPeaks incorrectly removed peaks\n");
+            printf("The pmCullPeaks() output had %d peaks, should have had %d peaks.\n", outData->n, 0);
+            testStatus = false;
+        }
+    */
+    psFree(outData);
+
+    printf("----------------------------------------------------------------------------------\n");
+    psFree(imgData);
+    return(testStatus);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#define TST04_NUM_ROWS 100
+#define TST04_NUM_COLS 100
+#define TST04_SKY 20.0
+#define TST04_INNER_RADIUS 3
+#define TST04_OUTER_RADIUS 5
+/******************************************************************************
+test04(): We first test pmSourceLocalSky() with various NULL and unallowable
+input parameters.
+ 
+XXX: Should we produce tests with boundary numbers for the inner/outer radius?
+ 
+XXX: Call this with varying sizes for the image.
+ *****************************************************************************/
+int test04( void )
+{
+    bool testStatus = true;
+    psImage *imgData = psImageAlloc(TST04_NUM_COLS, TST04_NUM_ROWS, PS_TYPE_F32);
+    psImageInit(imgData, TST04_SKY);
+    psImage *imgMask = psImageAlloc(TST04_NUM_COLS, TST04_NUM_ROWS, PS_TYPE_U8);
+    psImageInit(imgMask, 0);
+    //    psImage *imgMaskS8 = psImageAlloc(TST04_NUM_COLS, TST04_NUM_ROWS, PS_TYPE_S8);
+    //    psImageInit(imgMaskS8, 0);
+    psImage *imgDataF64 = psImageAlloc(TST04_NUM_COLS, TST04_NUM_ROWS, PS_TYPE_F64);
+    psImageInit(imgDataF64, 0.0);
+    pmPeak *tmpPeak = pmPeakAlloc((psF32) (TST04_NUM_ROWS / 2),
+                                  (psF32) (TST04_NUM_COLS / 2),
+                                  200.0,
+                                  PM_PEAK_LONE);
+    pmSource *tmpSource = pmSourceAlloc();
+    tmpSource->pixels = imgData;
+    tmpSource->mask = imgMask;
+    tmpSource->peak = tmpPeak;
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmSourceLocalSky with NULL tmpSource.  Should generate error and return FALSE.\n");
+    bool rc = pmSourceLocalSky(NULL, PS_STAT_SAMPLE_MEAN, 10.0);
+    if (rc != false) {
+        printf("TEST ERROR: pmSourceLocalSky() returned a non-FALSE pmSource.\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmSourceLocalSky with Radius<0.0.  Should generate error and return FALSE.\n");
+    rc = pmSourceLocalSky(tmpSource, PS_STAT_SAMPLE_MEAN, -10.0);
+    if (rc != false) {
+        printf("TEST ERROR: pmSourceLocalSky() returned a non-FALSE pmSource.\n");
+        testStatus = false;
+    }
+
+    //
+    // XXX: The following code should be a separate function, and we should call it
+    // with a variety of image sizes, peaks.
+    //
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmSourceLocalSky with valid data.\n");
+    tmpPeak->x = (psF32) (TST04_NUM_ROWS / 2);
+    tmpPeak->y = (psF32) (TST04_NUM_COLS / 2);
+    rc = pmSourceLocalSky(tmpSource, PS_STAT_SAMPLE_MEAN, 10.0);
+
+    if (rc == false) {
+        printf("TEST ERROR: pmSourceLocalSky() returned a FALSE pmSource.\n");
+        testStatus = false;
+    } else {
+        if (tmpSource->peak == NULL) {
+            printf("TEST ERROR: pmSourceLocalSky() returned a NULL pmSource->peak.\n");
+            testStatus = false;
+        } else {
+            if (tmpSource->peak->x != tmpPeak->x) {
+                printf("TEST ERROR: pmSourceLocalSky() pmSource->peak->x was %d, should have been %d.\n",
+                       tmpSource->peak->x, tmpPeak->x);
+                testStatus = false;
+            }
+
+            if (tmpSource->peak->y != tmpPeak->y) {
+                printf("TEST ERROR: pmSourceLocalSky() pmSource->peak->y was %d, should have been %d.\n",
+                       tmpSource->peak->y, tmpPeak->y);
+                testStatus = false;
+            }
+
+            if (tmpSource->peak->counts != tmpPeak->counts) {
+                printf("TEST ERROR: pmSourceLocalSky() pmSource->peak->counts was %f, should have been %f.\n",
+                       tmpSource->peak->counts, tmpPeak->counts);
+                testStatus = false;
+            }
+
+            if (tmpSource->peak->class != tmpPeak->class) {
+                printf("TEST ERROR: pmSourceLocalSky() pmSource->peak->class was %d, should have been %d.\n",
+                       tmpSource->peak->class, tmpPeak->class);
+                testStatus = false;
+            }
+        }
+
+        if (tmpSource->moments == NULL) {
+            printf("TEST ERROR: pmSourceLocalSky() returned a NULL pmSource->moments.\n");
+            testStatus = false;
+        } else {
+            if (tmpSource->moments->Sky != TST04_SKY) {
+                printf("TEST ERROR: pmSourceLocalSky() pmSource->moments->Sky was %f, should have been %f.\n", tmpSource->moments->Sky, TST04_SKY);
+                testStatus = false;
+            }
+        }
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    psFree(tmpSource);
+    //    psFree(imgData);
+    //    psFree(imgDataF64);
+    //    psFree(imgMask);
+    //    psFree(imgMaskS8);
+    return(testStatus);
+}
+
+#define TST05_NUM_ROWS 100
+#define TST05_NUM_COLS 100
+#define TST05_SKY 20.0
+#define TST05_INNER_RADIUS 3
+#define TST05_OUTER_RADIUS 5
+/******************************************************************************
+test05(): We first test pmSourceMoments() with various NULL and unallowable
+input parameters.
+ 
+XXX: Should we produce tests with boundary numbers for the inner/outer radius?
+ 
+XXX: Call this with varying sizes for the image.
+ 
+XXX: The actual values of the moments are not tested.
+ *****************************************************************************/
+int test05( void )
+{
+    bool testStatus = true;
+    psImage *imgData = psImageAlloc(TST04_NUM_COLS, TST04_NUM_ROWS, PS_TYPE_F32);
+    psImageInit(imgData, TST04_SKY);
+    psImage *imgMask = psImageAlloc(TST04_NUM_COLS, TST04_NUM_ROWS, PS_TYPE_U8);
+    psImageInit(imgMask, 0);
+    pmPeak *tmpPeak = pmPeakAlloc((psF32) (TST04_NUM_ROWS / 2),
+                                  (psF32) (TST04_NUM_COLS / 2),
+                                  200.0,
+                                  PM_PEAK_LONE);
+    pmSource *tmpSource = pmSourceAlloc();
+    tmpSource->pixels = imgData;
+    tmpSource->mask = imgMask;
+    tmpSource->peak = tmpPeak;
+    psBool rc = pmSourceLocalSky(tmpSource, PS_STAT_SAMPLE_MEAN, 10.0);
+
+    if (rc == false) {
+        printf("TEST ERROR: pmSourceLocalSky() returned a FALSE pmSource.\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmSourceMoments with NULL pmSource.  Should generate error and return FALSE.\n");
+    rc = pmSourceMoments(NULL, 10.0);
+    if (rc != false) {
+        printf("TEST ERROR: pmSourceMoments() returned TRUE.\n");
+        testStatus = false;
+    }
+    // XXX: test with pmSource->peaks NULL
+    // XXX: test with pmSource->pixels NULL
+    // XXX: test with pmSource->mask NULL
+
+    printf("----------------------------------------------------------------------------------\n");
+    printf("Calling pmSourceMoments with radius < 0.0.  Should generate error and return FALSE.\n");
+    rc = pmSourceMoments(tmpSource, -10.0);
+    if (rc != false) {
+        printf("TEST ERROR: pmSourceMoments() returned TRUE.\n");
+        testStatus = false;
+    }
+
+    printf("----------------------------------------------------------------------------------\n");
+    psFree(tmpSource);
+    return(testStatus);
+
+}
+
+// this code will
+
+
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/objects/verified/tst_pmObjects01.stderr
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/objects/verified/tst_pmObjects01.stderr	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/objects/verified/tst_pmObjects01.stderr	(revision 22115)
@@ -0,0 +1,78 @@
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmObjects01.c                                          *
+*            TestPoint: Test Point Driver{pmObjects: structure allocators and deallocators} *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<HOST>|E|pmModelParameterCount (FILE:LINENO)
+    Undefined pmModelType
+
+---> TESTPOINT PASSED (Test Point Driver{pmObjects: structure allocators and deallocators} | tst_pmObjects01.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmObjects01.c                                          *
+*            TestPoint: Test Point Driver{pmObjects: psFindVectorPeaks()}          *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<HOST>|E|pmFindVectorPeaks (FILE:LINENO)
+    Unallowable operation: psVector vector or its data is NULL.
+<HOST>|E|pmFindVectorPeaks (FILE:LINENO)
+    Unallowable operation: psVector vector has no elements.
+<HOST>|E|pmFindVectorPeaks (FILE:LINENO)
+    Unallowable operation: psVector vector has incorrect type.
+
+---> TESTPOINT PASSED (Test Point Driver{pmObjects: psFindVectorPeaks()} | tst_pmObjects01.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmObjects01.c                                          *
+*            TestPoint: Test Point Driver{pmObjects: psFindImagePeaks()}           *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<HOST>|E|psImageAlloc (FILE:LINENO)
+    Specified number of rows (0) or columns (0) is invalid.
+<HOST>|E|pmFindImagePeaks (FILE:LINENO)
+    Unallowable operation: psImage image or its data is NULL.
+<HOST>|E|pmFindImagePeaks (FILE:LINENO)
+    Unallowable operation: psImage image or its data is NULL.
+<HOST>|E|pmFindImagePeaks (FILE:LINENO)
+    Unallowable operation: psImage image has incorrect type.
+
+---> TESTPOINT PASSED (Test Point Driver{pmObjects: psFindImagePeaks()} | tst_pmObjects01.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmObjects01.c                                          *
+*            TestPoint: Test Point Driver{pmObjects: pmCullPeaks()}                *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+
+---> TESTPOINT PASSED (Test Point Driver{pmObjects: pmCullPeaks()} | tst_pmObjects01.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmObjects01.c                                          *
+*            TestPoint: Test Point Driver{pmObjects: pmSourceLocalSky()}           *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<HOST>|E|pmSourceLocalSky (FILE:LINENO)
+    Unallowable operation: source is NULL.
+<HOST>|E|pmSourceLocalSky (FILE:LINENO)
+    Error: Radius is 0 or less.
+
+---> TESTPOINT PASSED (Test Point Driver{pmObjects: pmSourceLocalSky()} | tst_pmObjects01.c)
+
+/***************************** TESTPOINT ******************************************\
+*             TestFile: tst_pmObjects01.c                                          *
+*            TestPoint: Test Point Driver{pmObjects: pmSourceMoments()}            *
+*             TestType: Positive                                                   *
+\**********************************************************************************/
+
+<HOST>|E|pmSourceMoments (FILE:LINENO)
+    Unallowable operation: source is NULL.
+<HOST>|E|pmSourceMoments (FILE:LINENO)
+    Error: !(radius > 0.0) (-10.000000 0.000000).
+
+---> TESTPOINT PASSED (Test Point Driver{pmObjects: pmSourceMoments()} | tst_pmObjects01.c)
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/objects/verified/tst_pmObjects01.stdout
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/objects/verified/tst_pmObjects01.stdout	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/objects/verified/tst_pmObjects01.stdout	(revision 22115)
@@ -0,0 +1,87 @@
+Testing pmPeakAlloc()...
+Testing pmMomentsAlloc()...
+Testing pmModelAlloc(PS_MODEL_GAUSS)...
+Testing pmModelAlloc(PS_MODEL_GAUSS)...
+Testing pmModelAlloc(PS_MODEL_GAUSS)...
+Testing pmModelAlloc(PS_MODEL_GAUSS)...
+----------------------------------------------------------------------------------
+Calling pmFindVectorPeaks with NULL psVector.  Should generate error and return NULL.
+----------------------------------------------------------------------------------
+Calling pmFindVectorPeaks with empty psVector.  Should generate error and return NULL.
+----------------------------------------------------------------------------------
+Calling pmFindVectorPeaks with PS_TYPE_F64 psVector.  Should generate error and return NULL.
+-------------- Calling test_pmFindVectorPeaks on an 1 size vector. --------------
+Test pmFindVectorPeaks() with a first-element peak.
+Test pmFindVectorPeaks() with a first-element peak, large threshold.
+-------------- Calling test_pmFindVectorPeaks on an 10 size vector. --------------
+Test pmFindVectorPeaks() with a first-element peak.
+Test pmFindVectorPeaks() with a first-element peak, large threshold.
+Test pmFindVectorPeaks() with a last-element peak.
+Test pmFindVectorPeaks() with a last-element peak, large threshold.
+Test pmFindVectorPeaks() with all even-numbered elements peak.
+Test pmFindVectorPeaks() with all even-numbered elements peak, large threshold.
+----------------------------------------------------------------------------------
+Calling pmFindImagePeaks with NULL psImage.  Should generate error and return NULL.
+----------------------------------------------------------------------------------
+Calling pmFindImagePeaks with empty psImage.  Should generate error and return NULL.
+----------------------------------------------------------------------------------
+Calling pmFindImagePeaks with PS_TYPE_F64 psImage.  Should generate error and return NULL.
+----------------------------------------------------------------------------------
+-------------- Calling test_pmFindImagePeaks on an 5-by-5 image. --------------
+(50.0) (5.0) (4.0) (5.0) (50.0) 
+(5.0) (2.0) (1.0) (2.0) (5.0) 
+(4.0) (1.0) (50.0) (1.0) (4.0) 
+(5.0) (2.0) (1.0) (2.0) (5.0) 
+(50.0) (5.0) (4.0) (5.0) (50.0) 
+-------------- Calling test_pmFindImagePeaks on an 10-by-5 image. --------------
+(125.0) (26.0) (25.0) (26.0) (125.0) 
+(20.0) (17.0) (16.0) (17.0) (20.0) 
+(13.0) (10.0) (9.0) (10.0) (13.0) 
+(8.0) (5.0) (4.0) (5.0) (8.0) 
+(5.0) (2.0) (1.0) (2.0) (5.0) 
+(4.0) (1.0) (125.0) (1.0) (4.0) 
+(5.0) (2.0) (1.0) (2.0) (5.0) 
+(8.0) (5.0) (4.0) (5.0) (8.0) 
+(13.0) (10.0) (9.0) (10.0) (13.0) 
+(125.0) (17.0) (16.0) (17.0) (125.0) 
+-------------- Calling test_pmFindImagePeaks on an 5-by-10 image. --------------
+(125.0) (20.0) (13.0) (8.0) (5.0) (4.0) (5.0) (8.0) (13.0) (125.0) 
+(26.0) (17.0) (10.0) (5.0) (2.0) (1.0) (2.0) (5.0) (10.0) (17.0) 
+(25.0) (16.0) (9.0) (4.0) (1.0) (125.0) (1.0) (4.0) (9.0) (16.0) 
+(26.0) (17.0) (10.0) (5.0) (2.0) (1.0) (2.0) (5.0) (10.0) (17.0) 
+(125.0) (20.0) (13.0) (8.0) (5.0) (4.0) (5.0) (8.0) (13.0) (125.0) 
+(10.0) (0.0) (10.0) (0.0) (10.0) (0.0) (10.0) (0.0) (10.0) (0.0) 
+(0.0) (0.0) (0.0) (0.0) (0.0) (0.0) (0.0) (0.0) (0.0) (0.0) 
+(12.0) (0.0) (12.0) (0.0) (12.0) (0.0) (12.0) (0.0) (12.0) (0.0) 
+(0.0) (0.0) (0.0) (0.0) (0.0) (0.0) (0.0) (0.0) (0.0) (0.0) 
+(14.0) (0.0) (14.0) (0.0) (14.0) (0.0) (14.0) (0.0) (14.0) (0.0) 
+(0.0) (0.0) (0.0) (0.0) (0.0) (0.0) (0.0) (0.0) (0.0) (0.0) 
+(16.0) (0.0) (16.0) (0.0) (16.0) (0.0) (16.0) (0.0) (16.0) (0.0) 
+(0.0) (0.0) (0.0) (0.0) (0.0) (0.0) (0.0) (0.0) (0.0) (0.0) 
+(18.0) (0.0) (18.0) (0.0) (18.0) (0.0) (18.0) (0.0) (18.0) (0.0) 
+(0.0) (0.0) (0.0) (0.0) (0.0) (0.0) (0.0) (0.0) (0.0) (0.0) 
+Set 25 peaks
+----------------------------------------------------------------------------------
+Calling pmCullPeaks with large maxValue and NULL psRegion.
+----------------------------------------------------------------------------------
+Calling pmCullPeaks with tiny maxValue and NULL psRegion.
+pmFindImagePeaks found 25 peaks
+----------------------------------------------------------------------------------
+Calling pmCullPeaks with large maxValue and disjoint psRegion.
+pmFindImagePeaks found 25 peaks
+----------------------------------------------------------------------------------
+Calling pmCullPeaks with large maxValue and non-disjoint psRegion.
+pmFindImagePeaks found 25 peaks
+----------------------------------------------------------------------------------
+----------------------------------------------------------------------------------
+Calling pmSourceLocalSky with NULL tmpSource.  Should generate error and return FALSE.
+----------------------------------------------------------------------------------
+Calling pmSourceLocalSky with Radius<0.0.  Should generate error and return FALSE.
+----------------------------------------------------------------------------------
+Calling pmSourceLocalSky with valid data.
+----------------------------------------------------------------------------------
+----------------------------------------------------------------------------------
+Calling pmSourceMoments with NULL pmSource.  Should generate error and return FALSE.
+----------------------------------------------------------------------------------
+Calling pmSourceMoments with radius < 0.0.  Should generate error and return FALSE.
+----------------------------------------------------------------------------------
Index: /tags/ipp-1-X/rel8_2/psModules/test/photom/.cvsignore
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/photom/.cvsignore	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/photom/.cvsignore	(revision 22115)
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+temp
+
Index: /tags/ipp-1-X/rel8_2/psModules/test/photom/Makefile.am
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/photom/Makefile.am	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/photom/Makefile.am	(revision 22115)
@@ -0,0 +1,16 @@
+# Makefile for psModule tests
+
+AM_LDFLAGS = -L$(top_builddir)/src -lpsmodule $(PSMODULE_LIBS)
+AM_CFLAGS  = @AM_CFLAGS@ $(PSMODULE_CFLAGS) $(SRCINC)
+
+TESTS =
+
+check_PROGRAMS = $(TESTS)
+
+TESTS_ENVIRONMENT = perl $(top_srcdir)/test/runTest --verified=$(srcdir)/verified
+
+tests: $(TESTS)
+
+CLEANFILES = $(TESTS) temp/*
+
+test: check
Index: /tags/ipp-1-X/rel8_2/psModules/test/runTest
===================================================================
--- /tags/ipp-1-X/rel8_2/psModules/test/runTest	(revision 22115)
+++ /tags/ipp-1-X/rel8_2/psModules/test/runTest	(revision 22115)
@@ -0,0 +1,400 @@
+#!/usr/bin/perl
+#
+#  This is a perl script to execute a single test driver program.  The script
+#  will examine the return value of the test driver and capture STDOUT, STDERR
+#  to files.  If the return value of the test driver is not zero, the test is
+#  deemed a failure.  STDOUT and STDERR files will be place in a subdirectory
+#  named temp and the files will be compared with verified STDOUT, STDERR files
+#  in a subdirectory named verified.  If there are no STDOUT, STDERR files in
+#  the verified directory, the captured STDOUT, STDERR files will be scanned
+#  for words which indicate a failure.
+#
+#  Assumptions:  A verified subdirectory with STDOUT, STDERR files exists for
+#                the test driver specified in the arguements if an exact
+#                match of the streams STDOUT, STDERR is necessary.
+#
+#  Return values:
+#                 Bit mapped values
+#                 0    Test run successfull and all tests passed
+#                 1    Verified directory did not exist
+#                 8    STDOUT files did not compare
+#                16    STDERR files did not compare
+#                32    Test driver doesn't exist or is not executable
+#                64    Test driver did not return zero
+#
+#  SYNOSIS:  runTest testDriverName
+#
+#  $Revison:  $  $Name: not supported by cvs2svn $
+#  $Date: 2005-09-13 20:26:33 $
+#
+#  Copyright 2004 Maui High Performance Computering Center, University of Hawaii
+#
+################################################################################
+
+# Provides functions for handling psS64 command line options
+use Getopt::Long;
+
+$verifiedDir = "verified";
+
+# Assign variables based on the presence of command line options to the script
+GetOptions(
+    "reset!"         => \$reset,
+    "resetStderr!"   => \$resetStderr,
+    "resetStdout!"   => \$resetStdout,
+    "verified=s"     => \$verifiedDir,
+    "help!"          => \$help,
+    "quiet!"         => \$quiet,
+    "printpassfail!" => \$verbose,
+    "printdiff!"     => \$printdiff
+);
+
+if ($help || $#ARGV < 0) {
+    print "\nUsage: runTest  [--help] [--quiet] [--resetStderr] [--resetStdout] [--reset] \\\n",
+          "                [--verified=DIR] [--printpassfail] [--printdiff] testfile(s)\n\n",
+          "Options include:\n",
+          "    --help           Prints this help message\n",
+          "    --quiet          Suppresses all messages\n",
+          "    --resetStderr    Resets the STDERR expected output file in the verified\n",
+          "                     directory\n",
+          "    --resetStdout    Resets the STDOUT expected output file in the verified\n",
+          "                     directory\n",
+          "    --reset          Equivalent to --resetStderr plus --resetStdout\n",
+          "    --verified=DIR   Specifies the location of the verified directory\n",
+          "    --printpassfail  Prints a PASS or FAIL message for each test in each file\n",
+          "    --printdiff      Prints any differences found in the test output and\n",
+          "                     verified expected output.\n\n";
+    exit(0);
+}
+
+if ($reset) {
+    $resetStderr = 1;
+    $resetStdout = 1;
+}
+
+# Initialize exit value
+$exitValue = 0;
+
+# Set up the PSLIB_ROOT environment variable if the user doesn't have it set
+if ( $ENV{'PSLIB_ROOT'} ) {
+
+	# Add PSLIB_ROOT/lib to LD_LIBRARY_PATH and DYLD_LIBRARY_PATH environment vars
+	$ENV{'LD_LIBRARY_PATH'}   = "$ENV{'PSLIB_ROOT'}/src/.libs:$ENV{'LD_LIBRARY_PATH'}";
+	$ENV{'DYLD_LIBRARY_PATH'} = "$ENV{'PSLIB_ROOT'}/src/.libs:$ENV{'DYLD_LIBRARY_PATH'}";
+}
+
+# Loop through the arguements passed to the script
+foreach (@ARGV) {
+    chomp;
+    $testFile = $_;
+
+    # Check if a temp directory already exists in the current work directory
+    if ( !( -e "temp" ) ) {
+        # Create temp directory to store STDOUT, STDERR files during test run
+        `mkdir temp`;
+    }
+
+    # Check if a verified directory exists in the current work directory
+    if ( !( -e $verifiedDir ) ) {
+
+        # Display message that verified subdirectory doesn't exist
+        print STDERR "        Verified directory doesn't exist.\n";
+
+        # Exit script since the test cannot be run with verified files
+        $exitValue = 1;
+    }
+
+    # Check if the test driver file exists and is executable
+    if ( ( -e $testFile ) && ( -x $testFile ) ) {
+
+        # Invoke the test driver
+        system("./$testFile 1> temp/$testFile.stdout 2> temp/$testFile.stderr");
+
+        # Check the return value of the test driver.  A value other than 0
+        # indicates failure of the test driver unless the test driver is name
+        # with a leading 'a' which indicates the test driver is expecting an
+        # abort condition to terminate the driver.
+        if (   ( $? != 0 && ( $testFile !~ /^A/i ) )
+            || ( $? == 0 && ( $testFile =~ /^A/i ) ) )
+        {
+
+            # Display failure message with return value to user
+            if ( $? != 0 && ( $testFile !~ /^A/i ) ) {
+                print STDERR "Failed - Test Driver returned $?, expected 0.\n";
+            }
+            elsif ( $? == 0 && ( $testFile =~ /^A/i ) ) {
+                print STDERR "Failed - Test Driver returned $?, expected abort.\n";
+            }
+            $exitValue |= 64;
+        }
+
+        # Test driver succeeded.
+
+        # Create filtered version of stdout and stderr
+
+        # Open the STDOUT file for reading
+        open( OUTFILE, "< temp/$testFile.stdout" );
+
+        # Open mod file to place filtered STDOUT
+        open( MODFILE,  "> temp/$testFile.stdout.mod" );
+        open( MODFILE2, "> $verifiedDir/$testFile.stdout" ) if $resetStdout;
+
+        # Replace the variable date, time and host information with constants
+        $hostname = `hostname`;
+        chop $hostname;
+        while (<OUTFILE>) {
+            s/\s+\d+:\d+:\d+\w/<TIME>/g;
+            s/\d+:\d+:\d+/<DATE>/g;
+            s/$hostname\s*/<HOST>/g;
+            s/: Line \d+/: Line <LINENO>/g;
+            s/\(.*\:\d+\)/\(FILE\:LINENO\)/g;
+            s/\s+[\_\-\/\.\w]+\:\d+/ FILE\:LINENO/g;
+            s/allocate \d+ bytes at/allocate <N> bytes at/g;
+            s/v\d+.\d+.\d+/vX.X.X/g;
+            s/'xxx' \(\d+\)/'xxx' \(NUM\)/;
+            if (m/TestPoint:\s*([^\*]+)/) {
+                $testfile = $1;
+            }
+            if (m/^---> TESTPOINT\s(\S+)/) {
+                print "\t$testfile- $1\n" if ($verbose || $1 eq "FAILED");
+                if ($1 eq "FAILED") {
+                    print $testoutput;
+                }
+            }
+
+            # Filter lines with malloc.  This is an artifact of memory testing
+            # with the Mac testbed
+            if ( !m/\*\*\*\smalloc/ ) {
+                print MODFILE ($_);
+                print MODFILE2 ($_) if $resetStdout;
+            }
+        }
+
+        # Close mod file
+        close(MODFILE);
+        close(MODFILE2) if $resetStdout;
+
+        # Close STDERR file
+        close(OUTFILE);
+
+        # Open the STDERR file for reading
+        open( OUTFILE, "< temp/$testFile.stderr" );
+
+        # Open mod file to place filtered STDERR
+        open( MODFILE,  "> temp/$testFile.stderr.mod" );
+        open( MODFILE2, "> $verifiedDir/$testFile.stderr" ) if $resetStderr;
+
+        # Replace the variable date, time and host information with constants
+        while (<OUTFILE>) {
+            s/\s+\d+:\d+:\d+\w/<TIME>/g;
+            s/\d+:\d+:\d+/<DATE>/g;
+            s/$hostname\s*/<HOST>/g;
+            s/: Line \d+/: Line <LINENO>/g;
+            s/\(.*\:\d+\)/\(FILE\:LINENO\)/g;
+            s/\s+[\_\-\/\.\w]+\:\d+/ FILE\:LINENO/g;
+            s/allocate \d+ bytes at/allocate <N> bytes at/g;
+            s/v\d+.\d+.\d+/vX.X.X/g;
+            s/'xxx' \(\d+\)/'xxx' \(NUM\)/;
+
+            if (m/\*\*\*\*\*\* TESTPOINT \*\*\*\*\*\*/) {
+                $testoutput = $_;
+            }
+            $testoutput += $_;
+
+            if (m/ TestPoint:\s*([^\*]+)/) {
+                $testfile = $1;
+            }
+            if (m/^---> TESTPOINT\s(\S+)/) {
+                print "\t$testfile- $1\n" if ($verbose || $1 eq "FAILED");
+                if ($1 eq "FAILED") {
+                    print $testoutput;
+                }
+            }
+
+            # Filter lines with malloc.  This is an artifact of memory testing
+            # with the Mac testbed
+
+            if ( !m/\*\*\*\smalloc/ ) {
+                print MODFILE ($_);
+                print MODFILE2 ($_) if $resetStderr;
+            }
+        }
+
+        # Close mod file
+        close(MODFILE);
+        close(MODFILE2) if $resetStderr;
+
+        # Close STDERR file
+        close(OUTFILE);
+
+        # Compare STDOUT capture with verified file
+        $exitValue = &compareStream("$verifiedDir/$testFile.stdout");
+
+        # Check exit value to determine if verified file doesn't exist
+        if ( $exitValue & 2 ) {
+
+            # STDOUT verified doesn't exist.  Search STDOUT capture
+            # for strings indicating error or failure
+            $exitValue |= &errorStrSearch("$testFile.stdout");
+        }
+
+        # Compare STDERR capture with verified file
+        $exitValue |= &compareStream("$verifiedDir/$testFile.stderr");
+
+        # Check exit value to determine if verified file doesn't exist
+        if ( $exitValue & 4 ) {
+
+            # STDERR verified doesn't exist.  Search STDERR capture
+            # for strings indicating error or failure
+            $exitValue |= &errorStrSearch("$testFile.stderr");
+        }
+   }
+    else {
+
+        # Since test driver doesn't exist or is not executable then display
+        # message to user.
+        print STDERR "\tNeed to specify an executable test file.\n";
+
+        # Exit value set to indicate test driver doesn't exist or not executable
+        $exitValue |= 32;
+    }
+}
+
+# Exit for the script with exit value
+# Ignore the first three bit flags in exitValue since there are not indicators
+# of a failed test
+if ( ( $exitValue >> 3 ) != 0 ) {
+    print STDERR "Test failed - return status = $exitValue\n\n";
+}
+else {
+    exit(0);
+}
+
+exit($exitValue);
+
+################################################################################
+#
+#  SUBROUTINE: errorStrSearch
+#
+#      Description:  This subroutine will search the file specified in its
+#                    parameter for error strings and if they do exists
+#                    the appropriate value will be returned.
+#
+#      Parameter(s): fileName - input file to search for strings
+#
+#      Return:   0  -  No error strings found
+#               64  -  Error strings found in STDOUT
+#              128  -  Error strings found in STDERR
+#
+###############################################################################
+
+sub errorStrSearch {
+    local ($fileName)  = @_;
+    local ($returnVal) = 0;
+
+    # Open the captured file
+    open( CAPT_FILE, "<temp/$fileName" );
+
+    # Scan through all the lines of the file searching for error strings
+    while (<CAPT_FILE>) {
+        if (   m/FAIL/i
+            || m/FAULT/i
+            || m/ERROR/i
+            || m/Not Found/i
+            || m/SIGNAL/i
+            || m/NO SUCH FILE/i
+            || m/\|E\|/i
+            || m/\|A\|/i )
+        {
+            print STDERR "\tFailed - File $fileName contains error strings.\n";
+            if ( $fileName =~ m/out/ ) {
+                $returnVal = 64;
+            }
+            elsif ( $fileName =~ m/err/ ) {
+                $returnVal = 128;
+            }
+            last;
+        }
+    }
+
+    # Close the capture file
+    close(CAPT_FILE);
+
+    # Return the return value
+    return ($returnVal);
+}
+
+################################################################################
+#
+#  SUBROUTINE: compareStream
+#
+#      Description:  This subroutine will compare the captured stream file with
+#                    a file in the verified directory if one exists.
+#
+#      Parameter(s): streamFile - input file to compare
+#
+#      Return:   0  -  Compare successful
+#                2  -  STDOUT verified file doesn't exist
+#                4  -  STDERR verified file doesn't exist
+#                8  -  STDOUT verified file doesn't compare
+#               16  -  STDERR verified file doesn't compare
+#
+###############################################################################
+
+sub compareStream {
+    local ($streamFile) = @_;
+    local ($returnVal)  = 0;
+    local ($tempFile)   = "";
+
+    # Check for existence of verified STD stream files
+    if ( !( -e $streamFile ) ) {
+
+        # Set exit value bit 1 to indicate proper failure
+        if ( $streamFile =~ /out$/ ) {
+            $returnVal |= 2;
+        }
+        elsif ( $streamFile =~ /err$/ ) {
+            $returnVal |= 4;
+        }
+    }
+    else {
+
+        # Verified STD stream file exists
+
+        # Create name of the temp file to compare
+        $tempFile = $streamFile;
+        $tempFile =~ s/$verifiedDir/temp/;
+        $tempFile = $tempFile . ".mod";
+
+        # Perform difference on the STD stream files
+        $diffstdout = `diff -b -y --suppress-common-lines $streamFile $tempFile`;
+
+        # Check the return value of the difference
+        if ( $? != 0 ) {
+
+            # Difference of STD stream files failed
+
+            # Check for STDOUT in file name to convey appropirate return value
+            if ( $streamFile =~ /out$/ ) {
+
+                # Display message of the failure of difference to user
+                print STDERR "\tFailed - STDOUT differences\n";
+                print STDERR $diffstdout if $printdiff;
+                # Exit value to indicate STDOUT did not compare
+                $returnVal |= 8;
+            }
+            elsif ( $streamFile =~ /err$/ ) {
+
+                # Display message of the failure of difference to user
+                print STDERR "\tFailed - STDERR differences\n";
+                print STDERR $diffstdout if $printdiff;
+
+                # Exit value to indicate STDERR did not compare
+                $returnVal |= 16;
+            }
+        }
+    }
+
+    # Return the result of the compare
+    return ($returnVal);
+}
+
