Index: /trunk/psLib/psLib.kdevelop.filelist
===================================================================
--- /trunk/psLib/psLib.kdevelop.filelist	(revision 2374)
+++ /trunk/psLib/psLib.kdevelop.filelist	(revision 2375)
@@ -1,2 +1,4 @@
 # KDevelop Custom Project File List
 tags
+src/fileUtils/psFits.c
+src/fileUtils/psFits.h
Index: /trunk/psLib/psLib.kdevses
===================================================================
--- /trunk/psLib/psLib.kdevses	(revision 2374)
+++ /trunk/psLib/psLib.kdevses	(revision 2375)
@@ -2,12 +2,42 @@
 <!DOCTYPE KDevPrjSession>
 <KDevPrjSession>
- <DocsAndViews NumberOfDocuments="0" />
+ <DocsAndViews NumberOfDocuments="8" >
+  <Doc0 NumberOfViews="1" URL="file:/home/desonia/psLib/src/image/psImageConvolve.c" >
+   <View0 Type="Source" />
+  </Doc0>
+  <Doc1 NumberOfViews="1" URL="file:/home/desonia/psLib/src/fileUtils/psFits.c" >
+   <View0 Type="Source" />
+  </Doc1>
+  <Doc2 NumberOfViews="1" URL="file:/home/desonia/psLib/src/fileUtils/psFits.h" >
+   <View0 Type="Source" />
+  </Doc2>
+  <Doc3 NumberOfViews="1" URL="file:/home/desonia/psLib/src/image/psImageIO.c" >
+   <View0 Type="Source" />
+  </Doc3>
+  <Doc4 NumberOfViews="1" URL="file:/home/desonia/psLib/src/image/psImageIO.h" >
+   <View0 Type="Source" />
+  </Doc4>
+  <Doc5 NumberOfViews="1" URL="file:/home/desonia/psLib/src/fileUtils/psFileUtilsErrors.dat" >
+   <View0 Type="Source" />
+  </Doc5>
+  <Doc6 NumberOfViews="1" URL="file:/home/desonia/psLib/src/image/psImageErrors.dat" >
+   <View0 Type="Source" />
+  </Doc6>
+  <Doc7 NumberOfViews="1" URL="file:/home/desonia/psLib/src/psErrorCodes.dat" >
+   <View0 line="9" Type="Source" />
+  </Doc7>
+ </DocsAndViews>
  <pluginList>
   <kdevbookmarks>
    <bookmarks/>
   </kdevbookmarks>
+  <kdevsubversion>
+   <subversion recurseresolve="1" recurserelocate="1" recursemerge="1" recursecommit="1" base="" recursepropget="1" recurseswitch="1" recurseupdate="1" recursepropset="1" recursediff="1" recurserevert="1" forcemove="1" recursecheckout="1" forceremove="1" recurseadd="1" recurseproplist="1" forcemerge="1" />
+  </kdevsubversion>
   <kdevvalgrind>
    <executable path="" params="" />
    <valgrind path="" params="" />
+   <calltree path="" params="" />
+   <kcachegrind path="" />
   </kdevvalgrind>
   <kdevdebugger>
Index: /trunk/psLib/src/astronomy/psAstronomyErrors.h
===================================================================
--- /trunk/psLib/src/astronomy/psAstronomyErrors.h	(revision 2374)
+++ /trunk/psLib/src/astronomy/psAstronomyErrors.h	(revision 2375)
@@ -7,12 +7,12 @@
  *  @author Robert DeSonia, MHPCC
  *
- *  @version $Revision: 1.4 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2004-11-09 00:36:13 $
+ *  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-11-16 20:00:20 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
  */
 
-#ifndef PS_SYSUTILS_ERRORS_H
-#define PS_SYSUTILS_ERRORS_H
+#ifndef PS_ASTRONOMY_ERRORS_H
+#define PS_ASTRONOMY_ERRORS_H
 
 /* N.B., lines between '//~Start' and '//~End' are automatic generated from
Index: /trunk/psLib/src/collections/psList.c
===================================================================
--- /trunk/psLib/src/collections/psList.c	(revision 2374)
+++ /trunk/psLib/src/collections/psList.c	(revision 2375)
@@ -6,6 +6,6 @@
  *  @author Robert Daniel DeSonia, MHPCC
  *
- *  @version $Revision: 1.21 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2004-11-04 01:04:57 $
+ *  @version $Revision: 1.22 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-11-16 20:00:20 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -42,6 +42,6 @@
     list->size = 0;
     list->head = list->tail = NULL;
-    list->iter = ITER_INIT_HEAD;
-    list->iterIndex = PS_LIST_HEAD;
+    list->p_iter = ITER_INIT_HEAD;
+    list->p_iterIndex = PS_LIST_HEAD;
 
     pthread_mutex_init(&(list->lock), NULL)
@@ -127,6 +127,6 @@
 
         list->size++;
-        list->iter = elem;
-        list->iterIndex = list->size - 1;
+        list->p_iter = elem;
+        list->p_iterIndex = list->size - 1;
     } else {
         // move ourselves to the given position
@@ -152,6 +152,6 @@
 
         list->size++;
-        list->iter = elem;
-        list->iterIndex = cursorIndex;
+        list->p_iter = elem;
+        list->p_iterIndex = cursorIndex;
     }
 
@@ -227,13 +227,13 @@
         // removed tail, so iter should be the last element of list to keep it valid
         if (list->size > 0) {
-            list->iter = list->tail;
-            list->iterIndex = list->size - 1;
+            list->p_iter = list->tail;
+            list->p_iterIndex = list->size - 1;
         } else {
-            list->iter = ITER_INIT_TAIL;
+            list->p_iter = ITER_INIT_TAIL;
         }
     } else {
         elem->next->prev = elem->prev;
-        list->iter = elem->next;
-        list->iterIndex = cursorIndex;
+        list->p_iter = elem->next;
+        list->p_iterIndex = cursorIndex;
     }
 
@@ -275,5 +275,5 @@
 
     if (where >= (psS32)list->size) {
-        list->iter = NULL;
+        list->p_iter = NULL;
         if (lockList) {
             pthread_mutex_unlock(&list->lock)
@@ -285,9 +285,9 @@
     switch (where) {
     case PS_LIST_HEAD:
-        list->iter = ITER_INIT_HEAD;
+        list->p_iter = ITER_INIT_HEAD;
         break;
 
     case PS_LIST_TAIL:
-        list->iter = ITER_INIT_TAIL;
+        list->p_iter = ITER_INIT_TAIL;
         break;
 
@@ -297,6 +297,6 @@
 
         if (cursor != NULL) {
-            list->iter = cursor->prev;
-            list->iterIndex = position - 1;
+            list->p_iter = cursor->prev;
+            list->p_iterIndex = position - 1;
         }
         break;
@@ -307,6 +307,6 @@
 
         if (cursor != NULL) {
-            list->iter = cursor->next;
-            list->iterIndex = position + 1;
+            list->p_iter = cursor->next;
+            list->p_iterIndex = position + 1;
         }
         break;
@@ -322,6 +322,6 @@
             cursor = listGetIterator(list);
             if (cursor == NULL) {      // reset the iterator if it is invalid
-                list->iter = ITER_INIT_HEAD;
-                list->iterIndex = 0;
+                list->p_iter = ITER_INIT_HEAD;
+                list->p_iterIndex = 0;
             }
 
@@ -357,10 +357,10 @@
     }
 
-    if (list->iter == ITER_INIT_HEAD) {
+    if (list->p_iter == ITER_INIT_HEAD) {
         return list->head;
-    } else if (list->iter == ITER_INIT_TAIL) {
+    } else if (list->p_iter == ITER_INIT_TAIL) {
         return list->tail;
     } else {
-        return list->iter;
+        return list->p_iter;
     }
 }
@@ -368,10 +368,10 @@
 psS32 listGetIteratorIndex(psList* list)
 {
-    if (list->iter == ITER_INIT_HEAD) {
+    if (list->p_iter == ITER_INIT_HEAD) {
         return 0;
-    } else if (list->iter == ITER_INIT_TAIL) {
+    } else if (list->p_iter == ITER_INIT_TAIL) {
         return list->size - 1;
     } else {
-        return list->iterIndex;
+        return list->p_iterIndex;
     }
 }
Index: /trunk/psLib/src/collections/psList.h
===================================================================
--- /trunk/psLib/src/collections/psList.h	(revision 2374)
+++ /trunk/psLib/src/collections/psList.h	(revision 2375)
@@ -10,6 +10,6 @@
  *  @ingroup LinkedList
  *
- *  @version $Revision: 1.16 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2004-10-27 00:57:31 $
+ *  @version $Revision: 1.17 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-11-16 20:00:20 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -52,16 +52,35 @@
  *  directly; rather the psListAlloc should be used.
  *
- *  @see psListAlloc, psListFree
+ *  @see psListAlloc
  */
 typedef struct
 {
-    psU32 size;                 ///< number of elements on list
+    psU32 size;                        ///< number of elements on list
     psListElem* head;                  ///< first element on list (may be NULL)
     psListElem* tail;                  ///< last element on list (may be NULL)
-    psListElem* iter;                  ///< iteration cursor
-    psU32 iterIndex;            ///< the numeric position of the iteration cursor in the list
+    psArray* iterators;                ///< iterators
     pthread_mutex_t lock;              ///< mutex to lock a node during changes
+psListElem* p_iter;                ///< internal cursor for increased performance index accessing
+int p_iterIndex;                   ///< index position of the iter.
 }
 psList;
+
+/** The psList iterator structure.  This should be allocated via
+ *  psListIteratorAlloc and not directly.
+ *
+ *  The life span of a psListIterator object is ended by either a psFree
+ *  of this structure OR psFree of the psList in which it operates on.
+ *
+ *  @see psListIteratorAlloc, psListIteratorSet, psListGetNext, psListGetPrevious
+ */
+typedef struct
+{
+psList* list;                      ///< List iterator to works on
+psU32 number;                      ///< List iterator number
+psListElem* cursor;                ///< current cursor position
+bool offEnd;                       ///< Iterator off the end?
+}
+psListIterator;
+
 
 /** Creates a psList linked list object.
Index: /trunk/psLib/src/dataIO/Makefile
===================================================================
--- /trunk/psLib/src/dataIO/Makefile	(revision 2374)
+++ /trunk/psLib/src/dataIO/Makefile	(revision 2375)
@@ -16,6 +16,6 @@
 all: $(TARGET_STATIC)
 
-#psAstronomyErrors.h: psAstronomyErrors.dat
-#	perl ../parseErrorCodes.pl --data=$? $@
+psFileUtilsErrors.h: psFileUtilsErrors.dat
+	perl ../parseErrorCodes.pl --data=$? $@
 
 
Index: /trunk/psLib/src/dataIO/psFileUtilsErrors.dat
===================================================================
--- /trunk/psLib/src/dataIO/psFileUtilsErrors.dat	(revision 2374)
+++ /trunk/psLib/src/dataIO/psFileUtilsErrors.dat	(revision 2375)
@@ -15,2 +15,22 @@
 psLookupTable_DIVIDE_BY_ZERO           Divide by zero error during interpolation.
 psLookupTable_INVALID_TYPE             Invalid psLookupType, %d;
+#
+psFits_NULL                            The input psFits object can not NULL.
+psFits_FILENAME_INVALID                Could not open file,'%s'.\nCFITSIO Error: %s
+psFits_FILENAME_NULL                   Specified filename can not be NULL.
+psFits_EXTNAME_INVALID                 Could not find HDU '%s' in file %s.\nCFITSIO Error: %s
+psFits_EXTNUM_INVALID                  Could not find HDU #%d in file %s.\nCFITSIO Error: %s
+psFits_GET_EXTNUM_FAILED               Failed to determine the current HDU number in file %s.\nCFITSIO Error: %s
+psFits_GETNUMHDUS_FAILED               Failed to determine the number of HDUs in file %s.\nCFITSIO Error: %s
+psFits_GETHDUTYPE_FAILED               Failed to determine an HDU type in file %s.\nCFITSIO Error: %s
+psFits_GETNUMKEYS_FAILED               Failed to determine the number of header keys in file %s.\nCFITSIO Error: %s
+psFits_FILENAME_CREATE_FAILED          Could not create file,'%s'.\nCFITSIO Error: %s
+psFits_TYPE_UNSUPPORTED                Specified type, %s, is not supported.
+psFits_CREATE_HDU_FAILED               Could not create new image HDU in file,'%s'.\nCFITSIO Error: %s
+psFits_NOT_IMAGE_TYPE                  Current FITS HDU type must be an IMAGE.
+psFits_TABLE_FIRSTROW_NULL             Can't create a table without the first row, where table column information is derived.
+psFits_CFITSIO_ERROR                   CFITSIO error: %s
+psFits_METATYPE_INVALID                Specified FITS metadata type, %c, is not supported.
+psFits_METADATA_ADD_FAILED             Failed to add metadata item, %s.
+psFits_WRITE_FAILED                    Could not write psImage data to file,'%s'.\nCFITSIO Error: %s
+psFits_IMAGE_NULL                      The input psImage was NULL.  Need a non-NULL psImage for operation.
Index: /trunk/psLib/src/dataIO/psFileUtilsErrors.h
===================================================================
--- /trunk/psLib/src/dataIO/psFileUtilsErrors.h	(revision 2374)
+++ /trunk/psLib/src/dataIO/psFileUtilsErrors.h	(revision 2375)
@@ -1,5 +1,5 @@
-/** @file  psAstronomyErrors.h
+/** @file  psFileUtilsErrors.h
  *
- *  @brief Contains the error text for the astronomy functions
+ *  @brief Contains the error text for the fileUtils functions
  *
  *  @ingroup ErrorHandling
@@ -7,12 +7,12 @@
  *  @author Robert DeSonia, MHPCC
  *
- *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2004-11-09 00:38:14 $
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-11-16 20:00:21 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
  */
 
-#ifndef PS_SYSUTILS_ERRORS_H
-#define PS_SYSUTILS_ERRORS_H
+#ifndef PS_FILEUTIL_ERRORS_H
+#define PS_FILEUTIL_ERRORS_H
 
 /* N.B., lines between '//~Start' and '//~End' are automatic generated from
@@ -38,4 +38,23 @@
 #define PS_ERRORTEXT_psLookupTable_DIVIDE_BY_ZERO "Divide by zero error during interpolation."
 #define PS_ERRORTEXT_psLookupTable_INVALID_TYPE "Invalid psLookupType, %d;"
+#define PS_ERRORTEXT_psFits_NULL "The input psFits object can not NULL."
+#define PS_ERRORTEXT_psFits_FILENAME_INVALID "Could not open file,'%s'.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_FILENAME_NULL "Specified filename can not be NULL."
+#define PS_ERRORTEXT_psFits_EXTNAME_INVALID "Could not find HDU '%s' in file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_EXTNUM_INVALID "Could not find HDU #%d in file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_GET_EXTNUM_FAILED "Failed to determine the current HDU number in file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_GETNUMHDUS_FAILED "Failed to determine the number of HDUs in file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_GETHDUTYPE_FAILED "Failed to determine an HDU type in file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_GETNUMKEYS_FAILED "Failed to determine the number of header keys in file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_FILENAME_CREATE_FAILED "Could not create file,'%s'.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_TYPE_UNSUPPORTED "Specified type, %s, is not supported."
+#define PS_ERRORTEXT_psFits_CREATE_HDU_FAILED "Could not create new image HDU in file,'%s'.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_NOT_IMAGE_TYPE "Current FITS HDU type must be an IMAGE."
+#define PS_ERRORTEXT_psFits_TABLE_FIRSTROW_NULL "Can't create a table without the first row, where table column information is derived."
+#define PS_ERRORTEXT_psFits_CFITSIO_ERROR "CFITSIO error: %s"
+#define PS_ERRORTEXT_psFits_METATYPE_INVALID "Specified FITS metadata type, %c, is not supported."
+#define PS_ERRORTEXT_psFits_METADATA_ADD_FAILED "Failed to add metadata item, %s."
+#define PS_ERRORTEXT_psFits_WRITE_FAILED "Could not write psImage data to file,'%s'.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_IMAGE_NULL "The input psImage was NULL.  Need a non-NULL psImage for operation."
 //~End
 
Index: /trunk/psLib/src/dataIO/psFits.c
===================================================================
--- /trunk/psLib/src/dataIO/psFits.c	(revision 2375)
+++ /trunk/psLib/src/dataIO/psFits.c	(revision 2375)
@@ -0,0 +1,614 @@
+/** @file  psFits.c
+ *
+ *  @brief Contains Fits I/O routines
+ *
+ *  @ingroup FileIO
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-11-16 20:00:21 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <unistd.h>
+
+#include "psFits.h"
+#include "string.h"
+#include "psError.h"
+#include "psFileUtilsErrors.h"
+#include "psMemory.h"
+#include "psString.h"
+#include "psLogMsg.h"
+
+#define MAX_STRING_LENGTH 256  // maximum length string for FITS routines
+
+static bool convertPsTypeToFits(psElemType type, int* bitPix, double* bZero, int* dataType)
+{
+
+    int bitpix;
+    int datatype;
+    double bzero = 0.0;
+
+    switch (type) {
+
+    case PS_TYPE_U8:
+        bitpix = BYTE_IMG;
+        datatype = TBYTE;
+        break;
+
+    case PS_TYPE_S8:
+        bitpix = BYTE_IMG;
+        bzero = INT8_MIN;
+        datatype = TSBYTE;
+        break;
+
+    case PS_TYPE_U16:
+        bitpix = SHORT_IMG;
+        bzero = -1.0 * INT16_MIN;
+        datatype = TUSHORT;
+        break;
+
+    case PS_TYPE_S16:
+        bitpix = SHORT_IMG;
+        datatype = TSHORT;
+        break;
+
+    case PS_TYPE_U32:
+        bitpix = LONG_IMG;
+        bzero = -1.0 * INT32_MIN;
+        datatype = TUINT;
+        break;
+
+    case PS_TYPE_S32:
+        bitpix = LONG_IMG;
+        datatype = TINT;
+        break;
+
+    case PS_TYPE_F32:
+        bitpix = FLOAT_IMG;
+        datatype = TFLOAT;
+        break;
+
+    case PS_TYPE_F64:
+        bitpix = DOUBLE_IMG;
+        datatype = TDOUBLE;
+        break;
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psFits_TYPE_UNSUPPORTED,
+                    typeStr);
+            return false;
+        }
+    }
+
+    // pass the requested parameters  (NULL parameters are not set, of course).
+    if (bitPix != NULL) {
+        *bitPix = bitpix;
+    }
+
+    if (dataType != NULL) {
+        *dataType = datatype;
+    }
+
+    if (bZero != NULL) {
+        *bZero = bzero;
+    }
+
+    return true;
+}
+
+static bool convertPsMetadataTypeToBinaryTForm(psMetadataType type, char** fitsType)
+{
+    switch (type) {
+    case PS_META_BOOL:
+        *fitsType = "1L";
+        break;
+    case PS_META_S32:
+        *fitsType = "1J";
+        break;
+    case PS_META_F32:
+        *fitsType = "1E";
+        break;
+    case PS_META_F64:
+        *fitsType = "1D";
+        break;
+        // XXX: Handle other types, e.g., Vectors, etc.
+    default:
+        return false;
+    }
+
+    return true;
+}
+
+static bool isHDUEmpty(psFits* fits)
+{
+    /* check for keys - no keys means this is really an empty HDU */
+    int keysexist = -1;
+    int morekeys;
+    int status = 0;
+
+    fits_get_hdrspace(fits->p_fd, &keysexist, &morekeys, &status);
+
+    // if no keys exist and not primary HDU, this really is an empty HDU
+    if (keysexist == 0) {
+        return true;
+    }
+
+    return false;
+
+}
+
+static void fitsFree(psFits* fits)
+{
+    int status = 0;
+
+    if (fits != NULL) {
+        (void)fits_close_file(fits->p_fd, &status);
+        psFree((void*)fits->filename);
+    }
+}
+
+psFits* psFitsAlloc(const char* name,                  ///< the FITS file name
+                    bool readwrite)                    ///< if TRUE, the file is open for read/write.
+{
+    int status = 0;
+    fitsfile *fptr = NULL;      /* Pointer to the FITS file */
+
+    if (name == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_FILENAME_NULL);
+        return NULL;
+    }
+
+    int iomode = (readwrite) ? READWRITE : READONLY;
+
+    /* Open/Create the FITS file */
+    if (access(name, F_OK) == 0) {     // file exists
+        (void)fits_open_file(&fptr, name, iomode, &status);
+        if (fptr == NULL || status != 0) {
+            char fitsErr[MAX_STRING_LENGTH];
+            fits_get_errstatus(status, fitsErr);
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psFits_FILENAME_INVALID,
+                    name, fitsErr);
+            return NULL;
+        }
+    } else {  // file does not exist
+        (void)fits_create_file(&fptr, name, &status);
+        if (fptr == NULL || status != 0) {
+            char fitsErr[MAX_STRING_LENGTH];
+            fits_get_errstatus(status, fitsErr);
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psFits_FILENAME_CREATE_FAILED,
+                    name, fitsErr);
+            return NULL;
+        }
+    }
+
+    psFits* fits = psAlloc(sizeof(psFits));
+    fits->filename = psAlloc(strlen(name)+1);
+    fits->p_fd = fptr;
+    strcpy((char*)fits->filename,name);
+    p_psMemSetDeallocator(fits,(psFreeFcn)fitsFree);
+
+    return fits;
+}
+
+psFitsType psFitsMoveExtName(psFits* fits,
+                             const char* extname)
+{
+    int status = 0;
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return PS_FITS_TYPE_NONE;
+    }
+
+
+    if (fits_movnam_hdu(fits->p_fd, ANY_HDU, (char*)extname, 0, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psFits_EXTNAME_INVALID,
+                extname, fits->filename, fitsErr);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    return psFitsGetExtType(fits);
+}
+
+psFitsType psFitsMoveExtNum(psFits* fits,
+                            int extnum,
+                            bool relative)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    int status = 0;
+    int hdutype = 0;
+
+    if (relative) {
+        fits_movrel_hdu(fits->p_fd, extnum+1, &hdutype, &status);
+    } else {
+        fits_movabs_hdu(fits->p_fd, extnum+1, &hdutype, &status);
+    }
+
+    if (status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psFits_EXTNUM_INVALID,
+                extnum, fits->filename, fitsErr);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    return hdutype;
+}
+
+int psFitsGetExtNum(psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    int hdutype = ANY_HDU;
+    int status = 0;
+
+    if (fits_get_hdu_type(fits->p_fd, &hdutype, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psFits_GET_EXTNUM_FAILED,
+                fits->filename, fitsErr);
+        return -1;
+    }
+
+    return hdutype-1;
+}
+
+char* psFitsGetExtName(psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return NULL;
+    }
+
+    int status = 0;
+    char name[MAX_STRING_LENGTH];
+
+    if (fits_read_key_str(fits->p_fd, "EXTNAME", name, NULL, &status) != 0) {
+        status = 0;
+        if (fits_read_key_str(fits->p_fd, "HDUNAME", name, NULL, &status) != 0) {
+            int num = psFitsGetExtNum(fits);
+            snprintf(name, MAX_STRING_LENGTH, "EXT-%3d",num);
+        }
+    }
+    return psStringCopy(name);
+}
+
+int psFitsGetSize(psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    int num = 0;
+    int status = 0;
+
+    if (fits_get_num_hdus(fits->p_fd, &num, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psFits_GETNUMHDUS_FAILED,
+                fits->filename, fitsErr);
+        return -1;
+    }
+
+    return num;
+}
+
+bool psFitsCreateExt(psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return false;
+    }
+
+    int status = 0;
+
+    fits_create_hdu(fits->p_fd,&status);
+
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_CREATE_HDU_FAILED,
+                fits->filename, fitsErr);
+        return false;
+    }
+
+    return true;
+}
+
+psFitsType psFitsGetExtType(psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    int status = 0;
+    int hdutype = PS_FITS_TYPE_NONE;
+
+    if (fits_get_hdu_type(fits->p_fd, &hdutype, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psFits_GETHDUTYPE_FAILED,
+                fits->filename, fitsErr);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    if (hdutype == PS_FITS_TYPE_IMAGE &&
+            psFitsGetExtNum(fits) > 0 &&
+            isHDUEmpty(fits)) {
+        return PS_FITS_TYPE_ANY;
+    }
+
+    return hdutype;
+}
+
+psMetadata* psFitsReadHeader(psMetadata* out,
+                             const psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return NULL;
+    }
+
+    if (out == NULL) {
+        out = psMetadataAlloc();
+        if (out == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to allocate a new psMetadata container.");
+            return NULL;
+        }
+    }
+
+    // Get number of key names
+    int numKeys = 0;
+    int keyNum = 0;
+    int status = 0;
+    fits_get_hdrpos(fits->p_fd, &numKeys, &keyNum, &status);
+
+    // Get each key name. Keywords start at one.
+    char keyType;
+    char keyName[MAX_STRING_LENGTH];
+    char keyValue[MAX_STRING_LENGTH];
+    char keyComment[MAX_STRING_LENGTH];
+    psMetadataType metadataItemType;
+    psBool tempBool;
+    psBool success;
+    for (int i = 1; i <= numKeys; i++) {
+
+        fits_read_keyn(fits->p_fd, i, keyName, keyValue, keyComment, &status);
+        fits_get_keytype(keyValue, &keyType, &status);
+        if (status != 0) {
+            break;
+        }
+
+        switch (keyType) {
+        case 'I':
+            metadataItemType = PS_META_S32;
+            success = psMetadataAdd(out, PS_LIST_TAIL, keyName, metadataItemType, keyComment, atoi(keyValue));
+            break;
+        case 'F':
+            metadataItemType = PS_META_F64;
+            success = psMetadataAdd(out, PS_LIST_TAIL, keyName, metadataItemType, keyComment, atof(keyValue));
+            break;
+        case 'C':
+            metadataItemType = PS_META_STR;
+            success = psMetadataAdd(out, PS_LIST_TAIL, keyName, metadataItemType, keyComment, keyValue);
+            break;
+        case 'L':
+            metadataItemType = PS_META_BOOL;
+            tempBool = (keyValue[0] == 'T') ? 1 : 0;
+            success = psMetadataAdd(out, PS_LIST_TAIL, keyName, metadataItemType, keyComment, tempBool);
+            break;
+        case 'U':
+        case 'X':
+        default:
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psFits_METATYPE_INVALID,
+                    keyType);
+            return out;
+        }
+
+        if (!success) {
+            psError(PS_ERR_UNKNOWN, false,
+                    PS_ERRORTEXT_psFits_METADATA_ADD_FAILED,
+                    keyName);
+            return out;
+        }
+    }
+
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_METADATA_ADD_FAILED,
+                fitsErr);
+        return false;
+    }
+
+    return out;
+}
+
+psHash* psFitsReadHeaderSet(psHash* out,
+                            psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return out;
+    }
+
+    if (out == NULL) {
+        out = psHashAlloc(10);
+        // XXX: what is the appropriate number of buckets? Got 10 from psMetadataAlloc.
+        if (out == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to allocate a new psHash container.");
+            return NULL;
+        }
+    }
+
+    int status = 0;
+
+    int size = psFitsGetSize(fits);
+    int position = psFitsGetExtNum(fits);
+
+    for (int lcv=0; lcv < size; lcv++) {
+        if (psFitsMoveExtNum(fits, lcv, false)) {
+            char* name = psFitsGetExtName(fits);
+            psMetadata* header = psFitsReadHeader(NULL, fits);
+            if (name != NULL || header != NULL) {
+                psHashAdd(out, name, header);
+            } else { // XXX: is this a warning or error?
+                psLogMsg(__func__, PS_LOG_WARN,
+                         "Failed to read HDU#%d header data.",
+                         lcv);
+            }
+
+            psFree(name);
+            psFree(header);
+        }
+    }
+}
+
+bool psFitsWriteImage(psFits* fits,
+                      const psImage* input,
+                      int x,
+                      int y,
+                      int z)
+{
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return false;
+    }
+
+    if (input == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_IMAGE_NULL);
+        return false;
+    }
+    int numCols = input->numCols;
+    int numRows = input->numRows;
+
+    int status = 0;
+
+    psFitsType fitsType = psFitsGetExtType(fits);
+
+    if (fitsType != PS_FITS_TYPE_IMAGE && fitsType != PS_FITS_TYPE_ANY) {
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_NOT_IMAGE_TYPE);
+        return false;
+    }
+
+    // determine the FITS-equivalent parameters
+    int bitPix;
+    double bZero;
+    int dataType;
+    if (! convertPsTypeToFits(input->type.type, &bitPix, &bZero, &dataType) ) {
+        return false;
+    }
+
+    if (fitsType == PS_FITS_TYPE_ANY) {
+        int naxis = 3;
+        long naxes[3];
+
+        naxes[0] = numCols;
+        naxes[1] = numRows;
+        naxes[2] = z;
+
+        if (z < 2) {
+            naxis = 2;
+        }
+
+        /* write the required keys to make an empty HDU be an IMAGE HDU */
+        fits_write_imghdr(fits->p_fd, bitPix, naxis, naxes, &status);
+        if (bZero != 0) {        // set the bscale/bzero
+            fits_write_key_dbl(fits->p_fd, "BZERO", bZero, 12, "Pixel Value Offset", &status);
+            fits_write_key_dbl(fits->p_fd, "BSCALE", 1.0, 12, "Pixel Value Scale", &status);
+            // don't think this is needed: fits_set_bscale(fits->p_fd, bScale, bZero, &status);
+        }
+    }
+
+    long firstPixel[3];         /* First Pixel to read */
+    long lastPixel[3];          /* Last Pixel to read */
+    firstPixel[0] = x + 1;
+    firstPixel[1] = y + 1;
+    firstPixel[2] = z + 1;
+
+    lastPixel[0] = firstPixel[0] + numCols - 1;
+    lastPixel[1] = firstPixel[1] + numRows - 1;
+    lastPixel[2] = z + 1;
+
+    fits_write_subset(fits->p_fd, dataType, firstPixel, lastPixel, input->data.V[0], &status);
+
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_WRITE_FAILED,
+                fits->filename, fitsErr);
+        return false;
+    }
+
+    return true;
+
+}
+
+psMetadata* psFitsReadTableRow(psFits* fits,
+                               int row)
+{
+}
+
+psArray* psFitsReadTableColumn(psFits* fits,
+                               const char* colname)
+{
+}
+
+psVector* psFitsReadTableColumnNum(psFits* fits,
+                                   const char* colname)
+{
+}
+
+
+psArray* psFitsReadTable(psFits* fits)
+{
+}
+
+bool psFitsWriteTable(psFits* fits,
+                      psArray* table)
+{
+}
Index: /trunk/psLib/src/dataIO/psFits.h
===================================================================
--- /trunk/psLib/src/dataIO/psFits.h	(revision 2375)
+++ /trunk/psLib/src/dataIO/psFits.h	(revision 2375)
@@ -0,0 +1,240 @@
+/** @file  psFits.h
+ *
+ *  @brief Contains Fits I/O routines
+ *
+ *  @ingroup FileIO
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-11-16 20:00:21 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_FITS_H
+#define PS_FITS_H
+
+#include<fitsio.h>
+
+#include "psType.h"
+#include "psArray.h"
+#include "psVector.h"
+#include "psMetadata.h"
+#include "psHash.h"
+#include "psImage.h"
+
+/// @addtogroup FileIO
+/// @{
+
+typedef enum {
+    PS_FITS_TYPE_NONE = -1,
+    PS_FITS_TYPE_IMAGE = IMAGE_HDU,
+    PS_FITS_TYPE_BINARY_TABLE = BINARY_TBL,
+    PS_FITS_TYPE_ASCII_TABLE = ASCII_TBL,
+    PS_FITS_TYPE_ANY = ANY_HDU
+} psFitsType;
+
+/** FITS file object.
+ * 
+ *  This object should be considered opaque to the user; no item in this
+ *  struct should be accessed directly.
+ *
+ */
+typedef struct
+{
+    fitsfile* p_fd;                    ///< the CFITSIO fits files handle.
+    const char* filename;              ///< the filename of the fits file
+}
+psFits;
+
+/** Opens a FITS file and allocates the associated psFits object.
+ *
+ *  @return psFits*    new psFits object for the FITS files specified or 
+ *                     NULL if the open of the FITS file failed
+ */
+psFits* psFitsAlloc(
+    const char* name,                  ///< the FITS file name
+    bool readwrite                     ///< if TRUE, the file is open for read/write.
+);
+
+/** Moves the FITS HDU to the specified extension name.
+ *
+ *  @return psFitsType    The HDU type, or PS_FITS_TYPE_NONE if move failed.
+ */
+psFitsType psFitsMoveExtName(
+    psFits* fits,                      ///< the psFits object to move
+    const char* extname                ///< the extension name
+);
+
+/** Moves the FITS HDU to the specified extension number
+ *
+ *  @return psFitsType    The HDU type, or PS_FITS_TYPE_NONE if move failed.
+ */
+psFitsType psFitsMoveExtNum(
+    psFits* fits,                      ///< the psFits object to move
+    int extnum,                        ///< the extension number to move to (zero is primary HDU)
+    bool relative                      ///< if true, extnum is a relative number to the current position
+);
+
+/** Get the current extension number, where 0 is the primary HDU.
+ *
+ *  @return int        Current HDU number of the psFits file or < 0 if an error
+ *                     occurred.
+ */
+int psFitsGetExtNum(
+    psFits* fits                       ///< the psFits object
+);
+
+/** Get the current extension name.
+ *
+ *  @return int        Current HDU name of the psFits file or NULL if an 
+ *                     error occurred.
+ */
+char* psFitsGetExtName(
+    psFits* fits                       ///< the psFits object
+);
+
+/** Get the total number of HDUs in the FITS file.
+ *
+ *  @return int        The total number of HDUs in the FITS file or < 0 if an 
+ *                     error occurred.
+ */
+int psFitsGetSize(
+    psFits* fits                       ///< the psFits object
+);
+
+/** Create a new extension on the end of the file of the given type.  The
+ *  FITS file is 
+ *
+ *  @return bool       true if successful, otherwise false
+ */
+bool psFitsCreateExt(
+    psFits* fits                       ///< the psFits object
+);
+
+/** Get the extension type of the current HDU.
+ *
+ *  @return psFitsType The type of the current HDU.  If PS_FITS_TYPE_UNKNOWN, 
+ *                     the type could not be determined.
+ */
+psFitsType psFitsGetExtType(
+    psFits* fits                       ///< the psFits object
+);
+
+/** Reads the header of the current HDU.
+ *
+ *  @return psMetadata*   the header data
+ */
+psMetadata* psFitsReadHeader(
+    psMetadata* out,
+    ///< The psMetadata to add the header data.  If null, a new psMetadata is created.
+
+    const psFits* fits                 ///< the psFits object
+);
+
+/** Reads the header of all HDUs.  The current HDU is not changed.
+ *
+ *  @return psHash*      the header data
+ */
+psHash* psFitsReadHeaderSet(
+    psHash* out,
+    ///< The psHash to add the header data via psMetadata items.  If null, a
+    ///< new psHash is created.  The keys of the psHash are the extension names
+    ///< of the cooresponding HDUs.
+
+    psFits* fits                       ///< the psFits object
+);
+
+/** Writes the values of the metadata to the current HDU header.
+ *
+ *  @return bool        if TRUE, the write was successful, otherwise FALSE.
+ */
+bool psFitsWriteHeader(
+    psFits* fits,                      ///< the psFits object
+    psMetadata* out                    ///< the psMetadata data in which to write
+);
+
+/** Reads an image, given the desired region and z-plane.
+ *
+ *  @return psImage*     the read image or NULL if there was an error.
+ */
+psImage* psfitsReadImageSection(
+    psImage* out,                      ///< a psImage to recycle.
+    psFits* fits,                      ///< the psFits object
+    psRegion region,                   ///< the region in the FITS image to read
+    int z                              ///< the z-plane in the FITS image cube to read
+);
+
+/** Writes an image, given the desired region and z-plane.
+ *
+ *  @return bool        TRUE is the write was successful, otherwise FALSE.
+ */
+bool psFitsWriteImageSection(
+    psFits* fits,                      ///< the psFits object
+    const psImage* input,              ///< the image to output
+    psRegion region,                   ///< the part of the FITS image to overwrite
+    int z                              ///< the z-plane of the FITS image data cube to use
+);
+
+/** Reads a table row.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psMetadata*    The table row's data.  The keys are the column names.
+ */
+psMetadata* psFitsReadTableRow(
+    psFits* fits,                      ///< the psFits object
+    int row                            ///< row number to read
+);
+
+/** Reads a table column.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psArray*    Array of data items for the specified column or NULL 
+ *                      if an error occurred.
+ */
+psArray* psFitsReadTableColumn(
+    psFits* fits,
+    const char* colname
+);
+
+/** Reads a table column of numbers.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psVector*    Vector of data for the specified column or NULL
+ *                       if an error occurred.
+ */
+psVector* psFitsReadTableColumnNum(
+    psFits* fits,                      ///< the psFits object
+    const char* colname                ///< the column name
+);
+
+
+/** Reads a whole FITS table.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psArray*     Array of psMetadata items, which contains the output
+ *                       data items of each row.
+ *  
+ *  @see psFitsReadTableRow
+ */
+psArray* psFitsReadTable(
+    psFits* fits                       ///< the psFits object
+);
+
+/** Writes a whole FITS table.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return bool        TRUE if the write was successful, otherwise FALSE
+ *  
+ *  @see psFitsReadTableRow
+ */
+bool psFitsWriteTable(
+    psFits* fits,                      ///< the psFits object
+    psArray* table
+    ///< Array of psMetadata items, which contains the output data items of each row.
+);
+
+/// @}
+
+#endif
Index: /trunk/psLib/src/fileUtils/Makefile
===================================================================
--- /trunk/psLib/src/fileUtils/Makefile	(revision 2374)
+++ /trunk/psLib/src/fileUtils/Makefile	(revision 2375)
@@ -16,6 +16,6 @@
 all: $(TARGET_STATIC)
 
-#psAstronomyErrors.h: psAstronomyErrors.dat
-#	perl ../parseErrorCodes.pl --data=$? $@
+psFileUtilsErrors.h: psFileUtilsErrors.dat
+	perl ../parseErrorCodes.pl --data=$? $@
 
 
Index: /trunk/psLib/src/fileUtils/psFileUtilsErrors.dat
===================================================================
--- /trunk/psLib/src/fileUtils/psFileUtilsErrors.dat	(revision 2374)
+++ /trunk/psLib/src/fileUtils/psFileUtilsErrors.dat	(revision 2375)
@@ -15,2 +15,22 @@
 psLookupTable_DIVIDE_BY_ZERO           Divide by zero error during interpolation.
 psLookupTable_INVALID_TYPE             Invalid psLookupType, %d;
+#
+psFits_NULL                            The input psFits object can not NULL.
+psFits_FILENAME_INVALID                Could not open file,'%s'.\nCFITSIO Error: %s
+psFits_FILENAME_NULL                   Specified filename can not be NULL.
+psFits_EXTNAME_INVALID                 Could not find HDU '%s' in file %s.\nCFITSIO Error: %s
+psFits_EXTNUM_INVALID                  Could not find HDU #%d in file %s.\nCFITSIO Error: %s
+psFits_GET_EXTNUM_FAILED               Failed to determine the current HDU number in file %s.\nCFITSIO Error: %s
+psFits_GETNUMHDUS_FAILED               Failed to determine the number of HDUs in file %s.\nCFITSIO Error: %s
+psFits_GETHDUTYPE_FAILED               Failed to determine an HDU type in file %s.\nCFITSIO Error: %s
+psFits_GETNUMKEYS_FAILED               Failed to determine the number of header keys in file %s.\nCFITSIO Error: %s
+psFits_FILENAME_CREATE_FAILED          Could not create file,'%s'.\nCFITSIO Error: %s
+psFits_TYPE_UNSUPPORTED                Specified type, %s, is not supported.
+psFits_CREATE_HDU_FAILED               Could not create new image HDU in file,'%s'.\nCFITSIO Error: %s
+psFits_NOT_IMAGE_TYPE                  Current FITS HDU type must be an IMAGE.
+psFits_TABLE_FIRSTROW_NULL             Can't create a table without the first row, where table column information is derived.
+psFits_CFITSIO_ERROR                   CFITSIO error: %s
+psFits_METATYPE_INVALID                Specified FITS metadata type, %c, is not supported.
+psFits_METADATA_ADD_FAILED             Failed to add metadata item, %s.
+psFits_WRITE_FAILED                    Could not write psImage data to file,'%s'.\nCFITSIO Error: %s
+psFits_IMAGE_NULL                      The input psImage was NULL.  Need a non-NULL psImage for operation.
Index: /trunk/psLib/src/fileUtils/psFileUtilsErrors.h
===================================================================
--- /trunk/psLib/src/fileUtils/psFileUtilsErrors.h	(revision 2374)
+++ /trunk/psLib/src/fileUtils/psFileUtilsErrors.h	(revision 2375)
@@ -1,5 +1,5 @@
-/** @file  psAstronomyErrors.h
+/** @file  psFileUtilsErrors.h
  *
- *  @brief Contains the error text for the astronomy functions
+ *  @brief Contains the error text for the fileUtils functions
  *
  *  @ingroup ErrorHandling
@@ -7,12 +7,12 @@
  *  @author Robert DeSonia, MHPCC
  *
- *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2004-11-09 00:38:14 $
+ *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-11-16 20:00:21 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
  */
 
-#ifndef PS_SYSUTILS_ERRORS_H
-#define PS_SYSUTILS_ERRORS_H
+#ifndef PS_FILEUTIL_ERRORS_H
+#define PS_FILEUTIL_ERRORS_H
 
 /* N.B., lines between '//~Start' and '//~End' are automatic generated from
@@ -38,4 +38,23 @@
 #define PS_ERRORTEXT_psLookupTable_DIVIDE_BY_ZERO "Divide by zero error during interpolation."
 #define PS_ERRORTEXT_psLookupTable_INVALID_TYPE "Invalid psLookupType, %d;"
+#define PS_ERRORTEXT_psFits_NULL "The input psFits object can not NULL."
+#define PS_ERRORTEXT_psFits_FILENAME_INVALID "Could not open file,'%s'.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_FILENAME_NULL "Specified filename can not be NULL."
+#define PS_ERRORTEXT_psFits_EXTNAME_INVALID "Could not find HDU '%s' in file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_EXTNUM_INVALID "Could not find HDU #%d in file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_GET_EXTNUM_FAILED "Failed to determine the current HDU number in file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_GETNUMHDUS_FAILED "Failed to determine the number of HDUs in file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_GETHDUTYPE_FAILED "Failed to determine an HDU type in file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_GETNUMKEYS_FAILED "Failed to determine the number of header keys in file %s.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_FILENAME_CREATE_FAILED "Could not create file,'%s'.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_TYPE_UNSUPPORTED "Specified type, %s, is not supported."
+#define PS_ERRORTEXT_psFits_CREATE_HDU_FAILED "Could not create new image HDU in file,'%s'.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_NOT_IMAGE_TYPE "Current FITS HDU type must be an IMAGE."
+#define PS_ERRORTEXT_psFits_TABLE_FIRSTROW_NULL "Can't create a table without the first row, where table column information is derived."
+#define PS_ERRORTEXT_psFits_CFITSIO_ERROR "CFITSIO error: %s"
+#define PS_ERRORTEXT_psFits_METATYPE_INVALID "Specified FITS metadata type, %c, is not supported."
+#define PS_ERRORTEXT_psFits_METADATA_ADD_FAILED "Failed to add metadata item, %s."
+#define PS_ERRORTEXT_psFits_WRITE_FAILED "Could not write psImage data to file,'%s'.\nCFITSIO Error: %s"
+#define PS_ERRORTEXT_psFits_IMAGE_NULL "The input psImage was NULL.  Need a non-NULL psImage for operation."
 //~End
 
Index: /trunk/psLib/src/fileUtils/psFits.c
===================================================================
--- /trunk/psLib/src/fileUtils/psFits.c	(revision 2375)
+++ /trunk/psLib/src/fileUtils/psFits.c	(revision 2375)
@@ -0,0 +1,614 @@
+/** @file  psFits.c
+ *
+ *  @brief Contains Fits I/O routines
+ *
+ *  @ingroup FileIO
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-11-16 20:00:21 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <unistd.h>
+
+#include "psFits.h"
+#include "string.h"
+#include "psError.h"
+#include "psFileUtilsErrors.h"
+#include "psMemory.h"
+#include "psString.h"
+#include "psLogMsg.h"
+
+#define MAX_STRING_LENGTH 256  // maximum length string for FITS routines
+
+static bool convertPsTypeToFits(psElemType type, int* bitPix, double* bZero, int* dataType)
+{
+
+    int bitpix;
+    int datatype;
+    double bzero = 0.0;
+
+    switch (type) {
+
+    case PS_TYPE_U8:
+        bitpix = BYTE_IMG;
+        datatype = TBYTE;
+        break;
+
+    case PS_TYPE_S8:
+        bitpix = BYTE_IMG;
+        bzero = INT8_MIN;
+        datatype = TSBYTE;
+        break;
+
+    case PS_TYPE_U16:
+        bitpix = SHORT_IMG;
+        bzero = -1.0 * INT16_MIN;
+        datatype = TUSHORT;
+        break;
+
+    case PS_TYPE_S16:
+        bitpix = SHORT_IMG;
+        datatype = TSHORT;
+        break;
+
+    case PS_TYPE_U32:
+        bitpix = LONG_IMG;
+        bzero = -1.0 * INT32_MIN;
+        datatype = TUINT;
+        break;
+
+    case PS_TYPE_S32:
+        bitpix = LONG_IMG;
+        datatype = TINT;
+        break;
+
+    case PS_TYPE_F32:
+        bitpix = FLOAT_IMG;
+        datatype = TFLOAT;
+        break;
+
+    case PS_TYPE_F64:
+        bitpix = DOUBLE_IMG;
+        datatype = TDOUBLE;
+        break;
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psFits_TYPE_UNSUPPORTED,
+                    typeStr);
+            return false;
+        }
+    }
+
+    // pass the requested parameters  (NULL parameters are not set, of course).
+    if (bitPix != NULL) {
+        *bitPix = bitpix;
+    }
+
+    if (dataType != NULL) {
+        *dataType = datatype;
+    }
+
+    if (bZero != NULL) {
+        *bZero = bzero;
+    }
+
+    return true;
+}
+
+static bool convertPsMetadataTypeToBinaryTForm(psMetadataType type, char** fitsType)
+{
+    switch (type) {
+    case PS_META_BOOL:
+        *fitsType = "1L";
+        break;
+    case PS_META_S32:
+        *fitsType = "1J";
+        break;
+    case PS_META_F32:
+        *fitsType = "1E";
+        break;
+    case PS_META_F64:
+        *fitsType = "1D";
+        break;
+        // XXX: Handle other types, e.g., Vectors, etc.
+    default:
+        return false;
+    }
+
+    return true;
+}
+
+static bool isHDUEmpty(psFits* fits)
+{
+    /* check for keys - no keys means this is really an empty HDU */
+    int keysexist = -1;
+    int morekeys;
+    int status = 0;
+
+    fits_get_hdrspace(fits->p_fd, &keysexist, &morekeys, &status);
+
+    // if no keys exist and not primary HDU, this really is an empty HDU
+    if (keysexist == 0) {
+        return true;
+    }
+
+    return false;
+
+}
+
+static void fitsFree(psFits* fits)
+{
+    int status = 0;
+
+    if (fits != NULL) {
+        (void)fits_close_file(fits->p_fd, &status);
+        psFree((void*)fits->filename);
+    }
+}
+
+psFits* psFitsAlloc(const char* name,                  ///< the FITS file name
+                    bool readwrite)                    ///< if TRUE, the file is open for read/write.
+{
+    int status = 0;
+    fitsfile *fptr = NULL;      /* Pointer to the FITS file */
+
+    if (name == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_FILENAME_NULL);
+        return NULL;
+    }
+
+    int iomode = (readwrite) ? READWRITE : READONLY;
+
+    /* Open/Create the FITS file */
+    if (access(name, F_OK) == 0) {     // file exists
+        (void)fits_open_file(&fptr, name, iomode, &status);
+        if (fptr == NULL || status != 0) {
+            char fitsErr[MAX_STRING_LENGTH];
+            fits_get_errstatus(status, fitsErr);
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psFits_FILENAME_INVALID,
+                    name, fitsErr);
+            return NULL;
+        }
+    } else {  // file does not exist
+        (void)fits_create_file(&fptr, name, &status);
+        if (fptr == NULL || status != 0) {
+            char fitsErr[MAX_STRING_LENGTH];
+            fits_get_errstatus(status, fitsErr);
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psFits_FILENAME_CREATE_FAILED,
+                    name, fitsErr);
+            return NULL;
+        }
+    }
+
+    psFits* fits = psAlloc(sizeof(psFits));
+    fits->filename = psAlloc(strlen(name)+1);
+    fits->p_fd = fptr;
+    strcpy((char*)fits->filename,name);
+    p_psMemSetDeallocator(fits,(psFreeFcn)fitsFree);
+
+    return fits;
+}
+
+psFitsType psFitsMoveExtName(psFits* fits,
+                             const char* extname)
+{
+    int status = 0;
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return PS_FITS_TYPE_NONE;
+    }
+
+
+    if (fits_movnam_hdu(fits->p_fd, ANY_HDU, (char*)extname, 0, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psFits_EXTNAME_INVALID,
+                extname, fits->filename, fitsErr);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    return psFitsGetExtType(fits);
+}
+
+psFitsType psFitsMoveExtNum(psFits* fits,
+                            int extnum,
+                            bool relative)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    int status = 0;
+    int hdutype = 0;
+
+    if (relative) {
+        fits_movrel_hdu(fits->p_fd, extnum+1, &hdutype, &status);
+    } else {
+        fits_movabs_hdu(fits->p_fd, extnum+1, &hdutype, &status);
+    }
+
+    if (status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psFits_EXTNUM_INVALID,
+                extnum, fits->filename, fitsErr);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    return hdutype;
+}
+
+int psFitsGetExtNum(psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    int hdutype = ANY_HDU;
+    int status = 0;
+
+    if (fits_get_hdu_type(fits->p_fd, &hdutype, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psFits_GET_EXTNUM_FAILED,
+                fits->filename, fitsErr);
+        return -1;
+    }
+
+    return hdutype-1;
+}
+
+char* psFitsGetExtName(psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return NULL;
+    }
+
+    int status = 0;
+    char name[MAX_STRING_LENGTH];
+
+    if (fits_read_key_str(fits->p_fd, "EXTNAME", name, NULL, &status) != 0) {
+        status = 0;
+        if (fits_read_key_str(fits->p_fd, "HDUNAME", name, NULL, &status) != 0) {
+            int num = psFitsGetExtNum(fits);
+            snprintf(name, MAX_STRING_LENGTH, "EXT-%3d",num);
+        }
+    }
+    return psStringCopy(name);
+}
+
+int psFitsGetSize(psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    int num = 0;
+    int status = 0;
+
+    if (fits_get_num_hdus(fits->p_fd, &num, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psFits_GETNUMHDUS_FAILED,
+                fits->filename, fitsErr);
+        return -1;
+    }
+
+    return num;
+}
+
+bool psFitsCreateExt(psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return false;
+    }
+
+    int status = 0;
+
+    fits_create_hdu(fits->p_fd,&status);
+
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_CREATE_HDU_FAILED,
+                fits->filename, fitsErr);
+        return false;
+    }
+
+    return true;
+}
+
+psFitsType psFitsGetExtType(psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    int status = 0;
+    int hdutype = PS_FITS_TYPE_NONE;
+
+    if (fits_get_hdu_type(fits->p_fd, &hdutype, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psFits_GETHDUTYPE_FAILED,
+                fits->filename, fitsErr);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    if (hdutype == PS_FITS_TYPE_IMAGE &&
+            psFitsGetExtNum(fits) > 0 &&
+            isHDUEmpty(fits)) {
+        return PS_FITS_TYPE_ANY;
+    }
+
+    return hdutype;
+}
+
+psMetadata* psFitsReadHeader(psMetadata* out,
+                             const psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return NULL;
+    }
+
+    if (out == NULL) {
+        out = psMetadataAlloc();
+        if (out == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to allocate a new psMetadata container.");
+            return NULL;
+        }
+    }
+
+    // Get number of key names
+    int numKeys = 0;
+    int keyNum = 0;
+    int status = 0;
+    fits_get_hdrpos(fits->p_fd, &numKeys, &keyNum, &status);
+
+    // Get each key name. Keywords start at one.
+    char keyType;
+    char keyName[MAX_STRING_LENGTH];
+    char keyValue[MAX_STRING_LENGTH];
+    char keyComment[MAX_STRING_LENGTH];
+    psMetadataType metadataItemType;
+    psBool tempBool;
+    psBool success;
+    for (int i = 1; i <= numKeys; i++) {
+
+        fits_read_keyn(fits->p_fd, i, keyName, keyValue, keyComment, &status);
+        fits_get_keytype(keyValue, &keyType, &status);
+        if (status != 0) {
+            break;
+        }
+
+        switch (keyType) {
+        case 'I':
+            metadataItemType = PS_META_S32;
+            success = psMetadataAdd(out, PS_LIST_TAIL, keyName, metadataItemType, keyComment, atoi(keyValue));
+            break;
+        case 'F':
+            metadataItemType = PS_META_F64;
+            success = psMetadataAdd(out, PS_LIST_TAIL, keyName, metadataItemType, keyComment, atof(keyValue));
+            break;
+        case 'C':
+            metadataItemType = PS_META_STR;
+            success = psMetadataAdd(out, PS_LIST_TAIL, keyName, metadataItemType, keyComment, keyValue);
+            break;
+        case 'L':
+            metadataItemType = PS_META_BOOL;
+            tempBool = (keyValue[0] == 'T') ? 1 : 0;
+            success = psMetadataAdd(out, PS_LIST_TAIL, keyName, metadataItemType, keyComment, tempBool);
+            break;
+        case 'U':
+        case 'X':
+        default:
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psFits_METATYPE_INVALID,
+                    keyType);
+            return out;
+        }
+
+        if (!success) {
+            psError(PS_ERR_UNKNOWN, false,
+                    PS_ERRORTEXT_psFits_METADATA_ADD_FAILED,
+                    keyName);
+            return out;
+        }
+    }
+
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_METADATA_ADD_FAILED,
+                fitsErr);
+        return false;
+    }
+
+    return out;
+}
+
+psHash* psFitsReadHeaderSet(psHash* out,
+                            psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return out;
+    }
+
+    if (out == NULL) {
+        out = psHashAlloc(10);
+        // XXX: what is the appropriate number of buckets? Got 10 from psMetadataAlloc.
+        if (out == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to allocate a new psHash container.");
+            return NULL;
+        }
+    }
+
+    int status = 0;
+
+    int size = psFitsGetSize(fits);
+    int position = psFitsGetExtNum(fits);
+
+    for (int lcv=0; lcv < size; lcv++) {
+        if (psFitsMoveExtNum(fits, lcv, false)) {
+            char* name = psFitsGetExtName(fits);
+            psMetadata* header = psFitsReadHeader(NULL, fits);
+            if (name != NULL || header != NULL) {
+                psHashAdd(out, name, header);
+            } else { // XXX: is this a warning or error?
+                psLogMsg(__func__, PS_LOG_WARN,
+                         "Failed to read HDU#%d header data.",
+                         lcv);
+            }
+
+            psFree(name);
+            psFree(header);
+        }
+    }
+}
+
+bool psFitsWriteImage(psFits* fits,
+                      const psImage* input,
+                      int x,
+                      int y,
+                      int z)
+{
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return false;
+    }
+
+    if (input == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_IMAGE_NULL);
+        return false;
+    }
+    int numCols = input->numCols;
+    int numRows = input->numRows;
+
+    int status = 0;
+
+    psFitsType fitsType = psFitsGetExtType(fits);
+
+    if (fitsType != PS_FITS_TYPE_IMAGE && fitsType != PS_FITS_TYPE_ANY) {
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_NOT_IMAGE_TYPE);
+        return false;
+    }
+
+    // determine the FITS-equivalent parameters
+    int bitPix;
+    double bZero;
+    int dataType;
+    if (! convertPsTypeToFits(input->type.type, &bitPix, &bZero, &dataType) ) {
+        return false;
+    }
+
+    if (fitsType == PS_FITS_TYPE_ANY) {
+        int naxis = 3;
+        long naxes[3];
+
+        naxes[0] = numCols;
+        naxes[1] = numRows;
+        naxes[2] = z;
+
+        if (z < 2) {
+            naxis = 2;
+        }
+
+        /* write the required keys to make an empty HDU be an IMAGE HDU */
+        fits_write_imghdr(fits->p_fd, bitPix, naxis, naxes, &status);
+        if (bZero != 0) {        // set the bscale/bzero
+            fits_write_key_dbl(fits->p_fd, "BZERO", bZero, 12, "Pixel Value Offset", &status);
+            fits_write_key_dbl(fits->p_fd, "BSCALE", 1.0, 12, "Pixel Value Scale", &status);
+            // don't think this is needed: fits_set_bscale(fits->p_fd, bScale, bZero, &status);
+        }
+    }
+
+    long firstPixel[3];         /* First Pixel to read */
+    long lastPixel[3];          /* Last Pixel to read */
+    firstPixel[0] = x + 1;
+    firstPixel[1] = y + 1;
+    firstPixel[2] = z + 1;
+
+    lastPixel[0] = firstPixel[0] + numCols - 1;
+    lastPixel[1] = firstPixel[1] + numRows - 1;
+    lastPixel[2] = z + 1;
+
+    fits_write_subset(fits->p_fd, dataType, firstPixel, lastPixel, input->data.V[0], &status);
+
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_WRITE_FAILED,
+                fits->filename, fitsErr);
+        return false;
+    }
+
+    return true;
+
+}
+
+psMetadata* psFitsReadTableRow(psFits* fits,
+                               int row)
+{
+}
+
+psArray* psFitsReadTableColumn(psFits* fits,
+                               const char* colname)
+{
+}
+
+psVector* psFitsReadTableColumnNum(psFits* fits,
+                                   const char* colname)
+{
+}
+
+
+psArray* psFitsReadTable(psFits* fits)
+{
+}
+
+bool psFitsWriteTable(psFits* fits,
+                      psArray* table)
+{
+}
Index: /trunk/psLib/src/fileUtils/psFits.h
===================================================================
--- /trunk/psLib/src/fileUtils/psFits.h	(revision 2375)
+++ /trunk/psLib/src/fileUtils/psFits.h	(revision 2375)
@@ -0,0 +1,240 @@
+/** @file  psFits.h
+ *
+ *  @brief Contains Fits I/O routines
+ *
+ *  @ingroup FileIO
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-11-16 20:00:21 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_FITS_H
+#define PS_FITS_H
+
+#include<fitsio.h>
+
+#include "psType.h"
+#include "psArray.h"
+#include "psVector.h"
+#include "psMetadata.h"
+#include "psHash.h"
+#include "psImage.h"
+
+/// @addtogroup FileIO
+/// @{
+
+typedef enum {
+    PS_FITS_TYPE_NONE = -1,
+    PS_FITS_TYPE_IMAGE = IMAGE_HDU,
+    PS_FITS_TYPE_BINARY_TABLE = BINARY_TBL,
+    PS_FITS_TYPE_ASCII_TABLE = ASCII_TBL,
+    PS_FITS_TYPE_ANY = ANY_HDU
+} psFitsType;
+
+/** FITS file object.
+ * 
+ *  This object should be considered opaque to the user; no item in this
+ *  struct should be accessed directly.
+ *
+ */
+typedef struct
+{
+    fitsfile* p_fd;                    ///< the CFITSIO fits files handle.
+    const char* filename;              ///< the filename of the fits file
+}
+psFits;
+
+/** Opens a FITS file and allocates the associated psFits object.
+ *
+ *  @return psFits*    new psFits object for the FITS files specified or 
+ *                     NULL if the open of the FITS file failed
+ */
+psFits* psFitsAlloc(
+    const char* name,                  ///< the FITS file name
+    bool readwrite                     ///< if TRUE, the file is open for read/write.
+);
+
+/** Moves the FITS HDU to the specified extension name.
+ *
+ *  @return psFitsType    The HDU type, or PS_FITS_TYPE_NONE if move failed.
+ */
+psFitsType psFitsMoveExtName(
+    psFits* fits,                      ///< the psFits object to move
+    const char* extname                ///< the extension name
+);
+
+/** Moves the FITS HDU to the specified extension number
+ *
+ *  @return psFitsType    The HDU type, or PS_FITS_TYPE_NONE if move failed.
+ */
+psFitsType psFitsMoveExtNum(
+    psFits* fits,                      ///< the psFits object to move
+    int extnum,                        ///< the extension number to move to (zero is primary HDU)
+    bool relative                      ///< if true, extnum is a relative number to the current position
+);
+
+/** Get the current extension number, where 0 is the primary HDU.
+ *
+ *  @return int        Current HDU number of the psFits file or < 0 if an error
+ *                     occurred.
+ */
+int psFitsGetExtNum(
+    psFits* fits                       ///< the psFits object
+);
+
+/** Get the current extension name.
+ *
+ *  @return int        Current HDU name of the psFits file or NULL if an 
+ *                     error occurred.
+ */
+char* psFitsGetExtName(
+    psFits* fits                       ///< the psFits object
+);
+
+/** Get the total number of HDUs in the FITS file.
+ *
+ *  @return int        The total number of HDUs in the FITS file or < 0 if an 
+ *                     error occurred.
+ */
+int psFitsGetSize(
+    psFits* fits                       ///< the psFits object
+);
+
+/** Create a new extension on the end of the file of the given type.  The
+ *  FITS file is 
+ *
+ *  @return bool       true if successful, otherwise false
+ */
+bool psFitsCreateExt(
+    psFits* fits                       ///< the psFits object
+);
+
+/** Get the extension type of the current HDU.
+ *
+ *  @return psFitsType The type of the current HDU.  If PS_FITS_TYPE_UNKNOWN, 
+ *                     the type could not be determined.
+ */
+psFitsType psFitsGetExtType(
+    psFits* fits                       ///< the psFits object
+);
+
+/** Reads the header of the current HDU.
+ *
+ *  @return psMetadata*   the header data
+ */
+psMetadata* psFitsReadHeader(
+    psMetadata* out,
+    ///< The psMetadata to add the header data.  If null, a new psMetadata is created.
+
+    const psFits* fits                 ///< the psFits object
+);
+
+/** Reads the header of all HDUs.  The current HDU is not changed.
+ *
+ *  @return psHash*      the header data
+ */
+psHash* psFitsReadHeaderSet(
+    psHash* out,
+    ///< The psHash to add the header data via psMetadata items.  If null, a
+    ///< new psHash is created.  The keys of the psHash are the extension names
+    ///< of the cooresponding HDUs.
+
+    psFits* fits                       ///< the psFits object
+);
+
+/** Writes the values of the metadata to the current HDU header.
+ *
+ *  @return bool        if TRUE, the write was successful, otherwise FALSE.
+ */
+bool psFitsWriteHeader(
+    psFits* fits,                      ///< the psFits object
+    psMetadata* out                    ///< the psMetadata data in which to write
+);
+
+/** Reads an image, given the desired region and z-plane.
+ *
+ *  @return psImage*     the read image or NULL if there was an error.
+ */
+psImage* psfitsReadImageSection(
+    psImage* out,                      ///< a psImage to recycle.
+    psFits* fits,                      ///< the psFits object
+    psRegion region,                   ///< the region in the FITS image to read
+    int z                              ///< the z-plane in the FITS image cube to read
+);
+
+/** Writes an image, given the desired region and z-plane.
+ *
+ *  @return bool        TRUE is the write was successful, otherwise FALSE.
+ */
+bool psFitsWriteImageSection(
+    psFits* fits,                      ///< the psFits object
+    const psImage* input,              ///< the image to output
+    psRegion region,                   ///< the part of the FITS image to overwrite
+    int z                              ///< the z-plane of the FITS image data cube to use
+);
+
+/** Reads a table row.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psMetadata*    The table row's data.  The keys are the column names.
+ */
+psMetadata* psFitsReadTableRow(
+    psFits* fits,                      ///< the psFits object
+    int row                            ///< row number to read
+);
+
+/** Reads a table column.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psArray*    Array of data items for the specified column or NULL 
+ *                      if an error occurred.
+ */
+psArray* psFitsReadTableColumn(
+    psFits* fits,
+    const char* colname
+);
+
+/** Reads a table column of numbers.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psVector*    Vector of data for the specified column or NULL
+ *                       if an error occurred.
+ */
+psVector* psFitsReadTableColumnNum(
+    psFits* fits,                      ///< the psFits object
+    const char* colname                ///< the column name
+);
+
+
+/** Reads a whole FITS table.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psArray*     Array of psMetadata items, which contains the output
+ *                       data items of each row.
+ *  
+ *  @see psFitsReadTableRow
+ */
+psArray* psFitsReadTable(
+    psFits* fits                       ///< the psFits object
+);
+
+/** Writes a whole FITS table.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return bool        TRUE if the write was successful, otherwise FALSE
+ *  
+ *  @see psFitsReadTableRow
+ */
+bool psFitsWriteTable(
+    psFits* fits,                      ///< the psFits object
+    psArray* table
+    ///< Array of psMetadata items, which contains the output data items of each row.
+);
+
+/// @}
+
+#endif
Index: /trunk/psLib/src/fits/psFits.c
===================================================================
--- /trunk/psLib/src/fits/psFits.c	(revision 2375)
+++ /trunk/psLib/src/fits/psFits.c	(revision 2375)
@@ -0,0 +1,614 @@
+/** @file  psFits.c
+ *
+ *  @brief Contains Fits I/O routines
+ *
+ *  @ingroup FileIO
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-11-16 20:00:21 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#include <unistd.h>
+
+#include "psFits.h"
+#include "string.h"
+#include "psError.h"
+#include "psFileUtilsErrors.h"
+#include "psMemory.h"
+#include "psString.h"
+#include "psLogMsg.h"
+
+#define MAX_STRING_LENGTH 256  // maximum length string for FITS routines
+
+static bool convertPsTypeToFits(psElemType type, int* bitPix, double* bZero, int* dataType)
+{
+
+    int bitpix;
+    int datatype;
+    double bzero = 0.0;
+
+    switch (type) {
+
+    case PS_TYPE_U8:
+        bitpix = BYTE_IMG;
+        datatype = TBYTE;
+        break;
+
+    case PS_TYPE_S8:
+        bitpix = BYTE_IMG;
+        bzero = INT8_MIN;
+        datatype = TSBYTE;
+        break;
+
+    case PS_TYPE_U16:
+        bitpix = SHORT_IMG;
+        bzero = -1.0 * INT16_MIN;
+        datatype = TUSHORT;
+        break;
+
+    case PS_TYPE_S16:
+        bitpix = SHORT_IMG;
+        datatype = TSHORT;
+        break;
+
+    case PS_TYPE_U32:
+        bitpix = LONG_IMG;
+        bzero = -1.0 * INT32_MIN;
+        datatype = TUINT;
+        break;
+
+    case PS_TYPE_S32:
+        bitpix = LONG_IMG;
+        datatype = TINT;
+        break;
+
+    case PS_TYPE_F32:
+        bitpix = FLOAT_IMG;
+        datatype = TFLOAT;
+        break;
+
+    case PS_TYPE_F64:
+        bitpix = DOUBLE_IMG;
+        datatype = TDOUBLE;
+        break;
+
+    default: {
+            char* typeStr;
+            PS_TYPE_NAME(typeStr,type);
+            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                    PS_ERRORTEXT_psFits_TYPE_UNSUPPORTED,
+                    typeStr);
+            return false;
+        }
+    }
+
+    // pass the requested parameters  (NULL parameters are not set, of course).
+    if (bitPix != NULL) {
+        *bitPix = bitpix;
+    }
+
+    if (dataType != NULL) {
+        *dataType = datatype;
+    }
+
+    if (bZero != NULL) {
+        *bZero = bzero;
+    }
+
+    return true;
+}
+
+static bool convertPsMetadataTypeToBinaryTForm(psMetadataType type, char** fitsType)
+{
+    switch (type) {
+    case PS_META_BOOL:
+        *fitsType = "1L";
+        break;
+    case PS_META_S32:
+        *fitsType = "1J";
+        break;
+    case PS_META_F32:
+        *fitsType = "1E";
+        break;
+    case PS_META_F64:
+        *fitsType = "1D";
+        break;
+        // XXX: Handle other types, e.g., Vectors, etc.
+    default:
+        return false;
+    }
+
+    return true;
+}
+
+static bool isHDUEmpty(psFits* fits)
+{
+    /* check for keys - no keys means this is really an empty HDU */
+    int keysexist = -1;
+    int morekeys;
+    int status = 0;
+
+    fits_get_hdrspace(fits->p_fd, &keysexist, &morekeys, &status);
+
+    // if no keys exist and not primary HDU, this really is an empty HDU
+    if (keysexist == 0) {
+        return true;
+    }
+
+    return false;
+
+}
+
+static void fitsFree(psFits* fits)
+{
+    int status = 0;
+
+    if (fits != NULL) {
+        (void)fits_close_file(fits->p_fd, &status);
+        psFree((void*)fits->filename);
+    }
+}
+
+psFits* psFitsAlloc(const char* name,                  ///< the FITS file name
+                    bool readwrite)                    ///< if TRUE, the file is open for read/write.
+{
+    int status = 0;
+    fitsfile *fptr = NULL;      /* Pointer to the FITS file */
+
+    if (name == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_FILENAME_NULL);
+        return NULL;
+    }
+
+    int iomode = (readwrite) ? READWRITE : READONLY;
+
+    /* Open/Create the FITS file */
+    if (access(name, F_OK) == 0) {     // file exists
+        (void)fits_open_file(&fptr, name, iomode, &status);
+        if (fptr == NULL || status != 0) {
+            char fitsErr[MAX_STRING_LENGTH];
+            fits_get_errstatus(status, fitsErr);
+            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                    PS_ERRORTEXT_psFits_FILENAME_INVALID,
+                    name, fitsErr);
+            return NULL;
+        }
+    } else {  // file does not exist
+        (void)fits_create_file(&fptr, name, &status);
+        if (fptr == NULL || status != 0) {
+            char fitsErr[MAX_STRING_LENGTH];
+            fits_get_errstatus(status, fitsErr);
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psFits_FILENAME_CREATE_FAILED,
+                    name, fitsErr);
+            return NULL;
+        }
+    }
+
+    psFits* fits = psAlloc(sizeof(psFits));
+    fits->filename = psAlloc(strlen(name)+1);
+    fits->p_fd = fptr;
+    strcpy((char*)fits->filename,name);
+    p_psMemSetDeallocator(fits,(psFreeFcn)fitsFree);
+
+    return fits;
+}
+
+psFitsType psFitsMoveExtName(psFits* fits,
+                             const char* extname)
+{
+    int status = 0;
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return PS_FITS_TYPE_NONE;
+    }
+
+
+    if (fits_movnam_hdu(fits->p_fd, ANY_HDU, (char*)extname, 0, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psFits_EXTNAME_INVALID,
+                extname, fits->filename, fitsErr);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    return psFitsGetExtType(fits);
+}
+
+psFitsType psFitsMoveExtNum(psFits* fits,
+                            int extnum,
+                            bool relative)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    int status = 0;
+    int hdutype = 0;
+
+    if (relative) {
+        fits_movrel_hdu(fits->p_fd, extnum+1, &hdutype, &status);
+    } else {
+        fits_movabs_hdu(fits->p_fd, extnum+1, &hdutype, &status);
+    }
+
+    if (status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psFits_EXTNUM_INVALID,
+                extnum, fits->filename, fitsErr);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    return hdutype;
+}
+
+int psFitsGetExtNum(psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    int hdutype = ANY_HDU;
+    int status = 0;
+
+    if (fits_get_hdu_type(fits->p_fd, &hdutype, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psFits_GET_EXTNUM_FAILED,
+                fits->filename, fitsErr);
+        return -1;
+    }
+
+    return hdutype-1;
+}
+
+char* psFitsGetExtName(psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return NULL;
+    }
+
+    int status = 0;
+    char name[MAX_STRING_LENGTH];
+
+    if (fits_read_key_str(fits->p_fd, "EXTNAME", name, NULL, &status) != 0) {
+        status = 0;
+        if (fits_read_key_str(fits->p_fd, "HDUNAME", name, NULL, &status) != 0) {
+            int num = psFitsGetExtNum(fits);
+            snprintf(name, MAX_STRING_LENGTH, "EXT-%3d",num);
+        }
+    }
+    return psStringCopy(name);
+}
+
+int psFitsGetSize(psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    int num = 0;
+    int status = 0;
+
+    if (fits_get_num_hdus(fits->p_fd, &num, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psFits_GETNUMHDUS_FAILED,
+                fits->filename, fitsErr);
+        return -1;
+    }
+
+    return num;
+}
+
+bool psFitsCreateExt(psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return false;
+    }
+
+    int status = 0;
+
+    fits_create_hdu(fits->p_fd,&status);
+
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_CREATE_HDU_FAILED,
+                fits->filename, fitsErr);
+        return false;
+    }
+
+    return true;
+}
+
+psFitsType psFitsGetExtType(psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    int status = 0;
+    int hdutype = PS_FITS_TYPE_NONE;
+
+    if (fits_get_hdu_type(fits->p_fd, &hdutype, &status) != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_LOCATION_INVALID, true,
+                PS_ERRORTEXT_psFits_GETHDUTYPE_FAILED,
+                fits->filename, fitsErr);
+        return PS_FITS_TYPE_NONE;
+    }
+
+    if (hdutype == PS_FITS_TYPE_IMAGE &&
+            psFitsGetExtNum(fits) > 0 &&
+            isHDUEmpty(fits)) {
+        return PS_FITS_TYPE_ANY;
+    }
+
+    return hdutype;
+}
+
+psMetadata* psFitsReadHeader(psMetadata* out,
+                             const psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return NULL;
+    }
+
+    if (out == NULL) {
+        out = psMetadataAlloc();
+        if (out == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to allocate a new psMetadata container.");
+            return NULL;
+        }
+    }
+
+    // Get number of key names
+    int numKeys = 0;
+    int keyNum = 0;
+    int status = 0;
+    fits_get_hdrpos(fits->p_fd, &numKeys, &keyNum, &status);
+
+    // Get each key name. Keywords start at one.
+    char keyType;
+    char keyName[MAX_STRING_LENGTH];
+    char keyValue[MAX_STRING_LENGTH];
+    char keyComment[MAX_STRING_LENGTH];
+    psMetadataType metadataItemType;
+    psBool tempBool;
+    psBool success;
+    for (int i = 1; i <= numKeys; i++) {
+
+        fits_read_keyn(fits->p_fd, i, keyName, keyValue, keyComment, &status);
+        fits_get_keytype(keyValue, &keyType, &status);
+        if (status != 0) {
+            break;
+        }
+
+        switch (keyType) {
+        case 'I':
+            metadataItemType = PS_META_S32;
+            success = psMetadataAdd(out, PS_LIST_TAIL, keyName, metadataItemType, keyComment, atoi(keyValue));
+            break;
+        case 'F':
+            metadataItemType = PS_META_F64;
+            success = psMetadataAdd(out, PS_LIST_TAIL, keyName, metadataItemType, keyComment, atof(keyValue));
+            break;
+        case 'C':
+            metadataItemType = PS_META_STR;
+            success = psMetadataAdd(out, PS_LIST_TAIL, keyName, metadataItemType, keyComment, keyValue);
+            break;
+        case 'L':
+            metadataItemType = PS_META_BOOL;
+            tempBool = (keyValue[0] == 'T') ? 1 : 0;
+            success = psMetadataAdd(out, PS_LIST_TAIL, keyName, metadataItemType, keyComment, tempBool);
+            break;
+        case 'U':
+        case 'X':
+        default:
+            psError(PS_ERR_IO, true,
+                    PS_ERRORTEXT_psFits_METATYPE_INVALID,
+                    keyType);
+            return out;
+        }
+
+        if (!success) {
+            psError(PS_ERR_UNKNOWN, false,
+                    PS_ERRORTEXT_psFits_METADATA_ADD_FAILED,
+                    keyName);
+            return out;
+        }
+    }
+
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_METADATA_ADD_FAILED,
+                fitsErr);
+        return false;
+    }
+
+    return out;
+}
+
+psHash* psFitsReadHeaderSet(psHash* out,
+                            psFits* fits)
+{
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return out;
+    }
+
+    if (out == NULL) {
+        out = psHashAlloc(10);
+        // XXX: what is the appropriate number of buckets? Got 10 from psMetadataAlloc.
+        if (out == NULL) {
+            psError(PS_ERR_UNKNOWN, false,
+                    "Failed to allocate a new psHash container.");
+            return NULL;
+        }
+    }
+
+    int status = 0;
+
+    int size = psFitsGetSize(fits);
+    int position = psFitsGetExtNum(fits);
+
+    for (int lcv=0; lcv < size; lcv++) {
+        if (psFitsMoveExtNum(fits, lcv, false)) {
+            char* name = psFitsGetExtName(fits);
+            psMetadata* header = psFitsReadHeader(NULL, fits);
+            if (name != NULL || header != NULL) {
+                psHashAdd(out, name, header);
+            } else { // XXX: is this a warning or error?
+                psLogMsg(__func__, PS_LOG_WARN,
+                         "Failed to read HDU#%d header data.",
+                         lcv);
+            }
+
+            psFree(name);
+            psFree(header);
+        }
+    }
+}
+
+bool psFitsWriteImage(psFits* fits,
+                      const psImage* input,
+                      int x,
+                      int y,
+                      int z)
+{
+
+    if (fits == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_NULL);
+        return false;
+    }
+
+    if (input == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psFits_IMAGE_NULL);
+        return false;
+    }
+    int numCols = input->numCols;
+    int numRows = input->numRows;
+
+    int status = 0;
+
+    psFitsType fitsType = psFitsGetExtType(fits);
+
+    if (fitsType != PS_FITS_TYPE_IMAGE && fitsType != PS_FITS_TYPE_ANY) {
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_NOT_IMAGE_TYPE);
+        return false;
+    }
+
+    // determine the FITS-equivalent parameters
+    int bitPix;
+    double bZero;
+    int dataType;
+    if (! convertPsTypeToFits(input->type.type, &bitPix, &bZero, &dataType) ) {
+        return false;
+    }
+
+    if (fitsType == PS_FITS_TYPE_ANY) {
+        int naxis = 3;
+        long naxes[3];
+
+        naxes[0] = numCols;
+        naxes[1] = numRows;
+        naxes[2] = z;
+
+        if (z < 2) {
+            naxis = 2;
+        }
+
+        /* write the required keys to make an empty HDU be an IMAGE HDU */
+        fits_write_imghdr(fits->p_fd, bitPix, naxis, naxes, &status);
+        if (bZero != 0) {        // set the bscale/bzero
+            fits_write_key_dbl(fits->p_fd, "BZERO", bZero, 12, "Pixel Value Offset", &status);
+            fits_write_key_dbl(fits->p_fd, "BSCALE", 1.0, 12, "Pixel Value Scale", &status);
+            // don't think this is needed: fits_set_bscale(fits->p_fd, bScale, bZero, &status);
+        }
+    }
+
+    long firstPixel[3];         /* First Pixel to read */
+    long lastPixel[3];          /* Last Pixel to read */
+    firstPixel[0] = x + 1;
+    firstPixel[1] = y + 1;
+    firstPixel[2] = z + 1;
+
+    lastPixel[0] = firstPixel[0] + numCols - 1;
+    lastPixel[1] = firstPixel[1] + numRows - 1;
+    lastPixel[2] = z + 1;
+
+    fits_write_subset(fits->p_fd, dataType, firstPixel, lastPixel, input->data.V[0], &status);
+
+    if ( status != 0) {
+        char fitsErr[MAX_STRING_LENGTH];
+        (void)fits_get_errstatus(status, fitsErr);
+        psError(PS_ERR_IO, true,
+                PS_ERRORTEXT_psFits_WRITE_FAILED,
+                fits->filename, fitsErr);
+        return false;
+    }
+
+    return true;
+
+}
+
+psMetadata* psFitsReadTableRow(psFits* fits,
+                               int row)
+{
+}
+
+psArray* psFitsReadTableColumn(psFits* fits,
+                               const char* colname)
+{
+}
+
+psVector* psFitsReadTableColumnNum(psFits* fits,
+                                   const char* colname)
+{
+}
+
+
+psArray* psFitsReadTable(psFits* fits)
+{
+}
+
+bool psFitsWriteTable(psFits* fits,
+                      psArray* table)
+{
+}
Index: /trunk/psLib/src/fits/psFits.h
===================================================================
--- /trunk/psLib/src/fits/psFits.h	(revision 2375)
+++ /trunk/psLib/src/fits/psFits.h	(revision 2375)
@@ -0,0 +1,240 @@
+/** @file  psFits.h
+ *
+ *  @brief Contains Fits I/O routines
+ *
+ *  @ingroup FileIO
+ *
+ *  @author Robert DeSonia, MHPCC
+ *
+ *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-11-16 20:00:21 $
+ *
+ *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_FITS_H
+#define PS_FITS_H
+
+#include<fitsio.h>
+
+#include "psType.h"
+#include "psArray.h"
+#include "psVector.h"
+#include "psMetadata.h"
+#include "psHash.h"
+#include "psImage.h"
+
+/// @addtogroup FileIO
+/// @{
+
+typedef enum {
+    PS_FITS_TYPE_NONE = -1,
+    PS_FITS_TYPE_IMAGE = IMAGE_HDU,
+    PS_FITS_TYPE_BINARY_TABLE = BINARY_TBL,
+    PS_FITS_TYPE_ASCII_TABLE = ASCII_TBL,
+    PS_FITS_TYPE_ANY = ANY_HDU
+} psFitsType;
+
+/** FITS file object.
+ * 
+ *  This object should be considered opaque to the user; no item in this
+ *  struct should be accessed directly.
+ *
+ */
+typedef struct
+{
+    fitsfile* p_fd;                    ///< the CFITSIO fits files handle.
+    const char* filename;              ///< the filename of the fits file
+}
+psFits;
+
+/** Opens a FITS file and allocates the associated psFits object.
+ *
+ *  @return psFits*    new psFits object for the FITS files specified or 
+ *                     NULL if the open of the FITS file failed
+ */
+psFits* psFitsAlloc(
+    const char* name,                  ///< the FITS file name
+    bool readwrite                     ///< if TRUE, the file is open for read/write.
+);
+
+/** Moves the FITS HDU to the specified extension name.
+ *
+ *  @return psFitsType    The HDU type, or PS_FITS_TYPE_NONE if move failed.
+ */
+psFitsType psFitsMoveExtName(
+    psFits* fits,                      ///< the psFits object to move
+    const char* extname                ///< the extension name
+);
+
+/** Moves the FITS HDU to the specified extension number
+ *
+ *  @return psFitsType    The HDU type, or PS_FITS_TYPE_NONE if move failed.
+ */
+psFitsType psFitsMoveExtNum(
+    psFits* fits,                      ///< the psFits object to move
+    int extnum,                        ///< the extension number to move to (zero is primary HDU)
+    bool relative                      ///< if true, extnum is a relative number to the current position
+);
+
+/** Get the current extension number, where 0 is the primary HDU.
+ *
+ *  @return int        Current HDU number of the psFits file or < 0 if an error
+ *                     occurred.
+ */
+int psFitsGetExtNum(
+    psFits* fits                       ///< the psFits object
+);
+
+/** Get the current extension name.
+ *
+ *  @return int        Current HDU name of the psFits file or NULL if an 
+ *                     error occurred.
+ */
+char* psFitsGetExtName(
+    psFits* fits                       ///< the psFits object
+);
+
+/** Get the total number of HDUs in the FITS file.
+ *
+ *  @return int        The total number of HDUs in the FITS file or < 0 if an 
+ *                     error occurred.
+ */
+int psFitsGetSize(
+    psFits* fits                       ///< the psFits object
+);
+
+/** Create a new extension on the end of the file of the given type.  The
+ *  FITS file is 
+ *
+ *  @return bool       true if successful, otherwise false
+ */
+bool psFitsCreateExt(
+    psFits* fits                       ///< the psFits object
+);
+
+/** Get the extension type of the current HDU.
+ *
+ *  @return psFitsType The type of the current HDU.  If PS_FITS_TYPE_UNKNOWN, 
+ *                     the type could not be determined.
+ */
+psFitsType psFitsGetExtType(
+    psFits* fits                       ///< the psFits object
+);
+
+/** Reads the header of the current HDU.
+ *
+ *  @return psMetadata*   the header data
+ */
+psMetadata* psFitsReadHeader(
+    psMetadata* out,
+    ///< The psMetadata to add the header data.  If null, a new psMetadata is created.
+
+    const psFits* fits                 ///< the psFits object
+);
+
+/** Reads the header of all HDUs.  The current HDU is not changed.
+ *
+ *  @return psHash*      the header data
+ */
+psHash* psFitsReadHeaderSet(
+    psHash* out,
+    ///< The psHash to add the header data via psMetadata items.  If null, a
+    ///< new psHash is created.  The keys of the psHash are the extension names
+    ///< of the cooresponding HDUs.
+
+    psFits* fits                       ///< the psFits object
+);
+
+/** Writes the values of the metadata to the current HDU header.
+ *
+ *  @return bool        if TRUE, the write was successful, otherwise FALSE.
+ */
+bool psFitsWriteHeader(
+    psFits* fits,                      ///< the psFits object
+    psMetadata* out                    ///< the psMetadata data in which to write
+);
+
+/** Reads an image, given the desired region and z-plane.
+ *
+ *  @return psImage*     the read image or NULL if there was an error.
+ */
+psImage* psfitsReadImageSection(
+    psImage* out,                      ///< a psImage to recycle.
+    psFits* fits,                      ///< the psFits object
+    psRegion region,                   ///< the region in the FITS image to read
+    int z                              ///< the z-plane in the FITS image cube to read
+);
+
+/** Writes an image, given the desired region and z-plane.
+ *
+ *  @return bool        TRUE is the write was successful, otherwise FALSE.
+ */
+bool psFitsWriteImageSection(
+    psFits* fits,                      ///< the psFits object
+    const psImage* input,              ///< the image to output
+    psRegion region,                   ///< the part of the FITS image to overwrite
+    int z                              ///< the z-plane of the FITS image data cube to use
+);
+
+/** Reads a table row.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psMetadata*    The table row's data.  The keys are the column names.
+ */
+psMetadata* psFitsReadTableRow(
+    psFits* fits,                      ///< the psFits object
+    int row                            ///< row number to read
+);
+
+/** Reads a table column.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psArray*    Array of data items for the specified column or NULL 
+ *                      if an error occurred.
+ */
+psArray* psFitsReadTableColumn(
+    psFits* fits,
+    const char* colname
+);
+
+/** Reads a table column of numbers.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psVector*    Vector of data for the specified column or NULL
+ *                       if an error occurred.
+ */
+psVector* psFitsReadTableColumnNum(
+    psFits* fits,                      ///< the psFits object
+    const char* colname                ///< the column name
+);
+
+
+/** Reads a whole FITS table.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return psArray*     Array of psMetadata items, which contains the output
+ *                       data items of each row.
+ *  
+ *  @see psFitsReadTableRow
+ */
+psArray* psFitsReadTable(
+    psFits* fits                       ///< the psFits object
+);
+
+/** Writes a whole FITS table.  The current HDU type must be either
+ *  PS_FITS_TYPE_BINARY_TABLE or PS_FITS_TYPE_ASCII_TABLE.
+ *
+ *  @return bool        TRUE if the write was successful, otherwise FALSE
+ *  
+ *  @see psFitsReadTableRow
+ */
+bool psFitsWriteTable(
+    psFits* fits,                      ///< the psFits object
+    psArray* table
+    ///< Array of psMetadata items, which contains the output data items of each row.
+);
+
+/// @}
+
+#endif
Index: /trunk/psLib/src/image/psImage.c
===================================================================
--- /trunk/psLib/src/image/psImage.c	(revision 2374)
+++ /trunk/psLib/src/image/psImage.c	(revision 2375)
@@ -9,6 +9,6 @@
  *  @author Ross Harman, MHPCC
  *
- *  @version $Revision: 1.53 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2004-11-06 00:44:56 $
+ *  @version $Revision: 1.54 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-11-16 20:00:21 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -26,5 +26,34 @@
 #include "psImageErrors.h"
 
-static void imageFree(psImage* image);
+static void imageFree(psImage* image)
+{
+    if (image == NULL) {
+        return;
+    }
+
+    if (image->type.type == PS_TYPE_PTR) {
+        // 2-D array of pointers -- must dereference elements
+        psU32 oldNumRows = image->numRows;
+        psU32 oldNumCols = image->numCols;
+        psPtr* rowPtr;
+
+        for (psU32 row = 0; row < oldNumRows; row++) {
+            rowPtr = image->data.PTR[row];
+            for (psU32 col = 0; col < oldNumCols; col++) {
+                psMemDecrRefCounter(rowPtr[col]);
+            }
+        }
+    }
+
+    if (image->parent != NULL) {
+        psArrayRemove(image->parent->children,image);
+        image->parent = NULL;
+    }
+
+    psImageFreeChildren(image);
+
+    psFree(image->rawDataBuffer);
+    psFree(image->data.V);
+}
 
 psImage* psImageAlloc(psU32 numCols,
@@ -71,34 +100,52 @@
 }
 
-static void imageFree(psImage* image)
+psRegion* psRegionAlloc(double x0,
+                        double x1,
+                        double y0,
+                        double y1)
 {
-    if (image == NULL) {
-        return;
-    }
-
-    if (image->type.type == PS_TYPE_PTR) {
-        // 2-D array of pointers -- must dereference elements
-        psU32 oldNumRows = image->numRows;
-        psU32 oldNumCols = image->numCols;
-        psPtr* rowPtr;
-
-        for (psU32 row = 0; row < oldNumRows; row++) {
-            rowPtr = image->data.PTR[row];
-            for (psU32 col = 0; col < oldNumCols; col++) {
-                psMemDecrRefCounter(rowPtr[col]);
-            }
-        }
-    }
-
-    if (image->parent != NULL) {
-        psArrayRemove(image->parent->children,image);
-        image->parent = NULL;
-    }
-
-    psImageFreeChildren(image);
-
-    psFree(image->rawDataBuffer);
-    psFree(image->data.V);
-}
+    psRegion* out = psAlloc(sizeof(psRegion));
+
+    out->x0 = x0;
+    out->y0 = y0;
+    out->x1 = x1;
+    out->y1 = y1;
+
+    return out;
+}
+
+
+
+psRegion* psRegionFromString(char* region)
+{
+    psS32 col0;
+    psS32 col1;
+    psS32 row0;
+    psS32 row1;
+
+    // section should be of the form '[col0:col1,row0:row1]'
+    if (region == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_SUBSECTION_NULL);
+        return NULL;
+    }
+
+    if (sscanf(region,"[%d:%d,%d:%d]",&col0,&col1,&row0,&row1) < 4) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_SUBSECTION_INVALID,
+                region);
+        return NULL;
+    }
+
+    if (col0 > col1 || row0 > row1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_SUBSET_RANGE_MALFORMED,
+                col0,col1,row0,row1);
+        return NULL;
+    }
+
+    return psRegionAlloc(col0,col1,row0,row1);
+}
+
 
 psImage* psImageRecycle(psImage* old,
Index: /trunk/psLib/src/image/psImage.h
===================================================================
--- /trunk/psLib/src/image/psImage.h	(revision 2374)
+++ /trunk/psLib/src/image/psImage.h	(revision 2375)
@@ -11,6 +11,6 @@
  *  @author Ross Harman, MHPCC
  *
- *  @version $Revision: 1.42 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2004-10-27 00:57:31 $
+ *  @version $Revision: 1.43 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-11-16 20:00:21 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -75,4 +75,18 @@
 psImage;
 
+/** Basic image region structure.
+ *
+ * Struct for specifying a rectangular area in an image.
+ *
+ */
+typedef struct
+{
+    double x0;                         ///< the first column of the region.
+    double x1;                         ///< the last column of the region.
+    double y0;                         ///< the first row of the region.
+    double y1;                         ///< the last row of the region.
+}
+psRegion;
+
 /** Create an image of the specified size and type.
  *
@@ -84,7 +98,26 @@
  */
 psImage* psImageAlloc(
-    psU32 numCols,              ///< Number of rows in image.
-    psU32 numRows,              ///< Number of columns in image.
+    psU32 numCols,                     ///< Number of rows in image.
+    psU32 numRows,                     ///< Number of columns in image.
     const psElemType type              ///< Type of data for image.
+);
+
+/** Create an image of the specified size and type.
+ *
+ * Uses psLib memory allocation functions to create an image struct of the
+ * specified size and type.
+ *
+ * @return psImage* : Pointer to psImage.
+ *
+ */
+psRegion* psRegionAlloc(
+    double x0,                         ///< the first column of the region.
+    double x1,                         ///< the last column of the region.
+    double y0,                         ///< the first row of the region.
+    double y1                          ///< the last row of the region.
+);
+
+psRegion* psRegionFromString(
+    char* region                       ///< image rectangular region in the form '[x0:x1,y0:y1]'
 );
 
@@ -96,6 +129,6 @@
 psImage* psImageRecycle(
     psImage* old,                      ///< the psImage to recycle by resizing image buffer
-    psU32 numCols,              ///< the desired number of columns in image
-    psU32 numRows,              ///< the desired number of rows in image
+    psU32 numCols,                     ///< the desired number of columns in image
+    psU32 numRows,                     ///< the desired number of rows in image
     const psElemType type              ///< the desired datatype of the image
 );
Index: /trunk/psLib/src/image/psImageConvolve.c
===================================================================
--- /trunk/psLib/src/image/psImageConvolve.c	(revision 2374)
+++ /trunk/psLib/src/image/psImageConvolve.c	(revision 2375)
@@ -5,6 +5,6 @@
  *  @author Robert DeSonia, MHPCC
  *
- *  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2004-11-04 01:05:00 $
+ *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-11-16 20:00:21 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -93,8 +93,10 @@
                            psBool relative)
 {
-    psS32 x = 0;
-    psS32 y = 0;
-    psS32 t = 0;
-    psS32 deltaT;
+    psS32 lastX;
+    psS32 lastY;
+    psS32 lastT;
+    psS32 x;
+    psS32 y;
+    psS32 t;
     psS32 oldX;
     psS32 oldY;
@@ -148,17 +150,11 @@
         ps##TYPE *xShiftData = xShifts->data.TYPE; \
         ps##TYPE *yShiftData = yShifts->data.TYPE; \
-        x = 0; \
-        y = 0; \
-        t = 0; \
-        for (psS32 lcv = 0; lcv < length; lcv++) { \
-            if (relative) { \
-                x += xShiftData[lcv]; \
-                y += yShiftData[lcv]; \
-                t += tShiftData[lcv]; \
-            } else { \
-                x = xShiftData[lcv]; \
-                y = yShiftData[lcv]; \
-                t = tShiftData[lcv]; \
-            } \
+        lastX =  xShiftData[length-1]; \
+        lastY =  yShiftData[length-1]; \
+        \
+        for (int lcv = 0; lcv < length; lcv++) { \
+            x = lastX - xShiftData[lcv]; \
+            y = lastY - yShiftData[lcv]; \
+            \
             if (x < xMin) { \
                 xMin = x; \
@@ -172,51 +168,108 @@
             } \
         } \
+        \
+        normalizeTime = 1.0 / (psKernelType)(lastT - tShiftData[0]); \
         result = psKernelAlloc(xMin,xMax,yMin,yMax); \
         kernel = result->kernel; \
         \
-        normalizeTime = 1.0 / (psKernelType)(t); \
+        lastT = 0; \
+        for (int i = 0; i < length; i++) { \
+            t = tShiftData[i] - lastT; \
+            x = lastX - xShiftData[i]; \
+            y = lastY - yShiftData[i]; \
+            \
+            kernel[y][x] += (psKernelType)t * normalizeTime; \
+            lastT = t; \
+        } \
+        break; \
+    }
+
+    #define RELATIVE_KERNEL_GENERATE_CASE(TYPE) \
+case PS_TYPE_##TYPE: { \
+        ps##TYPE *tShiftData = tShifts->data.TYPE; \
+        ps##TYPE *xShiftData = xShifts->data.TYPE; \
+        ps##TYPE *yShiftData = yShifts->data.TYPE; \
+        \
         x = 0; \
         y = 0; \
         t = 0; \
-        for (psS32 i = 0; i < length; i++) { \
-            deltaT = t; \
-            oldX = x; \
-            oldY = y; \
-            if (relative) { \
-                t += tShiftData[i]; \
-                x += xShiftData[i]; \
-                y += yShiftData[i]; \
-            } else { \
-                t = tShiftData[i]; \
-                x = xShiftData[i]; \
-                y = yShiftData[i]; \
+        \
+        for (int lcv = length-1; lcv >= 0; lcv--) { \
+            t += tShiftData[lcv]; \
+            \
+            if (x < xMin) { \
+                xMin = x; \
+            } else if (x > xMax) { \
+                xMax = x; \
             } \
-            deltaT = t - deltaT; \
+            if (y < yMin) { \
+                yMin = y; \
+            } else if (y > yMax) { \
+                yMax = y; \
+            } \
+            x += xShiftData[lcv]; \
+            y += yShiftData[lcv]; \
             \
-            kernel[oldY][oldX] += deltaT * normalizeTime; \
+        } \
+        result = psKernelAlloc(xMin,xMax,yMin,yMax); \
+        kernel = result->kernel; \
+        \
+        normalizeTime = 1.0 / (psKernelType)t; \
+        x = 0; \
+        y = 0; \
+        for (psS32 i = length-1; i >= 0; i--) { \
+            kernel[y][x] += (psKernelType)(tShiftData[i]) * normalizeTime; \
+            x += xShiftData[i]; \
+            y += yShiftData[i]; \
+            \
         } \
         break; \
     }
 
-    switch (xShifts->type.type) {
-        KERNEL_GENERATE_CASE(U8);
-        KERNEL_GENERATE_CASE(U16);
-        KERNEL_GENERATE_CASE(U32);
-        KERNEL_GENERATE_CASE(U64);
-        KERNEL_GENERATE_CASE(S8);
-        KERNEL_GENERATE_CASE(S16);
-        KERNEL_GENERATE_CASE(S32);
-        KERNEL_GENERATE_CASE(S64);
-        KERNEL_GENERATE_CASE(F32);
-        KERNEL_GENERATE_CASE(F64);
-        KERNEL_GENERATE_CASE(C32);
-        KERNEL_GENERATE_CASE(C64);
-
-    default: {
-            char* typeStr;
-            PS_TYPE_NAME(typeStr,xShifts->type.type);
-            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
-                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
-                    typeStr);
+    if (relative) {
+        switch (xShifts->type.type) {
+            RELATIVE_KERNEL_GENERATE_CASE(U8);
+            RELATIVE_KERNEL_GENERATE_CASE(U16);
+            RELATIVE_KERNEL_GENERATE_CASE(U32);
+            RELATIVE_KERNEL_GENERATE_CASE(U64);
+            RELATIVE_KERNEL_GENERATE_CASE(S8);
+            RELATIVE_KERNEL_GENERATE_CASE(S16);
+            RELATIVE_KERNEL_GENERATE_CASE(S32);
+            RELATIVE_KERNEL_GENERATE_CASE(S64);
+            RELATIVE_KERNEL_GENERATE_CASE(F32);
+            RELATIVE_KERNEL_GENERATE_CASE(F64);
+            RELATIVE_KERNEL_GENERATE_CASE(C32);
+            RELATIVE_KERNEL_GENERATE_CASE(C64);
+
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,xShifts->type.type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                        typeStr);
+            }
+        }
+    } else {
+        switch (xShifts->type.type) {
+            KERNEL_GENERATE_CASE(U8);
+            KERNEL_GENERATE_CASE(U16);
+            KERNEL_GENERATE_CASE(U32);
+            KERNEL_GENERATE_CASE(U64);
+            KERNEL_GENERATE_CASE(S8);
+            KERNEL_GENERATE_CASE(S16);
+            KERNEL_GENERATE_CASE(S32);
+            KERNEL_GENERATE_CASE(S64);
+            KERNEL_GENERATE_CASE(F32);
+            KERNEL_GENERATE_CASE(F64);
+            KERNEL_GENERATE_CASE(C32);
+            KERNEL_GENERATE_CASE(C64);
+
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,xShifts->type.type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                        typeStr);
+            }
         }
     }
Index: /trunk/psLib/src/image/psImageErrors.h
===================================================================
--- /trunk/psLib/src/image/psImageErrors.h	(revision 2374)
+++ /trunk/psLib/src/image/psImageErrors.h	(revision 2375)
@@ -7,6 +7,6 @@
  *  @author Robert DeSonia, MHPCC
  *
- *  @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2004-10-13 23:34:57 $
+ *  @version $Revision: 1.11 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-11-16 20:00:21 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
Index: /trunk/psLib/src/image/psImageManip.h
===================================================================
--- /trunk/psLib/src/image/psImageManip.h	(revision 2374)
+++ /trunk/psLib/src/image/psImageManip.h	(revision 2375)
@@ -11,6 +11,6 @@
  *  @author Ross Harman, MHPCC
  *
- *  @version $Revision: 1.14 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2004-10-27 00:57:31 $
+ *  @version $Revision: 1.15 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-11-16 20:00:21 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -83,6 +83,6 @@
     psImage* image,                    ///< target image
     const psImage* overlay,            ///< the overlay image
-    psS32 col0,                          ///< the column to start overlay
-    psS32 row0,                          ///< the row to start overlay
+    psS32 col0,                        ///< the column to start overlay
+    psS32 row0,                        ///< the row to start overlay
     const char *op                     ///< the operation to perform for overlay
 );
@@ -103,5 +103,5 @@
     const psImage* restrict mask,      ///< mask for input image.  If NULL, no masking is done.
     psMaskType maskVal,                ///< the bits to check in mask.
-    psU32 scale,                ///< the scale to rebin for each dimension
+    psU32 scale,                       ///< the scale to rebin for each dimension
     const psStats* stats
     ///< the statistic to perform when rebinning.  Only one method should be set.
@@ -121,5 +121,5 @@
     psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
     const psImage* in,                 ///< input image
-    psS32 scale,                         ///< resample scaling factor
+    psS32 scale,                       ///< resample scaling factor
     psImageInterpolateMode mode        ///< the interpolation mode used in resampling
 );
@@ -176,6 +176,6 @@
     psImage* out,                      ///< an psImage to recycle.  If NULL, a new image is created
     const psImage* in,                 ///< input image
-    psS32 dx,                            ///< number of pixels to roll in the x-dimension
-    psS32 dy                             ///< number of pixels to roll in the y-dimension
+    psS32 dx,                          ///< number of pixels to roll in the x-dimension
+    psS32 dy                           ///< number of pixels to roll in the y-dimension
 );
 
Index: /trunk/psLib/src/imageops/psImageConvolve.c
===================================================================
--- /trunk/psLib/src/imageops/psImageConvolve.c	(revision 2374)
+++ /trunk/psLib/src/imageops/psImageConvolve.c	(revision 2375)
@@ -5,6 +5,6 @@
  *  @author Robert DeSonia, MHPCC
  *
- *  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2004-11-04 01:05:00 $
+ *  @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-11-16 20:00:21 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -93,8 +93,10 @@
                            psBool relative)
 {
-    psS32 x = 0;
-    psS32 y = 0;
-    psS32 t = 0;
-    psS32 deltaT;
+    psS32 lastX;
+    psS32 lastY;
+    psS32 lastT;
+    psS32 x;
+    psS32 y;
+    psS32 t;
     psS32 oldX;
     psS32 oldY;
@@ -148,17 +150,11 @@
         ps##TYPE *xShiftData = xShifts->data.TYPE; \
         ps##TYPE *yShiftData = yShifts->data.TYPE; \
-        x = 0; \
-        y = 0; \
-        t = 0; \
-        for (psS32 lcv = 0; lcv < length; lcv++) { \
-            if (relative) { \
-                x += xShiftData[lcv]; \
-                y += yShiftData[lcv]; \
-                t += tShiftData[lcv]; \
-            } else { \
-                x = xShiftData[lcv]; \
-                y = yShiftData[lcv]; \
-                t = tShiftData[lcv]; \
-            } \
+        lastX =  xShiftData[length-1]; \
+        lastY =  yShiftData[length-1]; \
+        \
+        for (int lcv = 0; lcv < length; lcv++) { \
+            x = lastX - xShiftData[lcv]; \
+            y = lastY - yShiftData[lcv]; \
+            \
             if (x < xMin) { \
                 xMin = x; \
@@ -172,51 +168,108 @@
             } \
         } \
+        \
+        normalizeTime = 1.0 / (psKernelType)(lastT - tShiftData[0]); \
         result = psKernelAlloc(xMin,xMax,yMin,yMax); \
         kernel = result->kernel; \
         \
-        normalizeTime = 1.0 / (psKernelType)(t); \
+        lastT = 0; \
+        for (int i = 0; i < length; i++) { \
+            t = tShiftData[i] - lastT; \
+            x = lastX - xShiftData[i]; \
+            y = lastY - yShiftData[i]; \
+            \
+            kernel[y][x] += (psKernelType)t * normalizeTime; \
+            lastT = t; \
+        } \
+        break; \
+    }
+
+    #define RELATIVE_KERNEL_GENERATE_CASE(TYPE) \
+case PS_TYPE_##TYPE: { \
+        ps##TYPE *tShiftData = tShifts->data.TYPE; \
+        ps##TYPE *xShiftData = xShifts->data.TYPE; \
+        ps##TYPE *yShiftData = yShifts->data.TYPE; \
+        \
         x = 0; \
         y = 0; \
         t = 0; \
-        for (psS32 i = 0; i < length; i++) { \
-            deltaT = t; \
-            oldX = x; \
-            oldY = y; \
-            if (relative) { \
-                t += tShiftData[i]; \
-                x += xShiftData[i]; \
-                y += yShiftData[i]; \
-            } else { \
-                t = tShiftData[i]; \
-                x = xShiftData[i]; \
-                y = yShiftData[i]; \
+        \
+        for (int lcv = length-1; lcv >= 0; lcv--) { \
+            t += tShiftData[lcv]; \
+            \
+            if (x < xMin) { \
+                xMin = x; \
+            } else if (x > xMax) { \
+                xMax = x; \
             } \
-            deltaT = t - deltaT; \
+            if (y < yMin) { \
+                yMin = y; \
+            } else if (y > yMax) { \
+                yMax = y; \
+            } \
+            x += xShiftData[lcv]; \
+            y += yShiftData[lcv]; \
             \
-            kernel[oldY][oldX] += deltaT * normalizeTime; \
+        } \
+        result = psKernelAlloc(xMin,xMax,yMin,yMax); \
+        kernel = result->kernel; \
+        \
+        normalizeTime = 1.0 / (psKernelType)t; \
+        x = 0; \
+        y = 0; \
+        for (psS32 i = length-1; i >= 0; i--) { \
+            kernel[y][x] += (psKernelType)(tShiftData[i]) * normalizeTime; \
+            x += xShiftData[i]; \
+            y += yShiftData[i]; \
+            \
         } \
         break; \
     }
 
-    switch (xShifts->type.type) {
-        KERNEL_GENERATE_CASE(U8);
-        KERNEL_GENERATE_CASE(U16);
-        KERNEL_GENERATE_CASE(U32);
-        KERNEL_GENERATE_CASE(U64);
-        KERNEL_GENERATE_CASE(S8);
-        KERNEL_GENERATE_CASE(S16);
-        KERNEL_GENERATE_CASE(S32);
-        KERNEL_GENERATE_CASE(S64);
-        KERNEL_GENERATE_CASE(F32);
-        KERNEL_GENERATE_CASE(F64);
-        KERNEL_GENERATE_CASE(C32);
-        KERNEL_GENERATE_CASE(C64);
-
-    default: {
-            char* typeStr;
-            PS_TYPE_NAME(typeStr,xShifts->type.type);
-            psError(PS_ERR_BAD_PARAMETER_TYPE, true,
-                    PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
-                    typeStr);
+    if (relative) {
+        switch (xShifts->type.type) {
+            RELATIVE_KERNEL_GENERATE_CASE(U8);
+            RELATIVE_KERNEL_GENERATE_CASE(U16);
+            RELATIVE_KERNEL_GENERATE_CASE(U32);
+            RELATIVE_KERNEL_GENERATE_CASE(U64);
+            RELATIVE_KERNEL_GENERATE_CASE(S8);
+            RELATIVE_KERNEL_GENERATE_CASE(S16);
+            RELATIVE_KERNEL_GENERATE_CASE(S32);
+            RELATIVE_KERNEL_GENERATE_CASE(S64);
+            RELATIVE_KERNEL_GENERATE_CASE(F32);
+            RELATIVE_KERNEL_GENERATE_CASE(F64);
+            RELATIVE_KERNEL_GENERATE_CASE(C32);
+            RELATIVE_KERNEL_GENERATE_CASE(C64);
+
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,xShifts->type.type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                        typeStr);
+            }
+        }
+    } else {
+        switch (xShifts->type.type) {
+            KERNEL_GENERATE_CASE(U8);
+            KERNEL_GENERATE_CASE(U16);
+            KERNEL_GENERATE_CASE(U32);
+            KERNEL_GENERATE_CASE(U64);
+            KERNEL_GENERATE_CASE(S8);
+            KERNEL_GENERATE_CASE(S16);
+            KERNEL_GENERATE_CASE(S32);
+            KERNEL_GENERATE_CASE(S64);
+            KERNEL_GENERATE_CASE(F32);
+            KERNEL_GENERATE_CASE(F64);
+            KERNEL_GENERATE_CASE(C32);
+            KERNEL_GENERATE_CASE(C64);
+
+        default: {
+                char* typeStr;
+                PS_TYPE_NAME(typeStr,xShifts->type.type);
+                psError(PS_ERR_BAD_PARAMETER_TYPE, true,
+                        PS_ERRORTEXT_psImage_IMAGE_TYPE_UNSUPPORTED,
+                        typeStr);
+            }
         }
     }
Index: /trunk/psLib/src/mathtypes/psImage.c
===================================================================
--- /trunk/psLib/src/mathtypes/psImage.c	(revision 2374)
+++ /trunk/psLib/src/mathtypes/psImage.c	(revision 2375)
@@ -9,6 +9,6 @@
  *  @author Ross Harman, MHPCC
  *
- *  @version $Revision: 1.53 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2004-11-06 00:44:56 $
+ *  @version $Revision: 1.54 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-11-16 20:00:21 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -26,5 +26,34 @@
 #include "psImageErrors.h"
 
-static void imageFree(psImage* image);
+static void imageFree(psImage* image)
+{
+    if (image == NULL) {
+        return;
+    }
+
+    if (image->type.type == PS_TYPE_PTR) {
+        // 2-D array of pointers -- must dereference elements
+        psU32 oldNumRows = image->numRows;
+        psU32 oldNumCols = image->numCols;
+        psPtr* rowPtr;
+
+        for (psU32 row = 0; row < oldNumRows; row++) {
+            rowPtr = image->data.PTR[row];
+            for (psU32 col = 0; col < oldNumCols; col++) {
+                psMemDecrRefCounter(rowPtr[col]);
+            }
+        }
+    }
+
+    if (image->parent != NULL) {
+        psArrayRemove(image->parent->children,image);
+        image->parent = NULL;
+    }
+
+    psImageFreeChildren(image);
+
+    psFree(image->rawDataBuffer);
+    psFree(image->data.V);
+}
 
 psImage* psImageAlloc(psU32 numCols,
@@ -71,34 +100,52 @@
 }
 
-static void imageFree(psImage* image)
+psRegion* psRegionAlloc(double x0,
+                        double x1,
+                        double y0,
+                        double y1)
 {
-    if (image == NULL) {
-        return;
-    }
-
-    if (image->type.type == PS_TYPE_PTR) {
-        // 2-D array of pointers -- must dereference elements
-        psU32 oldNumRows = image->numRows;
-        psU32 oldNumCols = image->numCols;
-        psPtr* rowPtr;
-
-        for (psU32 row = 0; row < oldNumRows; row++) {
-            rowPtr = image->data.PTR[row];
-            for (psU32 col = 0; col < oldNumCols; col++) {
-                psMemDecrRefCounter(rowPtr[col]);
-            }
-        }
-    }
-
-    if (image->parent != NULL) {
-        psArrayRemove(image->parent->children,image);
-        image->parent = NULL;
-    }
-
-    psImageFreeChildren(image);
-
-    psFree(image->rawDataBuffer);
-    psFree(image->data.V);
-}
+    psRegion* out = psAlloc(sizeof(psRegion));
+
+    out->x0 = x0;
+    out->y0 = y0;
+    out->x1 = x1;
+    out->y1 = y1;
+
+    return out;
+}
+
+
+
+psRegion* psRegionFromString(char* region)
+{
+    psS32 col0;
+    psS32 col1;
+    psS32 row0;
+    psS32 row1;
+
+    // section should be of the form '[col0:col1,row0:row1]'
+    if (region == NULL) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_SUBSECTION_NULL);
+        return NULL;
+    }
+
+    if (sscanf(region,"[%d:%d,%d:%d]",&col0,&col1,&row0,&row1) < 4) {
+        psError(PS_ERR_BAD_PARAMETER_NULL, true,
+                PS_ERRORTEXT_psImage_SUBSECTION_INVALID,
+                region);
+        return NULL;
+    }
+
+    if (col0 > col1 || row0 > row1) {
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                PS_ERRORTEXT_psImage_SUBSET_RANGE_MALFORMED,
+                col0,col1,row0,row1);
+        return NULL;
+    }
+
+    return psRegionAlloc(col0,col1,row0,row1);
+}
+
 
 psImage* psImageRecycle(psImage* old,
Index: /trunk/psLib/src/mathtypes/psImage.h
===================================================================
--- /trunk/psLib/src/mathtypes/psImage.h	(revision 2374)
+++ /trunk/psLib/src/mathtypes/psImage.h	(revision 2375)
@@ -11,6 +11,6 @@
  *  @author Ross Harman, MHPCC
  *
- *  @version $Revision: 1.42 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2004-10-27 00:57:31 $
+ *  @version $Revision: 1.43 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-11-16 20:00:21 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -75,4 +75,18 @@
 psImage;
 
+/** Basic image region structure.
+ *
+ * Struct for specifying a rectangular area in an image.
+ *
+ */
+typedef struct
+{
+    double x0;                         ///< the first column of the region.
+    double x1;                         ///< the last column of the region.
+    double y0;                         ///< the first row of the region.
+    double y1;                         ///< the last row of the region.
+}
+psRegion;
+
 /** Create an image of the specified size and type.
  *
@@ -84,7 +98,26 @@
  */
 psImage* psImageAlloc(
-    psU32 numCols,              ///< Number of rows in image.
-    psU32 numRows,              ///< Number of columns in image.
+    psU32 numCols,                     ///< Number of rows in image.
+    psU32 numRows,                     ///< Number of columns in image.
     const psElemType type              ///< Type of data for image.
+);
+
+/** Create an image of the specified size and type.
+ *
+ * Uses psLib memory allocation functions to create an image struct of the
+ * specified size and type.
+ *
+ * @return psImage* : Pointer to psImage.
+ *
+ */
+psRegion* psRegionAlloc(
+    double x0,                         ///< the first column of the region.
+    double x1,                         ///< the last column of the region.
+    double y0,                         ///< the first row of the region.
+    double y1                          ///< the last row of the region.
+);
+
+psRegion* psRegionFromString(
+    char* region                       ///< image rectangular region in the form '[x0:x1,y0:y1]'
 );
 
@@ -96,6 +129,6 @@
 psImage* psImageRecycle(
     psImage* old,                      ///< the psImage to recycle by resizing image buffer
-    psU32 numCols,              ///< the desired number of columns in image
-    psU32 numRows,              ///< the desired number of rows in image
+    psU32 numCols,                     ///< the desired number of columns in image
+    psU32 numRows,                     ///< the desired number of rows in image
     const psElemType type              ///< the desired datatype of the image
 );
Index: /trunk/psLib/src/types/psList.c
===================================================================
--- /trunk/psLib/src/types/psList.c	(revision 2374)
+++ /trunk/psLib/src/types/psList.c	(revision 2375)
@@ -6,6 +6,6 @@
  *  @author Robert Daniel DeSonia, MHPCC
  *
- *  @version $Revision: 1.21 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2004-11-04 01:04:57 $
+ *  @version $Revision: 1.22 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-11-16 20:00:20 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -42,6 +42,6 @@
     list->size = 0;
     list->head = list->tail = NULL;
-    list->iter = ITER_INIT_HEAD;
-    list->iterIndex = PS_LIST_HEAD;
+    list->p_iter = ITER_INIT_HEAD;
+    list->p_iterIndex = PS_LIST_HEAD;
 
     pthread_mutex_init(&(list->lock), NULL)
@@ -127,6 +127,6 @@
 
         list->size++;
-        list->iter = elem;
-        list->iterIndex = list->size - 1;
+        list->p_iter = elem;
+        list->p_iterIndex = list->size - 1;
     } else {
         // move ourselves to the given position
@@ -152,6 +152,6 @@
 
         list->size++;
-        list->iter = elem;
-        list->iterIndex = cursorIndex;
+        list->p_iter = elem;
+        list->p_iterIndex = cursorIndex;
     }
 
@@ -227,13 +227,13 @@
         // removed tail, so iter should be the last element of list to keep it valid
         if (list->size > 0) {
-            list->iter = list->tail;
-            list->iterIndex = list->size - 1;
+            list->p_iter = list->tail;
+            list->p_iterIndex = list->size - 1;
         } else {
-            list->iter = ITER_INIT_TAIL;
+            list->p_iter = ITER_INIT_TAIL;
         }
     } else {
         elem->next->prev = elem->prev;
-        list->iter = elem->next;
-        list->iterIndex = cursorIndex;
+        list->p_iter = elem->next;
+        list->p_iterIndex = cursorIndex;
     }
 
@@ -275,5 +275,5 @@
 
     if (where >= (psS32)list->size) {
-        list->iter = NULL;
+        list->p_iter = NULL;
         if (lockList) {
             pthread_mutex_unlock(&list->lock)
@@ -285,9 +285,9 @@
     switch (where) {
     case PS_LIST_HEAD:
-        list->iter = ITER_INIT_HEAD;
+        list->p_iter = ITER_INIT_HEAD;
         break;
 
     case PS_LIST_TAIL:
-        list->iter = ITER_INIT_TAIL;
+        list->p_iter = ITER_INIT_TAIL;
         break;
 
@@ -297,6 +297,6 @@
 
         if (cursor != NULL) {
-            list->iter = cursor->prev;
-            list->iterIndex = position - 1;
+            list->p_iter = cursor->prev;
+            list->p_iterIndex = position - 1;
         }
         break;
@@ -307,6 +307,6 @@
 
         if (cursor != NULL) {
-            list->iter = cursor->next;
-            list->iterIndex = position + 1;
+            list->p_iter = cursor->next;
+            list->p_iterIndex = position + 1;
         }
         break;
@@ -322,6 +322,6 @@
             cursor = listGetIterator(list);
             if (cursor == NULL) {      // reset the iterator if it is invalid
-                list->iter = ITER_INIT_HEAD;
-                list->iterIndex = 0;
+                list->p_iter = ITER_INIT_HEAD;
+                list->p_iterIndex = 0;
             }
 
@@ -357,10 +357,10 @@
     }
 
-    if (list->iter == ITER_INIT_HEAD) {
+    if (list->p_iter == ITER_INIT_HEAD) {
         return list->head;
-    } else if (list->iter == ITER_INIT_TAIL) {
+    } else if (list->p_iter == ITER_INIT_TAIL) {
         return list->tail;
     } else {
-        return list->iter;
+        return list->p_iter;
     }
 }
@@ -368,10 +368,10 @@
 psS32 listGetIteratorIndex(psList* list)
 {
-    if (list->iter == ITER_INIT_HEAD) {
+    if (list->p_iter == ITER_INIT_HEAD) {
         return 0;
-    } else if (list->iter == ITER_INIT_TAIL) {
+    } else if (list->p_iter == ITER_INIT_TAIL) {
         return list->size - 1;
     } else {
-        return list->iterIndex;
+        return list->p_iterIndex;
     }
 }
Index: /trunk/psLib/src/types/psList.h
===================================================================
--- /trunk/psLib/src/types/psList.h	(revision 2374)
+++ /trunk/psLib/src/types/psList.h	(revision 2375)
@@ -10,6 +10,6 @@
  *  @ingroup LinkedList
  *
- *  @version $Revision: 1.16 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2004-10-27 00:57:31 $
+ *  @version $Revision: 1.17 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2004-11-16 20:00:20 $
  *
  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
@@ -52,16 +52,35 @@
  *  directly; rather the psListAlloc should be used.
  *
- *  @see psListAlloc, psListFree
+ *  @see psListAlloc
  */
 typedef struct
 {
-    psU32 size;                 ///< number of elements on list
+    psU32 size;                        ///< number of elements on list
     psListElem* head;                  ///< first element on list (may be NULL)
     psListElem* tail;                  ///< last element on list (may be NULL)
-    psListElem* iter;                  ///< iteration cursor
-    psU32 iterIndex;            ///< the numeric position of the iteration cursor in the list
+    psArray* iterators;                ///< iterators
     pthread_mutex_t lock;              ///< mutex to lock a node during changes
+psListElem* p_iter;                ///< internal cursor for increased performance index accessing
+int p_iterIndex;                   ///< index position of the iter.
 }
 psList;
+
+/** The psList iterator structure.  This should be allocated via
+ *  psListIteratorAlloc and not directly.
+ *
+ *  The life span of a psListIterator object is ended by either a psFree
+ *  of this structure OR psFree of the psList in which it operates on.
+ *
+ *  @see psListIteratorAlloc, psListIteratorSet, psListGetNext, psListGetPrevious
+ */
+typedef struct
+{
+psList* list;                      ///< List iterator to works on
+psU32 number;                      ///< List iterator number
+psListElem* cursor;                ///< current cursor position
+bool offEnd;                       ///< Iterator off the end?
+}
+psListIterator;
+
 
 /** Creates a psList linked list object.
