Index: branches/eam_branches/ipp-20110213/ippToPsps/src/Batch.c
===================================================================
--- branches/eam_branches/ipp-20110213/ippToPsps/src/Batch.c	(revision 30812)
+++ branches/eam_branches/ipp-20110213/ippToPsps/src/Batch.c	(revision 31083)
@@ -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);
-            }
+            this->logger->print(this->logger, MSG_ERROR, "Batch", "Failed, so deleting fits file\n");
+            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,7 @@
     if (this->dvoConfig != NULL) dvoConfigFree(this->dvoConfig);
 
-    ippToPspsConfig_Destructor(this->config);
+    // destroy objects
+    this->fitsGenerator->destroy(this->fitsGenerator);
+    this->initData->destroy(this->initData);
 
     pmConfigDone();
@@ -115,5 +105,5 @@
     if (file == NULL) {
 
-        psError(PS_ERR_UNKNOWN, false, "Unable to open file at %s", this->fitsInPath);
+        this->logger->print(this->logger, MSG_ERROR, "Batch", "Unable to open file at %s\n", this->fitsInPath);
         return false;
     }
@@ -123,5 +113,6 @@
     while (fgets(line, 1000, file) != NULL) this->numOfInputFiles++;
 
-    printf ("Number of input files %d\n", this->numOfInputFiles);
+    this->logger->print(this->logger, MSG_INFO, "Batch", 
+            "%d input file%s found\n", this->numOfInputFiles, (this->numOfInputFiles > 1) ? "s" : "");
 
     if (this->numOfInputFiles < 1) return false;
@@ -151,5 +142,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
@@ -166,5 +157,5 @@
     if (!this->pmconfig) {
 
-        psError(PS_ERR_UNKNOWN, false, "Unable to read configuration.");
+        this->logger->print(this->logger, MSG_ERROR, "Batch", "Unable to read configuration\n");
         this->exitCode = PS_EXIT_CONFIG_ERROR;
         return false;
@@ -178,4 +169,7 @@
     haveSurveyType = false;
 
+    char fitsOutPath[1000];
+    char configsBaseDir[1000];
+
     // decode arguments
     int32_t optind = 1;
@@ -193,5 +187,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 +197,5 @@
             else if(strcmp(sw, "-config") == 0 ) {
                 optind++; if( optind > ( argc-1 ) ) break;
-                strcpy(this->configsDir, argv[optind]);
+                strcpy(configsBaseDir, argv[optind]);
                 haveConfig = true;
             }
@@ -211,4 +205,7 @@
                 haveSurveyType = true;
             }
+            else if(strcmp(sw, "-test") == 0 ) {
+                this->testMode = true;
+            }
         }
 
@@ -216,19 +213,5 @@
     }
 
-
-    // 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"));
-
+    // check we have some input paths
     if (haveFitsInPath && !readInputFilePaths(this)) {
 
@@ -237,15 +220,8 @@
     }
 
-    return true;
-}
-
-/**
-  Initialises file paths, creates FITS file etc
-  */
-bool init(Batch* this) {
-
-    // create a config object
-    this->config = ippToPspsConfig_Constructor(this->configsDir);
-    if (this->config == NULL) {
+    // create an InitData object and get survey ID
+    this->initData = new_InitData(configsBaseDir, this->logger);
+    if (strlen(this->surveyType) > 0 && 
+            !this->initData->getSurveyId(this->initData, this->surveyType, &this->surveyID)) {
 
         this->exitCode = PS_EXIT_CONFIG_ERROR;
@@ -253,22 +229,14 @@
     }
 
-    // get survey ID using config object
-    if (strlen(this->surveyType) > 0 && !ippToPspsConfig_getSurveyId(this->config, this->surveyType, &this->surveyID)) {
-
-        this->exitCode = PS_EXIT_CONFIG_ERROR;
-        return false;
-    }
+    // create a FitsGenerator object
+    strcat(configsBaseDir, configsDir);
+    this->fitsGenerator = new_FitsGenerator(this->logger, configsBaseDir);
+
     // 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, this->logger);
+    if (this->fitsOut->getFilePtr(this->fitsOut) == NULL) return false;
 
     // create XML document for results 
@@ -278,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);
     }
 
@@ -289,13 +257,12 @@
 static void print(Batch* this) {
 
-    printf("\n");
-    printf("* surveyType      : '%s'\n", this->surveyType);
-    printf("* surveyID        : %d\n", this->surveyID);
-    printf("* resultsPath     : '%s'\n", this->resultsPath);
-    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);
+    this->logger->print(this->logger, MSG_INFO, "Batch", "\n");
+    this->logger->print(this->logger, MSG_INFO, "Batch", "     Class fields:\n");
+    this->logger->print(this->logger, MSG_INFO, "Batch", "surveyType      : %s\n", this->surveyType);
+    this->logger->print(this->logger, MSG_INFO, "Batch", "surveyID        : %d\n", this->surveyID);
+    this->logger->print(this->logger, MSG_INFO, "Batch", "resultsPath     : %s\n", this->resultsPath);
+    this->logger->print(this->logger, MSG_INFO, "Batch", "numOfInputFiles : %d\n", this->numOfInputFiles);
+    this->logger->print(this->logger, MSG_INFO, "Batch", "fitsInPath      : %s\n", this->fitsInPath);
+    this->logger->print(this->logger, MSG_INFO, "Batch", "testMode        : %s\n", this->testMode ? "yes" : "no");
 }
 
@@ -342,6 +309,7 @@
   Constructor. Takes a Batch object and initialises it.
   */
-bool new_Batch(Batch *this) {
-
+bool new_Batch(Logger* logger, Batch *this) {
+
+    this->logger = logger;
     this->surveyID = -1;
     this->resultsXmlDoc = NULL;
@@ -349,13 +317,12 @@
     this->inputFiles = NULL;
     this->fitsOut = NULL;
-    this->config = NULL;
+    this->fitsGenerator = NULL;
     this->pmconfig = NULL;
     this->exitCode = PS_EXIT_SUCCESS;
-
+    this->testMode = false;
+
+    // set up function pointers
     this->parseArguments = parseArguments;
-    this->init = init;
     this->print = print;
-    this->destroy = destroy;
-
     this->gotResultsPath = gotResultsPath;
     this->gotFitsInPath = gotFitsInPath;
@@ -363,4 +330,5 @@
     this->gotConfig = gotConfig;
     this->gotSurveyType = gotSurveyType;
+    this->destroy = destroy;
 
     assert(this);
Index: branches/eam_branches/ipp-20110213/ippToPsps/src/Batch.h
===================================================================
--- branches/eam_branches/ipp-20110213/ippToPsps/src/Batch.h	(revision 30812)
+++ branches/eam_branches/ipp-20110213/ippToPsps/src/Batch.h	(revision 31083)
@@ -1,3 +1,3 @@
-/** @file ippToPsps.h
+/** @file Batch.h
  *
  *  @brief Batch
@@ -6,5 +6,5 @@
  *
  *  @author IfA
- *  Copyright 2009 Institute for Astronomy, University of Hawaii
+ *  Copyright 2011 Institute for Astronomy, University of Hawaii
  */
 
@@ -12,9 +12,14 @@
 #define IPPTOPSPS_BATCH_H
 
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
 #include <psmodules.h>
 #include <dvo_util.h>
-#include "Config.h"
-#include <libxml/parser.h>
-#include <libxml/tree.h>
+
+#include "FitsGenerator.h"
+#include "Fits.h"
+#include "InitData.h"
+#include "Logger.h"
 
 /**
@@ -25,29 +30,28 @@
   - 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 implement print()
   */
 typedef struct Batch {
 
     // fields
-    char surveyType[10];        // the survey type, eg 3PI, MD01, STS, SSS
-    int8_t surveyID;            // survey ID
-    char fitsInPath[1000];      // path to FITS input
-    char resultsPath[1000];     // path to results file
-    xmlDocPtr resultsXmlDoc;    // pointer to XML document for results
-    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
-    pmConfig* pmconfig;         // pmConfig
-    dvoConfig* dvoConfig;       // dvo database
-    IppToPspsConfig* config;    // config structure
-    char todaysDate[20];        // today's date
-    int exitCode;               // ps exit code
+    char surveyType[10];           // the survey type, eg 3PI, MD01, STS, SSS
+    int8_t surveyID;               // survey ID
+    char fitsInPath[1000];         // path to FITS input
+    char resultsPath[1000];        // path to results file
+    xmlDocPtr resultsXmlDoc;       // pointer to XML document for results
+    uint16_t numOfInputFiles;      // number of input files
+    char** inputFiles;             // array of input file names
+    Fits *fitsOut;                 // output FITS file
+    pmConfig* pmconfig;            // pmConfig
+    dvoConfig* dvoConfig;          // dvo database
+    FitsGenerator* fitsGenerator;  // FitsGenerator object
+    InitData* initData;            // InitData object
+    char todaysDate[20];           // today's date
+    int exitCode;                  // ps exit code
+    bool testMode;                 // test mode boolean
+    Logger* logger;                // Logger object 
 
     // methods
     bool (*parseArguments)();
-    bool (*init)();
     int (*run)();
     void (*print)();
@@ -57,4 +61,6 @@
     bool (*gotConfig)();
     bool (*gotSurveyType)(); 
+
+    // destructor
     void (*destroy)();
 
@@ -62,5 +68,5 @@
 
 // public functions
-bool new_Batch(Batch *this);
+bool new_Batch(Logger* logger, Batch *this);
 
 void ippToPsps_VersionPrint(void);
Index: branches/eam_branches/ipp-20110213/ippToPsps/src/Config.c
===================================================================
--- branches/eam_branches/ipp-20110213/ippToPsps/src/Config.c	(revision 30812)
+++ 	(revision )
@@ -1,948 +1,0 @@
-/** @file ippToPspsConfig.c
- *
- *  @ingroup ippToPsps
- *
- *  @author IfA
- *  Copyright 2009 Institute for Astronomy, University of Hawaii
- */
-
-#include "ippToPspsConfig.h"
-#include <ctype.h>
-#include <libxml/parser.h>
-#include <libxml/tree.h>
-
-// Gets PS type from string
-static int ippToPsps_GetDataType(char *typename) {
-
-    if (!strncmp(typename, "TBYTE", 5))  return TBYTE;
-    if (!strncmp(typename, "TSHORT", 6)) return TSHORT;
-    if (!strncmp(typename, "TLONGLONG", 9)) return TLONGLONG;
-    if (!strncmp(typename, "TLONG", 5)) return TLONG;
-    if (!strncmp(typename, "TFLOAT", 6)) return TFLOAT;
-    if (!strncmp(typename, "TDOUBLE", 7)) return TDOUBLE;
-    if (!strncmp(typename, "TSTRING", 7)) return TSTRING;
-
-    psError(PS_ERR_IO, false, "Don't understand type '%s'", typename);
-
-    return 0;
-}
-
-// finds a column within this table
-static IppToPspsConfig_Column* ippToPspsConfig_findColumn(IppToPspsConfig_Table* table, const char* name) {
-
-    IppToPspsConfig_Column* column = NULL;
-
-    for (uint32_t i=0; i<table->numOfColumns; i++) {
-
-        if (strcmp(table->columns[i].pspsName, name) == 0) {
-            column = table->columns+i;
-            break;
-        }
-    }
-
-    if (!column) psError(PS_ERR_IO, false, "Could not find column '%s' in table '%s'", name, table->name);
-
-    return column;
-}
-
-// finds a table from the array of tables
-static IppToPspsConfig_Table* ippToPspsConfig_findTable(IppToPspsConfig* this, const char* name) {
-
-    IppToPspsConfig_Table* table = NULL;
-
-    for (uint32_t i=0; i<this->numOfTables; i++) {
-
-        if (strcmp(this->tables[i].name, name) == 0) {
-            table = this->tables+i;
-            break;
-        }
-    }
-
-    if (!table) psError(PS_ERR_IO, false, "Could not find table '%s'", name);
-
-    return table;
-}
-
-// gets an attribute value from XML node
-static bool ippToPspsConfig_getAttribute(xmlNode* node, const char* attName, char* _value) {
-
-    xmlChar* value = xmlGetProp(node, (const xmlChar*)attName);
-    if (!value) return false;
-    sprintf(_value, "%s", value);
-    xmlFree(value);
-
-    return true;
-}
-
-// 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) {
-
-    if (!table) return false;
-    if (nRows < 1) return false;
-
-    long nCols = table->numOfColumns;
-
-    char** colNames = (char**)calloc(nCols, sizeof(char*));
-    char** colTypes = (char**)calloc(nCols, sizeof(char*));
-
-    uint32_t i = 0;
-    for (i=0;i<nCols;i++) colTypes[i] = (char*)calloc(5,sizeof(char));
-
-    i = 0;
-    for (uint32_t m=0; m<table->numOfColumns; m++) {
-
-        colNames[i] = table->columns[i].pspsName;
-
-        //        sprintf(colNames[i], table->columns[i].pspsName);
-
-        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;
-
-            default:
-                break;
-        }
-
-        i++;
-    }
-
-    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);
-    }
-
-    for (i=0;i<nCols;i++) free(colTypes[i]);
-    free(colTypes);
-    free(colNames);
-
-    // now insert defaults
-    int8_t* int8col = (int8_t*)calloc(nRows, sizeof(int8_t));
-    int16_t* int16col = (int16_t*)calloc(nRows, sizeof(int16_t));
-    long* longcol = (long*)calloc(nRows, sizeof(long));
-    float* floatcol = (float*)calloc(nRows, sizeof(float));
-    double* doublecol = (double*)calloc(nRows, sizeof(double));
-    char** strcol = (char**)calloc(nRows, sizeof(char**));
-    for (uint32_t i=0; i<nRows;i++)
-        strcol[i] = (char*)calloc(20,sizeof(char)); // TODO 20? size issue
-
-    uint32_t j;
-    int64_t tempLong;
-    double tempDouble;
-    char tempStr[20];
-
-    int col;
-    for (uint32_t i=0; i<table->numOfColumns; i++) {
-
-        col = i+1;
-
-        strcpy(tempStr,table->columns[i].defaultValue);
-        tempLong = atol(tempStr);
-        tempDouble = atof(tempStr);
-
-        switch (table->columns[i].pspsType) {
-
-            case TBYTE:
-                for(j=0;j<nRows;j++) int8col[j] = (int8_t)tempLong;
-                fits_write_col(fitsOut, TBYTE, col, 1, 1, nRows, int8col, &status);
-                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);
-                break;
-            case TLONG:
-                for(j=0;j<nRows;j++) longcol[j] = (long)tempLong;
-                fits_write_col(fitsOut, TLONG, col, 1, 1, nRows, longcol, &status);
-                break;
-
-            case TLONGLONG:
-                for(j=0;j<nRows;j++) longcol[j] = tempLong;
-                fits_write_col(fitsOut, TLONGLONG, col, 1, 1, nRows, longcol, &status);
-                break;
-
-            case TFLOAT:
-                for(j=0;j<nRows;j++) floatcol[j] = (float)tempDouble;
-                fits_write_col(fitsOut, TFLOAT, col, 1, 1, nRows, floatcol, &status);
-                break;
-
-            case TDOUBLE:
-                for(j=0;j<nRows;j++) doublecol[j] = tempDouble;
-                fits_write_col(fitsOut, TDOUBLE, col, 1, 1, nRows, doublecol, &status);
-                break;
-
-            case TSTRING:
-                for(j=0;j<nRows;j++) strcpy(strcol[j], tempStr);
-                fits_write_col(fitsOut, TSTRING, col, 1, 1, nRows, strcol, &status);
-                break;
-
-            default:
-                break;
-
-        }
-    }
-
-    free(int8col);
-    free(int16col);
-    free(longcol);
-    free(floatcol);
-    free(doublecol);
-    for (uint32_t i=0; i<nRows;i++) free(strcol[i]);
-    free(strcol);
-
-    return true;
-}
-
-// count number of children with the provided name
-static int ippToPspsConfig_countChildren(xmlNode* rootNode, const char* name) {
-
-    int count = 0;
-    xmlNode* node = NULL;
-
-    for (node = rootNode->children; node; node = node->next) {
-
-        if (strcmp((const char*)node->name, name)==0) count++;
-    } 
-
-    return count;
-}
-
-// searches through this table and finds attribute value for a provided key
-static bool ippToPspsConfig_getRowAttribute(IppToPspsConfig* this, xmlNode* tableNode, 
-        const char* keyName, const char* keyValue, 
-        const char* attName, char* attValue) {
-
-    xmlNode* node = NULL;
-
-    char buffer[100];
-
-    // loop round all available rows for this table
-    for (node = tableNode->children; node; node = node->next) {
-        if (node->type == XML_ELEMENT_NODE) {
-            //psLogMsg("ippToPsps", PS_LOG_INFO, "Node name '%s'",  (const char*)node->name );
-
-            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 );
-
-                if (strcmp(buffer, keyValue) != 0) continue;
-                ippToPspsConfig_getAttribute(node, attName, attValue);
-                //printf("FOUND %s %s %s %s \n", keyName, buffer, attName, attValue );
-                return true;
-
-            }
-        }
-    }
-
-    psLogMsg("ippToPsps", PS_LOG_INFO, "Could not find value for '%s' for '%s' with value '%s'", attName, keyName, keyValue );
-    return false;
-}
-
-// opens an XML file and returns the document
-static xmlDoc* ippToPspsConfig_openXmlFile(const char* path) {
-
-    xmlDoc* doc = xmlReadFile(path, NULL, 0);
-    if (doc == NULL) psError(PS_ERR_IO, false, "Unable to open XML file at %s", path);
-    return doc;
-}
-
-// closes an XML file 
-static bool ippToPspsConfig_closeXmlFile(xmlDoc* doc) {
-
-    xmlFreeDoc(doc);
-    xmlCleanupParser();
-
-    return true;
-}
-
-// gets filter ID for this filter name from 'init' data 
-static bool ippToPspsConfig_getInitValue(
-        IppToPspsConfig* this, 
-        const char* tableName,
-        const char* keyName, const char* keyValue,
-        const char* attName, char* attValue) {
-
-    bool ret = true;
-
-    psString path = NULL;
-    psStringAppend(&path, "%s/../init/data.xml", this->configsPath); // TODO nasty
-
-    xmlDoc* doc = ippToPspsConfig_openXmlFile(path);
-
-    if (doc == NULL) {
-        psFree(path);
-        return false;
-    }
-    psFree(path);
-
-    xmlNode* rootElement = xmlDocGetRootElement(doc);
-
-    if (strcmp((const char*)rootElement->name, "tabledata")!=0) {
-        psError(PS_ERR_IO, false, "Root node of XML is not 'tabledata', as it should be");
-        return false;
-    }
-
-    xmlNode* node = NULL;
-    char tempStr[100];
-
-    // loop round all available tables
-    for (node = rootElement->children; node; node = node->next) {
-        if (node->type == XML_ELEMENT_NODE) {
-
-            if (strcmp((const char*)node->name, "table")!=0) continue;
-            if (!ippToPspsConfig_getAttribute(node, "name", tempStr)) continue;
-            if (strcmp(tempStr, tableName)!=0) continue;
-
-            ret = ippToPspsConfig_getRowAttribute(this, node, keyName, keyValue, attName, attValue);
-            break;
-        }
-
-    }
-
-    ippToPspsConfig_closeXmlFile(doc);
-
-    return ret;
-}
-
-// gets survey ID from survey name
-bool ippToPspsConfig_getSurveyId(IppToPspsConfig* this, const char* surveyType, int8_t* surveyId) {
-
-    char buffer[10];
-    if (!ippToPspsConfig_getInitValue(this, "Survey", "name", surveyType, "surveyID", buffer)) {
-
-        *surveyId = -1;
-        return false;
-    }
-
-    *surveyId = atoi(buffer);
-    return true;
-}
-
-// gets filter ID from filter type
-bool ippToPspsConfig_getFilterId(IppToPspsConfig* this, const char* filterType, int8_t* filterId) {
-
-    char tmp[2];
-    strncpy(tmp,filterType,1);
-    tmp[1] = '\0';
-    char buffer[10];
-    if (!ippToPspsConfig_getInitValue(this, "Filter", "filterType", tmp, "filterID", buffer)) {
-
-        *filterId = -1;
-        return false;
-    }
-
-    *filterId = atoi(buffer);
-    return true;
-}
-
-// populate the provided table with data from XML
-static bool ippToPspsConfig_populateTable(IppToPspsConfig_Table* table, xmlNode* rootElement, fitsfile *fitsOut) {
-
-    char tempStr[100];
-    xmlNode* node = NULL;
-    xmlNode* tableNode = NULL;
-
-    // first check we have data for this table in XML file
-    for (node = rootElement->children; node; node = node->next) {
-        if (node->type == XML_ELEMENT_NODE) {
-
-            if (strcmp((const char*)node->name, "table")!=0) continue;
-            if (!ippToPspsConfig_getAttribute(node, "name", tempStr)) continue;
-            if (strcmp(tempStr, table->name)==0) {tableNode = node; break;}
-        }
-    }
-
-    if(!tableNode) {
-
-        psError(PS_ERR_IO,"Could not find table: %s", table->name);
-        return false;
-    }
-
-    // now count the number of rows
-    int nRows = ippToPspsConfig_countChildren(tableNode, "row");
-    if (nRows < 1) return false;
-    if (!ippToPspsConfig_createTable(table, fitsOut, nRows)) return false;
-
-    int rows=0;
-    int status;
-    char** strcol = (char**)calloc(1, sizeof(char**));
-    strcol[0] = (char*)calloc(32,sizeof(char)); // TODO 20? size issue
-
-    int8_t* bytecol = calloc(1, sizeof(uint8_t));
-    int16_t* shortcol = calloc(1, sizeof(uint16_t));
-    long* longcol = calloc(1, sizeof(long));
-    double* doublecol = calloc(1, sizeof(double));
-    float* floatcol = calloc(1, sizeof(float));
-
-    int col;
-
-    // now get rows
-    for (node = tableNode->children; node; node = node->next) {
-
-        if (strcmp((const char*)node->name, "row")!=0) continue;
-
-        rows++;
-
-        // get column data for this row
-        for (int i=0;i<table->numOfColumns;i++) {
-
-            col = i+1;
-
-            if (!ippToPspsConfig_getAttribute(node, table->columns[i].pspsName, tempStr)) continue;
-
-            strncpy(strcol[0], tempStr, 32);
-            status=0;
-            //psLogMsg("ippToPsps", PS_LOG_INFO, "Values %s - %ld - %f - %d", strcol[0], longcol[0], doublecol[0], bytecol[0]);
-            switch (table->columns[i].pspsType) {
-
-                case TBYTE:
-                    bytecol[0] = atoi(tempStr);
-                    fits_write_col(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, bytecol, &status); 
-                    break;
-                case TSHORT:
-                    shortcol[0] = atoi(tempStr);
-                    fits_write_col(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, shortcol, &status); 
-                    break;
-                case TLONG:
-                case TLONGLONG:
-                    longcol[0] = atol(tempStr);
-                    fits_write_col(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, longcol, &status); 
-                    break;
-                case TFLOAT:
-                    floatcol[0] = atof(tempStr);
-                    fits_write_col(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, floatcol, &status);
-                    break;
-                case TDOUBLE:
-                    doublecol[0] = atof(tempStr);
-                    fits_write_col(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, doublecol, &status);
-                    break;
-                case TSTRING:
-                    fits_write_col(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, strcol, &status); 
-                    break;
-
-            }
-            if (status) psError(PS_ERR_IO, false, "Unable to write value of %s to column %s in table %s", 
-                    tempStr, table->columns[i].pspsName, table->name);
-        }
-    }
-
-    free(bytecol);
-    free(shortcol);
-    free(longcol);
-    free(floatcol);
-    free(doublecol);
-    free(strcol[0]);
-    free(strcol);
-
-    return true;
-}
-
-// loads a table description from XML
-static bool ippToPspsConfig_loadTableDescription(IppToPspsConfig_Table* table, xmlNode* tableNode) {
-
-    bool ret = true;
-    char tableName[100];
-    ippToPspsConfig_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));
-    //    psLogMsg("ippToPsps", PS_LOG_INFO, " Found '%d' columns", table->numOfColumns);
-
-    char buffer[200];
-    xmlNode* node;
-    int columnNum = 0;
-
-    for (node = tableNode->children; node; node = node->next) {
-
-        if (strcmp((const char*)node->name, "column") != 0) continue;
-
-        table->columns[columnNum].usingDefault = true;
-
-        // column name
-        ippToPspsConfig_getAttribute(node, "name", buffer);
-        sprintf(table->columns[columnNum].pspsName, buffer);
-
-        // column type
-        ippToPspsConfig_getAttribute(node, "type", buffer);
-        table->columns[columnNum].pspsType = ippToPsps_GetDataType(buffer);
-
-        // default value
-        ippToPspsConfig_getAttribute(node, "default", buffer);
-        sprintf(table->columns[columnNum].defaultValue, buffer);
-
-        // comment
-        ippToPspsConfig_getAttribute(node, "comment", buffer);
-        sprintf(table->columns[columnNum].comment, buffer);
-
-        # if 0
-        psLogMsg("ippToPsps", PS_LOG_INFO, "'%s'  name='%s' type='%d'  default='%s' comment='%s'", 
-                table->name,
-                table->columns[columnNum].pspsName, 
-                table->columns[columnNum].pspsType, 
-                table->columns[columnNum].defaultValue, 
-                table->columns[columnNum].comment);
-# endif
-        columnNum++;
-    }   
-
-
-    if (columnNum < 1) {
-
-        psError(PS_ERR_IO, false, "Found no columns for table '%s'", tableName);
-        ret = false;
-    }
-
-    if (columnNum != table->numOfColumns)
-        psError(PS_ERR_IO, false, "Mismatch between number of columns expected (%d) and those found (%d)", table->numOfColumns, columnNum);
-
-    return ret;
-}
-
-// read table shapes from XML file
-bool ippToPspsConfig_loadTableDescriptions(IppToPspsConfig* this) {
-
-    bool ret = true;
-
-    psString path = NULL;
-    psStringAppend(&path, "%s/tables.xml", this->configsPath);
-
-    xmlDoc* doc = ippToPspsConfig_openXmlFile(path);
-
-    if (doc == NULL) {
-
-        psFree(path);
-        return false;
-    }
-    psFree(path);
-
-    xmlNode* rootElement = xmlDocGetRootElement(doc);
-
-    if (strcmp((const char*)rootElement->name, "tableDescriptions")!=0) {
-
-        psError(PS_ERR_IO, false, "Root node of XML is not 'tableDescriptions', as it should be");
-        return false;
-    }
-
-    // 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));
-    //    psLogMsg("ippToPsps", PS_LOG_INFO, " Found '%d' tables", this->numOfTables);
-
-    xmlNode* node = NULL;
-    IppToPspsConfig_Table* table = NULL;
-
-    int tableNum = 0;
-    // loop round all available table descriptions
-    for (node = rootElement->children; node; node = node->next) {
-        if (node->type == XML_ELEMENT_NODE) {
-
-            if (strcmp((const char*)node->name, "table")==0) {
-
-                table = this->tables+tableNum;
-
-                if (!ippToPspsConfig_loadTableDescription(table, node)) ret = false;
-                else tableNum++;
-            }
-        }
-    }
-
-    if (tableNum != this->numOfTables)
-        psError(PS_ERR_IO, false, "Mismatch between number of tables expected (%d) and those found (%d)", this->numOfTables, tableNum);
-
-    ippToPspsConfig_closeXmlFile(doc);
-
-    return ret;
-}
-
-// loads mappings from XML for a particular table
-static bool ippToPspsConfig_loadTableMappings(IppToPspsConfig* this, xmlNode* tableNode) {
-
-    bool ret = false;
-
-    xmlNode* node = NULL;
-
-    char tableName[100];
-    ippToPspsConfig_getAttribute(tableNode, "name", tableName);
-
-    IppToPspsConfig_Table* table = ippToPspsConfig_findTable(this, tableName);
-    IppToPspsConfig_Column* column = NULL;
-    if (!table) return false;
-
-    char buffer[100];
-    //psLogMsg("ippToPsps", PS_LOG_INFO, "Table '%s'", tableName);
-
-    // loop round all available mappings for this table
-    for (node = tableNode->children; node; node = node->next) {
-        if (node->type == XML_ELEMENT_NODE) {
-
-            if (strcmp((const char*)node->name, "map")==0) {
-
-                // column type
-                ippToPspsConfig_getAttribute(node, "pspsName", buffer);
-                column = ippToPspsConfig_findColumn(table, buffer);
-
-                if (!column) continue;
-
-                // IPP name
-                ippToPspsConfig_getAttribute(node, "ippName", buffer);
-                strcpy(column->ippName, buffer);
-
-                // IPP type
-                ippToPspsConfig_getAttribute(node, "ippType", buffer);
-                column->ippType = ippToPsps_GetDataType(buffer); 
-
-                column->usingDefault = false;
-
-                //psLogMsg("ippToPsps", PS_LOG_INFO, "...mapping PSPS:'%s' to '%s' '%d'", column->pspsName, column->ippName, column->ippColNum );
-            }
-        }
-    }
-
-    return ret;
-}
-
-// reads table mappings from XML file
-static bool ippToPspsConfig_loadMappings(IppToPspsConfig* this) {
-
-    bool ret = true;
-
-    psString path = NULL;
-    psStringAppend(&path, "%s/map.xml", this->configsPath);
-
-    xmlDoc* doc = ippToPspsConfig_openXmlFile(path);
-
-    if (doc == NULL) {
-        psFree(path);
-        return false;
-    }
-    psFree(path);
-
-    xmlNode* rootElement = xmlDocGetRootElement(doc);
-
-    if (strcmp((const char*)rootElement->name, "tabledata")!=0) {
-        psError(PS_ERR_IO, false, "Root node of XML is not 'tabledata', as it should be");
-        return false;
-    }
-
-    xmlNode* node = NULL;
-
-    // loop round all available tables
-    for (node = rootElement->children; node; node = node->next) {
-        if (node->type == XML_ELEMENT_NODE) {
-
-            if (strcmp((const char*)node->name, "table")==0) {
-
-                if (!ippToPspsConfig_loadTableMappings(this, node)) ret = false;
-            }
-        }
-    }
-
-    ippToPspsConfig_closeXmlFile(doc);
-
-    return ret;
-}
-
-// populate the provided table with data from XML
-bool ippToPspsConfig_populateFromFile(IppToPspsConfig* this, fitsfile *fitsOut) {
-
-    bool ret = true;
-    psString path = NULL;
-    psStringAppend(&path, "%s/data.xml", this->configsPath);
-
-    xmlDoc* doc = ippToPspsConfig_openXmlFile(path);
-
-    if (doc == NULL) {
-        psFree(path);
-        return false;
-    }
-    psFree(path);
-
-    xmlNode* rootElement = xmlDocGetRootElement(doc);
-
-    if (strcmp((const char*)rootElement->name, "tabledata")!=0) {
-        psError(PS_ERR_IO, false, "Root node of XML is not 'tabledata', as it should be");
-        return false;
-    }
-
-    for (uint32_t i=0; i<this->numOfTables; i++) {
-        if (!ippToPspsConfig_populateTable(this->tables+i, rootElement, fitsOut)) {
-            ret = false;
-        }
-    }
-
-    ippToPspsConfig_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,
-        const long nRows, 
-        const bool fromHeader) {
-
-    if (!table) return false;
-    if (!fitsIn) return false;
-
-    int8_t* int8col = (int8_t*)calloc(nRows, sizeof(int8_t));
-    int16_t* int16col = (int16_t*)calloc(nRows, sizeof(int16_t));
-    long* longcol = (long*)calloc(nRows, sizeof(long));
-    float* floatcol = (float*)calloc(nRows, sizeof(float));
-    double* doublecol = (double*)calloc(nRows, sizeof(double));
-    char** strcol = (char**)calloc(nRows, sizeof(char**));
-    for (uint32_t i=0; i<nRows;i++)
-        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) {
-
-        for (uint32_t i=0; i<table->numOfColumns; i++) {
-
-            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 col;
-    for (uint32_t i=0; i<table->numOfColumns; i++) {
-
-        readStatus = 0;
-        writeStatus = 0;
-
-        if (!table->columns[i].usingDefault) {
-
-            col = i+1;
-
-            switch (table->columns[i].ippType) {
-
-                case TBYTE:
-                    if (fromHeader) {
-                        fits_read_key(fitsIn, TBYTE, table->columns[i].ippName, &int8col[0], NULL, &readStatus);
-                        if (!isfinite(int8col[0])) int8col[0] = int8null;
-                    }
-                    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);
-                    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;
-                    }
-                    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);
-                    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;
-                    }
-                    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);
-                    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;
-                    }
-                    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);
-                    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;
-                    }
-                    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);
-                    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;
-                    }
-                    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);
-                    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);
-                    break;
-
-                default:
-                    psError(PS_ERR_IO, false, "Don't know IPP type (%d) for mapping from  '%d'  '%s' to '%s'", table->columns[i].ippType,
-                            table->columns[i].ippColNum, table->columns[i].ippName, table->columns[i].pspsName);
-                    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);
-            //}
-        }
-    }
-
-    free(int8col);
-    free(int16col);
-    free(longcol);
-    free(floatcol);
-    free(doublecol);
-    for (uint32_t i=0; i<nRows;i++) free(strcol[i]);
-    free(strcol);
-
-    return true;
-}
-
-// creates and populates a table
-bool ippToPspsConfig_writeTable(
-        IppToPspsConfig* this, 
-        fitsfile* fitsIn,
-        fitsfile* fitsOut,
-        const long nRows,
-        const char* tableName,
-        const bool fromHeader) {
-
-
-    IppToPspsConfig_Table* table = ippToPspsConfig_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 (this != NULL ) {
-
-        // TODO check logic here
-        for (int i=0;i<this->numOfTables;i++)
-            free(this->tables[i].columns);
-
-        free(this->tables);
-        psFree(this->configsPath);
-        free(this);
-    }
-}
-
-// Constructor. Loads IPP -> PSPS mappings from a csv
-IppToPspsConfig* ippToPspsConfig_Constructor(const char* path) {
-
-    IppToPspsConfig* this = NULL;
-
-    this = malloc(sizeof(IppToPspsConfig));
-
-    this->configsPath = NULL;
-    psStringAppend(&this->configsPath, path);
-
-    // load tables descriptions from XML
-    if (ippToPspsConfig_loadTableDescriptions(this)) {
-
-        // load any mappings we may have from XML        
-        ippToPspsConfig_loadMappings(this);
-    }
-
-    return this;
-}
-
Index: branches/eam_branches/ipp-20110213/ippToPsps/src/Config.h
===================================================================
--- branches/eam_branches/ipp-20110213/ippToPsps/src/Config.h	(revision 30812)
+++ 	(revision )
@@ -1,82 +1,0 @@
-/** @file ippToPspsConfig.h
- *
- *  @brief ippToPsps
- *
- *  @ingroup ippToPsps
- *
- *  @author IfA
- *  Copyright 2009 Institute for Astronomy, University of Hawaii
- */
-
-#ifndef IPPTOPSPSCONFIG_H
-#define IPPTOPSPSCONFIG_H
-
-#include <psmodules.h>
-
-// column class
-typedef struct {
-
-    int ippColNum;
-    char ippName[100];
-    int ippType;
-    long ippRepeat;
-    char pspsName[100]; // TODO change to 'name'
-    int pspsType;
-    bool usingDefault;
-    char defaultValue[100];
-    char comment[300];
-
-} IppToPspsConfig_Column;
-
-// table class
-typedef struct {
-
-    char name[32];
-    IppToPspsConfig_Column* columns;
-    int numOfColumns;
-
-} IppToPspsConfig_Table;
-
-//  
-typedef struct {
-
-    psString configsPath;
-    IppToPspsConfig_Table* tables;
-    int numOfTables;
-
-} IppToPspsConfig; 
-
-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);
-
-// 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);
-
-bool ippToPspsConfig_getColumnVector(
-        int col,
-        long row,
-        int type,
-        int repeat,
-        float* vector,
-        fitsfile *fitsIn);
-
-
-
-
-#endif // IPPTOPSPSCONFIG_H
-
Index: branches/eam_branches/ipp-20110213/ippToPsps/src/DetectionBatch.c
===================================================================
--- branches/eam_branches/ipp-20110213/ippToPsps/src/DetectionBatch.c	(revision 30812)
+++ branches/eam_branches/ipp-20110213/ippToPsps/src/DetectionBatch.c	(revision 31083)
@@ -1,4 +1,7 @@
 #include "DetectionBatch.h"
 #include "DetectionBatchEnums.h"
+
+#include "Fits.h"
+
 
 /**
@@ -18,4 +21,23 @@
 
 /**
+  Writes results to XML file
+  */
+static bool writeResults(DetectionBatch* this, long minObjID, long maxObjID, long totalDetectionsOut) {
+
+    if (!this->base.resultsXmlDoc) return false;
+
+    xmlNodePtr rootNode = xmlDocGetRootElement(this->base.resultsXmlDoc);
+    char tmp[100];
+    sprintf(tmp, "%ld", minObjID);
+    xmlNewChild(rootNode, NULL, BAD_CAST "minObjID", BAD_CAST tmp);
+    sprintf(tmp, "%ld", maxObjID);
+    xmlNewChild(rootNode, NULL, BAD_CAST "maxObjID", BAD_CAST tmp);
+    sprintf(tmp, "%ld", totalDetectionsOut);
+    xmlNewChild(rootNode, NULL, BAD_CAST "totalDetections", BAD_CAST tmp);
+
+    return true;
+}
+
+/**
   Does the work. Loops through the provided smf files, tallies the contents with the DVO database already connected to and generates a new FITS file of the fomat required by PSPS (i.e. a binary table per PSPS database table)
   */
@@ -24,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], this->base.logger);
+    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.fitsGenerator->createAndPopulateTable(this->base.fitsGenerator, 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.initData->getFilterId(this->base.initData, 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
@@ -88,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));
@@ -110,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));
@@ -119,5 +121,8 @@
         filterIDs[s] = filterID;
         surveyIDs[s] = this->base.surveyID;
-        strcpy(assocDate[s], this->base.todaysDate);
+
+        // 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);
     }
 
@@ -133,8 +138,31 @@
     int ippIDetNum, instMagNum, instMagErrNum, peakMagNum;
     bool error = false;
-
-    // loop round all 60 chips
-    for (int x=0; x<8; x++) {
-        for (int y=0; y<8; y++) {
+    int startX, stopX, startY, stopY;
+
+    // in test mode, only run for chip 33
+    if (this->base.testMode) {
+
+        startX = 3;
+        stopX = 4;
+        startY = 3;
+        stopY = 4;
+    }
+    // in 'normal' mode, run for all chips
+    else {
+    
+        startX = 0;
+        stopX = 8;
+        startY = 0;
+        stopY = 8;
+    }
+
+    this->base.logger->print(this->base.logger, MSG_INFO, "DetectionBatch",  
+            "+-----------+---------+----------+----------------+--------------+--------------+\n");
+    this->base.logger->print(this->base.logger, MSG_INFO, "DetectionBatch",  
+            "| Extension | Rows in | Rows out |  Duplicate IDs | Invalid Flux | Bogus det ID |\n");
+
+    // loop round chips
+    for (int x=startX; x<stopX; x++) {
+        for (int y=startY; y<stopY; y++) {
 
             // dodge the corners
@@ -146,33 +174,26 @@
             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);
-            status=0;
-            if (fits_movnam_hdu(fitsIn, BINARY_TBL, extensionName, 0, &status)) {
-                psError(PS_ERR_IO, false, "Can't move to extension: %s skipping this chip\n", extensionName);
-                continue;
-            }
+            if (!fitsIn->moveToBinaryTable(fitsIn, extensionName)) continue;
 
             // move to header extension
             sprintf(extensionName, "XY%s.hdr", ccdNumber);
-            status=0;
-            if (fits_movnam_hdu(fitsIn, IMAGE_HDU, extensionName, 0, &status)) {
-                psError(PS_ERR_IO, false, "Can't move to extension: %s skipping this chip\n", extensionName);
-                continue;
-            }
+            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);
             if (skylist == NULL) {
-                psError(PS_ERR_IO, false,
+                this->base.logger->print(this->base.logger, MSG_ERROR, "DetectionBatch",  
                         "DVO: can't find SkyList for sourceId='%d' imageId='%d' (CCD = XY%s): skipping this chip\n",
                         sourceId, imageId, ccdNumber);
@@ -189,6 +210,5 @@
             if (numDvoDetections > this->MAXDETECT ) {
 
-                psError(PS_ERR_IO, false,
-                        " Number of detections (%d) exceeds max limit (%ld)\n",
+                this->base.logger->print(this->base.logger, MSG_ERROR, "DetectionBatch", "Number of detections (%d) exceeds max limit (%ld)\n",
                         numDvoDetections, this->MAXDETECT);
                 error = true;
@@ -197,58 +217,46 @@
 
             // create ImageMeta
-            ippToPspsConfig_writeTable(this->base.config, fitsIn, this->base.fitsOut, 1, "ImageMeta", true);
+            this->base.fitsGenerator->createAndPopulateTable(this->base.fitsGenerator, 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);
-            status=0;
-            if (fits_movnam_hdu(fitsIn, BINARY_TBL, extensionName, 0, &status)) {
-                psError(PS_ERR_IO, false, "Can't move to extension: %s skipping\n", extensionName);
-                continue;
-            }
-
-            // keep a running count of 'images' we find in order to write total to FrameMeta at the end
+            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
@@ -260,4 +268,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)
@@ -312,5 +321,5 @@
 
                     // check for invalid flux values, and skip them
-                    if ( !peakFluxOk || !instFluxOk) {
+                    if (!peakFluxOk || !instFluxOk) {
                         removeList[numOfDuplicates+numInvalidFlux+numOfInvalidIppIDet] = s+1;
                         numInvalidFlux++;
@@ -327,5 +336,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;
@@ -335,41 +344,36 @@
 
                 // 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.fitsGenerator->createAndPopulateTable(this->base.fitsGenerator, 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.fitsGenerator->createAndPopulateTable(this->base.fitsGenerator, 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.fitsGenerator->createAndPopulateTable(this->base.fitsGenerator, 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);
             }
 
-            psLogMsg("ippToPsps", PS_LOG_INFO,
-                    "+-----------+---------+----------+----------------+--------------+--------------+\n"
-                    "| Extension | Rows in | Rows out |  Duplicate IDs | Invalid Flux | Bogus det ID |\n"
+            this->base.logger->print(this->base.logger, MSG_INFO, "DetectionBatch",
                     "|  %s |  %5ld  |   %5d  |     %5d      |    %5d     |    %5d     |\n",
                     extensionName, nChipDetectionsIn, nChipDetectionsOut, numOfDuplicates, numInvalidFlux, numOfInvalidIppIDet);
@@ -384,5 +388,8 @@
     }
 
-    psLogMsg("ippToPsps", PS_LOG_INFO, "Total detections for this exposure = %ld\n", totalDetectionsOut);
+    this->base.logger->print(this->base.logger, MSG_INFO, "DetectionBatch",  
+            "+-----------+---------+----------+----------------+--------------+--------------+\n");
+
+    this->base.logger->print(this->base.logger, MSG_INFO, "DetectionBatch", "Total detections for this exposure = %ld\n", totalDetectionsOut);
 
     // free-up memory
@@ -408,27 +415,15 @@
 
     // write number of images we have found into FrameMeta table
-    status=0;
-    if (fits_movnam_hdu(this->base.fitsOut, BINARY_TBL, "FrameMeta", 0, &status))
-        psError(PS_ERR_IO, false, "Can't move back to FrameMeta extension to write number of OTAs\n");
-    else {
-        fits_write_col(this->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);
-    // write results
-    if (this->base.resultsXmlDoc) {
-
-        xmlNodePtr rootNode = xmlDocGetRootElement(this->base.resultsXmlDoc);
-        char tmp[100];
-        sprintf(tmp, "%ld", minObjID);
-        xmlNewChild(rootNode, NULL, BAD_CAST "minObjID", BAD_CAST tmp);
-        sprintf(tmp, "%ld", maxObjID);
-        xmlNewChild(rootNode, NULL, BAD_CAST "maxObjID", BAD_CAST tmp);
-        sprintf(tmp, "%ld", totalDetectionsOut);
-        xmlNewChild(rootNode, NULL, BAD_CAST "totalDetections", BAD_CAST tmp);
-    }
-
-    psLogMsg("ippToPsps", PS_LOG_INFO, "Data written for a total of %d chips/OTAs", nOta);
+    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);
+
+    this->base.logger->print(this->base.logger, MSG_INFO, "DetectionBatch", "Data written for a total of %d chips/OTAs\n", nOta);
 
     if (error || nOta < 1) this->base.exitCode = PS_EXIT_DATA_ERROR;
@@ -438,5 +433,4 @@
 }
 
-
 /**
   Print-out this class. Calls base-calls print method first.
@@ -446,7 +440,7 @@
     this->base.print(&this->base);
 
-    printf("* exp ID          : %d\n", this->expId);
-    printf("* exp name        : '%s'\n",  this->expName ? this->expName : "undef");
-    printf("\n");
+    this->base.logger->print(this->base.logger, MSG_INFO, "DetectionBatch", "exp ID          : %d\n", this->expId);
+    this->base.logger->print(this->base.logger, MSG_INFO, "DetectionBatch", "exp name        : %s\n",  this->expName ? this->expName : "undef");
+    this->base.logger->print(this->base.logger, MSG_INFO, "DetectionBatch", "\n");
 }
 
@@ -480,5 +474,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 (
@@ -491,5 +487,5 @@
             !haveExpName) {
 
-        printf("\n* ERROR with supplied arguments:");
+        this->base.logger->print(this->base.logger, MSG_ERROR, "DetectionBatch", "Problem with supplied arguments\n");
         this->print(this);
         this->base.exitCode = PS_EXIT_CONFIG_ERROR;
@@ -503,17 +499,15 @@
   Constructor. Returns a new DetectionBatch object.
   */
-DetectionBatch* new_DetectionBatch(int *argc, char **argv) {
-
+DetectionBatch* new_DetectionBatch(Logger* logger, int *argc, char **argv) {
+
+    logger->print(logger, MSG_DEBUG, "DetectionBatch", "Constructor\n");
     DetectionBatch *this = (DetectionBatch*)calloc(1, sizeof(DetectionBatch));
 
     // call base-class constructor
-    if (!new_Batch(&this->base)) {
-
-        psError(PS_ERR_IO, false, "Unable to create Batch base-class object");
-        return this;
-    }
+    new_Batch(logger, &this->base);
 
     this->MAXDETECT = 200000;
 
+    // method pointers
     this->print = print;
     this->destroy = destroy;
@@ -522,10 +516,7 @@
     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);
+
+    this->print(this);
 
     return this;
@@ -537,27 +528,19 @@
 int main(int argc, char **argv) {
 
-    psTimerStart("detectionbatch");
-
-    ippToPsps_VersionPrint();
-
-    int exitCode;
-
-    DetectionBatch* detectionBatch = new_DetectionBatch(&argc, argv);
+//    ippToPsps_VersionPrint();
+
+    Logger* logger = new_Logger(NULL, false);
+//    Logger* logger = new_Logger("./detBatchLog.txt", false);
+    logger->print(logger, MSG_INFO, "main", "Creating new detection batch\n");
+
+    DetectionBatch* detectionBatch = new_DetectionBatch(logger, &argc, argv);
     detectionBatch->base.run(detectionBatch);
-    exitCode = detectionBatch->base.exitCode;
+    int exitCode = detectionBatch->base.exitCode;
 
     detectionBatch->destroy(detectionBatch);
 
-    double secs = psTimerMark("ippToPsps");
-
-    psLogMsg("detectionbatch", 3, "detectionbatch completed %ssuccessfully, with exit code %d, in %.1f %s\n",
-            (exitCode == PS_EXIT_SUCCESS) ? "" : "un",
-            exitCode,
-            (secs<60.0) ? secs : (secs/60.0),
-            (secs<60.0) ? "sec(s)" : "min(s)");
-
     // tidy up
-    psTimerStop();
     psLibFinalize();
+    logger->destroy(logger);
 
     return exitCode;
Index: branches/eam_branches/ipp-20110213/ippToPsps/src/DetectionBatch.h
===================================================================
--- branches/eam_branches/ipp-20110213/ippToPsps/src/DetectionBatch.h	(revision 30812)
+++ branches/eam_branches/ipp-20110213/ippToPsps/src/DetectionBatch.h	(revision 31083)
@@ -31,5 +31,5 @@
 } DetectionBatch;
 
-DetectionBatch *new_DetectionBatch(int *argc, char **argv);
+DetectionBatch *new_DetectionBatch(Logger* logger, int *argc, char **argv);
 
 # endif // IPPTOPSPS_DETECTIONBATCH_H 
Index: branches/eam_branches/ipp-20110213/ippToPsps/src/Fits.c
===================================================================
--- branches/eam_branches/ipp-20110213/ippToPsps/src/Fits.c	(revision 31083)
+++ branches/eam_branches/ipp-20110213/ippToPsps/src/Fits.c	(revision 31083)
@@ -0,0 +1,450 @@
+/** @file Fits.c
+ *
+ *  @ingroup ippToPsps
+ *
+ *  Source for all methods in the Fits class
+ *
+ *  @author IfA
+ *  Copyright 2011 Institute for Astronomy, University of Hawaii
+ */
+
+#include "Fits.h"
+
+/**
+  Non-class method
+
+  Gets cfitsio type from string
+  */
+int Fits_getDataType(char *typename) {
+
+    if (!strncmp(typename, "TBYTE", 5))  return TBYTE;
+    if (!strncmp(typename, "TSHORT", 6)) return TSHORT;
+    if (!strncmp(typename, "TLONGLONG", 9)) return TLONGLONG;
+    if (!strncmp(typename, "TLONG", 5)) return TLONG;
+    if (!strncmp(typename, "TFLOAT", 6)) return TFLOAT;
+    if (!strncmp(typename, "TDOUBLE", 7)) return TDOUBLE;
+    if (!strncmp(typename, "TSTRING", 7)) return TSTRING;
+
+//    this->logger->print(this->logger, MSG_ERROR, "Fits", "Don't understand data type '%s'", typename);
+
+    return 0;
+}
+
+/**
+  Destructor
+  */
+static void destroy(Fits* this) {
+
+    this->logger->print(this->logger, MSG_DEBUG, "Fits", "Destructor\n");
+
+    int status = 0;
+    if (fits_close_file(this->file, &status)) {
+
+        this->logger->print(this->logger, MSG_ERROR, "Fits", "Unable to close FITS file '%s'\n", this->path);
+        fits_report_error(stderr, status);
+    }
+    else 
+        this->logger->print(this->logger, MSG_DEBUG, "Fits", "Closed FITS file '%s'\n", this->path);
+
+    free(this);
+}
+
+/**
+  Gets a header key value
+  */
+static bool getHeaderKeyValue(Fits* this, int type, char* name, void* value) {
+
+    int status = 0;
+    fits_read_key(this->file, type, name, value, NULL, &status);
+
+    if (status) {
+
+        this->logger->print(this->logger, MSG_ERROR, "Fits", "Could not get value for header key '%s' from file at '%s'\n", name, this->path);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+   Gets the contents of a column vector
+   */
+static bool getColumnVector(
+        Fits* this,
+        int col,
+        long row,
+        int type,
+        int repeat,
+        float* vector) {
+
+    int status = 0;
+    int anynull = 0;
+
+    // remember to add 1 to row number since cfitsio is the stupidest libary ever written
+    fits_read_col(this->file, type, col, row+1, 1, repeat, NULL, vector, &anynull, &status);
+
+    if (status) {
+
+        this->logger->print(this->logger, MSG_ERROR, "Fits", "Failed to read vector column %d row %ld\n", col, row);
+        return false;
+    }
+
+    return true;
+}
+
+/**
+  Gets the column number for the provided column name
+  */
+static bool getColumnNumber(Fits* this, char* name, int* number) {
+
+    int status = 0;
+    fits_get_colnum(this->file, CASESEN, name, number, &status);
+    if (status) {
+
+        this->logger->print(this->logger, MSG_ERROR, "Fits", "Could not get column number for '%s'\n", name);
+        return false;
+    }
+
+    return true;
+}
+
+/*
+   Gets metadata about a column 
+   */
+bool getColumnMeta(
+        Fits* this,
+        char* name,
+        int* colNum,
+        int* type,
+        long* repeat) {
+
+    int status = 0;
+    fits_get_colnum(this->file, CASESEN, name, colNum, &status);
+    if (status) {
+
+        this->logger->print(this->logger, MSG_ERROR, "Fits", "Unable to read col '%s'\n", name);
+        return false;
+    }
+
+    status = 0;
+    fits_get_eqcoltype(this->file, *colNum, type, repeat, NULL, &status);
+    if (status) {
+
+        this->logger->print(this->logger, MSG_ERROR, "Fits", "Unable to read type info for '%s'\n", name);
+        return false;
+    }
+
+    return true;
+}
+
+
+/**
+  Moves to a certain extension of provided name and type
+  */
+bool moveToExtension(Fits* this, char* name, int type) {
+
+    int status = 0;
+    if (fits_movnam_hdu(this->file, type, name, 0, &status)) {
+
+        this->logger->print(this->logger, MSG_ERROR, "Fits", "Can't move to table extension named '%s'\n", name);
+        return false;
+    }
+
+    return true;
+}
+
+/**
+  Moves to a certain header extension
+  */
+static bool moveToHeader(Fits* this, char* name) {
+
+    return moveToExtension(this, name, IMAGE_HDU);
+}
+
+/**
+  Moves to a certain binary table extension
+  */
+static bool moveToBinaryTable(Fits* this, char* name) {
+
+    return moveToExtension(this, name, BINARY_TBL);
+}
+
+/**
+  Counts the rows in the current table extension
+  */
+static bool countRows(Fits* this, long* count) {
+
+    int status = 0;
+    if (fits_get_num_rows(this->file, count, &status)) {
+
+        this->logger->print(this->logger, MSG_ERROR, "Fits", "Count not count rows in this table\n");
+        return false;
+    }
+
+    return true;
+}
+
+/**
+  Moves to a certain binary table extension and counts rows within
+  */
+static bool moveToBinaryTableAndCountRows(Fits* this, char* name, long* count) {
+
+    if (!moveToExtension(this, name, BINARY_TBL)) return false;
+    return countRows(this, count);
+}
+
+/**
+  Deletes this file.
+  */
+static bool delete(Fits* this) {
+
+    if (remove(this->path) == -1) {
+
+        this->logger->print(this->logger, MSG_ERROR, "Fits", "Unable to delete '%s'\n", this->path);
+        return false;
+    }
+
+    this->logger->print(this->logger, MSG_INFO, "Fits", "Deleted FITS file at '%s'\n", this->path);
+
+    return true;
+}
+
+/**
+  Returns the file path.
+  */
+static char* getPath(Fits* this) {
+
+    return this->path;
+}
+
+/**
+  Returns the file pointer.
+  */
+static fitsfile* getFilePtr(Fits* this) {
+
+    return this->file;
+}
+
+/**
+  Deletes a bunch of rows from the current binary table
+  */
+static bool deleteRows(Fits* this, long *rowlist, long nrows) {
+
+    int status = 0;
+    fits_delete_rowlist(this->file, rowlist, nrows, &status);
+    if (status) {
+
+        this->logger->print(this->logger, MSG_ERROR, "Fits", "Unable to delete rows using rowlist\n");
+        return false;
+    }
+
+    return true;
+}
+
+/**
+  Reads a column from the current binary table
+  */
+static bool readColumn(
+        Fits* this, 
+        int datatype, 
+        int colnum, 
+        LONGLONG firstrow, 
+        LONGLONG firstelem,
+        LONGLONG nelements, 
+        void* coldata)  {
+
+    int anynull = 0;
+    int status = 0;
+
+    if (datatype == TBYTE) {
+
+        int8_t nullval = -99;
+        fits_read_col(this->file, datatype, colnum, firstrow, firstelem, nelements, &nullval, coldata, &anynull, &status);
+    }
+    else if (datatype == TSHORT) {
+
+        int16_t nullval = -999;
+        fits_read_col(this->file, datatype, colnum, firstrow, firstelem, nelements, &nullval, coldata, &anynull, &status);
+    }
+    else if (datatype == TLONG || datatype == TLONGLONG) {
+
+        long nullval = -999;
+        fits_read_col(this->file, datatype, colnum, firstrow, firstelem, nelements, &nullval, coldata, &anynull, &status);
+    }
+    else if (datatype == TFLOAT) {
+
+        float nullval = -999.0;
+        fits_read_col(this->file, datatype, colnum, firstrow, firstelem, nelements, &nullval, coldata, &anynull, &status);
+    }
+    else if (datatype == TDOUBLE) {
+
+        double nullval = -999.0;
+        fits_read_col(this->file, datatype, colnum, firstrow, firstelem, nelements, &nullval, coldata, &anynull, &status);
+    }
+    else if (datatype == TSTRING) {
+
+        char nullval[20];
+        strcpy(nullval, "null");
+        fits_read_col(this->file, datatype, colnum, firstrow, firstelem, nelements, &nullval, coldata, &anynull, &status);
+    }
+    else {
+
+        this->logger->print(this->logger, MSG_ERROR, "Fits", "Don't understand datatype '%d'\n", datatype);
+        return false;
+    }
+
+    if (status) {
+
+        this->logger->print(this->logger, MSG_ERROR, "Fits", "Unable to read column data from column %d\n", colnum);
+        return false;
+
+    }
+
+    return true;
+}
+
+/**
+  Reads a column from the current binary table providing column name
+  */
+static bool readColumnUsingName(
+        Fits* this,
+        int datatype, 
+        char* colname,
+        LONGLONG firstrow, 
+        LONGLONG firstelem,
+        LONGLONG nelements, 
+        void* coldata)  {
+
+    int colnum;
+    if (!getColumnNumber(this, colname, &colnum)) return false;
+
+    return  readColumn(this, datatype, colnum, firstrow, firstelem, nelements, coldata);
+}
+
+/**
+  Writes a column to the current binary table
+  */
+static bool writeColumn(
+        Fits* this, 
+        int datatype, 
+        int colnum, 
+        LONGLONG firstrow,
+        LONGLONG firstelem, 
+        LONGLONG nelements, 
+        void* coldata) { 
+
+    int status = 0;
+    fits_write_col(this->file, datatype, colnum, firstrow, firstelem, nelements, coldata, &status);
+    if (status) {
+
+        this->logger->print(this->logger, MSG_ERROR, "Fits", "Unable to write column data for column %d\n", colnum);
+        return false;
+
+    }
+
+    return true;
+}
+
+/**
+  Creates a new binary table
+  */
+static bool createBinaryTable(
+        Fits* this, 
+        LONGLONG nRows, 
+        int nCols, 
+        char *names[],
+        char *types[], 
+        char *name) {
+
+    int status = 0;
+    fits_create_tbl(this->file, BINARY_TBL, nRows, nCols, names, types, NULL, name, &status);
+
+    if (status) {
+
+        this->logger->print(this->logger, MSG_ERROR, "Fits", "Unable to create table: '%s'\n", name);
+        return false;
+    }
+
+
+    return true;
+}
+
+/**
+  Initilization common to all constructors
+  */
+static Fits* init(const char* path, Logger* logger) {
+
+    Fits* this = (Fits*)calloc(1, sizeof(Fits));
+
+    // fields
+    strcpy(this->path, path);
+    this->logger = logger;
+
+    // method pointers
+    this->getFilePtr = getFilePtr;
+    this->getPath = getPath;
+    this->countRows = countRows;
+    this->getColumnMeta = getColumnMeta;
+    this->getColumnNumber = getColumnNumber;
+    this->getColumnVector = getColumnVector;
+    this->readColumn = readColumn;
+    this->readColumnUsingName = readColumnUsingName;
+    this->getHeaderKeyValue = getHeaderKeyValue;
+    this->moveToExtension = moveToExtension;
+    this->moveToHeader = moveToHeader;
+    this->moveToBinaryTable = moveToBinaryTable;
+    this->moveToBinaryTableAndCountRows = moveToBinaryTableAndCountRows;
+    this->deleteRows = deleteRows;
+    this->createBinaryTable = createBinaryTable;
+    this->writeColumn = writeColumn;
+    this->delete = delete;
+    this->destroy = destroy;
+
+    assert(this);
+
+    return this;
+}
+
+/**
+  Constructor.
+
+  Returns a new Fits object representing an existing FITS file.
+
+*/
+Fits* existing_Fits(const char* path, Logger* logger) {
+
+    Fits* this = init(path, logger);
+    this->logger->print(this->logger, MSG_DEBUG, "Fits", "Constructor for existing file at '%s'\n", path);
+
+    int status = 0;
+    if (fits_open_file(&this->file, path, READONLY, &status)) {
+
+        this->logger->print(this->logger, MSG_ERROR, "Fits", "Unable to open FITS file here %s\n", path);
+        this->file = NULL;
+    }
+
+
+    return this;
+}
+
+/**
+  Constructor. 
+
+  Returns a new Fits object representing a new FITS file.
+  */
+Fits* new_Fits(const char* path, Logger* logger) {
+
+    Fits* this = init(path, logger);
+    this->logger->print(this->logger, MSG_DEBUG, "Fits", "Constructor for new file at '%s'\n", path);
+
+    int status = 0;
+    if (fits_create_file(&this->file, path, &status)) {
+
+        this->logger->print(this->logger, MSG_ERROR, "Fits", "Unable to create FITS file here '%s'\n", path);
+        this->file = NULL;
+    }
+
+    return this;
+}
+
+
Index: branches/eam_branches/ipp-20110213/ippToPsps/src/Fits.h
===================================================================
--- branches/eam_branches/ipp-20110213/ippToPsps/src/Fits.h	(revision 31083)
+++ branches/eam_branches/ipp-20110213/ippToPsps/src/Fits.h	(revision 31083)
@@ -0,0 +1,64 @@
+/** @file Fits.h
+ *
+ *  @brief An encapsulation of necessary cfitsio functions
+ *
+ *  @ingroup ippToPsps
+ *
+ *  @author IfA
+ *  Copyright 2011 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef IPPTOPSPS_FITS_H
+#define IPPTOPSPS_FITS_H
+
+#include <psmodules.h>
+
+#include "Logger.h"
+
+/**
+
+  Class that encapsulates a FITS file, making an OO interface to the dreaded cfitsio
+
+  */
+typedef struct Fits {
+
+    // fields
+    fitsfile* file;             // cfitsio file pointer
+    char path[1000];            // FITS path
+    Logger* logger;             // logger object
+
+    // accessor methods
+    fitsfile* (*getFilePtr)();
+    int (*getDataType)();
+    char* (*getPath)();
+    bool (*countRows)();
+    bool (*getColumnMeta)();
+    bool (*getColumnNumber)();
+    bool (*getColumnVector)();
+    bool (*readColumn)();
+    bool (*readColumnUsingName)();
+    bool (*getHeaderKeyValue)();
+
+    // mutators
+    bool (*moveToExtension)();
+    bool (*moveToHeader)();
+    bool (*moveToBinaryTable)();
+    bool (*moveToBinaryTableAndCountRows)();
+    bool (*createBinaryTable)();
+    bool (*writeColumn)();
+    bool (*deleteRows)();
+    bool (*delete)();
+
+    // destructor
+    void (*destroy)();
+
+} Fits;
+
+// constructors
+Fits* existing_Fits(const char* path, Logger* logger);
+Fits* new_Fits(const char* path, Logger* logger);
+
+// non-class methods
+int Fits_getDataType(char *typename);
+
+# endif // IPPTOPSPS_FITS_H 
Index: branches/eam_branches/ipp-20110213/ippToPsps/src/FitsGenerator.c
===================================================================
--- branches/eam_branches/ipp-20110213/ippToPsps/src/FitsGenerator.c	(revision 31083)
+++ branches/eam_branches/ipp-20110213/ippToPsps/src/FitsGenerator.c	(revision 31083)
@@ -0,0 +1,781 @@
+/** @file FitsGenerator.c
+ *
+ *  @ingroup ippToPsps
+ *
+ *  @author IfA
+ *  Copyright 2011 Institute for Astronomy, University of Hawaii
+ */
+
+#include "FitsGenerator.h"
+#include <ctype.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+#include "Fits.h"
+
+/**
+  Finds a column within this table
+  */
+static Column* findColumn(FitsGenerator* this, Table* table, const char* name) {
+
+    Column* column = NULL;
+
+    for (uint32_t i=0; i<table->numOfColumns; i++) {
+
+        if (strcmp(table->columns[i].pspsName, name) == 0) {
+            column = table->columns+i;
+            break;
+        }
+    }
+
+    if (!column) this->logger->print(this->logger, MSG_ERROR, "FitsGenerator", 
+            "Could not find column '%s' in table '%s'\n", name, table->name);
+
+    return column;
+}
+
+/**
+  Finds a table from the array of tables
+  */
+static Table* findTable(FitsGenerator* this, const char* name) {
+
+    Table* table = NULL;
+
+    for (uint32_t i=0; i<this->numOfTables; i++) {
+
+        if (strcmp(this->tables[i].name, name) == 0) {
+            table = this->tables+i;
+            break;
+        }
+    }
+
+    if (!table) this->logger->print(this->logger, MSG_ERROR, "FitsGenerator", 
+            "Could not find table '%s'\n", name);
+
+    return table;
+}
+
+/**
+  Gets an attribute value from XML node
+  */
+static bool getAttribute(xmlNode* node, const char* attName, char* _value) {
+
+    xmlChar* value = xmlGetProp(node, (const xmlChar*)attName);
+    if (!value) return false;
+    sprintf(_value, "%s", value);
+    xmlFree(value);
+
+    return true;
+}
+
+/**
+  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;
+    if (nRows < 1) return false;
+
+    long nCols = table->numOfColumns;
+
+    char** colNames = (char**)calloc(nCols, sizeof(char*));
+    char** colTypes = (char**)calloc(nCols, sizeof(char*));
+
+    uint32_t i = 0;
+    for (i=0;i<nCols;i++) colTypes[i] = (char*)calloc(5,sizeof(char));
+
+    i = 0;
+    for (uint32_t m=0; m<table->numOfColumns; m++) {
+
+        colNames[i] = table->columns[i].pspsName;
+
+        //        sprintf(colNames[i], table->columns[i].pspsName);
+
+        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;
+
+            default:
+                          break;
+        }
+
+        i++;
+    }
+
+    fitsOut->createBinaryTable(fitsOut, nRows, nCols, colNames, colTypes, table->name);
+
+    for (i=0;i<nCols;i++) free(colTypes[i]);
+    free(colTypes);
+    free(colNames);
+
+    // now insert defaults
+    int8_t* int8col = (int8_t*)calloc(nRows, sizeof(int8_t));
+    int16_t* int16col = (int16_t*)calloc(nRows, sizeof(int16_t));
+    long* longcol = (long*)calloc(nRows, sizeof(long));
+    float* floatcol = (float*)calloc(nRows, sizeof(float));
+    double* doublecol = (double*)calloc(nRows, sizeof(double));
+    char** strcol = (char**)calloc(nRows, sizeof(char**));
+    for (uint32_t i=0; i<nRows;i++)
+        strcol[i] = (char*)calloc(20,sizeof(char)); // TODO 20? size issue
+
+    uint32_t j;
+    int64_t tempLong;
+    double tempDouble;
+    char tempStr[20];
+
+    int col;
+    for (uint32_t i=0; i<table->numOfColumns; i++) {
+
+        col = i+1;
+
+        strcpy(tempStr,table->columns[i].defaultValue);
+        tempLong = atol(tempStr);
+        tempDouble = atof(tempStr);
+
+        switch (table->columns[i].pspsType) {
+
+            case TBYTE:
+                for(j=0;j<nRows;j++) int8col[j] = (int8_t)tempLong;
+                fitsOut->writeColumn(fitsOut, TBYTE, col, 1, 1, nRows, int8col);
+                break;
+            case TSHORT:
+                for(j=0;j<nRows;j++) int16col[j] = (int16_t)tempLong;
+                fitsOut->writeColumn(fitsOut, TSHORT, col, 1, 1, nRows, int16col);
+                break;
+            case TLONG:
+                for(j=0;j<nRows;j++) longcol[j] = (long)tempLong;
+                fitsOut->writeColumn(fitsOut, TLONG, col, 1, 1, nRows, longcol);
+                break;
+
+            case TLONGLONG:
+                for(j=0;j<nRows;j++) longcol[j] = tempLong;
+                fitsOut->writeColumn(fitsOut, TLONGLONG, col, 1, 1, nRows, longcol);
+                break;
+
+            case TFLOAT:
+                for(j=0;j<nRows;j++) floatcol[j] = (float)tempDouble;
+                fitsOut->writeColumn(fitsOut, TFLOAT, col, 1, 1, nRows, floatcol);
+                break;
+
+            case TDOUBLE:
+                for(j=0;j<nRows;j++) doublecol[j] = tempDouble;
+                fitsOut->writeColumn(fitsOut, TDOUBLE, col, 1, 1, nRows, doublecol);
+                break;
+
+            case TSTRING:
+                for(j=0;j<nRows;j++) strcpy(strcol[j], tempStr);
+                fitsOut->writeColumn(fitsOut, TSTRING, col, 1, 1, nRows, strcol);
+                break;
+
+            default:
+                break;
+        }
+    }
+
+    free(int8col);
+    free(int16col);
+    free(longcol);
+    free(floatcol);
+    free(doublecol);
+    for (uint32_t i=0; i<nRows;i++) free(strcol[i]);
+    free(strcol);
+
+    return true;
+}
+
+/*
+   Counts number of children with the provided name
+   */
+static int countChildren(xmlNode* rootNode, const char* name) {
+
+    int count = 0;
+    xmlNode* node = NULL;
+
+    for (node = rootNode->children; node; node = node->next) {
+
+        if (strcmp((const char*)node->name, name)==0) count++;
+    } 
+
+    return count;
+}
+
+/*
+   opens an XML file and returns the document
+   */
+static xmlDoc* openXmlFile(FitsGenerator* this, const char* path) {
+
+    xmlDoc* doc = xmlReadFile(path, NULL, 0);
+    if (doc == NULL) this->logger->print(this->logger, MSG_ERROR, "FitsGenerator", 
+            "Unable to open XML file at %s\n", path);
+    return doc;
+}
+
+/*
+   closes an XML file 
+   */
+static bool closeXmlFile(xmlDoc* doc) {
+
+    xmlFreeDoc(doc);
+    xmlCleanupParser();
+
+    return true;
+}
+
+/**
+  Populates the provided table with data from XML
+  */
+static bool populateTable(FitsGenerator* this, Table* table, xmlNode* rootElement, Fits *fitsOut) {
+
+    char tempStr[100];
+    xmlNode* node = NULL;
+    xmlNode* tableNode = NULL;
+
+    // first check we have data for this table in XML file
+    for (node = rootElement->children; node; node = node->next) {
+        if (node->type == XML_ELEMENT_NODE) {
+
+            if (strcmp((const char*)node->name, "table")!=0) continue;
+            if (!getAttribute(node, "name", tempStr)) continue;
+            if (strcmp(tempStr, table->name)==0) {tableNode = node; break;}
+        }
+    }
+
+    if(!tableNode) {
+
+        this->logger->print(this->logger, MSG_ERROR, "FitsGenerator", 
+                "Could not find table: %s\n", table->name);
+        return false;
+    }
+
+    // now count the number of rows
+    int nRows = countChildren(tableNode, "row");
+    if (nRows < 1) return false;
+    if (!createTable(table, fitsOut, nRows)) return false;
+
+    int rows=0;
+    int status;
+    char** strcol = (char**)calloc(1, sizeof(char**));
+    strcol[0] = (char*)calloc(32,sizeof(char)); // TODO 20? size issue
+
+    int8_t* bytecol = calloc(1, sizeof(uint8_t));
+    int16_t* shortcol = calloc(1, sizeof(uint16_t));
+    long* longcol = calloc(1, sizeof(long));
+    double* doublecol = calloc(1, sizeof(double));
+    float* floatcol = calloc(1, sizeof(float));
+
+    int col;
+
+    // now get rows
+    for (node = tableNode->children; node; node = node->next) {
+
+        if (strcmp((const char*)node->name, "row")!=0) continue;
+
+        rows++;
+
+        // get column data for this row
+        for (int i=0;i<table->numOfColumns;i++) {
+
+            col = i+1;
+
+            if (!getAttribute(node, table->columns[i].pspsName, tempStr)) continue;
+
+            strncpy(strcol[0], tempStr, 32);
+            status=0;
+            //psLogMsg("ippToPsps", PS_LOG_INFO, "Values %s - %ld - %f - %d", strcol[0], longcol[0], doublecol[0], bytecol[0]);
+            switch (table->columns[i].pspsType) {
+
+                case TBYTE:
+                    bytecol[0] = atoi(tempStr);
+                    fitsOut->writeColumn(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, bytecol); 
+                    break;
+                case TSHORT:
+                    shortcol[0] = atoi(tempStr);
+                    fitsOut->writeColumn(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, shortcol); 
+                    break;
+                case TLONG:
+                case TLONGLONG:
+                    longcol[0] = atol(tempStr);
+                    fitsOut->writeColumn(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, longcol); 
+                    break;
+                case TFLOAT:
+                    floatcol[0] = atof(tempStr);
+                    fitsOut->writeColumn(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, floatcol);
+                    break;
+                case TDOUBLE:
+                    doublecol[0] = atof(tempStr);
+                    fitsOut->writeColumn(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, doublecol);
+                    break;
+                case TSTRING:
+                    fitsOut->writeColumn(fitsOut, table->columns[i].pspsType, col, rows, 1, 1, strcol); 
+                    break;
+
+            }
+            if (status) this->logger->print(this->logger, MSG_ERROR, "FitsGenerator", 
+                    "Unable to write value of %s to column %s in table %s\n", 
+                    tempStr, table->columns[i].pspsName, table->name);
+        }
+    }
+
+    free(bytecol);
+    free(shortcol);
+    free(longcol);
+    free(floatcol);
+    free(doublecol);
+    free(strcol[0]);
+    free(strcol);
+
+    return true;
+}
+
+/**
+  Loads a table description from XML
+  */
+static bool loadTableDescription(FitsGenerator* this, Table* table, xmlNode* tableNode) {
+
+    bool ret = true;
+    char tableName[100];
+    getAttribute(tableNode, "name", tableName);
+    strcpy(table->name, tableName);
+
+    // count how many columns we have coming in and allocate memory for them
+    table->numOfColumns = countChildren(tableNode, "column");
+    table->columns = calloc(table->numOfColumns, sizeof(Column));
+    //    psLogMsg("ippToPsps", PS_LOG_INFO, " Found '%d' columns", table->numOfColumns);
+
+    char buffer[200];
+    xmlNode* node;
+    int columnNum = 0;
+
+    for (node = tableNode->children; node; node = node->next) {
+
+        if (strcmp((const char*)node->name, "column") != 0) continue;
+
+        table->columns[columnNum].usingDefault = true;
+
+        // column name
+        getAttribute(node, "name", buffer);
+        sprintf(table->columns[columnNum].pspsName, buffer);
+
+        // column type
+        getAttribute(node, "type", buffer);
+        table->columns[columnNum].pspsType = Fits_getDataType(buffer);
+
+        // default value
+        getAttribute(node, "default", buffer);
+        sprintf(table->columns[columnNum].defaultValue, buffer);
+
+        // comment
+        getAttribute(node, "comment", buffer);
+        sprintf(table->columns[columnNum].comment, buffer);
+
+        # if 0
+        psLogMsg("ippToPsps", PS_LOG_INFO, "'%s'  name='%s' type='%d'  default='%s' comment='%s'", 
+                table->name,
+                table->columns[columnNum].pspsName, 
+                table->columns[columnNum].pspsType, 
+                table->columns[columnNum].defaultValue, 
+                table->columns[columnNum].comment);
+# endif
+        columnNum++;
+    }   
+
+
+    if (columnNum < 1) {
+
+        this->logger->print(this->logger, MSG_ERROR, "FitsGenerator", 
+                "Found no columns for table '%s'\n", tableName);
+        ret = false;
+    }
+
+    if (columnNum != table->numOfColumns)
+        this->logger->print(this->logger, MSG_ERROR, "FitsGenerator", 
+                "Mismatch between number of columns expected (%d) and those found (%d)\n", 
+                table->numOfColumns, columnNum);
+
+    return ret;
+}
+
+/**
+  Reads table shapes from XML file
+  */
+static bool loadTableDescriptions(FitsGenerator* this) {
+
+    bool ret = true;
+
+    psString path = NULL;
+    psStringAppend(&path, "%s/tables.xml", this->configsPath);
+
+    xmlDoc* doc = openXmlFile(this, path);
+
+    if (doc == NULL) {
+
+        psFree(path);
+        return false;
+    }
+    psFree(path);
+
+    xmlNode* rootElement = xmlDocGetRootElement(doc);
+
+    if (strcmp((const char*)rootElement->name, "tableDescriptions")!=0) {
+
+        this->logger->print(this->logger, MSG_ERROR, "FitsGenerator", 
+                "Root node of XML is not 'tableDescriptions', as it should be\n");
+        return false;
+    }
+
+    // count how many tables we have coming in and allocate memory for them
+    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;
+    Table* table = NULL;
+
+    int tableNum = 0;
+    // loop round all available table descriptions
+    for (node = rootElement->children; node; node = node->next) {
+        if (node->type == XML_ELEMENT_NODE) {
+
+            if (strcmp((const char*)node->name, "table")==0) {
+
+                table = this->tables+tableNum;
+
+                if (!loadTableDescription(this, table, node)) ret = false;
+                else tableNum++;
+            }
+        }
+    }
+
+    if (tableNum != this->numOfTables)
+        this->logger->print(this->logger, MSG_ERROR, "FitsGenerator", 
+                "Mismatch between number of tables expected (%d) and those found (%d)\n", 
+                this->numOfTables, tableNum);
+
+    closeXmlFile(doc);
+
+    return ret;
+}
+
+/**
+  Loads mappings from XML for a particular table
+  */
+static bool loadTableMappings(FitsGenerator* this, xmlNode* tableNode) {
+
+    bool ret = false;
+
+    xmlNode* node = NULL;
+
+    char tableName[100];
+    getAttribute(tableNode, "name", tableName);
+
+    Table* table = findTable(this, tableName);
+    Column* column = NULL;
+    if (!table) return false;
+
+    char buffer[100];
+    //psLogMsg("ippToPsps", PS_LOG_INFO, "Table '%s'", tableName);
+
+    // loop round all available mappings for this table
+    for (node = tableNode->children; node; node = node->next) {
+        if (node->type == XML_ELEMENT_NODE) {
+
+            if (strcmp((const char*)node->name, "map")==0) {
+
+                // column type
+                getAttribute(node, "pspsName", buffer);
+                column = findColumn(this, table, buffer);
+
+                if (!column) continue;
+
+                // IPP name
+                getAttribute(node, "ippName", buffer);
+                strcpy(column->ippName, buffer);
+
+                // IPP type
+                getAttribute(node, "ippType", buffer);
+                column->ippType = Fits_getDataType(buffer); 
+
+                column->usingDefault = false;
+
+                //psLogMsg("ippToPsps", PS_LOG_INFO, "...mapping PSPS:'%s' to '%s' '%d'", column->pspsName, column->ippName, column->ippColNum );
+            }
+        }
+    }
+
+    return ret;
+}
+
+/**
+  Reads table mappings from XML file
+  */
+static bool loadMappings(FitsGenerator* this) {
+
+    bool ret = true;
+
+    char path[1000];
+    sprintf(path, "%s/map.xml", this->configsPath);
+
+    struct stat sts;
+    if ((stat(path, &sts)) == -1) {
+        this->logger->print(this->logger, MSG_INFO, "FitsGenerator", 
+                "No map file found at '%s'\n", path);
+        return false;
+    }
+
+    xmlDoc* doc = openXmlFile(this, path);
+
+    if (doc == NULL) {
+        
+        this->logger->print(this->logger, MSG_ERROR,"FitsGenerator",  
+                "Could not create XML document from '%s'\n", path);
+        return false;
+    }
+
+    this->logger->print(this->logger, MSG_INFO, "FitsGenerator", 
+            "Using map file at '%s'\n", path);
+    xmlNode* rootElement = xmlDocGetRootElement(doc);
+
+    if (strcmp((const char*)rootElement->name, "tabledata")!=0) {
+        this->logger->print(this->logger, MSG_ERROR,"FitsGenerator",  
+                "Root node of XML is not 'tabledata', as it should be\n");
+        return false;
+    }
+
+    xmlNode* node = NULL;
+
+    // loop round all available tables
+    for (node = rootElement->children; node; node = node->next) {
+        if (node->type == XML_ELEMENT_NODE) {
+
+            if (strcmp((const char*)node->name, "table")==0) {
+
+                if (!loadTableMappings(this, node)) ret = false;
+            }
+        }
+    }
+
+    closeXmlFile(doc);
+
+    return ret;
+}
+
+/**
+  Populate the provided table with data from XML
+  */
+static bool populateFromFile(FitsGenerator* this, Fits *fitsOut) {
+
+    bool ret = true;
+    psString path = NULL;
+    psStringAppend(&path, "%s/data.xml", this->configsPath);
+
+    xmlDoc* doc = openXmlFile(this, path);
+
+    if (doc == NULL) {
+        psFree(path);
+        return false;
+    }
+    psFree(path);
+
+    xmlNode* rootElement = xmlDocGetRootElement(doc);
+
+    if (strcmp((const char*)rootElement->name, "tabledata")!=0) {
+        this->logger->print(this->logger, MSG_ERROR, "FitsGenerator", 
+                "Root node of XML is not 'tabledata', as it should be\n");
+        return false;
+    }
+
+    for (uint32_t i=0; i<this->numOfTables; i++) {
+        if (!populateTable(this, this->tables+i, rootElement, fitsOut)) {
+            ret = false;
+        }
+    }
+
+    closeXmlFile(doc);
+
+    return ret;
+}
+
+/**
+  Populate with data from another FITS table into this one
+  */
+static bool populateTableFromFits(
+        FitsGenerator* this,
+        Table* table,
+        Fits *fitsIn, 
+        Fits *fitsOut,
+        const long nRows, 
+        const bool fromHeader) {
+
+    if (!table) return false;
+    if (!fitsIn) return false;
+
+    int8_t* int8col = (int8_t*)calloc(nRows, sizeof(int8_t));
+    int16_t* int16col = (int16_t*)calloc(nRows, sizeof(int16_t));
+    long* longcol = (long*)calloc(nRows, sizeof(long));
+    float* floatcol = (float*)calloc(nRows, sizeof(float));
+    double* doublecol = (double*)calloc(nRows, sizeof(double));
+    char** strcol = (char**)calloc(nRows, sizeof(char**));
+    for (uint32_t i=0; i<nRows;i++)
+        strcol[i] = (char*)calloc(50,sizeof(char)); // TODO 20? size issue
+
+    // first loop round all columns and get IPP col numbers for provided column names TODO only do once, first time in
+    if(!fromHeader) {
+
+        for (uint32_t i=0; i<table->numOfColumns; i++) {
+
+            if (strlen(table->columns[i].ippName) < 1) continue;
+
+            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;
+        }
+    }
+
+    int col;
+    for (uint32_t i=0; i<table->numOfColumns; i++) {
+
+        if (!table->columns[i].usingDefault) {
+
+            col = i+1;
+
+            switch (table->columns[i].ippType) {
+
+                case TBYTE:
+                    if (fromHeader) {
+                        fitsIn->getHeaderKeyValue(fitsIn, TBYTE, table->columns[i].ippName, &int8col[0]); 
+                    }
+                    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) {
+                        fitsIn->getHeaderKeyValue(fitsIn, TSHORT, table->columns[i].ippName, &int16col[0]);
+                    }
+                    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) {
+                        fitsIn->getHeaderKeyValue(fitsIn, TLONG, table->columns[i].ippName, &longcol[0]);
+                    }
+                    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) {
+                        fitsIn->getHeaderKeyValue(fitsIn, TLONGLONG, table->columns[i].ippName, &longcol[0]);
+                    }
+                    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) {
+                        fitsIn->getHeaderKeyValue(fitsIn, TFLOAT, table->columns[i].ippName,&floatcol[0]); 
+                    }
+                    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) {
+                        fitsIn->getHeaderKeyValue(fitsIn, TDOUBLE, table->columns[i].ippName, &doublecol[0]);
+                    }
+                    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) 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;
+
+                default:
+                    this->logger->print(this->logger, MSG_ERROR, "FitsGenerator", "Don't know IPP type (%d) for mapping from  '%d'  '%s' to '%s'\n", table->columns[i].ippType,
+                            table->columns[i].ippColNum, table->columns[i].ippName, table->columns[i].pspsName);
+                    break;
+            }
+        }
+    }
+
+    free(int8col);
+    free(int16col);
+    free(longcol);
+    free(floatcol);
+    free(doublecol);
+    for (uint32_t i=0; i<nRows;i++) free(strcol[i]);
+    free(strcol);
+
+    return true;
+}
+
+/**
+  Creates and populates a table
+  */
+static bool createAndPopulateTable(
+        FitsGenerator* this, 
+        Fits* fitsIn,
+        Fits* fitsOut,
+        const long nRows,
+        const char* tableName,
+        const int fromHeader) {
+
+    Table* table = findTable(this, tableName);
+    if (!table) return false;
+
+    if(!createTable(table, fitsOut, nRows)) return false;
+    return populateTableFromFits(this, table, fitsIn, fitsOut, nRows, fromHeader);
+}
+
+/**
+  Destructor.
+  */
+static void destroy(FitsGenerator* this) {
+
+    if (this == NULL) return;
+
+    // TODO check logic here
+    for (int i=0;i<this->numOfTables;i++)
+        free(this->tables[i].columns);
+
+    free(this->tables);
+    psFree(this->configsPath);
+    free(this);
+}
+
+/**
+  Constructor.
+  */
+FitsGenerator* new_FitsGenerator(Logger* logger, const char* path) {
+
+    FitsGenerator* this = (FitsGenerator*)calloc(1, sizeof(FitsGenerator));
+
+    this->configsPath = NULL;
+    psStringAppend(&this->configsPath, path);
+    this->logger = logger;
+
+    // method pointers
+    this->createAndPopulateTable = createAndPopulateTable;
+    this->populateFromFile = populateFromFile;
+    this->destroy = destroy;
+
+    // load tables descriptions from XML
+    if (loadTableDescriptions(this)) {
+
+        // load any mappings we may have from XML        
+        loadMappings(this);
+    }
+
+    return this;
+}
+
Index: branches/eam_branches/ipp-20110213/ippToPsps/src/FitsGenerator.h
===================================================================
--- branches/eam_branches/ipp-20110213/ippToPsps/src/FitsGenerator.h	(revision 31083)
+++ branches/eam_branches/ipp-20110213/ippToPsps/src/FitsGenerator.h	(revision 31083)
@@ -0,0 +1,72 @@
+/** @file FitsGenerator.h
+ *
+ *  @brief ippToPsps
+ *
+ *  @ingroup ippToPsps
+ *
+ *  @author IfA
+ *  Copyright 2011 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef IPPTOPSPS_FITSGENERATOR_H
+#define IPPTOPSPS_FITSGENERATOR_H
+
+#include <psmodules.h>
+
+#include "Logger.h"
+
+// column class
+typedef struct Column {
+
+    int ippColNum;
+    char ippName[100];
+    int ippType;
+    long ippRepeat;
+    char pspsName[100]; // TODO change to 'name'
+    int pspsType;
+    bool usingDefault;
+    char defaultValue[100];
+    char comment[300];
+
+} Column;
+
+// table class
+typedef struct Table {
+
+    char name[32];
+    Column* columns;
+    int numOfColumns;
+
+} Table;
+
+/**
+
+  Class used for generating FITS files.
+
+  - Uses XML table definitions to create table shapes
+  - Uses XML map files to populate tables from another FITS files
+  - Uses XML data files to populate tables with set values
+
+*/
+typedef struct FitsGenerator {
+
+    // fields
+    psString configsPath;
+    Table* tables;
+    int numOfTables;
+    Logger* logger;
+
+    // methods
+    bool (*createAndPopulateTable)();
+    bool (*populateFromFile)();
+
+    // destructor
+    void (*destroy)();
+
+} FitsGenerator; 
+
+// constructor
+FitsGenerator* new_FitsGenerator(Logger* logger, const char* path);
+
+#endif // IPPTOPSPS_FITSGENERATOR_H
+
Index: branches/eam_branches/ipp-20110213/ippToPsps/src/InitBatch.c
===================================================================
--- branches/eam_branches/ipp-20110213/ippToPsps/src/InitBatch.c	(revision 30812)
+++ branches/eam_branches/ipp-20110213/ippToPsps/src/InitBatch.c	(revision 31083)
@@ -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.fitsGenerator->populateFromFile(this->base.fitsGenerator, 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 (
@@ -44,10 +44,8 @@
             !this->base.gotConfig() ) {
 
-        printf("\n* ERROR with supplied arguments:");
-        this->print(this);
+        this->base.logger->print(this->base.logger, MSG_ERROR, "InitBatch", "Problem with supplied arguments\n");
         this->base.exitCode = PS_EXIT_CONFIG_ERROR;
         return false;
     }
-
 
     return true;
@@ -57,25 +55,20 @@
   Constructor
   */
-InitBatch* new_InitBatch(int *argc, char **argv) {
+InitBatch* new_InitBatch(Logger* logger, int *argc, char **argv) {
 
+    logger->print(logger, MSG_DEBUG, "InitBatch", "Constructor\n");
     InitBatch *this = (InitBatch*)calloc(1, sizeof(InitBatch));
 
     // call base-class constructor
-    if (!new_Batch(&this->base)) {
+    new_Batch(logger, &this->base);
 
-        psError(PS_ERR_IO, false, "Unable to create Batch base-class object");
-        return this;
-    }
-
+    // method pointers
     this->print = print;
     this->destroy = destroy;
     this->base.run = run;
 
-    if (!parseArguments(this, *argc, argv)) { return this; }
+    parseArguments(this, *argc, argv);
 
-    strcat(this->base.configsDir, "/init");
-    sprintf(this->base.fitsOutFile, "00000000.FITS");
-
-    this->base.init(&this->base);
+    this->print(this);
 
     return this;
@@ -87,27 +80,18 @@
 int main(int argc, char **argv) {
 
-    psTimerStart("initbatch");
+//    ippToPsps_VersionPrint();
 
-    ippToPsps_VersionPrint();
+    Logger* logger = new_Logger(NULL, false);
+    logger->print(logger, MSG_INFO, "main", "Creating new initialization batch\n");
 
-    int exitCode;
-
-    InitBatch* initBatch = new_InitBatch(&argc, argv);
+    InitBatch* initBatch = new_InitBatch(logger, &argc, argv);
     initBatch->base.run(initBatch);
-    exitCode = initBatch->base.exitCode;
+    int exitCode = initBatch->base.exitCode;
 
     initBatch->destroy(initBatch);
 
-    double secs = psTimerMark("initbatch");
-
-    psLogMsg("initbatch", 3, "initbatch completed %ssuccessfully, with exit code %d, in %.1f %s\n",
-            (exitCode == PS_EXIT_SUCCESS) ? "" : "un",
-            exitCode,
-            (secs<60.0) ? secs : (secs/60.0),
-            (secs<60.0) ? "sec(s)" : "min(s)");
-
     // tidy up
-    psTimerStop();
     psLibFinalize();
+    logger->destroy(logger);
 
     return exitCode;
Index: branches/eam_branches/ipp-20110213/ippToPsps/src/InitBatch.h
===================================================================
--- branches/eam_branches/ipp-20110213/ippToPsps/src/InitBatch.h	(revision 30812)
+++ branches/eam_branches/ipp-20110213/ippToPsps/src/InitBatch.h	(revision 31083)
@@ -24,9 +24,12 @@
     // methods
     void (*print)();
+
+    // destructor
     void (*destroy)();
 
 } InitBatch;
 
-InitBatch *new_InitBatch(int *argc, char **argv);
+// constructor
+InitBatch *new_InitBatch(Logger *logger, int *argc, char **argv);
 
 # endif // IPPTOPSPS_INITBATCH_H 
Index: branches/eam_branches/ipp-20110213/ippToPsps/src/InitData.c
===================================================================
--- branches/eam_branches/ipp-20110213/ippToPsps/src/InitData.c	(revision 31083)
+++ branches/eam_branches/ipp-20110213/ippToPsps/src/InitData.c	(revision 31083)
@@ -0,0 +1,225 @@
+/** @file InitData.c
+ *
+ *  @ingroup ippToPsps
+ *
+ *  @author IfA
+ *  Copyright 2011 Institute for Astronomy, University of Hawaii
+ */
+
+#include "InitData.h"
+#include "Fits.h"
+
+
+/**
+  Gets an attribute value from XML node
+  */
+static bool getAttribute(xmlNode* node, const char* attName, char* _value) {
+
+    xmlChar* value = xmlGetProp(node, (const xmlChar*)attName);
+    if (!value) return false;
+    sprintf(_value, "%s", value);
+    xmlFree(value);
+
+    return true;
+}
+
+/**
+  Searches through this table and finds attribute value for a provided key
+  */
+static bool getRowAttribute(
+        InitData* this, 
+        xmlNode* tableNode,
+        const char* keyName, 
+        const char* keyValue,
+        const char* attName, 
+        char* attValue) {
+
+    xmlNode* node = NULL;
+
+    char buffer[100];
+
+    // loop round all available rows for this table
+    for (node = tableNode->children; node; node = node->next) {
+        if (node->type == XML_ELEMENT_NODE) {
+            //psLogMsg("ippToPsps", PS_LOG_INFO, "Node name '%s'",  (const char*)node->name );
+
+            if (strcmp((const char*)node->name, "row")==0) {
+                //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;
+                getAttribute(node, attName, attValue);
+                //printf("FOUND %s %s %s %s \n", keyName, buffer, attName, attValue );
+                return true;
+
+            }
+        }
+    }
+
+    this->logger->print(this->logger, MSG_ERROR, 
+            "InitData", "Could not find value for '%s' for '%s' with value '%s'\n", 
+            attName, keyName, keyValue);
+
+    return false;
+}
+
+/**
+  Gets a value from the 'init' data 
+  */
+static bool getInitValue(
+        InitData* this, 
+        const char* tableName,
+        const char* keyName, 
+        const char* keyValue,
+        const char* attName, 
+        char* attValue) {
+
+    bool ret = true;
+
+    xmlNode* rootElement = xmlDocGetRootElement(this->doc);
+
+    if (strcmp((const char*)rootElement->name, "tabledata")!=0) {
+        this->logger->print(this->logger, MSG_ERROR, 
+                "InitData", "Root node of XML is not 'tabledata', as it should be\n");
+        return false;
+    }
+
+    xmlNode* node = NULL;
+    char tempStr[100];
+
+    // loop round all available tables
+    for (node = rootElement->children; node; node = node->next) {
+        if (node->type == XML_ELEMENT_NODE) {
+
+            if (strcmp((const char*)node->name, "table")!=0) continue;
+            if (!getAttribute(node, "name", tempStr)) continue;
+            if (strcmp(tempStr, tableName)!=0) continue;
+
+            ret = getRowAttribute(this, node, keyName, keyValue, attName, attValue);
+            break;
+        }
+
+    }
+
+    return ret;
+}
+
+/**
+   Gets model fit ID from name
+   */
+static bool getModelFitId(InitData* this, const char* name, int8_t* id) {
+
+    char buffer[10];
+    if (!getInitValue(this, "FitModel", "name", name, "fitModelID", buffer)) {
+
+        *id = -1;
+        return false;
+    }
+
+    *id = atoi(buffer);
+    return true;
+}
+
+/**
+   Gets survey ID from survey name
+   */
+static bool getSurveyId(InitData* this, const char* name, int8_t* id) {
+
+    char buffer[10];
+    if (!getInitValue(this, "Survey", "name", name, "surveyID", buffer)) {
+
+        *id = -1;
+        return false;
+    }
+
+    *id = atoi(buffer);
+    return true;
+}
+
+/**
+   Gets stack type ID from stack type string
+   */
+static bool getStackTypeId(InitData* this, const char* type, int8_t* id) {
+
+    char buffer[20];
+    if (!getInitValue(this, "StackType", "stackType", type, "stackTypeID", buffer)) {
+
+        *id = -1;
+        return false;
+    }
+
+    *id = atoi(buffer);
+    return true;
+}
+
+/**
+   Gets filter ID from filter type string
+   */
+static bool getFilterId(InitData* this, const char* type, int8_t* id) {
+
+    char tmp[2];
+    strncpy(tmp, type,1);
+    tmp[1] = '\0';
+    char buffer[10];
+    if (!getInitValue(this, "Filter", "filterType", tmp, "filterID", buffer)) {
+
+        *id = -1;
+        return false;
+    }
+
+    *id = atoi(buffer);
+    return true;
+}
+
+/**
+  Destructor.
+  */
+static void destroy(InitData* this) {
+
+    if (this == NULL) return;
+
+    if (this->doc != NULL) {
+
+        xmlFreeDoc(this->doc);
+        xmlCleanupParser();
+    }
+
+    this->logger->print(this->logger, MSG_DEBUG, "InitData", "Destructor\n");
+
+    free(this);
+}
+
+/**
+  Constructor.
+  */
+InitData* new_InitData(const char* path, Logger* logger) {
+
+    InitData* this = (InitData*)calloc(1, sizeof(InitData));
+    this->logger = logger;
+
+    this->logger->print(this->logger, MSG_DEBUG, "InitData", "Constructor\n");
+
+    sprintf(this->path, "%s/init/data.xml", path);
+
+    this->doc = xmlReadFile(this->path, NULL, 0);
+
+    if (this->doc == NULL)
+        this->logger->print(this->logger, MSG_ERROR, 
+                "InitData", "Unable to open init data file at '%s'\n", this->path);
+    else
+        this->logger->print(this->logger, MSG_INFO, 
+                "InitData", "Using init data file at '%s'\n", this->path);
+
+
+    // method pointers
+    this->getFilterId = getFilterId;
+    this->getSurveyId = getSurveyId;
+    this->getStackTypeId = getStackTypeId;
+    this->getModelFitId = getModelFitId;
+    this->destroy = destroy;
+
+    return this;
+}
+
Index: branches/eam_branches/ipp-20110213/ippToPsps/src/InitData.h
===================================================================
--- branches/eam_branches/ipp-20110213/ippToPsps/src/InitData.h	(revision 31083)
+++ branches/eam_branches/ipp-20110213/ippToPsps/src/InitData.h	(revision 31083)
@@ -0,0 +1,48 @@
+/** @file InitData.h
+ *
+ *  @brief ippToPsps
+ *
+ *  @ingroup ippToPsps
+ *
+ *  @author IfA
+ *  Copyright 2011 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef IPPTOPSPS_INITDATA_H
+#define IPPTOPSPS_INITDATA_H
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+#include <psmodules.h>
+
+#include "Logger.h"
+
+/**
+
+  Class encapsulating the initialization data
+
+*/
+typedef struct InitData {
+
+    // fields
+    char path[1000];
+    xmlDoc* doc;
+    Logger* logger;
+
+    // methods
+    bool (*getFilterId)();
+    bool (*getSurveyId)();
+    bool (*getStackTypeId)();
+    bool (*getModelFitId)();
+
+    // destructor
+    void (*destroy)();
+
+} InitData; 
+
+// constructor
+InitData* new_InitData(const char* path, Logger* logger);
+
+#endif // IPPTOPSPS_INITDATA_H
+
Index: branches/eam_branches/ipp-20110213/ippToPsps/src/Logger.c
===================================================================
--- branches/eam_branches/ipp-20110213/ippToPsps/src/Logger.c	(revision 31083)
+++ branches/eam_branches/ipp-20110213/ippToPsps/src/Logger.c	(revision 31083)
@@ -0,0 +1,119 @@
+/** @file Logger.c
+ *
+ *  @ingroup ippToPsps
+ *
+ *  Source for all methods in the Logger class
+ *
+ *  @author IfA
+ *  Copyright 2011 Institute for Astronomy, University of Hawaii
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <time.h>
+       #include <sys/time.h>
+
+#include "Logger.h"
+
+/**
+  Destructor
+  */
+static void destroy(Logger* this) {
+
+    struct timeval stopTime;
+    gettimeofday(&stopTime, 0);
+    double secs = (double)(stopTime.tv_sec-this->startTime.tv_sec);
+
+    this->print(this, MSG_INFO, "Logger", 
+            "Completed in %.1f %s\n",
+            (secs<60.0) ? secs : (secs/60.0),
+            (secs<60.0) ? "sec(s)" : "min(s)");
+
+    this->print(this, MSG_DEBUG, "Logger", "Destructor\n");
+    free(this);
+}
+
+/**
+  Prints a message
+  */
+static void print(void* this, const int8_t typeInt, const char* class, const char* fmt, ...) {
+
+    Logger* logger = (Logger*)this;
+
+    va_list args;
+    va_start(args, fmt);
+    char msg_fmt[1000];
+
+    // get message-type string
+    char typeStr[20];
+    switch(typeInt) {
+
+        case MSG_INFO:
+            sprintf(typeStr, "INFO");
+            break;
+        case MSG_ERROR:
+            sprintf(typeStr, "ERROR");
+            break;
+        case MSG_DEBUG:
+            if (!logger->showDebug) return;
+            sprintf(typeStr, "DEBUG");
+            break;
+        default:
+            break;
+    }
+
+    // get time string
+    char timeStr[30];
+    time_t now = time(NULL);
+    struct tm tim = *(localtime(&now));
+    strftime(timeStr, 30, "%Y/%m/%d %H:%M:%S",&tim);
+
+    if (typeInt == MSG_NONE)
+        sprintf(msg_fmt, "%s", fmt);
+    else
+        sprintf(msg_fmt, "%18s | %6s | %15s | %s ", timeStr, typeStr, class,  fmt);
+
+
+    // write either to file or stdout
+    if(logger->file == NULL) vprintf(msg_fmt, args);
+    else vfprintf(logger->file, msg_fmt, args);
+}
+
+/**
+  Constructor. 
+
+  Returns a new Logger object.
+  
+  path      - path to file where log should be saved, otherwise stdout is used
+  showDebug - show debug messages or not 
+  */
+Logger* new_Logger(const char* path, const int8_t showDebug) {
+
+    Logger* this = (Logger*)calloc(1, sizeof(Logger));
+
+    this->file = NULL;
+    this->showDebug = showDebug;
+    gettimeofday(&this->startTime, 0);
+
+    // open a file to write to
+    if (path != NULL) this->file = fopen(path, "w+");
+
+    print(this, MSG_DEBUG, "Logger", "Constructor\n");
+    print(this, MSG_INFO, "Logger", "Starting timer\n");
+
+    if (path != NULL) { 
+
+        if (this->file == NULL)
+            print(this, MSG_ERROR, "Logger", "Cannot open file for writing here '%s'\n", path);
+        else
+            printf("**** Saving log to '%s'\n", path);
+    }
+
+    // method pointers
+    this->print = print;
+    this->destroy = destroy;
+
+    return this;
+}
+
Index: branches/eam_branches/ipp-20110213/ippToPsps/src/Logger.h
===================================================================
--- branches/eam_branches/ipp-20110213/ippToPsps/src/Logger.h	(revision 31083)
+++ branches/eam_branches/ipp-20110213/ippToPsps/src/Logger.h	(revision 31083)
@@ -0,0 +1,49 @@
+/** @file logger.h
+ *
+ *  @brief Batch
+ *
+ *  @ingroup ippToPsps
+ *
+ *  @author IfA
+ *  Copyright 2011 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef IPPTOPSPS_LOGGER_H
+#define IPPTOPSPS_LOGGER_H
+
+// message types
+typedef enum {
+
+    MSG_INFO,
+    MSG_ERROR,
+    MSG_DEBUG,
+    MSG_NONE
+
+} MessageTypes;
+
+
+/**
+  Logger class.
+
+  Prints either to stdout or a file
+  */
+typedef struct Logger {
+
+    // fields
+    FILE* file;
+    struct timeval startTime;
+    int8_t showDebug;
+
+    // methods
+    void (*print)(void* logger, const int8_t typeInt, const char* class, const char* fmt, ...);
+
+    // destructor
+    void (*destroy)();
+
+} Logger;
+
+// constructor
+Logger* new_Logger(const char* path, const int8_t showDebug);
+
+# endif // IPPTOPSPS_LOGGER_H 
+
Index: branches/eam_branches/ipp-20110213/ippToPsps/src/Makefile.am
===================================================================
--- branches/eam_branches/ipp-20110213/ippToPsps/src/Makefile.am	(revision 30812)
+++ branches/eam_branches/ipp-20110213/ippToPsps/src/Makefile.am	(revision 31083)
@@ -31,5 +31,8 @@
         Batch.c \
 	Version.c \
-	Config.c
+	Fits.c \
+	InitData.c \
+	Logger.c \
+	FitsGenerator.c
 
 # detectionbatch
@@ -40,5 +43,8 @@
         Batch.c \
 	Version.c \
-	Config.c
+	Fits.c \
+	InitData.c \
+	Logger.c \
+	FitsGenerator.c
 
 # stackbatch
@@ -49,5 +55,8 @@
         Batch.c \
 	Version.c \
-	Config.c
+	Fits.c \
+	InitData.c \
+	Logger.c \
+	FitsGenerator.c
 
 clean-local:
Index: branches/eam_branches/ipp-20110213/ippToPsps/src/StackBatch.c
===================================================================
--- branches/eam_branches/ipp-20110213/ippToPsps/src/StackBatch.c	(revision 30812)
+++ branches/eam_branches/ipp-20110213/ippToPsps/src/StackBatch.c	(revision 31083)
@@ -1,4 +1,6 @@
 #include "StackBatch.h"
 #include "StackBatchEnums.h"
+
+#include "Fits.h"
 
 /**
@@ -13,181 +15,103 @@
 
 /**
-  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);
+
+    this->base.logger->print(this->base.logger, MSG_INFO, "StackBatch", 
+            "Looping through %ld psf detection rows from '%s'\n", nDet, extensionName);
+    float mag;
+    long unmatched = 0, totalDetections = 0, numOfDuplicates = 0, numInvalidFlux = 0, numDetectionsOut = 0;
+
+    this->base.logger->print(this->base.logger, MSG_INFO, "StackBatch",  
+            "+---------------+---------+----------+------------------+---------------+--------------+\n");
+    this->base.logger->print(this->base.logger, MSG_INFO, "StackBatch",  
+            "|   Extension   | Rows in | Rows out | Missing from DVO | Duplicate IDs | Invalid Flux |\n");
+
+    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.fitsGenerator->createAndPopulateTable(this->base.fitsGenerator, 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);
+
+    }
+
+    this->base.logger->print(this->base.logger, MSG_INFO, "StackBatch",
+            "|  %12s |  %5ld  |   %5ld  |      %5ld       |    %5ld      |    %5ld     |\n",
+            extensionName, nDet, numDetectionsOut, unmatched, numOfDuplicates, numInvalidFlux);
+    this->base.logger->print(this->base.logger, MSG_INFO, "StackBatch",
+            "+---------------+---------+----------+------------------+---------------+--------------+\n");
 
 
@@ -195,15 +119,264 @@
     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;
+    char extensionName[15];
+    sprintf(extensionName, "SkyChip.xrad");
+    if (!fitsIn->moveToBinaryTableAndCountRows(fitsIn, extensionName, &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));
+    }
+
+    this->base.logger->print(this->base.logger, MSG_INFO, "StackBatch", 
+            "Looping through %ld extended source detections from extension '%s'\n", nDet, extensionName);
+    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.fitsGenerator->createAndPopulateTable(this->base.fitsGenerator, 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;
+    char extensionName[40];
+    sprintf(extensionName, "SkyChip.xfit");
+    if (!fitsIn->moveToBinaryTableAndCountRows(fitsIn, extensionName, &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);
+
+    this->base.logger->print(this->base.logger, MSG_INFO, "StackBatch", 
+            "Looping through %ld model fit rows from '%s'\n", nDet, extensionName);
+    for (long i = 0; i<nDet; i++) {
+
+        printf("IPP_IDET %ld model %s\n", ippIdets[i], modelTypes[i]);
+
+    }
+
+    this->base.fitsGenerator->createAndPopulateTable(this->base.fitsGenerator, 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.fitsGenerator->createAndPopulateTable(this->base.fitsGenerator, 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], this->base.logger);
+    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.initData->getFilterId(this->base.initData, 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.fitsGenerator->createAndPopulateTable(this->base.fitsGenerator, 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);
+    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;
 }
 
-
 /**
   Print-out this class. Calls base-calls print method first.
@@ -213,6 +386,6 @@
     this->base.print(&this->base);
 
-    printf("* skycell ID      : %d\n", this->skycellId);
-    printf("\n");
+    this->base.logger->print(this->base.logger, MSG_INFO, "StackBatch", "skycell ID      : %d\n", this->skycellId);
+    this->base.logger->print(this->base.logger, MSG_INFO, "StackBatch", "\n");
 }
 
@@ -240,5 +413,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 (
@@ -262,27 +437,22 @@
   Constructor
   */
-StackBatch* new_StackBatch(int *argc, char **argv) {
-
+StackBatch* new_StackBatch(Logger* logger, int *argc, char **argv) {
+
+    logger->print(logger, MSG_DEBUG, "StackBatch", "Constructor\n");
     StackBatch *this = (StackBatch*)calloc(1, sizeof(StackBatch));
 
     // call base-class constructor
-    if (!new_Batch(&this->base)) {
-
-        psError(PS_ERR_IO, false, "Unable to create Batch base-class object");
-        return this;
-    }
+    new_Batch(logger, &this->base);
 
     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);
+
+    this->print(this);
 
     return this;
@@ -294,26 +464,17 @@
 int main(int argc, char **argv) {
 
-    psTimerStart("stackbatch");
-
-    ippToPsps_VersionPrint();
-
-    int exitCode;
-
-    StackBatch* stackBatch = new_StackBatch(&argc, argv);
+//    ippToPsps_VersionPrint();
+
+    Logger* logger = new_Logger(NULL, false);
+    logger->print(logger, MSG_INFO, "main", "Creating new stack batch\n");
+
+    StackBatch* stackBatch = new_StackBatch(logger, &argc, argv);
     stackBatch->base.run(stackBatch);
-    exitCode = stackBatch->base.exitCode;
+    int exitCode = stackBatch->base.exitCode;
 
     stackBatch->destroy(stackBatch);
 
-    double secs = psTimerMark("stackbatch");
-
-    psLogMsg("stackbatch", 3, "stackbatch completed %ssuccessfully, with exit code %d, in %.1f %s\n",
-            (exitCode == PS_EXIT_SUCCESS) ? "" : "un",
-            exitCode,
-            (secs<60.0) ? secs : (secs/60.0),
-            (secs<60.0) ? "sec(s)" : "min(s)");
-
     // tidy up
-    psTimerStop();
+    logger->destroy(logger);
     psLibFinalize();
 
@@ -321,4 +482,2 @@
 }
 
-
-
Index: branches/eam_branches/ipp-20110213/ippToPsps/src/StackBatch.h
===================================================================
--- branches/eam_branches/ipp-20110213/ippToPsps/src/StackBatch.h	(revision 30812)
+++ branches/eam_branches/ipp-20110213/ippToPsps/src/StackBatch.h	(revision 31083)
@@ -30,5 +30,5 @@
 } StackBatch;
 
-StackBatch *new_StackBatch(int *argc, char **argv);
+StackBatch *new_StackBatch(Logger* logger, int *argc, char **argv);
 
 # endif // IPPTOPSPS_STACKBATCH_H 
