Index: /branches/eam_branches/eam_branch_20090303/ppImage/src/ppImage.h
===================================================================
--- /branches/eam_branches/eam_branch_20090303/ppImage/src/ppImage.h	(revision 23204)
+++ /branches/eam_branches/eam_branch_20090303/ppImage/src/ppImage.h	(revision 23205)
@@ -86,4 +86,6 @@
     int remnanceSize;                   // Size for remnance detection
     float remnanceThresh;               // Threshold for remnance detection
+
+    char *normClass;			// class to use for per-class normalization 
 } ppImageOptions;
 
Index: /branches/eam_branches/eam_branch_20090303/ppImage/src/ppImageArguments.c
===================================================================
--- /branches/eam_branches/eam_branch_20090303/ppImage/src/ppImageArguments.c	(revision 23204)
+++ /branches/eam_branches/eam_branch_20090303/ppImage/src/ppImageArguments.c	(revision 23205)
@@ -14,4 +14,5 @@
     fprintf(stderr, "\t-chip CHIPNUM: Only process this chip number.\n");
     fprintf(stderr, "\t-norm VALUE: Divide through by this value when done.\n");
+    fprintf(stderr, "\t-normlist file.mdc: normalizations by class_id.\n");
     fprintf(stderr, "\n");
     fprintf(stderr, "Input options (single file / file list):\n");
@@ -128,10 +129,23 @@
     }
 
-    // Optional normalisation factor
+    // Optional normalization factor
     if ((argnum = psArgumentGet(argc, argv, "-norm"))) {
         psArgumentRemove(argnum, &argc, argv);
         float norm = atof(argv[argnum]);
-        psMetadataAddF32(config->arguments, PS_LIST_TAIL, "NORMALISATION", 0,
+        psMetadataAddF32(config->arguments, PS_LIST_TAIL, "NORMALIZATION", 0,
                          "Normalisation to apply", norm);
+        psArgumentRemove(argnum, &argc, argv);
+    }
+
+    // Optional per-class normalization table
+    if ((argnum = psArgumentGet(argc, argv, "-normlist"))) {
+        psArgumentRemove(argnum, &argc, argv);
+
+	unsigned int nFail = 0;
+	psMetadata *normlist = psMetadataConfigRead (NULL, &nFail, argv[argnum], false);
+	// XXX allow this file to be in nebulous?
+
+        psMetadataAddMetadata(config->arguments, PS_LIST_TAIL, "NORMALIZATION.TABLE", 0, "Normalization to apply", normlist);
+	psFree (normlist);
         psArgumentRemove(argnum, &argc, argv);
     }
Index: /branches/eam_branches/eam_branch_20090303/ppImage/src/ppImageDetrendReadout.c
===================================================================
--- /branches/eam_branches/eam_branch_20090303/ppImage/src/ppImageDetrendReadout.c	(revision 23204)
+++ /branches/eam_branches/eam_branch_20090303/ppImage/src/ppImageDetrendReadout.c	(revision 23205)
@@ -54,4 +54,5 @@
         if (!pmBiasSubtract(input, options->overscan, bias, oldDark, view)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to subtract bias.");
+	    psFree(detview);
             return false;
         }
@@ -67,4 +68,5 @@
         if (!pmDarkApply(input, dark, options->maskValue)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to subtract dark.");
+	    psFree(detview);
             return false;
         }
@@ -75,4 +77,5 @@
                         options->remnanceSize, options->remnanceThresh)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to mask remnance.");
+	    psFree(detview);
             return false;
         }
@@ -83,4 +86,5 @@
         pmReadout *shutter = pmFPAfileThisReadout(config->files, detview, "PPIMAGE.SHUTTER");
         if (!pmShutterCorrectionApply(input, shutter, pmConfigMaskGet("FLAT", config))) {
+	    psFree(detview);
             return false;
         }
@@ -91,11 +95,12 @@
         pmReadout *flat = pmFPAfileThisReadout(config->files, detview, "PPIMAGE.FLAT");
         if (!pmFlatField(input, flat, options->flatMask)) {
+	    psFree(detview);
             return false;
         }
     }
 
-    // Normalisation by a (known) constant
+    // Normalization by a single (known) constant
     bool mdok;                          // Status of MD lookup
-    float norm = psMetadataLookupF32(&mdok, config->arguments, "NORMALISATION");
+    float norm = psMetadataLookupF32(&mdok, config->arguments, "NORMALIZATION");
     if (mdok && isfinite(norm) && norm != 1.0) {
         pmHDU *hdu = pmHDUFromReadout(input); // HDU of interest
@@ -108,7 +113,52 @@
     }
 
+# if (1)
+    // Normalization by per-class values
+    psMetadata *normlist = psMetadataLookupMetadata(&mdok, config->arguments, "NORMALIZATION.TABLE");
+    if (normlist) {
+	pmFPAfile *inputFile = psMetadataLookupPtr(&mdok, config->files, "PPIMAGE.INPUT");
+
+	// get the menu of class IDs
+        psMetadata *menu = psMetadataLookupMetadata(&mdok, inputFile->camera, "CLASSID"); 
+        if (!menu) {
+            psError(PS_ERR_IO, false, "Unable to find CLASSID metadata in camera configuration");
+	    psFree(detview);
+            return false;
+        }
+	// get the rule for class_id for the desired class
+        const char *rule = psMetadataLookupStr(&mdok, menu, options->normClass); 
+        if (!rule) {
+            psError(PS_ERR_IO, false, "Unable to find NORM.CLASS value %s in CLASSID in camera configuration", options->normClass);
+	    psFree(detview);
+            return false;
+        }
+	// get the class_id from the rule
+        char *classID = pmFPAfileNameFromRule(rule, inputFile, view);
+        if (!classID) {
+            psError(PS_ERR_IO, false, "error converting CLASSID rule %s to name\n", rule);
+	    psFree(detview);
+            return false;
+        }
+
+	// get normalization from the class_id
+	float norm = psMetadataLookupF32 (&mdok, normlist, classID);
+
+        pmHDU *hdu = pmHDUFromReadout(input); // HDU of interest
+        psString comment = NULL;        // Comment to add
+        psStringAppend(&comment, "Normalization: %f", norm);
+        psMetadataAddStr(hdu->header, PS_LIST_TAIL, "HISTORY", PS_META_DUPLICATE_OK, comment, "");
+        psFree(comment);
+
+	// apply the normalization
+        psBinaryOp(input->image, input->image, "*", psScalarAlloc(norm, PS_TYPE_F32));
+
+	psFree (classID);
+    }
+# endif
+
     if (options->doFringe) {
         pmCell *fringe = pmFPAfileThisCell(config->files, detview, "PPIMAGE.FRINGE");
         if (!ppImageDetrendFringeMeasure(input, fringe, false, options)) {
+	    psFree(detview);
             return false;
         }
Index: /branches/eam_branches/eam_branch_20090303/ppImage/src/ppImageOptions.c
===================================================================
--- /branches/eam_branches/eam_branch_20090303/ppImage/src/ppImageOptions.c	(revision 23204)
+++ /branches/eam_branches/eam_branch_20090303/ppImage/src/ppImageOptions.c	(revision 23205)
@@ -81,4 +81,7 @@
     options->remnanceThresh  = 25.0;    // Threshold for remnance detection
 
+    // per-class normalization source
+    options->normClass       = NULL;    // per-class normalizations refer to this class
+
     return options;
 }
@@ -278,4 +281,7 @@
     options->remnanceThresh = psMetadataLookupS32(NULL, recipe, "REMNANCE.THRESH");
 
+    // per-class normalization source (just a reference; don't free)
+    options->normClass = psMetadataLookupStr(NULL, recipe, "NORM.CLASS");
+
     return options;
 }
