Index: /branches/eam_branches/20091201/ppViz/Makefile.am
===================================================================
--- /branches/eam_branches/20091201/ppViz/Makefile.am	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/Makefile.am	(revision 26837)
@@ -0,0 +1,3 @@
+SUBDIRS = src
+
+CLEANFILES = *.pyc *~ core core.*
Index: /branches/eam_branches/20091201/ppViz/autogen.sh
===================================================================
--- /branches/eam_branches/20091201/ppViz/autogen.sh	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/autogen.sh	(revision 26837)
@@ -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=ppViz
+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/20091201/ppViz/configure.ac
===================================================================
--- /branches/eam_branches/20091201/ppViz/configure.ac	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/configure.ac	(revision 26837)
@@ -0,0 +1,40 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_PREREQ(2.61)
+
+AC_INIT([ppViz], [0.1.1], [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_STDCFLAGS
+
+AC_LANG(C)
+AC_GNU_SOURCE
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+AC_SYS_LARGEFILE
+
+PKG_CHECK_MODULES([PSLIB], [pslib >= 1.0.0])
+PKG_CHECK_MODULES([PSMODULE], [psmodules >= 1.0.0])
+
+dnl Set CFLAGS for build
+IPP_STDOPTS
+CFLAGS="${CFLAGS} -Wall -Werror"
+
+IPP_VERSION
+
+AC_SUBST([PPVIZ_CFLAGS])
+AC_SUBST([PPVIZ_LIBS])
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+  src/ppVizPSF/Makefile
+  src/ppCoord/Makefile
+  src/ppVizPattern/Makefile
+])
+
+AC_OUTPUT
Index: /branches/eam_branches/20091201/ppViz/src/Makefile.am
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/Makefile.am	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/Makefile.am	(revision 26837)
@@ -0,0 +1,3 @@
+SUBDIRS = ppVizPSF ppCoord ppVizPattern
+
+CLEANFILES = *.pyc *~ core core.*
Index: /branches/eam_branches/20091201/ppViz/src/ppCoord/Makefile.am
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppCoord/Makefile.am	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppCoord/Makefile.am	(revision 26837)
@@ -0,0 +1,47 @@
+bin_PROGRAMS = ppCoord
+
+if HAVE_SVNVERSION
+PPCOORD_VERSION=`$(SVNVERSION) ..`
+else
+PPCOORD_VERSION="UNKNOWN"
+endif
+
+if HAVE_SVN
+PPCOORD_BRANCH=`$(SVN) info .. | $(SED) -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'`
+PPCOORD_SOURCE=`$(SVN) info | $(SED) -n -e 's/Repository UUID: // p'`
+else
+PPCOORD_BRANCH="UNKNOWN"
+PPCOORD_SOURCE="UNKNOWN"
+endif
+
+# Force recompilation of ppCoordVersion.c, since it gets the version information
+ppCoordVersion.c: ppCoordVersionDefinitions.h
+ppCoordVersionDefinitions.h: ppCoordVersionDefinitions.h.in FORCE
+	-$(RM) ppCoordVersionDefinitions.h
+	$(SED) -e "s|@PPCOORD_VERSION@|\"$(PPCOORD_VERSION)\"|" -e "s|@PPCOORD_BRANCH@|\"$(PPCOORD_BRANCH)\"|" -e "s|@PPCOORD_SOURCE@|\"$(PPCOORD_SOURCE)\"|" ppCoordVersionDefinitions.h.in > ppCoordVersionDefinitions.h
+FORCE: ;
+
+BUILT_SOURCES = ppCoordVersionDefinitions.h
+
+
+ppCoord_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PPCOORD_CFLAGS)
+ppCoord_LDFLAGS  = $(PSLIB_LIBS)   $(PSMODULE_LIBS)   $(PPCOORD_LIBS)
+
+ppCoord_SOURCES =		\
+	ppCoord.c		\
+	ppCoordArguments.c	\
+	ppCoordCamera.c	\
+	ppCoordData.c		\
+	ppCoordLoop.c		\
+	ppCoordVersion.c            
+
+noinst_HEADERS = \
+	ppCoord.h
+
+clean-local:
+	-rm -f TAGS
+
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
+
Index: /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoord.c
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoord.c	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoord.c	(revision 26837)
@@ -0,0 +1,43 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppCoord.h"
+
+int main(int argc, char *argv[])
+{
+    ppCoordVersionPrint();
+
+    ppCoordData *data = ppCoordDataInit(&argc, argv);
+    if (!data) {
+        psErrorStackPrint(stderr, "Unable to initialise.");
+        return PS_EXIT_CONFIG_ERROR;
+    }
+
+    if (!ppCoordArguments(data, argc, argv)) {
+        psErrorStackPrint(stderr, "Unable to parse arguments.");
+        psFree(data);
+        return PS_EXIT_CONFIG_ERROR;
+    }
+
+    if (!ppCoordCamera(data)) {
+        psErrorStackPrint(stderr, "Unable to parse camera configuration.");
+        psFree(data);
+        return PS_EXIT_CONFIG_ERROR;
+    }
+
+    if (!ppCoordLoop(data)) {
+        psErrorStackPrint(stderr, "Unable to process data.");
+        psFree(data);
+        return PS_EXIT_DATA_ERROR;
+    }
+
+    psFree(data);
+
+    return PS_EXIT_SUCCESS;
+}
+
Index: /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoord.h
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoord.h	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoord.h	(revision 26837)
@@ -0,0 +1,43 @@
+#ifndef PP_VIZPSF_H
+#define PP_VIZPSF_H
+
+#include <pslib.h>
+#include <psmodules.h>
+
+#define PPVIZPSF_RECIPE "PPVIZPSF"      // Recipe name
+
+// Data for processing
+typedef struct {
+    psString astromName;                // Filename with astrometry
+    psString pixelsName;                // Filename with pixel coordinates
+    psString chipName;                  // Name of chip of interest
+    psString radecName;                 // Filename with sky coordinates
+    pmConfig *config;                   // Configuration
+} ppCoordData;
+
+/// Initialise data for processing
+ppCoordData *ppCoordDataInit(int *argc, char *argv[] // Command-line arguments
+    );
+
+/// Parse command-line arguments
+bool ppCoordArguments(ppCoordData *data, // Data for processing
+                        int argc, char *argv[] // Command-line arguments
+    );
+
+/// Parse camera configurations
+bool ppCoordCamera(ppCoordData *data // Data for processing
+    );
+
+/// Loop over input data, processing
+bool ppCoordLoop(ppCoordData *data // Data for processing
+    );
+
+/// Add version information to header
+bool ppCoordVersionHeader(
+    psMetadata *header                  // Header to supplement
+    );
+
+/// Print version information to stdout
+void ppCoordVersionPrint(void);
+
+#endif
Index: /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoordArguments.c
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoordArguments.c	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoordArguments.c	(revision 26837)
@@ -0,0 +1,79 @@
+/** @file ppCoordArguments.c
+ *
+ *  @brief
+ *
+ *  @ingroup ppCoord
+ *
+ *  @author Paul Price
+ *  Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppCoord.h"
+
+/// Print usage information and die
+static void usage(const char *program,  // Name of the program
+                  psMetadata *arguments, // Command-line arguments
+                  ppCoordData *data   // Run-time data
+    )
+{
+    fprintf(stderr, "\nPan-STARRS coordinate transformation\n\n");
+    fprintf(stderr, "Usage: %s \n\n", program);
+    fprintf(stderr, "\n");
+    psArgumentHelp(arguments);
+    psFree(arguments);
+    psFree(data);
+
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(PS_EXIT_CONFIG_ERROR);
+}
+
+
+bool ppCoordArguments(ppCoordData *data, int argc, char *argv[])
+{
+    assert(data);
+    assert(data->config);
+
+    psMetadata *arguments = psMetadataAlloc(); // Command-line arguments
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-astrom", 0, "Filename with astrometry", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-pixels", 0, "Filename with pixel coordinates", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-chip", 0, "Chip for pixel coordinates", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-radec", 0, "Filename with RA, Dec", NULL);
+    if (!psArgumentParse(arguments, &argc, argv) || argc != 1) {
+        usage(argv[0], arguments, data);
+    }
+
+    bool mdok;                          // Status of MD lookup
+    data->astromName = psMemIncrRefCounter(psMetadataLookupStr(NULL, arguments, "-astrom"));
+    data->pixelsName = psMemIncrRefCounter(psMetadataLookupStr(NULL, arguments, "-pixels"));
+    data->chipName = psMemIncrRefCounter(psMetadataLookupStr(NULL, arguments, "-chip"));
+    data->radecName = psMemIncrRefCounter(psMetadataLookupStr(&mdok, arguments, "-radec"));
+//    psMetadataAddStr(data->config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Output root name", data->outRoot);
+
+    psTrace("ppCoord", 1, "Done reading command-line arguments\n");
+    psFree(arguments);
+
+    if (!data->astromName) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "No astrometry file specified via -astrom");
+        return false;
+    }
+
+    if (!data->pixelsName && !data->radecName) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Neither -pixels nor -radec provided.");
+        return false;
+    }
+
+    return true;
+}
+
+
Index: /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoordCamera.c
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoordCamera.c	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoordCamera.c	(revision 26837)
@@ -0,0 +1,51 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppCoord.h"
+
+/// Add a single filename to the arguments as an array, so that it can be used with pmFPAfileBindFromArgs, etc
+static void fileArguments(const char *file, // The symbolic name for the file
+                          const char *name, // The name of the file
+                          const char *comment, // Description of the file
+                          pmConfig *config // Configuration
+    )
+{
+    psArray *files = psArrayAlloc(1); // Array with file names
+    files->data[0] = psStringCopy(name);
+    if (psMetadataLookup(config->arguments, file)) {
+        psMetadataRemoveKey(config->arguments, file);
+    }
+    psMetadataAddArray(config->arguments, PS_LIST_TAIL, file, 0, comment, files);
+    psFree(files);
+    return;
+}
+
+
+bool ppCoordCamera(ppCoordData *data // Run-time data
+    )
+{
+    bool status;                        // Status of file definition
+
+    fileArguments("ASTROM", data->astromName, "Input astrometry", data->config);
+    pmFPAfile *astrom = pmFPAfileDefineFromArgs(&status, data->config, "PSWARP.ASTROM", "ASTROM"); // File
+    if (!status || !astrom) {
+        psError(PS_ERR_IO, false, "Failed to build file from PSWARP.ASTROM");
+        return false;
+    }
+
+#if 0
+    // Now the camera has been determined, we can read the recipe
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, data->config->recipes, PPVIZPSF_RECIPE); // Recipe
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find recipe %s", PPVIZPSF_RECIPE);
+        return false;
+    }
+#endif
+
+    return true;
+}
Index: /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoordData.c
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoordData.c	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoordData.c	(revision 26837)
@@ -0,0 +1,51 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppCoord.h"
+
+// Destructor
+static void coordDataFree(ppCoordData *data // Data to free
+    )
+{
+    psFree(data->astromName);
+    psFree(data->pixelsName);
+    psFree(data->chipName);
+    psFree(data->radecName);
+    psFree(data->config);
+    return;
+}
+
+
+ppCoordData *ppCoordDataAlloc(void)
+{
+    ppCoordData *data = psAlloc(sizeof(ppCoordData)); // Processing data, to return
+    psMemSetDeallocator(data, (psFreeFunc)coordDataFree);
+
+    data->astromName = NULL;
+    data->pixelsName = NULL;
+    data->chipName = NULL;
+    data->radecName = NULL;
+    data->config = NULL;
+
+    return data;
+}
+
+
+ppCoordData *ppCoordDataInit(int *argc, char **argv)
+{
+    PS_ASSERT_PTR_NON_NULL(argc, NULL);
+    PS_ASSERT_PTR_NON_NULL(argv, NULL);
+
+    ppCoordData *data = ppCoordDataAlloc(); // Processing data, to return
+    data->config = pmConfigRead(argc, argv, NULL);
+    if (!data->config) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read configuration.");
+        return NULL;
+    }
+    return data;
+}
Index: /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoordLoop.c
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoordLoop.c	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoordLoop.c	(revision 26837)
@@ -0,0 +1,206 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppCoord.h"
+
+
+bool ppCoordLoop(ppCoordData *data // Run-time data
+    )
+{
+    pmConfig *config = data->config;                                        // Configuration data
+    pmFPAfile *astromFile = pmFPAfileSelectSingle(config->files, "PSWARP.ASTROM", 0); // File with astrometry
+
+    if (astromFile->fpa->chips->n > 0 && data->pixelsName && !data->chipName) {
+        psWarning("Pixel coordinates supplied, but no chip name provided.");
+    }
+
+    psArray *pixels = NULL, *radec = NULL; // Array of vectors with coordinates
+    psArray *radecOut = NULL;              // Output for sky coordinates
+    if (data->pixelsName) {
+        pixels = psVectorsReadFromFile(data->pixelsName, "%f %f");
+        if (!pixels || pixels->n != 2) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to read pixel coordinates");
+            return false;
+        }
+    }
+    if (data->radecName) {
+        radec = psVectorsReadFromFile(data->radecName, "%lf %lf");
+        if (!radec || radec->n != 2) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to read sky coordinates");
+            return false;
+        }
+        psVector *ra = radec->data[0];  // RA coordinates
+        long num = ra->n;               // Number of coordinates
+        radecOut = psArrayAlloc(3);
+        radecOut->data[0] = psArrayAlloc(num);
+        radecOut->data[1] = psVectorAlloc(num, PS_TYPE_F32);
+        radecOut->data[2] = psVectorAlloc(num, PS_TYPE_F32);
+        psVectorInit(radecOut->data[1], NAN);
+        psVectorInit(radecOut->data[2], NAN);
+    }
+
+    pmFPAview *view = pmFPAviewAlloc(0); // Pointer into FPA hierarchy
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+        return NULL;
+    }
+
+    // find the FPA phu
+    bool bilevelAstrometry = false;
+    pmHDU *phu = pmFPAviewThisPHU(view, astromFile->fpa);
+    if (phu) {
+        char *ctype = psMetadataLookupStr(NULL, phu->header, "CTYPE1");
+        if (ctype) {
+            bilevelAstrometry = !strcmp (&ctype[4], "-DIS");
+        }
+    }
+    if (bilevelAstrometry) {
+        if (!pmAstromReadBilevelMosaic(astromFile->fpa, phu->header)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to read bilevel mosaic astrometry for input FPA.");
+            psFree(view);
+            return false;
+        }
+    }
+
+    pmChip *chip;                       // Chip from FPA
+    while ((chip = pmFPAviewNextChip(view, astromFile->fpa, 1))) {
+        if (!chip->process || !chip->file_exists) {
+            continue;
+        }
+        const char *chipName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME"); // Name of chip
+        if (data->chipName && strcmp(chipName, data->chipName) != 0) {
+                continue;
+        }
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+            psError(PS_ERR_UNKNOWN, false, "Error loading data from files.");
+            return false;
+        }
+
+        if (chip->cells->n != 1) {
+            psWarning("More than one cell present for chip %d", view->chip);
+        }
+
+        // read WCS data from the corresponding header
+        pmHDU *hdu = pmFPAviewThisHDU (view, astromFile->fpa);
+        if (bilevelAstrometry) {
+            if (!pmAstromReadBilevelChip (chip, hdu->header)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to read bilevel chip astrometry for input FPA.");
+                psFree(view);
+                return false;
+            }
+        } else {
+            // we use a default FPA pixel scale of 1.0
+            psWarning("Reading WCS astrometry for chip %s.", chipName);
+            if (!pmAstromReadWCS(astromFile->fpa, chip, hdu->header, 1.0)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to read WCS astrometry for input FPA.");
+                psFree(view);
+                return false;
+            }
+        }
+
+        if (pixels) {
+            psVector *x = pixels->data[0], *y = pixels->data[1]; // Pixel coordinates
+            long num = x->n;                                     // Number of coordinates
+
+            psPlane *pix = psPlaneAlloc();   // Pixel coordinates on chip
+            psPlane *fp = psPlaneAlloc();    // Focal plane coordinates
+            psPlane *tp = psPlaneAlloc();    // Tangent plane coordinates
+            psSphere *sky = psSphereAlloc(); // Sky coordinates
+
+            for (long i = 0; i < num; i++) {
+                pix->x = x->data.F32[i];
+                pix->y = y->data.F32[i];
+
+                psPlaneTransformApply(fp, chip->toFPA, pix);
+                psPlaneTransformApply(tp, astromFile->fpa->toTPA, fp);
+                psDeproject(sky, tp, astromFile->fpa->toSky);
+
+                fprintf(stdout, "%s %.3f %.3f --> %.10lf %.10lf\n", chipName, pix->x, pix->y, sky->r, sky->d);
+            }
+            psFree(pix);
+            psFree(fp);
+            psFree(tp);
+            psFree(sky);
+        }
+
+        if (radec) {
+            psVector *ra = radec->data[0], *dec = radec->data[1]; // Pixel coordinates
+            long num = ra->n;                                     // Number of coordinates
+
+            int numCols = psMetadataLookupS32(NULL, hdu->header, "IMNAXIS1"); // Number of columns
+            int numRows = psMetadataLookupS32(NULL, hdu->header, "IMNAXIS2"); // Number of rows
+            if (numCols <= 0 || numRows <= 0) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to read size of chip.");
+                return false;
+            }
+
+            psPlane *pix = psPlaneAlloc();   // Pixel coordinates on chip
+            psPlane *fp = psPlaneAlloc();    // Focal plane coordinates
+            psPlane *tp = psPlaneAlloc();    // Tangent plane coordinates
+            psSphere *sky = psSphereAlloc(); // Sky coordinates
+
+            psArray *chipPix = radecOut->data[0]; // Chip for pixels
+            psVector *xPix = radecOut->data[1];   // x coordinate for pixels
+            psVector *yPix = radecOut->data[2];   // y coordinate for pixels
+
+            for (long i = 0; i < num; i++) {
+                sky->r = ra->data.F64[i];
+                sky->d = dec->data.F64[i];
+
+                psProject(tp, sky, astromFile->fpa->toSky);
+                psPlaneTransformApply(fp, astromFile->fpa->fromTPA, tp);
+                psPlaneTransformApply(pix, chip->fromFPA, fp);
+
+                float x = pix->x, y = pix->y; // Pixel coordinates
+                if (!data->chipName && (x < 0 || x > numCols || y < 0 || y > numRows)) {
+                    // Not on this chip
+                    continue;
+                }
+
+                chipPix->data[i] = psStringCopy(chipName);
+                xPix->data.F32[i] = x;
+                yPix->data.F32[i] = y;
+            }
+            psFree(pix);
+            psFree(fp);
+            psFree(tp);
+            psFree(sky);
+        }
+
+        // Chip
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+            psError(PS_ERR_UNKNOWN, false, "Error saving data to files.");
+            return false;
+        }
+    }
+    // FPA
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+        psError(PS_ERR_UNKNOWN, false, "Error saving data to files.");
+        return false;
+    }
+
+    if (radecOut) {
+        psArray *chipPix = radecOut->data[0]; // Chip for pixels
+        psVector *xPix = radecOut->data[1];   // x coordinate for pixels
+        psVector *yPix = radecOut->data[2];   // y coordinate for pixels
+        psVector *ra = radec->data[0];        // RA coordinate
+        psVector *dec = radec->data[1];       // Dec coordinate
+
+        for (long i = 0; i < chipPix->n; i++) {
+            const char *chipName = chipPix->data[i]; // Name of chip
+            fprintf(stdout, "%.10lf %.10lf --> %.3f %.3f %s\n",
+                    ra->data.F64[i], dec->data.F64[i], xPix->data.F32[i], yPix->data.F32[i],
+                    chipName ? chipName : "UNKNOWN");
+        }
+    }
+
+    psFree(pixels);
+    psFree(radec);
+    psFree(radecOut);
+
+    return true;
+}
Index: /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoordVersion.c
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoordVersion.c	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoordVersion.c	(revision 26837)
@@ -0,0 +1,116 @@
+/** @file ppCoordVersion.c
+ *
+ *  @brief
+ *
+ *  @ingroup ppCoord
+ *
+ *  @author IfA
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2009-02-06 19:45:30 $
+ *  Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppCoord.h"
+#include "ppCoordVersionDefinitions.h"
+
+#ifndef PPCOORD_VERSION
+#error "PPCOORD_VERSION is not set"
+#endif
+#ifndef PPCOORD_BRANCH
+#error "PPCOORD_BRANCH is not set"
+#endif
+#ifndef PPCOORD_SOURCE
+#error "PPCOORD_SOURCE is not set"
+#endif
+
+psString ppCoordVersion(void)
+{
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", PPCOORD_BRANCH, PPCOORD_VERSION);
+    return value;
+}
+
+psString ppCoordSource(void)
+{
+    return psStringCopy(PPCOORD_SOURCE);
+}
+
+psString ppCoordVersionLong(void)
+{
+    psString version = ppCoordVersion();  // Version, to return
+    psString source = ppCoordSource();    // Source
+
+    psStringPrepend(&version, "ppCoord ");
+    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 ppCoordVersionHeader(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, "ppCoord at %s", timeString);
+    psFree(timeString);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, history);
+    psFree(history);
+
+    psLibVersionHeader(header);
+    psModulesVersionHeader(header);
+
+    psString version = ppCoordVersion(); // Software version
+    psString source  = ppCoordSource();  // Software source
+
+    psStringPrepend(&version, "ppCoord version: ");
+    psStringPrepend(&version, "ppCoord 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;
+}
+
+void ppCoordVersionPrint(void)
+{
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psLogMsg("ppCoord", PS_LOG_INFO, "ppCoord at %s", timeString);
+    psFree(timeString);
+
+    psString pslib = psLibVersionLong();// psLib version
+    psString psmodules = psModulesVersionLong(); // psModules version
+    psString ppCoord = ppCoordVersionLong(); // ppCoord version
+
+    psLogMsg("ppCoord", PS_LOG_INFO, "%s", pslib);
+    psLogMsg("ppCoord", PS_LOG_INFO, "%s", psmodules);
+    psLogMsg("ppCoord", PS_LOG_INFO, "%s", ppCoord);
+
+    psFree(pslib);
+    psFree(psmodules);
+    psFree(ppCoord);
+
+    return;
+}
Index: /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoordVersionDefinitions.h.in
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoordVersionDefinitions.h.in	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppCoord/ppCoordVersionDefinitions.h.in	(revision 26837)
@@ -0,0 +1,8 @@
+#ifndef PPCOORD_VERSION_DEFINITIONS_H
+#define PPCOORD_VERSION_DEFINITIONS_H
+
+#define PPCOORD_VERSION @PPCOORD_VERSION@ // SVN version
+#define PPCOORD_BRANCH  @PPCOORD_BRANCH@  // SVN branch
+#define PPCOORD_SOURCE  @PPCOORD_SOURCE@  // SVN source
+
+#endif
Index: /branches/eam_branches/20091201/ppViz/src/ppVizPSF/Makefile.am
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppVizPSF/Makefile.am	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppVizPSF/Makefile.am	(revision 26837)
@@ -0,0 +1,47 @@
+bin_PROGRAMS = ppVizPSF
+
+if HAVE_SVNVERSION
+PPVIZPSF_VERSION=`$(SVNVERSION) ..`
+else
+PPVIZPSF_VERSION="UNKNOWN"
+endif
+
+if HAVE_SVN
+PPVIZPSF_BRANCH=`$(SVN) info .. | $(SED) -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'`
+PPVIZPSF_SOURCE=`$(SVN) info | $(SED) -n -e 's/Repository UUID: // p'`
+else
+PPVIZPSF_BRANCH="UNKNOWN"
+PPVIZPSF_SOURCE="UNKNOWN"
+endif
+
+# Force recompilation of ppVizPSFVersion.c, since it gets the version information
+ppVizPSFVersion.c: ppVizPSFVersionDefinitions.h
+ppVizPSFVersionDefinitions.h: ppVizPSFVersionDefinitions.h.in FORCE
+	-$(RM) ppVizPSFVersionDefinitions.h
+	$(SED) -e "s|@PPVIZPSF_VERSION@|\"$(PPVIZPSF_VERSION)\"|" -e "s|@PPVIZPSF_BRANCH@|\"$(PPVIZPSF_BRANCH)\"|" -e "s|@PPVIZPSF_SOURCE@|\"$(PPVIZPSF_SOURCE)\"|" ppVizPSFVersionDefinitions.h.in > ppVizPSFVersionDefinitions.h
+FORCE: ;
+
+BUILT_SOURCES = ppVizPSFVersionDefinitions.h
+
+
+ppVizPSF_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PPVIZPSF_CFLAGS)
+ppVizPSF_LDFLAGS  = $(PSLIB_LIBS)   $(PSMODULE_LIBS)   $(PPVIZPSF_LIBS)
+
+ppVizPSF_SOURCES =		\
+	ppVizPSF.c		\
+	ppVizPSFArguments.c	\
+	ppVizPSFCamera.c	\
+	ppVizPSFData.c		\
+	ppVizPSFLoop.c		\
+	ppVizPSFVersion.c            
+
+noinst_HEADERS = \
+	ppVizPSF.h
+
+clean-local:
+	-rm -f TAGS
+
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
+
Index: /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSF.c
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSF.c	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSF.c	(revision 26837)
@@ -0,0 +1,43 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppVizPSF.h"
+
+int main(int argc, char *argv[])
+{
+    ppVizPSFVersionPrint();
+
+    ppVizPSFData *data = ppVizPSFDataInit(&argc, argv);
+    if (!data) {
+        psErrorStackPrint(stderr, "Unable to initialise.");
+        return PS_EXIT_CONFIG_ERROR;
+    }
+
+    if (!ppVizPSFArguments(data, argc, argv)) {
+        psErrorStackPrint(stderr, "Unable to parse arguments.");
+        psFree(data);
+        return PS_EXIT_CONFIG_ERROR;
+    }
+
+    if (!ppVizPSFCamera(data)) {
+        psErrorStackPrint(stderr, "Unable to parse camera configuration.");
+        psFree(data);
+        return PS_EXIT_CONFIG_ERROR;
+    }
+
+    if (!ppVizPSFLoop(data)) {
+        psErrorStackPrint(stderr, "Unable to process data.");
+        psFree(data);
+        return PS_EXIT_DATA_ERROR;
+    }
+
+    psFree(data);
+
+    return PS_EXIT_SUCCESS;
+}
+
Index: /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSF.h
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSF.h	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSF.h	(revision 26837)
@@ -0,0 +1,47 @@
+#ifndef PP_VIZPSF_H
+#define PP_VIZPSF_H
+
+#include <pslib.h>
+#include <psmodules.h>
+
+#define PPVIZPSF_RECIPE "PPVIZPSF"      // Recipe name
+
+// Data for processing
+typedef struct {
+    psString psfName;                   // Filename with PSF
+    psString sourcesName;               // Filename with sources
+    int fakeNum;		        // Number of fake sources
+    float fakeMag;                      // Magnitude of fake sources
+    psString outRoot;                   // Output root name
+    float minFlux;                      // Minimum flux for sources
+    int size;                           // Size of PSF image
+    float x, y;                         // Position of fake source
+    pmConfig *config;                   // Configuration
+} ppVizPSFData;
+
+/// Initialise data for processing
+ppVizPSFData *ppVizPSFDataInit(int *argc, char *argv[] // Command-line arguments
+    );
+
+/// Parse command-line arguments
+bool ppVizPSFArguments(ppVizPSFData *data, // Data for processing
+                        int argc, char *argv[] // Command-line arguments
+    );
+
+/// Parse camera configurations
+bool ppVizPSFCamera(ppVizPSFData *data // Data for processing
+    );
+
+/// Loop over input data, processing
+bool ppVizPSFLoop(ppVizPSFData *data // Data for processing
+    );
+
+/// Add version information to header
+bool ppVizPSFVersionHeader(
+    psMetadata *header                  // Header to supplement
+    );
+
+/// Print version information to stdout
+void ppVizPSFVersionPrint(void);
+
+#endif
Index: /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSFArguments.c
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSFArguments.c	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSFArguments.c	(revision 26837)
@@ -0,0 +1,79 @@
+/** @file ppVizPSFArguments.c
+ *
+ *  @brief
+ *
+ *  @ingroup ppVizPSF
+ *
+ *  @author Paul Price
+ *  Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppVizPSF.h"
+
+/// Print usage information and die
+static void usage(const char *program,  // Name of the program
+                  psMetadata *arguments, // Command-line arguments
+                  ppVizPSFData *data   // Run-time data
+    )
+{
+    fprintf(stderr, "\nPan-STARRS PSF visualisation\n\n");
+    fprintf(stderr,
+            "Usage: %s -psf PSF.psf -sources SOURCES.cmf OUTPUT_ROOT\n\n",
+            program);
+    fprintf(stderr, "\n");
+    psArgumentHelp(arguments);
+    psFree(arguments);
+    psFree(data);
+
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(PS_EXIT_CONFIG_ERROR);
+}
+
+
+bool ppVizPSFArguments(ppVizPSFData *data, int argc, char *argv[])
+{
+    assert(data);
+    assert(data->config);
+
+    psMetadata *arguments = psMetadataAlloc(); // Command-line arguments
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-psf", 0, "Filename of PSF", NULL);
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-sources", 0, "Filename of CMF sources", NULL);
+    psMetadataAddS32(arguments, PS_LIST_TAIL, "-fake-num", 0, "Number of fake sources", 0);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-fake-mag", 0, "Magnitude of fake sources", NAN);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-x", 0, "x position of fake source [0..1]", NAN);
+    psMetadataAddF32(arguments, PS_LIST_TAIL, "-y", 0, "y position of fake source [0..1]", NAN);
+    if (argc == 1 || !psArgumentParse(arguments, &argc, argv) || argc != 2) {
+        usage(argv[0], arguments, data);
+    }
+
+    bool mdok;                          // Status of MD lookup
+    data->psfName = psMemIncrRefCounter(psMetadataLookupStr(NULL, arguments, "-psf"));
+    data->sourcesName = psMemIncrRefCounter(psMetadataLookupStr(&mdok, arguments, "-sources"));
+    data->fakeNum = psMetadataLookupS32(NULL, arguments, "-fake-num");
+    data->fakeMag = psMetadataLookupF32(NULL, arguments, "-fake-mag");
+    data->outRoot = psStringCopy(argv[1]);
+    data->x = psMetadataLookupF32(NULL, arguments, "-x");
+    data->y = psMetadataLookupF32(NULL, arguments, "-y");
+    psMetadataAddStr(data->config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Output root name", data->outRoot);
+
+    psTrace("ppVizPSF", 1, "Done reading command-line arguments\n");
+    psFree(arguments);
+
+    PS_ASSERT_STRING_NON_EMPTY(data->psfName, false);
+    PS_ASSERT_STRING_NON_EMPTY(data->outRoot, false);
+
+    return true;
+}
+
+
Index: /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSFCamera.c
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSFCamera.c	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSFCamera.c	(revision 26837)
@@ -0,0 +1,80 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppVizPSF.h"
+
+/// Add a single filename to the arguments as an array, so that it can be used with pmFPAfileBindFromArgs, etc
+static void fileArguments(const char *file, // The symbolic name for the file
+                          const char *name, // The name of the file
+                          const char *comment, // Description of the file
+                          pmConfig *config // Configuration
+    )
+{
+    psArray *files = psArrayAlloc(1); // Array with file names
+    files->data[0] = psStringCopy(name);
+    if (psMetadataLookup(config->arguments, file)) {
+        psMetadataRemoveKey(config->arguments, file);
+    }
+    psMetadataAddArray(config->arguments, PS_LIST_TAIL, file, 0, comment, files);
+    psFree(files);
+    return;
+}
+
+
+bool ppVizPSFCamera(ppVizPSFData *data // Run-time data
+    )
+{
+    bool status;                        // Status of file definition
+
+    fileArguments("PSF", data->psfName, "Input PSF", data->config);
+    pmFPAfile *psf = pmFPAfileDefineFromArgs(&status, data->config, "PSPHOT.PSF.LOAD", "PSF"); // File
+    if (!status || !psf) {
+        psError(PS_ERR_IO, false, "Failed to build file from PSPHOT.LOAD.PSF");
+        return false;
+    }
+
+    if (data->sourcesName) {
+        fileArguments("SOURCES", data->sourcesName, "Input sources", data->config);
+        pmFPAfile *srcs = pmFPAfileBindFromArgs(&status, psf, data->config,
+                                                "PSPHOT.INPUT.CMF", "SOURCES"); // File
+        if (!status || !srcs) {
+            psError(PS_ERR_IO, false, "Failed to build file from PSPHOT.INPUT.CMF");
+            return false;
+        }
+    }
+
+    pmFPAfile *output = pmFPAfileDefineOutput(data->config, psf->fpa, "PPVIZPSF.OUTPUT");
+    if (!output) {
+        psError(psErrorCodeLast(), false, "Unable to define output.");
+        return false;
+    }
+    output->save = true;
+
+    // Now the camera has been determined, we can read the recipe
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, data->config->recipes, PPVIZPSF_RECIPE); // Recipe
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find recipe %s", PPVIZPSF_RECIPE);
+        return false;
+    }
+
+    data->minFlux = psMetadataLookupF32(NULL, recipe, "MINFLUX"); // Minimum flux
+    if (!isfinite(data->minFlux) || data->minFlux <= 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find legitimate value for MINFLUX");
+        return false;
+    }
+
+    if (!data->sourcesName) {
+        data->size = psMetadataLookupS32(NULL, recipe, "SIZE"); // Size of PSF
+        if (data->size <= 0) {
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to find legitimate value for SIZE");
+            return false;
+        }
+    }
+
+    return true;
+}
Index: /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSFData.c
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSFData.c	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSFData.c	(revision 26837)
@@ -0,0 +1,55 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppVizPSF.h"
+
+// Destructor
+static void vizPSFDataFree(ppVizPSFData *data // Data to free
+    )
+{
+    psFree(data->psfName);
+    psFree(data->sourcesName);
+    psFree(data->outRoot);
+    psFree(data->config);
+    return;
+}
+
+
+ppVizPSFData *ppVizPSFDataAlloc(void)
+{
+    ppVizPSFData *data = psAlloc(sizeof(ppVizPSFData)); // Processing data, to return
+    psMemSetDeallocator(data, (psFreeFunc)vizPSFDataFree);
+
+    data->psfName = NULL;
+    data->sourcesName = NULL;
+    data->fakeNum = 0;
+    data->fakeMag = NAN;
+    data->outRoot = NULL;
+    data->config = NULL;
+    data->minFlux = NAN;
+    data->size = 0;
+    data->x = NAN;
+    data->y = NAN;
+
+    return data;
+}
+
+
+ppVizPSFData *ppVizPSFDataInit(int *argc, char **argv)
+{
+    PS_ASSERT_PTR_NON_NULL(argc, NULL);
+    PS_ASSERT_PTR_NON_NULL(argv, NULL);
+
+    ppVizPSFData *data = ppVizPSFDataAlloc(); // Processing data, to return
+    data->config = pmConfigRead(argc, argv, PPVIZPSF_RECIPE);
+    if (!data->config) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read configuration.");
+        return NULL;
+    }
+    return data;
+}
Index: /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSFLoop.c
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSFLoop.c	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSFLoop.c	(revision 26837)
@@ -0,0 +1,189 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppVizPSF.h"
+
+
+bool ppVizPSFLoop(ppVizPSFData *data // Run-time data
+    )
+{
+    pmConfig *config = data->config;                                        // Configuration data
+    pmFPAfile *psfFile = pmFPAfileSelectSingle(config->files, "PSPHOT.PSF.LOAD", 0); // File with PSF
+
+    pmFPAview *view = pmFPAviewAlloc(0); // Pointer into FPA hierarchy
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+        return NULL;
+    }
+
+    pmChip *chip;                       // Chip from FPA
+    while ((chip = pmFPAviewNextChip(view, psfFile->fpa, 1))) {
+        if (!chip->process || !chip->file_exists) {
+            continue;
+        }
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+            psError(PS_ERR_UNKNOWN, false, "Error loading data from files.");
+            return false;
+        }
+
+        if (chip->cells->n != 1) {
+            psWarning("More than one cell present for chip %d", view->chip);
+        }
+
+        pmCell *cell;                   // Cell from chip
+        while ((cell = pmFPAviewNextCell(view, psfFile->fpa, 1))) {
+            if (!cell->process || !cell->file_exists) {
+                continue;
+            }
+            if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+                psError(PS_ERR_UNKNOWN, false, "Error loading data from files.");
+                return false;
+            }
+
+            if (cell->readouts->n == 0) {
+                pmReadout *ro = pmReadoutAlloc(cell);
+                ro->data_exists = true;
+                cell->data_exists = true;
+                chip->data_exists = true;
+                psFree(ro);             // Drop reference
+            }
+
+            if (cell->readouts->n > 1) {
+                psWarning("More than one readout present for chip %d, cell %d", view->chip, view->cell);
+            }
+
+            pmReadout *readout;         // Readout from cell
+            while ((readout = pmFPAviewNextReadout(view, psfFile->fpa, 1))) {
+                if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+                    psError(PS_ERR_UNKNOWN, false, "Error loading data from files.");
+                    return false;
+                }
+                if (!readout->data_exists) {
+                    continue;
+                }
+
+                pmPSF *psf = psMetadataLookupPtr(NULL, chip->analysis, "PSPHOT.PSF");              // PSF
+                assert(psf);
+
+                bool mdok;              // Status of MD lookup
+                psArray *sources = psMetadataLookupPtr(&mdok, readout->analysis, "PSPHOT.SOURCES"); // Sources
+                int numCols = 0, numRows = 0;              // Size of image
+                psVector *xOffset = NULL, *yOffset = NULL; // Offset from source to true position
+
+		if (sources || (data->fakeNum > 0 && isfinite(data->fakeMag))) {
+		  numCols = psf->fieldNx;
+		  numRows = psf->fieldNy;
+		  psLogMsg("ppVizPSF", PS_LOG_INFO, "Generating %dx%d image", numCols, numRows);
+		}
+                if (sources) {
+                    psMemIncrRefCounter(sources);
+                    psLogMsg("ppVizPSF", PS_LOG_INFO, "Using %ld input sources", sources->n);
+                }
+		if (data->fakeNum > 0 && isfinite(data->fakeMag)) {
+		  long numOld = 0; // Old number of sources
+		  long numNew = -1; // New number of sources
+		  psLogMsg("ppVizPSF", PS_LOG_INFO, "Adding %d fake sources", data->fakeNum);
+		  if (sources) {
+		    numOld = sources->n;
+		    numNew = numOld + data->fakeNum;
+		    sources = psArrayRealloc(sources, numNew);
+		    sources->n = numNew;
+		  } else {
+		    numNew = data->fakeNum;
+		    sources = psArrayAlloc(numNew);
+		  }
+
+		  psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
+
+		  for (int i = numOld; i < numNew; i++) {
+		    pmSource *source = pmSourceAlloc(); // Fake source
+                    sources->data[i] = source;
+		    float xSrc = psRandomUniform(rng) * numCols, ySrc = psRandomUniform(rng) * numRows; // Position of source
+
+                    pmModel *model = pmModelFromPSFforXY(psf, xSrc, ySrc, 1.0); // Model for normalisation
+                    float fluxNorm = model->modelFlux(model->params);               // Flux for peak=1
+		    float fluxPeak = powf(10.0, -0.4 * data->fakeMag) / fluxNorm; // Peak flux
+                    source->peak = pmPeakAlloc(xSrc, ySrc, fluxPeak, PM_PEAK_LONE);
+                    source->psfMag = data->fakeMag;
+                    psFree(model);
+		  }
+		}		  
+		if (!sources) {
+		  // Generate fake image with only a single realisation of the PSF
+                    sources = psArrayAlloc(1);
+                    pmSource *source = pmSourceAlloc(); // Fake source
+                    sources->data[0] = source;
+                    float xRel = isfinite(data->x) ? data->x : 0.5; // Relative position in x
+                    float yRel = isfinite(data->y) ? data->y : 0.5; // Relative position in y
+                    float xSrc = xRel * psf->fieldNx, ySrc = yRel * psf->fieldNy; // Position of source
+                    source->peak = pmPeakAlloc(xSrc, ySrc, 1.0, PM_PEAK_LONE);
+                    pmModel *model = pmModelFromPSFforXY(psf, xSrc, ySrc, 1.0); // Model for normalisation
+                    float flux = model->modelFlux(model->params);               // Flux for peak=1
+                    psFree(model);
+                    source->psfMag = -2.5 * log10(flux);
+                    xOffset = psVectorAlloc(1, PS_TYPE_S32);
+                    yOffset = psVectorAlloc(1, PS_TYPE_S32);
+                    xOffset->data.S32[0] = data->size - xSrc;
+                    yOffset->data.S32[0] = data->size - ySrc;
+                    numCols = 2 * data->size + 1;
+                    numRows = 2 * data->size + 1;
+                    psLogMsg("ppVizPSF", PS_LOG_INFO, "Generating %dx%d image with single PSF",
+                             numCols, numRows);
+                }
+
+                if (numCols <= 0 || numRows <= 0) {
+                    psError(PS_ERR_UNKNOWN, true, "Image size isn't set: %dx%d.", numCols, numRows);
+                    return false;
+                }
+
+                if (!pmReadoutFakeFromSources(readout, numCols, numRows, sources, 0, xOffset, yOffset, psf,
+                                              data->minFlux, 0, false, true)) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to generate fake readout.");
+                    return false;
+                }
+
+                psFree(sources);
+                psFree(xOffset);
+                psFree(yOffset);
+
+                pmHDU *hdu = pmHDUGetLowest(psfFile->fpa, chip, cell); // HDU for readout
+                if (!hdu) {
+                    psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find HDU for data.");
+                    return false;
+                }
+                if (!hdu->header) {
+                    hdu->header = psMetadataAlloc();
+                }
+                ppVizPSFVersionHeader(hdu->header);
+
+
+                // Readout
+                if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+                    psError(PS_ERR_UNKNOWN, false, "Error saving data to files.");
+                    return false;
+                }
+            }
+            // Cell
+            if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+                psError(PS_ERR_UNKNOWN, false, "Error saving data to files.");
+                return false;
+            }
+        }
+        // Chip
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+            psError(PS_ERR_UNKNOWN, false, "Error saving data to files.");
+            return false;
+        }
+    }
+    // FPA
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+        psError(PS_ERR_UNKNOWN, false, "Error saving data to files.");
+        return false;
+    }
+
+    return true;
+}
Index: /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSFVersion.c
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSFVersion.c	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSFVersion.c	(revision 26837)
@@ -0,0 +1,116 @@
+/** @file ppVizPSFVersion.c
+ *
+ *  @brief
+ *
+ *  @ingroup ppVizPSF
+ *
+ *  @author IfA
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2009-02-06 19:45:30 $
+ *  Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppVizPSF.h"
+#include "ppVizPSFVersionDefinitions.h"
+
+#ifndef PPVIZPSF_VERSION
+#error "PPVIZPSF_VERSION is not set"
+#endif
+#ifndef PPVIZPSF_BRANCH
+#error "PPVIZPSF_BRANCH is not set"
+#endif
+#ifndef PPVIZPSF_SOURCE
+#error "PPVIZPSF_SOURCE is not set"
+#endif
+
+psString ppVizPSFVersion(void)
+{
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", PPVIZPSF_BRANCH, PPVIZPSF_VERSION);
+    return value;
+}
+
+psString ppVizPSFSource(void)
+{
+    return psStringCopy(PPVIZPSF_SOURCE);
+}
+
+psString ppVizPSFVersionLong(void)
+{
+    psString version = ppVizPSFVersion();  // Version, to return
+    psString source = ppVizPSFSource();    // Source
+
+    psStringPrepend(&version, "ppVizPSF ");
+    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 ppVizPSFVersionHeader(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, "ppVizPSF at %s", timeString);
+    psFree(timeString);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, history);
+    psFree(history);
+
+    psLibVersionHeader(header);
+    psModulesVersionHeader(header);
+
+    psString version = ppVizPSFVersion(); // Software version
+    psString source  = ppVizPSFSource();  // Software source
+
+    psStringPrepend(&version, "ppVizPSF version: ");
+    psStringPrepend(&version, "ppVizPSF 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;
+}
+
+void ppVizPSFVersionPrint(void)
+{
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psLogMsg("ppVizPSF", PS_LOG_INFO, "ppVizPSF at %s", timeString);
+    psFree(timeString);
+
+    psString pslib = psLibVersionLong();// psLib version
+    psString psmodules = psModulesVersionLong(); // psModules version
+    psString ppVizPSF = ppVizPSFVersionLong(); // ppVizPSF version
+
+    psLogMsg("ppVizPSF", PS_LOG_INFO, "%s", pslib);
+    psLogMsg("ppVizPSF", PS_LOG_INFO, "%s", psmodules);
+    psLogMsg("ppVizPSF", PS_LOG_INFO, "%s", ppVizPSF);
+
+    psFree(pslib);
+    psFree(psmodules);
+    psFree(ppVizPSF);
+
+    return;
+}
Index: /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSFVersionDefinitions.h.in
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSFVersionDefinitions.h.in	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppVizPSF/ppVizPSFVersionDefinitions.h.in	(revision 26837)
@@ -0,0 +1,8 @@
+#ifndef PPVIZPSF_VERSION_DEFINITIONS_H
+#define PPVIZPSF_VERSION_DEFINITIONS_H
+
+#define PPVIZPSF_VERSION @PPVIZPSF_VERSION@ // SVN version
+#define PPVIZPSF_BRANCH  @PPVIZPSF_BRANCH@  // SVN branch
+#define PPVIZPSF_SOURCE  @PPVIZPSF_SOURCE@  // SVN source
+
+#endif
Index: /branches/eam_branches/20091201/ppViz/src/ppVizPattern/Makefile.am
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppVizPattern/Makefile.am	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppVizPattern/Makefile.am	(revision 26837)
@@ -0,0 +1,47 @@
+bin_PROGRAMS = ppVizPattern
+
+if HAVE_SVNVERSION
+PPVIZPATTERN_VERSION=`$(SVNVERSION) ..`
+else
+PPVIZPATTERN_VERSION="UNKNOWN"
+endif
+
+if HAVE_SVN
+PPVIZPATTERN_BRANCH=`$(SVN) info .. | $(SED) -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'`
+PPVIZPATTERN_SOURCE=`$(SVN) info | $(SED) -n -e 's/Repository UUID: // p'`
+else
+PPVIZPATTERN_BRANCH="UNKNOWN"
+PPVIZPATTERN_SOURCE="UNKNOWN"
+endif
+
+# Force recompilation of ppVizPatternVersion.c, since it gets the version information
+ppVizPatternVersion.c: ppVizPatternVersionDefinitions.h
+ppVizPatternVersionDefinitions.h: ppVizPatternVersionDefinitions.h.in FORCE
+	-$(RM) ppVizPatternVersionDefinitions.h
+	$(SED) -e "s|@PPVIZPATTERN_VERSION@|\"$(PPVIZPATTERN_VERSION)\"|" -e "s|@PPVIZPATTERN_BRANCH@|\"$(PPVIZPATTERN_BRANCH)\"|" -e "s|@PPVIZPATTERN_SOURCE@|\"$(PPVIZPATTERN_SOURCE)\"|" ppVizPatternVersionDefinitions.h.in > ppVizPatternVersionDefinitions.h
+FORCE: ;
+
+BUILT_SOURCES = ppVizPatternVersionDefinitions.h
+
+
+ppVizPattern_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PPVIZPATTERN_CFLAGS)
+ppVizPattern_LDFLAGS  = $(PSLIB_LIBS)   $(PSMODULE_LIBS)   $(PPVIZPATTERN_LIBS)
+
+ppVizPattern_SOURCES =		\
+	ppVizPattern.c		\
+	ppVizPatternArguments.c	\
+	ppVizPatternCamera.c	\
+	ppVizPatternData.c		\
+	ppVizPatternLoop.c		\
+	ppVizPatternVersion.c            
+
+noinst_HEADERS = \
+	ppVizPattern.h
+
+clean-local:
+	-rm -f TAGS
+
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
+
Index: /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPattern.c
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPattern.c	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPattern.c	(revision 26837)
@@ -0,0 +1,43 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppVizPattern.h"
+
+int main(int argc, char *argv[])
+{
+    ppVizPatternVersionPrint();
+
+    ppVizPatternData *data = ppVizPatternDataInit(&argc, argv);
+    if (!data) {
+        psErrorStackPrint(stderr, "Unable to initialise.");
+        return PS_EXIT_CONFIG_ERROR;
+    }
+
+    if (!ppVizPatternArguments(data, argc, argv)) {
+        psErrorStackPrint(stderr, "Unable to parse arguments.");
+        psFree(data);
+        return PS_EXIT_CONFIG_ERROR;
+    }
+
+    if (!ppVizPatternCamera(data)) {
+        psErrorStackPrint(stderr, "Unable to parse camera configuration.");
+        psFree(data);
+        return PS_EXIT_CONFIG_ERROR;
+    }
+
+    if (!ppVizPatternLoop(data)) {
+        psErrorStackPrint(stderr, "Unable to process data.");
+        psFree(data);
+        return PS_EXIT_DATA_ERROR;
+    }
+
+    psFree(data);
+
+    return PS_EXIT_SUCCESS;
+}
+
Index: /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPattern.h
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPattern.h	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPattern.h	(revision 26837)
@@ -0,0 +1,41 @@
+#ifndef PP_VIZPSF_H
+#define PP_VIZPSF_H
+
+#include <pslib.h>
+#include <psmodules.h>
+
+#define PPVIZPATTERN_RECIPE "PPVIZPATTERN"      // Recipe name
+
+// Data for processing
+typedef struct {
+    psString patternName;               // Filename with pattern
+    psString outRoot;                   // Output root name
+    pmConfig *config;                   // Configuration
+} ppVizPatternData;
+
+/// Initialise data for processing
+ppVizPatternData *ppVizPatternDataInit(int *argc, char *argv[] // Command-line arguments
+    );
+
+/// Parse command-line arguments
+bool ppVizPatternArguments(ppVizPatternData *data, // Data for processing
+                        int argc, char *argv[] // Command-line arguments
+    );
+
+/// Parse camera configurations
+bool ppVizPatternCamera(ppVizPatternData *data // Data for processing
+    );
+
+/// Loop over input data, processing
+bool ppVizPatternLoop(ppVizPatternData *data // Data for processing
+    );
+
+/// Add version information to header
+bool ppVizPatternVersionHeader(
+    psMetadata *header                  // Header to supplement
+    );
+
+/// Print version information to stdout
+void ppVizPatternVersionPrint(void);
+
+#endif
Index: /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPatternArguments.c
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPatternArguments.c	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPatternArguments.c	(revision 26837)
@@ -0,0 +1,68 @@
+/** @file ppVizPatternArguments.c
+ *
+ *  @brief
+ *
+ *  @ingroup ppVizPattern
+ *
+ *  @author Paul Price
+ *  Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppVizPattern.h"
+
+/// Print usage information and die
+static void usage(const char *program,  // Name of the program
+                  psMetadata *arguments, // Command-line arguments
+                  ppVizPatternData *data   // Run-time data
+    )
+{
+    fprintf(stderr, "\nPan-STARRS pattern visualisation\n\n");
+    fprintf(stderr,
+            "Usage: %s -pattern PATTERN.ptn OUTPUT_ROOT\n\n",
+            program);
+    fprintf(stderr, "\n");
+    psArgumentHelp(arguments);
+    psFree(arguments);
+    psFree(data);
+
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(PS_EXIT_CONFIG_ERROR);
+}
+
+
+bool ppVizPatternArguments(ppVizPatternData *data, int argc, char *argv[])
+{
+    assert(data);
+    assert(data->config);
+
+    psMetadata *arguments = psMetadataAlloc(); // Command-line arguments
+    psMetadataAddStr(arguments, PS_LIST_TAIL, "-pattern", 0, "Filename of pattern correction", NULL);
+    if (argc == 1 || !psArgumentParse(arguments, &argc, argv) || argc != 2) {
+        usage(argv[0], arguments, data);
+    }
+
+    data->patternName = psMemIncrRefCounter(psMetadataLookupStr(NULL, arguments, "-pattern"));
+    data->outRoot = psStringCopy(argv[1]);
+    psMetadataAddStr(data->config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Output root name", data->outRoot);
+
+    psTrace("ppVizPattern", 1, "Done reading command-line arguments\n");
+    psFree(arguments);
+
+    PS_ASSERT_STRING_NON_EMPTY(data->patternName, false);
+    PS_ASSERT_STRING_NON_EMPTY(data->outRoot, false);
+
+    return true;
+}
+
+
Index: /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPatternCamera.c
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPatternCamera.c	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPatternCamera.c	(revision 26837)
@@ -0,0 +1,59 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppVizPattern.h"
+
+/// Add a single filename to the arguments as an array, so that it can be used with pmFPAfileBindFromArgs, etc
+static void fileArguments(const char *file, // The symbolic name for the file
+                          const char *name, // The name of the file
+                          const char *comment, // Description of the file
+                          pmConfig *config // Configuration
+    )
+{
+    psArray *files = psArrayAlloc(1); // Array with file names
+    files->data[0] = psStringCopy(name);
+    if (psMetadataLookup(config->arguments, file)) {
+        psMetadataRemoveKey(config->arguments, file);
+    }
+    psMetadataAddArray(config->arguments, PS_LIST_TAIL, file, 0, comment, files);
+    psFree(files);
+    return;
+}
+
+
+bool ppVizPatternCamera(ppVizPatternData *data // Run-time data
+    )
+{
+    bool status;                        // Status of file definition
+
+    fileArguments("PATTERN", data->patternName, "Input pattern", data->config);
+    pmFPAfile *input = pmFPAfileDefineFromArgs(&status, data->config, "PPVIZPATTERN.INPUT",
+                                               "PATTERN"); // File
+    if (!status || !input) {
+        psError(PS_ERR_IO, false, "Failed to build file from PPVIZPATTERN.INPUT");
+        return false;
+    }
+
+    pmFPAfile *output = pmFPAfileDefineOutput(data->config, input->fpa, "PPVIZPATTERN.OUTPUT");
+    if (!output) {
+        psError(psErrorCodeLast(), false, "Unable to define output.");
+        return false;
+    }
+    output->save = true;
+
+#if 0
+    // Now the camera has been determined, we can read the recipe
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, data->config->recipes, PPVIZPATTERN_RECIPE); // Recipe
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to find recipe %s", PPVIZPATTERN_RECIPE);
+        return false;
+    }
+#endif
+
+    return true;
+}
Index: /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPatternData.c
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPatternData.c	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPatternData.c	(revision 26837)
@@ -0,0 +1,47 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppVizPattern.h"
+
+// Destructor
+static void vizPatternDataFree(ppVizPatternData *data // Data to free
+    )
+{
+    psFree(data->patternName);
+    psFree(data->outRoot);
+    psFree(data->config);
+    return;
+}
+
+
+ppVizPatternData *ppVizPatternDataAlloc(void)
+{
+    ppVizPatternData *data = psAlloc(sizeof(ppVizPatternData)); // Processing data, to return
+    psMemSetDeallocator(data, (psFreeFunc)vizPatternDataFree);
+
+    data->patternName = NULL;
+    data->outRoot = NULL;
+    data->config = NULL;
+
+    return data;
+}
+
+
+ppVizPatternData *ppVizPatternDataInit(int *argc, char **argv)
+{
+    PS_ASSERT_PTR_NON_NULL(argc, NULL);
+    PS_ASSERT_PTR_NON_NULL(argv, NULL);
+
+    ppVizPatternData *data = ppVizPatternDataAlloc(); // Processing data, to return
+    data->config = pmConfigRead(argc, argv, PPVIZPATTERN_RECIPE);
+    if (!data->config) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read configuration.");
+        return NULL;
+    }
+    return data;
+}
Index: /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPatternLoop.c
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPatternLoop.c	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPatternLoop.c	(revision 26837)
@@ -0,0 +1,118 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppVizPattern.h"
+
+#define MASK_BAD 0xFFFF                 // Mask for bad pixels
+
+bool ppVizPatternLoop(ppVizPatternData *data // Run-time data
+    )
+{
+    pmConfig *config = data->config;                                        // Configuration data
+    pmFPAfile *input = pmFPAfileSelectSingle(config->files, "PPVIZPATTERN.INPUT", 0); // File with PSF
+
+    pmFPAview *view = pmFPAviewAlloc(0); // Pointer into FPA hierarchy
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+        return NULL;
+    }
+
+    pmChip *chip;                       // Chip from FPA
+    while ((chip = pmFPAviewNextChip(view, input->fpa, 1))) {
+        if (!chip->process || !chip->file_exists) {
+            continue;
+        }
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+            psError(PS_ERR_UNKNOWN, false, "Error loading data from files.");
+            return false;
+        }
+
+        pmCell *cell;                   // Cell from chip
+        while ((cell = pmFPAviewNextCell(view, input->fpa, 1))) {
+            if (!cell->process || !cell->file_exists) {
+                continue;
+            }
+            if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+                psError(PS_ERR_UNKNOWN, false, "Error loading data from files.");
+                return false;
+            }
+
+            pmReadout *readout;         // Readout from cell
+            while ((readout = pmFPAviewNextReadout(view, input->fpa, 1))) {
+                if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+                    psError(PS_ERR_UNKNOWN, false, "Error loading data from files.");
+                    return false;
+                }
+                if (!readout->data_exists) {
+                    continue;
+                }
+
+                int numCols = 0, numRows = 0; // Size of image
+
+                // Get size of image from concepts
+                if (numCols == 0 || numRows == 0) {
+                    numCols = psMetadataLookupS32(NULL, cell->concepts, "CELL.XSIZE");
+                    numRows = psMetadataLookupS32(NULL, cell->concepts, "CELL.YSIZE");
+                }
+
+                // Get size of image from TRIMSEC
+                if (numCols == 0 || numRows == 0) {
+                    psRegion *trimsec = psMetadataLookupPtr(NULL, cell->concepts, "CELL.TRIMSEC");
+                    numCols = trimsec->x1 - trimsec->x0;
+                    numRows = trimsec->y1 - trimsec->y0;
+                }
+
+                if (numCols <= 0 || numRows <= 0) {
+                    psError(PS_ERR_UNKNOWN, true, "Image size isn't set: %dx%d.", numCols, numRows);
+                    return false;
+                }
+
+                readout->image = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+                psImageInit(readout->image, 0.0);
+
+                if (!pmPatternRowApply(readout, MASK_BAD) || !pmPatternCellApply(readout, MASK_BAD)) {
+                    psError(PS_ERR_UNKNOWN, false, "Unable to apply pattern correction.");
+                    return false;
+                }
+
+                pmHDU *hdu = pmHDUGetLowest(input->fpa, chip, cell); // HDU for readout
+                if (!hdu) {
+                    psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find HDU for data.");
+                    return false;
+                }
+                if (!hdu->header) {
+                    hdu->header = psMetadataAlloc();
+                }
+                ppVizPatternVersionHeader(hdu->header);
+
+
+                // Readout
+                if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+                    psError(PS_ERR_UNKNOWN, false, "Error saving data to files.");
+                    return false;
+                }
+            }
+            // Cell
+            if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+                psError(PS_ERR_UNKNOWN, false, "Error saving data to files.");
+                return false;
+            }
+        }
+        // Chip
+        if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+            psError(PS_ERR_UNKNOWN, false, "Error saving data to files.");
+            return false;
+        }
+    }
+    // FPA
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_AFTER)) {
+        psError(PS_ERR_UNKNOWN, false, "Error saving data to files.");
+        return false;
+    }
+
+    return true;
+}
Index: /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPatternVersion.c
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPatternVersion.c	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPatternVersion.c	(revision 26837)
@@ -0,0 +1,116 @@
+/** @file ppVizPatternVersion.c
+ *
+ *  @brief
+ *
+ *  @ingroup ppVizPattern
+ *
+ *  @author IfA
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2009-02-06 19:45:30 $
+ *  Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppVizPattern.h"
+#include "ppVizPatternVersionDefinitions.h"
+
+#ifndef PPVIZPATTERN_VERSION
+#error "PPVIZPATTERN_VERSION is not set"
+#endif
+#ifndef PPVIZPATTERN_BRANCH
+#error "PPVIZPATTERN_BRANCH is not set"
+#endif
+#ifndef PPVIZPATTERN_SOURCE
+#error "PPVIZPATTERN_SOURCE is not set"
+#endif
+
+psString ppVizPatternVersion(void)
+{
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", PPVIZPATTERN_BRANCH, PPVIZPATTERN_VERSION);
+    return value;
+}
+
+psString ppVizPatternSource(void)
+{
+    return psStringCopy(PPVIZPATTERN_SOURCE);
+}
+
+psString ppVizPatternVersionLong(void)
+{
+    psString version = ppVizPatternVersion();  // Version, to return
+    psString source = ppVizPatternSource();    // Source
+
+    psStringPrepend(&version, "ppVizPattern ");
+    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 ppVizPatternVersionHeader(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, "ppVizPattern at %s", timeString);
+    psFree(timeString);
+    psMetadataAddStr(header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, NULL, history);
+    psFree(history);
+
+    psLibVersionHeader(header);
+    psModulesVersionHeader(header);
+
+    psString version = ppVizPatternVersion(); // Software version
+    psString source  = ppVizPatternSource();  // Software source
+
+    psStringPrepend(&version, "ppVizPattern version: ");
+    psStringPrepend(&version, "ppVizPattern 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;
+}
+
+void ppVizPatternVersionPrint(void)
+{
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psLogMsg("ppVizPattern", PS_LOG_INFO, "ppVizPattern at %s", timeString);
+    psFree(timeString);
+
+    psString pslib = psLibVersionLong();// psLib version
+    psString psmodules = psModulesVersionLong(); // psModules version
+    psString ppVizPattern = ppVizPatternVersionLong(); // ppVizPattern version
+
+    psLogMsg("ppVizPattern", PS_LOG_INFO, "%s", pslib);
+    psLogMsg("ppVizPattern", PS_LOG_INFO, "%s", psmodules);
+    psLogMsg("ppVizPattern", PS_LOG_INFO, "%s", ppVizPattern);
+
+    psFree(pslib);
+    psFree(psmodules);
+    psFree(ppVizPattern);
+
+    return;
+}
Index: /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPatternVersionDefinitions.h.in
===================================================================
--- /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPatternVersionDefinitions.h.in	(revision 26837)
+++ /branches/eam_branches/20091201/ppViz/src/ppVizPattern/ppVizPatternVersionDefinitions.h.in	(revision 26837)
@@ -0,0 +1,8 @@
+#ifndef PPVIZPATTERN_VERSION_DEFINITIONS_H
+#define PPVIZPATTERN_VERSION_DEFINITIONS_H
+
+#define PPVIZPATTERN_VERSION @PPVIZPATTERN_VERSION@ // SVN version
+#define PPVIZPATTERN_BRANCH  @PPVIZPATTERN_BRANCH@  // SVN branch
+#define PPVIZPATTERN_SOURCE  @PPVIZPATTERN_SOURCE@  // SVN source
+
+#endif
