Index: trunk/ppViz/configure.ac
===================================================================
--- trunk/ppViz/configure.ac	(revision 26367)
+++ trunk/ppViz/configure.ac	(revision 26377)
@@ -34,4 +34,5 @@
   src/Makefile
   src/ppVizPSF/Makefile
+  src/ppCoord/Makefile
 ])
 
Index: trunk/ppViz/src/Makefile.am
===================================================================
--- trunk/ppViz/src/Makefile.am	(revision 26367)
+++ trunk/ppViz/src/Makefile.am	(revision 26377)
@@ -1,3 +1,3 @@
-SUBDIRS = ppVizPSF
+SUBDIRS = ppVizPSF ppCoord
 
 CLEANFILES = *.pyc *~ core core.*
Index: trunk/ppViz/src/ppCoord/Makefile.am
===================================================================
--- trunk/ppViz/src/ppCoord/Makefile.am	(revision 26377)
+++ trunk/ppViz/src/ppCoord/Makefile.am	(revision 26377)
@@ -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: trunk/ppViz/src/ppCoord/ppCoord.c
===================================================================
--- trunk/ppViz/src/ppCoord/ppCoord.c	(revision 26377)
+++ trunk/ppViz/src/ppCoord/ppCoord.c	(revision 26377)
@@ -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: trunk/ppViz/src/ppCoord/ppCoord.h
===================================================================
--- trunk/ppViz/src/ppCoord/ppCoord.h	(revision 26377)
+++ trunk/ppViz/src/ppCoord/ppCoord.h	(revision 26377)
@@ -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: trunk/ppViz/src/ppCoord/ppCoordArguments.c
===================================================================
--- trunk/ppViz/src/ppCoord/ppCoordArguments.c	(revision 26377)
+++ trunk/ppViz/src/ppCoord/ppCoordArguments.c	(revision 26377)
@@ -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: trunk/ppViz/src/ppCoord/ppCoordCamera.c
===================================================================
--- trunk/ppViz/src/ppCoord/ppCoordCamera.c	(revision 26377)
+++ trunk/ppViz/src/ppCoord/ppCoordCamera.c	(revision 26377)
@@ -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: trunk/ppViz/src/ppCoord/ppCoordData.c
===================================================================
--- trunk/ppViz/src/ppCoord/ppCoordData.c	(revision 26377)
+++ trunk/ppViz/src/ppCoord/ppCoordData.c	(revision 26377)
@@ -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: trunk/ppViz/src/ppCoord/ppCoordLoop.c
===================================================================
--- trunk/ppViz/src/ppCoord/ppCoordLoop.c	(revision 26377)
+++ trunk/ppViz/src/ppCoord/ppCoordLoop.c	(revision 26377)
@@ -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: trunk/ppViz/src/ppCoord/ppCoordVersion.c
===================================================================
--- trunk/ppViz/src/ppCoord/ppCoordVersion.c	(revision 26377)
+++ trunk/ppViz/src/ppCoord/ppCoordVersion.c	(revision 26377)
@@ -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: trunk/ppViz/src/ppCoord/ppCoordVersionDefinitions.h.in
===================================================================
--- trunk/ppViz/src/ppCoord/ppCoordVersionDefinitions.h.in	(revision 26377)
+++ trunk/ppViz/src/ppCoord/ppCoordVersionDefinitions.h.in	(revision 26377)
@@ -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
