Index: /trunk/psModules/src/camera/Makefile.am
===================================================================
--- /trunk/psModules/src/camera/Makefile.am	(revision 24835)
+++ /trunk/psModules/src/camera/Makefile.am	(revision 24836)
@@ -23,4 +23,5 @@
 	pmFPAfileIO.c \
 	pmFPAfileFitsIO.c \
+	pmFPAfileFringeIO.c \
 	pmFPAFlags.c \
 	pmFPALevel.c \
@@ -51,4 +52,5 @@
 	pmFPAfileIO.h \
 	pmFPAfileFitsIO.h \
+	pmFPAfileFringeIO.h \
 	pmFPAFlags.h \
 	pmFPALevel.h \
Index: /trunk/psModules/src/camera/pmFPAfileFringeIO.c
===================================================================
--- /trunk/psModules/src/camera/pmFPAfileFringeIO.c	(revision 24836)
+++ /trunk/psModules/src/camera/pmFPAfileFringeIO.c	(revision 24836)
@@ -0,0 +1,238 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <pslib.h>
+
+#include "pmConfig.h"
+//#include "pmConfigMask.h"
+//#include "pmDetrendDB.h"
+
+#include "pmHDU.h"
+#include "pmFPA.h"
+#include "pmFPALevel.h"
+//#include "pmFPARead.h"
+//#include "pmFPAWrite.h"
+//#include "pmFPAMaskWeight.h"
+#include "pmFPAview.h"
+#include "pmFPAfile.h"
+#include "pmFPAfileFringeIO.h"
+#include "pmFPAfileFitsIO.h"
+//#include "pmFPACopy.h"
+//#include "pmFPAConstruct.h"
+//#include "pmConceptsWrite.h"
+#include "pmFringeStats.h"
+
+# define FRINGE_TABLE "FRINGE.MEASUREMENTS"
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Reading Fringe data
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// given an already-opened fits file, read the table corresponding to the specified view
+bool pmFPAviewReadFringes(const pmFPAview *view, pmFPAfile *file)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+
+    pmFPA *fpa = file->fpa;             // FPA of interest
+    psFits *fits = file->fits;          // FITS file
+
+    if (view->chip == -1) {
+        return pmFPAReadFringes(fpa, fits) > 0;
+    }
+
+    if (view->cell == -1) {
+        pmChip *chip = pmFPAviewThisChip(view, fpa); // Chip of interest
+        return pmChipReadFringes(chip, fits) > 0;
+    }
+
+    pmCell *cell = pmFPAviewThisCell(view, fpa); // Cell of interest
+    return pmCellReadFringes(cell, fits) > 0;
+}
+
+int pmFPAReadFringes(pmFPA *fpa, psFits *fits)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, 0);
+    PS_ASSERT_FITS_NON_NULL(fits, 0);
+
+    int numRead = 0;                    // Number of reads
+    psArray *chips = fpa->chips;        // Array of chips
+    for (int i = 0; i < chips->n; i++) {
+        pmChip *chip = chips->data[i];  // Chip of interest
+        numRead += pmChipReadFringes(chip, fits);
+    }
+
+    return numRead;
+}
+
+int pmChipReadFringes(pmChip *chip, psFits *fits)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, 0);
+    PS_ASSERT_FITS_NON_NULL(fits, 0);
+
+    int numRead = 0;                    // Number of reads
+    psArray *cells = chip->cells;       // Array of cells
+    for (int i = 0; i < cells->n; i++) {
+        pmCell *cell = cells->data[i];  // Cell of interest
+        numRead += pmCellReadFringes(cell, fits);
+    }
+
+    return numRead;
+}
+
+int pmCellReadFringes(pmCell *cell, psFits *fits)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, 0);
+    PS_ASSERT_FITS_NON_NULL(fits, 0);
+
+    const char *chipName = psMetadataLookupStr(NULL, cell->parent->concepts, "CHIP.NAME"); // Name of chip
+    const char *cellName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME"); // Name of cell
+
+    // Fringe Table Extension name
+    psString extname = NULL;
+    psStringAppend(&extname, "FRINGE_%s_%s", chipName, cellName);
+
+    if (!psFitsMoveExtName(fits, extname)) {
+        psError(PS_ERR_IO, false, "Unable to move to extension %s\n", extname);
+        psFree(extname);
+        return 0;
+    }
+
+    psMetadata *header = psFitsReadHeader(NULL, fits); // The FITS header
+    if (!header) {
+        psError(PS_ERR_IO, false, "Unable to read header for extension %s\n", extname);
+        psFree(extname);
+        psFree(header);
+        return 0;
+    }
+
+    psArray *table = psFitsReadTable(fits); // The table
+    if (!table) {
+	psError(PS_ERR_UNKNOWN, false, "Unable to add read the fringe table data from the file");
+	psFree (header);
+	psFree (extname);
+	return 0;
+    }
+
+    psArray *fringes = pmFringesParseTable(table, header);
+    psFree(table);
+    psFree(header);
+
+    if (!fringes) {
+	psError(PS_ERR_UNKNOWN, false, "Unable to add parse the fringe table data");
+        psFree(extname);
+	return 0;
+    }
+
+    if (!psMetadataAdd(cell->analysis, PS_LIST_TAIL, FRINGE_TABLE, PS_DATA_ARRAY, "Fringes", fringes)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to add fringe from extension %s to analysis metadata "
+                "for chip %s, cell %s\n", extname, chipName, cellName);
+        psFree(fringes);
+        psFree(extname);
+        return 0;
+    }
+
+    psFree(fringes); // drop local reference
+    psFree(extname);
+
+    return 1;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Writing fringe data
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// given an already-opened fits file, write the table corresponding to the specified view
+bool pmFPAviewWriteFringes(const pmFPAview *view, pmFPAfile *file, pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    pmFPA *fpa = pmFPAfileSuitableFPA(file, view, config, false); // FPA of interest
+    psFits *fits = file->fits;          // FITS file
+
+    if (view->chip == -1) {
+        return pmFPAWriteFringes(fits, fpa) > 0;
+    }
+
+    if (view->cell == -1) {
+        pmChip *chip = pmFPAviewThisChip(view, fpa); // Chip of interest
+        return pmChipWriteFringes(fits, chip) > 0;
+    }
+
+    pmCell *cell = pmFPAviewThisCell(view, fpa); // Cell of interest
+    return pmCellWriteFringes(fits, cell) > 0;
+}
+
+int pmFPAWriteFringes(psFits *fits, const pmFPA *fpa)
+{
+    PS_ASSERT_PTR_NON_NULL(fpa, 0);
+    PS_ASSERT_PTR_NON_NULL(fits, 0);
+
+    int numWrite = 0;                    // Number of reads
+    psArray *chips = fpa->chips;        // Array of chips
+    for (int i = 0; i < chips->n; i++) {
+        pmChip *chip = chips->data[i];  // Chip of interest
+        numWrite += pmChipWriteFringes(fits, chip);
+    }
+
+    return numWrite;
+}
+
+int pmChipWriteFringes(psFits *fits, const pmChip *chip)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, 0);
+    PS_ASSERT_PTR_NON_NULL(fits, 0);
+
+    int numWrite = 0;                    // Number of reads
+    psArray *cells = chip->cells;       // Array of cells
+    for (int i = 0; i < cells->n; i++) {
+        pmCell *cell = cells->data[i];  // Cell of interest
+        numWrite += pmCellWriteFringes(fits, cell);
+    }
+
+    return numWrite;
+}
+
+int pmCellWriteFringes(psFits *fits, const pmCell *cell)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, 0);
+    PS_ASSERT_PTR_NON_NULL(fits, 0);
+
+    const char *chipName = psMetadataLookupStr(NULL, cell->parent->concepts, "CHIP.NAME"); // Name of chip
+    const char *cellName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME"); // Name of cell
+
+    psArray *fringes = psMetadataLookupPtr(NULL, cell->analysis, FRINGE_TABLE); // The FITS table
+    if (!fringes) {
+        // We wrote everything we could find
+        return 0;
+    }
+
+    psMetadata *header = psMetadataAlloc();
+    psArray *table = pmFringesFormatTable(header, fringes);
+    if (!table) {
+        // We wrote everything we could find
+	psFree(header);
+        return 0;
+    }
+
+    psString extname = NULL;            // Extension name
+    psStringAppend(&extname, "FRINGE_%s_%s", chipName, cellName);
+
+    if (!psFitsWriteTable(fits, header, table, extname)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to write table from chip %s, cell %s to extension %s\n",
+                chipName, cellName, extname);
+        psFree(extname);
+	psFree(header);
+        return 0;
+    }
+
+    psFree(extname);
+    psFree(header);
+    return 1;
+}
+
+
Index: /trunk/psModules/src/camera/pmFPAfileFringeIO.h
===================================================================
--- /trunk/psModules/src/camera/pmFPAfileFringeIO.h	(revision 24836)
+++ /trunk/psModules/src/camera/pmFPAfileFringeIO.h	(revision 24836)
@@ -0,0 +1,30 @@
+/* @file  pmFPAfileFringeIO.h
+ * @brief Read & Write Fringe tables
+ *
+ * @author EAM, IfA
+ * @author PAP, IfA
+ *
+ * @version $Revision: 1.16 $
+ * @date $Date: 2009-02-06 02:31:24 $
+ * Copyright 2004-2005 Institute for Astronomy, University of Hawaii
+ */
+
+#ifndef PM_FPA_FILE_FRINGE_IO_H
+#define PM_FPA_FILE_FRINGE_IO_H
+
+/// @addtogroup Camera Camera Layout
+/// @{
+
+/// Read an fringes into the current view
+bool pmFPAviewReadFringes(const pmFPAview *view, pmFPAfile *file);
+int pmFPAReadFringes(pmFPA *fpa, psFits *fits);
+int pmChipReadFringes(pmChip *chip, psFits *fits);
+int pmCellReadFringes(pmCell *cell, psFits *fits);
+bool pmFPAviewWriteFringes(const pmFPAview *view, pmFPAfile *file, pmConfig *config);
+int pmFPAWriteFringes(psFits *fits, const pmFPA *fpa);
+int pmChipWriteFringes(psFits *fits, const pmChip *chip);
+int pmCellWriteFringes(psFits *fits, const pmCell *cell);
+
+/// @}
+
+# endif
Index: /trunk/psModules/src/camera/pmFPAfileIO.c
===================================================================
--- /trunk/psModules/src/camera/pmFPAfileIO.c	(revision 24835)
+++ /trunk/psModules/src/camera/pmFPAfileIO.c	(revision 24836)
@@ -23,4 +23,5 @@
 #include "pmFPAWrite.h"
 #include "pmFPAfileFitsIO.h"
+#include "pmFPAfileFringeIO.h"
 #include "pmSpan.h"
 #include "pmFootprint.h"
@@ -192,5 +193,5 @@
         status = pmFPAviewReadFitsImage(view, file, config);
         if (status) {
-            if (!pmFPAviewReadFitsTable(view, file, "FRINGE")) {
+            if (!pmFPAviewReadFringes(view, file)) {
                 psError(PS_ERR_UNKNOWN, false, "Unable to read fringe data from %s.\n", file->filename);
                 return false;
@@ -451,5 +452,5 @@
         status = pmFPAviewWriteFitsImage (view, file, config);
         if (status) {
-            if (!pmFPAviewWriteFitsTable(view, file, "FRINGE", config)) {
+            if (!pmFPAviewWriteFringes(view, file, config)) {
                 psError(PS_ERR_UNKNOWN, false, "Unable to write fringe data from %s.\n", file->filename);
                 return false;
Index: /trunk/psModules/src/detrend/pmFringeStats.c
===================================================================
--- /trunk/psModules/src/detrend/pmFringeStats.c	(revision 24835)
+++ /trunk/psModules/src/detrend/pmFringeStats.c	(revision 24836)
@@ -464,4 +464,8 @@
     for (long i = 0; i < fringes->n; i++) {
         pmFringeStats *fringe = fringes->data[i]; // The fringe of interest
+	if (!fringe) {
+	    psWarning ("skipping empty fringe stats for entry %ld -- video cell?\n", i);
+	    continue;
+	}
         pmFringeRegions *regions = fringe->regions; // The fringe regions
         if (numPoints == 0) {
@@ -496,4 +500,8 @@
     for (long i = 0; i < fringes->n; i++) {
         pmFringeStats *fringe = fringes->data[i]; // The fringe of interest
+	if (!fringe) {
+	    psWarning ("skipping empty fringe stats for entry %ld -- video cell?\n", i);
+	    continue;
+	}
         pmFringeRegions *regions = fringe->regions; // The fringe regions
         // Copy the data over
@@ -520,7 +528,7 @@
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
-bool pmFringesFormat(pmCell *cell, psMetadata *header, const psArray *fringes)
-{
-    PS_ASSERT_PTR_NON_NULL(cell, false);
+psArray *pmFringesFormatTable(psMetadata *header, const psArray *fringes)
+{
+    PS_ASSERT_PTR_NON_NULL(header, false);
     PS_ASSERT_ARRAY_NON_NULL(fringes, false);
 
@@ -531,5 +539,5 @@
         if (stats->regions != regions) {
             psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Regions for fringe statistics are not identical.\n");
-            return false;
+            return NULL;
         }
     }
@@ -557,20 +565,8 @@
     // Vectors: x, y, mask, f, df
 
-    psMetadata *scalars = psMemIncrRefCounter(header); // Metadata to hold the scalars; will be the header
-    if (!scalars) {
-        scalars = psMetadataAlloc();
-    }
-    psMetadataAddS32(scalars, PS_LIST_TAIL, "PSFRNGDX", PS_META_REPLACE, "Median box half-width",
-                     regions->dX);
-    psMetadataAddS32(scalars, PS_LIST_TAIL, "PSFRNGDY", PS_META_REPLACE, "Median box half-height",
-                     regions->dY);
-    psMetadataAddS32(scalars, PS_LIST_TAIL, "PSFRNGNX", PS_META_REPLACE, "Large-scale smoothing in x",
-                     regions->nX);
-    psMetadataAddS32(scalars, PS_LIST_TAIL, "PSFRNGNY", PS_META_REPLACE, "Large-scale smoothing in y",
-                     regions->nY);
-    psMetadataAdd(cell->analysis, PS_LIST_TAIL, "FRINGE.HEADER", PS_DATA_METADATA,
-                  "Header for fringe data", scalars);
-    psFree(scalars);
-
+    psMetadataAddS32(header, PS_LIST_TAIL, "PSFRNGDX", PS_META_REPLACE, "Median box half-width", regions->dX);
+    psMetadataAddS32(header, PS_LIST_TAIL, "PSFRNGDY", PS_META_REPLACE, "Median box half-height", regions->dY);
+    psMetadataAddS32(header, PS_LIST_TAIL, "PSFRNGNX", PS_META_REPLACE, "Large-scale smoothing in x", regions->nX);
+    psMetadataAddS32(header, PS_LIST_TAIL, "PSFRNGNY", PS_META_REPLACE, "Large-scale smoothing in y", regions->nY);
 
     psArray *table = psArrayAlloc(numRows); // The table
@@ -605,27 +601,13 @@
     }
 
-    psMetadataAdd(cell->analysis, PS_LIST_TAIL, "FRINGE", PS_DATA_ARRAY, "Fringe data", table);
-
-    return true;
-}
-
-
-psArray *pmFringesParse(pmCell *cell)
-{
-    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+    return table;
+}
+
+psArray *pmFringesParseTable(psArray *table, psMetadata *header)
+{
+    PS_ASSERT_PTR_NON_NULL(table, NULL);
+    PS_ASSERT_PTR_NON_NULL(header, NULL);
 
     bool mdok;                          // Status of MD lookup
-    psMetadata *header = psMetadataLookupMetadata(&mdok, cell->analysis, "FRINGE.HEADER"); // Header
-    if (!mdok || !header) {
-        psError(PS_ERR_UNKNOWN, false, "Unable to find header for fringe data.\n");
-        return NULL;
-    }
-
-    psArray *table = psMetadataLookupPtr(NULL, cell->analysis, "FRINGE"); // FITS table
-    if (!table) {
-        psError(PS_ERR_UNKNOWN, false, "Unable to find table for fringe data.\n");
-        return NULL;
-    }
-
 
     // Read the scalars from the header
@@ -722,4 +704,55 @@
 }
 
+bool pmFringesFormat(pmCell *cell, psMetadata *inHeader, const psArray *fringes)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, false);
+    PS_ASSERT_ARRAY_NON_NULL(fringes, false);
+
+    psMetadata *header = psMemIncrRefCounter(inHeader); // Metadata to hold the scalars; will be the header
+    if (!inHeader) {
+	inHeader = psMetadataAlloc();
+    }
+
+    psArray *table = pmFringesFormatTable(header, fringes);
+    if (!table) {
+	psError (PS_ERR_UNKNOWN, false, "unable to generate table from fringes");
+	psFree(header);
+	return NULL;
+    }
+
+    psMetadataAdd(cell->analysis, PS_LIST_TAIL, "FRINGE.HEADER", PS_DATA_METADATA, "Header for fringe data", header);
+    psFree(header);
+
+    psMetadataAdd(cell->analysis, PS_LIST_TAIL, "FRINGE.TABLE", PS_DATA_ARRAY, "Fringe data", table);
+    psFree(table);
+
+    return true;
+}
+
+psArray *pmFringesParse(pmCell *cell)
+{
+    PS_ASSERT_PTR_NON_NULL(cell, NULL);
+
+    bool mdok;                          // Status of MD lookup
+    psMetadata *header = psMetadataLookupMetadata(&mdok, cell->analysis, "FRINGE.HEADER"); // Header
+    if (!mdok || !header) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to find header for fringe data.\n");
+        return NULL;
+    }
+
+    psArray *table = psMetadataLookupPtr(NULL, cell->analysis, "FRINGE.TABLE"); // FITS table
+    if (!table) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to find table for fringe data.\n");
+        return NULL;
+    }
+
+    psArray *fringes = pmFringesParseTable(table, header);
+    if (!fringes) {
+	psError(PS_ERR_UNKNOWN, false, "Unable to add parse the fringe table data");
+	return NULL;
+    }
+
+    return fringes;
+}
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
Index: /trunk/psModules/src/detrend/pmFringeStats.h
===================================================================
--- /trunk/psModules/src/detrend/pmFringeStats.h	(revision 24835)
+++ /trunk/psModules/src/detrend/pmFringeStats.h	(revision 24836)
@@ -142,11 +142,27 @@
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
-/// Write an array of fringes measurements to a FITS table.
-///
-/// Writes an array of fringe measurements for a cell as a FITS table in the analysis metadata.  The array of
-/// fringe statistics must all use the same fringe regions (or there is no point in storing them all
-/// together).  The header is supplemented with scalar values dX, dY, nX and nY (as PSFRNGDX, PSFRNGDY,
-/// PSFRNGNX, PSFRNGNY) from the fringe regions, while the fringe coordinates and mask are written as a FITS
-/// table (as x, y, mask, f, df; f and df are vectors).
+/// Convert an array of fringes measurements to a psArray suitable for writing as a FITS table.
+///
+/// Converts an array of fringe measurements for a cell into the corresponding rows of a FITS
+/// table (array of psMetadata).  The array of fringe statistics must all use the same fringe
+/// regions (or there is no point in storing them all together).  The header is supplemented
+/// with scalar values dX, dY, nX and nY (as PSFRNGDX, PSFRNGDY, PSFRNGNX, PSFRNGNY) from the
+/// fringe regions, while the fringe coordinates and mask are written as a FITS table rows (as
+/// x, y, mask, f, df; f and df are vectors).  Use psFitsTableWrite to save the resulting rows
+/// to disk.
+psArray *pmFringesFormatTable(psMetadata *header, const psArray *fringes);
+
+
+/// Parses an array of fringes measurements from a FITS table.
+///
+/// The fringes for the cell are read from the FITS table (array of psMetadata rows).  The
+/// table provides the region and the (possibly multiple) fringe statistics for that region.
+/// The supplied header defines the scalar values dX, dY, nX and nY (as PSFRNGDX, PSFRNGDY,
+/// PSFRNGNX, PSFRNGNY)
+psArray *pmFringesParseTable(psArray *table, psMetadata *header);
+
+/// Deprecated Function: converts the fringes to a FITS table (array of psMetadata) and saves
+/// them on the cell->analysis; scalar values are dX, dY, nX and nY are written to the header
+/// (as PSFRNGDX, PSFRNGDY, PSFRNGNX, PSFRNGNY)
 bool pmFringesFormat(pmCell *cell,   ///< Cell for which to write
                      psMetadata *header, ///< Header, or NULL
@@ -154,12 +170,8 @@
                     );
 
-/// Parses an array of fringes measurements from a FITS table.
-///
-/// The fringes for the cell are read from the FITS table in the analysis metadata.  The table provides the
-/// region and the (possibly multiple) fringe statistics for that region.  The current extension is used if
-/// the extension name is not provided.
+/// Deprecated Function: pulls a the header and FITS table (array of psMetadata) representing
+/// the fringes from the cell->analysis and converts to fringe measurements
 psArray *pmFringesParse(pmCell *cell ///< Cell for which to read fringes
                        );
-
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////
Index: /trunk/psModules/src/psmodules.h
===================================================================
--- /trunk/psModules/src/psmodules.h	(revision 24835)
+++ /trunk/psModules/src/psmodules.h	(revision 24836)
@@ -47,4 +47,5 @@
 #include <pmFPAfileDefine.h>
 #include <pmFPAfileFitsIO.h>
+#include <pmFPAfileFringeIO.h>
 #include <pmFPAfileIO.h>
 #include <pmFPARead.h>
