Index: /trunk/ppImage/src/Makefile.am
===================================================================
--- /trunk/ppImage/src/Makefile.am	(revision 9951)
+++ /trunk/ppImage/src/Makefile.am	(revision 9952)
@@ -5,4 +5,5 @@
 	ppImageOptions.h \
 	ppImageDetrendFringe.h \
+	ppImagePreserve.h \
 	ppMem.h
 
@@ -20,4 +21,5 @@
 	ppImageDetrendNonLinear.c \
 	ppImageDetrendFringe.c \
+	ppImagePreserve.c \
 	ppImageRebinReadout.c \
 	ppImageMosaic.c \
@@ -41,4 +43,5 @@
 	ppImageDetrendNonLinear.c \
 	ppImageDetrendFringe.c \
+	ppImagePreserve.c \
 	ppImageRebinReadout.c \
 	ppImageMosaic.c \
Index: /trunk/ppImage/src/ppImageArguments.c
===================================================================
--- /trunk/ppImage/src/ppImageArguments.c	(revision 9951)
+++ /trunk/ppImage/src/ppImageArguments.c	(revision 9952)
@@ -33,4 +33,10 @@
                          "Filename for summary statistics", argv[N]);
         psArgumentRemove(N, &argc, argv);
+    }
+
+    if ((N = psArgumentGet(argc, argv, "-fringeimage"))) {
+        psArgumentRemove(N, &argc, argv);
+        psMetadataAddBool(options, PS_LIST_TAIL, "INPUT_IS_FRINGE", PS_META_REPLACE,
+                          "Input is fringe image", true);
     }
 
@@ -73,6 +79,7 @@
     if ((N = psArgumentGet(argc, argv, "-norm"))) {
         psArgumentRemove(N, &argc, argv);
-        psMetadataAddStr(config->arguments, PS_LIST_TAIL, "NORMALISATION", PS_TYPE_F32,
-                         "Normalisation to apply", argv[N]);
+        float norm = atof(argv[N]);
+        psMetadataAddF32(config->arguments, PS_LIST_TAIL, "NORMALISATION", 0,
+                         "Normalisation to apply", norm);
         psArgumentRemove(N, &argc, argv);
     }
Index: /trunk/ppImage/src/ppImageDetrendFringe.c
===================================================================
--- /trunk/ppImage/src/ppImageDetrendFringe.c	(revision 9951)
+++ /trunk/ppImage/src/ppImageDetrendFringe.c	(revision 9952)
@@ -5,16 +5,29 @@
 #include "ppImageDetrendFringe.h"
 
-bool ppImageDetrendFringeMeasure(pmFringeStats **science, // (Ptr to) science fringe measurements
-                                 psArray *references, // Array of reference fringe measurements
-                                 const pmReadout *readout, // Readout to measure
+bool ppImageDetrendFringeMeasure(pmReadout *readout, // Readout to measure
+                                 pmCell *fringe, // Fringe cell (each readout is a different component)
                                  const ppImageOptions *options // Options
                                  )
 {
-    assert(science);
-    PS_ASSERT_ARRAY_NON_NULL(references, false);
+    PS_ASSERT_PTR_NON_NULL(readout, false);
+    PS_ASSERT_PTR_NON_NULL(fringe, false);
+    PS_ASSERT_PTR_NON_NULL(options, false);
+
+    // Reference fringe measurements
+    psArray *references = psMemIncrRefCounter(psMetadataLookupPtr(NULL, fringe->analysis,
+                                                                  "FRINGE.MEASUREMENTS"));
+    if (!references) {
+        references = pmFringesParse(fringe); // Reference fringes
+        if (!references) {
+            psError(PS_ERR_IO, false, "Unable to find fringe references.\n");
+            return false;
+        }
+        psMetadataAdd(fringe->analysis, PS_LIST_TAIL, "FRINGE.MEASUREMENTS", PS_DATA_UNKNOWN,
+                      "Fringe measurements", references);
+    }
 
     pmFringeStats *reference = references->data[0]; // Take the first as representative
     pmFringeRegions *regions = reference->regions; // Regions to measure
-    pmFringeStats *fringe = pmFringeStatsMeasure(regions, readout, options->maskValue); // Fringe stats
+    pmFringeStats *measurements = pmFringeStatsMeasure(regions, readout, options->maskValue); // Fringe stats
 
     // Normalise measurements by the exposure time
@@ -23,5 +36,5 @@
     if (!mdok || !isfinite(expTime)) {
         psError(PS_ERR_UNKNOWN, false, "CELL.EXPOSURE is not set for --- can't normalise fringes\n");
-        psFree(fringe);
+        psFree(measurements);
         return false;
     }
@@ -30,22 +43,21 @@
         expTime = 1.0;
     }
-    psBinaryOp(fringe->f, fringe->f, "*", psScalarAlloc(1.0 / expTime, PS_TYPE_F32));
-    psBinaryOp(fringe->df, fringe->df, "*", psScalarAlloc(1.0 / expTime, PS_TYPE_F32));
+    psBinaryOp(measurements->f, measurements->f, "*", psScalarAlloc(1.0 / expTime, PS_TYPE_F32));
+    psBinaryOp(measurements->df, measurements->df, "*", psScalarAlloc(1.0 / expTime, PS_TYPE_F32));
 
-    if (!*science) {
-        // Only a single readout
-        *science = fringe;
-    } else {
+    // Science fringe measurements
+    pmFringeStats *previous = psMetadataLookupPtr(NULL, readout->parent->analysis, "FRINGE.MEASUREMENTS");
+    if (previous) {
         // Multiple readouts: concatenate
         psArray *concatenate = psArrayAlloc(2); // Array to hold fringes
 
         // Concatenate science measurements
-        concatenate->data[0] = *science;
-        concatenate->data[1] = fringe;
-        pmFringeStats *sciNew = pmFringeStatsConcatenate(concatenate, NULL, NULL); // New measurements
-        psFree(*science);
-        *science = sciNew;
+        concatenate->data[0] = previous;
+        concatenate->data[1] = measurements;
+        pmFringeStats *new = pmFringeStatsConcatenate(concatenate, NULL, NULL); // New measurements
+        psFree(measurements);
+        measurements = new;
 
-        // Concatenate reference measurements
+        // Concatenate reference measurements (duplication, so the science and reference line up)
         for (int i = 0; i < references->n; i++) {
             concatenate->data[0] = concatenate->data[1] = references->data[i];
@@ -54,8 +66,13 @@
             references->data[i] = refNew;
         }
-
         concatenate->data[0] = concatenate->data[1] = NULL;
         psFree(concatenate);
     }
+
+    psMetadataAdd(readout->parent->analysis, PS_LIST_TAIL, "FRINGE.MEASUREMENTS",
+                  PS_DATA_UNKNOWN | PS_META_REPLACE, "Fringe measurements", measurements);
+    psFree(measurements);
+
+    psFree(references);
 
     return true;
@@ -63,16 +80,38 @@
 
 
+// Pull the fringes out of the cell analysis FRINGE.MEASUREMENTS for a chip
+static psArray *getFringes(const pmChip *chip // Chip of interest
+    )
+{
+    psArray *cells = chip->cells;       // Component cells
+    psArray *fringes = psArrayAlloc(cells->n); // Fringes, to return
+    for (int i = 0; i < cells->n; i++) {
+        pmCell *cell = cells->data[i];  // Cell of interest
+        fringes->data[i] = psMemIncrRefCounter(psMetadataLookupPtr(NULL, cell->analysis,
+                                                                   "FRINGE.MEASUREMENTS"));
+    }
+
+    return fringes;
+}
+
 
 // Solve the fringe system: we have science fringe measurements for each cell, and an array of reference
 // fringe measurements for each cell.  Need to concatenate these together first, and then solve.
-pmFringeScale *ppImageDetrendFringeSolve(psArray *science, // Science fringe measurements (per cell)
-                                         psArray *references, // Array of array of fringe measurements
-                                         const ppImageOptions *options // Options
+bool ppImageDetrendFringeSolve(pmChip *scienceChip, // Chip with science
+                               const pmChip *refChip, // Chip with reference fringes
+                               const ppImageOptions *options // Options
     )
 {
+    PS_ASSERT_PTR_NON_NULL(scienceChip, NULL);
+    PS_ASSERT_PTR_NON_NULL(refChip, NULL);
+    PS_ASSERT_PTR_NON_NULL(options, NULL);
+
+    psArray *science = getFringes(scienceChip); // Fringe measurements on science chip
     pmFringeStats *scienceCat = pmFringeStatsConcatenate(science, NULL, NULL); // Science fringes
+    psFree(science);
 
     // Need to transform the array of cells each with an array of fringes --> array of fringes for the chip as
     // a whole
+    psArray *references = getFringes(refChip); // Fringe measurements on reference chip
     int numRefs = ((psArray*)references->data[0])->n; // Number of reference fringes
     psArray *referencesCat = psArrayAlloc(numRefs); // Reference fringes
@@ -86,4 +125,5 @@
         psFree(refs);
     }
+    psFree(references);
 
     // Now we can solve
@@ -92,18 +132,22 @@
                                                    options->fringeIter, options->fringeKeep);
 
+    psMetadataAdd(scienceChip->analysis, PS_LIST_TAIL, "FRINGE.SOLUTION", PS_DATA_UNKNOWN,
+                  "Fringe solution", solution);
+    psFree(solution);
     psFree(scienceCat);
     psFree(referencesCat);
 
-    return solution;
+    return true;
 }
 
 
 psImage *ppImageDetrendFringeGenerate(pmCell *science, // Science cell
-                                      pmCell *fringes, // Fringe cell, one readout per fringe component
-                                      const pmFringeScale *solution // Fringe solution to apply
+                                      pmCell *fringes // Fringe cell, one readout per fringe component
     )
 {
     PS_ASSERT_PTR_NON_NULL(science, false);
-    PS_ASSERT_PTR_NON_NULL(solution, false);
+    PS_ASSERT_PTR_NON_NULL(fringes, false);
+
+    pmFringeScale *solution = psMetadataLookupPtr(NULL, science->parent->analysis, "FRINGE.SOLUTION");
     assert(fringes->readouts->n == solution->nFringeFrames);
 
Index: /trunk/ppImage/src/ppImageDetrendFringe.h
===================================================================
--- /trunk/ppImage/src/ppImageDetrendFringe.h	(revision 9951)
+++ /trunk/ppImage/src/ppImageDetrendFringe.h	(revision 9952)
@@ -6,19 +6,19 @@
 #include "ppImageOptions.h"
 
-bool ppImageDetrendFringeMeasure(pmFringeStats **science, // (Ptr to) science fringe measurements
-                                 psArray *references, // Array of reference fringe measurements
-                                 const pmReadout *readout, // Readout to measure
+bool ppImageDetrendFringeMeasure(pmReadout *readout, // Readout to measure
+                                 pmCell *fringe, // Fringe cell (each readout is a different component)
                                  const ppImageOptions *options // Options
     );
 
-pmFringeScale *ppImageDetrendFringeSolve(psArray *science, // Science fringe measurements (per cell)
-                                         psArray *references, // Array of array of fringe measurements
-                                         const ppImageOptions *options // Options
+bool ppImageDetrendFringeSolve(pmChip *scienceChip, // Chip with science
+                               const pmChip *refChip, // Chip with reference fringes
+                               const ppImageOptions *options // Options
     );
 
+
 psImage *ppImageDetrendFringeGenerate(pmCell *science, // Science cell
-                                      pmCell *fringes, // Fringe cell, one readout per fringe component
-                                      const pmFringeScale *solution // Fringe solution to apply
+                                      pmCell *fringes // Fringe cell, one readout per fringe component
     );
 
+
 #endif
Index: /trunk/ppImage/src/ppImageDetrendReadout.c
===================================================================
--- /trunk/ppImage/src/ppImageDetrendReadout.c	(revision 9951)
+++ /trunk/ppImage/src/ppImageDetrendReadout.c	(revision 9952)
@@ -4,4 +4,5 @@
 
 #include "ppImage.h"
+#include "ppImageDetrendFringe.h"
 
 bool ppImageDetrendReadout (pmConfig *config, ppImageOptions *options, pmFPAview *view)
@@ -72,4 +73,11 @@
     }
 
+    if (options->doFringe) {
+        pmCell *fringe = pmFPAfileThisCell(config->files, detview, "PPIMAGE.FRINGE");
+        if (!ppImageDetrendFringeMeasure(input, fringe, options)) {
+            return false;
+        }
+    }
+
     psFree (detview);
     return true;
Index: /trunk/ppImage/src/ppImageLoop.c
===================================================================
--- /trunk/ppImage/src/ppImageLoop.c	(revision 9951)
+++ /trunk/ppImage/src/ppImageLoop.c	(revision 9952)
@@ -6,4 +6,5 @@
 #include "ppImage.h"
 #include "ppImageDetrendFringe.h"
+
 
 bool ppImageLoop (pmConfig *config, ppImageOptions *options) {
@@ -45,30 +46,8 @@
         if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) return false;
 
-        const char *chipName = psMetadataLookupStr(NULL, chip->concepts, "CHIP.NAME"); // Name of chip
-        psArray *fringeRef = NULL;      // Array of array of fringe statistics for reference --- for each cell
-        psArray *fringeSci = NULL;      // Array of fringe statistics for science --- for each cell
-        if (options->doFringe) {
-            fringeRef = psArrayAlloc(chip->cells->n);
-            fringeSci = psArrayAlloc(chip->cells->n);
-        }
-
         while ((cell = pmFPAviewNextCell (view, input->fpa, 1)) != NULL) {
             psLogMsg ("ppImageLoop", 4, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
             if (!cell->process || !cell->file_exists) { continue; }
             if (!pmFPAfileIOChecks (config, view, PM_FPA_BEFORE)) return false;
-            const char *cellName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME"); // Name of cell
-
-            if (options->doFringe) {
-                pmFPAfile *fringeFile = psMetadataLookupPtr(&status, config->files, "PPIMAGE.FRINGE");
-                if (!status || !fringeFile) {
-                    psErrorStackPrint(stderr, "Can't find fringe data!\n");
-                    exit(EXIT_FAILURE);
-                }
-
-                psString fringeExt = NULL; // Fringe extension name
-                psStringAppend(&fringeExt, "FRINGE_%s_%s", chipName, cellName);
-                fringeRef->data[view->cell] = pmFringesReadFits(NULL, fringeFile->fits, fringeExt);
-                psFree(fringeExt);
-            }
 
             // process each of the readouts
@@ -80,17 +59,13 @@
                 if (!ppImageDetrendReadout (config, options, view)) return false;
 
-                if (options->doFringe) {
-                    ppImageDetrendFringeMeasure((pmFringeStats**)(&fringeSci->data[view->cell]),
-                                                fringeRef->data[view->cell], readout, options);
-                }
             }
         }
 
         // Solve the fringe system
-        pmFringeScale *fringeSoln = NULL; // Solution for the fringes
         if (options->doFringe) {
-            fringeSoln = ppImageDetrendFringeSolve(fringeSci, fringeRef, options);
-            psFree(fringeSci);
-            psFree(fringeRef);
+            pmChip *fringe = pmFPAfileThisChip(config->files, view, "PPIMAGE.FRINGE");
+            if (!ppImageDetrendFringeSolve(chip, fringe, options)) {
+                return false;
+            }
         }
 
@@ -103,8 +78,6 @@
             if (options->doFringe) {
                 psTrace("ppImage", 3, "Applying fringe correction...\n");
-                bool mdok;                          // Status of MD lookup
-                pmFPAfile *fringeFile = psMetadataLookupPtr(&mdok, config->files, "PPIMAGE.FRINGE");
-                pmCell *fringeCell = pmFPAviewThisCell(view, fringeFile->fpa);
-                psImage *fringe = ppImageDetrendFringeGenerate(cell, fringeCell, fringeSoln);
+                pmCell *fringeCell = pmFPAfileThisCell(config->files, view, "PPIMAGE.FRINGE");
+                psImage *fringe = ppImageDetrendFringeGenerate(cell, fringeCell);
                 while ((readout = pmFPAviewNextReadout (view, input->fpa, 1)) != NULL) {
                     if (!readout->data_exists) { continue; }
@@ -125,5 +98,4 @@
             if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) return false;
         }
-        psFree(fringeSoln);
 
         ppImageMosaicChip (config, view, "PPIMAGE.OUTPUT.CHIP", "PPIMAGE.OUTPUT");
@@ -163,5 +135,6 @@
     if (!pmFPAfileIOChecks (config, view, PM_FPA_AFTER)) return false;
 
-    psFree (view);
+    psFree(view);
+
     return true;
 }
Index: /trunk/ppImage/src/ppImageParseCamera.c
===================================================================
--- /trunk/ppImage/src/ppImageParseCamera.c	(revision 9951)
+++ /trunk/ppImage/src/ppImageParseCamera.c	(revision 9952)
@@ -12,5 +12,5 @@
     // the input image defines the camera, and all recipes and options the follow
     pmFPAfile *input = pmFPAfileDefineFromArgs (&status, config, "PPIMAGE.INPUT", "INPUT");
-    if (!status) {
+    if (!status || !input) {
         psError(PS_ERR_IO, false, "Failed to build FPA from PPIMAGE.INPUT");
         return NULL;
@@ -177,4 +177,10 @@
     psFree (chips);
 
+    if (psMetadataLookupBool(NULL, recipe, "INPUT_IS_FRINGE")) {
+        // It's a fringe file, so change the file type
+        input->type = PM_FPA_FILE_FRINGE;
+        output->type = PM_FPA_FILE_FRINGE;
+    }
+
     return (options);
 }
