Index: /trunk/ppMerge/src/ppMerge.c
===================================================================
--- /trunk/ppMerge/src/ppMerge.c	(revision 5861)
+++ /trunk/ppMerge/src/ppMerge.c	(revision 5862)
@@ -1,3 +1,3 @@
-# include "ppImage.h"
+# include "ppMerge.h"
 
 int main (int argc, char **argv) {
@@ -6,4 +6,5 @@
     ppConfig config;
     ppOptions options;
+
 
     psTimerStart("merge");
@@ -15,5 +16,5 @@
     // Get camera configuration from header if not already defined
     // Construct camera in preparation for reading
-    ppMergeParseCamera (&data, &config, argv);
+    ppMergeParseCamera (&data, &config);
 
     // Set various tasks (define optional operations)
@@ -27,6 +28,43 @@
 
     // output options
-    // ppImageOutput (&data, &options, &config);
+    ppMergeOutput (&data, &options, &config);
 
     exit (0);
 }
+
+int speedTest (void) 
+{
+    # define NTEST 10000000
+    psStats *testData, *stats;
+    stats = psStatsAlloc (PS_STAT_ROBUST_MEAN);
+    psList *testList = psListAlloc (NULL);
+    psArray *testArray = psArrayAlloc (NTEST);
+
+    psTimerStart("merge");
+    for (int i = 0; i < NTEST; i++) {
+	testArray->data[i] = stats;
+    }
+    fprintf (stderr, "added %d objects to array: %f usec / obj\n", NTEST, 1e6*psTimerMark ("merge")/NTEST);
+
+    psTimerStart("merge");
+    for (int i = 0; i < NTEST; i++) {
+	psListAdd (testList, PS_LIST_TAIL, stats);
+    }
+    fprintf (stderr, "added %d objects to list: %f usec / obj\n", NTEST, 1e6*psTimerMark ("merge")/NTEST);
+
+    psTimerStart("merge");
+    for (int i = 0; i < NTEST; i++) {
+	testData = testArray->data[i];
+    }
+    fprintf (stderr, "pulled %d objects from array: %f usec / obj\n", NTEST, 1e6*psTimerMark ("merge")/NTEST);
+
+    psTimerStart("merge");
+    psListElem *tmpInput = (psListElem *) testList->head;
+    while (tmpInput != NULL) {
+	testData = tmpInput->data;
+	tmpInput = tmpInput->next;
+    }
+    fprintf (stderr, "pulled %d objects from list: %f usec / obj\n", NTEST, 1e6*psTimerMark ("merge")/NTEST);
+
+    exit (0);
+}
Index: /trunk/ppMerge/src/ppMerge.h
===================================================================
--- /trunk/ppMerge/src/ppMerge.h	(revision 5861)
+++ /trunk/ppMerge/src/ppMerge.h	(revision 5862)
@@ -1,26 +1,23 @@
-#include <stdio.h>
-#include <strings.h>
+# include <stdio.h>
+# include <strings.h>
+# include <glob.h>
 
-#include "pslib.h"
+# include "pslib.h"
 
-#include "psAdditionals.h"
+# include "psAdditionals.h"
 
-#include "pmAstrometry.h"
-#include "pmReadout.h"
-#include "pmConfig.h"
-#include "pmFPAConstruct.h"
-#include "pmFPARead.h"
-#include "pmFPAConceptsGet.h"
-#include "pmFPAWrite.h"
+# include "pmAstrometry.h"
+# include "pmReadout.h"
+# include "pmConfig.h"
+# include "pmFPAConstruct.h"
+# include "pmFPARead.h"
+# include "pmFPAConceptsGet.h"
+# include "pmFPAWrite.h"
 
-#include "pmFlatField.h"
-#include "pmMaskBadPixels.h"
-#include "pmNonLinear.h"
-#include "pmSubtractBias.h"
-#include "pmChipMosaic.h"
-//#include "pmFPAMorph.h"
+# include "pmReadoutCombine.h"
 
-#define RECIPE "PHASE2"                 // Name of the recipe to use
+# define RECIPE "MERGE" // Name of the recipe to use
 
+// XXX : same as in ppImage
 typedef enum {
     PP_LOAD_NONE,
@@ -30,4 +27,5 @@
 } ppImageLoadDepth;
 
+// XXX : same as in ppImage
 typedef struct {
     psMetadata *site;
@@ -48,5 +46,6 @@
 
 typedef struct {
-    psArray *data;
+    psArray *input;
+    ppFPA *output;
     ppFPA *process;
     ppFPA *mask;
@@ -55,42 +54,22 @@
 typedef struct {
     ppImageLoadDepth imageLoadDepth;
-
-    psStats *mergeStats;		// Statistics for overscan
-    void *overscanFit;			// Overscan fit (polynomial or spline)
-    pmFit overscanFitType;		// Fit type for overscan
-    pmOverscanAxis overscanMode;	// Axis for overscan
-
-    bool doNonLin;			// Non-linearity correction
-    psDataType nonLinearType;
-    psMetadataItem *nonLinearData;
-    void *nonLinearSource;
+    bool doMask;			// apply a pixel mask before stacking
+    pmCombineParams *combineParams;
+    bool applyZeroScale;
+    double gain;
+    double readnoise;
 } ppOptions;
 
-typedef struct {
-    pmCell *input;
-    pmCell *mask;
-    pmCell *bias;
-    pmCell *dark;
-    pmCell *flat;
-} ppDetrend;
+bool ppMergeConfig (ppConfig *config, int argc, char **argv);
+bool ppMergeLoop (ppData *data, ppOptions *options, ppConfig *config);
+bool ppMergeOptions (ppData *data, ppOptions *options, ppConfig *config);
+bool ppMergeOutput (ppData *data, ppOptions *options, ppConfig *config);
+bool ppMergeParseCamera (ppData *data, ppConfig *config);
+bool ppMergeParseDetrend (ppData *data, ppOptions *options, ppConfig *config);
 
-bool ppImageConfig (ppConfig *config, int argc, char **argv);
-bool ppImageLoadPixels (ppFPA *input, ppFPA *process, psDB *db, int nChip, int nCell);
-bool ppImageLoop (ppData *data, ppOptions *options, ppConfig *config);
-bool ppImageOptions (ppData *data, ppOptions *options, ppConfig *config);
-bool ppImageParseCamera (ppData *data, ppConfig *config, char **argv);
-bool ppImageParseDetrend (ppData *data, ppOptions *options, ppConfig *config);
+bool ppMergeCell (pmCell *output, pmCell *mask, psArray *cellList, ppOptions *options, ppConfig *config);
 
-bool ppReadoutWeights (pmReadout *readout);
-
-bool ppDetrendCell (ppDetrend *detrend, ppOptions *options, ppConfig *config);
-
-bool ppDetrendMask (pmCell *cell, pmReadout *input, pmReadout *mask);
-bool ppDetrendNonLinear (pmCell *cell, pmReadout *input, ppOptions *options);
-bool ppDetrendNonLinearLookup (pmReadout *input, psMetadataItem *dataItem);
-bool ppDetrendNonLinearPolynomial (pmReadout *input, psMetadataItem *dataItem);
-bool ppDetrendBias (pmCell *inputCell, pmReadout *inputReadout, pmReadout *pedestal, ppOptions *options);
-pmReadout* ppDetrendPedestal (pmReadout *pedestal, pmCell *input, pmReadout *bias, pmReadout *dark, float darkTime, ppOptions *options);
+// XXX : these functions are identical to the ppImage equivalents
 pmReadout* ppDetrendSelectFirst (pmCell *cell, char *name, bool doThis);
-
 bool ppFPAOpen (ppFPA *fpa, psMetadata *camera, char *name, bool doThis);
+bool ppMergeLoadPixels (ppFPA *input, ppFPA *process, psDB *db, int nChip, int nCell);
Index: /trunk/ppMerge/src/ppMergeCell.c
===================================================================
--- /trunk/ppMerge/src/ppMergeCell.c	(revision 5861)
+++ /trunk/ppMerge/src/ppMergeCell.c	(revision 5862)
@@ -1,6 +1,3 @@
 # include "ppMerge.h"
-
-// mask, bias, dark, flat are defined per Cell
-// pedestal is constructed for each readout, which may have different exposure times
 
 pmReadout *ppDetrendSelectFirst (pmCell *cell, char *name, bool doThis) {
@@ -19,27 +16,59 @@
 }
 
-bool ppMergeCell (psArray *cellList, pmCell *maskCell, ppOptions *options, ppConfig *config) {
+bool ppMergeCell (pmCell *outputCell, pmCell *maskCell, psArray *cellList, ppOptions *options, ppConfig *config) {
 
     pmCell *sample = cellList->data[0];
-    pmReadout *mask   = ppDetrendSelectFirst (maskCell, "mask", options->doMask);
+    // pmReadout *mask = ppDetrendSelectFirst (maskCell, "mask", options->doMask);
+
+    // list to carry readouts for processing
+    psArray *inputs = psArrayAlloc (cellList->n);
+    inputs->n = 0;
+
+    psVector *combineZero = psVectorAlloc (128, PS_TYPE_F32);
+    combineZero->n = 0;
+
+    psVector *combineScale = psVectorAlloc (128, PS_TYPE_F32);
+    combineScale->n = 0;
+
+    int nVal = 0;
 
     // push all readouts from each image onto the list
     // not clear that this is the right process -- how to merge readouts?
     // are all readouts from this cell equivalent? (ie, across images?)
-    // XXX need to set / carry the zero,scale values
-
-    readouts = psListAlloc ();
-
+    // XXX need to set the zero,scale values correctly for each readout
     for (int i = 0; i < cellList->n; i++) {
 	pmCell *cell = cellList->data[i];
 	for (int j = 0; j < cell->readouts->n; j++) {
-	    psListAdd (cell->readouts->data[j], PS_LIST_TAIL);
+	    psArrayAdd (inputs, 16, cell->readouts->data[j]);
+
+	    combineZero->data.F32[nVal] = 0.0;
+	    combineScale->data.F32[nVal] = 1.0;
+	    psVectorExtend (combineZero, 100, 1);
+	    psVectorExtend (combineScale, 100, 1);
+	    nVal ++;
 	}
     }
 
-    // XXX : need to consider the output
-    
-    psImage *output = pmReadoutCombine (NULL, readouts, params,...);
+    // XXX : need to assign these correctly
+    float combineGain = 1.0;
+    float combineNoise = 0.0;
 
+    // XXX somehow need to transfer the input cell concepts to the 
+    //     the output cell.  this is weak: are the concepts fixed 
+    //     or not for a collection of images...
+    outputCell->concepts = sample->concepts;
+
+    // XXX where is the output readouts array allocated?
+    pmReadout *outputReadout = pmReadoutAlloc (outputCell);
+    outputReadout->image = pmReadoutCombine (NULL, inputs, 
+					     combineZero, 
+					     combineScale, 
+					     options->combineParams, 
+					     options->applyZeroScale,
+					     combineGain, combineNoise);
     return true;
 }
+
+
+// XXX a psList does not make sense here: we are using the fixed length psVectors
+//     to carry bzero and bscale, a psArray should be used for the images
Index: /trunk/ppMerge/src/ppMergeConfig.c
===================================================================
--- /trunk/ppMerge/src/ppMergeConfig.c	(revision 5861)
+++ /trunk/ppMerge/src/ppMergeConfig.c	(revision 5862)
@@ -1,3 +1,3 @@
-# include "ppImage.h"
+# include "ppMerge.h"
 
 bool ppMergeConfig (ppConfig *config, int argc, char **argv) {
@@ -20,8 +20,9 @@
     psMetadataAddS32(config->arguments, PS_LIST_TAIL, "-zero",  0, "Name of the mask image", 0);
     psMetadataAddS32(config->arguments, PS_LIST_TAIL, "-scale", 0, "Chip number to process (if positive)", 1);
+    psMetadataAddS32(config->arguments, PS_LIST_TAIL, "-chip",  0, "Chip number to process (if positive)",-1);
 
     if (! psArgumentParse(config->arguments, &argc, argv) || argc != 2) {
         printf("\nPan-STARRS Phase 2 processing\n\n");
-        printf("Usage: %s INPUT.fits OUTPUT.fits\n\n", argv[0]);
+        printf("Usage: %s OUTPUT.fits\n\n", argv[0]);
         psArgumentHelp(config->arguments);
         psFree(config->arguments);
Index: /trunk/ppMerge/src/ppMergeLoop.c
===================================================================
--- /trunk/ppMerge/src/ppMergeLoop.c	(revision 5861)
+++ /trunk/ppMerge/src/ppMergeLoop.c	(revision 5862)
@@ -3,7 +3,7 @@
 bool ppMergeLoop (ppData *data, ppOptions *options, ppConfig *config) {
 
-    ppDetrend detrend;
-
-    ppFPA *sampleFPA = data->input->data[0];
+    char name[128];
+    ppFPA *sample = data->input->data[0];
+    pmFPA *sampleFPA = sample->fpa;
 
     psArray *cellList = psArrayAlloc (data->input->n);
@@ -20,6 +20,7 @@
     // iterate over all chips, using sampleFPA as reference
     for (int i = 0; i < sampleFPA->chips->n; i++) {
+        pmChip *maskChip = data->mask->fpa->chips->data[i]; // Chip of interest in input image
+        pmChip *outputChip = data->output->fpa->chips->data[i]; // Chip of interest in input image
         pmChip *sampleChip = sampleFPA->chips->data[i]; // Chip of interest in input image
-        pmChip *maskChip = data->mask->fpa->chips->data[i]; // Chip of interest in input image
         if (! sampleChip->valid) { continue; }
 
@@ -35,6 +36,7 @@
 	// iterate over all cells, using sampleChip as reference
         for (int j = 0; j < sampleChip->cells->n; j++) {
+	    pmCell *maskCell = maskChip->cells->data[j];
+	    pmCell *outputCell = outputChip->cells->data[j];
 	    pmCell *sampleCell = sampleChip->cells->data[j];
-	    pmCell *maskCell = maskChip->cells->data[j];
             if (! sampleCell->valid) { continue; }
 
@@ -50,5 +52,6 @@
 	    // build an array of the cells of interest
 	    for (int k = 0; k < cellList->n; k++) {
-		pmFPA *thisFPA = data->input->data[k];
+		ppFPA *thisFrame = data->input->data[k];
+		pmFPA *thisFPA = thisFrame->fpa;
 		pmChip *thisChip = thisFPA->chips->data[i];
 		cellList->data[k] = thisChip->cells->data[j];
@@ -56,6 +59,27 @@
 
 	    // run the merge function
-	    ppMergeCell (cellList, maskCell, options, config);
+	    ppMergeCell (outputCell, maskCell, cellList, options, config);
+
+	    sprintf (name, "test.%d.%d.fits", i, j);
+	    psFits *fits = psFitsOpen (name, "w");
+	    pmReadout *readout = outputCell->readouts->data[0];
+	    psFitsWriteImage (fits, NULL, readout->image, 0);
+	    psFitsClose (fits);
+
+# if (0)
+	    if (sampleCell->hdu) {
+		outputCell->hdu = sampleCell->hdu;
+		outputCell->hdu->images = psArrayAlloc (1);
+		outputCell->hdu->images->data[0] = outputCell->readouts->data[0];
+	    }
+# endif
 	}
+# if (0)	
+	if (sampleChip->hdu) {
+	    outputChip->hdu = sampleChip->hdu;
+	    outputChip->hdu->images = psArrayAlloc (1);
+	    // outputChip->hdu->images->data[0] = outputChip->readouts->data[0];
+	}
+# endif
     }
     return true;
Index: /trunk/ppMerge/src/ppMergeOptions.c
===================================================================
--- /trunk/ppMerge/src/ppMergeOptions.c	(revision 5861)
+++ /trunk/ppMerge/src/ppMergeOptions.c	(revision 5862)
@@ -1,3 +1,3 @@
-# include "ppImage.h"
+# include "ppMerge.h"
 
 // XXX EAM : optionally choose the mask image based on the detrend database
@@ -26,17 +26,6 @@
     }
 
-    // how do we calculate the merge stack?
-    options->mergeStats = NULL;
-    psString stat = psMetadataLookupStr(NULL, config->recipe, "MERGE.STAT");
-    if (! strcasecmp(stat, "MEAN")) {
-      options->mergeStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
-    } else if (! strcasecmp(stat, "MEDIAN")) {
-      options->mergeStats = psStatsAlloc(PS_STAT_SAMPLE_MEDIAN);
-    } else {
-      psErrorStackPrint(stderr, "MERGE.STAT (%s) is not one of MEAN, MEDIAN: assuming MEAN\n", stat);
-      options->mergeStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
-    }
-
-    // mask - recipe options
+    // global pixel mask
+    options->doMask = false;
     if (psMetadataLookupBool(NULL, config->recipe, "MASK")) {
 	data->mask->filename = psMetadataLookupStr(NULL, config->arguments, "-mask");
@@ -49,4 +38,41 @@
     }
 
+    // how do we calculate the merge stack?
+    psStatsOptions mergeStats = 0;
+    psString stat = psMetadataLookupStr(NULL, config->recipe, "MERGE.STAT");
+    if (! strcasecmp(stat, "MEAN")) {
+	mergeStats = PS_STAT_SAMPLE_MEAN;
+    } else if (! strcasecmp(stat, "MEDIAN")) {
+	mergeStats = PS_STAT_SAMPLE_MEDIAN;
+    } else {
+	psAbort ("merge", "MERGE.STAT (%s) is not one of MEAN, MEDIAN\n", stat);
+    }
+    options->combineParams = pmCombineParamsAlloc (mergeStats);
+
+    // other merge stack options
+    options->applyZeroScale = psMetadataLookupBool(NULL, config->recipe, "MERGE.RESCALE");
+
+    int nKeep = psMetadataLookupS32(&status, config->recipe, "MERGE.NKEEP");
+    if (status && nKeep > 0) {
+	options->combineParams->nKeep = nKeep;
+    }
+
+    float fracHigh = psMetadataLookupF32(&status, config->recipe, "MERGE.FRAC.HIGH");
+    if (status) {
+	options->combineParams->fracHigh = fracHigh;
+    }
+
+    float fracLow = psMetadataLookupF32(&status, config->recipe, "MERGE.FRAC.LOW");
+    if (status) {
+	options->combineParams->fracLow = fracLow;
+    }
+
+    // XXX need to set the masking value somehow...
+
+    // gain and readnoise come from camera parameters and depend on chip/cell
+    // XXX drop these from the options structure?
+    options->gain = 1.0;
+    options->readnoise = 0.0;
+
     return true;
 }
Index: /trunk/ppMerge/src/ppMergeOutput.c
===================================================================
--- /trunk/ppMerge/src/ppMergeOutput.c	(revision 5862)
+++ /trunk/ppMerge/src/ppMergeOutput.c	(revision 5862)
@@ -0,0 +1,16 @@
+# include "ppMerge.h"
+
+bool ppMergeOutput (ppData *data, ppOptions *options, ppConfig *config) {
+    
+    data->output->fits = psFitsOpen (data->output->filename, "w");
+    if (data->output->fits == NULL) {
+	psAbort ("merge", "failed to open output file %s", data->output->filename);
+    }
+
+    // Write the output
+    pmFPAWrite (data->output->fits, data->output->fpa, config->database);
+    psFitsClose (data->output->fits);
+
+    psLogMsg("merge", PS_LOG_INFO, "Output completed after %f sec.\n", psTimerMark("merge"));
+    return true;
+}
Index: /trunk/ppMerge/src/ppMergeParseCamera.c
===================================================================
--- /trunk/ppMerge/src/ppMergeParseCamera.c	(revision 5861)
+++ /trunk/ppMerge/src/ppMergeParseCamera.c	(revision 5862)
@@ -38,6 +38,6 @@
 	    ppFPA *fpa = ppFPA_Alloc ();
 	    fpa->filename = psStringCopy (globList.gl_pathv[i]);
+	    psArrayAdd (input, 100, fpa);
 	}
-	psArrayAdd (input, 100, fpa);
     }
 
@@ -56,6 +56,6 @@
 	}
 	while (fgets (line, 1024, f) != NULL) {
-	    nItems = fscanf (f, "%s %lf %lf", filename, &zero, &scale);
-	    fpa = ppFPA_Alloc ();
+	    nItems = sscanf (line, "%s %lf %lf", filename, &zero, &scale);
+	    ppFPA *fpa = ppFPA_Alloc ();
 	    switch (nItems) {
 	      case 0:
@@ -65,5 +65,7 @@
 		fpa->zero  = zero;
 		fpa->scale = scale;
-		// NOTE: continues to case 1 below
+		fpa->filename = psStringCopy (filename);
+		psArrayAdd (input, 100, fpa);
+		break;
 	      case 1:
 		fpa->filename = psStringCopy (filename);
@@ -85,8 +87,10 @@
 
     // Open the input images, test that they are the same camera
-    psLogMsg("phase2", PS_LOG_INFO, "Opening input images\n");
+    psLogMsg("merge", PS_LOG_INFO, "Opening input images\n");
     for (int i = 0; i < input->n; i++) {
 	
 	ppFPA *fpa = input->data[i];
+
+	psLogMsg("merge", PS_LOG_INFO, "Trying %s...\n", fpa->filename);
 
 	fpa->fits = psFitsOpen(fpa->filename, "r"); // File handle for FITS file
@@ -130,5 +134,6 @@
 
     // data->process acts as a process mask for the full FPA
-    data->process = pmFPAConstruct(config->camera);
+    data->process = ppFPA_Alloc ();
+    data->process->fpa = pmFPAConstruct(config->camera);
 
     // see if we have selected a specific chip
@@ -144,5 +149,15 @@
     // data->mask carries a pixel mask for pixels to be ignored
     // the name and contents of the mask are determined in ppMergeOptions
-    data->mask = pmFPAConstruct(config->camera);
+    data->mask = ppFPA_Alloc ();
+    data->mask->fpa = pmFPAConstruct(config->camera);
+
+    // data->output will store the output file
+    // currently, we keep the complete output file and write it at the end
+    data->output = ppFPA_Alloc ();
+    data->output->fpa = pmFPAConstruct(config->camera);
+    data->output->filename = psMetadataLookupPtr (NULL, config->arguments, "-output");
+    if (! *data->output->filename) {
+	psAbort ("merge", "output file not specified");
+    }
 
     return true;
Index: /trunk/ppMerge/src/ppMergeParseDetrend.c
===================================================================
--- /trunk/ppMerge/src/ppMergeParseDetrend.c	(revision 5861)
+++ /trunk/ppMerge/src/ppMergeParseDetrend.c	(revision 5862)
@@ -1,3 +1,3 @@
-# include "ppImage.h"
+# include "ppMerge.h"
 
 // open all needed detrend files
