Index: /branches/eam_branches/ipp-20220316/fpcamera/Makefile.am
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/Makefile.am	(revision 42175)
+++ /branches/eam_branches/ipp-20220316/fpcamera/Makefile.am	(revision 42175)
@@ -0,0 +1,10 @@
+SUBDIRS = src
+
+CLEANFILES = *.pyc *~ core core.*
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA= fpcamera.pc
+
+EXTRA_DIST = \
+	fpcamera.pc.in \
+	autogen.sh
Index: /branches/eam_branches/ipp-20220316/fpcamera/autogen.sh
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/autogen.sh	(revision 42175)
+++ /branches/eam_branches/ipp-20220316/fpcamera/autogen.sh	(revision 42175)
@@ -0,0 +1,103 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=fpcamera
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL="aclocal $ACLOCAL_FLAGS"
+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: /branches/eam_branches/ipp-20220316/fpcamera/configure.ac
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/configure.ac	(revision 42175)
+++ /branches/eam_branches/ipp-20220316/fpcamera/configure.ac	(revision 42175)
@@ -0,0 +1,206 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_PREREQ(2.61)
+
+AC_INIT([fpcamera], [0.1.0], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([src])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+IPP_STDLDFLAGS
+
+AC_LANG(C)
+AC_GNU_SOURCE
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+AC_SYS_LARGEFILE
+
+dnl ------------------------------------------------------------
+
+AC_PATH_PROG([ERRORCODES], [psParseErrorCodes], [missing])
+if test "$ERRORCODES" = "missing" ; then
+  AC_MSG_ERROR([psParseErrorCodes is required])
+fi
+
+dnl ------------------ kapa,libkapa options -------------------------
+dnl -- libkapa implies the requirement for libpng, libjpeg as well --
+
+dnl save LIBS/CFLAGS/LDFLAGS
+TMP_LIBS=${LIBS}
+TMP_CFLAGS=${CFLAGS}
+TMP_LDFLAGS=${LDFLAGS}
+TMP_CPPFLAGS=${CPPFLAGS}
+
+dnl test for command-line options: use ohana-config if not supplied
+KAPA_CFLAGS_CONFIG="true"
+KAPA_LIBS_CONFIG="true"
+AC_ARG_WITH(kapa,
+[AS_HELP_STRING(--with-kapa=DIR,Specify location of libkapa)],
+[KAPA_CFLAGS="-I$withval/include" KAPA_LIBS="-L$withval/lib" 
+ KAPA_CFLAGS_CONFIG="false"       KAPA_LIBS_CONFIG="false"])
+AC_ARG_WITH(kapa-include,
+[AS_HELP_STRING(--with-kapa-include=DIR,Specify libkapa include directory.)],
+[KAPA_CFLAGS="-I$withval" KAPA_CFLAGS_CONFIG="false"])
+AC_ARG_WITH(kapa-lib,
+[AS_HELP_STRING(--with-kapa-lib=DIR,Specify libkapa library directory.)],
+[KAPA_LIBS="-L$withval" KAPA_LIBS_CONFIG="false"])
+
+echo "KAPA_CFLAGS_CONFIG: $KAPA_CFLAGS_CONFIG"
+echo "KAPA_LIBS_CONFIG: $KAPA_LIBS_CONFIG"
+echo "KAPA_CFLAGS: $KAPA_CFLAGS"
+echo "KAPA_LIBS: $KAPA_LIBS"
+
+dnl HAVE_KAPA is set to false if any of the tests fail
+HAVE_KAPA="true"
+AC_MSG_NOTICE([checking for libkapa])
+if test "$KAPA_CFLAGS_CONFIG" = "true" -o "$KAPA_LIBS_CONFIG" = "true"; then
+  AC_MSG_NOTICE([kapa info supplied by ohana-config])
+  KAPA_CONFIG=`which ohana-config`
+  AC_CHECK_FILE($KAPA_CONFIG,[],
+    [HAVE_KAPA="false"; AC_MSG_WARN([libkapa is not found: output plots disabled.  Obtain libkapa at http://kiawe.ifa.hawaii.edu/Elixir/Ohana or use --with-kapa to specify location])])
+  
+  echo "HAVE_KAPA: $HAVE_KAPA"
+  echo "KAPA_CFLAGS_CONFIG: $KAPA_CFLAGS_CONFIG"
+
+  if test "$HAVE_KAPA" = "true" -a "$KAPA_CFLAGS_CONFIG" = "true" ; then
+   AC_MSG_NOTICE([libkapa cflags info supplied by ohana-config])
+   AC_MSG_CHECKING([libkapa cflags])
+   KAPA_CFLAGS="`${KAPA_CONFIG} --cflags`"
+   AC_MSG_RESULT([${KAPA_CFLAGS}])
+  fi
+
+  if test "$HAVE_KAPA" = "true" -a "$KAPA_LIBS_CONFIG" = "true" ; then
+   AC_MSG_NOTICE([libkapa ldflags info supplied by ohana-config])
+   AC_MSG_CHECKING([libkapa ldflags])
+   KAPA_LIBS="`${KAPA_CONFIG} --libs` -lX11"
+   AC_MSG_RESULT([${KAPA_LIBS}])
+  fi
+fi
+
+if test "$HAVE_KAPA" = "true" ; then
+ AC_MSG_NOTICE([libkapa supplied])
+ FPCAMERA_CFLAGS="${FPCAMERA_CFLAGS} ${KAPA_CFLAGS}"
+ FPCAMERA_LIBS="${FPCAMERA_LIBS} ${KAPA_LIBS}"
+else
+ AC_MSG_NOTICE([libkapa ignored])
+fi
+
+dnl restore the CFLAGS/LDFLAGS
+LIBS=${TMP_LIBS}
+CFLAGS=${TMP_CFLAGS}
+LDFLAGS=${TMP_LDFLAGS}
+CPPFLAGS=${TMP_CPPFLAGS}
+
+dnl ------------------ libjpeg options ---------------------
+
+dnl save LIBS/CFLAGS/LDFLAGS
+TMP_LIBS=${LIBS}
+TMP_CFLAGS=${CFLAGS}
+TMP_LDFLAGS=${LDFLAGS}
+TMP_CPPFLAGS=${CPPFLAGS}
+
+AC_ARG_WITH(jpeg,
+[AS_HELP_STRING(--with-jpeg=DIR,Specify location of libjpeg.)],
+[JPEG_CFLAGS="-I$withval/include"
+ JPEG_LDFLAGS="-L$withval/lib"])
+AC_ARG_WITH(jpeg-include,
+[AS_HELP_STRING(--with-jpeg-include=DIR,Specify libjpeg include directory.)],
+[JPEG_CFLAGS="-I$withval"])
+AC_ARG_WITH(jpeg-lib,
+[AS_HELP_STRING(--with-jpeg-lib=DIR,Specify libjpeg library directory.)],
+[JPEG_LDFLAGS="-L$withval"])
+
+CFLAGS="${CFLAGS} ${JPEG_CFLAGS}"
+CPPFLAGS=${CFLAGS}
+LDFLAGS="${LDFLAGS} ${JPEG_LDFLAGS}"
+
+AC_CHECK_HEADERS([jpeglib.h],
+  [FPCAMERA_CFLAGS="$FPCAMERA_CFLAGS $JPEG_CFLAGS" AC_SUBST(JPEG_CFLAGS)],
+  [HAVE_KAPA=false; AC_MSG_WARN([libjpeg headers not found: output plots disabled.  Obtain libjpeg from http://www.ijg.org/ or use --with-jpeg to specify location.])]
+)
+
+AC_CHECK_LIB(jpeg,jpeg_CreateCompress,
+  [FPCAMERA_LIBS="$FPCAMERA_LIBS $JPEG_LDFLAGS -ljpeg"],
+  [HAVE_KAPA=false; AC_MSG_WARN([libjpeg library not found: output plots disabled.  Obtain libjpeg from http://www.ijg.org/ or use --with-jpeg to specify location.])]
+)
+
+dnl restore the CFLAGS/LDFLAGS
+LIBS=${TMP_LIBS}
+CFLAGS=${TMP_CFLAGS}
+LDFLAGS=${TMP_LDFLAGS}
+CPPFLAGS=${TMP_CPPFLAGS}
+
+dnl ------------------ libpng options ---------------------
+
+dnl save LIBS/CFLAGS/LDFLAGS
+TMP_LIBS=${LIBS}
+TMP_CFLAGS=${CFLAGS}
+TMP_LDFLAGS=${LDFLAGS}
+TMP_CPPFLAGS=${CPPFLAGS}
+
+AC_ARG_WITH(png,
+[AS_HELP_STRING(--with-png=DIR,Specify location of libpng.)],
+[PNG_CFLAGS="-I$withval/include"
+ PNG_LDFLAGS="-L$withval/lib"])
+AC_ARG_WITH(png-include,
+[AS_HELP_STRING(--with-png-include=DIR,Specify libpng include directory.)],
+[PNG_CFLAGS="-I$withval"])
+AC_ARG_WITH(png-lib,
+[AS_HELP_STRING(--with-png-lib=DIR,Specify libpng library directory.)],
+[PNG_LDFLAGS="-L$withval"])
+
+CFLAGS="${CFLAGS} ${PNG_CFLAGS}"
+CPPFLAGS=${CFLAGS}
+LDFLAGS="${LDFLAGS} ${PNG_LDFLAGS}"
+
+AC_CHECK_HEADERS([png.h],
+  [FPCAMERA_CFLAGS="$FPCAMERA_CFLAGS $PNG_CFLAGS" AC_SUBST(PNG_CFLAGS)],
+  [HAVE_KAPA=false; AC_MSG_WARN([libpng headers not found: output plots disabled.  Obtain libpng from http://www.ijg.org/ or use --with-png to specify location.])]
+)
+
+AC_CHECK_LIB(png,png_init_io,
+  [FPCAMERA_LIBS="$FPCAMERA_LIBS $PNG_LDFLAGS -lpng"],
+  [HAVE_KAPA=false; AC_MSG_WARN([libpng library not found: output plots disabled.  Obtain libpng from http://www.ijg.org/ or use --with-png to specify location.])]
+)
+
+dnl restore the CFLAGS/LDFLAGS
+LIBS=${TMP_LIBS}
+CFLAGS=${TMP_CFLAGS}
+LDFLAGS=${TMP_LDFLAGS}
+CPPFLAGS=${TMP_CPPFLAGS}
+
+dnl ------------------ use kapa or not? ---------------------
+
+if test "$HAVE_KAPA" == "true" ; then
+  AC_MSG_RESULT([including plotting functions])
+  AC_DEFINE([HAVE_KAPA],[1],[enable use of libkapa])
+else
+  AC_MSG_RESULT([skipping plotting functions])
+  AC_DEFINE([HAVE_KAPA],[0],[disable use of libkapa])
+fi
+
+dnl ------------- psLib, psModules ---------------
+PKG_CHECK_MODULES([PSLIB],    [pslib >= 1.0.0])
+PKG_CHECK_MODULES([PSMODULE], [psmodules >= 1.0.0])
+PKG_CHECK_MODULES([PPSTATS],  [ppStats >= 1.0.0]) 
+
+dnl Set CFLAGS for build
+IPP_STDOPTS
+IPP_STDCFLAGS
+
+echo "FPCAMERA_CFLAGS: $FPCAMERA_CFLAGS"
+echo "FPCAMERA_LIBS: $FPCAMERA_LIBS"
+
+IPP_VERSION
+
+AC_SUBST([FPCAMERA_CFLAGS])
+AC_SUBST([FPCAMERA_LIBS])
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+])
+
+AC_OUTPUT
Index: /branches/eam_branches/ipp-20220316/fpcamera/src/Makefile.am
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/src/Makefile.am	(revision 42175)
+++ /branches/eam_branches/ipp-20220316/fpcamera/src/Makefile.am	(revision 42175)
@@ -0,0 +1,74 @@
+
+bin_PROGRAMS = psastro
+
+noinst_HEADERS = fpcamera.h fpcameraErrorCodes.h
+
+fpcamera_CFLAGS = $(FPCAMERA_CFLAGS) $(PPSTATS_CFLAGS) $(PSPHOT_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+fpcamera_LDFLAGS = $(FPCAMERA_LIBS) $(PPSTATS_LIBS) $(PSPHOT_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+
+fpcamera_SOURCES = \
+	fpcamera.c		    \
+        fpcameraArguments.c	    \
+	fpcameraVersion.c           \
+	fpcameraParseCamera.c       \
+	fpcameraDataLoad.c          \
+	fpcameraAnalysis.c          \
+	fpcameraDataSave.c          \
+	fpcameraErrorCodes.c        \
+	fpcameraReadAstrometry.c    \
+	fpcameraLoadRefstars.c      \
+	fpcameraCleanup.c
+
+unused = \
+	fpcameraMetadataStats.c      \
+	fpcameraMaskUpdates.c        \
+	fpcameraMaskUtils.c          \
+	fpcameraChooseGlintStars.c   \
+	fpcameraLoadCrosstalk.c \
+	fpcameraLoadGlints.c   \
+	fpcameraLoadGhosts.c         \
+	fpcameraGhostUtils.c         \
+	fpcameraDefineFiles.c        \
+	fpcameraAstromGuess.c        \
+	fpcameraLoadRefstars.c       \
+	fpcameraChooseRefstars.c     \
+	fpcameraConvert.c	    \
+	fpcameraChipAstrom.c         \
+	fpcameraOneChipGrid.c	    \
+	fpcameraOneChipFit.c	    \
+	fpcameraRemoveClumps.c	    \
+	fpcameraUtils.c	       	    \
+	fpcameraTestFuncs.c          \
+	fpcameraLuminosityFunction.c \
+	fpcameraRefstarSubset.c      \
+	fpcameraFixChips.c           \
+	fpcameraFixChipsTest.c       \
+	fpcameraUseModel.c           \
+	fpcameraFindChip.c           \
+	fpcameraZeroPoint.c    	    \
+	fpcameraDemoDump.c           \
+	fpcameraDemoPlot.c
+
+CLEANFILES = fpcameraErrorCodes.h fpcameraErrorCodes.c
+EXTRA_DIST = fpcameraErrorCodes.dat fpcameraErrorCodes.h.in fpcameraErrorCodes.c.in
+
+clean-local:
+	-rm -f TAGS
+
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
+
+BUILT_SOURCES = fpcameraErrorCodes.h fpcameraErrorCodes.c fpcameraVersionDefinitions.h
+
+fpcameraErrorCodes.h : fpcameraErrorCodes.dat fpcameraErrorCodes.h.in
+	$(ERRORCODES) --data=fpcameraErrorCodes.dat --outdir=. fpcameraErrorCodes.h
+
+fpcameraErrorCodes.c : fpcameraErrorCodes.dat fpcameraErrorCodes.c.in fpcameraErrorCodes.h
+	$(ERRORCODES) --data=fpcameraErrorCodes.dat --outdir=. fpcameraErrorCodes.c
+
+# Force recompilation of fpcameraVersion.c, since it gets the version information
+fpcameraVersion.c: fpcameraVersionDefinitions.h
+fpcameraVersionDefinitions.h: fpcameraVersionDefinitions.h.in FORCE
+	pslib-setsvnversion.pl FPCAMERA fpcameraVersionDefinitions.h.in fpcameraVersionDefinitions.h
+FORCE: ;
Index: /branches/eam_branches/ipp-20220316/fpcamera/src/fpcamera.c
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/src/fpcamera.c	(revision 42175)
+++ /branches/eam_branches/ipp-20220316/fpcamera/src/fpcamera.c	(revision 42175)
@@ -0,0 +1,89 @@
+# include "fpcamera.h"
+
+/** @file fpcamera.c
+ *
+ *  @brief
+ *
+ *  @ingroup fpcamera
+ *
+ *  @author Eugene Magnier, IfA
+ *  @version $Revision$ $LastChangedBy$
+ *  @date $Date$
+ *  Copyright 2022 Institute for Astronomy, University of Hawaii
+ */
+
+# define ESCAPE(ERROR, MSG) { psErrorStackPrint(stderr, MSG); psFree(config); psFree(stats); exit(ERROR); }
+
+void usage(void) {
+    fprintf (stderr, "USAGE: one of the following\n");
+    fprintf (stderr, "fpcamera -file filename[,filename,...] -mask maskfile[,maskfile,...] -variance varfile[,varfile,...] -astrom-file (smffile) -ref (dvopath) -OutFileBaseName\n\n");
+    fprintf (stderr, "fpcamera -list filelist -masklist masklist -varlist varlist -astrom-file (smffile) -ref (dvopath) -OutFileBaseName\n\n");
+    fprintf (stderr, "where:\n");
+    fprintf (stderr, "  FileNameList is a text file containing filenames, one per line\n");
+    fprintf (stderr, "  MaskFileNameList is a text file of mask filenames, one per line\n");
+    fprintf (stderr, "  VarFileNameList is a text file of variance filenames, one per line\n");
+    fprintf (stderr, "  OutFileBaseName is the 'root name' for output files\n\n");
+    fprintf(stderr, "Optional arguments:\n");
+    fprintf(stderr, "\t-stats STATS.mdc: Output statistics into STATS.mdc\n");
+    fprintf(stderr, "\t-chip CHIPNUM: Only process this chip number.\n\n");
+    exit (PS_EXIT_CONFIG_ERROR);
+}
+
+int main (int argc, char **argv) {
+
+    // these two must be defined and set to NULL so ESCAPE can try to free them
+    psMetadata *stats = NULL;
+    pmConfig  *config = NULL;
+
+    // initialize & load configuration information
+    config = fpcameraArguments(argc, argv);
+    if (!config) usage();
+
+    fpcameraVersionPrint();
+
+    // identify the data sources from the header of the astrometry file
+    if (!fpcameraParseCamera (config)) ESCAPE(PS_EXIT_CONFIG_ERROR, "Error setting up the camera");
+
+    // load the raw detection data (using PSPHOT.SOURCES filerule)
+    // select subset of stars for astrometry
+    if (!fpcameraDataLoad (config)) ESCAPE(PS_EXIT_DATA_ERROR, "error loading input data");
+    psLogMsg("fpcamera", 3, "TIMEMARK: fpcameraDataLoad: %f sec\n", psTimerMark ("complete"));
+
+    stats = psMetadataAlloc(); // Statistics, for output
+    psMetadataAddS32(stats, PS_LIST_TAIL, "QUALITY", 0, "No problems", 0);
+
+    // run the full astrometry analysis (chip and/or mosaic)
+    if (!fpcameraAnalysis(config, stats)) ESCAPE(PS_EXIT_SYS_ERROR, "failure in fpcamera analysis");
+    psLogMsg("fpcamera", 3, "TIMEMARK: fpcameraAnalysis: %f sec\n", psTimerMark ("complete"));
+
+    // write out the results
+    if (!fpcameraDataSave(config, stats)) ESCAPE(PS_EXIT_DATA_ERROR, "failed to write out data");
+    psLogMsg("fpcamera", 3, "complete fpcamera run: %f sec\n", psTimerMark ("complete"));
+
+    fpcameraCleanup(config);
+    exit(PS_EXIT_SUCCESS);
+}
+
+/* code outline
+
+   - init & parse arguments
+   
+   - construct pmFPAfiles for the inputs and output, identify camera
+
+   - load astrometry from smf file
+
+   - load reference sources for full field
+
+   - loop over chips (already detrended)
+
+     - generate subset of sources for this chip
+
+     - generate a PSF model for this chip
+       - use stars in a specific apparent magnitude range?
+       - use a starting guess PSF model and choose stars based on initial fit?
+
+     - fit known stars with PSF model to pixel data
+
+     - output cmf / smf
+*/
+
Index: /branches/eam_branches/ipp-20220316/fpcamera/src/fpcamera.h
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/src/fpcamera.h	(revision 42175)
+++ /branches/eam_branches/ipp-20220316/fpcamera/src/fpcamera.h	(revision 42175)
@@ -0,0 +1,202 @@
+/** @file psastro.h
+ *
+ *  @brief This file defines the library functions available to external
+ *  programs.
+ *
+ *  It must be included by programs which are compiled against
+ *  psphot functions.
+ *
+ *  @ingroup psastro
+ *
+ *  @author IfA
+ *  @version $Revision: 1.49 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2009-02-09 21:25:34 $
+ *  Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+# ifndef PSASTRO_H
+# define PSASTRO_H
+
+# include "psastroErrorCodes.h"
+
+/// @addtogroup psastro
+/// @{
+
+# define PSASTRO_RECIPE "PSASTRO" ///< Name of the recipe to use
+
+# define psMemCopy(A)(psMemIncrRefCounter((A)))
+# define DEG_RAD 57.295779513082322
+# define RAD_DEG  0.017453292519943
+# define SIGN(X)  (((X) == 0) ? 0 : ((fabs((double)(X))) / (X)))
+
+#if 0
+/**
+ * this structure represents a fit to the logN / logS curve for a set of stars
+ * logN = offset + slope * logS
+ */
+typedef struct {
+    double mMin;                        ///< minimum magnitude bin with data
+    double mMax;                        ///< maximum magnitude bin with data
+    double offset;                      ///< fitted line offset
+    double slope;                       ///< fitted line slope
+    double mPeak;                       ///< mag of peak bin
+    int nPeak;                          ///< # of stars in peak bin
+    int sPeak;                          ///< sum of stars to peak bin
+} pmLumFunc;
+#endif
+
+/**
+ * this structure defines the parameters to describe a ghost
+ */
+typedef struct {
+    psPlane *srcFP;			///< location in FPA coords of the source star
+    psPlane *FP;			///< location in FPA coords of the ghost center
+    psPlane *chip;			///< location in chip coords of the ghost center
+    double Mag;				///< instrumental magnitude of source star
+    psEllipseAxes inner;		///< inner elliptical annulus boundary
+    psEllipseAxes outer;		///< outer elliptical annulus boundary
+} psastroGhost;
+
+psastroGhost     *psastroGhostAlloc (void);
+bool              psastroLoadGhosts (pmConfig *config);
+
+bool              psastroDataSave (pmConfig *config, psMetadata *stats);
+bool              psastroDefineFiles (pmConfig *config, pmFPAfile *input);
+bool              psastroDefineFile (pmConfig *config, pmFPA *input, char *filerule, char *argname, pmFPAfileType fileType, pmDetrendType detrendType);
+bool              psastroAnalysis (pmConfig *config, psMetadata *stats);
+
+bool              psastroConvertFPA (pmConfig *config, pmFPA *fpa, psMetadata *recipe);
+bool              psastroConvertReadout (pmConfig *config, pmFPAview *view, pmReadout *readout, psMetadata *recipe);
+bool              psastroCorrectKH (pmConfig *config, pmFPAview *view, pmReadout *readout, psMetadata *recipe, psArray *inStars);
+
+psArray          *pmSourceToAstromObj (psArray *sources, float MagOffset);
+bool              psastroAstromGuess (int *nStars, pmConfig *config);
+bool              psastroAstromGuessCheck (pmConfig *config);
+bool              psastroMaskUpdates (pmConfig *config, psMetadata *stats);
+
+bool              psastroLoadGlints (pmConfig *config);
+bool              psastroChooseGlintStars (pmConfig *config, psArray *refs, const char *source);
+
+bool              psastroLoadCrosstalk (pmConfig *config);
+
+psArray          *psastroLoadRefstars (pmConfig *config, const char *source);
+bool              psastroChipAstrom (pmConfig *config);
+bool              psastroOneChip (pmFPA *fpa, pmChip *chip, psArray *refset, psArray *rawset, psMetadata *recipe, psMetadata *updates);
+bool              psastroOneChipGrid (pmFPA *fpa, pmChip *chip, psArray *refset, psArray *rawset, psMetadata *recipe, psMetadata *updates);
+bool              psastroOneChipFit (pmFPA *fpa, pmChip *chip, pmReadout *readout, psArray *refset, psArray *rawset, psMetadata *recipe, psMetadata *updates);
+bool              psastroChooseRefstars (pmConfig *config, psArray *refs, const char *source, bool saveExistingMatchedRefs);
+bool              psastroRefstarSubset (pmReadout *readout);
+pmLumFunc        *psastroLuminosityFunction (psArray *stars, pmLumFunc *rawFunc);
+bool              psastroLuminosityFunctionPlot(psVector *lnMag, psVector *Mag, pmLumFunc *lumFunc, pmLumFunc *rawFunc);
+psArray          *psastroRemoveClumps (psArray *input, int scale);
+psArray          *psastroRemoveClumpsIterate (psArray *input, int scale, int nIter);
+bool              psastroRemoveClumpsRawstars (pmConfig *config);
+
+bool              psastroChipFailureHeader (psMetadata *updates);
+
+
+// utility functions:
+bool              psastroUpdateChipToFPA (pmFPA *fpa, pmChip *chip);
+bool              psastroWriteStars (char *filename, psArray *sources);
+bool              psastroWriteTransform (psPlaneTransform *map);
+
+// mosaic fitting functions
+bool              psastroMosaicDistortion (pmFPA *fpa, psMetadata *recipe, int pass);
+psPlaneTransform *psastroMosaicFitRotAndScale (pmFPA *fpa);
+bool              psastroMosaicApplyRotAndScale (pmFPA *fpa, psPlaneTransform *TPtoFP);
+bool              psastroMosaicDistortionFromGradients (pmFPA *fpa, psMetadata *recipe);
+bool              psastroMosaicCorrectDistortion (pmFPA *fpa, psPlaneTransform *TPtoFP);
+bool              psastroMosaicCommonScale (pmFPA *fpa, psMetadata *recipe);
+bool              psastroMosaicAstrom (pmConfig *config, psMetadata *stats);
+bool              psastroMosaicChipAstrom (pmFPA *fpa, psMetadata *stats, psMetadata *recipe, int iteration);
+bool              psastroMosaicSetMatch (pmFPA *fpa, psMetadata *recipe, int iteration);
+bool              psastroMosaicSetAstrom (pmFPA *fpa);
+bool              psastroMosaicHeaders (pmConfig *config);
+bool              psastroMosaicRescaleChips (pmFPA *fpa);
+bool              psastroMosaicOneChip (pmChip *chip, pmReadout *readout, psMetadata *recipe, psMetadata *updates, int iteration);
+
+// Return version strings.
+psString          psastroVersion(void);
+psString          psastroSource(void);
+psString          psastroVersionLong(void);
+bool              psastroVersionHeader(psMetadata *header);
+bool              psastroVersionHeaderFull(psMetadata *header);
+void              psastroVersionPrint(void);
+
+// demo plots
+bool              psastroPlotRawstars (psArray *rawstars, pmFPA *fpa, pmChip *chip, psMetadata *recipe);
+bool              psastroPlotRefstars (psArray *refstars, psMetadata *recipe);
+bool              psastroPlotOneChipFit (psArray *rawstars, psArray *refstars, psArray *match, psMetadata *recipe);
+
+bool              psastroDumpRawstars (psArray *rawstars, pmFPA *fpa, pmChip *chip);
+bool              psastroDumpRefstars (psArray *refstars, char *filename);
+bool              psastroDumpMatches (pmFPA *fpa, char *filename);
+bool              psastroDumpStars (psArray *stars, char *filename);
+bool              psastroDumpMatchedStars (char *filename, psArray *rawstars, psArray *refstars, psArray *match);
+bool              psastroDumpGradients (psArray *gradients, char *filename);
+
+bool              psastroMosaicSetAstrom_tmp (pmFPA *fpa);
+int               psastroSortByMag (const void *a, const void *b);
+
+bool              psastroFixChips (pmConfig *config, psMetadata *recipe);
+bool              psastroFixChipsTest (pmConfig *config, psMetadata *recipe);
+bool              psastroUseModel (pmConfig *config, psMetadata *recipe);
+bool              psastroDumpCorners (char *filenameU, char *filenameD, pmFPA *fpa);
+
+char             *psastroSetMagLimit (float *minMag, float *maxRho, pmConfig *config, const char *source);
+
+psArray          *psastroReadGetstarCatalog (psFits *fits);
+psArray          *psastroReadGetstar_PS1_DEV_0 (psFits *fits);
+
+bool              psastroAstromGuessSetChip (pmFPA *fpa, pmChip *chip, const pmFPAview *view, double pixelScale, bool bilevelAstrometry);
+bool              psastroAstromGuessSetFPA (pmFPA *fpa, bool *bilevelAstrometry);
+bool              psastroMetadataStats (pmConfig *config, psMetadata *stats);
+
+bool 		  psastroZeroPoint (pmConfig *config);
+psVector         *psastroZeroPointReadoutAccum(psVector *dMag, pmReadout *readout, float exptime, float refminMag, float refmaxMag);
+bool              psastroZeroPointAnalysis (psMetadata *header, psVector *dMag, float zeropt, psMetadata *recipe);
+bool 		  psastroZeroPointFromRecipe (float *zeropt, float *exptime, float *ghostMaxMag, double *glintMaxMag, pmFPA *fpa, psMetadata *recipe);
+bool              psastroZeroPointRefMagLimitFromRecipe (float *refminMag, float *refmaxMag, pmFPA *fpa, psMetadata *recipe);
+
+psStats          *psastroStatsPercentile (psVector *myVector, psMetadata *recipe);
+float             psastroStatsPercentileValue (psHistogram *histogram, psHistogram *cumulative, psVector *myVector, float flimit);
+
+// masking functions
+pmCell           *pmCellInChip (pmChip *chip, float x, float y);
+bool 		  pmCellCoordsForChip (float *xCell, float *yCell, pmCell *cell, float xChip, float yChip);
+bool 		  pmChipCoordsForCell (float *xChip, float *yChip, pmCell *cell, float xCell, float yCell);
+		  
+bool 		  psastroMaskCircle (psImage *mask, psImageMaskType value, float x0, float y0, float dX, float dY);
+bool 		  psastroMaskEllipse (psImage *mask, psImageMaskType value, float x0, float y0, psEllipseAxes axes);
+bool 		  psastroMaskEllipticalAnnulus (psImage *mask, psImageMaskType value, float x0, float y0, psEllipseAxes eInner, psEllipseAxes eOuter);
+		  
+bool 		  psastroMaskBox (psImage *mask, psImageMaskType value, float x0, float y0, float dL, float dW, float theta);
+void 		  psastroMaskLine (psImage *mask, psImageMaskType value, double x1, double y1, double x2, double y2, int dW);
+void 		  psastroMaskLineBresen (psImage *mask, psImageMaskType value, int X1, int Y1, int X2, int Y2, int dW, int swapcoords);
+void 		  psastroMaskRectangle (psImage *mask, psImageMaskType value, int x0, int y0, int x1, int y1);
+
+pmChip           *psastroFindChip (double *xChip, double *yChip, pmFPA *fpa, double xFPA, double yFPA);
+bool 		  psastroChipBounds (pmFPA *fpa);
+bool              psastroFindChipInXrange (pmFPA *fpa, int nChip, double xFPA, double yFPA);
+bool              psastroFindChipInYrange (pmFPA *fpa, int nChip, double xFPA, double yFPA);
+bool 		  psastroFindChipYedges (double *yFPAs, double *yFPAe, pmFPA *fpa, int nChip);
+bool 		  psastroFindChipXedges (double *yFPAs, double *yFPAe, pmFPA *fpa, int nChip);
+bool 		  psastroFPAtoChip (double *xChip, double *yChip, pmFPA *fpa, int nChip, double xFPA, double yFPA);
+
+//bool              psastroMaskStats(pmConfig *config, psMetadata *stats);
+
+// psastroExtract functions
+bool 		  psastroExtractAnalysis (pmConfig *config);
+bool 		  psastroExtractStars (pmConfig *config);
+bool 		  psastroExtractGhosts (pmConfig *config);
+
+psImage          *psastroExtractStar (psImage *input, double x, double y, double dX, double dY);
+bool 		  psastroExtractParseCamera (pmConfig *config);
+bool 		  psastroExtractDataLoad (pmConfig *config);
+pmConfig         *psastroExtractArguments (int argc, char **argv);
+bool              psastroExtractFreeChipBounds(void);
+
+bool              psastroGalaxyShapeErrors (psMetadata *recipe, pmReadout *readout);
+
+///@}
+# endif /* PSASTRO_H */
Index: /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraAnalysis.c
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraAnalysis.c	(revision 42175)
+++ /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraAnalysis.c	(revision 42175)
@@ -0,0 +1,20 @@
+# include "fpcamera.h"
+
+# define ESCAPE(ERROR, MSG) { psErrorStackPrint(stderr, MSG); return false; }
+
+/* \brief this function loops over chips and performs forced photometry for the references */
+bool fpcameraAnalysis (pmConfig *config, psMetadata *stats) {
+
+    bool status;
+    int nStars;
+
+    // measure the total elapsed time in fpcameraAnalysis.
+    psTimerStart ("fpcameraAnalysis");
+
+    // select the current recipe
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, FPCAMERA_RECIPE);
+    if (!recipe) ESCAPE (FPCAMERA_ERR_CONFIG, "Can't find FPCAMERA recipe");
+
+    psLogMsg("fpcamera", 3, "TIMEMARK: fpcameraMosaicAstrom: %f sec\n", psTimerMark ("complete"));
+    return true;
+}
Index: /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraArguments.c
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraArguments.c	(revision 42175)
+++ /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraArguments.c	(revision 42175)
@@ -0,0 +1,82 @@
+# include "fpcamera.h"
+
+# define ESCAPE(ERROR,MSG) { psError(ERROR, true, MSG); psErrorStackPrint(stderr, "exit"); return NULL; }
+
+pmConfig *fpcameraArguments (int argc, char **argv) {
+
+    bool status;
+    int N;
+
+    psTimerStart ("complete");		// set an overall timer
+    fpcameraErrorRegister();		// register our error codes/messages
+    pmModelClassInit();			// model inits are needed in pmSourceIO
+
+    if (argc == 1) ESCAPE(FPCAMERA_ERR_ARGUMENTS, "No arguments supplied");
+
+    // load config data from default locations
+    pmConfig *config = pmConfigRead(&argc, argv, FPCAMERA_RECIPE);
+    if (config == NULL) ESCAPE(FPCAMERA_ERR_CONFIG, "Can't read site configuration");
+
+    // save the following additional recipe values based on command-line options
+    // these options override the FPCAMERA recipe values loaded from recipe files
+    psMetadata *options = pmConfigRecipeOptions (config, FPCAMERA_RECIPE);
+
+    // photcode : used in output to supplement header data (argument or recipe?)
+    if ((N = psArgumentGet (argc, argv, "-photcode"))) {
+        psArgumentRemove (N, &argc, argv);
+        psMetadataAddStr (options, PS_LIST_TAIL, "PHOTCODE", PS_META_REPLACE, "", argv[N]);
+        psArgumentRemove (N, &argc, argv);
+    }
+
+    // chip selection is used to limit chips to be processed
+    if ((N = psArgumentGet (argc, argv, "-chip"))) {
+        psArgumentRemove (N, &argc, argv);
+        psMetadataAddStr (config->arguments, PS_LIST_TAIL, "CHIP_SELECTIONS", PS_META_REPLACE, "", argv[N]);
+        psArgumentRemove (N, &argc, argv);
+    }
+
+    // dump the statistics to a file
+    if ((N = psArgumentGet(argc, argv, "-stats"))) {
+        psArgumentRemove(N, &argc, argv);
+        psMetadataAddStr(config->arguments, PS_LIST_TAIL, "STATS", PS_META_REPLACE, "Filename for summary statistics", argv[N]);
+        psArgumentRemove(N, &argc, argv);
+    }
+
+    // dump the configuration to a file?
+    if ((N = psArgumentGet (argc, argv, "-dumpconfig"))) {
+        psArgumentRemove (N, &argc, argv);
+        psMetadataAddStr (config->arguments, PS_LIST_TAIL, "DUMP_CONFIG", PS_META_REPLACE, "", argv[N]);
+        psArgumentRemove (N, &argc, argv);
+    }
+
+    // specify the input images to process
+    status = pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT",    "-file",     "-list");
+    if (!status) ESCAPE(FPCAMERA_ERR_ARGUMENTS, "Missing -file (input) or -list (input)");
+
+    // specify the input images to process
+    status = pmConfigFileSetsMD (config->arguments, &argc, argv, "MASK",     "-mask",     "-masklist");
+    if (!status) ESCAPE(FPCAMERA_ERR_ARGUMENTS, "Missing -mask (input) or -masklist (input)");
+
+    // specify the input images to process
+    status = pmConfigFileSetsMD (config->arguments, &argc, argv, "VARIANCE", "-variance", "-varlist");
+    if (!status) ESCAPE(FPCAMERA_ERR_ARGUMENTS, "Missing -variance (input) or -varlist (input)");
+
+    // specify the reference data source (DVO format or ??)
+    N = psArgumentGet (argc, argv, "-ref");
+    if (!N) ESCAPE (FPCAMERA_ERR_ARGUMENTS, "Missing -ref (database)");
+    psArgumentRemove (N, &argc, argv);
+    psMetadataAddStr (config->arguments, PS_LIST_TAIL, "FPCAMERA.CATDIR", PS_META_REPLACE, "", argv[N]);
+    psArgumentRemove (N, &argc, argv);
+
+    // specify the astrometry calibration
+    status = pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT.ASTROM", "-astrom-file", NULL);
+    if (!status) ESCAPE(FPCAMERA_ERR_ARGUMENTS, "Missing -astrom-file (input) : provide an smf or similar");
+
+    if (argc != 2) ESCAPE(FPCAMERA_ERR_ARGUMENTS, "Incorrect number of arguments supplied");
+
+    // output position is fixed
+    psMetadataAddStr (config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "", argv[1]);
+
+    psTrace("fpcamera", 1, "Done with fpcameraArguments...\n");
+    return (config);
+}
Index: /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraCleanup.c
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraCleanup.c	(revision 42175)
+++ /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraCleanup.c	(revision 42175)
@@ -0,0 +1,30 @@
+/** @file fpcameraCleanup.c
+ *
+ *  @brief
+ *
+ *  @ingroup fpcamera
+ *
+ *  @author IfA
+ *  @version $Revision: 1.8.2.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2009-02-19 17:59:50 $
+ *  Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+# include "fpcameraStandAlone.h"
+
+void fpcameraCleanup (pmConfig *config, psMetadata *stats) {
+
+    psFree (stats);
+    psFree (config);
+    pmVisualClose ();
+    pmVisualCleanup ();
+
+    psTimerStop ();
+    psMemCheckCorruption (stderr, true);
+    pmModelClassCleanup ();
+    psTimeFinalize ();
+    pmConceptsDone ();
+    pmConfigDone ();
+    fprintf (stderr, "found %d leaks at %s\n", psMemCheckLeaks (0, NULL, stdout, false), "fpcamera");
+    return;
+}
Index: /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraDataLoad.c
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraDataLoad.c	(revision 42175)
+++ /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraDataLoad.c	(revision 42175)
@@ -0,0 +1,29 @@
+# include "fpcamera.h"
+
+# define ESCAPE(ERROR,MSG) { psError(ERROR, false, MSG); return false; }
+  
+/*\brief this loop loads the data from the input files */
+
+bool fpcameraDataLoad (pmConfig *config) {
+
+    pmChip *chip;
+    pmCell *cell;
+    pmReadout *readout;
+
+    psTimerStart ("fpcamera");
+
+    // select the input data sources
+    pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, "FPCAMERA.INPUT.SMF");
+    if (!input) ESCAPE (PS_ERR_UNKNOWN, "FPCAMERA.INPUT.SMF not listed in config->files");
+
+    // XXX pass input to fpcameraReadAstrometry?
+    // XXX change name of function?
+    if (!fpcameraReadAstrometry (input, config)) ESCAPE (PS_ERR_UNKNOWN, "Failure to read astrometry calibration");
+
+    // load the reference stars overlapping the data stars
+    if (!fpcameraLoadRefstars(input, config)) ESCAPE (PS_ERR_UNKNOWN, "failed to load reference data");
+
+    psLogMsg ("fpcamera", 3, "load data : %f sec\n", psTimerMark ("fpcamera"));
+
+    return true;
+}
Index: /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraDataSave.c
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraDataSave.c	(revision 42175)
+++ /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraDataSave.c	(revision 42175)
@@ -0,0 +1,85 @@
+# include "fpcamera.h"
+
+# define ESCAPE(ERROR, MSG) { psErrorStackPrint(stderr, MSG); psFree (view); return false; }
+
+/* \brief this loop saves the photometry/astrometry data files */
+bool fpcameraDataSave (pmConfig *config, psMetadata *stats) {
+
+    pmChip *chip;
+    pmCell *cell;
+    pmReadout *readout;
+
+    pmFPAview *view = NULL;
+
+    // select the current recipe
+    psMetadata *recipe  = psMetadataLookupPtr (NULL, config->recipes, FPCAMERA_RECIPE);
+    if (!recipe) ESCAPE(FPCAMERA_ERR_CONFIG, "Can't find FPCAMERA recipe!");
+
+    // select the output data sources
+    pmFPAfile *output = psMetadataLookupPtr (NULL, config->files, "FPCAMERA.OUTPUT");
+    if (!output) ESCAPE(FPCAMERA_ERR_CONFIG, "Can't find or interpret output file rule FPCAMERA.OUTPUT!");
+
+    // de-activate all files except FPCAMERA.OUTPUT
+    pmFPAfileActivate (config->files, false, NULL);
+    pmFPAfileActivate (config->files, true, "FPCAMERA.OUTPUT");
+
+    view = pmFPAviewAlloc (0);
+
+    // open/load files as needed
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE(FPCAMERA_ERR_IO, "failure to save at FPA");
+
+    pmHDU *lastHDU = NULL;              // Last HDU updated
+    while ((chip = pmFPAviewNextChip (view, output->fpa, 1)) != NULL) {
+        psTrace ("fpcamera", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+        if (!chip->process || !chip->file_exists) { continue; }
+        if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE(FPCAMERA_ERR_IO, "failure to save at Chip");
+
+        while ((cell = pmFPAviewNextCell (view, output->fpa, 1)) != NULL) {
+            psTrace ("fpcamera", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+            if (!cell->process || !cell->file_exists) { continue; }
+            if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE(FPCAMERA_ERR_IO, "failure to save at Cell");
+
+            // process each of the readouts
+            while ((readout = pmFPAviewNextReadout (view, output->fpa, 1)) != NULL) {
+                if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) ESCAPE(FPCAMERA_ERR_IO, "failure in IOChecks(BEFORE) at Readout");
+                if (!readout->data_exists) { continue; }
+
+                // Put version information into the header
+                pmHDU *hdu = pmHDUGetHighest(output->fpa, chip, cell);
+                if (hdu && hdu != lastHDU) {
+                    fpcameraVersionHeaderFull(hdu->header);
+		    // Append teh reference catalog to the header as well.
+		    char *catdir = psMetadataLookupStr(NULL,recipe,"FPCAMERA.CATDIR");
+
+		    psMetadata *recipe = psMetadataLookupPtr(NULL, config->recipes, FPCAMERA_RECIPE);
+		    psMetadataAddStr(hdu->header,PS_LIST_TAIL, "PSREFCAT", PS_META_REPLACE, NULL, catdir);
+
+                    lastHDU = hdu;
+                }
+                if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE(FPCAMERA_ERR_IO, "failure in IOChecks(AFTER) at Readout");
+            }
+            if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE(FPCAMERA_ERR_IO, "failure in IOChecks(AFTER) at Cell");
+        }
+        if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE(FPCAMERA_ERR_IO, "failure in IOChecks(AFTER) at Chip");
+    }
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) ESCAPE(FPCAMERA_ERR_IO, "failure in IOChecks(AFTER) at FPA");
+
+    // Write out summary statistics
+    // XXX fix : if (!fpcameraMetadataStats (config, stats)) ESCAPE(FPCAMERA_ERR_UNKNOWN, "failure to save state in Metadata");
+
+    bool status;
+    psString dump_file = psMetadataLookupStr(&status, config->arguments, "DUMP_CONFIG");
+    if (dump_file) {
+        pmConfigCamerasCull(config, NULL);
+        pmConfigRecipesCull(config, "PPIMAGE,PPSTATS,PSPHOT,MASKS,FPCAMERA");
+
+        if (!pmConfigDump(config, dump_file)) ESCAPE(FPCAMERA_ERR_IO, "Unable to dump configuration.");
+    }
+
+    // activate all files except FPCAMERA.OUTPUT, and PSPHOT.OUTPUT.CFF
+    pmFPAfileActivate (config->files, true, NULL);
+    pmFPAfileActivate (config->files, false, "FPCAMERA.OUTPUT");
+
+    psFree (view);
+    return true;
+}
Index: /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraDefineFiles.c
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraDefineFiles.c	(revision 42175)
+++ /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraDefineFiles.c	(revision 42175)
@@ -0,0 +1,11 @@
+# include "fpcamera.h"
+
+bool fpcameraDefineFiles (pmConfig *config, pmFPAfile *input) {
+
+    // these calls bind the I/O handle to the specified fpa
+    bool status;
+
+    return true;
+}
+
+
Index: /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraErrorCodes.c.in
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraErrorCodes.c.in	(revision 42175)
+++ /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraErrorCodes.c.in	(revision 42175)
@@ -0,0 +1,37 @@
+/** @file fpcameraErrorCodes.c
+ *
+ *  @brief
+ *
+ *  @ingroup fpcamera
+ *
+ *  @author IfA
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2009-02-07 02:03:34 $
+ *  Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+#include "pslib.h"
+#include "fpcameraErrorCodes.h"
+
+/*
+ * The line
+    { FPCAMERA_ERR_$X{ErrorCode}, "$X{ErrorDescription}"},
+ * (without the Xs)
+ * will be replaced by values from errorCodes.dat
+ */
+void fpcameraErrorRegister(void)
+{
+    static psErrorDescription errors[] = {
+       { FPCAMERA_ERR_BASE, "First value we use; lower values belong to psLib" },
+       { FPCAMERA_ERR_${ErrorCode}, "${ErrorDescription}"},
+    };
+    static int nerror = FPCAMERA_ERR_NERROR - FPCAMERA_ERR_BASE; // number of values in enum
+
+    for (int i = 0; i < nerror; i++) {
+       psErrorDescription *tmp = psAlloc(sizeof(psErrorDescription));
+       *tmp = errors[i];
+       psErrorRegister(tmp, 1);
+       psFree(tmp);			/* it's on the internal list */
+    }
+    nerror = 0;			                // don't register more than once
+}
Index: /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraErrorCodes.h
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraErrorCodes.h	(revision 42175)
+++ /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraErrorCodes.h	(revision 42175)
@@ -0,0 +1,37 @@
+/** @file fpcameraErrorCodes.h
+ *
+ *  @brief
+ *
+ *  @ingroup fpcamera
+ *
+ *  @author IfA
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2009-02-07 02:03:34 $
+ *  Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+#if !defined(FPCAMERA_ERROR_CODES_H)
+#define FPCAMERA_ERROR_CODES_H
+/*
+ * The line
+ *  FPCAMERA_ERR_$X{ErrorCode},
+ * (without the X)
+ *
+ * will be replaced by values from errorCodes.dat
+ */
+typedef enum {
+    FPCAMERA_ERR_BASE = 8000,
+    FPCAMERA_ERR_UNKNOWN,
+    FPCAMERA_ERR_NOT_IMPLEMENTED,
+    FPCAMERA_ERR_ARGUMENTS,
+    FPCAMERA_ERR_CONFIG,
+    FPCAMERA_ERR_IO,
+    FPCAMERA_ERR_WCS,
+    FPCAMERA_ERR_DATA,
+    FPCAMERA_ERR_REFSTARS,
+    FPCAMERA_ERR_NERROR
+} fpcameraErrorCode;
+
+void fpcameraErrorRegister(void);
+
+#endif
Index: /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraLoadRefstars.c
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraLoadRefstars.c	(revision 42175)
+++ /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraLoadRefstars.c	(revision 42175)
@@ -0,0 +1,357 @@
+# include "fpcamera.h"
+# define ELIXIR_MODE 1
+
+# define ESCAPE(ERROR,FORMAT,...) { psError(ERROR, false, FORMAT, __VA_ARGS__); return false; }
+
+bool fpcameraLoadRefstars (pmFPAfile *input, pmConfig *config) {
+
+    int fd;
+    bool status;
+
+    // select the current recipe
+    psMetadata *recipe  = psMetadataLookupPtr (NULL, config->recipes, FPCAMERA_RECIPE);
+
+    // DVO APIs expect decimal degrees
+    float RAmin  = DEG_RAD*psMetadataLookupF32(NULL, recipe, "RA_MIN");
+    float RAmax  = DEG_RAD*psMetadataLookupF32(NULL, recipe, "RA_MAX");
+    float DECmin = DEG_RAD*psMetadataLookupF32(NULL, recipe, "DEC_MIN");
+    float DECmax = DEG_RAD*psMetadataLookupF32(NULL, recipe, "DEC_MAX");
+
+    // extra field fraction to add
+    double fieldPadding = psMetadataLookupF32 (&status, recipe, "FPCAMERA.FIELD.PADDING");
+    if (!status) ESCAPE(FPCAMERA_ERR_CONFIG, "missing FPCAMERA.FIELD.PADDING in recipe");
+
+    float dRA = RAmax - RAmin;
+    if (dRA * (1. + 2 * fieldPadding) < 180.) {
+        RAmin -= dRA * fieldPadding;
+        RAmax += dRA * fieldPadding;
+    } else {
+        // if dRA > 180 getstar has problems. Just search the entire range
+        RAmin = 0;
+        RAmax = 360;
+    }
+
+    float dDEC = DECmax - DECmin;
+    DECmin -= dDEC * fieldPadding;
+    DECmax += dDEC * fieldPadding;
+
+    // grab the FPCAMERA.CATDIR name from the FPCAMERA recipe
+    char *catdir_recipe = psMetadataLookupStr(&status, recipe, "FPCAMERA.CATDIR");
+    if (!catdir_recipe) ESCAPE(FPCAMERA_ERR_CONFIG, "Need a recipe for the catdir!");
+
+    // substitute abstract name with concrete name, if present in FPCAMERA.CATDIRS
+    psMetadata *catdirs = psMetadataLookupMetadata(&status, config->site, "FPCAMERA.CATDIRS"); // List of cameras
+    if (!catdir) ESCAPE(FPCAMERA_ERR_CONFIG, "Unable to find FPCAMERA.CATDIRS in the system configuration.\n");
+
+    // the name in the recipe may be one of:
+    // (A) the actual directory name
+    // (B) a reference to the name in the FPCAMERA.CATDIRS folder in site.config
+    // (C) a reference to a folder in the FPCAMERA.CATDIRS folder in site.config, containing multiple copy locations
+    char *catdir_virtual = psMetadataLookupStr(&status, catdirs, catdir_recipe);
+
+    psMetadata *catdir_folder = psMetadataLookupMetadata(&status, catdirs, catdir_recipe);
+    if (catdir_folder) {
+        // randomly choose one of the entries
+        psLogMsg ("fpcamera", 3, "choosing catdir_folder\n");
+        int nEntry = catdir_folder->list->n;
+
+        psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS);
+        double frnd = psRandomUniform(rng);
+        int entry = PS_MIN(nEntry - 1, PS_MAX(0, nEntry * frnd));
+        psFree(rng);
+
+        psMetadataItem *item = psListGet(catdir_folder->list, entry);
+	psAssert (item, "programming error? no valid entry?");
+
+        if (item->type != PS_DATA_STRING) ESCAPE(FPCAMERA_ERR_CONFIG, "Invalid entry in FPCAMERA.CATDIR folder: %s\n", item->name);
+        catdir_virtual = item->data.str;
+    }
+
+    char *catdir = (catdir_virtual == NULL) ? catdir_recipe : catdir_virtual;
+
+    // convert the uri to a real filename (ie, path://foobar)
+    psString CATDIR = pmConfigConvertFilename(catdir, config, false, false); // Resolved filename
+    ESCAPE(FPCAMERA_ERR_CONFIG, "unable to determine filename for CATDIR %s", catdir);
+
+    psLogMsg ("fpcamera", 3, "looking up reference objects in %s\n", CATDIR);
+
+    char *getstarCommand = psStringCopy(psMetadataLookupStr(NULL, recipe, "DVO.GETSTAR"));
+    ESCAPE(FPCAMERA_ERR_CONFIG, "unable to find DVO.GETSTAR in recipe");
+
+    char *outformat = psMetadataLookupStr(NULL, recipe, "DVO.GETSTAR.OUTFORMAT");
+    ESCAPE(FPCAMERA_ERR_CONFIG, "unable to find DVO.GETSTAR.OUTFORMAT in recipe");
+
+    // issue the following command:
+    // getstar -region RAmin RAmax DECmin DECmax
+    char tempFile[64];
+    sprintf (tempFile, "/tmp/fpcamera.XXXXXX");
+    if ((fd = mkstemp (tempFile)) == -1) {
+        psFree(CATDIR);
+	ESCAPE(FPCAMERA_ERR_IO, "error creating temp output file for getstar");
+    }
+    close (fd);
+
+    psTimerStart ("fpcamera");
+
+    // supply a known output format (for CATALOG output) so the code below knows what to read
+    // XXX I do not think this affects the getstar command (CATFORMAT is used to define the dvo db format)
+    if (ELIXIR_MODE) {
+        psStringAppend (&getstarCommand, " -D CATMODE mef -D CATFORMAT elixir");
+    } else {
+        psStringAppend (&getstarCommand, " -D CATMODE mef -D CATFORMAT panstarrs");
+    }
+
+    // check for default name (use .ptolemyrc), or use specified CATDIR
+    if (strcasecmp(CATDIR, "NONE")) {
+        psStringAppend (&getstarCommand, " -D CATDIR %s", CATDIR);
+    }
+    psFree(CATDIR);
+
+    psStringAppend (&getstarCommand, " -format %s", outformat);
+
+    // add region and output filename
+    psStringAppend (&getstarCommand, " -region %f %f %f %f -o %s", RAmin, DECmin, RAmax, DECmax, tempFile);
+    psTrace ("fpcamera", 3, "%s\n", getstarCommand);
+
+    psLogMsg("fpcamera", PS_LOG_INFO, "getstar command: %s", getstarCommand);
+
+    // XXX use psPipe: catch stderr, stdout, allow for Nsec timeout...
+    // use fork to add timeout capability
+    status = system (getstarCommand);
+    if (status) ESCAPE(FPCAMERA_ERR_REFSTARS, "error loading reference data");
+    psFree (getstarCommand);
+
+    psLogMsg ("fpcamera", 3, "ran getstar : %f sec\n", psTimerMark ("fpcamera"));
+
+    // the output from getstar is a file with the Average table
+    psFits *fits = psFitsOpen (tempFile, "r");
+
+    psTimerStart ("fpcamera");
+
+    psArray *refstars = NULL;
+    if (!strcmp (outformat, "CATALOG")) {
+      refstars = fpcameraReadGetstarCatalog (fits);
+    }
+    if (!strcmp (outformat, "PS1_DEV_0")) {
+      refstars = fpcameraReadGetstar_PS1_DEV_0 (fits);
+    }
+    if (!refstars) {
+        psFitsClose (fits);
+        ESCAPE(FPCAMERA_ERR_REFSTARS, "error reading reference data");
+    }
+    if (refstars->n == 0) ESCAPE(PSASTRO_ERR_REFSTARS, "no reference stars found");
+
+    psLogMsg ("fpcamera", 3, "loaded %ld reference stars : %f sec\n", refstars->n, psTimerMark ("fpcamera"));
+
+    psTrace ("fpcamera", 3, "loaded %ld reference stars from (%10.6f,%10.6f) - (%10.6f,%10.6f)\n",
+             refstars->n, RAmin, DECmin, RAmax, DECmax);
+
+    psFitsClose (fits);
+    unlink (tempFile);
+
+# if (0)
+
+    // dump or plot the available refstars
+    if (psTraceGetLevel("fpcamera.dump") > 0) {
+        fpcameraDumpRefstars (refstars, "refstars.dat");
+    }
+
+    pmAstromVisualPlotRefStars (refstars, recipe);
+
+    if (psTraceGetLevel("fpcamera.plot") > 0) {
+        fpcameraPlotRefstars (refstars, recipe);
+    }
+# endif
+    
+    psMetadataAdd (input->fpa->analysis, PS_LIST_TAIL, "FPCAMERA.REFSTARS", PS_DATA_ARRAY, "reference sources", refstars);
+    return true;
+}
+
+psArray *fpcameraReadGetstarCatalog (psFits *fits) {
+
+    bool status;
+
+    if (ELIXIR_MODE) {
+        psFitsMoveExtName (fits, "DVO_AVERAGE_ELIXIR");
+    } else {
+        psFitsMoveExtName (fits, "DVO_AVERAGE_PANSTARRS");
+    }
+
+    long numSources = psFitsTableSize(fits); // Number of sources in table
+
+    // convert the Average table to the pmAstromObj entries
+    psArray *refstars = psArrayAllocEmpty (numSources);
+    for (int i = 0; i < numSources; i++) {
+        pmAstromObj *ref = pmAstromObjAlloc ();
+
+        psMetadata *row = psFitsReadTableRow(fits, i); // Table row
+
+        // DVO tables are stored in degrees
+        if (ELIXIR_MODE) {
+            ref->sky->r   = RAD_DEG*psMetadataLookupF32 (&status, row, "RA");
+            ref->sky->d   = RAD_DEG*psMetadataLookupF32 (&status, row, "DEC");
+            ref->Mag      = 0.001*psMetadataLookupS32 (&status, row, "MAG");  // ELIXIR uses millimags
+            ref->Color    = 0.0;
+        } else {
+            ref->sky->r   = RAD_DEG*psMetadataLookupF64 (&status, row, "RA");
+            ref->sky->d   = RAD_DEG*psMetadataLookupF64 (&status, row, "DEC");
+            ref->Mag      = psMetadataLookupF32 (&status, row, "MAG"); // PANSTARRS uses mags
+            ref->Color    = 0.0;
+        }
+	ref->magCal   = ref->Mag;
+
+        // XXX VERY temporary hack to avoid M31 bulge
+        if ((fabs(ref->sky->r - 0.186438) < 0.002) && (fabs(ref->sky->d - 0.720270) < 0.002)) {
+          psFree (ref);
+          psFree (row);
+          continue;
+        }
+
+        psArrayAdd (refstars, 100, ref);
+        psFree (ref);
+        psFree (row);
+    }
+    return refstars;
+}
+
+psArray *fpcameraReadGetstar_PS1_DEV_0 (psFits *fits) {
+
+    bool status;
+
+    psFitsMoveExtName (fits, "GETSTAR_PS1_DEV_0");
+
+    long numSources = psFitsTableSize(fits); // Number of sources in table
+
+    // convert the Average table to the pmAstromObj entries
+    psArray *refstars = psArrayAllocEmpty (numSources);
+    for (int i = 0; i < numSources; i++) {
+        pmAstromObj *ref = pmAstromObjAlloc ();
+
+        psMetadata *row = psFitsReadTableRow(fits, i); // Table row
+
+        ref->sky->r   = RAD_DEG*psMetadataLookupF64 (&status, row, "RA");
+        ref->sky->d   = RAD_DEG*psMetadataLookupF64 (&status, row, "DEC");
+        ref->Mag      = psMetadataLookupF32 (&status, row, "MAG");
+        float MagC1   = psMetadataLookupF32 (&status, row, "MAG_C1");
+        float MagC2   = psMetadataLookupF32 (&status, row, "MAG_C2");
+        if (!isnan(MagC1) && !isnan(MagC2)) {
+            ref->Color = MagC1 - MagC2;
+        } else {
+            // XXX save the color and the slope in the table header?
+            ref->Color = 0.0;
+        }
+	ref->magCal   = ref->Mag;
+
+        // XXX VERY temporary hack to avoid M31 bulge
+        if ((fabs(ref->sky->r - 0.186438) < 0.002) && (fabs(ref->sky->d - 0.720270) < 0.002)) {
+          psFree (ref);
+          psFree (row);
+          continue;
+        }
+
+        psArrayAdd (refstars, 100, ref);
+        psFree (ref);
+        psFree (row);
+    }
+    return refstars;
+}
+
+# undef ESCAPE
+
+# if (0)
+
+# define ESCAPE(MSG) { \
+  psLogMsg ("fpcamera", PS_LOG_INFO, MSG); \
+  goto escape; }
+
+char *fpcameraSetMagLimit (float *minMag, float *maxRho, pmConfig *config, const char *source) {
+
+    bool status;
+    char *photcode;
+
+    // select the current recipe
+    psMetadata *recipe  = psMetadataLookupPtr (NULL, config->recipes, FPCAMERA_RECIPE);
+
+    // select the input data sources
+    pmFPAfile *input = psMetadataLookupPtr (NULL, config->files, source);
+    if (!input) {
+        psLogMsg ("fpcamera", PS_LOG_DETAIL, "no supplied reference header data");
+        photcode = psStringCopy ("NONE");
+        return photcode;
+    }
+    assert (input->fpa);
+
+    *maxRho = psMetadataLookupF32(&status, recipe, "DVO.GETSTAR.MAX.RHO");
+    if (!status) {
+        psError(FPCAMERA_ERR_CONFIG, false, "DVO.GETSTAR.MAX.RHO missing from recipe");
+        return NULL;
+    }
+
+    // select the filter; default to fixed photcode and mag limit otherwise
+    char *filter = psMetadataLookupStr (&status, input->fpa->concepts, "FPA.FILTERID");
+    if (!status) ESCAPE ("missing FPA.FILTER in concepts");
+
+    float exptime = psMetadataLookupF32 (&status, input->fpa->concepts, "FPA.EXPOSURE");
+    if (!status) ESCAPE ("missing FPA.EXPOSURE in concepts");
+
+    // we need to select the PHOTCODE.DATA folder that matches our filter
+    psMetadataItem *item = psMetadataLookup (recipe, "PHOTCODE.DATA");
+    if (!item) ESCAPE ("PHOTCODE.DATA folders missing");
+    if (item->type != PS_DATA_METADATA_MULTI) ESCAPE ("PHOTCODE.DATA not a multi");
+
+    float minInst = psMetadataLookupF32(&status, recipe, "DVO.GETSTAR.MIN.MAG.INST");
+    if (!status) ESCAPE ("missing DVO.GETSTAR.MIN.MAG.INST");
+
+    // if non zero override the zero point in the PHOTCODE.DATA with this value
+    float fixedzeropt = psMetadataLookupF32(&status, recipe, "DVO.GETSTAR.FIXED.ZEROPT");
+
+    // PHOTCODE.DATA is a multi of metadata items
+    psListIterator *iter = psListIteratorAlloc(item->data.list, PS_LIST_HEAD, false);
+
+    psMetadataItem *refItem = NULL;
+    while ((refItem = psListGetAndIncrement (iter))) {
+        if (refItem->type != PS_DATA_METADATA) ESCAPE ("PHOTCODE.DATA entry is not a metadata folder");
+
+        char *refFilter = psMetadataLookupStr (&status, refItem->data.md, "FILTER");
+        if (!status) {
+            psLogMsg ("fpcamera", PS_LOG_INFO, "a PHOTCODE.DATA recipe folder is missing FILTER");
+            continue;
+        }
+
+        // does this entry match the current filter?
+        if (strcmp (refFilter, filter)) continue;
+
+        psLogMsg ("fpcamera", PS_LOG_DETAIL, "PHOTCODE.DATA found for filter %s", filter);
+
+        float zeropt = psMetadataLookupF32 (&status, refItem->data.md, "ZEROPT");
+        if (!status) {
+            psLogMsg ("fpcamera", PS_LOG_INFO, "a PHOTCODE.DATA recipe folder is missing FILTER");
+            continue;
+        }
+        photcode = psMetadataLookupStr (&status, refItem->data.md, "PHOTCODE");
+        if (!status) {
+            psLogMsg ("fpcamera", PS_LOG_INFO, "a PHOTCODE.DATA recipe folder is missing FILTER");
+            continue;
+        }
+        if (fixedzeropt != 0.0) {
+            // override the recipe's zero point with the fixed value (used for stacks)
+            zeropt = fixedzeropt;
+        }
+
+        // convert the minInst to a calibrated minimum magnitude
+        *minMag = minInst + 2.5*log10(exptime) + zeropt;
+
+        psFree (iter);
+        return photcode;
+    }
+    psFree (iter);
+
+  escape:
+    photcode = psMetadataLookupStr(NULL, recipe, "DVO.GETSTAR.PHOTCODE");
+    PS_ASSERT (photcode, NULL);
+
+    // give up and use fixed value
+    *minMag = psMetadataLookupF32(NULL, recipe, "DVO.GETSTAR.MIN.MAG");
+    return photcode;
+}
+# endif
Index: /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraParseCamera.c
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraParseCamera.c	(revision 42175)
+++ /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraParseCamera.c	(revision 42175)
@@ -0,0 +1,50 @@
+# include "fpcamera.h"
+
+# define ESCAPE(ERROR,FORMAT,...) { psError(ERROR, false, FORMAT, __VA_ARGS__); return false; }
+
+bool fpcameraParseCamera (pmConfig *config) {
+
+    bool status = false;
+
+    // the input image(s) are required arguments; they define the camera (or use the smf?)
+    pmFPAfile *input = pmFPAfileDefineFromArgs (&status, config, "FPCAMERA.INPUT", "INPUT");
+    if (!status) ESCAPE(FPCAMERA_ERR_CONFIG, "Failed to build FPA from FPCAMERA.INPUT");
+
+    // if MASK was supplied on command line, bind files to 'input'
+    pmFPAfileBindFromArgs (&status, input, config, "FPCAMERA.INPUT.MASK", "MASK");
+    if (!status) ESCAPE(PS_ERR_UNKNOWN, "failed to load find definition");
+
+    // if VARIANCE was supplied on command line, bind files to 'input'
+    pmFPAfileBindFromArgs (&status, input, config, "FPCAMERA.INPUT.VARIANCE", "VARIANCE");
+    if (!status) ESCAPE (PS_ERR_UNKNOWN, "failed to load find definition");
+
+    if (!psphotSetMaskBits (config)) ESCAPE (PS_ERR_UNKNOWN, "failed to set mask bit values");
+
+    // the input smf file is a required argument
+    pmFPAfileDefineFromArgs (&status, config, "FPCAMERA.INPUT.ASTROM", "INPUT.ASTROM");
+    if (!status) ESCAPE(FPCAMERA_ERR_CONFIG, "Failed to build FPA from FPCAMERA.INPUT.SMF");
+
+    // select the current recipe
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, FPCAMERA_RECIPE);
+    if (!recipe) ESCAPE(FPCAMERA_ERR_CONFIG, "Can't find FPCAMERA recipe!");
+
+    // associates the output pmFPA with the file OUTPUT on config->arguments
+    pmFPAfile *output = pmFPAfileDefineOutput (config, input->fpa, "FPCAMERA.OUTPUT");
+    if (!output) ESCAPE(FPCAMERA_ERR_CONFIG, "Failed to build OUTPUT FPA from FPCAMERA.INPUT");
+    output->save = true;
+
+    // Chip selection: turn on only the chips specified (option is not required)
+    char *chipLine = psMetadataLookupStr(&status, config->arguments, "CHIP_SELECTIONS"); 
+    psArray *chips = psStringSplitArray (chipLine, ",", false);
+    if (chips->n > 0) {
+	pmFPASelectChip (input->fpa, -1, true); // deselect all chips
+	for (int i = 0; i < chips->n; i++) {
+	    int chipNum = atoi(chips->data[i]);
+	    if (! pmFPASelectChip(input->fpa, chipNum, false)) ESCAPE(FPCAMERA_ERR_CONFIG, "Chip number %d doesn't exist in camera.\n", chipNum);
+        }
+    }
+    psFree (chips);
+
+    psTrace("fpcamera", 1, "Done with fpcameraParseCamera...\n");
+    return true;
+}
Index: /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraReadAstrometry.c
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraReadAstrometry.c	(revision 42175)
+++ /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraReadAstrometry.c	(revision 42175)
@@ -0,0 +1,59 @@
+# include "fpcamera.h"
+
+/* \brief this function loads the astrometry calibration from the input smf file */
+bool fpcameraReadAstrometry (pmFPAfile *input, pmConfig *config) {
+
+    // select the current recipe (just needed for pixel scale)
+    psMetadata *recipe  = psMetadataLookupPtr (NULL, config->recipes, FPCAMERA_RECIPE);
+    if (!recipe) {
+	psError(FPCAMERA_ERR_CONFIG, true, "Can't find FPCAMERA recipe!\n");
+	return false;
+    }
+
+    // physical pixel scale in microns per pixel (used in case of non-bilevel astrometry)
+    double pixelScale = psMetadataLookupF32 (&status, recipe, "FPCAMERA.PIXEL.SCALE");
+    if (!status) {
+	psError(PS_ERR_IO, true, "Failed to lookup pixel scale"); 
+	return false; 
+    } 
+
+    pmFPAview *view = pmFPAviewAlloc (0);
+
+    // check PHU header to see if we are using mosaic-level or per-chip astrometry
+    bool bilevelAstrometry = false;
+    pmHDU *phu = pmFPAviewThisPHU (view, input->fpa);
+    if (phu) {
+	char *ctype = psMetadataLookupStr (NULL, phu->header, "CTYPE1");
+	if (ctype) bilevelAstrometry = !strcmp (&ctype[4], "-DIS");
+    }
+    if (bilevelAstrometry) {
+	pmAstromReadBilevelMosaic (input->fpa, phu->header);
+    } 
+
+    while ((chip = pmFPAviewNextChip (view, input->fpa, 1)) != NULL) {
+	psTrace ("fpcamera", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+	if (!chip->process || !chip->file_exists || !chip->data_exists) { continue; }
+
+	// read WCS data from the corresponding header
+	pmHDU *hdu = pmFPAviewThisHDU (view, input->fpa);
+	int nAstro = psMetadataLookupS32 (&status, hdu->header, "NASTRO");
+	if (!nAstro) continue;
+	// XXX use additional information to identify poor quality astrometry?
+
+	if (bilevelAstrometry) {
+	    if (!pmAstromReadBilevelChip (chip, hdu->header)) {
+		psWarning("Could not get WCS information from header for chip %d, skipping", view->chip); 
+		continue;
+	    } 
+	} else {
+	    if (!pmAstromReadWCS (input->fpa, chip, hdu->header, pixelScale)) {
+		psWarning("Could not get WCS information from header for chip %d, skipping", view->chip); 
+		continue;
+	    } 
+	}
+    }
+    psLogMsg ("fpcamera", 3, "convert wcs terms to internal format : %f sec\n", psTimerMark ("fpcamera"));
+
+    psFree (view);
+    return true;
+}
Index: /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraVersion.c
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraVersion.c	(revision 42175)
+++ /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraVersion.c	(revision 42175)
@@ -0,0 +1,119 @@
+/** @file fpcameraVersion.c
+ *
+ *  @brief
+ *
+ *  @ingroup libfpcamera
+ *
+ *  Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+#include "fpcameraInternal.h"
+#include "fpcameraVersionDefinitions.h"
+
+#ifndef FPCAMERA_VERSION
+#error "FPCAMERA_VERSION is not set"
+#endif
+#ifndef FPCAMERA_BRANCH
+#error "FPCAMERA_BRANCH is not set"
+#endif
+#ifndef FPCAMERA_SOURCE
+#error "FPCAMERA_SOURCE is not set"
+#endif
+
+psString fpcameraVersion(void)
+{
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", FPCAMERA_BRANCH, FPCAMERA_VERSION);
+    return value;
+}
+
+psString fpcameraSource(void)
+{
+  return psStringCopy(FPCAMERA_SOURCE);
+}
+
+psString fpcameraVersionLong(void)
+{
+    psString version = fpcameraVersion();  // Version, to return
+    psString source = fpcameraSource();    // Source
+
+    psStringPrepend(&version, "fpcamera ");
+    psStringAppend(&version, " from %s, built %s, %s", source, __DATE__, __TIME__);
+    psFree(source);
+
+#ifdef __OPTIMIZE__
+    psStringAppend(&version, " optimised");
+#else
+    psStringAppend(&version, " unoptimised");
+#endif
+
+    return version;
+};
+
+bool fpcameraVersionHeader(psMetadata *header)
+{
+    PS_ASSERT_METADATA_NON_NULL(header, false);
+
+    psString version = fpcameraVersion(); // Software version
+    psString source = fpcameraSource();   // Software source
+
+    psMetadataAddStr(header, PS_LIST_TAIL, "ASTRO_V", PS_META_REPLACE, NULL, FPCAMERA_VERSION);
+    
+    psStringPrepend(&version, "fpcamera version: ");
+    psStringPrepend(&source, "fpcamera source: ");
+
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, version);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, source);
+
+    psFree(version);
+    psFree(source);
+
+    return true;
+}
+
+bool fpcameraVersionHeaderFull(psMetadata *header)
+{
+    PS_ASSERT_METADATA_NON_NULL(header, false);
+
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psString history = NULL;               // History string
+    psStringAppend(&history, "fpcamera at %s", timeString);
+    psFree(timeString);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, history);
+    psFree(history);
+
+    psLibVersionHeader(header);
+    psModulesVersionHeader(header);
+    ppStatsVersionHeader(header);
+    fpcameraVersionHeader(header);
+
+    return true;
+}
+
+void fpcameraVersionPrint(void)
+{
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psLogMsg("fpcamera", PS_LOG_INFO, "fpcamera at %s", timeString);
+    psFree(timeString);
+
+    psString pslib = psLibVersionLong();// psLib version
+    psString psmodules = psModulesVersionLong(); // psModules version
+    psString ppStats = ppStatsVersionLong(); // ppStats version
+    psString fpcamera = fpcameraVersionLong(); // fpcamera version
+
+    psLogMsg("fpcamera", PS_LOG_INFO, "%s", pslib);
+    psLogMsg("fpcamera", PS_LOG_INFO, "%s", psmodules);
+    psLogMsg("fpcamera", PS_LOG_INFO, "%s", ppStats);
+    psLogMsg("fpcamera", PS_LOG_INFO, "%s", fpcamera);
+
+    psFree(pslib);
+    psFree(psmodules);
+    psFree(ppStats);
+    psFree(fpcamera);
+
+    return;
+}
Index: /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraVersionDefinitions.h.in
===================================================================
--- /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraVersionDefinitions.h.in	(revision 42175)
+++ /branches/eam_branches/ipp-20220316/fpcamera/src/fpcameraVersionDefinitions.h.in	(revision 42175)
@@ -0,0 +1,8 @@
+#ifndef FPCAMERA_VERSION_DEFINITIONS_H
+#define FPCAMERA_VERSION_DEFINITIONS_H
+
+#define FPCAMERA_VERSION @FPCAMERA_VERSION@ // SVN version
+#define FPCAMERA_BRANCH  @FPCAMERA_BRANCH@  // SVN branch
+#define FPCAMERA_SOURCE  @FPCAMERA_SOURCE@  // SVN source
+
+#endif
