Index: trunk/ppImage/src/ppImageDetrendReadout.c
===================================================================
--- trunk/ppImage/src/ppImageDetrendReadout.c	(revision 21364)
+++ trunk/ppImage/src/ppImageDetrendReadout.c	(revision 23229)
@@ -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;
         }
