Index: /trunk/ippToPsps/src/Batch.c
===================================================================
--- /trunk/ippToPsps/src/Batch.c	(revision 31010)
+++ /trunk/ippToPsps/src/Batch.c	(revision 31011)
@@ -18,4 +18,5 @@
 #include <pslib.h>
 #include <psmodules.h>
+
 #include "Batch.h"
 
@@ -62,22 +63,9 @@
         if (this->exitCode != PS_EXIT_SUCCESS) {
 
-            psError(PS_ERR_UNKNOWN, false, "Failed, so deleting %s", this->fitsOutPath);
-
-            // if process failed, we need to delete that FITS file
-            if (remove(this->fitsOutPath) == -1) {
-
-                psError(PS_ERR_UNKNOWN, false, "Unable to delete %s", this->fitsOutPath);
-            }
+            psError(PS_ERR_UNKNOWN, false, "Failed, so deleting fits file");
+            this->fitsOut->delete(this->fitsOut);
         }
-        // ...otherwise, close file
-        else {
-
-            int status = 0;
-            if (fits_close_file(this->fitsOut, &status)) {
-
-                psError(PS_ERR_IO, false, "Unable to close FITS file %s", this->fitsOutPath);
-                fits_report_error(stderr, status);
-            }
-        }
+
+        this->fitsOut->destroy(this->fitsOut);
     }
 
@@ -99,5 +87,5 @@
     if (this->dvoConfig != NULL) dvoConfigFree(this->dvoConfig);
 
-    ippToPspsConfig_Destructor(this->config);
+    this->config->destroy(this->config);
 
     pmConfigDone();
@@ -151,5 +139,5 @@
   Sets-up and reads command-line arguments
 */
-static bool parseArguments(Batch* this, int argc, char **argv) {
+static bool parseArguments(Batch* this, int argc, char **argv, char* configsDir, char* fitsOutFile) {
 
     // first deal with DVO database
@@ -178,4 +166,7 @@
     haveSurveyType = false;
 
+    char fitsOutPath[1000];
+    char configsBaseDir[1000];
+
     // decode arguments
     int32_t optind = 1;
@@ -193,5 +184,5 @@
             else if(strcmp(sw, "-output") == 0 ) {
                 optind++; if( optind > ( argc-1 ) ) break;
-                strcpy(this->fitsOutPath, argv[optind]);
+                strcpy(fitsOutPath, argv[optind]);
                 haveFitsOutPath = true;
             }
@@ -203,5 +194,5 @@
             else if(strcmp(sw, "-config") == 0 ) {
                 optind++; if( optind > ( argc-1 ) ) break;
-                strcpy(this->configsDir, argv[optind]);
+                strcpy(configsBaseDir, argv[optind]);
                 haveConfig = true;
             }
@@ -220,17 +211,4 @@
 
 
-    // setup command line arguments
-    //psMetadataAddStr(this->arguments, PS_LIST_TAIL, "-survey", 0, "Survey type", NULL);
-
-
-    // now check rest of arguments
-    //if (argc < 2 || !psArgumentParse(this->arguments, &argc, argv)) {
-
-      //  usage(this, argv[0]);
-        //this->exitCode = PS_EXIT_CONFIG_ERROR;
-        //return false;
-   // }
-
-    //this->surveyType = psMemIncrRefCounter(psMetadataLookupStr(NULL, this->arguments, "-survey"));
 
     if (haveFitsInPath && !readInputFilePaths(this)) {
@@ -240,14 +218,7 @@
     }
 
-    return true;
-}
-
-/**
-  Initialises file paths, creates FITS file etc
-  */
-bool init(Batch* this) {
-
     // create a config object
-    this->config = ippToPspsConfig_Constructor(this->configsDir);
+    strcat(configsBaseDir, configsDir);
+    this->config = new_Config(configsBaseDir);
     if (this->config == NULL) {
 
@@ -257,5 +228,5 @@
 
     // get survey ID using config object
-    if (strlen(this->surveyType) > 0 && !ippToPspsConfig_getSurveyId(this->config, this->surveyType, &this->surveyID)) {
+    if (strlen(this->surveyType) > 0 && !this->config->getSurveyId(this->config, this->surveyType, &this->surveyID)) {
 
         this->exitCode = PS_EXIT_CONFIG_ERROR;
@@ -263,15 +234,9 @@
     }
     // create full FITS out path
-    sprintf (this->fitsOutPath, "%s/%s", this->fitsOutPath, this->fitsOutFile);
+    sprintf (fitsOutPath, "%s/%s", fitsOutPath, fitsOutFile);
 
     // create an output FITS file
-    int status=0;
-    if (fits_create_file(&this->fitsOut, this->fitsOutPath, &status)) {
-        fits_report_error(stderr, status);
-        psError(PS_ERR_IO, false, "Unable to create file at: '%s'", this->fitsOutPath);
-        this->exitCode = PS_EXIT_SYS_ERROR;
-        this->fitsOut = NULL;
-        return false;
-    }
+    this->fitsOut = new_Fits(fitsOutPath);
+    if (this->fitsOut->getFilePtr(this->fitsOut) == NULL) return false;
 
     // create XML document for results 
@@ -281,5 +246,5 @@
         xmlNodePtr rootNode = xmlNewNode(NULL, BAD_CAST "ippToPsps_Results");
         xmlDocSetRootElement(this->resultsXmlDoc, rootNode);
-        xmlNewChild(rootNode, NULL, BAD_CAST "filename", BAD_CAST this->fitsOutFile);
+        xmlNewChild(rootNode, NULL, BAD_CAST "filename", BAD_CAST fitsOutFile);
     }
 
@@ -298,7 +263,4 @@
     printf("* numOfInputFiles : %d\n", this->numOfInputFiles);
     printf("* fitsInPath      : '%s'\n", this->fitsInPath);
-    printf("* fitsOutFile     : '%s'\n", this->fitsOutFile);
-    printf("* fitsOutPath     : '%s'\n", this->fitsOutPath);
-    printf("* configsDir      : '%s'\n", this->configsDir);
 }
 
@@ -359,5 +321,5 @@
     // set up function pointers
     this->parseArguments = parseArguments;
-    this->init = init;
+    //this->init = init;
     this->print = print;
     this->destroy = destroy;
Index: /trunk/ippToPsps/src/Batch.h
===================================================================
--- /trunk/ippToPsps/src/Batch.h	(revision 31010)
+++ /trunk/ippToPsps/src/Batch.h	(revision 31011)
@@ -18,4 +18,6 @@
 #include <libxml/tree.h>
 
+#include "Fits.h"
+
 /**
   Abstract base class for all batches. Known subclasses are:
@@ -25,5 +27,5 @@
   - StackBatch
 
-  All subclasses need to implement the run() method and may need to implenent print() and/or init()
+  All subclasses need to implement the run() method and may need to implenent print()
   */
 typedef struct Batch {
@@ -37,11 +39,8 @@
     uint16_t numOfInputFiles;   // number of input files
     char** inputFiles;          // array of input file names
-    char fitsOutFile[100];      // FITS output filename
-    char fitsOutPath[1000];     // path to FITS output
-    fitsfile *fitsOut;          // output FITS file
-    char configsDir[500];       // path to IPP/PSPS mapping file
+    Fits *fitsOut;              // output FITS file
     pmConfig* pmconfig;         // pmConfig
     dvoConfig* dvoConfig;       // dvo database
-    IppToPspsConfig* config;    // config structure
+    Config* config;    // config structure
     char todaysDate[20];        // today's date
     int exitCode;               // ps exit code
@@ -50,5 +49,4 @@
     // methods
     bool (*parseArguments)();
-    bool (*init)();
     int (*run)();
     void (*print)();
@@ -58,4 +56,6 @@
     bool (*gotConfig)();
     bool (*gotSurveyType)(); 
+
+    // destructor
     void (*destroy)();
 
Index: /trunk/ippToPsps/src/Config.c
===================================================================
--- /trunk/ippToPsps/src/Config.c	(revision 31010)
+++ /trunk/ippToPsps/src/Config.c	(revision 31011)
@@ -1,3 +1,3 @@
-/** @file ippToPspsConfig.c
+/** @file Config.c
  *
  *  @ingroup ippToPsps
@@ -7,10 +7,14 @@
  */
 
-#include "ippToPspsConfig.h"
+#include "Config.h"
 #include <ctype.h>
 #include <libxml/parser.h>
 #include <libxml/tree.h>
 
-// Gets PS type from string
+#include "Fits.h"
+
+/**
+   Gets PS type from string
+   */
 static int ippToPsps_GetDataType(char *typename) {
 
@@ -28,8 +32,10 @@
 }
 
-// finds a column within this table
-static IppToPspsConfig_Column* ippToPspsConfig_findColumn(IppToPspsConfig_Table* table, const char* name) {
-
-    IppToPspsConfig_Column* column = NULL;
+/**
+  Finds a column within this table
+  */
+static Column* findColumn(Table* table, const char* name) {
+
+    Column* column = NULL;
 
     for (uint32_t i=0; i<table->numOfColumns; i++) {
@@ -46,8 +52,10 @@
 }
 
-// finds a table from the array of tables
-static IppToPspsConfig_Table* ippToPspsConfig_findTable(IppToPspsConfig* this, const char* name) {
-
-    IppToPspsConfig_Table* table = NULL;
+/**
+  Finds a table from the array of tables
+  */
+static Table* findTable(Config* this, const char* name) {
+
+    Table* table = NULL;
 
     for (uint32_t i=0; i<this->numOfTables; i++) {
@@ -64,6 +72,8 @@
 }
 
-// gets an attribute value from XML node
-static bool ippToPspsConfig_getAttribute(xmlNode* node, const char* attName, char* _value) {
+/**
+  Gets an attribute value from XML node
+  */
+static bool getAttribute(xmlNode* node, const char* attName, char* _value) {
 
     xmlChar* value = xmlGetProp(node, (const xmlChar*)attName);
@@ -75,6 +85,8 @@
 }
 
-// creates a FITS binary table and populates it with defaults based on definition in schema XML
-static bool ippToPspsConfig_createTable(IppToPspsConfig_Table* table, fitsfile* fitsOut, const long nRows) {
+/**
+  Creates a FITS binary table and populates it with defaults based on definition in schema XML
+  */
+static bool createTable(Table* table, Fits* fitsOut, const long nRows) {
 
     if (!table) return false;
@@ -98,28 +110,21 @@
         switch (table->columns[i].pspsType) {
 
-            case TBYTE:
-                sprintf(colTypes[i],"1B");
-                break;
-            case TSHORT:
-                sprintf(colTypes[i],"1I");
-                break;
-            case TLONG:
-                sprintf(colTypes[i],"1J"); // TODO
-                break;
-            case TLONGLONG:
-                sprintf(colTypes[i],"1K");
-                break;
-            case TFLOAT:
-                sprintf(colTypes[i],"1E");
-                break;
-            case TDOUBLE:
-                sprintf(colTypes[i],"1D");
-                break;
-            case TSTRING:
-                sprintf(colTypes[i],"32A"); // TODO width?
-                break;
+            case TBYTE: sprintf(colTypes[i],"1B");
+                        break;
+            case TSHORT: sprintf(colTypes[i],"1I");
+                         break;
+            case TLONG: sprintf(colTypes[i],"1J"); // TODO
+                        break;
+            case TLONGLONG: sprintf(colTypes[i],"1K");
+                            break;
+            case TFLOAT: sprintf(colTypes[i],"1E");
+                         break;
+            case TDOUBLE: sprintf(colTypes[i],"1D");
+                          break;
+            case TSTRING: sprintf(colTypes[i],"32A"); // TODO width?
+                          break;
 
             default:
-                break;
+                          break;
         }
 
@@ -127,10 +132,5 @@
     }
 
-    int status = 0;
-    if (fits_create_tbl(fitsOut, BINARY_TBL, nRows, nCols, colNames, colTypes, NULL, table->name, &status)) {
-
-        fits_report_error(stderr, status);
-        psError(PS_ERR_IO,"Unable to create table: %s", table->name);
-    }
+    fitsOut->createBinaryTable(fitsOut, nRows, nCols, colNames, colTypes, table->name);
 
     for (i=0;i<nCols;i++) free(colTypes[i]);
@@ -166,33 +166,33 @@
             case TBYTE:
                 for(j=0;j<nRows;j++) int8col[j] = (int8_t)tempLong;
-                fits_write_col(fitsOut, TBYTE, col, 1, 1, nRows, int8col, &status);
+                fitsOut->writeColumn(fitsOut, TBYTE, col, 1, 1, nRows, int8col);
                 break;
             case TSHORT:
                 for(j=0;j<nRows;j++) int16col[j] = (int16_t)tempLong;
-                fits_write_col(fitsOut, TSHORT, col, 1, 1, nRows, int16col, &status);
+                fitsOut->writeColumn(fitsOut, TSHORT, col, 1, 1, nRows, int16col);
                 break;
             case TLONG:
                 for(j=0;j<nRows;j++) longcol[j] = (long)tempLong;
-                fits_write_col(fitsOut, TLONG, col, 1, 1, nRows, longcol, &status);
+                fitsOut->writeColumn(fitsOut, TLONG, col, 1, 1, nRows, longcol);
                 break;
 
             case TLONGLONG:
                 for(j=0;j<nRows;j++) longcol[j] = tempLong;
-                fits_write_col(fitsOut, TLONGLONG, col, 1, 1, nRows, longcol, &status);
+                fitsOut->writeColumn(fitsOut, TLONGLONG, col, 1, 1, nRows, longcol);
                 break;
 
             case TFLOAT:
                 for(j=0;j<nRows;j++) floatcol[j] = (float)tempDouble;
-                fits_write_col(fitsOut, TFLOAT, col, 1, 1, nRows, floatcol, &status);
+                fitsOut->writeColumn(fitsOut, TFLOAT, col, 1, 1, nRows, floatcol);
                 break;
 
             case TDOUBLE:
                 for(j=0;j<nRows;j++) doublecol[j] = tempDouble;
-                fits_write_col(fitsOut, TDOUBLE, col, 1, 1, nRows, doublecol, &status);
+                fitsOut->writeColumn(fitsOut, TDOUBLE, col, 1, 1, nRows, doublecol);
                 break;
 
             case TSTRING:
                 for(j=0;j<nRows;j++) strcpy(strcol[j], tempStr);
-                fits_write_col(fitsOut, TSTRING, col, 1, 1, nRows, strcol, &status);
+                fitsOut->writeColumn(fitsOut, TSTRING, col, 1, 1, nRows, strcol);
                 break;
 
@@ -214,6 +214,8 @@
 }
 
-// count number of children with the provided name
-static int ippToPspsConfig_countChildren(xmlNode* rootNode, const char* name) {
+/*
+   Counts number of children with the provided name
+   */
+static int countChildren(xmlNode* rootNode, const char* name) {
 
     int count = 0;
@@ -228,6 +230,8 @@
 }
 
-// searches through this table and finds attribute value for a provided key
-static bool ippToPspsConfig_getRowAttribute(IppToPspsConfig* this, xmlNode* tableNode, 
+/**
+  Searches through this table and finds attribute value for a provided key
+  */
+static bool getRowAttribute(Config* this, xmlNode* tableNode, 
         const char* keyName, const char* keyValue, 
         const char* attName, char* attValue) {
@@ -243,11 +247,11 @@
 
             if (strcmp((const char*)node->name, "row")==0) {
-            //psLogMsg("ippToPsps", PS_LOG_INFO, " Looking for key '%s'",  keyName );
-
-                if (!ippToPspsConfig_getAttribute(node, keyName, buffer)) continue;
-            //psLogMsg("ippToPsps", PS_LOG_INFO, "Found key '%s' value '%s'",  keyName, buffer );
+                //psLogMsg("ippToPsps", PS_LOG_INFO, " Looking for key '%s'",  keyName );
+
+                if (!getAttribute(node, keyName, buffer)) continue;
+                //psLogMsg("ippToPsps", PS_LOG_INFO, "Found key '%s' value '%s'",  keyName, buffer );
 
                 if (strcmp(buffer, keyValue) != 0) continue;
-                ippToPspsConfig_getAttribute(node, attName, attValue);
+                getAttribute(node, attName, attValue);
                 //printf("FOUND %s %s %s %s \n", keyName, buffer, attName, attValue );
                 return true;
@@ -261,6 +265,8 @@
 }
 
-// opens an XML file and returns the document
-static xmlDoc* ippToPspsConfig_openXmlFile(const char* path) {
+/*
+   opens an XML file and returns the document
+   */
+static xmlDoc* openXmlFile(const char* path) {
 
     xmlDoc* doc = xmlReadFile(path, NULL, 0);
@@ -269,6 +275,8 @@
 }
 
-// closes an XML file 
-static bool ippToPspsConfig_closeXmlFile(xmlDoc* doc) {
+/*
+   closes an XML file 
+   */
+static bool closeXmlFile(xmlDoc* doc) {
 
     xmlFreeDoc(doc);
@@ -278,7 +286,9 @@
 }
 
-// gets filter ID for this filter name from 'init' data 
-static bool ippToPspsConfig_getInitValue(
-        IppToPspsConfig* this, 
+/**
+  Gets a value from the 'init' data 
+  */
+static bool getInitValue(
+        Config* this, 
         const char* tableName,
         const char* keyName, const char* keyValue,
@@ -290,5 +300,5 @@
     psStringAppend(&path, "%s/../init/data.xml", this->configsPath); // TODO nasty
 
-    xmlDoc* doc = ippToPspsConfig_openXmlFile(path);
+    xmlDoc* doc = openXmlFile(path);
 
     if (doc == NULL) {
@@ -313,8 +323,8 @@
 
             if (strcmp((const char*)node->name, "table")!=0) continue;
-            if (!ippToPspsConfig_getAttribute(node, "name", tempStr)) continue;
+            if (!getAttribute(node, "name", tempStr)) continue;
             if (strcmp(tempStr, tableName)!=0) continue;
 
-            ret = ippToPspsConfig_getRowAttribute(this, node, keyName, keyValue, attName, attValue);
+            ret = getRowAttribute(this, node, keyName, keyValue, attName, attValue);
             break;
         }
@@ -322,14 +332,16 @@
     }
 
-    ippToPspsConfig_closeXmlFile(doc);
+    closeXmlFile(doc);
 
     return ret;
 }
 
-// gets survey ID from survey name
-bool ippToPspsConfig_getSurveyId(IppToPspsConfig* this, const char* surveyType, int8_t* surveyId) {
+/*
+   Gets survey ID from survey name
+   */
+static bool getSurveyId(Config* this, const char* surveyType, int8_t* surveyId) {
 
     char buffer[10];
-    if (!ippToPspsConfig_getInitValue(this, "Survey", "name", surveyType, "surveyID", buffer)) {
+    if (!getInitValue(this, "Survey", "name", surveyType, "surveyID", buffer)) {
 
         *surveyId = -1;
@@ -341,6 +353,8 @@
 }
 
-// gets filter ID from filter type
-bool ippToPspsConfig_getFilterId(IppToPspsConfig* this, const char* filterType, int8_t* filterId) {
+/*
+   Gets filter ID from filter type
+   */
+static bool getFilterId(Config* this, const char* filterType, int8_t* filterId) {
 
     char tmp[2];
@@ -348,5 +362,5 @@
     tmp[1] = '\0';
     char buffer[10];
-    if (!ippToPspsConfig_getInitValue(this, "Filter", "filterType", tmp, "filterID", buffer)) {
+    if (!getInitValue(this, "Filter", "filterType", tmp, "filterID", buffer)) {
 
         *filterId = -1;
@@ -358,6 +372,8 @@
 }
 
-// populate the provided table with data from XML
-static bool ippToPspsConfig_populateTable(IppToPspsConfig_Table* table, xmlNode* rootElement, fitsfile *fitsOut) {
+/**
+  Populates the provided table with data from XML
+  */
+static bool populateTable(Table* table, xmlNode* rootElement, Fits *fitsOut) {
 
     char tempStr[100];
@@ -370,5 +386,5 @@
 
             if (strcmp((const char*)node->name, "table")!=0) continue;
-            if (!ippToPspsConfig_getAttribute(node, "name", tempStr)) continue;
+            if (!getAttribute(node, "name", tempStr)) continue;
             if (strcmp(tempStr, table->name)==0) {tableNode = node; break;}
         }
@@ -382,7 +398,7 @@
 
     // now count the number of rows
-    int nRows = ippToPspsConfig_countChildren(tableNode, "row");
+    int nRows = countChildren(tableNode, "row");
     if (nRows < 1) return false;
-    if (!ippToPspsConfig_createTable(table, fitsOut, nRows)) return false;
+    if (!createTable(table, fitsOut, nRows)) return false;
 
     int rows=0;
@@ -411,5 +427,5 @@
             col = i+1;
 
-            if (!ippToPspsConfig_getAttribute(node, table->columns[i].pspsName, tempStr)) continue;
+            if (!getAttribute(node, table->columns[i].pspsName, tempStr)) continue;
 
             strncpy(strcol[0], tempStr, 32);
@@ -420,25 +436,25 @@
                 case TBYTE:
                     bytecol[0] = atoi(tempStr);
-                    fits_write_col(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, bytecol, &status); 
+                    fitsOut->writeColumn(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, bytecol); 
                     break;
                 case TSHORT:
                     shortcol[0] = atoi(tempStr);
-                    fits_write_col(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, shortcol, &status); 
+                    fitsOut->writeColumn(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, shortcol); 
                     break;
                 case TLONG:
                 case TLONGLONG:
                     longcol[0] = atol(tempStr);
-                    fits_write_col(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, longcol, &status); 
+                    fitsOut->writeColumn(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, longcol); 
                     break;
                 case TFLOAT:
                     floatcol[0] = atof(tempStr);
-                    fits_write_col(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, floatcol, &status);
+                    fitsOut->writeColumn(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, floatcol);
                     break;
                 case TDOUBLE:
                     doublecol[0] = atof(tempStr);
-                    fits_write_col(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, doublecol, &status);
+                    fitsOut->writeColumn(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, doublecol);
                     break;
                 case TSTRING:
-                    fits_write_col(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, strcol, &status); 
+                    fitsOut->writeColumn(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, strcol); 
                     break;
 
@@ -460,15 +476,17 @@
 }
 
-// loads a table description from XML
-static bool ippToPspsConfig_loadTableDescription(IppToPspsConfig_Table* table, xmlNode* tableNode) {
+/**
+  Loads a table description from XML
+  */
+static bool loadTableDescription(Table* table, xmlNode* tableNode) {
 
     bool ret = true;
     char tableName[100];
-    ippToPspsConfig_getAttribute(tableNode, "name", tableName);
+    getAttribute(tableNode, "name", tableName);
     strcpy(table->name, tableName);
 
     // count how many columns we have coming in and allocate memory for them
-    table->numOfColumns = ippToPspsConfig_countChildren(tableNode, "column");
-    table->columns = calloc(table->numOfColumns, sizeof(IppToPspsConfig_Column));
+    table->numOfColumns = countChildren(tableNode, "column");
+    table->columns = calloc(table->numOfColumns, sizeof(Column));
     //    psLogMsg("ippToPsps", PS_LOG_INFO, " Found '%d' columns", table->numOfColumns);
 
@@ -484,17 +502,17 @@
 
         // column name
-        ippToPspsConfig_getAttribute(node, "name", buffer);
+        getAttribute(node, "name", buffer);
         sprintf(table->columns[columnNum].pspsName, buffer);
 
         // column type
-        ippToPspsConfig_getAttribute(node, "type", buffer);
+        getAttribute(node, "type", buffer);
         table->columns[columnNum].pspsType = ippToPsps_GetDataType(buffer);
 
         // default value
-        ippToPspsConfig_getAttribute(node, "default", buffer);
+        getAttribute(node, "default", buffer);
         sprintf(table->columns[columnNum].defaultValue, buffer);
 
         // comment
-        ippToPspsConfig_getAttribute(node, "comment", buffer);
+        getAttribute(node, "comment", buffer);
         sprintf(table->columns[columnNum].comment, buffer);
 
@@ -523,6 +541,8 @@
 }
 
-// read table shapes from XML file
-bool ippToPspsConfig_loadTableDescriptions(IppToPspsConfig* this) {
+/**
+  Reads table shapes from XML file
+  */
+static bool loadTableDescriptions(Config* this) {
 
     bool ret = true;
@@ -531,5 +551,5 @@
     psStringAppend(&path, "%s/tables.xml", this->configsPath);
 
-    xmlDoc* doc = ippToPspsConfig_openXmlFile(path);
+    xmlDoc* doc = openXmlFile(path);
 
     if (doc == NULL) {
@@ -549,10 +569,10 @@
 
     // count how many tables we have coming in and allocate memory for them
-    this->numOfTables = ippToPspsConfig_countChildren(rootElement, "table");
-    this->tables = calloc(this->numOfTables, sizeof(IppToPspsConfig_Table));
+    this->numOfTables = countChildren(rootElement, "table");
+    this->tables = calloc(this->numOfTables, sizeof(Table));
     //    psLogMsg("ippToPsps", PS_LOG_INFO, " Found '%d' tables", this->numOfTables);
 
     xmlNode* node = NULL;
-    IppToPspsConfig_Table* table = NULL;
+    Table* table = NULL;
 
     int tableNum = 0;
@@ -565,5 +585,5 @@
                 table = this->tables+tableNum;
 
-                if (!ippToPspsConfig_loadTableDescription(table, node)) ret = false;
+                if (!loadTableDescription(table, node)) ret = false;
                 else tableNum++;
             }
@@ -574,11 +594,13 @@
         psError(PS_ERR_IO, false, "Mismatch between number of tables expected (%d) and those found (%d)", this->numOfTables, tableNum);
 
-    ippToPspsConfig_closeXmlFile(doc);
+    closeXmlFile(doc);
 
     return ret;
 }
 
-// loads mappings from XML for a particular table
-static bool ippToPspsConfig_loadTableMappings(IppToPspsConfig* this, xmlNode* tableNode) {
+/**
+  Loads mappings from XML for a particular table
+  */
+static bool loadTableMappings(Config* this, xmlNode* tableNode) {
 
     bool ret = false;
@@ -587,8 +609,8 @@
 
     char tableName[100];
-    ippToPspsConfig_getAttribute(tableNode, "name", tableName);
-
-    IppToPspsConfig_Table* table = ippToPspsConfig_findTable(this, tableName);
-    IppToPspsConfig_Column* column = NULL;
+    getAttribute(tableNode, "name", tableName);
+
+    Table* table = findTable(this, tableName);
+    Column* column = NULL;
     if (!table) return false;
 
@@ -603,15 +625,15 @@
 
                 // column type
-                ippToPspsConfig_getAttribute(node, "pspsName", buffer);
-                column = ippToPspsConfig_findColumn(table, buffer);
+                getAttribute(node, "pspsName", buffer);
+                column = findColumn(table, buffer);
 
                 if (!column) continue;
 
                 // IPP name
-                ippToPspsConfig_getAttribute(node, "ippName", buffer);
+                getAttribute(node, "ippName", buffer);
                 strcpy(column->ippName, buffer);
 
                 // IPP type
-                ippToPspsConfig_getAttribute(node, "ippType", buffer);
+                getAttribute(node, "ippType", buffer);
                 column->ippType = ippToPsps_GetDataType(buffer); 
 
@@ -626,6 +648,8 @@
 }
 
-// reads table mappings from XML file
-static bool ippToPspsConfig_loadMappings(IppToPspsConfig* this) {
+/**
+  Reads table mappings from XML file
+  */
+static bool loadMappings(Config* this) {
 
     bool ret = true;
@@ -634,5 +658,5 @@
     psStringAppend(&path, "%s/map.xml", this->configsPath);
 
-    xmlDoc* doc = ippToPspsConfig_openXmlFile(path);
+    xmlDoc* doc = openXmlFile(path);
 
     if (doc == NULL) {
@@ -657,16 +681,18 @@
             if (strcmp((const char*)node->name, "table")==0) {
 
-                if (!ippToPspsConfig_loadTableMappings(this, node)) ret = false;
+                if (!loadTableMappings(this, node)) ret = false;
             }
         }
     }
 
-    ippToPspsConfig_closeXmlFile(doc);
+    closeXmlFile(doc);
 
     return ret;
 }
 
-// populate the provided table with data from XML
-bool ippToPspsConfig_populateFromFile(IppToPspsConfig* this, fitsfile *fitsOut) {
+/**
+  Populate the provided table with data from XML
+  */
+static bool populateFromFile(Config* this, Fits *fitsOut) {
 
     bool ret = true;
@@ -674,5 +700,5 @@
     psStringAppend(&path, "%s/data.xml", this->configsPath);
 
-    xmlDoc* doc = ippToPspsConfig_openXmlFile(path);
+    xmlDoc* doc = openXmlFile(path);
 
     if (doc == NULL) {
@@ -690,69 +716,21 @@
 
     for (uint32_t i=0; i<this->numOfTables; i++) {
-        if (!ippToPspsConfig_populateTable(this->tables+i, rootElement, fitsOut)) {
+        if (!populateTable(this->tables+i, rootElement, fitsOut)) {
             ret = false;
         }
     }
 
-    ippToPspsConfig_closeXmlFile(doc);
+    closeXmlFile(doc);
 
     return ret;
 }
 
-// gets the contents of a FITS column vector
-bool ippToPspsConfig_getColumnVector(
-        int col,
-        long row,
-        int type,
-        int repeat,
-        float* vector,
-        fitsfile *fitsIn) {
-
-    int status = 0;
-    int anynull = 0;  
-
-    fits_read_col(fitsIn, type, col, row, 1, repeat, NULL, vector, &anynull, &status);
-
-    if (status) {
-        psError(PS_ERR_IO, false, "Failed to read vector column %d row %ld", col, row);
-        return false;
-    }
-
-    int i;
-    for(i=0; i<repeat; i++) printf("JJJ %f\n", vector[i]);
-
-    return true;
-}
-
-// gets metadata about a column 
-bool ippToPspsConfig_getFitsColumnMeta(
-        char* name,
-        int* colNum,
-        int* type,
-        long* repeat,
-        fitsfile *fitsIn) {
-
-    int status = 0;
-    fits_get_colnum(fitsIn, CASESEN, name, colNum, &status);
-    if (status) {
-        psError(PS_ERR_IO, false, "Unable to read col '%s'", name);
-        return false;
-    }
-
-    status = 0;
-    fits_get_eqcoltype(fitsIn, *colNum, type, repeat, NULL, &status);
-    if (status) {
-        psError(PS_ERR_IO, false, "Unable to read type info for '%s'", name);
-        return false;
-    }
-
-    return true;
-}
-
-// populate with data from another FITS table into this one
-static bool ippToPspsConfig_populateTableFromFits(
-        IppToPspsConfig_Table* table,
-        fitsfile *fitsIn, 
-        fitsfile *fitsOut,
+/**
+  Populate with data from another FITS table into this one
+  */
+static bool populateTableFromFits(
+        Table* table,
+        Fits *fitsIn, 
+        Fits *fitsOut,
         const long nRows, 
         const bool fromHeader) {
@@ -770,14 +748,4 @@
         strcol[i] = (char*)calloc(50,sizeof(char)); // TODO 20? size issue
 
-    int8_t int8null = -99;
-    int16_t int16null = -999;
-    long longnull = -999;
-    float floatnull = -999.0;
-    double doublenull = -999.0;
-
-    int anynull = 0;
-    int readStatus = 0;
-    int writeStatus = 0;
-
     // first loop round all columns and get IPP col numbers for provided column names TODO only do once, first time in
     if(!fromHeader) {
@@ -787,13 +755,6 @@
             if (strlen(table->columns[i].ippName) < 1) continue;
 
-int dummy;
-
-            if (!ippToPspsConfig_getFitsColumnMeta(
-                        table->columns[i].ippName,
-                        &table->columns[i].ippColNum,
-          //              &table->columns[i].ippType,  TODO getting wrong type for some reason
-                        &dummy,
-                        &table->columns[i].ippRepeat,
-                        fitsIn)) {return false;}
+            int dummy; // TODO getting wrong type for some reason
+            if (!fitsIn->getColumnMeta(fitsIn, table->columns[i].ippName, &table->columns[i].ippColNum, &dummy, &table->columns[i].ippRepeat)) return false;
         }
     }
@@ -802,7 +763,4 @@
     for (uint32_t i=0; i<table->numOfColumns; i++) {
 
-        readStatus = 0;
-        writeStatus = 0;
-
         if (!table->columns[i].usingDefault) {
 
@@ -813,54 +771,48 @@
                 case TBYTE:
                     if (fromHeader) {
-                        fits_read_key(fitsIn, TBYTE, table->columns[i].ippName, &int8col[0], NULL, &readStatus);
-                        if (!isfinite(int8col[0])) int8col[0] = int8null;
+                        fitsIn->getHeaderKeyValue(fitsIn, TBYTE, table->columns[i].ippName, &int8col[0]); 
                     }
-                    else fits_read_col(fitsIn, TBYTE, table->columns[i].ippColNum, 1, 1, nRows, &int8null, int8col, &anynull, &readStatus);
-                    fits_write_col(fitsOut, TBYTE, col, 1, 1, nRows, int8col, &writeStatus);
+                    else fitsIn->readColumn(fitsIn, TBYTE, table->columns[i].ippColNum, 1, 1, nRows, int8col);
+                    fitsOut->writeColumn(fitsOut, TBYTE, col, 1, 1, nRows, int8col);
                     break;
                 case TSHORT:
                     if (fromHeader) {
-                        fits_read_key(fitsIn, TSHORT, table->columns[i].ippName, &int16col[0], NULL, &readStatus);
-                        if (!isfinite(int16col[0])) int16col[0] = int16null;
+                        fitsIn->getHeaderKeyValue(fitsIn, TSHORT, table->columns[i].ippName, &int16col[0]);
                     }
-                    else fits_read_col(fitsIn, TSHORT, table->columns[i].ippColNum, 1, 1, nRows, &int16null, int16col, &anynull, &readStatus);
-                    fits_write_col(fitsOut, TSHORT, col, 1, 1, nRows, int16col, &writeStatus);
+                    else fitsIn->readColumn(fitsIn, TSHORT, table->columns[i].ippColNum, 1, 1, nRows, int16col);
+                    fitsOut->writeColumn(fitsOut, TSHORT, col, 1, 1, nRows, int16col);
                     break;
                 case TLONG:
                     if (fromHeader) {
-                        fits_read_key(fitsIn, TLONG, table->columns[i].ippName, &longcol[0], NULL, &readStatus);
-                        if (!isfinite(longcol[0])) longcol[0] = longnull;
+                        fitsIn->getHeaderKeyValue(fitsIn, TLONG, table->columns[i].ippName, &longcol[0]);
                     }
-                    else fits_read_col(fitsIn, TLONG, table->columns[i].ippColNum, 1, 1, nRows, &longnull, longcol, &anynull, &readStatus);
-                    fits_write_col(fitsOut, TLONG, col, 1, 1, nRows, longcol, &writeStatus);
+                    else fitsIn->readColumn(fitsIn, TLONG, table->columns[i].ippColNum, 1, 1, nRows, longcol);
+                    fitsOut->writeColumn(fitsOut, TLONG, col, 1, 1, nRows, longcol);
                     break;
                 case TLONGLONG:
                     if (fromHeader) {
-                        fits_read_key(fitsIn, TLONGLONG, table->columns[i].ippName, &longcol[0], NULL, &readStatus);
-                        if (!isfinite(longcol[0])) longcol[0] = longnull;
+                        fitsIn->getHeaderKeyValue(fitsIn, TLONGLONG, table->columns[i].ippName, &longcol[0]);
                     }
-                    else fits_read_col(fitsIn, TLONGLONG, table->columns[i].ippColNum, 1, 1, nRows, &longnull, longcol, &anynull, &readStatus);
-                    fits_write_col(fitsOut, TLONGLONG, col, 1, 1, nRows, longcol, &writeStatus);
+                    else fitsIn->readColumn(fitsIn, TLONGLONG, table->columns[i].ippColNum, 1, 1, nRows, longcol);
+                    fitsOut->writeColumn(fitsOut, TLONGLONG, col, 1, 1, nRows, longcol);
                     break;
                 case TFLOAT:
                     if (fromHeader) {
-                        fits_read_key(fitsIn, TFLOAT, table->columns[i].ippName,&floatcol[0], NULL, &readStatus); 
-                        if (!isfinite(floatcol[0])) floatcol[0] = floatnull;
+                        fitsIn->getHeaderKeyValue(fitsIn, TFLOAT, table->columns[i].ippName,&floatcol[0]); 
                     }
-                    else fits_read_col(fitsIn, TFLOAT, table->columns[i].ippColNum, 1, 1, nRows, &floatnull, floatcol, &anynull, &readStatus);
-                    fits_write_col(fitsOut, TFLOAT, col, 1, 1, nRows, floatcol, &writeStatus);
+                    else fitsIn->readColumn(fitsIn, TFLOAT, table->columns[i].ippColNum, 1, 1, nRows, floatcol);
+                    fitsOut->writeColumn(fitsOut, TFLOAT, col, 1, 1, nRows, floatcol);
                     break;
                 case TDOUBLE:
                     if (fromHeader) {
-                        fits_read_key(fitsIn, TDOUBLE, table->columns[i].ippName, &doublecol[0], NULL, &readStatus);
-                        if (!isfinite(doublecol[0])) doublecol[0] = doublenull;
+                        fitsIn->getHeaderKeyValue(fitsIn, TDOUBLE, table->columns[i].ippName, &doublecol[0]);
                     }
-                    else fits_read_col(fitsIn, TDOUBLE, table->columns[i].ippColNum, 1, 1, nRows, &doublenull, doublecol, &anynull, &readStatus);
-                    fits_write_col(fitsOut, TDOUBLE, col, 1, 1, nRows, doublecol, &writeStatus);
+                    else fitsIn->readColumn(fitsIn, TDOUBLE, table->columns[i].ippColNum, 1, 1, nRows, doublecol);
+                    fitsOut->writeColumn(fitsOut, TDOUBLE, col, 1, 1, nRows, doublecol);
                     break;
                 case TSTRING:
-                    if (fromHeader) fits_read_key(fitsIn, TSTRING, table->columns[i].ippName, strcol[0], NULL, &readStatus);
-                    else fits_read_col(fitsIn, TSTRING, table->columns[i].ippColNum, 1, 1, nRows, " ", strcol, &anynull, &readStatus);
-                    fits_write_col(fitsOut, TSTRING, col, 1, 1, nRows, strcol, &writeStatus);
+                    if (fromHeader) fitsIn->getHeaderKeyValue(fitsIn, TSTRING, table->columns[i].ippName, strcol[0]);
+                    else fitsIn->readColumn(fitsIn, TSTRING, table->columns[i].ippColNum, 1, 1, nRows, strcol);
+                    fitsOut->writeColumn(fitsOut, TSTRING, col, 1, 1, nRows, strcol);
                     break;
 
@@ -870,15 +822,4 @@
                     break;
             }
-
-            // TODO need these errors, but strange error handling runs out of memory
-            //if (readStatus) {
-            //    psError(PS_ERR_IO, false, "Unable to read col num '%d' col name '%s', type %d", 
-            //        table->columns[i].ippColNum, table->columns[i].ippName, table->columns[i].ippType);
-            //    fits_report_error(stderr, readStatus);
-            //}
-            //if (writeStatus) {
-            //    psError(PS_ERR_IO, false, "Unable to write col '%s'", table->columns[i].pspsName );
-            //    fits_report_error(stderr, writeStatus);
-            //}
         }
     }
@@ -895,23 +836,26 @@
 }
 
-// creates and populates a table
-bool ippToPspsConfig_writeTable(
-        IppToPspsConfig* this, 
-        fitsfile* fitsIn,
-        fitsfile* fitsOut,
+/**
+  Creates and populates a table
+  */
+static bool createAndPopulateTable(
+        Config* this, 
+        Fits* fitsIn,
+        Fits* fitsOut,
         const long nRows,
         const char* tableName,
-        const bool fromHeader) {
-
-
-    IppToPspsConfig_Table* table = ippToPspsConfig_findTable(this, tableName);
+        const int fromHeader) {
+
+    Table* table = findTable(this, tableName);
     if (!table) return false;
 
-    if(!ippToPspsConfig_createTable(table, fitsOut, nRows)) return false;
-    return ippToPspsConfig_populateTableFromFits(table, fitsIn, fitsOut, nRows, fromHeader);
-}
-
-// Destructor.
-void ippToPspsConfig_Destructor(IppToPspsConfig* this) {
+    if(!createTable(table, fitsOut, nRows)) return false;
+    return populateTableFromFits(table, fitsIn, fitsOut, nRows, fromHeader);
+}
+
+/**
+  Destructor.
+  */
+static void destroy(Config* this) {
 
     if (this != NULL ) {
@@ -927,19 +871,26 @@
 }
 
-// Constructor. Loads IPP -> PSPS mappings from a csv
-IppToPspsConfig* ippToPspsConfig_Constructor(const char* path) {
-
-    IppToPspsConfig* this = NULL;
-
-    this = malloc(sizeof(IppToPspsConfig));
+/**
+  Constructor. Loads IPP -> PSPS mappings from an XML file
+  */
+Config* new_Config(const char* path) {
+
+    Config* this = (Config*)calloc(1, sizeof(Config));
 
     this->configsPath = NULL;
     psStringAppend(&this->configsPath, path);
 
+    // method pointers
+    this->getFilterId = getFilterId;
+    this->getSurveyId = getSurveyId;
+    this->createAndPopulateTable = createAndPopulateTable;
+    this->populateFromFile = populateFromFile;
+    this->destroy = destroy;
+
     // load tables descriptions from XML
-    if (ippToPspsConfig_loadTableDescriptions(this)) {
+    if (loadTableDescriptions(this)) {
 
         // load any mappings we may have from XML        
-        ippToPspsConfig_loadMappings(this);
+        loadMappings(this);
     }
 
Index: /trunk/ippToPsps/src/Config.h
===================================================================
--- /trunk/ippToPsps/src/Config.h	(revision 31010)
+++ /trunk/ippToPsps/src/Config.h	(revision 31011)
@@ -1,3 +1,3 @@
-/** @file ippToPspsConfig.h
+/** @file Config.h
  *
  *  @brief ippToPsps
@@ -9,11 +9,11 @@
  */
 
-#ifndef IPPTOPSPSCONFIG_H
-#define IPPTOPSPSCONFIG_H
+#ifndef IPPTOPSPS_CONFIG_H
+#define IPPTOPSPS_CONFIG_H
 
 #include <psmodules.h>
 
 // column class
-typedef struct {
+typedef struct Column {
 
     int ippColNum;
@@ -27,56 +27,41 @@
     char comment[300];
 
-} IppToPspsConfig_Column;
+} Column;
 
 // table class
-typedef struct {
+typedef struct Table {
 
     char name[32];
-    IppToPspsConfig_Column* columns;
+    Column* columns;
     int numOfColumns;
 
-} IppToPspsConfig_Table;
+} Table;
 
-//  
-typedef struct {
+/**
 
+  Class encapsulating ... TODO
+
+*/
+typedef struct Config {
+
+    // fields
     psString configsPath;
-    IppToPspsConfig_Table* tables;
+    Table* tables;
     int numOfTables;
 
-} IppToPspsConfig; 
+    // methods
+    bool (*getFilterId)();
+    bool (*getSurveyId)();
+    bool (*createAndPopulateTable)();
+    bool (*populateFromFile)();
 
-IppToPspsConfig* ippToPspsConfig_Constructor(const char* filePath);
-bool ippToPspsConfig_populateFromFile(IppToPspsConfig* this, fitsfile *fitsOut); // TODO remove
-void ippToPspsConfig_Destructor();
-bool ippToPspsConfig_writeTable(
-        IppToPspsConfig* this,
-        fitsfile* fitsIn,
-        fitsfile* fitsOut,
-        const long nRows,
-        const char* tableName, 
-        const bool fromHeader);
+    // destructor
+    void (*destroy)();
 
-// getters
-bool ippToPspsConfig_getFilterId(IppToPspsConfig* this, const char* filterType, int8_t* filterId);
-bool ippToPspsConfig_getSurveyId(IppToPspsConfig* this, const char* surveyType, int8_t* surveyId);
-bool ippToPspsConfig_getFitsColumnMeta(
-        char* name,
-        int* colNum,
-        int* type,
-        long* repeat,
-        fitsfile *fitsIn);
+} Config; 
 
-bool ippToPspsConfig_getColumnVector(
-        int col,
-        long row,
-        int type,
-        int repeat,
-        float* vector,
-        fitsfile *fitsIn);
+// constructor
+Config* new_Config(const char* path);
 
+#endif // IPPTOPSPS_CONFIG_H
 
-
-
-#endif // IPPTOPSPSCONFIG_H
-
Index: /trunk/ippToPsps/src/DetectionBatch.c
===================================================================
--- /trunk/ippToPsps/src/DetectionBatch.c	(revision 31010)
+++ /trunk/ippToPsps/src/DetectionBatch.c	(revision 31011)
@@ -1,4 +1,7 @@
 #include "DetectionBatch.h"
 #include "DetectionBatchEnums.h"
+
+#include "Fits.h"
+
 
 /**
@@ -43,53 +46,38 @@
     if (this->base.exitCode != PS_EXIT_SUCCESS) return this->base.exitCode;
 
-    int status = 0;
-    fitsfile *fitsIn;
-
-    if (fits_open_file(&fitsIn, this->base.inputFiles[0], READONLY, &status)) {
-
-        fits_report_error(stderr, status);
-        return PS_EXIT_SYS_ERROR;
-    }
-
-    // get primary header and pull stuff out for later
-    int nKeys;
-    fits_get_hdrspace(fitsIn, &nKeys, NULL, &status);
-
-    float zptObs, exposureTime;
-    char filterType[20];
-    double obsTime;
-    double expStart;
-    double expTime;
-    status=0; fits_read_key(fitsIn, TFLOAT, "ZPT_OBS", &zptObs, NULL, &status);
-    status=0; fits_read_key(fitsIn, TFLOAT, "EXPREQ", &exposureTime, NULL, &status);
-    status=0; fits_read_key(fitsIn, TSTRING, "FILTERID", filterType, NULL, &status);
-    status=0; fits_read_key(fitsIn, TDOUBLE, "MJD-OBS", &expStart, NULL, &status);
-    status=0; fits_read_key(fitsIn, TDOUBLE, "EXPTIME", &expTime, NULL, &status);
-    obsTime = expStart + (expTime/172800.0); // exp start plus half exp time (converted from secs to days)
-
-    ippToPspsConfig_writeTable(this->base.config, fitsIn, this->base.fitsOut, 1, "FrameMeta", true);
+    // open input FITS file
+    Fits* fitsIn = existing_Fits(this->base.inputFiles[0]);
+    if (fitsIn->getFilePtr(fitsIn) == NULL)  return PS_EXIT_SYS_ERROR;
+
+    // read some header values 
+    float zptObs; fitsIn->getHeaderKeyValue(fitsIn, TFLOAT, "ZPT_OBS", &zptObs);
+    float exposureTime; fitsIn->getHeaderKeyValue(fitsIn, TFLOAT, "EXPREQ", &exposureTime);
+    char filterType[20]; fitsIn->getHeaderKeyValue(fitsIn, TSTRING, "FILTERID", filterType);
+    double expStart; fitsIn->getHeaderKeyValue(fitsIn, TDOUBLE, "MJD-OBS", &expStart); 
+    double expTime; fitsIn->getHeaderKeyValue(fitsIn, TDOUBLE, "EXPTIME", &expTime);
+    double obsTime = expStart + (expTime/172800.0); // exp start plus half exp time (converted from secs to days)
+
+
+    this->base.config->createAndPopulateTable(this->base.config, fitsIn, this->base.fitsOut, 1, "FrameMeta", true);
 
     // FrameMeta values
-    fits_write_col(this->base.fitsOut, TLONG, FRAMEMETA_FRAMEID, 1, 1, 1, &this->expId, &status);
-    fits_write_col(this->base.fitsOut, TSTRING, FRAMEMETA_FRAMENAME, 1, 1, 1, &(this->expName), &status);
-    fits_write_col(this->base.fitsOut, TBYTE, FRAMEMETA_SURVEYID, 1, 1, 1, &this->base.surveyID, &status);
-
     int8_t filterID = -1;
-    if (!ippToPspsConfig_getFilterId(this->base.config, filterType, &filterID)) {
+    if (!this->base.config->getFilterId(this->base.config, filterType, &filterID)) {
     
         this->base.exitCode = PS_EXIT_DATA_ERROR;
         return this->base.exitCode;
     }
-
-    fits_write_col(this->base.fitsOut, TBYTE, FRAMEMETA_FILTERID, 1, 1, 1, &filterID, &status);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TBYTE, FRAMEMETA_FILTERID, 1, 1, 1, &filterID);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TLONG, FRAMEMETA_FRAMEID, 1, 1, 1, &this->expId);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TSTRING, FRAMEMETA_FRAMENAME, 1, 1, 1, &(this->expName));
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TBYTE, FRAMEMETA_SURVEYID, 1, 1, 1, &this->base.surveyID);
+
 
     int16_t cameraID = 1; // TODO
-    fits_write_col(this->base.fitsOut, TSHORT, FRAMEMETA_CAMERAID, 1, 1, 1, &cameraID, &status);
-
     int16_t cameraConfigID = 1; // TODO
-    fits_write_col(this->base.fitsOut, TSHORT, FRAMEMETA_CAMERACONFIGID, 1, 1, 1, &cameraConfigID, &status);
-
     int16_t telescopeID = 1; // TODO
-    fits_write_col(this->base.fitsOut, TSHORT, FRAMEMETA_TELESCOPEID, 1, 1, 1, &telescopeID, &status);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TSHORT, FRAMEMETA_CAMERAID, 1, 1, 1, &cameraID);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TSHORT, FRAMEMETA_CAMERACONFIGID, 1, 1, 1, &cameraConfigID);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TSHORT, FRAMEMETA_TELESCOPEID, 1, 1, 1, &telescopeID);
 
     // stuff to keep from psf.hdr header
@@ -107,10 +95,6 @@
     uint32_t s,d, invalidDvoRows, nChipDetectionsOut = 0;
 
-    long longnull = -999;
-    float floatnull = -999.0;
-    int anynull = 0;
-
     char ccdNumber[3], extensionName[15];
-    // for storing FITS column data
+    // for storing FITS column data - do this once, to avoid expension free/calloc for each chip
     long* ippIDet = (long*)calloc(this->MAXDETECT, sizeof(long));
     float* instMag = (float*)calloc(this->MAXDETECT, sizeof(float));
@@ -129,5 +113,4 @@
     int8_t* filterIDs = (int8_t*)calloc(this->MAXDETECT, sizeof(int8_t));
     int8_t* surveyIDs = (int8_t*)calloc(this->MAXDETECT, sizeof(int8_t));
-
     char** assocDate = (char**)calloc(this->MAXDETECT, sizeof(char*));
     for (uint32_t i=0; i<this->MAXDETECT;i++) assocDate[i] = (char*)malloc(20*sizeof(char));
@@ -186,21 +169,22 @@
             sprintf(ccdNumber, "%d%d", x, y);
 
-            // check we can move to detections table in smf
+            // check we CAN move to detections table in smf
             sprintf(extensionName, "XY%s.psf", ccdNumber);
-            if (!ippToPspsConfig_moveToExtensionTable(fitsIn, extensionName)) continue;
+            if (!fitsIn->moveToBinaryTable(fitsIn, extensionName)) continue;
 
             // move to header extension
             sprintf(extensionName, "XY%s.hdr", ccdNumber);
-            if (!ippToPspsConfig_moveToExtensionHeader(fitsIn, extensionName)) continue;
+            if (!fitsIn->moveToHeader(fitsIn, 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, TLONG, "IMAGEID", &imageId, NULL, &status);
-            status=0; fits_read_key(fitsIn, TLONG, "SOURCEID", &sourceId, NULL, &status);
-            status=0; fits_read_key(fitsIn, TLONG, "NASTRO", &numPhotoRef, NULL, &status);
+            fitsIn->getHeaderKeyValue(fitsIn, TFLOAT, "FWHM_MAJ", &fwhmMaj);
+            fitsIn->getHeaderKeyValue(fitsIn, TFLOAT, "FWHM_MIN", &fwhmMin);
+            fitsIn->getHeaderKeyValue(fitsIn, TFLOAT, "IQ_FW1", &momentMaj);
+            fitsIn->getHeaderKeyValue(fitsIn, TFLOAT, "IQ_FW2", &momentMin);
+            fitsIn->getHeaderKeyValue(fitsIn, TLONG, "IMAGEID", &imageId);
+            fitsIn->getHeaderKeyValue(fitsIn, TLONG, "SOURCEID", &sourceId);
+            fitsIn->getHeaderKeyValue(fitsIn, TLONG, "NASTRO", &numPhotoRef);
             totalNumPhotoRef += numPhotoRef; // total up for storing in FrameMeta
+
             // access DVO database
             skylist = dvoSkyListByExternID(this->base.dvoConfig, sourceId, imageId, &image);
@@ -221,6 +205,5 @@
             if (numDvoDetections > this->MAXDETECT ) {
 
-                psError(PS_ERR_IO, false,
-                        " Number of detections (%d) exceeds max limit (%ld)\n",
+                psError(PS_ERR_IO, false, " Number of detections (%d) exceeds max limit (%ld)\n",
                         numDvoDetections, this->MAXDETECT);
                 error = true;
@@ -229,54 +212,46 @@
 
             // create ImageMeta
-            ippToPspsConfig_writeTable(this->base.config, fitsIn, this->base.fitsOut, 1, "ImageMeta", true);
+            this->base.config->createAndPopulateTable(this->base.config, fitsIn, this->base.fitsOut, 1, "ImageMeta", true);
             psfFwhm = (fwhmMaj+fwhmMin)/2;
             momentFwhm = (momentMaj+momentMin)/2;
             imageFlags = (uint64_t)image->flags;
-            fits_write_col(this->base.fitsOut, TLONGLONG, IMAGEMETA_IMAGEID, 1, 1, 1, &pspsImageId, &status);
-            fits_write_col(this->base.fitsOut, TLONG, IMAGEMETA_FRAMEID, 1, 1, 1, &this->expId, &status);
-            fits_write_col(this->base.fitsOut, TSHORT, IMAGEMETA_CCDID, 1, 1, 1, &image->ccdnum, &status);
-            fits_write_col(this->base.fitsOut, TLONG, IMAGEMETA_PHOTOCALID, 1, 1, 1, &image->photcode, &status);
-            fits_write_col(this->base.fitsOut, TBYTE, IMAGEMETA_FILTERID, 1, 1, 1, &filterID, &status);
-            fits_write_col(this->base.fitsOut, TFLOAT, IMAGEMETA_PHOTOSCAT, 1, 1, 1, &zptObs, &status);
-            fits_write_col(this->base.fitsOut, TFLOAT, IMAGEMETA_PSFFWHM, 1, 1, 1, &psfFwhm, &status);
-            fits_write_col(this->base.fitsOut, TFLOAT, IMAGEMETA_MOMENTFWHM, 1, 1, 1, &momentFwhm, &status);
-            fits_write_col(this->base.fitsOut, TFLOAT, IMAGEMETA_PHOTOZERO, 1, 1, 1, &zptObs, &status);
-            fits_write_col(this->base.fitsOut, TLONGLONG, IMAGEMETA_QAFLAGS, 1, 1, 1, &imageFlags, &status);
+            this->base.fitsOut->writeColumn(this->base.fitsOut, TLONGLONG, IMAGEMETA_IMAGEID, 1, 1, 1, &pspsImageId);
+            this->base.fitsOut->writeColumn(this->base.fitsOut, TLONG, IMAGEMETA_FRAMEID, 1, 1, 1, &this->expId);
+            this->base.fitsOut->writeColumn(this->base.fitsOut, TSHORT, IMAGEMETA_CCDID, 1, 1, 1, &image->ccdnum);
+            this->base.fitsOut->writeColumn(this->base.fitsOut, TLONG, IMAGEMETA_PHOTOCALID, 1, 1, 1, &image->photcode);
+            this->base.fitsOut->writeColumn(this->base.fitsOut, TBYTE, IMAGEMETA_FILTERID, 1, 1, 1, &filterID);
+            this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, IMAGEMETA_PHOTOSCAT, 1, 1, 1, &zptObs);
+            this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, IMAGEMETA_PSFFWHM, 1, 1, 1, &psfFwhm);
+            this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, IMAGEMETA_MOMENTFWHM, 1, 1, 1, &momentFwhm);
+            this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, IMAGEMETA_PHOTOZERO, 1, 1, 1, &zptObs);
+            this->base.fitsOut->writeColumn(this->base.fitsOut, TLONGLONG, IMAGEMETA_QAFLAGS, 1, 1, 1, &imageFlags);
 
             // now move BACK to detections table in smf
             sprintf(extensionName, "XY%s.psf", ccdNumber);
-            if (!ippToPspsConfig_moveToExtensionTable(fitsIn, extensionName)) continue;
-
-            // keep a running count of 'images' we find in order to write total to FrameMeta at the end
+            long nChipDetectionsIn = 0;
+            if (!fitsIn->moveToBinaryTableAndCountRows(fitsIn, extensionName, &nChipDetectionsIn)) continue;
+
+            // keep a running count of 'images' we find in order to write total into FrameMeta at the end
             nOta++;
-
-            long nChipDetectionsIn = 0;
-            if (fits_get_num_rows(fitsIn, &nChipDetectionsIn, &status)) {
-                fits_report_error(stderr, status);
-            }
 
             // loop round detections to populate some extra stuff
             s = d = nChipDetectionsOut = invalidDvoRows = 0;
 
-            // determine column numbers for certain IPP detection columns
+            // determine column numbers for certain IPP detection columns - do this only once to save time
             if (firstTimeIn) {
 
-                status=0;fits_get_colnum(fitsIn, CASESEN, "IPP_IDET", &ippIDetNum, &status);
-                if (status) psError(PS_ERR_IO, false, "Unable to read col num for IPP_IDET");
-                status=0;fits_get_colnum(fitsIn, CASESEN, "PSF_INST_MAG", &instMagNum, &status);
-                if (status) psError(PS_ERR_IO, false, "Unable to read col num for PSF_INST_MAG");
-                status=0;fits_get_colnum(fitsIn, CASESEN, "PSF_INST_MAG_SIG", &instMagErrNum, &status);
-                if (status) psError(PS_ERR_IO, false, "Unable to read col num for PSF_INST_MAG_SIG");
-                status=0;fits_get_colnum(fitsIn, CASESEN, "PEAK_FLUX_AS_MAG", &peakMagNum, &status);
-                if (status) psError(PS_ERR_IO, false, "Unable to read col num for PEAK_FLUX_AS_MAG");
+                fitsIn->getColumnNumber(fitsIn, "IPP_IDET", &ippIDetNum);
+                fitsIn->getColumnNumber(fitsIn, "PSF_INST_MAG", &instMagNum);
+                fitsIn->getColumnNumber(fitsIn, "PSF_INST_MAG_SIG", &instMagErrNum);
+                fitsIn->getColumnNumber(fitsIn, "PEAK_FLUX_AS_MAG", &peakMagNum);
 
                 firstTimeIn=false;
             }
 
-            anynull = 0;
-            fits_read_col(fitsIn, TLONG, ippIDetNum, 1, 1, nChipDetectionsIn, &longnull, ippIDet, &anynull, &status);
-            fits_read_col(fitsIn, TFLOAT, instMagNum, 1, 1, nChipDetectionsIn, &floatnull, instMag, &anynull, &status);
-            fits_read_col(fitsIn, TFLOAT, instMagErrNum, 1, 1, nChipDetectionsIn, &floatnull, instMagErr, &anynull, &status);
-            fits_read_col(fitsIn, TFLOAT, peakMagNum, 1, 1, nChipDetectionsIn, &floatnull, peakMag, &anynull, &status);
+            //anynull = 0;
+            fitsIn->readColumn(fitsIn, TLONG, ippIDetNum, 1, 1, nChipDetectionsIn, ippIDet);
+            fitsIn->readColumn(fitsIn, TFLOAT, instMagNum, 1, 1, nChipDetectionsIn, instMag);
+            fitsIn->readColumn(fitsIn, TFLOAT, instMagErrNum, 1, 1, nChipDetectionsIn, instMagErr);
+            fitsIn->readColumn(fitsIn, TFLOAT, peakMagNum, 1, 1, nChipDetectionsIn, peakMag);
 
             // DVO detections are ordered by IPP_IDET, which increments from 0 in SMF table
@@ -288,4 +263,5 @@
             // loop through all detections in smf file extension for this chip.
             // there are three ways for a detection to be ommitted from the final FITS output:
+            //
             // 1. it has a duplicate obj ID to a previous detection
             // 2. it has an invalid detection ID (< 0 or > max DVO det ID)
@@ -355,5 +331,5 @@
 
             // write number of rows (detections) to ImageMeta
-            fits_write_col(this->base.fitsOut, TLONG, IMAGEMETA_NDETECT, 1, 1, 1, &nChipDetectionsOut, &status);
+            this->base.fitsOut->writeColumn(this->base.fitsOut, TLONG, IMAGEMETA_NDETECT, 1, 1, 1, &nChipDetectionsOut);
 
             int totalRemove = numOfDuplicates+numInvalidFlux+numOfInvalidIppIDet;
@@ -363,36 +339,33 @@
 
                 // detections
-                ippToPspsConfig_writeTable(this->base.config, fitsIn, this->base.fitsOut, nChipDetectionsIn, "Detection", false);
-                fits_write_col(this->base.fitsOut, TLONGLONG, DETECTION_OBJID, 1, 1, nChipDetectionsIn, objID, &status);
-                fits_write_col(this->base.fitsOut, TLONGLONG, DETECTION_DETECTID, 1, 1, nChipDetectionsIn, detectID, &status);
-                fits_write_col(this->base.fitsOut, TLONGLONG, DETECTION_IPPOBJID, 1, 1, nChipDetectionsIn, ippObjID, &status);
-                fits_write_col(this->base.fitsOut, TLONGLONG, DETECTION_IPPDETECTID, 1, 1, nChipDetectionsIn, ippDetectID, &status);
-                fits_write_col(this->base.fitsOut, TBYTE, DETECTION_FILTERID, 1, 1, nChipDetectionsIn, filterIDs, &status);
-                fits_write_col(this->base.fitsOut, TBYTE, DETECTION_SURVEYID, 1, 1, nChipDetectionsIn, surveyIDs, &status);
-                fits_write_col(this->base.fitsOut, TLONGLONG, DETECTION_IMAGEID, 1, 1, nChipDetectionsIn, imageID, &status);
-                fits_write_col(this->base.fitsOut, TDOUBLE, DETECTION_OBSTIME, 1, 1, nChipDetectionsIn, obsTimes, &status);
-                fits_write_col(this->base.fitsOut, TFLOAT, DETECTION_INSTFLUX, 1, 1, nChipDetectionsIn, instFlux, &status);
-                fits_write_col(this->base.fitsOut, TFLOAT, DETECTION_INSTFLUXERR, 1, 1, nChipDetectionsIn, instFluxErr, &status);
-                fits_write_col(this->base.fitsOut, TFLOAT, DETECTION_PEAKADU, 1, 1, nChipDetectionsIn, peakFlux, &status);
-                fits_write_col(this->base.fitsOut, TSTRING, DETECTION_ASSOCDATE, 1, 1, nChipDetectionsIn, assocDate, &status);
-                fits_write_col(this->base.fitsOut, TLONGLONG, DETECTION_INFOFLAG, 1, 1, nChipDetectionsIn, flags, &status);
-                if (numOfDuplicates||numInvalidFlux||numOfInvalidIppIDet) 
-                    fits_delete_rowlist(this->base.fitsOut, removeList, totalRemove, &status);
+                this->base.config->createAndPopulateTable(this->base.config, fitsIn, this->base.fitsOut, nChipDetectionsIn, "Detection", false);
+                this->base.fitsOut->writeColumn(this->base.fitsOut, TLONGLONG, DETECTION_OBJID, 1, 1, nChipDetectionsIn, objID);
+                this->base.fitsOut->writeColumn(this->base.fitsOut, TLONGLONG, DETECTION_DETECTID, 1, 1, nChipDetectionsIn, detectID);
+                this->base.fitsOut->writeColumn(this->base.fitsOut, TLONGLONG, DETECTION_IPPOBJID, 1, 1, nChipDetectionsIn, ippObjID);
+                this->base.fitsOut->writeColumn(this->base.fitsOut, TLONGLONG, DETECTION_IPPDETECTID, 1, 1, nChipDetectionsIn, ippDetectID);
+                this->base.fitsOut->writeColumn(this->base.fitsOut, TBYTE, DETECTION_FILTERID, 1, 1, nChipDetectionsIn, filterIDs);
+                this->base.fitsOut->writeColumn(this->base.fitsOut, TBYTE, DETECTION_SURVEYID, 1, 1, nChipDetectionsIn, surveyIDs);
+                this->base.fitsOut->writeColumn(this->base.fitsOut, TLONGLONG, DETECTION_IMAGEID, 1, 1, nChipDetectionsIn, imageID);
+                this->base.fitsOut->writeColumn(this->base.fitsOut, TDOUBLE, DETECTION_OBSTIME, 1, 1, nChipDetectionsIn, obsTimes);
+                this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, DETECTION_INSTFLUX, 1, 1, nChipDetectionsIn, instFlux);
+                this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, DETECTION_INSTFLUXERR, 1, 1, nChipDetectionsIn, instFluxErr);
+                this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, DETECTION_PEAKADU, 1, 1, nChipDetectionsIn, peakFlux);
+                this->base.fitsOut->writeColumn(this->base.fitsOut, TSTRING, DETECTION_ASSOCDATE, 1, 1, nChipDetectionsIn, assocDate);
+                this->base.fitsOut->writeColumn(this->base.fitsOut, TLONGLONG, DETECTION_INFOFLAG, 1, 1, nChipDetectionsIn, flags);
+                if (numOfDuplicates||numInvalidFlux||numOfInvalidIppIDet) this->base.fitsOut->deleteRows(this->base.fitsOut, removeList, totalRemove); 
 
                 // skinny object
-                ippToPspsConfig_writeTable(this->base.config, fitsIn, this->base.fitsOut, nChipDetectionsIn, "SkinnyObject", false);
-                fits_write_col(this->base.fitsOut, TLONGLONG, SKINNYOBJECT_OBJID, 1, 1, nChipDetectionsIn, objID, &status);
-                fits_write_col(this->base.fitsOut, TLONGLONG, SKINNYOBJECT_IPPOBJID, 1, 1, nChipDetectionsIn, ippObjID, &status);
-                fits_write_col(this->base.fitsOut, TBYTE, SKINNYOBJECT_SURVEYID, 1, 1, nChipDetectionsIn, surveyIDs, &status);
-                if (numOfDuplicates||numInvalidFlux||numOfInvalidIppIDet)
-                    fits_delete_rowlist(this->base.fitsOut, removeList, totalRemove, &status);
+                this->base.config->createAndPopulateTable(this->base.config, fitsIn, this->base.fitsOut, nChipDetectionsIn, "SkinnyObject", false);
+                this->base.fitsOut->writeColumn(this->base.fitsOut, TLONGLONG, SKINNYOBJECT_OBJID, 1, 1, nChipDetectionsIn, objID);
+                this->base.fitsOut->writeColumn(this->base.fitsOut, TLONGLONG, SKINNYOBJECT_IPPOBJID, 1, 1, nChipDetectionsIn, ippObjID);
+                this->base.fitsOut->writeColumn(this->base.fitsOut, TBYTE, SKINNYOBJECT_SURVEYID, 1, 1, nChipDetectionsIn, surveyIDs);
+                if (numOfDuplicates||numInvalidFlux||numOfInvalidIppIDet) this->base.fitsOut->deleteRows(this->base.fitsOut, removeList, totalRemove);
 
                 // object calibration color
-                ippToPspsConfig_writeTable(this->base.config, fitsIn, this->base.fitsOut, nChipDetectionsIn, "ObjectCalColor", false);
-                fits_write_col(this->base.fitsOut, TLONGLONG, OBJECTCALCOLOR_OBJID, 1, 1, nChipDetectionsIn, objID, &status);
-                fits_write_col(this->base.fitsOut, TLONGLONG, OBJECTCALCOLOR_IPPOBJID, 1, 1, nChipDetectionsIn, ippObjID, &status);
-                fits_write_col(this->base.fitsOut, TBYTE, OBJECTCALCOLOR_FILTERID, 1, 1, nChipDetectionsIn, filterIDs, &status);
-                if (numOfDuplicates||numInvalidFlux||numOfInvalidIppIDet)
-                    fits_delete_rowlist(this->base.fitsOut, removeList, totalRemove, &status);
+                this->base.config->createAndPopulateTable(this->base.config, fitsIn, this->base.fitsOut, nChipDetectionsIn, "ObjectCalColor", false);
+                this->base.fitsOut->writeColumn(this->base.fitsOut, TLONGLONG, OBJECTCALCOLOR_OBJID, 1, 1, nChipDetectionsIn, objID);
+                this->base.fitsOut->writeColumn(this->base.fitsOut, TLONGLONG, OBJECTCALCOLOR_IPPOBJID, 1, 1, nChipDetectionsIn, ippObjID);
+                this->base.fitsOut->writeColumn(this->base.fitsOut, TBYTE, OBJECTCALCOLOR_FILTERID, 1, 1, nChipDetectionsIn, filterIDs);
+                if (numOfDuplicates||numInvalidFlux||numOfInvalidIppIDet) this->base.fitsOut->deleteRows(this->base.fitsOut, removeList, totalRemove);
             }
 
@@ -436,12 +409,11 @@
 
     // write number of images we have found into FrameMeta table
-    if (ippToPspsConfig_moveToExtensionTable(this->base.fitsOut, "FrameMeta")) { 
-
-        fits_write_col(this->base.fitsOut, TSHORT, FRAMEMETA_NOTA, 1, 1, 1, &nOta, &status);
-        fits_write_col(this->base.fitsOut, TSHORT, FRAMEMETA_NUMPHOTOREF, 1, 1, 1, &totalNumPhotoRef, &status);
-    }
-
-    status=0;
-    if (fits_close_file(fitsIn, &status)) fits_report_error(stderr, status);
+    if (this->base.fitsOut->moveToBinaryTable(this->base.fitsOut, "FrameMeta")) {
+
+        this->base.fitsOut->writeColumn(this->base.fitsOut, TSHORT, FRAMEMETA_NOTA, 1, 1, 1, &nOta);
+        this->base.fitsOut->writeColumn(this->base.fitsOut, TSHORT, FRAMEMETA_NUMPHOTOREF, 1, 1, 1, &totalNumPhotoRef);
+    }
+
+    fitsIn->destroy(fitsIn);
 
     writeResults(this, minObjID, maxObjID, totalDetectionsOut);
@@ -496,5 +468,7 @@
     }
 
-    this->base.parseArguments(&this->base, argc, argv);
+    char fitsOutFile[40];
+    sprintf(fitsOutFile, "%08d.FITS", this->expId);
+    this->base.parseArguments(&this->base, argc, argv, "/detection", fitsOutFile);
 
     if (
@@ -532,4 +506,5 @@
     this->MAXDETECT = 200000;
 
+    // method pointers
     this->print = print;
     this->destroy = destroy;
@@ -538,10 +513,5 @@
     this->expName = (char*)calloc(100, sizeof(char));
 
-    if (!parseArguments(this, *argc, argv)) { return this; }
-
-    strcat(this->base.configsDir, "/detection");
-    sprintf(this->base.fitsOutFile, "%08d.FITS", this->expId);
-
-    this->base.init(&this->base);
+    parseArguments(this, *argc, argv);
 
     return this;
@@ -565,5 +535,5 @@
     detectionBatch->destroy(detectionBatch);
 
-    double secs = psTimerMark("ippToPsps");
+    double secs = psTimerMark("detectionbatch");
 
     psLogMsg("detectionbatch", 3, "detectionbatch completed %ssuccessfully, with exit code %d, in %.1f %s\n",
Index: /trunk/ippToPsps/src/InitBatch.c
===================================================================
--- /trunk/ippToPsps/src/InitBatch.c	(revision 31010)
+++ /trunk/ippToPsps/src/InitBatch.c	(revision 31011)
@@ -18,5 +18,5 @@
     if (this->base.exitCode != PS_EXIT_SUCCESS) return this->base.exitCode;
 
-    if (!ippToPspsConfig_populateFromFile(this->base.config, this->base.fitsOut)) 
+    if (!this->base.config->populateFromFile(this->base.config, this->base.fitsOut)) 
         this->base.exitCode = PS_EXIT_CONFIG_ERROR;
     else this->base.exitCode = PS_EXIT_SUCCESS;
@@ -34,9 +34,9 @@
 
 /**
-  Reads command-line arguments.  Calls base-calls print method first.
+  Reads command-line arguments.
   */
 static bool parseArguments(InitBatch* this, int argc, char **argv) {
 
-    this->base.parseArguments(&this->base, argc, argv);
+    this->base.parseArguments(&this->base, argc, argv, "/init", "00000000.FITS");
 
     if (
@@ -68,14 +68,10 @@
     }
 
+    // method pointers
     this->print = print;
     this->destroy = destroy;
     this->base.run = run;
 
-    if (!parseArguments(this, *argc, argv)) { return this; }
-
-    strcat(this->base.configsDir, "/init");
-    sprintf(this->base.fitsOutFile, "00000000.FITS");
-
-    this->base.init(&this->base);
+    parseArguments(this, *argc, argv);
 
     return this;
Index: /trunk/ippToPsps/src/InitBatch.h
===================================================================
--- /trunk/ippToPsps/src/InitBatch.h	(revision 31010)
+++ /trunk/ippToPsps/src/InitBatch.h	(revision 31011)
@@ -24,8 +24,11 @@
     // methods
     void (*print)();
+
+    // destructor
     void (*destroy)();
 
 } InitBatch;
 
+// constructor
 InitBatch *new_InitBatch(int *argc, char **argv);
 
Index: /trunk/ippToPsps/src/StackBatch.c
===================================================================
--- /trunk/ippToPsps/src/StackBatch.c	(revision 31010)
+++ /trunk/ippToPsps/src/StackBatch.c	(revision 31011)
@@ -1,4 +1,6 @@
 #include "StackBatch.h"
 #include "StackBatchEnums.h"
+
+#include "Fits.h"
 
 /**
@@ -13,181 +15,96 @@
 
 /**
-  Does the work.
-  */
-static int run(StackBatch* this) {
-
-    if (this->base.exitCode != PS_EXIT_SUCCESS) return this->base.exitCode; 
-
-    int status = 0;
-    fitsfile *fitsIn;
-
-    if (fits_open_file(&fitsIn, this->base.inputFiles[0], READONLY, &status)) {
-
-        fits_report_error(stderr, status);
-        return PS_EXIT_SYS_ERROR;
-    }
-
-
-    // get primary header and pull stuff out for later
-    int nKeys = 0;
-    fits_get_hdrspace(fitsIn, &nKeys, NULL, &status);
-    float exposureTime; status=0; fits_read_key(fitsIn, TFLOAT, "EXPTIME", &exposureTime, NULL, &status);
-    char filterType[20]; status=0; fits_read_key(fitsIn, TSTRING, "FILTER", filterType, NULL, &status);
-
-
+  Creates the StackDetection table
+  */
+static bool createStackDetectionTable(
+        StackBatch* this,
+        Fits* fitsIn,
+        int8_t* filterIDs,
+        int8_t* surveyIDs,
+        long* skycellIDs,
+        float exposureTime
+        ) {
+
+    char extensionName[25];
+    sprintf(extensionName, "SkyChip.psf");
+    long nDet = 0;
+    if (!fitsIn->moveToBinaryTableAndCountRows(fitsIn, extensionName, &nDet)) return false;
+
+
+    // allocate stuff
     char** assocDate = (char**)calloc(this->MAXDETECT, sizeof(char**));
     for (uint32_t i=0; i<this->MAXDETECT;i++) assocDate[i] = (char*)calloc(20,sizeof(char));
-
-
-
-    // write StackMeta
-    ippToPspsConfig_writeTable(this->base.config, fitsIn, this->base.fitsOut, 1, "StackMeta", true);
-    fits_write_col(this->base.fitsOut, TLONG, STACKMETA_SKYCELLID, 1, 1, 1, &this->skycellId, &status);
-
-    int8_t filterID = -1;
-    if (!ippToPspsConfig_getFilterId(this->base.config, filterType, &filterID)) {
-
-//        this->base.exitCode = PS_EXIT_DATA_ERROR;
-    //    return this->base.exitCode; TODO
-    }
-
     long* removeList = (long*)calloc(this->MAXDETECT, sizeof(long));
     float* instMag = (float*)calloc(this->MAXDETECT, sizeof(float));
-    int8_t* filterIDs = (int8_t*)calloc(this->MAXDETECT, sizeof(int8_t));
-    int8_t* surveyIDs = (int8_t*)calloc(this->MAXDETECT, sizeof(int8_t));
-    float floatnull = -999.0;
-    int anynull = 0;
-
-    fits_write_col(this->base.fitsOut, TBYTE, STACKMETA_FILTERID, 1, 1, 1, &filterID, &status);
-
-    fits_write_col(this->base.fitsOut, TBYTE, STACKMETA_SURVEYID, 1, 1, 1, &this->base.surveyID, &status);
-
-
-    // psf detections
-    char extensionName[15];
-    sprintf(extensionName, "Chip.psf");
-    if (fits_movnam_hdu(fitsIn, BINARY_TBL, extensionName, 0, &status)) {
-        psError(PS_ERR_IO, false, "Can't move to extension: %s\n", extensionName);
-
-    }
-    else {
-
-        // some stuff is the same for all detections so we can populate here
-        for (long s = 0; s<this->MAXDETECT; s++) {
-
-            filterIDs[s] = filterID;
-            surveyIDs[s] = this->base.surveyID;
-            strcpy(assocDate[s], this->base.todaysDate);
+    float* peakMag = (float*)calloc(this->MAXDETECT, sizeof(float));
+    float* peakFlux = (float*)calloc(this->MAXDETECT, sizeof(float));
+    int* flags1 = (int*)calloc(this->MAXDETECT, sizeof(int));
+    int* flags2 = (int*)calloc(this->MAXDETECT, sizeof(int));
+    uint64_t* infoFlags = (uint64_t*)calloc(this->MAXDETECT, sizeof(uint64_t));
+
+    bool peakFluxOk;
+
+    // some stuff is the same for all detections so we can populate here
+    for (long s = 0; s<this->MAXDETECT; s++) {
+
+        // if running in test mode, don't use today's date
+        if (this->base.testMode) strcpy(assocDate[s], "2010-01-01");
+        else strcpy(assocDate[s], this->base.todaysDate);
+    }
+
+    fitsIn->readColumnUsingName(fitsIn, TFLOAT, "PSF_INST_MAG", 1, 1, nDet, instMag);
+    fitsIn->readColumnUsingName(fitsIn, TFLOAT, "PEAK_FLUX_AS_MAG", 1, 1, nDet, peakMag);
+    fitsIn->readColumnUsingName(fitsIn, TLONG, "FLAGS", 1, 1, nDet, flags1);
+    fitsIn->readColumnUsingName(fitsIn, TLONG, "FLAGS2", 1, 1, nDet, flags2);
+
+    printf("Looping through %ld psf detections\n", nDet);
+    float mag;
+    long unmatched = 0, totalDetections = 0, numOfDuplicates = 0, numInvalidFlux = 0, numDetectionsOut = 0;
+
+    for (long s=0; s<nDet; s++) {
+
+        // TODO implement this match in DVO
+        if (1) {
+
+            //infoFlags[s] = ((uint64_t)flags1[s] << 32) | (uint64_t)flags2[s]; TODO implement after schema change to make infoFlag 64-bit
+            infoFlags[s] = (uint64_t)flags1[s];
+
+            peakFluxOk = getFlux(exposureTime, peakMag[s], &peakFlux[s], 0.0, NULL);
+
+            mag = instMag[s];
+            if (!peakFluxOk || !isfinite(mag) || mag < -998.0) {
+
+                removeList[numOfDuplicates+numInvalidFlux] = s+1;
+                numInvalidFlux++;
+            }
+
+            totalDetections++;
         }
-
-
-        long nDet = 0;
-        if (fits_get_num_rows(fitsIn, &nDet, &status)) {
-            fits_report_error(stderr, status);
+        else {
+
+            unmatched++;
+            continue;
         }
-
-        int instMagNum;
-        status=0;fits_get_colnum(fitsIn, CASESEN, "PSF_INST_MAG", &instMagNum, &status);
-        if (status) psError(PS_ERR_IO, false, "Unable to read col num for PSF_INST_MAG");
-        fits_read_col(fitsIn, TFLOAT, instMagNum, 1, 1, nDet, &floatnull, instMag, &anynull, &status);
-
-
-        printf("Looping through %ld psf detections\n", nDet);
-        float mag;
-        long unmatched = 0, totalDetections = 0, numOfDuplicates = 0, numInvalidFlux = 0, numDetectionsOut = 0;
-
-        for (long s = 0; s<nDet; s++) {
-
-            // TODO implement this match in DVO
-            if (1) {
-
-                mag = instMag[s];
-                if (!isfinite(mag) || mag < -998.0) {
-
-                    removeList[numOfDuplicates+numInvalidFlux] = s+1;
-                    numInvalidFlux++;
-                }
-
-                totalDetections++;
-            }
-            else {
-
-                unmatched++;
-                continue;
-            }
-        }
-
-        numDetectionsOut = totalDetections - numInvalidFlux;
-
-        if (numDetectionsOut > 0) {
-
-            ippToPspsConfig_writeTable(this->base.config, fitsIn, this->base.fitsOut, nDet, "StackDetection", false);
-            fits_write_col(this->base.fitsOut, TLONG, STACKDETECTION_SKYCELLID, 1, 1, 1, &this->skycellId, &status);
-            fits_write_col(this->base.fitsOut, TBYTE, STACKDETECTION_FILTERID, 1, 1, nDet, filterIDs, &status);
-            fits_write_col(this->base.fitsOut, TBYTE, STACKDETECTION_SURVEYID, 1, 1, nDet, surveyIDs, &status);
-            fits_write_col(this->base.fitsOut, TSTRING, STACKDETECTION_ASSOCDATE, 1, 1, nDet, assocDate, &status);
-
-            if (numInvalidFlux) fits_delete_rowlist(this->base.fitsOut, removeList, numInvalidFlux, &status);
-
-        }
-        psLogMsg("ippToPsps", PS_LOG_INFO,
-                "+---------------+---------+----------+------------------+---------------+--------------+\n"
-                "|   Extension   | Rows in | Rows out | Missing from DVO | Duplicate IDs | Invalid Flux |\n"
-                "|  %12s |  %5ld  |   %5ld  |      %5ld       |    %5ld      |    %5ld     |\n",
-                extensionName, nDet, numDetectionsOut, unmatched, numOfDuplicates, numInvalidFlux);
-
-    }
-
-
-
-    // extended source
-    sprintf(extensionName, "Chip.xsrc");
-    if (fits_movnam_hdu(fitsIn, BINARY_TBL, extensionName, 0, &status)) {
-        psError(PS_ERR_IO, false, "Can't move to extension: %s\n", extensionName);
-
-    }
-    else {
-
-        int colNum;
-        int type;
-        long repeat;
-
-        ippToPspsConfig_getFitsColumnMeta(
-                "PROF_FLUX",
-                &colNum,
-                &type,
-                &repeat,
-                fitsIn);
-        printf("PROF_FILL = %d %d %ld\n", colNum, type, repeat);
-        float* vector = calloc(repeat, sizeof(float));
-
-        ippToPspsConfig_getColumnVector(
-                colNum,
-                1,
-                type,
-                repeat,
-                vector,
-                fitsIn);
-
-
-
-        free(vector);
-
-        long nDet = 0;
-        if (fits_get_num_rows(fitsIn, &nDet, &status)) {
-            fits_report_error(stderr, status);
-        }
-
-        printf("Looping through %ld extended source detections\n", nDet);
-        for (long s = 0; s<nDet; s++) {
-
-
-
-        }
-
-        ippToPspsConfig_writeTable(this->base.config, fitsIn, this->base.fitsOut, nDet, "StackApFlx", false);
-        fits_write_col(this->base.fitsOut, TBYTE, STACKAPFLX_FILTERID, 1, 1, nDet, filterIDs, &status);
-        fits_write_col(this->base.fitsOut, TBYTE, STACKAPFLX_SURVEYID, 1, 1, nDet, surveyIDs, &status);
-    }
+    }
+
+    numDetectionsOut = totalDetections - numInvalidFlux;
+    if (numDetectionsOut > 0) {
+
+        this->base.config->createAndPopulateTable(this->base.config, fitsIn, this->base.fitsOut, nDet, "StackDetection", false);
+        this->base.fitsOut->writeColumn(this->base.fitsOut, TLONG, STACKDETECTION_SKYCELLID, 1, 1, nDet, skycellIDs);
+        this->base.fitsOut->writeColumn(this->base.fitsOut, TBYTE, STACKDETECTION_FILTERID, 1, 1, nDet, filterIDs);
+        this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKDETECTION_PEAKFLUX, 1, 1, nDet, peakFlux);
+        this->base.fitsOut->writeColumn(this->base.fitsOut, TBYTE, STACKDETECTION_SURVEYID, 1, 1, nDet, surveyIDs);
+        this->base.fitsOut->writeColumn(this->base.fitsOut, TSTRING, STACKDETECTION_ASSOCDATE, 1, 1, nDet, assocDate);
+        this->base.fitsOut->writeColumn(this->base.fitsOut, TLONGLONG, STACKDETECTION_INFOFLAG, 1, 1, nDet, flags1);
+
+        if (numInvalidFlux) this->base.fitsOut->deleteRows(this->base.fitsOut, removeList, numInvalidFlux);
+
+    }
+    psLogMsg("ippToPsps", PS_LOG_INFO,
+            "+---------------+---------+----------+------------------+---------------+--------------+\n"
+            "|   Extension   | Rows in | Rows out | Missing from DVO | Duplicate IDs | Invalid Flux |\n"
+            "|  %12s |  %5ld  |   %5ld  |      %5ld       |    %5ld      |    %5ld     |\n",
+            extensionName, nDet, numDetectionsOut, unmatched, numOfDuplicates, numInvalidFlux);
 
 
@@ -195,14 +112,258 @@
     free(removeList);
     free(instMag);
+    free(flags1);
+    free(flags2);
+    free(infoFlags);
+    for (uint32_t i=0; i<this->MAXDETECT;i++) free(assocDate[i]);
+    free(assocDate);
+
+    return true;
+}
+/**
+  Structure to encapsulate block of flux values for StackApFlx table
+  */
+typedef struct {
+
+    float* flx;
+    float* flxErr;
+    float* flxStd;
+    float* flxFill;
+
+} StackApFlxFluxes;
+
+/**
+  Creates the StackApFlx table for extended source attributes
+  */
+static bool createStackApFlxTable(
+        StackBatch* this,
+        Fits* fitsIn,
+        int8_t* filterIDs,
+        int8_t* surveyIDs
+        ) {
+
+    long nDet = 0;
+    if (!fitsIn->moveToBinaryTableAndCountRows(fitsIn, "SkyChip.xrad", &nDet)) return false;
+
+
+    int aperFluxColNum, aperFluxErrColNum, aperFluxStDevColNum, aperFluxFillColNum;
+    int type; // all the same type
+    long repeat;
+
+    fitsIn->getColumnMeta(fitsIn, "APER_FLUX", &aperFluxColNum, &type, &repeat);
+    fitsIn->getColumnMeta(fitsIn, "APER_FLUX_ERR", &aperFluxErrColNum, &type, &repeat);
+    fitsIn->getColumnMeta(fitsIn, "APER_FLUX_STDEV", &aperFluxStDevColNum, &type, &repeat);
+    fitsIn->getColumnMeta(fitsIn, "APER_FILL", &aperFluxFillColNum, &type, &repeat);
+    float* vector = calloc(repeat, sizeof(float));
+
+    // we store 10 different flux values
+    StackApFlxFluxes* stackApFlxFluxes = (StackApFlxFluxes*)calloc(11, sizeof(StackApFlxFluxes));
+    for (long i=0; i<11; i++) {
+
+        stackApFlxFluxes[i].flx = (float*)calloc(nDet, sizeof(float));
+        stackApFlxFluxes[i].flxErr = (float*)calloc(nDet, sizeof(float));
+        stackApFlxFluxes[i].flxStd = (float*)calloc(nDet, sizeof(float));
+        stackApFlxFluxes[i].flxFill = (float*)calloc(nDet, sizeof(float));
+    }
+
+    printf("Looping through %ld extended source detections\n", nDet);
+    for (long s=0; s<nDet; s++) {
+
+        fitsIn->getColumnVector(fitsIn, aperFluxColNum,s, type, repeat, vector);
+        for (int i=0; i<repeat; i++) stackApFlxFluxes[i].flx[s] = vector[i];
+
+        fitsIn->getColumnVector(fitsIn, aperFluxErrColNum,s, type, repeat, vector);
+        for (int i=0; i<repeat; i++) stackApFlxFluxes[i].flxErr[s] = vector[i];
+
+        fitsIn->getColumnVector(fitsIn, aperFluxStDevColNum,s, type, repeat, vector);
+        for (int i=0; i<repeat; i++) stackApFlxFluxes[i].flxStd[s] = vector[i];
+
+        fitsIn->getColumnVector(fitsIn, aperFluxFillColNum,s, type, repeat, vector);
+        for (int i=0; i<repeat; i++) stackApFlxFluxes[i].flxFill[s] = vector[i];
+
+    }
+
+    free(vector);
+
+    //int status = 0;
+    this->base.config->createAndPopulateTable(this->base.config, fitsIn, this->base.fitsOut, nDet, "StackApFlx", false);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TBYTE, STACKAPFLX_FILTERID, 1, 1, nDet, filterIDs);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TBYTE, STACKAPFLX_SURVEYID, 1, 1, nDet, surveyIDs);
+
+    // R1->r10 flux values
+    // 1
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR1, 1, 1, nDet, stackApFlxFluxes[0].flx);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR1ERR, 1, 1, nDet, stackApFlxFluxes[0].flxErr);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR1STD, 1, 1, nDet, stackApFlxFluxes[0].flxStd);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR1FILL, 1, 1, nDet, stackApFlxFluxes[0].flxFill);
+    // 2
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR2, 1, 1, nDet, stackApFlxFluxes[1].flx);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR2ERR, 1, 1, nDet, stackApFlxFluxes[1].flxErr);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR2STD, 1, 1, nDet, stackApFlxFluxes[1].flxStd);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR2FILL, 1, 1, nDet, stackApFlxFluxes[1].flxFill);
+    // 3
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR3, 1, 1, nDet, stackApFlxFluxes[2].flx);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR3ERR, 1, 1, nDet, stackApFlxFluxes[2].flxErr);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR3STD, 1, 1, nDet, stackApFlxFluxes[2].flxStd);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR3FILL, 1, 1, nDet, stackApFlxFluxes[2].flxFill);
+    // 4
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR4, 1, 1, nDet, stackApFlxFluxes[3].flx);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR4ERR, 1, 1, nDet, stackApFlxFluxes[3].flxErr);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR4STD, 1, 1, nDet, stackApFlxFluxes[3].flxStd);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR4FILL, 1, 1, nDet, stackApFlxFluxes[3].flxFill);
+    // 5
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR5, 1, 1, nDet, stackApFlxFluxes[4].flx);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR5ERR, 1, 1, nDet, stackApFlxFluxes[4].flxErr);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR5STD, 1, 1, nDet, stackApFlxFluxes[4].flxStd);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR5FILL, 1, 1, nDet, stackApFlxFluxes[4].flxFill);
+    // 6
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR6, 1, 1, nDet, stackApFlxFluxes[5].flx);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR6ERR, 1, 1, nDet, stackApFlxFluxes[5].flxErr);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR6STD, 1, 1, nDet, stackApFlxFluxes[5].flxStd);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR6FILL, 1, 1, nDet, stackApFlxFluxes[5].flxFill);
+    // 7
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR7, 1, 1, nDet, stackApFlxFluxes[6].flx);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR7ERR, 1, 1, nDet, stackApFlxFluxes[6].flxErr);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR7STD, 1, 1, nDet, stackApFlxFluxes[6].flxStd);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR7FILL, 1, 1, nDet, stackApFlxFluxes[6].flxFill);
+    // 8
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR8, 1, 1, nDet, stackApFlxFluxes[7].flx);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR8ERR, 1, 1, nDet, stackApFlxFluxes[7].flxErr);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR8STD, 1, 1, nDet, stackApFlxFluxes[7].flxStd);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR8FILL, 1, 1, nDet, stackApFlxFluxes[7].flxFill);
+    // 9
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR9, 1, 1, nDet, stackApFlxFluxes[8].flx);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR9ERR, 1, 1, nDet, stackApFlxFluxes[8].flxErr);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR9STD, 1, 1, nDet, stackApFlxFluxes[8].flxStd);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR9FILL, 1, 1, nDet, stackApFlxFluxes[8].flxFill);
+    // 10
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR10, 1, 1, nDet, stackApFlxFluxes[9].flx);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR10ERR, 1, 1, nDet, stackApFlxFluxes[9].flxErr);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR10STD, 1, 1, nDet, stackApFlxFluxes[9].flxStd);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TFLOAT, STACKAPFLX_FLXR10FILL, 1, 1, nDet, stackApFlxFluxes[9].flxFill);
+
+    for (long i=0; i<11; i++) {
+
+        free(stackApFlxFluxes[i].flx);
+        free(stackApFlxFluxes[i].flxErr);
+        free(stackApFlxFluxes[i].flxStd);
+        free(stackApFlxFluxes[i].flxFill);
+    }
+
+    free(stackApFlxFluxes);
+
+    return true;
+}
+
+/**
+  Creates the StackModelFit table
+  */
+static bool createStackModelFitTable(
+        StackBatch* this,
+        Fits *fitsIn,
+        int8_t* filterIDs,
+        int8_t* surveyIDs) {
+
+    long nDet = 0;
+    if (!fitsIn->moveToBinaryTableAndCountRows(fitsIn, "SkyChip.xfit", &nDet)) return false;
+
+    long* ippIdets = (long*)calloc(this->MAXDETECT, sizeof(long));
+    char** modelTypes = (char**)calloc(this->MAXDETECT, sizeof(char**));
+    for (uint32_t i=0; i<this->MAXDETECT;i++) modelTypes[i] = (char*)calloc(20,sizeof(char));
+
+    // read whole columns
+    fitsIn->readColumnUsingName(fitsIn, TLONG, "IPP_IDET", 1, 1, nDet, ippIdets);
+    fitsIn->readColumnUsingName(fitsIn, TSTRING, "MODEL_TYPE", 1, 1, nDet, modelTypes);
+
+    printf("Looping through %ld model fit rows\n", nDet);
+    for (long i = 0; i<nDet; i++) {
+
+        printf("IPP_IDET %ld model %s\n", ippIdets[i], modelTypes[i]);
+
+    }
+
+    this->base.config->createAndPopulateTable(this->base.config, fitsIn, this->base.fitsOut, nDet, "StackModelFit", false);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TBYTE, STACKMODELFIT_FILTERID, 1, 1, nDet, filterIDs);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TBYTE, STACKMODELFIT_SURVEYID, 1, 1, nDet, surveyIDs);
+
+    // free up
+    free(ippIdets);
+    for (uint32_t i=0; i<this->MAXDETECT;i++) free(modelTypes[i]);
+    free(modelTypes);
+
+    return true;
+}
+
+/**
+  Creates the StackToImage table
+  */
+static bool createStackToImageTable(
+        StackBatch* this,
+        Fits* fitsIn
+        ) {
+
+    this->base.config->createAndPopulateTable(this->base.config, fitsIn, this->base.fitsOut, 3/*TODO*/, "StackToImage", false);
+
+    return true;
+}
+
+/**
+  Does the work. Writes all extensions to new FITS file.
+  */
+static int run(StackBatch* this) {
+
+    if (this->base.exitCode != PS_EXIT_SUCCESS) return this->base.exitCode; 
+
+    // open input FITS file
+    Fits* fitsIn = existing_Fits(this->base.inputFiles[0]);
+    if (fitsIn->getFilePtr(fitsIn) == NULL)  return PS_EXIT_SYS_ERROR;
+
+    // get primary header and pull stuff out for later
+    float exposureTime; fitsIn->getHeaderKeyValue(fitsIn, TFLOAT, "EXPTIME", &exposureTime);
+    char filterType[20]; fitsIn->getHeaderKeyValue(fitsIn, TSTRING, "FILTER", filterType);
+
+    int8_t filterID = -1;
+    if (!this->base.config->getFilterId(this->base.config, filterType, &filterID)) {
+
+        //        this->base.exitCode = PS_EXIT_DATA_ERROR;
+        //    return this->base.exitCode; TODO
+    }
+
+    exposureTime = 60.0;// TODO
+    filterID = 3; // TODO
+
+    // write StackMeta
+    this->base.config->createAndPopulateTable(this->base.config, fitsIn, this->base.fitsOut, 1, "StackMeta", true);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TLONG, STACKMETA_SKYCELLID, 1, 1, 1, &this->skycellId);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TBYTE, STACKMETA_FILTERID, 1, 1, 1, &filterID);
+    this->base.fitsOut->writeColumn(this->base.fitsOut, TBYTE, STACKMETA_SURVEYID, 1, 1, 1, &this->base.surveyID);
+
+    // allocate stuff for other extensions
+    int8_t* filterIDs = (int8_t*)calloc(this->MAXDETECT, sizeof(int8_t));
+    int8_t* surveyIDs = (int8_t*)calloc(this->MAXDETECT, sizeof(int8_t));
+    long* skycellIDs = (long*)calloc(this->MAXDETECT, sizeof(long));
+
+    // some stuff is the same for all detections so we can populate here
+    for (long s = 0; s<this->MAXDETECT; s++) {
+
+        skycellIDs[s] = this->skycellId;
+        filterIDs[s] = filterID;
+        surveyIDs[s] = this->base.surveyID;
+    }
+
+    // create all extensions
+    createStackDetectionTable(this, fitsIn, filterIDs, surveyIDs, skycellIDs, exposureTime);
+    createStackApFlxTable(this, fitsIn, filterIDs, surveyIDs);
+    printf("dsdsdddds\n\n");
+    createStackModelFitTable(this, fitsIn, filterIDs, surveyIDs);
+    createStackToImageTable(this, fitsIn);
+
+    // free stuff up
     free(filterIDs);
     free(surveyIDs);
-
-    status=0;
-    if (fits_close_file(fitsIn, &status)) fits_report_error(stderr, status);
-
+    free(skycellIDs);
+    fitsIn->destroy(fitsIn);
 
     return this->base.exitCode;
 }
-
 
 /**
@@ -240,5 +401,7 @@
     }
 
-    this->base.parseArguments(&this->base, argc, argv);
+    char fitsOutFile[40];
+    sprintf(fitsOutFile, "%08d.FITS", this->skycellId);
+    this->base.parseArguments(&this->base, argc, argv, "/stack", fitsOutFile);
 
     if (
@@ -275,14 +438,10 @@
     this->MAXDETECT = 150000;
 
+    // method pointers
     this->print = print;
     this->destroy = destroy;
     this->base.run = run;
 
-    if (!parseArguments(this, *argc, argv)) { return this; }
-
-    strcat(this->base.configsDir, "/stack");
-    sprintf(this->base.fitsOutFile, "%08d.FITS", this->skycellId);
-
-    this->base.init(&this->base);
+    parseArguments(this, *argc, argv);
 
     return this;
@@ -321,4 +480,2 @@
 }
 
-
-
