Index: /trunk/ippToPsps/config/test/map.xml
===================================================================
--- /trunk/ippToPsps/config/test/map.xml	(revision 27809)
+++ /trunk/ippToPsps/config/test/map.xml	(revision 27809)
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<tabledata>
+
+ <table name="FrameMeta">
+  <map ippName="ZPT_ERR" ippType="TFLOAT" pspsName="photoScat" />
+  <map ippName="MJD-OBS" ippType="TFLOAT" pspsName="expStart" />
+  <map ippName="EXPREQ" ippType="TFLOAT" pspsName="expTime" />
+  <map ippName="AIRMASS" ippType="TFLOAT" pspsName="airmass" />
+  <map ippName="RA" ippType="TFLOAT" pspsName="raBore" />
+  <map ippName="DEC" ippType="TFLOAT" pspsName="decBore" />
+  <map ippName="CTYPE1" ippType="TSTRING" pspsName="ctype1" />
+  <map ippName="CTYPE2" ippType="TSTRING" pspsName="ctype2" />
+  <map ippName="CRVAL1" ippType="TFLOAT" pspsName="crval1" />
+  <map ippName="CRVAL2" ippType="TFLOAT" pspsName="crval2" />
+  <map ippName="CRPIX1" ippType="TFLOAT" pspsName="crpix1" />
+  <map ippName="CRPIX2" ippType="TFLOAT" pspsName="crpix2" />
+  <map ippName="PC001001" ippType="TFLOAT" pspsName="pc001001" />
+  <map ippName="PC001002" ippType="TFLOAT" pspsName="pc001002" />
+  <map ippName="PC002001" ippType="TFLOAT" pspsName="pc002001" />
+  <map ippName="PC002002" ippType="TFLOAT" pspsName="pc002002" />
+  <map ippName="NPLYTERM" ippType="TBYTE" pspsName="polyOrder" />
+  <map ippName="PCA1X3Y0" ippType="TFLOAT" pspsName="pca1x3y0" />
+  <map ippName="PCA1X2Y1" ippType="TFLOAT" pspsName="pca1x2y1" />
+  <map ippName="PCA1X1Y2" ippType="TFLOAT" pspsName="pca1x1y2" />
+  <map ippName="PCA1X0Y3" ippType="TFLOAT" pspsName="pca1x0y3" />
+  <map ippName="PCA1X2Y0" ippType="TFLOAT" pspsName="pca1x2y0" />
+  <map ippName="PCA1X1Y1" ippType="TFLOAT" pspsName="pca1x1y1" />
+  <map ippName="PCA1X0Y2" ippType="TFLOAT" pspsName="pca1x0y2" />
+  <map ippName="PCA2X3Y0" ippType="TFLOAT" pspsName="pca2x3y0" />
+  <map ippName="PCA2X2Y1" ippType="TFLOAT" pspsName="pca2x2y1" />
+  <map ippName="PCA2X1Y2" ippType="TFLOAT" pspsName="pca2x1y2" />
+  <map ippName="PCA2X0Y3" ippType="TFLOAT" pspsName="pca2x0y3" />
+  <map ippName="PCA2X2Y0" ippType="TFLOAT" pspsName="pca2x2y0" />
+  <map ippName="PCA2X1Y1" ippType="TFLOAT" pspsName="pca2x1y1" />
+  <map ippName="PCA2X0Y2" ippType="TFLOAT" pspsName="pca2x0y2" />
+ </table>
+
+ <table name="ImageMeta">
+  <map ippName="MSKY_MN" ippType="TFLOAT" pspsName="sky" />
+  <map ippName="MSKY_SIG" ippType="TFLOAT" pspsName="skyScat" />
+  <map ippName="FSATUR" ippType="TFLOAT" pspsName="magSat" />
+  <map ippName="FLIMIT" ippType="TFLOAT" pspsName="completMag" />
+  <map ippName="CERROR" ippType="TFLOAT" pspsName="astroScat" />
+  <map ippName="NASTRO" ippType="TLONG" pspsName="numAstroRef" />
+  <map ippName="CNAXIS1" ippType="TSHORT" pspsName="nx" />
+  <map ippName="CNAXIS2" ippType="TSHORT" pspsName="ny" />
+  <map ippName="FWHM_MAJ" ippType="TFLOAT" pspsName="psfWidMajor" />
+  <map ippName="FWHM_MIN" ippType="TFLOAT" pspsName="psfWidMinor" />
+  <map ippName="ANGLE" ippType="TFLOAT" pspsName="psfTheta" />
+  <map ippName="APMIFIT" ippType="TFLOAT" pspsName="apResid" />
+  <map ippName="DAPMIFIT" ippType="TFLOAT" pspsName="dapResid" />
+  <map ippName="CTYPE1" ippType="TSTRING" pspsName="ctype1" />
+  <map ippName="CTYPE2" ippType="TSTRING" pspsName="ctype2" />
+  <map ippName="CRVAL1" ippType="TFLOAT" pspsName="crval1" />
+  <map ippName="CRVAL2" ippType="TFLOAT" pspsName="crval2" />
+  <map ippName="CRPIX1" ippType="TFLOAT" pspsName="crpix1" />
+  <map ippName="CRPIX2" ippType="TFLOAT" pspsName="crpix2" />
+  <map ippName="PC001001" ippType="TFLOAT" pspsName="pc001001" />
+  <map ippName="PC001002" ippType="TFLOAT" pspsName="pc001002" />
+  <map ippName="PC002001" ippType="TFLOAT" pspsName="pc002001" />
+  <map ippName="PC002002" ippType="TFLOAT" pspsName="pc002002" />
+  <map ippName="NPLYTERM" ippType="TBYTE" pspsName="polyOrder" />
+  <map ippName="PCA1X3Y0" ippType="TFLOAT" pspsName="pca1x3y0" />
+  <map ippName="PCA1X2Y1" ippType="TFLOAT" pspsName="pca1x2y1" />
+  <map ippName="PCA1X1Y2" ippType="TFLOAT" pspsName="pca1x1y2" />
+  <map ippName="PCA1X0Y3" ippType="TFLOAT" pspsName="pca1x0y3" />
+  <map ippName="PCA1X2Y0" ippType="TFLOAT" pspsName="pca1x2y0" />
+  <map ippName="PCA1X1Y1" ippType="TFLOAT" pspsName="pca1x1y1" />
+  <map ippName="PCA1X0Y2" ippType="TFLOAT" pspsName="pca1x0y2" />
+  <map ippName="PCA2X3Y0" ippType="TFLOAT" pspsName="pca2x3y0" />
+  <map ippName="PCA2X2Y1" ippType="TFLOAT" pspsName="pca2x2y1" />
+  <map ippName="PCA2X1Y2" ippType="TFLOAT" pspsName="pca2x1y2" />
+  <map ippName="PCA2X0Y3" ippType="TFLOAT" pspsName="pca2x0y3" />
+  <map ippName="PCA2X2Y0" ippType="TFLOAT" pspsName="pca2x2y0" />
+  <map ippName="PCA2X1Y1" ippType="TFLOAT" pspsName="pca2x1y1" />
+  <map ippName="PCA2X0Y2" ippType="TFLOAT" pspsName="pca2x0y2" />
+ </table>
+
+ <table name="Detection">
+  <map ippColumn="2" ippName="X_PSF" ippType="TFLOAT" pspsName="xPos" />
+  <map ippColumn="3" ippName="Y_PSF" ippType="TFLOAT" pspsName="yPos" />
+  <map ippColumn="4" ippName="X_PSF_SIG" ippType="TFLOAT" pspsName="xPosErr" />
+  <map ippColumn="5" ippName="Y_PSF_SIG" ippType="TFLOAT" pspsName="yPosErr" />
+  <map ippColumn="15" ippName="RA_PSF" ippType="TFLOAT" pspsName="ippRa" />
+  <map ippColumn="16" ippName="DEC_PSF" ippType="TFLOAT" pspsName="ippDec" />
+  <map ippColumn="22" ippName="PSF_MAJOR" ippType="TFLOAT" pspsName="psfWidMajor" />
+  <map ippColumn="23" ippName="PSF_MINOR" ippType="TFLOAT" pspsName="psfWidMinor" />
+  <map ippColumn="24" ippName="PSF_THETA" ippType="TFLOAT" pspsName="psfTheta" />
+  <map ippColumn="25" ippName="PSF_QF" ippType="TFLOAT" pspsName="psfCf" />
+  <map ippColumn="17" ippName="SKY" ippType="TFLOAT" pspsName="sky" />
+  <map ippColumn="18" ippName="SKY_SIGMA" ippType="TFLOAT" pspsName="skyErr" />
+ </table>
+
+</tabledata>
Index: /trunk/ippToPsps/config/test/tables.xml
===================================================================
--- /trunk/ippToPsps/config/test/tables.xml	(revision 27809)
+++ /trunk/ippToPsps/config/test/tables.xml	(revision 27809)
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<tableDescriptions type="det">
+  <table name="FrameMeta">
+    <column name="frameID" type="TLONG" default="0" comment="unique exposure/frame identifier."></column>
+    <column name="filterID" type="TBYTE" default="0" comment="filter identifier"></column>
+    <column name="nOTA" type="TSHORT" default="-999" comment="number of valid OTA/CCD images in this exposure"></column>
+    <column name="photoScat" type="TFLOAT" default="-999" comment=" global photometric scatter (unit = mag)"></column>
+    <column name="expStart" type="TFLOAT" default="-999" comment=" exposure start time in MJD (unit = day)"></column>
+    <column name="expTime" type="TFLOAT" default="-999" comment=" exposure time (unit = s)"></column>
+    <column name="airmass" type="TFLOAT" default="-999" comment="airmass at mid-exposure"></column>
+    <column name="raBore" type="TFLOAT" default="-999" comment=" RA of telescope boresight (unit = deg)"></column>
+    <column name="decBore" type="TFLOAT" default="-999" comment=" DEC of telescope boresight (unit = deg)"></column>
+    <column name="ctype1" type="TSTRING" default=" " comment="name of astrometric projection in RA"></column>
+    <column name="ctype2" type="TSTRING" default=" " comment="name of astrometric projection in DEC"></column>
+    <column name="crval1" type="TFLOAT" default="-999" comment=" RA corresponding to reference pixel (unit = deg)"></column>
+    <column name="crval2" type="TFLOAT" default="-999" comment=" DEC corresponding to reference pixel (unit = deg)"></column>
+    <column name="crpix1" type="TFLOAT" default="-999" comment="reference pixel value for RA"></column>
+    <column name="crpix2" type="TFLOAT" default="-999" comment="reference pixel value for DEC"></column>
+    <column name="pc001001" type="TFLOAT" default="-999" comment="elements of rotation/Dcale matrix"></column>
+    <column name="pc001002" type="TFLOAT" default="-999" comment="elements of rotation/Dcale matrix"></column>
+    <column name="pc002001" type="TFLOAT" default="-999" comment="elements of rotation/Dcale matrix"></column>
+    <column name="pc002002" type="TFLOAT" default="-999" comment="elements of rotation/Dcale matrix"></column>
+    <column name="polyOrder" type="TBYTE" default="255" comment="polynomial order of astrometry fit"></column>
+    <column name="pca1x3y0" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca1x2y1" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca1x1y2" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca1x0y3" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca1x2y0" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca1x1y1" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca1x0y2" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca2x3y0" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca2x2y1" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca2x1y2" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca2x0y3" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca2x2y0" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca2x1y1" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca2x0y2" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+  </table>
+  <table name="ImageMeta">
+    <column name="photoCalID" type="TLONG" default="0" comment="photometry reduction code identifier"></column>
+    <column name="filterID" type="TBYTE" default="0" comment="filter ID"></column>
+    <column name="sky" type="TFLOAT" default="-999" comment=" mean sky level (unit = ADU)"></column>
+    <column name="skyScat" type="TFLOAT" default="-999" comment=" scatter in mean sky level (unit = ADU)"></column>
+    <column name="nDetect" type="TLONG" default="-999" comment="number of detections on CCD"></column>
+    <column name="magSat" type="TFLOAT" default="-999" comment=" saturation magnitude level (unit = mag)"></column>
+    <column name="completMag" type="TFLOAT" default="-999" comment=" 95% completion level in mag (unit = mag)"></column>
+    <column name="astroScat" type="TFLOAT" default="-999" comment=" astrometric scatter for chip (unit = mag)"></column>
+    <column name="photoScat" type="TFLOAT" default="-999" comment="photometric scatter for chip"></column>
+    <column name="numAstroRef" type="TLONG" default="-999" comment="number of astrometric reference sources"></column>
+    <column name="nx" type="TSHORT" default="-999" comment="chip dimension in x"></column>
+    <column name="ny" type="TSHORT" default="-999" comment="chip dimension in y"></column>
+    <column name="psfModelID" type="TLONG" default="-999" comment="PSF model identifier"></column>
+    <column name="psfFwhm" type="TFLOAT" default="-999" comment=" model psf full width at half maximum at chip center (unit = arcsec)"></column>
+    <column name="psfWidMajor" type="TFLOAT" default="-999" comment=" model PSF parameters at chip center (unit = arcsec)"></column>
+    <column name="psfWidMinor" type="TFLOAT" default="-999" comment=" model PSF parameters at chip center (unit = arcsec)"></column>
+    <column name="psfTheta" type="TFLOAT" default="-999" comment=" model PSF parameters at chip center (unit = deg)"></column>
+    <column name="apResid" type="TFLOAT" default="-999" comment="corrected aperture residual"></column>
+    <column name="dapResid" type="TFLOAT" default="-999" comment="scatter of aperture corrections"></column>
+    <column name="photoZero" type="TFLOAT" default="-999" comment=" local derived photometric zero point (unit = mag)"></column>
+    <column name="ctype1" type="TSTRING" default=" " comment="name of astrometric projection in RA"></column>
+    <column name="ctype2" type="TSTRING" default=" " comment="name of astrometric projection in DEC"></column>
+    <column name="crval1" type="TFLOAT" default="-999" comment=" RA corresponding to reference pixel (unit = deg)"></column>
+    <column name="crval2" type="TFLOAT" default="-999" comment=" DEC corresponding to reference pixel (unit = deg)"></column>
+    <column name="crpix1" type="TFLOAT" default="-999" comment=" reference pixel value for RA (unit = pix)"></column>
+    <column name="crpix2" type="TFLOAT" default="-999" comment=" reference pixel value for DEC (unit = pix)"></column>
+    <column name="pc001001" type="TFLOAT" default="-999" comment="elements of rotation/Dcale matrix"></column>
+    <column name="pc001002" type="TFLOAT" default="-999" comment="elements of rotation/Dcale matrix"></column>
+    <column name="pc002001" type="TFLOAT" default="-999" comment="elements of rotation/Dcale matrix"></column>
+    <column name="pc002002" type="TFLOAT" default="-999" comment="elements of rotation/Dcale matrix"></column>
+    <column name="polyOrder" type="TBYTE" default="255" comment="polynomial order of astrometry fit"></column>
+    <column name="pca1x3y0" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca1x2y1" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca1x1y2" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca1x0y3" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca1x2y0" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca1x1y1" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca1x0y2" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca2x3y0" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca2x2y1" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca2x1y2" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca2x0y3" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca2x2y0" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca2x1y1" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+    <column name="pca2x0y2" type="TFLOAT" default="-999" comment="polynomial coefficients for the astrometric fit"></column>
+  </table>
+  <table name="Detection">
+    <column name="objID" type="TLONGLONG" default="0" comment="ODM object identifier"></column>
+    <column name="detectID" type="TLONGLONG" default="0" comment="ODM detection identifier"></column>
+    <column name="ippObjID" type="TLONGLONG" default="0" comment="object id generated by IPP"></column>
+    <column name="ippDetectID" type="TLONGLONG" default="0" comment="detection id generated by IPP"></column>
+    <column name="filterID" type="TBYTE" default="0" comment="filter ID: g=1, r=2, i=3, z=4, y=5, w=6, ..."></column>
+    <column name="xPos" type="TFLOAT" default="-999" comment=" measured x on CCD from PSF fit (unit = pix)"></column>
+    <column name="yPos" type="TFLOAT" default="-999" comment=" measured y on CCD from PSF fit (unit = pix)"></column>
+    <column name="xPosErr" type="TFLOAT" default="-999" comment=" estimated error in x (unit = pix)"></column>
+    <column name="yPosErr" type="TFLOAT" default="-999" comment=" estimated error in y (unit = pix)"></column>
+    <column name="ippRa" type="TFLOAT" default="-999" comment="RA"></column>
+    <column name="ippDec" type="TFLOAT" default="-999" comment="dec"></column>
+    <column name="instFlux" type="TFLOAT" default="-999" comment=" PSF instrumental flux (unit = adu/s)"></column>
+    <column name="instFluxErr" type="TFLOAT" default="-999" comment=" estimated error in flux (unit = adu/s)"></column>
+    <column name="peakADU" type="TFLOAT" default="-999" comment=" peak count in source pixel (unit = adu)"></column>
+    <column name="psfWidMajor" type="TFLOAT" default="-999" comment=" model PSF width in major axis (unit = arcsec)"></column>
+    <column name="psfWidMinor" type="TFLOAT" default="-999" comment=" model PSF width in minor axis (unit = arcsec)"></column>
+    <column name="psfTheta" type="TFLOAT" default="-999" comment=" model PSF orientation angle (unit = deg)"></column>
+    <column name="psfCf" type="TFLOAT" default="-999" comment="PSF coverage factor"></column>
+    <column name="sky" type="TFLOAT" default="-999" comment=" PSF sky level at source (unit = adu)"></column>
+    <column name="skyErr" type="TFLOAT" default="-999" comment=" estimated error in sky (unit = adu)"></column>
+  </table>
+</tableDescriptions>
Index: /trunk/ippToPsps/src/Makefile.am
===================================================================
--- /trunk/ippToPsps/src/Makefile.am	(revision 27808)
+++ /trunk/ippToPsps/src/Makefile.am	(revision 27809)
@@ -30,4 +30,5 @@
 	ippToPspsVersion.c \
 	ippToPspsConfig.c \
+	ippToPspsBatchTest.c \
 	ippToPspsBatchInit.c \
 	ippToPspsBatchDetection.c \
Index: /trunk/ippToPsps/src/ippToPspsBatchTest.c
===================================================================
--- /trunk/ippToPsps/src/ippToPspsBatchTest.c	(revision 27809)
+++ /trunk/ippToPsps/src/ippToPspsBatchTest.c	(revision 27809)
@@ -0,0 +1,347 @@
+/** @file ippToPspsBatchDetection.c
+ *
+ *  @ingroup ippToPsps
+ *
+ *  @author IfA
+ *  Copyright 2009 Institute for Astronomy, University of Hawaii
+ */
+
+#include <limits.h>
+#include "ippToPsps.h"
+#include "ippToPspsConfig.h"
+#include "ippToPspsTestEnums.h"
+#include "fitsio.h"
+
+// Gets flux from magnitude
+static __inline bool ippToPsps_getFlux(const float zeroPoint, const float exposureTime, const float magnitude, float* flux) {
+
+    // use uncalibrated instrumental flux
+    *flux = powf(10.0, -0.4*magnitude) / exposureTime;
+
+    // use calibrated flux in Janskys, where 3631 Jy is the zero point flux for the filter: constant over all filters because we're using AB magnitudes
+    //float flux = 3631 * powf(10.0, -0.4*(magnitude + zeroPoint)) / exposureTime;
+
+    //    printf("mag=%f\texpTime=%f\tzeroPoint=%f\tflux=%f\n", magnitude, exposureTime, zeroPoint, flux);
+    return (!isfinite(*flux) || *flux < 0.000001) ? false : true;
+}
+
+/**
+  \brief test data for checking PSPS values correspond with IPP 
+
+  */
+int ippToPsps_batchTest(IppToPsps *this) {
+
+    if (this->numOfInputFiles < 1) return PS_EXIT_DATA_ERROR;
+
+    int status = 0;
+    fitsfile *fitsIn;         
+    int nKeys;
+
+    if (fits_open_file(&fitsIn, this->inputFiles[0], READONLY, &status)) {
+
+        fits_report_error(stderr, status);
+        return PS_EXIT_SYS_ERROR;
+    }
+
+    // get primary header and pull stuff out for later
+    fits_get_hdrspace(fitsIn, &nKeys, NULL, &status);
+
+    float zptObs, zeroPoint, exposureTime;
+    char filterType[20];
+    status=0; fits_read_key(fitsIn, TFLOAT, "ZPT_OBS", &zptObs, NULL, &status);
+    status=0; fits_read_key(fitsIn, TFLOAT, "ZPT_REF", &zeroPoint, NULL, &status);
+    status=0; fits_read_key(fitsIn, TFLOAT, "EXPREQ", &exposureTime, NULL, &status);
+    status=0; fits_read_key(fitsIn, TSTRING, "FILTERID", filterType, NULL, &status);
+    
+    ippToPspsConfig_writeTable(this->config, fitsIn, this->fitsOut, 1, "FrameMeta", true);
+
+    // FrameMeta values
+    fits_write_col(this->fitsOut, TLONG, FRAMEMETA_FRAMEID, 1, 1, 1, &this->expId, &status);
+
+    int8_t filterID = -1;
+    ippToPspsConfig_getFilterId(this->config, filterType, &filterID);
+    fits_write_col(this->fitsOut, TBYTE, FRAMEMETA_FILTERID, 1, 1, 1, &filterID, &status);
+
+    // stuff to keep from psf.hdr header
+    double obsTime = 0.0;
+    int sourceId = -1;
+    int imageId = -1;
+    float fwhmMaj;
+    float fwhmMin;
+    float momentMaj;
+    float momentMin;
+    float psfFwhm;
+    float momentFwhm;
+    long pspsImageId = -1;
+
+    // DVO variables
+    SkyList *skylist = NULL;
+    dvoDetection *dvoDetections = NULL;
+    int maxDvoDetId = -1;
+    int numDvoDetections = -1;
+    Image *pImage = NULL;
+
+    // stuff for detections table
+    time_t now;
+    struct tm *ts;
+    char timeStr[12];
+    now = time(NULL);
+    ts = gmtime(&now); // TODO should be UTC
+    strftime(timeStr, sizeof(timeStr), "%Y-%m-%d", ts);
+
+    uint32_t totalDetections = 0;
+    uint32_t s,d;
+    uint32_t invalidDvoRows;
+    uint32_t smfJumps;
+    uint32_t unmatched;
+
+    long longnull = -999;
+    float floatnull = -999.0;
+    int anynull = 0;
+
+    char ccdNumber[3];
+    char extensionName[15];
+
+    long ippIDet[MAXDETECT];
+    float instMag[MAXDETECT];
+    float instMagErr[MAXDETECT];
+    float peakMag[MAXDETECT];
+    long objID[MAXDETECT];
+    long detectID[MAXDETECT];
+    long ippObjID[MAXDETECT];
+    long ippDetectID[MAXDETECT];
+    long imageID[MAXDETECT];
+    float obsTimes[MAXDETECT];
+    float instFlux[MAXDETECT];
+    float instFluxErr[MAXDETECT];
+    float peakFlux[MAXDETECT];
+    int8_t filterIDs[MAXDETECT];
+
+    char** assocDate = (char**)calloc(MAXDETECT, sizeof(char**));
+    for (uint32_t i=0; i<MAXDETECT;i++) assocDate[i] = (char*)calloc(20,sizeof(char));
+
+    // some stuff is the same for all detections so we can populate here
+    for (long s = 0; s<MAXDETECT; s++) {
+
+        filterIDs[s] = filterID;
+        strcpy(assocDate[s], timeStr);
+    }
+
+    long maxObjID = LONG_MIN; 
+    long minObjID = LONG_MAX;
+    short nOta = 0;
+    long i;
+    bool isDuplicate;
+    uint32_t numOfDuplicates;
+    uint32_t numInvalidFlux;
+    long numDetectionsOut;
+    long removeList[MAXDETECT];
+    long thisObjId;
+
+    // loop round all 60 chips
+    for (int x=3; x<4; x++) {
+        for (int y=3; y<4; y++) {
+
+            // dodge the corners
+            if (x==0 && y==0) continue;
+            if (x==0 && y==7) continue;
+            if (x==7 && y==0) continue;
+            if (x==7 && y==7) continue;
+
+            sprintf(ccdNumber, "%d%d", x, y);
+
+            // check we can move to detections table in smf
+            sprintf(extensionName, "XY%s.psf", ccdNumber);
+            status=0;
+            if (fits_movnam_hdu(fitsIn, BINARY_TBL, extensionName, 0, &status)) {
+                psError(PS_ERR_IO, false, "Can't move to extension: %s skipping this chip\n", extensionName);
+                continue;
+            }
+
+            // move to header extension
+            sprintf(extensionName, "XY%s.hdr", ccdNumber);
+            status=0;
+            if (fits_movnam_hdu(fitsIn, IMAGE_HDU, extensionName, 0, &status)) {
+                psError(PS_ERR_IO, false, "Can't move to extension: %s skipping this chip\n", extensionName);
+                continue;
+            }
+
+            // stuff to save from psf.hdr
+            status=0; fits_read_key(fitsIn, TFLOAT, "FWHM_MAJ", &fwhmMaj, NULL, &status);
+            status=0; fits_read_key(fitsIn, TFLOAT, "FWHM_MIN", &fwhmMin, NULL, &status);
+            status=0; fits_read_key(fitsIn, TFLOAT, "IQ_FW1", &momentMaj, NULL, &status);
+            status=0; fits_read_key(fitsIn, TFLOAT, "IQ_FW2", &momentMin, NULL, &status);
+            status=0; fits_read_key(fitsIn, TDOUBLE, "MJD-OBS", &obsTime, NULL, &status);
+
+            status=0; fits_read_key(fitsIn, TLONG, "IMAGEID", &imageId, NULL, &status);
+            status=0; fits_read_key(fitsIn, TLONG, "SOURCEID", &sourceId, NULL, &status);
+
+            // access DVO database
+            skylist = dvoSkyListByExternID(this->dvoConfig, sourceId, imageId, &pImage);
+            if (skylist == NULL) {
+                psError(PS_ERR_IO, false, "DVO: can't find SkyList for sourceId='%d' imageId='%d' (CCD = XY%s): skipping\n", sourceId, imageId, ccdNumber);
+                continue;
+            }
+
+            // create unique int from 'frameID' (aka exposure ID) and ccd number
+            pspsImageId = (this->expId*100) + pImage->ccdnum;
+
+            // now get DVO detections
+            dvoDetections = NULL;
+            numDvoDetections = dvoGetDetections(skylist, pImage->imageID, &dvoDetections, &maxDvoDetId);
+
+            // TODO check nDet < MAXDETECT
+
+            // create ImageMeta
+            ippToPspsConfig_writeTable(this->config, fitsIn, this->fitsOut, 1, "ImageMeta", true);
+            psfFwhm = (fwhmMaj+fwhmMin)/2;
+            momentFwhm = (momentMaj+momentMin)/2;
+            fits_write_col(this->fitsOut, TLONG, IMAGEMETA_PHOTOCALID, 1, 1, 1, &pImage->photcode, &status); 
+            fits_write_col(this->fitsOut, TBYTE, IMAGEMETA_FILTERID, 1, 1, 1, &filterID, &status);
+            fits_write_col(this->fitsOut, TFLOAT, IMAGEMETA_PHOTOSCAT, 1, 1, 1, &zptObs, &status);
+            fits_write_col(this->fitsOut, TFLOAT, IMAGEMETA_PSFFWHM, 1, 1, 1, &psfFwhm, &status);
+            fits_write_col(this->fitsOut, TFLOAT, IMAGEMETA_PHOTOZERO, 1, 1, 1, &zptObs, &status);
+
+            // now move BACK to detections table in smf
+            sprintf(extensionName, "XY%s.psf", ccdNumber);
+            status=0;
+            if (fits_movnam_hdu(fitsIn, BINARY_TBL, extensionName, 0, &status)) {
+                psError(PS_ERR_IO, false, "Can't move to extension: %s skipping\n", extensionName);
+                continue;
+            }
+
+            // keep a running count of 'images' we find in order to write total to FrameMeta at the end
+            nOta++;
+
+            long nDet = 0;
+            if (fits_get_num_rows(fitsIn, &nDet, &status)) {
+                fits_report_error(stderr, status);
+            }
+
+            // loop round detections to populate some extra stuff
+            s = d = totalDetections = invalidDvoRows = smfJumps = unmatched = 0;
+
+            anynull = 0;
+            fits_read_col(fitsIn, TLONG, 1, 1, 1, nDet, &longnull, ippIDet, &anynull, &status);
+            fits_read_col(fitsIn, TFLOAT, 8, 1, 1, nDet, &floatnull, instMag, &anynull, &status);
+            fits_read_col(fitsIn, TFLOAT, 9, 1, 1, nDet, &floatnull, instMagErr, &anynull, &status);
+            fits_read_col(fitsIn, TFLOAT, 12, 1, 1, nDet, &floatnull, peakMag, &anynull, &status);
+
+            // DVO detections are ordered by IPP_IDET, which increments from 0 in SMF table
+            // both DVO detections and smf detections should increment. however, there may be gaps in smf and 'invalid' rows in dvo
+            numOfDuplicates = 0;
+            numInvalidFlux = 0;
+            for (long s = 0; s<nDet; s++) {
+
+                isDuplicate = false;
+
+                // count jumps in smf file
+                if (s>0 && (ippIDet[s] != (ippIDet[s-1] + 1))) smfJumps++;
+
+                while (ippIDet[s] > dvoDetections[d].meas.detID && dvoDetections[d].meas.detID <= maxDvoDetId) {
+
+                    d++;
+                    if (!dvoDetections[d].valid) invalidDvoRows++;
+                }
+
+                if (ippIDet[s] == dvoDetections[d].meas.detID ) {
+
+                    // TODO HACK duplicates bug - don't include any duplicates
+                    thisObjId = dvoDetections[d].ave.extID;
+                    for (i=0; i<totalDetections; i++) {
+                        if (thisObjId == objID[i]) {
+                            removeList[numOfDuplicates+numInvalidFlux] = s+1;
+                            numOfDuplicates++;
+                            isDuplicate=true;
+                            break;
+                        }
+                    }
+                    // populate some arrays to be shoved in table in bulk later
+                    objID[s] = thisObjId;
+                    detectID[s] = dvoDetections[d].meas.extID;
+                    ippObjID[s] = (uint64_t)dvoDetections[d].ave.catID*1000000000 + (uint64_t)dvoDetections[d].ave.objID;
+                    ippDetectID[s] = dvoDetections[d].meas.detID;
+                    imageID[s] = pspsImageId;
+                    obsTimes[s] = obsTime;
+
+                    ippToPsps_getFlux(zeroPoint, exposureTime, instMagErr[s], &instFluxErr[s]);
+
+                    // check for invalid flux values (if not already labelled as a duplicate)
+                    if ((!ippToPsps_getFlux(zeroPoint, exposureTime, peakMag[s], &peakFlux[s]) ||
+                                !ippToPsps_getFlux(zeroPoint, exposureTime, instMag[s], &instFlux[s])) && !isDuplicate) {
+                        removeList[numOfDuplicates+numInvalidFlux] = s+1;
+                        numInvalidFlux++;
+                    }
+
+                    // store max/min objID
+                    if (objID[s] > maxObjID) maxObjID = objID[s];
+                    if (objID[s] < minObjID) minObjID = objID[s];
+
+                    totalDetections++;
+                }
+                // if we get here then we cannot find this smf detection in DVO 
+                else {
+
+                    unmatched++;
+                    continue;
+                }
+
+                if (dvoDetections[d].meas.detID == maxDvoDetId) break;
+            }
+
+            // write number of rows (detections) to ImageMeta
+            numDetectionsOut = totalDetections - numOfDuplicates - numInvalidFlux;
+            fits_write_col(this->fitsOut, TLONG, IMAGEMETA_NDETECT, 1, 1, 1, &numDetectionsOut, &status);
+
+            // detections
+            ippToPspsConfig_writeTable(this->config, fitsIn, this->fitsOut, nDet, "Detection", false);
+            fits_write_col(this->fitsOut, TLONGLONG, DETECTION_OBJID, 1, 1, nDet, objID, &status);
+            fits_write_col(this->fitsOut, TLONGLONG, DETECTION_DETECTID, 1, 1, nDet, detectID, &status);
+            fits_write_col(this->fitsOut, TLONGLONG, DETECTION_IPPOBJID, 1, 1, nDet, ippObjID, &status);
+            fits_write_col(this->fitsOut, TLONGLONG, DETECTION_IPPDETECTID, 1, 1, nDet, ippDetectID, &status);
+            fits_write_col(this->fitsOut, TBYTE, DETECTION_FILTERID, 1, 1, nDet, filterIDs, &status);
+            fits_write_col(this->fitsOut, TFLOAT, DETECTION_INSTFLUX, 1, 1, nDet, instFlux, &status);
+            fits_write_col(this->fitsOut, TFLOAT, DETECTION_INSTFLUXERR, 1, 1, nDet, instFluxErr, &status);
+            fits_write_col(this->fitsOut, TFLOAT, DETECTION_PEAKADU, 1, 1, nDet, peakFlux, &status);
+            if (numOfDuplicates||numInvalidFlux) fits_delete_rowlist(this->fitsOut, removeList, numOfDuplicates+numInvalidFlux, &status);
+
+
+            psLogMsg("ippToPsps", PS_LOG_INFO,
+                    "+-----------+---------+----------+------------------+---------------+--------------+\n"
+                    "| Extension | Rows in | Rows out | Missing from DVO | Duplicate IDs | Invalid Flux |\n"
+                    "|  %s |  %5ld  |   %5ld  |      %5d       |    %5d      |    %5d     |\n",
+                    extensionName, nDet, numDetectionsOut, unmatched, numOfDuplicates, numInvalidFlux);
+
+            if (dvoDetections!= NULL) dvoFree(dvoDetections);
+            SkyListFree(skylist);
+        }
+    }
+
+    for (uint32_t i=0; i<MAXDETECT;i++) free(assocDate[i]);
+    free(assocDate);
+
+    // write number of images we have found into FrameMeta table
+    status=0; 
+    if (fits_movnam_hdu(this->fitsOut, BINARY_TBL, "FrameMeta", 0, &status))
+        psError(PS_ERR_IO, false, "Can't move back to FrameMeta extension to write number of OTAs\n");
+    else 
+        fits_write_col(this->fitsOut, TSHORT, FRAMEMETA_NOTA, 1, 1, 1, &nOta, &status);
+
+    status=0;
+    if (fits_close_file(fitsIn, &status)) fits_report_error(stderr, status);
+
+    // write results
+    if (this->resultsFile) {
+
+        if (fprintf(this->resultsFile, "%ld\n", minObjID) < 1 ) 
+            psError(PS_ERR_IO, false, "Unable to write min Object ID to'%s'\n", this->resultsPath);
+        if (fprintf(this->resultsFile, "%ld\n", maxObjID) < 1 ) 
+            psError(PS_ERR_IO, false, "Unable to write max Object ID to'%s'\n", this->resultsPath);
+    }
+
+    psLogMsg("ippToPsps", PS_LOG_INFO, "Data written for a total of %d chips/OTAs", nOta);
+
+    return PS_EXIT_SUCCESS;
+}
+
+
