Index: /trunk/psModules/configure.ac
===================================================================
--- /trunk/psModules/configure.ac	(revision 7018)
+++ /trunk/psModules/configure.ac	(revision 7019)
@@ -49,5 +49,5 @@
 
 SRCPATH='${top_srcdir}/src'
-SRCDIRS="astrom config detrend imcombine imsubtract objects pslib"
+SRCDIRS="pslib config concepts camera astrom detrend imcombine objects"
 # escape two escapes at this level so \\ gets passed to the shell and \ to perl
 SRCINC=`echo "${SRCDIRS=}" | ${PERL} -pe "s|(\w+)|-I\\\\${SRCPATH=}/\1|g"`
@@ -114,8 +114,9 @@
   src/Makefile
   src/astrom/Makefile
+  src/camera/Makefile
   src/config/Makefile
+  src/concepts/Makefile
   src/detrend/Makefile
   src/imcombine/Makefile
-  src/imsubtract/Makefile
   src/objects/Makefile
   src/pslib/Makefile
@@ -123,7 +124,8 @@
   test/astrom/Makefile
   test/config/Makefile
+  test/camera/Makefile
+  test/concepts/Makefile
   test/detrend/Makefile
   test/imcombine/Makefile
-  test/imsubtract/Makefile
   test/objects/Makefile
   test/pslib/Makefile
Index: /trunk/psModules/src/astrom/Makefile.am
===================================================================
--- /trunk/psModules/src/astrom/Makefile.am	(revision 7018)
+++ /trunk/psModules/src/astrom/Makefile.am	(revision 7019)
@@ -4,52 +4,11 @@
 libpsmoduleastrom_la_LDFLAGS  = -release $(PACKAGE_VERSION)
 libpsmoduleastrom_la_SOURCES  = \
-	pmFPA.c \
-	pmFPAConstruct.c \
-	pmFPACopy.c \
-	pmFPAHeader.c \
-	pmFPARead.c \
-	pmFPAUtils.c \
-	pmFPAWrite.c \
-	pmHDU.c \
-	pmHDUUtils.c \
-	pmReadout.c \
-	pmChipMosaic.c \
-	pmConcepts.c \
-	pmConceptsRead.c \
-	pmConceptsWrite.c \
-	pmConceptsStandard.c \
-	pmFPA_JPEG.c \
-	pmFPAview.c \
-	pmFPAfile.c \
 	pmAstrometryObjects.c
 
-#	pmFPAMaskWeight.c \
 #	pmFPAAstrometry.c
-#	pmChipMosaic.c
 
 psmoduleincludedir = $(includedir)
 psmoduleinclude_HEADERS = \
-	pmFPA.h \
-	pmFPAConstruct.h \
-	pmFPACopy.h \
-	pmFPAHeader.h \
-	pmFPARead.h \
-	pmFPAUtils.h \
-	pmFPAWrite.h \
-	pmHDU.h \
-	pmHDUUtils.h \
-	pmReadout.h \
-	pmChipMosaic.h \
-	pmConcepts.h \
-	pmConceptsRead.h \
-	pmConceptsWrite.h \
-	pmConceptsStandard.h \
-	pmFPA_JPEG.h \
-	pmFPAview.h \
-	pmFPAfile.h \
 	pmAstrometryObjects.h
 
-#	pmFPAMaskWeight.h \
 #	pmFPAAstrometry.h
-#	pmAstrometryObjects.h
-#	pmChipMosaic.h 
Index: unk/psModules/src/astrom/pmAstrometry.c
===================================================================
--- /trunk/psModules/src/astrom/pmAstrometry.c	(revision 7018)
+++ 	(revision )
@@ -1,534 +1,0 @@
-/** @file  psAstrometry.c
-*
-*  @brief This file defines the basic types for astronomical coordinate
-*  transformation
-*
-*  @ingroup AstroImage
-*
-*  @author GLG, MHPCC
-*
-* XXX: We should review the extent of the warning messages on these functions
-* when the transformations are not successful.
-*
-* XXX: Should we implement non-linear cell->chip transforms?
-*
-*  @version $Revision: 1.14 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2006-04-17 18:10:08 $
-*
-*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
-*/
-
-/******************************************************************************/
-/*  INCLUDE FILES                                                             */
-/******************************************************************************/
-#include <string.h>
-#include <math.h>
-#include "pslib.h"
-
-#include "pmAstrometry.h"
-#include "pmConcepts.h"
-
-/*****************************************************************************
-checkValidImageCoords(): this is a private function which simply determines if
-the supplied x,y coordinates are in the range for the supplied psImage.
- 
-XXX: What about col0 and row0
-XXX: This should return a psBool.
-XXX: Macro this for speed.
- *****************************************************************************/
-static psS32 checkValidImageCoords(
-    double x,
-    double y,
-    psImage* tmpImage)
-{
-    PS_ASSERT_IMAGE_NON_NULL(tmpImage, 0);
-
-    // The FLT_EPSILON is because -0.0 was failing this.
-    if (((x+FLT_EPSILON) < 0.0) || (x > (double)tmpImage->numCols) ||
-            ((y+FLT_EPSILON) < 0.0) || (y > (double)tmpImage->numRows)) {
-        return (0);
-    }
-
-    return (1);
-}
-
-/******************************************************************************
- *****************************************************************************/
-#define PS_FREE_HIERARCHY 1
-static void readoutFree(pmReadout *readout)
-{
-    if (readout != NULL) {
-        psFree(readout->image);
-        psFree(readout->mask);
-        psFree(readout->weight);
-        psFree(readout->analysis);
-        #if 0
-
-        psFree(readout->parent);
-        #endif
-
-        readout->parent = NULL;
-    }
-}
-
-static void cellFree(pmCell *cell)
-{
-    if (cell != NULL) {
-        psFree(cell->toChip);
-        psFree(cell->toFPA);
-        psFree(cell->toSky);
-        psFree(cell->concepts);
-        psFree(cell->analysis);
-        psFree(cell->camera);
-        //
-        // Set the parent to NULL in all cell->readouts before psFree(cell->readouts)
-        // in order to avoid memory reference counter problems.
-        //
-        #if 0
-
-        for (psS32 i = 0 ; i < cell->readouts->n ; i++) {
-            pmReadout *tmpReadout = (pmReadout *) cell->readouts->data[i];
-            tmpReadout->parent = NULL;
-            if (PS_FREE_HIERARCHY == 1) {
-                psFree(tmpReadout);
-            }
-        }
-        psFree(cell->parent);
-        #endif
-
-        cell->parent = NULL;
-
-        psFree(cell->readouts);
-        psFree(cell->hdu);
-
-    }
-}
-
-static void chipFree(pmChip* chip)
-{
-    if (chip != NULL) {
-        psFree(chip->toFPA);
-        psFree(chip->fromFPA);
-        psFree(chip->concepts);
-        psFree(chip->analysis);
-        //
-        // Set the parent to NULL in all chip->cells before psFree(chip->cells)
-        // in order to avoid memory reference counter problems.
-        //
-        #if 0
-
-        for (psS32 i = 0 ; i < chip->cells->n ; i++) {
-            pmCell *tmpCell = (pmCell *) chip->cells->data[i];
-            tmpCell->parent = NULL;
-            if (PS_FREE_HIERARCHY == 1) {
-                psFree(tmpCell);
-            }
-        }
-        psFree(chip->parent);
-        #endif
-
-        chip->parent = NULL;
-        psFree(chip->cells);
-        psFree(chip->hdu);
-    }
-}
-
-
-static void FPAFree(pmFPA *fpa)
-{
-    if (fpa != NULL) {
-        psFree(fpa->fromTangentPlane);
-        psFree(fpa->toTangentPlane);
-        psFree(fpa->projection);
-        psFree(fpa->concepts);
-        psFree(fpa->analysis);
-        psFree(fpa->camera);
-        //
-        // Set the parent to NULL in all fpa->chips before psFree(fpa->chips)
-        // in order to avoid memory reference counter problems.
-        //
-        #if 0
-
-        for (psS32 i = 0 ; i < fpa->chips->n ; i++) {
-            pmChip *tmpChip = (pmChip *) fpa->chips->data[i];
-            tmpChip->parent = NULL;
-            if (PS_FREE_HIERARCHY == 1) {
-                psFree(tmpChip);
-            }
-        }
-        #endif
-        psFree(fpa->chips);
-        psFree(fpa->hdu);
-        psFree(fpa->phu);
-    }
-}
-
-void p_pmHDUFree(p_pmHDU *hdu)
-{
-    if (hdu) {
-        psFree(hdu->extname);
-        psFree(hdu->header);
-        psFree(hdu->images);
-        psFree(hdu->masks);
-        psFree(hdu->weights);
-    }
-}
-
-// XXX: Verify these default values for row0, col0, rowBins, colBins
-// PAP: These values may disappear in the future in favour of values in parent->concepts?
-pmReadout *pmReadoutAlloc(pmCell *cell)
-{
-    pmReadout *tmpReadout = (pmReadout *) psAlloc(sizeof(pmReadout));
-
-    tmpReadout->col0 = 0;
-    tmpReadout->row0 = 0;
-    tmpReadout->colBins = 0;
-    tmpReadout->rowBins = 0;
-    tmpReadout->image = NULL;
-    tmpReadout->mask = NULL;
-    tmpReadout->weight = NULL;
-    tmpReadout->analysis = psMetadataAlloc();
-    tmpReadout->parent = cell;
-    if (cell != NULL) {
-        cell->readouts = psArrayAdd(cell->readouts, 1, (psPtr) tmpReadout);
-    }
-    psMemSetDeallocator(tmpReadout, (psFreeFunc) readoutFree);
-    return(tmpReadout);
-}
-
-// XXX: Verify these default values for row0, col0.
-// PAP: These values may disappear in the future in favour of values in the "concepts"?
-pmCell *pmCellAlloc(
-    pmChip *chip,
-    psMetadata *cameraData,
-    const char *name)
-{
-    pmCell *tmpCell = (pmCell *) psAlloc(sizeof(pmCell));
-
-    tmpCell->col0 = 0;
-    tmpCell->row0 = 0;
-    tmpCell->toChip = NULL;
-    tmpCell->toFPA = NULL;
-    tmpCell->toSky = NULL;
-    tmpCell->concepts = psMetadataAlloc();
-    psBool rc = psMetadataAddStr(tmpCell->concepts, PS_LIST_HEAD, "CELL.NAME", 0, NULL, name);
-    if (rc == false) {
-        psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not add CELL.NAME to metadata.\n");
-    }
-    tmpCell->camera = psMemIncrRefCounter(cameraData);
-    tmpCell->analysis = psMetadataAlloc();
-    tmpCell->readouts = psArrayAlloc(0);
-    tmpCell->parent = chip;
-    if (chip != NULL) {
-        chip->cells = psArrayAdd(chip->cells, 1, (psPtr) tmpCell);
-    }
-    tmpCell->process = true;            // All cells are processed by default
-    tmpCell->exists = false;            // Not yet read in
-    tmpCell->hdu = NULL;
-
-    pmConceptsBlankCell(tmpCell);
-
-    psMemSetDeallocator(tmpCell, (psFreeFunc) cellFree);
-    return(tmpCell);
-}
-
-// XXX: Verify these default values for row0, col0.
-// PAP: row0, col0 may disappear in the future in favour of storing values in the "concepts".
-pmChip *pmChipAlloc(
-    pmFPA *fpa,
-    const char *name)
-{
-    pmChip *tmpChip = (pmChip *) psAlloc(sizeof(pmChip));
-
-    tmpChip->col0 = 0;
-    tmpChip->row0 = 0;
-    tmpChip->toFPA = NULL;
-    tmpChip->fromFPA = NULL;
-    tmpChip->concepts = psMetadataAlloc();
-    psBool rc = psMetadataAddStr(tmpChip->concepts, PS_LIST_HEAD, "CHIP.NAME", 0, NULL, name);
-    if (rc == false) {
-        psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not add CHIP.NAME to metadata.\n");
-    }
-    tmpChip->analysis = psMetadataAlloc();
-    tmpChip->cells = psArrayAlloc(0);
-    tmpChip->parent = fpa;
-    if (fpa != NULL) {
-        fpa->chips = psArrayAdd(fpa->chips, 1, (psPtr) tmpChip);
-    }
-    tmpChip->process = true;            // Work on all chips, by default
-    tmpChip->exists = false;            // Not read in yet
-    tmpChip->hdu = NULL;
-
-    pmConceptsBlankChip(tmpChip);
-
-    psMemSetDeallocator(tmpChip, (psFreeFunc) chipFree);
-    return(tmpChip);
-}
-
-pmFPA *pmFPAAlloc(const psMetadata *camera)
-{
-    pmFPA *tmpFPA = (pmFPA *) psAlloc(sizeof(pmFPA));
-
-    tmpFPA->fromTangentPlane = NULL;
-    tmpFPA->toTangentPlane = NULL;
-    tmpFPA->projection = NULL;
-    tmpFPA->concepts = psMetadataAlloc();
-    tmpFPA->analysis = NULL;
-    tmpFPA->camera = psMemIncrRefCounter((psPtr)camera);
-    tmpFPA->chips = psArrayAlloc(0);
-    tmpFPA->hdu = NULL;
-    tmpFPA->phu = NULL;
-
-    pmConceptsBlankFPA(tmpFPA);
-
-    psMemSetDeallocator(tmpFPA, (psFreeFunc) FPAFree);
-    return(tmpFPA);
-}
-
-p_pmHDU *p_pmHDUAlloc(const char *extname)
-{
-    p_pmHDU *hdu = psAlloc(sizeof(p_pmHDU));
-    psMemSetDeallocator(hdu, (psFreeFunc)p_pmHDUFree);
-
-    hdu->extname = psStringCopy(extname);
-    hdu->header = NULL;
-    hdu->images = NULL;
-    hdu->masks = NULL;
-    hdu->weights = NULL;
-
-    return hdu;
-}
-
-static psBool cellCheckParents(pmCell *cell)
-{
-    if (cell == NULL) {
-        return(true);
-    }
-    psBool flag = true;
-
-    for (psS32 i = 0 ; i < cell->readouts->n ; i++) {
-        pmReadout *tmpReadout = (pmReadout *) cell->readouts->data[i];
-        PS_ASSERT_PTR_NON_NULL(tmpReadout, false);
-        if (tmpReadout->parent != cell) {
-            tmpReadout->parent = cell;
-            flag = false;
-        }
-    }
-    return(flag);
-}
-
-static psBool chipCheckParents(pmChip *chip)
-{
-    if (chip == NULL) {
-        return(true);
-    }
-    psBool flag = true;
-
-    for (psS32 i = 0 ; i < chip->cells->n ; i++) {
-        pmCell *tmpCell = (pmCell *) chip->cells->data[i];
-        PS_ASSERT_PTR_NON_NULL(tmpCell, false);
-        if (tmpCell->parent != chip) {
-            tmpCell->parent = chip;
-            flag = false;
-        }
-
-        flag &= cellCheckParents(tmpCell);
-    }
-    return(flag);
-}
-
-psBool pmFPACheckParents(pmFPA *fpa)
-{
-    if (fpa == NULL) {
-        return(true);
-    }
-    psBool flag = true;
-
-    for (psS32 i = 0 ; i < fpa->chips->n ; i++) {
-        pmChip *tmpChip = (pmChip *) fpa->chips->data[i];
-        PS_ASSERT_PTR_NON_NULL(tmpChip, false);
-        if (tmpChip->parent != fpa) {
-            tmpChip->parent = fpa;
-            flag = false;
-        }
-
-        flag &= chipCheckParents(tmpChip);
-    }
-    return(flag);
-}
-
-
-
-/*****************************************************************************
- *****************************************************************************/
-
-// Set cells within a chip to be processed or not
-static bool setCellsProcess(const pmChip *chip, // Chip of interest
-                            bool process  // Process this chip?
-                           )
-{
-    PS_ASSERT_PTR_NON_NULL(chip, false);
-
-    psArray *cells = chip->cells;       // Component cells
-    if (! cells) {
-        return false;
-    }
-    for (int i = 0; i < cells->n; i++) {
-        pmCell *tmpCell = cells->data[i]; // Cell of interest
-        if (tmpCell) {
-            tmpCell->process = process;
-        }
-    }
-
-    return true;
-}
-
-/*****************************************************************************
- *****************************************************************************/
-bool pmFPASelectChip(
-    pmFPA *fpa,
-    int chipNum)
-{
-    PS_ASSERT_PTR_NON_NULL(fpa, false);
-
-    psArray *chips = fpa->chips;        // Component chips
-    if ((chips == NULL) || (chipNum >= chips->n)) {
-        return(false);
-    }
-
-    for (int i = 0 ; i < chips->n ; i++) {
-        pmChip *tmpChip = (pmChip *) chips->data[i];
-        if (tmpChip == NULL) {
-            continue;
-        }
-        if (i == chipNum) {
-            tmpChip->process = true;
-            setCellsProcess(tmpChip, true);
-        } else {
-            tmpChip->process = false;
-            setCellsProcess(tmpChip, false);
-        }
-
-    }
-
-    return true;
-}
-
-
-/*****************************************************************************
-XXX: The SDRS is ambiguous on a few things:
-    Whether or not the other chips should be set process=true. [PAP: No]
-    Should we return the number of chip process=true before or after they're set, [PAP: After]
- *****************************************************************************/
-/**
- *
- * pmFPAExcludeChip shall set process to false only for the specified chip
- * number (chipNum). In the event that the specified chip number does not exist
- * within the fpa, the function shall generate a warning, and perform no action.
- * The function shall return the number of chips within the fpa that have process
- * set to true.
- *
- */
-int pmFPAExcludeChip(
-    pmFPA *fpa,
-    int chipNum)
-{
-    PS_ASSERT_PTR_NON_NULL(fpa, false);
-
-    psArray *chips = fpa->chips;        // Component chips
-    if (chips == NULL) {
-        psLogMsg(__func__, PS_LOG_WARN, "WARNING: fpa->chips == NULL\n");
-        return(0);
-    }
-    if ((chipNum >= chips->n) || (NULL == (pmChip *) chips->data[chipNum])) {
-        psLogMsg(__func__, PS_LOG_WARN, "WARNING: the specified chip (%d) does not exist.\n", chipNum);
-        return(0);
-    }
-
-    int numChips = 0;                   // Number of chips to be processed
-    for (int i = 0 ; i < chips->n ; i++) {
-        pmChip *tmpChip = (pmChip *) chips->data[i]; // Chip of interest
-        if (tmpChip != NULL) {
-            if (i == chipNum) {
-                tmpChip->process = false;
-                setCellsProcess(tmpChip, false); // Wipe out the cell as well
-            } else if (tmpChip->process) {
-                numChips++;
-            }
-        }
-    }
-
-    return(numChips);
-}
-
-
-bool pmCellSetWeights(pmCell *cell // Cell for which to set weights
-                     )
-{
-    float gain = psMetadataLookupF32(NULL, cell->concepts, "CELL.GAIN"); // Cell gain
-    float readnoise = psMetadataLookupF32(NULL, cell->concepts, "CELL.READNOISE"); // Cell read noise
-    psRegion *trimsec = psMetadataLookupPtr(NULL, cell->concepts, "CELL.TRIMSEC"); // Trim section
-
-    p_pmHDU *hdu = cell->hdu;           // The data unit, containing the weight and mask originals
-    if (!hdu) {
-        pmChip *chip = cell->parent;    // The parent chip
-        if (chip->hdu) {
-            hdu = chip->hdu;
-        } else {
-            pmFPA *fpa = chip->parent;  // The parent FPA
-            hdu = fpa->hdu;
-            if (!hdu) {
-                psError(PS_ERR_UNKNOWN, true, "Unable to find the HDU in the hierarchy!\n");
-                return false;
-            }
-        }
-    }
-
-    psArray *pixels = hdu->images;      // Array of images
-    psArray *weights = hdu->weights;    // Array of weight images
-    psArray *masks = hdu->masks;        // Array of mask images
-    // Generate the weights and masks if required
-    if (! weights) {
-        weights = psArrayAlloc(pixels->n);
-        for (int i = 0; i < pixels->n; i++) {
-            psImage *image = pixels->data[i];
-            weights->data[i] = psImageAlloc(image->numCols, image->numRows, PS_TYPE_F32);
-            psImageInit(weights->data[i], 0.0);
-        }
-        hdu->weights = weights;
-    }
-    if (! masks) {
-        masks = psArrayAlloc(pixels->n);
-        for (int i = 0; i < pixels->n; i++) {
-            psImage *image = pixels->data[i];
-            masks->data[i] = psImageAlloc(image->numCols, image->numRows, PS_TYPE_U8);
-            psImageInit(masks->data[i], 0);
-        }
-        hdu->masks = masks;
-    }
-
-    // Set the pixels
-    psArray *readouts = cell->readouts; // Array of readouts
-    for (int i = 0; i < readouts->n; i++) {
-        pmReadout *readout = readouts->data[i]; // The readout of interest
-
-        if (! readout->weight) {
-            readout->weight = weights->data[i];
-        }
-        if (! readout->mask) {
-            readout->mask = masks->data[i];
-        }
-
-        // Mask is already set to 0
-
-        // Set weight image to the variance = g*f + rn^2
-        psImage *image = psImageSubset(readout->image, *trimsec); // The pixels
-        psImage *weight = psImageSubset(readout->weight, *trimsec); // The weight map
-        psBinaryOp(weight, image, "*", psScalarAlloc(gain, PS_TYPE_F32));
-        psBinaryOp(weight, weight, "+", psScalarAlloc(readnoise*readnoise, PS_TYPE_F32));
-    }
-
-    return true;
-}
-
-
Index: unk/psModules/src/astrom/pmAstrometry.h
===================================================================
--- /trunk/psModules/src/astrom/pmAstrometry.h	(revision 7018)
+++ 	(revision )
@@ -1,467 +1,0 @@
-/** @file  pmAstrometry.h
-*
-*  @brief This file defines the basic types for astronomical coordinate
-*  transformation
-*
-*  @ingroup AstroImage
-*
-*  @author GLG, MHPCC
-*
-*  @version $Revision: 1.8 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2006-04-17 18:10:08 $
-*
-*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
-*/
-
-#ifndef PS_ASTROMETRY_H
-#define PS_ASTROMETRY_H
-#if HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include "pslib.h"
-#include "psDB.h"
-
-/// @addtogroup AstroImage
-/// @{
-
-// XXX: Is this correct?  Must determine what p_pmHDU is.
-// XXX: Create the p_pmHDU alloc/free.
-typedef struct
-{
-    const char *extname;                // Extension name, if it corresponds to this level
-    psMetadata *header;                 // The FITS header, if it corresponds to this level
-    psArray *images;                    // The pixel data, if it corresponds to this level
-    psArray *masks;                     // The mask data, if it corresponds to this level
-    psArray *weights;                   // The weight data, if it corresponds to this level
-}
-p_pmHDU;
-
-/** Focal plane data structure
- *
- *  A focal plane consists of one or more chips (according to the number of
- *  pieces of contiguous silicon). It contains metadata containers for the
- *  concepts and analysis, a link to the parent, and pointers to the FITS header,
- *  if that corresponds to this level (the FPA may be the PHU, but will not ever
- *  contain pixels). For astrometry, it contains a transformation from the focal
- *  plane to the tangent plane and the fixed pattern residuals. It is expected
- *  that the transformation will consist of two 4D polynomials (i.e. a function
- *  of two coordinates in position, the magnitude of the object, and the color of
- *  the object) in order to correct for optical distortions and the effects of
- *  the atmosphere; hence we think that it is prudent to include a reverse
- *  transformation which will be derived from numerically inverting the forward
- *  transformation.
- *
- */
-typedef struct
-{
-    // Astrometric transformations
-    psPlaneDistort* fromTangentPlane;   ///< Transformation from tangent plane to focal plane
-    psPlaneDistort* toTangentPlane;     ///< Transformation from focal plane to tangent plane
-    psProjection *projection;           ///< Projection from tangent plane to sky
-    // Information
-    psMetadata *concepts;               ///< Cache for PS concepts
-    psMetadata *analysis;               ///< FPA-level analysis metadata
-    const psMetadata *camera;           ///< Camera configuration
-    psArray *chips;                     ///< The chips
-    p_pmHDU *hdu;                       ///< FITS data
-    psMetadata *phu;                    ///< Primary Header
-}
-pmFPA;
-
-/** Chip data structure
- *
- *  A chip consists of one or more cells (according to the number of amplifiers
- *  on the device). The chip contains metadata containers for the concepts and
- *  analysis, a link to the parent, and pointers to the pointers to the various
- *  FITS data, if that corresponds to this level. For astrometry, in addition to
- *  the rough positioning information, it contains a coordinate transform from
- *  the chip to the focal plane. It is expected that this transform will consist
- *  of two second-order 2D polynomials; hence we think that it is prudent to
- *  include a reverse transformation which will be derived from numerically
- *  inverting the forward transformation. A boolean indicates whether the chip is
- *  of interest, allowing it to be excluded from analysis.
- *
- */
-typedef struct
-{
-    // Offset specifying position on focal plane
-    int col0;                           ///< Offset from the left of FPA.
-    int row0;                           ///< Offset from the bottom of FPA.
-    // Astrometric transformations
-    psPlaneTransform* toFPA;            ///< Transformation from chip to FPA coordinates
-    psPlaneTransform* fromFPA;          ///< Transformation from FPA to chip coordinates
-    // Information
-    psMetadata *concepts;               ///< Cache for PS concepts
-    psMetadata *analysis;               ///< Chip-level analysis metadata
-    psArray *cells;                     ///< The cells (referred to by name)
-    pmFPA *parent;                      ///< Parent FPA
-    bool process;                       ///< Do we bother about reading and working with this chip?
-    bool exists;                        ///< Does the chip exist --- has it been read in?
-    p_pmHDU *hdu;                       ///< FITS data
-}
-pmChip;
-
-/** Cell data structure
- *
- *  A cell consists of one or more readouts.  It also contains a pointer to the
- *  cell's metadata, and its parent chip.  On the astrometry side, it also
- *  contains coordinate transforms from the cell to chip, from the cell to
- *  focal-plane, as well as a "quick and dirty" tranform from the cell to
- *  sky coordinates.
- *
- */
-typedef struct
-{
-    // Offset specifying position on chip
-    int col0;                           ///< Offset from the left of chip.
-    int row0;                           ///< Offset from the bottom of chip.
-    // Astrometric transformations
-    psPlaneTransform* toChip;           ///< Transformations from cell to chip coordinates
-    psPlaneTransform* toFPA;            ///< Transformations from cell to FPA coordinates
-    psPlaneTransform* toSky;            ///< Transformations from cell to sky coordinates
-    // Information
-    psMetadata *concepts;               ///< Cache for PS concepts
-    psMetadata *camera;                 ///< Camera Info
-    psMetadata *analysis;               ///< Cell-level analysis metadata
-    psArray *readouts;                  ///< The readouts (referred to by number)
-    pmChip *parent;                     ///< Parent chip
-    bool process;                       ///< Do we bother about reading and working with this cell?
-    bool exists;                        ///< Does the cell exist --- has it been read in?
-    p_pmHDU *hdu;                       ///< FITS data
-}
-pmCell;
-
-/** Readout data structure.
- *
- *  A readout is the result of a single read of a cell (or a portion thereof).
- *  It contains the offset from the lower-left corner of the chip, in the case
- *  that the CCD was windowed, as well as the binning factors and parity (if the
- *  binning value is negative, then the parity is reversed). It also contains the
- *  pixel data, metadata containers for the concepts and analysis, and a link to
- *  the parent.
- *
- */
-typedef struct
-{
-    // Position on the cell
-    // XXX: These may be removed in the future; use parent->concepts instead?
-    int col0;                           ///< Offset from the left of chip.
-    int row0;                           ///< Offset from the bottom of chip.
-    int colBins;                        ///< Amount of binning in x-dimension
-    int rowBins;                        ///< Amount of binning in y-dimension
-    // Information
-    psImage *image;                     ///< Imaging area of readout
-    psImage *mask;                      ///< Mask of input image
-    psImage *weight;                    ///< Weight of input image
-    psMetadata *analysis;               ///< Readout-level analysis metadata
-    pmCell *parent;                     ///< Parent cell
-}
-pmReadout;
-
-
-/** Allocates a pmReadout
- *
- *  The constructor shall make an empty pmReadout. If the parent cell is not
- *  NULL, the parent link is made and the readout shall be placed in the
- *  parents array of readouts. The metadata containers shall be allocated. All
- *  other pointers in the structure shall be initialized to NULL.
- *
- *  @return pmReadout*    newly allocated pmReadout with all internal pointers set to NULL
- */
-pmReadout *pmReadoutAlloc(
-    pmCell *cell                        ///< Parent cell
-);
-
-/** Allocates a pmCell
- *
- *  The constructor shall make an empty pmCell. If the parent chip is not NULL,
- *  the parent link is made and the cell shall be placed in the parents array of
- *  cells. The readouts array shall be allocated with a zero size, and the
- *  metadata containers constructed. All other pointers in the structure shall be
- *  initialized to NULL.
- *
- *  @return pmCell*    newly allocated pmCell
- */
-pmCell *pmCellAlloc(
-    pmChip *chip,       ///< Parent chip
-    psMetadata *cameraData, ///< Camera data
-    const char *name    ///< Name of cell
-);
-
-/** Allocates a pmChip
- *
- *  The constructor shall make an empty pmChip. If the parent fpa is not NULL,
- *  the parent link is made and the chip shall be placed in the parent's array
- *  of chips. The cells array shall be allocated with a zero size, and the
- *  metadata containers constructed. All other pointers in the structure shall be
- *  initialized to NULL.
- *
- *  @return pmChip*    newly allocated pmChip
- */
-pmChip *pmChipAlloc(
-    pmFPA *fpa,                         ///< FPA to which the chip belongs
-    const char *name                    ///< Name of chip
-);
-
-/** Allocates a pmFPA
- *
- *  The constructor shall make an empty pmFPA. The chips array shall be
- *  allocated with a zero size, the camera and db pointers set to the values
- *  provided, and the concepts metadata constructed. All other pointers in the
- *  structure shall be initialized to NULL.
- *
- */
-pmFPA *pmFPAAlloc(
-    const psMetadata *camera            ///< Camera configuration
-);
-
-/** Allocates a p_pmHDU
- *
- * XXX: More detailed description
- *
- * @return p_pmHDU*    newly allocated p_pmHDU
- */
-p_pmHDU *p_pmHDUAlloc(const char *extname // Extension name
-                     );
-
-
-/** Verify parent links.
- *
- *  This function checks the validity of the parent links in the FPA hierarchy.
- *  If a parent link is not set (or not set correctly), it is corrected, and the
- *  function shall return false. If all the parent pointers were correct, the
- *  function shall return true.
- *
- */
-bool pmFPACheckParents(
-    pmFPA *fpa
-);
-
-
-
-/** FUNC DESC
- *
- *
- *
- *
- */
-
-
-
-/**
- *
- * pmFPASelectChip shall set valid to true for the specified chip number
- * (chipNum), and all other chips shall have valid set to false. In the event
- * that the specified chip number does not exist within the fpa, the function
- * shall return false.
- *
- */
-bool pmFPASelectChip(
-    pmFPA *fpa,
-    int chipNum
-);
-
-/**
- *
- * pmFPAExcludeChip shall set valid to false only for the specified chip
- * number (chipNum). In the event that the specified chip number does not exist
- * within the fpa, the function shall generate a warning, and perform no action.
- * The function shall return the number of chips within the fpa that have valid
- * set to true.
- *
- */
-int pmFPAExcludeChip(
-    pmFPA *fpa,
-    int chipNum
-);
-
-
-/*****************************************************************************
-    Old Stuff
- *****************************************************************************/
-
-
-
-/** Find cooresponding cell for given FPA coordinate
- *
- *  @return pmCell*    the cell cooresponding to the coord in FPA
- */
-pmCell* pmCellInFPA(
-    const psPlane* coord,              ///< the coordinate in FPA plane
-    const pmFPA* FPA                   ///< the FPA to search for the cell
-);
-
-
-/** Find cooresponding chip for given FPA coordinate
- *
- *  @return pmChip*    the chip cooresponding to coord
- */
-pmChip* pmChipInFPA(
-    const psPlane* coord,              ///< the coordinate in FPA plane
-    const pmFPA* FPA                   ///< the FPA to search for the cell
-);
-
-
-/** Find cooresponding cell for given Chip coordinate
- *
- *  @return pmCell*    the cell cooresponding to coord
- */
-pmCell* pmCellInChip(
-    const psPlane* coord,              ///< the coordinate in Chip plane
-    const pmChip* chip                 ///< the chip to search for the cell
-);
-
-
-/** Translate a cell coordinate into a chip coordinate
- *
- *  @return psPlane*    the resulting chip coordinate
- */
-psPlane* pmCoordCellToChip(
-    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
-    const psPlane* in,                 ///< the coordinate within Cell
-    const pmCell* cell                 ///< the Cell in interest
-);
-
-
-/** Translate a chip coordinate into a FPA coordinate
- *
- *  @return psPlane*    the resulting FPA coordinate
- */
-psPlane* pmCoordChipToFPA(
-    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
-    const psPlane* in,                 ///< the coordinate within Chip
-    const pmChip* chip                 ///< the chip in interest
-);
-
-
-/** Translate a FPA coordinate into a Tangent Plane coordinate
- *
- *  @return psPlane*    the resulting Tangent Plane coordinate
- */
-psPlane* pmCoordFPAToTP(
-    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
-    const psPlane* in,                 ///< the coordinate within FPA
-    double color,                      ///< Color of source
-    double magnitude,                  ///< Magnitude of source
-    const pmFPA* fpa                   ///< the FPA in interest
-);
-
-
-/** Translate a Tangent Plane coordinate into a Sky coordinate
- *
- *  @return psSphere*    the resulting Sky coordinate
- */
-psSphere* pmCoordTPToSky(
-    psSphere* out,                     ///< a sphere struct to recycle. If NULL, a new struct is created
-    const psPlane* in,                ///< the coordinate within Tangent Plane
-    const psProjection *projection
-);
-
-/** Translate a cell coordinate into a FPA coordinate
- *
- *  @return psPlane*    the resulting FPA coordinate
- */
-psPlane* pmCoordCellToFPA(
-    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
-    const psPlane* in,                 ///< the coordinate within cell
-    const pmCell* cell                 ///< the cell in interest
-);
-
-
-/** Translate a cell coordinate into a Sky coordinate
- *
- *  @return psSphere*    the resulting Sky coordinate
- */
-psSphere* pmCoordCellToSky(
-    psSphere* out,                     ///< a sphere struct to recycle. If NULL, a new struct is created
-    const psPlane* in,                 ///< the coordinate within cell
-    double color,                      ///< Color of source
-    double magnitude,                  ///< Magnitude of source
-    const pmCell* cell                 ///< the cell in interest
-);
-
-
-/** Translate a cell coordinate into a Sky coordinate using a 'quick and
- *  dirty' method
- *
- *  @return psSphere*    the resulting Sky coordinate
- */
-psSphere* pmCoordCellToSkyQuick(
-    psSphere* out,                     ///< a sphere struct to recycle. If NULL, a new struct is created
-    const psPlane* in,                 ///< the coordinate within cell
-    const pmCell* cell                 ///< the cell in interest
-);
-
-
-/** Translate a Sky coordinate into a Tangent Plane coordinate
- *
- *  @return psPlane*    the resulting Tangent Plane coordinate
- */
-psPlane* pmCoordSkyToTP(
-    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
-    const psSphere* in,                ///< the sky coordinate
-    const psProjection *projection
-);
-
-/** Translate a Tangent Plane coordinate into a FPA coordinate
- *
- *  @return psPlane*    the resulting FPA coordinate
- */
-psPlane* pmCoordTPToFPA(
-    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
-    const psPlane* in,                 ///< the coordinate within tangent plane
-    double color,                      ///< Color of source
-    double magnitude,                  ///< Magnitude of source
-    const pmFPA* fpa                   ///< the FPA of interest
-);
-
-
-/** Translate a FPA coordinate into a chip coordinate
- *
- *  @return psPlane*    the resulting chip coordinate
- */
-psPlane* pmCoordFPAToChip(
-    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
-    const psPlane* in,                 ///< the FPA coordinate
-    const pmChip* chip                 ///< the chip of interest
-);
-
-
-/** Translate a chip coordinate into a cell coordinate
- *
- *  @return psPlane*    the resulting cell coordinate
- */
-psPlane* pmCoordChipToCell(
-    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
-    const psPlane* in,                 ///< the Chip coordinate
-    const pmCell* cell                 ///< the cell of interest
-);
-
-
-/** Translate a sky coordinate into a cell coordinate
- *
- *  @return psPlane*    the resulting cell coordinate
- */
-psPlane* pmCoordSkyToCell(
-    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
-    const psSphere* in,                ///< the Sky coordinate
-    float color,                       ///< Color of source
-    float magnitude,                   ///< Magnitude of source
-    const pmCell* cell                 ///< the cell of interest
-);
-
-
-/** Translate a sky coordinate into a cell coordinate using a 'quick and
- *  dirty' method
- *
- *  @return psPlane*    the resulting cell coordinate
- */
-psPlane* pmCoordSkyToCellQuick(
-    psPlane* out,                      ///< a plane struct to recycle. If NULL, a new struct is created
-    const psSphere* in,                ///< the Sky coordinate
-    const pmCell* cell                 ///< the cell of interest
-);
-
-
-#endif // #ifndef PS_ASTROMETRY_H
-
Index: unk/psModules/src/astrom/pmChipMosaic.c
===================================================================
--- /trunk/psModules/src/astrom/pmChipMosaic.c	(revision 7018)
+++ 	(revision )
@@ -1,634 +1,0 @@
-#include <stdio.h>
-#include <assert.h>
-#include "pslib.h"
-#include "pmFPA.h"
-#include "pmHDU.h"
-#include "psRegionIsBad.h"
-
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// File-static (private) functions
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Do two regions overlap?
-#define REGIONS_OVERLAP(region1, region2) \
-((region1->x0 > region2->x0 && region1->x0 < region2->x1) || \
- (region1->x1 > region2->x0 && region1->x1 < region2->x1) || \
- (region1->y0 > region2->y0 && region1->y0 < region2->y1) || \
- (region1->y1 > region2->y0 && region1->y1 < region2->y1))
-
-// Compare a value with a maximum and minimum
-#define COMPARE(value,min,max) \
-if ((value) < (min)) { \
-    (min) = (value); \
-} \
-if ((value) > (max)) { \
-    (max) = (value); \
-}
-
-// Update a metadata entry directly
-#define MD_UPDATE(MD, NAME, TYPE, VALUE) \
-{ \
-    psMetadataItem *item = psMetadataLookup(MD, NAME); \
-    item->data.TYPE = VALUE; \
-}
-
-// Are the pixels for the chip contiguous on the HDU?
-// Work this out by examining all the CELL.TRIMSEC and CELL.BIASSEC regions for the component cells
-static bool chipContiguous(psRegion *bounds, // The bounds of the image, altered if primary==true
-                           pmChip *chip, // The chip to examine for contiguity
-                           bool primary // Is this the primary chip of interest?
-                          )
-{
-    if (primary) {
-        *bounds = psRegionSet(INFINITY, 0, INFINITY, 0);
-    }
-    psArray *cells = chip->cells;       // The array of cells
-    bool mdok = true;                   // Status of MD lookup
-    for (int i = 0; i < cells->n; i++) {
-        pmCell *cell = cells->data[i];  // Cell of interest
-        psRegion *trimsec = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.TRIMSEC"); // Trim section
-        if (!mdok || !trimsec || psRegionIsBad(*trimsec)) {
-            psError(PS_ERR_IO, true, "CELL.TRIMSEC hasn't been set for cell %d.\n", i);
-            return false;
-        }
-
-        if (primary) {
-            if (trimsec->x0 < bounds->x0) {
-                bounds->x0 = trimsec->x0;
-            }
-            if (trimsec->x1 > bounds->x1) {
-                bounds->x1 = trimsec->x1;
-            }
-            if (trimsec->y0 < bounds->y0) {
-                bounds->y0 = trimsec->y0;
-            }
-            if (trimsec->y1 > bounds->y1) {
-                bounds->y1 = trimsec->y1;
-            }
-        } else if (REGIONS_OVERLAP(trimsec, bounds)) {
-            return false;
-        }
-
-        psList *biassecs = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.BIASSEC"); // Bias sections
-        if (!mdok || !biassecs) {
-            psError(PS_ERR_IO, true, "CELL.BIASSEC hasn't been set for cell %d.\n", i);
-            return false;
-        }
-        if (biassecs->n == 0) {
-            // No point allocating an iterator if there's nothing there to iterate on
-            continue;
-        }
-        psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false); // Iterator
-        psRegion *biassec = NULL;       // Bias section from iteration
-        while ((biassec = psListGetAndIncrement(biassecsIter))) {
-            if (psRegionIsBad(*biassec)) {
-                continue;
-            }
-            if (REGIONS_OVERLAP(biassec, bounds)) {
-                psFree(biassecsIter);
-                return false;
-            }
-        }
-        psFree(biassecsIter);
-    }
-
-    // If we've gotten this far, everything is fine.
-    return true;
-}
-
-
-// Is the chip "nice"?  If so, return the region containing the chip pixels
-static psRegion *niceChip(int *xBinChip, int *yBinChip, // Binning for chip, to be returned
-                          pmChip *chip  // Chip to examine for "niceness".
-                         )
-{
-    // Check that we've got the HDU in the chip or the FPA
-    if ((!chip->hdu || !chip->hdu->images) && (!chip->parent->hdu || !chip->parent->hdu->images)) {
-        return NULL;
-    }
-
-    // Check parity and binning for component cells
-    bool mdok = true;                   // Status of MD lookup
-    *xBinChip = 0;
-    *yBinChip = 0;
-    for (int i = 0; i < chip->cells->n; i++) {
-        pmCell *cell = chip->cells->data[i]; // The cell of interest
-
-        // A "nice" chip must have only a single readout
-        if (cell->readouts->n != 1) {
-            return NULL;
-        }
-
-        // A "nice" chip must have parity == 1
-        int xParity = psMetadataLookupS32(&mdok, cell->concepts, "CELL.XPARITY"); // Parity in x
-        if (!mdok || xParity == 0) {
-            psError(PS_ERR_IO, true, "CELL.XPARITY hasn't been set for cell %d.\n", i);
-            return NULL;
-        }
-        if (xParity != 1) {
-            return NULL;
-        }
-        int yParity = psMetadataLookupS32(&mdok, cell->concepts, "CELL.YPARITY"); // Parity in y
-        if (!mdok || yParity == 0) {
-            psError(PS_ERR_IO, true, "CELL.YPARITY hasn't been set for cell %d.\n", i);
-            return NULL;
-        }
-        if (yParity != 1) {
-            return NULL;
-        }
-
-        // A "nice" chip must have consistent binning
-        int xBin = psMetadataLookupS32(&mdok, cell->concepts, "CELL.XBIN"); // Binning in x
-        if (!mdok || xBin <= 0) {
-            psError(PS_ERR_IO, true, "CELL.XPARITY hasn't been set for cell %d.\n", i);
-            return NULL;
-        }
-        int yBin = psMetadataLookupS32(&mdok, cell->concepts, "CELL.YBIN"); // Binning in y
-        if (!mdok || yBin <= 0) {
-            psError(PS_ERR_IO, true, "CELL.YPARITY hasn't been set for cell %d.\n", i);
-            return NULL;
-        }
-        if (*xBinChip == 0 || *yBinChip == 0) {
-            *xBinChip = xBin;
-            *yBinChip = yBin;
-        } else if (xBin != *xBinChip || yBin != *yBinChip) {
-            return NULL;
-        }
-    }
-
-    // Now check that the pixels are all contiguous
-    psRegion *imageBounds = psRegionAlloc(0, 0, 0, 0); // Bound of image on HDU
-    if (!chipContiguous(imageBounds, chip, true)) {
-        psTrace(__func__, 5, "Image isn't contiguous.\n");
-        psFree(imageBounds);
-        return NULL;
-    }
-
-    psString region = psRegionToString(*imageBounds);
-    psTrace(__func__, 7, "Image bounds: %s\n", region);
-    psFree(region);
-
-    for (int i = 0; i < chip->cells->n; i++) {
-        pmCell *cell = chip->cells->data[i]; // The cell of interest
-
-        // A "nice" chip must have the (0,0) pixel at CELL.X0,CELL.Y0
-        int x0 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.X0"); // Position of (0,0) on chip
-        if (!mdok) {
-            psError(PS_ERR_IO, true, "CELL.X0 hasn't been set for cell %d.\n", i);
-            return NULL;
-        }
-        int y0 = psMetadataLookupS32(&mdok, cell->concepts, "CELL.Y0"); // Position of (0,0) on chip
-        if (!mdok) {
-            psError(PS_ERR_IO, true, "CELL.Y0 hasn't been set for cell %d.\n", i);
-            return NULL;
-        }
-        pmReadout *readout = cell->readouts->data[0]; // A representative readout
-        if (!readout) {
-            return NULL;                // Nothing here
-        }
-        if (x0 != readout->col0 + readout->image->col0 - (int)imageBounds->x0 ||
-                y0 != readout->row0 + readout->image->row0 - (int)imageBounds->y0) {
-            psTrace(__func__, 5, "CELL.X0,Y0 don't match: %d,%d vs %d,%d\n", x0, y0,
-                    readout->col0 + readout->image->col0 - (int)imageBounds->x0,
-                    readout->row0 + readout->image->row0 - (int)imageBounds->y0);
-            psFree(imageBounds);
-            return NULL;
-        }
-    }
-
-    // Need to check all the other chips if the HDU is in the FPA
-    pmFPA *fpa = chip->parent;          // The parent FPA
-    if (fpa->hdu && fpa->hdu->images) {
-        psArray *chips = fpa->chips;    // Array of chips
-        for (int i = 0; i < chips->n; i++) {
-            pmChip *testChip = chips->data[i]; // The chip of interest
-            if (testChip == chip) {
-                // Already done this one
-                continue;
-            }
-            if (!chipContiguous(imageBounds, testChip, false)) {
-                psTrace(__func__, 5, "Image isn't contiguous.\n");
-                psFree(imageBounds);
-                return NULL;
-            }
-        }
-    }
-
-    return imageBounds;
-}
-
-// Mosaic multiple images, with flips, binning and offsets
-static psImage *imageMosaic(const psArray *source, // Images to splice in
-                            const psVector *xFlip, const psVector *yFlip, // Need to flip x and y?
-                            const psVector *xBinSource, // Binning in x of source images
-                            const psVector *yBinSource, // Binning in y of source images
-                            int xBinTarget, int yBinTarget, // Binning in x and y of target images
-                            const psVector *x0, const psVector *y0 // Offsets for source images on target
-                           )
-{
-    assert(source);
-    assert(xFlip && xFlip->type.type == PS_TYPE_U8);
-    assert(yFlip && yFlip->type.type == PS_TYPE_U8);
-    assert(xBinSource && xBinSource->type.type == PS_TYPE_S32);
-    assert(yBinSource && yBinSource->type.type == PS_TYPE_S32);
-    assert(x0 && x0->type.type == PS_TYPE_S32);
-    assert(y0 && y0->type.type == PS_TYPE_S32);
-    assert(xFlip->n == source->n);
-    assert(yFlip->n == source->n);
-    assert(xBinSource->n == source->n);
-    assert(yBinSource->n == source->n);
-    assert(x0->n == source->n);
-    assert(y0->n == source->n);
-
-    if (source->n == 0) {
-        return NULL;
-    }
-
-    // Get the maximum extent of the mosaic image
-    int xMin = INT_MAX;
-    int xMax = - INT_MAX;
-    int yMin = INT_MAX;
-    int yMax = - INT_MAX;
-    psElemType type = 0;
-    int numImages = 0;                  // Number of images
-    for (int i = 0; i < source->n; i++) {
-        psImage *image = source->data[i]; // The image of interest
-        if (!image) {
-            continue;
-        }
-        numImages++;
-
-        // Only implemented for F32 and U8 images so far.
-        assert(image->type.type == PS_TYPE_F32 || image->type.type == PS_TYPE_U8);
-        // All input types must be the same
-        if (type == 0) {
-            type = image->type.type;
-        }
-        assert(type == image->type.type);
-
-        // Size of cell in x and y
-        int xParity = xFlip->data.U8[i] ? -1 : 1;
-        int yParity = yFlip->data.U8[i] ? -1 : 1;
-        psTrace(__func__, 5, "Extent of cell %d: %d -> %d , %d -> %d\n", i, x0->data.S32[i],
-                x0->data.S32[i] + xParity * xBinSource->data.S32[i] * image->numCols, y0->data.S32[i],
-                y0->data.S32[i] + yParity * yBinSource->data.S32[i] * image->numRows);
-
-        COMPARE(x0->data.S32[i], xMin, xMax);
-        COMPARE(y0->data.S32[i], yMin, yMax);
-        // Subtract the parity to get the inclusive limit (not exclusive)
-        COMPARE(x0->data.S32[i] + xParity * xBinSource->data.S32[i] * image->numCols - xParity, xMin, xMax);
-        COMPARE(y0->data.S32[i] + yParity * yBinSource->data.S32[i] * image->numRows - yParity, yMin, yMax);
-    }
-    if (numImages == 0) {
-        return NULL;
-    }
-
-    // Set up the image
-    // Since both upper and lower values are inclusive, we need to add one to the size
-    float xSize = (float)(xMax - xMin + 1) / (float)xBinTarget;
-    if (xSize - (int)xSize > 0) {
-        xSize += 1;
-    }
-    float ySize = (float)(yMax - yMin + 1) / (float)yBinTarget;
-    if (ySize - (int)ySize > 0) {
-        ySize += 1;
-    }
-
-    psTrace(__func__, 3, "Spliced image will be %dx%d\n", (int)xSize, (int)ySize);
-    psImage *mosaic = psImageAlloc((int)xSize, (int)ySize, type); // The mosaic image
-    psImageInit(mosaic, 0.0);
-
-    // Next pass through the images to do the mosaicking
-    for (int i = 0; i < source->n; i++) {
-        psImage *image = source->data[i]; // The image of interest
-        if (!image) {
-            continue;
-        }
-        if (xBinSource->data.S32[i] == xBinTarget && yBinSource->data.S32[i] == yBinTarget &&
-                xFlip->data.U8[i] == 0 && yFlip->data.U8[i] == 0) {
-            // Let someone else do the hard work; useful to test psImageOverlaySection if no other reason
-            psImageOverlaySection(mosaic, image, x0->data.S32[i], y0->data.S32[i], "+");
-        } else {
-            // We have to do the hard work ourself
-            for (int y = 0; y < image->numRows; y++) {
-                int yParity = yFlip->data.U8[i] ? -1 : 1;
-                float yTargetBase = (y0->data.S32[i] + yParity * yBinSource->data.S32[i] * y) / yBinTarget;
-                for (int x = 0; x < image->numCols; x++) {
-                    int xParity = xFlip->data.U8[i] ? -1 : 1;
-                    float xTargetBase = (x0->data.S32[i] + xParity * xBinSource->data.S32[i] * x) /
-                                        xBinTarget;
-
-                    // In case the original image is binned but the mosaic is not, we need to fill in the
-                    // values in the mosaic.
-                    #define FILL_IN(TYPE)                                                                    \
-                    for (int j = 0; j < yBinSource->data.S32[i]; j++) {                                      \
-                        int yTarget = (int)(yTargetBase + yParity * (float)j / (float)yBinTarget);           \
-                        for (int i = 0; i < xBinSource->data.S32[i]; i++) {                                  \
-                            int xTarget = (int)(xTargetBase + xParity * (float)i / (float)xBinTarget);       \
-                            mosaic->data.TYPE[yTarget][xTarget] += image->data.TYPE[y][x];                   \
-                        }                                                                                    \
-                    }
-
-                    switch (type) {
-                    case PS_TYPE_F32:
-                        FILL_IN(F32);
-                        break;
-                    case PS_TYPE_U8:
-                        FILL_IN(U8);
-                        break;
-                    default:
-                        psAbort(__func__, "Should never get here.\n");
-                    }
-
-                }
-            } // Iterating over input image
-        }
-    }
-
-    return mosaic;
-}
-
-
-// Set the concepts in the new cell, based on the values in the old one
-static bool cellConcepts(pmCell *target,// Target cell
-                         psArray *sources, // Source cells
-                         int xBin, int yBin, // Binning
-                         psRegion *trimsec // The trim section
-                        )
-{
-    bool success = true;                // Result of setting everything
-    float gain       = 0.0;             // Gain
-    float readnoise  = 0.0;             // Read noise
-    float saturation = INFINITY;        // Saturation level
-    float bad        = -INFINITY;       // Bad level
-    float exposure   = 0.0;             // Exposure time
-    float darktime   = 0.0;             // Dark time
-    double time      = 0.0;             // Time of observation
-    psTimeType timeSys = 0;             // Time system
-
-    int nCells = 0;                     // Number of cells;
-    for (int i = 0; i < sources->n; i++) {
-        pmCell *cell = sources->data[i];// The cell of interest
-        if (!cell) {
-            continue;
-        }
-
-        nCells++;
-        gain       += psMetadataLookupF32(NULL, cell->concepts, "CELL.GAIN");
-        readnoise  += psMetadataLookupF32(NULL, cell->concepts, "CELL.READNOISE");
-        exposure   += psMetadataLookupF32(NULL, cell->concepts, "CELL.EXPOSURE");
-        darktime   += psMetadataLookupF32(NULL, cell->concepts, "CELL.DARKTIME");
-        psTime *cellTime = psMetadataLookupPtr(NULL, cell->concepts, "CELL.TIME");
-        time       += psTimeToMJD(cellTime);
-        if (i == 0) {
-            timeSys = psMetadataLookupS32(NULL, cell->concepts, "CELL.TIMESYS");
-        } else if (timeSys != psMetadataLookupS32(NULL, cell->concepts, "CELL.TIMESYS")) {
-            psLogMsg(__func__, PS_LOG_ERROR, "Differing time systems in use: %d vs %d\n", timeSys,
-                     psMetadataLookupS32(NULL, cell->concepts, "CELL.TIMESYS"));
-            success = false;
-        }
-
-        float cellSaturation = psMetadataLookupF32(NULL, cell->concepts, "CELL.SATURATION");
-        if (cellSaturation < saturation) {
-            saturation = cellSaturation;
-        }
-        float cellBad = psMetadataLookupF32(NULL, cell->concepts, "CELL.BAD");
-        if (cellBad < bad) {
-            bad = cellBad;
-        }
-    }
-    gain      /= (float)nCells;
-    readnoise /= (float)nCells;
-    exposure  /= (float)nCells;
-    darktime  /= (float)nCells;
-    time      /= (double)nCells;
-
-    MD_UPDATE(target->concepts, "CELL.GAIN", F32, gain);
-    MD_UPDATE(target->concepts, "CELL.READNOISE", F32, readnoise);
-    MD_UPDATE(target->concepts, "CELL.SATURATION", F32, saturation);
-    MD_UPDATE(target->concepts, "CELL.BAD", F32, bad);
-    MD_UPDATE(target->concepts, "CELL.EXPOSURE", F32, exposure);
-    MD_UPDATE(target->concepts, "CELL.DARKTIME", F32, darktime);
-    MD_UPDATE(target->concepts, "CELL.TIMESYS", S32, timeSys);
-    MD_UPDATE(target->concepts, "CELL.X0", S32, 0);
-    MD_UPDATE(target->concepts, "CELL.Y0", S32, 0);
-    MD_UPDATE(target->concepts, "CELL.XPARITY", S32, 1);
-    MD_UPDATE(target->concepts, "CELL.YPARITY", S32, 1);
-    MD_UPDATE(target->concepts, "CELL.XBIN", S32, xBin);
-    MD_UPDATE(target->concepts, "CELL.YBIN", S32, yBin);
-
-    // CELL.TIME needs special care
-    {
-        psMetadataItem *timeItem = psMetadataLookup(target->concepts, "CELL.TIME");
-        psFree(timeItem->data.V);
-        timeItem->data.V = psTimeFromMJD(time);
-    }
-
-    // CELL.TRIMSEC needs special care
-    {
-        psMetadataItem *trimsecItem = psMetadataLookup(target->concepts, "CELL.TRIMSEC");
-        psFree(trimsecItem->data.V);
-        trimsecItem->data.V = psMemIncrRefCounter(trimsec);
-    }
-
-
-    return success;
-}
-
-
-// Mosaic together the cells in a chip
-bool chipMosaic(psImage **mosaicImage,  // The mosaic image, to be returned
-                psImage **mosaicMask,   // The mosaic mask, to be returned
-                psImage **mosaicWeights, // The mosaic weights, to be returned
-                int *xBinChip, int *yBinChip, // The binning in x and y, to be returned
-                pmChip *chip            // Chip to mosaic
-               )
-{
-    psArray *cells = chip->cells;       // The array of cells
-    int numCells = cells->n;            // Number of cells
-    psArray *images = psArrayAlloc(numCells); // Array of images that will be mosaicked
-    psArray *weights = psArrayAlloc(numCells); // Array of weight images to be mosaicked
-    psArray *masks = psArrayAlloc(numCells); // Array of mask images to be mosaicked
-    psVector *x0 = psVectorAlloc(numCells, PS_TYPE_S32); // Origin x coordinates
-    psVector *y0 = psVectorAlloc(numCells, PS_TYPE_S32); // Origin y coordinates
-    psVector *xBin = psVectorAlloc(numCells, PS_TYPE_S32); // Binning in x
-    psVector *yBin = psVectorAlloc(numCells, PS_TYPE_S32); // Binning in y
-    psVector *xFlip = psVectorAlloc(numCells, PS_TYPE_U8); // Flip in x?
-    psVector *yFlip = psVectorAlloc(numCells, PS_TYPE_U8); // Flip in y?
-    images->n = weights->n = masks->n = numCells;
-    x0->n = y0->n = xBin->n = yBin->n = xFlip->n = yFlip->n = numCells;
-
-    // Binning for the mosaicked chip is the minimum binning allowed by the cells
-    *xBinChip = INT_MAX;
-    *yBinChip = INT_MAX;
-
-    // Set up the required inputs
-    psTrace(__func__, 1, "Mosaicking %d cells...\n", numCells);
-    bool good = true;                   // Is everything good, well-behaved?
-    for (int i = 0; i < numCells && good; i++) {
-        pmCell *cell = cells->data[i];  // The cell of interest
-        if (!cell) {
-            continue;
-        }
-        bool mdok = true;               // Status of MD lookup
-        x0->data.S32[i] = psMetadataLookupS32(&mdok, cell->concepts, "CELL.X0");
-        if (!mdok) {
-            psError(PS_ERR_IO, true, "CELL.X0 for cell %d is not set.\n", i);
-            good = false;
-        }
-        y0->data.S32[i] = psMetadataLookupS32(&mdok, cell->concepts, "CELL.Y0");
-        if (!mdok) {
-            psError(PS_ERR_IO, true, "CELL.Y0 for cell %d is not set.\n", i);
-            good = false;
-        }
-        psTrace(__func__, 5, "Cell %d: x0=%d y0=%d\n", i, x0->data.S32[i], y0->data.S32[i]);
-        xBin->data.S32[i] = psMetadataLookupS32(&mdok, cell->concepts, "CELL.XBIN");
-        if (!mdok || xBin->data.S32[i] == 0) {
-            psError(PS_ERR_IO, true, "CELL.XBIN for cell %d is not set.\n", i);
-            good = false;
-        }
-        if (xBin->data.S32[i] < *xBinChip) {
-            *xBinChip = xBin->data.S32[i];
-        }
-        yBin->data.S32[i] = psMetadataLookupS32(&mdok, cell->concepts, "CELL.YBIN");
-        if (!mdok || yBin->data.S32[i] == 0) {
-            psError(PS_ERR_IO, true, "CELL.YBIN for cell %d is not set.\n", i);
-            good = false;
-        }
-        if (yBin->data.S32[i] < *yBinChip) {
-            *yBinChip = yBin->data.S32[i];
-        }
-        int xParity = psMetadataLookupS32(&mdok, cell->concepts, "CELL.XPARITY");
-        if (!mdok || (xParity != 1 && xParity != -1)) {
-            psError(PS_ERR_IO, true, "CELL.XPARITY for cell %d is not set.\n", i);
-            good = false;
-        }
-        int yParity = psMetadataLookupS32(&mdok, cell->concepts, "CELL.YPARITY");
-        if (!mdok || (yParity != 1 && yParity != -1)) {
-            psError(PS_ERR_IO, true, "CELL.YPARITY for cell %d is not set.\n", i);
-            good = false;
-        }
-        if (xParity == -1) {
-            xFlip->data.U8[i] = 1;
-        } else {
-            xFlip->data.U8[i] = 0;
-        }
-        if (yParity == -1) {
-            yFlip->data.U8[i] = 1;
-        } else {
-            yFlip->data.U8[i] = 0;
-        }
-
-        psArray *readouts = cell->readouts; // The array of readouts
-        if (readouts->n > 1) {
-            psLogMsg(__func__, PS_LOG_WARN, "Cell %d contains more than one readout --- only the first will "
-                     "be mosaicked.\n", i);
-        }
-        pmReadout *readout = readouts->data[0]; // The only readout we'll bother with
-
-        // The images to put into the mosaic
-        images->data[i]  = psMemIncrRefCounter(readout->image);
-        weights->data[i] = psMemIncrRefCounter(readout->weight);
-        masks->data[i]   = psMemIncrRefCounter(readout->mask);
-    }
-
-    // Mosaic the images together and we're done
-    if (good) {
-        *mosaicImage = imageMosaic(images, xFlip, yFlip, xBin, yBin, *xBinChip, *yBinChip, x0, y0);
-        *mosaicWeights = imageMosaic(weights, xFlip, yFlip, xBin, yBin, *xBinChip, *yBinChip, x0, y0);
-        *mosaicMask = imageMosaic(masks, xFlip, yFlip, xBin, yBin, *xBinChip, *yBinChip, x0, y0);
-    }
-
-    // Clean up
-    psFree(images);
-    psFree(weights);
-    psFree(masks);
-    psFree(xFlip);
-    psFree(yFlip);
-    psFree(xBin);
-    psFree(yBin);
-    psFree(x0);
-    psFree(y0);
-
-    return good;
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Public functions
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Mosaic all the cells in a chip together.
-//
-// It is desirable to do this without using psImageOverlay (or similar) if it can be at all avoided (because
-// it's really really slow in that case).  There are therefore two cases:
-//
-// 1. The HDU is at the Chip or FPA level.  This is the fast case, and only works if the HDU is "nice", by
-// which I mean:
-//
-//    - the CELL.TRIMSECs are contiguous on the HDU image
-//    - the CELL.PARITYs are identically +1
-//    - the CELL.XBIN and CELL.YBIN are all identical
-//
-// Then we can just use psImageSubset to get the "mosaicked" chip.
-//
-//
-// 2. The HDU is at the cell level, or the above requirements are not met, in which case we mosaic the cells.
-// This is the slow case.  We need to:
-//
-//    - Throw away the bias regions
-//    - Convert all cells to common parity
-//    - Mosaic the cells into an HDU image using CELL.X0 and CELL.Y0
-//    - Update CELL.TRIMSECs
-//
-// Once the demands of case 1 have been met, or case 2 has been performed, then we can create a cell to hold
-// the mosaic image.
-bool pmChipMosaic(pmChip *chip      // Chip whose cells will be mosaicked
-                 )
-{
-    psImage *mosaicImage   = NULL;      // The mosaic image
-    psImage *mosaicMask    = NULL;      // The mosaic mask
-    psImage *mosaicWeights = NULL;      // The mosaic weights
-
-    // Find the HDU
-    psRegion *chipRegion = NULL;        // Region on the HDU that corresponds to the chip
-    int xBin = 0, yBin = 0;             // Binning for the chip mosaic
-    if ((chipRegion = niceChip(&xBin, &yBin, chip))) {
-        // Case 1 --- we need only cut out the region
-        psTrace(__func__, 1, "Case 1 mosaicking: simple cut-out.\n");
-        pmHDU *hdu = chip->hdu;         // The HDU that has the pixels
-        if (!hdu || !hdu->images) {
-            hdu = chip->parent->hdu;
-        }
-        mosaicImage   = psMemIncrRefCounter(psImageSubset(hdu->images->data[0], *chipRegion));
-        if (hdu->masks) {
-            mosaicMask    = psMemIncrRefCounter(psImageSubset(hdu->masks->data[0], *chipRegion));
-        }
-        if (hdu->weights) {
-            mosaicWeights = psMemIncrRefCounter(psImageSubset(hdu->weights->data[0], *chipRegion));
-        }
-    } else {
-        // Case 2 --- we need to mosaic by cut and paste
-        psTrace(__func__, 1, "Case 2 mosaicking: cut and paste.\n");
-        if (!chipMosaic(&mosaicImage, &mosaicMask, &mosaicWeights, &xBin, &yBin, chip)) {
-            psError(PS_ERR_IO, false, "Unable to mosaic cells.\n");
-            return false;
-        }
-        chipRegion = psRegionAlloc(NAN, NAN, NAN, NAN); // We've cut and paste, so there's no valid trimsec
-    }
-
-    // Construct a new cell, set the concepts, and add the mosaic in
-    pmCell *newCell = pmCellAlloc(NULL, "MOSAIC"); // New cell
-    cellConcepts(newCell, chip->cells, xBin, yBin, chipRegion);
-    psFree(chipRegion);
-    chip->mosaic = (struct pmCell*)newCell;
-
-    // Now make a new readout to go in the new cell
-    pmReadout *newReadout = pmReadoutAlloc(newCell); // New readout
-    newReadout->image  = mosaicImage;
-    newReadout->mask   = mosaicMask;
-    newReadout->weight = mosaicWeights;
-    psFree(newReadout);                 // Drop reference
-
-    return true;
-}
Index: unk/psModules/src/astrom/pmChipMosaic.h
===================================================================
--- /trunk/psModules/src/astrom/pmChipMosaic.h	(revision 7018)
+++ 	(revision )
@@ -1,11 +1,0 @@
-#ifndef PM_CHIP_MOSAIC_H
-#define PM_CHIP_MOSAIC_H
-
-#include "pslib.h"
-#include "pmFPA.h"
-
-// Mosaic all cells within a chip
-bool pmChipMosaic(pmChip *chip      // Chip whose cells will be mosaicked
-                 );
-
-#endif
Index: unk/psModules/src/astrom/pmConcepts.c
===================================================================
--- /trunk/psModules/src/astrom/pmConcepts.c	(revision 7018)
+++ 	(revision )
@@ -1,574 +1,0 @@
-// XXX *REALLY* need generic "concept update" and "concept read" functions that handles the type transparently
-
-#include <stdio.h>
-#include <assert.h>
-
-#include "pslib.h"
-#include "pmConcepts.h"
-#include "pmConceptsRead.h"
-#include "pmConceptsWrite.h"
-#include "pmConceptsStandard.h"
-#include "psAdditionals.h"
-
-static bool conceptsInitialised = false;// Have concepts been read?
-static psMetadata *conceptsFPA = NULL;  // Known concepts for FPA
-static psMetadata *conceptsChip = NULL; // Known concepts for chip
-static psMetadata *conceptsCell = NULL; // Known concepts for cell
-
-// Free a concept
-static void conceptSpecFree(pmConceptSpec *spec)
-{
-    psFree(spec->blank);
-}
-
-pmConceptSpec *pmConceptSpecAlloc(psMetadataItem *blank, // Blank value; contains the name
-                                  pmConceptParseFunc parse, // Function to call to parse the concept
-                                  pmConceptFormatFunc format // Function to call to format the concept
-                                 )
-{
-    pmConceptSpec *spec = psAlloc(sizeof(pmConceptSpec));
-    psMemSetDeallocator(spec, (psFreeFunc)conceptSpecFree);
-
-    spec->blank = psMemIncrRefCounter(blank);
-    spec->parse = parse;
-    spec->format = format;
-
-    return spec;
-}
-
-
-bool pmConceptRegister(psMetadataItem *blank, // Blank value; contains the name
-                       pmConceptParseFunc parse, // Function to call to parse the concept
-                       pmConceptFormatFunc format, // Function to call to format the concept
-                       pmFPALevel level // Level at which to store concept in the FPA hierarchy
-                      )
-{
-    assert(blank);
-    if (!conceptsInitialised) {
-        pmConceptsInit();
-    }
-
-    pmConceptSpec *spec = pmConceptSpecAlloc(blank, parse, format); // The concept specification
-    psMetadata **target = NULL;         // The metadata of known concepts to write to
-    switch (level) {
-    case PM_FPA_LEVEL_FPA:
-        target = &conceptsFPA;
-        break;
-    case PM_FPA_LEVEL_CHIP:
-        target = &conceptsChip;
-        break;
-    case PM_FPA_LEVEL_CELL:
-        target = &conceptsCell;
-        break;
-    default:
-        psError(PS_ERR_IO, true, "Invalid concept level provided: %d\n", level);
-        psFree(spec);
-        return false;
-    }
-
-    psMetadataAdd(*target, PS_LIST_TAIL, blank->name, PS_DATA_UNKNOWN | PS_META_REPLACE,
-                  "Concepts specification", spec);
-    psFree(spec);                       // Drop reference
-
-    return true;
-}
-
-
-// Set all registered concepts to blank value for the specified level
-static bool conceptsBlank(psMetadata **specs, // One of the concepts specifications
-                          psMetadata *target // Place to install the concepts
-                         )
-{
-    if (!conceptsInitialised) {
-        pmConceptsInit();
-    }
-    psMetadataIterator *specsIter = psMetadataIteratorAlloc(*specs, PS_LIST_HEAD, NULL); // Iterator on specs
-    psMetadataItem *specItem = NULL;    // Item from the specs metadata
-    while ((specItem = psMetadataGetAndIncrement(specsIter))) {
-        psTrace(__func__, 9, "Blanking %s...\n", specItem->name);
-        pmConceptSpec *spec = specItem->data.V; // The specification
-        psMetadataItem *blank = spec->blank; // The concept
-        psMetadataItem *copy = NULL;    // Copy of the blank concept
-        // Trap the lists, which can't be copied in the ordinary way without a warning
-        if (blank->type == PS_DATA_LIST) {
-            copy = psMetadataItemAllocPtr(blank->name, PS_DATA_LIST, blank->comment, blank->data.V);
-        } else {
-            copy = psMetadataItemCopy(blank);
-        }
-        if (!psMetadataAddItem(target, copy, PS_LIST_TAIL, PS_META_REPLACE)) {
-            psLogMsg(__func__, PS_LOG_WARN, "Unable to add blank version of concept %s\n", blank->name);
-        }
-        psFree(copy);                   // Drop reference
-    }
-    psFree(specsIter);
-
-    return true;
-}
-
-
-
-// Read all registered concepts for the specified level
-static bool conceptsRead(psMetadata **specs, // One of the concepts specifications
-                         pmFPA *fpa,    // The FPA
-                         pmChip *chip,  // The chip
-                         pmCell *cell,  // The cell
-                         unsigned int *read,     // What's already been read
-                         pmConceptSource source, // The source of the concepts to read
-                         psDB *db,      // Database handle
-                         psMetadata *target // Place into which to read the concepts
-                        )
-{
-    if (!conceptsInitialised) {
-        pmConceptsInit();
-    }
-
-    if (source & PM_CONCEPT_SOURCE_CAMERA && !(*read & PM_CONCEPT_SOURCE_CAMERA)) {
-        pmConceptsReadFromCamera(*specs, cell, target);
-        *read |= PM_CONCEPT_SOURCE_CAMERA;
-    }
-
-    if (source & PM_CONCEPT_SOURCE_DEFAULTS && !(*read & PM_CONCEPT_SOURCE_DEFAULTS)) {
-        pmConceptsReadFromDefaults(*specs, fpa, chip, cell, target);
-        *read |= PM_CONCEPT_SOURCE_DEFAULTS;
-    }
-
-    if (source & PM_CONCEPT_SOURCE_HEADER && !(*read & PM_CONCEPT_SOURCE_HEADER)) {
-        pmConceptsReadFromHeader(*specs, fpa, chip, cell, target);
-        *read |= PM_CONCEPT_SOURCE_HEADER;
-    }
-
-    if (source & PM_CONCEPT_SOURCE_DATABASE && !(*read & PM_CONCEPT_SOURCE_DATABASE)) {
-        pmConceptsReadFromDatabase(*specs, fpa, chip, cell, db, target);
-        *read |= PM_CONCEPT_SOURCE_DATABASE;
-    }
-
-    return true;
-}
-
-// Write all registered concepts for the specified level
-static bool conceptsWrite(psMetadata **specs, // One of the concepts specifications
-                          pmFPA *fpa,   // The FPA
-                          pmChip *chip, // The chip
-                          pmCell *cell, // The cell
-                          pmConceptSource source, // The source of the concepts to write
-                          psDB *db,      // Database handle
-                          psMetadata *concepts // The concepts to write out
-                         )
-{
-    if (!conceptsInitialised) {
-        pmConceptsInit();
-    }
-
-    if (source & PM_CONCEPT_SOURCE_CAMERA) {
-        pmConceptsWriteToCamera(*specs, cell, concepts);
-    }
-    if (source & PM_CONCEPT_SOURCE_DEFAULTS) {
-        pmConceptsWriteToDefaults(*specs, fpa, chip, cell, concepts);
-    }
-    if (source & PM_CONCEPT_SOURCE_HEADER) {
-        pmConceptsWriteToHeader(*specs, fpa, chip, cell, concepts);
-    }
-    if (source & PM_CONCEPT_SOURCE_DATABASE) {
-        pmConceptsWriteToDatabase(*specs, fpa, chip, cell, db, concepts);
-    }
-
-    return true;
-}
-
-// Set the concepts for a given FPA to blanks
-bool pmConceptsBlankFPA(pmFPA *fpa    // FPA for which to set blank concepts
-                       )
-{
-    psTrace("psModule.concepts", 5, "Blanking FPA concepts: %x %x\n", conceptsFPA, fpa->concepts);
-    return conceptsBlank(&conceptsFPA, fpa->concepts);
-}
-
-
-// Read the concepts for a given FPA
-bool pmConceptsReadFPA(pmFPA *fpa,      // FPA for which to read concepts
-                       pmConceptSource source, // The source of the concepts to read
-                       psDB *db         // Database handle
-                      )
-{
-    psTrace("psModule.concepts", 5, "Reading FPA concepts: %x %x\n", conceptsFPA, fpa->concepts);
-    return conceptsRead(&conceptsFPA, fpa, NULL, NULL, &fpa->conceptsRead, source, db, fpa->concepts);
-}
-
-// Read the concepts for a given FPA
-bool pmConceptsWriteFPA(pmFPA *fpa,     // FPA for which to write concepts
-                        pmConceptSource source, // The source of the concepts to read
-                        psDB *db        // Database handle
-                       )
-{
-    psTrace("psModule.concepts", 5, "Writing FPA concepts: %x %x\n", conceptsFPA, fpa->concepts);
-    return conceptsWrite(&conceptsFPA, fpa, NULL, NULL, source, db, fpa->concepts);
-}
-
-// Set the concepts for a given chip to blanks
-bool pmConceptsBlankChip(pmChip *chip // FPA for which to set blank concepts
-                        )
-{
-    psTrace("psModule.concepts", 5, "Blanking chip concepts: %x %x\n", conceptsChip, chip->concepts);
-    return conceptsBlank(&conceptsChip, chip->concepts);
-}
-
-// Read the concepts for a given FPA
-bool pmConceptsReadChip(pmChip *chip,   // Chip for which to read concepts
-                        pmConceptSource source, // The source of the concepts to read
-                        bool propagate, // Propagate to higher levels as well?
-                        psDB *db        // Database handle
-                       )
-{
-    psTrace("psModule.concepts", 5, "Reading chip concepts: %x %x\n", conceptsChip, chip->concepts);
-    pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
-    return conceptsRead(&conceptsChip, fpa, chip, NULL, &chip->conceptsRead, source, db, chip->concepts) &&
-           ((propagate && conceptsRead(&conceptsFPA, fpa, chip, NULL, &fpa->conceptsRead, source, db,
-                                       fpa->concepts)) ||
-            !propagate);
-}
-
-// Read the concepts for a given FPA
-bool pmConceptsWriteChip(pmChip *chip,  // Chip for which to write concepts
-                         pmConceptSource source, // The source of the concepts to read
-                         bool propagate,// Propagate to higher levels as well?
-                         psDB *db        // Database handle
-                        )
-{
-    psTrace("psModule.concepts", 5, "Writing chip concepts: %x %x\n", conceptsChip, chip->concepts);
-    pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
-    return conceptsWrite(&conceptsChip, fpa, chip, NULL, source, db, chip->concepts) &&
-           ((propagate && conceptsWrite(&conceptsFPA, fpa, chip, NULL, source, db, fpa->concepts)) ||
-            !propagate);
-}
-
-// Set the concepts for a given chip to blanks
-bool pmConceptsBlankCell(pmCell *cell // Cell for which to set blank concepts
-                        )
-{
-    psTrace("psModule.concepts", 5, "Blanking cell concepts: %x %x\n", conceptsCell, cell->concepts);
-    return conceptsBlank(&conceptsCell, cell->concepts);
-}
-
-// Read the concepts for a given FPA
-bool pmConceptsReadCell(pmCell *cell,   // Cell for which to read concepts
-                        pmConceptSource source, // The source of the concepts to read
-                        bool propagate,// Propagate to higher levels as well?
-                        psDB *db        // Database handle
-                       )
-{
-    psTrace("psModule.concepts", 5, "Reading cell concepts: %x %x\n", conceptsCell, cell->concepts);
-    pmChip *chip = cell->parent;        // Chip to which the cell belongs
-    pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
-    return conceptsRead(&conceptsCell, fpa, chip, cell, &cell->conceptsRead, source, db, cell->concepts) &&
-           ((propagate && conceptsRead(&conceptsChip, fpa, chip, cell, &chip->conceptsRead, source, db,
-                                       chip->concepts) &&
-             conceptsRead(&conceptsFPA, fpa, chip, cell, &fpa->conceptsRead, source, db, fpa->concepts)) ||
-            !propagate);
-}
-
-// Read the concepts for a given FPA
-bool pmConceptsWriteCell(pmCell *cell,  // FPA for which to write concepts
-                         pmConceptSource source, // The source of the concepts to read
-                         bool propagate,// Propagate to higher levels as well?
-                         psDB *db       // Database handle
-                        )
-{
-    psTrace("psModule.concepts", 5, "Writing cell concepts: %x %x\n", conceptsCell, cell->concepts);
-    pmChip *chip = cell->parent;        // Chip to which the cell belongs
-    pmFPA *fpa = chip->parent;          // FPA to which the chip belongs
-    return conceptsWrite(&conceptsCell, fpa, chip, cell, source, db, cell->concepts) &&
-           ((propagate && conceptsWrite(&conceptsChip, fpa, chip, cell, source, db, chip->concepts) &&
-             conceptsWrite(&conceptsFPA, fpa, chip, cell, source, db, fpa->concepts)) || !propagate);
-}
-
-
-bool pmConceptsInit(void)
-{
-    conceptsInitialised = true;
-
-    bool init = false;                  // Did we initialise anything?
-    if (! conceptsFPA) {
-        conceptsFPA = psMetadataAlloc();
-        init = true;
-
-        // Install the standard concepts
-
-        #if 0
-        // FPA.NAME
-        {
-            psMetadataItem *fpaName = psMetadataItemAllocStr("FPA.NAME", "Name of FPA", "");
-            pmConceptRegister(fpaName, NULL, NULL, PM_FPA_LEVEL_FPA);
-            psFree(fpaName);
-        }
-        #endif
-
-        // FPA.AIRMASS
-        {
-            psMetadataItem *fpaAirmass = psMetadataItemAllocF32("FPA.AIRMASS", "Airmass at boresight", 0.0);
-            pmConceptRegister(fpaAirmass, NULL, NULL, PM_FPA_LEVEL_FPA);
-            psFree(fpaAirmass);
-        }
-
-        // FPA.FILTER
-        {
-            psMetadataItem *fpaFilter = psMetadataItemAllocStr("FPA.FILTER", "Filter used", "");
-            pmConceptRegister(fpaFilter, NULL, NULL, PM_FPA_LEVEL_FPA);
-            psFree(fpaFilter);
-        }
-
-        // FPA.POSANGLE
-        {
-            psMetadataItem *fpaPosangle = psMetadataItemAllocF32("FPA.POSANGLE",
-                                          "Position angle of instrument", 0.0);
-            pmConceptRegister(fpaPosangle, NULL, NULL, PM_FPA_LEVEL_FPA);
-            psFree(fpaPosangle);
-        }
-
-        // FPA.RADECSYS
-        {
-            psMetadataItem *fpaRadecsys = psMetadataItemAllocStr("FPA.RADECSYS",
-                                          "Celestial coordinate system", "");
-            pmConceptRegister(fpaRadecsys, NULL, NULL, PM_FPA_LEVEL_FPA);
-            psFree(fpaRadecsys);
-        }
-
-        // FPA.RA
-        {
-            psMetadataItem *fpaRa = psMetadataItemAllocF64("FPA.RA", "Right Ascension of boresight", NAN);
-            pmConceptRegister(fpaRa, (pmConceptParseFunc)pmConceptParse_FPA_Coords,
-                              (pmConceptFormatFunc)pmConceptFormat_FPA_Coords, PM_FPA_LEVEL_FPA);
-            psFree(fpaRa);
-        }
-
-        // FPA.DEC
-        {
-            psMetadataItem *fpaDec = psMetadataItemAllocF64("FPA.DEC", "Declination of boresight", NAN);
-            pmConceptRegister(fpaDec, (pmConceptParseFunc)pmConceptParse_FPA_Coords,
-                              (pmConceptFormatFunc)pmConceptFormat_FPA_Coords, PM_FPA_LEVEL_FPA);
-            psFree(fpaDec);
-        }
-
-        // Done with FPA level concepts
-    }
-    if (! conceptsChip) {
-        conceptsChip = psMetadataAlloc();
-        init = true;
-        // There are no standard concepts at the chip level to be installed
-    }
-    if (! conceptsCell) {
-        conceptsCell = psMetadataAlloc();
-        init = true;
-
-        // Install the standard concepts
-
-        // CELL.GAIN
-        {
-            psMetadataItem *cellGain = psMetadataItemAllocF32("CELL.GAIN", "CCD gain (e/count)", NAN);
-            pmConceptRegister(cellGain, NULL, NULL, PM_FPA_LEVEL_CELL);
-            psFree(cellGain);
-        }
-
-        // CELL.READNOISE
-        {
-            psMetadataItem *cellReadnoise = psMetadataItemAllocF32("CELL.READNOISE",
-                                            "CCD read noise (e)", NAN);
-            pmConceptRegister(cellReadnoise, NULL, NULL, PM_FPA_LEVEL_CELL);
-            psFree(cellReadnoise);
-        }
-
-        // CELL.SATURATION
-        {
-            psMetadataItem *cellSaturation = psMetadataItemAllocF32("CELL.SATURATION",
-                                             "Saturation level (counts)", NAN);
-            pmConceptRegister(cellSaturation, NULL, NULL, PM_FPA_LEVEL_CELL);
-            psFree(cellSaturation);
-        }
-
-        // CELL.BAD
-        {
-            psMetadataItem *cellBad = psMetadataItemAllocF32("CELL.BAD", "Bad level (counts)", NAN);
-            pmConceptRegister(cellBad, NULL, NULL, PM_FPA_LEVEL_CELL);
-            psFree(cellBad);
-        }
-
-        // CELL.XPARITY
-        {
-            psMetadataItem *cellXparity = psMetadataItemAllocS32("CELL.XPARITY",
-                                          "Orientation in x compared to the rest of the FPA", 0);
-            pmConceptRegister(cellXparity, NULL, NULL, PM_FPA_LEVEL_CELL);
-            psFree(cellXparity);
-        }
-
-        // CELL.YPARITY
-        {
-            psMetadataItem *cellYparity = psMetadataItemAllocS32("CELL.YPARITY",
-                                          "Orientation in x compared to the rest of the FPA", 0);
-            pmConceptRegister(cellYparity, NULL, NULL, PM_FPA_LEVEL_CELL);
-            psFree(cellYparity);
-        }
-
-        // CELL.READDIR
-        {
-            psMetadataItem *cellReaddir = psMetadataItemAllocS32("CELL.READDIR",
-                                          "Read direction, rows=1, cols=2", 0);
-            pmConceptRegister(cellReaddir, NULL, NULL, PM_FPA_LEVEL_CELL);
-            psFree(cellReaddir);
-        }
-
-
-        // These (CELL.EXPOSURE and CELL.DARKTIME) used to be READOUT.EXPOSURE and READOUT.DARKTIME, but that
-        // doesn't really make sense at the moment.  Maybe we need to add a "parent" link to the readouts.
-        // But then how are the exposure times REALLY derived?  They're not in the FITS headers, because a
-        // readout is a plane in a 3D image.  We'll have to dream up some additional suffix to specify these,
-        // but for now....
-
-        // CELL.EXPOSURE
-        {
-            psMetadataItem *cellExposure = psMetadataItemAllocF32("CELL.EXPOSURE",
-                                           "Exposure time (sec)", NAN);
-            pmConceptRegister(cellExposure, NULL, NULL, PM_FPA_LEVEL_CELL);
-            psFree(cellExposure);
-        }
-
-        // CELL.DARKTIME
-        {
-            psMetadataItem *cellDarktime = psMetadataItemAllocF32("CELL.DARKTIME",
-                                           "Time since flush (sec)", NAN);
-            pmConceptRegister(cellDarktime, NULL, NULL, PM_FPA_LEVEL_CELL);
-            psFree(cellDarktime);
-        }
-
-        // CELL.TRIMSEC
-        {
-            psRegion *trimsec = psAlloc(sizeof(psRegion)); // Blank trimsec
-            trimsec->x0 = trimsec->y0 = trimsec->x1 = trimsec->y1 = NAN;
-            psMetadataItem *cellTrimsec = psMetadataItemAllocPtr("CELL.TRIMSEC", PS_DATA_REGION,
-                                          "Trim section", trimsec);
-            psFree(trimsec);
-            pmConceptRegister(cellTrimsec, (pmConceptParseFunc)pmConceptParse_CELL_TRIMSEC,
-                              (pmConceptFormatFunc)pmConceptFormat_CELL_TRIMSEC, PM_FPA_LEVEL_CELL);
-            psFree(cellTrimsec);
-        }
-
-        // CELL.BIASSEC
-        {
-            psList *biassecs = psListAlloc(NULL); // Blank biassecs
-            psMetadataItem *cellBiassec = psMetadataItemAllocPtr("CELL.BIASSEC", PS_DATA_LIST,
-                                          "Bias sections", biassecs);
-            psFree(biassecs);
-            pmConceptRegister(cellBiassec, (pmConceptParseFunc)pmConceptParse_CELL_BIASSEC,
-                              (pmConceptFormatFunc)pmConceptFormat_CELL_BIASSEC, PM_FPA_LEVEL_CELL);
-            psFree(cellBiassec);
-        }
-
-        // CELL.XBIN
-        {
-            psMetadataItem *cellXbin = psMetadataItemAllocS32("CELL.XBIN", "Binning in x", 0);
-            pmConceptRegister(cellXbin, (pmConceptParseFunc)pmConceptParse_CELL_Binning,
-                              (pmConceptFormatFunc)pmConceptFormat_CELL_XBIN, PM_FPA_LEVEL_CELL);
-            psFree(cellXbin);
-        }
-
-        // CELL.YBIN
-        {
-            psMetadataItem *cellYbin = psMetadataItemAllocS32("CELL.YBIN", "Binning in y", 0);
-            pmConceptRegister(cellYbin, (pmConceptParseFunc)pmConceptParse_CELL_Binning,
-                              (pmConceptFormatFunc)pmConceptFormat_CELL_YBIN, PM_FPA_LEVEL_CELL);
-            psFree(cellYbin);
-        }
-
-        // CELL.TIMESYS
-        {
-            psMetadataItem *cellTimesys = psMetadataItemAllocS32("CELL.TIMESYS", "Time system", -1);
-            pmConceptRegister(cellTimesys, (pmConceptParseFunc)pmConceptParse_CELL_TIMESYS,
-                              (pmConceptFormatFunc)pmConceptFormat_CELL_TIMESYS, PM_FPA_LEVEL_CELL);
-            psFree(cellTimesys);
-        }
-
-        // CELL.TIME
-        {
-            psTime *time = psTimeAlloc(PS_TIME_TAI); // Blank time
-            // Not particularly distinguishing, but should be good enough
-            time->sec = 0;
-            time->nsec = 0;
-            psMetadataItem *cellTime = psMetadataItemAlloc("CELL.TIME", PS_DATA_TIME,
-                                       "Time of exposure", time);
-            psFree(time);
-            pmConceptRegister(cellTime, (pmConceptParseFunc)pmConceptParse_CELL_TIME,
-                              (pmConceptFormatFunc)pmConceptFormat_CELL_TIME, PM_FPA_LEVEL_CELL);
-            psFree(cellTime);
-        }
-
-        // CELL.X0
-        {
-            psMetadataItem *cellX0 = psMetadataItemAllocS32("CELL.X0", "Position of (0,0) on the chip", 0);
-            pmConceptRegister(cellX0, (pmConceptParseFunc)pmConceptParse_CELL_Positions,
-                              (pmConceptFormatFunc)pmConceptFormat_CELL_Positions, PM_FPA_LEVEL_CELL);
-            psFree(cellX0);
-        }
-
-        // CELL.Y0
-        {
-            psMetadataItem *cellY0 = psMetadataItemAllocS32("CELL.Y0", "Position of (0,0) on the chip", 0);
-            pmConceptRegister(cellY0, (pmConceptParseFunc)pmConceptParse_CELL_Positions,
-                              (pmConceptFormatFunc)pmConceptFormat_CELL_Positions, PM_FPA_LEVEL_CELL);
-            psFree(cellY0);
-        }
-
-    }
-
-    return init;
-}
-
-void pmConceptsDone(void)
-{
-    psFree(conceptsFPA);
-    psFree(conceptsChip);
-    psFree(conceptsCell);
-}
-
-
-// Copy concepts from one FPA to another
-bool pmFPACopyConcepts(pmFPA *target,   // The target FPA
-                       pmFPA *source    // The target FPA
-                      )
-{
-    // Copy FPA concepts
-    target->concepts = psMetadataCopy(target->concepts, source->concepts);
-
-    // Copy chip concepts
-    psArray *targetChips = target->chips; // Chips in target
-    psArray *sourceChips = source->chips; // Chips in source
-    if (targetChips->n != sourceChips->n) {
-        psError(PS_ERR_IO, true, "Number of chips in target (%d) and source (%d) differ --- unable to copy "
-                "concepts.\n", targetChips->n, sourceChips->n);
-        return false;
-    }
-    for (int i = 0; i < targetChips->n; i++) {
-        pmChip *targetChip = targetChips->data[i]; // Target chip of interest
-        pmChip *sourceChip = sourceChips->data[i]; // Source chip of interest
-        if (! targetChip || ! sourceChip) {
-            continue;
-        }
-        targetChip->concepts = psMetadataCopy(targetChip->concepts, sourceChip->concepts);
-
-        // Copy cell concepts
-        psArray *targetCells = targetChip->cells; // Cells in target
-        psArray *sourceCells = sourceChip->cells; // Cells in source
-        if (targetCells->n != sourceCells->n) {
-            psError(PS_ERR_IO, true, "Number of cells in target (%d) and source (%d) differ for chip %d ---"
-                    " unable to copy concepts.\n", targetCells->n, sourceCells->n, i);
-            return false;
-        }
-        for (int j = 0; j < targetCells->n; j++) {
-            pmCell *targetCell = targetCells->data[j]; // Target chip of interest
-            pmCell *sourceCell = sourceCells->data[j]; // Source chip of interest
-            if (! targetCell || ! sourceCell) {
-                continue;
-            }
-            targetCell->concepts = psMetadataCopy(targetCell->concepts, sourceCell->concepts);
-        }
-    }
-
-    return true;
-}
Index: unk/psModules/src/astrom/pmConcepts.h
===================================================================
--- /trunk/psModules/src/astrom/pmConcepts.h	(revision 7018)
+++ 	(revision )
@@ -1,91 +1,0 @@
-#ifndef PM_CONCEPTS_H
-#define PM_CONCEPTS_H
-
-#include "pslib.h"
-#include "pmFPA.h"
-
-// Function to call to parse a concept once it has been read
-typedef psMetadataItem* (*pmConceptParseFunc)(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
-// Function to call to format a concept for writing
-typedef psMetadataItem* (*pmConceptFormatFunc)(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
-
-// A "concept" specification
-typedef struct
-{
-    psMetadataItem *blank;              // Blank value of concept; also contains the name
-    pmConceptParseFunc parse;           // Function to call to read the concept
-    pmConceptFormatFunc format;         // Function to call to write the concept
-}
-pmConceptSpec;
-
-// Allocator
-pmConceptSpec *pmConceptSpecAlloc(psMetadataItem *blank, // Blank value; contains the name
-                                  pmConceptParseFunc parse, // Function to call to parse the concept
-                                  pmConceptFormatFunc format // Function to call to format the concept
-                                 );
-
-// Register a new concept
-bool pmConceptRegister(psMetadataItem *blank, // Blank value; contains the name
-                       pmConceptParseFunc parse, // Function to call to parse the concept
-                       pmConceptFormatFunc format, // Function to call to format the concept
-                       pmFPALevel level // Level at which to store concept in the FPA hierarchy
-                      );
-
-// Some specificity to reading and writing concepts
-typedef enum {
-    PM_CONCEPT_SOURCE_NONE     = 0x00,  // No concepts
-    PM_CONCEPT_SOURCE_CAMERA   = 0x01,  // Concept comes from the camera information
-    PM_CONCEPT_SOURCE_DEFAULTS = 0x02,  // Concept comes from defaults
-    PM_CONCEPT_SOURCE_HEADER   = 0x04,  // Concept comes from FITS header
-    PM_CONCEPT_SOURCE_DATABASE = 0x08,  // Concept comes from database
-    PM_CONCEPT_SOURCE_ALL      = 0x0e   // All concepts
-} pmConceptSource;
-
-// Set blanks, read or write concepts at the appropriate level
-bool pmConceptsBlankFPA(pmFPA *fpa      // FPA for which to set blank concepts
-                       );
-bool pmConceptsReadFPA(pmFPA *fpa,      // FPA for which to read concepts
-                       pmConceptSource source, // Source for concepts
-                       psDB *db         // Database handle
-                      );
-bool pmConceptsWriteFPA(pmFPA *fpa,     // FPA for which to write concepts
-                        pmConceptSource source, // Source for concepts
-                        psDB *db        // Database handle
-                       );
-bool pmConceptsBlankChip(pmChip *chip   // FPA for which to set blank concepts
-                        );
-bool pmConceptsReadChip(pmChip *chip,   // Chip for which to read concepts
-                        pmConceptSource source, // Source for concepts
-                        bool propagate, // Propagate to higher levels as well?
-                        psDB *db        // Database handle
-                       );
-bool pmConceptsWriteChip(pmChip *chip,  // Chip for which to write concepts
-                         pmConceptSource source, // Source for concepts
-                         bool propagate,// Propagate to higher levels as well?
-                         psDB *db       // Database handle
-                        );
-bool pmConceptsBlankCell(pmCell *cell   // Cell for which to set blank concepts
-                        );
-bool pmConceptsReadCell(pmCell *cell,   // Cell for which to read concepts
-                        pmConceptSource source, // Source for concepts
-                        bool propagate, // Propagate to higher levels as well?
-                        psDB *db        // Database handle
-                       );
-bool pmConceptsWriteCell(pmCell *cell,  // FPA for which to write concepts
-                         pmConceptSource source, // Source for concepts
-                         bool propagate,// Propagate to higher levels as well?
-                         psDB *db       // Database handle
-                        );
-
-// Set up the blank concepts
-bool pmConceptsInit(void);
-// Free the concept specs so there's no memory leak
-void pmConceptsDone(void);
-
-// Copy all the concepts within an FPA to another FPA
-bool pmFPACopyConcepts(pmFPA *target,   // The target FPA
-                       pmFPA *source    // The target FPA
-                      );
-
-
-#endif
Index: unk/psModules/src/astrom/pmConceptsRead.c
===================================================================
--- /trunk/psModules/src/astrom/pmConceptsRead.c	(revision 7018)
+++ 	(revision )
@@ -1,608 +1,0 @@
-#include <stdio.h>
-
-#include "pslib.h"
-#include "psMetadataItemParse.h"
-
-#include "pmFPA.h"
-#include "pmHDU.h"
-#include "pmHDUUtils.h"
-#include "pmConcepts.h"
-#include "pmConceptsRead.h"
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// File-static functions
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// This function gets called for the really boring concepts --- where all you have to do is parse from a
-// header or database and you don't need to muck around with conversions.  There is no similar "formatPlain",
-// since the type is already known.
-static psMetadataItem *parsePlain(psMetadataItem *concept, // The concept to parse
-                                  psMetadataItem *pattern // The concept pattern
-                                 )
-{
-    switch (pattern->type) {
-    case PS_DATA_STRING: {
-            psString string = psMetadataItemParseString(concept); // Get the string, so I can free it after it
-            // goes on the MetadataItem
-            psMetadataItem *item = psMetadataItemAllocStr(pattern->name, pattern->comment, string);
-            psFree(string);
-            return item;
-        }
-    case PS_DATA_S32:
-        return psMetadataItemAllocS32(pattern->name, pattern->comment, psMetadataItemParseS32(concept));
-    case PS_DATA_F32:
-        return psMetadataItemAllocF32(pattern->name, pattern->comment, psMetadataItemParseF32(concept));
-    case PS_DATA_F64:
-        return psMetadataItemAllocF64(pattern->name, pattern->comment, psMetadataItemParseF64(concept));
-    default:
-        psLogMsg(__func__, PS_LOG_WARN, "Concept %s (%s) is not of a standard type (%x)\n",
-                 pattern->name, pattern->comment, pattern->type);
-        return NULL;
-    }
-}
-
-
-// Parse a single concept
-static bool conceptParse(pmConceptSpec *spec, // The concept specification
-                         psMetadataItem *concept, // The concept to parse
-                         psMetadata *cameraFormat, // The camera format
-                         psMetadata *target, // The target
-                         pmFPA *fpa,    // The FPA
-                         pmChip *chip,  // The chip
-                         pmCell *cell   // The cell
-                        )
-{
-    if (concept) {
-        psMetadataItem *parsed = NULL;  // The parsed concept
-        if (spec->parse) {
-            parsed = spec->parse(concept, spec->blank, cameraFormat, fpa, chip, cell);
-        } else {
-            parsed = parsePlain(concept, spec->blank);
-        }
-
-        // Plug the parsed concept into a new psMetadataItem, so each "concept" has its own version that can
-        // be altered without affecting the others.  Also, so that we maintain the template name and comment.
-        psMetadataItem *cleaned = NULL;     // Item that's been cleaned up --- correct name and comment
-        switch (spec->blank->type) {
-        case PS_DATA_STRING:
-            cleaned = psMetadataItemAllocStr(spec->blank->name, spec->blank->comment, parsed->data.V);
-            break;
-        case PS_DATA_S32:
-            cleaned = psMetadataItemAllocS32(spec->blank->name, spec->blank->comment, parsed->data.S32);
-            break;
-        case PS_DATA_F32:
-            cleaned = psMetadataItemAllocF32(spec->blank->name, spec->blank->comment, parsed->data.F32);
-            break;
-        case PS_DATA_F64:
-            cleaned = psMetadataItemAllocF64(spec->blank->name, spec->blank->comment, parsed->data.F64);
-            break;
-        default:
-            cleaned = psMetadataItemAlloc(spec->blank->name, parsed->type, spec->blank->comment,
-                                          parsed->data.V);
-        }
-        psFree(parsed);
-        psMetadataAddItem(target, cleaned, PS_LIST_TAIL, PS_META_REPLACE);
-        psFree(cleaned);                 // Drop reference
-        return true;
-    }
-
-    return false;
-}
-
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Public functions
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-bool pmConceptsReadFromCamera(psMetadata *specs, // The concept specifications
-                              pmCell *cell,  // The cell
-                              psMetadata *target // Place into which to read the concepts
-                             )
-{
-    if (cell) {
-        pmHDU *hdu = pmHDUGetLowest(NULL, NULL, cell); // The HDU at the lowest level
-        if (! hdu) {
-            return false;
-        }
-        psMetadata *cameraFormat = hdu->format; // The camera format
-        psMetadata *cellConfig = cell->config; // The camera configuration for this cell
-        psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
-        psMetadataItem *specItem = NULL;    // Item from the specs metadata
-        while ((specItem = psMetadataGetAndIncrement(specsIter))) {
-            pmConceptSpec *spec = specItem->data.V; // The specification
-            psString name = specItem->name; // The concept name
-            psMetadataItem *conceptItem = psMetadataLookup(cellConfig, name); // The concept, or NULL
-            psMetadataItem *value = NULL; // The value of the concept
-            if (conceptItem) {
-                if (conceptItem->type == PS_DATA_STRING) {
-                    // Check the SOURCE
-                    psString nameSource = NULL; // String with the concept name and ".SOURCE" added
-                    psStringAppend(&nameSource, "%s.SOURCE", name);
-                    bool mdok = true;       // Status of MD lookup
-                    psString source = psMetadataLookupStr(&mdok, cell->config, nameSource); // The source
-                    psFree(nameSource);
-                    if (mdok && strlen(source) > 0 && strcasecmp(source, "VALUE") == 0) {
-                        value = conceptItem;
-                        conceptParse(spec, value, cameraFormat, target, NULL, NULL, cell);
-                    } else if (source && (strlen(source) == 0 || strcasecmp(source, "HEADER") != 0)) {
-                        // We leave "HEADER" to pmConceptsReadFromHeader
-                        psError(PS_ERR_IO, true, "%s isn't HEADER or VALUE --- can't read %s\n", source,
-                                name);
-                        continue;
-                    }
-                } else {
-                    // Another type --- should be OK
-                    conceptParse(spec, conceptItem, cameraFormat, target, NULL, NULL, cell);
-                }
-            }
-        }
-        psFree(specsIter);
-        return true;
-    }
-    return false;
-}
-
-
-bool pmConceptsReadFromDefaults(psMetadata *specs, // The concept specifications
-                                pmFPA *fpa, // The FPA
-                                pmChip *chip, // The chip
-                                pmCell *cell, // The cell
-                                psMetadata *target // Place into which to read the concepts
-                               )
-{
-    pmHDU *hdu = pmHDUGetLowest(fpa, chip, cell); // The HDU at the lowest level
-    if (!hdu) {
-        return false;
-    }
-    psMetadata *cameraFormat = hdu->format; // The camera format
-    bool mdok = true;                   // Status of MD lookup
-    psMetadata *defaults = psMetadataLookupMD(&mdok, cameraFormat, "DEFAULTS"); // The DEFAULTS spec
-    if (mdok && defaults) {
-        pmHDU *hdu = pmHDUGetLowest(fpa, chip, cell); // The HDU at the lowest level
-        psMetadata *cameraFormat = hdu->format; // The camera format
-        psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
-        psMetadataItem *specItem = NULL;    // Item from the specs metadata
-        while ((specItem = psMetadataGetAndIncrement(specsIter))) {
-            pmConceptSpec *spec = specItem->data.V; // The specification
-            psString name = specItem->name; // The concept name
-            psMetadataItem *conceptItem = psMetadataLookup(defaults, name); // The concept, or NULL
-            conceptParse(spec, conceptItem, cameraFormat, target, fpa, chip, cell);
-        }
-        psFree(specsIter);
-        return true;
-    }
-    return false;
-}
-
-
-bool pmConceptsReadFromHeader(psMetadata *specs, // The concept specifications
-                              pmFPA *fpa, // The FPA
-                              pmChip *chip, // The chip
-                              pmCell *cell,  // The cell
-                              psMetadata *target // Place into which to read the concepts
-                             )
-{
-    pmHDU *hdu = pmHDUGetLowest(fpa, chip, cell); // The HDU at the lowest level
-    if (!hdu) {
-        return false;
-    }
-    psMetadata *cameraFormat = hdu->format; // The camera format
-    bool mdok = true;                   // Status of MD lookup
-    psMetadata *transSpec = psMetadataLookupMD(&mdok, cameraFormat, "TRANSLATION"); // The TRANSLATION spec
-    if (mdok && transSpec) {
-        psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
-        psMetadataItem *specItem = NULL;    // Item from the specs metadata
-        while ((specItem = psMetadataGetAndIncrement(specsIter))) {
-            pmConceptSpec *spec = specItem->data.V; // The specification
-            psString name = specItem->name; // The concept name
-            psMetadataItem *headerItem = NULL; // The value of the concept from the header
-            // First check the cell configuration
-            if (cell && cell->config) {
-                psMetadataItem *conceptItem = psMetadataLookup(cell->config, name); // The concept, or NULL
-                if (conceptItem) {
-                    // Check the SOURCE
-                    psString nameSource = NULL; // String with the concept name and ".SOURCE" added
-                    psStringAppend(&nameSource, "%s.SOURCE", name);
-                    psString source = psMetadataLookupStr(&mdok, cell->config, nameSource); // The source
-                    psFree(nameSource);
-                    if (mdok && strlen(source) && strcasecmp(source, "HEADER") == 0) {
-                        headerItem = psMetadataLookup(hdu->header, conceptItem->data.V);
-                    }
-                    // Leave the error handling to pmConceptsFromCamera, which should already have been called
-                }
-            }
-            if (! headerItem) {
-                psString keywords = psMetadataLookupStr(&mdok, transSpec, name); // The FITS keywords
-                if (mdok && strlen(keywords) > 0) {
-                    // In case there are multiple headers
-                    psList *keys = psStringSplit(keywords, " ,;", true); // List of keywords
-                    if (keys->n == 1) {
-                        // Only one key --- proceed as usual
-                        headerItem = psMetadataLookup(hdu->header, keywords);
-                    } else {
-                        psListIterator *keysIter = psListIteratorAlloc(keys, PS_LIST_HEAD, false); // Iterator
-                        psString key = NULL; // Item from iteration
-                        psList *values = psListAlloc(NULL); // List containing the values
-                        while ((key = psListGetAndIncrement(keysIter))) {
-                            psMetadataItem *value = psMetadataLookup(hdu->header, key);
-                            psListAdd(values, PS_LIST_TAIL, value);
-                        }
-                        psFree(keysIter);
-                        headerItem = psMetadataItemAlloc(name, PS_DATA_LIST, specItem->comment, values);
-                        psFree(values);
-                    }
-                    psFree(keys);
-                }
-            }
-
-            // This will also clean up the name
-            conceptParse(spec, headerItem, cameraFormat, target, fpa, chip, cell);
-        }
-        psFree(specsIter);
-        return true;
-    }
-    return false;
-}
-
-
-// XXX --- the below code has NOT been tested!
-bool pmConceptsReadFromDatabase(psMetadata *specs, // The concept specifications
-                                pmFPA *fpa, // The FPA
-                                pmChip *chip, // The chip
-                                pmCell *cell,  // The cell
-                                psDB *db, // The database handle
-                                psMetadata *target // Place into which to read the concepts
-                               )
-{
-    #ifdef OMIT_PSDB
-    return false;
-    #else
-
-    pmHDU *hdu = pmHDUGetLowest(fpa, chip, cell); // The HDU at the lowest level
-    if (!hdu) {
-        return false;
-    }
-    psMetadata *cameraFormat = hdu->format; // The camera format
-    bool mdok = true;                   // Status of MD lookup
-    psMetadata *dbSpec = psMetadataLookupMD(&mdok, cameraFormat, "DATABSE"); // The DATABASE spec
-    if (mdok && dbSpec) {
-        pmHDU *hdu = pmHDUGetLowest(fpa, chip, cell); // The HDU at the lowest level
-        psMetadata *cameraFormat = hdu->format; // The camera format
-        psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
-        psMetadataItem *specItem = NULL;    // Item from the specs metadata
-        while ((specItem = psMetadataGetAndIncrement(specsIter))) {
-            pmConceptSpec *spec = specItem->data.V; // The specification
-            psString name = specItem->name; // The concept name
-
-            psMetadata *dbLookup = psMetadataLookupMD(&mdok, dbSpec, name);
-            if (mdok && dbLookup) {
-                const char *tableName = psMetadataLookupStr(&mdok, dbLookup, "TABLE"); // Table name
-                // Names of the "where" columns
-                const char *givenCols = psMetadataLookupStr(&mdok, dbLookup, "GIVENDBCOL");
-                // Values of the "where" columns
-                const char *givenPS = psMetadataLookupStr(&mdok, dbLookup, "GIVENPS");
-
-                // Now, need to get the "given"s
-                if (strlen(givenCols) > 0 || strlen(givenPS) > 0) {
-                    psList *cols = psStringSplit(givenCols, ",;", true); // List of column names
-                    psList *values = psStringSplit(givenPS, ",;", true); // List of value names for the columns
-                    psMetadata *selection = psMetadataAlloc(); // The stuff to select in the DB
-                    if (cols->n != values->n) {
-                        psLogMsg(__func__, PS_LOG_WARN,
-                                 "The GIVENDBCOL and GIVENPS entries for %s do not have "
-                                 "the same number of entries --- ignored.\n", name);
-                    } else {
-                        // Iterators for the lists
-                        psListIterator *colsIter = psListIteratorAlloc(cols, PS_LIST_HEAD, false);
-                        psListIterator *valuesIter = psListIteratorAlloc(values, PS_LIST_HEAD, false);
-                        char *column = NULL;    // Name of the column
-                        while ((column = psListGetAndIncrement(colsIter))) {
-                            char *dependName = psListGetAndIncrement(valuesIter); // Name for the value
-                            if (!strlen(column) || !strlen(name)) {
-                                psLogMsg(__func__, PS_LOG_WARN, "One of the columns or value names for %s is "
-                                         " empty --- ignored.\n", name);
-                            } else {
-                                // Search for the value name
-                                psMetadataItem *item = NULL; // The value
-                                if (!item && cell) {
-                                    item = psMetadataLookup(cell->concepts, dependName);
-                                }
-                                if (!item && chip) {
-                                    item = psMetadataLookup(chip->concepts, dependName);
-                                }
-                                if (!item && fpa) {
-                                    item = psMetadataLookup(fpa->concepts, dependName);
-                                }
-                                if (! item) {
-                                    psLogMsg(__func__, PS_LOG_ERROR, "Unable to find the value name %s for DB"
-                                             " lookup on %s --- ignored.\n", dependName, name);
-                                } else {
-                                    // We need to create a new psMetadataItem.  I don't think we can't
-                                    // simply hack the existing one, since that could conceivably cause
-                                    // memory leaks
-                                    psMetadataItem *newItem = psMetadataItemAlloc(name, item->type,
-                                                              item->comment,
-                                                              item->data.V);
-                                    psMetadataAddItem(selection, newItem, PS_LIST_TAIL, PS_META_REPLACE);
-                                    psFree(newItem);
-                                }
-                            }
-                            psFree(dependName);
-                            psFree(column);
-                        } // Iterating through the columns
-                        psFree(colsIter);
-                        psFree(valuesIter);
-
-                        psArray *dbResult = psDBSelectRows(db, tableName, selection, 2); // Lookup result
-                        // Note that we use limit=2 in order to test if there are multiple rows returned
-
-                        psMetadataItem *conceptItem = NULL; // The final result of the DB lookup
-                        if (dbResult->n == 0) {
-                            psLogMsg(__func__, PS_LOG_WARN,
-                                     "Unable to find any rows in DB for %s --- ignored\n", name);
-                        } else {
-                            if (dbResult-> n > 1) {
-                                psLogMsg(__func__, PS_LOG_WARN,
-                                         "Multiple rows returned in DB lookup for %s --- "
-                                         " using the first one only.\n", name);
-                            }
-                            conceptItem = (psMetadataItem*)dbResult->data[0];
-                        }
-
-                        // Now we have the result
-                        conceptParse(spec, conceptItem, cameraFormat, target, fpa, chip, cell);
-
-                    }
-                    psFree(cols);
-                    psFree(values);
-                }
-            } // Doing the "given"s.
-
-        } // Iterating through the concept specifications
-        psFree(specsIter);
-
-        return true;
-    }
-    return false;
-    #endif
-}
-
-
-
-
-#ifdef OLD
-
-psMetadataItem *pmConceptReadFromCamera(pmCell *cell, // The cell
-                                        const char *concept // Name of concept
-                                       )
-{
-    if (cell) {
-        psMetadata *camera = cell->config; // Camera configuration
-        psMetadataItem *item = psMetadataLookup(camera, concept);
-        return item;
-    }
-    return NULL;
-}
-
-psMetadataItem *pmConceptReadFromHeader(pmFPA *fpa, // The FPA that contains the chip
-                                        pmChip *chip, // The chip that contains the cell
-                                        pmCell *cell, // The cell
-                                        const char *concept // Name of concept
-                                       )
-{
-    bool mdStatus = true;               // Status of MD lookup
-    psMetadata *translation = psMetadataLookupMD(&mdStatus, fpa->camera, "TRANSLATION"); // FITS translation
-    if (! mdStatus) {
-        psError(PS_ERR_IO, false, "Unable to find TRANSLATION in camera configuration.\n");
-        return NULL;
-    }
-
-    // Look for how to translate the concept into a FITS header name
-    const char *keyword = psMetadataLookupStr(&mdStatus, translation, concept);
-    if (mdStatus && strlen(keyword) > 0) {
-        // We have a FITS header to look up --- search each level
-        if (cell && cell->hdu) {
-            psMetadataItem *cellItem = psMetadataLookup(cell->hdu->header, keyword);
-            if (cellItem) {
-                // XXX: Need to clean up before returning
-                return cellItem;
-            }
-        }
-
-        if (chip && chip->hdu) {
-            psMetadataItem *chipItem = psMetadataLookup(chip->hdu->header, keyword);
-            if (chipItem) {
-                // XXX: Need to clean up before returning
-                return chipItem;
-            }
-        }
-
-        if (fpa->hdu) {
-            psMetadataItem *fpaItem = psMetadataLookup(fpa->hdu->header, keyword);
-            if (fpaItem) {
-                // XXX: Need to clean up before returning
-                return fpaItem;
-            }
-        }
-    }
-
-    // No header value
-    return NULL;
-}
-
-
-// Look for a default
-psMetadataItem *pmConceptReadFromDefault(pmFPA *fpa, // The FPA that contains the chip
-        pmChip *chip, // The chip that contains the cell
-        pmCell *cell, // The cell
-        const char *concept // Name of concept
-                                        )
-{
-    bool mdOK = true;                   // Status of MD lookup
-    psMetadata *defaults = psMetadataLookupMD(&mdOK, fpa->camera, "DEFAULTS");
-    if (! mdOK) {
-        psError(PS_ERR_IO, false, "Unable to find DEFAULTS in camera configuration.\n");
-        return NULL;
-    }
-
-    psMetadataItem *defItem = psMetadataLookup(defaults, concept);
-    if (defItem) {
-        if (defItem->type == PS_DATA_METADATA) {
-            // A dependent default
-            psTrace(__func__, 7, "Evaluating dependent default....\n");
-            psMetadata *dependents = defItem->data.V; // The list of dependents
-            // Find out what it depends on
-            psString dependName = psStringCopy(concept);
-            psStringAppend(&dependName, ".DEPEND");
-            psString dependsOn = psMetadataLookupStr(&mdOK, defaults, dependName);
-            if (! mdOK) {
-                psError(PS_ERR_IO, false, "Unable to find %s in camera configuration for dependent default"
-                        " --- ignored\n", dependName);
-                // XXX: Need to clean up before returning
-                return NULL;
-            }
-            psFree(dependName);
-            // Find the value of the dependent concept
-            psMetadataItem *depItem = pmConceptReadFromHeader(fpa, chip, cell, dependsOn);
-            if (! depItem) {
-                psError(PS_ERR_IO, true, "Unable to find value for %s (required for %s)\n", dependsOn,
-                        concept);
-                return NULL;
-            }
-            if (depItem->type != PS_DATA_STRING) {
-                psError(PS_ERR_IO, true, "Value of %s is not of type string, as required for dependency"
-                        " --- ignored.\n", dependsOn);
-            }
-
-            defItem = psMetadataLookup(dependents, depItem->data.V);    // This is now what we were after
-        }
-    }
-
-    // XXX: Need to clean up before returning
-    return defItem;                     // defItem is either NULL or points to what was desired
-}
-
-
-// Look for a database lookup
-// XXX: Not tested
-psMetadataItem *pmConceptReadFromDB(pmFPA *fpa, // The FPA that contains the chip
-                                    pmChip *chip, // The chip that contains the cell
-                                    pmCell *cell, // The cell
-                                    psDB *db, // DB handle
-                                    const char *concept // Name of concept
-                                   )
-{
-    if (! db) {
-        // No database initialised
-        return NULL;
-    }
-
-    bool mdStatus = true;               // Status of MD lookup
-    psMetadata *database = psMetadataLookupMD(&mdStatus, fpa->camera, "DATABASE");
-    if (! mdStatus) {
-        // No error, because not everyone needs to use the DB
-        return NULL;
-    }
-
-    psMetadata *dbLookup = psMetadataLookupMD(&mdStatus, database, concept);
-    if (dbLookup) {
-        const char *tableName = psMetadataLookupStr(&mdStatus, dbLookup, "TABLE"); // Name of the table
-        // const char *colName = psMetadataLookupStr(&mdStatus, dbLookup, "COLUMN"); // Name of the column
-        const char *givenCols = psMetadataLookupStr(&mdStatus, dbLookup, "GIVENDBCOL"); // Name of "where"
-        // columns
-        const char *givenPS = psMetadataLookupStr(&mdStatus, dbLookup, "GIVENPS"); // Values for "where"
-        // columns
-
-        // Now, need to get the "given"s
-        if (strlen(givenCols) || strlen(givenPS)) {
-            psList *cols = psStringSplit(givenCols, ",;", true); // List of column names
-            psList *values = psStringSplit(givenPS, ",;", true); // List of value names for the columns
-            psMetadata *selection = psMetadataAlloc(); // The stuff to select in the DB
-            if (cols->n != values->n) {
-                psLogMsg(__func__, PS_LOG_WARN, "The GIVENDBCOL and GIVENPS entries for %s do not have "
-                         "the same number of entries --- ignored.\n", concept);
-            } else {
-                // Iterators for the lists
-                psListIterator *colsIter = psListIteratorAlloc(cols, PS_LIST_HEAD, false);
-                psListIterator *valuesIter = psListIteratorAlloc(values, PS_LIST_HEAD, false);
-                char *column = NULL;    // Name of the column
-                while ((column = psListGetAndIncrement(colsIter))) {
-                    char *name = psListGetAndIncrement(valuesIter); // Name for the value
-                    if (!strlen(column) || !strlen(name)) {
-                        psLogMsg(__func__, PS_LOG_WARN, "One of the columns or value names for %s is "
-                                 " empty --- ignored.\n", concept);
-                    } else {
-                        // Search for the value name
-                        psMetadataItem *item = pmConceptReadFromHeader(fpa, chip, cell, name);
-                        if (! item) {
-                            item = pmConceptReadFromDefault(fpa, chip, cell, name);
-                        }
-                        if (! item) {
-                            psLogMsg(__func__, PS_LOG_ERROR, "Unable to find the value name %s for DB "
-                                     " lookup on %s --- ignored.\n", name, concept);
-                        } else {
-                            // We need to create a new psMetadataItem.  I don't think we can't simply hack
-                            // the existing one, since that could conceivably cause memory leaks
-                            psMetadataItem *newItem = psMetadataItemAlloc(concept, item->type,
-                                                      item->comment, item->data.V);
-                            psMetadataAddItem(selection, newItem, PS_LIST_TAIL, PS_META_REPLACE);
-                            psFree(newItem);
-                        }
-                    }
-                    psFree(name);
-                    psFree(column);
-                } // Iterating through the columns
-                psFree(colsIter);
-                psFree(valuesIter);
-
-                psArray *dbResult = psDBSelectRows(db, tableName, selection, 2); // Lookup result
-                // Note that we use limit=2 in order to test if there are multiple rows returned
-
-                psMetadataItem *result = NULL; // The final result of the DB lookup
-                if (dbResult->n == 0) {
-                    psLogMsg(__func__, PS_LOG_WARN, "Unable to find any rows in DB for %s --- ignored\n",
-                             concept);
-                } else {
-                    if (dbResult-> n > 1) {
-                        psLogMsg(__func__, PS_LOG_WARN, "Multiple rows returned in DB lookup for %s --- "
-                                 " using the first one only.\n", concept);
-                    }
-                    result = (psMetadataItem*)dbResult->data[0];
-                }
-                // XXX: Need to clean up before returning
-                return result;
-            }
-            psFree(cols);
-            psFree(values);
-        }
-    } // Doing the "given"s.
-
-    psAbort(__func__, "Shouldn't ever get here.\n");
-    return NULL;
-}
-
-
-// Concept lookup
-psMetadataItem *pmConceptRead(pmFPA *fpa, // The FPA
-                              pmChip *chip,// The chip
-                              pmCell *cell, // The cell
-                              psDB *db, // DB handle
-                              const char *name // Concept name
-                             )
-{
-    // Try headers, database, defaults in order
-    psMetadataItem *item = pmConceptReadFromCamera(cell, name);
-    if (! item) {
-        item = pmConceptReadFromHeader(fpa, chip, cell, name);
-    }
-    if (! item) {
-        item = pmConceptReadFromDB(fpa, chip, cell, db, name);
-    }
-    if (! item) {
-        item = pmConceptReadFromDefault(fpa, chip, cell, name);
-    }
-    return item; // item is either NULL, or points to what was desired
-}
-
-
-#endif
Index: unk/psModules/src/astrom/pmConceptsRead.h
===================================================================
--- /trunk/psModules/src/astrom/pmConceptsRead.h	(revision 7018)
+++ 	(revision )
@@ -1,94 +1,0 @@
-#ifndef PM_CONCEPTS_READ_H
-#define PM_CONCEPTS_READ_H
-
-#include "pslib.h"
-#include "pmFPA.h"
-
-bool pmConceptsReadFromCamera(psMetadata *specs, // The concept specifications
-                              pmCell *cell,  // The cell
-                              psMetadata *target // Place into which to read the concepts
-                             );
-bool pmConceptsReadFromDefaults(psMetadata *specs, // The concept specifications
-                                pmFPA *fpa, // The FPA
-                                pmChip *chip, // The chip
-                                pmCell *cell, // The cell
-                                psMetadata *target // Place into which to read the concepts
-                               );
-bool pmConceptsReadFromHeader(psMetadata *specs, // The concept specifications
-                              pmFPA *fpa, // The FPA
-                              pmChip *chip, // The chip
-                              pmCell *cell,  // The cell
-                              psMetadata *target // Place into which to read the concepts
-                             );
-bool pmConceptsReadFromDatabase(psMetadata *specs, // The concept specifications
-                                pmFPA *fpa, // The FPA
-                                pmChip *chip, // The chip
-                                pmCell *cell,  // The cell
-                                psDB *db, // The database handle
-                                psMetadata *target // Place into which to read the concepts
-                               );
-
-
-
-
-#ifdef OLD
-psMetadataItem *pmConceptReadFromCamera(pmCell *cell, // The cell
-                                        const char *concept // Name of concept
-                                       );
-
-psMetadataItem *pmConceptReadFromHeader(pmFPA *fpa, // The FPA that contains the chip
-                                        pmChip *chip, // The chip that contains the cell
-                                        pmCell *cell, // The cell
-                                        const char *concept // Name of concept
-                                       );
-
-psMetadataItem *pmConceptReadFromDefault(pmFPA *fpa, // The FPA that contains the chip
-        pmChip *chip, // The chip that contains the cell
-        pmCell *cell, // The cell
-        const char *concept // Name of concept
-                                        );
-
-psMetadataItem *pmConceptReadFromDB(pmFPA *fpa, // The FPA that contains the chip
-                                    pmChip *chip, // The chip that contains the cell
-                                    pmCell *cell, // The cell
-                                    psDB *db, // DB handle
-                                    const char *concept // Name of concept
-                                   );
-
-psMetadataItem *pmConceptRead(pmFPA *fpa, // The FPA
-                              pmChip *chip,// The chip
-                              pmCell *cell, // The cell
-                              psDB *db, // DB handle
-                              const char *concept // Concept name
-                             );
-
-float pmConceptReadF32(pmFPA *fpa,        // The FPA
-                       pmChip *chip,      // The chip
-                       pmCell *cell,      // The cell
-                       psDB *db,          // DB handle
-                       const char *name // Name of the concept
-                      );
-
-double pmConceptReadF64(pmFPA *fpa,   // The FPA
-                        pmChip *chip, // The chip
-                        pmCell *cell, // The cell
-                        psDB *db,     // DB handle
-                        const char *name // Name of the concept
-                       );
-
-int pmConceptReadS32(pmFPA *fpa,   // The FPA
-                     pmChip *chip, // The chip
-                     pmCell *cell, // The cell
-                     psDB *db,     // DB handle
-                     const char *name // Name of the concept
-                    );
-
-psString pmConceptReadString(pmFPA *fpa, // The FPA
-                             pmChip *chip, // The chip
-                             pmCell *cell, // The cell
-                             psDB *db,  // DB handle
-                             const char *name // Name of the concept
-                            );
-#endif
-
-#endif
Index: unk/psModules/src/astrom/pmConceptsStandard.c
===================================================================
--- /trunk/psModules/src/astrom/pmConceptsStandard.c	(revision 7018)
+++ 	(revision )
@@ -1,629 +1,0 @@
-#include <stdio.h>
-#include <assert.h>
-
-#include "pslib.h"
-
-#include "pmFPA.h"
-#include "pmConceptsRead.h"
-#include "pmConceptsWrite.h"
-#include "pmConceptsStandard.h"
-
-
-#define COMPARE_REGIONS(a,b) (((a)->x0 == (b)->x0 && \
-                               (a)->x1 == (b)->x1 && \
-                               (a)->y0 == (b)->y0 && \
-                               (a)->y1 == (b)->y1) ? true : false)
-
-#define TYPE_CASE(assign, item, TYPE) \
-case PS_TYPE_##TYPE: \
-assign = item->data.TYPE; \
-break;
-
-
-
-static double defaultCoordScaling(psMetadataItem *pattern)
-{
-    if (strcmp(pattern->name, "FPA.RA") == 0) {
-        psLogMsg(__func__, PS_LOG_WARN, "Assuming format for %s is HOURS.\n", pattern->name);
-        return M_PI / 12.0;
-    }
-    if (strcmp(pattern->name, "FPA.DEC") == 0) {
-        psLogMsg(__func__, PS_LOG_WARN, "Assuming format for %s is DEGREES.\n", pattern->name);
-        return M_PI / 180.0;
-    }
-    psAbort(__func__, "Should never ever get here.\n");
-    return NAN;
-}
-
-
-// FPA.RA and FPA.DEC
-psMetadataItem *pmConceptParse_FPA_Coords(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
-{
-    assert(concept);
-    assert(pattern);
-    assert(cameraFormat);
-
-    double coords = NAN;                // The coordinates
-    switch (concept->type) {
-    case PS_TYPE_F32:
-        coords = concept->data.F32;
-        break;
-    case PS_TYPE_F64:
-        coords = concept->data.F64;
-        break;
-    case PS_DATA_STRING:
-        // Sexagesimal format
-        {
-            int big, medium;
-            float small;
-            // XXX: Upgrade path is to allow dd:mm.mmm
-            if (sscanf(concept->data.V, "%d:%d:%f", &big, &medium, &small) != 3 &&
-                    sscanf(concept->data.V, "%d %d %f", &big, &medium, &small) != 3)
-            {
-                psError(PS_ERR_IO, true, "Cannot interpret FPA.RA: %s\n", concept->data.V);
-                break;
-            }
-            coords = abs(big) + (float)medium/60.0 + small/3600.0;
-            if (big < 0)
-            {
-                coords *= -1.0;
-            }
-        }
-        break;
-    default:
-        psError(PS_ERR_IO, true, "%s concept is of an unexpected type: %x\n", pattern->name, concept->type);
-        return NULL;
-    }
-
-    // How to interpret the coordinates
-    bool mdok = true;           // Status of MD lookup
-    psMetadata *formats = psMetadataLookupMD(&mdok, cameraFormat, "FORMATS");
-    if (mdok && formats) {
-        psString format = psMetadataLookupStr(&mdok, formats, pattern->name);
-        if (mdok && strlen(format) > 0) {
-            if (strcasecmp(format, "HOURS") == 0) {
-                coords *= M_PI / 12.0;
-            } else if (strcasecmp(format, "DEGREES") == 0) {
-                coords *= M_PI / 180.0;
-            } else if (strcasecmp(format, "RADIANS") == 0) {
-                // No action required
-            } else {
-                coords *= defaultCoordScaling(pattern);
-            }
-        } else {
-            coords *= defaultCoordScaling(pattern);
-        }
-    } else {
-        coords *= defaultCoordScaling(pattern);
-    }
-
-    return psMetadataItemAllocF64(pattern->name, pattern->comment, coords);
-}
-
-// FPA.RA and FPA.DEC
-psMetadataItem *pmConceptFormat_FPA_Coords(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat)
-{
-    assert(concept);
-    assert(pattern);
-    assert(cameraFormat);
-
-    double coords = concept->data.F64;  // The coordinates
-
-    // How to interpret the coordinates
-    bool mdok = true;                   // Status of MD lookup
-    psMetadata *formats = psMetadataLookupMD(&mdok, cameraFormat, "FORMATS");
-    if (mdok && formats) {
-        psString format = psMetadataLookupStr(&mdok, formats, pattern->name);
-        if (mdok && strlen(format) > 0) {
-            if (strcasecmp(format, "HOURS") == 0) {
-                coords /= M_PI / 12.0;
-            } else if (strcasecmp(format, "DEGREES") == 0) {
-                coords /= M_PI / 180.0;
-            } else if (strcasecmp(format, "RADIANS") == 0) {
-                // No action required
-            } else {
-                coords /= defaultCoordScaling(pattern);
-            }
-        } else {
-            coords /= defaultCoordScaling(pattern);
-        }
-    } else {
-        coords /= defaultCoordScaling(pattern);
-    }
-
-    // We choose to write sexagesimal format
-    int big, medium;
-    float small;
-    big = (int)coords;
-    medium = (int)(60.0*(coords - (double)big));
-    small = 3600.0*(coords - (double)big - 60.0 * (double)medium);
-    psString coordString = NULL;        // String with the coordinates in sexagesimal format
-    psStringAppend(&coordString, "%d:%d:%.2f", big, medium, small);
-    psMetadataItem *coordItem = psMetadataItemAllocStr(pattern->name, pattern->comment, coordString);
-    psFree(coordString);
-
-    return coordItem;
-}
-
-
-psMetadataItem *pmConceptParse_CELL_TRIMSEC(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
-{
-    assert(concept);
-    assert(cell);
-    assert(pattern);
-
-    psRegion *trimsec = psAlloc(sizeof(psRegion)); // Make space for a psRegion (usually passed by value)
-
-    if (concept->type != PS_DATA_STRING) {
-        psError(PS_ERR_IO, true, "CELL.TRIMSEC after read is not of type STR (%x)\n", concept->type);
-        *trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
-    } else {
-        *trimsec = psRegionFromString(concept->data.V);
-    }
-
-    psMetadataItem *item = psMetadataItemAllocPtr(pattern->name, PS_DATA_REGION, pattern->comment, trimsec);
-    psFree(trimsec);
-    return item;
-}
-
-psMetadataItem *pmConceptParse_CELL_BIASSEC(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
-{
-    assert(concept);
-    assert(cell);
-    assert(pattern);
-
-    psList *biassecs = psListAlloc(NULL); // List of bias sections
-
-    switch (concept->type) {
-    case PS_DATA_STRING: {
-            psList *regions = psStringSplit(concept->data.V, " ;", true); // List of regions
-            psListIterator *regionsIter = psListIteratorAlloc(regions, PS_LIST_HEAD, false); // Iterator
-            psString regionString = NULL; // Region string from iteration
-            while ((regionString = psListGetAndIncrement(regionsIter))) {
-                psRegion *region = psAlloc(sizeof(psRegion)); // The region
-                *region = psRegionFromString(regionString);
-                psListAdd(biassecs, PS_LIST_TAIL, region);
-                psFree(region);           // Drop reference
-            }
-            psFree(regionsIter);
-            psFree(regions);
-            break;
-        }
-    case PS_DATA_LIST: {
-            psList *regions = concept->data.V; // The list of regions
-            psListIterator *regionsIter = psListIteratorAlloc(regions, PS_LIST_HEAD, false); // Iterator
-            psMetadataItem *regionItem = NULL; // Item from list iteration
-            while ((regionItem = psListGetAndIncrement(regionsIter))) {
-                if (regionItem->type != PS_DATA_STRING) {
-                    psLogMsg(__func__, PS_LOG_WARN, "CELL.BIASSEC member is not of type STR --- ignored.\n");
-                    continue;
-                }
-                psRegion *region = psAlloc(sizeof(psRegion)); // The region
-                *region = psRegionFromString(regionItem->data.V);
-                psListAdd(biassecs, PS_LIST_TAIL, region);
-                psFree(region);           // Drop reference
-            }
-            psFree(regionsIter);
-            break;
-        }
-    default:
-        psError(PS_ERR_IO, true, "CELL.BIASSEC after read is not of type STRING or LIST --- assuming "
-                "blank.\n");
-    }
-
-    psMetadataItem *item = psMetadataItemAllocPtr(pattern->name, PS_DATA_LIST, pattern->comment, biassecs);
-    psFree(biassecs);               // Drop reference
-    return item;
-}
-
-// CELL.XBIN and CELL.YBIN
-psMetadataItem *pmConceptParse_CELL_Binning(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
-{
-    assert(concept);
-    assert(pattern);
-
-    int binning = 1;                    // Binning factor in x
-    switch (concept->type) {
-    case PS_DATA_STRING: {
-            psString binString = concept->data.V; // The string containing the binning
-            if ((strcmp(pattern->name, "CELL.XBIN") == 0 && sscanf(binString, "%d %*d", &binning) != 1 &&
-                    sscanf(binString, "%d,%*d", &binning) != 1) ||
-                    (strcmp(pattern->name, "CELL.YBIN") == 0 && sscanf(binString, "%*d %d", &binning) != 1 &&
-                     sscanf(binString, "%*d,%d", &binning) != 1)) {
-                psError(PS_ERR_IO, true, "Unable to parse string to get %s: %s\n", pattern->name, binString);
-            }
-        }
-        TYPE_CASE(binning, concept, U8);
-        TYPE_CASE(binning, concept, U16);
-        TYPE_CASE(binning, concept, U32);
-        TYPE_CASE(binning, concept, S8);
-        TYPE_CASE(binning, concept, S16);
-        TYPE_CASE(binning, concept, S32);
-    default:
-        psError(PS_ERR_IO, true, "Note sure how to parse %s of type %x --- assuming 1.\n", pattern->name,
-                concept->type);
-    }
-
-    return psMetadataItemAllocS32(pattern->name, pattern->comment, binning);
-}
-
-
-psMetadataItem *pmConceptParse_CELL_TIMESYS(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
-{
-    assert(concept);
-    assert(pattern);
-
-    psTimeType timeSys = PS_TIME_UTC;   // The time system
-    psString sys = concept->data.V;     // The time system string
-    if (concept->type != PS_DATA_STRING || strlen(sys) <= 0) {
-        psError(PS_ERR_IO, true, "Can't interpret CELL.TIMESYS --- assuming UTC.\n");
-    } else if (strcasecmp(sys, "TAI") == 0) {
-        timeSys = PS_TIME_TAI;
-    } else if (strcasecmp(sys, "UTC") == 0) {
-        timeSys = PS_TIME_UTC;
-    } else if (strcasecmp(sys, "UT1") == 0) {
-        timeSys = PS_TIME_UT1;
-    } else if (strcasecmp(sys, "TT") == 0) {
-        timeSys = PS_TIME_TT;
-    } else {
-        psError(PS_ERR_IO, true, "Can't interpret CELL.TIMESYS --- assuming UTC.\n");
-    }
-
-    return psMetadataItemAllocS32(pattern->name, pattern->comment, timeSys);
-}
-
-
-psMetadataItem *pmConceptParse_CELL_TIME(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
-{
-    assert(concept);
-    assert(cameraFormat);
-    assert(cell);
-
-    // Need CELL.TIMESYS first
-    bool mdok = true;                  // Result of MD lookup
-    psTimeType timeSys = psMetadataLookupS32(&mdok, cell->concepts, "CELL.TIMESYS"); // The time system
-    if (!mdok) {
-        psLogMsg(__func__, PS_LOG_WARN, "Unable to find CELL.TIMESYS in concepts --- assuming UTC.\n");
-        timeSys = PS_TIME_UTC;
-    }
-
-    // Get format
-    psMetadata *formats = psMetadataLookupMD(&mdok, cameraFormat, "FORMATS");
-    if (!mdok || !formats) {
-        psError(PS_ERR_IO, false, "Unable to find FORMATS in camera configuration.\n");
-        return NULL;
-    }
-
-    psString timeFormat = psMetadataLookupStr(&mdok, formats, "CELL.TIME");
-    if (!mdok || strlen(timeFormat) == 0) {
-        psError(PS_ERR_IO, false, "Unable to find CELL.TIME in FORMATS.\n");
-        return NULL;
-    }
-
-    psTime *time = NULL;                // The time
-    switch (concept->type) {
-    case PS_DATA_LIST: {
-            // The date and time are stored separately
-            // Assume the date is first and the time second
-            psList *dateTime = concept->data.V; // The list containing items for date and time
-            psMetadataItem *dateItem = psListGet(dateTime, PS_LIST_HEAD); // Item containing the date
-            psMetadataItem *timeItem = psListGet(dateTime, PS_LIST_HEAD + 1); // Item containing the time
-            if (dateItem->type != PS_DATA_STRING) {
-                psError(PS_ERR_IO, true, "Date is not of type STR.\n");
-                return NULL;
-            }
-            psString dateString = dateItem->data.V; // The string with the date
-            int day = 0, month = 0, year = 0;
-            if (sscanf(dateString, "%d-%d-%d", &day, &month, &year) != 3 &&
-                    sscanf(dateString, "%d/%d/%d", &day, &month, &year) != 3) {
-                psError(PS_ERR_IO, true, "Unable to read date: %s\n", dateString);
-                return NULL;
-            }
-            if (strstr(timeFormat, "BACKWARDS")) {
-                int temp = day;
-                day = year;
-                year = temp;
-            }
-            if (strstr(timeFormat, "PRE2000") || year < 2000) {
-                year += 2000;
-            }
-
-            psString timeString = NULL; // The string with the time
-            if (timeItem->type == PS_DATA_STRING) {
-                timeString = timeItem->data.V;
-            } else {
-                // Assume that time is specified in Second of Day (!)
-                double seconds = NAN;
-                switch (timeItem->type) {
-                    TYPE_CASE(seconds, timeItem, U8);
-                    TYPE_CASE(seconds, timeItem, U16);
-                    TYPE_CASE(seconds, timeItem, U32);
-                    TYPE_CASE(seconds, timeItem, S8);
-                    TYPE_CASE(seconds, timeItem, S16);
-                    TYPE_CASE(seconds, timeItem, S32);
-                    TYPE_CASE(seconds, timeItem, F32);
-                    TYPE_CASE(seconds, timeItem, F64);
-                default:
-                    psError(PS_ERR_IO, true, "Time is not of an expected type: %x\n", timeItem->type);
-                    return NULL;
-                }
-                // Now print to timeString as "hh:mm:ss.ss"
-                int hours = seconds / 3600;
-                seconds -= (double)hours * 3600.0;
-                int minutes = seconds / 60;
-                seconds -= (double)minutes * 60.0;
-                psStringAppend(&timeString, "%02d:%02d:%02f", hours, minutes, seconds);
-            }
-            psString dateTimeString = NULL;
-            psStringAppend(&dateTimeString, "%sT%s", dateString, timeString);
-            time = psTimeFromISO(dateTimeString, timeSys);
-            break;
-        }
-    case PS_DATA_STRING: {
-            psString timeString = concept->data.V;   // String with the time
-            if (strcasecmp(timeFormat, "ISO") == 0) {
-                // timeString contains an ISO time
-                time = psTimeFromISO(timeString, timeSys);
-            } else if (strcasecmp(timeFormat, "JD") == 0) {
-                double timeValue = strtod (timeString, NULL);
-                time = psTimeFromJD(timeValue);
-            } else if (strcasecmp(timeFormat, "MJD") == 0) {
-                double timeValue = strtod (timeString, NULL);
-                time = psTimeFromMJD(timeValue);
-            } else {
-                psError(PS_ERR_IO, true, "Not sure how to parse CELL.TIME (%s) --- trying "
-                        "ISO\n", timeString);
-                time = psTimeFromISO(timeString, timeSys);
-            } // Interpreting the time string
-            break;
-        }
-    case PS_TYPE_F32: {
-            double timeValue = (double)concept->data.F32;
-            if (strcasecmp(timeFormat, "JD") == 0) {
-                time = psTimeFromJD(timeValue);
-            } else if (strcasecmp(timeFormat, "MJD") == 0) {
-                time = psTimeFromMJD(timeValue);
-            } else {
-                psError(PS_ERR_IO, true, "Not sure how to parse CELL.TIME (%f) --- trying "
-                        "JD\n", timeValue);
-                time = psTimeFromJD(timeValue);
-            }
-            break;
-        }
-    case PS_TYPE_F64: {
-            double timeValue = (double)concept->data.F64;
-            if (strcasecmp(timeFormat, "JD") == 0) {
-                time = psTimeFromJD(timeValue);
-            } else if (strcasecmp(timeFormat, "MJD") == 0) {
-                time = psTimeFromMJD(timeValue);
-            } else {
-                psError(PS_ERR_IO, true, "Not sure how to parse CELL.TIME (%f) --- trying "
-                        "JD\n", timeValue);
-                time = psTimeFromJD(timeValue);
-            }
-            break;
-        }
-    default:
-        psError(PS_ERR_IO, true, "Unable to parse CELL.TIME.\n");
-        return NULL;
-    }
-
-    psMetadataItem *item = psMetadataItemAllocPtr(pattern->name, PS_DATA_TIME, pattern->comment, time);
-    psFree(time);
-    return item;
-}
-
-// Correct a position --- in case the user wants FORTRAN indexing (like the FITS standard...)
-static int fortranCorr(psMetadata *cameraFormat, // The camera format description
-                       const char *name // Name of concept to check for FORTRAN indexing
-                      )
-{
-    bool mdok = false;                  // Result of MD lookup
-    psMetadata *formats = psMetadataLookupMD(&mdok, cameraFormat, "FORMATS");
-    if (mdok && formats) {
-        psString format = psMetadataLookupStr(&mdok, formats, name);
-        if (mdok && strlen(format) > 0 && strcasecmp(format, "FORTRAN") == 0) {
-            return 1;
-        }
-    }
-    return 0;
-}
-
-psMetadataItem *pmConceptParse_CELL_Positions(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
-{
-    assert(concept);
-    assert(cameraFormat);
-
-    int offset = 0;                     // Offset of cell (0,0) corner from the chip (0,0) corner
-
-    switch (concept->type) {
-        TYPE_CASE(offset, concept, U8);
-        TYPE_CASE(offset, concept, U16);
-        TYPE_CASE(offset, concept, U32);
-        TYPE_CASE(offset, concept, S8);
-        TYPE_CASE(offset, concept, S16);
-        TYPE_CASE(offset, concept, S32);
-    default:
-        psError(PS_ERR_IO, true, "Concept %s is not of integer type, as expected.\n", pattern->name);
-        return NULL;
-    }
-    offset -= fortranCorr(cameraFormat, pattern->name);
-    return psMetadataItemAllocS32(pattern->name, pattern->comment, offset);
-}
-
-
-psMetadataItem *pmConceptFormat_CELL_TRIMSEC(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
-{
-    assert(concept);
-
-    psRegion *trimsec = concept->data.V; // The trimsec region
-    psString trimsecString = psRegionToString(*trimsec);
-    psMetadataItem *formatted = psMetadataItemAllocStr(concept->name, concept->comment,
-                                trimsecString);
-    psFree(trimsecString);
-    return formatted;
-}
-
-psMetadataItem *pmConceptFormat_CELL_BIASSEC(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
-{
-    // Return a metadata item containing a list of metadata items of region strings
-    psList *biassecs = concept->data.V; // The biassecs region list
-    psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false); // Iterator
-    psRegion *region = NULL;            // Region from iteration
-    psList *new = psListAlloc(NULL);    // New list containing metadatas
-    while ((region = psListGetAndIncrement(biassecsIter))) {
-        psString regionString = psRegionToString(*region); // The string region "[x0:x1,y0:y1]"
-        psMetadataItem *item = psMetadataItemAllocStr(concept->name, concept->comment, regionString);
-        psFree(regionString);           // Drop reference
-        psListAdd(new, PS_LIST_TAIL, item);
-        psFree(item);                   // Drop reference
-    }
-    psFree(biassecsIter);
-    psMetadataItem *formatted = psMetadataItemAllocPtr(concept->name, PS_DATA_LIST, concept->comment, new);
-    psFree(new);                        // Drop reference
-    return formatted;
-}
-
-// This function actually does both CELL.XBIN and CELL.YBIN if CELL.XBIN and CELL.YBIN are specified by the
-// same header.
-psMetadataItem *pmConceptFormat_CELL_XBIN(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
-{
-    assert(concept);
-
-    psMetadata *translation = psMetadataLookupMD(NULL, cameraFormat, "TRANSLATION");
-    bool xBinOK = true, yBinOK = true;  // Status of MD lookups
-    psString xKeyword = psMetadataLookupStr(&xBinOK, translation, "CELL.XBIN");
-    psString yKeyword = psMetadataLookupStr(&yBinOK, translation, "CELL.YBIN");
-    if (xBinOK && yBinOK && strlen(xKeyword) > 0 && strlen(yKeyword) > 0 &&
-            strcasecmp(xKeyword, yKeyword) == 0) {
-        psMetadataItem *yBinItem = psMetadataLookup(cell->concepts, "CELL.YBIN"); // Binning factor in y
-        psString binString = psStringCopy("");
-        psStringAppend(&binString, "%d %d", concept->data.S32, yBinItem->data.S32);
-        psMetadataItem *binItem = psMetadataItemAllocStr(concept->name, concept->comment, binString);
-        psFree(binString);
-        return binItem;
-    }
-
-    // Otherwise, there's no formatting required
-    return psMemIncrRefCounter(concept);
-}
-
-// Only need to format if both if CELL.XBIN and CELL.YBIN are not specified by the same header.
-psMetadataItem *pmConceptFormat_CELL_YBIN(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
-{
-    assert(concept);
-
-    psMetadata *translation = psMetadataLookupMD(NULL, cameraFormat, "TRANSLATION");
-    bool xBinOK = true, yBinOK = true;  // Status of MD lookups
-    psString xKeyword = psMetadataLookupStr(&xBinOK, translation, "CELL.XBIN");
-    psString yKeyword = psMetadataLookupStr(&yBinOK, translation, "CELL.YBIN");
-    if (xBinOK && yBinOK && strlen(xKeyword) > 0 && strlen(yKeyword) > 0 &&
-            strcasecmp(xKeyword, yKeyword) == 0) {
-        // Censor this --- it's already done (though no harm if it's done twice
-        return NULL;
-    }
-
-    // No formatting required
-    return psMemIncrRefCounter(concept);
-}
-
-
-psMetadataItem *pmConceptFormat_CELL_TIMESYS(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
-{
-    psString sys = NULL;            // String to store
-    switch (concept->data.S32) {
-    case PS_TIME_TAI:
-        sys = psStringCopy("TAI");
-        break;
-    case PS_TIME_UTC:
-        sys = psStringCopy("UTC");
-        break;
-    case PS_TIME_UT1:
-        sys = psStringCopy("UT1");
-        break;
-    case PS_TIME_TT:
-        sys = psStringCopy("TT");
-        break;
-    default:
-        sys = psStringCopy("Unknown");
-    }
-    psMetadataItem *newItem = psMetadataItemAllocStr(concept->name, concept->comment, sys);
-    psFree(sys);
-
-    return newItem;
-}
-
-psMetadataItem *pmConceptFormat_CELL_TIME(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
-{
-    psTime *time = concept->data.V;     // The time
-    psMetadata *formats = psMetadataLookupMD(NULL, cameraFormat, "FORMATS");
-    psString format = psMetadataLookupStr(NULL, formats, "CELL.TIME");
-
-    if (strlen(format) == 0 || strcasecmp(format, "ISO") == 0) {
-        psString dateTimeString = psTimeToISO(time); // String representation
-        psMetadataItem *item = psMetadataItemAllocStr(concept->name, concept->comment, dateTimeString);
-        psFree(dateTimeString);
-        return item;
-    }
-    if (strstr(format, "SEPARATE")) {
-        // We're working with two separate headers --- construct a list with the date and time separately
-        psString dateTimeString = psTimeToISO(time); // String representation
-        psList *dateTime = psStringSplit(dateTimeString, "T", true);
-        psFree(dateTimeString);
-        psString dateString = psListGet(dateTime, PS_LIST_HEAD); // The date string
-        psString timeString = psListGet(dateTime, PS_LIST_TAIL); // The time string
-
-        // Need to format the strings....
-        // XXX: Couldn't be bothered doing these right now
-        if (strstr(format, "PRE2000")) {
-            psError(PS_ERR_IO, true, "Don't you realise it's the twenty-first century?\n");
-            return NULL;
-        }
-        if (strstr(format, "BACKWARDS")) {
-            psError(PS_ERR_IO, true, "You want it BACKWARDS?  Not right now, thanks.\n");
-            return NULL;
-        }
-
-        psMetadataItem *dateItem = psMetadataItemAllocStr(concept->name, "The date of observation",
-                                   dateString);
-        psMetadataItem *timeItem = psMetadataItemAllocStr(concept->name, "The time of observation",
-                                   timeString);
-        psFree(dateString);
-        psFree(timeString);
-
-        psListRemove(dateTime, PS_LIST_HEAD);
-        psListRemove(dateTime, PS_LIST_HEAD);
-        psListAdd(dateTime, PS_LIST_HEAD, dateItem);
-        psListAdd(dateTime, PS_LIST_TAIL, timeItem);
-
-        psMetadataItem *item = psMetadataItemAllocPtr(concept->name, PS_DATA_LIST, concept->comment,
-                               dateTime);
-        return item;
-    }
-    if (strcasecmp(format, "MJD") == 0) {
-        double mjd = psTimeToMJD(time);
-        return psMetadataItemAllocF64(concept->name, concept->comment, mjd);
-    }
-    if (strcasecmp(format, "JD") == 0) {
-        double jd = psTimeToMJD(time);
-        return psMetadataItemAllocF64("CELL.TIME", "JD of observation", jd);
-    }
-
-    psError(PS_ERR_IO, true, "Not sure how to format concept CELL.TIME\n");
-    return NULL;
-}
-
-psMetadataItem *pmConceptFormat_CELL_Positions(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell)
-{
-    assert(concept);
-    assert(cameraFormat);
-
-    if (concept->type != PS_TYPE_S32) {
-        psError(PS_ERR_IO, true, "Concept %s is not of type S32, as expected.\n", concept->name);
-        return NULL;
-    }
-    int offset = concept->data.S32;
-    offset += fortranCorr(cameraFormat, concept->name);
-    return psMetadataItemAllocS32(concept->name, concept->comment, offset);
-}
-
Index: unk/psModules/src/astrom/pmConceptsStandard.h
===================================================================
--- /trunk/psModules/src/astrom/pmConceptsStandard.h	(revision 7018)
+++ 	(revision )
@@ -1,24 +1,0 @@
-#ifndef PM_CONCEPTS_STANDARD_H
-#define PM_CONCEPTS_STANDARD_H
-
-#include "pslib.h"
-#include "pmFPA.h"
-
-
-psMetadataItem *pmConceptParse_FPA_Coords(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
-psMetadataItem *pmConceptFormat_FPA_Coords(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat);
-psMetadataItem *pmConceptParse_CELL_TRIMSEC(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
-psMetadataItem *pmConceptParse_CELL_BIASSEC(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
-psMetadataItem *pmConceptParse_CELL_Binning(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
-psMetadataItem *pmConceptParse_CELL_TIMESYS(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
-psMetadataItem *pmConceptParse_CELL_TIME(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
-psMetadataItem *pmConceptParse_CELL_Positions(psMetadataItem *concept, psMetadataItem *pattern, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
-psMetadataItem *pmConceptFormat_CELL_TRIMSEC(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
-psMetadataItem *pmConceptFormat_CELL_BIASSEC(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
-psMetadataItem *pmConceptFormat_CELL_XBIN(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
-psMetadataItem *pmConceptFormat_CELL_YBIN(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
-psMetadataItem *pmConceptFormat_CELL_TIMESYS(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
-psMetadataItem *pmConceptFormat_CELL_TIME(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
-psMetadataItem *pmConceptFormat_CELL_Positions(psMetadataItem *concept, psMetadata *cameraFormat, pmFPA *fpa, pmChip *chip, pmCell *cell);
-
-#endif
Index: unk/psModules/src/astrom/pmConceptsWrite.c
===================================================================
--- /trunk/psModules/src/astrom/pmConceptsWrite.c	(revision 7018)
+++ 	(revision )
@@ -1,833 +1,0 @@
-#include <stdio.h>
-#include <strings.h>
-#include "pslib.h"
-
-#include "pmFPA.h"
-#include "pmHDU.h"
-#include "pmHDUUtils.h"
-#include "pmConcepts.h"
-#include "pmConceptsRead.h"
-#include "psAdditionals.h"
-#include "psMetadataItemCompare.h"
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// File-static functions
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-static bool compareConcepts(psMetadataItem *compare, // Item to compare
-                            psMetadataItem *standard // Standard for comparison
-                           )
-{
-    // First order checks
-    if (! compare || ! standard) {
-        return false;
-    }
-    if (strcasecmp(compare->name, standard->name) != 0) {
-        return false;
-    }
-
-    // Special case: list
-    if (compare->type == PS_DATA_LIST) {
-        // "compare" contains a list of psMetadataItems
-        // "standard" likely contains just a string (but it might possibly be a list of strings)
-        psList *cList = compare->data.V; // The list from comparison item
-        psList *sList = NULL;         // The list from standard item
-        switch (standard->type) {
-        case PS_DATA_STRING:
-            sList = psStringSplit(standard->data.V, " ;", true);
-            break;
-        case PS_DATA_LIST:
-            sList = psMemIncrRefCounter(standard->data.V);
-            break;
-        default:
-            return false;
-        }
-        if (cList->n != sList->n) {
-            psFree(sList);
-            return false;
-        }
-        psVector *match = psVectorAlloc(cList->n, PS_TYPE_U8); // Array indicating which values match
-        match->n = cList->n;
-        psVectorInit(match, 0);
-        psListIterator *cIter = psListIteratorAlloc(cList, PS_LIST_HEAD, false); // compare iterator
-        psListIterator *sIter = psListIteratorAlloc(sList, PS_LIST_HEAD, false); // standard iterator
-        psMetadataItem *cItem = NULL; // Item from compare list
-        while ((cItem = psListGetAndIncrement(cIter))) {
-            if (cItem->type != PS_DATA_STRING) {
-                psLogMsg(__func__, PS_LOG_WARN, "psMetadataItem from list is of type %x instead of "
-                         "%x (PS_DATA_STRING) --- can't interpret.\n", cItem->type, PS_DATA_STRING);
-                psFree(cIter);
-                psFree(sIter);
-                psFree(match);
-                psFree(sList);
-                return false;
-            }
-            psString cString = cItem->data.V; // String from compare list
-            psListIteratorSet(sIter, PS_LIST_HEAD);
-            int index = 0;            // Index for list
-            bool found = false;       // Found a match?
-            for (psString sString = NULL; (sString = psListGetAndIncrement(sIter)) && !found; index++) {
-                if (strcasecmp(cString, sString) == 0) {
-                    match->data.U8[index]++;
-                    found = true;
-                }
-            }
-            if (! found) {
-                // Can give up immediately
-                psFree(cIter);
-                psFree(sIter);
-                psFree(match);
-                psFree(sList);
-                return false;
-            }
-        }
-        // Make sure we got 100% matches in both directions
-        bool allMatch = true;         // Did all of them match?
-        for (int i = 0; i < match->n && allMatch; i++) {
-            if (!match->data.U8[i]) {
-                allMatch = false;
-            }
-        }
-        psFree(cIter);
-        psFree(sIter);
-        psFree(sList);
-        psFree(match);
-        return allMatch;
-    }
-
-    return psMetadataItemCompare(compare, standard);
-
-}
-
-
-// Format a single concept
-static psMetadataItem *conceptFormat(pmConceptSpec *spec, // The concept specification
-                                     psMetadataItem *concept, // The concept to parse
-                                     psMetadata *cameraFormat, // The camera format
-                                     pmFPA *fpa, // The FPA
-                                     pmChip *chip, // The chip
-                                     pmCell *cell // The cell
-                                    )
-{
-    if (concept) {
-        psMetadataItem *formatted = NULL;  // The formatted concept
-        if (spec->format) {
-            formatted = spec->format(concept, cameraFormat, fpa, chip, cell);
-        } else {
-            formatted = psMemIncrRefCounter(concept);
-        }
-
-        return formatted;
-    }
-    return NULL;
-}
-
-// Write a single value to a header
-static bool writeSingleHeader(pmHDU *hdu, // HDU for which to add to the header
-                              const char *keyword, // Keyword to add
-                              psMetadataItem *item // Item to add to the header
-                             )
-{
-    if (!hdu->header) {
-        return false;
-    }
-    switch (item->type) {
-    case PS_DATA_STRING:
-        return psMetadataAddStr(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, item->comment,
-                                item->data.V);
-    case PS_DATA_S32:
-        return psMetadataAddS32(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, item->comment,
-                                item->data.S32);
-    case PS_DATA_F32:
-        return psMetadataAddF32(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, item->comment,
-                                item->data.F32);
-    case PS_DATA_F64:
-        return psMetadataAddF64(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, item->comment,
-                                item->data.F64);
-    case PS_DATA_REGION: {
-            psString region = psRegionToString(*(psRegion*)item->data.V);
-            bool result = psMetadataAddStr(hdu->header, PS_LIST_TAIL, keyword, PS_META_REPLACE, item->comment,
-                                           region);
-            psFree(region);
-            return result;
-        }
-    default:
-        psLogMsg(__func__, PS_LOG_WARN, "Type of %s is not suitable for a FITS header --- not added.\n",
-                 item->name);
-        return false;
-    }
-}
-
-
-// Write potentially multiple values to a header
-static bool writeHeader(pmHDU *hdu,     // HDU for which to add to the header
-                        const char *keywords, // Keywords to add
-                        psMetadataItem *item // Item to add to the header
-                       )
-{
-    bool status = true;                 // Status of writing headers, to be returned
-    if (item->type == PS_DATA_LIST) {
-        psList *values = item->data.V;  // List of outputs
-        if (values->n == 0) {
-            // Nothing to write
-            return false;
-        }
-        psList *keys = psStringSplit(keywords, " ,;", true); // List of keywords
-        if (keys->n != values->n) {
-            psLogMsg(__func__, PS_LOG_WARN, "Number of keywords (%d) does not match number of values (%d).\n",
-                     keys->n, values->n);
-        }
-        psListIterator *keysIter = psListIteratorAlloc(keys, PS_LIST_HEAD, false); // Iterator for keywords
-        psListIterator *valuesIter = psListIteratorAlloc(values, PS_LIST_HEAD, false); // Iterator for values
-        psString key = NULL;            // Keyword from iteration
-        psMetadataItem *value = NULL;   // Value from iteration
-        while ((key = psListGetAndIncrement(keysIter)) && (value = psListGetAndIncrement(valuesIter))) {
-            status |= writeSingleHeader(hdu, key, value);
-        }
-        psFree(keysIter);
-        psFree(valuesIter);
-        psFree(keys);
-    } else {
-        status = writeSingleHeader(hdu, keywords, item);
-    }
-    return status;
-}
-
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Public functions
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-bool pmConceptsWriteToCamera(psMetadata *specs, // The concept specifications
-                             pmCell *cell,   // The cell
-                             psMetadata *concepts // The concepts
-                            )
-{
-    if (cell) {
-        pmHDU *hdu = pmHDUGetLowest(NULL, NULL, cell); // The HDU at the lowest level
-        if (!hdu) {
-            return false;
-        }
-        psMetadata *cameraFormat = hdu->format; // The camera format
-        psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
-        psMetadataItem *specItem = NULL;    // Item from the specs metadata
-        while ((specItem = psMetadataGetAndIncrement(specsIter))) {
-            pmConceptSpec *spec = specItem->data.V; // The specification
-            psString name = specItem->name; // The concept name
-            psMetadataItem *cameraItem = psMetadataLookup(cell->config, name); // The concept from the camera,
-            // or NULL
-            if (cameraItem) {
-                // Grab the concept
-                psMetadataItem *conceptItem = psMetadataLookup(concepts, name); // The concept
-                // Formatted version
-                psMetadataItem *formatted = conceptFormat(spec, conceptItem, cameraFormat, NULL, NULL, cell);
-                if (!formatted) {
-                    continue;
-                }
-                psString nameSource = NULL; // String with the concept name and ".SOURCE" added
-                psStringAppend(&nameSource, "%s.SOURCE", name);
-                bool mdok = true;       // Status of MD lookup
-                psString source = psMetadataLookupStr(&mdok, cell->config, nameSource); // The source
-                if (mdok && strlen(source) > 0) {
-                    psTrace(__func__, 8, "%s is %s\n", nameSource, source);
-                    if (strcasecmp(source, "HEADER") == 0) {
-                        if (cameraItem->type != PS_DATA_STRING) {
-                            psLogMsg(__func__, PS_LOG_WARN, "Concept %s is specified by header, but is not "
-                                     "of type STR --- ignored.\n", conceptItem->name);
-                            continue;
-                        }
-                        psTrace(__func__, 8, "Writing %s to header %s\n", name, cameraItem->data.V);
-                        writeHeader(hdu, cameraItem->data.V, formatted);
-                    } else if (strcasecmp(source, "VALUE") == 0) {
-                        psTrace(__func__, 8, "Checking %s against camera format.\n", name);
-                        if (! compareConcepts(formatted, cameraItem)) {
-                            psLogMsg(__func__, PS_LOG_WARN, "Concept %s is specified by value in the camera "
-                                     "format, but the values don't match.\n", name);
-                        }
-                    } else {
-                        psLogMsg(__func__, PS_LOG_WARN, "Concept source %s isn't HEADER or VALUE --- can't "
-                                 "write\n", nameSource);
-                    }
-                } else if (! compareConcepts(formatted, cameraItem)) {
-                    // Assume it's specified by value
-                    psLogMsg(__func__, PS_LOG_WARN, "Concept %s is specified by value in the camera "
-                             "format, but the values don't match.\n", name);
-                }
-                psFree(formatted);
-                psFree(nameSource);
-            }
-
-        }
-        psFree(specsIter);
-        return true;
-    }
-    return false;
-}
-
-bool pmConceptsWriteToDefaults(psMetadata *specs, // The concept specifications
-                               pmFPA *fpa, // The FPA
-                               pmChip *chip, // The chip
-                               pmCell *cell, // The cell
-                               psMetadata *concepts // The concepts
-                              )
-{
-    pmHDU *hdu = pmHDUGetLowest(NULL, NULL, cell); // The HDU at the lowest level
-    if (!hdu) {
-        return false;
-    }
-    psMetadata *cameraFormat = hdu->format; // The camera format
-    bool mdok = true;                   // Status of MD lookup
-    psMetadata *defaults = psMetadataLookupMD(&mdok, cameraFormat, "DEFAULTS"); // The DEFAULTS spec
-    if (mdok && defaults) {
-        pmHDU *hdu = pmHDUGetLowest(fpa, chip, cell); // The HDU at the lowest level
-        psMetadata *cameraFormat = hdu->format; // The camera format
-        psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
-        psMetadataItem *specItem = NULL;    // Item from the specs metadata
-        while ((specItem = psMetadataGetAndIncrement(specsIter))) {
-            pmConceptSpec *spec = specItem->data.V; // The specification
-            psString name = specItem->name; // The concept name
-            psMetadataItem *defaultItem = psMetadataLookup(defaults, name); // The item from the DEFAULTS
-            if (defaultItem) {
-                psMetadataItem *conceptItem = NULL; // The item from the concepts
-                if (defaultItem->type == PS_DATA_METADATA) {
-                    // It's a menu --- need to look up the .DEPEND
-                    psString dependName = NULL; // The concept name with ".DEPEND" on the end
-                    psStringAppend(&dependName, ".DEPEND");
-                    psString dependKey = psMetadataLookupStr(&mdok, defaults, dependName); // The keyword
-                    psFree(dependName);
-                    if (!mdok || !dependKey || strlen(dependKey) == 0) {
-                        psLogMsg(__func__, PS_LOG_WARN, "Can't find %s in the DEFAULTS for %s --- ignored.\n",
-                                 dependName, name);
-                        continue;
-                    }
-                    psString dependValue = psMetadataLookupStr(&mdok, concepts, dependName); // The value
-                    if (!mdok || !dependKey || strlen(dependKey) == 0) {
-                        psLogMsg(__func__, PS_LOG_WARN, "Concept %s specified by %s isn't of type STR -- "
-                                 "ignored.\n", name, dependName);
-                        continue;
-                    }
-                    conceptItem = psMetadataLookup(defaultItem->data.V, dependValue);
-                } else {
-                    conceptItem = psMetadataLookup(concepts, name); // The item from the concepts
-                }
-                psMetadataItem *formatted = conceptFormat(spec, conceptItem, cameraFormat, fpa, chip, cell);
-                if (!formatted) {
-                    continue;
-                }
-                if (! compareConcepts(formatted, defaultItem)) {
-                    psLogMsg(__func__, PS_LOG_WARN, "Concept %s is specified by the DEFAULTS in the camera "
-                             "format, but the values don't match.\n", name);
-                }
-                psFree(formatted);
-            }
-        }
-        psFree(specsIter);
-        return true;
-    }
-    return false;
-}
-
-
-bool pmConceptsWriteToHeader(psMetadata *specs, // The concept specifications
-                             pmFPA *fpa, // The FPA
-                             pmChip *chip, // The chip
-                             pmCell *cell, // The cell
-                             psMetadata *concepts // The concepts
-                            )
-{
-    pmHDU *hdu = pmHDUGetLowest(NULL, NULL, cell); // The HDU at the lowest level
-    if (!hdu) {
-        return false;
-    }
-    psMetadata *cameraFormat = hdu->format; // The camera format
-    bool mdok = true;                   // Status of MD lookup
-    psMetadata *translation = psMetadataLookupMD(&mdok, cameraFormat, "TRANSLATION"); // The TRANSLATION spec
-    if (mdok && translation) {
-        pmHDU *hdu = pmHDUGetLowest(fpa, chip, cell); // The HDU at the lowest level
-        psMetadata *cameraFormat = hdu->format; // The camera format
-        psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
-        psMetadataItem *specItem = NULL;    // Item from the specs metadata
-        while ((specItem = psMetadataGetAndIncrement(specsIter))) {
-            pmConceptSpec *spec = specItem->data.V; // The specification
-            psString name = specItem->name; // The concept name
-            psMetadataItem *headerItem = psMetadataLookup(translation, name); // The item from the TRANSLATION
-            if (headerItem) {
-                if (headerItem->type != PS_DATA_STRING) {
-                    psLogMsg(__func__, PS_LOG_WARN, "TRANSLATION keyword for concept %s isn't of type STR ---"
-                             " ignored.", name);
-                    continue;
-                }
-                psMetadataItem *conceptItem = psMetadataLookup(concepts, name); // The item from the concepts
-                psMetadataItem *formatted = conceptFormat(spec, conceptItem, cameraFormat, fpa, chip, cell);
-                if (!formatted) {
-                    continue;
-                }
-                psList *keywords = psStringSplit(headerItem->data.V, " ,;", true); // List of header keywords
-                if (formatted->type == PS_DATA_LIST) {
-                    psList *values = formatted->data.V; // The values for the headers
-                    if (values->n != keywords->n) {
-                        psLogMsg(__func__, PS_LOG_WARN, "Number of headers specified does not match number "
-                                 "of values for concept %s.\n", name);
-                    }
-                    psListIterator *valuesIter = psListIteratorAlloc(values, PS_LIST_HEAD, false); // Iterator
-                    psListIterator *keywordsIter = psListIteratorAlloc(keywords, PS_LIST_HEAD, false);
-                    psMetadataItem *valuesItem = NULL; // Item from list
-                    while ((valuesItem = psListGetAndIncrement(valuesIter))) {
-                        psString keyword = psListGetAndIncrement(keywordsIter); // Keyword from the list
-                        if (strlen(keyword) > 0) {
-                            writeHeader(hdu, keyword, formatted);
-                        }
-                    }
-                    psFree(valuesIter);
-                    psFree(keywordsIter);
-                } else {
-                    psString keyword = psListGet(keywords, PS_LIST_HEAD); // The keyword
-                    writeHeader(hdu, keyword, formatted);
-                }
-                psFree(formatted);
-                psFree(keywords);
-            }
-        }
-        psFree(specsIter);
-        return true;
-    }
-    return false;
-}
-
-// XXX Warning: This code has not been tested at all
-bool pmConceptsWriteToDatabase(psMetadata *specs, // The concept specifications
-                               pmFPA *fpa, // The FPA
-                               pmChip *chip, // The chip
-                               pmCell *cell, // The cell
-                               psDB *db,// The database handle
-                               psMetadata *concepts // The concepts
-                              )
-{
-    #ifdef OMIT_PSDB
-    return false;
-    #else
-
-    pmHDU *hdu = pmHDUGetLowest(NULL, NULL, cell); // The HDU at the lowest level
-    if (!hdu) {
-        return false;
-    }
-    psMetadata *cameraFormat = hdu->format; // The camera format
-    bool mdok = true;                   // Status of MD lookup
-    psMetadata *database = psMetadataLookupMD(&mdok, cameraFormat, "DATABASE"); // The DATABASE spec
-    if (mdok && database) {
-        pmHDU *hdu = pmHDUGetLowest(fpa, chip, cell); // The HDU at the lowest level
-        psMetadata *cameraFormat = hdu->format; // The camera format
-        psMetadataIterator *specsIter = psMetadataIteratorAlloc(specs, PS_LIST_HEAD, NULL); // Iterator
-        psMetadataItem *specItem = NULL;    // Item from the specs metadata
-        while ((specItem = psMetadataGetAndIncrement(specsIter))) {
-            pmConceptSpec *spec = specItem->data.V; // The specification
-            psString name = specItem->name; // The concept name
-
-            psMetadataItem *dbItem = psMetadataLookup(database, name); // The item from the DATABASE
-            if (dbItem) {
-                if (dbItem->type != PS_DATA_METADATA) {
-                    psLogMsg(__func__, PS_LOG_WARN, "DATABASE keyword for concept %s isn't of type METADATA "
-                             "--- ignored.\n", name);
-                    continue;
-                }
-
-                psMetadataItem *conceptItem = psMetadataLookup(concepts, name); // The item from the concepts
-                psMetadataItem *formatted = conceptFormat(spec, conceptItem, cameraFormat, fpa, chip, cell);
-                if (!formatted) {
-                    continue;
-                }
-
-                psMetadata *dbLookup = dbItem->data.V; // How to look up the value of interest
-                // Name of the table
-                const char *tableName = psMetadataLookupStr(&mdok, dbLookup, "TABLE");
-                // Name of "where" columns
-                const char *givenCols = psMetadataLookupStr(&mdok, dbLookup, "GIVENDBCOL");
-                // Values for "where" columns
-                const char *givenPS = psMetadataLookupStr(&mdok, dbLookup, "GIVENPS");
-
-                // Now, need to get the "given"s
-                if (strlen(givenCols) || strlen(givenPS)) {
-                    psList *cols = psStringSplit(givenCols, ",;", true); // List of column names
-                    psList *values = psStringSplit(givenPS, ",;", true); // List of value names for the columns
-                    psMetadata *selection = psMetadataAlloc(); // The stuff to select in the DB
-                    if (cols->n != values->n) {
-                        psLogMsg(__func__, PS_LOG_WARN,
-                                 "The GIVENDBCOL and GIVENPS entries for %s do not have "
-                                 "the same number of entries --- ignored.\n", name);
-                    } else {
-                        // Iterators for the lists
-                        psListIterator *colsIter = psListIteratorAlloc(cols, PS_LIST_HEAD, false);
-                        psListIterator *valuesIter = psListIteratorAlloc(values, PS_LIST_HEAD, false);
-                        char *column = NULL;    // Name of the column
-                        while ((column = psListGetAndIncrement(colsIter))) {
-                            char *dependName = psListGetAndIncrement(valuesIter); // Name for the value
-                            if (!strlen(column) || !strlen(name)) {
-                                psLogMsg(__func__, PS_LOG_WARN, "One of the columns or value names for %s is "
-                                         " empty --- ignored.\n", name);
-                            } else {
-                                // Search for the value name
-                                psMetadataItem *item = NULL; // The value
-                                if (!item && cell) {
-                                    item = psMetadataLookup(cell->concepts, dependName);
-                                }
-                                if (!item && chip) {
-                                    item = psMetadataLookup(chip->concepts, dependName);
-                                }
-                                if (!item && fpa) {
-                                    item = psMetadataLookup(fpa->concepts, dependName);
-                                }
-                                if (! item) {
-                                    psLogMsg(__func__, PS_LOG_ERROR,
-                                             "Unable to find the value name %s for DB "
-                                             " lookup on %s --- ignored.\n", dependName, name);
-                                } else {
-                                    // We need to create a new psMetadataItem.  I don't think we can't simply
-                                    // hack the existing one, since that could conceivably cause memory leaks
-                                    psMetadataAddItem(selection, formatted, PS_LIST_TAIL, PS_META_REPLACE);
-                                    psFree(formatted);
-                                }
-                            }
-                            psFree(dependName);
-                            psFree(column);
-                        } // Iterating through the columns
-                        psFree(colsIter);
-                        psFree(valuesIter);
-
-                        // Check first to make sure we're only going to touch one row
-                        psArray *dbResult = psDBSelectRows(db, tableName, selection, 2); // Lookup result
-                        // Note that we use limit=2 in order to test if there are multiple rows returned
-                        if (! dbResult || dbResult->n == 0) {
-                            psLogMsg(__func__, PS_LOG_WARN, "Unable to find any rows in DB for %s --- "
-                                     "ignored\n", name);
-                            return false;
-                        } else {
-                            if (dbResult->n > 1) {
-                                psLogMsg(__func__, PS_LOG_WARN, "Multiple rows returned in DB lookup for %s "
-                                         "--- ignored.\n", name);
-                            }
-                            // Update the DB
-                            psMetadata *update = psMetadataAlloc();
-                            psMetadataAddItem(update, conceptItem, PS_LIST_HEAD, 0);
-                            psDBUpdateRows(db, tableName, selection, update);
-                            psFree(update);
-                            return true;
-                        }
-                    }
-                    psFree(cols);
-                    psFree(values);
-                } // Doing the "given"s.
-            }
-        }
-        psFree(specsIter);
-        return true;
-    }
-    return false;
-    #endif
-}
-
-
-
-
-#if 0
-
-// Well, not really "write", but check to make sure it's there and matches
-bool pmConceptWriteToCamera(pmCell *cell, // The cell
-                            psMetadataItem *concept // Concept
-                           )
-{
-    if (! cell->config) {
-        return false;
-    }
-    if (cell) {
-        psMetadataItem *item = psMetadataLookup(cell->config, concept->name); // Info we want
-        return compareConcepts(item, concept);
-    }
-
-    return false;
-}
-
-// Write the concept to the header in the appropriate location
-bool pmConceptWriteToHeader(pmFPA *fpa, // The FPA that contains the chip
-                            pmChip *chip, // The chip that contains the cell
-                            pmCell *cell, // The cell
-                            psMetadataItem *concept // Concept
-                           )
-{
-    bool mdok = true;                   // Status of MD lookup
-    bool status = false;                // Status of setting header
-    if (! fpa->camera) {
-        return false;
-    }
-    psMetadata *translation = psMetadataLookupMD(&mdok, fpa->camera, "TRANSLATION"); // FITS translation
-    if (! mdok) {
-        psError(PS_ERR_IO, false, "Unable to find TRANSLATION in camera configuration.\n");
-        return false;
-    }
-
-    // Look for how to translate the concept into a FITS header name
-    const char *keyword = psMetadataLookupStr(&mdok, translation, concept->name);
-    if (mdok && strlen(keyword) > 0) {
-        psTrace(__func__, 6, "It's in keyword %s\n", keyword);
-        psMetadataItem *headerItem = NULL; // Item to add to header
-        // XXX: Need to expand range of types
-        switch (concept->type) {
-        case PS_DATA_STRING:
-            headerItem = psMetadataItemAllocStr(keyword, concept->comment, concept->data.V);
-            break;
-        case PS_DATA_S32:
-            headerItem = psMetadataItemAllocS32(keyword, concept->comment, concept->data.S32);
-            break;
-        case PS_DATA_F32:
-            headerItem = psMetadataItemAllocF32(keyword, concept->comment, concept->data.F32);
-            break;
-        case PS_DATA_F64:
-            headerItem = psMetadataItemAllocF64(keyword, concept->comment, concept->data.F64);
-            break;
-        default:
-            headerItem = psMetadataItemAlloc(keyword, concept->type, concept->comment,
-                                             concept->data.V); // Item for the header
-        }
-
-        // We have a FITS header to look up --- search each level
-        if (cell && cell->hdu) {
-            psTrace(__func__, 7, "Adding to the cell level header...\n");
-            psMetadataAddItem(cell->hdu->header, headerItem, PS_LIST_TAIL, PS_META_REPLACE);
-            status = true;
-        } else if (chip && chip->hdu) {
-            psTrace(__func__, 7, "Adding to the chip level header...\n");
-            psMetadataAddItem(chip->hdu->header, headerItem, PS_LIST_TAIL, PS_META_REPLACE);
-            status = true;
-        } else if (fpa->hdu) {
-            psTrace(__func__, 7, "Adding to the FPA level header...\n");
-            psMetadataAddItem(fpa->hdu->header, headerItem, PS_LIST_TAIL, PS_META_REPLACE);
-            status = true;
-        }
-        psFree(headerItem);
-    }
-
-    // No header value
-    return status;
-}
-
-
-// Well, not really "write", but check to see if it's there, and matches
-bool pmConceptWriteToDefault(pmFPA *fpa, // The FPA that contains the chip
-                             pmChip *chip, // The chip that contains the cell
-                             pmCell *cell, // The cell
-                             psMetadataItem *concept // Concept
-                            )
-{
-    bool mdOK = true;                   // Status of MD lookup
-    if (! fpa->camera) {
-        return false;
-    }
-    psMetadata *defaults = psMetadataLookupMD(&mdOK, fpa->camera, "DEFAULTS");
-    if (! mdOK || ! defaults) {
-        psError(PS_ERR_IO, false, "Unable to find DEFAULTS in camera configuration.\n");
-        return false;
-    }
-
-    psMetadataItem *defItem = psMetadataLookup(defaults, concept->name);
-    bool status = false;                // Result of checking the database
-    if (defItem) {
-        if (defItem->type == PS_DATA_METADATA) {
-            // A dependent default
-            psTrace(__func__, 7, "Evaluating dependent default....\n");
-            psMetadata *dependents = defItem->data.V; // The list of dependents
-            // Find out what it depends on
-            psString dependName = psStringCopy(concept->name);
-            psStringAppend(&dependName, ".DEPEND");
-            psString dependsOn = psMetadataLookupStr(&mdOK, defaults, dependName);
-            if (! mdOK) {
-                psError(PS_ERR_IO, false, "Unable to find %s in camera configuration for dependent default"
-                        " --- ignored\n", dependName);
-                // XXX: Need to clean up before returning
-                return false;
-            }
-            psFree(dependName);
-            // Find the value of the dependent concept
-            psMetadataItem *depItem = pmConceptReadFromHeader(fpa, chip, cell, dependsOn);
-            if (! depItem) {
-                psError(PS_ERR_IO, true, "Unable to find value for %s (required for %s)\n", dependsOn,
-                        concept->name);
-                return false;
-            }
-            if (depItem->type != PS_DATA_STRING) {
-                psError(PS_ERR_IO, true, "Value of %s is not of type string, as required for dependency"
-                        " --- ignored.\n", dependsOn);
-            }
-
-            defItem = psMetadataLookup(dependents, depItem->data.V); // This is now what we were after
-        }
-
-        status = compareConcepts(defItem, concept);
-        if (! status) {
-            psError(PS_ERR_IO, true, "Concept %s is specified by default in the camera configuration, "
-                    "but doesn't match the actual value.\n", concept->name);
-        }
-    }
-
-    // XXX: Need to clean up before returning
-    return status;
-}
-
-
-// XXX: Not tested at all
-// XXX I WOULD NOT TRUST THIS FUNCTION IN THE SLIGHTEST YET! --- PAP
-bool pmConceptWriteToDB(pmFPA *fpa, // The FPA that contains the chip
-                        pmChip *chip, // The chip that contains the cell
-                        pmCell *cell, // The cell
-                        psDB *db,    // DB handle
-                        psMetadataItem *concept // Concept
-                       )
-{
-    if (! db) {
-        // No database initialised
-        return false;
-    }
-
-    bool mdStatus = true;               // Status of MD lookup
-    if (! fpa->camera) {
-        return false;
-    }
-    psMetadata *database = psMetadataLookupMD(&mdStatus, fpa->camera, "DATABASE");
-    if (! mdStatus) {
-        // No error, because not everyone needs to use the DB
-        return NULL;
-    }
-
-    psMetadata *dbLookup = psMetadataLookupMD(&mdStatus, database, concept->name);
-    if (dbLookup) {
-        const char *tableName = psMetadataLookupStr(&mdStatus, dbLookup, "TABLE"); // Name of the table
-        //        const char *colName = psMetadataLookupStr(&mdStatus, dbLookup, "COLUMN"); // Name of the column
-        const char *givenCols = psMetadataLookupStr(&mdStatus, dbLookup, "GIVENDBCOL"); // Name of "where"
-        // columns
-        const char *givenPS = psMetadataLookupStr(&mdStatus, dbLookup, "GIVENPS"); // Values for "where"
-        // columns
-
-        // Now, need to get the "given"s
-        if (strlen(givenCols) || strlen(givenPS)) {
-            psList *cols = psStringSplit(givenCols, ",;", true); // List of column names
-            psList *values = psStringSplit(givenPS, ",;", true); // List of value names for the columns
-            psMetadata *selection = psMetadataAlloc(); // The stuff to select in the DB
-            if (cols->n != values->n) {
-                psLogMsg(__func__, PS_LOG_WARN, "The GIVENDBCOL and GIVENPS entries for %s do not have "
-                         "the same number of entries --- ignored.\n", concept);
-            } else {
-                // Iterators for the lists
-                psListIterator *colsIter = psListIteratorAlloc(cols, PS_LIST_HEAD, false);
-                psListIterator *valuesIter = psListIteratorAlloc(values, PS_LIST_HEAD, false);
-                char *column = NULL;    // Name of the column
-                while ((column = psListGetAndIncrement(colsIter))) {
-                    char *name = psListGetAndIncrement(valuesIter); // Name for the value
-                    if (!strlen(column) || !strlen(name)) {
-                        psLogMsg(__func__, PS_LOG_WARN, "One of the columns or value names for %s is "
-                                 " empty --- ignored.\n", concept);
-                    } else {
-                        // Search for the value name
-                        psMetadataItem *item = pmConceptReadFromHeader(fpa, chip, cell, name);
-                        if (! item) {
-                            item = pmConceptReadFromDefault(fpa, chip, cell, name);
-                        }
-                        if (! item) {
-                            psLogMsg(__func__, PS_LOG_ERROR, "Unable to find the value name %s for DB "
-                                     " lookup on %s --- ignored.\n", name, concept);
-                        } else {
-                            // We need to create a new psMetadataItem.  I don't think we can't simply hack
-                            // the existing one, since that could conceivably cause memory leaks
-                            psMetadataItem *newItem = psMetadataItemAlloc(concept->name, item->type,
-                                                      item->comment, item->data.V);
-                            psMetadataAddItem(selection, newItem, PS_LIST_TAIL, PS_META_REPLACE);
-                            psFree(newItem);
-                        }
-                    }
-                    psFree(name);
-                    psFree(column);
-                } // Iterating through the columns
-                psFree(colsIter);
-                psFree(valuesIter);
-
-                // Check first to make sure we're only going to touch one row
-                psArray *dbResult = psDBSelectRows(db, tableName, selection, 2); // Lookup result
-                // Note that we use limit=2 in order to test if there are multiple rows returned
-                if (! dbResult || dbResult->n == 0) {
-                    psLogMsg(__func__, PS_LOG_WARN, "Unable to find any rows in DB for %s --- ignored\n",
-                             concept->name);
-                    return false;
-                } else {
-                    if (dbResult->n > 1) {
-                        psLogMsg(__func__, PS_LOG_WARN, "Multiple rows returned in DB lookup for %s --- "
-                                 " ignored.\n", concept->name);
-                    }
-                    // Update the DB
-                    psMetadata *update = psMetadataAlloc();
-                    psMetadataAddItem(update, concept, PS_LIST_HEAD, 0);
-                    psDBUpdateRows(db, tableName, selection, update);
-                    psFree(update);
-                    return true;
-                }
-            }
-            psFree(cols);
-            psFree(values);
-        }
-    } // Doing the "given"s.
-
-    psAbort(__func__, "Shouldn't ever get here?\n");
-    return false;
-}
-
-
-// Concept write from item
-bool pmConceptWriteItem(pmFPA *fpa,     // The FPA
-                        pmChip *chip,   // The chip
-                        pmCell *cell,   // The cell
-                        psDB *db,       // DB handle
-                        psMetadataItem *concept // Concept item
-                       )
-{
-    if (! fpa->camera) {
-        return false;
-    }
-
-    // Try headers, database, defaults in order
-    psTrace(__func__, 3, "Trying to set concept %s...\n", concept->name);
-    bool status = pmConceptWriteToCamera(cell, concept); // Status for return
-    if (! status) {
-        psTrace(__func__, 5, "Trying header....\n");
-        status = pmConceptWriteToHeader(fpa, chip, cell, concept);
-    }
-    if (! status) {
-        psTrace(__func__, 5, "Trying database....\n");
-        status = pmConceptWriteToDB(fpa, chip, cell, db, concept);
-    }
-    if (! status) {
-        psTrace(__func__, 5, "Checking defaults....\n");
-        status = pmConceptWriteToDefault(fpa, chip, cell, concept);
-    }
-
-    if (! status) {
-        psError(PS_ERR_IO, true, "Unable to set %s (%s).\n", concept->name, concept->comment);
-    }
-
-    return status;
-}
-
-
-// Concept write
-bool pmConceptWrite(pmFPA *fpa, // The FPA
-                    pmChip *chip,// The chip
-                    pmCell *cell,    // The cell
-                    psDB *db, // DB handle
-                    psMetadata *concepts, // Concepts MD from which to set
-                    const char *name // Name of the concept
-                   )
-{
-    psMetadataItem *concept = psMetadataLookup(concepts, name);
-    if (! concept) {
-        psError(PS_ERR_IO, true, "No such concept as %s\n", name);
-        return false;
-    }
-    return pmConceptWriteItem(fpa, chip, cell, db, concept);
-}
-
-#endif
Index: unk/psModules/src/astrom/pmConceptsWrite.h
===================================================================
--- /trunk/psModules/src/astrom/pmConceptsWrite.h	(revision 7018)
+++ 	(revision )
@@ -1,78 +1,0 @@
-#ifndef PM_CONCEPTS_WRITE_H
-#define PM_CONCEPTS_WRITE_H
-
-#include "pslib.h"
-#include "pmFPA.h"
-
-bool pmConceptsWriteToCamera(psMetadata *specs, // The concept specifications
-                             pmCell *cell,   // The cell
-                             psMetadata *concepts // The concepts
-                            );
-bool pmConceptsWriteToDefaults(psMetadata *specs, // The concept specifications
-                               pmFPA *fpa, // The FPA
-                               pmChip *chip, // The chip
-                               pmCell *cell, // The cell
-                               psMetadata *concepts // The concepts
-                              );
-bool pmConceptsWriteToHeader(psMetadata *specs, // The concept specifications
-                             pmFPA *fpa, // The FPA
-                             pmChip *chip, // The chip
-                             pmCell *cell, // The cell
-                             psMetadata *concepts // The concepts
-                            );
-bool pmConceptsWriteToDatabase(psMetadata *specs, // The concept specifications
-                               pmFPA *fpa, // The FPA
-                               pmChip *chip, // The chip
-                               pmCell *cell, // The cell
-                               psDB *db,// The database handle
-                               psMetadata *concepts // The concepts
-                              );
-
-
-#ifdef OLD
-
-// Well, not really "write", but check to make sure it's there and matches
-bool pmConceptWriteToCamera(pmCell *cell, // The cell
-                            psMetadataItem *concept // Concept
-                           );
-
-// Write the concept to the header in the appropriate location
-bool pmConceptWriteToHeader(pmFPA *fpa, // The FPA that contains the chip
-                            pmChip *chip, // The chip that contains the cell
-                            pmCell *cell, // The cell
-                            psMetadataItem *concept // Concept
-                           );
-
-// Well, not really "write", but check to see if it's there, and matches
-bool pmConceptWriteToDefault(pmFPA *fpa, // The FPA that contains the chip
-                             pmChip *chip, // The chip that contains the cell
-                             pmCell *cell, // The cell
-                             psMetadataItem *concept // Concept
-                            );
-
-bool pmConceptWriteToDB(pmFPA *fpa, // The FPA that contains the chip
-                        pmChip *chip, // The chip that contains the cell
-                        pmCell *cell, // The cell
-                        psDB *db,    // DB handle
-                        psMetadataItem *concept // Concept
-                       );
-
-// Concept write from item
-bool pmConceptWriteItem(pmFPA *fpa,     // The FPA
-                        pmChip *chip,   // The chip
-                        pmCell *cell,   // The cell
-                        psDB *db,       // DB handle
-                        psMetadataItem *concept // Concept item
-                       );
-
-bool pmConceptWrite(pmFPA *fpa, // The FPA
-                    pmChip *chip,// The chip
-                    pmCell *cell,    // The cell
-                    psDB *db, // DB handle
-                    psMetadata *concepts, // Concepts MD from which to set
-                    const char *name // Name of the concept
-                   );
-
-#endif
-
-#endif
Index: unk/psModules/src/astrom/pmFPA.c
===================================================================
--- /trunk/psModules/src/astrom/pmFPA.c	(revision 7018)
+++ 	(revision )
@@ -1,575 +1,0 @@
-/** @file  pmFPA.c
-*
-*  @brief This file defines the basic types for the FPA hierarchy
-*
-*  @ingroup AstroImage
-*
-*  @author GLG, MHPCC
-*
-* XXX: We should review the extent of the warning messages on these functions
-* when the transformations are not successful.
-*
-* XXX: Should we implement non-linear cell->chip transforms?
-*
-*  @version $Revision: 1.7 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2006-04-30 22:04:46 $
-*
-*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
-*/
-
-/******************************************************************************/
-/*  INCLUDE FILES                                                             */
-/******************************************************************************/
-#include <string.h>
-#include <math.h>
-#include <assert.h>
-#include "pslib.h"
-
-#include "pmHDU.h"
-#include "pmFPA.h"
-#include "pmConcepts.h"
-#include "pmMaskBadPixels.h"
-
-/******************************************************************************
- *****************************************************************************/
-
-static void readoutFree(pmReadout *readout)
-{
-    // if this readout has a parent, drop that instance
-    if (readout->parent) {
-        psTrace(__func__, 9, "Removing readout %lx from cell %lx...\n", (size_t)readout, (size_t)readout->parent);
-        psArray *readouts = readout->parent->readouts;
-        for (int i = 0; i < readouts->n; i++) {
-            if (readouts->data[i] == readout) {
-                readouts->data[i] = NULL;
-            }
-        }
-    }
-    psTrace(__func__, 9, "Freeing readout %lx\n", (size_t) readout);
-    psFree(readout->image);
-    psFree(readout->mask);
-    psFree(readout->weight);
-    psFree(readout->analysis);
-    psFree(readout->bias);
-}
-
-static void cellFree(pmCell *cell)
-{
-
-    // if this cell has a parent, drop that instance
-    if (cell->parent) {
-        psTrace(__func__, 9, "Removing cell %lx from chip %lx...\n", (size_t)cell, (size_t)cell->parent);
-        psArray *cells = cell->parent->cells;
-        for (int i = 0; i < cells->n; i++) {
-            if (cells->data[i] == cell) {
-                cells->data[i] = NULL;
-            }
-        }
-    }
-    psTrace(__func__, 9, "Freeing cell %lx\n", (size_t)cell);
-    pmCellFreeReadouts(cell);
-    psFree(cell->readouts);
-
-    psFree(cell->concepts);
-    psFree(cell->analysis);
-    psFree(cell->config);
-    psFree(cell->hdu);
-}
-
-static void chipFree(pmChip* chip)
-{
-    // if this chip has a parent, drop that instance
-    if (chip->parent) {
-        psTrace(__func__, 9, "Removing chip %lx from fpa %lx...\n", (size_t)chip, (size_t)chip->parent);
-        psArray *chips = chip->parent->chips;
-        for (int i = 0; i < chips->n; i++) {
-            if (chips->data[i] == chip) {
-                chips->data[i] = NULL;
-            }
-        }
-    }
-
-    psTrace(__func__, 9, "Freeing chip %lx\n", (size_t)chip);
-    pmChipFreeCells(chip);
-    psFree(chip->cells);
-
-    psFree(chip->concepts);
-    psFree(chip->analysis);
-    psFree(chip->hdu);
-    psFree(chip->mosaic);
-
-    # if FPA_ASTROM
-
-    psFree(chip->toFPA);
-    psFree(chip->fromFPA);
-    # endif
-}
-
-
-static void FPAFree(pmFPA *fpa)
-{
-    psTrace(__func__, 9, "Freeing fpa %lx\n", (size_t)fpa);
-
-    // NULL the parent pointers
-    psArray *chips = fpa->chips;
-    for (int i = 0 ; i < chips->n ; i++) {
-        pmChip *tmpChip = chips->data[i];
-        if (! tmpChip) {
-            continue;
-        }
-        tmpChip->parent = NULL;
-    }
-    psFree(fpa->chips);
-    psFree(fpa->concepts);
-    psFree(fpa->analysis);
-    psFree(fpa->camera);
-    psFree(fpa->hdu);
-
-    # if FPA_ASTROM
-
-    psFree(fpa->fromTangentPlane);
-    psFree(fpa->toTangentPlane);
-    psFree(fpa->projection);
-    # endif
-}
-
-void pmCellFreeReadouts(pmCell *cell)
-{
-    //
-    // Set the parent to NULL in all cell->readouts before psFree(cell->readouts)
-    // in order to avoid memory reference counter problems.
-    //
-    psArray *readouts = cell->readouts;
-    for (psS32 i = 0 ; i < readouts->n ; i++) {
-        pmReadout *tmpReadout = readouts->data[i];
-        if (! tmpReadout) {
-            continue;
-        }
-        tmpReadout->parent = NULL;
-        psTrace(__func__, 9, "Will now free readout %lx...\n", (size_t)tmpReadout);
-    }
-    cell->readouts = psArrayRealloc(cell->readouts, 0);
-    cell->readouts->n = 0;
-}
-
-
-void pmChipFreeCells(pmChip *chip)
-{
-    //
-    // Set the parent to NULL in all chip->cells before psFree(chip->cells)
-    // in order to avoid memory reference counter problems.
-    //
-    psArray *cells = chip->cells;
-    for (int i = 0 ; i < cells->n ; i++) {
-        pmCell *tmpCell = cells->data[i];
-        if (! tmpCell) {
-            continue;
-        }
-        tmpCell->parent = NULL;
-        pmCellFreeReadouts(tmpCell);// Drop all readouts the cell holds
-    }
-    chip->cells = psArrayRealloc(chip->cells, 0);
-    chip->cells->n = 0;
-}
-
-
-pmReadout *pmReadoutAlloc(pmCell *cell)
-{
-    pmReadout *tmpReadout = (pmReadout *) psAlloc(sizeof(pmReadout));
-    psMemSetDeallocator(tmpReadout, (psFreeFunc) readoutFree);
-
-    tmpReadout->image = NULL;
-    tmpReadout->mask = NULL;
-    tmpReadout->weight = NULL;
-    tmpReadout->bias = psListAlloc(NULL);
-    tmpReadout->analysis = psMetadataAlloc();
-    tmpReadout->parent = cell;
-    if (cell != NULL) {
-        cell->readouts = psArrayAdd(cell->readouts, 1, (psPtr) tmpReadout);
-    }
-
-    tmpReadout->process = true;            // All cells are processed by default
-    tmpReadout->file_exists = false;       // file not yet identified
-    tmpReadout->data_exists = false;       // data yet read in
-
-    tmpReadout->row0 = 0;
-    tmpReadout->col0 = 0;
-
-    return(tmpReadout);
-}
-
-pmCell *pmCellAlloc(
-    pmChip *chip,
-    const char *name
-)
-{
-    pmCell *tmpCell = (pmCell *) psAlloc(sizeof(pmCell));
-    psMemSetDeallocator(tmpCell, (psFreeFunc) cellFree);
-
-    tmpCell->config = NULL;
-    tmpCell->analysis = psMetadataAlloc();
-    tmpCell->readouts = psArrayAlloc(0);
-    tmpCell->parent = chip;
-    if (chip != NULL) {
-        chip->cells = psArrayAdd(chip->cells, 1, (psPtr) tmpCell);
-    }
-    tmpCell->hdu = NULL;
-    tmpCell->process = true;            // All cells are processed by default
-    tmpCell->file_exists = false;       // Not yet identified
-    tmpCell->data_exists = false;       // Not yet read in
-
-    tmpCell->concepts = psMetadataAlloc();
-    tmpCell->conceptsRead = PM_CONCEPT_SOURCE_NONE;
-    if (!psMetadataAddStr(tmpCell->concepts, PS_LIST_HEAD, "CELL.NAME", 0, NULL, name)) {
-        psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not add CELL.NAME to metadata.\n");
-    }
-    pmConceptsBlankCell(tmpCell);
-
-    return(tmpCell);
-}
-
-pmChip *pmChipAlloc(
-    pmFPA *fpa,
-    const char *name)
-{
-    pmChip *tmpChip = (pmChip *) psAlloc(sizeof(pmChip));
-    psMemSetDeallocator(tmpChip, (psFreeFunc) chipFree);
-
-    # if FPA_ASTROM
-
-    tmpChip->col0 = 0;
-tmpChip->row0 = 0;
-tmpChip->toFPA = NULL;
-tmpChip->fromFPA = NULL;
-# endif
-
-tmpChip->analysis = psMetadataAlloc();
-    tmpChip->cells = psArrayAlloc(0);
-    tmpChip->parent = fpa;
-    if (fpa != NULL) {
-        psArrayAdd(fpa->chips, 1, (psPtr) tmpChip);
-    }
-    tmpChip->hdu = NULL;
-    tmpChip->mosaic = NULL;
-    tmpChip->process = true;            // Work on all chips, by default
-    tmpChip->file_exists = false;       // Not yet identified
-    tmpChip->data_exists = false;       // Not yet read in
-
-    tmpChip->concepts = psMetadataAlloc();
-    tmpChip->conceptsRead = PM_CONCEPT_SOURCE_NONE;
-    if (!psMetadataAddStr(tmpChip->concepts, PS_LIST_HEAD, "CHIP.NAME", 0, NULL, name)) {
-        psLogMsg(__func__, PS_LOG_WARN, "WARNING: Could not add CHIP.NAME %s to concepts.\n", name);
-    }
-    pmConceptsBlankChip(tmpChip);
-    return(tmpChip);
-}
-
-pmFPA *pmFPAAlloc(const psMetadata *camera)
-{
-    pmFPA *tmpFPA = (pmFPA *) psAlloc(sizeof(pmFPA));
-    psMemSetDeallocator(tmpFPA, (psFreeFunc) FPAFree);
-
-    # if FPA_ASTROM
-
-    tmpFPA->fromTangentPlane = NULL;
-tmpFPA->toTangentPlane = NULL;
-tmpFPA->projection = NULL;
-# endif
-
-tmpFPA->analysis = NULL;
-tmpFPA->camera = psMemIncrRefCounter((psPtr)camera);
-    tmpFPA->chips = psArrayAlloc(0);
-    tmpFPA->hdu = NULL;
-
-    tmpFPA->concepts = psMetadataAlloc();
-    tmpFPA->conceptsRead = PM_CONCEPT_SOURCE_NONE;
-    pmConceptsBlankFPA(tmpFPA);
-
-    return(tmpFPA);
-}
-
-static psBool cellCheckParents(pmCell *cell)
-{
-    if (!cell) {
-        return(true);
-    }
-    psBool flag = true;
-
-    for (psS32 i = 0 ; i < cell->readouts->n ; i++) {
-        pmReadout *tmpReadout = (pmReadout *) cell->readouts->data[i];
-        PS_ASSERT_PTR_NON_NULL(tmpReadout, false);
-        if (tmpReadout->parent != cell) {
-            tmpReadout->parent = cell;
-            flag = false;
-        }
-    }
-    return(flag);
-}
-
-static psBool chipCheckParents(pmChip *chip)
-{
-    if (!chip) {
-        return(true);
-    }
-    psBool flag = true;
-
-    for (psS32 i = 0 ; i < chip->cells->n ; i++) {
-        pmCell *tmpCell = (pmCell *) chip->cells->data[i];
-        PS_ASSERT_PTR_NON_NULL(tmpCell, false);
-        if (tmpCell->parent != chip) {
-            tmpCell->parent = chip;
-            flag = false;
-        }
-
-        flag &= cellCheckParents(tmpCell);
-    }
-    return(flag);
-}
-
-psBool pmFPACheckParents(pmFPA *fpa)
-{
-    if (!fpa) {
-        return(true);
-    }
-    psBool flag = true;
-
-    for (psS32 i = 0 ; i < fpa->chips->n ; i++) {
-        pmChip *tmpChip = (pmChip *) fpa->chips->data[i];
-        PS_ASSERT_PTR_NON_NULL(tmpChip, false);
-        if (tmpChip->parent != fpa) {
-            tmpChip->parent = fpa;
-            flag = false;
-        }
-
-        flag &= chipCheckParents(tmpChip);
-    }
-    return(flag);
-}
-
-/** functions to turn on/off the file_exists flag **/
-bool pmFPASetFileStatus (pmFPA *fpa, bool status)
-{
-
-    for (int i = 0; i < fpa->chips->n; i++) {
-        pmChip *chip = fpa->chips->data[i];
-        pmChipSetFileStatus (chip, status);
-    }
-    return true;
-}
-
-bool pmChipSetFileStatus (pmChip *chip, bool status)
-{
-
-    chip->file_exists = status;
-    for (int i = 0; i < chip->cells->n; i++) {
-        pmCell *cell = chip->cells->data[i];
-        pmCellSetFileStatus (cell, status);
-    }
-    return true;
-}
-
-bool pmCellSetFileStatus (pmCell *cell, bool status)
-{
-
-    cell->file_exists = status;
-    for (int i = 0; i < cell->readouts->n; i++) {
-        pmReadout *readout = cell->readouts->data[i];
-        readout->file_exists = status;
-    }
-    return true;
-}
-
-/** functions to turn on/off the data_exists flag **/
-bool pmFPASetDataStatus (pmFPA *fpa, bool status)
-{
-
-    for (int i = 0; i < fpa->chips->n; i++) {
-        pmChip *chip = fpa->chips->data[i];
-        pmChipSetDataStatus (chip, status);
-    }
-    return true;
-}
-
-bool pmChipSetDataStatus (pmChip *chip, bool status)
-{
-
-    chip->data_exists = status;
-    for (int i = 0; i < chip->cells->n; i++) {
-        pmCell *cell = chip->cells->data[i];
-        pmCellSetDataStatus (cell, status);
-    }
-    return true;
-}
-
-bool pmCellSetDataStatus (pmCell *cell, bool status)
-{
-
-    cell->data_exists = status;
-    for (int i = 0; i < cell->readouts->n; i++) {
-        pmReadout *readout = cell->readouts->data[i];
-        readout->data_exists = status;
-    }
-    return true;
-}
-
-/*****************************************************************************
- *****************************************************************************/
-
-// Set cells within a chip to be processed or not
-static bool setCellsProcess(const pmChip *chip, // Chip of interest
-                            bool process  // Process this chip?
-                           )
-{
-    PS_ASSERT_PTR_NON_NULL(chip, false);
-
-    psArray *cells = chip->cells;       // Component cells
-    if (! cells) {
-        return false;
-    }
-    for (int i = 0; i < cells->n; i++) {
-        pmCell *tmpCell = cells->data[i]; // Cell of interest
-        if (tmpCell) {
-            tmpCell->process = process;
-        }
-    }
-
-    return true;
-}
-
-/*****************************************************************************
-XXX EAM : I've added the 'exclusive' option.  if true all other chips are de-selected
-XXX EAM : a negative value is valid and, in combinations with exclusive, de-selects all chips
- *****************************************************************************/
-bool pmFPASelectChip(pmFPA *fpa, int chipNum, bool exclusive)
-{
-    PS_ASSERT_PTR_NON_NULL(fpa, false);
-
-    psArray *chips = fpa->chips;        // Component chips
-    if ((chips == NULL) || (chipNum >= chips->n)) {
-        return(false);
-    }
-
-    for (int i = 0 ; i < chips->n ; i++) {
-        pmChip *tmpChip = (pmChip *) chips->data[i];
-        if (tmpChip == NULL) {
-            continue;
-        }
-        if (i == chipNum) {
-            tmpChip->process = true;
-            setCellsProcess(tmpChip, true);
-        } else {
-            if (exclusive) {
-                tmpChip->process = false;
-                setCellsProcess(tmpChip, false);
-            }
-        }
-
-    }
-
-    return true;
-}
-
-/*****************************************************************************
-XXX EAM : I've added the 'exclusive' option.  if true all other chips are de-selected
-XXX EAM : a negative value is valid and, in combinations with exclusive, de-selects all cells
-XXX this function should probably be re-defined to merge with 'setCellsProcess'
- *****************************************************************************/
-bool pmChipSelectCell(pmChip *chip, int cellNum, bool exclusive)
-{
-    assert(chip);
-
-    psArray *cells = chip->cells;       // Component cells
-    if (!cells || cellNum > cells->n) {
-        return false;
-    }
-
-    for (int i = 0; i < cells->n; i++) {
-        pmCell *cell = cells->data[i];
-        if (!cell) {
-            continue;
-        }
-        if (i == cellNum) {
-            cell->process = true;
-        } else {
-            if (exclusive) {
-                cell->process = false;
-            }
-        }
-    }
-    return true;
-}
-
-/*****************************************************************************
-XXX: The SDRS is ambiguous on a few things:
-    Whether or not the other chips should be set process=true. [PAP: No]
-    Should we return the number of chip process=true before or after they're set, [PAP: After]
- *****************************************************************************/
-/**
- *
- * pmFPAExcludeChip shall set process to false only for the specified chip
- * number (chipNum). In the event that the specified chip number does not exist
- * within the fpa, the function shall generate a warning, and perform no action.
- * The function shall return the number of chips within the fpa that have process
- * set to true.
- *
- */
-int pmFPAExcludeChip(
-    pmFPA *fpa,
-    int chipNum)
-{
-    PS_ASSERT_PTR_NON_NULL(fpa, false);
-
-    psArray *chips = fpa->chips;        // Component chips
-    if (chips == NULL) {
-        psLogMsg(__func__, PS_LOG_WARN, "WARNING: fpa->chips == NULL\n");
-        return(0);
-    }
-    if ((chipNum >= chips->n) || (NULL == (pmChip *) chips->data[chipNum])) {
-        psLogMsg(__func__, PS_LOG_WARN, "WARNING: the specified chip (%d) does not exist.\n", chipNum);
-        return(0);
-    }
-
-    int numChips = 0;                   // Number of chips to be processed
-    for (int i = 0 ; i < chips->n ; i++) {
-        pmChip *tmpChip = (pmChip *) chips->data[i]; // Chip of interest
-        if (tmpChip != NULL) {
-            if (i == chipNum) {
-                tmpChip->process = false;
-                setCellsProcess(tmpChip, false); // Wipe out the cell as well
-            } else if (tmpChip->process) {
-                numChips++;
-            }
-        }
-    }
-
-    return(numChips);
-}
-
-int pmChipExcludeCell(pmChip *chip,
-                      int cellNum
-                     )
-{
-    assert(chip);
-
-    psArray *cells = chip->cells;       // The component cells
-    if (!cells || cellNum > cells->n) {
-        return 0;
-    }
-
-    int numCells = 0;                   // Number of cells to be processed
-    for (int i = 0; i < cells->n; i++) {
-        pmCell *cell = cells->data[i];
-        if (!cell) {
-            continue;
-        }
-        if (i == cellNum) {
-            cell->process = false;
-        } else {
-            numCells++;
-        }
-    }
-
-    return numCells;
-}
-
-
Index: unk/psModules/src/astrom/pmFPA.h
===================================================================
--- /trunk/psModules/src/astrom/pmFPA.h	(revision 7018)
+++ 	(revision )
@@ -1,286 +1,0 @@
-/** @file  pmFPA.h
-*
-*  @brief This file defines the basic types the focal plane hierarchy.
-*
-*  @ingroup AstroImage
-*
-*  @author GLG, MHPCC
-*
-*  @version $Revision: 1.7 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2006-04-30 22:05:41 $
-*
-*  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
-*/
-
-#ifndef PM_FPA_H
-#define PM_FPA_H
-#if HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "pslib.h"
-#include "pmHDU.h"
-
-# define FPA_ASTROM 1
-
-/// @addtogroup AstroImage
-/// @{
-
-/** Focal plane data structure
- *
- *  A focal plane consists of one or more chips (according to the number of
- *  pieces of contiguous silicon). It contains metadata containers for the
- *  concepts and analysis, a link to the parent, and pointers to the FITS header,
- *  if that corresponds to this level (the FPA may be the PHU, but will not ever
- *  contain pixels). For astrometry, it contains a transformation from the focal
- *  plane to the tangent plane and the fixed pattern residuals. It is expected
- *  that the transformation will consist of two 4D polynomials (i.e. a function
- *  of two coordinates in position, the magnitude of the object, and the color of
- *  the object) in order to correct for optical distortions and the effects of
- *  the atmosphere; hence we think that it is prudent to include a reverse
- *  transformation which will be derived from numerically inverting the forward
- *  transformation.
- *
- */
-typedef struct
-{
-    # if FPA_ASTROM
-    // Astrometric transformations
-    psPlaneDistort* fromTangentPlane;   ///< Transformation from tangent plane to focal plane
-psPlaneDistort* toTangentPlane;     ///< Transformation from focal plane to tangent plane
-psProjection *projection;           ///< Projection from tangent plane to sky
-# endif
-// Information
-psMetadata *concepts;               ///< Cache for PS concepts
-unsigned int conceptsRead;          ///< Which concepts have been read
-psMetadata *analysis;               ///< FPA-level analysis metadata
-const psMetadata *camera;           ///< Camera configuration
-psArray *chips;                     ///< The chips
-pmHDU *hdu;                         ///< FITS data
-}
-pmFPA;
-
-/** Chip data structure
- *
- *  A chip consists of one or more cells (according to the number of amplifiers
- *  on the device). The chip contains metadata containers for the concepts and
- *  analysis, a link to the parent, and pointers to the pointers to the various
- *  FITS data, if that corresponds to this level. For astrometry, in addition to
- *  the rough positioning information, it contains a coordinate transform from
- *  the chip to the focal plane. It is expected that this transform will consist
- *  of two second-order 2D polynomials; hence we think that it is prudent to
- *  include a reverse transformation which will be derived from numerically
- *  inverting the forward transformation. A boolean indicates whether the chip is
- *  of interest, allowing it to be excluded from analysis.
- *
- */
-typedef struct
-{
-# if FPA_ASTROM
-    // Offset specifying position on focal plane
-    int col0;                           ///< Offset from the left of FPA.
-int row0;                           ///< Offset from the bottom of FPA.
-// Astrometric transformations
-psPlaneTransform* toFPA;            ///< Transformation from chip to FPA coordinates
-psPlaneTransform* fromFPA;          ///< Transformation from FPA to chip coordinates
-# endif
-// Information
-psMetadata *concepts;               ///< Cache for PS concepts
-unsigned int conceptsRead;          ///< Which concepts have been read
-psMetadata *analysis;               ///< Chip-level analysis metadata
-psArray *cells;                     ///< The cells (referred to by name)
-pmFPA *parent;                      ///< Parent FPA
-bool process;                       ///< Do we bother about reading and working with this chip?
-bool file_exists;                   ///< Does the file for this chip exist (read case only)?
-bool data_exists;                   ///< Does the data for this chip exist (read case only)?
-pmHDU *hdu;                         ///< FITS data
-struct pmCell *mosaic;              ///< A mosaic cell
-}
-pmChip;
-
-/** Cell data structure
- *
- *  A cell consists of one or more readouts.  It also contains a pointer to the
- *  cell's metadata, and its parent chip.  On the astrometry side, it also
- *  contains coordinate transforms from the cell to chip, from the cell to
- *  focal-plane, as well as a "quick and dirty" tranform from the cell to
- *  sky coordinates.
- *
- */
-typedef struct
-{
-psMetadata *concepts;               ///< Cache for PS concepts
-unsigned int conceptsRead;          ///< Which concepts have been read
-psMetadata *config;                 ///< Cell configuration info
-psMetadata *analysis;               ///< Cell-level analysis metadata
-psArray *readouts;                  ///< The readouts (referred to by number)
-pmChip *parent;                     ///< Parent chip
-bool process;                       ///< Do we bother about reading and working with this cell?
-bool file_exists;                   ///< Does the file for this cell exist (read case only)?
-bool data_exists;                   ///< Does the data for this cell exist (read case only)?
-pmHDU *hdu;                         ///< FITS data
-}
-pmCell;
-
-/** Readout data structure.
- *
- *  A readout is the result of a single read of a cell (or a portion thereof).
- *  It contains the offset from the lower-left corner of the chip, in the case
- *  that the CCD was windowed, as well as the binning factors and parity (if the
- *  binning value is negative, then the parity is reversed). It also contains the
- *  pixel data, metadata containers for the concepts and analysis, and a link to
- *  the parent.
- *
- */
-typedef struct
-{
-int col0;                           ///< Column offset; non-zero if reading in columns bit by bit
-int row0;                           ///< Row offset; non-zero if reading in rows bit by bit
-psImage *image;                     ///< Imaging area of readout
-psImage *mask;                      ///< Mask of input image
-psImage *weight;                    ///< Weight of input image
-psList *bias;                       ///< Overscan images
-psMetadata *analysis;               ///< Readout-level analysis metadata
-pmCell *parent;                     ///< Parent cell
-bool process;                       ///< Do we bother about reading and working with this readout?
-bool file_exists;                   ///< Does the file for this readout exist (read case only)?
-bool data_exists;                   ///< Does the data for this readout exist (read case only)?
-}
-pmReadout;
-
-void pmCellFreeReadouts(pmCell *cell);
-void pmChipFreeCells(pmChip *chip);
-
-/** Allocates a pmReadout
- *
- *  The constructor shall make an empty pmReadout. If the parent cell is not
- *  NULL, the parent link is made and the readout shall be placed in the
- *  parents array of readouts. The metadata containers shall be allocated. All
- *  other pointers in the structure shall be initialized to NULL.
- *
- *  @return pmReadout*    newly allocated pmReadout with all internal pointers set to NULL
- */
-pmReadout *pmReadoutAlloc(
-    pmCell *cell                        ///< Parent cell
-);
-
-/** Allocates a pmCell
- *
- *  The constructor shall make an empty pmCell. If the parent chip is not NULL,
- *  the parent link is made and the cell shall be placed in the parents array of
- *  cells. The readouts array shall be allocated with a zero size, and the
- *  metadata containers constructed. All other pointers in the structure shall be
- *  initialized to NULL.
- *
- *  @return pmCell*    newly allocated pmCell
- */
-pmCell *pmCellAlloc(
-    pmChip *chip,       ///< Parent chip
-    const char *name    ///< Name of cell
-);
-
-/** Allocates a pmChip
- *
- *  The constructor shall make an empty pmChip. If the parent fpa is not NULL,
- *  the parent link is made and the chip shall be placed in the parent's array
- *  of chips. The cells array shall be allocated with a zero size, and the
- *  metadata containers constructed. All other pointers in the structure shall be
- *  initialized to NULL.
- *
- *  @return pmChip*    newly allocated pmChip
- */
-pmChip *pmChipAlloc(
-    pmFPA *fpa,                         ///< FPA to which the chip belongs
-    const char *name                    ///< Name of chip
-);
-
-/** Allocates a pmFPA
- *
- *  The constructor shall make an empty pmFPA. The chips array shall be
- *  allocated with a zero size, the camera and db pointers set to the values
- *  provided, and the concepts metadata constructed. All other pointers in the
- *  structure shall be initialized to NULL.
- *
- */
-pmFPA *pmFPAAlloc(
-    const psMetadata *camera            ///< Camera configuration
-);
-
-
-/** Verify parent links.
- *
- *  This function checks the validity of the parent links in the FPA hierarchy.
- *  If a parent link is not set (or not set correctly), it is corrected, and the
- *  function shall return false. If all the parent pointers were correct, the
- *  function shall return true.
- *
- */
-bool pmFPACheckParents(
-    pmFPA *fpa
-);
-
-/* functions to turn on/off the file_exists flags */
-bool pmFPASetFileStatus (pmFPA *fpa, bool status);
-bool pmChipSetFileStatus (pmChip *chip, bool status);
-bool pmCellSetFileStatus (pmCell *cell, bool status);
-
-/* functions to turn on/off the data_exists flags */
-bool pmFPASetDataStatus (pmFPA *fpa, bool status);
-bool pmChipSetDataStatus (pmChip *chip, bool status);
-bool pmCellSetDataStatus (pmCell *cell, bool status);
-
-/** Specify the level for an operation.
- */
-typedef enum {
-    PM_FPA_LEVEL_NONE,                  ///< No particular level specified
-    PM_FPA_LEVEL_FPA,                   ///< Level corresponds to an FPA
-    PM_FPA_LEVEL_CHIP,                  ///< Level corresponds to a Chip
-    PM_FPA_LEVEL_CELL,                  ///< Level corresponds to a Cell
-    PM_FPA_LEVEL_READOUT                ///< Level corresponds to a Readout
-} pmFPALevel;
-
-
-/**
- *
- * pmFPASelectChip shall set valid to true for the specified chip number
- * (chipNum), and all other chips shall have valid set to false. In the event
- * that the specified chip number does not exist within the fpa, the function
- * shall return false.
- *
- */
-bool pmFPASelectChip(
-    pmFPA *fpa,
-    int chipNum,
-    bool exclusive
-);
-
-bool pmChipSelectCell(pmChip *chip,
-                      int cellNum,
-                      bool exclusive
-                     );
-
-/**
- *
- * pmFPAExcludeChip shall set valid to false only for the specified chip
- * number (chipNum). In the event that the specified chip number does not exist
- * within the fpa, the function shall generate a warning, and perform no action.
- * The function shall return the number of chips within the fpa that have valid
- * set to true.
- *
- */
-int pmFPAExcludeChip(
-    pmFPA *fpa,
-    int chipNum
-);
-
-int pmChipExcludeCell(pmChip *chip,
-                      int cellNum
-                     );
-
-// Set the weights and masks within a cell, based on the gain and RN
-bool pmCellSetWeights(pmCell *cell // Cell for which to set weights
-                     );
-
-
-
-#endif // #ifndef PM_FPA_H
Index: unk/psModules/src/astrom/pmFPAConceptsGet.c
===================================================================
--- /trunk/psModules/src/astrom/pmFPAConceptsGet.c	(revision 7018)
+++ 	(revision )
@@ -1,1197 +1,0 @@
-#include <stdio.h>
-#include <strings.h>
-#include <assert.h>
-#include "pslib.h"
-
-#include "pmAstrometry.h"
-#include "pmFPAConceptsGet.h"
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Private functions
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-psMetadataItem *p_pmFPAConceptGetFromCamera(pmCell *cell, // The cell
-        const char *concept // Name of concept
-                                           )
-{
-    if (cell) {
-        psMetadata *camera = cell->camera;      // Camera data
-        psMetadataItem *item = psMetadataLookup(camera, concept);
-        return item;
-    }
-    return NULL;
-}
-
-
-psMetadataItem *p_pmFPAConceptGetFromHeader(pmFPA *fpa, // The FPA that contains the chip
-        pmChip *chip, // The chip that contains the cell
-        pmCell *cell, // The cell
-        const char *concept // Name of concept
-                                           )
-{
-    bool mdStatus = true;               // Status of MD lookup
-    psMetadata *translation = psMetadataLookupMD(&mdStatus, fpa->camera, "TRANSLATION"); // FITS translation
-    if (! mdStatus) {
-        psError(PS_ERR_IO, false, "Unable to find TRANSLATION in camera configuration.\n");
-        return NULL;
-    }
-
-    // Look for how to translate the concept into a FITS header name
-    const char *keyword = psMetadataLookupStr(&mdStatus, translation, concept);
-    if (mdStatus && strlen(keyword) > 0) {
-        // We have a FITS header to look up --- search each level
-        if (cell && cell->hdu) {
-            psMetadataItem *cellItem = psMetadataLookup(cell->hdu->header, keyword);
-            if (cellItem) {
-                // XXX: Need to clean up before returning
-                return cellItem;
-            }
-        }
-
-        if (chip && chip->hdu) {
-            psMetadataItem *chipItem = psMetadataLookup(chip->hdu->header, keyword);
-            if (chipItem) {
-                // XXX: Need to clean up before returning
-                return chipItem;
-            }
-        }
-
-        if (fpa->hdu) {
-            psMetadataItem *fpaItem = psMetadataLookup(fpa->hdu->header, keyword);
-            if (fpaItem) {
-                // XXX: Need to clean up before returning
-                return fpaItem;
-            }
-        }
-
-        if (fpa->phu) {
-            psMetadataItem *fpaItem = psMetadataLookup(fpa->phu, keyword);
-            if (fpaItem) {
-                // XXX: Need to clean up before returning
-                return fpaItem;
-            }
-        }
-    }
-
-    // No header value
-    return NULL;
-}
-
-
-// Look for a default
-psMetadataItem *p_pmFPAConceptGetFromDefault(pmFPA *fpa, // The FPA that contains the chip
-        pmChip *chip, // The chip that contains the cell
-        pmCell *cell, // The cell
-        const char *concept // Name of concept
-                                            )
-{
-    bool mdOK = true;                   // Status of MD lookup
-    psMetadata *defaults = psMetadataLookupMD(&mdOK, fpa->camera, "DEFAULTS");
-    if (! mdOK) {
-        psError(PS_ERR_IO, false, "Unable to find DEFAULTS in camera configuration.\n");
-        return NULL;
-    }
-
-    psMetadataItem *defItem = psMetadataLookup(defaults, concept);
-    if (defItem) {
-        if (defItem->type == PS_DATA_METADATA) {
-            // A dependent default
-            psTrace(__func__, 7, "Evaluating dependent default....\n");
-            psMetadata *dependents = defItem->data.V; // The list of dependents
-            // Find out what it depends on
-            psString dependName = psStringCopy(concept);
-            psStringAppend(&dependName, ".DEPEND");
-            psString dependsOn = psMetadataLookupStr(&mdOK, defaults, dependName);
-            if (! mdOK) {
-                psError(PS_ERR_IO, false, "Unable to find %s in camera configuration for dependent default"
-                        " --- ignored\n", dependName);
-                // XXX: Need to clean up before returning
-                return NULL;
-            }
-            psFree(dependName);
-            // Find the value of the dependent concept
-            psMetadataItem *depItem = p_pmFPAConceptGetFromHeader(fpa, chip, cell, dependsOn);
-            if (! depItem) {
-                psError(PS_ERR_IO, true, "Unable to find value for %s (required for %s)\n", dependsOn,
-                        concept);
-                return NULL;
-            }
-            if (depItem->type != PS_DATA_STRING) {
-                psError(PS_ERR_IO, true, "Value of %s is not of type string, as required for dependency"
-                        " --- ignored.\n", dependsOn);
-            }
-
-            defItem = psMetadataLookup(dependents, depItem->data.V);    // This is now what we were after
-        }
-    }
-
-    // XXX: Need to clean up before returning
-    return defItem;                     // defItem is either NULL or points to what was desired
-}
-
-
-// Look for a database lookup
-// XXX: Not tested
-psMetadataItem *p_pmFPAConceptGetFromDB(pmFPA *fpa, // The FPA that contains the chip
-                                        pmChip *chip, // The chip that contains the cell
-                                        pmCell *cell, // The cell
-                                        psDB *db,       // DB handle
-                                        const char *concept // Name of concept
-                                       )
-{
-    if (! db) {
-        // No database initialised
-        return NULL;
-    }
-
-    bool mdStatus = true;               // Status of MD lookup
-    psMetadata *database = psMetadataLookupMD(&mdStatus, fpa->camera, "DATABASE");
-    if (! mdStatus) {
-        // No error, because not everyone needs to use the DB
-        return NULL;
-    }
-
-    psMetadata *dbLookup = psMetadataLookupMD(&mdStatus, database, concept);
-    if (dbLookup) {
-        const char *tableName = psMetadataLookupStr(&mdStatus, dbLookup, "TABLE"); // Name of the table
-        //        const char *colName = psMetadataLookupStr(&mdStatus, dbLookup, "COLUMN"); // Name of the column
-        const char *givenCols = psMetadataLookupStr(&mdStatus, dbLookup, "GIVENDBCOL"); // Name of "where"
-        // columns
-        const char *givenPS = psMetadataLookupStr(&mdStatus, dbLookup, "GIVENPS"); // Values for "where"
-        // columns
-
-        // Now, need to get the "given"s
-        if (strlen(givenCols) || strlen(givenPS)) {
-            psList *cols = psStringSplit(givenCols, ",;", true); // List of column names
-            psList *values = psStringSplit(givenPS, ",;", true); // List of value names for the columns
-            psMetadata *selection = psMetadataAlloc(); // The stuff to select in the DB
-            if (cols->n != values->n) {
-                psLogMsg(__func__, PS_LOG_WARN, "The GIVENDBCOL and GIVENPS entries for %s do not have "
-                         "the same number of entries --- ignored.\n", concept);
-            } else {
-                // Iterators for the lists
-                psListIterator *colsIter = psListIteratorAlloc(cols, PS_LIST_HEAD, false);
-                psListIterator *valuesIter = psListIteratorAlloc(values, PS_LIST_HEAD, false);
-                char *column = NULL;    // Name of the column
-                while ((column = psListGetAndIncrement(colsIter))) {
-                    char *name = psListGetAndIncrement(valuesIter); // Name for the value
-                    if (!strlen(column) || !strlen(name)) {
-                        psLogMsg(__func__, PS_LOG_WARN, "One of the columns or value names for %s is "
-                                 " empty --- ignored.\n", concept);
-                    } else {
-                        // Search for the value name
-                        psMetadataItem *item = p_pmFPAConceptGetFromHeader(fpa, chip, cell, name);
-                        if (! item) {
-                            item = p_pmFPAConceptGetFromDefault(fpa, chip, cell, name);
-                        }
-                        if (! item) {
-                            psLogMsg(__func__, PS_LOG_ERROR, "Unable to find the value name %s for DB "
-                                     " lookup on %s --- ignored.\n", name, concept);
-                        } else {
-                            // We need to create a new psMetadataItem.  I don't think we can't simply hack
-                            // the existing one, since that could conceivably cause memory leaks
-                            psMetadataItem *newItem = psMetadataItemAlloc(concept, item->type,
-                                                      item->comment, item->data.V);
-                            psMetadataAddItem(selection, newItem, PS_LIST_TAIL, PS_META_REPLACE);
-                            psFree(newItem);
-                        }
-                    }
-                    psFree(name);
-                    psFree(column);
-                } // Iterating through the columns
-                psFree(colsIter);
-                psFree(valuesIter);
-
-                psArray *dbResult = psDBSelectRows(db, tableName, selection, 2); // Lookup result
-                // Note that we use limit=2 in order to test if there are multiple rows returned
-
-                psMetadataItem *result = NULL; // The final result of the DB lookup
-                if (dbResult->n == 0) {
-                    psLogMsg(__func__, PS_LOG_WARN, "Unable to find any rows in DB for %s --- ignored\n",
-                             concept);
-                } else {
-                    if (dbResult-> n > 1) {
-                        psLogMsg(__func__, PS_LOG_WARN, "Multiple rows returned in DB lookup for %s --- "
-                                 " using the first one only.\n", concept);
-                    }
-                    result = (psMetadataItem*)dbResult->data[0];
-                }
-                // XXX: Need to clean up before returning
-                return result;
-            }
-            psFree(cols);
-            psFree(values);
-        }
-    } // Doing the "given"s.
-
-    psAbort(__func__, "Shouldn't ever get here.\n");
-    return NULL;
-}
-
-
-// Concept lookup
-psMetadataItem *p_pmFPAConceptGet(pmFPA *fpa, // The FPA
-                                  pmChip *chip,// The chip
-                                  pmCell *cell, // The cell
-                                  psDB *db, // DB handle
-                                  const char *concept // Concept name
-                                 )
-{
-    // Try headers, database, defaults in order
-    psMetadataItem *item = p_pmFPAConceptGetFromCamera(cell, concept);
-    if (! item) {
-        item = p_pmFPAConceptGetFromHeader(fpa, chip, cell, concept);
-    }
-    if (! item) {
-        item = p_pmFPAConceptGetFromDB(fpa, chip, cell, db, concept);
-    }
-    if (! item) {
-        item = p_pmFPAConceptGetFromDefault(fpa, chip, cell, concept);
-    }
-    return item; // item is either NULL, or points to what was desired
-}
-
-
-void p_pmFPAConceptGetF32(pmFPA *fpa,   // The FPA
-                          pmChip *chip, // The chip
-                          pmCell *cell, // The cell
-                          psDB *db,     // DB handle
-                          psMetadata *concepts, // The concepts MD
-                          const char *name, // Name of the concept
-                          const char *comment // Comment for concept
-                         )
-{
-    psMetadataItem *item = p_pmFPAConceptGet(fpa, chip, cell, db, name);
-    float value = NAN;
-    if (item) {
-        switch (item->type) {
-        case PS_DATA_F32:
-            value = item->data.F32;
-            break;
-        case PS_DATA_F64:
-            // Assume it's OK to truncate to floating point from double
-            value = (float)item->data.F64;
-            break;
-        case PS_DATA_S32:
-            // Promote to float
-            value = (float)item->data.S32;
-            break;
-        default:
-            psError(PS_ERR_IO, true, "Concept %s (%s) is not of floating point type (%x) --- treating as "
-                    "undefined.\n", name, comment, item->type);
-        }
-    } else {
-        psError(PS_ERR_IO, true, "Concept %s (%s) is not defined.\n", name, comment);
-    }
-    psTrace(__func__, 7, "Adding %s (%s): %f\n", name, comment, value);
-
-    psMetadataAdd(concepts, PS_LIST_TAIL, name, PS_TYPE_F32 | PS_META_REPLACE, comment, value);
-}
-
-void p_pmFPAConceptGetF64(pmFPA *fpa,   // The FPA
-                          pmChip *chip, // The chip
-                          pmCell *cell, // The cell
-                          psDB *db,     // DB handle
-                          psMetadata *concepts, // The concepts MD
-                          const char *name, // Name of the concept
-                          const char *comment // Comment for concept
-                         )
-{
-    psMetadataItem *item = p_pmFPAConceptGet(fpa, chip, cell, db, name);
-    double value = NAN;
-    if (item) {
-        switch (item->type) {
-        case PS_TYPE_F64:
-            value = item->data.F64;
-            break;
-        case PS_TYPE_F32:
-            // Promote to double
-            value = (double)item->data.F32;
-            break;
-        case PS_TYPE_S32:
-            // Promote to double
-            value = (double)item->data.S32;
-            break;
-        default:
-            psError(PS_ERR_IO, true, "Concept %s (%s) is not of double-precision floating point type (%x) "
-                    "--- treating as undefined.\n", name, comment, item->type);
-        }
-    } else {
-        psError(PS_ERR_IO, true, "Concept %s (%s) is not defined.\n", name, comment);
-    }
-    psTrace(__func__, 7, "Adding %s (%s): %f\n", name, comment, value);
-
-    psMetadataAdd(concepts, PS_LIST_TAIL, name, PS_TYPE_F64 | PS_META_REPLACE, comment, value);
-}
-
-void p_pmFPAConceptGetS32(pmFPA *fpa,   // The FPA
-                          pmChip *chip, // The chip
-                          pmCell *cell, // The cell
-                          psDB *db,     // DB handle
-                          psMetadata *concepts, // The concepts MD
-                          const char *name, // Name of the concept
-                          const char *comment // Comment for concept
-                         )
-{
-    psMetadataItem *item = p_pmFPAConceptGet(fpa, chip, cell, db, name);
-    int value = 0;
-    if (item) {
-        switch (item->type) {
-        case PS_TYPE_S32:
-            value = item->data.S32;
-            break;
-        case PS_TYPE_F32:
-            psLogMsg(__func__, PS_LOG_WARN, "Concept %s (%s) should be S32, but is F32 --- converting.\n",
-                     name, comment);
-            value = (int)item->data.F32;
-            break;
-        case PS_TYPE_F64:
-            psLogMsg(__func__, PS_LOG_WARN, "Concept %s (%s) should be S32, but is F64 --- converting.\n",
-                     name, comment);
-            value = (int)item->data.F64;
-            break;
-        default:
-            psError(PS_ERR_IO, true, "Concept %s (%s) is not of integer type (%x) --- treating as "
-                    "undefined.\n", name, comment, item->type);
-        }
-    } else {
-        psError(PS_ERR_IO, true, "Concept %s (%s) is not defined.\n", name, comment);
-    }
-    psTrace(__func__, 7, "Adding %s (%s): %d\n", name, comment, value);
-
-    psMetadataAdd(concepts, PS_LIST_TAIL, name, PS_TYPE_S32 | PS_META_REPLACE, comment, value);
-}
-
-void p_pmFPAConceptGetString(pmFPA *fpa, // The FPA
-                             pmChip *chip, // The chip
-                             pmCell *cell, // The cell
-                             psDB *db,  // DB handle
-                             psMetadata *concepts, // The concepts MD
-                             const char *name, // Name of the concept
-                             const char *comment // Comment for concept
-                            )
-{
-    psMetadataItem *item = p_pmFPAConceptGet(fpa, chip, cell, db, name);
-    psString value = NULL;
-    if (item) {
-        switch (item->type) {
-        case PS_DATA_STRING:
-            value = psMemIncrRefCounter(item->data.V);
-            break;
-        case PS_DATA_F32:
-            psStringAppend(&value, "%f", item->data.F32);
-            break;
-        case PS_DATA_S32:
-            psStringAppend(&value, "%d", item->data.S32);
-            break;
-        default:
-            psError(PS_ERR_IO, true, "Concept %s (%s) is not of string type (%x) --- treating as "
-                    "undefined.\n", name, comment, item->type);
-        }
-    } else {
-        psError(PS_ERR_IO, true, "Concept %s (%s) is not defined.\n", name, comment);
-        value = psStringCopy("");
-    }
-    psTrace(__func__, 7, "Adding %s (%s): %s\n", name, comment, value);
-
-    psMetadataAdd(concepts, PS_LIST_TAIL, name, PS_DATA_STRING | PS_META_REPLACE, comment, value);
-    psFree(value);
-}
-
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Public functions
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Ingest concepts for the FPA
-void pmFPAIngestConcepts(pmFPA *fpa,    // The FPA
-                         psDB *db       // DB handle
-                        )
-{
-    if (! fpa->concepts) {
-        fpa->concepts = psMetadataAlloc();
-    }
-
-    // FPA.NAME
-    p_pmFPAConceptGetString(fpa, NULL, NULL, db, fpa->concepts, "FPA.NAME", "Name of FPA");
-
-    // FPA.AIRMASS
-    p_pmFPAConceptGetF32(fpa, NULL, NULL, db, fpa->concepts, "FPA.AIRMASS", "Airmass at boresight");
-
-    // FPA.FILTER
-    p_pmFPAConceptGetString(fpa, NULL, NULL, db, fpa->concepts, "FPA.FILTER", "Filter used");
-
-    // FPA.POSANGLE
-    p_pmFPAConceptGetF32(fpa, NULL, NULL, db, fpa->concepts, "FPA.POSANGLE", "Position angle for instrument");
-
-    // FPA.RADECSYS
-    p_pmFPAConceptGetString(fpa, NULL, NULL, db, fpa->concepts, "FPA.RADECSYS", "Celestial coordinate system");
-
-    // These take some extra work
-
-    // FPA.RA
-    {
-        double ra = NAN;                // The RA
-        psMetadataItem *raItem = p_pmFPAConceptGet(fpa, NULL, NULL, db, "FPA.RA"); // The FPA.RA item
-        if (raItem)
-        {
-            switch (raItem->type) {
-            case PS_TYPE_F32:
-                ra = raItem->data.F32;
-                break;
-            case PS_TYPE_F64:
-                ra = raItem->data.F64;
-                break;
-            case PS_DATA_STRING:
-                // Sexagesimal format
-                {
-                    int big, medium;
-                    float small;
-                    // XXX: Upgrade path is to allow dd:mm.mmm
-                    if (sscanf(raItem->data.V, "%d:%d:%f", &big, &medium, &small) != 3 &&
-                            sscanf(raItem->data.V, "%d %d %f", &big, &medium, &small) != 3)
-                    {
-                        psError(PS_ERR_IO, true, "Cannot interpret FPA.RA: %s\n", raItem->data.V);
-                        break;
-                    }
-                    ra = abs(big) + (float)medium/60.0 + small/3600.0;
-                    if (big < 0)
-                    {
-                        ra *= -1.0;
-                    }
-                }
-                break;
-            default:
-                psError(PS_ERR_IO, true, "FPA.RA is of an unexpected type: %x\n", raItem->type);
-            }
-
-            // How to interpret the RA
-            const psMetadata *camera = fpa->camera; // Camera configuration data
-            bool mdok = true;           // Status of MD lookup
-            psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS");
-            if (mdok && formats) {
-                psString raFormat = psMetadataLookupStr(&mdok, formats, "FPA.RA");
-                if (mdok && strlen(raFormat) > 0) {
-                    if (strcasecmp(raFormat, "HOURS") == 0) {
-                        ra *= M_PI / 12.0;
-                    } else if (strcasecmp(raFormat, "DEGREES") == 0) {
-                        ra *= M_PI / 180.0;
-                    } else if (strcasecmp(raFormat, "RADIANS") == 0) {
-                        // No action required
-                    } else {
-                        psLogMsg(__func__, PS_LOG_WARN, "Don't understand FPA.RA in FORMATS (%s) --- assuming"
-                                 " HOURS.\n");
-                        ra *= M_PI / 12.0;
-                    }
-                } else {
-                    psError(PS_ERR_IO, false, "Unable to find FPA.RA in FORMATS --- assuming HOURS.\n");
-                    ra *= M_PI / 12.0;
-                }
-            } else {
-                psError(PS_ERR_IO, false, "Unable to find FORMAT metadata in camera configuration --- "
-                        "assuming format for FPA.RA is HOURS.\n");
-                ra *= M_PI / 12.0;
-            }
-        } else
-        {
-            psError(PS_ERR_IO, false, "Couldn't find FPA.RA.\n");
-        }
-
-        psMetadataAdd(fpa->concepts, PS_LIST_TAIL, "FPA.RA", PS_DATA_F64 | PS_META_REPLACE,
-                      "Right Ascension of the boresight (radians)", ra);
-
-    }
-
-    // FPA.DEC
-    {
-        double dec = NAN;               // The DEC
-        psMetadataItem *decItem = p_pmFPAConceptGet(fpa, NULL, NULL, db, "FPA.DEC"); // The FPA.DEC item
-        if (decItem) {
-            switch (decItem->type) {
-            case PS_TYPE_F32:
-                dec = decItem->data.F32;
-                break;
-            case PS_TYPE_F64:
-                dec = decItem->data.F64;
-                break;
-            case PS_DATA_STRING:
-                // Sexagesimal format
-                {
-                    int big, medium;
-                    float small;
-                    // XXX: Upgrade path is to allow dd:mm.mmm
-                    if (sscanf(decItem->data.V, "%d:%d:%f", &big, &medium, &small) != 3 &&
-                            sscanf(decItem->data.V, "%d %d %f", &big, &medium, &small) != 3)
-                    {
-                        psError(PS_ERR_IO, true, "Cannot interpret FPA.DEC: %s\n", decItem->data.V);
-                        break;
-                    }
-                    dec = abs(big) + (float)medium/60.0 + small/3600.0;
-                    if (big < 0)
-                    {
-                        dec *= -1.0;
-                    }
-                }
-                break;
-            default:
-                psError(PS_ERR_IO, true, "FPA.DEC is of an unexpected type: %x\n", decItem->type);
-            }
-
-            // How to interpret the DEC
-            const psMetadata *camera = fpa->camera; // Camera configuration data
-            bool mdok = true;           // Status of MD lookup
-            psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS");
-            if (mdok && formats) {
-                psString decFormat = psMetadataLookupStr(&mdok, formats, "FPA.DEC");
-                if (mdok && strlen(decFormat) > 0) {
-                    if (strcasecmp(decFormat, "HOURS") == 0) {
-                        dec *= M_PI / 12.0;
-                    } else if (strcasecmp(decFormat, "DEGREES") == 0) {
-                        dec *= M_PI / 180.0;
-                    } else if (strcasecmp(decFormat, "RADIANS") == 0) {
-                        // No action required
-                    } else {
-                        psLogMsg(__func__, PS_LOG_WARN, "Don't understand FPA.DEC in FORMATS (%s) --- "
-                                 "assuming DEGREES.\n");
-                        dec *= M_PI / 180.0;
-                    }
-                } else {
-                    psError(PS_ERR_IO, false, "Unable to find FPA.DEC in FORMATS --- assuming DEGREES.\n");
-                    dec *= M_PI / 180.0;
-                }
-            } else {
-                psError(PS_ERR_IO, false, "Unable to find FORMATS metadata in camera configuration --- "
-                        "assuming format for FPA.DEC is DEGREES.\n");
-                dec *= M_PI / 180.0;
-            }
-        } else {
-            psError(PS_ERR_IO, false, "Couldn't find FPA.DEC.\n");
-        }
-
-        psMetadataAdd(fpa->concepts, PS_LIST_TAIL, "FPA.DEC", PS_DATA_F64 | PS_META_REPLACE,
-                      "Declination of the boresight (radians)", dec);
-
-    }
-
-    // Pau.
-}
-
-
-// Ingest concepts for the chip
-bool pmChipIngestConcepts(pmChip *chip, // The chip
-                          psDB *db      // DB handle
-                         )
-{
-    //    pmFPA *fpa = chip->parent;          // The parent FPA
-
-    if (! chip->concepts) {
-        chip->concepts = psMetadataAlloc();
-    }
-
-    // CHIP.NAME --- added by pmFPAConstruct
-
-    // Pau.
-    return true;
-}
-
-
-// Add corrective to a position --- in case the user wants FORTRAN indexing (like the FITS standard...)
-static bool correctPosition(pmFPA *fpa, // FPA, contains the camera configuration
-                            pmCell *cell, // Cell containing the concept to correct
-                            const char *conceptName // Name of concept to correct
-                           )
-{
-    bool mdok = false;              // Result of MD lookup
-    psMetadata *formats = psMetadataLookupMD(&mdok, fpa->camera, "FORMATS");
-    if (mdok && formats) {
-        psString format = psMetadataLookupStr(&mdok, formats, conceptName);
-        if (mdok && strlen(format) > 0 && strcasecmp(format, "FORTRAN") == 0) {
-            psMetadataItem *valueItem = psMetadataLookup(cell->concepts, conceptName);
-            valueItem->data.S32 -= 1;
-        }
-    }
-    return true;
-}
-
-bool p_pmCellIngestConcept(pmFPA *fpa,  // The FPA
-                           pmChip *chip, // The chip
-                           pmCell *cell, // The cell
-                           psDB *db,    // DB handle
-                           const char *concept // Name of the concept
-                          )
-{
-    if (strcmp(concept, "CELL.GAIN") == 0) {
-        p_pmFPAConceptGetF32(fpa, chip, cell, db, cell->concepts, "CELL.GAIN", "CCD gain (e/count)");
-    } else if (strcmp(concept, "CELL.READNOISE") == 0) {
-        p_pmFPAConceptGetF32(fpa, chip, cell, db, cell->concepts, "CELL.READNOISE", "CCD read noise (e)");
-    } else if (strcmp(concept, "CELL.SATURATION") == 0) {
-        p_pmFPAConceptGetF32(fpa, chip, cell, db, cell->concepts, "CELL.SATURATION",
-                             "Saturation level (ADU)");
-    } else if (strcmp(concept, "CELL.BAD") == 0) {
-        p_pmFPAConceptGetF32(fpa, chip, cell, db, cell->concepts, "CELL.BAD", "Bad level (ADU)");
-    } else if (strcmp(concept, "CELL.XPARITY") == 0) {
-        p_pmFPAConceptGetS32(fpa, chip, cell, db, cell->concepts, "CELL.XPARITY",
-                             "Orientation in x compared to the rest of the FPA");
-    } else if (strcmp(concept, "CELL.YPARITY") == 0) {
-        p_pmFPAConceptGetS32(fpa, chip, cell, db, cell->concepts, "CELL.YPARITY",
-                             "Orientation in y compared to the rest of the FPA");
-    } else if (strcmp(concept, "CELL.READDIR") == 0) {
-        p_pmFPAConceptGetS32(fpa, chip, cell, db, cell->concepts, "CELL.READDIR",
-                             "Read direction: 1=row, 2=col");
-    } else if (strcmp(concept, "CELL.EXPOSURE") == 0) { // used to be READOUT.EXPOSURE
-        p_pmFPAConceptGetF32(fpa, chip, cell, db, cell->concepts, "CELL.EXPOSURE", "Exposure time (sec)");
-    } else if (strcmp(concept, "CELL.DARKTIME") == 0) { // used to be READOUT.DARKTIME
-        p_pmFPAConceptGetF32(fpa, chip, cell, db, cell->concepts, "CELL.DARKTIME",
-                             "Time since CCD flush (sec)");
-        // These take some extra work
-    } else if (strcmp(concept, "CELL.TRIMSEC") == 0) {
-        psRegion *trimsec = psAlloc(sizeof(psRegion)); // Make space for a psRegion (usually passed by value)
-
-        psMetadataItem *secItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.TRIMSEC");
-        if (! secItem) {
-            psError(PS_ERR_IO, false, "Couldn't find CELL.TRIMSEC.\n");
-            *trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
-        } else if (secItem->type != PS_DATA_STRING) {
-            psError(PS_ERR_IO, true, "CELL.TRIMSEC is not of type STR (%x)\n", secItem->type);
-            *trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
-        } else {
-            psString section = secItem->data.V; // The section string
-
-            psMetadataItem *sourceItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.TRIMSEC.SOURCE");
-            if (! sourceItem) {
-                psError(PS_ERR_IO, false, "Couldn't find CELL.TRIMSEC.SOURCE.\n");
-                *trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
-            } else if (sourceItem->type != PS_DATA_STRING) {
-                psError(PS_ERR_IO, true, "CELL.TRIMSEC.SOURCE is not of type STR (%x)\n", sourceItem->type);
-                *trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
-            } else {
-                psString source = sourceItem->data.V; // The source string
-
-                if (strcasecmp(source, "VALUE") == 0) {
-                    *trimsec = psRegionFromString(section);
-                } else if (strcasecmp(source, "HEADER") == 0) {
-                    psMetadata *header = NULL; // The FITS header
-                    if (cell->hdu) {
-                        header = cell->hdu->header;
-                    } else if (chip->hdu) {
-                        header = chip->hdu->header;
-                    } else if (fpa->hdu) {
-                        header = fpa->hdu->header;
-                    }
-                    if (! header) {
-                        psError(PS_ERR_IO, true, "Unable to find FITS header!\n");
-                        *trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
-                    } else {
-                        bool mdok = true;               // Status of MD lookup
-                        psString secValue = psMetadataLookupStr(&mdok, header, section);
-                        if (! mdok || ! secValue) {
-                            psError(PS_ERR_IO, false, "Unable to locate header %s\n", section);
-                            *trimsec = psRegionSet(0.0, 0.0, 0.0, 0.0);
-                        } else {
-                            *trimsec = psRegionFromString(secValue);
-                        }
-                    }
-                } else {
-                    psError(PS_ERR_IO, true, "CELL.TRIMSEC.SOURCE (%s) is not HEADER or VALUE --- trying "
-                            "VALUE.\n", source);
-                    *trimsec = psRegionFromString(section);
-                } // Value of CELL.TRIMSEC.SOURCE
-            } // Looking up CELL.TRIMSEC.SOURCE
-        } // Looking up CELL.TRIMSEC
-
-        psMetadataAdd(cell->concepts, PS_LIST_TAIL, "CELL.TRIMSEC", PS_DATA_UNKNOWN | PS_META_REPLACE,
-                      "Trim section", trimsec);
-        psFree(trimsec);
-
-    } else if (strcmp(concept, "CELL.BIASSEC") == 0) {
-        psList *biassecs = psListAlloc(NULL); // List of bias sections
-
-        psMetadataItem *secItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.BIASSEC");
-        if (! secItem) {
-            psError(PS_ERR_IO, false, "Couldn't find CELL.BIASSEC.\n");
-        } else if (secItem->type != PS_DATA_STRING) {
-            psError(PS_ERR_IO, true, "CELL.BIASSEC is not of type STR (%x)\n", secItem->type);
-        } else {
-            psString sections = secItem->data.V; // The section string
-
-            psMetadataItem *sourceItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.BIASSEC.SOURCE");
-            if (! sourceItem) {
-                psError(PS_ERR_IO, false, "Couldn't find CELL.BIASSEC.SOURCE.\n");
-            } else if (sourceItem->type != PS_DATA_STRING) {
-                psError(PS_ERR_IO, true, "CELL.BIASSEC.SOURCE is not of type STR (%x)\n", sourceItem->type);
-            } else {
-                psString source = sourceItem->data.V; // The source string
-
-                psList *secList = psStringSplit(sections, " ;", true); // List of sections
-                psListIterator *secIter = psListIteratorAlloc(secList, PS_LIST_HEAD, false); // Iterator over
-                // sections
-                psString aSection = NULL; // A section from the list
-                while ((aSection = psListGetAndIncrement(secIter))) {
-                    psRegion *region = psAlloc(sizeof(psRegion)); // Make space for a psRegion (usually passed
-                    // by value)
-
-                    if (strcasecmp(source, "VALUE") == 0) {
-                        *region = psRegionFromString(aSection);
-                    } else if (strcasecmp(source, "HEADER") == 0) {
-                        psMetadata *header = NULL; // The FITS header
-                        if (cell->hdu) {
-                            header = cell->hdu->header;
-                        } else if (chip->hdu) {
-                            header = chip->hdu->header;
-                        } else if (fpa->hdu) {
-                            header = fpa->hdu->header;
-                        }
-                        if (! header) {
-                            psError(PS_ERR_IO, true, "Unable to find FITS header!\n");
-                            *region = psRegionSet(0.0,0.0,0.0,0.0);
-                        } else {
-                            bool mdok = true;           // Status of MD lookup
-                            psString secValue = psMetadataLookupStr(&mdok, header, aSection);
-                            if (! mdok || ! secValue) {
-                                psError(PS_ERR_IO, false, "Unable to locate header %s\n", aSection);
-                                *region = psRegionSet(0.0,0.0,0.0,0.0);
-                            } else {
-                                *region = psRegionFromString(secValue);
-                            }
-                        }
-                    } else {
-                        psError(PS_ERR_IO, true, "CELL.BIASSEC.SOURCE (%s) is not HEADER or VALUE --- trying "
-                                "VALUE.\n", source);
-                        *region = psRegionFromString(aSection);
-                    } // Value of CELL.BIASSEC.SOURCE
-
-                    psListAdd(biassecs, PS_LIST_TAIL, region);
-                    psFree(region);
-                } // Iterating over multiple sections
-                psFree(secIter);
-                psFree(secList);
-            } // Looking up CELL.BIASSEC.SOURCE
-        } // Looking up CELL.BIASSEC
-
-        psMetadataAdd(cell->concepts, PS_LIST_TAIL, "CELL.BIASSEC", PS_DATA_LIST | PS_META_REPLACE,
-                      "Bias sections", biassecs);
-        psFree(biassecs);
-
-    } else if (strcmp(concept, "CELL.XBIN") == 0) {
-        int xBin = 1;                   // Binning factor in x
-        psMetadataItem *binItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.XBIN");
-        if (! binItem) {
-            psError(PS_ERR_IO, false, "Couldn't find CELL.XBIN.\n");
-        } else if (binItem->type == PS_DATA_STRING) {
-            psString binString = binItem->data.V; // The string containing the binning
-            if (sscanf(binString, "%d %*d", &xBin) != 1 &&
-                    sscanf(binString, "%d,%*d", &xBin) != 1) {
-                psError(PS_ERR_IO, true, "Unable to read string to get x binning: %s\n", binString);
-            }
-        } else if (binItem->type == PS_TYPE_S32) {
-            xBin = binItem->data.S32;
-        } else {
-            psError(PS_ERR_IO, true, "Note sure how to interpret CELL.XBIN of type %x --- assuming 1.\n",
-                    binItem->type);
-        }
-
-        psMetadataAdd(cell->concepts, PS_LIST_TAIL, "CELL.XBIN", PS_TYPE_S32 | PS_META_REPLACE,
-                      "Binning in x", xBin);
-
-    } else if (strcmp(concept, "CELL.YBIN") == 0) {
-        int yBin = 1;                   // Binning factor in y
-        psMetadataItem *binItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.YBIN");
-        if (! binItem) {
-            psError(PS_ERR_IO, false, "Couldn't find CELL.YBIN.\n");
-        } else if (binItem->type == PS_DATA_STRING) {
-            psString binString = binItem->data.V; // The string containing the binning
-            if (sscanf(binString, "%*d %d", &yBin) != 1 &&
-                    sscanf(binString, "%*d,%d", &yBin) != 1) {
-                psError(PS_ERR_IO, true, "Unable to read string to get y binning: %s\n", binString);
-            }
-        } else if (binItem->type == PS_TYPE_S32) {
-            yBin = binItem->data.S32;
-        } else {
-            psError(PS_ERR_IO, true, "Note sure how to interpret CELL.YBIN of type %x --- assuming 1.\n",
-                    binItem->type);
-        }
-
-        psMetadataAdd(cell->concepts, PS_LIST_TAIL, "CELL.YBIN", PS_TYPE_S32 | PS_META_REPLACE,
-                      "Binning in y", yBin);
-
-    } else if (strcmp(concept, "CELL.TIME") == 0 || strcmp(concept, "CELL.TIMESYS") == 0) {
-        // Do CELL.TIME and CELL.TIMESYS together
-        psTime *time = NULL;            // The time
-        psTimeType timeSys = PS_TIME_UTC; // The time system
-
-        // CELL.TIMESYS
-        psMetadataItem *sysItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.TIMESYS");
-        if (! sysItem) {
-            psError(PS_ERR_IO, true, "Couldn't find CELL.TIMESYS --- assuming UTC.\n");
-        } else if (sysItem->type != PS_DATA_STRING) {
-            psError(PS_ERR_IO, true, "CELL.TIMESYS isn't of type STRING --- assuming UTC.\n");
-        } else {
-            psString sys = sysItem->data.V; // The time system string
-            if (strcasecmp(sys, "TAI") == 0) {
-                timeSys = PS_TIME_TAI;
-            } else if (strcasecmp(sys, "UTC") == 0) {
-                timeSys = PS_TIME_UTC;
-            } else if (strcasecmp(sys, "UT1") == 0) {
-                timeSys = PS_TIME_UT1;
-            } else if (strcasecmp(sys, "TT") == 0) {
-                timeSys = PS_TIME_TT;
-            } else {
-                psError(PS_ERR_IO, true, "Can't interpret CELL.TIMESYS --- assuming UTC.\n");
-            }
-        }
-        psMetadataAdd(cell->concepts, PS_LIST_TAIL, "CELL.TIMESYS", PS_TYPE_S32 | PS_META_REPLACE,
-                      "Time system", timeSys);
-
-        psMetadataItem *timeItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.TIME");
-        if (! timeItem) {
-            psError(PS_ERR_IO, false, "Couldn't find CELL.TIME.\n");
-        } else {
-            // Get format
-            const psMetadata *camera = fpa->camera; // The camera configuration data
-            bool mdok = true;           // Status of MD lookup
-            psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS");
-            if (mdok && formats) {
-                psString timeFormat = psMetadataLookupStr(&mdok, formats, "CELL.TIME");
-                if (mdok && strlen(timeFormat) > 0) {
-                    switch (timeItem->type) {
-                    case PS_DATA_STRING: {
-                            psString timeString = timeItem->data.V;     // String with the time
-                            if (strcasecmp(timeFormat, "ISO") == 0) {
-                                // timeString contains an ISO time
-                                time = psTimeFromISO(timeString, timeSys);
-                            } else if (strcasecmp(timeFormat, "JD") == 0) {
-                                double timeValue = strtod (timeString, NULL);
-                                time = psTimeFromJD(timeValue);
-                            } else if (strcasecmp(timeFormat, "MJD") == 0) {
-                                double timeValue = strtod (timeString, NULL);
-                                time = psTimeFromMJD(timeValue);
-                            } else if (strstr(timeFormat, "SEPARATE")) {
-                                // timeString contains headers for the date and time
-                                psMetadata *header = NULL; // The FITS header
-                                if (cell->hdu) {
-                                    header = cell->hdu->header;
-                                } else if (chip->hdu) {
-                                    header = chip->hdu->header;
-                                } else if (fpa->hdu) {
-                                    header = fpa->hdu->header;
-                                }
-                                if (! header) {
-                                    psError(PS_ERR_IO, true, "Unable to find FITS header!\n");
-                                } else {
-                                    // Get the headers
-                                    char *stuff1 = strpbrk(timeString, " ,;");
-                                    psString dateName = psStringNCopy(timeString,
-                                                                      strlen(timeString) - strlen(stuff1));
-                                    char *stuff2 = strpbrk(stuff1, "abcdefghijklmnopqrstuvwxyz"
-                                                           "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
-                                    psString timeName = psStringCopy(stuff2);
-
-                                    bool mdok = true; // Status of MD lookup
-                                    psString dateString = psMetadataLookupStr(&mdok, header, dateName);
-                                    psFree(dateName);
-                                    int day = 0, month = 0, year = 0;
-                                    if (sscanf(dateString, "%d-%d-%d", &day, &month, &year) != 3 &&
-                                            sscanf(dateString, "%d/%d/%d", &day, &month, &year) != 3) {
-                                        psError(PS_ERR_IO, true, "Unable to read date: %s\n", dateString);
-                                    } else {
-                                        if (strstr(timeFormat, "BACKWARDS")) {
-                                            int temp = day;
-                                            day = year;
-                                            year = temp;
-                                        }
-                                        if (strstr(timeFormat, "PRE2000") || year < 2000) {
-                                            year += 2000;
-                                        }
-
-                                        psMetadataItem *timeItem = psMetadataLookup(header, timeName);
-                                        if (! timeItem) {
-                                            psError(PS_ERR_IO, false, "Unable to find time header: %s\n",
-                                                    timeName);
-                                        } else if (timeItem->type == PS_DATA_STRING) {
-                                            // Time is a string, in the usual way:
-                                            psStringAppend(&dateString, "T%s", timeItem->data.V);
-                                        } else {
-                                            // Assume that time is specified in Second of Day
-                                            double seconds = NAN;
-                                            switch (timeItem->type) {
-                                            case PS_TYPE_S32:
-                                                seconds = timeItem->data.S32;
-                                                break;
-                                            case PS_TYPE_F32:
-                                                seconds = timeItem->data.F32;
-                                                break;
-                                            case PS_TYPE_F64:
-                                                seconds = timeItem->data.F64;
-                                                break;
-                                            default:
-                                                psError(PS_ERR_IO, true, "Time header (%s) is not of an "
-                                                        "expected type: %x\n", timeName, timeItem->type);
-                                            }
-                                            // Now print to timeString as "hh:mm:ss.ss"
-                                            int hours = seconds / 3600;
-                                            seconds -= (double)hours * 3600.0;
-                                            int minutes = seconds / 60;
-                                            seconds -= (double)minutes * 60.0;
-                                            psStringAppend(&dateString, "T%02d:%02d:%02f", hours, minutes,
-                                                           seconds);
-                                        }
-                                        time = psTimeFromISO(dateString, timeSys);
-                                    } // Reading date and time
-                                    psFree(timeName);
-                                } // Reading headers
-                            } else {
-                                psError(PS_ERR_IO, true, "Not sure how to parse CELL.TIME (%s) --- trying "
-                                        "ISO\n", timeString);
-                                time = psTimeFromISO(timeString, timeSys);
-                            } // Interpreting the time string
-                        }
-                        break;
-                    case PS_TYPE_F32: {
-                            double timeValue = (double)timeItem->data.F32;
-                            if (strcasecmp(timeFormat, "JD") == 0) {
-                                time = psTimeFromJD(timeValue);
-                            } else if (strcasecmp(timeFormat, "MJD") == 0) {
-                                time = psTimeFromMJD(timeValue);
-                            } else {
-                                psError(PS_ERR_IO, true, "Not sure how to parse CELL.TIME (%f) --- trying "
-                                        "JD\n", timeValue);
-                                time = psTimeFromJD(timeValue);
-                            }
-                        }
-                        break;
-                    case PS_TYPE_F64: {
-                            double timeValue = (double)timeItem->data.F64;
-                            if (strcasecmp(timeFormat, "JD") == 0) {
-                                time = psTimeFromJD(timeValue);
-                            } else if (strcasecmp(timeFormat, "MJD") == 0) {
-                                time = psTimeFromMJD(timeValue);
-                            } else {
-                                psError(PS_ERR_IO, true, "Not sure how to parse CELL.TIME (%f) --- trying "
-                                        "JD\n", timeValue);
-                                time = psTimeFromJD(timeValue);
-                            }
-                        }
-                        break;
-                    default:
-                        psError(PS_ERR_IO, true, "Unable to parse CELL.TIME.\n");
-                    }
-                } else {
-                    psError(PS_ERR_IO, false, "Unable to find CELL.TIME in FORMATS.\n");
-                } // Getting the format
-            } else {
-                psError(PS_ERR_IO, false, "Unable to find FORMATS in camera configuration.\n");
-            } // Getting the formats
-        } // Getting CELL.TIME
-
-        psMetadataAdd(cell->concepts, PS_LIST_TAIL, "CELL.TIME", PS_DATA_TIME | PS_META_REPLACE,
-                      "Time of exposure", time);
-        psFree(time);
-
-        // These are new and experimental concepts: CELL.X0 and CELL.Y0
-    } else if (strcmp(concept, "CELL.X0") == 0) {
-        p_pmFPAConceptGetS32(fpa, chip, cell, db, cell->concepts, "CELL.X0", "Position of (0,0) on the chip");
-        correctPosition(fpa, cell, "CELL.X0");
-    } else if (strcmp(concept, "CELL.Y0") == 0) {
-        p_pmFPAConceptGetS32(fpa, chip, cell, db, cell->concepts, "CELL.Y0", "Position of (0,0) on the chip");
-        correctPosition(fpa, cell, "CELL.Y0");
-    }
-
-    return true;
-}
-
-
-
-// Ingest concepts for the cell
-bool pmCellIngestConcepts(pmCell *cell, // The cell
-                          psDB *db      // DB handle
-                         )
-{
-    pmChip *chip = cell->parent;        // The parent chip
-    pmFPA *fpa = chip->parent;          // The parent FPA
-
-    if (! cell->concepts) {
-        cell->concepts = psMetadataAlloc();
-    }
-
-    // CELL.NAME --- added by pmFPAConstruct
-
-    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.GAIN");
-    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.READNOISE");
-    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.SATURATION");
-    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.BAD");
-    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.XPARITY");
-    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.YPARITY");
-    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.READDIR");
-
-    // These used to be pmReadoutGetExposure and pmReadoutGetDarkTime, but that doesn't really make sense at
-    // the moment.  Maybe we need to add a "parent" link to the readouts.  But then how are the exposure times
-    // REALLY derived?  They're not in the FITS headers, because a readout is a plane in a 3D image.  We'll
-    // have to dream up some additional suffix to specify these, but for now....
-    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.EXPOSURE");
-    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.DARKTIME");
-
-
-    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.TRIMSEC");
-    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.BIASSEC");
-    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.TIME"); // This also does CELL.TIMESYS
-    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.XBIN");
-    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.YBIN");
-    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.X0");
-    p_pmCellIngestConcept(fpa, chip, cell, db, "CELL.Y0");
-
-    return true;
-}
-
-
-// Retrieve a concept
-psMetadataItem *pmCellGetConcept(pmCell *cell, // The cell
-                                 const char *concept // The concept
-                                )
-{
-    psMetadataItem *item = psMetadataLookup(cell->concepts, concept);
-    if (! item) {
-        pmChip *chip = cell->parent;
-        item = psMetadataLookup(chip->concepts, concept);
-        if (! item) {
-            pmFPA *fpa = chip->parent;
-            item = psMetadataLookup(fpa->concepts, concept);
-        }
-    }
-    return item;                        // item is either NULL or is what we want.
-}
-
-
-// Set up concepts for blank image
-bool pmFPABlankConcepts(pmFPA *fpa      // The FPA
-                       )
-{
-    assert(fpa);
-
-    // FPA-level concepts
-    if (! fpa->concepts) {
-        fpa->concepts = psMetadataAlloc();
-    }
-    psMetadataAddStr(fpa->concepts, PS_LIST_TAIL, "FPA.NAME", PS_META_REPLACE, "Name of FPA", "");
-    psMetadataAddF32(fpa->concepts, PS_LIST_TAIL, "FPA.AIRMASS", PS_META_REPLACE,
-                     "Airmass at boresight", NAN);
-    psMetadataAddStr(fpa->concepts, PS_LIST_TAIL, "FPA.FILTER", PS_META_REPLACE, "Filter used", "");
-    psMetadataAddF32(fpa->concepts, PS_LIST_TAIL, "FPA.POSANGLE", PS_META_REPLACE,
-                     "Position angle for instrument", NAN);
-    psMetadataAddStr(fpa->concepts, PS_LIST_TAIL, "FPA.RADECSYS", PS_META_REPLACE,
-                     "Celestial coordinate system", "");
-    psMetadataAddF64(fpa->concepts, PS_LIST_TAIL, "FPA.RA", PS_META_REPLACE,
-                     "Right Ascension of the boresight (radians)", NAN);
-    psMetadataAddF64(fpa->concepts, PS_LIST_TAIL, "FPA.DEC", PS_META_REPLACE,
-                     "Declination of the boresight (radians)", NAN);
-
-    // Iterate through the chips
-    psArray *chips = fpa->chips;        // Array of chips
-    for (int i = 0; i < chips->n; i++) {
-        pmChip *chip = chips->data[i];  // The chip of interest
-        if (! chip) {
-            continue;
-        }
-
-        // Chip-level concepts
-        if (! chip->concepts) {
-            chip->concepts = psMetadataAlloc();
-        }
-        // CHIP.NAME *should* have been added by pmFPAConstruct --- make sure
-        psMetadataItem *chipNameItem = psMetadataLookup(chip->concepts, "CHIP.NAME");
-        if (! chipNameItem) {
-            psMetadataAddStr(fpa->concepts, PS_LIST_TAIL, "CHIP.NAME", PS_META_REPLACE, "Name of Chip", "");
-        }
-
-        // Iterate through the cells
-        psArray *cells = chip->cells;   // Array of cells
-        for (int j = 0; j < cells->n; j++) {
-            pmCell *cell = cells->data[i]; // The cell of interest
-            if (! cell) {
-                continue;
-            }
-
-            // Cell-level concepts
-            if (! cell->concepts) {
-                cell->concepts = psMetadataAlloc();
-            }
-            // CELL.NAME *should* have been added by pmFPAConstruct --- make sure
-            psMetadataItem *cellNameItem = psMetadataLookup(cell->concepts, "CELL.NAME");
-            if (!cellNameItem) {
-                psMetadataAddStr(fpa->concepts, PS_LIST_TAIL, "CELL.NAME", PS_META_REPLACE,
-                                 "Name of Cell", "");
-            }
-            psMetadataAddF32(cell->concepts, PS_LIST_TAIL, "CELL.GAIN", PS_META_REPLACE, "CCD gain (e/count)", NAN);
-            psMetadataAddF32(cell->concepts, PS_LIST_TAIL, "CELL.READNOISE", PS_META_REPLACE, "CCD read noise (e)", NAN);
-            psMetadataAddF32(cell->concepts, PS_LIST_TAIL, "CELL.SATURATION", PS_META_REPLACE, "Saturation level (ADU)", NAN);
-            psMetadataAddF32(cell->concepts, PS_LIST_TAIL, "CELL.BAD", PS_META_REPLACE, "Bad level (ADU)", NAN);
-            psMetadataAddF32(cell->concepts, PS_LIST_TAIL, "CELL.XPARITY", PS_META_REPLACE, "Orientation in x compared to the rest of the FPA", NAN);
-            psMetadataAddF32(cell->concepts, PS_LIST_TAIL, "CELL.YPARITY", PS_META_REPLACE, "Orientation in x compared to the rest of the FPA", NAN);
-            psMetadataAddF32(cell->concepts, PS_LIST_TAIL, "CELL.READDIR", PS_META_REPLACE, "Read direction: 1=row, 2=col", NAN);
-            psMetadataAddF32(cell->concepts, PS_LIST_TAIL, "CELL.EXPOSURE", PS_META_REPLACE, "Exposure time (sec)", NAN);
-            psMetadataAddF32(cell->concepts, PS_LIST_TAIL, "CELL.DARKTIME", PS_META_REPLACE, "Time since CCD flush (sec)", NAN);
-            psMetadataAddS32(cell->concepts, PS_LIST_TAIL, "CELL.XBIN", PS_META_REPLACE, "Binning in x", 0);
-            psMetadataAddS32(cell->concepts, PS_LIST_TAIL, "CELL.YBIN", PS_META_REPLACE, "Binning in y", 0);
-            psMetadataAddS32(cell->concepts, PS_LIST_TAIL, "CELL.X0", PS_META_REPLACE, "Position of (0,0) on the chip", 0);
-            psMetadataAddS32(cell->concepts, PS_LIST_TAIL, "CELL.Y0", PS_META_REPLACE, "Position of (0,0) on the chip", 0);
-
-            psRegion *trimsec = psAlloc(sizeof(psRegion)); // Dummy region
-            trimsec->x0 = NAN;
-            trimsec->x1 = NAN;
-            trimsec->y0 = NAN;
-            trimsec->y1 = NAN;
-            psMetadataAddPtr(cell->concepts, PS_LIST_TAIL, "CELL.TRIMSEC", PS_META_REPLACE, "Trim section", trimsec);
-            psList *biassecs = psListAlloc(NULL); // Dummy list
-            psMetadataAddPtr(cell->concepts, PS_LIST_TAIL, "CELL.BIASSEC", PS_META_REPLACE, "Bias section", biassecs);
-            psTime *time = psTimeAlloc(PS_TIME_TAI); // Dummy time
-            psMetadataAddPtr(cell->concepts, PS_LIST_TAIL, "CELL.TIME", PS_META_REPLACE, "Time of exposure", time);
-            psMetadataAddS32(cell->concepts, PS_LIST_TAIL, "CELL.TIMESYS", PS_META_REPLACE, "Time system", PS_TIME_TAI);
-        }
-    }
-
-    return true;
-}
-
-// Copy concepts from one FPA to another
-bool pmFPACopyConcepts(pmFPA *target,   // The target FPA
-                       pmFPA *source    // The target FPA
-                      )
-{
-    // Copy FPA concepts
-    target->concepts = pap_psMetadataCopy(target->concepts, source->concepts);
-
-    // Copy chip concepts
-    psArray *targetChips = target->chips; // Chips in target
-    psArray *sourceChips = source->chips; // Chips in source
-    if (targetChips->n != sourceChips->n) {
-        psError(PS_ERR_IO, true, "Number of chips in target (%d) and source (%d) differ --- unable to copy "
-                "concepts.\n", targetChips->n, sourceChips->n);
-        return false;
-    }
-    for (int i = 0; i < targetChips->n; i++) {
-        pmChip *targetChip = targetChips->data[i]; // Target chip of interest
-        pmChip *sourceChip = sourceChips->data[i]; // Source chip of interest
-        if (! targetChip || ! sourceChip) {
-            continue;
-        }
-        targetChip->concepts = pap_psMetadataCopy(targetChip->concepts, sourceChip->concepts);
-
-        // Copy cell concepts
-        psArray *targetCells = targetChip->cells; // Cells in target
-        psArray *sourceCells = sourceChip->cells; // Cells in source
-        if (targetCells->n != sourceCells->n) {
-            psError(PS_ERR_IO, true, "Number of cells in target (%d) and source (%d) differ for chip %d ---"
-                    " unable to copy concepts.\n", targetCells->n, sourceCells->n, i);
-            return false;
-        }
-        for (int j = 0; j < targetCells->n; j++) {
-            pmCell *targetCell = targetCells->data[j]; // Target chip of interest
-            pmCell *sourceCell = sourceCells->data[j]; // Source chip of interest
-            if (! targetCell || ! sourceCell) {
-                continue;
-            }
-            targetCell->concepts = pap_psMetadataCopy(targetCell->concepts, sourceCell->concepts);
-        }
-    }
-
-    return true;
-}
Index: unk/psModules/src/astrom/pmFPAConceptsGet.h
===================================================================
--- /trunk/psModules/src/astrom/pmFPAConceptsGet.h	(revision 7018)
+++ 	(revision )
@@ -1,103 +1,0 @@
-#ifndef PM_FPA_CONCEPTS_GET_H
-#define PM_FPA_CONCEPTS_GET_H
-
-#include "pslib.h"
-#include "pmAstrometry.h"
-#include "psAdditionals.h"
-
-psMetadataItem *p_pmFPAConceptGetFromCamera(pmCell *cell, // The cell
-        const char *concept // Name of concept
-                                           );
-psMetadataItem *p_pmFPAConceptGetFromHeader(pmFPA *fpa, // The FPA that contains the chip
-        pmChip *chip, // The chip that contains the cell
-        pmCell *cell, // The cell
-        const char *concept // Name of concept
-                                           );
-psMetadataItem *p_pmFPAConceptGetFromDefault(pmFPA *fpa, // The FPA that contains the chip
-        pmChip *chip, // The chip that contains the cell
-        pmCell *cell, // The cell
-        const char *concept // Name of concept
-                                            );
-psMetadataItem *p_pmFPAConceptGetFromDB(pmFPA *fpa, // The FPA that contains the chip
-                                        pmChip *chip, // The chip that contains the cell
-                                        pmCell *cell, // The cell
-                                        psDB *db,       // DB handle
-                                        const char *concept // Name of concept
-                                       );
-psMetadataItem *p_pmFPAConceptGet(pmFPA *fpa, // The FPA
-                                  pmChip *chip,// The chip
-                                  pmCell *cell, // The cell
-                                  psDB *db, // DB handle
-                                  const char *concept // Concept name
-                                 );
-void p_pmFPAConceptGetF32(pmFPA *fpa,   // The FPA
-                          pmChip *chip, // The chip
-                          pmCell *cell, // The cell
-                          psDB *db,     // DB handle
-                          psMetadata *concepts, // The concepts MD
-                          const char *name, // Name of the concept
-                          const char *comment // Comment for concept
-                         );
-void p_pmFPAConceptGetF64(pmFPA *fpa,   // The FPA
-                          pmChip *chip, // The chip
-                          pmCell *cell, // The cell
-                          psDB *db,     // DB handle
-                          psMetadata *concepts, // The concepts MD
-                          const char *name, // Name of the concept
-                          const char *comment // Comment for concept
-                         );
-void p_pmFPAConceptGetS32(pmFPA *fpa,   // The FPA
-                          pmChip *chip, // The chip
-                          pmCell *cell, // The cell
-                          psDB *db,     // DB handle
-                          psMetadata *concepts, // The concepts MD
-                          const char *name, // Name of the concept
-                          const char *comment // Comment for concept
-                         );
-void p_pmFPAConceptGetString(pmFPA *fpa, // The FPA
-                             pmChip *chip, // The chip
-                             pmCell *cell, // The cell
-                             psDB *db,  // DB handle
-                             psMetadata *concepts, // The concepts MD
-                             const char *name, // Name of the concept
-                             const char *comment // Comment for concept
-                            );
-
-
-// Ingest a single cell concept; this is the workhorse for ingesting cell concepts
-bool p_pmCellIngestConcept(pmFPA *fpa,  // The FPA
-                           pmChip *chip, // The chip
-                           pmCell *cell, // The cell
-                           psDB *db,    // DB handle
-                           const char *concept // Name of the concept
-                          );
-
-// Ingest concepts for the FPA
-void pmFPAIngestConcepts(pmFPA *fpa,    // The FPA
-                         psDB *db       // DB handle
-                        );
-// Ingest concepts for the chip
-bool pmChipIngestConcepts(pmChip *chip, // The chip
-                          psDB *db      // DB handle
-                         );
-// Ingest concepts for the cell
-bool pmCellIngestConcepts(pmCell *cell, // The cell
-                          psDB *db      // DB handle
-                         );
-
-// Set up concepts for blank image
-bool pmFPABlankConcepts(pmFPA *fpa      // The FPA
-                       );
-
-// Copy concepts from one FPA to another
-bool pmFPACopyConcepts(pmFPA *target,   // The target FPA
-                       pmFPA *source    // The target FPA
-                      );
-
-// Retrieve a concept
-psMetadataItem *pmCellGetConcept(pmCell *cell, // The cell
-                                 const char *concept // The concept
-                                );
-
-
-#endif
Index: unk/psModules/src/astrom/pmFPAConceptsSet.c
===================================================================
--- /trunk/psModules/src/astrom/pmFPAConceptsSet.c	(revision 7018)
+++ 	(revision )
@@ -1,904 +1,0 @@
-#include <stdio.h>
-#include <strings.h>
-#include "pslib.h"
-
-#include "pmAstrometry.h"
-#include "pmFPAConceptsGet.h"
-#include "pmFPAConceptsSet.h"
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// File-static functions
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-#define COMPARE_REGIONS(a,b) (((a)->x0 == (b)->x0 && \
-                               (a)->x1 == (b)->x1 && \
-                               (a)->y0 == (b)->y0 && \
-                               (a)->y1 == (b)->y1) ? true : false)
-
-
-static bool compareConcepts(psMetadataItem *item1, // First item to compare
-                            psMetadataItem *item2 // Second item to compare
-                           )
-{
-    // First order checks
-    if (! item1 || ! item2) {
-        return false;
-    }
-    if (strcasecmp(item1->name, item2->name) != 0) {
-        return false;
-    }
-
-    // Check the more boring types
-    switch (item1->type) {
-    case PS_TYPE_S32:
-        switch (item2->type) {
-        case PS_TYPE_S32:
-            return (item1->data.S32 == item2->data.S32) ? true : false;
-        case PS_TYPE_F32:
-            return (item1->data.S32 == (int)item2->data.F32) ? true : false;
-        case PS_TYPE_F64:
-            return (item1->data.S32 == (int)item2->data.F64) ? true : false;
-        default:
-            return false;
-        }
-    case PS_TYPE_F32:
-        switch (item2->type) {
-        case PS_TYPE_S32:
-            return (item1->data.F32 == (float)item2->data.S32) ? true : false;
-        case PS_TYPE_F32:
-            return (item1->data.F32 == item2->data.F32) ? true : false;
-        case PS_TYPE_F64:
-            return (item1->data.F32 == (float)item2->data.F64) ? true : false;
-        default:
-            return false;
-        }
-    case PS_TYPE_F64:
-        switch (item2->type) {
-        case PS_TYPE_S32:
-            return (item1->data.F64 == (double)item2->data.S32) ? true : false;
-        case PS_TYPE_F32:
-            return (item1->data.F64 == (double)item2->data.F32) ? true : false;
-        case PS_TYPE_F64:
-            return (item1->data.F64 == item2->data.F64) ? true : false;
-        default:
-            return false;
-        }
-        return (item1->data.F64 == item2->data.F64) ? true : false;
-    case PS_DATA_STRING:
-        if (item2->type != PS_DATA_STRING) {
-            return false;
-        }
-        return (strcasecmp(item1->data.V, item2->data.V) == 0) ? true : false;
-    default:
-        return false;
-    }
-    psAbort(__func__, "Should never get here.\n");
-}
-
-
-// Well, not really "set", but check to make sure it's there and matches
-static bool setConceptInCamera(pmCell *cell, // The cell
-                               psMetadataItem *concept // Concept
-                              )
-{
-    if (cell) {
-        psMetadataItem *item = psMetadataLookup(cell->camera, concept->name); // Info we want
-        return compareConcepts(item, concept);
-    }
-
-    return false;
-}
-
-
-static bool setConceptInHeader(pmFPA *fpa, // The FPA that contains the chip
-                               pmChip *chip, // The chip that contains the cell
-                               pmCell *cell, // The cell
-                               psMetadataItem *concept // Concept
-                              )
-{
-    bool mdok = true;                   // Status of MD lookup
-    bool status = false;                // Status of setting header
-    psMetadata *translation = psMetadataLookupMD(&mdok, fpa->camera, "TRANSLATION"); // FITS translation
-    if (! mdok) {
-        psError(PS_ERR_IO, false, "Unable to find TRANSLATION in camera configuration.\n");
-        return false;
-    }
-
-    // Look for how to translate the concept into a FITS header name
-    const char *keyword = psMetadataLookupStr(&mdok, translation, concept->name);
-    if (mdok && strlen(keyword) > 0) {
-        psTrace(__func__, 6, "It's in keyword %s\n", keyword);
-        psMetadataItem *headerItem = NULL; // Item to add to header
-        // XXX: Need to expand range of types
-        switch (concept->type) {
-        case PS_DATA_STRING:
-            headerItem = psMetadataItemAllocStr(keyword, concept->comment, concept->data.V);
-            break;
-        case PS_DATA_S32:
-            headerItem = psMetadataItemAllocS32(keyword, concept->comment, concept->data.S32);
-            break;
-        case PS_DATA_F32:
-            headerItem = psMetadataItemAllocF32(keyword, concept->comment, concept->data.F32);
-            break;
-        case PS_DATA_F64:
-            headerItem = psMetadataItemAllocF64(keyword, concept->comment, concept->data.F64);
-            break;
-        default:
-            headerItem = psMetadataItemAlloc(keyword, concept->type, concept->comment,
-                                             concept->data.V); // Item for the header
-        }
-
-        // We have a FITS header to look up --- search each level
-        if (cell && cell->hdu) {
-            psTrace(__func__, 7, "Adding to the cell level header...\n");
-            psMetadataAddItem(cell->hdu->header, headerItem, PS_LIST_TAIL, PS_META_REPLACE);
-            status = true;
-        } else if (chip && chip->hdu) {
-            psTrace(__func__, 7, "Adding to the chip level header...\n");
-            psMetadataAddItem(chip->hdu->header, headerItem, PS_LIST_TAIL, PS_META_REPLACE);
-            status = true;
-        } else if (fpa->hdu) {
-            psTrace(__func__, 7, "Adding to the FPA level header...\n");
-            psMetadataAddItem(fpa->hdu->header, headerItem, PS_LIST_TAIL, PS_META_REPLACE);
-            status = true;
-        } else {
-            // In desperation, add to the PHU --- it HAS to be in the header somewhere!
-            if (! fpa->phu) {
-                fpa->phu = psMetadataAlloc();
-            }
-            psTrace(__func__, 7, "Adding to the PHU...\n");
-            psMetadataAddItem(fpa->phu, headerItem, PS_LIST_TAIL, PS_META_REPLACE);
-            status = true;
-        }
-        psFree(headerItem);
-    }
-
-    // No header value
-    return status;
-}
-
-
-// Well, not really "set", but check to see if it's there, and matches
-static bool setConceptInDefault(pmFPA *fpa, // The FPA that contains the chip
-                                pmChip *chip, // The chip that contains the cell
-                                pmCell *cell, // The cell
-                                psMetadataItem *concept // Concept
-                               )
-{
-    bool mdOK = true;                   // Status of MD lookup
-    psMetadata *defaults = psMetadataLookupMD(&mdOK, fpa->camera, "DEFAULTS");
-    if (! mdOK || ! defaults) {
-        psError(PS_ERR_IO, false, "Unable to find DEFAULTS in camera configuration.\n");
-        return false;
-    }
-
-    psMetadataItem *defItem = psMetadataLookup(defaults, concept->name);
-    bool status = false;                // Result of checking the database
-    if (defItem) {
-        if (defItem->type == PS_DATA_METADATA) {
-            // A dependent default
-            psTrace(__func__, 7, "Evaluating dependent default....\n");
-            psMetadata *dependents = defItem->data.V; // The list of dependents
-            // Find out what it depends on
-            psString dependName = psStringCopy(concept->name);
-            psStringAppend(&dependName, ".DEPEND");
-            psString dependsOn = psMetadataLookupStr(&mdOK, defaults, dependName);
-            if (! mdOK) {
-                psError(PS_ERR_IO, false, "Unable to find %s in camera configuration for dependent default"
-                        " --- ignored\n", dependName);
-                // XXX: Need to clean up before returning
-                return false;
-            }
-            psFree(dependName);
-            // Find the value of the dependent concept
-            psMetadataItem *depItem = p_pmFPAConceptGetFromHeader(fpa, chip, cell, dependsOn);
-            if (! depItem) {
-                psError(PS_ERR_IO, true, "Unable to find value for %s (required for %s)\n", dependsOn,
-                        concept->name);
-                return false;
-            }
-            if (depItem->type != PS_DATA_STRING) {
-                psError(PS_ERR_IO, true, "Value of %s is not of type string, as required for dependency"
-                        " --- ignored.\n", dependsOn);
-            }
-
-            defItem = psMetadataLookup(dependents, depItem->data.V); // This is now what we were after
-        }
-
-        status = compareConcepts(defItem, concept);
-        if (! status) {
-            psError(PS_ERR_IO, true, "Concept %s is specified by default in the camera configuration, "
-                    "but doesn't match the actual value.\n", concept->name);
-        }
-    }
-
-    // XXX: Need to clean up before returning
-    return status;
-}
-
-
-// XXX: Not tested at all
-static bool setConceptInDB(pmFPA *fpa, // The FPA that contains the chip
-                           pmChip *chip, // The chip that contains the cell
-                           pmCell *cell, // The cell
-                           psDB *db,    // DB handle
-                           psMetadataItem *concept // Concept
-                          )
-{
-    if (! db) {
-        // No database initialised
-        return false;
-    }
-
-    bool mdStatus = true;               // Status of MD lookup
-    psMetadata *database = psMetadataLookupMD(&mdStatus, fpa->camera, "DATABASE");
-    if (! mdStatus) {
-        // No error, because not everyone needs to use the DB
-        return NULL;
-    }
-
-    psMetadata *dbLookup = psMetadataLookupMD(&mdStatus, database, concept->name);
-    if (dbLookup) {
-        const char *tableName = psMetadataLookupStr(&mdStatus, dbLookup, "TABLE"); // Name of the table
-        //        const char *colName = psMetadataLookupStr(&mdStatus, dbLookup, "COLUMN"); // Name of the column
-        const char *givenCols = psMetadataLookupStr(&mdStatus, dbLookup, "GIVENDBCOL"); // Name of "where"
-        // columns
-        const char *givenPS = psMetadataLookupStr(&mdStatus, dbLookup, "GIVENPS"); // Values for "where"
-        // columns
-
-        // Now, need to get the "given"s
-        if (strlen(givenCols) || strlen(givenPS)) {
-            psList *cols = psStringSplit(givenCols, ",;", true); // List of column names
-            psList *values = psStringSplit(givenPS, ",;", true); // List of value names for the columns
-            psMetadata *selection = psMetadataAlloc(); // The stuff to select in the DB
-            if (cols->n != values->n) {
-                psLogMsg(__func__, PS_LOG_WARN, "The GIVENDBCOL and GIVENPS entries for %s do not have "
-                         "the same number of entries --- ignored.\n", concept);
-            } else {
-                // Iterators for the lists
-                psListIterator *colsIter = psListIteratorAlloc(cols, PS_LIST_HEAD, false);
-                psListIterator *valuesIter = psListIteratorAlloc(values, PS_LIST_HEAD, false);
-                char *column = NULL;    // Name of the column
-                while ((column = psListGetAndIncrement(colsIter))) {
-                    char *name = psListGetAndIncrement(valuesIter); // Name for the value
-                    if (!strlen(column) || !strlen(name)) {
-                        psLogMsg(__func__, PS_LOG_WARN, "One of the columns or value names for %s is "
-                                 " empty --- ignored.\n", concept);
-                    } else {
-                        // Search for the value name
-                        psMetadataItem *item = p_pmFPAConceptGetFromHeader(fpa, chip, cell, name);
-                        if (! item) {
-                            item = p_pmFPAConceptGetFromDefault(fpa, chip, cell, name);
-                        }
-                        if (! item) {
-                            psLogMsg(__func__, PS_LOG_ERROR, "Unable to find the value name %s for DB "
-                                     " lookup on %s --- ignored.\n", name, concept);
-                        } else {
-                            // We need to create a new psMetadataItem.  I don't think we can't simply hack
-                            // the existing one, since that could conceivably cause memory leaks
-                            psMetadataItem *newItem = psMetadataItemAlloc(concept->name, item->type,
-                                                      item->comment, item->data.V);
-                            psMetadataAddItem(selection, newItem, PS_LIST_TAIL, PS_META_REPLACE);
-                            psFree(newItem);
-                        }
-                    }
-                    psFree(name);
-                    psFree(column);
-                } // Iterating through the columns
-                psFree(colsIter);
-                psFree(valuesIter);
-
-                // Check first to make sure we're only going to touch one row
-                psArray *dbResult = psDBSelectRows(db, tableName, selection, 2); // Lookup result
-                // Note that we use limit=2 in order to test if there are multiple rows returned
-                if (! dbResult || dbResult->n == 0) {
-                    psLogMsg(__func__, PS_LOG_WARN, "Unable to find any rows in DB for %s --- ignored\n",
-                             concept->name);
-                    return false;
-                } else {
-                    if (dbResult->n > 1) {
-                        psLogMsg(__func__, PS_LOG_WARN, "Multiple rows returned in DB lookup for %s --- "
-                                 " ignored.\n", concept->name);
-                    }
-                    // Update the DB
-                    psMetadata *update = psMetadataAlloc();
-                    psMetadataAddItem(update, concept, PS_LIST_HEAD, 0);
-                    psDBUpdateRows(db, tableName, selection, update);
-                    psFree(update);
-                    return true;
-                }
-            }
-            psFree(cols);
-            psFree(values);
-        }
-    } // Doing the "given"s.
-
-    psAbort(__func__, "Shouldn't ever get here?\n");
-    return false;
-}
-
-
-// Concept set from item
-static bool setConceptItem(pmFPA *fpa, // The FPA
-                           pmChip *chip,// The chip
-                           pmCell *cell,        // The cell
-                           psDB *db, // DB handle
-                           psMetadataItem *concept // Concept item
-                          )
-{
-    // Try headers, database, defaults in order
-    psTrace(__func__, 3, "Trying to set concept %s...\n", concept->name);
-    bool status = setConceptInCamera(cell, concept); // Status for return
-    if (! status) {
-        psTrace(__func__, 5, "Trying header....\n");
-        status = setConceptInHeader(fpa, chip, cell, concept);
-    }
-    if (! status) {
-        psTrace(__func__, 5, "Trying database....\n");
-        status = setConceptInDB(fpa, chip, cell, db, concept);
-    }
-    if (! status) {
-        psTrace(__func__, 5, "Checking defaults....\n");
-        status = setConceptInDefault(fpa, chip, cell, concept);
-    }
-
-    if (! status) {
-        psError(PS_ERR_IO, true, "Unable to set %s (%s).\n", concept->name, concept->comment);
-    }
-
-    return status;
-}
-
-
-// Concept set
-static bool setConcept(pmFPA *fpa, // The FPA
-                       pmChip *chip,// The chip
-                       pmCell *cell,    // The cell
-                       psDB *db, // DB handle
-                       psMetadata *concepts, // Concepts MD from which to set
-                       const char *name // Name of the concept
-                      )
-{
-    psMetadataItem *concept = psMetadataLookup(concepts, name);
-    if (! concept) {
-        psError(PS_ERR_IO, true, "No such concept as %s\n", name);
-        return false;
-    }
-    return setConceptItem(fpa, chip, cell, db, concept);
-}
-
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Public functions
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Outget concepts from the FPA
-void pmFPAOutgestConcepts(pmFPA *fpa,   // The FPA
-                          psDB *db      // DB handle
-                         )
-{
-    // FPA.NAME
-    setConcept(fpa, NULL, NULL, db, fpa->concepts, "FPA.NAME");
-
-    // FPA.AIRMASS
-    setConcept(fpa, NULL, NULL, db, fpa->concepts, "FPA.AIRMASS");
-
-    // FPA.FILTER
-    setConcept(fpa, NULL, NULL, db, fpa->concepts, "FPA.FILTER");
-
-    // FPA.POSANGLE
-    setConcept(fpa, NULL, NULL, db, fpa->concepts, "FPA.POSANGLE");
-
-    // FPA.RADECSYS
-    setConcept(fpa, NULL, NULL, db, fpa->concepts, "FPA.RADECSYS");
-
-    // These take some extra work
-
-    // FPA.RA
-    {
-        double ra = psMetadataLookupF64(NULL, fpa->concepts, "FPA.RA"); // The RA
-
-        // How to interpret the RA
-        const psMetadata *camera = fpa->camera; // Camera configuration data
-        bool mdok = true;               // Status of MD lookup
-        psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS");
-        if (mdok && formats)
-        {
-            psString raFormat = psMetadataLookupStr(&mdok, formats, "FPA.RA");
-            if (mdok && strlen(raFormat) > 0) {
-                if (strcasecmp(raFormat, "HOURS") == 0) {
-                    ra /= M_PI / 12.0;
-                } else if (strcasecmp(raFormat, "DEGREES") == 0) {
-                    ra /= M_PI / 180.0;
-                } else if (strcasecmp(raFormat, "RADIANS") == 0) {
-                    // No action required
-                } else {
-                    psLogMsg(__func__, PS_LOG_WARN, "Don't understand FPA.RA in FORMATS (%s) --- assuming"
-                             " HOURS.\n");
-                    ra /= M_PI / 12.0;
-                }
-            } else {
-                psError(PS_ERR_IO, false, "Unable to find FPA.RA in FORMATS --- assuming HOURS.\n");
-                ra /= M_PI / 12.0;
-            }
-        } else
-        {
-            psError(PS_ERR_IO, false, "Unable to find FORMAT metadata in camera configuration --- "
-                    "assuming format for FPA.RA is HOURS.\n");
-            ra /= M_PI / 12.0;
-        }
-
-        // We choose to write sexagesimal format
-        int big, medium;
-        float small;
-        big = (int)ra;
-        medium = (int)(60.0*(ra - (double)big));
-        small = 3600.0*(ra - (double)big - 60.0 * (double)medium);
-        psString raString = psStringCopy("");
-        psStringAppend(&raString, "%d:%d:%.2f", big, medium, small);
-        psMetadataItem *raItem = psMetadataItemAllocStr("FPA.RA", "Right Ascension of the boresight",
-                                 raString);
-        setConceptItem(fpa, NULL, NULL, db, raItem);
-        psFree(raItem);
-        psFree(raString);
-    }
-
-    // FPA.DEC
-    {
-        double dec = psMetadataLookupF64(NULL, fpa->concepts, "FPA.DEC"); // The DEC
-
-        // How to interpret the DEC
-        const psMetadata *camera = fpa->camera; // Camera configuration data
-        bool mdok = true;               // Status of MD lookup
-        psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS");
-        if (mdok && formats)
-        {
-            psString decFormat = psMetadataLookupStr(&mdok, formats, "FPA.DEC");
-            if (mdok && strlen(decFormat) > 0) {
-                if (strcasecmp(decFormat, "HOURS") == 0) {
-                    dec /= M_PI / 12.0;
-                } else if (strcasecmp(decFormat, "DEGREES") == 0) {
-                    dec /= M_PI / 180.0;
-                } else if (strcasecmp(decFormat, "RADIANS") == 0) {
-                    // No action required
-                } else {
-                    psLogMsg(__func__, PS_LOG_WARN, "Don't understand FPA.DEC in FORMATS (%s) --- assuming"
-                             " DEGREES.\n");
-                    dec /= M_PI / 180.0;
-                }
-            } else {
-                psError(PS_ERR_IO, false, "Unable to find FPA.DEC in FORMATS --- assuming DEGREES.\n");
-                dec /= M_PI / 180.0;
-            }
-        } else
-        {
-            psError(PS_ERR_IO, false, "Unable to find FORMAT metadata in camera configuration --- "
-                    "assuming format for FPA.DEC is HOURS.\n");
-            dec /= M_PI / 12.0;
-        }
-
-        // We choose to write sexagesimal format
-        int big, medium;
-        float small;
-        big = (int)dec;
-        medium = (int)(60.0*(dec - (double)big));
-        small = 3600.0*(dec - (double)big - 60.0 * (double)medium);
-        psString decString = psStringCopy("");
-        psStringAppend(&decString, "%d:%d:%.2f", big, medium, small);
-        psMetadataItem *decItem = psMetadataItemAllocStr("FPA.DEC", "Right Ascension of the boresight",
-                                  decString);
-        setConceptItem(fpa, NULL, NULL, db, decItem);
-        psFree(decItem);
-        psFree(decString);
-
-    }
-
-    // Pau.
-}
-
-
-// Outgest concepts for the chip
-void pmChipOutgestConcepts(pmChip *chip, // The chip
-                           psDB *db     // DB handle
-                          )
-{
-    //    pmFPA *fpa = chip->parent;          // The parent FPA
-
-    // CHIP.NAME --- no need to do anything
-
-    // Pau.
-}
-
-
-// Outgest concepts for the cell
-void pmCellOutgestConcepts(pmCell *cell, // The cell
-                           psDB *db     // DB handle
-                          )
-{
-    pmChip *chip = cell->parent;        // The parent chip
-    pmFPA *fpa = chip->parent;          // The parent FPA
-
-    // CELL.NAME --- no need to do anything
-
-    // CELL.GAIN
-    setConcept(fpa, chip, cell, db, cell->concepts, "CELL.GAIN");
-
-    // CELL.READNOISE
-    setConcept(fpa, chip, cell, db, cell->concepts, "CELL.READNOISE");
-
-    // CELL.SATURATION
-    setConcept(fpa, chip, cell, db, cell->concepts, "CELL.SATURATION");
-
-    // CELL.BAD
-    setConcept(fpa, chip, cell, db, cell->concepts, "CELL.BAD");
-
-    // CELL.XPARITY
-    setConcept(fpa, chip, cell, db, cell->concepts, "CELL.XPARITY");
-
-    // CELL.YPARITY
-    setConcept(fpa, chip, cell, db, cell->concepts, "CELL.YPARITY");
-
-    // CELL.READDIR
-    setConcept(fpa, chip, cell, db, cell->concepts, "CELL.READDIR");
-
-    // These used to be pmReadoutGetExposure and pmReadoutGetDarkTime, but that doesn't really make sense at
-    // the moment.  Maybe we need to add a "parent" link to the readouts.  But then how are the exposure times
-    // REALLY derived?  They're not in the FITS headers, because a readout is a plane in a 3D image.  We'll
-    // have to dream up some additional suffix to specify these, but for now....
-
-    // CELL.EXPOSURE (used to be READOUT.EXPOSURE)
-    setConcept(fpa, chip, cell, db, cell->concepts, "CELL.EXPOSURE");
-
-    // CELL.DARKTIME (used to be READOUT.DARKTIME)
-    setConcept(fpa, chip, cell, db, cell->concepts, "CELL.DARKTIME");
-
-    // These take some extra work
-
-    // CELL.TRIMSEC
-    {
-        psMetadataItem *trimsecItem = psMetadataLookup(cell->concepts, "CELL.TRIMSEC");
-        psRegion *trimsec = trimsecItem->data.V; // The trimsec region
-        psMetadataItem *sourceItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.TRIMSEC.SOURCE");
-        if (! sourceItem)
-        {
-            psError(PS_ERR_IO, false, "Couldn't find CELL.TRIMSEC.SOURCE.\n");
-        } else if (sourceItem->type != PS_DATA_STRING)
-        {
-            psError(PS_ERR_IO, true, "CELL.TRIMSEC.SOURCE is not of type STR (%x)\n", sourceItem->type);
-        } else
-        {
-            psString source = sourceItem->data.V; // The source string
-
-            if (strcasecmp(source, "VALUE") == 0) {
-                // Check that it's the same value as stored in the camera
-                psString checkString = psMetadataLookupStr(NULL, cell->camera, "CELL.TRIMSEC");
-                psRegion checkRegion = psRegionFromString(checkString);
-                if (! COMPARE_REGIONS(&checkRegion, trimsec)) {
-                    psError(PS_ERR_IO, true, "Target CELL.TRIMSEC is specified by value, and values don't "
-                            "match.\n");
-                }
-            } else if (strcasecmp(source, "HEADER") == 0) {
-                //                psString keyword = psMetadataLookupStr(NULL, cell->camera, "CELL.TRIMSEC");
-                psMetadata *header = NULL; // The FITS header
-                if (cell->hdu) {
-                    header = cell->hdu->header;
-                } else if (chip->hdu) {
-                    header = chip->hdu->header;
-                } else if (fpa->hdu) {
-                    header = fpa->hdu->header;
-                }
-                if (! header) {
-                    psError(PS_ERR_IO, true, "Unable to find FITS header!\n");
-                } else {
-                    psMetadataAddItem(header, trimsecItem, PS_LIST_TAIL, PS_META_REPLACE);
-                }
-            } else {
-                psError(PS_ERR_IO, true, "CELL.TRIMSEC.SOURCE (%s) is not HEADER or VALUE.\n", source);
-            }
-        }
-    }
-
-    // CELL.BIASSEC
-    {
-        psMetadataItem *biassecItem = psMetadataLookup(cell->concepts, "CELL.BIASSEC");
-        psList *biassecs = biassecItem->data.V; // The biassecs region list
-        psMetadataItem *sourceItem = p_pmFPAConceptGet(fpa, chip, cell, db, "CELL.BIASSEC.SOURCE");
-        if (! sourceItem)
-        {
-            psError(PS_ERR_IO, false, "Couldn't find CELL.BIASSEC.SOURCE.\n");
-        } else if (sourceItem->type != PS_DATA_STRING)
-        {
-            psError(PS_ERR_IO, true, "CELL.BIASSEC.SOURCE is not of type STR (%x)\n", sourceItem->type);
-        } else
-        {
-            psString source = sourceItem->data.V; // The source string
-
-            if (strcasecmp(source, "VALUE") == 0) {
-                // Check that it's the same value as stored in the camera
-                psString checkString = psMetadataLookupStr(NULL, cell->camera, "CELL.BIASSEC");
-                psList *checkList = psStringSplit(checkString, " ;", true);
-                if (biassecs->n != checkList->n) {
-                    psError(PS_ERR_IO, true, "Target CELL.BIASSEC is specified by value, but number of "
-                            "entries doesn't match.\n");
-                } else {
-                    // We don't care if the order matches or not
-                    psVector *check = psVectorAlloc(biassecs->n, PS_TYPE_U8); // Vector to mark regions off
-                    for (int i = 0; i < check->n; i++) {
-                        check->data.U8[i] = 0;
-                    }
-                    psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD,
-                                                   false); // Iterator
-                    psListIterator *checkListIter = psListIteratorAlloc(checkList, PS_LIST_HEAD, false);
-                    psRegion *biassec = NULL; // Region from iteration
-                    while ((biassec = psListGetAndIncrement(biassecsIter))) {
-                        psListIteratorSet(checkListIter, PS_LIST_HEAD);
-                        psString checkRegionString = NULL; // Region string from iteration
-                        int i = 0;              // Counter
-                        while ((checkRegionString = psListGetAndIncrement(checkListIter))) {
-                            psRegion checkRegion = psRegionFromString(checkRegionString);
-                            psTrace(__func__, 7, "Checking [%.0f:%.0f,%.0f:%.0f] against "
-                                    "[%.0f:%.0f,%.0f:%.0f]\n", biassec->x0, biassec->x1, biassec->y0,
-                                    biassec->y1, checkRegion.x0, checkRegion.x1, checkRegion.y0,
-                                    checkRegion.y1);
-                            if (COMPARE_REGIONS(biassec, &checkRegion)) {
-                                check->data.U8[i] = 1;
-                                i++;
-                                break;
-                            }
-                            i++;
-                        }
-                    }
-                    for (int i = 0; i < check->n; i++) {
-                        if (check->data.U8[i] == 0) {
-                            psError(PS_ERR_IO, true, "Target CELL.BIASSEC is specified by value, but values "
-                                    "don't match.\n");
-                            break;
-                        }
-                    }
-                    psFree(checkListIter);
-                    psFree(biassecsIter);
-                    psFree(check);
-                }
-                psFree(checkList);
-            } else if (strcasecmp(source, "HEADER") == 0) {
-                psString keywordsString = psMetadataLookupStr(NULL, cell->camera, "CELL.BIASSEC");
-                psList *keywords = psStringSplit(keywordsString, " ,;", true);
-                if (biassecs->n != keywords->n) {
-                    psError(PS_ERR_IO, true, "Target CELL.BIASSEC is sepcified by headers, but the number "
-                            "of headers doesn't match.\n");
-                } else {
-                    psMetadata *header = NULL; // The FITS header
-                    if (cell->hdu) {
-                        header = cell->hdu->header;
-                    } else if (chip->hdu) {
-                        header = chip->hdu->header;
-                    } else if (fpa->hdu) {
-                        header = fpa->hdu->header;
-                    }
-                    if (! header) {
-                        psError(PS_ERR_IO, true, "Unable to find FITS header!\n");
-                    } else {
-                        psListIterator *keywordsIter = psListIteratorAlloc(keywords, PS_LIST_HEAD, false);
-                        psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false);
-                        psString keyword = NULL; // Header keyword from list
-                        while ((keyword = psListGetAndIncrement(keywordsIter))) {
-                            // Update the header
-                            psRegion *biassec = psListGetAndIncrement(biassecsIter);
-                            psString biassecString = psRegionToString(*biassec);
-                            psMetadataAdd(header, PS_LIST_TAIL, keyword, PS_DATA_STRING | PS_META_REPLACE,
-                                          "Bias section", biassecString);
-                            psFree(biassecString);
-                        }
-                        psFree(keywordsIter);
-                        psFree(biassecsIter);
-                    }
-                }
-                psFree(keywords);
-            } else {
-                psError(PS_ERR_IO, true, "CELL.BIASSEC.SOURCE (%s) is not HEADER or VALUE.\n", source);
-            }
-        }
-    }
-
-    // CELL.XBIN and CELL.YBIN
-    {
-        psMetadataItem *xBinItem = psMetadataLookup(cell->concepts, "CELL.XBIN"); // Binning factor in x
-        psMetadataItem *yBinItem = psMetadataLookup(cell->concepts, "CELL.YBIN"); // Binning factor in y
-
-        // If these are specified by the header, we need to check to see if it's the same header keyword
-        const psMetadata *camera = fpa->camera;
-        psMetadata *translation = psMetadataLookupMD(NULL, camera, "TRANSLATION");
-        psString xKeyword = psMetadataLookupStr(NULL, translation, "CELL.XBIN");
-        psString yKeyword = psMetadataLookupStr(NULL, translation, "CELL.YBIN");
-        if (strlen(xKeyword) > 0 && strcasecmp(xKeyword, yKeyword) != 0)
-        {
-            setConceptInHeader(fpa, chip, cell, xBinItem);
-            xBinItem = NULL;
-        }
-        if (strlen(yKeyword) > 0 && strcasecmp(xKeyword, yKeyword) != 0)
-        {
-            setConceptInHeader(fpa, chip, cell, yBinItem);
-            yBinItem = NULL;
-        }
-        if (strlen(xKeyword) > 0 && strlen(yKeyword) > 0 && strcasecmp(xKeyword, yKeyword) == 0)
-        {
-            psString binString = psStringCopy("");
-            psStringAppend(&binString, "%d,%d", xBinItem->data.S32, yBinItem->data.S32);
-            psMetadataItem *binItem = psMetadataItemAllocStr(xKeyword, "Binning factor in x and y",
-                                      binString);
-            psFree(binString);
-            psMetadata *header = NULL; // The FITS header
-            if (cell->hdu) {
-                header = cell->hdu->header;
-            } else if (chip->hdu) {
-                header = chip->hdu->header;
-            } else if (fpa->hdu) {
-                header = fpa->hdu->header;
-            }
-            if (! header) {
-                psError(PS_ERR_IO, true, "Unable to find FITS header!\n");
-            } else {
-                psMetadataAddItem(header, binItem, PS_LIST_TAIL, PS_META_REPLACE);
-                xBinItem = NULL;
-                yBinItem = NULL;
-            }
-            psFree(binItem);
-        }
-
-        // Do it in the usual way if we have to
-        if (xBinItem)
-        {
-            setConceptItem(fpa, chip, cell, db, xBinItem);
-        }
-        if (yBinItem)
-        {
-            setConceptItem(fpa, chip, cell, db, yBinItem);
-        }
-    }
-
-    // CELL.TIMESYS
-    {
-        psMetadataItem *sysItem = psMetadataLookup(cell->concepts, "CELL.TIMESYS");
-        psString sys = NULL;            // String to store
-        switch (sysItem->data.S32)
-        {
-        case PS_TIME_TAI:
-            sys = psStringCopy("TAI");
-            break;
-        case PS_TIME_UTC:
-            sys = psStringCopy("UTC");
-            break;
-        case PS_TIME_UT1:
-            sys = psStringCopy("UT1");
-            break;
-        case PS_TIME_TT:
-            sys = psStringCopy("TT");
-            break;
-        default:
-            sys = psStringCopy("Unknown");
-        }
-        psMetadataItem *newItem = psMetadataItemAllocStr("CELL.TIMESYS", "Time system", sys);
-        setConceptItem(fpa, chip, cell, db, newItem);
-        psFree(newItem);
-        psFree(sys);
-    }
-
-    // CELL.TIME
-    {
-        psMetadataItem *timeItem = psMetadataLookup(cell->concepts, "CELL.TIME");
-        psTime *time = timeItem->data.V; // The time
-        psString dateTimeString = psTimeToISO(time); // String representation
-
-        psMetadata *formats = psMetadataLookupMD(NULL, fpa->camera, "FORMATS");
-        psString format = psMetadataLookupStr(NULL, formats, "CELL.TIME");
-
-        if (strlen(format) == 0)
-        {
-            // No format specified --> do it in the usual way (maybe it's a DB lookup)
-            psMetadataItem *newTimeItem = psMetadataItemAllocStr("CELL.TIME", "Time of observation",
-                                          dateTimeString);
-            setConceptItem(fpa, chip, cell, db, newTimeItem);
-            psFree(newTimeItem);
-        } else
-        {
-            if (strcasecmp(format, "ISO") == 0) {
-                // dateTimeString contains an ISO time
-                psMetadataItem *newTimeItem = psMetadataItemAllocStr("CELL.TIME", "Time of observation",
-                                              dateTimeString);
-                setConceptItem(fpa, chip, cell, db, newTimeItem);
-                psFree(newTimeItem);
-            } else if (strstr(format, "SEPARATE")) {
-                // We're working with two separate headers
-                psMetadata *header = NULL; // The FITS header
-                if (cell->hdu) {
-                    header = cell->hdu->header;
-                } else if (chip->hdu) {
-                    header = chip->hdu->header;
-                } else if (fpa->hdu) {
-                    header = fpa->hdu->header;
-                }
-                if (! header) {
-                    psError(PS_ERR_IO, true, "Unable to find FITS header!\n");
-                } else {
-                    // Get the headers
-                    const psMetadata *camera = fpa->camera;
-                    psMetadata *translation = psMetadataLookupMD(NULL, camera, "TRANSLATION");
-                    psString keywords = psMetadataLookupStr(NULL, translation, "CELL.TIME");
-                    psList *dateTimeKeywords = psStringSplit(keywords, " ,;", true);
-                    psString dateKeyword = psListGet(dateTimeKeywords, PS_LIST_HEAD);
-                    psString timeKeyword = psListGet(dateTimeKeywords, PS_LIST_TAIL);
-
-                    psList *dateTime = psStringSplit(dateTimeString, " T", true); // Find the middle T
-                    psString dateString = psListGet(dateTime, PS_LIST_HEAD);
-                    psString timeString = psListGet(dateTime, PS_LIST_TAIL);
-
-                    // XXX: Couldn't be bothered doing these right now
-                    if (strstr(format, "PRE2000")) {
-                        psError(PS_ERR_IO, true, "Don't you realise it's the twenty-first century?\n");
-                    }
-                    if (strstr(format, "BACKWARDS")) {
-                        psError(PS_ERR_IO, true, "You want it BACKWARDS?  Not right now, thanks.\n");
-                    }
-
-                    psMetadataItem *dateItem = psMetadataItemAllocStr(dateKeyword, "Date of observation",
-                                               dateString);
-                    psMetadataItem *timeItem = psMetadataItemAllocStr(timeKeyword, "Time of observation",
-                                               timeString);
-                    psMetadataAddItem(header, dateItem, PS_LIST_TAIL, PS_META_REPLACE);
-                    psMetadataAddItem(header, timeItem, PS_LIST_TAIL, PS_META_REPLACE);
-
-                    psFree(dateTimeKeywords);
-                    psFree(dateTime);
-                    psFree(dateItem);
-                    psFree(timeItem);
-                }
-            } else if (strcasecmp(format, "MJD") == 0) {
-                double mjd = psTimeToMJD(time);
-                psMetadataItem *newTimeItem = psMetadataItemAllocF64("CELL.TIME", "MJD of observation", mjd);
-                setConceptItem(fpa, chip, cell, db, newTimeItem);
-                psFree(newTimeItem);
-            } else if (strcasecmp(format, "JD") == 0) {
-                double jd = psTimeToMJD(time);
-                psMetadataItem *newTimeItem = psMetadataItemAllocF64("CELL.TIME", "JD of observation", jd);
-                setConceptItem(fpa, chip, cell, db, newTimeItem);
-                psFree(newTimeItem);
-            } else {
-                psError(PS_ERR_IO, true, "Not sure how to outgest CELL.TIME (%s) --- trying "
-                        "ISO\n", dateTimeString);
-                psMetadataItem *newTimeItem = psMetadataItemAllocStr("CELL.TIME", "Time of observation",
-                                              dateTimeString);
-                setConceptItem(fpa, chip, cell, db, newTimeItem);
-                psFree(newTimeItem);
-            }
-        }
-
-        psFree(dateTimeString);
-    }
-
-    // Remove corrective
-    {
-        const psMetadata *camera = fpa->camera;
-        bool mdok = false;              // Result of MD lookup
-        psMetadata *formats = psMetadataLookupMD(&mdok, camera, "FORMATS");
-        if (mdok && formats)
-        {
-            psString format = psMetadataLookupStr(&mdok, formats, "CELL.X0");
-            if (mdok && strlen(format) > 0 && strcasecmp(format, "FORTRAN") == 0) {
-                psMetadataItem *cellx0 = psMetadataLookup(cell->concepts, "CELL.X0");
-                cellx0->data.S32 += 1;
-            }
-            format = psMetadataLookupStr(&mdok, formats, "CELL.Y0");
-            if (mdok && strlen(format) > 0 && strcasecmp(format, "FORTRAN") == 0) {
-                psMetadataItem *celly0 = psMetadataLookup(cell->concepts, "CELL.Y0");
-                celly0->data.S32 += 1;
-            }
-        }
-    }
-    // CELL.X0
-    setConcept(fpa, chip, cell, db, cell->concepts, "CELL.X0");
-    // CELL.Y0
-    setConcept(fpa, chip, cell, db, cell->concepts, "CELL.Y0");
-
-    // Pau.
-}
-
Index: unk/psModules/src/astrom/pmFPAConceptsSet.h
===================================================================
--- /trunk/psModules/src/astrom/pmFPAConceptsSet.h	(revision 7018)
+++ 	(revision )
@@ -1,20 +1,0 @@
-#ifndef PM_FPA_CONCEPTS_SET_H
-#define PM_FPA_CONCEPTS_SET_H
-
-#include "pslib.h"
-#include "pmAstrometry.h"
-
-void pmFPAOutgestConcepts(pmFPA *fpa,   // The FPA
-                          psDB *db      // DB handle
-                         );
-void pmChipOutgestConcepts(pmChip *chip,        // The chip
-                           psDB *db     // DB handle
-                          );
-void pmCellOutgestConcepts(pmCell *cell, // The cell
-                           psDB *db     // DB handle
-                          );
-
-
-
-
-#endif
Index: unk/psModules/src/astrom/pmFPAConstruct.c
===================================================================
--- /trunk/psModules/src/astrom/pmFPAConstruct.c	(revision 7018)
+++ 	(revision )
@@ -1,781 +1,0 @@
-#include <stdio.h>
-#include <assert.h>
-#include <string.h>
-#include "pslib.h"
-#include "psMetadataItemParse.h"
-
-#include "pmFPA.h"
-#include "pmConcepts.h"
-#include "pmFPAConstruct.h"
-#include "pmFPAview.h"
-#include "pmFPAUtils.h"
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// File-static functions
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Read data for a particular cell from the camera format description
-static psMetadata *getCellData(const psMetadata *format, // The camera format description
-                               const char *cellName // The name of the cell
-                              )
-{
-    bool status = true;                 // Result of MD lookup
-    psMetadata *cells = psMetadataLookupMD(&status, format, "CELLS"); // The CELLS
-    if (! status) {
-        psError(PS_ERR_IO, false, "Unable to determine CELLS of camera.\n");
-        return NULL;
-    }
-
-    psMetadata *cellData = psMetadataLookupMD(&status, cells, cellName); // The data for the particular cell
-    if (!status || !cellData) {
-        psLogMsg(__func__, PS_LOG_WARN, "Unable to find specs for cell %s: ignored\n", cellName);
-    }
-
-    return cellData;
-}
-
-// Parse a list of first:second:third pairs in a string
-static bool parseContent(psArray **first, // Array of the first values
-                         psArray **second, // Array of the second values
-                         psArray **third, // Array of the third values
-                         const char *string // The string to parse
-                        )
-{
-    bool allOK = true;                  // Everything was OK?
-    psList *values = psStringSplit(string, " ,;", true); // List of the parts
-    *first = psArrayAlloc(values->n);
-    *second = psArrayAlloc(values->n);
-    *third = psArrayAlloc(values->n);
-    (*first)->n = (*second)->n = (*third)->n = values->n;
-    int num = 0;
-    psListIterator *valuesIter = psListIteratorAlloc(values, PS_LIST_HEAD, false); // Iterator for values
-    psString value = NULL;               // "first:second:third" string
-    while ((value = psListGetAndIncrement(valuesIter))) {
-        psList *firstSecondThird = psStringSplit(value, ":", true); // List containing the first, second, third
-        psArray *fst = psListToArray(firstSecondThird); // An array representation
-        psFree(firstSecondThird);
-        psString firstPart = NULL;      // The first part
-        psString secondPart = NULL;     // The second part
-        psString thirdPart = NULL;      // The third part
-        switch (firstSecondThird->n) {
-        case 1:
-            thirdPart = fst->data[0];
-            break;
-        case 2:
-            secondPart = fst->data[0];
-            thirdPart = fst->data[1];
-            break;
-        case 3:
-            firstPart = fst->data[0];
-            secondPart = fst->data[1];
-            thirdPart = fst->data[2];
-            break;
-        default:
-            psLogMsg(__func__, PS_LOG_WARN, "Badly formated specifier: %s --- ignored.\n", value);
-            allOK = false;
-            psFree(fst);
-            continue;
-        }
-        psArraySet(*first, num, firstPart);
-        psArraySet(*second, num, secondPart);
-        psArraySet(*third, num, thirdPart);
-        num++;
-        psFree(fst);
-    }
-    psFree(valuesIter);
-    psFree(values);
-
-    return allOK;
-}
-
-// Get the name of a PHU chip or cell from the header
-static psString phuNameFromHeader(const char *name, // The name to lookup: "CELL.NAME" or "CHIP.NAME"
-                                  const psMetadata *fileInfo, // FILE within the camera format description
-                                  const psMetadata *header // Primary header
-                                 )
-{
-    bool mdok = true;                   // Result of MD lookup
-    psString keyword = psMetadataLookupStr(&mdok, fileInfo, name);
-    if (!mdok || strlen(keyword) == 0) {
-        return false;
-    }
-    psMetadataItem *resultItem = psMetadataLookup(header, keyword);
-    if (! resultItem) {
-        psError(PS_ERR_IO, false, "Unable to find %s in primary header to identify %s.\n", keyword, name);
-        return NULL;
-    }
-    return psMetadataItemParseString(resultItem);
-}
-
-// Add an HDU to the FPA
-static bool addHDUtoFPA(pmFPA *fpa,     // FPA to which to add
-                        pmHDU *hdu      // HDU to be added
-                       )
-{
-    assert(fpa);
-    assert(hdu);
-
-    if (fpa->hdu) {
-        // Something's already here
-        if (fpa->hdu != hdu) {
-            psError(PS_ERR_IO, true, "Unable to add HDU since FPA already has one.\n");
-        }
-        return false;
-    }
-    fpa->hdu = psMemIncrRefCounter(hdu);
-    if (hdu->header) {
-        pmConceptsReadFPA(fpa, PM_CONCEPT_SOURCE_HEADER, NULL);
-    }
-    pmFPASetFileStatus(fpa, true);
-
-    return true;
-}
-
-// Add an HDU to the chip
-static bool addHDUtoChip(pmChip *chip,  // Chip to which to add
-                         pmHDU *hdu     // HDU to be added
-                        )
-{
-    assert(chip);
-    assert(hdu);
-
-    if (chip->hdu) {
-        // Something's already here
-        if (chip->hdu != hdu) {
-            psError(PS_ERR_IO, true, "Unable to add HDU since chip already has one.\n");
-        }
-        return false;
-    }
-    chip->hdu = psMemIncrRefCounter(hdu);
-    if (hdu->header) {
-        pmConceptsReadChip(chip, PM_CONCEPT_SOURCE_HEADER, true, NULL);
-    }
-    pmChipSetFileStatus(chip, true);
-
-    return true;
-}
-
-// Add an HDU to the cell
-static bool addHDUtoCell(pmCell *cell,  // Cell to which to add
-                         pmHDU *hdu     // HDU to be added
-                        )
-{
-    assert(cell);
-    assert(hdu);
-
-    if (cell->hdu) {
-        // Something's already here
-        if (cell->hdu != hdu) {
-            psError(PS_ERR_IO, true, "Unable to add HDU since cell already has one.\n");
-        }
-        return false;
-    }
-    cell->hdu = psMemIncrRefCounter(hdu);
-    if (hdu->header) {
-        pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_HEADER, true, NULL);
-    }
-    pmCellSetFileStatus(cell, true);
-
-    return true;
-}
-
-
-// Looks up the particular content based on the header
-static const char *getContent(psMetadata *fileInfo, // The FILE from the camera format configuration
-                              psMetadata *contents, // The CONTENTS from the camera format configuration
-                              psMetadata *header // The (primary) header
-                             )
-{
-    bool mdok = true;                   // Status of MD lookup
-    const char *contentHeaders = psMetadataLookupStr(&mdok, fileInfo, "CONTENT"); // Headers for content
-    if (!mdok || !contentHeaders || strlen(contentHeaders) == 0) {
-        psError(PS_ERR_IO, true, "Unable to find CONTENT in FILE within camera format configuration.\n");
-        return NULL;
-    }
-
-    psList *keywords = psStringSplit(contentHeaders, " ,;", true); // List of keywords
-    psListIterator *keywordsIter = psListIteratorAlloc(keywords, PS_LIST_HEAD, false); // Iterator
-    psString keyword = NULL;        // Keyword, from iteration
-    psString contentsKey = NULL;    // Key to the CONTENTS menu
-    bool first = true;              // Is it the first value?
-    while ((keyword = psListGetAndIncrement(keywordsIter))) {
-        const char *value = psMetadataLookupStr(&mdok, header, keyword);
-        if (first) {
-            psStringAppend(&contentsKey, "%s", value);
-            first = false;
-        } else {
-            psStringAppend(&contentsKey, "_%s", value);
-        }
-    }
-    psFree(keywordsIter);
-    psFree(keywords);
-    psTrace(__func__, 5, "Looking up %s in the CONTENTS.\n", contentsKey);
-    const char *content = psMetadataLookupStr(&mdok, contents, contentsKey);
-    if (!mdok || !content || strlen(content) == 0) {
-        psError(PS_ERR_IO, true, "Unable to find %s in the CONTENTS.\n", contentsKey);
-        return NULL;
-    }
-    psFree(contentsKey);
-
-    return content;
-}
-
-
-// Given a (string) list of contents "chip:cell:type chip:cell:type", put the HDU in the correct place and
-// plug in the cell configuration information
-static int processContents(pmFPA *fpa,  // The FPA
-                           pmChip *chip, // The chip, or NULL
-                           pmCell *cell, // The cell, or NULL
-                           pmHDU *hdu,  // The HDU to be added
-                           pmFPALevel level, // The level at which to add the HDU
-                           const char *contents, // The contents line, consisting of a list of chip:cell
-                           psMetadata *format // Camera format configuration
-                          )
-{
-    assert(fpa);
-    assert(contents && strlen(contents) > 0);
-    assert(!cell || (cell && chip));    // Need both chip and cell if given a cell
-
-    if (hdu && level == PM_FPA_LEVEL_FPA) {
-        addHDUtoFPA(fpa, hdu);
-    }
-
-    // Parse the list of chip:cell:type
-    psArray *chips = NULL;              // The chips (first bits)
-    psArray *cells = NULL;              // The cells (second bits)
-    psArray *types = NULL;              // The cell types (third bits)
-    int numCells = 0;                   // Number of cells processed
-    parseContent(&chips, &cells, &types, contents);
-    for (int i = 0; i < types->n; i++) {
-        psString chipName = chips->data[i]; // The name of the chip
-        psString cellName = cells->data[i]; // The name of the cell
-        psString cellType = types->data[i]; // The type of the cell
-
-        // Get the chip
-        pmChip *newChip = NULL;         // The chip specified
-        if (chip) {
-            newChip = chip;
-        } else if (chipName) {
-            // Find the chip
-            int chipNum = pmFPAFindChip(fpa, chipName); // The chip we're looking for
-            if (chipNum == -1) {
-                psLogMsg(__func__, PS_LOG_WARN, "Unable to find chip %s in fpa --- ignored.\n", chipName);
-                continue;
-            }
-            newChip = fpa->chips->data[chipNum];
-        }
-
-        if (!newChip) {
-            psLogMsg(__func__, PS_LOG_WARN, "Unable to determine chip for entry %d of content (follows) --- "
-                     "ignored.\n\t%s", i, contents);
-            continue;
-        }
-
-        // Get the cell
-        pmCell *newCell = NULL;         // The cell specified
-        if (cell) {
-            newCell = cell;
-        } else if (cellName) {
-            // Find the cell
-            int cellNum = pmChipFindCell(newChip, cellName); // The cell we're looking for
-            if (cellNum == -1) {
-                psLogMsg(__func__, PS_LOG_WARN, "Unable to find cell %s in chip %s --- ignored.\n",
-                         cellName, chipName);
-                continue;
-            }
-            newCell = newChip->cells->data[cellNum];
-        }
-        if (!newCell) {
-            psLogMsg(__func__, PS_LOG_WARN, "Unable to determine cell for entry %d of content (follows) --- "
-                     "ignored.\n\t%s", i, contents);
-            continue;
-        }
-
-        // Get the type
-        if (!cellType) {
-            psLogMsg(__func__, PS_LOG_WARN, "Unable to determine cell type for entry %d of content (follows) "
-                     "--- ignored.\n\t%s", i, contents);
-            continue;
-        }
-        psMetadata *cellData = getCellData(format, cellType); // Data for this cell
-
-        // Put in the HDU
-        if (hdu && level == PM_FPA_LEVEL_CHIP) {
-            addHDUtoChip(newChip, hdu);
-        }
-        if (hdu && level == PM_FPA_LEVEL_CELL) {
-            addHDUtoCell(newCell, hdu);
-        }
-
-        // Put in the cell data
-        if (newCell->config) {
-            psLogMsg(__func__, PS_LOG_WARN, "Overwriting cell data in chip %s, cell %s\n", chipName,
-                     cellName);
-            psFree(newCell->config); // Make way!
-        }
-        newCell->config = psMemIncrRefCounter(cellData);
-        pmConceptsReadCell(newCell, PM_CONCEPT_SOURCE_CAMERA | PM_CONCEPT_SOURCE_DEFAULTS, false, NULL);
-        numCells++;
-    }
-    psFree(chips);
-    psFree(cells);
-    psFree(types);
-
-    return numCells;
-}
-
-static pmFPALevel hduLevel(psMetadata *format // The camera format configuration
-                          )
-{
-    bool mdok = true;                   // Status of MD lookup
-    psMetadata *file = psMetadataLookupMD(&mdok, format, "FILE"); // File information
-    if (!mdok || !file) {
-        psError(PS_ERR_IO, true, "Unable to find FILE information in camera format configuration.\n");
-        return PM_FPA_LEVEL_NONE;
-    }
-    const char *extType = psMetadataLookupStr(&mdok, file, "EXTENSIONS");
-    if (!mdok || !extType || strlen(extType) == 0) {
-        psError(PS_ERR_IO, true, "Unable to find EXTENSIONS in the FILE information in the camera format"
-                " configuration.\n");
-        return PM_FPA_LEVEL_NONE;
-    }
-
-    // Where do we stick in the HDUs?
-    pmFPALevel level = PM_FPA_LEVEL_NONE; // Level for HDU insertion
-    if (strcasecmp(extType, "CHIP") == 0) {
-        level = PM_FPA_LEVEL_CHIP;
-    } else if (strcasecmp(extType, "CELL") == 0) {
-        level = PM_FPA_LEVEL_CELL;
-    } else if (strcasecmp(extType, "NONE") != 0) {
-        psError(PS_ERR_IO, true, "EXTENSIONS is not CHIP or CELL or NONE.\n");
-    }
-
-    return level;
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Public functions
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Construct an FPA instance on the basis of a camera configuration
-pmFPA *pmFPAConstruct(const psMetadata *camera // The camera configuration
-                     )
-{
-    pmFPA *fpa = pmFPAAlloc(camera);    // The FPA to fill out
-
-    bool mdok = true;                   // Status from MD lookups
-    psMetadata *components = psMetadataLookupMD(&mdok, camera, "FPA"); // FPA components
-    psMetadataIterator *componentsIter = psMetadataIteratorAlloc(components, PS_LIST_HEAD, NULL);
-    psMetadataItem *componentsItem = NULL; // Item from components
-    while ((componentsItem = psMetadataGetAndIncrement(componentsIter))) {
-        const char *chipName = componentsItem->name; // Name of the chip
-        if (componentsItem->type != PS_DATA_STRING) {
-            psLogMsg(__func__, PS_LOG_WARN, "Element %s in FPA within the camera configuration is not of "
-                     "type STR (type=%x) --- ignored.\n", chipName, componentsItem->type);
-            continue;
-        }
-
-        pmChip *chip = pmChipAlloc(fpa, chipName); // The chip
-        psList *cellNames = psStringSplit(componentsItem->data.V, " ,;", true); // List of cell names
-        psListIterator *cellNamesIter = psListIteratorAlloc(cellNames, PS_LIST_HEAD, false); // Iterator
-
-        psString cellName = NULL;       // Name of cell
-        while ((cellName = psListGetAndIncrement(cellNamesIter))) {
-            pmCell *cell = pmCellAlloc(chip, cellName); // New cell
-            psFree(cell);               // Drop reference
-        }
-        psFree(chip);                   // Drop reference
-        psFree(cellNamesIter);
-        psFree(cellNames);
-    }
-    psFree(componentsIter);
-
-    return fpa;
-}
-
-bool pmFPAAddSourceFromView(pmFPA *fpa,   // The FPA
-                            const pmFPAview *phuView, // The view, corresponding to the PHU
-                            psMetadata *format // Format of file
-                           )
-{
-    assert(fpa);
-    assert(phuView);
-    assert(format);
-
-    // Where does the PHU go?
-    bool mdok = true;                   // Status of MD lookup
-    psMetadata *fileInfo = psMetadataLookupMD(&mdok, format, "FILE"); // File information from the format
-    if (!mdok || !fileInfo) {
-        psError(PS_ERR_IO, false, "Unable to find FILE information in the camera format configuration.\n");
-        return false;
-    }
-    const char *phuType = psMetadataLookupStr(&mdok, fileInfo, "PHU"); // What is the PHU?
-    if (!mdok || strlen(phuType) == 0) {
-        psError(PS_ERR_IO, false, "Unable to find PHU in the FILE information of the camera format.\n");
-        return false;
-    }
-
-    // Generate the PHU
-    pmHDU *phdu = pmHDUAlloc("PHU");    // The primary header data unit
-    phdu->format = psMemIncrRefCounter(format);
-
-    // Put in the PHU
-    pmChip *chip = NULL;                // The chip that corresponds to the PHU
-    pmCell *cell = NULL;                // The cell that corresponds to the PHU
-    if (strcasecmp(phuType, "FPA") == 0) {
-        addHDUtoFPA(fpa, phdu);
-        psFree(phdu);
-    } else {
-        psArray *chips = fpa->chips;    // Array of chips
-        if (phuView->chip < 0 || phuView->chip > chips->n) {
-            psError(PS_ERR_IO, true, "PHU specified by the camera format requires specification of a "
-                    "particular chip, which cannot be determined from the view (%d).\n", phuView->chip);
-            psFree(phdu);
-            return false;
-        }
-        chip = chips->data[phuView->chip];
-        if (strcasecmp(phuType, "CHIP") == 0) {
-            addHDUtoChip(chip, phdu);
-            psFree(phdu);
-        } else if (strcasecmp(phuType, "CELL") == 0) {
-            psArray *cells = chip->cells; // Array of cells
-            if (phuView->cell < 0 || phuView->cell > cells->n) {
-                psError(PS_ERR_IO, true, "PHU specified by the camera format requires specification of a "
-                        "particular cell, which cannot be determined from the view (%d).\n", phuView->cell);
-                psFree(phdu);
-                return false;
-            }
-            addHDUtoCell(cell, phdu);
-            psFree(phdu);
-        } else {
-            psError(PS_ERR_IO, true, "PHU in the camera configuration format is not FPA, CHIP or CELL.\n");
-            psFree(phdu);
-            return false;
-        }
-    }
-
-    // Put in the extensions
-    pmFPALevel level = hduLevel(format);// The level for the HDUs to go
-    if (level == PM_FPA_LEVEL_NONE) {
-        // No extensions --- we're done
-        return true;
-    }
-    psMetadata *contents = psMetadataLookupMD(&mdok, format, "CONTENTS"); // The contents of the FITS file
-    if (!mdok || !contents) {
-        psError(PS_ERR_IO, false, "Unable to find CONTENTS in the camera format configuration.\n");
-        return false;
-    }
-
-    psMetadataIterator *contentsIter = psMetadataIteratorAlloc(contents, PS_LIST_HEAD, NULL); // Iterator
-    psMetadataItem *contentsItem = NULL;// Item from the contents iteration
-    while ((contentsItem = psMetadataGetAndIncrement(contentsIter))) {
-        if (contentsItem->type != PS_DATA_STRING) {
-            psLogMsg(__func__, PS_LOG_WARN, "Content item %s is not of type STR --- ignored.\n",
-                     contentsItem->name);
-            continue;
-        }
-        pmHDU *hdu = pmHDUAlloc(contentsItem->name); // HDU to add
-        hdu->format = psMemIncrRefCounter(format);
-        const char *content = contentsItem->data.V; // The content data
-        processContents(fpa, chip, cell, hdu, level, content, format);
-        psFree(hdu);
-    }
-    psFree(contentsIter);
-
-    return true;
-}
-
-
-
-// Add an input file to the FPA
-pmFPAview *pmFPAAddSourceFromHeader(pmFPA *fpa, // The FPA
-                                    psMetadata *phu, // Primary header of file
-                                    psMetadata *format // Format of file
-                                   )
-{
-    assert(fpa);
-    assert(phu);
-    assert(format);
-
-    bool mdok = true;                   // Status from metadata lookups
-    psMetadata *fileInfo = psMetadataLookupMD(&mdok, format, "FILE"); // The file information
-    if (!mdok || !fileInfo) {
-        psError(PS_ERR_IO, false, "Unable to find FILE in the camera format configuration.\n");
-        return NULL;
-    }
-
-    // Check the name of the FPA
-    psString newFPAname = phuNameFromHeader("FPA.NAME", fileInfo, phu); // New name for the FPA
-    const char *currentFPAname = psMetadataLookupStr(&mdok, fpa->concepts, "FPA.NAME"); // Current name
-    if (mdok && currentFPAname && strlen(currentFPAname) > 0 && strcmp(currentFPAname, newFPAname) != 0) {
-        psLogMsg(__func__, PS_LOG_WARN, "FPA.NAME for new source (%s) doesn't match FPA.NAME for current "
-                 "fpa (%s).\n", newFPAname, currentFPAname);
-    }
-    psMetadataAddStr(fpa->concepts, PS_LIST_HEAD, "FPA.NAME", PS_META_REPLACE, "Name of FPA", newFPAname);
-    psFree(newFPAname);                 // Drop reference
-
-    // Where does the PHU go?
-    const char *phuType = psMetadataLookupStr(&mdok, fileInfo, "PHU"); // What is the PHU?
-    if (!mdok || strlen(phuType) == 0) {
-        psError(PS_ERR_IO, false, "Unable to find PHU in the format specification.\n");
-        return NULL;
-    }
-    pmHDU *phdu = pmHDUAlloc("PHU");    // The primary header data unit
-    phdu->header = psMemIncrRefCounter(phu);
-    phdu->format = psMemIncrRefCounter(format);
-    // Generate the view
-    pmFPAview *view = pmFPAviewAlloc(0); // The FPA view corresponding to the PHU, to be returned
-    view->chip = -1;
-    view->cell = -1;
-    view->readout = -1;
-
-    // And what are the individual extensions?
-    const char *extType = psMetadataLookupStr(&mdok, fileInfo, "EXTENSIONS"); // What's in the extns?
-    if (!mdok || strlen(extType) == 0) {
-        psError(PS_ERR_IO, false, "Unable to find EXTENSIONS in the format specification.\n");
-        psFree(view);
-        return NULL;
-    }
-
-
-    // This is a special case: PHU=FPA and EXTENSIONS=NONE.  The only reason to do this is in the case of
-    // single CCD imagers, where the entire FPA is in the PHU image.  In this case, the CONTENTS is of type
-    // STR (rather than METADATA), and has the usual chip:cell.
-    if (strcasecmp(phuType, "FPA") == 0 && strcasecmp(extType, "NONE") == 0) {
-        const char *contents = psMetadataLookupStr(&mdok, format, "CONTENTS"); // The contents of the file
-        if (!mdok || !contents || strlen(contents) == 0) {
-            psError(PS_ERR_IO, true, "Unable to find CONTENTS in the camera format configuration.\n");
-            psFree(view);
-            return NULL;
-        }
-
-        processContents(fpa, NULL, NULL, phdu, PM_FPA_LEVEL_FPA, contents, format);
-        psFree(phdu);
-        return view;
-    }
-
-    // In all other cases, the CONTENTS is of type METADATA, and is either a menu if EXTENSIONS=NONE, or
-    // a list of extensions otherwise.
-    psMetadata *contents = psMetadataLookupMD(&mdok, format, "CONTENTS"); // The contents of the FITS file
-    if (!mdok || !contents) {
-        psError(PS_ERR_IO, false, "Unable to find CONTENTS in the camera format configuration.\n");
-        psFree(view);
-        return NULL;
-    }
-
-    // No extensions --- only have the PHU.  In this case, the CONTENTS is a menu, with CONTENT indicating
-    // header keywords that provides a key to the CONTENTS menu.
-    if (strcasecmp(extType, "NONE") == 0) {
-        // We have already dealt with the case PHU=FPA, in a special case, above.
-        const char *content = getContent(fileInfo, contents, phu); // The content: string of chip:cell pairs
-
-        // Need to look up what chip we have.
-        psString chipName = phuNameFromHeader("CHIP.NAME", fileInfo, phu);
-        psTrace(__func__, 5, "This is chip %s\n", chipName);
-        int chipNum = pmFPAFindChip(fpa, chipName); // Chip number
-        if (chipNum == -1) {
-            psError(PS_ERR_IO, true, "Unable to find chip %s in FPA.\n", chipName);
-            psFree(view);
-            return NULL;
-        }
-        pmChip *chip = fpa->chips->data[chipNum]; // Chip of interest
-        view->chip = chipNum;
-
-        pmCell *cell = NULL;            // Cell of interest
-
-        pmFPALevel level = PM_FPA_LEVEL_NONE; // Level for HDU to be added
-        if (strcasecmp(phuType, "CHIP") == 0) {
-            level = PM_FPA_LEVEL_CHIP;
-        } else if (strcasecmp(phuType, "CELL") == 0) {
-            level = PM_FPA_LEVEL_CELL;
-            // Need to look up what cell we have.
-            psString cellName = phuNameFromHeader("CELL.NAME", fileInfo, phu);
-            int cellNum = pmChipFindCell(chip, cellName); // Cell number
-            if (cellNum == -1) {
-                psError(PS_ERR_IO, true, "Unable to find cell %s in chip %s.\n", cellName, chipName);
-                psFree(view);
-                return NULL;
-            }
-            cell = chip->cells->data[cellNum];
-            view->cell = cellNum;
-            psFree(cellName);
-        } else {
-            psError(PS_ERR_IO, true, "PHU is not FPA, CHIP or CELL.\n");
-            psFree(view);
-            return NULL;
-        }
-        psFree(chipName);
-
-        processContents(fpa, chip, cell, phdu, level, content, format);
-        psFree(phdu);
-        return view;
-    }
-
-
-    // From here on, we have extensions that we iterate through.  The CONTENTS is a list of extensions.
-    pmChip *chip = NULL;                // The chip of interest
-    pmCell *cell = NULL;                // The cell of interest
-
-    // First, put in the PHU
-    if (strcasecmp(phuType, "FPA") == 0) {
-        addHDUtoFPA(fpa, phdu);
-    } else {
-        // Get the chip
-        psString chipName = phuNameFromHeader("CHIP.NAME", fileInfo, phu); // Name of the chip
-        int chipNum = pmFPAFindChip(fpa, chipName); // Chip number
-        if (chipNum == -1) {
-            psError(PS_ERR_IO, true, "Unable to find chip %s in FPA.\n", chipName);
-            psFree(view);
-            return NULL;
-        }
-        chip = fpa->chips->data[chipNum]; // The specified chip
-        view->chip = chipNum;
-
-        if (strcasecmp(phuType, "CHIP") == 0) {
-            addHDUtoChip(chip, phdu);
-        } else if (strcasecmp(phuType, "CELL") == 0) {
-            psString cellName = phuNameFromHeader("CELL.NAME", fileInfo, phu); // Name of the cell
-            int cellNum = pmChipFindCell(chip, cellName); // Cell number
-            if (cellNum == -1) {
-                psError(PS_ERR_IO, true, "Unable to find cell %s in chip %s.\n", cellName, chipName);
-                psFree(view);
-                return NULL;
-            }
-            cell = chip->cells->data[cellNum]; // The specified cell
-            view->cell = cellNum;
-            psFree(cellName);
-
-            addHDUtoCell(cell, phdu);
-        } else {
-            psError(PS_ERR_IO, true, "The format of the PHU (%s) is not FPA, CHIP or CELL.\n", phuType);
-            psFree(view);
-            return NULL;
-        }
-        psFree(chipName);
-    }
-    psFree(phdu);
-
-    pmFPALevel level = hduLevel(format);// The level at which to plug in HDU
-
-    // Now go through the contents
-    psMetadataIterator *contentsIter = psMetadataIteratorAlloc(contents, PS_LIST_HEAD, NULL);
-    psMetadataItem *contentsItem = NULL; // Item from contents
-    while ((contentsItem = psMetadataGetAndIncrement(contentsIter))) {
-        const char *extName = contentsItem->name;
-        if (contentsItem->type != PS_DATA_STRING) {
-            psLogMsg(__func__, PS_LOG_WARN, "CONTENTS item %s is not of type STR --- ignored.\n", extName);
-            continue;
-        }
-
-        pmHDU *hdu = pmHDUAlloc(extName); // The extension
-        hdu->format = psMemIncrRefCounter(format);
-
-        processContents(fpa, chip, cell, hdu, level, contentsItem->data.V, format);
-        psFree(hdu);
-    }
-    psFree(contentsIter);
-
-    pmConceptsReadFPA(fpa, PM_CONCEPT_SOURCE_DEFAULTS, NULL);
-
-    return view;
-}
-
-
-// Print out the focal plane structure
-void pmFPAPrint(pmFPA *fpa,             // FPA to print
-                bool header,            // Print headers?
-                bool concepts           // Print concepts?
-               )
-{
-    psTrace(__func__, 1, "FPA:\n");
-    if (fpa->hdu) {
-        psTrace(__func__, 2, "---> FPA is extension %s.\n", fpa->hdu->extname);
-        if (! fpa->hdu->images) {
-            psTrace(__func__, 2, "---> NO PIXELS read in for extension %s\n", fpa->hdu->extname);
-        }
-        if (header) {
-            if (fpa->hdu->header) {
-                psTrace(__func__, 2, "---> Header:\n");
-                psMetadataPrint(fpa->hdu->header, 8);
-            } else {
-                psTrace(__func__, 2, "---> NO HEADER read in for extension %s\n", fpa->hdu->extname);
-            }
-        }
-    }
-    if (concepts) {
-        psMetadataPrint(fpa->concepts, 2);
-    }
-
-    psArray *chips = fpa->chips;        // Array of chips
-    // Iterate over the FPA
-    for (int i = 0; i < chips->n; i++) {
-        psTrace(__func__, 3, "Chip: %d\n", i);
-        pmChip *chip = chips->data[i]; // The chip
-        if (chip->hdu) {
-            psTrace(__func__, 4, "---> Chip is extension %s.\n", chip->hdu->extname);
-            if (header) {
-                if (chip->hdu->header) {
-                    psTrace(__func__, 4, "---> Header:\n");
-                    psMetadataPrint(chip->hdu->header, 8);
-                } else {
-                    psTrace(__func__, 4, "---> NO HEADER read in for extension %s\n", chip->hdu->extname);
-                }
-            }
-            if (! chip->hdu->images) {
-                psTrace(__func__, 4, "---> NO PIXELS read in for extension %s\n", chip->hdu->extname);
-            }
-        }
-        if (concepts) {
-            psMetadataPrint(chip->concepts, 4);
-        }
-
-        // Iterate over the chip
-        psArray *cells = chip->cells;   // Array of cells
-        for (int j = 0; j < cells->n; j++) {
-            psTrace(__func__, 5, "Cell: %d\n", j);
-            pmCell *cell = cells->data[j]; // The cell
-            if (cell->hdu) {
-                psTrace(__func__, 6, "---> Cell is extension %s.\n", cell->hdu->extname);
-                if (header) {
-                    if (cell->hdu->header) {
-                        psTrace(__func__, 6, "---> Header:\n");
-                        psMetadataPrint(cell->hdu->header, 8);
-                    } else {
-                        psTrace(__func__, 6, "---> NO HEADER read in for extension %s\n", cell->hdu->extname);
-                    }
-                }
-                if (! cell->hdu->images) {
-                    psTrace(__func__, 6, "---> NO PIXELS read in for extension %s\n", cell->hdu->extname);
-                }
-            }
-            if (concepts) {
-                psMetadataPrint(cell->concepts, 6);
-            }
-
-            psTrace(__func__, 7, "Readouts:\n");
-            psArray *readouts = cell->readouts; // Array of readouts
-            for (int k = 0; k < readouts->n; k++) {
-                pmReadout *readout = readouts->data[k]; // The readout
-                psTrace(__func__, 8, "row0: %d\n", readout->row0);
-                psImage *image = readout->image; // The image
-                psList *bias = readout->bias; // The list of bias images
-                if (image) {
-                    psTrace(__func__, 8, "Image: [%d:%d,%d:%d] (%dx%d)\n", image->col0, image->col0 +
-                            image->numCols, image->row0, image->row0 + image->numRows, image->numCols,
-                            image->numRows);
-                }
-                if (bias) {
-                    psListIterator *biasIter = psListIteratorAlloc(bias, PS_LIST_HEAD, false); // Iterator
-                    psImage *biasImage = NULL; // Bias image from iteration
-                    while ((biasImage = psListGetAndIncrement(biasIter))) {
-                        psTrace(__func__, 8, "Bias:  [%d:%d,%d:%d] (%dx%d)\n", biasImage->col0,
-                                biasImage->col0 + biasImage->numCols, biasImage->row0,
-                                biasImage->row0 + biasImage->numRows, biasImage->numCols, biasImage->numRows);
-                    }
-                    psFree(biasIter);
-                }
-            } // Iterating over cell
-        } // Iterating over chip
-    } // Iterating over FPA
-
-}
Index: unk/psModules/src/astrom/pmFPAConstruct.h
===================================================================
--- /trunk/psModules/src/astrom/pmFPAConstruct.h	(revision 7018)
+++ 	(revision )
@@ -1,28 +1,0 @@
-#ifndef PM_FPA_CONSTRUCT_H
-#define PM_FPA_CONSTRUCT_H
-
-#include "pslib.h"
-#include "pmFPA.h"
-#include "pmFPAview.h"
-
-// Construct an FPA instance on the basis of a camera configuration
-pmFPA *pmFPAConstruct(const psMetadata *camera // The camera configuration
-                     );
-
-bool pmFPAAddSourceFromView(pmFPA *fpa,   // The FPA
-                            const pmFPAview *phuView, // The view, corresponding to the PHU
-                            psMetadata *format // Format of file
-                           );
-
-pmFPAview *pmFPAAddSourceFromHeader(pmFPA *fpa, // The FPA
-                                    psMetadata *phu, // Primary header of file
-                                    psMetadata *format // Format of file
-                                   );
-
-// Print out the FPA
-void pmFPAPrint(pmFPA *fpa,             // FPA to print
-                bool header,            // Print headers?
-                bool concepts           // Print concepts?
-               );
-
-#endif
Index: unk/psModules/src/astrom/pmFPACopy.c
===================================================================
--- /trunk/psModules/src/astrom/pmFPACopy.c	(revision 7018)
+++ 	(revision )
@@ -1,693 +1,0 @@
-#include <stdio.h>
-#include <assert.h>
-
-#include "pslib.h"
-#include "psImageFlip.h"
-#include "psRegionIsBad.h"
-
-#include "pmFPA.h"
-#include "pmFPAUtils.h"
-#include "pmHDU.h"
-#include "pmHDUUtils.h"
-#include "pmFPACopy.h"
-
-#define MAX(x,y) ((x) > (y) ? (x) : (y))
-
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// File-static functions
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Add cells in a chip to a list
-static bool addCellsFromChip(psList *list, // List of cells
-                             const pmChip *chip // The chip from which to add cells
-                            )
-{
-    assert(list);
-    assert(chip);
-
-    psArray *cells = chip->cells;       // Array of cells
-    bool result = true;                 // Result of adding cells
-    for (int i = 0; i < cells->n; i++) {
-        pmCell *cell = cells->data[i];  // A cell
-        result |= psListAdd(list, PS_LIST_TAIL, cell);
-    }
-
-    return result;
-}
-
-// Add cells in an FPA to a list
-static bool addCellsFromFPA(psList *list, // List of cells
-                            const pmFPA *fpa // The FPA from which to add cells
-                           )
-{
-    assert(list);
-    assert(fpa);
-
-    psArray *chips = fpa->chips;        // Array of chips
-    bool result = true;                 // Result of adding cells
-    for (int i = 0; i < chips->n; i++) {
-        pmChip *chip = chips->data[i];  // A chip
-        result |= addCellsFromChip(list, chip);
-    }
-
-    return result;
-}
-
-// Get a list of cells that share the HDU for the target cell
-static bool cellList(psList *targets,   // The list of target cells
-                     psList *sources,   // The list of source cells
-                     pmCell *targetCell, // The target cell
-                     pmCell *sourceCell // The source cell
-                    )
-{
-    assert(targetCell);
-    assert(sourceCell);
-
-    if (targetCell->hdu) {
-        if (targets) {
-            psListAdd(targets, PS_LIST_TAIL, targetCell);
-        }
-        if (sources) {
-            psListAdd(sources, PS_LIST_TAIL, sourceCell);
-        }
-    } else {
-        pmChip *targetChip = targetCell->parent; // The target parent chip
-        pmChip *sourceChip = sourceCell->parent; // The source parent chip
-        if (targetChip->hdu) {
-            if (targets) {
-                addCellsFromChip(targets, targetChip);
-            }
-            if (sources) {
-                addCellsFromChip(sources, sourceChip);
-            }
-        } else {
-            pmFPA *targetFPA = targetChip->parent; // The target parent FPA
-            pmFPA *sourceFPA = sourceChip->parent; // The source parent FPA
-            if (targetFPA->hdu) {
-                if (targets) {
-                    addCellsFromFPA(targets, targetFPA);
-                }
-                if (sources) {
-                    addCellsFromFPA(sources, sourceFPA);
-                }
-            } else {
-                psError(PS_ERR_IO, true, "Unable to find HDU for cell to generate list!\n");
-                return false;
-            }
-        }
-    }
-
-    return true;
-}
-
-// Get the maximum extent of the HDU from the trimsec and biassecs
-static bool sizeHDU(int *xSize, int *ySize, // Size of HDU
-                    psList *cells       // List of cells
-                   )
-{
-    psListIterator *cellsIter = psListIteratorAlloc(cells, PS_LIST_HEAD, false); // Iterator for cells
-    pmCell *cell = NULL;                // The cell from iteration
-    bool mdok = true;                   // Status of MD lookup
-    *xSize = 0;
-    *ySize = 0;
-    while ((cell = psListGetAndIncrement(cellsIter))) {
-        psRegion *trimsec = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.TRIMSEC"); // Trim section
-        if (mdok && trimsec && !psRegionIsBad(*trimsec)) {
-            *xSize = MAX(trimsec->x1, *xSize);
-            *ySize = MAX(trimsec->y1, *ySize);
-        } else {
-            psFree(cellsIter);
-            return false;
-        }
-        psList *biassecs = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.BIASSEC"); // Bias sections
-        if (mdok && biassecs) {
-            psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false); // Iterator
-            psRegion *biassec = NULL;   // The bias section
-            while ((biassec = psListGetAndIncrement(biassecsIter))) {
-                if (!psRegionIsBad(*trimsec)) {
-                    *xSize = MAX(biassec->x1, *xSize);
-                    *ySize = MAX(biassec->y1, *ySize);
-                } else {
-                    psFree(biassecsIter);
-                    psFree(cellsIter);
-                    return false;
-                }
-            }
-            psFree(biassecsIter);
-        }
-    }
-    psFree(cellsIter);
-
-    return (*xSize != 0 && *ySize != 0);
-}
-
-
-static psRegion sectionForImage(int *position, // Position on the output image, updated
-                                const psImage *image, // Image containing the sizes and offsets
-                                int readdir // Read direction, 1=rows, 2=cols
-                               )
-{
-    psRegion region;
-    switch (readdir) {
-    case 1:                           // Read direction is rows
-        region = psRegionSet(*position, *position + image->numCols, image->row0,
-                             image->row0 + image->numRows);
-        *position += image->numCols;
-        break;
-    case 2:                           // Read direction is columns
-        region = psRegionSet(image->col0, image->col0 + image->numCols, *position,
-                             *position + image->numRows);
-        *position += image->numRows;
-        break;
-    default:
-        psAbort(__func__, "Shouldn't ever get here!\n");
-    }
-
-    return region;
-}
-
-static bool doBiasSections(int *position, // Position on the output image, updated
-                           pmCell *target, // Target cell
-                           const pmCell *source // Source cell
-                          )
-{
-    psMetadataItem *biassecItem = psMetadataLookup(target->concepts, "CELL.BIASSEC"); // Bias sections
-    if (!biassecItem) {
-        psLogMsg(__func__, PS_LOG_WARN, "CELL.BIASSEC has not been initialised in target cell --- "
-                 "ignored.\n");
-        return false;
-    }
-    psFree(biassecItem->data.V);        // Blow away the old list
-    psList *biassecs = psListAlloc(NULL);
-    biassecItem->data.V = biassecs;
-
-    bool mdok = true;                   // Status of MD lookup
-    int readdir = psMetadataLookupS32(&mdok, source->concepts, "CELL.READDIR"); // Read direction
-    if (!mdok || (readdir != 1 && readdir != 2)) {
-        // Probably unnecessary, but just in case....
-        psLogMsg(__func__, PS_LOG_WARN, "CELL.READDIR is not set in source cell --- ignored.\n");
-        return false;
-    }
-
-    pmReadout *readout = source->readouts->data[0]; // The first source readout, as representative
-    psList *biases = readout->bias; // The bias images from the source readout
-
-    psListIterator *biasIter = psListIteratorAlloc(biases, PS_LIST_HEAD, true); // Iterator for biases
-    psImage *bias = NULL;       // Bias image from iteration
-    while ((bias = psListGetAndIncrement(biasIter))) {
-        // Construct a region
-        psRegion *biassec = psAlloc(sizeof(psRegion)); // The new region; need a psMemBlock
-        *biassec = sectionForImage(position, bias, readdir);
-        psListAdd(biassecs, PS_LIST_TAIL, biassec);
-        psFree(biassec);        // Drop reference
-    }
-    psFree(biasIter);
-
-    return true;
-}
-
-// Generate CELL.TRIMSEC and CELL.BIASSEC for the target cells
-static bool generateTrimBias(psList *targets, // List of target cells
-                             psList *sources // List of source cells
-                            )
-{
-    pmCell *target = NULL, *source = NULL; // Cells from iteration
-    int numCells = targets->n;          // Number of cells
-    int cellNum = 0;                    // The cell number
-    int position = 0;                   // Position on the image
-    bool mdok = true;                   // Status of MD lookup
-
-    // First run through to do the LHS biases
-    psListIterator *targetsIter = psListIteratorAlloc(targets, PS_LIST_HEAD, false); // Iterator for targets
-    psListIterator *sourcesIter = psListIteratorAlloc(sources, PS_LIST_HEAD, false); // Iterator for sources
-    bool done = false;                   // Done with iteration?
-    while ((target = psListGetAndIncrement(targetsIter)) && (source = psListGetAndIncrement(sourcesIter)) &&
-            !done) {
-        if (cellNum <= numCells/2 - 1) {
-            doBiasSections(&position, target, source);
-            cellNum++;
-        } else {
-            done = true;
-        }
-    }
-
-    // Second run through to do the trim sections
-    psListIteratorSet(targetsIter, PS_LIST_HEAD);
-    psListIteratorSet(sourcesIter, PS_LIST_HEAD);
-    while ((target = psListGetAndIncrement(targetsIter)) && (source = psListGetAndIncrement(sourcesIter))) {
-        psRegion *trimsec = psMetadataLookupPtr(&mdok, target->concepts, "CELL.TRIMSEC"); // Trim section
-        if (!mdok || !trimsec) {
-            psLogMsg(__func__, PS_LOG_WARN, "CELL.TRIMSEC has not been initialised in target cell --- "
-                     "ignored.\n");
-            continue;
-        }
-
-        int readdir = psMetadataLookupS32(&mdok, source->concepts, "CELL.READDIR"); // Read direction
-        if (!mdok || (readdir != 1 && readdir != 2)) {
-            // Probably unnecessary, but just in case....
-            psLogMsg(__func__, PS_LOG_WARN, "CELL.READDIR is not set in source cell --- ignored.\n");
-            continue;
-        }
-
-        pmReadout *readout = source->readouts->data[0]; // The first source readout, as representative
-        psImage *image = readout->image;// The proper image
-        *trimsec = sectionForImage(&position, image, readdir);
-    }
-
-    // A final run through to do the RHS biases
-    psListIteratorSet(targetsIter, cellNum);
-    psListIteratorSet(sourcesIter, cellNum);
-    while ((target = psListGetAndIncrement(targetsIter)) && (source = psListGetAndIncrement(sourcesIter))) {
-        doBiasSections(&position, target, source);
-    }
-
-    // Clean up
-    psFree(targetsIter);
-    psFree(sourcesIter);
-
-    return (position > 0);
-}
-
-
-// Generate an HDU with the pixels
-static bool generateHDU(pmCell *target,  // The target cell
-                        pmCell *source, // The source cell
-                        int xBin, int yBin // Binning in x and y
-                       )
-{
-    // Get the HDU and a list of cells below it
-    pmHDU *hdu = pmHDUFromCell(target); // The HDU in the target cell
-    psList *targetCells = psListAlloc(NULL); // List of target cells below the target HDU
-    psList *sourceCells = psListAlloc(NULL); // List of source cells below the target HDU
-    if (! cellList(targetCells, sourceCells, target, source)) {
-        psError(PS_ERR_IO, true, "Unable to find cells to generate HDU!\n");
-        return false;
-    }
-
-    // Check the number of readouts
-    int numReadouts = -1;               // Number of readouts
-    {
-        psListIterator *iter = psListIteratorAlloc(sourceCells, PS_LIST_HEAD, false); // Iterator for cells
-        pmCell *cell = NULL;                // The cell from iteration
-        while ((cell = psListGetAndIncrement(iter)))
-        {
-            psArray *readouts = cell->readouts;
-            if (numReadouts == -1) {
-                numReadouts = readouts->n;
-            } else if (readouts->n != numReadouts) {
-                psError(PS_ERR_IO, true, "Number of readouts doesn't match: %d vs %d\n", readouts->n,
-                        numReadouts);
-                return false;
-            }
-
-        }
-        psFree(iter);
-    }
-
-    // Get the size of the HDU, either from existing trimsec and biassec, or generate these and try again
-    int xSize = 0, ySize = 0;           // Size of HDU
-    if (!sizeHDU(&xSize, &ySize, targetCells) && !(generateTrimBias(targetCells, sourceCells) &&
-            sizeHDU(&xSize, &ySize, targetCells))) {
-        psError(PS_ERR_IO, true, "Unable to determine size of HDU!\n");
-        return false;
-    }
-    psFree(targetCells);
-    psFree(sourceCells);
-
-    xSize = (int)ceilf((float)xSize/(float)xBin);
-    ySize = (int)ceilf((float)ySize/(float)yBin);
-
-    hdu->images = psArrayAlloc(numReadouts);
-    hdu->images->n = numReadouts;
-    for (int i = 0; i < numReadouts; i++) {
-        psImage *image = psImageAlloc(xSize, ySize, PS_TYPE_F32);
-        psImageInit(image, 0.0);
-        hdu->images->data[i] = image;
-    }
-
-    return true;
-}
-
-// Copy pixels from a target image to a source image, with flips
-static bool copyPixels(psImage *target, // Target image (HDU pixels)
-                       psImage *source, // Source image (from source cell)
-                       psRegion region, // Region for pasting
-                       bool xFlip,      // Flip in x?
-                       bool yFlip       // Flip in y?
-                      )
-{
-    psImage *overlay = psMemIncrRefCounter(source);
-    if (xFlip) {
-        psImage *temp = psImageFlipX(overlay);
-        psFree(overlay);
-        overlay = temp;
-    }
-    if (yFlip) {
-        psImage *temp = psImageFlipY(overlay);
-        psFree(overlay);
-        overlay = temp;
-    }
-    int numPix = psImageOverlaySection(target, overlay, region.x0, region.y0, "=");
-    psFree(overlay);
-    return (numPix > 0);
-}
-
-// Bin a region down by specified factors in x and y
-static void binRegion(psRegion *region, // Region to be binned
-                      int xBin, int yBin// Binning in x and y
-                     )
-{
-    // Want to include the lower bound: 1 binned by 4 --> 0; 3 binned by 4 --> 0; 4 binned by 4 --> 1
-    region->x0 = (int)(region->x0 / xBin);
-    region->y0 = (int)(region->y0 / yBin);
-    // Want to exclude the upper bound: 4 binned by 4 --> 1; 5 binned by 4 --> 2; 7 binned by 4 --> 2
-    region->x1 = (int)((region->x1 + xBin - 1) / xBin);
-    region->y1 = (int)((region->y1 + yBin - 1) / yBin);
-}
-
-
-static pmHDU *findPHU(const pmCell *cell// The cell for which to find the PHU
-                     )
-{
-    if (cell->hdu && cell->hdu->phu) {
-        return cell->hdu;
-    }
-    pmChip *chip = cell->parent;        // The parent chip
-    if (chip->hdu && chip->hdu->phu) {
-        return chip->hdu;
-    }
-    pmFPA *fpa = chip->parent;  // The parent FPA
-    if (fpa->hdu && fpa->hdu->phu) {
-        return fpa->hdu;
-    }
-
-    psError(PS_ERR_IO, true, "Unable to find PHU for target cell.\n");
-    return NULL;
-}
-
-
-static bool copyPHU(pmCell *targetCell, // The target cell
-                    pmCell *sourceCell  // The source cell
-                   )
-{
-    // Find the respective PHUs
-    pmHDU *targetPHU = findPHU(targetCell); // The target PHU
-    if (targetPHU->header) {
-        return false;                   // No work required
-    }
-    // Copy the header over
-    pmHDU *sourcePHU = findPHU(sourceCell); // The source PHU
-    targetPHU->header = psMetadataCopy(NULL, sourcePHU->header);
-    return true;
-}
-
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// File-static engine functions --- these do all the work.  Actually, cellCopy does all the work; the others
-// merely iterate on the higher-level components.
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-static int cellCopy(pmCell *target,     // The target cell
-                    pmCell *source,     // The source cell, to be copied
-                    bool pixels,        // Copy the pixels?
-                    int xBin, int yBin  // (Relative) binning factors in x and y
-                   )
-{
-    assert(target);
-    assert(source);
-
-    psArray *sourceReadouts = source->readouts; // The source readouts
-    int numReadouts = sourceReadouts->n; // Number of readouts copied
-
-    // Copy any headers
-    if (target->hdu && !target->hdu->phu) {
-        pmHDU *sourceHDU = pmHDUFromCell(source);
-        target->hdu->header = psMetadataCopy(target->hdu->header, sourceHDU->header);
-    }
-
-    pmHDU *hdu = pmHDUFromCell(target); // The target HDU; we need to fix this up
-    if (!hdu->images) {
-        generateHDU(target, source, xBin, yBin);
-    }
-    if (!hdu->header) {
-        hdu->header = psMetadataAlloc();
-    }
-    // Copy the PHU over as well, if required
-    copyPHU(target, source);
-
-    // Need to check/change CELL.XPARITY and CELL.YPARITY
-    bool xFlip = (psMetadataLookupS32(NULL, target->concepts, "CELL.XPARITY") !=
-                  psMetadataLookupS32(NULL, source->concepts, "CELL.XPARITY")); // Switch parity in x?
-    bool yFlip = (psMetadataLookupS32(NULL, target->concepts, "CELL.YPARITY") !=
-                  psMetadataLookupS32(NULL, source->concepts, "CELL.YPARITY")); // Switch parity in y?
-    psTrace(__func__, 3, "xFlip: %d; yFlip: %d\n", xFlip, yFlip);
-
-    bool mdok = true;                   // Status of MD lookup
-    psRegion *trimsec = psMetadataLookupPtr(&mdok, target->concepts, "CELL.TRIMSEC"); // The trim section
-    if (!mdok || !trimsec || psRegionIsBad(*trimsec)) {
-        psError(PS_ERR_IO, true, "CELL.TRIMSEC isn't set!\n");
-        return 0;
-    }
-    psList *biassecs = psMetadataLookupPtr(&mdok, target->concepts, "CELL.BIASSEC"); // The bias sections
-    if (!mdok || !biassecs) {
-        psError(PS_ERR_IO, true, "CELL.BIASSEC isn't set!\n");
-        return 0;
-    }
-
-    for (int i = 0; i < numReadouts; i++) {
-        pmReadout *sourceReadout = sourceReadouts->data[i]; // The source readout
-        psImage *sourceImage = sourceReadout->image; // The source image
-        pmReadout *targetReadout = pmReadoutAlloc(target); // The target readout; this adds it to the cell
-        if (sourceImage->numCols != trimsec->x1 - trimsec->x0 ||
-                sourceImage->numRows != trimsec->y1 - trimsec->y0) {
-            psString trimsecString = psRegionToString(*trimsec); // String with the trim section
-            psLogMsg(__func__, PS_LOG_WARN, "Source image size (%dx%d) for readout %d doesn't match "
-                     "CELL.TRIMSEC for target (%s) -- ignored.\n", sourceImage->numCols, sourceImage->numRows,
-                     i, trimsecString);
-            psFree(trimsecString);
-        } else {
-            binRegion(trimsec, xBin, yBin);
-            if (pixels) {
-                copyPixels(hdu->images->data[i], sourceImage, *trimsec, xFlip, yFlip);
-            }
-            targetReadout->image = psImageSubset(hdu->images->data[i], *trimsec);
-        }
-
-        psListIterator *biassecsIter = psListIteratorAlloc(biassecs, PS_LIST_HEAD, false); // Iterator
-        psRegion *biassec = NULL;       // Bias section from iteration
-        psListIterator *biasIter = psListIteratorAlloc(sourceReadout->bias, PS_LIST_HEAD, false); // Iterator
-        psImage *bias = NULL;           // Bias image from iteration
-        while ((biassec = psListGetAndIncrement(biassecsIter)) && (bias = psListGetAndIncrement(biasIter))) {
-            if (psRegionIsBad(*biassec)) {
-                psString biassecString = psRegionToString(*biassec); // String for bias section
-                psLogMsg(__func__, PS_LOG_WARN, "Bias section (%s) isn't set --- ignored.\n", biassecString);
-                psFree(biassecString);
-                continue;
-            }
-            if (bias->numCols != biassec->x1 - biassec->x0 ||
-                    bias->numRows != biassec->y1 - biassec->y0) {
-                psString biassecString = psRegionToString(*biassec); // String with the bias section
-                psLogMsg(__func__, PS_LOG_WARN, "Source image size (%dx%d) for readout %d doesn't match "
-                         "CELL.BIASSEC for target (%s) -- ignored.\n", bias->numCols, bias->numRows, i,
-                         biassecString);
-                psFree(biassecString);
-            } else {
-                binRegion(biassec, xBin, yBin);
-                if (pixels) {
-                    copyPixels(hdu->images->data[i], bias, *biassec, xFlip, yFlip);
-                }
-                psImage *newBias = psImageSubset(hdu->images->data[i], *biassec);
-                psListAdd(targetReadout->bias, PS_LIST_TAIL, newBias);
-                psFree(newBias);        // Drop reference
-            }
-        }
-        psFree(targetReadout);          // Drop reference
-        psFree(biassecsIter);
-        psFree(biasIter);
-    }
-
-    // Copy the remaining "concepts" over
-    psMetadataIterator *conceptsIter = psMetadataIteratorAlloc(source->concepts, PS_LIST_HEAD, NULL);
-    psMetadataItem *conceptItem = NULL; // Item from iteration
-    while ((conceptItem = psMetadataGetAndIncrement(conceptsIter))) {
-        psString name = conceptItem->name; // Name of concept
-        if (strcmp(name, "CELL.TRIMSEC") != 0 && strcmp(name, "CELL.BIASSEC") != 0 &&
-                strcmp(name, "CELL.XPARITY") != 0 && strcmp(name, "CELL.YPARITY") != 0 &&
-                strcmp(name, "CELL.X0") != 0 && strcmp(name, "CELL.Y0") != 0) {
-            psMetadataAddItem(target->concepts, conceptItem, PS_LIST_TAIL, PS_META_REPLACE);
-        }
-    }
-    psFree(conceptsIter);
-
-    // Need to update CELL.X0 and CELL.Y0 if we flipped
-    if (xFlip) {
-        int xZero = psMetadataLookupS32(NULL, source->concepts, "CELL.X0"); // CELL.X0 from source
-        int xParity = psMetadataLookupS32(NULL, target->concepts, "CELL.XPARITY"); // Parity in x
-        int xBin = psMetadataLookupS32(NULL, source->concepts, "CELL.XBIN"); // CELL.XBIN from source
-        pmReadout *readout = source->readouts->data[0]; // A representative readout
-        psTrace(__func__, 3, "CELL.X0: Before: %d After: %d\n", xZero,
-                xZero - (readout->image->numCols - 1) * xParity * xBin);
-        psTrace(__func__, 9, "(xParity: %d xBin: %d numCols: %d)\n", xParity, xBin, readout->image->numCols);
-        xZero -= (readout->image->numCols - 1) * xParity * xBin; // Change the parity on the X0 position
-        psMetadataItem *newItem = psMetadataLookup(target->concepts, "CELL.X0"); // CELL.X0 from target
-        newItem->data.S32 = xZero;
-    }
-    if (yFlip) {
-        int yZero = psMetadataLookupS32(NULL, source->concepts, "CELL.Y0"); // CELL.Y0 from source
-        int yParity = psMetadataLookupS32(NULL, target->concepts, "CELL.YPARITY"); // Parity in y
-        int yBin = psMetadataLookupS32(NULL, source->concepts, "CELL.YBIN"); // Parity in y
-        pmReadout *readout = source->readouts->data[0]; // A representative readout
-        psTrace(__func__, 3, "CELL.Y0: Before: %d After: %d\n", yZero,
-                yZero - (readout->image->numRows - 1) * yParity * yBin);
-        psTrace(__func__, 9, "(yParity: %d yBin: %d numRows: %d)\n", yParity, yBin, readout->image->numRows);
-        yZero -= (readout->image->numRows - 1) * yParity * yBin; // Change the parity on the Y0 position
-        psMetadataItem *newItem = psMetadataLookup(target->concepts, "CELL.Y0"); // CELL.Y0 from target
-        newItem->data.S32 = yZero;
-    }
-
-    // Update the binning concepts
-    psMetadataItem *binItem = psMetadataLookup(target->concepts, "CELL.XBIN");
-    binItem->data.S32 *= xBin;
-    binItem = psMetadataLookup(target->concepts, "CELL.YBIN");
-    binItem->data.S32 *= yBin;
-
-    return numReadouts;
-}
-
-static int chipCopy(pmChip *target,          // The target chip
-                    pmChip *source,          // The source chip, to be copied
-                    bool pixels,             // Copy the pixels?
-                    int xBin, int yBin       // (Relative) binning factors in x and y
-                   )
-{
-    assert(target);
-    assert(source);
-
-    psArray *targetCells = target->cells; // The target cells
-    psArray *sourceCells = source->cells; // The source cells
-    if (targetCells->n != sourceCells->n) {
-        psError(PS_ERR_IO, true, "Number of source cells (%d) differs from the number of target cells (%d)\n",
-                sourceCells->n, targetCells->n);
-        return false;
-    }
-
-    // Copy any headers
-    if (target->hdu && !target->hdu->phu) {
-        pmHDU *sourceHDU = pmHDUFromChip(source);
-        target->hdu->header = psMetadataCopy(target->hdu->header, sourceHDU->header);
-    }
-
-    int numCells = 0;                   // Number of cells copied
-    for (int i = 0; i < targetCells->n; i++) {
-        pmCell *targetCell = targetCells->data[i]; // The target cell
-        const char *cellName = psMetadataLookupStr(NULL, targetCell->concepts, "CELL.NAME"); // Name of cell
-        int cellNum = pmChipFindCell(source, cellName); // Number of cell with that name
-        if (cellNum >= 0) {
-            pmCell *sourceCell = sourceCells->data[cellNum]; // The source cell
-            int numReadouts = cellCopy(targetCell, sourceCell, pixels, xBin, yBin); // Number of readouts
-            // copied
-            psTrace(__func__, 5, "Copied %d readouts for cell %s\n", numReadouts, cellName);
-            numCells++;
-        }
-    }
-
-    // Update the concepts
-    psMetadataCopy(target->concepts, source->concepts);
-
-    return numCells;
-
-}
-
-static int fpaCopy(pmFPA *target,            // The target FPA
-                   pmFPA *source,            // The source FPA, to be copied
-                   bool pixels,              // Copy the pixels?
-                   int xBin, int yBin        // (Relative) binning factors in x and y
-                  )
-{
-    assert(target);
-    assert(source);
-
-    psArray *targetChips = target->chips; // The target chips
-    psArray *sourceChips = source->chips; // The source chips
-    if (targetChips->n != sourceChips->n) {
-        psError(PS_ERR_IO, true, "Number of source chips (%d) differs from the number of target chips (%d)\n",
-                sourceChips->n, targetChips->n);
-        return false;
-    }
-
-    // Copy any headers
-    if (target->hdu && !target->hdu->phu) {
-        pmHDU *sourceHDU = pmHDUFromFPA(source);
-        target->hdu->header = psMetadataCopy(target->hdu->header, sourceHDU->header);
-    }
-
-    int numChips = 0;                   // Number of chips copied
-    for (int i = 0; i < targetChips->n; i++) {
-        pmChip *targetChip = targetChips->data[i]; // The target chip
-        const char *chipName = psMetadataLookupStr(NULL, targetChip->concepts, "CHIP.NAME"); // Name of chip
-        int chipNum = pmFPAFindChip(source, chipName); // Number of chip with that name
-        if (chipNum >= 0) {
-            pmChip *sourceChip = sourceChips->data[chipNum]; // The source chip
-            int numCells = chipCopy(targetChip, sourceChip, pixels, xBin, yBin); // Number of cells copied
-            psTrace(__func__, 5, "Copied %d cells for chip %s\n", numCells, chipName);
-            numChips++;
-        }
-    }
-
-    // Update the concepts
-    psMetadataCopy(target->concepts, source->concepts);
-
-    return numChips;
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Public functions
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-
-int pmFPACopy(pmFPA *target,            // The target FPA
-              pmFPA *source             // The source FPA, to be copied
-             )
-{
-    return fpaCopy(target, source, true, 1, 1);
-}
-
-int pmChipCopy(pmChip *target,          // The target chip
-               pmChip *source           // The source chip, to be copied
-              )
-{
-    return chipCopy(target, source, true, 1, 1);
-}
-
-int pmCellCopy(pmCell *target,          // The target cell
-               pmCell *source           // The source cell, to be copied
-              )
-{
-    return cellCopy(target, source, true, 1, 1);
-}
-
-
-int pmFPACopyStructure(pmFPA *target,   // The target FPA
-                       pmFPA *source,   // The source FPA, to be copied
-                       int xBin, int yBin // Binning factors in x and y
-                      )
-{
-    return fpaCopy(target, source, false, xBin, yBin);
-}
-
-int pmChipCopyStructure(pmChip *target, // The target chip
-                        pmChip *source, // The source chip, to be copied
-                        int xBin, int yBin // Binning factors in x and y
-                       )
-{
-    return chipCopy(target, source, false, xBin, yBin);
-}
-
-int pmCellCopyStructure(pmCell *target, // The target cell
-                        pmCell *source, // The source cell, to be copied
-                        int xBin, int yBin // Binning factors in x and y
-                       )
-{
-    return cellCopy(target, source, false, xBin, yBin);
-}
Index: unk/psModules/src/astrom/pmFPACopy.h
===================================================================
--- /trunk/psModules/src/astrom/pmFPACopy.h	(revision 7018)
+++ 	(revision )
@@ -1,30 +1,0 @@
-#ifndef PM_FPA_COPY_H
-#define PM_FPA_COPY_H
-
-// Copy the FPA components, including the pixels
-int pmFPACopy(pmFPA *target,            // The target FPA
-              pmFPA *source             // The source FPA, to be copied
-             );
-int pmChipCopy(pmChip *target,          // The target chip
-               pmChip *source           // The source chip, to be copied
-              );
-int pmCellCopy(pmCell *target,          // The target cell
-               pmCell *source           // The source cell, to be copied
-              );
-
-// Versions that copy the structure and not the pixels; they also allow binning
-int pmFPACopyStructure(pmFPA *target,   // The target FPA
-                       pmFPA *source,   // The source FPA, to be copied
-                       int xBin, int yBin     // Binning factors in x and y
-                      );
-int pmChipCopyStructure(pmChip *target, // The target chip
-                        pmChip *source, // The source chip, to be copied
-                        int xBin, int yBin   // Binning factors in x and y
-                       );
-int pmCellCopyStructure(pmCell *target, // The target cell
-                        pmCell *source, // The source cell, to be copied
-                        int xBin, int yBin // Binning factors in x and y
-                       );
-
-
-#endif
Index: unk/psModules/src/astrom/pmFPAHeader.c
===================================================================
--- /trunk/psModules/src/astrom/pmFPAHeader.c	(revision 7018)
+++ 	(revision )
@@ -1,99 +1,0 @@
-#include <stdio.h>
-#include <assert.h>
-#include "pslib.h"
-#include "pmFPA.h"
-#include "pmHDU.h"
-#include "pmConcepts.h"
-#include "pmFPAHeader.h"
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// File-static (private) functions
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Read concepts for all cells in a chip
-static bool chipConcepts(pmChip *chip   // The chip for which to read the concepts
-                        )
-{
-    bool status = true;                 // Status of concept reading
-    status |= pmConceptsReadChip(chip, PM_CONCEPT_SOURCE_HEADER, false, NULL);
-    psArray *cells = chip->cells;       // The cells
-    for (int i = 0; i < cells->n; i++) {
-        pmCell *cell = cells->data[i];  // The cell of interest
-        if (!cell) {
-            continue;
-        }
-        status |= pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_HEADER, false, NULL);
-    }
-
-    return status;
-}
-
-// Read concepts for all chips in an FPA
-static bool fpaConcepts(pmFPA *fpa   // The FPA for which to read the concepts
-                       )
-{
-    bool status = true;                 // Status of concept reading
-    status |= pmConceptsReadFPA(fpa, PM_CONCEPT_SOURCE_HEADER, NULL);
-    psArray *chips = fpa->chips;        // The chips
-    for (int i = 0; i < chips->n; i++) {
-        pmChip *chip = chips->data[i];  // The chip of interest
-        if (!chip) {
-            continue;
-        }
-        status |= chipConcepts(chip);
-    }
-
-    return status;
-}
-
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Public functions
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-bool pmCellReadHeader(pmCell *cell,     // Cell for which to read header
-                      psFits *fits      // FITS file handle
-                     )
-{
-    if (!cell->hdu) {
-        return pmChipReadHeader(cell->parent, fits);
-    }
-    if (!pmHDUReadHeader(cell->hdu, fits)) {
-        psError(PS_ERR_IO, false, "Unable to read header for cell.\n");
-        return false;
-    }
-
-    return pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_HEADER, false, NULL);
-}
-
-
-bool pmChipReadHeader(pmChip *chip,     // Chip for which to read header
-                      psFits *fits      // FITS file handle
-                     )
-{
-    if (!chip->hdu) {
-        return pmFPAReadHeader(chip->parent, fits);
-    }
-    if (!pmHDUReadHeader(chip->hdu, fits)) {
-        psError(PS_ERR_IO, false, "Unable to read header for cell.\n");
-        return false;
-    }
-
-    return chipConcepts(chip);
-}
-
-
-bool pmFPAReadHeader(pmFPA *fpa,        // FPA for which to read header
-                     psFits *fits       // FITS file handle
-                    )
-{
-    if (!fpa->hdu) {
-        return false;
-    }
-    if (!pmHDUReadHeader(fpa->hdu, fits)) {
-        psError(PS_ERR_IO, false, "Unable to read header for cell.\n");
-        return false;
-    }
-
-    return fpaConcepts(fpa);
-}
Index: unk/psModules/src/astrom/pmFPAHeader.h
===================================================================
--- /trunk/psModules/src/astrom/pmFPAHeader.h	(revision 7018)
+++ 	(revision )
@@ -1,14 +1,0 @@
-#ifndef PM_FPA_HEADER_H
-#define PM_FPA_HEADER_H
-
-bool pmCellReadHeader(pmCell *cell,     // Cell for which to read header
-                      psFits *fits      // FITS file handle
-                     );
-bool pmChipReadHeader(pmChip *chip,     // Chip for which to read header
-                      psFits *fits      // FITS file handle
-                     );
-bool pmFPAReadHeader(pmFPA *fpa,        // FPA for which to read header
-                     psFits *fits       // FITS file handle
-                    );
-
-#endif
Index: unk/psModules/src/astrom/pmFPAMaskWeight.c
===================================================================
--- /trunk/psModules/src/astrom/pmFPAMaskWeight.c	(revision 7018)
+++ 	(revision )
@@ -1,101 +1,0 @@
-#include <stdio.h>
-#include "pslib.h"
-#include "pmFPA.h"
-#include "pmFPAMaskWeight.h"
-
-bool pmReadoutSetMask(pmReadout *readout // Readout for which to set mask
-                     )
-{
-    PS_ASSERT_PTR_NON_NULL(readout, false);
-
-    pmCell *cell = readout->parent;     // The parent cell
-    bool mdok = true;                   // Status of MD lookup
-
-    // Get the "concepts" of interest
-    float saturation = psMetadataLookupF32(&mdok, cell->concepts, "CELL.SATURATION"); // Saturation level
-    if (!mdok || isnan(saturation)) {
-        psError(PS_ERR_IO, true, "CELL.SATURATION is not set --- unable to set mask.\n");
-        return false;
-    }
-    float bad = psMetadataLookupF32(NULL, cell->concepts, "CELL.BAD"); // Bad level
-    if (!mdok || isnan(bad)) {
-        psError(PS_ERR_IO, true, "CELL.BAD is not set --- unable to set mask.\n");
-        return false;
-    }
-
-    psImage *image = readout->image;    // The image pixels
-    psImage *mask = readout->mask;      // The mask pixels
-    if (!mask) {
-        mask = psImageAlloc(image->numCols, image->numRows, PS_TYPE_U8);
-        readout->mask = mask;
-        psImageInit(mask, 0);
-    }
-
-    // Set up the mask
-    for (int i = 0; i < image->numRows; i++) {
-        for (int j = 0; j < image->numCols; j++) {
-            if (image->data.F32[i][j] > saturation) {
-                mask->data.U8[i][j] |= PM_MASK_SAT;
-            }
-            if (image->data.F32[i][j] < bad) {
-                mask->data.U8[i][j] |= PM_MASK_BAD;
-            }
-        }
-    }
-
-    return true;
-}
-
-bool pmReadoutSetWeight(pmReadout *readout // Readout for which to set weight
-                       )
-{
-    PS_ASSERT_PTR_NON_NULL(readout, false);
-
-    pmCell *cell = readout->parent;     // The parent cell
-    bool mdok = true;                   // Status of MD lookup
-
-    // Get the "concepts" of interest
-    float gain = psMetadataLookupF32(&mdok, cell->concepts, "CELL.GAIN"); // Cell gain
-    if (!mdok || isnan(gain)) {
-        psError(PS_ERR_IO, true, "CELL.GAIN is not set --- unable to set weight.\n");
-        return false;
-    }
-    float readnoise = psMetadataLookupF32(&mdok, cell->concepts, "CELL.READNOISE"); // Cell read noise
-    if (!mdok || isnan(readnoise)) {
-        psError(PS_ERR_IO, true, "CELL.READNOISE is not set --- unable to set weight.\n");
-        return false;
-    }
-
-    psImage *image = readout->image;    // The image pixels
-    psImage *weight = readout->weight;  // The weight pixels
-    if (!weight) {
-        weight = psImageAlloc(image->numCols, image->numRows, PS_TYPE_F32);
-        readout->weight = weight;
-        psImageInit(weight, 0.0);
-    }
-
-    // Set weight image to the variance in ADU = f/g + rn^2
-    psBinaryOp(readout->weight, image, "/", psScalarAlloc(gain, PS_TYPE_F32));
-    psBinaryOp(readout->weight, readout->weight, "+",
-               psScalarAlloc(readnoise*readnoise/gain/gain, PS_TYPE_F32));
-
-    return true;
-}
-
-
-
-bool pmCellSetMaskWeight(pmCell *cell // Cell for which to set weights
-                        )
-{
-    PS_ASSERT_PTR_NON_NULL(cell, false);
-
-    bool success = true;                // Was everything successful?
-    psArray *readouts = cell->readouts; // Array of readouts
-    for (int i = 0; i < readouts->n; i++) {
-        pmReadout *readout = readouts->data[i]; // The readout
-        success |= pmReadoutSetMask(readout);
-        success |= pmReadoutSetWeight(readout);
-    }
-
-    return success;
-}
Index: unk/psModules/src/astrom/pmFPAMaskWeight.h
===================================================================
--- /trunk/psModules/src/astrom/pmFPAMaskWeight.h	(revision 7018)
+++ 	(revision )
@@ -1,23 +1,0 @@
-#ifndef PM_READOUT_MASK_WEIGHT_H
-#define PM_READOUT_MASK_WEIGHT_H
-
-#include "pmFPA.h"
-
-/** Mask values */
-typedef enum {
-    PM_MASK_TRAP    = 0x0001,   ///< The pixel is a charge trap.
-    PM_MASK_BADCOL  = 0x0002,   ///< The pixel is a bad column.
-    PM_MASK_SAT     = 0x0004,   ///< The pixel is saturated.
-    PM_MASK_BAD     = 0x0008,   ///< The pixel is low
-    PM_MASK_FLAT    = 0x0010    ///< The pixel is non-positive in the flat-field.
-} pmMaskValue;
-
-bool pmReadoutSetMask(pmReadout *readout // Readout for which to set mask
-                     );
-bool pmReadoutSetWeight(pmReadout *readout // Readout for which to set weight
-                       );
-bool pmCellSetMaskWeight(pmCell *cell // Cell for which to set weights
-                        );
-
-
-#endif
Index: unk/psModules/src/astrom/pmFPARead.c
===================================================================
--- /trunk/psModules/src/astrom/pmFPARead.c	(revision 7018)
+++ 	(revision )
@@ -1,295 +1,0 @@
-#include <stdio.h>
-#include <strings.h>
-#include <assert.h>
-#include "pslib.h"
-
-#include "pmFPA.h"
-#include "pmFPARead.h"
-#include "pmHDU.h"
-#include "pmHDUUtils.h"
-#include "pmConcepts.h"
-#include "psRegionIsBad.h"
-#include "pmFPAHeader.h"
-
-#define MAX(x,y) ((x) > (y) ? (x) : (y))
-#define MIN(x,y) ((x) < (y) ? (x) : (y))
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// File-static functions
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Carve a readout from the image pixels
-static bool readoutCarve(pmReadout *readout, // Readout to be carved up
-                         psImage *image, // Image that will be carved
-                         const psRegion *trimsec, // Trim section
-                         const psList *biassecs // Bias sections
-                        )
-{
-    // The image corresponding to the trim region
-    if (psRegionIsBad(*trimsec)) {
-        psString regionString = psRegionToString(*trimsec);
-        psError(PS_ERR_IO, true, "Invalid trim section: %s\n", regionString);
-        psFree(regionString);
-        psFree(readout);
-        return NULL;
-    }
-    psRegion region = psRegionSet(MAX(trimsec->x0 - readout->col0, 0), // x0
-                                  MIN(trimsec->x1 - readout->col0, image->numCols), // x1
-                                  MAX(trimsec->y0 - readout->row0, 0), // y0
-                                  MIN(trimsec->y1 - readout->row0, image->numRows) // y1
-                                 );
-    if (readout->image) {
-        psFree(readout->image);         // Make way!
-    }
-    readout->image = psMemIncrRefCounter(psImageSubset(image, region));
-
-    // Get the list of overscans
-    if (readout->bias->n != 0) {
-        // Make way!
-        psFree(readout->bias);
-        readout->bias = psListAlloc(NULL);
-    }
-    psListIterator *iter = psListIteratorAlloc((psList*)biassecs, PS_LIST_HEAD, false); // Iterator
-    psRegion *biassec = NULL;       // A BIASSEC region from the list
-    while ((biassec = psListGetAndIncrement(iter))) {
-        if (psRegionIsBad(*biassec)) {
-            psString regionString = psRegionToString(*biassec);
-            psError(PS_ERR_IO, true, "Invalid bias section: %s\n", regionString);
-            psFree(regionString);
-            psFree(readout);
-            return NULL;
-        }
-        psRegion region = psRegionSet(MAX(biassec->x0 - readout->col0, 0), // x0
-                                      MIN(biassec->x1 - readout->col0, image->numCols), // x1
-                                      MAX(biassec->y0 - readout->row0, 0), // y0
-                                      MIN(biassec->y1 - readout->row0, image->numRows) // y1
-                                     );
-        psImage *overscan = psMemIncrRefCounter(psImageSubset(image, region));
-        psListAdd(readout->bias, PS_LIST_TAIL, overscan);
-        psFree(overscan);
-    }
-    psFree(iter);
-
-    return readout;
-}
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Public functions
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Read the next readout; return true if we read pixels in.
-bool pmReadoutReadNext(pmReadout *readout, // Readout into which to read
-                       psFits *fits,    // FITS file from which to read
-                       int z,           // Readout number/plane; zero-offset indexing
-                       int numScans     // The number of scans to read
-                      )
-{
-    // Get the HDU and read the header
-    pmCell *cell = readout->parent;     // The parent cell
-    if (!pmCellReadHeader(cell, fits)) {
-        psError(PS_ERR_IO, false, "Unable to read header for cell!\n");
-        return false;
-    }
-
-    pmHDU *hdu = pmHDUFromCell(cell);   // The HDU
-    if (!hdu) {
-        return false;
-    }
-
-    // Make sure we have the information we need
-    pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_HEADER, false, NULL);
-
-    // Get the trim and bias sections
-    bool mdok = true;                   // Status of MD lookup
-    psRegion *trimsec = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.TRIMSEC"); // Trim sections
-    if (!mdok || !trimsec || psRegionIsBad(*trimsec)) {
-        psError(PS_ERR_IO, true, "CELL.TRIMSEC is not set.\n");
-        return false;
-    }
-    psList *biassecs = psMetadataLookupPtr(&mdok, cell->concepts, "CELL.BIASSEC"); // Bias sections
-    if (!mdok || !biassecs) {
-        psError(PS_ERR_IO, true, "CELL.BIASSEC is not set.\n");
-        return false;
-    }
-    int readdir = psMetadataLookupS32(&mdok, cell->concepts, "CELL.READDIR"); // Read direction
-    if (!mdok || readdir == 0 || (readdir != -1 && readdir != 1)) {
-        psError(PS_ERR_IO, true, "CELL.READDIR is not set to -1 or +1.\n");
-        return false;
-    }
-
-    // Check the third dimension
-    int naxis = psMetadataLookupS32(&mdok, hdu->header, "NAXIS"); // The number of axes
-    if (!mdok) {
-        psError(PS_ERR_IO, true, "Unable to find NAXIS in header for extension %s\n", hdu->extname);
-        return false;
-    }
-    if (naxis == 0) {
-        // No pixels to read, as for a PHU.
-        return false;
-    }
-    if (naxis < 2 || naxis > 3) {
-        psError(PS_ERR_IO, true, "NAXIS in header of extension %s (= %d) is not valid.\n",
-                hdu->extname, naxis);
-        return false;
-    }
-    int naxis3 = 1;                     // The number of image planes
-    if (naxis == 3) {
-        naxis3 = psMetadataLookupS32(&mdok, hdu->header, "NAXIS3");
-        if (!mdok) {
-            psError(PS_ERR_IO, true, "Unable to find NAXIS3 in header for extension %s\n", hdu->extname);
-            return false;
-        }
-    }
-    if (z >= naxis3) {
-        // Nothing to see here.  Move along.
-        return false;
-    }
-
-    // Get the size of the image plane
-    int naxis1 = psMetadataLookupS32(&mdok, hdu->header, "NAXIS1"); // The number of columns
-    if (!mdok) {
-        psError(PS_ERR_IO, true, "Unable to find NAXIS1 in header for extension %s\n", hdu->extname);
-        return false;
-    }
-    int naxis2 = psMetadataLookupS32(&mdok, hdu->header, "NAXIS2"); // The number of columns
-    if (!mdok) {
-        psError(PS_ERR_IO, true, "Unable to find NAXIS2 in header for extension %s\n", hdu->extname);
-        return false;
-    }
-
-    // Read the FITS image
-    int offset = readout->row0;         // The row number to read
-    if (readout->image) {
-        if (readdir > 0) {
-            // Reading rows
-            offset += readout->image->numRows;
-        } else {
-            // Reading columns
-            offset += readout->image->numCols;
-        }
-    }
-    if ((readdir > 0 && offset >= naxis2) || (readdir < 0 && offset > naxis1)) {
-        // We've read everything there is
-        return false;
-    }
-    int upper = offset + numScans;      // The upper limit for the pixel read
-    if (readdir > 0 && upper > naxis2) {
-        upper = naxis2;
-    } else if (readdir < 0 && upper > naxis1) {
-        upper = naxis1;
-    }
-    psRegion region = {0, 0, 0, 0};     // Region to be read
-    if (readdir > 0) {
-        region = psRegionSet(0, naxis1, offset, upper); // Read rows
-    } else {
-        region = psRegionSet(offset, upper, 0, naxis2); // Read columns
-    }
-    psImage *image = psFitsReadImage(fits, region, z); // The image
-
-    // Stick the image into the HDU and the readout
-    if (!hdu->images) {
-        hdu->images = psArrayAlloc(naxis3);
-        hdu->images->n = naxis3;
-    }
-    if (hdu->images->nalloc != naxis3) {
-        hdu->images = psArrayRealloc(hdu->images, naxis3);
-        hdu->images->n = naxis3;
-    }
-    if (hdu->images->data[z]) {
-        psFree(hdu->images->data[z]);
-    }
-    hdu->images->data[z] = image;
-    readout->row0 = region.y0;
-    readout->col0 = region.x0;
-    readoutCarve(readout, image, trimsec, biassecs);
-
-    return true;
-}
-
-
-// Read in the cell, and allocate the readouts
-bool pmCellRead(pmCell *cell,           // Cell to read into
-                psFits *fits,           // FITS file from which to read
-                psDB *db                // Database handle, for "concepts" ingest
-               )
-{
-    pmHDU *hdu = pmHDUFromCell(cell);   // The HDU
-    if (!hdu) {
-        return false;                    // Nothing to see here; move along
-    }
-    if (!hdu->images && !pmHDURead(hdu, fits)) {
-        psError(PS_ERR_IO, false, "Unable to read HDU for cell.\n");
-        return false;
-    }
-
-    pmConceptsReadCell(cell, PM_CONCEPT_SOURCE_HEADER, false, NULL);
-
-    // Having read the cell, we now have to cut it up
-    psRegion *trimsec = psMetadataLookupPtr(NULL, cell->concepts, "CELL.TRIMSEC");
-    psList *biassecs = psMetadataLookupPtr(NULL, cell->concepts, "CELL.BIASSEC");
-
-    // Iterate over each of the image planes
-    for (int i = 0; i < hdu->images->n; i++) {
-        psImage *image = hdu->images->data[i]; // The i-th plane
-
-        // XXX: Type conversion here to support the modules, which don't have multiple type support yet
-        if (image->type.type != PS_TYPE_F32) {
-            psImage *temp = psImageCopy(NULL, image, PS_TYPE_F32); // Temporary image
-            psFree(hdu->images->data[i]);
-            hdu->images->data[i] = temp;
-            image = temp;
-        }
-
-        pmReadout *readout = pmReadoutAlloc(cell);
-        readoutCarve(readout, image, trimsec, biassecs);
-        readout->mask = NULL;
-        readout->weight = NULL;
-        psFree(readout);                // Drop reference
-    }
-
-    pmCellSetDataStatus (cell, true);
-    return true;
-}
-
-// Read in the component cells
-bool pmChipRead(pmChip *chip,           // Chip to read into
-                psFits *fits,           // FITS file from which to read
-                psDB *db                // Database handle, for "concepts" ingest
-               )
-{
-    bool success = false;               // Were we able to read at least one HDU?
-    psArray *cells = chip->cells;       // Array of cells
-    for (int i = 0; i < cells->n; i++) {
-        pmCell *cell = cells->data[i];  // The cell of interest
-        success |= pmCellRead(cell, fits, db);
-    }
-    if (success) {
-        pmConceptsReadChip(chip, PM_CONCEPT_SOURCE_HEADER, false, NULL);
-        // XXX probably could just use chip->data_exists
-        pmChipSetDataStatus (chip, true);
-    }
-
-    return success;
-}
-
-// Read in the component chips
-bool pmFPARead(pmFPA *fpa,              // FPA to read into
-               psFits *fits,            // FITS file from which to read
-               psDB *db                 // Database handle, for "concepts" ingest
-              )
-{
-    bool success = false;               // Were we able to read at least one HDU?
-    psArray *chips = fpa->chips;        // Array of chips
-    for (int i = 0; i < chips->n; i++) {
-        pmChip *chip = chips->data[i];  // The cell of interest
-        success |= pmChipRead(chip, fits, db);
-    }
-    if (success) {
-        pmConceptsReadFPA(fpa, PM_CONCEPT_SOURCE_HEADER, NULL);
-    } else {
-        psLogMsg(__func__, PS_LOG_WARN, "Unable to read any chips in FPA.\n");
-    }
-
-    return success;
-}
-
Index: unk/psModules/src/astrom/pmFPARead.h
===================================================================
--- /trunk/psModules/src/astrom/pmFPARead.h	(revision 7018)
+++ 	(revision )
@@ -1,66 +1,0 @@
-#ifndef PM_FPA_READ_H
-#define PM_FPA_READ_H
-
-#include "pslib.h"
-#include "pmFPA.h"
-
-bool pmReadoutReadNext(pmReadout *readout, // Readout into which to read
-                       psFits *fits,    // FITS file from which to read
-                       int z,           // Readout number/plane; zero-offset indexing
-                       int numRows      // The number of rows to read
-                      );
-
-bool pmCellRead(pmCell *cell,           // Cell to read into
-                psFits *fits,           // FITS file from which to read
-                psDB *db                // Database handle, for "concepts" ingest
-               );
-
-bool pmChipRead(pmChip *chip,           // Chip to read into
-                psFits *fits,           // FITS file from which to read
-                psDB *db                // Database handle, for "concepts" ingest
-               );
-
-bool pmFPARead(pmFPA *fpa,              // FPA to read into
-               psFits *fits,            // FITS file from which to read
-               psDB *db                 // Database handle, for "concepts" ingest
-              );
-
-bool pmCellReadPHU(pmCell *cell,        // Cell to read into
-                   psFits *fits         // FITS file from which to read
-                  );
-
-bool pmChipReadPHU(pmChip *chip,        // Chip to read into
-                   psFits *fits         // FITS file from which to read
-                  );
-
-bool pmFPAReadPHU(pmFPA *fpa,           // FPA to read into
-                  psFits *fits          // FITS file from which to read
-                 );
-
-
-#if 0
-bool pmFPARead(pmFPA *fpa,              // FPA to read into
-               psFits *fits,            // FITS file from which to read
-               psMetadata *phu,         // Primary header
-               psDB *db                 // Database handle, for concept ingest
-              );
-
-psString p_pmFPATranslateName(const psString name, // The name to translate
-                              const pmCell *cell // The cell for which to translate
-                             );
-
-psString p_pmFPATranslateFileExt(psString *extName, // Extension name, to be returned
-                                 const psString filenameExt, // The string to parse into filename and ext
-                                 const pmCell *cell // The cell
-                                );
-
-bool pmFPAReadMask(pmFPA *fpa,          // FPA to read into
-                   psFits *source       // Source FITS file (for the original data)
-                  );
-
-bool pmFPAReadWeight(pmFPA *fpa,        // FPA to read into
-                     psFits *source     // Source FITS file (for the original data)
-                    );
-#endif
-
-#endif
Index: unk/psModules/src/astrom/pmFPAUtils.c
===================================================================
--- /trunk/psModules/src/astrom/pmFPAUtils.c	(revision 7018)
+++ 	(revision )
@@ -1,41 +1,0 @@
-#include <stdio.h>
-#include "pslib.h"
-#include "pmFPA.h"
-#include "pmFPAUtils.h"
-
-// Find a chip by name; return the index
-int pmFPAFindChip(const pmFPA *fpa, // FPA in which to find the chip
-                  const char *name // Name of the chip
-                 )
-{
-    psArray *chips = fpa->chips;    // Array of chips
-    for (int i = 0; i < chips->n; i++) {
-        pmChip *chip = chips->data[i]; // The chip of interest
-        psString testName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME"); // Name of this chip
-        if (strcmp(name, testName) == 0) {
-            return i;
-        }
-    }
-
-    psError(PS_ERR_IO, true, "Unable to find chip %s\n", name);
-    return -1;
-}
-
-// Find a cell by name; return the index
-int pmChipFindCell(const pmChip *chip, // Chip in which to find the cell
-                   const char *name // Name of the cell
-                  )
-{
-    psArray *cells = chip->cells;    // Array of cells
-    for (int i = 0; i < cells->n; i++) {
-        pmCell *cell = cells->data[i]; // The cell of interest
-        psString testName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME"); // Name of this cell
-        if (strcmp(name, testName) == 0) {
-            return i;
-        }
-    }
-
-    psError(PS_ERR_IO, true, "Unable to find cell %s\n", name);
-    return -1;
-}
-
Index: unk/psModules/src/astrom/pmFPAUtils.h
===================================================================
--- /trunk/psModules/src/astrom/pmFPAUtils.h	(revision 7018)
+++ 	(revision )
@@ -1,17 +1,0 @@
-#ifndef PM_FPA_UTILS_H
-#define PM_FPA_UTILS_H
-
-#include "pmFPA.h"
-
-// Find a chip by name; return the index
-int pmFPAFindChip(const pmFPA *fpa, // FPA in which to find the chip
-                  const char *name // Name of the chip
-                 );
-
-// Find a cell by name; return the index
-int pmChipFindCell(const pmChip *chip, // Chip in which to find the cell
-                   const char *name // Name of the cell
-                  );
-
-
-#endif
Index: unk/psModules/src/astrom/pmFPAWrite.c
===================================================================
--- /trunk/psModules/src/astrom/pmFPAWrite.c	(revision 7018)
+++ 	(revision )
@@ -1,153 +1,0 @@
-#include <stdio.h>
-#include <strings.h>
-#include "pslib.h"
-
-#include "pmFPA.h"
-#include "pmHDU.h"
-#include "pmHDUUtils.h"
-#include "pmConcepts.h"
-
-
-bool pmReadoutWriteNext(pmReadout *readout, // Readout to write
-                        psFits *fits,   // FITS file to which to write
-                        int z           // Image plane to write
-                       )
-{
-    pmHDU *hdu = pmHDUFromReadout(readout); // The HDU to which to write
-    psMetadata *header = hdu->header;   // The FITS header
-    if (!header) {
-        psError(PS_ERR_IO, true, "No FITS header available in the HDU.\n");
-        return false;
-    }
-
-    // We have to rely to a great extent on the FITS header, because otherwise we simply don't know how many
-    // image planes there are (NAXIS3) or the size of the original image (NAXIS1, NAXIS2).
-    bool mdok = true;                   // Status of MD lookup
-    int naxis1 = psMetadataLookupS32(&mdok, header, "NAXIS1"); // Number of columns
-    if (!mdok || naxis1 <= 0) {
-        psError(PS_ERR_IO, true, "Can't find NAXIS1 in header.\n");
-        return false;
-    }
-    int naxis2 = psMetadataLookupS32(&mdok, header, "NAXIS2"); // Number of rows
-    if (!mdok || naxis2 <= 0) {
-        psError(PS_ERR_IO, true, "Can't find NAXIS2 in header.\n");
-        return false;
-    }
-    int naxis3 = psMetadataLookupS32(&mdok, header, "NAXIS3"); // Number of image planes
-    if (!mdok || naxis3 <= 0) {
-        naxis3 = 1;
-    }
-    if (z >= naxis3) {
-        psError(PS_ERR_IO, true, "Specified a plane number (%d) greater than NAXIS3 allows.\n", z);
-        return false;
-    }
-
-    psImage *image = hdu->images->data[z]; // The image from the HDU to write
-    if (readout->row0 == 0 && readout->col0 == 0 && z == 0) {
-        // Then we can assume that nothing has been written to the FITS file for now
-        if (naxis1 == image->numCols && naxis2 == image->numRows) {
-            // We can write the whole lot at once
-            return psFitsWriteImage(fits, header, image, z, hdu->extname);
-        }
-        // Create a dummy image so we can write something larger than we actually have
-        psImage *dummy = psImageAlloc(naxis1, naxis2, image->type.type); // Dummy image
-        psImageInit(dummy, 0);
-        psImageOverlaySection(dummy, image, 0, 0, "=");
-        bool result = psFitsWriteImage(fits, header, dummy, z, hdu->extname);
-        psFree(dummy);
-        return result;
-    }
-
-    // We can simply update an existing HDU
-    if (!psFitsMoveExtName(fits, hdu->extname)) {
-        psError(PS_ERR_IO, false, "Unable to move to extension %s\n", hdu->extname);
-        return false;
-    }
-    return psFitsUpdateImage(fits, image, readout->col0, readout->row0, z);
-}
-
-
-
-
-bool pmCellWrite(pmCell *cell,          // Cell to write
-                 psFits *fits,          // FITS file to which to write
-                 psDB *db,              // Database handle for "concepts" update
-                 bool pixels            // Write the pixels?
-                )
-{
-    pmHDU *hdu = cell->hdu;             // The HDU
-    if (hdu && ((!pixels && hdu->phu) || pixels)) {
-        bool status = pmConceptsWriteCell(cell, PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CAMERA |
-                                          PM_CONCEPT_SOURCE_DEFAULTS, false, NULL);
-        status &= pmHDUWrite(hdu, fits);
-        if (!status) {
-            psError(PS_ERR_IO, false, "Unable to write HDU for Chip.\n");
-            return false;
-        }
-    }
-
-    return true;
-}
-
-
-bool pmChipWrite(pmChip *chip,          // Chip to write
-                 psFits *fits,          // FITS file to which to write
-                 psDB *db,              // Database handle for "concepts" update
-                 bool pixels            // Write the pixels?
-                )
-{
-    pmHDU *hdu = chip->hdu;             // The HDU
-    if (hdu && ((!pixels && hdu->phu) || pixels)) {
-        bool status = pmConceptsWriteChip(chip, PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CAMERA |
-                                          PM_CONCEPT_SOURCE_DEFAULTS, false, NULL);
-        status &= pmHDUWrite(hdu, fits);
-        if (!status) {
-            psError(PS_ERR_IO, false, "Unable to write HDU for Chip.\n");
-            return false;
-        }
-    }
-
-    bool success = true;                // Success of writing
-    if (pixels) {
-        psArray *cells = chip->cells;       // Array of cells
-        for (int i = 0; i < cells->n; i++) {
-            pmCell *cell = cells->data[i];  // The cell of interest
-            success |= pmCellWrite(cell, fits, db, true);
-        }
-    }
-
-    return success;
-}
-
-
-
-
-bool pmFPAWrite(pmFPA *fpa,             // FPA to write
-                psFits *fits,           // FITS file to which to write
-                psDB *db,               // Database handle for "concepts" update
-                bool pixels             // Write the pixels?
-               )
-{
-    pmHDU *hdu = fpa->hdu;              // The HDU
-    if (hdu && ((!pixels && hdu->phu) || pixels)) {
-        bool status = pmConceptsWriteFPA(fpa, PM_CONCEPT_SOURCE_HEADER | PM_CONCEPT_SOURCE_CAMERA |
-                                         PM_CONCEPT_SOURCE_DEFAULTS, NULL);
-        status &= pmHDUWrite(hdu, fits);
-        if (!status) {
-            psError(PS_ERR_IO, false, "Unable to write HDU for FPA.\n");
-            return false;
-        }
-    }
-
-    bool success = true;                // Success of writing
-    if (pixels) {
-        psArray *chips = fpa->chips;        // Array of chips
-        for (int i = 0; i < chips->n; i++) {
-            pmChip *chip = chips->data[i];  // The chip of interest
-            success |= pmChipWrite(chip, fits, db, true);
-        }
-    }
-
-    return success;
-}
-
Index: unk/psModules/src/astrom/pmFPAWrite.h
===================================================================
--- /trunk/psModules/src/astrom/pmFPAWrite.h	(revision 7018)
+++ 	(revision )
@@ -1,28 +1,0 @@
-#ifndef PM_FPA_WRITE_H
-#define PM_FPA_WRITE_H
-
-#include "pslib.h"
-#include "pmFPA.h"
-
-bool pmReadoutWriteNext(pmReadout *readout, // Readout to write
-                        psFits *fits,   // FITS file to which to write
-                        int z           // Image plane to write
-                       );
-
-bool pmCellWrite(pmCell *cell,          // Cell to write
-                 psFits *fits,          // FITS file to which to write
-                 psDB *db,              // Database handle for "concepts" update
-                 bool pixels            // Write the pixels?
-                );
-bool pmChipWrite(pmChip *chip,          // Chip to write
-                 psFits *fits,          // FITS file to which to write
-                 psDB *db,              // Database handle for "concepts" update
-                 bool pixels            // Write the pixels?
-                );
-bool pmFPAWrite(pmFPA *fpa,             // FPA to write
-                psFits *fits,           // FITS file to which to write
-                psDB *db,               // Database handle for "concepts" update
-                bool pixels             // Write the pixels?
-               );
-
-#endif
Index: unk/psModules/src/astrom/pmFPA_JPEG.c
===================================================================
--- /trunk/psModules/src/astrom/pmFPA_JPEG.c	(revision 7018)
+++ 	(revision )
@@ -1,163 +1,0 @@
-/** @file  pmFPA_JPEG.c
- *
- * This file contains functions to write JPEG images.
- *
- *  @author EAM, IfA
- *
- *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2006-05-01 01:08:45 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- */
-
-/*****************************************************************************/
-/* INCLUDE FILES                                                             */
-/*****************************************************************************/
-
-#include <pslib.h>
-# include "psImageJpeg.h"
-# include "psImageUnbin.h"
-# include "psAdditionals.h"
-
-#include "pmHDU.h"
-#include "pmFPA.h"
-#include "pmFPAfile.h"
-#include "pmFPAview.h"
-#include "pmFPA_JPEG.h"
-
-
-bool pmFPAviewWriteJPEG (const pmFPAview *view, pmFPAfile *file)
-{
-
-    pmFPA *fpa = file->fpa;
-
-    if (view->chip == -1) {
-        pmFPAWriteJPEG (fpa, view, file);
-        return true;
-    }
-
-    if (view->chip >= fpa->chips->n) {
-        return false;
-    }
-    pmChip *chip = fpa->chips->data[view->chip];
-
-    if (view->cell == -1) {
-        pmChipWriteJPEG (chip, view, file);
-        return true;
-    }
-
-    if (view->cell >= chip->cells->n) {
-        return false;
-    }
-    pmCell *cell = chip->cells->data[view->cell];
-
-    if (view->readout == -1) {
-        pmCellWriteJPEG (cell, view, file);
-        return true;
-    }
-
-    if (view->readout >= cell->readouts->n) {
-        return false;
-    }
-    pmReadout *readout = cell->readouts->data[view->readout];
-
-    pmReadoutWriteJPEG (readout, view, file);
-    return true;
-}
-
-// read in all chip-level JPEG files for this FPA
-bool pmFPAWriteJPEG (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file)
-{
-
-    for (int i = 0; i < fpa->chips->n; i++) {
-
-        pmChip *chip = fpa->chips->data[i];
-        pmChipWriteJPEG (chip, view, file);
-    }
-    return true;
-}
-
-// read in all cell-level JPEG files for this chip
-bool pmChipWriteJPEG (pmChip *chip, const pmFPAview *view, pmFPAfile *file)
-{
-
-    for (int i = 0; i < chip->cells->n; i++) {
-
-        pmCell *cell = chip->cells->data[i];
-        pmCellWriteJPEG (cell, view, file);
-    }
-    return true;
-}
-
-// read in all readout-level JPEG files for this cell
-bool pmCellWriteJPEG (pmCell *cell, const pmFPAview *view, pmFPAfile *file)
-{
-
-    for (int i = 0; i < cell->readouts->n; i++) {
-
-        pmReadout *readout = cell->readouts->data[i];
-        pmReadoutWriteJPEG (readout, view, file);
-    }
-    return true;
-}
-
-// read in all readout-level Objects files for this cell
-bool pmReadoutWriteJPEG (pmReadout *readout, const pmFPAview *view, pmFPAfile *file)
-{
-    char *name, *mode, *word, *mapname;
-    psArray *range;
-    psImageJpegColormap *map;
-
-    switch (file->type) {
-    case PM_FPA_FILE_JPEG:
-
-        name = pmFPAfileNameFromRule (file->filerule, file, view);
-
-        mapname = pmFPAfileNameFromRule (file->filextra, file, view);
-        map = psImageJpegColormapSet (NULL, mapname);
-        if (!map) {
-            map = psImageJpegColormapSet (NULL, "-greyscale");
-        }
-
-        mode = pmFPAfileNameFromRule (file->extrule, file, view);
-        word = pmFPAfileNameFromRule (file->extxtra, file, view);
-        range = psStringSplitArray (word, ":", false);
-
-        // XXX we need to decide where the scale will come from in the long term
-        psImageClippedStatsInit (10000);
-
-        psStats *stats = psImageClippedStats (readout->image, NULL, 0, 0.25, 0.75);
-        float delta = stats->robustUQ - stats->robustLQ;
-        float min = stats->robustMedian - 3*delta;
-        float max = stats->robustMedian - 6*delta;
-        if (!strcasecmp (mode, "RANGE") && range) {
-            float fmin = atof(range->data[0]);
-            float fmax = atof(range->data[1]);
-            min = stats->robustMedian + fmin*delta;
-            max = stats->robustMedian + fmax*delta;
-        }
-        if (!strcasecmp (mode, "FRACTION")) {
-            float fmin = atof(range->data[0]);
-            float fmax = atof(range->data[1]);
-            min = fmin*stats->robustMedian;
-            max = fmax*stats->robustMedian;
-        }
-        psImageJpeg (map, readout->image, name, min, max);
-
-        psFree (name);
-        psFree (mode);
-        psFree (word);
-        psFree (mapname);
-        psFree (map);
-        psFree (stats);
-        psImageClippedStatsCleanup ();
-
-        return true;
-
-    default:
-        fprintf (stderr, "warning: type mismatch\n");
-        break;
-    }
-    return false;
-}
Index: unk/psModules/src/astrom/pmFPA_JPEG.h
===================================================================
--- /trunk/psModules/src/astrom/pmFPA_JPEG.h	(revision 7018)
+++ 	(revision )
@@ -1,27 +1,0 @@
-/** @file  pmFPAview.h
-*
-*  @brief Tools to manipulate the FPA structure elements.
-*
-*  @ingroup AstroImage
-*
-*  @author EAM, IfA
-*
-*  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2006-04-17 18:01:04 $
-*
-*  Copyright 2004-2005 Institute for Astronomy, University of Hawaii
-*/
-
-#ifndef PM_FPA_JPEG_H
-#define PM_FPA_JPEG_H
-
-/// @addtogroup AstroImage
-/// @{
-
-bool pmFPAviewWriteJPEG (const pmFPAview *view, pmFPAfile *file);
-bool pmFPAWriteJPEG (pmFPA *fpa, const pmFPAview *view, pmFPAfile *file);
-bool pmChipWriteJPEG (pmChip *chip, const pmFPAview *view, pmFPAfile *file);
-bool pmCellWriteJPEG (pmCell *cell, const pmFPAview *view, pmFPAfile *file);
-bool pmReadoutWriteJPEG (pmReadout *readout, const pmFPAview *view, pmFPAfile *file);
-
-# endif
Index: unk/psModules/src/astrom/pmFPAfile.c
===================================================================
--- /trunk/psModules/src/astrom/pmFPAfile.c	(revision 7018)
+++ 	(revision )
@@ -1,1144 +1,0 @@
-#include <stdio.h>
-#include "pslib.h"
-#include "psAdditionals.h"
-#include "pmConfig.h"
-#include "pmHDU.h"
-#include "pmFPA.h"
-#include "pmMaskBadPixels.h"
-#include "pmFPAConstruct.h"
-#include "pmFPAview.h"
-#include "pmFPAfile.h"
-#include "pmFPACopy.h"
-#include "pmFPARead.h"
-#include "pmFPAWrite.h"
-#include "pmPeaks.h"
-#include "pmMoments.h"
-#include "pmModel.h"
-#include "pmSource.h"
-#include "pmSourceIO.h"
-#include "pmGrowthCurve.h"
-#include "pmPSF.h"
-#include "pmPSF_IO.h"
-#include "pmFPA_JPEG.h"
-
-static void pmFPAfileFree (pmFPAfile *file)
-{
-
-    if (file == NULL)
-        return;
-
-    psFree (file->fpa);
-    psFree (file->readout);
-    psFree (file->names);
-
-    psFree (file->format);
-    psFree (file->name);
-
-    if (file->fits != NULL) {
-        psFitsClose (file->fits);
-    }
-
-    psFree (file->filerule);
-    psFree (file->filextra);
-    psFree (file->extrule);
-    psFree (file->extxtra);
-
-    psFree (file->filename);
-    psFree (file->extname);
-
-    // these are just views ??
-    // psFree (file->phu);
-    // psFree (file->header);
-
-    return;
-}
-
-pmFPAfile *pmFPAfileAlloc ()
-{
-
-    pmFPAfile *file = psAlloc (sizeof(pmFPAfile));
-    psMemSetDeallocator (file, (psFreeFunc) pmFPAfileFree);
-
-    file->phu = NULL;
-    file->readout = NULL;
-    file->header = NULL;
-
-    file->fpa = NULL;
-    file->fits = NULL;
-    file->names = psMetadataAlloc();
-
-    file->format = NULL;
-    file->name = NULL;
-
-    file->filerule = NULL;
-    file->filextra = NULL;
-    file->extrule  = NULL;
-    file->extxtra  = NULL;
-
-    file->filename = NULL;
-    file->extname  = NULL;
-
-    file->type = PM_FPA_FILE_NONE;
-    file->mode = PM_FPA_MODE_NONE;
-    file->state = PM_FPA_STATE_CLOSED;
-
-    return (file);
-}
-
-pmFPAfile *pmFPAfileDefine (psMetadata *files, psMetadata *camera, pmFPA *fpa, char *name)
-{
-
-    bool status;
-    char *depth;
-    char *type;
-
-    // select the FILERULES from the camera config
-    psMetadata *filerules = psMetadataLookupPtr (&status, camera, "FILERULES");
-    if (filerules == NULL) {
-        psErrorStackPrint(stderr, "Can't find FILERULES in the CAMERA configuration!\n");
-        return NULL;
-    }
-
-    // select the name from the FILERULES
-    // check for alias name (type == STR, name is aliased name)
-    char *realname = psMetadataLookupStr (&status, filerules, name);
-    if (realname == NULL)
-        realname = name;
-
-    psMetadata *data = psMetadataLookupPtr (&status, filerules, realname);
-    if (data == NULL) {
-        psErrorStackPrint(stderr, "Can't find file concept %s!\n", name);
-        return NULL;
-    }
-
-    pmFPAfile *file = pmFPAfileAlloc ();
-
-    // save the name of this pmFPAfile
-    file->name = psStringCopy (name);
-
-    file->filerule = psMemIncrRefCounter(psMetadataLookupStr (&status, data, "FILENAME.RULE"));
-    file->filextra = psMemIncrRefCounter(psMetadataLookupStr (&status, data, "FILENAME.XTRA"));
-    file->extrule  = psMemIncrRefCounter(psMetadataLookupStr (&status, data, "EXTNAME.RULE"));
-    file->extxtra  = psMemIncrRefCounter(psMetadataLookupStr (&status, data, "EXTNAME.XTRA"));
-
-    file->fileDepth = PM_FPA_DEPTH_NONE;
-    depth = psMetadataLookupStr (&status, data, "FILE.DEPTH");
-    if (depth != NULL) {
-        if (!strcasecmp (depth, "FPA"))     {
-            file->fileDepth = PM_FPA_DEPTH_FPA;
-        }
-        if (!strcasecmp (depth, "CHIP"))    {
-            file->fileDepth = PM_FPA_DEPTH_CHIP;
-        }
-        if (!strcasecmp (depth, "CELL"))    {
-            file->fileDepth = PM_FPA_DEPTH_CELL;
-        }
-        if (!strcasecmp (depth, "READOUT")) {
-            file->fileDepth = PM_FPA_DEPTH_READOUT;
-        }
-    }
-    if (file->fileDepth == PM_FPA_DEPTH_NONE) {
-        psLogMsg (__func__, 3, "warning: FILE.DEPTH is not set for %s\n", name);
-    }
-
-    file->dataDepth = PM_FPA_DEPTH_NONE;
-    depth = psMetadataLookupStr (&status, data, "DATA.DEPTH");
-    if (depth != NULL) {
-        if (!strcasecmp (depth, "FPA"))     {
-            file->dataDepth = PM_FPA_DEPTH_FPA;
-        }
-        if (!strcasecmp (depth, "CHIP"))    {
-            file->dataDepth = PM_FPA_DEPTH_CHIP;
-        }
-        if (!strcasecmp (depth, "CELL"))    {
-            file->dataDepth = PM_FPA_DEPTH_CELL;
-        }
-        if (!strcasecmp (depth, "READOUT")) {
-            file->dataDepth = PM_FPA_DEPTH_READOUT;
-        }
-    }
-    if (file->dataDepth == PM_FPA_DEPTH_NONE) {
-        psLogMsg (__func__, 3, "warning: DATA.DEPTH is not set for %s\n", name);
-    }
-
-    file->type = PM_FPA_FILE_NONE;
-    type = psMetadataLookupStr (&status, data, "FILE.TYPE");
-    if (type != NULL) {
-        if (!strcasecmp (type, "SX"))     {
-            file->type = PM_FPA_FILE_SX;
-        }
-        if (!strcasecmp (type, "OBJ"))     {
-            file->type = PM_FPA_FILE_OBJ;
-        }
-        if (!strcasecmp (type, "CMP"))     {
-            file->type = PM_FPA_FILE_CMP;
-        }
-        if (!strcasecmp (type, "CMF"))     {
-            file->type = PM_FPA_FILE_CMF;
-        }
-        if (!strcasecmp (type, "RAW"))     {
-            file->type = PM_FPA_FILE_RAW;
-        }
-        if (!strcasecmp (type, "IMAGE"))     {
-            file->type = PM_FPA_FILE_IMAGE;
-        }
-        if (!strcasecmp (type, "PSF"))     {
-            file->type = PM_FPA_FILE_PSF;
-        }
-        if (!strcasecmp (type, "JPEG"))     {
-            file->type = PM_FPA_FILE_JPEG;
-        }
-    }
-    if (file->type == PM_FPA_FILE_NONE) {
-        psLogMsg (__func__, 3, "warning: FILE.TYPE is not defined for %s\n", name);
-    }
-
-    file->mode = PM_FPA_MODE_NONE;
-    type = psMetadataLookupStr (&status, data, "FILE.MODE");
-    if (type != NULL) {
-        if (!strcasecmp (type, "READ"))     {
-            file->mode = PM_FPA_MODE_READ;
-        }
-        if (!strcasecmp (type, "WRITE"))     {
-            file->mode = PM_FPA_MODE_WRITE;
-        }
-    }
-    if (file->mode == PM_FPA_MODE_NONE) {
-        psLogMsg (__func__, 3, "warning: FILE.MODE is not defined for %s\n", name);
-    }
-
-    if (fpa != NULL) {
-        file->fpa = psMemIncrRefCounter(fpa);
-    }
-
-    // for WRITE type of data, the output format needs to be determined
-    // this is only needed if the output file is not identical in structure to the input
-    char *formatName = psMetadataLookupStr (&status, data, "FILE.FORMAT");
-    if (formatName && strcasecmp (formatName, "NONE")) {
-        psMetadata *formats = psMetadataLookupMD(&status, camera, "FORMATS"); // List of formats
-        char *formatFile = psMetadataLookupStr (&status, formats, formatName);
-        readConfig (&file->format, formatFile, formatName);
-    }
-
-    psMetadataAddPtr (files, PS_LIST_TAIL, name, PS_DATA_UNKNOWN, "", file);
-
-    // we free this copy of file, but 'files' still has a copy
-    psFree (file);
-
-    // the returned value is a view into the version on 'files'
-    return (file);
-}
-
-/*
-pmFPAfile *pmFPAfileConstruct (psMetadata *files, psMetadata *format, psMetadata *camera, char *name)
-{
-    pmFPA *fpa = pmFPAConstruct (camera);
-    pmFPAfile *file = pmFPAfileDefine (files, format, fpa, name);
-    psFree (fpa);
-    return file;
-}
-*/
-
-// open file (if not already opened)
-bool pmFPAfileOpen (pmFPAfile *file, const pmFPAview *view)
-{
-
-    bool status;
-    char *extra;
-    char *mode = NULL;
-    char *readMode = "r";
-    char *writeMode = "w";
-
-    if (file->state & PM_FPA_STATE_INACTIVE) {
-        return false;
-    }
-
-    if (file->state == PM_FPA_STATE_OPEN) {
-        return true;
-    }
-
-    if (file->mode == PM_FPA_MODE_NONE) {
-        return false;
-    }
-    if (file->mode == PM_FPA_MODE_INTERNAL) {
-        return false;
-    }
-    if (file->mode == PM_FPA_MODE_READ) {
-        mode = readMode;
-    }
-    if (file->mode == PM_FPA_MODE_WRITE) {
-        mode = writeMode;
-    }
-
-    // determine the file name
-    file->filename = pmFPAfileNameFromRule (file->filerule, file, view);
-    if (file->filename == NULL)
-        return false;
-
-    // indirect filenames
-    if (!strcasecmp (file->filename, "@FILES")) {
-        psFree (file->filename);
-        extra = pmFPAfileNameFromRule (file->filextra, file, view);
-        file->filename = psMetadataLookupStr (&status, file->names, extra);
-        psFree (extra);
-        if (file->filename == NULL)
-            return false;
-        // psMetadataLookupStr just returns a view, file->filename must be protected
-        psMemIncrRefCounter (file->filename);
-    }
-    if (!strcasecmp (file->filename, "@DETDB")) {
-        psFree (file->filename);
-        extra = pmFPAfileNameFromRule (file->filextra, file, view);
-        // file->filename = pmDetrendSelect (extra);
-        psFree (extra);
-        if (file->filename == NULL)
-            return false;
-        psMemIncrRefCounter (file->filename);
-    }
-
-    switch (file->type) {
-        // open the FITS types:
-    case PM_FPA_FILE_IMAGE:
-    case PM_FPA_FILE_CMF:
-        psTrace ("pmFPAfile", 5, "opening %s (type: %d)\n", file->filename, file->type);
-        file->fits = psFitsOpen (file->filename, mode);
-        if (file->fits == NULL)
-            psAbort (__func__, "error opening file %s\n", file->filename);
-        file->state = PM_FPA_STATE_OPEN;
-        break;
-
-        // defer opening TEXT types:
-    case PM_FPA_FILE_SX:
-    case PM_FPA_FILE_OBJ:
-    case PM_FPA_FILE_CMP:
-    case PM_FPA_FILE_RAW:
-    case PM_FPA_FILE_PSF:
-    case PM_FPA_FILE_JPEG:
-        psTrace ("pmFPAfile", 5, "not opening %s (type: %d)\n", file->filename, file->type);
-        break;
-
-    default:
-        fprintf (stderr, "warning: type mismatch for %s : %d\n", file->filename, file->type);
-        return false;
-    }
-    return true;
-}
-
-bool pmFPAfileRead (pmFPAfile *file, const pmFPAview *view)
-{
-    if (file->state & PM_FPA_STATE_INACTIVE)
-        return false;
-
-    if (file->mode != PM_FPA_MODE_READ)
-        return false;
-
-    // get the current depth
-    pmFPAdepth depth = pmFPAviewDepth (view);
-
-    // do we need to open this file?
-    if (depth == file->fileDepth) {
-        pmFPAfileOpen (file, view);
-    }
-
-    // do we need to read this file?
-    if (depth != file->dataDepth)
-        return false;
-
-    switch (file->type) {
-    case PM_FPA_FILE_IMAGE:
-        if (pmFPAviewReadFitsImage (view, file)) {
-            psTrace ("pmFPAfile", 5, "reading %s (type: %d)\n", file->filename, file->type);
-        } else {
-            psTrace ("pmFPAfile", 5, "skipping %s (type: %d)\n", file->filename, file->type);
-        }
-        break;
-
-    case PM_FPA_FILE_SX:
-    case PM_FPA_FILE_RAW:
-    case PM_FPA_FILE_OBJ:
-    case PM_FPA_FILE_CMP:
-    case PM_FPA_FILE_CMF:
-        pmFPAviewReadObjects (view, file);
-        psTrace ("pmFPAfile", 5, "reading %s (type: %d)\n", file->filename, file->type);
-        break;
-
-    case PM_FPA_FILE_PSF:
-        pmFPAviewReadPSFmodel (view, file);
-        psTrace ("pmFPAfile", 5, "reading %s (type: %d)\n", file->filename, file->type);
-        break;
-
-    case PM_FPA_FILE_JPEG:
-        break;
-
-    default:
-        fprintf (stderr, "warning: type mismatch\n");
-        return false;
-    }
-    return true;
-}
-
-bool pmFPAfileWrite (pmFPAfile *file, const pmFPAview *view)
-{
-
-    if (file->state & PM_FPA_STATE_INACTIVE) {
-        return false;
-    }
-
-    if (file->mode != PM_FPA_MODE_WRITE) {
-        return false;
-    }
-
-    // get the current depth
-    pmFPAdepth depth = pmFPAviewDepth (view);
-
-    // do we need to write this file?
-    if (depth != file->dataDepth)
-        return false;
-
-    // do we need to open this file?
-    if (depth >= file->fileDepth) {
-        pmFPAfileOpen (file, view);
-    }
-
-    switch (file->type) {
-    case PM_FPA_FILE_IMAGE:
-        pmFPAviewWriteFitsImage (view, file);
-        psTrace ("pmFPAfile", 5, "wrote image %s (fpa: %p)\n", file->filename, file->fpa);
-        break;
-
-    case PM_FPA_FILE_SX:
-    case PM_FPA_FILE_RAW:
-    case PM_FPA_FILE_OBJ:
-    case PM_FPA_FILE_CMP:
-    case PM_FPA_FILE_CMF:
-        pmFPAviewWriteObjects (view, file);
-        psTrace ("pmFPAfile", 5, "wrote object %s (fpa: %p)\n", file->filename, file->fpa);
-        break;
-
-    case PM_FPA_FILE_PSF:
-        pmFPAviewWritePSFmodel (view, file);
-        psTrace ("pmFPAfile", 5, "wrote PSF %s (fpa: %p)\n", file->filename, file->fpa);
-        break;
-
-    case PM_FPA_FILE_JPEG:
-        pmFPAviewWriteJPEG (view, file);
-        psTrace ("pmFPAfile", 5, "wrote PSF %s (fpa: %p)\n", file->filename, file->fpa);
-        break;
-
-    default:
-        fprintf (stderr, "warning: type mismatch\n");
-        return false;
-    }
-    return true;
-}
-
-// create the data elements (headers, images) appropriate for this view
-bool pmFPAfileCreate (pmFPAfile *file, const pmFPAview *view)
-{
-    if (file->state & PM_FPA_STATE_INACTIVE) {
-        return false;
-    }
-    if (file->mode != PM_FPA_MODE_WRITE)
-        return false;
-
-    // get the current depth
-    pmFPAdepth depth = pmFPAviewDepth (view);
-
-    // do we need to write this file?
-    if (depth != file->dataDepth)
-        return false;
-
-    // XXX is this a sufficient check to avoid creating elements?
-    if (file->src == NULL)
-        return false;
-
-    switch (file->type) {
-    case PM_FPA_FILE_IMAGE:
-        /* create a PHU for thie file, if it does not exist */
-        pmFPAfileCopyStructureView (file->fpa, file->src, file->format, file->xBin, file->yBin, view);
-        psTrace ("pmFPAfile", 5, "created fpa data elements for %s (fpa: %p)\n", file->filename, file->fpa);
-        break;
-
-    case PM_FPA_FILE_SX:
-    case PM_FPA_FILE_RAW:
-    case PM_FPA_FILE_OBJ:
-    case PM_FPA_FILE_CMP:
-    case PM_FPA_FILE_CMF:
-    case PM_FPA_FILE_PSF:
-    case PM_FPA_FILE_JPEG:
-        break;
-
-    default:
-        fprintf (stderr, "warning: type mismatch\n");
-        return false;
-    }
-    return true;
-}
-
-bool pmFPAfileClose (pmFPAfile *file, const pmFPAview *view)
-{
-    if (file->state & PM_FPA_STATE_INACTIVE) {
-        return false;
-    }
-    if (file->state == PM_FPA_STATE_CLOSED) {
-        return true;
-    }
-
-    // is current depth == open depth?
-    pmFPAdepth depth = pmFPAviewDepth (view);
-    if (file->fileDepth != depth) {
-        return false;
-    }
-
-    // check if we are actually open
-    switch (file->type) {
-        // check the FITS types
-    case PM_FPA_FILE_IMAGE:
-    case PM_FPA_FILE_CMF:
-        psFitsClose (file->fits);
-        psTrace ("pmFPAfile", 5, "closing %s (type: %d)\n", file->filename, file->type);
-        file->fits = NULL;
-        file->phu = NULL;
-        file->header = NULL;
-        file->state = PM_FPA_STATE_CLOSED;
-        break;
-
-        // ignore the TEXT types
-    case PM_FPA_FILE_SX:
-    case PM_FPA_FILE_RAW:
-    case PM_FPA_FILE_OBJ:
-    case PM_FPA_FILE_CMP:
-    case PM_FPA_FILE_PSF:
-    case PM_FPA_FILE_JPEG:
-        break;
-
-    default:
-        fprintf (stderr, "warning: type mismatch\n");
-        return false;
-    }
-    return true;
-}
-
-// set the state of the specified pmFPAfile to active (state == true) or inactive
-// if name is NULL, set the state for all pmFPAfiles
-bool pmFPAfileActivate (psMetadata *files, bool state, char *name)
-{
-    if (name == NULL) {
-        psMetadataItem *item = NULL;
-        psMetadataIterator *iter = psMetadataIteratorAlloc (files, PS_LIST_HEAD, NULL);
-        while ((item = psMetadataGetAndIncrement (iter)) != NULL) {
-            pmFPAfile *file = item->data.V;
-            if (state) {
-                file->state &= NOT_U8(PM_FPA_STATE_INACTIVE);
-            } else {
-                file->state |= PM_FPA_STATE_INACTIVE;
-            }
-        }
-        psFree (iter);
-        return true;
-    }
-
-    bool status = false;
-    pmFPAfile *file = psMetadataLookupPtr (&status, files, name);
-    if (!file) {
-        return false;
-    }
-    if (state) {
-        file->state &= NOT_U8(PM_FPA_STATE_INACTIVE);
-    } else {
-        file->state |= PM_FPA_STATE_INACTIVE;
-    }
-    return true;
-}
-
-// attempt open, read, write, or close pmFPAfiles in files
-bool pmFPAfileIOChecks (psMetadata *files, const pmFPAview *view, pmFPAfilePlace place)
-{
-    // recipe override values (command-line options):
-    psMetadataItem *item = NULL;
-    psMetadataIterator *iter = psMetadataIteratorAlloc (files, PS_LIST_HEAD, NULL);
-    while ((item = psMetadataGetAndIncrement (iter)) != NULL) {
-        pmFPAfile *file = item->data.V;
-
-        if (place == PM_FPA_BEFORE) {
-            pmFPAfileRead (file, view);
-            pmFPAfileCreate (file, view);
-        } else {
-            pmFPAfileWrite (file, view);
-            pmFPAfileClose (file, view);
-        }
-    }
-    psFree (iter);
-    return true;
-}
-
-// create a file with the given name, assign it type "INTERNAL", and supply it with an image
-// of the requested dimensions. (image only, mask and weight are ignored)
-pmReadout *pmFPAfileCreateInternal (psMetadata *files, char *name, int Nx, int Ny, int type)
-{
-    pmReadout *readout = pmReadoutAlloc (NULL);
-    readout->image = psImageAlloc (Nx, Ny, type);
-
-    // I want an image from the
-    pmFPAfile *file = pmFPAfileAlloc();
-    file->mode = PM_FPA_MODE_INTERNAL;
-
-    file->readout = readout;
-    psMetadataAddPtr (files, PS_LIST_TAIL, name, PS_DATA_UNKNOWN, "", file);
-    psFree (file);
-    // we free this copy of file, but 'files' still has a copy
-
-    return (readout);
-}
-
-bool pmFPAfileDropInternal (psMetadata *files, char *name)
-{
-    bool status;
-
-    pmFPAfile *file = psMetadataLookupPtr (&status, files, name);
-    if (file == NULL)
-        return false;
-
-    if (file->mode != PM_FPA_MODE_INTERNAL)
-        return false;
-
-    psMetadataRemoveKey (files, name);
-    return true;
-}
-
-// select the readout from the named pmFPAfile; if the named file does not exist,
-pmReadout *pmFPAfileThisReadout (psMetadata *files, const pmFPAview *view, const char *name)
-{
-    bool status;
-
-    pmFPAfile *file = psMetadataLookupPtr (&status, files, name);
-    if (file == NULL)
-        return NULL;
-
-    // internal files have the readout as a separate element:
-    if (file->mode == PM_FPA_MODE_INTERNAL) {
-        return file->readout;
-    }
-
-    pmReadout *readout = pmFPAviewThisReadout (view, file->fpa);
-    return readout;
-}
-
-// given an already-opened fits file, read the components corresponding
-// to the specified view
-bool pmFPAviewReadFitsImage (const pmFPAview *view, pmFPAfile *file)
-{
-    bool status;
-    pmFPA *fpa = file->fpa;
-    psFits *fits = file->fits;
-
-    if (view->chip == -1) {
-        status = pmFPARead (fpa, fits, NULL);
-        return status;
-    }
-
-    if (view->chip >= fpa->chips->n) {
-        return false;
-    }
-    pmChip *chip = fpa->chips->data[view->chip];
-
-    if (view->cell == -1) {
-        status = pmChipRead (chip, fits, NULL);
-        return status;
-    }
-
-    if (view->cell >= chip->cells->n) {
-        return false;
-    }
-    pmCell *cell = chip->cells->data[view->cell];
-
-    if (view->readout == -1) {
-        status = pmCellRead (cell, fits, NULL);
-        return status;
-    }
-    return false;
-
-    // XXX pmReadoutRead, pmReadoutReadSegement disabled for now
-    # if (0)
-
-        if (view->readout >= cell->readouts->n) {
-            return false;
-        }
-    pmReadout *readout = cell->readouts->data[view->readout];
-
-    if (view->nRows == 0) {
-        pmReadoutRead (readout, fits, NULL);
-    } else {
-        pmReadoutReadSegment (readout, fits, view->nRows, view->iRows, NULL, NULL);
-    }
-    return true;
-    # endif
-}
-
-// given an already-opened fits file, write the components corresponding
-// to the specified view
-bool pmFPAviewWriteFitsImage (const pmFPAview *view, pmFPAfile *file)
-{
-
-    pmFPA *fpa = file->fpa;
-    psFits *fits = file->fits;
-
-    // pmFPAWrite takes care of all PHUs as needed
-    if (view->chip == -1) {
-        pmFPAWrite (fpa, fits, NULL, true);
-        return true;
-    }
-
-    if (view->chip >= fpa->chips->n) {
-        return false;
-    }
-    pmChip *chip = fpa->chips->data[view->chip];
-
-    // do we need to write out a PHU for this entry?
-    if (file->phu == NULL) {
-        pmHDU *hdu = pmFPAviewThisHDU (view, file->fpa);
-        pmHDU *phu = pmFPAviewThisPHU (view, file->fpa);
-        // if this hdu is the phu, the write function below will create the phu
-        if (hdu != phu) {
-            // we assume that the PHU is just a header
-            psMetadata *outhead = psMetadataCopy (NULL, phu->header);
-            psMetadataAdd (outhead, PS_LIST_TAIL, "EXTEND", PS_DATA_BOOL | PS_META_REPLACE, "this file has extensions", true);
-            psFitsWriteHeaderNotImage (file->fits, outhead);
-            file->phu = phu->header;
-            psTrace ("pmFPAfile", 5, "wrote phu %s (type: %d)\n", file->filename, file->type);
-            psFree (outhead);
-        }
-    }
-
-    if (view->cell == -1) {
-        pmChipWrite (chip, fits, NULL, true);
-        return true;
-    }
-
-    if (view->cell >= chip->cells->n) {
-        return false;
-    }
-    pmCell *cell = chip->cells->data[view->cell];
-
-    if (view->readout == -1) {
-        pmCellWrite (cell, fits, NULL, true);
-        return true;
-    }
-    return false;
-
-    // XXX disable readout write for now
-    # if (0)
-
-        if (view->readout >= cell->readouts->n) {
-            return false;
-        }
-    pmReadout *readout = cell->readouts->data[view->readout];
-
-    if (view->nRows == 0) {
-        pmReadoutWrite (readout, fits, NULL, NULL);
-    } else {
-        pmReadoutWriteSegment (readout, fits, view->nRows, view->iRows, NULL, NULL);
-    }
-    return true;
-    # endif
-}
-
-// look for the given name on the argument list.
-// returns the file (a view to the one saved on config->files)
-pmFPAfile *pmFPAfileFromArgs (bool *found, pmConfig *config, char *filename, char *argname)
-{
-    bool status;
-    pmFPA *fpa = NULL;
-    psFits *fits = NULL;
-    pmFPAfile *file = NULL;
-    psMetadata *phu = NULL;
-    psMetadata *format = NULL;
-
-    if (*found)
-        return NULL;
-
-    // we search the argument data for the named fileset (argname)
-    psArray *infiles = psMetadataLookupPtr(&status, config->arguments, argname);
-    if (!status)
-        return NULL;
-    if (infiles->n < 1)
-        return NULL;
-
-    // determine the current format from the header
-    // if no camera has been specified, use the first image as a template for the rest.
-    fits = psFitsOpen (infiles->data[0], "r");
-    phu = psFitsReadHeader (NULL, fits);
-    format = pmConfigCameraFormatFromHeader (config, phu);
-    psFitsClose (fits);
-
-    // build the template fpa, set up the basic view
-    fpa = pmFPAConstruct (config->camera);
-
-    // load the given filerule (from config->camera) and associate it with the fpa
-    // the output file is just a view to the file on config->files
-    file = pmFPAfileDefine (config->files, config->camera, fpa, filename);
-    if (!file) {
-        psErrorStackPrint(stderr, "file %s not defined\n", filename);
-        psFree (fpa);
-        psFree (format);
-        return NULL;
-    }
-
-    // this file is (by virtue of being supplied in the argument list) coming from the fileset
-    psFree (file->filerule);
-    psFree (file->filextra);
-
-    // adjust the rules to identify these files in the file->names data
-    file->filerule = psStringCopy ("@FILES");
-    // XXX this rule does not work in general
-    file->filextra = psStringCopy ("{CHIP.NAME}.{CELL.NAME}");
-
-    // examine the list of input files and validate their cameras
-    for (int i = 0; i < infiles->n; i++) {
-        if (i > 0) {
-            fits = psFitsOpen (infiles->data[i], "r");
-            phu = psFitsReadHeader (NULL, fits);
-            pmConfigValidateCameraFormat (format, phu);
-            psFitsClose (fits);
-        }
-
-        // set the view to the corresponding entry for this phu
-        pmFPAview *view = pmFPAAddSourceFromHeader (fpa, phu, format);
-
-        // XXX is this the correct psMD to save the filename?
-        char *name = pmFPAfileNameFromRule (file->filextra, file, view);
-        psMetadataAddStr (file->names, PS_LIST_TAIL, name, 0, "", infiles->data[i]);
-
-        psFree (view);
-        psFree (name);
-        psFree (phu);
-    }
-    psFree (fpa);
-    psFree (format);
-    *found = true;
-
-    return file;
-}
-
-// XXX this this function through, then finish
-# if 0
-// look for the given name on the argument list.
-// returns the file (a view to the one saved on config->files)
-// in this case, each file should correspond to the same view
-// (except at the readout level), of multiple FPAs
-// XXX think this through a bit more:
-//  - do we create multiple input fpas, or assign the same files to the single fpa?
-//  - do we save the multiple pmFPAfiles in the config->files psMD?
-//  - do we save the multiple filenames in the file->names psMD?
-//  - if we have a single FPA and multiple names, we need a method to
-//    turn on a specific name for the I/O actions.  probably true if we
-//    have multiple FPAs as well.
-pmFPAfile *pmFPAfileSetFromArgs (bool *found, pmConfig *config, char *filename, char *argname)
-{
-    bool status;
-    pmFPA *fpa = NULL;
-    psFits *fits = NULL;
-    pmFPAfile *file = NULL;
-    psMetadata *phu = NULL;
-    psMetadata *format = NULL;
-
-    if (*found)
-        return NULL;
-
-    // we search the argument data for the named fileset (argname)
-    psArray *infiles = psMetadataLookupPtr(&status, config->arguments, argname);
-    if (!status)
-        return NULL;
-    if (infiles->n < 1)
-        return NULL;
-
-    // determine the current format from the header
-    // if no camera has been specified, use the first image as a template for the rest.
-    fits = psFitsOpen (infiles->data[0], "r");
-    phu = psFitsReadHeader (NULL, fits);
-    format = pmConfigCameraFormatFromHeader (config, phu);
-    psFitsClose (fits);
-
-    // build the template fpa, set up the basic view
-    fpa = pmFPAConstruct (config->camera);
-
-    // load the given filerule (from config->camera) and associate it with the fpa
-    // the output file is just a view to the file on config->files
-    // XXX the filenames placed on config->files should include the seq number
-    file = pmFPAfileDefine (config->files, config->camera, fpa, filename);
-    if (!file) {
-        psErrorStackPrint(stderr, "file %s not defined\n", filename);
-        psFree (fpa);
-        psFree (format);
-        return NULL;
-    }
-
-    // this file is (by virtue of being supplied in the argument list) coming from the fileset
-    psFree (file->filerule);
-    psFree (file->filextra);
-
-    // adjust the rules to identify these files in the file->names data
-    file->filerule = psStringCopy ("@FILES");
-    // XXX this rule does not work in general
-    file->filextra = psStringCopy ("{CHIP.NAME}.{CELL.NAME}");
-
-    // examine the list of input files and validate their cameras
-    for (int i = 0; i < infiles->n; i++) {
-        if (i > 0) {
-            fits = psFitsOpen (infiles->data[i], "r");
-            phu = psFitsReadHeader (NULL, fits);
-            pmConfigValidateCameraFormat (format, phu);
-            psFitsClose (fits);
-        }
-
-        // set the view to the corresponding entry for this phu
-        pmFPAview *view = pmFPAAddSourceFromHeader (fpa, phu, format);
-
-        // XXX is this the correct psMD to save the filename?
-        char *name = pmFPAfileNameFromRule (file->filextra, file, view);
-        psMetadataAddStr (file->names, PS_LIST_TAIL, name, 0, "", infiles->data[i]);
-
-        psFree (view);
-        psFree (name);
-        psFree (phu);
-    }
-    psFree (fpa);
-    psFree (format);
-    *found = true;
-
-    return file;
-}
-# endif
-
-// create a new output pmFPAfile based on an existing FPA
-pmFPAfile *pmFPAfileFromFPA (pmConfig *config, pmFPA *src, int xBin, int yBin, char *filename)
-{
-    // XXX pmFPAConstruct has many leaks (6919)
-    pmFPA *fpa = pmFPAConstruct (config->camera);
-    pmFPAfile *file = pmFPAfileDefine (config->files, config->camera, fpa, filename);
-    file->src = src; // inherit output elements from this source pmFPA
-    file->xBin = xBin;
-    file->yBin = yBin;
-
-    return file;
-}
-
-// look for the given name on the argument list.
-pmFPAfile *pmFPAfileFromConf (bool *found, pmConfig *config, char *filename, pmFPA *input)
-{
-    psFits *fits = NULL;
-    pmFPAfile *file = NULL;
-    psMetadata *phu = NULL;
-    psMetadata *format = NULL;
-    psArray *infiles = NULL;
-
-    if (*found)
-        return NULL;
-
-    // a camera config is needed (as source of file rule)
-    if (config->camera == NULL) {
-        psErrorStackPrint (stderr, "camera is not defined\n");
-        return NULL;
-    }
-
-    // expect @DETDB in the config filerule
-    file = pmFPAfileDefine (config->files, config->camera, NULL, filename);
-    if (!file) {
-        psErrorStackPrint(stderr, "file %s not defined\n", filename);
-        return NULL;
-    }
-
-    // image names come from the file->name list?
-    if (!strcasecmp (file->filerule, "@FILES"))
-        psAbort ("pmFPAfileFromConfig", "programming error");
-
-    // image needs to come from the detrend database
-    if (!strcasecmp (file->filerule, "@DETDB")) {
-        // char *extra = pmFPAfileNameFromRule (file->filextra, file, view);
-        // psArray *infiles = pmDetrendSelect (extra, input);
-        psAbort ("pmFPAfileFromConfig", "@DETDB not yet defined");
-    } else {
-        infiles = psArrayAlloc(1);
-        infiles->n = 1;
-        infiles->data[0] = psStringCopy (file->filerule);
-    }
-    if (infiles == NULL)
-        return NULL;
-    if (infiles->n < 1) {
-        psFree (infiles);
-        return NULL;
-    }
-
-    // determine the current format from the header
-    // if no camera has been specified, use the first image as a template for the rest.
-    fits = psFitsOpen (infiles->data[0], "r");
-    phu = psFitsReadHeader (NULL, fits);
-    format = pmConfigCameraFormatFromHeader (config, phu);
-    psFitsClose (fits);
-
-    // build the template fpa, set up the basic view
-    file->fpa = pmFPAConstruct (config->camera);
-
-    // this file is (by virtue of being supplied in the argument list) coming from the fileset
-    psFree (file->filerule);
-    psFree (file->filextra);
-
-    // adjust the rules to identify these files in the file->names data
-    file->filerule = psStringCopy ("@FILES");
-    // XXX this rule does not work in general
-    file->filextra = psStringCopy ("{CHIP.NAME}.{CELL.NAME}");
-
-    // examine the list of input files and validate their cameras
-    for (int i = 0; i < infiles->n; i++) {
-        if (i > 0) {
-            fits = psFitsOpen (infiles->data[i], "r");
-            phu = psFitsReadHeader (NULL, fits);
-            pmConfigValidateCameraFormat (format, phu);
-            psFitsClose (fits);
-        }
-
-        // set the view to the corresponding entry for this phu
-        pmFPAview *view = pmFPAAddSourceFromHeader (file->fpa, phu, format);
-
-        // XXX is this the correct psMD to save the filename?
-        char *name = pmFPAfileNameFromRule (file->filextra, file, view);
-        psMetadataAddStr (file->names, PS_LIST_TAIL, name, 0, "", infiles->data[i]);
-
-        psFree (view);
-        psFree (name);
-        psFree (phu);
-    }
-    psFree (format);
-    psFree (infiles);
-    *found = true;
-    return file;
-}
-
-bool pmFPAfileAddFileNames (psMetadata *files, char *name, char *value, int mode)
-{
-
-    // add the output names to the output-type files
-    psMetadataItem *item = NULL;
-    psMetadataIterator *iter = psMetadataIteratorAlloc (files, PS_LIST_HEAD, NULL);
-    while ((item = psMetadataGetAndIncrement (iter)) != NULL) {
-        pmFPAfile *file = item->data.V;
-
-        if (file->mode == mode) {
-            psMetadataAddStr (file->names, PS_LIST_TAIL, name, 0, "", value);
-        }
-    }
-    psFree (iter);
-    return true;
-}
-
-// select the rule from the camera configuration, perform substitutions as needed
-char *pmFPAfileNameFromRule (char *rule, pmFPAfile *file, const pmFPAview *view)
-{
-
-    char *newName = NULL;     // destination for resulting name
-
-    newName = psStringCopy (rule);
-
-    if (strstr (newName, "{CHIP.NAME}") != NULL) {
-        pmChip *chip = pmFPAviewThisChip (view, file->fpa);
-        if (chip != NULL) {
-            char *name = psMetadataLookupStr (NULL, chip->concepts, "CHIP.NAME");
-            if (name != NULL) {
-                newName = psStringSubstitute (newName, name, "{CHIP.NAME}");
-            }
-        }
-    }
-    if (strstr (newName, "{CELL.NAME}") != NULL) {
-        pmCell *cell = pmFPAviewThisCell (view, file->fpa);
-        if (cell != NULL) {
-            char *name = psMetadataLookupStr (NULL, cell->concepts, "CELL.NAME");
-            if (name != NULL) {
-                newName = psStringSubstitute (newName, name, "{CELL.NAME}");
-            }
-        }
-    }
-    if (strstr (newName, "{EXTNAME}") != NULL) {
-        pmHDU *hdu = pmFPAviewThisHDU (view, file->fpa);
-        if (hdu->extname != NULL) {
-            newName = psStringSubstitute (newName, hdu->extname, "{EXTNAME}");
-        }
-    }
-    if (strstr (newName, "{OUTPUT}") != NULL) {
-        char *name = psMetadataLookupStr (NULL, file->names, "OUTPUT");
-        if (name != NULL) {
-            newName = psStringSubstitute (newName, name, "{OUTPUT}");
-        }
-    }
-    return newName;
-}
-
-// given an already-opened fits file, write the components corresponding
-// to the specified view
-bool pmFPAfileCopyView (pmFPA *out, pmFPA *in, const pmFPAview *view)
-{
-    // pmFPAWrite takes care of all PHUs as needed
-    if (view->chip == -1) {
-        pmFPACopy (out, in);
-        return true;
-    }
-    if (view->chip >= in->chips->n) {
-        return false;
-    }
-    pmChip *inChip = in->chips->data[view->chip];
-    pmChip *outChip = out->chips->data[view->chip];
-
-    if (view->cell == -1) {
-        pmChipCopy (outChip, inChip);
-        return true;
-    }
-    if (view->cell >= inChip->cells->n) {
-        return false;
-    }
-    pmCell *inCell = inChip->cells->data[view->cell];
-    pmCell *outCell = outChip->cells->data[view->cell];
-
-    if (view->readout == -1) {
-        pmCellCopy (outCell, inCell);
-        return true;
-    }
-    return false;
-
-    // XXX add readout / segment equivalents
-}
-
-// given an already-opened fits file, write the components corresponding
-// to the specified view
-bool pmFPAfileCopyStructureView (pmFPA *out, pmFPA *in, psMetadata *format, int xBin, int yBin, const pmFPAview *view)
-{
-    // pmFPAWrite takes care of all PHUs as needed
-    if (view->chip == -1) {
-        pmFPAAddSourceFromView (out, view, format);
-        pmFPACopyStructure (out, in, xBin, yBin);
-        return true;
-    }
-    if (view->chip >= in->chips->n) {
-        return false;
-    }
-    pmChip *inChip = in->chips->data[view->chip];
-    pmChip *outChip = out->chips->data[view->chip];
-
-    if (view->cell == -1) {
-        pmFPAAddSourceFromView (out, view, format);
-        pmChipCopyStructure (outChip, inChip, xBin, yBin);
-        return true;
-    }
-    if (view->cell >= inChip->cells->n) {
-        return false;
-    }
-    pmCell *inCell = inChip->cells->data[view->cell];
-    pmCell *outCell = outChip->cells->data[view->cell];
-
-    if (view->readout == -1) {
-        pmFPAAddSourceFromView (out, view, format);
-        pmCellCopyStructure (outCell, inCell, xBin, yBin);
-        return true;
-    }
-    return false;
-
-    // XXX add readout / segment equivalents
-}
Index: unk/psModules/src/astrom/pmFPAfile.h
===================================================================
--- /trunk/psModules/src/astrom/pmFPAfile.h	(revision 7018)
+++ 	(revision )
@@ -1,160 +1,0 @@
-/** @file  pmFPAview.h
-*
-*  @brief Tools to manipulate the FPA structure elements.
-*
-*  @ingroup AstroImage
-*
-*  @author EAM, IfA
-*
-*  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2006-04-19 20:30:49 $
-*
-*  Copyright 2004-2005 Institute for Astronomy, University of Hawaii
-*/
-
-#ifndef PM_FPA_FILE_H
-#define PM_FPA_FILE_H
-
-#include "pslib.h"
-#include "pmConfig.h"
-#include "pmFPA.h"
-#include "pmFPAview.h"
-
-typedef enum {
-    PM_FPA_BEFORE,
-    PM_FPA_AFTER,
-} pmFPAfilePlace;
-
-typedef enum {
-    PM_FPA_FILE_NONE,
-    PM_FPA_FILE_SX,
-    PM_FPA_FILE_OBJ,
-    PM_FPA_FILE_CMP,
-    PM_FPA_FILE_CMF,
-    PM_FPA_FILE_RAW,
-    PM_FPA_FILE_IMAGE,
-    PM_FPA_FILE_PSF,
-    PM_FPA_FILE_JPEG,
-} pmFPAfileType;
-
-typedef enum {
-    PM_FPA_MODE_NONE,
-    PM_FPA_MODE_READ,
-    PM_FPA_MODE_WRITE,
-    PM_FPA_MODE_INTERNAL,
-} pmFPAfileMode;
-
-typedef enum {
-    PM_FPA_STATE_OPEN     = 0x01,
-    PM_FPA_STATE_CLOSED   = 0x02,
-    PM_FPA_STATE_INACTIVE = 0x04,
-} pmFPAfileState;
-
-typedef struct
-{
-    pmFPAfileMode mode;   // is this file read, written, or only used internally?
-    pmFPAfileType type;   // what type of data is read from / written to disk?
-    pmFPAfileState state;  // have we opened the file, etc?
-
-    pmFPAdepth fileDepth;  // what depth in the FPA hierarchy represents a unique file?
-    pmFPAdepth dataDepth;  // at what depth do we read/write the data segment?
-
-    pmFPA *fpa;    // for I/O files, we carry a pointer to the complete fpa
-    psFits *fits;   // for I/O files of fits type (IMAGE, CMP, CMF), we carry a file handle
-
-    psMetadata *phu;   // pointer (view) to the current phu header
-    psMetadata *header;   // pointer (view) to the current hdu header
-
-    pmReadout *readout;   // for internal files, we only carry a single readout
-
-    psMetadata *names;   // filenames supplied by the cmdline or detdb are saved here
-
-    char *filerule;   // rule for constructing a filename when needed
-    char *filextra;   // additional information used to define filenames (context dependent)
-    char *extrule;   // rule for constructing an extension name when needed
-    char *extxtra;   // additional information used to define extension names (context dependent)
-
-    char *name;    // the name of the rule (useful for debugging / tracing)
-    char *filename;   // the current name of an active file
-    char *extname;   // the current name of an active file extension
-
-    // the following elements are used for WRITE-mode IMAGE-type pmFPAfiles to inform
-    // the creation of a new image based on an existing image
-    pmFPA *src;    // if an output FPA, inherit from this FPA
-    int xBin;    // desired binning in x direction
-    int yBin;    // desired binning in y direction
-    psMetadata *format;
-}
-pmFPAfile;
-
-// allocate an empty pmFPAfile structure
-pmFPAfile *pmFPAfileAlloc ();
-
-// load the pmFPAfile information from the camera configuration data
-pmFPAfile *pmFPAfileDefine (psMetadata *files, psMetadata *camera, pmFPA *fpa, char *name);
-
-// load the pmFPAfile information from the camera configuration data, constructing the needed fpa structure
-// XXX deprecate this function?
-// pmFPAfile *pmFPAfileConstruct (psMetadata *files, psMetadata *format, psMetadata *camera, char *name);
-
-// open the real file corresponding to the given pmFPAfile appropriate to the current view
-bool pmFPAfileOpen (pmFPAfile *file, const pmFPAview *view);
-
-// read from the real file corresponding to the given pmFPAfile for the current view
-bool pmFPAfileRead (pmFPAfile *file, const pmFPAview *view);
-
-bool pmFPAfileCreate (pmFPAfile *file, const pmFPAview *view);
-
-// write to the real file corresponding to the given pmFPAfile for the current view
-bool pmFPAfileWrite (pmFPAfile *file, const pmFPAview *view);
-
-// close the real file corresponding to the given pmFPAfile appropriate to the current view
-bool pmFPAfileClose (pmFPAfile *file, const pmFPAview *view);
-
-// set the state of the specified pmFPAfile to active (state == true) or inactive
-// if name is NULL, set the state for all pmFPAfiles
-bool pmFPAfileActivate (psMetadata *files, bool state, char *name);
-
-// examine all pmFPAfiles listed in the files and perform the needed I/O operations (open,read,write,close)
-bool pmFPAfileIOChecks (psMetadata *files, const pmFPAview *view, pmFPAfilePlace place);
-
-// return an image corresponding to the current readout, from the specified file.  if the pmFPAfile does not
-// exist, construct the image using the given size and type (save it in a pmFPAfile??)
-// psImage *pmFPAfileReadoutImage (psMetadata *files, const pmFPAview *view, char *name, int Nx, int Ny, int type);
-
-// select the readout from the named pmFPAfile; if the named file does not exist,
-pmReadout *pmFPAfileThisReadout (psMetadata *files, const pmFPAview *view, const char *name);
-
-// create a file with the given name, assign it type "INTERNAL", and supply it with an image
-// of the requested dimensions. (image only, mask and weight are ignored)
-pmReadout *pmFPAfileCreateInternal (psMetadata *files, char *name, int Nx, int Ny, int type);
-
-// delete the INTERNAL file of the given name (if it exists)
-bool pmFPAfileDropInternal (psMetadata *files, char *name);
-
-// read an image into the current view
-bool pmFPAviewReadFitsImage (const pmFPAview *view, pmFPAfile *file);
-
-// write the components for the specified view
-bool pmFPAviewWriteFitsImage (const pmFPAview *view, pmFPAfile *file);
-
-// look for the given argname on the argument list.  find the give filename from the file rules
-pmFPAfile *pmFPAfileFromArgs (bool *found, pmConfig *config, char *filename, char *argname);
-
-// look for the given argname on the argument list.  find the give filename from the file rules
-pmFPAfile *pmFPAfileFromConf (bool *found, pmConfig *config, char *filename, pmFPA *input);
-
-// create a new output pmFPAfile based on an existing FPA
-pmFPAfile *pmFPAfileFromFPA (pmConfig *config, pmFPA *src, int xBin, int yBin, char *filename);
-
-// add the specified filename info (value) to the files of the given mode using the given reference name
-bool pmFPAfileAddFileNames (psMetadata *files, char *name, char *value, int mode);
-
-// convert the rule to a name based on the current view
-char *pmFPAfileNameFromRule (char *rule, pmFPAfile *file, const pmFPAview *view);
-
-bool pmFPAfileCopyView (pmFPA *out, pmFPA *in, const pmFPAview *view);
-
-bool pmFPAfileCopyStructureView (pmFPA *out, pmFPA *in, psMetadata *format, int xBin, int yBin, const pmFPAview *view);
-
-# endif
Index: unk/psModules/src/astrom/pmFPAview.c
===================================================================
--- /trunk/psModules/src/astrom/pmFPAview.c	(revision 7018)
+++ 	(revision )
@@ -1,267 +1,0 @@
-/** @file  pmFPAview.c
- *
- *  @author EAM, IfA
- *
- *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2006-04-30 22:06:57 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- */
-
-#include <stdio.h>
-#include <math.h>
-#include <string.h>
-#include "pslib.h"
-#include "pmHDU.h"
-#include "pmHDUUtils.h"
-#include "pmFPA.h"
-#include "pmFPAview.h"
-
-static void pmFPAviewFree (pmFPAview *view)
-{
-    if (view == NULL)
-        return;
-    return;
-}
-
-pmFPAview *pmFPAviewAlloc (int nRows)
-{
-    pmFPAview *view = psAlloc (sizeof(pmFPAview));
-    psMemSetDeallocator (view, (psFreeFunc) pmFPAviewFree);
-
-    view->chip    = -1;
-    view->cell    = -1;
-    view->readout = -1;
-    view->iRows   =  0;
-    view->nRows   = nRows;
-    return (view);
-}
-
-pmFPAdepth pmFPAviewDepth (const pmFPAview *view)
-{
-
-    if (view->chip < 0) {
-        return PM_FPA_DEPTH_FPA;
-    }
-    if (view->cell < 0) {
-        return PM_FPA_DEPTH_CHIP;
-    }
-    if (view->readout < 0) {
-        return PM_FPA_DEPTH_CELL;
-    }
-    return PM_FPA_DEPTH_READOUT;
-}
-
-pmChip *pmFPAviewThisChip (const pmFPAview *view, pmFPA *fpa)
-{
-
-    if (view->chip < 0) {
-        return NULL;
-    }
-
-    if (view->chip >= fpa->chips->n) {
-        return NULL;
-    }
-
-    pmChip *chip = fpa->chips->data[view->chip];
-    return chip;
-}
-
-pmChip *pmFPAviewNextChip (pmFPAview *view, pmFPA *fpa, int nStep)
-{
-    view->cell = -1;
-    view->readout = -1;
-    view->iRows = 0;
-
-    // if there are no available chips, return NULL
-    if (fpa->chips->n <= 0) {
-        view->chip = -1;
-        return NULL;
-    }
-
-    // clean up < -1 values
-    if (view->chip < -1) {
-        view->chip = -1;
-    }
-
-    // increment to the next chip
-    view->chip += nStep;
-
-    // if we are at the end of the stack, return NULL
-    if (view->chip >= fpa->chips->n) {
-        view->chip = -1;
-        return NULL;
-    }
-
-    // get the correct chip pointer
-    pmChip *chip = fpa->chips->data[view->chip];
-    return (chip);
-}
-
-pmCell *pmFPAviewThisCell (const pmFPAview *view, pmFPA *fpa)
-{
-
-    if (view->cell < 0) {
-        return NULL;
-    }
-
-    pmChip *chip = pmFPAviewThisChip (view, fpa);
-    if (chip == NULL) {
-        return NULL;
-    }
-
-    if (view->cell >= chip->cells->n) {
-        return NULL;
-    }
-
-    pmCell *cell = chip->cells->data[view->cell];
-    return cell;
-}
-
-pmCell *pmFPAviewNextCell (pmFPAview *view, pmFPA *fpa, int nStep)
-{
-
-    pmChip *chip = pmFPAviewThisChip (view, fpa);
-    if (chip == NULL) {
-        return NULL;
-    }
-
-    view->readout = -1;
-    view->iRows = 0;
-
-    // if there are no available cells, return NULL
-    if (chip->cells->n <= 0) {
-        view->cell = -1;
-        return NULL;
-    }
-
-    // clean up < -1 values
-    if (view->cell < -1) {
-        view->cell = -1;
-    }
-
-    // increment to the next cell
-    view->cell += nStep;
-
-    // if we are at the end of the stack, return NULL
-    if (view->cell >= chip->cells->n) {
-        view->cell = -1;
-        return NULL;
-    }
-
-    // get the correct cell pointer
-    pmCell *cell = chip->cells->data[view->cell];
-    return (cell);
-}
-
-pmReadout *pmFPAviewThisReadout (const pmFPAview *view, pmFPA *fpa)
-{
-
-    if (view->readout < 0) {
-        return NULL;
-    }
-
-    pmCell *cell = pmFPAviewThisCell (view, fpa);
-    if (cell == NULL) {
-        return NULL;
-    }
-
-    if (view->readout >= cell->readouts->n) {
-        return NULL;
-    }
-
-    pmReadout *readout = cell->readouts->data[view->readout];
-    return readout;
-}
-
-pmReadout *pmFPAviewNextReadout (pmFPAview *view, pmFPA *fpa, int nStep)
-{
-
-    pmCell *cell = pmFPAviewThisCell (view, fpa);
-    if (cell == NULL) {
-        return NULL;
-    }
-
-    view->iRows = 0;
-
-    // if there are no available cells, return NULL
-    if (cell->readouts->n <= 0) {
-        view->readout = -1;
-        return NULL;
-    }
-
-    // clean up < -1 values
-    if (view->readout < -1) {
-        view->readout = -1;
-    }
-
-    // increment to the next cell
-    view->readout += nStep;
-
-    // if we are at the end of the stack, return NULL
-    if (view->readout >= cell->readouts->n) {
-        view->readout = -1;
-        return NULL;
-    }
-
-    // get the correct cell pointer
-    pmReadout *readout = cell->readouts->data[view->readout];
-    return (readout);
-}
-
-pmHDU *pmFPAviewThisHDU (const pmFPAview *view, pmFPA *fpa)
-{
-    // the HDU is attached to a cell, chip or fpa
-    // if this view has a -1 for the level which contains the hdu,
-    // there is no unambiguous HDU
-
-    if (view->chip < 0) {
-        return pmHDUFromFPA (fpa);
-    }
-    if (view->cell < 0) {
-        return pmHDUFromChip (pmFPAviewThisChip (view, fpa));
-    }
-    if (view->readout < 0) {
-        return pmHDUFromCell (pmFPAviewThisCell (view, fpa));
-    }
-    return pmHDUFromReadout (pmFPAviewThisReadout (view, fpa));
-}
-
-pmHDU *pmFPAviewThisPHU (const pmFPAview *view, pmFPA *fpa)
-{
-    // select the HDU which corresponds to the PHU containing this view
-
-    pmHDU *hdu;
-    pmFPAview new;
-    pmChip *chip;
-    pmCell *cell;
-
-    new = *view;
-
-    if (view->chip < 0) {
-        hdu = pmHDUFromFPA (fpa);
-        if (hdu->phu)
-            return hdu;
-        return NULL;
-    }
-    if (view->cell < 0) {
-        chip = pmFPAviewThisChip (view, fpa);
-        hdu  = pmHDUFromChip (chip);
-        if (hdu->phu)
-            return hdu;
-        new.chip = -1;
-        hdu = pmFPAviewThisPHU (&new, fpa);
-        return hdu;
-    }
-    if (view->readout < 0) {
-        cell = pmFPAviewThisCell (view, fpa);
-        hdu  = pmHDUFromCell (cell);
-        if (hdu->phu)
-            return hdu;
-        new.cell = -1;
-        hdu = pmFPAviewThisPHU (&new, fpa);
-        return hdu;
-    }
-    return NULL;
-}
Index: unk/psModules/src/astrom/pmFPAview.h
===================================================================
--- /trunk/psModules/src/astrom/pmFPAview.h	(revision 7018)
+++ 	(revision )
@@ -1,70 +1,0 @@
-/** @file  pmFPAview.h
-*
-*  @brief Tools to manipulate the FPA structure elements.
-*
-*  @ingroup AstroImage
-*
-*  @author EAM, IfA
-*
-*  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
-*  @date $Date: 2006-04-17 18:01:04 $
-*
-*  Copyright 2004-2005 Institute for Astronomy, University of Hawaii
-*/
-
-#ifndef PM_FPA_VIEW_H
-#define PM_FPA_VIEW_H
-
-#include "pmFPA.h"
-
-/// @addtogroup AstroImage
-/// @{
-
-// depth of interest
-typedef enum {
-    PM_FPA_DEPTH_NONE,
-    PM_FPA_DEPTH_FPA,
-    PM_FPA_DEPTH_CHIP,
-    PM_FPA_DEPTH_CELL,
-    PM_FPA_DEPTH_READOUT,
-} pmFPAdepth;
-
-typedef struct
-{
-    int chip;                           // Number of the chip, or -1 for all
-    int cell;                           // Number of the cell, or -1 for all
-    int readout;                        // Number of the readout, or -1 for all
-    int nRows;                          // Maximum number of rows per readout segment read, or 0 for all
-    int iRows;                          // Starting point for this read
-}
-pmFPAview;
-
-// allocate a pmFPAview structure for this fpa and camera
-pmFPAview *pmFPAviewAlloc (int nRows);
-
-// determine the current view depth
-pmFPAdepth pmFPAviewDepth (const pmFPAview *view);
-
-// return the currently selected chip for this view
-pmChip *pmFPAviewThisChip (const pmFPAview *view, pmFPA *fpa);
-
-// advance view to the next chip
-pmChip *pmFPAviewNextChip (pmFPAview *view, pmFPA *fpa, int nStep);
-
-// return the currently selected cell for this view
-pmCell *pmFPAviewThisCell (const pmFPAview *view, pmFPA *fpa);
-
-// advance view to the next cell
-pmCell *pmFPAviewNextCell (pmFPAview *view, pmFPA *fpa, int nStep);
-
-// return the currently selected readout for this view
-pmReadout *pmFPAviewThisReadout (const pmFPAview *view, pmFPA *fpa);
-
-// advance view to the next readout
-pmReadout *pmFPAviewNextReadout (pmFPAview *view, pmFPA *fpa, int nStep);
-
-// return the HDU corresponding to the current view
-pmHDU *pmFPAviewThisHDU (const pmFPAview *view, pmFPA *fpa);
-pmHDU *pmFPAviewThisPHU (const pmFPAview *view, pmFPA *fpa);
-
-# endif
Index: unk/psModules/src/astrom/pmHDU.c
===================================================================
--- /trunk/psModules/src/astrom/pmHDU.c	(revision 7018)
+++ 	(revision )
@@ -1,225 +1,0 @@
-#include <stdio.h>
-#include <assert.h>
-
-#include "pslib.h"
-#include "pmFPA.h"
-#include "psAdditionals.h"
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// File-static (private) functions
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Move to the appropriate extension in FITS file for HDU
-static bool hduMove(pmHDU *hdu,         // HDU with extname
-                    psFits *fits        // FITS file in which to move
-                   )
-{
-    // Deal with the PHU case
-    if (strcasecmp(hdu->extname, "PHU") == 0 || hdu->phu) {
-        if (! psFitsMoveExtNum(fits, 0, false)) {
-            psError(PS_ERR_IO, false, "Unable to move to primary header!\n");
-            return false;
-        }
-        hdu->phu = true;
-        return true;
-    }
-
-    if (! psFitsMoveExtName(fits, hdu->extname)) {
-        psError(PS_ERR_IO, false, "Unable to move to extension %s\n", hdu->extname);
-        return false;
-    }
-    // Now, just in case for some reason the PHU has an extension name that we've moved to....
-    if (psFitsGetExtNum(fits) == 0) {
-        hdu->phu = true;
-    } else {
-        hdu->phu = false;
-    }
-
-    return true;
-}
-
-static void hduFree(pmHDU *hdu)
-{
-    psFree(hdu->extname);
-    psFree(hdu->format);
-    psFree(hdu->header);
-    psFree(hdu->images);
-    psFree(hdu->table);
-    psFree(hdu->weights);
-    psFree(hdu->masks);
-}
-
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// Public functions
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-pmHDU *pmHDUAlloc(const char *extname   // Extension name
-                 )
-{
-    pmHDU *hdu = psAlloc(sizeof(pmHDU));
-    psMemSetDeallocator(hdu, (psFreeFunc)hduFree);
-
-    if (! extname || strlen(extname) == 0) {
-        hdu->phu = true;
-        hdu->extname = psStringCopy("PHU");
-    } else {
-        if (strcasecmp(extname, "PHU") == 0) {
-            hdu->phu = true;
-        } else {
-            hdu->phu = false;
-        }
-        hdu->extname = psStringCopy(extname);
-    }
-    hdu->format  = NULL;
-    hdu->header  = NULL;
-    hdu->images  = NULL;
-    hdu->table   = NULL;
-    hdu->weights = NULL;
-    hdu->masks   = NULL;
-
-    return hdu;
-}
-
-bool pmHDUReadHeader(pmHDU *hdu,        // HDU for which to read header
-                     psFits *fits       // FITS file from which to read
-                    )
-{
-    assert(hdu);
-    assert(fits);
-
-    // Move to the appropriate extension
-    psTrace(__func__, 5, "Moving to extension %s...\n", hdu->extname);
-    if (!hduMove(hdu, fits)) {
-        return false;
-    }
-
-    if (!hdu->header) {
-        psTrace(__func__, 5, "Reading the header...\n");
-        hdu->header = psFitsReadHeader(NULL, fits);
-        if (! hdu->header) {
-            psError(PS_ERR_IO, false, "Unable to read header for extension %s\n", hdu->extname);
-            return false;
-        }
-    }
-
-    return true;
-}
-
-// Read the HDU
-// XXX: Add a region specifier?
-bool pmHDURead(pmHDU *hdu,              // HDU to read
-               psFits *fits             // FITS file to read from
-              )
-{
-    assert(hdu);
-    assert(fits);
-
-    // Read the header; includes the move
-    if (!pmHDUReadHeader(hdu, fits)) {
-        return false;
-    }
-
-    #ifdef FITS_TABLES
-    // What type is it?
-    if (psFitsIsImage(hdu->header)) {
-        #endif
-        if (hdu->images) {
-            psLogMsg(__func__, PS_LOG_WARN, "HDU %s has already been read --- overwriting.\n", hdu->extname);
-            psFree(hdu->images);        // Blow away anything existing
-        }
-        psTrace(__func__, 5, "Reading the pixels...\n");
-        hdu->images = psFitsReadImageCube(fits, psRegionSet(0,0,0,0));
-        if (! hdu->images) {
-            psError(PS_ERR_IO, false, "Unable to read pixels for extension %s\n", hdu->extname);
-            return false;
-        }
-        return true;
-        #ifdef FITS_TABLES
-
-    }
-    if (psFitsIsTable(hdu->table)) {
-        // Read the table
-        if (hdu->table) {
-            psFree(hdu->table);         // Blow away anything existing
-        }
-        hdu->table = psFitsReadTable(fits);
-        return true;
-    }
-
-    psError(PS_ERR_UNKNOWN, true, "No idea what this HDU consists of!\n");
-    return false;
-    #endif
-}
-
-// Write the HDU
-// XXX: Add a region specifier?
-bool pmHDUWrite(pmHDU *hdu,             // HDU to write
-                psFits *fits            // FITS file to write to
-               )
-{
-    assert(hdu);
-    assert(fits);
-
-    psTrace(__func__, 7, "Writing HDU %s\n", hdu->extname);
-
-    if (hdu->images && hdu->table && !hdu->header) {
-        psError(PS_ERR_IO, true, "Both image and table data provided in HDU, but no header --- "
-                "don't know which to write!\n");
-        return false;
-    }
-
-    if (!hdu->images && !hdu->table && !hdu->header) {
-        psLogMsg(__func__, PS_LOG_WARN, "Nothing to write for HDU %s\n", hdu->extname);
-        return false;
-    }
-
-    // Preserve the extension name, if it's the PHU
-    char *extname = hdu->extname;       // The name of the extension
-    if (strcasecmp(extname, "PHU") == 0 && hdu->header) {
-        bool mdok = true;               // Status of MD lookup
-        extname = psMetadataLookupStr(&mdok, hdu->header, "EXTNAME");
-        if (!mdok || !extname || strlen(extname) == 0) {
-            extname = "";
-        }
-    }
-
-    // Only a header
-    if (!hdu->images && !hdu->table) {
-        // Tell CFITSIO there's nothing there
-        psMetadataItem *naxis = psMetadataLookup(hdu->header, "NAXIS");
-        if (naxis) {
-            naxis->data.S32 = 0;
-        }
-
-        if (!psFitsWriteHeader(hdu->header, fits)) {
-            psError(PS_ERR_IO, false, "Unable to write header for extension %s\n", extname);
-        }
-    }
-
-    #ifdef FITS_TABLES
-    if (hdu->images && (!hdu->table || psFitsIsImage(hdu->header))) {
-        psFitsWriteImageCube(fits, hdu->header, hdu->images, extname);
-        return true;
-    }
-
-    if (hdu->table && (!hdu->images || psFitsIsTable(hdu->header))) {
-        bool psFitsWriteTable(psFits* fits, const psMetadata *header, const psArray* table);
-        return true;
-    }
-
-    psError(PS_ERR_IO, true, "No idea what this HDU consists of!\n");
-    return false;
-    #else
-
-    if (hdu->images) {
-        psTrace(__func__, 9, "Writing pixels for %s\n", hdu->extname);
-        if (!psFitsWriteImageCube(fits, hdu->header, hdu->images, extname)) {
-            psError(PS_ERR_IO, false, "Unable to write image to extension %s\n", hdu->extname);
-            return false;
-        }
-    }
-    return true;
-    #endif
-}
-
Index: unk/psModules/src/astrom/pmHDU.h
===================================================================
--- /trunk/psModules/src/astrom/pmHDU.h	(revision 7018)
+++ 	(revision )
@@ -1,37 +1,0 @@
-#ifndef PM_HDU_H
-#define PM_HDU_H
-
-// An instance of the FITS Header Data Unit
-typedef struct
-{
-    psString extname;                   // The extension name
-    bool phu;                           // Is this the FITS Primary Header Unit
-    psMetadata *format;                 // The camera format
-    psMetadata *header;                 // The FITS header, or NULL if primary for FITS; or section info
-    psArray *images;                    // The pixel data
-    psArray *weights;                   // The pixel data
-    psArray *masks;                     // The pixel data
-    psArray *table;                     // The table data
-}
-pmHDU;
-
-
-// Allocators
-pmHDU *pmHDUAlloc(const char *extname);
-
-// Read the header
-bool pmHDUReadHeader(pmHDU *hdu,        // HDU for which to read header
-                     psFits *fits       // FITS file from which to read
-                    );
-
-// Read the HDU
-bool pmHDURead(pmHDU *hdu,              // HDU to read
-               psFits *fits             // FITS file to read from
-              );
-
-// Write the HDU
-bool pmHDUWrite(pmHDU *hdu,             // HDU to write
-                psFits *fits            // FITS file to write to
-               );
-
-#endif
Index: unk/psModules/src/astrom/pmHDUUtils.c
===================================================================
--- /trunk/psModules/src/astrom/pmHDUUtils.c	(revision 7018)
+++ 	(revision )
@@ -1,60 +1,0 @@
-#include <stdio.h>
-#include "pmFPA.h"
-#include "pmHDU.h"
-#include "pmHDUUtils.h"
-
-
-pmHDU *pmHDUFromFPA(pmFPA *fpa          // FPA for which to find HDU
-                   )
-{
-    return fpa->hdu;
-}
-
-pmHDU *pmHDUFromChip(pmChip *chip       // Chip for which to find HDU
-                    )
-{
-    pmHDU *hdu = chip->hdu;             // The HDU information
-    if (!hdu) {
-        hdu = pmHDUFromFPA(chip->parent); // Grab HDU info from the FPA
-    }
-
-    return hdu;
-}
-
-pmHDU *pmHDUFromCell(pmCell *cell       // Cell for which to find HDU
-                    )
-{
-    pmHDU *hdu = cell->hdu;             // The HDU information
-    if (!hdu) {
-        hdu = pmHDUFromChip(cell->parent); // Grab HDU info from the chip
-    }
-
-    return hdu;
-}
-
-pmHDU *pmHDUFromReadout (pmReadout *readout)
-{
-
-    pmCell *cell = readout->parent; // cell containing this readout;
-    pmHDU *hdu = pmHDUFromCell (cell);
-    return hdu;
-}
-
-// Get the lowest HDU
-pmHDU *pmHDUGetLowest(pmFPA *fpa, // The FPA
-                      pmChip *chip, // The chip, or NULL
-                      pmCell *cell // The cell, or NULL
-                     )
-{
-    pmHDU *hdu = NULL;          // The HDU that's at the lowest level
-    if (cell) {
-        hdu = pmHDUFromCell(cell);
-    } else if (chip) {
-        hdu = pmHDUFromChip(chip);
-    } else if (fpa) {
-        hdu = pmHDUFromFPA(fpa);
-    }
-
-    return hdu;
-}
-
Index: unk/psModules/src/astrom/pmHDUUtils.h
===================================================================
--- /trunk/psModules/src/astrom/pmHDUUtils.h	(revision 7018)
+++ 	(revision )
@@ -1,24 +1,0 @@
-#ifndef PM_HDU_UTILS_H
-#define PM_HDU_UTILS_H
-
-#include "pmFPA.h"
-#include "pmHDU.h"
-
-// Get the lowest HDU in the hierarchy, as supplied
-pmHDU *pmHDUGetLowest(pmFPA *fpa, // The FPA
-                      pmChip *chip, // The chip, or NULL
-                      pmCell *cell // The cell, or NULL
-                     );
-
-// Find the HDU in the FPA hierarchy
-pmHDU *pmHDUFromFPA(pmFPA *fpa          // FPA for which to find HDU
-                   );
-pmHDU *pmHDUFromChip(pmChip *chip       // Chip for which to find HDU
-                    );
-pmHDU *pmHDUFromCell(pmCell *cell       // Cell for which to find HDU
-                    );
-
-pmHDU *pmHDUFromReadout (pmReadout *readout  // Readout for which to find HDU
-                        );
-
-#endif
Index: unk/psModules/src/astrom/pmReadout.c
===================================================================
--- /trunk/psModules/src/astrom/pmReadout.c	(revision 7018)
+++ 	(revision )
@@ -1,111 +1,0 @@
-#include <stdio.h>
-#include "pslib.h"
-#include "pmFPA.h"
-#include "pmMaskBadPixels.h"
-#include "pmHDUUtils.h"
-
-// Get the bias images for a readout, using the CELL.BIASSEC
-psList *pmReadoutGetBias(pmReadout *readout // Readout for which to get the bias sections
-                        )
-{
-    pmCell *cell = readout->parent;     // The parent cell
-    psList *sections = (psList*)psMetadataLookupPtr(NULL, cell->concepts, "CELL.BIASSEC"); // CELL.BIASSEC
-
-    psImage *image = readout->image;    // The image that contains both the biassec and the trimsec
-
-    psList *images = psListAlloc(NULL); // List of images from bias sections
-    psListIterator *sectionsIter = psListIteratorAlloc(sections, PS_LIST_HEAD, true); // Iterator
-    psRegion *region = NULL;            // Bias region from list
-    while ((region = psListGetAndIncrement(sectionsIter))) {
-        psImage *bias = psMemIncrRefCounter(psImageSubset(image, *region)); // Image from bias section
-        psListAdd(images, PS_LIST_TAIL, bias);
-        psFree(bias);
-    }
-    psFree(sectionsIter);
-
-    return images;
-}
-
-bool pmReadoutSetWeights(pmReadout *readout)
-{
-
-    pmCell *cell = readout->parent;
-
-    float gain = psMetadataLookupF32(NULL, cell->concepts, "CELL.GAIN"); // Cell gain
-    float readnoise = psMetadataLookupF32(NULL, cell->concepts, "CELL.READNOISE"); // Cell read noise
-    psRegion *trimsec = psMetadataLookupPtr(NULL, cell->concepts, "CELL.TRIMSEC"); // Trim section
-    float saturation = psMetadataLookupF32(NULL, cell->concepts, "CELL.SATURATION"); // Saturation level
-    float bad = psMetadataLookupF32(NULL, cell->concepts, "CELL.BAD"); // Bad level
-
-    pmHDU *hdu = pmHDUFromCell (cell);
-
-    psArray *pixels = hdu->images;      // Array of images
-    psArray *weights = hdu->weights;    // Array of weight images
-    psArray *masks = hdu->masks;        // Array of mask images
-    // Generate the weights and masks if required
-    if (! weights) {
-        weights = psArrayAlloc(pixels->n);
-        weights->n = pixels->n;
-        for (int i = 0; i < pixels->n; i++) {
-            psImage *image = pixels->data[i];
-            weights->data[i] = psImageAlloc(image->numCols, image->numRows, PS_TYPE_F32);
-            psImageInit(weights->data[i], 0.0);
-        }
-        hdu->weights = weights;
-    }
-    if (! masks) {
-        masks = psArrayAlloc(pixels->n);
-        masks->n = pixels->n;
-        for (int i = 0; i < pixels->n; i++) {
-            psImage *image = pixels->data[i];
-            masks->data[i] = psImageAlloc(image->numCols, image->numRows, PS_TYPE_U8);
-            psImageInit(masks->data[i], 0);
-        }
-        hdu->masks = masks;
-    }
-
-    // Set the pixels
-    psArray *readouts = cell->readouts; // Array of readouts
-    for (int i = 0; i < readouts->n; i++) {
-        pmReadout *readout = readouts->data[i]; // The readout of interest
-
-        if (! readout->weight) {
-            readout->weight = psMemIncrRefCounter(psImageSubset(weights->data[i], *trimsec));
-        }
-        if (! readout->mask) {
-            readout->mask = psMemIncrRefCounter(psImageSubset(masks->data[i], *trimsec));
-        }
-
-        // Set up the mask
-        psImage *image = readout->image;// Pixels
-        psImage *mask = readout->mask;  // Mask image
-        for (int i = 0; i < image->numRows; i++) {
-            for (int j = 0; j < image->numCols; j++) {
-                if (image->data.F32[i][j] >= saturation) {
-                    mask->data.U8[i][j] = PM_MASK_SAT;
-                }
-                if (image->data.F32[i][j] <= bad) {
-                    mask->data.U8[i][j] = PM_MASK_BAD;
-                }
-            }
-        }
-
-        psImage *weight = readout->weight;  // Mask image
-        float rnoise = PS_SQR(readnoise/gain);
-        for (int i = 0; i < image->numRows; i++) {
-            for (int j = 0; j < image->numCols; j++) {
-                if (!mask->data.U8[i][j]) {
-                    weight->data.F32[i][j] = image->data.F32[i][j]/ gain + rnoise;
-                }
-            }
-        }
-
-        // Set weight image to the variance = g*f + rn^2
-        // psBinaryOp(readout->weight, image, "/", psScalarAlloc(gain, PS_TYPE_F32));
-        // psBinaryOp(readout->weight, readout->weight, "+",
-        // psScalarAlloc(readnoise*readnoise/gain/gain, PS_TYPE_F32));
-    }
-
-    return true;
-}
-
Index: unk/psModules/src/astrom/pmReadout.h
===================================================================
--- /trunk/psModules/src/astrom/pmReadout.h	(revision 7018)
+++ 	(revision )
@@ -1,13 +1,0 @@
-#ifndef PM_READOUT_H
-#define PM_READOUT_H
-
-#include "pslib.h"
-#include "pmFPA.h"
-
-// Get the bias sections for a specific readout
-psList *pmReadoutGetBias(pmReadout *readout // Readout for which to get the bias sections
-                        );
-
-bool pmReadoutSetWeights(pmReadout *readout);
-
-#endif
Index: /trunk/psModules/src/imcombine/Makefile.am
===================================================================
--- /trunk/psModules/src/imcombine/Makefile.am	(revision 7018)
+++ /trunk/psModules/src/imcombine/Makefile.am	(revision 7019)
@@ -3,9 +3,13 @@
 libpsmoduleimcombine_la_CPPFLAGS = $(SRCINC) $(PSMODULE_CFLAGS)
 libpsmoduleimcombine_la_LDFLAGS  = -release $(PACKAGE_VERSION)
-libpsmoduleimcombine_la_SOURCES  = pmImageCombine.c
+
+libpsmoduleimcombine_la_SOURCES  = \
+     pmImageCombine.c \
+     pmImageSubtract.c
 #    pmReadoutCombine.c
 
 psmoduleincludedir = $(includedir)
 psmoduleinclude_HEADERS = \
-    pmImageCombine.h
+    pmImageCombine.h \
+    pmImageSubtract.h
 #    pmReadoutCombine.h
Index: /trunk/psModules/src/imsubtract/Makefile.am
===================================================================
--- /trunk/psModules/src/imsubtract/Makefile.am	(revision 7018)
+++ /trunk/psModules/src/imsubtract/Makefile.am	(revision 7019)
@@ -3,11 +3,14 @@
 libpsmoduleimsubtract_la_CPPFLAGS = $(SRCINC) $(PSMODULE_CFLAGS)
 libpsmoduleimsubtract_la_LDFLAGS  = -release $(PACKAGE_VERSION)
-libpsmoduleimsubtract_la_SOURCES  = pmImageSubtract.c \
-    pmSubtractBias.c
-#    pmSubtractSky.c
+libpsmoduleimsubtract_la_SOURCES  = 
+
+# pmImageSubtract.c \
+#  pmSubtractBias.c
+#   pmSubtractSky.c
 
 psmoduleincludedir = $(includedir)
-psmoduleinclude_HEADERS = \
-    pmImageSubtract.h \
-    pmSubtractBias.h
+psmoduleinclude_HEADERS = 
+
+#    pmImageSubtract.h \
+#    pmSubtractBias.h
 #    pmSubtractSky.h
Index: unk/psModules/src/imsubtract/pmImageSubtract.c
===================================================================
--- /trunk/psModules/src/imsubtract/pmImageSubtract.c	(revision 7018)
+++ 	(revision )
@@ -1,1407 +1,0 @@
-/** @file  ImageSubtract.c
- *
- *  This file will contain code which creates a set of kernel basis
- *  functions, solves for their solution, and applies them to an image.
- *
- *  @author Paul Price, IfA (original prototype)
- *  @author GLG, MHPCC
- *
- *  @version $Revision: 1.2 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2006-03-04 01:01:33 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- *   XXX: sync with IfA on this:
- *   The (x, y) (row, col) issue is becoming a problem.  In this file, and I
- *   think, the rest of psLib and psModules, the following conventions are used:
- *
- * 1) x will correspond to the column, and y will correspond to the row.
- * 2) When used in function prototypes, the column (and hence x) appears
- *    first.
- * 3) When used to index 2-D arrays, obviously, the row (and hence, y)
- *    appears first. (2 and 3 are the source of confusion).
- * 4) When (u, v) are used in certain structures.
- *  u corresponds to x
- *  v corresponds to y
- * 5) When element (a, b) is casually referred to in comments, or
- *    documentation it is unclear where a is the row, or the column.
- * 6) A convention on loop index variables (i, j) would be convenient.
- *    Currently, sometimes i corresponds to the column (x),
- *    usually it corresponds to the row (y).
- *
- *  XXX: The following variables are used an interpreted this way:
- * kernelSize: Means that the actual kernel is a square (1 + 2 * kernelSize) per side.
- * border: When accessing an image, a swath of pixels this wide is ignored.
- * footprint: When accessing a stample, a square of pixels, footprint pixels per side,
- *  are looked at.  We must ensure that (footprint+kernelSize) pixels exist
- *  around the center.
- */
-
-#include<stdio.h>
-#include<math.h>
-#include "pslib.h"
-#include "psConstants.h"
-#include "pmImageSubtract.h"
-
-/*******************************************************************************
-    Private alloc/free functions.
-XXX: It's not clear if the SubtractionKernels alloc/free functions are feasable.
- ******************************************************************************/
-void p_pmStampFree(pmStamp *stamp)
-{
-    psFree(stamp->matrix);
-    psFree(stamp->vector);
-}
-
-pmStamp *p_pmStampAlloc(pmStampStatus status)
-{
-    pmStamp *stamp = (pmStamp*)psAlloc(sizeof(pmStamp));
-    stamp->x = 0;
-    stamp->p_xSize = 0;
-    stamp->y = 0;
-    stamp->p_ySize = 0;
-    stamp->matrix = NULL;
-    stamp->vector = NULL;
-    stamp->status = status;
-
-    psMemSetDeallocator(stamp, (psFreeFunc)p_pmStampFree);
-
-    return(stamp);
-}
-
-void p_pmSubtractionKernelsFree(psSubtractionKernels *kernels)
-{
-    psFree(kernels->u);
-    psFree(kernels->v);
-    psFree(kernels->sigma);
-    psFree(kernels->xOrder);
-    psFree(kernels->yOrder);
-    psFree(kernels->preCalc);
-
-    psFree(kernels);
-}
-
-psSubtractionKernels *p_pmSubtractionKernelsAlloc(int numBasisFunctions,
-        pmSubtractionKernelsType type)
-{
-    psSubtractionKernels *tmp = (psSubtractionKernels *) psAlloc(sizeof(psSubtractionKernels));
-
-    tmp->type = type;
-    psMemSetDeallocator(tmp, (psFreeFunc) p_pmSubtractionKernelsFree);
-    return(tmp);
-}
-
-/*******************************************************************************
-psSubtractionKernels struct.
- ******************************************************************************/
-psSubtractionKernels *pmSubtractionKernelsAllocPOIS(int size,
-        int spatialOrder)
-{
-    psTrace("ImageSubtract.pmSubtractionKernelsAllocPOIS", 3,
-            "Calling pmSubtractionKernelsAllocPOIS(%d, %d)\n", size, spatialOrder);
-    PS_ASSERT_INT_POSITIVE(size, NULL);
-    PS_ASSERT_INT_POSITIVE(spatialOrder, NULL);
-    //
-    // Calculate the number of basis functions (nBF)
-    //
-    psS32 xKernelHalfSize = size;
-    psS32 yKernelHalfSize = size;
-    psS32 nBF = (2 * xKernelHalfSize + 1) *
-                (2 * yKernelHalfSize + 1) *
-                (spatialOrder + 1) *
-                (spatialOrder + 2) / 2;
-
-    //
-    // Generate the new psSubtractionKernels data structure:
-    //
-    psSubtractionKernels *tmp = (psSubtractionKernels *) psAlloc(sizeof(psSubtractionKernels));
-    tmp->type = PM_SUBTRACTION_KERNEL_POIS;
-    tmp->u = psVectorAlloc(nBF, PS_TYPE_F32);
-    tmp->v = psVectorAlloc(nBF, PS_TYPE_F32);
-    tmp->u->n = tmp->u->nalloc;
-    tmp->v->n = tmp->v->nalloc;
-    tmp->sigma = NULL;
-    tmp->xOrder = psVectorAlloc(nBF, PS_TYPE_F32);
-    tmp->xOrder->n = tmp->xOrder->nalloc;
-    tmp->yOrder = psVectorAlloc(nBF, PS_TYPE_F32);
-    tmp->yOrder->n = tmp->yOrder->nalloc;
-    tmp->subIndex = 0;
-    tmp->preCalc = NULL;
-    tmp->size = size;
-    tmp->spatialOrder = spatialOrder;
-
-    //
-    // This corresponds to the Kernel Basis Function with (u, v) = (0, 0)
-    //
-    // Put the (u,v) = (0,0) component right at the start of the list
-    // for convenience.
-    //
-    psS32 ptr = 0;
-    for (psS32 order = 0; order <= spatialOrder; order++) {
-        for (psS32 xOrder = 0; xOrder <= order; xOrder++) {
-            psS32 yOrder = order - xOrder;
-            tmp->u->data.F32[ptr] = 0;
-            tmp->v->data.F32[ptr] = 0;
-            tmp->xOrder->data.F32[ptr] = xOrder;
-            tmp->yOrder->data.F32[ptr] = yOrder;
-            ptr++;
-        }
-    }
-
-    //
-    // Iterate over (u,v).  Generate a set of kernels for each (u, v).
-    //
-    for (psS32 v = -yKernelHalfSize; v <= yKernelHalfSize; v++) {
-        for (psS32 u = -xKernelHalfSize; u <= xKernelHalfSize; u++) {
-            // Already did (u,v) = (0,0): it's at the start, so skip it now.
-            if ((u != 0) || (v != 0)) {
-                //
-                // Iterate over spatial order.  This loop creates the terms for
-                // x^xOrder * y^yOrder  such that (xOrder+yOrder) <= spatialOrder.
-                //
-                for (psS32 order = 0; order <= spatialOrder; order++) {
-                    for (psS32 xOrder = 0; xOrder <= order; xOrder++) {
-                        psS32 yOrder = order - xOrder;
-                        tmp->u->data.F32[ptr] = u;
-                        tmp->v->data.F32[ptr] = v;
-                        tmp->xOrder->data.F32[ptr] = xOrder;
-                        tmp->yOrder->data.F32[ptr] = yOrder;
-                        ptr++;
-                    }
-                }
-            }
-        }
-    }
-
-    psTrace("ImageSubtract.pmSubtractionKernelsAllocPOIS", 3,
-            "Exiting pmSubtractionKernelsAllocPOIS(%d, %d)\n", size, spatialOrder);
-    return(tmp);
-}
-
-/*******************************************************************************
-XXX: Get the types correct (u, v, xOrder, yOrder).
- 
-XXX: Code review this.
- ******************************************************************************/
-psSubtractionKernels *pmSubtractionKernelsAllocISIS(const psVector *sigmas,
-        const psVector *orders,
-        int size,
-        int spatialOrder)
-{
-    PS_ASSERT_VECTOR_NON_NULL(sigmas, NULL);
-    PS_ASSERT_VECTOR_NON_NULL(orders, NULL);
-    psTrace("ImageSubtract.pmSubtractionKernelsAllocISIS", 3,
-            "Calling pmSubtractionKernelsAllocISIS(%d, %d, %d, %d)\n",
-            sigmas->n, orders->n, size, spatialOrder);
-    PS_ASSERT_INT_POSITIVE(size, NULL);
-    PS_ASSERT_INT_POSITIVE(spatialOrder, NULL);
-    PS_ASSERT_VECTOR_TYPE(sigmas, PS_TYPE_F32, NULL);
-    PS_ASSERT_VECTOR_TYPE(orders, PS_TYPE_S32, NULL);
-    //
-    // Calculate the number of basis functions (nBF).
-    //
-    psS32 numSigmas = sigmas->n;
-
-    // XXX: Get rid of the sigma loop?  We merely multiple nBF by numSigmas?
-    // XXX: Verify that all this is correct.
-    psS32 nBF = 0;
-    for (psS32 s = 0 ; s < numSigmas ; s++) {
-        for (psS32 o = 0 ; o < orders->n ; o++) {
-            nBF+=((orders->data.S32[o] + 1) * (orders->data.S32[o] + 2) / 2);
-        }
-    }
-    nBF*= ((spatialOrder + 1) * (spatialOrder + 2) / 2);
-
-    //
-    // Generate the new psSubtractionKernels data structure:
-    //
-    psSubtractionKernels *tmp = (psSubtractionKernels *) psAlloc(sizeof(psSubtractionKernels));
-    tmp->type = PM_SUBTRACTION_KERNEL_ISIS;
-    tmp->u = psVectorAlloc(nBF, PS_TYPE_F32);
-    tmp->v = psVectorAlloc(nBF, PS_TYPE_F32);
-    tmp->u->n = tmp->u->nalloc;
-    tmp->v->n = tmp->v->nalloc;
-    tmp->sigma = psVectorAlloc(nBF, PS_TYPE_F32);
-    tmp->xOrder = psVectorAlloc(nBF, PS_TYPE_F32);
-    tmp->yOrder = psVectorAlloc(nBF, PS_TYPE_F32);
-    tmp->subIndex = 0;
-    tmp->size = size;
-    tmp->spatialOrder = spatialOrder;
-    tmp->preCalc = psArrayAlloc(nBF);
-    tmp->u->n = tmp->u->nalloc;
-    tmp->v->n = tmp->v->nalloc;
-    tmp->sigma->n = tmp->sigma->nalloc;
-    tmp->xOrder->n = tmp->xOrder->nalloc;
-    tmp->yOrder->n = tmp->yOrder->nalloc;
-    tmp->preCalc->n = tmp->preCalc->nalloc;
-
-    //
-    // We loop through all combinations of sigmas and polynomial orders
-    // creating the kernel basis functions.
-    //
-    psS32 ptr = 0;
-    for (psS32 sigPtr = 0 ; sigPtr < numSigmas ; sigPtr++) {
-        tmp->sigma->data.F32[sigPtr] = sigmas->data.F32[sigPtr];
-        //
-        // (xOrderP, yOrderP) are the order of the polynomial that modify the
-        // gaussian in the kernel.  They correspond to (j, k) in equation (5)
-        // from the psModules SDRS.
-        //
-        for (psS32 o = 0 ; o < orders->n ; o++) {
-            for (psS32 orderP = 0 ; orderP <= orders->data.S32[o] ; orderP++) {
-                for (psS32 xOrderP = 0 ; xOrderP <= orderP ; xOrderP++) {
-                    psS32 yOrderP = orderP - xOrderP;
-
-                    psImage *currPreCalc = psImageAlloc(1 + (2 * size), 1 + (2 * size), PS_TYPE_F32);
-                    PS_IMAGE_SET_F32(currPreCalc, 0.0);
-                    psBool setPreCalc = true;
-                    //
-                    // We loop through all spatial orders.  Since they have no effect on
-                    // the preCalc images, we only calculate them once, and store pointers
-                    // in tmp->preCalc->data[ptr] for other spatial orders.
-                    //
-                    for (psS32 order = 0; order <= spatialOrder; order++) {
-                        for (psS32 orderXTerm = 0; orderXTerm <= order; orderXTerm++) {
-                            PS_ASSERT_INT_LESS_THAN(ptr, nBF, NULL);
-
-                            psS32 orderYTerm = order - orderXTerm;
-
-                            tmp->u->data.F32[ptr] = xOrderP;
-                            tmp->v->data.F32[ptr] = yOrderP;
-                            tmp->xOrder->data.F32[ptr] = orderXTerm;
-                            tmp->yOrder->data.F32[ptr] = orderYTerm;
-                            tmp->sigma->data.F32[ptr] = sigmas->data.F32[sigPtr];
-                            tmp->preCalc->data[ptr] = (psPtr *) currPreCalc;
-
-                            //
-                            // We calculate the preCalc image only the first time through
-                            // this loop.  Otherwise, we increment the memory reference
-                            // counter.
-                            //
-                            if (setPreCalc == true) {
-                                for (psS32 v = -size; v <= size; v++) {
-                                    for (psS32 u = -size; u <= size; u++) {
-                                        // Scale the (u,v) coordinates in kernel space to [-1.0:1.0].
-                                        psF32 uScaled = ((psF32) u) / ((psF32) size);
-                                        psF32 vScaled = ((psF32) v) / ((psF32) size);
-
-                                        // Compute the value of the kernel at location (u, v):
-                                        psF32 exponent = (PS_SQR(uScaled) + PS_SQR(vScaled)) /
-                                                         (2.0 * PS_SQR(sigmas->data.F32[sigPtr]));
-                                        currPreCalc->data.F32[v+size][u+size] =
-                                            exp(-exponent) *
-                                            pow(uScaled, orderXTerm) *
-                                            pow(vScaled, orderYTerm);
-                                    }
-                                }
-                                setPreCalc = false;
-                            } else {
-                                psMemIncrRefCounter(currPreCalc);
-                            }
-                            ptr++;
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    psTrace("ImageSubtract.pmSubtractionKernelsAllocISIS", 3,
-            "Exiting pmSubtractionKernelsAllocISIS(%d, %d, %d, %d)\n",
-            sigmas->n, orders->n, size, spatialOrder);
-    return(tmp);
-}
-
-/*******************************************************************************
-pmSubtractionFindStamps(stamps, image, mask, maskVal, threshold, xNum, yNum, border)
- 
-XXX: The SDRS and the proptotype code differ significantly.
- Prototype: When a maximum pixel is found within a stamp, an area of size
-     2*footprint is searched around that pixel looking for masked pixels.
- SDRS: none of that is required.
- 
-XXX: Do we need to care about case where yNum/xNum does not evenly divide the
-nnumber of rows/columns in the image?
- ******************************************************************************/
-psArray *pmSubtractionFindStamps(psArray *stamps,        ///< Output stamps, or NULL
-                                 const psImage *image,   ///< Image for which to find stamps
-                                 const psImage *mask,    ///< Mask
-                                 psU32 maskVal,          ///< Value for mask
-                                 psF32 threshold,        ///< Threshold for stamps in the image
-                                 psS32 xNum,             ///< Number of stamps in x
-                                 psS32 yNum,             ///< Number of stamps in y
-                                 psS32 border            ///< Border around image to ignore (should be size of kernel or larger)
-                                )
-{
-    psTrace("ImageSubtract.pmSubtractionFindStamps", 3,
-            "Calling pmSubtractionFindStamps(%d, %f, %d, %d, %d)\n",
-            maskVal, threshold, xNum, yNum, border);
-    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
-    PS_ASSERT_IMAGE_NON_EMPTY(image, NULL);
-    PS_ASSERT_IMAGE_TYPE(image, PS_TYPE_F32, NULL);
-    if (mask != NULL) {
-        PS_ASSERT_IMAGES_SIZE_EQUAL(image, mask, NULL);
-        PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_U8, NULL);
-    }
-    PS_ASSERT_INT_POSITIVE(xNum, NULL);
-    PS_ASSERT_INT_POSITIVE(yNum, NULL);
-    PS_ASSERT_INT_POSITIVE(border, NULL);
-    PS_ASSERT_INT_LARGER_THAN(image->numCols, xNum, NULL);
-    PS_ASSERT_INT_LARGER_THAN(image->numRows, yNum, NULL);
-    PS_ASSERT_INT_LARGER_THAN(image->numCols, (2 * border), NULL);
-    PS_ASSERT_INT_LARGER_THAN(image->numRows, (2 * border), NULL);
-
-    if (stamps != NULL) {
-        PS_ASSERT_INT_EQUAL(stamps->n, (xNum * yNum), NULL);
-        //
-        // Ensure that a pmStamp struct exists at each psArray location.
-        //
-        for (psS32 s = 0 ; s < (xNum * yNum) ; s++) {
-            if (NULL == stamps->data[s]) {
-                stamps->data[s] = (psPtr *) p_pmStampAlloc(PM_STAMP_REJECTED);
-            }
-        }
-    } else {
-        stamps = (psArray *) psArrayAlloc(xNum * yNum);
-        stamps->n = stamps->nalloc;
-        for (psS32 s = 0 ; s < (xNum * yNum) ; s++) {
-            stamps->data[s] = (psPtr *) p_pmStampAlloc(PM_STAMP_REJECTED);
-        }
-    }
-    psS32 numRows = image->numRows;
-    psS32 numCols = image->numCols;
-
-    //
-    // Iterate over the image sections
-    //
-    // XXX: Must handle cases where image size is not an even multiple of xNum or yNum
-    // they are currently ignored.
-    //
-    psS32 num = 0;
-    for (psS32 j = 0; j < yNum; j++) {
-        for (psS32 i = 0; i < xNum; i++) {
-            pmStamp *stamp = (pmStamp *) stamps->data[num];
-            //
-            // Only find a new stamp if we need to
-            //
-            if (stamp->status == PM_STAMP_REJECTED) {
-                //
-                // Find maximum non-masked value in the image section,
-                // but don't include a footprint around the edge
-                //
-                psF32 max = -INFINITY;
-                psS32 bestx = 0;
-                psS32 besty = 0;
-                //
-                // The following nested loop iterates over every pixel in the mask
-                // associated with this (i, j).  It ignores pixels within a
-                // border of pixels from the image edge.
-                //
-                // XXX: verify (numX, numY), then get rid of it.
-                //
-                psS32 numX = xNum;
-                psS32 numY = yNum;
-                psS32 yMin = border + j * (numCols - 2.0 * border) / numY;
-                psS32 yMax = (border + (j + 1) * (numCols - 2.0 * border) / numY) - 1;
-                psS32 xMin = border + i * (numRows - 2.0 * border) / numX;
-                psS32 xMax = (border + (i + 1) * (numRows - 2.0 * border) / numX) - 1;
-
-                if ((yMax >= image->numRows) ||
-                        (xMax >= image->numCols) ||
-                        (yMin < 0) ||
-                        (xMin < 0)) {
-                    // XXX: We skip this stamp since its borders extends beyond the image.
-                    // XXX: This is here mainly as a safeguard.  We need to redefine the above
-                    // min/max pixels calculation to ensure that all stamps are legitimate.
-
-                    stamp->x = -1;
-                    stamp->y = -1;
-                    stamp->status = PM_STAMP_NONE;
-                } else {
-                    stamp->p_xSize = 1 + (xMax - xMin);
-                    stamp->p_ySize = 1 + (yMax - yMin);
-                    stamp->p_xMin = xMin;
-                    stamp->p_xMax = xMax;
-                    stamp->p_yMin = yMin;
-                    stamp->p_yMax = yMax;
-
-                    for (psS32 y = yMin; y <= yMax ; y++) {
-                        for (psS32 x = xMin; x <= xMax ; x++) {
-                            // Determine if this pixel is larger than the max, and unmasked.
-                            if (image->data.F32[y][x] > max) {
-                                if ((mask == NULL) || !((mask->data.U8[y][x]) & maskVal)) {
-                                    max = image->data.F32[y][x];
-                                    bestx = x;
-                                    besty = y;
-                                }
-                            }
-                        }
-                    }
-
-                    //
-                    // If the max pixel is larger than the threshold, we keep this stamp.
-                    // Otherwise, mark the stamp as PM_STAMP_NONE
-                    //
-                    if (image->data.F32[besty][bestx] >= threshold) {
-                        stamp->x = bestx;
-                        stamp->y = besty;
-                        stamp->status = PM_STAMP_RECALC;
-                    } else {
-                        stamp->x = bestx;
-                        stamp->y = besty;
-                        stamp->status = PM_STAMP_NONE;
-                    }
-                }
-            }
-            num++;
-        }
-    }
-    psTrace("ImageSubtract.pmSubtractionFindStamps", 3,
-            "Exiting pmSubtractionFindStamps(%d, %f, %d, %d, %d)\n",
-            maskVal, threshold, xNum, yNum, border);
-    return(stamps);
-}
-
-/*******************************************************************************
-GenSpatialOrder(spatialOrder, x, y): generates and returns a psImage in which
-the [i][j] location is calculated as (x^i * y^j).
- 
-XXX: Modify loop so that terms higher than spatialOrder are not computed.
- 
-XXX: Modify this so that [i][j] location is calculated as (x^j * y^i)?
- ******************************************************************************/
-static psImage *GenSpatialOrder(psS32 spatialOrder,
-                                psF32 x,
-                                psF32 y)
-{
-    psTrace("ImageSubtract.GenSpatialOrder", 4,
-            "Calling GenSpatialOrder(%d, %f, %f)\n", spatialOrder, x, y);
-
-    psImage *polyValues = psImageAlloc(spatialOrder+1, spatialOrder+1, PS_TYPE_F64);
-
-    psF64 xSum = 1.0;
-    psF64 ySum = 1.0;
-    for (psS32 i = 0; i < spatialOrder + 1; i++) {
-        ySum = xSum;
-        for (psS32 j = 0; j < spatialOrder + 1; j++) {
-            polyValues->data.F64[i][j] = ySum;
-            ySum*= y;
-        }
-        xSum*= x;
-    }
-
-    psTrace("ImageSubtract.GenSpatialOrder", 4,
-            "Exiting GenSpatialOrder(%d, %f, %f)\n", spatialOrder, x, y);
-
-    return(polyValues);
-}
-
-
-/*******************************************************************************
-IsisKernelConvolve(input, kernels, kernelID, col, row): This routine
-convolves a single kernel basis function with a pixel in an image.
-  ******************************************************************************/
-static psF32 IsisKernelConvolve(const psImage *input,
-                                const psSubtractionKernels *kernels,
-                                psS32 kernelID,
-                                psS32 col,
-                                psS32 row)
-{
-
-
-    psTrace("ImageSubtract.IsisKernelConvolve", 4,
-            "Calling IsisKernelConvolve(%d, %d, %d)\n", kernelID, col, row);
-    psS32 spatialOrder = kernels->spatialOrder;
-    psS32 kernelSize = kernels->size;
-    psS32 xOrder = (psS32) kernels->xOrder->data.F32[kernelID];
-    psS32 yOrder = (psS32) kernels->yOrder->data.F32[kernelID];
-    psF32 numColsHalf = 0.5 * (psF32) input->numCols;
-    psF32 numRowsHalf = 0.5 * (psF32) input->numRows;
-    psF32 imageX = (((psF32) col) - numColsHalf) / numColsHalf; // Normalised position
-    psF32 imageY = (((psF32) row) - numRowsHalf) / numRowsHalf; // Normalised position
-
-    psImage *polyValues = GenSpatialOrder(spatialOrder, imageX, imageY);
-
-    psF64 polyVal = polyValues->data.F64[yOrder][xOrder];
-
-    psImage *preCalc = (psImage *) kernels->preCalc->data[kernelID];
-
-    // XXX: Are the following asserts really necessary?
-    PS_ASSERT_INT_LARGER_THAN_OR_EQUAL(row-kernelSize, 0, NAN);
-    PS_ASSERT_INT_LESS_THAN(row+kernelSize, input->numRows, NAN);
-    PS_ASSERT_INT_LARGER_THAN_OR_EQUAL(col-kernelSize, 0, NAN);
-    PS_ASSERT_INT_LESS_THAN(col+kernelSize, input->numCols, NAN);
-    psF32 conv = 0.0;
-    for (psS32 yy = -kernelSize ; yy < kernelSize ; yy++) {
-        for (psS32 xx = -kernelSize ; xx < kernelSize ; xx++) {
-            conv += input->data.F32[yy+row][xx+col] *
-                    preCalc->data.F32[yy+kernelSize][xx+kernelSize] *
-                    polyVal;
-        }
-    }
-    psFree(polyValues);
-
-    psTrace("ImageSubtract.IsisKernelConvolve", 4,
-            "Exiting IsisKernelConvolve(%d, %d, %d)\n", kernelID, col, row);
-    return(conv);
-}
-
-/*******************************************************************************
-ConvolvePixelPois(input, mask, badStampMaskVal, solution, kernels, col, row):
- 
-This routine takes a single pixel in the psImage input and convolves it with
-the set of kernel basis functions and their appropriate weights in solution.
-It returns the value of the convolved pixel.
- 
-XXX: Static structure for polyValues?
- ******************************************************************************/
-static psF32 ConvolvePixelPois(const psImage *input,
-                               const psImage *mask,
-                               psU32 badStampMaskVal,
-                               const psVector *solution,
-                               const psSubtractionKernels *kernels,
-                               psS32 col,
-                               psS32 row)
-{
-    psTrace("ImageSubtract.ConvolvePixelPois", 4,
-            "Calling ConvolvePixelPois(%d, %d)\n", col, row);
-    psS32 nBF = kernels->u->n;
-    psF32 numColsHalf = 0.5 * (psF32) input->numCols;
-    psF32 numRowsHalf = 0.5 * (psF32) input->numRows;
-    psF32 background = solution->data.F64[solution->n-1];
-    psS32 spatialOrder = kernels->spatialOrder;
-    psF32 conv = background; // Initial convolved value
-
-    if ((mask == NULL) || !(mask->data.U8[row][col] & badStampMaskVal)) {
-        psF32 imageX = (((psF32) col) - numColsHalf) / numColsHalf; // Normalised position
-        psF32 imageY = (((psF32) row) - numRowsHalf) / numRowsHalf; // Normalised position
-        psImage *polyValues = GenSpatialOrder(spatialOrder, imageX, imageY);
-
-        // Iterate over the kernel basis functions
-        for (psS32 k = 0; k < nBF; k++) {
-            psS32 u = (psS32) kernels->u->data.F32[k];
-            psS32 v = (psS32) kernels->v->data.F32[k];
-
-            // XXX: What's the story with this?
-            #if 0
-
-            psS32 xOrder = (psS32) kernels->xOrder->data.F32[k];
-            psS32 yOrder = (psS32) kernels->yOrder->data.F32[k];
-            psF64 polyVal = polyValues->data.F64[yOrder][xOrder];
-            #else
-
-            psF32 polyVal = 1.0;
-            #endif
-
-            // XXX: Why this?
-            if (k == 0) {
-                conv += solution->data.F64[k] * input->data.F32[row - v][col - u] * polyVal;
-            } else {
-                conv += solution->data.F64[k] *
-                        (input->data.F32[row - v][col - u] * polyVal - input->data.F32[row][col]);
-            }
-        }
-        psFree(polyValues);
-    }
-
-    psTrace("ImageSubtract.ConvolvePixelPois", 4,
-            "Exiting ConvolvePixelPois(%d, %d)\n", col, row);
-    return(conv);
-}
-
-
-
-/*******************************************************************************
-ConvolvePixelIsis(input, mask, badStampMaskVal, solution, kernels, col, row):
- 
-This routine takes a single pixel in the psImage input and convolves it with
-the set of kernel basis functions and their appropriate weights in solution.
-It returns the value of the convolved pixel.
- 
-XXX: Static structure for polyValues?
- ******************************************************************************/
-static psF32 ConvolvePixelIsis(const psImage *input,
-                               const psImage *mask,
-                               psU32 badStampMaskVal,
-                               const psVector *solution,
-                               const psSubtractionKernels *kernels,
-                               psS32 col,
-                               psS32 row)
-{
-    psTrace("ImageSubtract.ConvolvePixelIsis", 4,
-            "Calling ConvolvePixelIsis(%d, %d)\n", col, row);
-    psF32 background = solution->data.F64[solution->n-1];
-    psF32 conv = background; // Initial convolved value
-
-    if ((mask == NULL) || !(mask->data.U8[row][col] & badStampMaskVal)) {
-        // Iterate over the kernel basis functions
-        for (psS32 k = 0; k < kernels->u->n; k++) {
-            conv += IsisKernelConvolve(input, kernels, k, col, row);
-        }
-    }
-
-    psTrace("ImageSubtract.ConvolvePixelIsis", 4,
-            "Exiting ConvolvePixelIsis(%d, %d)\n", col, row);
-    return(conv);
-}
-
-/*******************************************************************************
-ConvolveImage(input, mask, badStampMaskVal, solution, kernels): convolves an
-arbitrary image with either an ISIS or POIS set of kernel basis functions.
- ******************************************************************************/
-static psImage *ConvolveImage(const psImage *input,
-                              const psImage *mask,
-                              psU32 badStampMaskVal,
-                              const psVector *solution,
-                              const psSubtractionKernels *kernels)
-{
-    psTrace("ImageSubtract.ConvolveImage", 4, "Calling ConvolveImage()\n");
-    PS_ASSERT_IMAGE_NON_NULL(input, NULL);
-    PS_ASSERT_IMAGE_NON_EMPTY(input, NULL);
-    PS_ASSERT_IMAGE_TYPE(input, PS_TYPE_F32, NULL);
-    if (mask != NULL) {
-        PS_ASSERT_IMAGES_SIZE_EQUAL(input, mask, NULL);
-        PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_U8, NULL);
-    }
-    PS_ASSERT_VECTOR_NON_NULL(solution, NULL);
-    PS_ASSERT_VECTOR_TYPE(solution, PS_TYPE_F64, NULL);
-    PS_ASSERT_PTR_NON_NULL(kernels, NULL);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->v, NULL);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->xOrder, NULL);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->yOrder, NULL);
-    if (kernels->preCalc != NULL) {
-        PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->preCalc, NULL);
-    } else {
-        if (kernels->type != PM_SUBTRACTION_KERNEL_POIS) {
-            psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                    "Unallowable operation: kernels->preCalc == NULL and kernels->type != PM_SUBTRACTION_KERNEL_POIS.\n");
-            return(NULL);
-        }
-    }
-    psS32 nBF = kernels->u->n;
-    PS_ASSERT_VECTOR_SIZE(solution, nBF+1, NULL);
-
-    psS32 numCols = input->numCols;
-    psS32 numRows = input->numRows;
-    psS32 kernelSize = kernels->size;
-
-    psImage *convolved = psImageAlloc(numCols, numRows, PS_TYPE_F32);
-
-    for (psS32 y = kernelSize; y < numRows - kernelSize; y++) {
-        for (psS32 x = kernelSize; x < numCols - kernelSize; x++) {
-            if (kernels->type == PM_SUBTRACTION_KERNEL_POIS) {
-                convolved->data.F32[y][x] = ConvolvePixelPois(input, mask, badStampMaskVal,
-                                            solution, kernels, x, y);
-            } else if (kernels->type == PM_SUBTRACTION_KERNEL_ISIS) {
-                convolved->data.F32[y][x] = ConvolvePixelIsis(input, mask, badStampMaskVal,
-                                            solution, kernels, x, y);
-            } else {
-                psLogMsg(__func__, PS_LOG_WARN, "WARNING: unknown kernel type.  Returning NULL\n");
-                return(NULL);
-            }
-        }
-    }
-
-    //
-    // Pad the rest of the convolved image with 0.0
-    //
-    for (psS32 y = kernelSize; y < numRows - kernelSize; y++) {
-        for (psS32 x = 0; x < kernelSize; x++) {
-            convolved->data.F32[y][x] = 0.0;
-        }
-        for (psS32 x = numCols - kernelSize; x < numCols; x++) {
-            convolved->data.F32[y][x] = 0.0;
-        }
-    }
-    for (psS32 y = 0; y < kernelSize; y++) {
-        for (psS32 x = 0; x < numCols; x++) {
-            convolved->data.F32[y][x] = 0.0;
-        }
-    }
-    for (psS32 y = numRows - kernelSize; y < numRows; y++) {
-        for (psS32 x = 0; x < numCols; x++) {
-            convolved->data.F32[y][x] = 0.0;
-        }
-    }
-
-    psTrace("ImageSubtract.ConvolveImage", 4, "Exiting ConvolveImage()\n");
-    return convolved;
-}
-
-
-
-
-
-/*******************************************************************************
-XXX: We should assert that the (footprint, kernelSize, imageSize) stuff
-ensures that all data is accessed in bounds?
- ******************************************************************************/
-bool pmSubtractionCalculateEquation(psArray *stamps,          ///< The stamps for which to calculate the equation,
-                                    const psImage *reference, ///< Reference image
-                                    const psImage *input,     ///< Input image
-                                    const psSubtractionKernels *kernels, ///< The kernel basis functions
-                                    psS32 footprint           ///< Half-size of region over which to calculate equation
-                                   )
-{
-    psTrace("ImageSubtract.pmSubtractionCalculateEquation", 3,
-            "Calling pmSubtractionCalculateEquation()\n");
-    PS_ASSERT_PTR_NON_NULL(stamps, false);
-    PS_ASSERT_IMAGE_NON_NULL(reference, false);
-    PS_ASSERT_IMAGE_NON_EMPTY(reference, false);
-    PS_ASSERT_IMAGE_NON_NULL(input, false);
-    PS_ASSERT_IMAGE_NON_EMPTY(input, false);
-    PS_ASSERT_IMAGES_SIZE_EQUAL(reference, input, false);
-    PS_ASSERT_PTR_NON_NULL(kernels, false);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->v, false);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->xOrder, false);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->yOrder, false);
-    if (kernels->type == PM_SUBTRACTION_KERNEL_ISIS) {
-        PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->preCalc, false);
-    }
-    psS32 kernelSize = kernels->size;
-    PS_ASSERT_INT_NONNEGATIVE(footprint, false);
-    //
-    // For each legitimate stamp, ensure that the footprint is small enough to perform
-    // the full calculation.
-    //
-    // XXX: Verify with IfA that this is a reasonable action.
-    //
-    for (psS32 s = 0; s < stamps->n; s++) {
-        pmStamp *stamp = (pmStamp *) stamps->data[s];
-        if (stamp->status == PM_STAMP_RECALC) {
-            // XXX: trace message
-            // printf("stamp %d (x, y) is (%d, %d).  Footprint is %d.  kernelSize is %d.\n", s, stamp->x, stamp->y, footprint, kernelSize);
-            if (((stamp->y - (footprint + kernelSize)) < 0) ||
-                    ((stamp->x - (footprint + kernelSize)) < 0) ||
-                    ((stamp->y + footprint + kernelSize) >= input->numRows) ||
-                    ((stamp->x + footprint + kernelSize) >= input->numCols)) {
-                stamp->status = PM_STAMP_NONE;
-                psLogMsg(__func__, PS_LOG_WARN,
-                         "WARNING: stamp %d will be ignored.  It exceeds image size: access columns (%d to %d) and rows (%d to %d)\n",
-                         s,
-                         stamp->x - (footprint + kernelSize),
-                         (stamp->x + footprint + kernelSize) - 1,
-                         stamp->y - (footprint + kernelSize),
-                         (stamp->y + footprint + kernelSize) - 1);
-            }
-        }
-    }
-
-    psS32 numHalfRows = reference->numRows;
-    psS32 numHalfCols = reference->numCols;
-    psS32 spatialOrder = kernels->spatialOrder;
-
-    //
-    // The numSolveParams incorporates the additional parameter for the
-    // background value, which we must solve for.
-    //
-    psS32 numKernels = kernels->u->n;
-    int numSolveParams = numKernels + 1;
-    int bgIndex = numKernels;        // Index in matrix for the background
-
-    //
-    // We iterate over each stamp, allocate the matrix and vectors if
-    // necessary, and then calculate those matrix/vectors.
-    //
-    for (psS32 s = 0; s < stamps->n; s++) {
-        pmStamp *stamp = (pmStamp *) stamps->data[s];
-        psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): stamp %d\n", s);
-        if (stamp->status == PM_STAMP_RECALC) {
-            psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): stamp %d: status is PM_STAMP_RECALC.\n", s);
-            psImage *stampMatrix = stamp->matrix;
-            psVector *stampVector = stamp->vector;
-
-            if (stampMatrix == NULL) {
-                stampMatrix = psImageAlloc(numSolveParams, numSolveParams, PS_TYPE_F64);
-                stamp->matrix = stampMatrix;
-            } else {
-                PS_ASSERT_IMAGE_TYPE(stampMatrix, PS_TYPE_F64, false);
-                PS_ASSERT_IMAGE_SIZE(stampMatrix, numSolveParams, numSolveParams, false);
-            }
-            PS_IMAGE_SET_F64(stampMatrix, 0.0);
-
-            if (stampVector == NULL) {
-                stampVector = psVectorAlloc(numSolveParams, PS_TYPE_F64);
-                stampVector->n = stampVector->nalloc;
-                stamp->vector = stampVector;
-            } else {
-                PS_ASSERT_VECTOR_TYPE(stampVector, PS_TYPE_F64, false);
-                PS_ASSERT_VECTOR_SIZE(stampVector, numSolveParams, false);
-            }
-            PS_VECTOR_SET_F64(stampVector, 0.0);
-            psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): stamp %d: allocate matrix and vector.\n", s);
-
-            //
-            // Evaluate the spatial-order polynomial.  The [i][j]-th element of
-            // the psImage polyValues will hold (x^i * y^j) for the stamp.  The
-            // (x, y) value are scaled to [-1:1]
-            //
-            psImage *polyValues = GenSpatialOrder(spatialOrder,
-                                                  ((psF64) (stamp->x - numHalfCols)) / ((psF64) numHalfCols),
-                                                  ((psF64) (stamp->y - numHalfRows)) / ((psF64) numHalfRows));
-
-            psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): stamp %d: generated spatial order terms.\n", s);
-
-            if (kernels->type == PM_SUBTRACTION_KERNEL_POIS) {
-                //
-                // Iterate over all pixels surrounding this stamp.
-                //
-                for (psS32 y = stamp->y - footprint; y < stamp->y + footprint; y++) {
-                    for (psS32 x = stamp->x - footprint; x < stamp->x + footprint; x++) {
-                        psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): pixel (%d, %d).\n", y, x);
-
-                        // The inverse of the noise, squared.
-                        psF32 invNoise2 = 1.0/reference->data.F32[y][x];
-
-                        //
-                        // Iterate over the first convolution */
-                        //
-                        for (psS32 k1 = 0; k1 < numKernels; k1++) {
-                            psS32 u1 = kernels->u->data.F32[k1];        // Offset in x
-                            psS32 v1 = kernels->v->data.F32[k1];        // Offset in y
-                            psS32 i1 = kernels->xOrder->data.F32[k1];   // Polynomial order in x
-                            psS32 j1 = kernels->yOrder->data.F32[k1];   // Polynomial order in y
-
-                            //
-                            // First convolution.  This will set the value for the stampVector.
-                            //
-                            // XXX: verify the [y-v2][x-u2] subscript.  This generated errors in
-                            // testing, depending on kernel size and footprint.
-                            //
-                            psF32 conv1 = polyValues->data.F64[j1][i1] * reference->data.F32[y - v1][x - u1];
-
-                            //
-                            // Assuming that the first kernel component is 0 order in x and y, and 0 offset
-                            // XXX: I don't understand this:
-                            //
-                            if (k1 != 0) {
-                                conv1 -= reference->data.F32[y][x];
-                            }
-
-                            //
-                            // Iterate over the second convolution
-                            //
-                            for (psS32 k2 = k1; k2 < numKernels; k2++) {
-                                psS32 u2 = (psS32) kernels->u->data.F32[k2];        // Offset in x
-                                psS32 v2 = (psS32) kernels->v->data.F32[k2];        // Offset in y
-                                psS32 i2 = (psS32) kernels->xOrder->data.F32[k2];   // Polynomial order in x
-                                psS32 j2 = (psS32) kernels->yOrder->data.F32[k2];   // Polynomial order in y
-                                //
-                                // XXX: verify the [y-v2][x-u2] subscript.  This generated errors in
-                                // testing, depending on kernel size and footprint.
-                                //
-                                // Second convolution
-                                //
-                                psF32 conv2 = polyValues->data.F64[j2][i2] *
-                                              reference->data.F32[y-v2][x-u2];
-                                //
-                                // Assuming that the first kernel component is 0 order in x and y, and 0 offset
-                                // XXX: I don't understand this:
-                                if (k2 != 0) {
-                                    //
-                                    conv2 -= reference->data.F32[y][x];
-                                }
-
-                                // Add into the matrix element
-                                stampMatrix->data.F64[k1][k2] += conv1 * conv2 * invNoise2;
-
-                            } // Iteration on second convolution
-
-                            // Add into the vector element
-                            stampVector->data.F64[k1] += input->data.F32[y][x] * conv1 * invNoise2;
-
-                            /* Background term */
-                            stampMatrix->data.F64[k1][bgIndex] += conv1 * invNoise2;
-
-                        } // Iteration on first convolution
-
-                        //
-                        // Background only terms.
-                        // XXX: understand this.
-                        //
-                        stampMatrix->data.F64[bgIndex][bgIndex] += invNoise2;
-                        stampVector->data.F64[bgIndex] += input->data.F32[y][x] * invNoise2;
-                    }
-                }
-            } else if (kernels->type == PM_SUBTRACTION_KERNEL_ISIS) {
-                //
-                // Iterate over all pixels surrounding this stamp.
-                //
-                // XXX: Why isn't there a polyValues term here?
-                //
-
-                for (psS32 y = stamp->y - footprint; y < stamp->y + footprint; y++) {
-                    for (psS32 x = stamp->x - footprint; x < stamp->x + footprint; x++) {
-                        psTrace("pmSubtractionCalculateEquation", 6, "subCalcEqn(): pixel (%d, %d).\n", y, x);
-                        psF32 invNoise2 = 1.0/reference->data.F32[y][x]; // The inverse of the noise, squared.
-
-                        for (psS32 k1 = 0; k1 < numKernels; k1++) {
-                            psF32 conv1 = IsisKernelConvolve(reference, kernels, k1, x, y);
-
-                            for (psS32 k2 = k1; k2 < numKernels; k2++) {
-                                //printf("(k1, k2) is (%d, %d)\n", k1, k2);
-                                psF32 conv2 = IsisKernelConvolve(reference, kernels, k2, x, y);
-                                stampMatrix->data.F64[k1][k2]+= conv1 * conv2 * invNoise2;
-                            }
-                            stampVector->data.F64[k1]+= input->data.F32[y][x] * conv1 * invNoise2;
-                            stampMatrix->data.F64[k1][bgIndex] += conv1 * invNoise2;
-                        }
-                        stampMatrix->data.F64[bgIndex][bgIndex] += invNoise2;
-                        stampVector->data.F64[bgIndex] += input->data.F32[y][x] * invNoise2;
-                    }
-                }
-            } else {
-                psLogMsg(__func__, PS_LOG_WARN, "WARNING: unknown kernel->type.\n");
-                return(false);
-            }
-            psFree(polyValues);
-
-            // XXX: Generate psTrace()
-            if (0) {
-                for (psS32 s = 0; s < stamps->n; s++) {
-                    pmStamp *stamp = (pmStamp *) stamps->data[s];
-                    if (stamp->status == PM_STAMP_RECALC) {
-                        psVector *stampVector = stamp->vector;
-                        printf("STAMP: stamp %d vector:\n", s);
-                        PS_VECTOR_PRINT_F64(stampVector);
-                    }
-                }
-            }
-
-            //
-            // Fill in other side of symmetric matrix
-            //
-            // XXX: understand this.
-            // XXX: Why aren't they using numSolveParams instead of numKernels?
-            // XXX: is this POIS specific?
-            //
-            for (psS32 k1 = 0; k1 < numKernels; k1++) {
-                for (psS32 k2 = 0; k2 < k1; k2++) {
-                    stampMatrix->data.F64[k1][k2] = stampMatrix->data.F64[k2][k1];
-                }
-                stampMatrix->data.F64[bgIndex][k1] = stampMatrix->data.F64[k1][bgIndex];
-            }
-
-            //
-            // XXX: Why aren't they using numSolveParams instead of numKernels?
-            // XXX: is this POIS specific?
-            //
-            #define XXX_CONFIG_PENALTY 1.0
-            for (psS32 k = 0; k < numKernels; k++)
-            {
-                psS32 u = kernels->u->data.F32[k];  // Offset in x
-                psS32 v = kernels->v->data.F32[k];  // Offset in y
-                stampMatrix->data.F64[k][k] += XXX_CONFIG_PENALTY * (psF32)(u*u + v*v);
-            }
-            stamp->status = PM_STAMP_USED;
-        } else {
-            // Stamp is ignored since it's not PM_STAMP_RECALC
-        }
-    }
-    psTrace("ImageSubtract.pmSubtractionCalculateEquation", 3,
-            "Exiting pmSubtractionCalculateEquation()\n");
-    return(true);
-}
-
-
-
-
-/*******************************************************************************
- ******************************************************************************/
-psVector *pmSubtractionSolveEquation(psVector *solution, ///< Solution vector, or NULL
-                                     const psArray *stamps      ///< Array of stamps
-                                    )
-{
-    psTrace("ImageSubtract.pmSubtractionSolveEquation", 3,
-            "Calling pmSubtractionSolveEquation()\n");
-    PS_ASSERT_PTR_NON_NULL(stamps, NULL);
-    psS32 size = -1;
-    psS32 s = 0;
-
-    //
-    // Determine the size of the stamp vectors and matrix.
-    // We iterate until we find the first acceptable stamp.
-    //
-    while ((size == -1) && (s < stamps->n)) {
-        pmStamp *stamp = (pmStamp *) stamps->data[s];
-        PS_ASSERT_PTR_NON_NULL(stamp, NULL);
-        if (stamp->status == PM_STAMP_USED) {
-            size = ((pmStamp *) stamps->data[s])->vector->n;
-            PS_ASSERT_INT_POSITIVE(size, NULL);
-        }
-        s++;
-    }
-    if (size == -1) {
-        psLogMsg(__func__, PS_LOG_WARN, "WARNING: no acceptable stamps.  Returning NULL\n");
-        return(NULL);
-    }
-
-    if (solution != NULL) {
-        PS_ASSERT_VECTOR_TYPE(solution, PS_TYPE_F64, NULL);
-        PS_ASSERT_VECTOR_SIZE(solution, size, NULL);
-    } else {
-        solution = psVectorAlloc(size, PS_TYPE_F64);
-        solution->n = solution->nalloc;
-    }
-
-    //
-    // Create the solution matrix and vector.
-    //
-    // XXX: Test these functions with size=-1.  This caused seg faults during test.
-    //      This should be done in the psImage.c and psVector.c test files.  It
-    //      should never occur here.
-    //
-    psImage *sumMatrix = psImageAlloc(size, size, PS_TYPE_F64);
-    psVector *sumVector = psVectorAlloc(size, PS_TYPE_F64);
-    sumVector->n = sumVector->nalloc;
-    PS_VECTOR_SET_F64(sumVector, 0.0);
-    PS_IMAGE_SET_F64(sumMatrix, 0.0);
-
-    //
-    // Verify that all stamps have similar sizes.
-    // Compute the sum matrix and vector.
-    //
-    for (psS32 s = 0; s < stamps->n; s++) {
-        pmStamp *stamp = (pmStamp *) stamps->data[s];
-
-        if (stamp->status == PM_STAMP_USED) {
-            PS_ASSERT_INT_EQUAL(((pmStamp *) stamps->data[s])->vector->n, size, NULL);
-
-            psImage *stampMatrix = stamp->matrix;
-            psVector *stampVector = stamp->vector;
-            PS_ASSERT_VECTOR_TYPE(stampVector, PS_TYPE_F64, NULL);
-            PS_ASSERT_VECTOR_SIZE(stampVector, size, NULL);
-            PS_ASSERT_IMAGE_TYPE(stampMatrix, PS_TYPE_F64, NULL);
-            PS_ASSERT_IMAGE_SIZE(stampMatrix, size, size, NULL);
-
-            (void)psBinaryOp(sumMatrix, sumMatrix, "+", stampMatrix);
-            (void)psBinaryOp(sumVector, sumVector, "+", stampVector);
-        }
-    }
-    psVector *permutation = NULL;
-    // XXX: Check output from these routines.
-
-    // XXX: psTrace()
-    if (0) {
-        PS_IMAGE_PRINT_F64(sumMatrix);
-    }
-
-    psImage *luMatrix = psMatrixLUD(NULL, &permutation, sumMatrix);
-    if (luMatrix == NULL) {
-        psError(PS_ERR_UNKNOWN, true, "Failed to LU-Decompose the matrix.\n");
-        psFree(sumMatrix);
-        psFree(sumVector);
-        psFree(luMatrix);
-        psFree(permutation);
-        return(NULL);
-    }
-    // XXX: psTrace()
-    if (0) {
-        PS_IMAGE_PRINT_F64(luMatrix);
-    }
-
-    solution = psMatrixLUSolve(solution, luMatrix, sumVector, permutation);
-    // XXX: psTrace()
-    // XXX: should we be checking for NAN's in the solution vector?
-    if (0) {
-        PS_VECTOR_PRINT_F64(solution);
-    }
-    if (solution == NULL) {
-        psError(PS_ERR_UNKNOWN, true, "Failed to solve the matrix.\n");
-        psFree(sumMatrix);
-        psFree(sumVector);
-        psFree(luMatrix);
-        psFree(permutation);
-        return(NULL);
-    }
-
-    psFree(sumMatrix);
-    psFree(sumVector);
-    psFree(luMatrix);
-    psFree(permutation);
-
-    psTrace("ImageSubtract.pmSubtractionSolveEquation", 3,
-            "Exiting pmSubtractionSolveEquation()\n");
-    return(solution);
-}
-
-
-/*******************************************************************************
- ******************************************************************************/
-static psVector *CalculateDeviations(psVector *deviations,
-                                     psArray *stamps,
-                                     psS32 footprint,
-                                     const psImage *refImage,
-                                     const psImage *inImage,
-                                     const psImage *mask,
-                                     psU32 badStampMaskVal,
-                                     const psSubtractionKernels *kernels,
-                                     const psVector *solution)
-{
-    psTrace("ImageSubtract.CalculateDeviations", 4,
-            "Calling CalculateDeviations()\n");
-    PS_ASSERT_PTR_NON_NULL(stamps, NULL);
-    if (deviations != NULL) {
-        PS_ASSERT_VECTOR_TYPE(deviations, PS_TYPE_F32, NULL);
-        PS_ASSERT_VECTORS_SIZE_EQUAL(deviations, stamps, NULL);
-    } else {
-        deviations = psVectorAlloc(stamps->n, PS_TYPE_F32);
-        deviations->n = deviations->nalloc;
-        // XXX: Probably not necessary.
-        PS_VECTOR_SET_F32(deviations, 0.0);
-    }
-    PS_ASSERT_IMAGE_NON_NULL(refImage, NULL);
-    PS_ASSERT_IMAGE_NON_EMPTY(refImage, NULL);
-    PS_ASSERT_IMAGE_TYPE(refImage, PS_TYPE_F32, NULL);
-    PS_ASSERT_IMAGE_NON_NULL(inImage, NULL);
-    PS_ASSERT_IMAGE_NON_EMPTY(inImage, NULL);
-    PS_ASSERT_IMAGE_TYPE(inImage, PS_TYPE_F32, NULL);
-    PS_ASSERT_IMAGES_SIZE_EQUAL(refImage, inImage, NULL);
-    PS_ASSERT_IMAGE_NON_NULL(mask, NULL);
-    PS_ASSERT_IMAGE_NON_EMPTY(mask, NULL);
-    PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_U8, NULL);
-    PS_ASSERT_IMAGES_SIZE_EQUAL(refImage, mask, NULL);
-    PS_ASSERT_VECTOR_NON_NULL(solution, NULL);
-    PS_ASSERT_VECTOR_TYPE(solution, PS_TYPE_F64, NULL);
-    PS_ASSERT_PTR_NON_NULL(kernels, NULL);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->v, NULL);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->xOrder, NULL);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->yOrder, NULL);
-    if (kernels->preCalc != NULL) {
-        PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->preCalc, NULL);
-    } else {
-        if (kernels->type != PM_SUBTRACTION_KERNEL_POIS) {
-            psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                    "Unallowable operation: kernels->preCalc == NULL and kernels->type != PM_SUBTRACTION_KERNEL_POIS.\n");
-            return(NULL);
-        }
-    }
-    psS32 nBF = kernels->u->n;
-    PS_ASSERT_VECTOR_SIZE(solution, nBF+1, NULL);
-
-    psS32 kernelSize = kernels->size;
-    int xSize = footprint + kernelSize;
-    int ySize = footprint + kernelSize;
-    psStats *stats = psStatsAlloc(PS_STAT_SAMPLE_MEAN); // Statistics
-    psImage *subStamp = psImageAlloc(2 * xSize, 2 * ySize, PS_TYPE_F32); // Subtraction of stamp
-    for (psS32 s = 0; s < stamps->n; s++) {
-        pmStamp *stamp = stamps->data[s]; // The coordinates of the stamp of interest
-        psS32 x = stamp->x;               // Stamp x coord
-        psS32 y = stamp->y;               // Stamp y coord
-        if (stamp->status == PM_STAMP_USED) {
-
-            psRegion myReg = psRegionSet(x - xSize, x + xSize, y - ySize, y + ySize);
-            psImage *refStamp = psImageSubset((psImage *) refImage, myReg);
-            psImage *inStamp = psImageSubset((psImage *) inImage, myReg);
-            psImage *maskStamp = psImageSubset((psImage *) mask, myReg);
-            psImage *convRefStamp = ConvolveImage(refStamp, maskStamp, badStampMaskVal, solution, kernels);
-
-            // Calculate chi^2
-            (void)psBinaryOp(subStamp, inStamp, "-", convRefStamp);
-            (void)psBinaryOp(subStamp, subStamp, "/", inStamp);
-            (void)psBinaryOp(subStamp, subStamp, "*", subStamp);
-            myReg = psRegionSet(kernelSize, kernelSize + 2 * footprint,
-                                kernelSize, kernelSize + 2 * footprint);
-            psImage *subStampTrim = psImageSubset((psImage *) subStamp, myReg);
-            psImage *maskStampTrim = psImageSubset((psImage *) maskStamp, myReg);
-            psImageStats(stats, subStampTrim, maskStampTrim, badStampMaskVal);
-
-            deviations->data.F32[s] = stats->sampleMean * (psF32)footprint * (psF32)footprint * 4.0;
-            // XXX: Allocate and free these elsewhere.
-            psFree(refStamp);
-            psFree(inStamp);
-            psFree(maskStamp);
-            psFree(convRefStamp);
-            psFree(subStampTrim);
-            psFree(maskStampTrim);
-        }
-    }
-
-    psFree(stats);
-    psFree(subStamp);
-
-    psTrace("ImageSubtract.CalculateDeviations", 4,
-            "Exiting CalculateDeviations()\n");
-    return deviations;
-}
-
-/*******************************************************************************
- ******************************************************************************/
-bool pmSubtractionRejectStamps(psArray *stamps,  ///< Array of stamps to check for rejection
-                               psImage *mask,  ///< Mask image
-                               psU32 badStampMaskVal, ///< Value to use in mask for bad stamp
-                               psS32 footprint,  ///< Region to mask if stamp is bad
-                               psF32 sigmaRej,  ///< Number of RMS deviations above zero at which to reject
-                               const psImage *refImage, ///< Reference image
-                               const psImage *inImage, ///< Input image
-                               const psVector *solution, ///< Solution vector
-                               const psSubtractionKernels *kernels ///< Array of kernel parameters
-                              )
-{
-    psTrace("ImageSubtract.pmSubtractionRejectStamps", 3,
-            "Calling pmSubtractionRejectStamps()\n");
-    PS_ASSERT_PTR_NON_NULL(stamps, false);
-    PS_ASSERT_IMAGE_NON_NULL(refImage, false);
-    PS_ASSERT_IMAGE_NON_EMPTY(refImage, false);
-    PS_ASSERT_IMAGE_TYPE(refImage, PS_TYPE_F32, false);
-    PS_ASSERT_IMAGE_NON_NULL(inImage, false);
-    PS_ASSERT_IMAGE_NON_EMPTY(inImage, false);
-    PS_ASSERT_IMAGE_TYPE(inImage, PS_TYPE_F32, false);
-    PS_ASSERT_IMAGES_SIZE_EQUAL(refImage, inImage, false);
-    PS_ASSERT_IMAGE_NON_NULL(mask, false);
-    PS_ASSERT_IMAGE_NON_EMPTY(mask, false);
-    PS_ASSERT_IMAGE_TYPE(mask, PS_TYPE_U8, false);
-    PS_ASSERT_IMAGES_SIZE_EQUAL(refImage, mask, false);
-    PS_ASSERT_VECTOR_NON_NULL(solution, false);
-    PS_ASSERT_VECTOR_TYPE(solution, PS_TYPE_F64, false);
-    PS_ASSERT_PTR_NON_NULL(kernels, false);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->v, false);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->xOrder, false);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->yOrder, false);
-    if (kernels->preCalc != NULL) {
-        PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->preCalc, false);
-    } else {
-        if (kernels->type != PM_SUBTRACTION_KERNEL_POIS) {
-            psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                    "Unallowable operation: kernels->preCalc == NULL and kernels->type != PM_SUBTRACTION_KERNEL_POIS.\n");
-            return(false);
-        }
-    }
-
-    psS32 nBF = kernels->u->n;
-    PS_ASSERT_VECTOR_SIZE(solution, nBF+1, false);
-
-    psVector *deviations = CalculateDeviations(NULL,
-                           stamps,
-                           footprint,
-                           refImage,
-                           inImage,
-                           mask,
-                           badStampMaskVal,
-                           kernels,
-                           solution);
-    //
-    // Calculate the deviation from zero.
-    //
-    psF64 meanDev = 0.0;
-    psS32 numDev = 0;
-    for (psS32 i = 0; i < deviations->n; i++) {
-        pmStamp *stamp = stamps->data[i];
-        if (stamp->status == PM_STAMP_USED) {
-            meanDev += PS_SQR(deviations->data.F32[i]);
-            numDev++;
-        }
-    }
-    psF32 rmsDev = sqrtf(meanDev / (psF64)(numDev - 1));
-    psF32 limit = rmsDev * sigmaRej;
-
-    for (psS32 s = 0; s < stamps->n; s++) {
-        pmStamp *stamp = (pmStamp *) stamps->data[s];
-        if (stamp->status == PM_STAMP_USED && fabsf(deviations->data.F32[s]) > limit) {
-            // Mask out the stamp in the image so you don't find it again
-            for (psS32 y = stamp->y - footprint; y < stamp->y + footprint; y++) {
-                for (psS32 x = stamp->x - footprint; x < stamp->x + footprint; x++) {
-                    mask->data.U8[y][x] |= badStampMaskVal;
-                }
-            }
-
-            // Set stamp for replacement
-            stamp->x = 0;
-            stamp->y = 0;
-            stamp->status = PM_STAMP_REJECTED;
-        }
-    }
-
-    psFree(deviations);
-    psTrace("ImageSubtract.pmSubtractionRejectStamps", 3,
-            "Exiting pmSubtractionRejectStamps()\n");
-    return(true);
-}
-
-/*******************************************************************************
- ******************************************************************************/
-psImage *pmSubtractionKernelImage(psImage *out,
-                                  const psVector *solution,
-                                  const psSubtractionKernels *kernels,
-                                  psF32 x,
-                                  psF32 y
-                                 )
-{
-    psTrace("ImageSubtract.pmSubtractionKernelImage", 3,
-            "Calling pmSubtractionKernelImage()\n");
-    PS_ASSERT_VECTOR_NON_NULL(solution, NULL);
-    PS_ASSERT_PTR_NON_NULL(kernels, NULL);
-    PS_ASSERT_FLOAT_WITHIN_RANGE(x, -1.0, 1.0, NULL);
-    PS_ASSERT_FLOAT_WITHIN_RANGE(y, -1.0, 1.0, NULL);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->v, NULL);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->xOrder, NULL);
-    PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->yOrder, NULL);
-    if (kernels->preCalc != NULL) {
-        PS_ASSERT_VECTORS_SIZE_EQUAL(kernels->u, kernels->preCalc, NULL);
-    } else {
-        if (kernels->type != PM_SUBTRACTION_KERNEL_POIS) {
-            psError(PS_ERR_BAD_PARAMETER_NULL, true,
-                    "Unallowable operation: kernels->preCalc == NULL and kernels->type != PM_SUBTRACTION_KERNEL_POIS.\n");
-            return(NULL);
-        }
-    }
-    PS_ASSERT_INT_EQUAL(1+kernels->u->n, solution->n, NULL);
-
-    psS32 nBF = kernels->u->n;
-    psS32 spatialOrder = kernels->spatialOrder;
-    psS32 kernelSize = kernels->size;
-
-    if (out != NULL) {
-        if ((out->numCols < (1+2*kernelSize)) || (out->numRows < (1+2*kernelSize))) {
-            psLogMsg(__func__, PS_LOG_WARN, "WARNING: out image is not large enough.\n");
-            return(out);
-        }
-    } else {
-        out = psImageAlloc(1+2*kernelSize, 1+2*kernelSize, PS_TYPE_F32);
-    }
-    PS_IMAGE_SET_F32(out, 0.0);
-
-    //
-    // Generate the spatial-order polynomial.  The [i][j]-th element of
-    // the psImage polyValues will hold (x^i * y^j) for the stamp.
-    //
-    psImage *polyValues = GenSpatialOrder(spatialOrder, x, y);
-
-    // XXX: switch (i, j) so they correspond to (x, y).
-    if (kernels->type == PM_SUBTRACTION_KERNEL_ISIS) {
-        for (psS32 k = 0 ; k < nBF ; k++) {
-            psS32 xOrder = (psS32) kernels->xOrder->data.F32[k];
-            psS32 yOrder = (psS32) kernels->yOrder->data.F32[k];
-            psF64 polyVal = polyValues->data.F64[yOrder][xOrder];
-
-            // XXX: Verify that this is correct.
-            for (psS32 i = -kernelSize ; i <= kernelSize ; i++) {
-                for (psS32 j = -kernelSize ; j <= kernelSize ; j++) {
-                    psImage *preCalc = (psImage *) kernels->preCalc->data[k];
-                    out->data.F32[i+kernelSize][j+kernelSize]+=
-                        solution->data.F64[k] *
-                        preCalc->data.F32[i+kernelSize][j+kernelSize] *
-                        polyVal;
-                }
-            }
-        }
-    } else if (kernels->type == PM_SUBTRACTION_KERNEL_POIS) {
-        for (psS32 k = 0 ; k < nBF ; k++) {
-            // XXX: Why don't we have compilation warnings on type here (if
-            // we remove the (psS32) cast)?
-            psS32 u = (psS32) kernels->u->data.F32[k];
-            psS32 v = (psS32) kernels->v->data.F32[k];
-            psS32 xOrder = (psS32) kernels->xOrder->data.F32[k];
-            psS32 yOrder = (psS32) kernels->yOrder->data.F32[k];
-            // XXX: Verify that this is correct.
-
-            out->data.F32[kernelSize - v][kernelSize - u]+=
-                solution->data.F64[k] * polyValues->data.F64[yOrder][xOrder];
-        }
-    }
-    psFree(polyValues);
-
-    psTrace("ImageSubtract.pmSubtractionKernelImage", 3,
-            "Exiting pmSubtractionKernelImage()\n");
-    return(out);
-}
Index: unk/psModules/src/imsubtract/pmImageSubtract.h
===================================================================
--- /trunk/psModules/src/imsubtract/pmImageSubtract.h	(revision 7018)
+++ 	(revision )
@@ -1,127 +1,0 @@
-/** @file  ImageSubtract.h
- *
- *  This file will contain code which creates a set of kernel basis
- *  functions, solves for their solution, and applies them to an image.
- *
- *  @author Paul Price, IfA (original prototype)
- *  @author GLG, MHPCC
- *
- *  @version $Revision: 1.1 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2005-09-28 20:43:52 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- */
-
-#if !defined(PM_IMAGE_COMBINE_H)
-#define PM_IMAGE_COMBINE_H
-
-#if HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include<stdio.h>
-#include<math.h>
-#include "pslib.h"
-#include "psConstants.h"
-
-typedef enum {
-    PM_SUBTRACTION_KERNEL_POIS,         ///< POIS kernel --- delta functions
-    PM_SUBTRACTION_KERNEL_ISIS          ///< ISIS kernel --- gaussians modified by polynomials
-} pmSubtractionKernelsType;
-
-typedef struct
-{
-    pmSubtractionKernelsType type;      ///< Type ofKernels --- allowing the use of multiple kernels
-    psVector *u, *v;                    ///< Offset (for POIS) or polynomial order (for ISIS)
-    psVector *sigma;                    ///< Width of Gaussian (for ISIS)
-    psVector *xOrder, *yOrder;          ///< Spatial Polynomial order (for all)
-    int subIndex;                       ///< Index of kernel to be subtracted to maintain flux conservation
-    psArray *preCalc;                   ///< Array of images containing pre-calculated kernel (to
-    ///< accelerate ISIS; don't use for POIS)
-    psS32 size;                         ///< The halfsize of the kernel
-    psS32 spatialOrder;                 ///< The spatial order of the kernels
-}
-psSubtractionKernels;
-
-psSubtractionKernels *pmSubtractionKernelsAllocPOIS(
-    int size,
-    int SpatialOrder
-);
-
-psSubtractionKernels *pmSubtractionKernelsAllocISIS(
-    const psVector *sigmas,
-    const psVector *orders,
-    int size,
-    int SpatialOrder
-);
-
-typedef enum {
-    PM_STAMP_INIT,                      ///< Initial state
-    PM_STAMP_USED,                      ///< Use this stamp
-    PM_STAMP_REJECTED,                  ///< This stamp has been rejected
-    PM_STAMP_RECALC,                    ///< Having been reset, this stamp is to be recalculated
-    PM_STAMP_NONE                       ///< No stamp in this region
-} pmStampStatus;
-
-typedef struct
-{
-    int x, y;                           ///< Position
-    int p_xSize;
-    int p_ySize;
-    int p_xMin;
-    int p_xMax;
-    int p_yMin;
-    int p_yMax;
-    psImage *matrix;                    ///< Associated matrix
-    psVector *vector;                   ///< Assoicated vector
-    pmStampStatus status;               ///< Status ofstamp
-}
-pmStamp;
-
-psArray *pmSubtractionFindStamps(
-    psArray *stamps,                    ///< Output stamps, or NULL
-    const psImage *image,               ///< Image for which to find stamps
-    const psImage *mask,                ///< Mask
-    psU32 maskVal,                      ///< Value for mask
-    psF32 threshold,                    ///< Threshold for stamps in the image
-    psS32 xNum,                         ///< Number of stamps in x
-    psS32 yNum,                         ///< Number of stamps in y
-    psS32 border                        ///< Border around image to ignore (should be size of kernel)
-);
-
-bool pmSubtractionCalculateEquation(
-    psArray *stamps,                    ///< The stamps for which to calculate the equation,
-    const psImage *reference,           ///< Reference image
-    const psImage *input,               ///< Input image
-    const psSubtractionKernels *kernels,///< The kernel basis functions
-    psS32 footprint                     ///< Half-size of region over which to calculate equation
-);
-
-
-psVector *pmSubtractionSolveEquation(
-    psVector *solution,                 ///< Solution vector, or NULL
-    const psArray *stamps               ///< Array of stamps
-);
-
-bool pmSubtractionRejectStamps(
-    psArray *stamps,                    ///< Array of stamps to check for rejection
-    psImage *mask,                      ///< Mask image
-    psU32 badStampMaskVal,              ///< Value to use in mask for bad stamp
-    psS32 footprint,                    ///< Region to mask if stamp is bad
-    psF32 sigmaRej,                     ///< Number of RMS deviations above zero at which to reject
-    const psImage *refImage,            ///< Reference image
-    const psImage *inImage,             ///< Input image
-    const psVector *solution,           ///< Solution vector
-    const psSubtractionKernels *kernels ///< Array of kernel parameters
-);
-
-psImage *pmSubtractionKernelImage(
-    psImage *out,
-    const psVector *solution,
-    const psSubtractionKernels *kernels,
-    psF32 x,
-    psF32 y
-);
-
-#endif
Index: unk/psModules/src/imsubtract/pmSubtractBias.c
===================================================================
--- /trunk/psModules/src/imsubtract/pmSubtractBias.c	(revision 7018)
+++ 	(revision )
@@ -1,577 +1,0 @@
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// XXX WARNING: I have completely replaced this file with an OLD VERSION (that works) instead of the
-// one that was being worked on.
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-/** @file  pmSubtractBias.c
- *
- *  This file will contain a module which will subtract the detector bias
- *  in place from an input image.
- *
- *  @author GLG, MHPCC
- *
- *  @version $Revision: 1.12 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2006-04-17 18:10:08 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- */
-
-#if HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <assert.h>
-#include "pmSubtractBias.h"
-
-#define PM_SUBTRACT_BIAS_POLYNOMIAL_ORDER 2
-#define PM_SUBTRACT_BIAS_SPLINE_ORDER 3
-
-
-#define MAX(a,b) ((a) > (b) ? (a) : (b))
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-
-
-// XXX: put these in psConstants.h
-void PS_POLY1D_PRINT(psPolynomial1D *poly)
-{
-    printf("-------------- PS_POLY1D_PRINT() --------------\n");
-    printf("poly->nX is %d\n", poly->nX);
-    for (psS32 i = 0 ; i < (1 + poly->nX) ; i++) {
-        printf("poly->coeff[%d] is %f\n", i, poly->coeff[i]);
-    }
-}
-
-void PS_PRINT_SPLINE(psSpline1D *mySpline)
-{
-    printf("-------------- PS_PRINT_SPLINE() --------------\n");
-    printf("mySpline->n is %d\n", mySpline->n);
-    for (psS32 i = 0 ; i < mySpline->n ; i++) {
-        PS_POLY1D_PRINT(mySpline->spline[i]);
-    }
-    PS_VECTOR_PRINT_F32(mySpline->knots);
-}
-
-#define PS_IMAGE_PRINT_F32_HIDEF(NAME) \
-printf("======== printing %s ========\n", #NAME); \
-for (int i = 0 ; i < (NAME)->numRows ; i++) { \
-    for (int j = 0 ; j < (NAME)->numCols ; j++) { \
-        printf("%.5f ", (NAME)->data.F32[i][j]); \
-    } \
-    printf("\n"); \
-}\
-
-
-void overscanOptionsFree(pmOverscanOptions *options)
-{
-    psFree(options->stat);
-    psFree(options->poly);
-    psFree(options->spline);
-}
-
-pmOverscanOptions *pmOverscanOptionsAlloc(bool single, pmFit fitType, unsigned int order, psStats *stat)
-{
-    pmOverscanOptions *opts = psAlloc(sizeof(pmOverscanOptions));
-    psMemSetDeallocator(opts, (psFreeFunc)overscanOptionsFree);
-
-    // Inputs
-    opts->single = single;
-    opts->fitType = fitType;
-    opts->order = order;
-    opts->stat = psMemIncrRefCounter(stat);
-
-    // Outputs
-    opts->poly = NULL;
-    opts->spline = NULL;
-
-    return opts;
-}
-
-
-/******************************************************************************
-psSubtractFrame(): this routine will take as input a readout for the input
-image and a readout for the bias image.  The bias image is subtracted in
-place from the input image.
-*****************************************************************************/
-static bool SubtractFrame(pmReadout *in,// Input readout
-                          const pmReadout *sub, // Readout to be subtracted from input
-                          float scale   // Scale to apply before subtracting
-                         )
-{
-    assert(in);
-    assert(sub);
-
-    psImage *inImage  = in->image;      // The input image
-    psImage *inMask   = in->mask;       // The input mask
-    psImage *subImage = sub->image;     // The image to be subtracted
-    psImage *subMask  = sub->mask;      // The mask for the subtraction image
-
-    // Offsets of the cells
-    int x0in = psMetadataLookupS32(NULL, in->parent->concepts, "CELL.X0");
-    int y0in = psMetadataLookupS32(NULL, in->parent->concepts, "CELL.Y0");
-    int x0sub = psMetadataLookupS32(NULL, sub->parent->concepts, "CELL.X0");
-    int y0sub = psMetadataLookupS32(NULL, sub->parent->concepts, "CELL.Y0");
-
-    if ((inImage->numCols + x0in - x0sub) > subImage->numCols) {
-        psError(PS_ERR_UNKNOWN, true, "Image does not have enough columns for subtraction.\n");
-        return false;
-    }
-    if ((inImage->numRows + y0in - y0sub) > subImage->numRows) {
-        psError(PS_ERR_UNKNOWN, true, "Image does not have enough rows for subtraction.\n");
-        return false;
-    }
-
-    if (scale == 1.0) {
-        for (int i = 0; i < inImage->numRows; i++) {
-            for (int j = 0; j < inImage->numCols; j++) {
-                inImage->data.F32[i][j] -= subImage->data.F32[i+y0in-y0sub][j+x0in-x0sub];
-                if (inMask && subMask) {
-                    inMask->data.U8[i][j] |= subMask->data.U8[i+y0in-y0sub][j+x0in-x0sub];
-                }
-            }
-        }
-    } else {
-        for (int i = 0; i < inImage->numRows; i++) {
-            for (int j = 0; j < inImage->numCols; j++) {
-                inImage->data.F32[i][j] -= subImage->data.F32[i+y0in-y0sub][j+x0in-x0sub] * scale;
-                if (inMask && subMask) {
-                    inMask->data.U8[i][j] |= subMask->data.U8[i+y0in-y0sub][j+x0in-x0sub];
-                }
-            }
-        }
-    }
-
-    return true;
-}
-
-
-#if 0
-/******************************************************************************
-ImageSubtractScalar(): subtract a scalar from the input image.
- 
-XXX: Use a psLib function for this.
- 
-XXX: This should
- *****************************************************************************/
-static psImage *ImageSubtractScalar(psImage *image,
-                                    psF32 scalar)
-{
-    for (psS32 i=0;i<image->numRows;i++) {
-        for (psS32 j=0;j<image->numCols;j++) {
-            image->data.F32[i][j]-= scalar;
-        }
-    }
-    return(image);
-}
-#endif
-
-/******************************************************************************
-GenNewStatOptions(): this routine will take as input the options member of the
-stat data structure, determine if multiple options have been specified, issue
-a warning message if so, and return the highest priority option (according to
-the order of the if-statements in this code).  The higher priority options are
-listed lower in the code.
- *****************************************************************************/
-static psStatsOptions GenNewStatOptions(const psStats *stat)
-{
-    psS32 numOptions = 0;
-    psStatsOptions opt = 0;
-
-    if (stat->options & PS_STAT_ROBUST_MEDIAN) {
-        if (numOptions == 0) {
-            opt = PS_STAT_ROBUST_MEDIAN;
-        }
-        numOptions++;
-    }
-
-    if (stat->options & PS_STAT_CLIPPED_MEAN) {
-        if (numOptions == 0) {
-            opt = PS_STAT_CLIPPED_MEAN;
-        }
-        numOptions++;
-    }
-
-    if (stat->options & PS_STAT_SAMPLE_MEDIAN) {
-        if (numOptions == 0) {
-            opt = PS_STAT_SAMPLE_MEDIAN;
-        }
-        numOptions++;
-    }
-
-    if (stat->options & PS_STAT_SAMPLE_MEAN) {
-        numOptions++;
-        opt = PS_STAT_SAMPLE_MEAN;
-    }
-
-
-    if (numOptions == 0) {
-        psError(PS_ERR_UNKNOWN,true, "No statistics options have been specified.\n");
-    }
-    if (numOptions != 1) {
-        psLogMsg(__func__, PS_LOG_WARN,
-                 "WARNING: pmSubtractBias.c: GenNewStatOptions(): Too many statistics options have been specified\n");
-    }
-    return(opt);
-}
-
-
-
-#if 0
-/******************************************************************************
-ScaleOverscanVector(): this routine takes as input an arbitrary vector,
-creates a new vector of length n, and fills the new vector with the
-interpolated values of the old vector.  The type of interpolation is:
-    PM_FIT_POLYNOMIAL: fit a polynomial to the entire input vector data.
-    PM_FIT_SPLINE: fit splines to the input vector data.
-XXX: Doesn't it make more sense to do polynomial interpolation on a few
-elements of the input vector, rather than fit a polynomial to the entire
-vector?
- *****************************************************************************/
-static psVector *ScaleOverscanVector(psVector *overscanVector,
-                                     psS32 n,
-                                     void *fitSpec,
-                                     pmFit fit)
-{
-    psTrace(".psModule.pmSubtracBias.ScaleOverscanVector", 4,
-            "---- ScaleOverscanVector() begin (%d -> %d) ----\n", overscanVector->n, n);
-    //    PS_VECTOR_PRINT_F32(overscanVector);
-
-    if (NULL == overscanVector) {
-        return(overscanVector);
-    }
-
-    // Allocate the new vector.
-    psVector *newVec = psVectorAlloc(n, PS_TYPE_F32);
-
-    //
-    // If the new vector is the same size as the old, simply copy the data.
-    //
-    if (n == overscanVector->n) {
-        for (psS32 i = 0 ; i < n ; i++) {
-            newVec->data.F32[i] = overscanVector->data.F32[i];
-        }
-        return(newVec);
-    }
-    psPolynomial1D *myPoly;
-    psSpline1D *mySpline;
-    psF32 x;
-    psS32 i;
-    if (fit == PM_FIT_POLYNOMIAL) {
-        // Fit a polynomial to the old overscan vector.
-        myPoly = (psPolynomial1D *) fitSpec;
-        PS_ASSERT_POLY_NON_NULL(myPoly, NULL);
-        myPoly = psVectorFitPolynomial1D(myPoly, NULL, 0, overscanVector, NULL, NULL);
-        if (myPoly == NULL) {
-            psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector()(1): Could not fit a polynomial to the psVector.\n");
-            return(NULL);
-        }
-
-        // For each element of the new vector, convert the x-ordinate to that
-        // of the old vector, use the fitted polynomial to determine the
-        // interpolated value at that point, and set the new vector.
-        for (i=0;i<n;i++) {
-            x = ((psF32) i) * ((psF32) overscanVector->n) / ((psF32) n);
-            newVec->data.F32[i] = psPolynomial1DEval(myPoly, x);
-        }
-    } else if (fit == PM_FIT_SPLINE) {
-        psS32 mustFreeSpline = 0;
-        // Fit a spline to the old overscan vector.
-        mySpline = (psSpline1D *) fitSpec;
-        // XXX: Does it make any sense to have a psSpline argument?
-        if (mySpline == NULL) {
-            mustFreeSpline = 1;
-        }
-
-        //
-        // NOTE: Since the X arg in the psVectorFitSpline1D() function is NULL,
-        // splines endpoints will be from 0.0 to overscanVector->n-1.  Must scale
-        // properly when doing the spline eval.
-        //
-        //        mySpline = psVectorFitSpline1D(mySpline, NULL, overscanVector, NULL);
-        mySpline = psVectorFitSpline1D(NULL, overscanVector);
-        if (mySpline == NULL) {
-            psError(PS_ERR_UNKNOWN, false, "ScaleOverscanVector()(2): Could not fit a spline to the psVector.\n");
-            return(NULL);
-        }
-        //        PS_PRINT_SPLINE(mySpline);
-
-        // For each element of the new vector, convert the x-ordinate to that
-        // of the old vector, use the fitted polynomial to determine the
-        // interpolated value at that point, and set the new vector.
-        for (i=0;i<n;i++) {
-            // Scale to [0 : overscanVector->n - 1]
-            x = ((psF32) i) * ((psF32) (overscanVector->n-1)) / ((psF32) n);
-            newVec->data.F32[i] = psSpline1DEval(mySpline, x);
-        }
-        if (mustFreeSpline ==1) {
-            psFree(mySpline);
-        }
-        //        PS_VECTOR_PRINT_F32(newVec);
-
-
-    } else {
-        psError(PS_ERR_UNKNOWN, true, "unknown fit type.  Returning NULL.\n");
-        psFree(newVec);
-        return(NULL);
-    }
-
-    psTrace(".psModule.pmSubtracBias.ScaleOverscanVector", 4,
-            "---- ScaleOverscanVector() exit ----\n");
-    return(newVec);
-}
-
-#endif
-
-// Produce an overscan vector from an array of pixels
-static psVector *overscanVector(pmOverscanOptions *overscanOpts, // Overscan options
-                                const psArray *pixels, // Array of vectors containing the pixel values
-                                psStats *myStats // Statistic to use in reducing the overscan
-                               )
-{
-    // Reduce the overscans
-    psVector *reduced = psVectorAlloc(pixels->n, PS_TYPE_F32); // Overscan for each row
-    psVector *ordinate = psVectorAlloc(pixels->n, PS_TYPE_F32); // Ordinate
-    psVector *mask = psVectorAlloc(pixels->n, PS_TYPE_U8); // Mask for fitting
-    for (int i = 0; i < pixels->n; i++) {
-        psVector *values = pixels->data[i]; // Vector with overscan values
-        if (values->n > 0) {
-            mask->data.U8[i] = 0;
-            ordinate->data.F32[i] = 2.0*(float)i/(float)pixels->n - 1.0; // Scale to [-1,1]
-            psVectorStats(myStats, values, NULL, NULL, 0);
-            double reducedVal = NAN; // Result of statistics
-            if (! p_psGetStatValue(myStats, &reducedVal)) {
-                psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result "
-                        "of statistics on row %d.\n", i);
-                return NULL;
-            }
-            reduced->data.F32[i] = reducedVal;
-        } else if (overscanOpts->fitType == PM_FIT_NONE) {
-            psError(PS_ERR_UNKNOWN, true, "The overscan is not supplied for all points on the "
-                    "image, and no fit is requested.\n");
-            return NULL;
-        } else {
-            // We'll fit this one out
-            mask->data.U8[i] = 1;
-        }
-    }
-
-    // Fit the overscan, if required
-    switch (overscanOpts->fitType) {
-    case PM_FIT_NONE:
-        // No fitting --- that's easy.
-        break;
-    case PM_FIT_POLY_ORD:
-        if (overscanOpts->poly && (overscanOpts->poly->nX != overscanOpts->order ||
-                                   overscanOpts->poly->type != PS_POLYNOMIAL_ORD)) {
-            psFree(overscanOpts->poly);
-            overscanOpts->poly = NULL;
-        }
-        if (! overscanOpts->poly) {
-            overscanOpts->poly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, overscanOpts->order);
-        }
-        psVectorFitPolynomial1D(overscanOpts->poly, mask, 1, reduced, NULL, ordinate);
-        psFree(reduced);
-        reduced = psPolynomial1DEvalVector(overscanOpts->poly, ordinate);
-        break;
-    case PM_FIT_POLY_CHEBY:
-        if (overscanOpts->poly && (overscanOpts->poly->nX != overscanOpts->order ||
-                                   overscanOpts->poly->type != PS_POLYNOMIAL_CHEB)) {
-            psFree(overscanOpts->poly);
-            overscanOpts->poly = NULL;
-        }
-        if (! overscanOpts->poly) {
-            overscanOpts->poly = psPolynomial1DAlloc(PS_POLYNOMIAL_CHEB, overscanOpts->order);
-        }
-        psVectorFitPolynomial1D(overscanOpts->poly, mask, 1, reduced, NULL, ordinate);
-        psFree(reduced);
-        reduced = psPolynomial1DEvalVector(overscanOpts->poly, ordinate);
-        break;
-    case PM_FIT_SPLINE:
-        // XXX I don't think psSpline1D is up to scratch yet --- it has no mask, and requires an
-        // input spline
-        overscanOpts->spline = psVectorFitSpline1D(reduced, ordinate);
-        psFree(reduced);
-        reduced = psSpline1DEvalVector(overscanOpts->spline, ordinate);
-        break;
-    default:
-        psError(PS_ERR_UNKNOWN, true, "Unknown value for the fitting type: %d\n", overscanOpts->fitType);
-        return NULL;
-        break;
-    }
-
-    psFree(ordinate);
-    psFree(mask);
-
-    return reduced;
-}
-
-
-
-/******************************************************************************
-XXX: The SDRS does not specify type support.  F32 is implemented here.
- *****************************************************************************/
-pmReadout *pmSubtractBias(pmReadout *in, pmOverscanOptions *overscanOpts,
-                          const pmReadout *bias, const pmReadout *dark)
-{
-    psTrace(".psModule.pmSubtracBias.pmSubtractBias", 4,
-            "---- pmSubtractBias() begin ----\n");
-    PS_ASSERT_READOUT_NON_NULL(in, NULL);
-    PS_ASSERT_READOUT_NON_EMPTY(in, NULL);
-    PS_ASSERT_READOUT_TYPE(in, PS_TYPE_F32, NULL);
-
-    psImage *image = in->image;         // The input image
-
-    // Overscan processing
-    if (overscanOpts) {
-        // Check for an unallowable pmFit.
-        if (overscanOpts->fitType != PM_FIT_NONE && overscanOpts->fitType != PM_FIT_POLY_ORD &&
-                overscanOpts->fitType != PM_FIT_POLY_CHEBY && overscanOpts->fitType != PM_FIT_SPLINE) {
-            psError(PS_ERR_UNKNOWN, true, "Invalid fit type (%d).  Returning original image.\n", overscanOpts->fitType);
-            return(in);
-        }
-
-        psList *overscans = in->bias; // List of the overscan images
-
-        psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN); // A new psStats, to avoid clobbering original
-        myStats->options = GenNewStatOptions(overscanOpts->stat);
-
-        // Reduce all overscan pixels to a single value
-        if (overscanOpts->single) {
-            psVector *pixels = psVectorAlloc(0, PS_TYPE_F32);
-            pixels->n = 0;
-            psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator
-            psImage *overscan = NULL;   // Overscan image from iterator
-            while ((overscan = psListGetAndIncrement(iter))) {
-                int index = pixels->n;  // Index
-                pixels = psVectorRealloc(pixels, pixels->n + overscan->numRows * overscan->numCols);
-                // XXX Reimplement with memcpy
-                for (int i = 0; i < overscan->numRows; i++) {
-                    for (int j = 0; j < overscan->numCols; j++) {
-                        pixels->data.F32[index++] = overscan->data.F32[i][j];
-                    }
-                }
-
-            }
-            psFree(iter);
-
-            (void)psVectorStats(myStats, pixels, NULL, NULL, 0);
-            double reduced = NAN;     // Result of statistics
-            if (! p_psGetStatValue(myStats, &reduced)) {
-                psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine result from requested statistical operation.  Returning input image.\n");
-                return(in);
-            }
-            (void)psBinaryOp(image, image, "-", psScalarAlloc((float)reduced, PS_TYPE_F32));
-        } else {
-
-            // We do the regular overscan subtraction
-
-            bool readRows = psMetadataLookupBool(NULL, in->parent->concepts, "CELL.READDIR");// Read direction
-
-            if (readRows) {
-                // The read direction is rows
-                psArray *pixels = psArrayAlloc(image->numRows); // Array of vectors containing pixels
-                for (int i = 0; i < pixels->n; i++) {
-                    psVector *values = psVectorAlloc(0, PS_TYPE_F32);
-                    values->n = 0;
-                    pixels->data[i] = values;
-                }
-
-                // Pull the pixels out into the vectors
-                psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator
-                psImage *overscan = NULL; // Overscan image from iterator
-                while ((overscan = psListGetAndIncrement(iter))) {
-                    int diff = image->row0 - overscan->row0; // Offset between the two regions
-                    for (int i = MAX(0,diff); i < MIN(image->numRows, overscan->numRows + diff); i++) {
-                        // i is row on overscan
-                        // XXX Reimplement with memcpy
-                        psVector *values = pixels->data[i];
-                        int index = values->n; // Index in the vector
-                        values = psVectorRealloc(values, values->n + overscan->numCols);
-                        for (int j = 0; j < overscan->numCols; j++) {
-                            values->data.F32[index++] = overscan->data.F32[i][j];
-                        }
-                        values->n += overscan->numCols;
-                        pixels->data[i] = values; // Update the pointer in case it's moved
-                    }
-                }
-                psFree(iter);
-
-                // Reduce the overscans
-                psVector *reduced = overscanVector(overscanOpts, pixels, myStats);
-                psFree(pixels);
-                if (! reduced) {
-                    return in;
-                }
-
-                // Subtract row by row
-                for (int i = 0; i < image->numRows; i++) {
-                    for (int j = 0; j < image->numCols; j++) {
-                        image->data.F32[i][j] -= reduced->data.F32[i];
-                    }
-                }
-                psFree(reduced);
-
-            } else {
-                // The read direction is columns
-                psArray *pixels = psArrayAlloc(image->numCols); // Array of vectors containing pixels
-                for (int i = 0; i < pixels->n; i++) {
-                    psVector *values = psVectorAlloc(0, PS_TYPE_F32);
-                    values->n = 0;
-                    pixels->data[i] = values;
-                }
-
-                // Pull the pixels out into the vectors
-                psListIterator *iter = psListIteratorAlloc(overscans, PS_LIST_HEAD, false); // Iterator
-                psImage *overscan = NULL; // Overscan image from iterator
-                while ((overscan = psListGetAndIncrement(iter))) {
-                    int diff = image->col0 - overscan->col0; // Offset between the two regions
-                    for (int i = MAX(0,diff); i < MIN(image->numCols, overscan->numCols + diff); i++) {
-                        // i is column on overscan
-                        // XXX Reimplement with memcpy
-                        psVector *values = pixels->data[i];
-                        int index = values->n; // Index in the vector
-                        values = psVectorRealloc(values, values->n + overscan->numRows);
-                        for (int j = 0; j < overscan->numRows; j++) {
-                            values->data.F32[index++] = overscan->data.F32[i][j];
-                        }
-                        values->n += overscan->numRows;
-                        pixels->data[i] = values; // Update the pointer in case it's moved
-                    }
-                }
-                psFree(iter);
-
-                // Reduce the overscans
-                psVector *reduced = overscanVector(overscanOpts, pixels, myStats);
-                psFree(pixels);
-                if (! reduced) {
-                    return in;
-                }
-
-                // Subtract column by column
-                for (int i = 0; i < image->numCols; i++) {
-                    for (int j = 0; j < image->numRows; j++) {
-                        image->data.F32[j][i] -= reduced->data.F32[i];
-                    }
-                }
-                psFree(reduced);
-            }
-        }
-        psFree(myStats);
-    } // End of overscan subtraction
-
-    // Bias frame subtraction
-    if (bias) {
-        SubtractFrame(in, bias, 1.0);
-    }
-
-    if (dark) {
-        // Get the scaling
-        float inTime = psMetadataLookupF32(NULL, in->parent->concepts, "CELL.DARKTIME");
-        float darkTime = psMetadataLookupF32(NULL, dark->parent->concepts, "CELL.DARKTIME");
-        SubtractFrame(in, dark, inTime/darkTime);
-    }
-
-    return in;
-}
-
-
Index: unk/psModules/src/imsubtract/pmSubtractBias.h
===================================================================
--- /trunk/psModules/src/imsubtract/pmSubtractBias.h	(revision 7018)
+++ 	(revision )
@@ -1,77 +1,0 @@
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// XXX WARNING: I have completely replaced this file with an OLD VERSION (that works) instead of the
-// one that was being worked on.
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-/** @file  pmSubtractBias.h
- *
- *  This file will contain a module which will subtract the detector bias
- *  in place from an input image.
- *
- *  @author GLG, MHPCC
- *
- *  @version $Revision: 1.6 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2006-04-17 18:10:08 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- */
-
-#if !defined(PM_SUBTRACT_BIAS_H)
-#define PM_SUBTRACT_BIAS_H
-
-#if HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include<stdio.h>
-#include<math.h>
-#include "pslib.h"
-
-#include "pmFPA.h"
-
-typedef enum {
-    PM_OVERSCAN_NONE,                         ///< No overscan subtraction
-    PM_OVERSCAN_EDGE,                         ///< Subtract the statistic of pixels along the to-be-determined readout direction
-    PM_OVERSCAN_ROWS,                         ///< Subtract rows
-    PM_OVERSCAN_COLUMNS,                      ///< Subtract columns
-    PM_OVERSCAN_ALL                           ///< Subtract the statistic of all pixels in overscan region
-} pmOverscanAxis;
-
-typedef enum {
-    PM_FIT_NONE,                        ///< No fit
-    PM_FIT_POLY_ORD,                    ///< Fit ordinary polynomial
-    PM_FIT_POLY_CHEBY,                  ///< Fit Chebyshev polynomial
-    PM_FIT_SPLINE                       ///< Fit cubic splines
-} pmFit;
-
-typedef struct
-{
-    // Inputs
-    bool single;                // Reduce all overscan regions to a single value?
-    pmFit fitType;              // Type of fit to overscan
-    unsigned int order;         // Order of polynomial, or number of spline pieces
-    psStats *stat;              // Statistic to use when reducing the minor direction
-    // Outputs
-    psPolynomial1D *poly;       // Result of polynomial fit
-    psSpline1D *spline;         // Result of spline fit
-}
-pmOverscanOptions;
-
-pmOverscanOptions *pmOverscanOptionsAlloc(bool single, pmFit fitType, unsigned int order, psStats *stat);
-
-pmReadout *pmSubtractBias(pmReadout *in, pmOverscanOptions *overscanOpts,
-                          const pmReadout *bias, const pmReadout *dark);
-
-#if 0
-pmReadout *pmSubtractBias(pmReadout *in,                ///< The input pmReadout image
-                          void *fitSpec,                ///< A polynomial or spline, defining the fit type.
-                          const psList *overscans,      ///< A psList of overscan images
-                          pmOverscanAxis overScanAxis,  ///< Defines how overscans are applied
-                          psStats *stat,                ///< The statistic to be used in combining overscan data
-                          int nBin,                     ///< The amount of binning to be done image pixels.
-                          pmFit fit,                    ///< PM_FIT_SPLINE, PM_FIT_POLYNOMIAL, or PM_FIT_NONE
-                          const pmReadout *bias);       ///< A possibly NULL bias pmReadout which is to be subtracted
-#endif
-
-#endif
Index: unk/psModules/src/imsubtract/pmSubtractSky.c
===================================================================
--- /trunk/psModules/src/imsubtract/pmSubtractSky.c	(revision 7018)
+++ 	(revision )
@@ -1,741 +1,0 @@
-/** @file  pmSubtractSky.c
- *
- *  This file will contain a module which will create a model of the
- *  background sky and subtract that from the input image.
- *
- *  @author GLG, MHPCC
- *
- *  @version $Revision: 1.5 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2006-03-04 01:01:33 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- *
- *
- */
-
-#include<stdio.h>
-#include<math.h>
-#include "pslib.h"
-#include "pmSubtractSky.h"
-
-// XXX: Get rid of the.  Create pmUtils.h
-psImage *p_psDetermineTrimmedImage(
-    pmReadout *in
-);
-
-/******************************************************************************
-DetermineNumBits(data): This routine takes an enum psStatsOptions as an
-argument and returns the number of non-zero bits.
- 
-XXX: This code is duplicated in the ReadoutCombine file.
- *****************************************************************************/
-static psS32 DetermineNumBits(psStatsOptions data)
-{
-    psTrace("SubtractSky.DetermineNumBits", 4, "Calling DetermineNumBits(0x%x)\n", data);
-
-    psS32 i;
-    psU64 tmpData = data;
-    psS32 numBits = 0;
-
-    for (i=0;i<(8 * sizeof(psStatsOptions));i++) {
-        if (0x0001 & tmpData) {
-            numBits++;
-        }
-        tmpData = tmpData >> 1;
-    }
-
-    psTrace("SubtractSky.DetermineNumBits", 4,
-            "Calling DetermineNumBits(0x%x) -> %d\n", data, numBits);
-    return(numBits);
-}
-
-/******************************************************************************
-getHighestPriorityStatOption(statOptions): this routine takes as input a
-psStats->options with multiple options set and returns one with a single
-option set according to the precedence set in the SDRS.
- *****************************************************************************/
-static psU64 getHighestPriorityStatOption(psU64 statOptions)
-{
-    psTrace("SubtractSky.getHighestPriorityStatOption", 4,
-            "Calling getHighestPriorityStatOption(0x%x)\n", statOptions);
-
-    if (statOptions & PS_STAT_SAMPLE_MEAN) {
-        return(PS_STAT_SAMPLE_MEAN);
-    } else if (statOptions & PS_STAT_SAMPLE_MEDIAN) {
-        return(PS_STAT_SAMPLE_MEDIAN);
-    } else if (statOptions & PS_STAT_CLIPPED_MEAN) {
-        return(PS_STAT_CLIPPED_MEAN);
-    } else if (statOptions & PS_STAT_FITTED_MEAN) {
-        return(PS_STAT_FITTED_MEAN);
-    } else if (statOptions & PS_STAT_ROBUST_MEDIAN) {
-        return(PS_STAT_ROBUST_MEDIAN);
-    }
-    psError(PS_ERR_UNKNOWN, true, "Unallowable option requested for statistically binning image pixels.\n");
-    return(-1);
-    // XXX
-    //else if (statOptions & PS_STAT_ROBUST_MODE) {
-    //    return(PS_STAT_ROBUST_MODE);
-    //}
-}
-
-/******************************************************************************
-psImage *binImage(origImage, binFactor, statOptions): This routine takes an
-input psImage and scales it smaller by a factor of binFactor.  The statistic
-used in combining input pixels is specified in statOptions.
- 
-XXX: use static vectors for myStats, binVector and binMask.
-XXX: I coded this before I was aware of a psLib reBin function.  I don't
-use this function in this module.  I'm keeping it here in the event that
-requirements change and we might need a custom reBin function.
- *****************************************************************************/
-/*
-static psImage *binImage(psImage *origImage,
-                         int binFactor,
-                         psStatsOptions statOptions)
-{
-    psTrace("SubtractSky.binImage", 4, "Calling binImage(%d)\n", binFactor);
- 
-    if (binFactor <= 0) {
-        psLogMsg(__func__, PS_LOG_WARN,
-                 "WARNING: binImage(): binFactor is %d\n", binFactor);
-        return(origImage);
-    }
-    if (binFactor == 1) {
-        return(origImage);
-    }
- 
-    psVector *binVector = psVectorAlloc(binFactor * binFactor, PS_TYPE_F32);
-    psVector *binMask = psVectorAlloc(binFactor * binFactor, PS_TYPE_U8);
-    psStats *myStats = psStatsAlloc(statOptions);
- 
-    for (psS32 row = 0; row < origImage->numRows ; row+=binFactor) {
-        for (psS32 col = 0; col < origImage->numCols ; col+=binFactor) {
-            psS32 count = 0;
-            for (psS32 binRow = 0; binRow <= binFactor ; binRow++) {
-                for (psS32 binCol = 0; binCol <= binFactor ; binCol++) {
-                    if (((row + binRow) < origImage->numRows) &&
-                            ((col + binCol) < origImage->numCols)) {
-                        binVector->data.F32[count] =
-                            origImage->data.F32[row + binRow][col + binCol];
-                        binMask->data.U8[count] = 0;
-                    } else {
-                        binVector->data.F32[count] = 0.0;
-                        binMask->data.U8[count] = 1;
-                    }
-                    count++;
-                }
-            }
-            psStats *rc1 = psVectorStats(myStats, binVector, NULL, binMask, 1);
-            if (rc1 == NULL) {
-                psError(PS_ERR_UNKNOWN, false, "psVectorStats(): could not perform requested statistical operation.  Returning in image.\n");
-                return(origImage);
-            }
-            psF64 statValue;
-            psBool rc = p_psGetStatValue(rc1, &statValue);
- 
-            if (rc == true) {
-                origImage->data.F32[row][col] = (psF32) statValue;
-            } else {
-                origImage->data.F32[row][col] = 0.0;
-                psLogMsg(__func__, PS_LOG_WARN,
-                         "WARNING: pmSubtractSky(), binImage(): p_psGetStatValue() was FALSE\n");
-            }
-        }
-    }
-    psFree(binVector);
-    psFree(binMask);
-    psFree(myStats);
- 
-    psTrace("SubtractSky.binImage", 4, "Exiting binImage(%d)\n", binFactor);
-    return(origImage);
-}
-*/
-
-/******************************************************************************
-CalculatePolyTerms(xOrder, yOrder): this routine will calculate the number of
-coefficients (or terms) in a 2-D polynomial of order (xOrder, yOrder).
- 
-XXX: Use your brain and figure out the analytical expression.
- 
-XXX: Why isn't it simply (xOrder+1) * (yOrder+1)?
- *****************************************************************************/
-static psS32 CalculatePolyTerms(psS32 xOrder, psS32 yOrder)
-{
-    psTrace("SubtractSky.CalculatePolyTerms", 4,
-            "Calling CalculatePolyTerms(%d, %d)\n", xOrder, yOrder);
-
-    psS32 maxOrder = PS_MAX(xOrder, yOrder);
-    psS32 localPolyTerms = 0;
-    psS32 order = 0;
-    psS32 num=0;
-
-    for (order=0;order<=maxOrder;order++) {
-        for (num=0;num<=order;num++) {
-            if (((order-num) <= xOrder) && (num <= yOrder)) {
-                localPolyTerms++;
-            }
-        }
-    }
-    psTrace("SubtractSky.CalculatePolyTerms", 4,
-            "Exiting CalculatePolyTerms(%d, %d) -> %d\n", xOrder, yOrder, localPolyTerms);
-    return(localPolyTerms);
-
-    //    return((xOrder+1) * (yOrder+1));
-}
-
-/******************************************************************************
-buildPolyTerms(): this routine computes a 2-D array polyTerms[][] that holds
-terms for the polynomial that is used to model the sky background.  We use
-this array primarily for convenience in computations involving sky model
-polynomials.  It is defined as:
-    polyTerms[i][0] = the power to which X is raised in the i-th term of in an
-    poly-order sky background polynomial.
- 
-    polyTerms[i][1] = the power to which Y is raised in the i-th term of in an
-    poly-order sky background polynomial.
- *****************************************************************************/
-static psS32 **buildPolyTerms(psS32 xOrder, psS32 yOrder)
-{
-    psTrace("SubtractSky.buildPolyTerms", 4,
-            "Calling buildPolyTerms(%d, %d)\n", xOrder, yOrder);
-
-    psS32 i=0;
-    psS32 order = 0;
-    psS32 num=0;
-    psS32 localPolyTerms = CalculatePolyTerms(xOrder, yOrder);
-    psS32 maxOrder = PS_MAX(xOrder, yOrder);
-
-    // Create the data structure which we hold the xy order of each coeff.
-    psS32 **polyTerms = (psS32 **) psAlloc(localPolyTerms * sizeof(psS32 *));
-    for (i=0; i < localPolyTerms ; i++) {
-        polyTerms[i] = (psS32 *) psAlloc(2 * sizeof(psS32));
-    }
-
-    i=0;
-    // This code segment loops through each term i in the polynomial and
-    // calculates the power to which x/y are raised in that i-th term.
-    // We first do the 0-order terms, then the 1-order terms, etc.
-    for (order=0;order<=maxOrder;order++) {
-        for (num=0;num<=order;num++) {
-            if (((order-num) <= xOrder) && (num <= yOrder)) {
-                polyTerms[i][0] = order-num;
-                polyTerms[i][1] = num;
-                i++;
-            }
-        }
-    }
-
-    if (psTraceGetLevel(".psModule.pmSubtractSky.buildPolyTerms") >= 10) {
-        for (i=0; i < localPolyTerms ; i++) {
-            printf("x^%d * y^%d\n", polyTerms[i][0], polyTerms[i][1]);
-        }
-    }
-
-    psTrace("SubtractSky.buildPolyTerms", 4,
-            "Exiting buildPolyTerms(%d, %d)\n", xOrder, yOrder);
-    return(polyTerms);
-}
-
-/******************************************************************************
-This procedure calculates various combinations of powers of x and y and stores
-them in the data structure p_psPolySums[][].  After it completes:
- 
-    p_psPolySums[i][j] == x^i * y^j
- 
-XXX: Use a psImage for the p_psPolySums data structure?
-XXX: p_psPolySums: should this be a global?  Did you get the storage classifier
-     and name correct?
-XXX: Use variable size arrays for p_psPolySums[][].
-XXX: Must initialize p_psPolySums[][]?
- *****************************************************************************/
-#define PS_MAX_POLYNOMIAL_ORDER 20
-
-psF64 p_psPolySums[PS_MAX_POLYNOMIAL_ORDER+1][PS_MAX_POLYNOMIAL_ORDER+1];
-static void buildSums(psF64 x,
-                      psF64 y,
-                      psS32 xOrder,
-                      psS32 yOrder)
-{
-    psTrace("SubtractSky.buildPolyTerms", 4,
-            "Calling buildPolyTerms(%d, %d)\n", xOrder, yOrder);
-
-    psS32 i = 0;
-    psS32 j = 0;
-    psF64 xSum = 0.0;
-    psF64 ySum = 0.0;
-
-    xSum = 1.0;
-    ySum = 1.0;
-    for(i=0;i<=xOrder;i++) {
-        ySum = xSum;
-        for(j=0;j<=yOrder;j++) {
-            p_psPolySums[i][j] = ySum;
-            ySum*= y;
-        }
-        xSum*= x;
-    }
-    psTrace("SubtractSky.buildPolyTerms", 4,
-            "Exiting buildPolyTerms(%d, %d)\n", xOrder, yOrder);
-}
-
-/******************************************************************************
-ImageFitPolynomial(myPoly, dataImage, maskImage): this private routine takes
-an input image along with a mask and fits a polynomial to it.  The degree of
-the polynomial is specified by input parameter myPoly, and need not be
-symmetrical in orders of X and Y.  The polynomial must be type
-PS_POLYNOMIAL_ORD.  If there are not enough rows or columns in the input image
-for the order of the polynomial, then that order is reduced.  The algorithm
-used in this routine is based on that of the pilot project ADD, but is not
-documented anywhere.
- 
-XXX: Different trace message facilities in use here.
- *****************************************************************************/
-static psPolynomial2D *ImageFitPolynomial(
-    psPolynomial2D *myPoly,
-    psImage *dataImage,
-    psImage *maskImage)
-{
-    psTrace("SubtractSky.ImageFitPolynomial", 4,
-            "Calling ImageFitPolynomial()\n");
-    PS_ASSERT_POLY_NON_NULL(myPoly, NULL);
-    PS_ASSERT_POLY_TYPE(myPoly, PS_POLYNOMIAL_ORD, NULL);
-    PS_ASSERT_IMAGE_NON_NULL(dataImage, NULL);
-    PS_ASSERT_IMAGE_NON_EMPTY(dataImage, NULL);
-    PS_ASSERT_IMAGE_TYPE(dataImage, PS_TYPE_F32, NULL);
-    PS_ASSERT_IMAGE_NON_NULL(maskImage, NULL);
-    PS_ASSERT_IMAGE_NON_EMPTY(maskImage, NULL);
-    PS_ASSERT_IMAGE_TYPE(maskImage, PS_TYPE_U8, NULL);
-    PS_ASSERT_IMAGES_SIZE_EQUAL(dataImage, maskImage, NULL);
-    psS32 oldPolyX = -1;
-    psS32 oldPolyY = -1;
-
-    // The matrix equations become singular if there are more powers of X
-    // in myPoly then there are rows of the image.  I think.  Similarly for
-    // powers of Y and columns.  So.  Here we reduce the complexity of the
-    // polynomial if there are not enough rows/columns in the input image.
-
-    if ((myPoly->nX + 1) > dataImage->numRows) {
-        psLogMsg(__func__, PS_LOG_WARN,
-                 "WARNING: ImageFitPolynomial(): Reducing polynomial complexity in x-dimension.\n");
-        oldPolyX = myPoly->nX;
-        myPoly->nX = dataImage->numRows - 1;
-    }
-    if ((myPoly->nY + 1) > dataImage->numCols) {
-        psLogMsg(__func__, PS_LOG_WARN,
-                 "WARNING: ImageFitPolynomial(): Reducing polynomial complexity in y-dimension.\n");
-        oldPolyY = myPoly->nY;
-        myPoly->nY = dataImage->numCols - 1;
-    }
-    psS32 i;
-    psS32 j;
-    psS32 x;
-    psS32 y;
-    psS32 aRow;
-    psS32 aCol;
-    psS32 **polyTerms = buildPolyTerms(myPoly->nX, myPoly->nY);
-    // We determine how many coefficients will be in the polynomial that we
-    // are fitting to this image.
-    psS32 localPolyTerms = CalculatePolyTerms(myPoly->nX, myPoly->nY);
-    psImage *A = psImageAlloc(localPolyTerms, localPolyTerms, PS_TYPE_F64);
-    psImage *Aout = psImageAlloc(localPolyTerms, localPolyTerms, PS_TYPE_F64);
-    psVector *B = psVectorAlloc(localPolyTerms, PS_TYPE_F64);
-    B->n = B->nalloc;
-    psVector *outPerm = NULL;
-
-    //
-    // Initialize A matrix and B vector.
-    //
-    PS_IMAGE_SET_F64(A, 0.0);
-    PS_VECTOR_SET_F64(B, 0.0);
-
-    //
-    // We build the A matrix and B vector.
-    //
-    for (x=0;x<dataImage->numRows;x++) {
-        for (y=0;y<dataImage->numCols;y++) {
-            if (maskImage->data.U8[x][y] == 0) {
-                buildSums((psF64) x, (psF64) y, myPoly->nX, myPoly->nY);
-
-                /************************************************************
-                This code dervies from equation (7) of the pilot ADD.  However,
-                it is not exactly the same in that the order of the polynomial
-                may be different in X And Y.
-
-                Equation (7) from the pilot ADD describes 16 linear equations.
-                The i-th equation is simply the partial derivative of the
-                sky background polynomial (1) w.r.t. to the i-th term in
-                that polynomial.  The i-th equation is stored in row i of
-                matrix A[][] (matrix A[][] has origin (1,1), not (0,0)).  To
-                compute A[i][j] we simply multiply the j-th term of the Sky
-                Background Polynomial (SBP) by the i-th term of SBP.
-                ************************************************************/
-                for (aRow=0;aRow<localPolyTerms;aRow++) {
-                    for (aCol=0;aCol<localPolyTerms;aCol++) {
-                        A->data.F64[aRow][aCol]+=
-                            (p_psPolySums[ polyTerms[aCol][0] ][ polyTerms[aCol][1] ] *
-                             p_psPolySums[ polyTerms[aRow][0] ][ polyTerms[aRow][1] ]);
-                    }
-                }
-                // Build the B[] vector, which is the right-hand side of (7).
-                for (i=0;i<localPolyTerms;i++) {
-                    B->data.F64[i]+= dataImage->data.F32[x][y] *
-                                     p_psPolySums[ polyTerms[i][0] ][ polyTerms[i][1] ];
-                }
-            }
-        }
-    }
-
-    if (psTraceGetLevel(".psModule.pmSubtractSky.ImageFitPolynomial") >= 8) {
-        for (aRow=0;aRow<localPolyTerms;aRow++) {
-            for (aCol=0;aCol<localPolyTerms;aCol++) {
-                printf("A[%d][%d] is %f\n", aRow, aCol,
-                       A->data.F64[aRow][aCol]);
-            }
-        }
-
-        for (i=0;i<=localPolyTerms;i++) {
-            printf("B[%d] is %f\n", i, B->data.F64[i]);
-        }
-    }
-
-    //
-    // Solve the matrix equations for the polynomial coefficients C.
-    // XXX: How do we know if these matrix operations were successful?
-    //
-    Aout = psMatrixLUD(Aout, &outPerm, A);
-    PS_ASSERT_IMAGE_NON_NULL(Aout, NULL);
-    PS_ASSERT_IMAGE_NON_EMPTY(Aout, NULL);
-    psVector *C = psVectorAlloc(localPolyTerms, PS_TYPE_F64);
-    C->n = C->nalloc;
-    psMatrixLUSolve(C, Aout, B, outPerm);
-
-    //
-    // Set the appropriate coefficients in the myPoly structure.
-    //
-    for (i=0;i<localPolyTerms;i++) {
-        myPoly->coeff[ polyTerms[i][0] ][ polyTerms[i][1] ] = C->data.F64[i];
-        psTrace(".psModule.pmSubtractSky.ImageFitPolynomial", 6,
-                "myPoly->coeff[%d][%d] is %f\n", polyTerms[i][0], polyTerms[i][1], myPoly->coeff[ polyTerms[i][0] ][ polyTerms[i][1] ]);
-    }
-
-    //
-    // Free data structures that were allocated in this module.
-    //
-    for (i=0;i<localPolyTerms;i++) {
-        psFree(polyTerms[i]);
-    }
-    psFree(polyTerms);
-    psFree(A);
-    psFree(Aout);
-    psFree(B);
-    psFree(C);
-    psFree(outPerm);
-
-    //
-    // We restore the original size of the polynomial and set remaining
-    // coefficients to 0.0, if necessary.
-    //
-    // XXX: Verify this works after poly nOrder/nTerm change.
-    //
-    if (oldPolyX != -1) {
-        myPoly->nX = oldPolyX;
-        for (i=oldPolyX ; i < (1 + myPoly->nX) ; i++) {
-            for (j=0;j<(1 + myPoly->nY) ; j++) {
-                myPoly->coeff[i][j] = 0.0;
-            }
-        }
-    }
-    if (oldPolyY != -1) {
-        myPoly->nY = oldPolyY;
-        for (i=0 ; i < (1 + myPoly->nX) ; i++) {
-            for (j=oldPolyY;j < (1 + myPoly->nY) ; j++) {
-                myPoly->coeff[i][j] = 0.0;
-            }
-        }
-    }
-
-    psTrace("SubtractSky.ImageFitPolynomial", 4,
-            "Exiting ImageFitPolynomial()\n");
-    //    psTrace(".psModule.pmSubtractSky.ImageFitPolynomial", 4,
-    //            "---- ImageFitPolynomial() end successfully ----\n");
-    return(myPoly);
-}
-
-
-/******************************************************************************
-pmReadout pmSubtractSky():
- 
-XXX: use static vectors for myStats, and the binned image
- 
-XXX: The SDR is silent about types.  PS_TYPE_F32 is implemented here.
- 
-XXX: Sync the psTrace message facilities.
- *****************************************************************************/
-pmReadout *pmSubtractSky(pmReadout *in,
-                         void *fitSpec,
-                         psFit fit,
-                         psS32 binFactor,
-                         psStats *stats,
-                         psF32 clipSD)
-{
-    PS_ASSERT_READOUT_NON_NULL(in, NULL);
-    PS_ASSERT_READOUT_NON_EMPTY(in, NULL);
-    PS_ASSERT_READOUT_TYPE(in, PS_TYPE_F32, NULL);
-    PS_WARN_PTR_NON_NULL(in->parent);
-    if (in->parent != NULL) {
-        PS_WARN_PTR_NON_NULL(in->parent->concepts);
-    }
-    psTrace(".psModule.pmSubtractSky", 4,
-            "---- pmSubtractSky() begin ----\n");
-
-    if ((fit != PM_FIT_NONE) &&
-            (fit != PM_FIT_POLYNOMIAL) &&
-            (fit != PM_FIT_SPLINE)) {
-        psError(PS_ERR_UNKNOWN, true, "psFit is unallowable (%d).  Returning in image.\n", fit);
-        return(in);
-    }
-
-    psStatsOptions statOptions = 0;
-
-    //
-    // Return the original input readout if the fit specs are poorly defined.
-    // No warning or error messages should be generated.
-    //
-    if ((fitSpec == NULL) ||
-            ((fit == PM_FIT_NONE) || (fit == PM_FIT_SPLINE))) {
-        //        psLogMsg(__func__, PS_LOG_WARN, "Fit specs are poorly defined.  Returning in image.\n");
-        return(in);
-    }
-
-    //
-    // Determine trimmed image from metadata.
-    //
-
-    psImage *trimmedImg = p_psDetermineTrimmedImage(in);
-    psImage *binnedImage = NULL;
-    psPolynomial2D *myPoly = NULL;
-    psImage *binnedMaskImage = NULL;
-    psU32 oldStatOptions = 0;
-
-    //
-    // Determine which statistic to use when binning pixels, if any.
-    //
-    if (stats != NULL) {
-        statOptions = stats->options;
-        if (1 < DetermineNumBits(statOptions)) {
-            psLogMsg(__func__, PS_LOG_WARN, "WARNING: Multiple statistical options have been requested.\n");
-            statOptions = getHighestPriorityStatOption(statOptions);
-            if (statOptions == -1) {
-                psError(PS_ERR_UNKNOWN, true, "Not allowable stats->option was specified.  Returning in image.\n");
-                return(in);
-            }
-            // Save old input "stats" parameter.
-            oldStatOptions = stats->options;
-            stats->options = statOptions;
-        }
-        if (0 == DetermineNumBits(statOptions)) {
-            psLogMsg(__func__, PS_LOG_WARN,
-                     "WARNING: pmSubtractSky(): no stats->options was requested\n");
-        }
-    }
-
-    //
-    // Generate required warning messages.
-    //
-    if (binFactor <= 0) {
-        psLogMsg(__func__, PS_LOG_WARN,
-                 "WARNING: pmSubtractSky(): binFactor is %d\n", binFactor);
-    }
-    if (stats == NULL) {
-        psLogMsg(__func__, PS_LOG_WARN,
-                 "WARNING: pmSubtractSky(): input parameter stats is NULL\n");
-    }
-
-    //
-    // Bin the input image according to input parameters.
-    // Create a new binned image mask.
-    //
-    if ((binFactor <= 1) || (stats == NULL) || (0 == DetermineNumBits(statOptions))) {
-        // No binning is required here.  Simply create a copy of the image
-        // and a mask.
-        binnedImage = psImageCopy(binnedImage, trimmedImg, PS_TYPE_F32);
-        if (binnedImage == NULL) {
-            psError(PS_ERR_UNKNOWN, false, "psImageCopy() returned NULL.  Returning in image.\n");
-            return(in);
-        }
-
-        if (in->mask != NULL) {
-            binnedMaskImage = psImageCopy(binnedMaskImage, in->mask, PS_TYPE_U8);
-            if (binnedMaskImage == NULL) {
-                psError(PS_ERR_UNKNOWN, false, "psImageCopy() returned NULL.  Returning in image.\n");
-                psFree(binnedImage);
-                return(in);
-            }
-        } else {
-            binnedMaskImage = psImageAlloc(binnedImage->numCols,
-                                           binnedImage->numRows,
-                                           PS_TYPE_U8);
-            PS_IMAGE_SET_U8(binnedMaskImage, 0);
-        }
-    } else {
-        binnedImage = psImageRebin(NULL, trimmedImg, in->mask, 0, binFactor, stats);
-        if (binnedImage == NULL) {
-            psError(PS_ERR_UNKNOWN, false, "psImageRebin() returned NULL.  Returning in image.\n");
-            return(in);
-        }
-        binnedMaskImage = psImageAlloc(binnedImage->numCols,
-                                       binnedImage->numRows,
-                                       PS_TYPE_U8);
-        PS_IMAGE_SET_U8(binnedMaskImage, 0);
-    }
-    psTrace(".psModule.pmSubtractSky", 4,
-            "binnedImage size is (%d, %d)\n", binnedImage->numRows, binnedImage->numCols);
-
-    //
-    // Clip pixels that are outside the acceptable range.
-    //
-    if (clipSD <= 0.0) {
-        psLogMsg(__func__, PS_LOG_WARN,
-                 "WARNING: pmSubtractSky(): clipSD is %f\n", clipSD);
-    } else {
-        // Determine the mean and standard deviation of the binned image.
-        psF64 binnedMean;
-        psF64 binnedStdev;
-        psStats *myStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
-        psStats *rc =  psImageStats(myStats, binnedImage, NULL, 0);
-        if (rc == NULL) {
-            psError(PS_ERR_UNKNOWN, false, "psImageStats(): could not perform requested statistical operation.  Returning in image.\n");
-            return(in);
-        }
-        if (false == p_psGetStatValue(rc, &binnedMean)) {
-            psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine requested statistical operation.  Returning in image.\n");
-            return(in);
-        }
-        psTrace(".psModule.pmSubtractSky", 6,
-                "binned Mean is %f\n", binnedMean);
-
-        myStats->options = PS_STAT_SAMPLE_STDEV;
-        rc =  psImageStats(myStats, binnedImage, NULL, 0);
-        if (rc == NULL) {
-            psError(PS_ERR_UNKNOWN, false, "psImageStats(): could not perform requested statistical operation.  Returning in image.\n");
-            return(in);
-        }
-        if (false == p_psGetStatValue(myStats, &binnedStdev)) {
-            psError(PS_ERR_UNKNOWN, false, "p_psGetStatValue(): could not determine requested statistical operation.  Returning in image.\n");
-            return(in);
-        }
-        psFree(myStats);
-        psTrace(".psModule.pmSubtractSky", 6,
-                "binned StDev is %f\n", binnedStdev);
-
-        // Clip all pixels which are more than clipSD sigmas from the mean.
-        psTrace(".psModule.pmSubtractSky", 6,
-                "clipSD is %f\n", clipSD);
-
-        for (psS32 row = 0; row < binnedImage->numRows ; row++) {
-            for (psS32 col = 0; col < binnedImage->numCols ; col++) {
-                if (fabs(binnedImage->data.F32[row][col] - binnedMean) >
-                        (clipSD * binnedStdev)) {
-                    binnedMaskImage->data.U8[row][col] = 1;
-                }
-            }
-        }
-    }
-
-    //
-    // Fit the polynomial to the binned image
-    //
-    if (fit == PM_FIT_POLYNOMIAL) {
-        myPoly = (psPolynomial2D *) fitSpec;
-        PS_ASSERT_POLY_NON_NULL(myPoly, NULL);
-        PS_ASSERT_POLY_TYPE(myPoly, PS_POLYNOMIAL_ORD, NULL);
-
-        myPoly = ImageFitPolynomial(myPoly, binnedImage, binnedMaskImage);
-
-        if (myPoly != NULL) {
-            // Set the pixels in the binned image to that of the polynomial.
-            binnedImage = psImageEvalPolynomial(binnedImage, myPoly);
-            if (binnedImage == NULL) {
-                psError(PS_ERR_UNKNOWN, false, "psImageEvalPolynomial() returned NULL.  Returning in image.\n");
-                psFree(binnedMaskImage);
-                if (!((binFactor <= 1) || (stats == NULL))) {
-                    psFree(binnedImage);
-                }
-                if (oldStatOptions != 0) {
-                    stats->options = statOptions;
-                }
-                return(in);
-            }
-        } else {
-            psLogMsg(__func__, PS_LOG_WARN,
-                     "WARNING: pmSubtractSky(): could not model sky with a polynomial.\n");
-            psFree(binnedMaskImage);
-            if (!((binFactor <= 1) || (stats == NULL))) {
-                psFree(binnedImage);
-            }
-            if (oldStatOptions != 0) {
-                stats->options = statOptions;
-            }
-            return(in);
-        }
-    } else {
-        // We shouldn't get here since we check this above.
-        psError(PS_ERR_UNKNOWN, true, "Unallowable fit type.  Returning in image.\n");
-        psFree(binnedMaskImage);
-        if (!((binFactor <= 1) || (stats == NULL))) {
-            psFree(binnedImage);
-        }
-        if (oldStatOptions != 0) {
-            stats->options = statOptions;
-        }
-        return(in);
-    }
-
-    //
-    //Subtract the polynomially fitted image from the original image
-    //
-    if (binFactor <= 1) {
-        // The binned image is the same size as the original image.
-        for (psS32 row = 0; row < trimmedImg->numRows ; row++) {
-            for (psS32 col = 0; col < trimmedImg->numCols ; col++) {
-                trimmedImg->data.F32[row][col]-= binnedImage->data.F32[row][col];
-            }
-        }
-    } else {
-        for (psS32 row = 0; row < trimmedImg->numRows ; row++) {
-            for (psS32 col = 0; col < trimmedImg->numCols ; col++) {
-                // We calculate the F32 value of the pixel coordinates in the
-                // binned image and then use a pixel interpolation routine to
-                // determine the value of the pixel at that location.
-                psF32 binRowF64 = ((psF32) row) / ((psF32) binFactor);
-                psF32 binColF64 = ((psF32) col) / ((psF32) binFactor);
-
-                // We add 0.5 to the pixel locations since the pixel
-                // interpolation routine defines the location of pixel
-                // (i, j) as (i+0.5, j+0.5).
-                binRowF64+= 0.5;
-                binColF64+= 0.5;
-
-                psF32 binPixel = (psF32) psImagePixelInterpolate(
-                                     binnedImage, binColF64, binRowF64,
-                                     NULL, 0, 0.0, PS_INTERPOLATE_BILINEAR);
-                trimmedImg->data.F32[row][col]-= binPixel;
-
-                psTrace(".psModule.pmSubtractSky", 8,
-                        "image[%d][%d] <--> binnedImage[%.2f][%.2f]: %f\n",
-                        row, col, binRowF64-0.5, binColF64-0.5, binPixel);
-            }
-        }
-
-    }
-    psFree(binnedMaskImage);
-    psFree(binnedImage);
-    if (oldStatOptions != 0) {
-        stats->options = statOptions;
-    }
-
-    psTrace(".psModule.pmSubtractSky", 4,
-            "---- pmSubtractSky() exit successfully ----\n");
-    return(in);
-}
Index: unk/psModules/src/imsubtract/pmSubtractSky.h
===================================================================
--- /trunk/psModules/src/imsubtract/pmSubtractSky.h	(revision 7018)
+++ 	(revision )
@@ -1,40 +1,0 @@
-/** @file  pmSubtractSky.h
- *
- *  This file will contain a module which will create a model of the
- *  background sky and subtract that from the input image.
- *
- *  @author GLG, MHPCC
- *
- *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2006-04-17 18:10:08 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- */
-
-#if !defined(PM_SUBTRACT_SKY_H)
-#define PM_SUBTRACT_SKY_H
-
-#if HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include<stdio.h>
-#include<math.h>
-#include "pslib.h"
-#include "pmFPA.h"
-
-// XXX: this is pmFit in pmSubtractBias.c, named psFit here.
-typedef enum {
-    PM_FIT_NONE,                              ///< No fit
-    PM_FIT_POLYNOMIAL,                        ///< Fit polynomial
-    PM_FIT_SPLINE                             ///< Fit cubic splines
-} psFit;
-
-pmReadout *pmSubtractSky(pmReadout *in,
-                         void *fitSpec,
-                         psFit fit,
-                         int binFactor,
-                         psStats *stats,
-                         float clipSD);
-#endif
Index: /trunk/psModules/src/psmodules.h
===================================================================
--- /trunk/psModules/src/psmodules.h	(revision 7018)
+++ /trunk/psModules/src/psmodules.h	(revision 7019)
@@ -21,9 +21,11 @@
 #include <pmConfig.h>
 
-// the following headers are from psModule:astrom
+// the following headers are from psModule:concepts
 #include <pmConcepts.h>
 #include <pmConceptsRead.h>
 #include <pmConceptsStandard.h>
 #include <pmConceptsWrite.h>
+
+// the following headers are from psModule:camera
 #include <pmHDU.h>
 #include <pmHDUUtils.h>
@@ -39,4 +41,6 @@
 #include <pmFPA_JPEG.h>
 #include <pmReadout.h>
+
+// the following headers are from psModule:astrom
 #include <pmAstrometryObjects.h>
 #include <pmChipMosaic.h>
@@ -49,13 +53,11 @@
 #include <pmMaskBadPixelsErrors.h>
 #include <pmNonLinear.h>
+#include <pmSubtractBias.h>
+// #include <pmSubtractSky.h>
 
 // the following headers are from psModule:imcombine
 #include <pmImageCombine.h>
+#include <pmImageSubtract.h>
 // #include <pmReadoutCombine.h>
-
-// the following headers are from psModule:detrend
-#include <pmSubtractBias.h>
-#include <pmImageSubtract.h>
-// #include <pmSubtractSky.h>
 
 // the following headers are from psModule:objects
Index: /trunk/psModules/test/camera/Makefile.am
===================================================================
--- /trunk/psModules/test/camera/Makefile.am	(revision 7018)
+++ /trunk/psModules/test/camera/Makefile.am	(revision 7019)
@@ -3,6 +3,10 @@
 AM_LDFLAGS = -L$(top_builddir)/src -lpsmodule $(PSMODULE_LIBS)
 AM_CFLAGS  = @AM_CFLAGS@ $(PSMODULE_CFLAGS) $(SRCINC)
+AM_CPPFLAGS = $(SRCINC) $(PSLIB_CFLAGS)
 
-TESTS =
+TESTS =\
+    tst_pmAstrometry
+
+tst_pmAstrometry_SOURCES = tst_pmAstrometry.c
 
 check_PROGRAMS = $(TESTS)
