Index: /trunk/Ohana/src/addstar/src/sky_tessalation.c
===================================================================
--- /trunk/Ohana/src/addstar/src/sky_tessalation.c	(revision 23228)
+++ /trunk/Ohana/src/addstar/src/sky_tessalation.c	(revision 23229)
@@ -240,11 +240,18 @@
   // integer number of dec zones between -90 and +90
 
-  nDEC = 180.0 / CELLSIZE;
-  dDEC = 180.0 / nDEC;
+  // in fact, we place a single image on each pole, so the real range of dec is 180.0 - CELLSIZE:
+
+  nDEC = (180.0 - CELLSIZE) / CELLSIZE;
+  dDEC = (180.0 - CELLSIZE) / nDEC;
+  nDEC += 2;
+
+  // a test
+  // for (dec = 0.0 + 0.5*dDEC; dec < +90.0; dec += dDEC) {
 
   // generate the a collection of rectangles for each ring
-  for (dec = -90.0 + 0.5*dDEC; dec < +90.0; dec += dDEC) {
+  for (dec = -90.0; dec < +90.0 + 0.5*dDEC; dec += dDEC) {
 
     ring = sky_rectangle_ring (dec, dDEC, &Nring);
+    if (!ring) continue;
 
     // subdivide each image (Nx x Ny subcells)
@@ -529,7 +536,7 @@
 SkyRectangle *sky_rectangle_ring (float dec, float dDEC, int *nring) {
 
-  int i, nRA, NX, NY;
-  float dRA, decLower;
+  int i, NX, NY, nRA;
   SkyRectangle *ring;
+  float theta, dRA;
 
   // 'dec' is a guess at the center of the cell; in fact, we need to choose decLower and
@@ -537,9 +544,62 @@
 
   // we can determine the 'lower' bound (bound closest to the equator):
-  decLower = (dec > 0.0) ? dec - 0.5*dDEC : dec + 0.5*dDEC;
-
-  // Subdivide the 'lower' bound into an integer number of segments:
-  nRA = cos(dec*RAD_DEG) * 360.0 / CELLSIZE; // CELLSIZE is a projection size
-  dRA = 360.0 / nRA;                         // dRA is a size in RA degrees
+  float decLower = (dec > 0.0) ? dec - 0.5*dDEC : dec + 0.5*dDEC;
+
+  // solve for actual cellsize (\theta):  tan(\delta_{n+1} - \theta/2) = tan(\delta_n + \theta/2)cos(\alpha_n / 2)
+  float decUpper = (dec > 0.0) ? dec + dDEC : dec - dDEC;
+
+  if (fabs(dec) + 0.5*dDEC > 90.0) {
+    // onPole = TRUE;
+    theta = dDEC;
+    nRA = 1;
+    dRA = theta / cos(decLower*RAD_DEG); // make a square at the pole
+  } else {
+    // onPole = FALSE;
+    // Subdivide the 'lower' bound into an integer number of segments:
+    nRA = cos(RAD_DEG*decLower) * 360.0 / CELLSIZE; // CELLSIZE is a projection size
+    dRA = 360.0 / nRA;                         // dRA is a size in RA degrees == \alpha_n
+
+    // tan(decUpper - theta/2) = tan(dec + theta/2) cos(dRA / 2);
+
+    // we solve this equation for theta (fairly ugly: expand the tangents into sin/cos, expand the 
+    // sum-of-angle sine and cosine, multiply through, convert via half-angle formulae and write 
+    // as a quadratic expression in sine(theta/2)
+  
+    float sd1 = sin(RAD_DEG*decUpper);
+    float cd1 = cos(RAD_DEG*decUpper);
+    float sd2 = sin(RAD_DEG*dec);
+    float cd2 = cos(RAD_DEG*dec);
+    float   k = cos(RAD_DEG*dRA/2.0);
+
+    float c1 =  (sd1*cd2 + sd2*cd1)*(1.0 - k);
+    float c2 =  (sd1*cd2 - sd2*cd1)*(1.0 + k);
+    float c3 = -(sd1*sd2 + cd1*cd2)*(1.0 + k); 
+
+    float A = SQ(c3) + SQ(c2);
+    float B = 2*c1*c3;
+    float C = SQ(c1) - SQ(c2);
+
+    float arg = SQ(B) - 4.0*A*C;
+
+    float root;
+
+    if (dec >= 0.0) {
+      root = (-B + sqrt (arg)) / (2.0*A);
+      theta = +DEG_RAD*asin(root);
+    } else {
+      root = (-B - sqrt (arg)) / (2.0*A);
+      theta = -DEG_RAD*asin(root);
+    }
+
+    // the negative solution yields a negative cellsize 
+    // float root2 = (-B - sqrt (arg)) / (2.0*A);
+    // float theta2 = DEG_RAD*asin(root2);
+
+    // test lines:
+    // float r1 = tan(RAD_DEG*(decUpper - 0.5*theta1));
+    // float r2 = tan(RAD_DEG*(dec + 0.5*theta1));
+    // fprintf (stdout, "%f %f  %f  %f  %f %f  %f %f  %f %f %f\n", dec, decUpper, dRA, arg, root1, root2, theta1, theta2, r1, r2, k*r2);
+  }
+  fprintf (stdout, "%f %f  %f x %f (%d)\n", dec, decUpper, dRA, theta, nRA);
 
   // I think we need to return the value of dec for the next ring, but I am not sure...
@@ -559,6 +619,6 @@
   
     // range values are in projected degrees
-    NX = cos(dec*RAD_DEG) * dRA  * 3600.0 / SCALE;
-    NY =                    dDEC * 3600.0 / SCALE;
+    NX = cos(decLower*RAD_DEG) * dRA   * 3600.0 / SCALE;
+    NY =                         theta * 3600.0 / SCALE;
 
     // crpix1,crpix2 is the projection center
@@ -576,7 +636,7 @@
 
 
-    fprintf (stderr, "%f %f  : %f %f\n", 
-	     ring[i].coords.crval1, ring[i].coords.crval2, 
-	     ring[i].coords.crpix1, ring[i].coords.crpix2);
+    // fprintf (stderr, "%f %f  : %f %f\n", 
+    // ring[i].coords.crval1, ring[i].coords.crval2, 
+    // ring[i].coords.crpix1, ring[i].coords.crpix2);
   }
 
Index: /trunk/dbconfig/changes.txt
===================================================================
--- /trunk/dbconfig/changes.txt	(revision 23228)
+++ /trunk/dbconfig/changes.txt	(revision 23229)
@@ -795,6 +795,4 @@
 ALTER TABLE magicInputSkyfile DROP PRIMARY KEY, ADD PRIMARY KEY(magic_id, diff_id, node);
 
-
-
 -- Version: 1.1.48
 
@@ -834,4 +832,6 @@
     WHERE warpImfile.skycell_id IS NULL;
 
-
-
+-- Version: 1.1.49 (change detrend sequence)
+
+show create table detResidImfile;
+alter table detResidImfile drop foreign key detResidImfile_ibfk_3;
Index: /trunk/dbconfig/config.md
===================================================================
--- /trunk/dbconfig/config.md	(revision 23228)
+++ /trunk/dbconfig/config.md	(revision 23229)
@@ -2,4 +2,4 @@
     pkg_name        STR     ippdb
     pkg_namespace   STR     ippdb
-    pkg_version     STR     1.1.48
+    pkg_version     STR     1.1.49
 END
Index: /trunk/dbconfig/det.md
===================================================================
--- /trunk/dbconfig/det.md	(revision 23228)
+++ /trunk/dbconfig/det.md	(revision 23229)
@@ -90,14 +90,15 @@
     class_id    STR         64      # Primary Key
     uri         STR         255
-    recipe      STR         64
-    bg          F64         0.0
-    bg_stdev    F64         0.0
-    bg_mean_stdev   F64     0.0
-    user_1      F64         0.0
-    user_2      F64         0.0
-    user_3      F64         0.0
-    user_4      F64         0.0
-    user_5      F64         0.0
-#   XXX does it make sense to 'clean' the stacked imfiled?
+    # XXX missing path_base
+    recipe      STR         64
+    bg          F64         0.0
+    bg_stdev    F64         0.0
+    bg_mean_stdev   F64     0.0
+    user_1      F64         0.0
+    user_2      F64         0.0
+    user_3      F64         0.0
+    user_4      F64         0.0
+    user_5      F64         0.0
+    #   XXX does it make sense to 'clean' the stacked imfiled? (EAM: yes)
     data_state  STR         64      # full, cleaned, purged (only track end states; request states are in detRunSummary by iteration)
     fault       S16         0       # Key NOT NULL
@@ -148,18 +149,4 @@
     fault       S16         0       # Key NOT NULL
 END
-
-#detMasterFrame METADATA
-#    det_id      S64         0       # Primary Key
-#    iteration   S32         0       # Primary Key
-#    comment     STR         255
-#END
-#
-## drop?
-#detMasterImfile METADATA
-#    det_id      S64         0       # Primary Key
-#    class_id    STR         64      # Primary Key
-#    uri         STR         255
-#    recipe      STR         64
-#END
 
 detResidImfile METADATA
Index: /trunk/doc/misc/detnorm.rework.txt
===================================================================
--- /trunk/doc/misc/detnorm.rework.txt	(revision 23229)
+++ /trunk/doc/misc/detnorm.rework.txt	(revision 23229)
@@ -0,0 +1,76 @@
+
+I've been running the detrend analysis on the new gpc1 flats, and
+discovered an important issue.  These chips have significant gain
+variations across the devices.  
+
+We need to have detrend.normstats run on the results of the
+detResidImfiles, rather than detProcessedImfiles.  This requires
+altering the processing sequence from this:
+
+* process each of the input images (detrend.process.imfile)
+* generate a stack for each chip (detrend.stack.imfile)
+* use the stats from the processed chips to determine per-chip
+renormalizations for the stacks. the goal in this step is to yield a
+flat-field image for which a flattened input image is flat across the
+entire mosaic, not just each chip.  (detrend.normstats.imfile)
+* apply the normalization to each of the stacks (detrend.norm.imfile)
+* generate mosaic-wide stats and jpegs for the normalized flats
+(detrend.norm.exp)
+* apply the normalized flats to the processed images (detrend.resid.imfile)
+* generate per-exposure stats and jpegs for the residual images.
+(detrend.resid.exp)
+
+The problem is that, with a large gain variation across each chip, the
+measurement of the per-chip stats from the processed images is not a
+good measurement of the flattened mean value on that chip.  We really
+should be measuring the statistics from the flattened images.  Paul
+and I discussed the pipeline organization and have concluded that we
+can re-work the detrend pipeline without too much work to achieve this
+if we perform the flat-fielding before the normalization, and do the
+following steps:
+
+* process each of the input images (detrend.process.imfile)
+* generate a stack for each chip (detrend.stack.imfile)
+* apply the flats to the processed images (detrend.resid.imfile)
+* use the stats from the residual chips to determine per-chip
+renormalizations for the stacks (detrend.normstats.imfile)
+* apply the normalization to each of the stacks (detrend.norm.imfile)
+* generate mosaic-wide stats and jpegs for the normalized flats
+(detrend.norm.exp)
+* generate per-exposure stats and jpegs for the residual images,
+applying the normalizations on-the-fly. (detrend.resid.exp)
+
+--
+
+Tasks to do:
+
+* ppStack could do an approximate normalization on the fly (perhaps
+  not needed; perhaps already done? what is the output scaling of the
+  resulting flat stack?)
+
+* dettool -toresidimfile should trigger on completed detStackedImfile
+  (not detNormalizedImfile) (DONE)
+
+* dettool -tonormalizedstat should trigger on completed
+  detResidImfile, not detStackedImfile (DONE)
+
+* detrend_norm_calc.pl needs to read the stats from detResidImfile,
+  not detProcessedImfile (DONE)
+
+* detrend.norm.imfile uses the results of detrend_norm_calc.pl as
+  before (DONE)
+
+* detrend.resid.exp applies the normalizations on-the-fly to the b1
+  and b2 input images, and to the exposure-wide stats pushed into the
+  db. (DONE)
+
+* detrend.resid.exp blocks until detrend.norm.exp is done
+
+*** the 'verify' analysis needs to be modified.  I think we can keep
+    it as it was (detrend.process.imfile -> detrend.resid.imfile ->
+    detrend.resid.exp) where the resid imfile is generated using the
+    detNormalizedImfile rather than the detStackedImfile.  Does
+    detrend.resid.exp need to be modified for 'verify' mode to *avoid*
+    applying the normalization?
+
+
Index: /trunk/ippScripts/scripts/detrend_norm_apply.pl
===================================================================
--- /trunk/ippScripts/scripts/detrend_norm_apply.pl	(revision 23228)
+++ /trunk/ippScripts/scripts/detrend_norm_apply.pl	(revision 23229)
@@ -38,5 +38,5 @@
     'iteration|n=s'     => \$iter,       # Iteration
     'class_id|i=s'      => \$class_id,   # Class ID
-    'value|v=s'         => \$value,      # Value to multiple (for normalisation)
+    'value|v=s'         => \$value,      # Value to apply (for normalisation)
     'input_uri|u=s'     => \$input_uri,  # Input file
     'camera|c=s'        => \$camera,     # Camera
Index: /trunk/ippScripts/scripts/detrend_norm_calc.pl
===================================================================
--- /trunk/ippScripts/scripts/detrend_norm_calc.pl	(revision 23228)
+++ /trunk/ippScripts/scripts/detrend_norm_calc.pl	(revision 23229)
@@ -90,6 +90,7 @@
 my @files;                      # The input files
 {
-    my $command = "$dettool -processedimfile";
-    $command .= " -det_id $det_id"; # Command to run
+    my $command = "$dettool -residimfile";
+    $command .= " -det_id $det_id";
+    $command .= " -iteration $iter";
     $command .= " -included"; # only use the inputs for this detrend run to calculate the norm
     $command .= " -dbname $dbname" if defined $dbname;
@@ -98,5 +99,5 @@
     print "Running [$command]...\n" if $verbose;
     if (not run(\@command, \$stdin, \$stdout, \$stderr)) {
-        &my_die("Unable to perform dettool -processedimfile on detrend $det_id/$iter: $?",
+        &my_die("Unable to perform dettool -residimfile on detrend $det_id/$iter: $?",
                 $det_id, $iter, $PS_EXIT_SYS_ERROR);
     }
Index: /trunk/ippScripts/scripts/detrend_resid_exp.pl
===================================================================
--- /trunk/ippScripts/scripts/detrend_resid_exp.pl	(revision 23228)
+++ /trunk/ippScripts/scripts/detrend_resid_exp.pl	(revision 23229)
@@ -88,7 +88,54 @@
 &my_die("Unrecognised detrend type: $det_type", $det_id, $iter, $PS_EXIT_PROG_ERROR) unless defined $recipe;
 
+# variables used for I/O
+my ($command, $success, $error_code, $full_buf, $stdout_buf, $stderr_buf);
+
+# Get list of normalizations by class_id : stored as $norms; save to temp file for ppImage runs below
+my (%norms, $normsName);
+{
+    # dettool command to select imfile data for this exp_id
+    $command  = "$dettool -normalizedstat";
+    $command .= " -det_id $det_id";
+    $command .= " -iteration $iter";
+    $command .= " -dbname $dbname" if defined $dbname;
+    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        warn("Unable to perform dettool -residimfile: $error_code\n");
+        exit($error_code);
+    }
+    if (@$stdout_buf == 0) {
+	&my_die("No normalizations were found", $det_id, $iter, $PS_EXIT_PROG_ERROR);
+    }
+
+    # Parse the stdout buffer into a metadata
+    my $mdcParser = PS::IPP::Metadata::Config->new;
+    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $det_id, $iter, $exp_id, $PS_EXIT_PROG_ERROR);
+
+    # parse the file info in the metadata
+    my $normsMD = parse_md_list($metadata) or
+        &my_die("Unable to parse metadata list", $det_id, $iter, $exp_id, $PS_EXIT_PROG_ERROR);
+
+
+    # write the normalizations to a file as a metadata config file in the form: class_id F32 value
+    # XXX a possible optimization: if there is only one imfile, skip normalization
+    my $normsFile;
+    ($normsFile, $normsName) = tempfile( "/tmp/$exp_tag.detresid.$det_id.$iter.norms.XXXX", UNLINK => !$save_temps );
+    print "saving norms to $normsName\n";
+    foreach my $norm (@$normsMD) {
+	my $class_id = $norm->{class_id};
+	my $normalization = $norm->{norm};
+
+	$norms{$class_id} = $normalization;
+        printf $normsFile "$class_id F32 $normalization\n", 
+    }
+    close $normsFile;
+}
+
 # Get list of imfile files
 my $cmdflags;
-my ($files, $command, $success, $error_code, $full_buf, $stdout_buf, $stderr_buf);
+my (@files);
 {
     # dettool command to select imfile data for this exp_id
@@ -105,5 +152,7 @@
         exit($error_code);
     }
-    # XXX report an error message if stdout_buf is empty
+    if (@$stdout_buf == 0) {
+	&my_die("No imfiles were found", $det_id, $iter, $PS_EXIT_PROG_ERROR);
+    }
 
     # Parse the stdout buffer into a metadata
@@ -112,13 +161,47 @@
         &my_die("Unable to parse metadata config doc", $det_id, $iter, $exp_id, $PS_EXIT_PROG_ERROR);
 
+    # since I can't figure out how to do input and output within PERL, I'm writing the (modified) metadata to a temp file
+    my ($statFile, $statName) = tempfile( "/tmp/$exp_tag.detresid.$det_id.$iter.stats.XXXX", UNLINK => !$save_temps );
+
     # parse the file info in the metadata
-    $files = parse_md_list($metadata) or
-        &my_die("Unable to parse metadata list", $det_id, $iter, $exp_id, $PS_EXIT_PROG_ERROR);
-
-    # since I can't figure out how to do input and output within PERL, I'm writing to a temp file
-    my ($statFile, $statName) = tempfile( "/tmp/$exp_tag.detresid.$det_id.$iter.stats.XXXX", UNLINK => !$save_temps );
-    print "saving stats to $statName\n";
-    foreach my $line (@$stdout_buf) {
-        print $statFile $line;
+    # as we parse the list of files and their stats, apply the normalization to the relevant fields
+    # also, write out the modified metadata set
+    foreach my $mdItem (@$metadata) {
+	if ($mdItem->{class} ne "metadata") {
+	    carp "MD element ", $mdItem->{name}, " isn't of type METADATA --- ignored.\n";
+	    next;
+	}
+	my %hash;		# Hash element
+	my $mdComponents = $mdItem->{value}; # Components of the metadata
+
+	# determine the class_id for this block:
+	my $class_id;
+	foreach my $data (@$mdComponents) {
+	    unless ($data->{name} eq "class_id") { next; }
+	    $class_id = $data->{value};
+	    last;
+	}
+
+	# a new metadata block
+	print $statFile "rawResidImfile  METADATA\n";
+
+	# modify and save the data in this block:
+	foreach my $data (@$mdComponents) {
+	    my $norm = $norms{$class_id};
+
+	    # fields to modify by the normalization:
+	    if ($data->{name} eq "bg")            { $data->{value} *= $norm; }
+	    if ($data->{name} eq "bg_stdev")      { $data->{value} *= $norm; }
+	    if ($data->{name} eq "bg_mean_stdev") { $data->{value} *= $norm; }
+	    if ($data->{name} eq "bg_skewness")   { $data->{value} *= $norm; }
+	    if ($data->{name} eq "bg_kurtosis")   { $data->{value} *= $norm; }
+	    if ($data->{name} eq "bin_stdev")     { $data->{value} *= $norm; }
+
+	    # write out the metadata, save on the array of hashes
+	    print $statFile "  $data->{name}  $data->{type}  $data->{value}\n";
+	    $hash{$data->{name}} = $data->{value};
+	}
+	print $statFile "END\n";
+	push @files, \%hash;
     }
     close $statFile;
@@ -154,5 +237,5 @@
 my ($list1File, $list1Name) = tempfile( "/tmp/$exp_tag.detresid.$det_id.$iter.b1.list.XXXX", UNLINK => !$save_temps );
 my ($list2File, $list2Name) = tempfile( "/tmp/$exp_tag.detresid.$det_id.$iter.b2.list.XXXX", UNLINK => !$save_temps );
-foreach my $file (@$files) {
+foreach my $file (@files) {
     print $list1File ($ipprc->filename( "PPIMAGE.BIN1", $file->{path_base}, $file->{class_id} ) . "\n");
     print $list2File ($ipprc->filename( "PPIMAGE.BIN2", $file->{path_base}, $file->{class_id} ) . "\n");
@@ -165,7 +248,9 @@
 unless ($no_op) {
     # Make the jpeg for binning 1
+    # XXX EAM : supply the collection of normalizations as a metadata
     $command = "$ppImage -list $list1Name $outroot"; # Command to run
     $command .= " -recipe PPIMAGE PPIMAGE_J1";
     $command .= " -recipe JPEG $recipe";
+    $command .= " -normlist $normsName";
     $command .= " -dbname $dbname" if defined $dbname;
     ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
@@ -178,7 +263,9 @@
 
     # Make the jpeg for binning 2
+    # XXX EAM : supply the collection of normalizations as a metadata
     $command = "$ppImage -list $list2Name $outroot"; # Command to run
     $command .= " -recipe PPIMAGE PPIMAGE_J2";
     $command .= " -recipe JPEG $recipe";
+    $command .= " -normlist $normsName";
     $command .= " -dbname $dbname" if defined $dbname;
     ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
@@ -215,5 +302,5 @@
 my @fluxes;
 
-foreach my $file (@$files) {
+foreach my $file (@files) {
     my $name      = $file->{class_id};
     my $mean      = $file->{bg};        # Mean for this imfile
Index: /trunk/ippScripts/scripts/ipp_cleanup.pl
===================================================================
--- /trunk/ippScripts/scripts/ipp_cleanup.pl	(revision 23228)
+++ /trunk/ippScripts/scripts/ipp_cleanup.pl	(revision 23229)
@@ -45,10 +45,18 @@
 my $ipprc = PS::IPP::Config->new( $camera ) or my_die("Unable to set up", $stage_id, $PS_EXIT_CONFIG_ERROR); # this is used for PATH, NEB filename conversions
 
-# $mode must be one of "goto_cleaned", "goto_scrubbed", or "goto_purged"
-# goto_cleaned and goto_scrubbed both result in 'cleaned': scrubbed allows chips without config files to
-# be cleaned (they cannot be recovered, but the small data is left behind)
+# $mode must be one of "goto_cleaned", "goto_scrubbed", or
+# "goto_purged" goto_cleaned and goto_scrubbed both result in
+# 'cleaned' on success ('scrubbed' allows chips without config files
+# to be cleaned; they cannot be recovered, but the small data is left
+# behind). XXX make 'scrubbed' a data_state?
 unless (($mode eq "goto_cleaned") || ($mode eq "goto_scrubbed") || ($mode eq "goto_purged")) {
     die "invalid cleanup mode $mode\n";
 }
+
+my $error_state;
+if ($mode eq "goto_cleaned")  { $error_state = "error_cleaned";  }
+if ($mode eq "goto_scrubbed") { $error_state = "error_scrubbed"; }
+if ($mode eq "goto_purged")   { $error_state = "error_purged";   }
+
 
 my %stages = ( chip => 1, camera => 1, fake => 1, warp => 1, stack => 1, diff  => 1);
@@ -148,7 +156,8 @@
             }
         } else {
-            # if an error happens for one chip, the chipRun will stay in goto_*, but the chips will stop be run
-            my $command = "$chiptool -updateprocessedimfile -chip_id $stage_id -class_id $class_id -code 1";
-            $command .= " -dbname $dbname" if defined $dbname;
+
+	    # if an error happens for one chip, the chipRun will stay in goto_*, but the chips will go to error_* (matching the goto_*)
+	    my $command = "$chiptool -updateprocessedimfile -chip_id $stage_id -class_id $class_id -set_state $error_state";
+	    $command .= " -dbname $dbname" if defined $dbname;
 
             my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
@@ -216,9 +225,13 @@
 
     if ($status)  {
-        my $command = "$camtool -cam_id $stage_id -updaterun";
+        my $command;
         if ($mode eq "goto_cleaned") {
-            $command .= " -state cleaned";
-        } else {
-            $command .= " -state purged";
+            $command = "$camtool -updaterun -cam_id $stage_id -set_state cleaned";
+	}
+        if ($mode eq "goto_scrubbed") {
+            $command = "$camtool -updaterun -cam_id $stage_id -set_state cleaned";
+	}
+        if ($mode eq "goto_purged") {
+            $command = "$camtool -updaterun -cam_id $stage_id -set_state purged";
         }
         $command .= " -dbname $dbname" if defined $dbname;
@@ -230,5 +243,6 @@
         }
     } else {
-        my $command = "$camtool -updateprocessedexp -cam_id $stage_id -code 1";
+	# since 'camera' has only a single imfile, we can just update the run
+        my $command = "$camtool -updaterun -cam_id $stage_id -set_state $error_state";
         $command .= " -dbname $dbname" if defined $dbname;
 
@@ -319,7 +333,6 @@
             }
          } else {
-            # XXX: -updateskyfile mode does not exist, need to add it
-            my $command = "$warptool -updateskyfile -warp_id $stage_id -skycell_id $skycell_id -code 1";
-            $command .= " -dbname $dbname" if defined $dbname;
+	    my $command = "$warptool -updateskyfile -warp_id $stage_id -skycell_id $skycell_id -set_state $error_state";
+	    $command .= " -dbname $dbname" if defined $dbname;
 
             my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
@@ -337,6 +350,6 @@
 # left TODO
 # fake : faketool : -pendingcleanupimfile (loop over imfiles)
-# stack: stacktool : -pendingcleanupskyfile (loop over skyfiles)
-# diff:  difftool : -pendingcleanupskyfile
+# stack: stacktool : -pendingcleanupskyfile
+# diff:  difftool : -pendingcleanupskyfile (loop over skyfiles)
 
 die "ipp_cleanup.pl -stage $stage not yet implemented\n";
@@ -367,4 +380,5 @@
 }
 
+# XXX we currently do not set the error state in the db on my_die
 sub my_die
 {
Index: /trunk/ippTools/scripts/test_create_detrend.sql
===================================================================
--- /trunk/ippTools/scripts/test_create_detrend.sql	(revision 23229)
+++ /trunk/ippTools/scripts/test_create_detrend.sql	(revision 23229)
@@ -0,0 +1,135 @@
+-- disable constraints
+
+DROP TABLE IF EXISTS detRun;
+DROP TABLE IF EXISTS detProcessedImfile;
+DROP TABLE IF EXISTS detNormalizedStatImfile;
+DROP TABLE IF EXISTS detResidImfile;
+
+CREATE TABLE detRun (
+    det_id BIGINT AUTO_INCREMENT,
+    iteration INT,
+    det_type VARCHAR(64),
+    mode VARCHAR(64),
+    state VARCHAR(64),
+    filelevel VARCHAR(64),
+    workdir VARCHAR(255),
+    camera VARCHAR(64),
+    telescope VARCHAR(64),
+    exp_type VARCHAR(64),
+    reduction VARCHAR(64),
+    filter VARCHAR(64),
+    airmass_min FLOAT,
+    airmass_max FLOAT,
+    exp_time_min FLOAT,
+    exp_time_max FLOAT,
+    ccd_temp_min FLOAT,
+    ccd_temp_max FLOAT,
+    posang_min DOUBLE,
+    posang_max DOUBLE,
+    registered DATETIME,
+    time_begin DATETIME,
+    time_end DATETIME,
+    use_begin DATETIME,
+    use_end DATETIME,
+    solang_min FLOAT,
+    solang_max FLOAT,
+    label VARCHAR(64),
+    ref_det_id BIGINT,
+    ref_iter INT,
+    -- parent INT, :: dropping this
+    PRIMARY KEY(det_id),
+    KEY(det_id),
+    KEY(iteration),
+    KEY(det_type),
+    KEY(mode),
+    KEY(state),
+    KEY(label),
+    -- KEY(parent), :: dropping this
+    INDEX(det_id, iteration))
+ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE detProcessedImfile (
+    det_id BIGINT,
+    exp_id BIGINT,
+    class_id VARCHAR(64),
+    uri VARCHAR(255),
+    recipe VARCHAR(64),
+    bg DOUBLE,
+    bg_stdev DOUBLE,
+    bg_mean_stdev DOUBLE,
+    fringe_0 DOUBLE,
+    fringe_1 DOUBLE,
+    fringe_2 DOUBLE,
+    user_1 DOUBLE,
+    user_2 DOUBLE,
+    user_3 DOUBLE,
+    user_4 DOUBLE,
+    user_5 DOUBLE,
+    path_base VARCHAR(255),
+    data_state VARCHAR(64),
+    fault SMALLINT NOT NULL,
+    PRIMARY KEY(det_id, exp_id, class_id),
+    KEY(fault),
+    INDEX(det_id, class_id),
+    INDEX(det_id, exp_id)
+    -- FOREIGN KEY (det_id, exp_id)
+    --     REFERENCES  detInputExp(det_id, exp_id),
+    -- FOREIGN KEY (exp_id, class_id)
+    --     REFERENCES  rawImfile(exp_id, class_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE detNormalizedStatImfile (
+    det_id BIGINT,
+    iteration INT,
+    class_id VARCHAR(64),
+    norm FLOAT,
+    data_state VARCHAR(64),
+    fault SMALLINT NOT NULL,
+    PRIMARY KEY(det_id, iteration, class_id),
+    KEY(fault)
+    -- FOREIGN KEY (det_id, iteration)
+    -- REFERENCES  detInputExp(det_id, iteration),
+    -- FOREIGN KEY (det_id, iteration, class_id)
+    -- REFERENCES  detStackedImfile(det_id, iteration, class_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE detResidImfile (
+    det_id BIGINT,
+    iteration INT,
+    ref_det_id BIGINT,
+    ref_iter INT,
+    exp_id BIGINT,
+    class_id VARCHAR(64),
+    uri VARCHAR(255),
+    recipe VARCHAR(64),
+    bg DOUBLE,
+    bg_stdev DOUBLE,
+    bg_mean_stdev DOUBLE,
+    bg_skewness DOUBLE,
+    bg_kurtosis DOUBLE,
+    bin_stdev DOUBLE,
+    fringe_0 DOUBLE,
+    fringe_1 DOUBLE,
+    fringe_2 DOUBLE,
+    fringe_resid_0 DOUBLE,
+    fringe_resid_1 DOUBLE,
+    fringe_resid_2 DOUBLE,
+    user_1 DOUBLE,
+    user_2 DOUBLE,
+    user_3 DOUBLE,
+    user_4 DOUBLE,
+    user_5 DOUBLE,
+    path_base VARCHAR(255),
+    data_state VARCHAR(64),
+    fault SMALLINT NOT NULL,
+    PRIMARY KEY(det_id, iteration, exp_id, class_id),
+    KEY(fault),
+    INDEX(det_id, iteration, exp_id)
+    -- FOREIGN KEY (det_id, iteration, exp_id)
+    -- REFERENCES  detInputExp(det_id, iteration, exp_id),
+    -- FOREIGN KEY (det_id, exp_id, class_id)
+    -- REFERENCES  detProcessedImfile(det_id, exp_id, class_id),
+    -- FOREIGN KEY (ref_det_id, ref_iter)
+    -- REFERENCES  detNormalizedExp(det_id, iteration)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
Index: /trunk/ippTools/scripts/test_insert_detrend.sql
===================================================================
--- /trunk/ippTools/scripts/test_insert_detrend.sql	(revision 23229)
+++ /trunk/ippTools/scripts/test_insert_detrend.sql	(revision 23229)
@@ -0,0 +1,47 @@
+-- create a fake detrun, add fake detProcessedImfile entries, etc
+
+insert into 
+       detRun (det_id, det_type, iteration, camera, workdir,   state, mode) 
+       values (1,      'flat',           0, 'GPC1', 'testdir', 'run', 'master');
+
+insert into 
+       detProcessedImfile (det_id, class_id) 
+       values (1, 'ccd00');
+
+insert into 
+       detProcessedImfile (det_id, class_id) 
+       values (1, 'ccd01');
+
+insert into 
+       detResidImfile (det_id, iteration, class_id) 
+       values (1, 0, 'ccd00');
+
+insert into 
+       detResidImfile (det_id, iteration, class_id) 
+       values (1, 0, 'ccd01');
+
+insert into 
+       detNormalizedStatImfile (det_id, iteration, class_id) 
+       values (1, 0, 'ccd01');
+
+insert into 
+       detNormalizedStatImfile (det_id, iteration, class_id) 
+       values (1, 0, 'ccd00');
+
+update detRun set iteration = 1 where det_id = 1;
+
+insert into 
+       detResidImfile (det_id, iteration, class_id) 
+       values (1, 1, 'ccd00');
+
+insert into 
+       detResidImfile (det_id, iteration, class_id) 
+       values (1, 1, 'ccd01');
+
+insert into 
+       detNormalizedStatImfile (det_id, iteration, class_id) 
+       values (1, 1, 'ccd00');
+
+insert into 
+       detNormalizedStatImfile (det_id, iteration, class_id) 
+       values (1, 1, 'ccd01');
Index: /trunk/ippTools/share/chiptool_change_imfile_data_state.sql
===================================================================
--- /trunk/ippTools/share/chiptool_change_imfile_data_state.sql	(revision 23228)
+++ /trunk/ippTools/share/chiptool_change_imfile_data_state.sql	(revision 23229)
@@ -7,8 +7,6 @@
     chip_id = %lld
     AND class_id = '%s'
-    -- only update if chipRun.state has the expected value
-    AND (
-        SELECT state from chipRun where chipRun.chip_id = chipProcessedImfile.chip_id
-    ) = '%s'
-    
-
+--    -- only update if chipRun.state has the expected value
+--    AND (
+--        SELECT state from chipRun where chipRun.chip_id = chipProcessedImfile.chip_id
+--    ) = '%s'
Index: /trunk/ippTools/share/dettool_processedimfile.sql
===================================================================
--- /trunk/ippTools/share/dettool_processedimfile.sql	(revision 23228)
+++ /trunk/ippTools/share/dettool_processedimfile.sql	(revision 23229)
@@ -1,2 +1,3 @@
+-- is this DISTINCT needed?
 SELECT DISTINCT
   detRun.det_type,
Index: /trunk/ippTools/share/dettool_residimfile.sql
===================================================================
--- /trunk/ippTools/share/dettool_residimfile.sql	(revision 23228)
+++ /trunk/ippTools/share/dettool_residimfile.sql	(revision 23229)
@@ -2,9 +2,13 @@
    detRun.det_type,
    detRun.mode,
-   detResidImfile.*,
-   rawExp.exp_time
- FROM detResidImfile
- JOIN detRun
-   USING(det_id, iteration)
- JOIN rawExp
-   USING(exp_id)
+   rawExp.exp_time,
+   detResidImfile.*
+FROM detResidImfile
+JOIN detRun
+  USING(det_id, iteration)
+JOIN detInputExp
+  ON detRun.det_id = detInputExp.det_id
+  AND detRun.iteration = detInputExp.iteration
+  AND detResidImfile.exp_id = detInputExp.exp_id
+JOIN rawExp
+  ON rawExp.exp_id = detResidImfile.exp_id
Index: /trunk/ippTools/share/dettool_tonormalizedstat.sql
===================================================================
--- /trunk/ippTools/share/dettool_tonormalizedstat.sql	(revision 23228)
+++ /trunk/ippTools/share/dettool_tonormalizedstat.sql	(revision 23229)
@@ -1,58 +1,33 @@
--- select detRun.det_id (det_id)
--- select detRun.iteration
--- by:
--- find the current iteration bassed on det_id
--- find all exp_ids in the current det_id/iteration from detInputExp
--- sort to detInputExp.imfiles to find the largest value per det_id/iter
--- compare imfiles to the number of detStackedImfiles by class_id
--- and:
--- ???
--- det_id is not in detStackedImfile;
--- iteration is not in detStackedImfile;
-
-SELECT
-    det_id,
-    det_type,
-    iteration,
-    camera,
-    workdir,
-    class_id
-FROM
-    (SELECT DISTINCT
-        detRun.det_id,
-        detRun.det_type,
-        detRun.iteration,
-        detRun.workdir,
-        rawExp.camera,
-        detStackedImfile.class_id,
-        rawImfile.class_id as rawimfile_class_id
-    FROM detRun
-    JOIN detInputExp
-        ON detRun.det_id = detInputExp.det_id
-        AND detRun.iteration = detInputExp.iteration
-    JOIN rawExp
-        ON detInputExp.exp_id = rawExp.exp_id
-    JOIN rawImfile
-        ON rawExp.exp_id = rawImfile.exp_id
-    LEFT JOIN detStackedImfile
-        ON detInputExp.det_id = detStackedImfile.det_id
-        AND detInputExp.iteration = detStackedImfile.iteration
-        AND rawImfile.class_id = detStackedImfile.class_id
-    LEFT JOIN detNormalizedStatImfile
-        ON detStackedImfile.det_id = detNormalizedStatImfile.det_id
-        AND detStackedImfile.iteration = detNormalizedStatImfile.iteration
-        AND detStackedImfile.class_id = detNormalizedStatImfile.class_id
-    WHERE
-        detRun.state = 'run'
-        AND detRun.mode = 'master'
-        AND detNormalizedStatImfile.det_id IS NULL
-        AND detNormalizedStatImfile.iteration IS NULL
-        AND detNormalizedStatImfile.class_id IS NULL
-    GROUP BY
---        rawExp.exp_id,
-        detRun.iteration,
-        detRun.det_id
-    HAVING
-        COUNT(rawImfile.class_id) = COUNT(detStackedImfile.class_id)
-        AND SUM(detStackedImfile.fault) = 0
-    ) as tonormalizedstat
+-- a det_run + iteration is ready for normstat when: all detResidImfile entries for that iteration corresponding to all detProcessedImfile entries are available 
+SELECT 
+  detRun.det_id, 
+  detRun.det_type, 
+  detRun.iteration, 
+  detRun.camera, 
+  detRun.workdir, 
+  detProcessedImfile.class_id,
+  COUNT(detProcessedImfile.class_id),
+  COUNT(detResidImfile.class_id)
+FROM detRun
+JOIN detProcessedImfile
+    ON  detProcessedImfile.det_id    = detRun.det_id
+LEFT JOIN detResidImfile
+    ON  detResidImfile.det_id    = detProcessedImfile.det_id
+    AND detResidImfile.class_id  = detProcessedImfile.class_id
+    AND detResidImfile.iteration = detRun.iteration
+LEFT JOIN detNormalizedStatImfile
+    ON  detNormalizedStatImfile.det_id    = detRun.det_id    
+    AND detNormalizedStatImfile.iteration = detRun.iteration 
+    AND detNormalizedStatImfile.class_id  = detProcessedImfile.class_id
+WHERE
+    detRun.state = 'run'
+    AND detRun.mode = 'master'
+    AND detNormalizedStatImfile.det_id IS NULL
+    AND detNormalizedStatImfile.iteration IS NULL
+    AND detNormalizedStatImfile.class_id IS NULL
+GROUP BY
+    detRun.iteration,
+    detRun.det_id
+HAVING
+    COUNT(detProcessedImfile.class_id) = COUNT(detResidImfile.class_id)
+    AND SUM(detResidImfile.fault) = 0
Index: /trunk/ippTools/share/dettool_toresidexp.sql
===================================================================
--- /trunk/ippTools/share/dettool_toresidexp.sql	(revision 23228)
+++ /trunk/ippTools/share/dettool_toresidexp.sql	(revision 23229)
@@ -5,4 +5,6 @@
 -- id, detrend type, and whether the exposure was included in the stack for
 -- this iteration.
+
+-- require the corresponding detNormalizedExp to complete before starting
 
 -- select detRun.det_id
@@ -41,4 +43,6 @@
         detResidImfile.class_id
     FROM detRun
+    JOIN detNormalizedExp
+    	USING(det_id, iteration)
     JOIN detInputExp
         USING(det_id, iteration)
Index: /trunk/ippTools/share/dettool_toresidimfile.sql
===================================================================
--- /trunk/ippTools/share/dettool_toresidimfile.sql	(revision 23228)
+++ /trunk/ippTools/share/dettool_toresidimfile.sql	(revision 23229)
@@ -13,5 +13,6 @@
     detProcessedImfile.class_id,
     detProcessedImfile.uri,
-    detNormalizedImfile.uri AS det_uri,
+--  detNormalizedImfile.uri AS det_uri,
+    detStackedImfile.uri AS det_uri,
     detRun.det_id AS ref_det_id,
     detRun.iteration AS ref_iter,
@@ -26,11 +27,17 @@
     ON detRun.det_id = detProcessedImfile.det_id
     AND detInputExp.exp_id = detProcessedImfile.exp_id
-JOIN detNormalizedImfile
-    ON detRun.det_id = detNormalizedImfile.det_id
-    AND detRun.iteration = detNormalizedImfile.iteration
-    AND detProcessedImfile.class_id = detNormalizedImfile.class_id
-JOIN detNormalizedExp
-    ON detRun.det_id = detNormalizedExp.det_id
-    AND detRun.iteration = detNormalizedExp.iteration
+JOIN detStackedImfile
+    ON detRun.det_id = detStackedImfile.det_id
+    AND detRun.iteration = detStackedImfile.iteration
+    AND detProcessedImfile.class_id = detStackedImfile.class_id
+-- EAM : replacing detNormalizedImfile with detStackedImfile to change the sequencing
+-- JOIN detNormalizedImfile
+--     ON detRun.det_id = detNormalizedImfile.det_id
+--     AND detRun.iteration = detNormalizedImfile.iteration
+--     AND detProcessedImfile.class_id = detNormalizedImfile.class_id
+-- EAM : we there is no reason to wait for all stacks to complete before continuing
+-- JOIN detNormalizedExp
+--     ON detRun.det_id = detNormalizedExp.det_id
+--     AND detRun.iteration = detNormalizedExp.iteration
 LEFT JOIN detResidImfile
     ON detRun.det_id = detResidImfile.det_id
@@ -41,6 +48,7 @@
     detRun.state = 'run'
     AND detRun.mode = 'master'
-    AND detNormalizedImfile.fault = 0
-    AND detNormalizedExp.fault = 0
+    AND detStackedImfile.fault = 0
+--  AND detNormalizedImfile.fault = 0
+--  AND detNormalizedExp.fault = 0
     AND detResidImfile.det_id IS NULL
     AND detResidImfile.iteration IS NULL
Index: /trunk/ippTools/share/pxadmin_create_tables.sql
===================================================================
--- /trunk/ippTools/share/pxadmin_create_tables.sql	(revision 23228)
+++ /trunk/ippTools/share/pxadmin_create_tables.sql	(revision 23229)
@@ -737,7 +737,5 @@
     REFERENCES  detInputExp(det_id, iteration, exp_id),
     FOREIGN KEY (det_id, exp_id, class_id)
-    REFERENCES  detProcessedImfile(det_id, exp_id, class_id),
-    FOREIGN KEY (ref_det_id, ref_iter)
-    REFERENCES  detNormalizedExp(det_id, iteration)
+    REFERENCES  detProcessedImfile(det_id, exp_id, class_id)
 ) ENGINE=innodb DEFAULT CHARSET=latin1;
 
Index: /trunk/ippTools/share/warptool_change_skyfile_data_state.sql
===================================================================
--- /trunk/ippTools/share/warptool_change_skyfile_data_state.sql	(revision 23228)
+++ /trunk/ippTools/share/warptool_change_skyfile_data_state.sql	(revision 23229)
@@ -9,7 +9,5 @@
     AND skycell_id = '%s'
     -- only update if chipRun.state has the expected value
-    AND (
-        SELECT state from warpRun where warpRun.warp_id = warpSkyfile.warp_id
-    ) = '%s'
-    
-
+--    AND (
+--        SELECT state from warpRun where warpRun.warp_id = warpSkyfile.warp_id
+--    ) = '%s'
Index: /trunk/ippTools/src/chiptool.c
===================================================================
--- /trunk/ippTools/src/chiptool.c	(revision 23228)
+++ /trunk/ippTools/src/chiptool.c	(revision 23229)
@@ -52,9 +52,10 @@
 static bool runMode(pxConfig *config);
 static bool tocleanedimfileMode(pxConfig *config);
-static bool tocleanedimfile_from_scrubbedMode(pxConfig *config);
+// static bool tocleanedimfile_from_scrubbedMode(pxConfig *config);
 static bool tofullimfileMode(pxConfig *config);
 static bool topurgedimfileMode(pxConfig *config);
 static bool exportrunMode(pxConfig *config);
 static bool importrunMode(pxConfig *config);
+static bool change_imfile_data_state(pxConfig *config, psString data_state, psString run_state);
 
 # define MODECASE(caseName, func) \
@@ -92,5 +93,5 @@
         MODECASE(CHIPTOOL_MODE_RUN,                     runMode);
         MODECASE(CHIPTOOL_MODE_TOCLEANEDIMFILE,         tocleanedimfileMode);
-        MODECASE(CHIPTOOL_MODE_TOCLEANEDIMFILE_FROM_SCRUBBED, tocleanedimfile_from_scrubbedMode);
+        // MODECASE(CHIPTOOL_MODE_TOCLEANEDIMFILE_FROM_SCRUBBED, tocleanedimfile_from_scrubbedMode);
         MODECASE(CHIPTOOL_MODE_TOFULLIMFILE,            tofullimfileMode);
         MODECASE(CHIPTOOL_MODE_TOPURGEDIMFILE,          topurgedimfileMode);
@@ -651,13 +652,36 @@
     PXOPT_COPY_S64(config->args, where, "-chip_id", "chip_id", "==");
     PXOPT_COPY_STR(config->args, where, "-class_id", "class_id", "==");
-    PXOPT_LOOKUP_S16(code, config->args, "-code", true, false);
-
-    if (!pxSetFaultCode(config->dbh, "chipProcessedImfile", where, code)) {
+    PXOPT_LOOKUP_STR(state, config->args, "-set_state", false, false);
+    PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
+
+    if (state && code) {
+        psError(PS_ERR_UNKNOWN, true, "only one of -set_state and -code may be supplied");
+        return false;
+    }
+
+    if (state) {
+      // make sure that the state string is valid
+      if (!pxIsValidState(state)) {
+        psError(PXTOOLS_ERR_DATA, false, "%s is not a valid state", state);
+        return false;
+      }
+      if (!change_imfile_data_state(config, state, "unknown")) {
         psError(PS_ERR_UNKNOWN, false, "failed to set set fault flag");
         return false;
-    }
-    psFree(where);
-
-    return true;
+      }
+      return true;
+    }
+
+    if (code) {
+      if (!pxSetFaultCode(config->dbh, "chipProcessedImfile", where, code)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to set set fault flag");
+        return false;
+      }
+      psFree(where);
+      return true;
+    }
+
+    psError(PS_ERR_UNKNOWN, true, "one of -set_state or -code must be supplied");
+    return false;
 }
 
@@ -1195,4 +1219,9 @@
 // shared code for the modes -tocleanedimfile -tofullimfile -topurgedimfile
 
+// XXX EAM : this function was enforcing only certain transitions with the SQL.  However,
+// this is getting fairly messy now that we have added a few additional target and
+// destination states.  I'm disabling these restrictions for now; is there are better way
+// to enforce the allowed state transitions?
+
 static bool change_imfile_data_state(pxConfig *config, psString data_state, psString run_state)
 {
@@ -1210,6 +1239,8 @@
     }
 
+    // XXX this feature is disabled (run_state is ignored)
     // note only updates if chipRun.state = run_state
-    if (!p_psDBRunQueryF(config->dbh, query, data_state, chip_id, class_id, run_state)) {
+
+    if (!p_psDBRunQueryF(config->dbh, query, data_state, chip_id, class_id)) {
         psError(PS_ERR_UNKNOWN, false, "database error");
         // rollback
@@ -1246,8 +1277,4 @@
 {
     return change_imfile_data_state(config, "cleaned", "goto_cleaned");
-}
-static bool tocleanedimfile_from_scrubbedMode(pxConfig *config)
-{
-    return change_imfile_data_state(config, "cleaned", "goto_scrubbed");
 }
 static bool tofullimfileMode(pxConfig *config)
Index: /trunk/ippTools/src/chiptoolConfig.c
===================================================================
--- /trunk/ippTools/src/chiptoolConfig.c	(revision 23228)
+++ /trunk/ippTools/src/chiptoolConfig.c	(revision 23229)
@@ -172,7 +172,8 @@
     // -updateprocessedimfile
     psMetadata *updateprocessedimfileArgs = psMetadataAlloc();
-    psMetadataAddS64(updateprocessedimfileArgs, PS_LIST_TAIL, "-chip_id",  0,            "search by chip ID", 0);
-    psMetadataAddStr(updateprocessedimfileArgs,  PS_LIST_TAIL, "-class_id",           0, "search by class ID", NULL);
-    psMetadataAddS16(updateprocessedimfileArgs, PS_LIST_TAIL, "-code",  0,            "set fault code (required)", 0);
+    psMetadataAddS64(updateprocessedimfileArgs, PS_LIST_TAIL, "-chip_id",    0, "search by chip ID", 0);
+    psMetadataAddStr(updateprocessedimfileArgs,  PS_LIST_TAIL, "-class_id",  0, "search by class ID", NULL);
+    psMetadataAddS16(updateprocessedimfileArgs, PS_LIST_TAIL, "-code",       0, "set fault code", 0);
+    psMetadataAddStr(updateprocessedimfileArgs, PS_LIST_TAIL, "-set_state",  0, "set state", NULL);
 
     // -promoteexp
@@ -234,9 +235,9 @@
     psMetadataAddStr(tocleanedimfileArgs, PS_LIST_TAIL, "-class_id",  0,        "class ID to update", NULL);
 
-    // -tocleanedimfile_from_scrubbed
-    psMetadata *tocleanedimfile_from_scrubbedArgs = psMetadataAlloc();
-    psMetadataAddS64(tocleanedimfile_from_scrubbedArgs, PS_LIST_TAIL, "-chip_id", 0,          "chip ID to update", 0);
-    psMetadataAddStr(tocleanedimfile_from_scrubbedArgs, PS_LIST_TAIL, "-class_id",  0,        "class ID to update", NULL);
-
+//    // -tocleanedimfile_from_scrubbed
+//    psMetadata *tocleanedimfile_from_scrubbedArgs = psMetadataAlloc();
+//    psMetadataAddS64(tocleanedimfile_from_scrubbedArgs, PS_LIST_TAIL, "-chip_id", 0,          "chip ID to update", 0);
+//    psMetadataAddStr(tocleanedimfile_from_scrubbedArgs, PS_LIST_TAIL, "-class_id",  0,        "class ID to update", NULL);
+//
     // -tofullimfile
     psMetadata *tofullimfileArgs = psMetadataAlloc();
@@ -280,5 +281,5 @@
     PXOPT_ADD_MODE("-run",                  "show runs",                            CHIPTOOL_MODE_RUN,                  runArgs);
     PXOPT_ADD_MODE("-tocleanedimfile",      "set imfile state to cleaned",          CHIPTOOL_MODE_TOCLEANEDIMFILE,      tocleanedimfileArgs);
-    PXOPT_ADD_MODE("-tocleanedimfile_from_scrubbed", "set imfile state to cleaned (for goto_scrubbed)", CHIPTOOL_MODE_TOCLEANEDIMFILE_FROM_SCRUBBED, tocleanedimfile_from_scrubbedArgs);
+    //    PXOPT_ADD_MODE("-tocleanedimfile_from_scrubbed", "set imfile state to cleaned (for goto_scrubbed)", CHIPTOOL_MODE_TOCLEANEDIMFILE_FROM_SCRUBBED, tocleanedimfile_from_scrubbedArgs);
     PXOPT_ADD_MODE("-tofullimfile",         "set imfile state to full",              CHIPTOOL_MODE_TOFULLIMFILE,         tofullimfileArgs);
     PXOPT_ADD_MODE("-topurgedimfile",       "set imfile state to purged",            CHIPTOOL_MODE_TOPURGEDIMFILE,       topurgedimfileArgs);
Index: /trunk/ippTools/src/dettoolConfig.c
===================================================================
--- /trunk/ippTools/src/dettoolConfig.c	(revision 23228)
+++ /trunk/ippTools/src/dettoolConfig.c	(revision 23229)
@@ -640,13 +640,14 @@
     // -residimfile
     psMetadata *residimfileArgs = psMetadataAlloc();
-    psMetadataAddS64(residimfileArgs, PS_LIST_TAIL, "-det_id",  0,            "search for detrend ID", 0);
-    psMetadataAddS32(residimfileArgs, PS_LIST_TAIL, "-iteration",  0,            "search for iteration number", 0);
-    psMetadataAddS64(residimfileArgs, PS_LIST_TAIL, "-exp_id",  0,            "search by detrend ID", 0);
-    psMetadataAddStr(residimfileArgs, PS_LIST_TAIL, "-class_id",  0,            "search for class ID", NULL);
-    psMetadataAddStr(residimfileArgs, PS_LIST_TAIL, "-recip",  0,            "search for recipe", NULL);
-    psMetadataAddU64(residimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddS64(residimfileArgs,  PS_LIST_TAIL, "-det_id",  0,            "search for detrend ID", 0);
+    psMetadataAddS32(residimfileArgs,  PS_LIST_TAIL, "-iteration",  0,            "search for iteration number", 0);
+    psMetadataAddS64(residimfileArgs,  PS_LIST_TAIL, "-exp_id",  0,            "search by detrend ID", 0);
+    psMetadataAddStr(residimfileArgs,  PS_LIST_TAIL, "-class_id",  0,            "search for class ID", NULL);
+    psMetadataAddStr(residimfileArgs,  PS_LIST_TAIL, "-recip",  0,            "search for recipe", NULL);
+    psMetadataAddU64(residimfileArgs,  PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
     psMetadataAddBool(residimfileArgs, PS_LIST_TAIL, "-faulted",  0,            "only return imfiles with a fault status set", false);
     psMetadataAddBool(residimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
-    psMetadataAddStr(residimfileArgs, PS_LIST_TAIL, "-select_state",  0,            "search for state", NULL);
+    psMetadataAddBool(residimfileArgs, PS_LIST_TAIL, "-included",  0,            "restrict results to exposures 'includeded' in the current iteration", false);
+    psMetadataAddStr(residimfileArgs,  PS_LIST_TAIL, "-select_state",  0,            "search for state", NULL);
 
     // -revertresidimfile
Index: /trunk/ippTools/src/dettool_processedimfile.c
===================================================================
--- /trunk/ippTools/src/dettool_processedimfile.c	(revision 23228)
+++ /trunk/ippTools/src/dettool_processedimfile.c	(revision 23229)
@@ -165,11 +165,4 @@
     PS_ASSERT_PTR_NON_NULL(config, false);
 
-    bool hasWhere = false;
-
-    PXOPT_LOOKUP_BOOL(included, config->args, "-included", false);
-    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
-    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
-    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
-
     psMetadata *where = psMetadataAlloc();
     PXOPT_COPY_S64(config->args, where, "-det_id", "detProcessedImfile.det_id", "==");
@@ -179,4 +172,10 @@
     PXOPT_COPY_STR(config->args, where, "-select_mode", "detRun.mode", "==");
 
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+    PXOPT_LOOKUP_BOOL(included, config->args, "-included", false);
+
     psString query = pxDataGet("dettool_processedimfile.sql");
     if (!query) {
@@ -186,4 +185,5 @@
     }
 
+    bool hasWhere = false;
     if (psListLength(where->list)) {
         psString whereClause = psDBGenerateWhereSQL(where, NULL);
@@ -211,4 +211,5 @@
 
     if (faulted) {
+        // list only faulted rows
 	psStringAppend(&query, " %s", " detProcessedImfile.fault != 0");
     } else {
Index: /trunk/ippTools/src/dettool_residimfile.c
===================================================================
--- /trunk/ippTools/src/dettool_residimfile.c	(revision 23228)
+++ /trunk/ippTools/src/dettool_residimfile.c	(revision 23229)
@@ -159,5 +159,7 @@
     PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
     PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
     PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+    PXOPT_LOOKUP_BOOL(included, config->args, "-included", false);
 
     psString query = pxDataGet("dettool_residimfile.sql");
@@ -175,4 +177,14 @@
     }
     psFree(where);
+
+    // restrict search to included imfiles
+    if (included) {
+	if (hasWhere) {
+	    psStringAppend(&query, " AND detInputExp.include = 1");
+	} else {
+	    psStringAppend(&query, " WHERE detInputExp.include = 1");
+	}
+	hasWhere = true;
+    }
 
     if (hasWhere) {
Index: /trunk/ippTools/src/warptool.c
===================================================================
--- /trunk/ippTools/src/warptool.c	(revision 23228)
+++ /trunk/ippTools/src/warptool.c	(revision 23229)
@@ -55,4 +55,5 @@
 static bool updateskyfileMode(pxConfig *config);
 
+static bool change_skyfile_data_state(pxConfig *config, psString data_state, psString run_state);
 static bool parseAndInsertSkyCellMap(pxConfig *config, const char *mapfile);
 static bool isValidMode(pxConfig *config, const char *mode);
@@ -1604,4 +1605,9 @@
 // shared code for the modes -tocleanedskyfile -tofullskyfile -topurgedskyfile
 
+// XXX EAM : this function was enforcing only certain transitions with the SQL.  However,
+// this is getting fairly messy now that we have added a few additional target and
+// destination states.  I'm disabling these restrictions for now; is there are better way
+// to enforce the allowed state transitions?
+
 static bool change_skyfile_data_state(pxConfig *config, psString data_state, psString run_state)
 {
@@ -1619,6 +1625,8 @@
     }
 
+    // XXX this feature is disabled (run_state is ignored)
     // note only updates if warpRun.state = run_state
-    if (!p_psDBRunQueryF(config->dbh, query, data_state, warp_id, skycell_id, run_state)) {
+
+    if (!p_psDBRunQueryF(config->dbh, query, data_state, warp_id, skycell_id)) {
         psError(PS_ERR_UNKNOWN, false, "database error");
         // rollback
@@ -1668,14 +1676,37 @@
     PXOPT_LOOKUP_S64(warp_id, config->args, "-warp_id", true, false);
     PXOPT_LOOKUP_STR(skycell_id, config->args, "-skycell_id", true, false);
+    PXOPT_LOOKUP_STR(state, config->args, "-set_state", false, false);
     PXOPT_LOOKUP_S16(code, config->args, "-code", false, false);
 
-    psString query = pxDataGet("warptool_updateskyfile.sql");
-
-    if (!p_psDBRunQueryF(config->dbh, query, code, warp_id, skycell_id)) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        return false;
-    }
-    psFree(query);
-
-    return true;
-}
+    if (state && code) {
+        psError(PS_ERR_UNKNOWN, true, "only one of -set_state and -code may be supplied");
+        return false;
+    }
+
+    if (state) {
+      // make sure that the state string is valid
+      if (!pxIsValidState(state)) {
+        psError(PXTOOLS_ERR_DATA, false, "%s is not a valid state", state);
+        return false;
+      }
+      if (!change_skyfile_data_state(config, state, "unknown")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to set set fault flag");
+        return false;
+      }
+      return true;
+    }
+
+    if (code) {
+      psString query = pxDataGet("warptool_updateskyfile.sql");
+
+      if (!p_psDBRunQueryF(config->dbh, query, code, warp_id, skycell_id)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+      }
+      psFree(query);
+      return true;
+    }
+
+    psError(PS_ERR_UNKNOWN, true, "one of -set_state or -code must be supplied");
+    return false;
+}
Index: /trunk/ippTools/src/warptoolConfig.c
===================================================================
--- /trunk/ippTools/src/warptoolConfig.c	(revision 23228)
+++ /trunk/ippTools/src/warptoolConfig.c	(revision 23229)
@@ -348,9 +348,10 @@
     psMetadataAddStr(tofullskyfileArgs, PS_LIST_TAIL, "-skycell_id", 0, "skycell ID to update", NULL);
 
-    // -toupdateskyfile
+    // -updateskyfile
     psMetadata *updateskyfileArgs = psMetadataAlloc();
-    psMetadataAddS64(updateskyfileArgs, PS_LIST_TAIL, "-warp_id", 0,    "warptool ID to update", 0);
+    psMetadataAddS64(updateskyfileArgs, PS_LIST_TAIL, "-warp_id",    0, "warptool ID to update", 0);
     psMetadataAddStr(updateskyfileArgs, PS_LIST_TAIL, "-skycell_id", 0, "skycell ID to update", NULL);
-    psMetadataAddS16(updateskyfileArgs, PS_LIST_TAIL, "-code",  0,      "new fault code", 0);
+    psMetadataAddS16(updateskyfileArgs, PS_LIST_TAIL, "-code",       0, "set fault code", 0);
+    psMetadataAddStr(updateskyfileArgs, PS_LIST_TAIL, "-set_state",  0, "set state", NULL);
 
     psFree(now);
Index: /trunk/ppArith/src/Makefile.am
===================================================================
--- /trunk/ppArith/src/Makefile.am	(revision 23228)
+++ /trunk/ppArith/src/Makefile.am	(revision 23229)
@@ -1,14 +1,14 @@
 bin_PROGRAMS = ppArith
 
-PPARITH_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
-PPARITH_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
-PPARITH_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+# PPARITH_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
+# PPARITH_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
+# PPARITH_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+# 
+# # Force recompilation of ppArithVersion.c, since it gets the version information
+# ppArithVersion.c: FORCE
+# 	touch ppArith.c
+# FORCE: ;
 
-# Force recompilation of ppArithVersion.c, since it gets the version information
-ppArithVersion.c: FORCE
-	touch ppArith.c
-FORCE: ;
-
-ppArith_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PPSTATS_CFLAGS) $(PSPHOT_CFLAGS) $(PPARITH_CFLAGS) -DPPARITH_VERSION=\"$(PPARITH_VERSION)\" -DPPARITH_BRANCH=\"$(PPARITH_BRANCH)\" -DPPARITH_SOURCE=\"$(PPARITH_SOURCE)\"
+ppArith_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PPSTATS_CFLAGS) $(PSPHOT_CFLAGS) $(PPARITH_CFLAGS) -DPPARITH_VERSION=$(SVN_VERSION) -DPPARITH_BRANCH=$(SVN_BRANCH) -DPPARITH_SOURCE=$(SVN_SOURCE)
 ppArith_LDFLAGS  = $(PSLIB_LIBS)   $(PSMODULE_LIBS)   $(PPSTATS_LIBS)   $(PSPHOT_LIBS)   $(PPARITH_LIBS)
 
Index: /trunk/ppArith/src/ppArithVersion.c
===================================================================
--- /trunk/ppArith/src/ppArithVersion.c	(revision 23228)
+++ /trunk/ppArith/src/ppArithVersion.c	(revision 23229)
@@ -22,6 +22,4 @@
 #include "ppArith.h"
 
-psString ppArithVersion(void)
-{
 #ifndef PPARITH_VERSION
 #error "PPARITH_VERSION is not set"
@@ -30,13 +28,21 @@
 #error "PPARITH_BRANCH is not set"
 #endif
-    return psStringCopy(PPARITH_BRANCH "@" PPARITH_VERSION);
+#ifndef PPARITH_SOURCE
+#error "PPARITH_SOURCE is not set"
+#endif
+
+#define xstr(s) str(s)
+#define str(s) #s
+
+psString ppArithVersion(void)
+{
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", xstr(PPARITH_BRANCH), xstr(PPARITH_VERSION));
+    return value;
 }
 
 psString ppArithSource(void)
 {
-#ifndef PPARITH_SOURCE
-#error "PPARITH_SOURCE is not set"
-#endif
-    return psStringCopy(PPARITH_SOURCE);
+    return psStringCopy(xstr(PPARITH_SOURCE));
 }
 
Index: /trunk/ppImage/src/Makefile.am
===================================================================
--- /trunk/ppImage/src/Makefile.am	(revision 23228)
+++ /trunk/ppImage/src/Makefile.am	(revision 23229)
@@ -4,14 +4,14 @@
 	ppImage.h 
 
-PPIMAGE_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
-PPIMAGE_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
-PPIMAGE_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+#PPIMAGE_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
+#PPIMAGE_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
+#PPIMAGE_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
 
 # Force recompilation of ppImageVersion.c, since it gets the version information
-ppImageVersion.c: FORCE
-	touch ppImageVersion.c
-FORCE: ;
+# ppImageVersion.c: FORCE
+# 	touch ppImageVersion.c
+# FORCE: ;
 
-ppImage_CFLAGS = $(PPIMAGE_CFLAGS) $(PPSTATS_CFLAGS) $(PSASTRO_CFLAGS) $(PSPHOT_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS) -DPPIMAGE_VERSION=\"$(PPIMAGE_VERSION)\" -DPPIMAGE_BRANCH=\"$(PPIMAGE_BRANCH)\" -DPPIMAGE_SOURCE=\"$(PPIMAGE_SOURCE)\"
+ppImage_CFLAGS = $(PPIMAGE_CFLAGS) $(PPSTATS_CFLAGS) $(PSASTRO_CFLAGS) $(PSPHOT_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS) -DPPIMAGE_VERSION=$(SVN_VERSION) -DPPIMAGE_BRANCH=$(SVN_BRANCH) -DPPIMAGE_SOURCE=$(SVN_SOURCE)
 ppImage_LDFLAGS = $(PPIMAGE_LIBS) $(PSASTRO_LIBS) $(PPSTATS_LIBS) $(PSPHOT_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
 ppImage_SOURCES = \
Index: /trunk/ppImage/src/ppImage.h
===================================================================
--- /trunk/ppImage/src/ppImage.h	(revision 23228)
+++ /trunk/ppImage/src/ppImage.h	(revision 23229)
@@ -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: /trunk/ppImage/src/ppImageArguments.c
===================================================================
--- /trunk/ppImage/src/ppImageArguments.c	(revision 23228)
+++ /trunk/ppImage/src/ppImageArguments.c	(revision 23229)
@@ -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: /trunk/ppImage/src/ppImageDetrendReadout.c
===================================================================
--- /trunk/ppImage/src/ppImageDetrendReadout.c	(revision 23228)
+++ /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;
         }
Index: /trunk/ppImage/src/ppImageOptions.c
===================================================================
--- /trunk/ppImage/src/ppImageOptions.c	(revision 23228)
+++ /trunk/ppImage/src/ppImageOptions.c	(revision 23229)
@@ -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;
 }
Index: /trunk/ppImage/src/ppImageVersion.c
===================================================================
--- /trunk/ppImage/src/ppImageVersion.c	(revision 23228)
+++ /trunk/ppImage/src/ppImageVersion.c	(revision 23229)
@@ -5,6 +5,4 @@
 #include "ppImage.h"
 
-psString ppImageVersion(void)
-{
 #ifndef PPIMAGE_VERSION
 #error "PPIMAGE_VERSION is not set"
@@ -13,13 +11,21 @@
 #error "PPIMAGE_BRANCH is not set"
 #endif
-    return psStringCopy(PPIMAGE_BRANCH "@" PPIMAGE_VERSION);
+#ifndef PPIMAGE_SOURCE
+#error "PPIMAGE_SOURCE is not set"
+#endif
+
+#define xstr(s) str(s)
+#define str(s) #s
+
+psString ppImageVersion(void)
+{
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", xstr(PPIMAGE_BRANCH), xstr(PPIMAGE_VERSION));
+    return value;
 }
 
 psString ppImageSource(void)
 {
-#ifndef PPIMAGE_SOURCE
-#error "PPIMAGE_SOURCE is not set"
-#endif
-    return psStringCopy(PPIMAGE_SOURCE);
+    return psStringCopy (xstr(PPIMAGE_SOURCE));
 }
 
@@ -27,5 +33,5 @@
 {
     psString version = ppImageVersion();  // Version, to return
-    psString source = ppImageSource();    // Source
+    psString source = ppImageSource(); // Source
 
     psStringPrepend(&version, "ppImage ");
Index: /trunk/ppMerge/src/Makefile.am
===================================================================
--- /trunk/ppMerge/src/Makefile.am	(revision 23228)
+++ /trunk/ppMerge/src/Makefile.am	(revision 23229)
@@ -1,14 +1,14 @@
 bin_PROGRAMS = ppMerge
 
-PPMERGE_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
-PPMERGE_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
-PPMERGE_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+# PPMERGE_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
+# PPMERGE_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
+# PPMERGE_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+# 
+# # Force recompilation of ppMergeVersion.c, since it gets the version information
+# ppMergeVersion.c: FORCE
+# 	touch ppMergeVersion.c
+# FORCE: ;
 
-# Force recompilation of ppMergeVersion.c, since it gets the version information
-ppMergeVersion.c: FORCE
-	touch ppMergeVersion.c
-FORCE: ;
-
-ppMerge_CFLAGS = $(PPMERGE_CFLAGS) $(PPSTATS_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS) -DPPMERGE_VERSION=\"$(PPMERGE_VERSION)\" -DPPMERGE_BRANCH=\"$(PPMERGE_BRANCH)\" -DPPMERGE_SOURCE=\"$(PPMERGE_SOURCE)\"
+ppMerge_CFLAGS = $(PPMERGE_CFLAGS) $(PPSTATS_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS) -DPPMERGE_VERSION=$(SVN_VERSION) -DPPMERGE_BRANCH=$(SVN_BRANCH) -DPPMERGE_SOURCE=$(SVN_SOURCE)
 ppMerge_LDFLAGS = $(PPMERGE_LIBS) $(PPSTATS_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
 
Index: /trunk/ppMerge/src/ppMergeVersion.c
===================================================================
--- /trunk/ppMerge/src/ppMergeVersion.c	(revision 23228)
+++ /trunk/ppMerge/src/ppMergeVersion.c	(revision 23229)
@@ -22,6 +22,4 @@
 #include "ppMergeVersion.h"
 
-psString ppMergeVersion(void)
-{
 #ifndef PPMERGE_VERSION
 #error "PPMERGE_VERSION is not set"
@@ -30,13 +28,21 @@
 #error "PPMERGE_BRANCH is not set"
 #endif
-    return psStringCopy(PPMERGE_BRANCH "@" PPMERGE_VERSION);
+#ifndef PPMERGE_SOURCE
+#error "PPMERGE_SOURCE is not set"
+#endif
+
+#define xstr(s) str(s)
+#define str(s) #s
+
+psString ppMergeVersion(void)
+{
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", xstr(PPMERGE_BRANCH), xstr(PPMERGE_VERSION));
+    return value;
 }
 
 psString ppMergeSource(void)
 {
-#ifndef PPMERGE_SOURCE
-#error "PPMERGE_SOURCE is not set"
-#endif
-    return psStringCopy(PPMERGE_SOURCE);
+    return psStringCopy(xstr(PPMERGE_SOURCE));
 }
 
Index: /trunk/ppSim/src/Makefile.am
===================================================================
--- /trunk/ppSim/src/Makefile.am	(revision 23228)
+++ /trunk/ppSim/src/Makefile.am	(revision 23229)
@@ -1,14 +1,14 @@
 bin_PROGRAMS = ppSim ppSimSequence
 
-PPSIM_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
-PPSIM_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
-PPSIM_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+# PPSIM_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
+# PPSIM_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
+# PPSIM_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+# 
+# # Force recompilation of ppSimVersion.c, since it gets the version information
+# ppSimVersion.c: FORCE
+# 	touch ppSimVersion.c
+# FORCE: ;
 
-# Force recompilation of ppSimVersion.c, since it gets the version information
-ppSimVersion.c: FORCE
-	touch ppSimVersion.c
-FORCE: ;
-
-ppSim_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PSPHOT_CFLAGS) $(PSASTRO_CFLAGS) $(ppSim_CFLAGS) -DPPSIM_VERSION=\"$(PPSIM_VERSION)\" -DPPSIM_BRANCH=\"$(PPSIM_BRANCH)\" -DPPSIM_SOURCE=\"$(PPSIM_SOURCE)\"
+ppSim_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PSPHOT_CFLAGS) $(PSASTRO_CFLAGS) $(ppSim_CFLAGS) -DPPSIM_VERSION=$(SVN_VERSION) -DPPSIM_BRANCH=$(SVN_BRANCH) -DPPSIM_SOURCE=$(SVN_SOURCE)
 ppSim_LDFLAGS = $(PSLIB_LIBS) $(PSMODULE_LIBS) $(PSPHOT_LIBS) $(PSASTRO_LIBS)
 ppSim_SOURCES = \
Index: /trunk/ppSim/src/ppSimVersion.c
===================================================================
--- /trunk/ppSim/src/ppSimVersion.c	(revision 23228)
+++ /trunk/ppSim/src/ppSimVersion.c	(revision 23229)
@@ -1,7 +1,4 @@
 #include "ppSim.h"
 
-
-psString ppSimVersion(void)
-{
 #ifndef PPSIM_VERSION
 #error "PPSIM_VERSION is not set"
@@ -10,13 +7,21 @@
 #error "PPSIM_BRANCH is not set"
 #endif
-    return psStringCopy(PPSIM_BRANCH "@" PPSIM_VERSION);
+#ifndef PPSIM_SOURCE
+#error "PPSIM_SOURCE is not set"
+#endif
+
+#define xstr(s) str(s)
+#define str(s) #s
+
+psString ppSimVersion(void)
+{
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", xstr(PPSIM_BRANCH), xstr(PPSIM_VERSION));
+    return value;
 }
 
 psString ppSimSource(void)
 {
-#ifndef PPSIM_SOURCE
-#error "PPSIM_SOURCE is not set"
-#endif
-    return psStringCopy(PPSIM_SOURCE);
+    return psStringCopy(xstr(PPSIM_SOURCE));
 }
 
Index: /trunk/ppStack/src/Makefile.am
===================================================================
--- /trunk/ppStack/src/Makefile.am	(revision 23228)
+++ /trunk/ppStack/src/Makefile.am	(revision 23229)
@@ -1,14 +1,14 @@
 bin_PROGRAMS = ppStack
 
-PPSTACK_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
-PPSTACK_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
-PPSTACK_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+# PPSTACK_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
+# PPSTACK_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
+# PPSTACK_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+# 
+# # Force recompilation of ppStackVersion.c, since it gets the version information
+# ppStackVersion.c: FORCE
+# 	touch ppStackVersion.c
+# FORCE: ;
 
-# Force recompilation of ppStackVersion.c, since it gets the version information
-ppStackVersion.c: FORCE
-	touch ppStackVersion.c
-FORCE: ;
-
-ppStack_CFLAGS 	= $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PSPHOT_CFLAGS) $(PPSTATS_CFLAGS) $(PPSTACK_CFLAGS) -DPPSTACK_VERSION=\"$(PPSTACK_VERSION)\" -DPPSTACK_BRANCH=\"$(PPSTACK_BRANCH)\" -DPPSTACK_SOURCE=\"$(PPSTACK_SOURCE)\"
+ppStack_CFLAGS 	= $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PSPHOT_CFLAGS) $(PPSTATS_CFLAGS) $(PPSTACK_CFLAGS) -DPPSTACK_VERSION=$(SVN_VERSION) -DPPSTACK_BRANCH=$(SVN_BRANCH) -DPPSTACK_SOURCE=$(SVN_SOURCE)
 ppStack_LDFLAGS = $(PSLIB_LIBS)   $(PSMODULE_LIBS)   $(PSPHOT_LIBS)   $(PPSTATS_LIBS)   $(PPSTACK_LIBS)
 
Index: /trunk/ppStack/src/ppStackVersion.c
===================================================================
--- /trunk/ppStack/src/ppStackVersion.c	(revision 23228)
+++ /trunk/ppStack/src/ppStackVersion.c	(revision 23229)
@@ -11,6 +11,4 @@
 #include "ppStack.h"
 
-psString ppStackVersion(void)
-{
 #ifndef PPSTACK_VERSION
 #error "PPSTACK_VERSION is not set"
@@ -19,13 +17,21 @@
 #error "PPSTACK_BRANCH is not set"
 #endif
-    return psStringCopy(PPSTACK_BRANCH "@" PPSTACK_VERSION);
+#ifndef PPSTACK_SOURCE
+#error "PPSTACK_SOURCE is not set"
+#endif
+
+#define xstr(s) str(s)
+#define str(s) #s
+
+psString ppStackVersion(void)
+{
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", xstr(PPSTACK_BRANCH), xstr(PPSTACK_VERSION));
+    return value;
 }
 
 psString ppStackSource(void)
 {
-#ifndef PPSTACK_SOURCE
-#error "PPSTACK_SOURCE is not set"
-#endif
-    return psStringCopy(PPSTACK_SOURCE);
+    return psStringCopy(xstr(PPSTACK_SOURCE));
 }
 
Index: /trunk/ppStats/src/Makefile.am
===================================================================
--- /trunk/ppStats/src/Makefile.am	(revision 23228)
+++ /trunk/ppStats/src/Makefile.am	(revision 23229)
@@ -1,14 +1,14 @@
 lib_LTLIBRARIES = libppStats.la
 
-PPSTATS_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
-PPSTATS_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
-PPSTATS_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+# PPSTATS_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
+# PPSTATS_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
+# PPSTATS_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+# 
+# # Force recompilation of ppStatsVersion.c, since it gets the version information
+# ppStatsVersion.c: FORCE
+# 	touch ppStatsVersion.c
+# FORCE: ;
 
-# Force recompilation of ppStatsVersion.c, since it gets the version information
-ppStatsVersion.c: FORCE
-	touch ppStatsVersion.c
-FORCE: ;
-
-libppStats_la_CFLAGS = $(PPSTATS_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS) -DPPSTATS_VERSION=\"$(PPSTATS_VERSION)\" -DPPSTATS_BRANCH=\"$(PPSTATS_BRANCH)\" -DPPSTATS_SOURCE=\"$(PPSTATS_SOURCE)\"
+libppStats_la_CFLAGS = $(PPSTATS_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS) -DPPSTATS_VERSION=$(SVN_VERSION) -DPPSTATS_BRANCH=$(SVN_BRANCH) -DPPSTATS_SOURCE=$(SVN_SOURCE)
 libppStats_la_LDFLAGS = $(PPSTATS_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
 
Index: /trunk/ppStats/src/ppStatsVersion.c
===================================================================
--- /trunk/ppStats/src/ppStatsVersion.c	(revision 23228)
+++ /trunk/ppStats/src/ppStatsVersion.c	(revision 23229)
@@ -1,6 +1,4 @@
 #include "ppStatsInternal.h"
 
-psString ppStatsVersion(void)
-{
 #ifndef PPSTATS_VERSION
 #error "PPSTATS_VERSION is not set"
@@ -9,13 +7,21 @@
 #error "PPSTATS_BRANCH is not set"
 #endif
-    return psStringCopy(PPSTATS_BRANCH "@" PPSTATS_VERSION);
+#ifndef PPSTATS_SOURCE
+#error "PPSTATS_SOURCE is not set"
+#endif
+
+#define xstr(s) str(s)
+#define str(s) #s
+
+psString ppStatsVersion(void)
+{
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", xstr(PPSTATS_BRANCH), xstr(PPSTATS_VERSION));
+    return value;
 }
 
 psString ppStatsSource(void)
 {
-#ifndef PPSTATS_SOURCE
-#error "PPSTATS_SOURCE is not set"
-#endif
-    return psStringCopy(PPSTATS_SOURCE);
+    return psStringCopy(xstr(PPSTATS_SOURCE));
 }
 
Index: /trunk/ppSub/src/Makefile.am
===================================================================
--- /trunk/ppSub/src/Makefile.am	(revision 23228)
+++ /trunk/ppSub/src/Makefile.am	(revision 23229)
@@ -1,14 +1,14 @@
 bin_PROGRAMS = ppSub ppSubKernel
 
-PPSUB_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
-PPSUB_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
-PPSUB_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+# PPSUB_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
+# PPSUB_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
+# PPSUB_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+# 
+# # Force recompilation of ppSubVersion.c, since it gets the version information
+# ppSubVersion.c: FORCE
+# 	touch ppSubVersion.c
+# FORCE: ;
 
-# Force recompilation of ppSubVersion.c, since it gets the version information
-ppSubVersion.c: FORCE
-	touch ppSubVersion.c
-FORCE: ;
-
-ppSub_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PPSTATS_CFLAGS) $(PSPHOT_CFLAGS) $(PPSUB_CFLAGS) -DPPSUB_VERSION=\"$(PPSUB_VERSION)\" -DPPSUB_BRANCH=\"$(PPSUB_BRANCH)\" -DPPSUB_SOURCE=\"$(PPSUB_SOURCE)\"
+ppSub_CPPFLAGS = $(PSLIB_CFLAGS) $(PSMODULE_CFLAGS) $(PPSTATS_CFLAGS) $(PSPHOT_CFLAGS) $(PPSUB_CFLAGS) -DPPSUB_VERSION=$(SVN_VERSION) -DPPSUB_BRANCH=$(SVN_BRANCH) -DPPSUB_SOURCE=$(SVN_SOURCE)
 ppSub_LDFLAGS  = $(PSLIB_LIBS)   $(PSMODULE_LIBS)   $(PPSTATS_LIBS)   $(PSPHOT_LIBS)   $(PPSUB_LIBS)
 
Index: /trunk/ppSub/src/ppSubVersion.c
===================================================================
--- /trunk/ppSub/src/ppSubVersion.c	(revision 23228)
+++ /trunk/ppSub/src/ppSubVersion.c	(revision 23229)
@@ -23,6 +23,4 @@
 #include "ppSub.h"
 
-psString ppSubVersion(void)
-{
 #ifndef PPSUB_VERSION
 #error "PPSUB_VERSION is not set"
@@ -31,13 +29,21 @@
 #error "PPSUB_BRANCH is not set"
 #endif
-    return psStringCopy(PPSUB_BRANCH "@" PPSUB_VERSION);
+#ifndef PPSUB_SOURCE
+#error "PPSUB_SOURCE is not set"
+#endif
+
+#define xstr(s) str(s)
+#define str(s) #s
+
+psString ppSubVersion(void)
+{
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", xstr(PPSUB_BRANCH), xstr(PPSUB_VERSION));
+    return value;
 }
 
 psString ppSubSource(void)
 {
-#ifndef PPSUB_SOURCE
-#error "PPSUB_SOURCE is not set"
-#endif
-    return psStringCopy(PPSUB_SOURCE);
+    return psStringCopy(xstr(PPSUB_SOURCE));
 }
 
Index: /trunk/psLib/src/sys/Makefile.am
===================================================================
--- /trunk/psLib/src/sys/Makefile.am	(revision 23228)
+++ /trunk/psLib/src/sys/Makefile.am	(revision 23229)
@@ -3,7 +3,7 @@
 noinst_LTLIBRARIES = libpslibsys.la
 
-PSLIB_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
-PSLIB_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
-PSLIB_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+# PSLIB_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
+# PSLIB_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
+# PSLIB_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
 
 # Force recompilation of psConfigure.c, since it gets the version information
@@ -12,5 +12,5 @@
 FORCE: ;
 
-libpslibsys_la_CPPFLAGS = $(SRCINC) $(PSLIB_CFLAGS) $(CFITSIO_CFLAGS) -DPSLIB_VERSION=\"$(PSLIB_VERSION)\" -DPSLIB_BRANCH=\"$(PSLIB_BRANCH)\" -DPSLIB_SOURCE=\"$(PSLIB_SOURCE)\"
+libpslibsys_la_CPPFLAGS = $(SRCINC) $(PSLIB_CFLAGS) $(CFITSIO_CFLAGS) -DPSLIB_VERSION=$(SVN_VERSION) -DPSLIB_BRANCH=$(SVN_BRANCH) -DPSLIB_SOURCE=$(SVN_SOURCE)
 libpslibsys_la_SOURCES = \
 	psAbort.c \
Index: /trunk/psLib/src/sys/psConfigure.c
===================================================================
--- /trunk/psLib/src/sys/psConfigure.c	(revision 23228)
+++ /trunk/psLib/src/sys/psConfigure.c	(revision 23229)
@@ -47,7 +47,4 @@
 static FILE *memCheckFile = NULL;       // File to which to write results of mem check
 
-
-psString psLibVersion(void)
-{
 #ifndef PSLIB_VERSION
 #error "PSLIB_VERSION is not set"
@@ -56,13 +53,21 @@
 #error "PSLIB_BRANCH is not set"
 #endif
-    return psStringCopy(PSLIB_BRANCH "@" PSLIB_VERSION);
+#ifndef PSLIB_SOURCE
+#error "PSLIB_SOURCE is not set"
+#endif
+
+#define xstr(s) str(s)
+#define str(s) #s
+
+psString psLibVersion(void)
+{
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", xstr(PSLIB_BRANCH), xstr(PSLIB_VERSION));
+    return value;
 }
 
 psString psLibSource(void)
 {
-#ifndef PSLIB_SOURCE
-#error "PSLIB_SOURCE is not set"
-#endif
-    return psStringCopy(PSLIB_SOURCE);
+    return psStringCopy(xstr(PSLIB_SOURCE));
 }
 
Index: /trunk/psModules/src/config/Makefile.am
===================================================================
--- /trunk/psModules/src/config/Makefile.am	(revision 23228)
+++ /trunk/psModules/src/config/Makefile.am	(revision 23229)
@@ -1,14 +1,14 @@
 noinst_LTLIBRARIES = libpsmodulesconfig.la
 
-PSMODULES_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
-PSMODULES_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
-PSMODULES_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+# PSMODULES_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
+# PSMODULES_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
+# PSMODULES_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
 
 # Force recompilation of pmVersion.c, since it gets the version information
-pmVersion.c: FORCE
-	touch pmVersion.c
-FORCE: ;
+# pmVersion.c: FORCE
+# 	touch pmVersion.c
+# FORCE: ;
 
-libpsmodulesconfig_la_CPPFLAGS = $(SRCINC) $(PSMODULES_CFLAGS) -DPSMODULES_VERSION=\"$(PSMODULES_VERSION)\" -DPSMODULES_BRANCH=\"$(PSMODULES_BRANCH)\" -DPSMODULES_SOURCE=\"$(PSMODULES_SOURCE)\"
+libpsmodulesconfig_la_CPPFLAGS = $(SRCINC) $(PSMODULES_CFLAGS) -DPSMODULES_VERSION=$(SVN_VERSION) -DPSMODULES_BRANCH=$(SVN_BRANCH) -DPSMODULES_SOURCE=$(SVN_SOURCE)
 libpsmodulesconfig_la_LDFLAGS  = -release $(PACKAGE_VERSION)
 libpsmodulesconfig_la_SOURCES  = \
Index: /trunk/psModules/src/config/pmVersion.c
===================================================================
--- /trunk/psModules/src/config/pmVersion.c	(revision 23228)
+++ /trunk/psModules/src/config/pmVersion.c	(revision 23229)
@@ -8,6 +8,4 @@
 #include "pmVersion.h"
 
-psString psModulesVersion(void)
-{
 #ifndef PSMODULES_VERSION
 #error "PSMODULES_VERSION is not set"
@@ -16,13 +14,21 @@
 #error "PSMODULES_BRANCH is not set"
 #endif
-    return psStringCopy(PSMODULES_BRANCH "@" PSMODULES_VERSION);
+#ifndef PSMODULES_SOURCE
+#error "PSMODULES_SOURCE is not set"
+#endif
+
+#define xstr(s) str(s)
+#define str(s) #s
+
+psString psModulesVersion(void)
+{
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", xstr(PSMODULES_BRANCH), xstr(PSMODULES_VERSION));
+    return value;
 }
 
 psString psModulesSource(void)
 {
-#ifndef PSMODULES_SOURCE
-#error "PSMODULES_SOURCE is not set"
-#endif
-    return psStringCopy(PSMODULES_SOURCE);
+    return psStringCopy(xstr(PSMODULES_SOURCE));
 }
 
Index: /trunk/psastro/src/Makefile.am
===================================================================
--- /trunk/psastro/src/Makefile.am	(revision 23228)
+++ /trunk/psastro/src/Makefile.am	(revision 23229)
@@ -1,15 +1,12 @@
 lib_LTLIBRARIES = libpsastro.la
 
-PSASTRO_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
-PSASTRO_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
-PSASTRO_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+libpsastro_la_CFLAGS = $(PSASTRO_CFLAGS) $(PPSTATS_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS) -DPSASTRO_VERSION=$(SVN_VERSION) -DPSASTRO_BRANCH=$(SVN_BRANCH) -DPSASTRO_SOURCE=$(SVN_SOURCE)
+libpsastro_la_LDFLAGS = $(PSASTRO_LIBS) $(PPSTATS_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
 
 # Force recompilation of psastroVersion.c, since it gets the version information
-psastroVersion.c: FORCE
-	touch psastroVersion.c
-FORCE: ;
-
-libpsastro_la_CFLAGS = $(PSASTRO_CFLAGS) $(PPSTATS_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS) -DPSASTRO_VERSION=\"$(PSASTRO_VERSION)\" -DPSASTRO_BRANCH=\"$(PSASTRO_BRANCH)\" -DPSASTRO_SOURCE=\"$(PSASTRO_SOURCE)\"
-libpsastro_la_LDFLAGS = $(PSASTRO_LIBS) $(PPSTATS_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+# can we do this with dependency info?
+# psastroVersion.c: FORCE
+# touch psastroVersion.c
+# FORCE: ;
 
 bin_PROGRAMS = psastro psastroModel psastroModelFit gpcModel
Index: /trunk/psastro/src/psastroVersion.c
===================================================================
--- /trunk/psastro/src/psastroVersion.c	(revision 23228)
+++ /trunk/psastro/src/psastroVersion.c	(revision 23229)
@@ -10,6 +10,4 @@
 #include "psastroInternal.h"
 
-psString psastroVersion(void)
-{
 #ifndef PSASTRO_VERSION
 #error "PSASTRO_VERSION is not set"
@@ -18,13 +16,21 @@
 #error "PSASTRO_BRANCH is not set"
 #endif
-    return psStringCopy(PSASTRO_BRANCH "@" PSASTRO_VERSION);
+#ifndef PSASTRO_SOURCE
+#error "PSASTRO_SOURCE is not set"
+#endif
+
+#define xstr(s) str(s)
+#define str(s) #s
+
+psString psastroVersion(void)
+{
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", xstr(PSASTRO_BRANCH), xstr(PSASTRO_VERSION));
+    return value;
 }
 
 psString psastroSource(void)
 {
-#ifndef PSASTRO_SOURCE
-#error "PSASTRO_SOURCE is not set"
-#endif
-    return psStringCopy(PSASTRO_SOURCE);
+  return psStringCopy(xstr(PSASTRO_SOURCE));
 }
 
Index: /trunk/psconfig/psbuild
===================================================================
--- /trunk/psconfig/psbuild	(revision 23228)
+++ /trunk/psconfig/psbuild	(revision 23229)
@@ -13,4 +13,5 @@
 $stop = "";
 $verbose = 0;
+$use_svn = 1;
 
 $extlibs = "none";
@@ -83,4 +84,8 @@
     if ($ARGV[0] eq "-bootstrap") {
         &bootstrap ();
+    }
+    if ($ARGV[0] eq "-skip-svn") {
+	$use_svn = 0;
+	shift; next;
     }
     if ($ARGV[0] eq "-env") {
@@ -157,6 +162,61 @@
 sub build_distribution {
 
+    # set environment variables used to supply SVN info to the compilation
+
+    if ($use_svn) {
+	# example dump from svn info:
+	# pikake: svn info
+	# Path: .
+	# URL: https://svn.pan-starrs.ifa.hawaii.edu/repo/ipp/branches/eam_branches/eam_branch_20090303/ppImage
+	# Repository Root: https://svn.pan-starrs.ifa.hawaii.edu/repo/ipp
+	# Repository UUID: 60eb6cdc-a59c-4636-a4e0-dba66a9721fd
+	# Revision: 23158
+	# Node Kind: directory
+	# Schedule: normal
+	# Last Changed Author: price
+	# Last Changed Rev: 23125
+	# Last Changed Date: 2009-03-03 15:41:16 -1000 (Tue, 03 Mar 2009)
+	
+	$svn_version = `svnversion`; chomp $svn_version;
+	@svn_info = `svn info`;
+
+	# get the svn_root first:
+	foreach $line (@svn_info) {
+	    if ($line =~ m|^Repository Root:|) {
+		($svn_root) = $line =~ m|^Repository Root:\s*(\S*)|;
+		last;
+	    }
+	}
+
+	# now get the branch and UUID values
+	foreach $line (@svn_info) {
+	    if ($line =~ m|^URL:|) {
+		($svn_branch) = $line =~ m|^URL:\s*$svn_root/*(\S*)|;
+	    }
+	    if ($line =~ m|^Repository UUID:|) {
+		($svn_source) = $line =~ m|^Repository UUID:\s*(\S*)|;
+	    }
+	}
+	
+	$ENV{SVN_VERSION} = $svn_version;
+	$ENV{SVN_BRANCH}  = $svn_branch;
+	$ENV{SVN_SOURCE}  = $svn_source;
+    } else {
+	# alternatively, grab these from the following files:
+	if (! -e "SVNINFO") { 
+	    print "missing SVNINFO file for repository info, skipping\n";
+	} else {
+	    @svn_info = `cat SVNINFO`;
+	    foreach $line (@svn_info) {
+		($name, $value) = split (" ", $line);
+		$ENV{$name} = $value;
+	    }
+	}
+    }
+    print "SVN_VERSION $ENV{SVN_VERSION}\n";
+    print "SVN_BRANCH  $ENV{SVN_BRANCH}\n";
+    print "SVN_SOURCE  $ENV{SVN_SOURCE}\n";
+
     # use psconfig.csh to set needed build aliases
-
     if ($extlibs eq "check") {
         $status = vsystem ("pschecklibs");
Index: /trunk/psphot/src/Makefile.am
===================================================================
--- /trunk/psphot/src/Makefile.am	(revision 23228)
+++ /trunk/psphot/src/Makefile.am	(revision 23229)
@@ -1,14 +1,14 @@
 lib_LTLIBRARIES = libpsphot.la
 
-PSPHOT_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
-PSPHOT_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
-PSPHOT_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+# PSPHOT_VERSION=`if [ -e ../../VERSION ]; then cat ../../VERSION; else svnversion; fi`
+# PSPHOT_BRANCH=`if [ -e ../../BRANCH ]; then cat ../../BRANCH; else svn info | sed -n -e '/URL:/ h' -e '/Repository Root:/ { x; H; x; s|Repository Root: \(.*\)\nURL: \1\(.*\)|\2| ; s|^/|| ; s|/[a-zA-Z]*/src.*|| ; p }'; fi`
+# PSPHOT_SOURCE=`if [ -e ../../SOURCE ]; then cat ../../SOURCE; else svn info | sed -n -e 's/Repository UUID: // p'; fi`
+# 
+# # Force recompilation of psphotVersion.c, since it gets the version information
+# psphotVersion.c: FORCE
+# 	touch psphotVersion.c
+# FORCE: ;
 
-# Force recompilation of psphotVersion.c, since it gets the version information
-psphotVersion.c: FORCE
-	touch psphotVersion.c
-FORCE: ;
-
-libpsphot_la_CFLAGS = $(PSPHOT_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS) -DPSPHOT_VERSION=\"$(PSPHOT_VERSION)\" -DPSPHOT_BRANCH=\"$(PSPHOT_BRANCH)\" -DPSPHOT_SOURCE=\"$(PSPHOT_SOURCE)\"
+libpsphot_la_CFLAGS = $(PSPHOT_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS) -DPSPHOT_VERSION=$(PSPHOT_VERSION) -DPSPHOT_BRANCH=$(PSPHOT_BRANCH) -DPSPHOT_SOURCE=$(SVN_SOURCE)
 libpsphot_la_LDFLAGS = $(PSPHOT_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
 
Index: /trunk/psphot/src/psphotVersion.c
===================================================================
--- /trunk/psphot/src/psphotVersion.c	(revision 23228)
+++ /trunk/psphot/src/psphotVersion.c	(revision 23229)
@@ -5,6 +5,4 @@
 #endif
 
-psString psphotVersion(void)
-{
 #ifndef PSPHOT_VERSION
 #error "PSPHOT_VERSION is not set"
@@ -13,13 +11,21 @@
 #error "PSPHOT_BRANCH is not set"
 #endif
-    return psStringCopy(PSPHOT_BRANCH "@" PSPHOT_VERSION);
+#ifndef PSPHOT_SOURCE
+#error "PSPHOT_SOURCE is not set"
+#endif
+
+#define xstr(s) str(s)
+#define str(s) #s
+
+psString psphotVersion(void)
+{
+    char *value = NULL;
+    psStringAppend(&value, "%s@%s", xstr(PSPHOT_BRANCH), xstr(PSPHOT_VERSION));
+    return value;
 }
 
 psString psphotSource(void)
 {
-#ifndef PSPHOT_SOURCE
-#error "PSPHOT_SOURCE is not set"
-#endif
-    return psStringCopy(PSPHOT_SOURCE);
+    return psStringCopy(xstr(PSPHOT_SOURCE));
 }
 
