Index: trunk/ippScripts/scripts/chip_imfile.pl
===================================================================
--- trunk/ippScripts/scripts/chip_imfile.pl	(revision 35684)
+++ trunk/ippScripts/scripts/chip_imfile.pl	(revision 35685)
@@ -228,4 +228,6 @@
     ## XXX make the feature more general?
     my $useDeburnedImage = metadataLookupBool($recipeData, 'USE.DEBURNED.IMAGE');
+    my $ppImageApplyBurntool = metadataLookupBool($recipeData, 'APPLY.BURNTOOL');
+    my $burntoolArgument;
 
     if ($useDeburnedImage) {
@@ -328,84 +330,107 @@
         }
 
-        ## We now know that we have an image that has been burntooled.
-        my ($tempFile, $tempName) = tempfile( "/tmp/chip.$exp_id.$class_id.deburned.XXXX",
+        # if recipe option to have ppImage apply burntool is set and the burntool information is store in
+        # an external table, check the existence of that table file and set $burntoolArgument.
+        # If burntool state does not match fall through to the code that uses the burntool program
+        if ($ppImageApplyBurntool && ($burntoolState == -1 * $burntoolStateGood)) {
+            my $burntoolTable_uri = $uri;
+            $burntoolTable_uri =~ s/fits$/burn.tbl/;
+            my $burntoolTable_uriReal = $ipprc->file_resolve( $burntoolTable_uri );
+            if ((!$burntoolTable_uriReal)||(!($ipprc->file_exists($burntoolTable_uri)))) {
+                my $repair_cmd = "$nebrepair $burntoolTable_uri";
+                my ($repair_success, $repair_error_code, $repair_full_buf, $repair_stdout_buf, $repair_stderr_buf ) = run(command => $repair_cmd, verbose => $verbose);
+                unless ($repair_success) {
+                    &my_die("Unable to attempt repair: $uri $repair_error_code", $exp_id,$chip_id, $class_id, $PS_EXIT_SYS_ERROR);
+                }
+                $burntoolTable_uriReal = $ipprc->file_resolve( $burntoolTable_uri );
+            }
+            unless ($ipprc->file_exists($burntoolTable_uri)) {
+                # Catch errors here
+                &my_die("Couldn't find burntool table: $burntoolTable_uri",$exp_id,$chip_id,$class_id, $PS_EXIT_SYS_ERROR);
+            }
+            $burntoolArgument = " -burntool $burntoolTable_uri";
+        } else {
+
+            ## We now know that we have an image that has been burntooled.
+            my ($tempFile, $tempName) = tempfile( "/tmp/chip.$exp_id.$class_id.deburned.XXXX",
                                               UNLINK => !$save_temps, SUFFIX => '.fits' );
 
-        # get the UNIX version of the (possible) neb: or path: filename
-        my $uriReal = $ipprc->file_resolve( $uri );
-	# Catch errors here
-	if (!$uriReal) {
-	    my $repair_cmd = "$nebrepair $uri";
-	    my ($repair_success, $repair_error_code, $repair_full_buf, $repair_stdout_buf, $repair_stderr_buf ) = run(command => $repair_cmd, verbose => $verbose);
-	    unless ($repair_success) {
-		&my_die("Unable to attempt repair: $uri $repair_error_code", $exp_id,$chip_id, $class_id, $PS_EXIT_SYS_ERROR);
-	    }
-	    $uriReal = $ipprc->file_resolve( $uri );
-	}
-	    
-        &my_die("Unable to resolve $uri on $host", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR) if !$uriReal;
-
-        # funpack into the temp file.
-        my $funpack_cmd = "$funpack -S $uriReal > $tempName";
-        ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) = run(command => $funpack_cmd, verbose => $verbose);
-        unless ($success) {
-	    # Catch errors here
-	    my $repair_cmd = "$nebrepair $uri";
-	    my ($repair_success, $repair_error_code, $repair_full_buf, $repair_stdout_buf, $repair_stderr_buf ) = run(command => $repair_cmd, verbose => $verbose);
-	    unless ($repair_success) {
-		&my_die("Unable to attempt repair: $uri $repair_error_code", $exp_id,$chip_id, $class_id, $PS_EXIT_SYS_ERROR);
-	    }
-	    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) = run(command => $funpack_cmd, verbose => $verbose);
-	}
-        unless ($success) {
-            $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
-            &my_die("Unable to perform funpack: $error_code", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR);
-        }
-
-        ## Construct commands to apply the pre-calculated burntool trailfits to the pixel data.
-        my ($burntoolTable_uri, $burntoolTable_uriReal);
-        my $burntool_cmd = "$burntool ";
-
-        if ($burntoolState == -1 * $burntoolStateGood) {   # Burntool information stored in an external table.
-            $burntoolTable_uri = $uri;
-            $burntoolTable_uri =~ s/fits$/burn.tbl/;
-            $burntoolTable_uriReal = $ipprc->file_resolve( $burntoolTable_uri );
-	    if ((!$burntoolTable_uriReal)||(!($ipprc->file_exists($burntoolTable_uri)))) {
-		my $repair_cmd = "$nebrepair $burntoolTable_uri";
-		my ($repair_success, $repair_error_code, $repair_full_buf, $repair_stdout_buf, $repair_stderr_buf ) = run(command => $repair_cmd, verbose => $verbose);
-		unless ($repair_success) {
-		    &my_die("Unable to attempt repair: $uri $repair_error_code", $exp_id,$chip_id, $class_id, $PS_EXIT_SYS_ERROR);
-		}
-		$burntoolTable_uriReal = $ipprc->file_resolve( $burntoolTable_uri );
-	    }
-            unless ($ipprc->file_exists($burntoolTable_uri)) {
-		# Catch errors here
-                &my_die("Couldn't find burntool table: $burntoolTable_uri",$exp_id,$chip_id,$class_id, $PS_EXIT_SYS_ERROR);
-            }
-
-            $burntool_cmd .= "$tempName in=${burntoolTable_uriReal} persist=t apply=t";
-        }
-        elsif ($burntoolState == $burntoolStateGood) { # Burntool information stored in a header table.
-            $burntool_cmd .= "$tempName persist=t apply=t";
-        }
-        else {
-            &my_die("Image data not properly burntooled, impossible state", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR);
-        }
-
-        ## Run burntool to change the pixels of tempfile, and repoint $uri to that file.
-        ($success, $error_code, $full_buf, $stdout_buf, $stderr_buf) =
-            run(command => $burntool_cmd, verbose => $verbose);
-        unless ($success) {
-            $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
-            &my_die("Unable to perform burntool: $error_code", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR);
-        }
-
-        $uri = $tempName;
-        unless ($ipprc->file_exists($uri)) {
-            &my_die("Couldn't find deburned input file: $uri\n", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR);
-        }
-
-#       print STDERR "$uri $uriReal $tempName $burntoolTable_uri $burntoolTable_uriReal $burntool_cmd $save_temps\n";
-#       exit(100);
+            # get the UNIX version of the (possible) neb: or path: filename
+            my $uriReal = $ipprc->file_resolve( $uri );
+                # Catch errors here
+            if (!$uriReal) {
+                my $repair_cmd = "$nebrepair $uri";
+                my ($repair_success, $repair_error_code, $repair_full_buf, $repair_stdout_buf, $repair_stderr_buf ) = run(command => $repair_cmd, verbose => $verbose);
+                unless ($repair_success) {
+                    &my_die("Unable to attempt repair: $uri $repair_error_code", $exp_id,$chip_id, $class_id, $PS_EXIT_SYS_ERROR);
+                }
+                $uriReal = $ipprc->file_resolve( $uri );
+            }
+                
+            &my_die("Unable to resolve $uri on $host", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR) if !$uriReal;
+
+            # funpack into the temp file.
+            my $funpack_cmd = "$funpack -S $uriReal > $tempName";
+            ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) = run(command => $funpack_cmd, verbose => $verbose);
+            unless ($success) {
+                # Catch errors here
+                my $repair_cmd = "$nebrepair $uri";
+                my ($repair_success, $repair_error_code, $repair_full_buf, $repair_stdout_buf, $repair_stderr_buf ) = run(command => $repair_cmd, verbose => $verbose);
+                unless ($repair_success) {
+                    &my_die("Unable to attempt repair: $uri $repair_error_code", $exp_id,$chip_id, $class_id, $PS_EXIT_SYS_ERROR);
+                }
+                ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) = run(command => $funpack_cmd, verbose => $verbose);
+            }
+            unless ($success) {
+                    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+                &my_die("Unable to perform funpack: $error_code", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR);
+            }
+
+            ## Construct commands to apply the pre-calculated burntool trailfits to the pixel data.
+            my ($burntoolTable_uri, $burntoolTable_uriReal);
+            my $burntool_cmd = "$burntool ";
+
+            if ($burntoolState == -1 * $burntoolStateGood) {   # Burntool information stored in an external table.
+                $burntoolTable_uri = $uri;
+                $burntoolTable_uri =~ s/fits$/burn.tbl/;
+                $burntoolTable_uriReal = $ipprc->file_resolve( $burntoolTable_uri );
+                if ((!$burntoolTable_uriReal)||(!($ipprc->file_exists($burntoolTable_uri)))) {
+                    my $repair_cmd = "$nebrepair $burntoolTable_uri";
+                    my ($repair_success, $repair_error_code, $repair_full_buf, $repair_stdout_buf, $repair_stderr_buf ) = run(command => $repair_cmd, verbose => $verbose);
+                    unless ($repair_success) {
+                        &my_die("Unable to attempt repair: $uri $repair_error_code", $exp_id,$chip_id, $class_id, $PS_EXIT_SYS_ERROR);
+                    }
+                    $burntoolTable_uriReal = $ipprc->file_resolve( $burntoolTable_uri );
+                }
+                unless ($ipprc->file_exists($burntoolTable_uri)) {
+                    # Catch errors here
+                    &my_die("Couldn't find burntool table: $burntoolTable_uri",$exp_id,$chip_id,$class_id, $PS_EXIT_SYS_ERROR);
+                }
+
+                $burntool_cmd .= "$tempName in=${burntoolTable_uriReal} persist=t apply=t";
+            }
+            elsif ($burntoolState == $burntoolStateGood) { # Burntool information stored in a header table.
+                $burntool_cmd .= "$tempName persist=t apply=t";
+            }
+            else {
+                &my_die("Image data not properly burntooled, impossible state", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR);
+            }
+
+            ## Run burntool to change the pixels of tempfile, and repoint $uri to that file.
+            ($success, $error_code, $full_buf, $stdout_buf, $stderr_buf) =
+                run(command => $burntool_cmd, verbose => $verbose);
+            unless ($success) {
+                $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+                &my_die("Unable to perform burntool: $error_code", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR);
+            }
+
+            $uri = $tempName;
+            unless ($ipprc->file_exists($uri)) {
+                &my_die("Couldn't find deburned input file: $uri\n", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR);
+            }
+
+    #       print STDERR "$uri $uriReal $tempName $burntoolTable_uri $burntoolTable_uriReal $burntool_cmd $save_temps\n";
+    #       exit(100);
+        }
     }
 
@@ -483,4 +508,5 @@
         $command .= " -R PPIMAGE.CHIP FITS.TYPE NONE";
     }
+    $command .= $burntoolArgument if $burntoolArgument;
     $command .= " -threads $threads" if defined $threads;
     $command .= " -image_id $chip_imfile_id" if defined $chip_imfile_id;
Index: trunk/ippconfig/gpc1/ppImage.config
===================================================================
--- trunk/ippconfig/gpc1/ppImage.config	(revision 35684)
+++ trunk/ippconfig/gpc1/ppImage.config	(revision 35685)
@@ -21,7 +21,9 @@
 USE.DEBURNED.IMAGE      BOOL    TRUE           # use burntool-repaired image?
 USE.BEST.BURNTOOL	BOOL	TRUE	       # require the best burntooled data
-MASK.BURNTOOL           BOOL    FALSE           # Mask potential burntool trails
+MASK.BURNTOOL           BOOL    FALSE          # Mask potential burntool trails
 TILTYSTREAK.APPLY       BOOL    FALSE          # apply the 'tiltystreak' tool
 NOISEMAP                BOOL    FALSE          # Apply read noise map
+
+APPLY.BURNTOOL          BOOL    TRUE           # apply burntool correction in ppImage
 
 # Final detrending options 
@@ -30,5 +32,5 @@
 USE.VIDEO.MASK  BOOL    TRUE            # Use a video mask if we have video data?
 
-# aditional auxillary mask applied to corners of all chips in video cells.
+# aditional auxiliary mask applied to corners of all chips in video cells.
 # Is only applied if MASK.AUXMASK and USE.VIDEO.MASK are both true
 AUX.VIDEO.MASK  STR     neb://any/gpc1/auxmask/mpg.20130506/mask_video_cell_corners.fits
@@ -153,5 +155,6 @@
 FRINGE.FILTERS	STR y.00000
 
-# Standard chip processing (NOTE: when changing this be sure to check whether changes should be applied toCHIP_BGPRESEVE and/or CHIP_AUXMASK
+# Standard chip processing (NOTE: when changing this be sure to check whether changes should be applied to
+# CHIP_PRESERVE_BG CHIP_AUXMASK or CHIP_PRESERVE_BG_AUXMASK
 CHIP METADATA
   BASE.FITS          BOOL    FALSE           # Save base detrended image?
@@ -172,5 +175,5 @@
   MASK.BUILD	     BOOL    TRUE	     # Build internal mask?
   MASK.BURNTOOL      BOOL    TRUE            # Mask potential burntool trails
-  MASK.AUXMASK       BOOL    FALSE           # augment with more restrictive auxillary mask
+  MASK.AUXMASK       BOOL    FALSE           # augment mask with more restrictive auxiliary mask
   PATTERN.ROW        BOOL    TRUE            # Row pattern correction
   PATTERN.CELL	     BOOL    FALSE	     # Cell pattern correction
@@ -1224,5 +1227,5 @@
 END
 
-# Standard chip processing + auxillary mask
+# Standard chip processing + auxiliary mask
 CHIP_AUXMASK METADATA
   BASE.FITS          BOOL    FALSE           # Save base detrended image?
@@ -1243,5 +1246,5 @@
   MASK.BUILD	     BOOL    TRUE	     # Build internal mask?
   MASK.BURNTOOL      BOOL    TRUE            # Mask potential burntool trails
-  MASK.AUXMASK       BOOL    TRUE            # augment with more restrictive auxillary mask
+  MASK.AUXMASK       BOOL    TRUE            # augment mask with more restrictive auxiliary mask
   PATTERN.ROW        BOOL    TRUE            # Row pattern correction
   PATTERN.CELL	     BOOL    FALSE	     # Cell pattern correction
@@ -1255,7 +1258,8 @@
   BACKGROUND         BOOL    TRUE            # Subtract background?
   CROSSTALK.MEASURE  BOOL    TRUE            # Subtract model background?
-END
-
-# Standard chip processing without any background removal
+  NO.COMPRESS        BOOL    TRUE            # turn off image compression
+END
+
+# Standard chip processing without background subtraction or pattern correction
 CHIP_PRESERVE_BG	METADATA
   BASE.FITS          BOOL    FALSE           # Save base detrended image?
@@ -1269,5 +1273,5 @@
   BIAS               BOOL    FALSE           # Bias subtraction
   DARK               BOOL    TRUE            # Dark subtraction
-  NOISEMAP           BOOL    FALSE
+  NOISEMAP           BOOL    TRUE
   REMNANCE           BOOL    FALSE           # Remnance masking
   SHUTTER            BOOL    FALSE           # Shutter correction
@@ -1276,7 +1280,8 @@
   MASK.BUILD	     BOOL    TRUE	     # Build internal mask?
   MASK.BURNTOOL      BOOL    TRUE            # Mask potential burntool trails
-  MASK.AUXMASK       BOOL    TRUE            # augment with more restrictive auxillary mask
+  MASK.AUXMASK       BOOL    FALSE           # augment mask with more restrictive auxiliary mask
   PATTERN.ROW        BOOL    FALSE           # Row pattern correction
   PATTERN.CELL	     BOOL    FALSE	     # Cell pattern correction
+  PATTERN.CONTINUITY BOOL    FALSE           # Cell pattern coorection using edge continuity
   FRINGE             BOOL    TRUE            # Fringe subtraction
   BIN1.FITS          BOOL    TRUE            # Save 1st binned chip image?
@@ -1287,4 +1292,38 @@
   BACKGROUND         BOOL    FALSE           # Subtract background?
   CROSSTALK.MEASURE  BOOL    TRUE            # Subtract model background?
+END
+
+# Standard chip processing without background subtraction, pattern correction, or photometry bug applying auxiliary mask
+CHIP_PRESERVE_BG_AUXMASK	METADATA
+  BASE.FITS          BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS     BOOL    FALSE           # Save base detrended image?
+  BASE.VARIANCE.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS          BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS     BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.VARIANCE.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN           BOOL    TRUE            # Overscan subtraction
+  NONLIN             BOOL    TRUE            # apply non-linearity correction 
+  BIAS               BOOL    FALSE           # Bias subtraction
+  DARK               BOOL    TRUE            # Dark subtraction
+  NOISEMAP           BOOL    TRUE
+  REMNANCE           BOOL    FALSE           # Remnance masking
+  SHUTTER            BOOL    FALSE           # Shutter correction
+  FLAT               BOOL    TRUE            # Flat-field normalisation
+  MASK               BOOL    TRUE            # Mask bad pixels
+  MASK.BUILD	     BOOL    TRUE	     # Build internal mask?
+  MASK.BURNTOOL      BOOL    TRUE            # Mask potential burntool trails
+  MASK.AUXMASK       BOOL    TRUE            # augment mask with more restrictive auxiliary mask
+  PATTERN.ROW        BOOL    FALSE           # Row pattern correction
+  PATTERN.CELL	     BOOL    FALSE	     # Cell pattern correction
+  PATTERN.CONTINUITY BOOL    TRUE            # Cell pattern coorection using edge continuity
+  FRINGE             BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS          BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS          BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM             BOOL    FALSE           # Source identification and photometry
+  ASTROM.CHIP        BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC      BOOL    FALSE           # Astrometry for mosaic?
+  BACKGROUND         BOOL    FALSE           # Subtract background?
+  CROSSTALK.MEASURE  BOOL    TRUE            # Subtract model background?
+  NO.COMPRESS        BOOL    TRUE            # turn off image compression
 END
 
Index: trunk/ippconfig/recipes/ppImage.config
===================================================================
--- trunk/ippconfig/recipes/ppImage.config	(revision 35684)
+++ trunk/ippconfig/recipes/ppImage.config	(revision 35685)
@@ -18,5 +18,6 @@
 MASK.BURNTOOL      BOOL    FALSE           # Mask potential burntool trails
 MASK.AUXMASK       BOOL    FALSE           # augment mask with auxillary mask
-AUX.VIDEO.MASK     STR     NULL
+AUX.VIDEO.MASK     STR     NULL            # name of aditional masking for video cells
+APPLY.BURNTOOL     BOOL    FALSE           # apply burntool coorection
 VARIANCE.BUILD     BOOL    FALSE           # Build internal variance image
 ADDNOISE           BOOL    FALSE           # Add noise to degrade an MD image to a 3pi image?
@@ -47,4 +48,5 @@
 TILTYSTREAK.BY.CLASS METADATA              # apply the 'tiltystreak' tool
 END
+NO.COMPRESS        BOOL     FALSE
 
 # output data formats to save
@@ -2139,4 +2141,33 @@
   MASK               BOOL    TRUE            # Mask bad pixels
   MASK.BUILD         BOOL    TRUE 	     # Build internal mask?
+  PATTERN.ROW        BOOL    FALSE           # Subtract pattern noise
+  PATTERN.CELL       BOOL    FALSE           # Subtract pattern noise
+  FRINGE             BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS          BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS          BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM             BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP        BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC      BOOL    FALSE           # Astrometry for mosaic?
+  BACKGROUND         BOOL    FALSE           # Subtract background?
+  MASK.STATS         BOOL    TRUE	     # Calculate Mask statistics
+END
+
+# Standard chip processing with auxiliary mask but without any background removal or pattern correction
+CHIP_PRESERVE_BG_AUXMASK	METADATA
+  BASE.FITS          BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS     BOOL    FALSE           # Save base detrended image?
+  BASE.VARIANCE.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS          BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS     BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.VARIANCE.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN           BOOL    TRUE            # Overscan subtraction
+  NONLIN             BOOL    FALSE           # Overscan subtraction
+  BIAS               BOOL    TRUE            # Bias subtraction
+  DARK               BOOL    TRUE            # Dark subtraction
+  SHUTTER            BOOL    FALSE           # Shutter correction
+  FLAT               BOOL    TRUE            # Flat-field normalisation
+  MASK               BOOL    TRUE            # Mask bad pixels
+  MASK.BUILD         BOOL    TRUE 	     # Build internal mask?
+  MASK.AUXMASK       BOOL    TRUE 	     # augment mask with more restrictive auxiliary mask
   PATTERN.ROW        BOOL    FALSE           # Subtract pattern noise
   PATTERN.CELL       BOOL    FALSE           # Subtract pattern noise
Index: trunk/ippconfig/recipes/reductionClasses.mdc
===================================================================
--- trunk/ippconfig/recipes/reductionClasses.mdc	(revision 35684)
+++ trunk/ippconfig/recipes/reductionClasses.mdc	(revision 35685)
@@ -1047,8 +1047,32 @@
 END
 
-M31_DATASET		METADATA
+M31_BG_SUBTRACT		METADATA
 	CHIP_PPIMAGE	  STR	  CHIP_AUXMASK
 	CHIP_PSPHOT	  STR	  CHIP
-	WARP_PSWARP	  STR	  WARP
+	WARP_PSWARP	  STR	  WARP_NOCOMPRESS
+	STACK_PPSTACK	  STR	  STACK
+	STACK_PPSUB	  STR	  STACK
+	STACK_PSPHOT	  STR	  STACK
+	DIFF_PPSUB	  STR	  DIFF
+	DIFF_PSPHOT	  STR	  DIFF
+	JPEG_BIN1	  STR	  PPIMAGE_J1
+	JPEG_BIN2	  STR	  PPIMAGE_J2
+	FAKEPHOT	  STR	  FAKEPHOT
+	ADDSTAR		  STR	  ADDSTAR
+	PSASTRO		  STR	  DEFAULT_RECIPE
+	STACKPHOT_PSPHOT  STR     STACKPHOT
+	STACKPHOT_PPSUB   STR     STACKPHOT
+	STACKPHOT_PPSTACK STR     STACKPHOT
+	STACKPHOT_SINGLE_PSPHOT  STR     STACKPHOT_SINGLE
+	BACKGROUND_PPBACKGROUND	STR	BACKGROUND
+	BACKGROUND_PSWARP	STR	BACKGROUND
+        PSVIDEOPHOT             STR     PSVIDEOPHOT
+        STATICSKY_CALIBRATION   STR     STATICSKY_CAL
+END
+
+M31_BG_PRESERVE		METADATA
+	CHIP_PPIMAGE	  STR	  CHIP_PRESERVE_BG_AUXMASK
+	CHIP_PSPHOT	  STR	  CHIP
+	WARP_PSWARP	  STR	  WARP_NOCOMPRESS
 	STACK_PPSTACK	  STR	  STACK
 	STACK_PPSUB	  STR	  STACK
Index: trunk/ppImage/src/Makefile.am
===================================================================
--- trunk/ppImage/src/Makefile.am	(revision 35684)
+++ trunk/ppImage/src/Makefile.am	(revision 35685)
@@ -66,5 +66,6 @@
         ppImageAddNoise.c \
         ppImageRandomGaussian.c \
-	ppImageAuxiliaryMask.c
+	ppImageAuxiliaryMask.c \
+	ppImageBurntoolApply.c
 
 CLEANFILES = *~
Index: trunk/ppImage/src/burnparams.h
===================================================================
--- trunk/ppImage/src/burnparams.h	(revision 35685)
+++ trunk/ppImage/src/burnparams.h	(revision 35685)
@@ -0,0 +1,59 @@
+/* A global buffer into which routines can accumulate detections */
+#ifndef _INCLUDED_burnparams_
+#define _INCLUDED_burnparams_
+
+/* '#define EXTERN' in just one file, burntool.c, to declare variables */
+#ifndef EXTERN
+#define EXTERN extern
+#endif
+
+#define MAXBURN 10000
+EXTERN OBJBOX boxbuf[MAXBURN];
+
+EXTERN int *median_buf;		/* Generic buffer for integer medians */
+EXTERN int nmedian_buf;
+
+EXTERN MTYPE *mbuf;		/* Buffer for object mask */
+EXTERN int nmbuf;
+
+EXTERN MTYPE *msbuf;		/* Buffer for vetoed star mask */
+EXTERN int nmsbuf;
+
+EXTERN DTYPE *imbuf;		/* Copy of cell data w/o BZERO */
+EXTERN int nimbuf;
+
+EXTERN int VERBOSE;		/* Verbosity level */
+
+EXTERN int BZERO;
+EXTERN int USHORT_BIAS;		/* Bias level restored to ushort stamps */
+
+EXTERN int MAX_READ_NOISE;	/* Maximum believable read noise (ADU) */
+EXTERN double MIN_EADU;		/* Minimum believable e/ADU */
+EXTERN int SAT4SURE;		/* Ignore pixels above for noise estimate */
+EXTERN double MIN_BLAST_PASS;	/* Allow blasted cells if they have BIG satfrac */
+EXTERN double MAX_BLAST_PASS;	/* But not if it's all wiped out! */
+
+EXTERN int BURN_THRESH  ;	/* Threshold for onset of burning */
+EXTERN int TRAIL_THRESH ;	/* Trailing might go this low */
+EXTERN int MAX_THRESH   ;	/* Possibly trailing stars? */
+EXTERN int STAR_THRESH  ;	/* Threshold for star above sky */
+EXTERN double STAR_FRAC ;	/* Fraction to follow star profile */
+EXTERN int PSF_THRESH   ;	/* Threshold for a star to be a PSF */
+
+//EXTERN double XMASK_GROW ;	/* Growth of burned boxes in x dir */
+//EXTERN double YMASK_GROW ;	/* Growth of burn/star in y dir */
+
+EXTERN double BMASK_GROW ;	/* Growth of burned boxes in size */
+EXTERN double RMASK_GROW ;	/* Growth of burn/star in diameter */
+
+EXTERN int MIN_PSF_SIZE  ;	/* Min box size for stamp selection */
+EXTERN int PSF_CTR_TOL   ;	/* Choose max or box ctr for stamp */
+EXTERN int CONCAT_FITS;		/* Write concat FITS for PSF? (else 3D) */
+EXTERN int MAX_PSF_PER_CELL ; 	/* Max number of PSF stars accepted per cell */
+
+EXTERN double NEGLIGIBLE_TRAIL;	/* Don't sweat less than this * sigma */
+EXTERN int EXPIRE_TRAIL_TIME;	/* Expire trails after this interval */
+
+EXTERN int PERSIST_RETAIN;	/* Retain bad-slope persistence fits */
+
+#endif /* _INCLUDED_burnparams_ */
Index: trunk/ppImage/src/burntool.h
===================================================================
--- trunk/ppImage/src/burntool.h	(revision 35685)
+++ trunk/ppImage/src/burntool.h	(revision 35685)
@@ -0,0 +1,229 @@
+#ifndef _INCLUDED_burntool_
+#define _INCLUDED_burntool_
+
+/* Burn correction routines:
+   -------------------------  
+ *  1. Identify burned spots from saturation threshold
+ ?  2. Identify burns and trails morphologically?
+ ?    2.1 Look for flat-topped stars
+ ?    2.2 Look for asymmetric, smooth, declining trails?
+ ?    2.3 Use y overscan?
+ ?    2.4 Bin in x?
+ *  3. Mask stars and other cruft?
+ *  4. Fit up and down row functions
+ *  5. Read burn time list
+ *  6. Write updated burn time list
+ *  8. Undo subtraction
+    7. Create and save burn correction FITS table
+*/
+/* Algorithm:
+   ----------
+    1. Find new burned spots
+    2. Create new burn entries: x1,x2,y,time=now
+    3. For each burn:
+        if t = now  detrail up
+              else  deburn down
+        if fit negligible  delete burn 
+                     else  save burn fits
+ */
+/* ToDo:
+   -----
+ *  clean up fit start points, esp for persistence
+ *  change burn= and persist= to trailin= and trailout=
+ *  trim edges of fits
+ *  implement MASK_SAT
+ >  test for garbage fits (positive slope, etc)
+ *  test nearly saturated stars for burn.
+ *  undo subtraction (almost perfect -- occasional pixels and baddies)
+ *  star gather into 3D FITS
+ *  test on donuts
+ *  stamp extraction center on box, not max
+ *  Add back nominal bias; output as ushort   
+ *  man page... ongoing...
+ *  Scale uses peak/sum?
+ *  Change psf coords to center; save in header not pixels
+ x  better trail function?  A tuned ln(x+A) is better, but too hard.
+ *  why various restorations imperfect?  edge of cell?
+ *  cell mask argument
+... why the honker star not ID'ed as a burn?  various other gotchas...
+ *  age away old persistence streaks; merge persistence streaks
+ *  PSF info on median psf
+ *  clean up sky/rms calculation with 5*quartile clipping
+ *  change VERBOSE to a bitmask for access to various portions of the code
+ *  add access to a lot of the tuning variables...
+ *  update man page
+    tuning, tuning, tuning...
+    add correction FITS table to MEF
+*/
+
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#define NINT(x) (x<0?(int)((x)-0.5):(int)((x)+0.5))
+#define ABS(a) (((a) > 0) ? (a) : -(a))
+
+// #define STATIC static	/* To make function declarations static */
+#define STATIC			/* To make function declarations global */
+
+#define MAXCELL	64		/* Max cells in an OTA */
+#define MAXSIZE 2048		/* Maximum vertical cell size */
+
+#define STAR_RADIUS  4		/* Radius over which a star ctr must be max */
+#define SKY_MARG 3		/* Horiz offset from burn area for sky */
+#define FIT_EDGE 10		/* How far beyond saturation to start fit? */
+#define Y_SCALE 0.01		/* Scale factor for y in fits */
+
+typedef signed short int IMTYPE;/* Data type of image */
+
+typedef int DTYPE;		/* Data type of data copy */
+#define NODATA 0		/* Marker for *No Data */
+
+/* Mask codes: keep in order of severity for psf_select()! */
+typedef int MTYPE;		/* Data type of mask */
+#define MASK_NONE      0	/* Unmasked pixel (MUST be 0) */
+#define MASK_STAR_HALO 1	/* Extended halo box from star */
+#define MASK_SAT_HALO  2	/* Extended halo box from grow_mask() */
+#define MASK_CTR       3	/* Center mask from star_detect() */
+#define MASK_SAT       4	/* Center mask of saturated pixels */
+
+/* Enumeration of function choices */
+#define FUNC_NONE 0		/* No associated function */
+#define BURN_PWR  1		/* Power law */
+#define BURN_EXP  2		/* Exponential */
+#define BURN_BLASTED 3		/* Blasted top to bottom: flag only for IPP */
+#define BURN_POSSLOPE 4		/* Positive slope fit (bad) but significant */
+#define PSF_STAR  9		/* Unfitted: good psf star */
+
+/* Fit error codes */
+#define FIT_ERROR  1		/* linearfit failed */
+#define FIT_TOP_ERROR  2	/* Saturation extends to top: no points */
+#define FIT_SLOPE_ERROR  3	/* Unreasonable fit */
+#define FIT_ALL_GONE  4		/* No column survived as significant */
+#define FIT_EXPIRED 9		/* Don't carry any more as persistent */
+
+/* Fit parameters */
+#define FIT_MIN_SLOPE  -5.0	/* minimum slope which is a credible fit */
+#define FIT_MAX_SLOPE   0.0	/* maximum slope which is a credible fit */
+
+/* Verbosity bits */
+#define VISTAMARKER "/tmp/markem.pro"	/* Write Vista pro to mark stars?*/
+#define VERB_NORM     0x0001	/* Normal, verbose output */
+#define VERB_DETECT   0x0002	/* Dump detection process */
+#define VERB_PSFSEL   0x0004	/* Dump out PSF selection process */
+#define VERB_FIT      0x0008	/* Dump fit progress */
+#define VERB_FITPROF  0x0010	/* Dump fit profiles */
+#define VERB_VISTA    0x0020	/* Write vista markers as /tmp/markem.pro */
+#define VERB_BOXGROW  0x0040	/* Dump box growth diagnostics */
+#define VERB_MASK     0x0080	/* Write mask in place of corrected image */
+
+/* Description of a potentially burned trail */
+typedef struct obj_box {
+      int cell;		/* what cell is this one in? */
+      int time;		/* PON time when it was created */
+      int sx;		/* left corner */
+      int sy;		/* bottom corner */
+      int ex;		/* right corner */
+      int ey;		/* top corner */
+      int cx;		/* center x (position of max) */
+      int cy;		/* center y (position of max) */
+      int max;		/* max data value (above sky) */
+      int y0m;		/* min y value at sx */
+      int y0p;		/* max y value at sx */
+      int y1m;		/* min y value at ex */
+      int y1p;		/* max y value at ex */
+      int x0m;		/* min x value at sy */
+      int x0p;		/* max x value at sy */
+      int x1m;		/* min x value at ey */
+      int x1p;		/* max x value at ey */
+      int sat;		/* saturated (i.e. bigger than BURNTHRESH)? */
+      int func;		/* what are we going to do about it? */
+      int diff;		/* median diff of up minus down (sum for stars) */
+      int up;		/* does it trail up or down? (stamp sx for stars) */
+      int y0;		/* y origin for the fit (stamp sy for stars) */
+      int burned;	/* do we think it's left a burn trail? */
+      int fiterr;	/* error fitting the trail? */
+      int sxfit;	/* starting column for fits */
+      int exfit;	/* ending column for fits */
+      int nfit;		/* how many columns were corrected? */
+      IMTYPE *stamp;	/* postage stamp of this object */
+      int *xfit;	/* x of each value of the start of correction */
+      int *yfit;	/* y value of the start of correction */
+      double slope;	/* slope of fit (stamp sum/max) */
+      double *zero;	/* zero of fit for each of the columns */
+} OBJBOX;
+
+/* Info for an entire cell */
+typedef struct cell_info {
+      int cell;			/* Cell number */
+      int bias;			/* Bias level */
+      int sky;			/* Sky level */
+      int rms;			/* RMS in the sky */
+      int time;			/* PON time of this cell */
+      double satfrac;		/* Fraction of SAT4SURE saturated pixels */
+      int nburn;		/* Number of trails left by sat stars */
+      OBJBOX *burn;		/* Stars which we think left a trail */
+      int npersist;		/* Number of old persistence streaks */
+      OBJBOX *persist;		/* Persistent streaks */
+      int nstar;		/* Number of stars */
+      OBJBOX *star;		/* Stars, harmless we think */
+} CELL;
+
+/* Prototypes */
+STATIC int mem_init(int nx, int ny, int NX, int NY);
+STATIC int burn_fix(int nx, int ny, int stride, int NY, IMTYPE *buf, 
+		    CELL *cell, int cellnum);
+STATIC int burn_test(int nx, int ny, int NX, DTYPE *data, int rms,
+		     MTYPE *mask, OBJBOX *box);
+STATIC int burn_restore(int nx, int ny, int NX, IMTYPE *buf, CELL *cell);
+STATIC int burn_apply(int nx, int ny, int NX, IMTYPE *buf, CELL *cell);
+STATIC int persist_read(CELL *cell, const char *infile, int apply);
+STATIC int persist_write(CELL *cell, const char *outfile);
+STATIC int persist_fix(int nx, int ny, int stride, IMTYPE *buf, CELL *cell);
+STATIC int persist_merge(CELL *cell);
+
+//fh_result persist_fits_read(CELL *cell, const char *filename, int apply);
+//fh_result persist_fits_write(CELL *cell, HeaderUnit phu);
+//fh_result persist_fits_remove_tables(HeaderUnit phu_in, const char *fileout);
+
+STATIC int star_detect(int nx, int ny, int NX, int NY, DTYPE *data,
+		       MTYPE *mask, MTYPE *veto, CELL *cell, int cellnum);
+STATIC int burn_check(int nx, int ny, int stride, int NY, DTYPE *buf,
+		       MTYPE *mask, CELL *cell);
+
+STATIC int cell_stats(int nx, int ny, int NX, int NY, DTYPE *data, CELL *cell);
+STATIC int burn_blab(CELL *cell);
+STATIC int persist_blab(CELL *cell);
+STATIC int vista_marker(CELL *cell, char *fname);
+
+STATIC int fit_trail(int nx, int ny, int NX, DTYPE *data, MTYPE *mask, 
+		    OBJBOX *box, int up, int sky, int rms, int fitfunc);
+STATIC int sub_fit(int nx, int ny, int NX, IMTYPE *buf, OBJBOX *box, int sign);
+
+STATIC int psf_select(int nx, int ny, int NX, MTYPE *mask, DTYPE *buf,
+		      int nbox, OBJBOX *box, int size, int sky);
+STATIC int psf_write(int nx, int ny, CELL *OTA, int otanum, const char *psffile);
+STATIC int psf_write_stats(int nx, int ny, CELL *OTA, int otanum, const char *statfile, int psfavg);
+STATIC int psf_stats(int nx, int ny, IMTYPE *data, int bias, 
+		     double *fwhm, double *q);
+
+STATIC int grow_box(int nx, int ny, int NX, DTYPE *data, int thr, OBJBOX *box);
+STATIC int grow_mask(int nx, int ny, int NX, DTYPE *mask, double xfac, 
+		     double yfac, int maskval, int nbox, OBJBOX *box);
+STATIC int local_max(int i, int j, int r, int nx, int ny, int NX, DTYPE *data);
+
+STATIC int wlinearfit(int npt, double *x, double *y, 
+		      double *w, double *a, double *b);
+STATIC int linearrms(int npt, double *x, double *y, double a, double b, double *rms);
+STATIC int int_median(int n, int *key);
+STATIC double double_median(int n, double *key);
+
+STATIC int qsort_int(int n, int *key);
+STATIC double qsort_dbl(int n, double *key);
+
+STATIC void syntax(const char *prog);
+
+int write_2dfits(int nx, int ny, int sx, int sy, IMTYPE *data, int fd);
+int write_3dhdr(int nx, int ny, int nz, int ncmt, char *cmt[], int fd);
+int write_2ddata(int nx, int ny, int *ntot, IMTYPE *data, int fd);
+int write_3dend(int *ntot, int fd);
+
+#endif /* _INCLUDED_burntool_ */
Index: trunk/ppImage/src/ppImage.h
===================================================================
--- trunk/ppImage/src/ppImage.h	(revision 35684)
+++ trunk/ppImage/src/ppImage.h	(revision 35685)
@@ -26,4 +26,5 @@
     bool doMaskBuild;                   // Build internal mask
     bool doVarianceBuild;               // Build internal variance map
+    bool doApplyBurntool;               // apply burntool correction
     bool doMaskBurntool;                // mask potential burntool trails
     bool doMaskSat;                     // mask saturated pixels
@@ -182,5 +183,8 @@
 bool ppImageCheckNoise(pmConfig *config, ppImageOptions *options, pmFPAview *view);
 
-bool ppImageBurntoolMask(pmConfig *config, ppImageOptions *options, pmFPAview *view, pmReadout *mask);
+bool ppImageBurntoolMask(pmConfig *config, ppImageOptions *options, pmFPAview *view, pmReadout *readout);
+bool ppImageBurntoolMaskFromTable(pmConfig *config, ppImageOptions *options, pmFPAview *view, pmReadout *readout);
+
+bool ppImageBurntoolApply(pmConfig *config, ppImageOptions *options, pmFPAview *view, pmReadout *readout);
 
 bool ppImageDetrendPatternApply(pmConfig *config, pmChip *chip, const pmFPAview *inputView, const ppImageOptions *options);
Index: trunk/ppImage/src/ppImageArguments.c
===================================================================
--- trunk/ppImage/src/ppImageArguments.c	(revision 35684)
+++ trunk/ppImage/src/ppImageArguments.c	(revision 35685)
@@ -117,4 +117,10 @@
     pmConfigFileSetsMD (config->arguments, &argc, argv, "LINEARITY", "-linearity", "-linearlist");
 
+    if ((argnum = psArgumentGet(argc, argv, "-burntool"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        psMetadataAddStr(config->arguments, PS_LIST_TAIL, "BURNTOOL.TABLE", PS_META_REPLACE, "", argv[argnum]);
+        psArgumentRemove(argnum, &argc, argv);
+    }
+
     // chip selection is used to limit chips to be processed
     if ((argnum = psArgumentGet (argc, argv, "-chip"))) {
Index: trunk/ppImage/src/ppImageBurntoolApply.c
===================================================================
--- trunk/ppImage/src/ppImageBurntoolApply.c	(revision 35685)
+++ trunk/ppImage/src/ppImageBurntoolApply.c	(revision 35685)
@@ -0,0 +1,557 @@
+/*
+ *   ppImageBurntoolApply.c
+ *   Includes code ported from gpcsw/gpcsrc to read burntool table files and apply the fits
+ */
+
+#include "ppImage.h"
+
+// Now include the files copied from gpcsw/gpcsrc
+
+// these get redefined
+#undef MAX
+#undef MIN
+
+#include "burntool.h"
+#define EXTERN  /* define EXTERN to declare variables in params.h */
+#include "burnparams.h"
+
+
+static void * getBurntoolDataForCell(pmConfig *config, ppImageOptions *options, int burntool_cell);
+
+bool ppImageBurntoolApply(pmConfig *config, ppImageOptions *options, pmFPAview *view, pmReadout *readout)
+{
+    // convert from pmFPA's cell number to burntool's notion
+    int burntool_cell = (view->cell % 8) * 8 + (view->cell - (view->cell % 8)) / 8;
+
+    psString cellName = psMetadataLookupStr(NULL, readout->parent->concepts, "CELL.NAME");
+    (void) cellName;
+
+    CELL *burntoolData = getBurntoolDataForCell(config, options, burntool_cell);
+    if (!burntoolData) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to find burntool data for cell");
+        return false;
+    }
+
+    psImage *image = readout->image;
+
+#ifdef DUMP_CELL_IMAGES
+    psString dumpPath = NULL;
+    psStringAppend(&dumpPath, "cellimages/%s.before.fits", cellName);
+    psphotSaveImage(NULL, image, dumpPath);
+    psFree(dumpPath);
+#endif
+
+    // The following code is adapted from burntool's functions burn_apply() which is in burn_fix.c and 
+    // sub_fit() which was located in trailfit.c
+
+    int ny = image->numRows;
+
+    psF32 satVal = psMetadataLookupF32(NULL, readout->parent->concepts, "CELL.SATURATION");
+
+    for (int p = 0; p < burntoolData->npersist; p++) {
+        OBJBOX *box = &(burntoolData->persist[p]);
+
+        if(box->fiterr) continue;
+        if(box->func != BURN_PWR && box->func != BURN_EXP) {
+            continue;
+        }
+
+        int i, j, k;
+        int y0, y1, ys, dy;
+
+        y0 = box->y0;
+        y1 = (box->up) ? ny-1 : 0;
+        dy = (y0<y1) ? +1 : -1;
+        if(box->up) {
+            ys = box->sy;
+        } else {
+            ys = box->ey;
+        }
+
+        /* Calculate all needed y values */
+        double ybuf[MAXSIZE];
+        for(j=ys; dy*j<=dy*y1; j+=dy) {
+            if(box->func == BURN_PWR) {
+                if(dy*(j-y0) <= 0) {
+                    ybuf[j] = exp(box->slope*log(Y_SCALE*1.0));
+                } else {
+                    ybuf[j] = exp(box->slope*log(Y_SCALE*dy*(j-y0)));
+                }
+            } else if(box->func == BURN_EXP) {
+                //	 ybuf[j] = exp(box->slope*Y_SCALE*MAX(0,dy*(j-y0)));
+                ybuf[j] = exp(box->slope*Y_SCALE*dy*(j-y0));
+            }
+        }
+
+
+        /* Subtract the fits */
+        //   printf("%3d %3d %3d %1d %1d %8.5f\n", 
+        //	  box->cx, box->cy, box->y0, box->func, box->up, box->slope);
+
+        for(k=0; k<box->nfit; k++) {
+            i = box->xfit[k];
+            for(j=box->yfit[k]; dy*j<=dy*y1; j+=dy) {
+                psF32 inputPixel = image->data.F32[j][i];
+                if (false && inputPixel >= satVal) {
+                    // don't modify saturated pixels
+                    continue;
+                }
+
+#ifndef MATCH_BURNTOOL
+                psF32 delta = box->zero[k] * ybuf[j];
+                image->data.F32[j][i] -= delta;
+#else
+                // do math in 16 bit signed int space in order to match how the burntool program
+                // behaves with regard to underflows. burntool allows underflows so that the burntool
+                // application is reversible.
+                // Verified that the image matches the burntool implementation with this code
+                // which shows that we are reading and processing the table correctly.
+                int delta = box->zero[k] * ybuf[j] + 0.5;
+                psS16 pixel;
+                // adjust for BZERO
+                if (inputPixel >= 65535) {
+                    pixel = 32767;
+                } else if (inputPixel <= 0) {
+                    pixel = -32768;
+                } else {
+                    pixel = inputPixel - 32768;
+                }
+                pixel -= delta;
+                // replace the pixel with correction applied
+                image->data.F32[j][i] = (psF32) pixel + 32768;
+#endif
+            }
+        }
+    }
+#ifdef DUMP_CELL_IMAGES
+    dumpPath = NULL;
+    psStringAppend(&dumpPath, "cellimages/%s.after.fits", cellName);
+    psphotSaveImage(NULL, image, dumpPath);
+    psFree(dumpPath);
+#endif
+
+    return true;
+}
+
+
+bool burntoolFileRead = false;
+static CELL OTA[MAXCELL]; // cell structure for entire OTA
+static void * getBurntoolDataForCell(pmConfig *config, ppImageOptions *options, int burntool_cell)
+{
+    if (!burntoolFileRead) {
+        psString burntoolTablePath = psMetadataLookupStr(NULL, config->arguments, "BURNTOOL.TABLE");
+        if (!burntoolTablePath) {
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup BURNTOOL.TABLE in arguments");
+            return NULL;
+        }
+        psString burntoolFileName = pmConfigConvertFilename(burntoolTablePath, config, false, false);
+        if (!burntoolFileName) {
+            psError(PS_ERR_UNKNOWN, false, "failed to convert filename for %s", burntoolTablePath);
+            return NULL;
+        }
+        if (persist_read(OTA, burntoolFileName, 1)) {
+            psError(PS_ERR_UNKNOWN, "true", "failed to read burntool file");
+            return NULL;
+        }
+        psFree(burntoolFileName);
+        burntoolFileRead = true;
+    }
+
+    return (void *) &OTA[burntool_cell];
+}
+#define PPIMAGE_BURNTOOL_DEBUG 0
+
+// adapted from ppImageBurntoolMask
+bool ppImageBurntoolMaskFromTable(pmConfig *config, ppImageOptions *options, pmFPAview *view, pmReadout *mask)
+{
+  bool status = true;
+  int burntool_cell;
+
+  /* Redirects and Memory juggling. */
+  view->readout = 0;
+  psImage *image = mask->mask;
+
+  /* Set the maskValue from the recipes. */
+  psImageMaskType maskValue = options->burntoolMask;
+
+  burntool_cell = (view->cell % 8) * 8 + (view->cell - (view->cell % 8)) / 8;
+  CELL *burntoolData = getBurntoolDataForCell(config, options, burntool_cell);
+  if (!burntoolData) {
+    psError(PS_ERR_UNKNOWN, false, "Unable to find burntool data for cell");
+    return false;
+  }
+
+#if PPIMAGE_BURNTOOL_DEBUG
+  psLogMsg("ppImageBurntoolMask", 4, "Status: %d %d\n",burntoolData->npersist,maskValue);
+#endif
+
+  psLogMsg("ppImageBurntoolMask", 4, "Cell mapping: %d %d %d\n",view->cell,burntool_cell,-1);
+  for (int row = 0; row < burntoolData->npersist; row++) {
+      OBJBOX *box = &(burntoolData->persist[row]);
+      if (((options->burntoolTrails & 0x01)&& (box->func == 4))||
+          (((options->burntoolTrails & 0x02)&& (box->up == 1))||
+           ((options->burntoolTrails & 0x04)&& (box->up == 0)))) {
+        /*       If the fit fails, burntool reports zero here.  This
+                 signifies that it expected to see a trail (else why
+                 fit) but did not find it when it attempted to
+                 correct. */
+#if PPIMAGE_BURNTOOL_DEBUG
+          psLogMsg ("ppImageBurntoolMask", 4, "Masking! %d (%d %d %d) %d %d",
+                  burntool_cell,
+                  ((options->burntoolTrails & 0x0001)&& (box->nfit == 0)),
+                  ((options->burntoolTrails & 0x02)&& (box->up == 1)),
+                  ((options->burntoolTrails & 0x04)&& (box->up == 0)),
+                  options->burntoolTrails,
+                  maskValue
+                  );
+#endif
+          for (int i = box->sxfit; i<= box->exfit; i++) {
+
+              if (box->up == 0) {
+                  for (int j = 0; j <= box->y1p; j++) {
+#if PPIMAGE_BURNTOOL_DEBUG
+                    psLogMsg("ppImageBurntoolMask", 4, "Noisy!: %d %d %d %d\n",
+                         i,j,image->data.PS_TYPE_IMAGE_MASK_DATA[j][i],maskValue);
+#endif
+                      image->data.PS_TYPE_IMAGE_MASK_DATA[j][i] |= maskValue;
+                  }
+              } else {
+                  for (int j = box->y1m; j < image->numRows ; j++) {
+#if PPIMAGE_BURNTOOL_DEBUG
+                      psLogMsg("ppImageBurntoolMask", 4, "Noisy!: %d %d %d %d\n",
+                       i,j,image->data.PS_TYPE_IMAGE_MASK_DATA[j][i],maskValue);
+#endif
+                      image->data.PS_TYPE_IMAGE_MASK_DATA[j][i] |= maskValue;
+                 }
+             }
+         }
+      }
+  }
+
+  return(status);
+}
+
+/* From persistio.c - read and write persistence info */
+/****************************************************************/
+/* persist_read(): Read all the persistence trails from a file */
+STATIC int persist_read(CELL *cell, const char *infile, int apply)
+{
+   int i, k, nbox=0;
+   char line[1024];
+   FILE *fp;
+
+/* Initialize the counts */
+   for(k=0; k<MAXCELL; k++) {
+      cell[k].npersist = 0;
+      cell[k].persist = NULL;
+   }
+
+   if(infile == NULL) return(0);
+
+   if( (fp=fopen(infile, "r")) == NULL) {
+      fprintf(stderr, "\rerror: cannot open '%s' for reading\n", infile);
+      return(-1);
+   }
+/* Read in all the burns */
+   nbox = 0;
+   while(fgets(line, 1024, fp) != NULL) {
+      if(line[0] == '#') continue;
+      sscanf(line, "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %lf %d %d %d %d",
+	     &boxbuf[nbox].cell, &boxbuf[nbox].time,
+	     &boxbuf[nbox].cx, &boxbuf[nbox].cy,
+	     &boxbuf[nbox].max, &boxbuf[nbox].y0,
+	     &boxbuf[nbox].sx, &boxbuf[nbox].sy, 
+	     &boxbuf[nbox].ex, &boxbuf[nbox].ey,
+	     &boxbuf[nbox].y0m, &boxbuf[nbox].y0p,
+	     &boxbuf[nbox].y1m, &boxbuf[nbox].y1p,
+	     &boxbuf[nbox].x0m, &boxbuf[nbox].x0p,
+	     &boxbuf[nbox].x1m, &boxbuf[nbox].x1p,
+	     &boxbuf[nbox].func, &boxbuf[nbox].up,
+	     &boxbuf[nbox].slope, &boxbuf[nbox].nfit,
+	     &boxbuf[nbox].sxfit, &boxbuf[nbox].exfit,
+	     &boxbuf[nbox].fiterr);
+      if(boxbuf[nbox].nfit > 0) {
+	 boxbuf[nbox].zero = (double *)calloc(boxbuf[nbox].nfit, sizeof(double));
+	 boxbuf[nbox].xfit = (int *)calloc(boxbuf[nbox].nfit, sizeof(int));
+	 boxbuf[nbox].yfit = (int *)calloc(boxbuf[nbox].nfit, sizeof(int));
+	 if(boxbuf[nbox].zero == NULL ||
+	    boxbuf[nbox].xfit == NULL ||
+	    boxbuf[nbox].yfit == NULL) {
+	    fprintf(stderr, "\rerror: failed to alloc boxbuf\n");
+	    exit(-673);
+	 }
+
+	 for(i=0; i<boxbuf[nbox].nfit; i++) {
+	    if(fgets(line, 1024, fp) == NULL) {
+	       fprintf(stderr, "\rerror: short read of burn lines\n");
+	       return(-1);
+	    }
+	    sscanf(line, "%d %d %lf\n", &boxbuf[nbox].xfit[i], 
+		   &boxbuf[nbox].yfit[i], &boxbuf[nbox].zero[i]);
+	 }
+      }
+// 100203 JT: fiterr now saved and read, refit if not just an "apply"
+      if(!apply) boxbuf[nbox].fiterr = 0;
+/* Augment counts */
+      k = boxbuf[nbox].cell;
+      if(k < 0 || k >= MAXCELL) {
+	 fprintf(stderr, "\rerror: illegal cell %d at %d from '%s'\n", 
+		 k, nbox, line);
+	 fclose(fp);
+	 return(-1);
+      }
+      cell[k].npersist += 1;
+
+      if(++nbox >= MAXBURN) {
+	 fprintf(stderr, "\rerror: persist_read overflowed MAXBURN\n");
+	 fclose(fp);
+	 return(-1);
+      }
+   }
+
+/* Allocate some space for them */
+   for(k=0; k<MAXCELL; k++) {
+      if( (i=cell[k].npersist) > 0) {
+	 if( (cell[k].persist = (OBJBOX *)calloc(i, sizeof(OBJBOX))) == NULL) {
+	    fprintf(stderr, "\rerror: failed to alloc cell persist buffer\n");
+	    exit(-674);
+	 }
+	 cell[k].npersist = 0;
+      }
+   }
+/* Copy the results to the cells */
+   for(i=0; i<nbox; i++) {
+      k = boxbuf[i].cell;
+      memcpy(cell[k].persist+cell[k].npersist, boxbuf+i, sizeof(OBJBOX));
+      cell[k].npersist += 1;
+   }
+
+   fclose(fp);
+   return(0);
+}
+
+
+#ifdef NOT_NEEDED
+
+#define DIFFERENT_STREAK 20	/* Proximity for union versus intersection */
+
+/****************************************************************/
+/* persist_merge(): Disentangle overlapping persistence streaks */
+STATIC int persist_merge(CELL *cell)
+{
+   int i, j, k, n, nbox, xs, xe;
+   int jp, kp;
+   int lapmax;
+   OBJBOX *box;
+   int xusage[MAXSIZE], yctr[MAXSIZE], boxid[MAXSIZE];
+   double zk, zj;
+
+/* Look at all the boxes -- if not isolated then merge or sever */
+   nbox = cell->npersist;
+   box = cell->persist;
+
+/* Assess the clustering of all the streaks */
+   for(i=0; i<MAXSIZE; i++) xusage[i] = 0;
+   for(i=0; i<MAXSIZE; i++) boxid[i] = -1;
+   for(k=0; k<nbox; k++) {
+      if(box[k].exfit >= MAXSIZE-1) continue;
+      for(i=box[k].sxfit; i<=box[k].exfit; i++) xusage[i] += 1;
+   }
+
+/* Identify clusters */
+   for(xs=0; xs<MAXSIZE-1; xs++) {
+      if(xusage[xs] == 0) continue;
+      for(xe=xs, lapmax=0; xe<MAXSIZE-1; xe++) {
+	 if(xusage[xe+1] == 0) break;
+	 lapmax = MAX(lapmax, xusage[xe]);
+      }
+      if(lapmax == 1) {	/* No overlap?  No problem. */
+	 xs = xe + 1;	/* Hop to next gap */
+	 continue;
+      }
+
+/* Which boxes overlap this cluster? */
+      for(k=n=0; k<nbox; k++) {
+	 if(box[k].sxfit > xe || box[k].exfit < xs) continue;
+	 boxid[n] = k;
+	 yctr[n] = (box[k].y0m+box[k].y1m+box[k].y0p+box[k].y0p+2) / 4;
+	 n++;
+      }
+
+/* Case 1: different y start => sever */
+      for(kp=0; kp<n; kp++) {
+	 k = boxid[kp];
+	 zk = 0.0;
+	 if(box[k].nfit > 0) zk = box[k].zero[box[k].nfit/2];
+	 for(jp=kp+1; jp<n; jp++) {
+	    j = boxid[jp];
+	    zj = 0.0;
+	    if(box[j].nfit > 0) zj = box[j].zero[box[j].nfit/2];
+	    if(ABS(yctr[jp]-yctr[kp]) > DIFFERENT_STREAK) {
+/* Trim back the feebler streak */
+	       if(zk > zj) {
+		  if(box[j].sxfit >= box[k].sxfit) 
+		     box[j].sxfit = MAX(box[j].sxfit, box[k].exfit+1);
+		  if(box[j].exfit <= box[k].exfit) 
+		     box[j].exfit = MIN(box[j].exfit, box[k].sxfit-1);
+	       } else {
+		  if(box[k].sxfit >= box[j].sxfit) 
+		     box[k].sxfit = MAX(box[k].sxfit, box[j].exfit+1);
+		  if(box[k].exfit <= box[j].exfit) 
+		     box[k].exfit = MIN(box[k].exfit, box[j].sxfit-1);
+	       }
+	    }
+	 }
+      }
+
+/* Case 2: pretty much the same y start => union */
+      for(kp=0; kp<n; kp++) {
+	 k = boxid[kp];
+	 for(jp=kp+1; jp<n; jp++) {
+	    j = boxid[jp];
+	    /* CZW: Since I added the initialization statement above, */
+	    /*      any box with boxid == -1 hasn't been set, and */
+	    /*      therefore needs to be dropped, I think. */
+	    if ((k < 0)||(j < 0)) { continue; }
+	    if(ABS(yctr[jp]-yctr[kp]) <= DIFFERENT_STREAK) {
+/* Merge k into j, trash k from further consideration */
+	       box[j].time = MAX(box[j].time, box[k].time);
+	       box[j].sx = MIN(box[j].sx, box[k].sx);
+	       box[j].ex = MAX(box[j].ex, box[k].ex);
+	       box[j].sy = MIN(box[j].sy, box[k].sy);
+	       box[j].ey = MAX(box[j].ey, box[k].ey);
+	       box[j].y0m = MIN(box[j].y0m, box[k].y0m);
+	       box[j].y0p = MAX(box[j].y0p, box[k].y0p);
+	       box[j].x0m = MIN(box[j].x0m, box[k].x0m);
+	       box[j].x0p = MAX(box[j].x0p, box[k].x0p);
+	       box[j].y1m = MIN(box[j].y1m, box[k].y1m);
+	       box[j].y1p = MAX(box[j].y1p, box[k].y1p);
+	       box[j].x1m = MIN(box[j].x1m, box[k].x1m);
+	       box[j].x1p = MAX(box[j].x1p, box[k].x1p);
+	       box[j].sxfit = MIN(box[j].sxfit, box[k].sxfit);
+	       box[j].exfit = MAX(box[j].exfit, box[k].exfit);
+
+	       box[k].exfit = box[k].sxfit - 1;
+	       yctr[kp] = -2 * DIFFERENT_STREAK;
+	    }
+	 }
+      }
+
+      xs = xe + 1;	/* Hop to next gap */
+
+   } /* Cluster loop */
+
+/* Excise the boxes which have been eradicated (sxfit > exfit) */
+   for(k=0; k<nbox; k++) {
+      if(box[k].sxfit > box[k].exfit) {
+	 for(j=k; j<nbox-1; j++) memcpy(box+j, box+j+1, sizeof(OBJBOX));
+	 nbox--;
+	 k--;
+      }
+   }
+   cell->npersist = nbox;
+
+
+   return(0);
+}
+
+
+/****************************************************************/
+/* persist_write(): Write all the persistence data for the next image */
+STATIC int persist_write(CELL *cell, const char *outfile)
+{
+   int i, k, j, err=0;
+   FILE *fp;
+
+   if(outfile == NULL) return(0);
+   if( (fp=fopen(outfile, "w")) == NULL) {
+      fprintf(stderr, "\rerror: cannot open '%s' for writing\n", outfile);
+      return(-1);
+   }
+
+/* Dump out all the burns for the next image... */
+/* FIXME: what to do about generic cell info? */
+   fprintf(fp,  "# Cell: %d  sky= %d   rms= %d   bias= %d\n", 
+	   cell[0].cell, cell[0].sky, cell[0].rms, cell[0].bias);
+   fprintf(fp, "#Cell time    cx  cy  max   y0   sx  sy   ex  ey  y0m y0p y1m y1p x0m x0p x1m x1p F up    slope nfit sxf exf\n");
+
+   for(j=0; j<MAXCELL; j++) {
+/* First: patched up persists */
+      for(k=0; k<cell[j].npersist; k++) {
+
+/* Retire old burns */
+	 if(cell[j].time - cell[j].persist[k].time > EXPIRE_TRAIL_TIME)
+	    continue;
+
+	 if(PERSIST_RETAIN) {
+/* Keep fits which have a dubious slope */
+	    if(cell[j].persist[k].fiterr != FIT_SLOPE_ERROR) {
+	       if(cell[j].persist[k].fiterr) continue;
+	       if(cell[j].persist[k].nfit <= 0) continue;
+	    }
+	 } else {
+	    if(cell[j].persist[k].fiterr) continue;
+	    if(cell[j].persist[k].nfit <= 0) continue;
+	 }
+	 fprintf(fp, "%3d %7d  %3d %3d %5d %3d  %3d %3d  %3d %3d  %3d %3d %3d %3d %3d %3d %3d %3d  %1d %1d %9.6f %3d %3d %3d %d\n",
+		 j, cell[j].persist[k].time, 
+		 cell[j].persist[k].cx, cell[j].persist[k].cy, 
+		 cell[j].persist[k].max, cell[j].persist[k].y0,
+		 cell[j].persist[k].sx, cell[j].persist[k].sy, 
+		 cell[j].persist[k].ex, cell[j].persist[k].ey,
+		 cell[j].persist[k].y0m, cell[j].persist[k].y0p,
+		 cell[j].persist[k].y1m, cell[j].persist[k].y1p,
+		 cell[j].persist[k].x0m, cell[j].persist[k].x0p,
+		 cell[j].persist[k].x1m, cell[j].persist[k].x1p,
+		 cell[j].persist[k].func, cell[j].persist[k].up, 
+		 cell[j].persist[k].slope, cell[j].persist[k].nfit,
+		 cell[j].persist[k].sxfit, cell[j].persist[k].exfit, 
+		 cell[j].persist[k].fiterr);
+	 for(i=0; i<cell[j].persist[k].nfit; i++) {
+	    fprintf(fp, "%3d %3d %8.4f\n", cell[j].persist[k].xfit[i], 
+		    cell[j].persist[k].yfit[i], cell[j].persist[k].zero[i]);
+	 }
+      }
+
+/* Second: new burns */
+      for(k=0; k<cell[j].nburn; k++) {
+	 if(!cell[j].burn[k].burned) continue;
+	 if(PERSIST_RETAIN) {
+/* Keep fits which have a dubious slope */
+	    if(cell[j].burn[k].fiterr != FIT_SLOPE_ERROR) {
+	       if(cell[j].burn[k].fiterr && 
+		  cell[j].burn[k].fiterr != FIT_TOP_ERROR) continue;
+	       if(cell[j].burn[k].nfit <= 0) continue;
+	    }
+	 } else {
+	    if(cell[j].burn[k].fiterr && 
+	       cell[j].burn[k].fiterr != FIT_TOP_ERROR) continue;
+	    if(cell[j].burn[k].nfit <= 0) continue;
+	 }
+
+	 i = (cell[j].burn[k].ex - cell[j].burn[k].sx + 1) / 2;
+	 fprintf(fp, "%3d %7d  %3d %3d %5d %3d  %3d %3d  %3d %3d  %3d %3d %3d %3d %3d %3d %3d %3d  %1d %1d %9.6f %3d %3d %3d %d\n", 
+		 j, cell[j].burn[k].time, 
+		 cell[j].burn[k].cx, cell[j].burn[k].cy,
+		 cell[j].burn[k].max, cell[j].burn[k].y0,
+		 cell[j].burn[k].sx, cell[j].burn[k].sy, 
+		 cell[j].burn[k].ex, cell[j].burn[k].ey,
+		 cell[j].burn[k].y0m, cell[j].burn[k].y0p,
+		 cell[j].burn[k].y1m, cell[j].burn[k].y1p,
+		 cell[j].burn[k].x0m, cell[j].burn[k].x0p,
+		 cell[j].burn[k].x1m, cell[j].burn[k].x1p,
+		 cell[j].burn[k].func, cell[j].burn[k].up, 
+		 cell[j].burn[k].slope, cell[j].burn[k].nfit,
+		 cell[j].burn[k].sxfit, cell[j].burn[k].exfit, 
+		 cell[j].burn[k].fiterr);
+	 for(i=0; i<cell[j].burn[k].nfit; i++) {
+	    fprintf(fp, "%3d %3d %8.4f\n", cell[j].burn[k].xfit[i], 
+		    cell[j].burn[k].yfit[i], cell[j].burn[k].zero[i]);
+	 }
+      }
+   }
+   fclose(fp);
+
+   return(err);
+}
+#endif /* NOT_NEEDED */
Index: trunk/ppImage/src/ppImageDetrendReadout.c
===================================================================
--- trunk/ppImage/src/ppImageDetrendReadout.c	(revision 35684)
+++ trunk/ppImage/src/ppImageDetrendReadout.c	(revision 35685)
@@ -56,5 +56,5 @@
     }
     done_video_check:
-    
+
     // Masking on the basis of pixel value needs to be done before anything else, so the values are pristine.
     if (options->doMaskBuild) {
@@ -74,6 +74,19 @@
       pmMaskBadPixels(input, mask, options->maskValue);
     }
+
+    if (options->doApplyBurntool)
+    {
+        // extern bool ppImageBurntoolApply(pmConfig *, ppImageOptions *, pmFPAview *, pmReadout *);
+        ppImageBurntoolApply(config, options, view, input);
+    }
+    
     if (options->doMaskBurntool) {
-      ppImageBurntoolMask(config,options,view,input);
+        if (options->doApplyBurntool) {
+            // build burntool mask from data input burntool table
+            ppImageBurntoolMaskFromTable(config,options,view,input);
+        } else {
+            // build burntool mask from data in the input image's fits extension
+            ppImageBurntoolMask(config,options,view,input);
+        }
     }
 
Index: trunk/ppImage/src/ppImageOptions.c
===================================================================
--- trunk/ppImage/src/ppImageOptions.c	(revision 35684)
+++ trunk/ppImage/src/ppImageOptions.c	(revision 35685)
@@ -23,4 +23,5 @@
     options->doMaskLow       = false;   // mask low pixels
     options->doMaskBurntool  = false;   // mask potential burntool trails
+    options->doApplyBurntool = false;   // apply burntool correction
     options->doVarianceBuild = false;   // Build internal variance
     options->doMask          = false;   // Mask bad pixels
@@ -231,4 +232,5 @@
     options->doMaskLow       = psMetadataLookupBool(NULL, recipe, "MASK.LOW");
     options->doMaskBurntool  = psMetadataLookupBool(NULL, recipe, "MASK.BURNTOOL");
+    options->doApplyBurntool = psMetadataLookupBool(NULL, recipe, "APPLY.BURNTOOL");
     options->doVarianceBuild = psMetadataLookupBool(NULL, recipe, "VARIANCE.BUILD");
     options->doAuxMask       = psMetadataLookupBool(NULL, recipe, "MASK.AUXMASK");
Index: trunk/ppImage/src/ppImageParseCamera.c
===================================================================
--- trunk/ppImage/src/ppImageParseCamera.c	(revision 35684)
+++ trunk/ppImage/src/ppImageParseCamera.c	(revision 35685)
@@ -184,4 +184,12 @@
         }
         outCTE->save = true;
+    }
+    if (options->doApplyBurntool) {
+        // If no burntool file was supplied do not fail. (camera_exp.pl does not supply it
+        // for example.
+        if (!psMetadataLookupStr(NULL, config->arguments, "BURNTOOL.TABLE")) {
+            psWarning("BURNTOOL.TABLE not supplied setting doApplyBurntool to false");
+            options->doApplyBurntool = false;
+        }
     }
 
