Index: /branches/ccl_branches/ippmini-20191108/Makefile.am
===================================================================
--- /branches/ccl_branches/ippmini-20191108/Makefile.am	(revision 41073)
+++ /branches/ccl_branches/ippmini-20191108/Makefile.am	(revision 41073)
@@ -0,0 +1,3 @@
+SUBDIRS = src
+
+CLEANFILES = *~ core core.*
Index: /branches/ccl_branches/ippmini-20191108/autogen.sh
===================================================================
--- /branches/ccl_branches/ippmini-20191108/autogen.sh	(revision 41073)
+++ /branches/ccl_branches/ippmini-20191108/autogen.sh	(revision 41073)
@@ -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=ppImage
+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/ccl_branches/ippmini-20191108/configure.ac
===================================================================
--- /branches/ccl_branches/ippmini-20191108/configure.ac	(revision 41073)
+++ /branches/ccl_branches/ippmini-20191108/configure.ac	(revision 41073)
@@ -0,0 +1,29 @@
+AC_PREREQ(2.61)
+
+AC_INIT([dvoTools], [1.0.0], [ipp-support@ifa.hawaii.edu])
+AC_CONFIG_SRCDIR([src])
+
+AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2])
+AM_CONFIG_HEADER([src/config.h])
+AM_MAINTAINER_MODE
+
+IPP_STDLDFLAGS
+
+AC_LANG(C)
+AC_GNU_SOURCE
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+AC_SYS_LARGEFILE
+
+PKG_CHECK_MODULES([PSLIB], [pslib >= 1.0.0])
+PKG_CHECK_MODULES([PSMODULE], [psmodules >= 1.0.0])
+
+IPP_STDOPTS
+IPP_STDCFLAGS
+
+AC_CONFIG_FILES([
+  Makefile
+  src/Makefile
+])
+AC_OUTPUT
Index: /branches/ccl_branches/ippmini-20191108/src/Makefile.am
===================================================================
--- /branches/ccl_branches/ippmini-20191108/src/Makefile.am	(revision 41073)
+++ /branches/ccl_branches/ippmini-20191108/src/Makefile.am	(revision 41073)
@@ -0,0 +1,38 @@
+bin_PROGRAMS = dvoMakeCorr dvoApplyCorr
+
+noinst_HEADERS = \
+	dvoMakeCorr.h \
+	dvoApplyCorr.h
+
+dvoMakeCorr_CFLAGS = $(DVOCORR_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+dvoMakeCorr_LDFLAGS = $(DVOCORR_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+dvoMakeCorr_SOURCES = \
+	dvoMakeCorr.c \
+	dvoMakeCorrArguments.c \
+	dvoMakeCorrParseCamera.c \
+	dvoMakeCorrLoop.c \
+	dvoMakeCorrUnbin.c \
+	dvoMakeCorrCleanup.c \
+	dvoMakeCorrOptions.c \
+	dvoMakeCorrVersion.c
+
+dvoApplyCorr_CFLAGS = $(DVOCORR_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+dvoApplyCorr_LDFLAGS = $(DVOCORR_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+dvoApplyCorr_SOURCES = \
+	dvoApplyCorr.c \
+	dvoApplyCorrArguments.c \
+	dvoApplyCorrParseCamera.c \
+	dvoApplyCorrLoop.c \
+	dvoApplyCorrReadout.c \
+	dvoApplyCorrCleanup.c \
+	dvoApplyCorrOptions.c \
+	dvoApplyCorrVersion.c
+
+CLEANFILES = *~
+
+clean-local:
+	-rm -f TAGS
+
+# Tags for emacs
+tags:
+	etags `find . -name \*.[ch] -print`
Index: /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorr.c
===================================================================
--- /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorr.c	(revision 41073)
+++ /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorr.c	(revision 41073)
@@ -0,0 +1,43 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoApplyCorr.h"
+
+int main(int argc, char **argv) {
+
+    psLibInit(NULL);
+
+    psTimerStart(TIMERNAME);
+
+    // USAGE: dvoApplyCorr -file INPUT OUTPUT
+
+    // Parse the configuration and arguments
+    pmConfig *config = dvoApplyCorrArguments (argc, argv);
+    if (config == NULL) {
+        psErrorStackPrint(stderr, "Unable to parse command-line arguments.");
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    // define recipe options
+    // define the active I/O files
+    dvoApplyCorrOptions *options = dvoApplyCorrParseCamera(config);
+    if (options == NULL) {
+        psErrorStackPrint(stderr, "Unable to parse camera.");
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    // Image Loop
+    if (!dvoApplyCorrLoop(config, options)) {
+        psErrorStackPrint(stderr, "Unable to loop over input");
+        exit(PS_EXIT_SYS_ERROR);
+    }
+
+    psLogMsg ("ppImage", 3, "Complete ppImage run: %f sec\n", psTimerMark(TIMERNAME));
+
+    // Cleaning up
+    dvoApplyCorrCleanup(config, options);
+
+    return PS_EXIT_SUCCESS;
+}
+
Index: /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorr.h
===================================================================
--- /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorr.h	(revision 41073)
+++ /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorr.h	(revision 41073)
@@ -0,0 +1,50 @@
+#ifndef DVO_MAKE_CORR_H
+#define DVO_MAKE_CORR_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include "pslib.h"
+#include "psmodules.h"
+
+#define RECIPE_NAME "DVOCORR"		// Name of the recipe to use
+#define TIMERNAME "dvoApplyCorr"		// Name of timer
+
+// Options for dvoApplyCorr
+typedef struct {
+    int test;
+} dvoApplyCorrOptions;
+
+dvoApplyCorrOptions *dvoApplyCorrOptionsAlloc(void);
+
+dvoApplyCorrOptions *dvoApplyCorrOptionsParse(pmConfig *config);
+
+// Get the configuration
+pmConfig *dvoApplyCorrArguments(int argc, char **argv);
+
+// Determine what type of camera, and initialise
+dvoApplyCorrOptions *dvoApplyCorrParseCamera(pmConfig *config);
+
+// Loop over the input
+bool dvoApplyCorrLoop(pmConfig *config, dvoApplyCorrOptions *options);
+
+// apply the correction factor pixel-by-pixel
+// XXX : old value bool dvoApplyCorrReadout (pmConfig *config, pmFPAview *view, char *inName, char *corrName);
+bool dvoApplyCorrReadout (pmCell *inCell, pmChip *corrChip);
+
+// free memory, check for leaks
+void dvoApplyCorrCleanup (pmConfig *config, dvoApplyCorrOptions *options);
+
+/// Return short version information
+psString dvoApplyCorrVersion(void);
+
+/// Return long version information
+psString dvoApplyCorrVersionLong(void);
+
+/// Update the metadata with version information for all dependencies
+void dvoApplyCorrVersionMetadata(psMetadata *metadata ///< Metadata to update with version information
+    );
+
+#endif
Index: /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorrArguments.c
===================================================================
--- /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorrArguments.c	(revision 41073)
+++ /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorrArguments.c	(revision 41073)
@@ -0,0 +1,66 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoApplyCorr.h"
+
+static void usage (void) {
+    fprintf(stderr, "USAGE: dvoApplyCorr -file INPUT.fits OUTPUT [-corr CORR.fits]\n\n");
+    exit (2);
+}
+
+pmConfig *dvoApplyCorrArguments(int argc, char **argv)
+{
+    int argnum;
+
+    if (argc == 1) {
+        usage();
+    }
+
+    if (psArgumentGet (argc, argv, "-version")) {
+	psString version;
+	version = dvoApplyCorrVersionLong(); fprintf (stdout, "%s\n", version); psFree (version);
+	version = psModulesVersionLong();   fprintf (stdout, "%s\n", version); psFree (version);
+	version = psLibVersionLong();       fprintf (stdout, "%s\n", version); psFree (version);
+	exit (0);
+    }
+
+    // load the site-wide configuration information
+    pmConfig *config = pmConfigRead(&argc, argv, RECIPE_NAME);
+    if (config == NULL) {
+        psErrorStackPrint(stderr, "Can't find site configuration!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    // save the following additional recipe values based on command-line options
+    // these options override the DVOCORR recipe values loaded from recipe files
+    // psMetadata *options = pmConfigRecipeOptions (config, RECIPE_NAME);
+
+    // XXX add options from command-line here
+
+    // drop the local view on the options (saved on config->arguments)
+    // psFree (options);
+
+    // chip selection is used to limit chips to be processed
+    if ((argnum = psArgumentGet (argc, argv, "-chip"))) {
+        psArgumentRemove (argnum, &argc, argv);
+        psMetadataAddStr (config->arguments, PS_LIST_TAIL, "CHIP_SELECTIONS", PS_DATA_STRING, "",
+                          argv[argnum]);
+        psArgumentRemove (argnum, &argc, argv);
+    }
+
+    // the input file is a required argument; if not found, we will exit
+    bool status = pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT", "-file", "-list");
+    if (!status) {
+        usage ();
+    }
+
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "CORR", "-corr", "-corrlist");
+
+    if (argc != 2) usage ();
+
+    // Add the output image (which remain on the command-line) to the arguments list
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Name of the output image", argv[1]);
+
+    return config;
+}
Index: /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorrCleanup.c
===================================================================
--- /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorrCleanup.c	(revision 41073)
+++ /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorrCleanup.c	(revision 41073)
@@ -0,0 +1,22 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+# include "dvoApplyCorr.h"
+
+void dvoApplyCorrCleanup (pmConfig *config, dvoApplyCorrOptions *options)
+{
+    // Free memory used by dvoApplyCorrCleanup
+    psFree(options);
+    psFree(config);
+
+    // Free memory used by psModules
+    pmConceptsDone();
+    pmConfigDone();
+
+    // Free memory used by psLib
+    psLibFinalize();
+    fprintf (stderr, "Found %d leaks at %s\n", psMemCheckLeaks (0, NULL, stderr, false), "dvoApplyCorrCleanup");
+
+    return;
+}
Index: /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorrLoop.c
===================================================================
--- /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorrLoop.c	(revision 41073)
+++ /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorrLoop.c	(revision 41073)
@@ -0,0 +1,90 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoApplyCorr.h"
+
+bool dvoApplyCorrLoop (pmConfig *config, dvoApplyCorrOptions *options) {
+
+    bool status;
+    pmChip *chip;
+    pmCell *cell;
+
+    // psMetadata *recipe = psMetadataLookupMetadata(&status, config->recipes, RECIPE_NAME);
+
+    // select the input image
+    pmFPAfile *input = psMetadataLookupPtr(&status, config->files, "DVOFLAT.INPUT");
+    if (!status) {
+        psErrorStackPrint(stderr, "Can't find input flat-field image\n");
+        exit(EXIT_FAILURE);
+    }
+
+    // select the input image
+    pmFPAfile *corr = psMetadataLookupPtr(&status, config->files, "DVOFLAT.CORR");
+    if (!status) {
+        psErrorStackPrint(stderr, "Can't find correction image\n");
+        exit(EXIT_FAILURE);
+    }
+
+    pmFPAview *view = pmFPAviewAlloc(0);// View for level of interest
+
+    // load data at FPA level
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+	psError(PS_ERR_UNKNOWN, false, "failed IO for fpa in dvoApplyCorr\n");
+	psFree(view);
+        return false;
+    }
+
+    // process each chip in the FPA
+    while ((chip = pmFPAviewNextChip(view, input->fpa, 1)) != NULL) {
+        psLogMsg ("dvoApplyCorrLoop", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+        if (!chip->process || !chip->file_exists) continue;
+	if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) {
+            psError(PS_ERR_UNKNOWN, false, "failed IO for chip %d in dvoApplyCorr\n", view->chip);
+	    psFree (view);
+	    return false;
+	}
+
+	pmChip *corrChip = pmFPAviewThisChip(view, corr->fpa);
+
+	// process each cell in the Chip
+        while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) {
+            psLogMsg ("dvoApplyCorrLoop", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+            if (!cell->process || !cell->file_exists) continue;
+            if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+		psError(PS_ERR_UNKNOWN, false, "failed IO for chip %d, cell %d in dvoApplyCorr\n", view->chip, view->cell);
+		psFree (view);
+		return false;
+	    }
+
+	    // multiply the input image and the correction image
+	    if (!dvoApplyCorrReadout (cell, corrChip)) {
+		psError(PS_ERR_UNKNOWN, false, "failed to apply correction to chip %d, cell %d dvoApplyCorr\n", view->chip, view->cell);
+		psFree (view);
+		return false;
+	    }
+	    if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) {
+		psError(PS_ERR_UNKNOWN, false, "failed IO for chip %d, cell %d in dvoApplyCorr\n", view->chip, view->cell);
+		psFree (view);
+		return false;
+	    }
+	}
+	if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) {
+	    psError(PS_ERR_UNKNOWN, false, "failed IO for chip %d in dvoApplyCorr\n", view->chip);
+	    psFree (view);
+	    return false;
+	}
+    }
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) {
+	psError(PS_ERR_UNKNOWN, false, "failed IO for fpa in dvoApplyCorr\n");
+	psFree (view);
+	return false;
+    }
+
+    psFree (view);
+
+    // fail if we failed to handle an error
+    if (psErrorCodeLast() != PS_ERR_NONE) return false;
+
+    return true;
+}
Index: /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorrOptions.c
===================================================================
--- /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorrOptions.c	(revision 41073)
+++ /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorrOptions.c	(revision 41073)
@@ -0,0 +1,28 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoApplyCorr.h"
+
+static void dvoApplyCorrOptionsFree(dvoApplyCorrOptions *options)
+{
+    if (options == NULL) return;
+
+    return;
+}
+
+dvoApplyCorrOptions *dvoApplyCorrOptionsAlloc(void)
+{
+    dvoApplyCorrOptions *options = psAlloc(sizeof(dvoApplyCorrOptions));
+    psMemSetDeallocator(options, (psFreeFunc)dvoApplyCorrOptionsFree);
+
+    // Initialise options
+    options->test = 0;
+    return options;
+}
+
+dvoApplyCorrOptions *dvoApplyCorrOptionsParse(pmConfig *config)
+{
+    dvoApplyCorrOptions *options = dvoApplyCorrOptionsAlloc ();
+    return options;
+}
Index: /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorrParseCamera.c
===================================================================
--- /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorrParseCamera.c	(revision 41073)
+++ /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorrParseCamera.c	(revision 41073)
@@ -0,0 +1,103 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+# include "dvoApplyCorr.h"
+bool dvoApplyCorrSelectCorrFile (pmConfig *config, pmFPA *input);
+
+dvoApplyCorrOptions *dvoApplyCorrParseCamera (pmConfig *config) {
+
+    bool status = false;
+
+    // the input image defines the camera, and all recipes and options the follow
+    pmFPAfile *input = pmFPAfileDefineFromArgs (NULL, config, "DVOFLAT.INPUT", "INPUT");
+    if (!input) {
+        psError(PS_ERR_IO, false, "Failed to build FPA from DVOFLAT.INPUT");
+        return NULL;
+    }
+
+    // add recipe options supplied on command line
+    // psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, RECIPE_NAME);
+
+    // parse the options from the metadata format to the dvoApplyCorrOptions structure
+    dvoApplyCorrOptions *options = dvoApplyCorrOptionsParse (config);
+
+    // the following files are output targets
+    pmFPAfile *output = pmFPAfileDefineOutput(config, input->fpa, "DVOFLAT.OUTPUT");
+    if (!output) {
+        psError(PS_ERR_IO, false, _("Unable to generate output file from DVOFLAT.OUTPUT"));
+        psFree(options);
+        return NULL;
+    }
+    output->save = TRUE;
+
+    // find the flat-field correction image (from command-line, config file, or detrend db)
+    status = dvoApplyCorrSelectCorrFile (config, input->fpa);
+    if (!status) {
+	psError (PS_ERR_IO, false, "can't find a flat-field correction image source");
+	return NULL;
+    }
+    
+    // Chip selection: turn on only the chips specified (pass status to suppress missing-key log msg)
+    status = false;
+    char *chipLine = psMetadataLookupStr(&status, config->arguments, "CHIP_SELECTIONS");
+    psArray *chips = psStringSplitArray (chipLine, ",", false);
+    if (chips->n > 0) {
+        pmFPASelectChip (output->fpa, -1, true); // deselect all chips
+        for (int i = 0; i < chips->n; i++) {
+            int chipNum = atoi(chips->data[i]);
+            if (! pmFPASelectChip(output->fpa, chipNum, false)) {
+                psError(PS_ERR_IO, false, "Chip number %d doesn't exist in camera.\n", chipNum);
+                return NULL;
+            }
+        }
+    }
+    psFree (chips);
+    return (options);
+}
+
+bool dvoApplyCorrSelectCorrFile (pmConfig *config, pmFPA *input) {
+
+    bool status;
+    pmFPAfile *file = NULL;
+
+    file = pmFPAfileDefineFromArgs  (&status, config, "DVOFLAT.CORR", "CORR");
+    if (!status) {
+	psError (PS_ERR_UNKNOWN, false, "failed to load file definition");
+	return false;
+    }
+    if (file) {
+	if (file->type != PM_FPA_FILE_IMAGE) {
+	    psError(PS_ERR_IO, true, "DVOFLAT.CORR is not of type IMAGE");
+	    return false;
+	}
+	return true;
+    }
+
+    file = pmFPAfileDefineFromConf  (&status, config, "DVOFLAT.CORR");
+    if (!status) {
+	psError (PS_ERR_UNKNOWN, false, "failed to load file definition");
+	return false;
+    }
+    if (file) {
+	if (file->type != PM_FPA_FILE_IMAGE) {
+	    psError(PS_ERR_IO, true, "DVOFLAT.CORR is not of type IMAGE");
+	    return false;
+	}
+	return true;
+    }
+
+    file = pmFPAfileDefineFromDetDB (&status, config, "DVOFLAT.CORR", input, PM_DETREND_TYPE_FLATCORR);
+    if (!status) {
+	psError (PS_ERR_UNKNOWN, false, "failed to load file definition");
+	return false;
+    }
+    if (file) {
+	if (file->type != PM_FPA_FILE_IMAGE) {
+	    psError(PS_ERR_IO, true, "DVOFLAT.CORR is not of type IMAGE");
+	    return false;
+	}
+	return true;
+    }
+    return false;
+}
Index: /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorrReadout.c
===================================================================
--- /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorrReadout.c	(revision 41073)
+++ /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorrReadout.c	(revision 41073)
@@ -0,0 +1,56 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoApplyCorr.h"
+
+// this function is operating on each cell (well, readout) of the flat to be corrected.  The
+// input chip is supplied, but may be in a different format (eg, chip-mosaic vs single cell)
+
+bool dvoApplyCorrReadout (pmCell *inCell, pmChip *corrChip) {
+    
+    // XXX for now, let's just assume the input is per-cell, and the correction is per-chip
+    // Also, let's assume they are both binned 1x1
+
+    bool status;
+
+    pmCell *corrCell = corrChip->cells->data[0];
+    pmReadout *corrRO = corrCell->readouts->data[0];
+    psImage *corrImage = corrRO->image;
+
+    pmReadout *inRO = inCell->readouts->data[0];
+    psImage *inImage = inRO->image;
+
+    int x0 = psMetadataLookupS32(&status, inCell->concepts, "CELL.X0"); // Position of (0,0) on chip
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "CELL.X0 hasn't been set for cell.\n");
+        return false;
+    }
+    int y0 = psMetadataLookupS32(&status, inCell->concepts, "CELL.Y0"); // Position of (0,0) on chip
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "CELL.Y0 hasn't been set for cell.\n");
+        return false;
+    }
+
+    int xParity = psMetadataLookupS32(&status, inCell->concepts, "CELL.XPARITY"); // Parity in x
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "CELL.XPARITY hasn't been set for cell.\n");
+        return false;
+    }
+    int yParity = psMetadataLookupS32(&status, inCell->concepts, "CELL.YPARITY"); // Parity in y
+    if (!status) {
+        psError(PS_ERR_UNKNOWN, true, "CELL.YPARITY hasn't been set for cell.\n");
+        return false;
+    }
+
+    // The corr image is a multiplicative factor.  Need to convert the input flat pixel
+    // coordinates to the correction cell/pixel coords.
+    for (int j = 0; j < inImage->numRows; j++) {
+	int jC = y0 + j*yParity;
+	for (int i = 0; i < inImage->numCols; i++) {
+	    int iC = x0 + i*xParity;
+	    inImage->data.F32[j][i] *= corrImage->data.F32[jC][iC]; 
+	}
+    }
+    return true;
+}
Index: /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorrVersion.c
===================================================================
--- /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorrVersion.c	(revision 41073)
+++ /branches/ccl_branches/ippmini-20191108/src/dvoApplyCorrVersion.c	(revision 41073)
@@ -0,0 +1,52 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoApplyCorr.h"
+
+static const char *cvsTag = "$Name: not supported by cvs2svn $";// CVS tag name
+
+psString dvoApplyCorrVersion(void)
+{
+    psString version = NULL;            // Version, to return
+    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
+    return version;
+}
+
+psString dvoApplyCorrVersionLong(void)
+{
+    psString version = dvoApplyCorrVersion(); // Version, to return
+    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
+    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
+    psFree(tag);
+    return version;
+}
+
+
+void dvoApplyCorrVersionMetadata(psMetadata *metadata)
+{
+    PS_ASSERT_METADATA_NON_NULL(metadata,);
+
+    psString pslib = psLibVersionLong();// psLib version
+    psString psmodules = psModulesVersionLong(); // psModules version
+    psString dvoApplyCorr = dvoApplyCorrVersionLong(); // dvoApplyCorr version
+
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psString head = NULL;               // Head string
+    psStringAppend(&head, "dvoApplyCorr processing at %s. Component information:", timeString);
+    psFree(timeString);
+
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, head, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, pslib, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, psmodules, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, dvoApplyCorr, "");
+
+    psFree(head);
+    psFree(pslib);
+    psFree(psmodules);
+    psFree(dvoApplyCorr);
+
+    return;
+}
Index: /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorr.c
===================================================================
--- /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorr.c	(revision 41073)
+++ /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorr.c	(revision 41073)
@@ -0,0 +1,43 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoMakeCorr.h"
+
+int main(int argc, char **argv) {
+
+    psLibInit(NULL);
+
+    psTimerStart(TIMERNAME);
+
+    // USAGE: dvoMakeCorr (mosaic.fits) (output)
+
+    // Parse the configuration and arguments
+    pmConfig *config = dvoMakeCorrArguments (argc, argv);
+    if (config == NULL) {
+        psErrorStackPrint(stderr, "Unable to parse command-line arguments.\n");
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    // define recipe options
+    // define the active I/O files
+    dvoMakeCorrOptions *options = dvoMakeCorrParseCamera(config);
+    if (options == NULL) {
+        psErrorStackPrint(stderr, "Unable to parse camera.\n");
+        exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    // Image Loop
+    if (!dvoMakeCorrLoop(config, options)) {
+        psErrorStackPrint(stderr, "Unable to loop over input\n");
+        exit(PS_EXIT_SYS_ERROR);
+    }
+
+    psLogMsg ("ppImage", 3, "Complete dvoMakeCorr run: %f sec\n", psTimerMark(TIMERNAME));
+
+    // Cleaning up
+    dvoMakeCorrCleanup(config, options);
+
+    return PS_EXIT_SUCCESS;
+}
+
Index: /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorr.h
===================================================================
--- /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorr.h	(revision 41073)
+++ /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorr.h	(revision 41073)
@@ -0,0 +1,49 @@
+#ifndef DVO_MAKE_CORR_H
+#define DVO_MAKE_CORR_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include "pslib.h"
+#include "psmodules.h"
+
+#define RECIPE_NAME "DVOCORR"		// Name of the recipe to use
+#define TIMERNAME "dvoMakeCorr"		// Name of timer
+
+// Options for dvoMakeCorr
+typedef struct {
+    int test;
+} dvoMakeCorrOptions;
+
+dvoMakeCorrOptions *dvoMakeCorrOptionsAlloc(void);
+
+dvoMakeCorrOptions *dvoMakeCorrOptionsParse(pmConfig *config);
+
+// Get the configuration
+pmConfig *dvoMakeCorrArguments(int argc, char **argv);
+
+// Determine what type of camera, and initialise
+dvoMakeCorrOptions *dvoMakeCorrParseCamera(pmConfig *config);
+
+// Loop over the input
+bool dvoMakeCorrLoop(pmConfig *config, dvoMakeCorrOptions *options);
+
+// convert low-res image to hi-res image
+bool dvoMakeCorrUnbin (pmConfig *config, pmFPAview *view, char *outName, psImage *inImage, pmChip *inChip, char *refName);
+
+// free memory, check for leaks
+void dvoMakeCorrCleanup (pmConfig *config, dvoMakeCorrOptions *options);
+
+/// Return short version information
+psString dvoMakeCorrVersion(void);
+
+/// Return long version information
+psString dvoMakeCorrVersionLong(void);
+
+/// Update the metadata with version information for all dependencies
+void dvoMakeCorrVersionMetadata(psMetadata *metadata ///< Metadata to update with version information
+    );
+
+#endif
Index: /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorrArguments.c
===================================================================
--- /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorrArguments.c	(revision 41073)
+++ /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorrArguments.c	(revision 41073)
@@ -0,0 +1,65 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoMakeCorr.h"
+
+static void usage (void) {
+    fprintf(stderr, "USAGE: dvoMakeCorr -file MOSAIC.fits -ref REF.fits OUTPUT\n\n");
+    exit (2);
+}
+
+pmConfig *dvoMakeCorrArguments(int argc, char **argv)
+{
+    int argnum;
+
+    if (argc == 1) {
+        usage();
+    }
+
+    if (psArgumentGet (argc, argv, "-version")) {
+	psString version;
+	version = dvoMakeCorrVersionLong(); fprintf (stdout, "%s\n", version); psFree (version);
+	version = psModulesVersionLong();   fprintf (stdout, "%s\n", version); psFree (version);
+	version = psLibVersionLong();       fprintf (stdout, "%s\n", version); psFree (version);
+	exit (0);
+    }
+
+    // load the site-wide configuration information
+    pmConfig *config = pmConfigRead(&argc, argv, RECIPE_NAME);
+    if (config == NULL) {
+        psErrorStackPrint(stderr, "Can't find site configuration!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    // save the following additional recipe values based on command-line options
+    // these options override the DVOCORR recipe values loaded from recipe files
+    // psMetadata *options = pmConfigRecipeOptions (config, RECIPE_NAME);
+
+    // XXX add options from command-line here
+
+    // the input file is a required argument; if not found, we will exit
+    if (!pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT", "-file", "-list")) {
+        usage ();
+    }
+
+    // the input file is a required argument; if not found, we will exit
+    if (!pmConfigFileSetsMD (config->arguments, &argc, argv, "REFHEAD", "-ref", "-reflist")) {
+        usage ();
+    }
+
+    // chip selection is used to limit chips to be processed
+    if ((argnum = psArgumentGet (argc, argv, "-chip"))) {
+        psArgumentRemove (argnum, &argc, argv);
+        psMetadataAddStr (config->arguments, PS_LIST_TAIL, "CHIP_SELECTIONS", PS_DATA_STRING, "",
+                          argv[argnum]);
+        psArgumentRemove (argnum, &argc, argv);
+    }
+
+    if (argc != 2) usage ();
+
+    // Add the output image (which remain on the command-line) to the arguments list
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Name of the output image", argv[1]);
+
+    return config;
+}
Index: /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorrCleanup.c
===================================================================
--- /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorrCleanup.c	(revision 41073)
+++ /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorrCleanup.c	(revision 41073)
@@ -0,0 +1,22 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+# include "dvoMakeCorr.h"
+
+void dvoMakeCorrCleanup (pmConfig *config, dvoMakeCorrOptions *options)
+{
+    // Free memory used by dvoMakeCorrCleanup
+    psFree(options);
+    psFree(config);
+
+    // Free memory used by psModules
+    pmConceptsDone();
+    pmConfigDone();
+
+    // Free memory used by psLib
+    psLibFinalize();
+    fprintf (stderr, "Found %d leaks at %s\n", psMemCheckLeaks (0, NULL, stderr, false), "dvoMakeCorrCleanup");
+
+    return;
+}
Index: /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorrLoop.c
===================================================================
--- /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorrLoop.c	(revision 41073)
+++ /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorrLoop.c	(revision 41073)
@@ -0,0 +1,107 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoMakeCorr.h"
+
+// XXX currently, the input image (the grid map generated by relphot) does not supply enough
+// header information so the camera and format can be identified.
+
+bool dvoMakeCorrLoop (pmConfig *config, dvoMakeCorrOptions *options) {
+
+    bool status;
+    pmChip *chip;
+    pmCell *cell;
+    psRegion fullImage = {0, 0, 0, 0};
+
+    // psMetadata *recipe = psMetadataLookupMetadata(&status, config->recipes, RECIPE_NAME);
+
+    // use the reference image to identify the desired extension from DVOCORR.INPUT
+    pmFPAfile *refhead = psMetadataLookupPtr(&status, config->files, "DVOCORR.REFHEAD");
+    if (!status) {
+        psErrorStackPrint(stderr, "Can't find reference image\n");
+        exit(EXIT_FAILURE);
+    }
+
+    pmFPAfile *input = psMetadataLookupPtr(&status, config->files, "DVOCORR.INPUT");
+    if (!status) {
+        psErrorStackPrint(stderr, "Can't find input data!\n");
+        exit(EXIT_FAILURE);
+    }
+
+    pmFPAview *view = pmFPAviewAlloc(0);// View for level of interest
+
+    // load data at FPA leve
+    if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+	psError(PS_ERR_UNKNOWN, false, "failed IO for fpa in psphot\n");
+	psFree(view);
+        return false;
+    }
+
+    // XXX for now, do not use the I/O system to read this file; do it manually
+    if (!pmFPAfileOpen (input, view, config)) {
+	psError(PS_ERR_UNKNOWN, false, "failed to open grid file\n");
+	psFree (view);
+	return false;
+    }
+    input->state = PM_FPA_STATE_INACTIVE;  
+
+    // process each chip in the FPA
+    while ((chip = pmFPAviewNextChip(view, refhead->fpa, 1)) != NULL) {
+        psLogMsg ("dvoMakeCorrLoop", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+        if (!chip->process || !chip->file_exists) continue;
+	if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) {
+            psError(PS_ERR_UNKNOWN, false, "failed IO for chip %d in dvoMakeCorr\n", view->chip);
+	    psFree (view);
+	    return false;
+	}
+
+	// load the input data for this extension
+	pmChip *inChip = pmFPAviewThisChip (view, input->fpa);
+	assert (inChip->hdu);
+	assert (inChip->hdu->extname);
+	psFitsMoveExtName (input->fits, inChip->hdu->extname);
+	psImage *inImage = psFitsReadImage (input->fits, fullImage, 0);
+	assert (inImage);
+
+	// process each cell in the Chip
+        while ((cell = pmFPAviewNextCell(view, refhead->fpa, 1)) != NULL) {
+            psLogMsg ("dvoMakeCorrLoop", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+            if (!cell->process || !cell->file_exists) continue;
+            if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
+		psError(PS_ERR_UNKNOWN, false, "failed IO for chip %d, cell %d in dvoMakeCorr\n", view->chip, view->cell);
+		psFree (view);
+		return false;
+	    }
+
+	    // XXX put the function here which unbins the input image
+	    dvoMakeCorrUnbin (config, view, "DVOCORR.OUTPUT", inImage, inChip, "DVOCORR.REFHEAD");
+
+	    if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) {
+		psError(PS_ERR_UNKNOWN, false, "failed IO for chip %d, cell %d in dvoMakeCorr\n", view->chip, view->cell);
+		psFree (view);
+		return false;
+	    }
+	}
+	psFree (inImage);
+
+	if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) {
+	    psError(PS_ERR_UNKNOWN, false, "failed IO for chip %d in dvoMakeCorr\n", view->chip);
+	    psFree (view);
+	    return false;
+	}
+    }
+    if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) {
+	psError(PS_ERR_UNKNOWN, false, "failed IO for fpa in dvoMakeCorr\n");
+	psFree (view);
+	return false;
+    }
+
+    pmFPAfileClose (input, view);
+    psFree (view);
+
+    // fail if we failed to handle an error
+    if (psErrorCodeLast() != PS_ERR_NONE) return false;
+
+    return true;
+}
Index: /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorrOptions.c
===================================================================
--- /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorrOptions.c	(revision 41073)
+++ /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorrOptions.c	(revision 41073)
@@ -0,0 +1,28 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoMakeCorr.h"
+
+static void dvoMakeCorrOptionsFree(dvoMakeCorrOptions *options)
+{
+    if (options == NULL) return;
+
+    return;
+}
+
+dvoMakeCorrOptions *dvoMakeCorrOptionsAlloc(void)
+{
+    dvoMakeCorrOptions *options = psAlloc(sizeof(dvoMakeCorrOptions));
+    psMemSetDeallocator(options, (psFreeFunc)dvoMakeCorrOptionsFree);
+
+    // Initialise options
+    options->test = 0;
+    return options;
+}
+
+dvoMakeCorrOptions *dvoMakeCorrOptionsParse(pmConfig *config)
+{
+    dvoMakeCorrOptions *options = dvoMakeCorrOptionsAlloc ();
+    return options;
+}
Index: /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorrParseCamera.c
===================================================================
--- /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorrParseCamera.c	(revision 41073)
+++ /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorrParseCamera.c	(revision 41073)
@@ -0,0 +1,55 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+# include "dvoMakeCorr.h"
+
+dvoMakeCorrOptions *dvoMakeCorrParseCamera (pmConfig *config) {
+
+    // the REFERENCE image defines the camera, and all recipes and options the follow
+    pmFPAfile *refhead = pmFPAfileDefineFromArgs (NULL, config, "DVOCORR.REFHEAD", "REFHEAD");
+    if (!refhead) {
+        psError(PS_ERR_IO, false, "Failed to build FPA from DVOCORR.REFHEAD");
+        return NULL;
+    }
+
+    // the input image defines the camera, and all recipes and options the follow
+    pmFPAfile *input = pmFPAfileDefineFromArgs (NULL, config, "DVOCORR.INPUT", "INPUT");
+    if (!input) {
+        psError(PS_ERR_IO, false, "Failed to build FPA from DVOCORR.INPUT");
+        return NULL;
+    }
+
+    // add recipe options supplied on command line
+    // psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, RECIPE_NAME);
+
+    // parse the options from the metadata format to the dvoMakeCorrOptions structure
+    dvoMakeCorrOptions *options = dvoMakeCorrOptionsParse (config);
+
+    // the following files are output targets
+    // XXX get the binning from where?
+    pmFPAfile *output = pmFPAfileDefineFromFPA(config, refhead->fpa, 1, 1, "DVOCORR.OUTPUT");
+    if (!output) {
+        psError(PS_ERR_IO, false, _("Unable to generate output file from DVOCORR.OUTPUT"));
+        psFree(options);
+        return NULL;
+    }
+    output->save = TRUE;
+
+    // Chip selection: turn on only the chips specified (pass status to suppress missing-key log msg)
+    bool status = false;
+    char *chipLine = psMetadataLookupStr(&status, config->arguments, "CHIP_SELECTIONS");
+    psArray *chips = psStringSplitArray (chipLine, ",", false);
+    if (chips->n > 0) {
+        pmFPASelectChip (input->fpa, -1, true); // deselect all chips
+        for (int i = 0; i < chips->n; i++) {
+            int chipNum = atoi(chips->data[i]);
+            if (! pmFPASelectChip(input->fpa, chipNum, false)) {
+                psError(PS_ERR_IO, false, "Chip number %d doesn't exist in camera.\n", chipNum);
+                return false;
+            }
+        }
+    }
+    psFree (chips);
+    return (options);
+}
Index: /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorrUnbin.c
===================================================================
--- /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorrUnbin.c	(revision 41073)
+++ /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorrUnbin.c	(revision 41073)
@@ -0,0 +1,76 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoMakeCorr.h"
+
+bool dvoMakeCorrUnbin (pmConfig *config, pmFPAview *view, char *outName, psImage *inImage, pmChip *inChip, char *refName) {
+    
+    bool status;
+
+    pmFPAfile *refFile = psMetadataLookupPtr (&status, config->files, refName);
+    pmFPAfile *outFile = psMetadataLookupPtr (&status, config->files, outName);
+    assert (refFile);
+    assert (outFile);
+
+    pmCell *refCell = pmFPAviewThisCell (view, refFile->fpa);
+    assert (refCell);
+
+    pmChip *refChip = pmFPAviewThisChip (view, refFile->fpa);
+    assert (refChip);
+
+    // determine the output array size based on the reference TRIMSEC values
+    psRegion *trimsec = psMetadataLookupPtr(NULL, refCell->concepts, "CELL.TRIMSEC");
+    assert (trimsec);
+
+    // I have the fine and ruff image sizes, determine the binning factor
+    psImageBinning *binning = psImageBinningAlloc();
+    binning->nXruff = inImage->numCols;
+    binning->nYruff = inImage->numRows;
+    binning->nXfine = trimsec->x1 - trimsec->x0;
+    binning->nYfine = trimsec->y1 - trimsec->y0;
+    psImageBinningSetScale (binning, PS_IMAGE_BINNING_CENTER);
+    psImageBinningSetSkip(binning, inImage);
+
+    // generate a local view of the 0th readout
+    pmFPAview *myView = pmFPAviewAlloc (0);
+    *myView = *view;
+    myView->readout = 0;
+
+    // construct the supporing pmFPA/pmChip/pmCell structures
+    pmReadout *outData = pmFPAviewThisReadout (myView, outFile->fpa);
+    if (outData == NULL) {
+	pmChip *outChip = pmFPAviewThisChip (myView, outFile->fpa);
+	pmCell *outCell = pmFPAviewThisCell (myView, outFile->fpa);
+	assert (outCell);
+
+	pmChipCopyStructure (outChip, refChip, 1, 1);
+
+	outData = pmReadoutAlloc (outCell);
+	assert (outData != NULL);
+    }
+
+    // generate the output (fine-scale) image array
+    outData->image = psImageRecycle (outData->image, binning->nXfine, binning->nYfine, PS_TYPE_F32);
+
+    // linear interpolation to full fine scale
+    if (!psImageUnbin (outData->image, inImage, binning)) {
+	psError (PS_ERR_UNKNOWN, true, "failed to unbin image");
+	psFree (myView);
+	psFree (binning);
+	return false;
+    }
+
+    // the input image is in magnitudes.  convert here to a multiplicative factor...
+    for (int j = 0; j < outData->image->numRows; j++) {
+	for (int i = 0; i < outData->image->numCols; i++) {
+	    float value = outData->image->data.F32[j][i]; 
+	    outData->image->data.F32[j][i] = pow(10.0, -0.4*value);
+	}
+    }
+
+    psFree (myView);
+    psFree (binning);
+ 
+    return true;
+}
Index: /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorrVersion.c
===================================================================
--- /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorrVersion.c	(revision 41073)
+++ /branches/ccl_branches/ippmini-20191108/src/dvoMakeCorrVersion.c	(revision 41073)
@@ -0,0 +1,52 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "dvoMakeCorr.h"
+
+static const char *cvsTag = "$Name: not supported by cvs2svn $";// CVS tag name
+
+psString dvoMakeCorrVersion(void)
+{
+    psString version = NULL;            // Version, to return
+    psStringAppend(&version, "%s-%s",PACKAGE_NAME,PACKAGE_VERSION);
+    return version;
+}
+
+psString dvoMakeCorrVersionLong(void)
+{
+    psString version = dvoMakeCorrVersion(); // Version, to return
+    psString tag = psStringStripCVS(cvsTag, "Name"); // CVS tag
+    psStringAppend(&version, " (cvs tag %s) %s, %s", tag, __DATE__, __TIME__);
+    psFree(tag);
+    return version;
+}
+
+
+void dvoMakeCorrVersionMetadata(psMetadata *metadata)
+{
+    PS_ASSERT_METADATA_NON_NULL(metadata,);
+
+    psString pslib = psLibVersionLong();// psLib version
+    psString psmodules = psModulesVersionLong(); // psModules version
+    psString dvoMakeCorr = dvoMakeCorrVersionLong(); // dvoMakeCorr version
+
+    psTime *time = psTimeGetNow(PS_TIME_TAI); // The time now
+    psString timeString = psTimeToISO(time); // The time in an ISO string
+    psFree(time);
+    psString head = NULL;               // Head string
+    psStringAppend(&head, "dvoMakeCorr processing at %s. Component information:", timeString);
+    psFree(timeString);
+
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, head, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, pslib, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, psmodules, "");
+    psMetadataAddStr(metadata, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, dvoMakeCorr, "");
+
+    psFree(head);
+    psFree(pslib);
+    psFree(psmodules);
+    psFree(dvoMakeCorr);
+
+    return;
+}
