Index: /trunk/ppStac/Makefile.am
===================================================================
--- /trunk/ppStac/Makefile.am	(revision 11116)
+++ /trunk/ppStac/Makefile.am	(revision 11116)
@@ -0,0 +1,9 @@
+SUBDIRS = src
+
+CLEANFILES = *~ core core.*
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA= ppStac.pc
+
+EXTRA_DIST = \
+	ppStac.pc.in
Index: /trunk/ppStac/autogen.sh
===================================================================
--- /trunk/ppStac/autogen.sh	(revision 11116)
+++ /trunk/ppStac/autogen.sh	(revision 11116)
@@ -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=ppStac
+TEST_TYPE=-f
+# change this to be a unique filename in the top level dir
+FILE=autogen.sh
+
+DIE=0
+
+LIBTOOLIZE=libtoolize
+ACLOCAL=aclocal
+AUTOHEADER=autoheader
+AUTOMAKE=automake
+AUTOCONF=autoconf
+
+($LIBTOOLIZE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $LIBTOOlIZE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/libtool/"
+        DIE=1
+}
+
+($ACLOCAL --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $ACLOCAL installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOHEADER --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOHEADER installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOMAKE installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/automake/"
+        DIE=1
+}
+
+($AUTOCONF --version) < /dev/null > /dev/null 2>&1 || {
+        echo
+        echo "You must have $AUTOCONF installed to compile $PROJECT."
+        echo "Download the appropriate package for your distribution,"
+        echo "or get the source tarball at http://ftp.gnu.org/gnu/autoconf/"
+        DIE=1
+}
+
+if test "$DIE" -eq 1; then
+        exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+        echo "You must run this script in the top-level $PROJECT directory"
+        exit 1
+}
+
+if test -z "$*"; then
+        echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+$LIBTOOLIZE --copy --force || echo "$LIBTOOlIZE failed"
+$ACLOCAL || echo "$ACLOCAL failed"
+$AUTOHEADER || echo "$AUTOHEADER failed"
+$AUTOMAKE --add-missing --force-missing --copy || echo "$AUTOMAKE failed"
+$AUTOCONF || echo "$AUTOCONF failed"
+
+cd $ORIGDIR
+
+run_configure=true
+for arg in $*; do
+    case $arg in
+        --no-configure)
+            run_configure=false
+            ;;
+        *)
+            ;;
+    esac
+done
+
+if $run_configure; then
+    $srcdir/configure --enable-maintainer-mode "$@"
+    echo
+    echo "Now type 'make' to compile $PROJECT."
+else
+    echo
+    echo "Now run 'configure' and 'make' to compile $PROJECT."
+fi
Index: /trunk/ppStac/configure.ac
===================================================================
--- /trunk/ppStac/configure.ac	(revision 11116)
+++ /trunk/ppStac/configure.ac	(revision 11116)
@@ -0,0 +1,54 @@
+AC_PREREQ(2.59)
+
+AC_INIT([ppStac], [0.0.1], [price@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([src])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+AC_LANG(C)
+AC_GNU_SOURCE
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+
+dnl enable largefile supports
+AC_SYS_LARGEFILE 
+
+dnl handle debug building
+AC_ARG_ENABLE(optimize,
+  [AS_HELP_STRING(--enable-optimize,enable compiler optimization)],
+  [AC_MSG_RESULT(compile optimization enabled)
+   CFLAGS="${CFLAGS=} -O2"],
+  [AC_MSG_RESULT([compile optimization disabled])
+   CFLAGS="${CFLAGS=} -O0 -g"]
+)
+
+dnl ppStac_CFLAGS="-Wall -Werror -std=c99"
+CFLAGS="${CFLAGS=} -Wall -Werror -std=c99"
+                                                                                
+dnl handle path coverage checking
+AC_ARG_ENABLE(coverage,
+  [AS_HELP_STRING(--enable-coverage,enable path coverage checking)],
+  [AC_MSG_RESULT(path coverage enabled)
+   CFLAGS="${CFLAGS=} -fprofile-arcs -ftest-coverage -pg"]
+)
+ 
+PKG_CHECK_MODULES([PSLIB], [pslib >= 0.12.99])
+PKG_CHECK_MODULES([PSMODULE], [psmodules >= 0.12.99])
+
+dnl handle profiler building
+AC_ARG_ENABLE(profile,
+  [AS_HELP_STRING(--enable-profile,enable compiler profiler information inclusion)],
+  [AC_MSG_RESULT(compile optimization enabled)
+  CFLAGS="${CFLAGS=} -g -pg"
+  LDFLAGS="${LDFLAGS=} -pg -Wl,--start-group -Wl,-Bstatic"]
+  )
+   
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+  ppStac.pc
+])
+AC_OUTPUT
Index: /trunk/ppStac/ppStac.pc.in
===================================================================
--- /trunk/ppStac/ppStac.pc.in	(revision 11116)
+++ /trunk/ppStac/ppStac.pc.in	(revision 11116)
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libppStac
+Description: Pan-STARRS Pipeline Simultaneous Telescope Array Combination
+Version: @VERSION@
+Requires: pslib psmodules
+Libs: -L${libdir}
+Cflags: -I${includedir}
Index: /trunk/ppStac/src/Makefile.am
===================================================================
--- /trunk/ppStac/src/Makefile.am	(revision 11116)
+++ /trunk/ppStac/src/Makefile.am	(revision 11116)
@@ -0,0 +1,20 @@
+bin_PROGRAMS = ppStac
+
+ppStac_CFLAGS 	= $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+ppStac_LDFLAGS = $(PSMODULE_LIBS) $(PSLIB_LIBS)
+
+ppStac_SOURCES =		\
+	ppStac.c		\
+	ppStacOptions.c		\
+	ppStacCombine.c
+
+noinst_HEADERS = 		\
+	ppStac.h
+
+CLEANFILES = *~
+
+clean-local:
+	-rm -f TAGS
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
Index: /trunk/ppStac/src/ppStac.c
===================================================================
--- /trunk/ppStac/src/ppStac.c	(revision 11116)
+++ /trunk/ppStac/src/ppStac.c	(revision 11116)
@@ -0,0 +1,43 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#define TIMERNAME "PPSTAC"
+
+#include "ppStac.h"
+
+int main(int argc, char *argv[])
+{
+    psExit status = PS_EXIT_SUCCESS;
+
+    psLibInit(NULL);
+    psTimerStart(TIMERNAME);
+
+    // Parse the command-line arguments, open the files
+    ppStacOptions *options = ppStacOptionsRead(&argc, argv);
+    if (!options) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to set program options.\n");
+        status = PS_EXIT_CONFIG_ERROR;
+        goto die;
+    }
+
+    if (!ppStacCombine(options)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to combine images.\n");
+        status = PS_EXIT_DATA_ERROR;
+        goto die;
+    }
+
+
+    // Common code for the death.
+die:
+    psFree(options);
+    pmConceptsDone();
+    pmConfigDone();
+    psLibFinalize();
+
+    return status;
+}
Index: /trunk/ppStac/src/ppStac.h
===================================================================
--- /trunk/ppStac/src/ppStac.h	(revision 11116)
+++ /trunk/ppStac/src/ppStac.h	(revision 11116)
@@ -0,0 +1,29 @@
+#ifndef PP_STAC_H
+#define PP_STAC_H
+
+#define PPSTAC_RECIPE "PPSTAC"
+
+#include <psmodules.h>
+
+// Configuration information
+typedef struct {
+    pmConfig *config;                   // psModules configuration
+    psArray *inFiles;                   // Input files
+    psFits *outFile;                    // Output file
+    psStatsOptions combine;             // Combination statistic
+    int iter;                           // Number of rejection iterations
+    float rej;                          // Rejection threshold (standard deviations)
+    psMaskType maskVal;                 // Mask value
+} ppStacOptions;
+
+
+// Allocator for ppStacOptions
+ppStacOptions *ppStacOptionsAlloc(void);
+
+// Read the options
+ppStacOptions *ppStacOptionsRead(int *argc, char *argv[]);
+
+// Do the combination
+bool ppStacCombine(ppStacOptions *options);
+
+#endif
Index: /trunk/ppStac/src/ppStacCombine.c
===================================================================
--- /trunk/ppStac/src/ppStacCombine.c	(revision 11116)
+++ /trunk/ppStac/src/ppStacCombine.c	(revision 11116)
@@ -0,0 +1,41 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppStac.h"
+
+bool ppStacCombine(ppStacOptions *options)
+{
+    PS_ASSERT_PTR_NON_NULL(options, false);
+
+    psArray *inputs = psArrayAlloc(options->inFiles->n); // Input images
+    psRegion region = psRegionSet(0, 0, 0, 0); // Region to read
+    for (int i = 0; i < inputs->n; i++) {
+        psImage *image = psFitsReadImage(options->inFiles->data[i], region, 0);
+        if (!image) {
+            psError(PS_ERR_IO, false, "Unable to read image %d\n", i);
+            return false;
+        }
+        inputs->data[i] = image;
+    }
+
+    psImage *output = pmCombineImages(NULL, NULL, inputs, NULL, NULL, options->maskVal, NULL,
+                                      options->iter, options->rej);
+
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to combine images.\n");
+        return false;
+    }
+
+    if (!psFitsWriteImage(options->outFile, NULL, output, 0, NULL)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to write output image.\n");
+        return false;
+    }
+    psFree(output);
+
+    return true;
+}
Index: /trunk/ppStac/src/ppStacOptions.c
===================================================================
--- /trunk/ppStac/src/ppStacOptions.c	(revision 11116)
+++ /trunk/ppStac/src/ppStacOptions.c	(revision 11116)
@@ -0,0 +1,123 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+#include "ppStac.h"
+
+static void stacOptionsFree(ppStacOptions *options)
+{
+    psFree(options->config);
+    psFree(options->inFiles);
+    if (options->outFile) {
+        psFitsClose(options->outFile);
+    }
+}
+
+
+ppStacOptions *ppStacOptionsAlloc(void)
+{
+    ppStacOptions *options = psAlloc(sizeof(ppStacOptions));
+    psMemSetDeallocator(options, (psFreeFunc)stacOptionsFree);
+
+    options->config = NULL;
+    options->inFiles = NULL;
+    options->outFile = NULL;
+    options->combine = 0;
+    options->iter = 1;
+    options->rej = 3.0;
+    options->maskVal = 0;
+
+    return options;
+}
+
+
+
+// Parse a recipe option according to its type
+#define OPTION_PARSE(OPTION,MD,NAME,TYPE) \
+{ \
+    psMetadataItem *item = psMetadataLookup(MD, NAME); \
+    if (item) { \
+        OPTION = psMetadataItemParse##TYPE(item); \
+    } else { \
+        psWarning("Recipe option %s isn't specified; using default.\n", NAME); \
+    } \
+}
+
+ppStacOptions *ppStacOptionsRead(int *argc, char *argv[])
+{
+    PS_ASSERT_INT_POSITIVE(*argc, NULL);
+    PS_ASSERT_PTR_NON_NULL(argv, NULL);
+
+    ppStacOptions *options = ppStacOptionsAlloc(); // Options, to be returned
+
+    // Parse the configuration and arguments
+    pmConfigReadParamsSet(false);
+    options->config = pmConfigRead(argc, argv, PPSTAC_RECIPE);
+    if (!options->config) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to read configuration.\n");
+        psFree(options);
+        return NULL;
+    }
+
+    if (*argc <= 1 || psArgumentGet(*argc, argv, "-help") || psArgumentGet(*argc, argv, "-h")) {
+        fprintf(stderr,
+                "ppStac: Combine multiple registered images.\n\n"
+                "Usage: %s OUT.fits IN1.fits IN2.fits ... [-recipe NAME FILE]\n\n",
+                argv[0]);
+        psFree(options);
+        exit(PS_EXIT_SUCCESS);
+    }
+
+    bool mdok;                          // Status of MD lookup
+    psMetadata *recipe = psMetadataLookupMetadata(&mdok, options->config->recipes,
+                                                  PPSTAC_RECIPE); // Recipe to use
+    if (!recipe) {
+        psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find recipe %s.\n", PPSTAC_RECIPE);
+        psFree(options);
+        return NULL;
+    }
+
+    const char *outName = argv[1];      // Output filename
+    options->outFile = psFitsOpen(outName, "w");
+    if (!options->outFile) {
+        psError(PS_ERR_IO, false, "Unable to open output file %s.\n", outName);
+        psFree(options);
+        return NULL;
+    }
+
+    int numInputs = *options->config->argc - 2; // Number of input files
+    if (numInputs <= 1) {
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "Not enough input images supplied to combine.\n");
+        return NULL;
+    }
+
+    options->inFiles = psArrayAlloc(numInputs);
+    for (int i = 0; i < numInputs; i++) {
+        const char *inName = argv[i + 2];
+        psFits *inFile = psFitsOpen(inName, "r");
+        if (!inFile) {
+            psError(PS_ERR_IO, false, "Unable to open input file %s.\n", inName);
+            psFree(options);
+            return NULL;
+        }
+        options->inFiles->data[i] = inFile;
+    }
+
+    OPTION_PARSE(options->rej,     recipe, "REJ",     F32);
+    OPTION_PARSE(options->iter,    recipe, "ITER",    S32);
+    OPTION_PARSE(options->maskVal, recipe, "MASKVAL", U8);
+
+    const char *stat = psMetadataLookupStr(&mdok, recipe, "COMBINE");  // The combination statistic
+    if (!mdok || !stat || strlen(stat) == 0) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Can't recognise statistic COMBINE.\n");
+        psFree(options);
+        return NULL;
+    }
+    options->combine = psStatsOptionFromString(stat);
+
+    return options;
+}
