Index: /trunk/psModules/src/camera/pmFPAfile.c
===================================================================
--- /trunk/psModules/src/camera/pmFPAfile.c	(revision 16840)
+++ /trunk/psModules/src/camera/pmFPAfile.c	(revision 16841)
@@ -435,4 +435,7 @@
   if (!strcasecmp (type, "FRINGE")) {
     return PM_FPA_FILE_FRINGE;
+  }
+  if (!strcasecmp (type, "DARK"))     {
+    return PM_FPA_FILE_DARK;
   }
   if (!strcasecmp (type, "HEADER"))     {
@@ -473,4 +476,6 @@
     case PM_FPA_FILE_FRINGE:
       return ("FRINGE");
+    case PM_FPA_FILE_DARK:
+      return("DARK");
     case PM_FPA_FILE_HEADER:
       return ("HEADER");
Index: /trunk/psModules/src/camera/pmFPAfile.h
===================================================================
--- /trunk/psModules/src/camera/pmFPAfile.h	(revision 16840)
+++ /trunk/psModules/src/camera/pmFPAfile.h	(revision 16841)
@@ -4,6 +4,6 @@
  * @author EAM, IfA
  *
- * @version $Revision: 1.28 $ $Name: not supported by cvs2svn $
- * @date $Date: 2008-02-07 23:45:56 $
+ * @version $Revision: 1.29 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-03-06 20:47:50 $
  * Copyright 2004-2005 Institute for Astronomy, University of Hawaii
  */
@@ -28,10 +28,11 @@
     PM_FPA_FILE_RAW,
     PM_FPA_FILE_IMAGE,
+    PM_FPA_FILE_MASK,
+    PM_FPA_FILE_WEIGHT,
+    PM_FPA_FILE_FRINGE,
+    PM_FPA_FILE_DARK,
     PM_FPA_FILE_PSF,
     PM_FPA_FILE_JPEG,
     PM_FPA_FILE_KAPA,
-    PM_FPA_FILE_MASK,
-    PM_FPA_FILE_WEIGHT,
-    PM_FPA_FILE_FRINGE,
     PM_FPA_FILE_HEADER,
     PM_FPA_FILE_ASTROM,
Index: /trunk/psModules/src/camera/pmFPAfileFitsIO.c
===================================================================
--- /trunk/psModules/src/camera/pmFPAfileFitsIO.c	(revision 16840)
+++ /trunk/psModules/src/camera/pmFPAfileFitsIO.c	(revision 16841)
@@ -20,4 +20,5 @@
 #include "pmFPACopy.h"
 #include "pmFPAConstruct.h"
+#include "pmDark.h"
 
 pmFPA *pmFPAfileSuitableFPA(const pmFPAfile *file, const pmFPAview *view, const pmConfig *config)
@@ -243,4 +244,11 @@
 }
 
+bool pmFPAviewReadFitsDark(const pmFPAview *view, pmFPAfile *file)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    return fpaViewReadFitsImage(view, file, pmFPAReadDark, pmChipReadDark, pmCellReadDark);
+}
+
 bool pmFPAviewReadFitsHeaderSet(const pmFPAview *view, pmFPAfile *file)
 {
@@ -337,4 +345,11 @@
     PS_ASSERT_PTR_NON_NULL(file, false);
     return fpaViewWriteFitsImage(view, file, config, pmFPAWriteWeight, pmChipWriteWeight, pmCellWriteWeight);
+}
+
+bool pmFPAviewWriteFitsDark(const pmFPAview *view, pmFPAfile *file, const pmConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(view, false);
+    PS_ASSERT_PTR_NON_NULL(file, false);
+    return fpaViewWriteFitsImage(view, file, config, pmFPAWriteDark, pmChipWriteDark, pmCellWriteDark);
 }
 
Index: /trunk/psModules/src/camera/pmFPAfileFitsIO.h
===================================================================
--- /trunk/psModules/src/camera/pmFPAfileFitsIO.h	(revision 16840)
+++ /trunk/psModules/src/camera/pmFPAfileFitsIO.h	(revision 16841)
@@ -5,6 +5,6 @@
  * @author PAP, IfA
  *
- * @version $Revision: 1.9 $ $Name: not supported by cvs2svn $
- * @date $Date: 2007-07-14 03:19:01 $
+ * @version $Revision: 1.10 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2008-03-06 20:47:50 $
  * Copyright 2004-2005 Institute for Astronomy, University of Hawaii
  */
@@ -30,4 +30,9 @@
                             );
 
+/// Read a dark into the current view
+bool pmFPAviewReadFitsDark(const pmFPAview *view,  ///< View specifying level of interest
+                           pmFPAfile *file ///< FPA file into which to read
+    );
+
 /// Read an image header into the current view
 bool pmFPAviewReadFitsHeaderSet(const pmFPAview *view,  ///< View specifying level of interest
@@ -52,4 +57,10 @@
                               const pmConfig *config ///< Configuration
                              );
+
+/// Write the dark for the specified view
+bool pmFPAviewWriteFitsDark(const pmFPAview *view, ///< View specifying level of interest
+                            pmFPAfile *file, ///< FPA file to write
+                            const pmConfig *config ///< Configuration
+    );
 
 /// Write a PHU for a fits image if needed
Index: /trunk/psModules/src/camera/pmFPAfileIO.c
===================================================================
--- /trunk/psModules/src/camera/pmFPAfileIO.c	(revision 16840)
+++ /trunk/psModules/src/camera/pmFPAfileIO.c	(revision 16841)
@@ -174,6 +174,9 @@
         status = pmFPAviewReadFitsHeaderSet(view, file);
         break;
+      case PM_FPA_FILE_DARK:
+        status = pmFPAviewReadFitsDark(view, file);
+        break;
       case PM_FPA_FILE_FRINGE:
-        status = pmFPAviewReadFitsImage (view, file);
+        status = pmFPAviewReadFitsImage(view, file);
         if (status) {
             if (!pmFPAviewReadFitsTable(view, file, "FRINGE")) {
@@ -247,8 +250,9 @@
 
     switch (file->type) {
-    case PM_FPA_FILE_IMAGE:
-    case PM_FPA_FILE_MASK:
-    case PM_FPA_FILE_WEIGHT:
-    case PM_FPA_FILE_FRINGE: {
+      case PM_FPA_FILE_IMAGE:
+      case PM_FPA_FILE_MASK:
+      case PM_FPA_FILE_WEIGHT:
+      case PM_FPA_FILE_FRINGE:
+      case PM_FPA_FILE_DARK: {
             // create FPA structure component based on view
             psMetadata *format = file->format; // Camera format configuration
@@ -397,4 +401,5 @@
     fitsType |= (file->type == PM_FPA_FILE_HEADER);
     fitsType |= (file->type == PM_FPA_FILE_FRINGE);
+    fitsType |= (file->type == PM_FPA_FILE_DARK);
     fitsType |= (file->type == PM_FPA_FILE_CMP);
     fitsType |= (file->type == PM_FPA_FILE_CMF);
@@ -451,4 +456,7 @@
       case PM_FPA_FILE_HEADER:
         psAbort ("no HEADER write functions defined");
+        break;
+      case PM_FPA_FILE_DARK:
+        status = pmFPAviewWriteFitsDark(view, file, config);
         break;
       case PM_FPA_FILE_FRINGE:
@@ -532,4 +540,5 @@
       case PM_FPA_FILE_HEADER:
       case PM_FPA_FILE_FRINGE:
+      case PM_FPA_FILE_DARK:
       case PM_FPA_FILE_CMF:
       case PM_FPA_FILE_PSF:
@@ -593,4 +602,5 @@
       case PM_FPA_FILE_HEADER:
       case PM_FPA_FILE_FRINGE:
+      case PM_FPA_FILE_DARK:
         status = pmFPAviewFreeData(view, file);
         break;
@@ -736,4 +746,5 @@
       case PM_FPA_FILE_HEADER:
       case PM_FPA_FILE_FRINGE:
+      case PM_FPA_FILE_DARK:
       case PM_FPA_FILE_CMF:
       case PM_FPA_FILE_PSF:
@@ -882,4 +893,5 @@
       case PM_FPA_FILE_MASK:
       case PM_FPA_FILE_WEIGHT:
+      case PM_FPA_FILE_DARK:
       case PM_FPA_FILE_FRINGE:
         status = pmFPAviewFitsWritePHU (view, file, config);
Index: /trunk/psModules/src/detrend/Makefile.am
===================================================================
--- /trunk/psModules/src/detrend/Makefile.am	(revision 16840)
+++ /trunk/psModules/src/detrend/Makefile.am	(revision 16841)
@@ -13,5 +13,6 @@
 	pmDetrendDB.c \
 	pmShutterCorrection.c \
-	pmShifts.c
+	pmShifts.c \
+	pmDark.c
 
 #	pmSkySubtract.c
@@ -27,5 +28,6 @@
 	pmDetrendDB.h \
 	pmShutterCorrection.h \
-	pmShifts.h
+	pmShifts.h \
+	pmDark.h
 
 #	pmSkySubtract.h
Index: /trunk/psModules/src/detrend/pmBias.c
===================================================================
--- /trunk/psModules/src/detrend/pmBias.c	(revision 16840)
+++ /trunk/psModules/src/detrend/pmBias.c	(revision 16841)
@@ -113,4 +113,5 @@
     }
     if (dark) {
+        psWarning("Dark processing is now available using pmDark --- perhaps you should use that instead?");
         PS_ASSERT_PTR_NON_NULL(view, false);
         PS_ASSERT_IMAGE_NON_NULL(dark->image, false);
@@ -121,5 +122,5 @@
 
     if (!pmOverscanSubtract (in, overscanOpts)) {
-	return false;
+        return false;
     }
 
@@ -148,16 +149,16 @@
         }
 
-	float darkNorm = 1.0;
+        float darkNorm = 1.0;
         float inNorm = pmFPADarkNorm(in->parent->parent->parent, view, inTime);
 
-	// if we have a normalized dark exposure, we simply multiply the master by inNorm.  if
-	// we do not have a normalized exposure, we have to scale the master as well.  XXX do
-	// we need to explicitly identify the master as normalized?
+        // if we have a normalized dark exposure, we simply multiply the master by inNorm.  if
+        // we do not have a normalized exposure, we have to scale the master as well.  XXX do
+        // we need to explicitly identify the master as normalized?
 
-	if (darkTime != 1.0) {
-	    darkNorm = pmFPADarkNorm(dark->parent->parent->parent, view, darkTime);
-	}
+        if (darkTime != 1.0) {
+            darkNorm = pmFPADarkNorm(dark->parent->parent->parent, view, darkTime);
+        }
 
-	if (isnan(inNorm) || isnan(darkNorm)) {
+        if (isnan(inNorm) || isnan(darkNorm)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to determine dark normalisations.");
             return false;
Index: /trunk/psModules/src/detrend/pmDark.c
===================================================================
--- /trunk/psModules/src/detrend/pmDark.c	(revision 16840)
+++ /trunk/psModules/src/detrend/pmDark.c	(revision 16841)
@@ -25,4 +25,6 @@
 static bool ordinateLookup(float *value, // Value of ordinate, to return
                            const char *name, // Name of ordinate (concept name)
+                           bool scale,  // Scale the value?
+                           float min, float max, // Minimum and maximum values for scaling
                            const pmReadout *ro // Readout of interest
                            )
@@ -60,4 +62,11 @@
         psWarning("Non-finite value (%f) of concept %s in readout", *value, name);
         return false;
+    }
+    if (scale) {
+        if (*value < min || *value > max) {
+            psWarning("Value of concept %s (%f) outside range (%f:%f)", name, *value, min, max);
+            return false;
+        }
+        *value = 2.0 * (*value - min) / (max - min) - 1.0;
     }
 
@@ -130,12 +139,9 @@
 
             float value = NAN;          // Value of ordinate
-            if (!ordinateLookup(&value, ord->name, ro)) {
+            if (!ordinateLookup(&value, ord->name, ord->scale, ord->min, ord->max, ro)) {
                 roMask->data.U8[j] = 0xff;
                 val->data.F32[i] = NAN;
                 numBadInputs++;
                 continue;
-            }
-            if (ord->scale) {
-                value = 2.0 * (value - ord->min) / (ord->max - ord->min) - 1.0;
             }
             val->data.F32[i] = value;
@@ -280,5 +286,4 @@
         return false;
     }
-    PS_ASSERT_ARRAYS_SIZE_EQUAL(dark->readouts, ordinates, false);
 
     int numOrdinates = ordinates->n;    // Number of ordinates
@@ -287,5 +292,5 @@
         pmDarkOrdinate *ord = ordinates->data[i]; // Ordinate of interest
         float value = NAN;              // Value for ordinate
-        if (!ordinateLookup(&value, ord->name, readout)) {
+        if (!ordinateLookup(&value, ord->name, ord->scale, ord->min, ord->max, readout)) {
             psError(PS_ERR_UNKNOWN, true, "Unable to find value for %s", ord->name);
             psFree(values);
@@ -317,5 +322,5 @@
             }
             float value = psPolynomialMDEval(poly, values); // Value of dark current
-            readout->image->data.F32[y][x] = value;
+            readout->image->data.F32[y][x] -= value;
             if (readout->mask && !isfinite(value)) {
                 readout->mask->data.PS_TYPE_MASK_DATA[y][x] = bad;
Index: /trunk/psModules/src/detrend/pmDark.h
===================================================================
--- /trunk/psModules/src/detrend/pmDark.h	(revision 16841)
+++ /trunk/psModules/src/detrend/pmDark.h	(revision 16841)
@@ -0,0 +1,93 @@
+#ifndef PM_DARK_H
+#define PM_DARK_H
+
+#include <pslib.h>
+#include <pmHDU.h>
+#include <pmFPA.h>
+
+#define PM_DARK_ANALYSIS_ORDINATES "DARK.ORDINATES" // Name for dark ordinates in the cell analysis metadata
+
+// An ordinate for fitting darks
+typedef struct {
+    psString name;                      // Name of concept to fit
+    int order;                          // Polynomial order to fit
+    bool scale;                         // Rescale values?
+    float min, max;                     // Minimum and maximum values for rescaling
+} pmDarkOrdinate;
+
+// Allocator
+pmDarkOrdinate *pmDarkOrdinateAlloc(const char *name, // Name for ordinate
+                                    int order // Order for ordinate
+    );
+
+
+// Combine darks
+bool pmDarkCombine(pmCell *output,      // Output cell; readouts will be attached
+                   const psArray *inputs, // Input readouts for combination
+                   psArray *ordinates,  // Ordinates for fitting
+                   int iter,            // Number of rejection iterations
+                   float rej,           // Rejection threshold (standard deviations)
+                   psMaskType maskVal   // Value to mask
+    );
+
+// Apply dark
+bool pmDarkApply(pmReadout *readout,    // Readout to which to apply dark
+                 const pmCell *dark,    // Dark to apply
+                 psMaskType bad         // Mask value to give bad pixels
+    );
+
+// I/O functions for darks
+
+// Write all darks within an FPA
+bool pmFPAWriteDark(pmFPA *fpa,         // FPA to write
+                    psFits *fits,       // FITS file to which to write
+                    psDB *db,           // Database, for concepts
+                    bool blank,         // Write a blank only?
+                    bool recurse        // Recurse to lower levels?
+    );
+
+// Write all darks within a chip
+bool pmChipWriteDark(pmChip *chip,      // Chip to write
+                     psFits *fits,      // FITS file to which to write
+                     psDB *db,          // Database, for concepts
+                     bool blank,        // Write a blank only?
+                     bool recurse       // Recurse to lower levels?
+    );
+
+// Write a dark to a FITS file
+bool pmCellWriteDark(pmCell *cell,      // Cell containing dark information
+                     psFits *fits,      // FITS file to which to write
+                     psDB *db,          // Database, for concepts
+                     bool blank         // Write a blank only?
+    );
+
+// Read dark for all FPA from a FITS file
+bool pmFPAReadDark(pmFPA *fpa,          // FPA for which to read
+                   psFits *fits,        // FITS file to read
+                   psDB *db             // Database, for concepts
+    );
+
+// Read dark for all chip from a FITS file
+bool pmChipReadDark(pmChip *chip,       // Chip for which to read
+                    psFits *fits,       // FITS file to read
+                    psDB *db            // Database, for concepts
+    );
+
+// Read dark for a cell from a FITS file
+bool pmCellReadDark(pmCell *cell,       // Cell for which to read
+                    psFits *fits,       // FITS file to read
+                    psDB *db            // Database, for concepts
+    );
+
+// Write dark table to FITS file
+bool pmDarkWrite(psFits *fits,          // FITS file to which to write
+                 const psMetadata *header, // Header to write
+                 const psArray *ordinates // Dark ordinates to write
+    );
+
+// Read dark table from FITS file
+psArray *pmDarkRead(psFits *fits        // FITS file to read
+    );
+
+
+#endif
Index: /trunk/psModules/src/psmodules.h
===================================================================
--- /trunk/psModules/src/psmodules.h	(revision 16840)
+++ /trunk/psModules/src/psmodules.h	(revision 16841)
@@ -68,4 +68,5 @@
 #include <pmShutterCorrection.h>
 // #include <pmSkySubtract.h>
+#include <pmDark.h>
 
 // the following headers are from psModule:astrom
