Index: branches/meh_branches/ppstack_test/Nebulous-Server/Build.PL
===================================================================
--- branches/meh_branches/ppstack_test/Nebulous-Server/Build.PL	(revision 32161)
+++ branches/meh_branches/ppstack_test/Nebulous-Server/Build.PL	(revision 33415)
@@ -49,4 +49,5 @@
         bin/neb-voladd
         bin/neb-voladm
+        bin/neb-insedit
         bin/neb-host
         bin/nebdiskd
Index: branches/meh_branches/ppstack_test/Nebulous-Server/MANIFEST
===================================================================
--- branches/meh_branches/ppstack_test/Nebulous-Server/MANIFEST	(revision 32161)
+++ branches/meh_branches/ppstack_test/Nebulous-Server/MANIFEST	(revision 33415)
@@ -10,4 +10,5 @@
 bin/neb-cabadd
 bin/neb-voladm
+bin/neb-insedit
 bin/neb-host
 bin/nebdiskd
Index: branches/meh_branches/ppstack_test/Nebulous-Server/bin/neb-admin
===================================================================
--- branches/meh_branches/ppstack_test/Nebulous-Server/bin/neb-admin	(revision 32161)
+++ branches/meh_branches/ppstack_test/Nebulous-Server/bin/neb-admin	(revision 33415)
@@ -29,4 +29,5 @@
     $pending,
     $balance,
+    $retarget,
     $balance_N_sources,
     $balance_M_destinations,
@@ -49,4 +50,5 @@
     'pendingreplicate|r'    => \$pending,
     'pendingbalance|b'      => \$balance,
+    'pendingtarget|t'       => \$retarget,
     'balancesources|N=s'      => \$balance_N_sources,
     'balancedestinations|M=s' => \$balance_M_destinations,
@@ -64,7 +66,7 @@
 pod2usage( -msg => "--limit is meaningless without --pendingreplicate",
         -exitval => 2 )
-    if defined $limit and not (defined $pending or defined $balance);
+    if defined $limit and not (defined $pending or defined $balance or defined $retarget);
 pod2usage( -msg => "no operation specified", -exitval => 2 )
-    unless defined $pending or defined $balance;
+    unless defined $pending or defined $balance or defined $retarget;
 pod2usage( -msg => "--pendingbalance needs --balancesources and --balancedestinations options",
 	   -exitval => 2)
@@ -81,4 +83,7 @@
 elsif (defined $balance) {
     $pidfile = '/var/tmp/neb-admin.balance';
+}
+elsif (defined $retarget) {
+    $pidfile = '/var/tmp/neb-admin.retarget';
 }
 else {
@@ -107,4 +112,6 @@
 } elsif ($balance) {
     balance();
+} elsif ($retarget) {
+    retarget();
 } else {
     die "THIS SHOULD NOT HAPPEN";
@@ -306,5 +313,5 @@
     SELECT S.*,vol_id FROM (
       -- Select objects that are in our so_id range, with two copies (and two requested copies)
-      SELECT MAX(ins_id) AS MAXins_id,MIN(ins_id) AS MINins_id,so_id,ext_id,value AS user_copies FROM
+      SELECT MAX(ins_id) AS MAXins_id,MIN(ins_id) AS MINins_id,so_id,ext_id FROM
        storage_object JOIN storage_object_xattr USING(so_id) RIGHT JOIN instance USING(so_id)
        WHERE name = 'user.copies' AND value >= 2
@@ -462,5 +469,5 @@
 	my $so_id = $obj->{so_id};
 	my $key   = $obj->{ext_id};
-	my $copies = $obj->{user_copies};
+#	my $copies = $obj->{user_copies};
 
 	my $source_vol_id = $obj->{vol_id};
@@ -480,5 +487,5 @@
         my %md = (
 	    key           => $key,
-	    copies        => $copies,
+#	    copies        => $copies,
 	    so_id         => $so_id,
 	    source_vol_id => $source_vol_id,
@@ -524,4 +531,218 @@
 }
 
+sub retarget
+{
+    my %destination;
+    my %vol_id_list = ('ipp004.0' => 1,'ipp005.0' => 2, 'ipp006.0' => 3, 'ipp007.0' => 4,'ipp008.0' => 50,'ipp009.0' => 6,'ipp010.0' => 7,
+		       'ipp011.0' => 8,'ipp012.0' => 15,'ipp013.0' => 16, 'ipp014.0' => 18,'ipp015.0' => 17,'ipp016.0' => 9,'ipp017.0' => 10,'ipp018.0' => 11,'ipp019.0' => 12,
+		       'ipp020.0' => 52,'ipp021.0' => 14,'ipp023.0' => 19,'ipp024.0' => 20,'ipp025.0' => 32,'ipp026.0' => 21, 'ipp027.0' => 49,
+		       'ipp028.0' => 23,'ipp029.0' => 24,'ipp030.0' => 25,'ipp031.0' => 26,'ipp032.0' => 27,'ipp033.0' => 28,'ipp034.0' => 29,
+		       'ipp035.0' => 30,'ipp036.0' => 31,'ipp037.0' => 51,'ipp038.0' => 33,'ipp039.0' => 34,'ipp040.0' => 35,'ipp041.0' => 36,
+		       'ipp042.0' => 37,'ipp043.0' => 38,'ipp044.0' => 39,'ipp045.0' => 40,'ipp046.0' => 41,'ipp047.0' => 42,'ipp048.0' => 43,
+		       'ipp049.0' => 44,'ipp050.0' => 45,'ipp051.0' => 46,'ipp052.0' => 47,'ipp053.0' => 48,'ipp054.0' => 62,'ipp055.0' => 63,
+		       'ipp056.0' => 64,'ipp057.0' => 65,'ipp058.0' => 66,'ipp059.0' => 67,'ipp060.0' => 68,'ipp061.0' => 69,'ipp062.0' => 70,
+		       'ipp063.0' => 71,'ipp064.0' => 72,'ipp065.0' => 73,'ipp066.0' => 74);
+    %{ $destination{VOL} }  = ('ota01' => 7,       'ota02' => 8,       'ota03' => 3,       'ota04' => 3,       'ota05' => 4,       'ota06' => 4,
+			       'ota10' => 50,      'ota11' => 50,      'ota12' => 6,       'ota13' => 6,       'ota14' => 7,       'ota15' => 8,       'ota16' => 15,      'ota17' => 15,
+			       'ota20' => 16,      'ota21' => 18,      'ota22' => 18,      'ota23' => 17,      'ota24' => 9,       'ota25' => 9,       'ota26' => 10,      'ota27' => 11,
+			       'ota30' => 12,      'ota31' => 52,      'ota32' => 14,      'ota33' => 14,      'ota34' => 19,      'ota35' => 16,      'ota36' => 20,      'ota37' => 12,
+			       'ota40' => 32,      'ota41' => 11,      'ota42' => 10,      'ota43' => 17,      'ota44' => 49,      'ota45' => 23,      'ota46' => 24,      'ota47' => 25,
+			       'ota50' => 26,      'ota51' => 27,      'ota52' => 28,      'ota53' => 29,      'ota54' => 30,      'ota55' => 31,      'ota56' => 51,      'ota57' => 33,
+			       'ota60' => 34,      'ota61' => 35,      'ota62' => 36,      'ota63' => 37,      'ota64' => 38,      'ota65' => 39,      'ota66' => 41,      'ota67' => 42,
+			       'ota71' => 43,      'ota72' => 44,      'ota73' => 45,      'ota74' => 46,      'ota75' => 47,      'ota76' => 48);
+    %{ $destination{HOST} } = ('ota01' => 'ipp010','ota02' => 'ipp011','ota03' => 'ipp006','ota04' => 'ipp054','ota05' => 'ipp007','ota06' => 'ipp055',
+			       'ota10' => 'ipp008','ota11' => 'ipp056','ota12' => 'ipp009','ota13' => 'ipp057','ota14' => 'ipp058','ota15' => 'ipp059','ota16' => 'ipp012','ota17' => 'ipp060',
+			       'ota20' => 'ipp013','ota21' => 'ipp014','ota22' => 'ipp061','ota23' => 'ipp015','ota24' => 'ipp016','ota25' => 'ipp062','ota26' => 'ipp064','ota27' => 'ipp065',
+			       'ota30' => 'ipp066','ota31' => 'ipp020','ota32' => 'ipp063','ota33' => 'ipp021','ota34' => 'ipp023','ota35' => 'ipp013','ota36' => 'ipp024','ota37' => 'ipp019',
+			       'ota40' => 'ipp025','ota41' => 'ipp018','ota42' => 'ipp017','ota43' => 'ipp015','ota44' => 'ipp027','ota45' => 'ipp028','ota46' => 'ipp029','ota47' => 'ipp030',
+			       'ota50' => 'ipp031','ota51' => 'ipp032','ota52' => 'ipp033','ota53' => 'ipp034','ota54' => 'ipp035','ota55' => 'ipp036','ota56' => 'ipp037','ota57' => 'ipp038',
+			       'ota60' => 'ipp039','ota61' => 'ipp040','ota62' => 'ipp041','ota63' => 'ipp042','ota64' => 'ipp043','ota65' => 'ipp044','ota66' => 'ipp046','ota67' => 'ipp047',
+			       'ota71' => 'ipp048','ota72' => 'ipp049','ota73' => 'ipp050','ota74' => 'ipp051','ota75' => 'ipp052','ota76' => 'ipp053');
+    %{ $destination{NAME} } = ('ota01' => 'ipp010.0','ota02' => 'ipp011.0','ota03' => 'ipp006.0','ota04' => 'ipp054.0','ota05' => 'ipp007.0','ota06' => 'ipp055.0',
+			       'ota10' => 'ipp008.0','ota11' => 'ipp056.0','ota12' => 'ipp009.0','ota13' => 'ipp057.0','ota14' => 'ipp058.0','ota15' => 'ipp059.0','ota16' => 'ipp012.0','ota17' => 'ipp060.0',
+			       'ota20' => 'ipp013.0','ota21' => 'ipp014.0','ota22' => 'ipp061.0','ota23' => 'ipp015.0','ota24' => 'ipp016.0','ota25' => 'ipp062.0','ota26' => 'ipp064.0','ota27' => 'ipp065.0',
+			       'ota30' => 'ipp066.0','ota31' => 'ipp020.0','ota32' => 'ipp063.0','ota33' => 'ipp021.0','ota34' => 'ipp023.0','ota35' => 'ipp013.0','ota36' => 'ipp024.0','ota37' => 'ipp019.0',
+			       'ota40' => 'ipp025.0','ota41' => 'ipp018.0','ota42' => 'ipp017.0','ota43' => 'ipp015.0','ota44' => 'ipp027.0','ota45' => 'ipp028.0','ota46' => 'ipp029.0','ota47' => 'ipp030.0',
+			       'ota50' => 'ipp031.0','ota51' => 'ipp032.0','ota52' => 'ipp033.0','ota53' => 'ipp034.0','ota54' => 'ipp035.0','ota55' => 'ipp036.0','ota56' => 'ipp037.0','ota57' => 'ipp038.0',
+			       'ota60' => 'ipp039.0','ota61' => 'ipp040.0','ota62' => 'ipp041.0','ota63' => 'ipp042.0','ota64' => 'ipp043.0','ota65' => 'ipp044.0','ota66' => 'ipp046.0','ota67' => 'ipp047.0',
+			       'ota71' => 'ipp048.0','ota72' => 'ipp049.0','ota73' => 'ipp050.0','ota74' => 'ipp051.0','ota75' => 'ipp052.0','ota76' => 'ipp053.0');
+    foreach my $k (keys %{ $destination{NAME} }) {
+	$destination{VOL}{$k} = $vol_id_list{$destination{NAME}{$k}};
+    }
+    my %blocked_volume = ('ipp027' => 1,
+			  'ipp037' => 1,
+			  'ipp043' => 1,
+			  'ipp044' => 1,
+			  'ipp053' => 1,
+			  'ipp023' => 1,'ipp024' => 1,'ipp025' => 1,'ipp026' => 1,'ipp028' => 1,'ipp029' => 1,'ipp030' => 1,'ipp031' => 1,'ipp032' => 1,'ipp033' => 1,
+			  'ipp034' => 1,'ipp035' => 1,'ipp036' => 1,'ipp038' => 1,'ipp039' => 1,'ipp040' => 1,'ipp041' => 1,'ipp042' => 1,'ipp045' => 1,'ipp046' => 1,
+			  'ipp047' => 1,'ipp048' => 1,'ipp049' => 1,'ipp050' => 1,'ipp051' => 1,'ipp052' => 1,
+			  # These hosts are blocked due to concern about their RAID arrays.
+#			  'ipp054' => 1,'ipp055' => 1,'ipp056' => 1,'ipp057' => 1,'ipp058' => 1,'ipp059' => 1,
+#			  'ipp060' => 1,'ipp061' => 1,'ipp062' => 1,'ipp063' => 1,'ipp064' => 1,'ipp065' => 1, 'ipp066' => 1
+			  'ipp064' => 1, 'ipp066' => 1
+	);
+    if (not defined $so_id_start) { $so_id_start = 0; }
+    if (not defined $so_id_range) { $so_id_range = 100000; }
+    my $so_id_end = $so_id_start + $so_id_range;
+
+    # XXX check if so_id_start is beyond MAX(so_id): if so, exit with exit status 10
+    { 
+        my $query = $dbh->prepare("SELECT MAX(so_id) from storage_object");
+        $query->execute;
+        my $answer = $query->fetchrow_arrayref;
+        my $max_so_id = $$answer[0];
+        $query->finish;
+
+        if ($so_id_start > $max_so_id) { 
+            print STDERR "at end of so_id range, reset please\n";
+            exit 10;
+        }
+    }
+
+    # Determine a list of first instances
+#     my $query = $dbh->prepare(
+# 	"
+# SELECT S.so_id,ext_id,ins_id,uri,substr(uri,-10,5) AS class_id,vol_id,name AS vol_name,host AS vol_host FROM (
+#   select * FROM (
+#   select MAX(ins_id) AS MAXins_id, MIN(ins_id) AS MINins_id, so_id,ext_id,value AS user_copies FROM storage_object 
+#     JOIN storage_object_xattr USING(so_id) 
+#     RIGHT JOIN instance USING(so_id) 
+#      WHERE name = 'user.copies' 
+#      AND value >= 2 
+#      AND ext_id LIKE '%ota%fits' 
+#      AND so_id >= $so_id_start
+#      AND so_id <  $so_id_end
+#      GROUP BY so_id 
+#   ) AS V 
+#   WHERE MAXins_id != MINins_id
+#   ) AS S
+#   JOIN instance ON (S.MINins_id = instance.ins_id AND S.so_id = instance.so_id) 
+#   JOIN volume USING(vol_id) WHERE volume.available = 1
+#   LIMIT $limit
+#   OFFSET $offset
+# ");
+    my $query = $dbh->prepare(
+	"
+SELECT Z.so_id,ext_id,Z.ins_id,Z.uri,substr(Z.uri,-10,5) AS class_id,Z.vol_id,vol_name,vol_host,
+       instance.vol_id AS  second_vol_id,volume.name AS second_name,volume.host AS second_host FROM (
+  SELECT S.so_id,ext_id,ins_id,uri,substr(uri,-10,5) AS class_id,vol_id,name AS vol_name,host AS vol_host,MAXins_id FROM (
+    select * FROM (
+      select MAX(ins_id) AS MAXins_id, MIN(ins_id) AS MINins_id, so_id,ext_id FROM storage_object
+        JOIN storage_object_xattr USING(so_id)
+        RIGHT JOIN instance USING(so_id)
+         WHERE name = 'user.copies'
+         AND value >= 2
+         AND ext_id LIKE '%ota%fits'
+         AND so_id >= $so_id_start
+         AND so_id <  $so_id_end
+         GROUP BY so_id
+      ) AS V
+      WHERE MAXins_id != MINins_id
+    ) AS S
+    JOIN instance ON (S.MINins_id = instance.ins_id AND S.so_id = instance.so_id)
+    JOIN volume USING(vol_id) WHERE volume.available = 1
+  ) AS Z
+JOIN instance ON (MAXins_id = instance.ins_id AND Z.so_id = instance.so_id)
+JOIN volume ON(instance.vol_id = volume.vol_id) WHERE volume.available = 1
+LIMIT $limit
+OFFSET $offset
+");
+
+    $query->execute();
+
+    my @rows;
+    while (my $row = $query->fetchrow_hashref) {
+        push @rows, $row;        
+    }
+    $query->finish;
+
+    print STDERR "No rows found\n" unless scalar @rows;
+    exit unless scalar @rows;
+    
+    # compare number of responses to limit below
+    my $Npending = @rows;
+
+    print "targetPending MULTI\n\n";
+
+    foreach my $obj (@rows) {
+        if (defined $verbose) {
+            require Data::Dumper;
+            print Data::Dumper::Dumper($obj);
+        }
+	
+	my $so_id = $obj->{so_id};
+	my $key   = $obj->{ext_id};
+
+	my $ins_id = $obj->{ins_id};
+	my $uri   = $obj->{uri};
+	my $class_id = $obj->{class_id};
+
+	my $source_vol_id = $obj->{vol_id};
+	my $source_name   = $obj->{vol_name};
+	my $source_host   = $obj->{vol_host};
+
+	my $second_vol_id   = $obj->{second_vol_id};
+	my $second_vol_name = $obj->{second_name};
+	my $second_vol_host = $obj->{second_host};
+
+	my $destination_vol_id = $destination{VOL}{$class_id};
+	my $destination_name   = $destination{NAME}{$class_id};
+	my $destination_host   = $destination{HOST}{$class_id};
+	my $destination_uri    = $uri;
+	unless ((defined($destination_uri))&&
+		(defined($source_name))&&
+		(defined($destination_name))) {
+	    next;
+	}
+	
+	$destination_uri =~ s/$source_name/$destination_name/;
+	
+        my %md = (
+	    so_id         => $so_id,
+	    key           => $key,
+	    ins_id        => $ins_id,
+	    uri           => $uri,
+	    class_id      => $class_id,
+	    
+	    source_vol_id => $source_vol_id,
+	    source_name   => $source_name,
+	    source_host   => $source_host,
+
+	    destination_vol_id => $destination_vol_id,
+	    destination_name   => $destination_name,
+	    destination_host   => $destination_host,
+	    destination_uri    => $destination_uri,
+	    second_host        => $second_vol_host,
+	    second_vol_id      => $second_vol_id
+#	    blocked_host_bool  => defined($blocked_volume{$destination_host})
+        );
+	
+	if ((defined($blocked_volume{$destination_host}))||
+	    ($source_vol_id == $destination_vol_id)||
+	    ($second_vol_id == $destination_vol_id)||
+	    ($source_name   eq $destination_name)||
+	    ($second_vol_name eq $destination_name)) {
+#	    print_metadata("targetFailed", \%md);
+	    next;
+	}
+
+	if ($source_vol_id != $destination_vol_id) {
+	    print_metadata("targetPending", \%md);
+	    print "\n";
+	}
+    }
+
+    # use a different exit status if we hit the limit (likely more files pending)
+    print STDERR "Which exit: $Npending $limit\n";
+    # This is a better way to do this. This should be even better-er.
+    if ($Npending < $limit) {
+	exit 0;
+    }
+    else {
+	exit 1;
+    }
+}
+
 sub removal
 {
Index: branches/meh_branches/ppstack_test/Nebulous-Server/bin/neb-host
===================================================================
--- branches/meh_branches/ppstack_test/Nebulous-Server/bin/neb-host	(revision 32161)
+++ branches/meh_branches/ppstack_test/Nebulous-Server/bin/neb-host	(revision 33415)
@@ -18,5 +18,5 @@
 my ($host, $state, $note,
     $db, $dbhost, $dbpass, $dbuser,
-    $allocate, $available, $debug);
+    $allocate, $available, $debug,$volume);
 
 # $db     = $ENV{'NEB_DB'} unless $db;
@@ -56,5 +56,5 @@
     'dbuser=s'       => \$dbuser,
     'dbpass=s'       => \$dbpass,
-
+    'volume'         => \$volume,
     
     ) || pod2usage( 2 );
@@ -98,5 +98,10 @@
     
     my %constraint;
-    $constraint{'v.host'} = $host;
+    if (defined($volume)) {
+	$constraint{'v.name'} = $host;
+    }
+    else {
+	$constraint{'v.host'} = $host;
+    }
     my %set;
     $set{'v.allocate'} = $allocate if defined $allocate;
Index: branches/meh_branches/ppstack_test/Nebulous-Server/bin/neb-insedit
===================================================================
--- branches/meh_branches/ppstack_test/Nebulous-Server/bin/neb-insedit	(revision 33415)
+++ branches/meh_branches/ppstack_test/Nebulous-Server/bin/neb-insedit	(revision 33415)
@@ -0,0 +1,292 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2005-2008  Joshua Hoblitt
+#
+# $Id: neb-insedit,v 1.2 2008-10-01 21:05:57 jhoblitt Exp $
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.02';
+
+use DBI;
+use Nebulous::Server::SQL;
+use Nebulous::Client;
+use URI::file;
+use URI;
+
+use Digest::MD5;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+my ($db, $dbhost, $dbuser, $dbpass, $key, $ins_id, $new_uri, $new_volume,$nebhost);
+
+$db     = $ENV{'NEB_DB'} unless $db;
+$dbhost = $ENV{'NEB_DBHOST'} || 'localhost';
+$dbuser = $ENV{'NEB_USER'} unless $dbuser;
+$dbpass = $ENV{'NEB_PASS'} unless $dbpass;
+$nebhost= $ENV{'NEB_SERVER'} unless $nebhost;
+
+GetOptions(
+    'db|d=s'            => \$db,
+    'host=s'            => \$dbhost,
+    'pass|p=s'          => \$dbpass,
+    'user|u=s'          => \$dbuser,
+    'neb_host=s'        => \$nebhost,
+    'key=s'             => \$key,
+    'ins_id=s'          => \$ins_id,
+    'uri=s'             => \$new_uri,
+    'volume=s'          => \$new_volume,
+) || pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --db --user --pass --key --ins_id --uri --volume", -exitval => 2 )
+    unless $db && $dbuser && $dbpass && $ins_id && $new_uri && $key && $new_volume;
+
+my $dbh = DBI->connect(
+    "DBI:mysql:database=$db:host=$dbhost",
+    $dbuser,
+    $dbpass,
+    {
+        RaiseError => 1,
+        PrintError => 0,
+        AutoCommit => 1,
+    },
+);
+
+# Connect and retrieve instance information via stat:
+my $neb = Nebulous::Client->new(
+    proxy => "$nebhost",
+    );
+
+die "can't connected to Nebulous Server: $nebhost"
+    unless defined $neb;
+
+
+# Pre-emptively attempt repair of file.  This will ~double our execution time, but should help the efficiency.
+system("neb-repair $key");
+if ($? >> 8 != 0) {
+    die "Failure to repair $key.\n";
+}
+
+# Do checks now.
+
+my $stat = $neb->stat($key);
+die "nebulous key: $key not found" unless $stat;   
+my $instances = $neb->find_instances($key, 'any');
+die "no instances found" unless $instances;   
+
+my $user_copies;
+# eval {
+#     $user_copies = $neb->getxattr($key, "user.copies");
+# };
+$user_copies = 1;
+unless(defined($user_copies)) {
+    $user_copies = 1;
+}
+my @validation;
+my %md5sum_uniq;
+my $existing_copies = 0;
+my @existance;
+my @md5sums;
+my @files = map {URI->new($_)->file if $_} @$instances;
+
+for (my $i = 0; $i <= $#files; $i++) {
+    if (-e $files[$i]) {
+	$existance[$i] = 1;
+	$existing_copies++;
+	my $fh;
+	open($fh,$files[$i]);
+	$md5sums[$i] = Digest::MD5->new->addfile($fh)->hexdigest;
+	$md5sum_uniq{$md5sums[$i]} = 1;
+	close($fh);
+    }
+    else {
+	$existance[$i] = 0;
+	$md5sums[$i] = 'NON-EXISTANT';
+	$md5sum_uniq{$md5sums[$i]} = 1;
+    }
+    $validation[$i] = sprintf("% 3d %32s %s",
+			      $existance[$i],
+			      $md5sums[$i],
+			      $files[$i]);
+}
+# Confirm everything is in a valid state
+my $N_md5sums = scalar(keys %md5sum_uniq);
+if ($N_md5sums != 1) {
+    die "Cannot modify this instance! storage_object $key in inconsistent state!";
+}
+if ($#files < 1) {
+    die "Insufficient instances! $key!";
+}
+my $test_md5sum = $md5sums[0];
+
+# Copy instance to new uri
+my $destination_file = URI->new($new_uri)->file;
+my $source_file      = $files[0];
+system("cp $source_file $destination_file");
+system("glockfile $destination_file xcld 0");
+
+my $fh;
+open($fh,$destination_file);
+my $new_md5sum = Digest::MD5->new->addfile($fh)->hexdigest;
+close($fh);
+
+if ($new_md5sum ne $test_md5sum) {
+    die "md5sum mismatch with new copy. $destination_file ($new_md5sum) != $source_file ($test_md5sum) Exiting!";
+}
+
+# Update database table
+my $sql = Nebulous::Server::SQL->new();
+
+my $query = $dbh->prepare( $sql->get_volume_by_name );
+$query->execute( $new_volume );
+my ($vol_id, $vol_name, $vol_host, $vol_path) = $query->fetchrow_array;
+$query->finish;
+
+print "$new_volume $vol_id $vol_name $vol_host $vol_path $new_uri $ins_id\n";
+unless (defined($vol_id)) {
+    die "Invalid destination volume.";
+}
+
+$query = $dbh->prepare( $sql->update_instance_uri );
+$query->execute( $vol_id, $new_uri, $ins_id );
+$query->finish;
+
+unlink($source_file);
+print " OK\n";
+
+__END__
+
+=pod
+
+=head1 NAME
+
+neb-insedit - edit the instance information
+
+=head1 SYNOPSIS
+
+    neb-insedit --key <neb_key> --ins_id <ins_id> --uri <destination_uri> --volume <destination_volume>
+    [--db <database>] [--user <username>] [--pass <password>] [--host <hostname]
+
+=head1 DESCRIPTION
+
+This program allows the given instance of an object to be moved from one volume/uri to another.
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --key <neb_key>
+
+Nebulous key for the storage object associated with the instance to move.
+
+=item * --ins_id <ins_id>
+
+Instance identifier that will be moved
+
+=item * --uri <destination_uri>
+
+Final location desired for this instance.  Should be of the form "file://data/ipp0XX.0/nebulous/aa/aa/ins_id.nebulous:directory:tree:file".
+
+=item * --volume <destination volume>
+
+Volume name that this instance will be moved to.
+
+=item * --db|-d <database>
+
+Name of database (C<namespace>) to create tables in.
+
+Optional if the appropriate environment variable is set.
+
+=item * --user|-u <username>
+
+Username to authenticate with.
+
+Optional if the appropriate environment variable is set.
+
+=item * --pass|-p <password>
+
+Password to authenticate with.
+
+Optional if the appropriate environment variable is set.
+
+=item * --host <database host>
+
+Name of host on which the database resides.
+
+=item * --neb_host <nebulous client server>
+
+Location of the client side server to connect to.
+
+Optional.  Defaults to C<localhost>.
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<NEB_DB>
+
+Equivalent to --db|-d
+
+=item * C<NEB_HOST>
+
+Equivalent to --host
+
+=item * C<NEB_USER>
+
+Equivalent to --user|-u
+
+=item * C<NEB_PASS>
+
+Equivalent to --pass|-p 
+
+=item * C<NEB_SERVER>
+
+Equivalent to --neb_host
+
+=back
+
+=head1 CREDITS
+
+Josh Hoblitt and Chris Waters 
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Joshua Hoblitt <jhoblitt@cpan.org>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2005-2008  Joshua Hoblitt.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Server>, L<neb-initdb>, L<neb-df>, L<nebdiskd>
+
+=cut
Index: branches/meh_branches/ppstack_test/Nebulous-Server/bin/neb-voladm
===================================================================
--- branches/meh_branches/ppstack_test/Nebulous-Server/bin/neb-voladm	(revision 32161)
+++ branches/meh_branches/ppstack_test/Nebulous-Server/bin/neb-voladm	(revision 33415)
@@ -68,5 +68,5 @@
         if (defined $allocate and $allocate !~ m/^[01]$/)
         or (defined $available and $available !~ m/^[01]$/)
-        or (defined $xattr and $xattr !~ m/^[01]$/);
+        or (defined $xattr and $xattr !~ m/^[012]$/);
 }
 
Index: branches/meh_branches/ppstack_test/Nebulous/Build.PL
===================================================================
--- branches/meh_branches/ppstack_test/Nebulous/Build.PL	(revision 32161)
+++ branches/meh_branches/ppstack_test/Nebulous/Build.PL	(revision 33415)
@@ -116,4 +116,5 @@
         bin/neb-mv
         bin/neb-replicate
+        bin/neb-repair
         bin/neb-rm
         bin/neb-shift
Index: branches/meh_branches/ppstack_test/Nebulous/MANIFEST
===================================================================
--- branches/meh_branches/ppstack_test/Nebulous/MANIFEST	(revision 32161)
+++ branches/meh_branches/ppstack_test/Nebulous/MANIFEST	(revision 33415)
@@ -17,4 +17,5 @@
 bin/neb-mv
 bin/neb-replicate
+bin/neb-repair
 bin/neb-rm
 bin/neb-shift
Index: branches/meh_branches/ppstack_test/Nebulous/bin/neb-repair
===================================================================
--- branches/meh_branches/ppstack_test/Nebulous/bin/neb-repair	(revision 33415)
+++ branches/meh_branches/ppstack_test/Nebulous/bin/neb-repair	(revision 33415)
@@ -0,0 +1,241 @@
+#!/usr/bin/env perl
+
+# Copyright (C) 2007-2008  Chris Waters
+#
+
+use strict;
+use warnings FATAL => qw( all );
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use Nebulous::Client;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+use Pod::Usage qw( pod2usage );
+
+use Digest::MD5;
+use URI;
+
+my ($validate,$server,$nouris);
+
+$server = $ENV{'NEB_SERVER'} unless $server;
+
+$validate = 1;
+$nouris   = 1;
+
+GetOptions(
+    'server|s=s'     => \$server,
+) || pod2usage( 2 );
+
+my $key = shift;
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --server", -exitval => 2 )
+    unless $server;
+pod2usage( -msg => "missing key", exitval => 2 )
+    unless defined $key;
+
+my $neb = Nebulous::Client->new(
+    proxy => "$server",
+);
+
+die "can't connected to Nebulous Server: $server"
+    unless defined $neb;
+
+my $stat = $neb->stat($key);
+die "nebulous key: $key not found" unless $stat;   
+my $instances = $neb->find_instances($key, 'any');
+die "no instances found" unless $instances;   
+
+my $user_copies;
+eval {
+    $user_copies = $neb->getxattr($key, "user.copies");
+};
+unless(defined($user_copies)) {
+    $user_copies = 2;
+}
+
+my @validation;
+my %md5sum_uniq;
+my %md5sum_valid;
+my $last_f = '';
+my $existing_copies = 0;
+my @existance;
+my @md5sums;
+my @files = map {URI->new($_)->file if $_} @$instances;
+
+for (my $i = 0; $i <= $#files; $i++) {
+    if (-e $files[$i]) {
+	$existance[$i] = 1;
+	$existing_copies++;
+	my $fh;
+	open($fh,$files[$i]);
+	$md5sums[$i] = Digest::MD5->new->addfile($fh)->hexdigest;
+	close($fh);
+
+	$md5sum_uniq{$md5sums[$i]} = 1;
+
+	if ($md5sums[$i] eq 'd41d8cd98f00b204e9800998ecf8427e') {
+	    $existance[$i] = 0;
+	    push @{ $md5sum_valid{$md5sums[$i]} }, $files[$i];
+	    next;
+	}
+
+	push @{ $md5sum_valid{$md5sums[$i]} }, $files[$i];
+	$last_f = $files[$i];
+    }
+    else {
+	$existance[$i] = 0;
+	$md5sums[$i] = 'NON-EXISTANT';
+	$md5sum_uniq{$md5sums[$i]} = 1;
+	push @{ $md5sum_valid{$md5sums[$i]} }, $files[$i];
+    }
+}
+
+my $N_mds = scalar(keys(%md5sum_uniq));
+my $N_files = scalar(@files);
+if ($N_files < $user_copies) {
+    print "Insufficient copies ($N_files) of $key, replicating!\n";
+    system("neb-replicate $key");
+}
+if ($N_mds == 1) {
+    print "Nebulous key $key is consistent.\n";
+}
+else {
+    my $valid_sum = '';
+    my $valid_file = '';
+    my $m;
+    foreach $m (keys %md5sum_valid) {
+	if (($m ne 'd41d8cd98f00b204e9800998ecf8427e')&&($m ne 'NON-EXISTANT')) {
+	    if ($valid_sum ne '') {
+		die "Too many acceptable files for key $key\n";
+	    }
+	    $valid_sum = $m;
+	    $valid_file = ${ $md5sum_valid{$m} }[0];
+	}
+    }
+    if ($valid_sum eq '') {
+	die "No acceptable file for key $key\n";
+    }
+    else {
+	print "Repairing instances\n";
+	foreach $m (keys %md5sum_valid) {
+	    if ($m ne $valid_sum) {
+		foreach my $f (@{ $md5sum_valid{$m} }) {
+		    print "\tcp $valid_file $f\n";
+		    system("cp $valid_file $f");
+		    if ($? >> 8 != 0) {
+			if ($existance[0] == 1) {
+			    # We can fix this with culls and replications.
+			    print "Attempting to fix this.\n";
+			    print("neb-replicate $key\n");
+			    system("neb-replicate $key");
+			    if ($? >> 8 == 0) {
+				my $bad_volume = (split /\//, $f)[2];
+				print("neb-cull --volume $bad_volume $key\n");
+				system("neb-cull --volume $bad_volume $key");
+			    }
+			    else {
+				die "Failed to copy file $valid_file to $f, and subsequent replication also failed!\n";
+			    }
+			}
+			else {
+			    die "Failed to copy file $valid_file to $f!\n";
+			}
+		    }
+		}
+	    }
+	}
+    }
+}
+
+# if ($N_files > $user_copies) {
+#     my @split = split /\//, $last_f;
+#     my $volume = $split[2];
+#     print "Culling extra copy: neb-cull --volume $volume $key\n";
+#     system("neb-cull --volume $volume $key");
+# }
+
+
+
+__END__
+
+=pod
+
+=head1 NAME
+
+neb-repair - tool to check and repair instances
+
+=head1 SYNOPSIS
+
+    neb-repair [--server <URL>] <key> 
+
+=head1 DESCRIPTION
+
+This program repairs instances and corrects instance counts for a nebulous key.  
+
+=head1 OPTIONS
+
+=over 4
+
+=item * --server|-s <URL>
+
+URL of the Nebulous server to connect to.
+
+Optional if the appropriate environment variable is set.
+
+=back
+
+=head1 ENVIRONMENT
+
+These environment variables may be used in place of the specified command line
+options.  All command line option will override the corresponding environment
+value.
+
+=over 4
+
+=item * C<NEB_SERVER>
+
+Equivalent to --server|-s
+
+=back
+
+=head1 CREDITS
+
+
+
+=head1 SUPPORT
+
+Please contact the author directly via e-mail.
+
+=head1 AUTHOR
+
+Chris Waters
+
+=head1 COPYRIGHT
+
+Copyright (C) 2007-2008  Chris Waters
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place - Suite 330, Boston, MA  02111-1307, USA.
+
+The full text of the license can be found in the L<perlgpl> Pod as supplied
+with Perl 5.8.1 and later.
+
+=head1 SEE ALSO
+
+L<Nebulous::Server>, L<neb-initdb>, L<neb-addvol>, L<nebdiskd>, L<neb-df>,
+L<neb-touch>, C<regex(7)>
+
+=cut
Index: branches/meh_branches/ppstack_test/Ohana/src/addstar/doc/wise.catalog.txt
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/addstar/doc/wise.catalog.txt	(revision 33415)
+++ branches/meh_branches/ppstack_test/Ohana/src/addstar/doc/wise.catalog.txt	(revision 33415)
@@ -0,0 +1,276 @@
+designation     %20s     ----   no       Sexagesimal, equatorial position-based source name in the form: hhmmss.ss+ddmmss.s. The full naming convention for WISE Preliminary Release Catalog sources has the form "WISEP  Jhhmmss.ss+ddmmss.s," where "WISEP" indicates the source is from the Preliminary Release Source Catalog, and "J" indicates the position is J2000. The "WISEP" acronym is not listed explicitly in the designation column.
+ra              %10.6f  deg     no       J2000 right ascension with respect to the 2MASS PSC reference frame     
+dec             %10.6f  deg     no       J2000 declination with respect to the 2MASS PSC reference frame.
+sigra           %4.2f   arcsec  no      One-sigma uncertainty in right ascension coordinate.     
+sigdec          %4.2f   arcsec  no      One-sigma uncertainty in declination coordinate.
+sigradec        %3d     deg     no      The co-sigma of the equatorial position uncertainties, sig_ra, sig_dec (ÏÎ±, ÏÎŽ). The co-sigma is related to the covariance between the position errors VÎ±ÎŽ   by the formula VÎ±ÎŽ = ÏÎ±ÎŽ|ÏÎ±ÎŽ|. This maintains the sign information for the correlation, since ÏÎ±ÎŽ may be negative. It is more natural to carry the co-sigma along with the other uncertainties instead of the covariance because the former is in the same units as the other uncertainties.         
+glon            %7.3f   deg     no      Galactic longitude. CAUTION: This coordinate should not be used as an astrometric reference.
+glat            %7.3f   deg     no      Galactic latitude. CAUTION: This coordinate should not be used as an astrometric reference.
+elon            %7.3f   deg     no      Ecliptic longitude. CAUTION: This coordinate should not be used as an astrometric reference.
+elat            %7.3f   deg     no      Ecliptic latitude. CAUTION: This coordinate should not be used as an astrometric reference.
+wx              %7.3f   pix     no      The x-pixel coordinate of this source on the Atlas Image.
+wy              %7.3f   pix     no      The y-pixel coordinate of this source on the Atlas Image.
+cntr            %19d    ----    no      Unique identification number for the Catalog source. This number is formed entirely from the source_id, which is in turn formed from the coadd_id and source number src. On average, sources with cntr values close to each other are also close to each other on the sky, except at Tile boundaries. Cntr ordering thus provides a useful mapping of the WISE Catalog on storage media.
+source_id       %28s    ----    no      Unique source ID, formed from a combination of the Atlas Tile ID, coadd_id, and sequential extracted source number, src, within the Tile. The source_id string has the form RRRRsDDD_[trev]-IIIIII where:
+coadd_id        %20s    ----    no      Atlas Tile identifier from which source was extracted. The identifier has the general form: RRRRsDDD_[trev], where
+src             %6d     ----    no      Sequential number of this source extraction in the Atlas Tile from which this source was extracted, in approximate descending order of W1 source brightness.     
+w1mpro          %7.3f   mag     yes     W1 magnitude measured with profile-fitting photometry, or the magnitude of the 95% confidence brightness upper limit if the W1 flux measurement has SNR<2. This column is null if the source is nominally detected in W1, but no useful brightness estimate could be made.       
+w1sigmpro       %5.3f   mag     yes     W1 profile-fit photometric measurement uncertainty in mag units. This column is null if the W1 profile-fit magnitude is a 95% confidence upper limit or if the source is not measurable.         
+w1snr           %7.1f   ----    yes     W1 profile-fit measurement signal-to-noise ratio. This value is the ratio of the flux (w1flux) to flux uncertainty (w1sigflux)in the W1 profile-fit photometry measurement. This column is null if w1flux is negative, or if w1flux or w1sigflux are null.       
+w1rchi2         %9.3e   ----    yes     Reduced Ï2 of the W1 profile-fit photometry measurement. This column is null if the W1 magnitude is a 95% confidence upper limit (i.e. the source is not detected).      
+w2mpro          %7.3f   mag     yes     W2 magnitude measured with profile-fitting photometry, or the magnitude of the 95% confidence brightness upper limit if the W2 flux measurement has SNR<2. This column is null if the source is nominally detected in W1, but no useful brightness estimate could be made.       
+w2sigmpro       %5.3f   mag     yes     W2 profile-fit photometric measurement uncertainty in mag units. This column is null if the W2 profile-fit magnitude is a 95% confidence upper limit or if the source is not measurable.         
+w2snr           %7.1f   ----    yes     W2 profile-fit measurement signal-to-noise ratio. This value is the ratio of the flux (w2flux to flux uncertainty (w2sigflux)in the W2 profile-fit photometry measurement. This column is null if w2flux is negative, or if w2flux or w2sigflux are null.        
+w2rchi2         %9.3e   ----    yes     Reduced Ï2 of the W2 profile-fit photometry measurement. This column is null if the W2 magnitude is a 95% confidence upper limit (i.e. the source is not detected).      
+w3mpro          %7.3f   mag     yes     W3 magnitude measured with profile-fitting photometry, or the magnitude of the 95% confidence brightness upper limit if the W3 flux measurement has SNR<2. This column is null if the source is nominally detected in W3, but no useful brightness estimate could be made.       
+w3sigmpro       %5.3f   mag     yes     W3 profile-fit photometric measurement uncertainty in mag units. This column is null if the W3 profile-fit magnitude is a 95% confidence upper limit or if the source is not measurable.         
+w3snr           %7.1f   ----    yes     W3 profile-fit measurement signal-to-noise ratio. This value is the ratio of the flux (w3flux to flux uncertainty (w3sigflux)in the W3 profile-fit photometry measurement. This column is null if w3flux is negative, or if w3flux or w3sigflux are null.        
+w3rchi2         %9.3e   ----    yes     Reduced Ï2 of the W3 profile-fit photometry measurement. This column is null if the W3 magnitude is a 95% confidence upper limit (i.e. the source is not detected).      
+w4mpro          %7.3f   mag     yes     W4 magnitude measured with profile-fitting photometry, or the magnitude of the 95% confidence brightness upper limit if the W4 flux measurement has SNR<2. This column is null if the source is nominally detected in W4, but no useful brightness estimate could be made.       
+w4sigmpro       %5.3f   mag     yes     W4 profile-fit photometric measurement uncertainty in mag units. This column is null if the W4 profile-fit magnitude is a 95% confidence upper limit or if the source is not measurable.         
+w4snr           %7.1f   ----    yes     W4 profile-fit measurement signal-to-noise ratio. This value is the ratio of the flux (w4flux to flux uncertainty (w4sigflux)in the W4 photometry measurement. This column is null if w4flux is negative, or if w4flux or w4sigflux are null.    
+w4rchi2         %9.3e   ----    yes     Reduced Ï2 of the W4 profile-fit photometry measurement. This column is null if the W4 magnitude is a 95% confidence upper limit (i.e. the source is not detected).      
+rchi2           %9.3f   ----    yes     Combine reduced Ï2 of the profile-fit photometry measurement in all bands.       
+nb              %1d     ----    yes     Number of PSF components used simultaneously in the profile-fitting for this source. This number includes the source itself, so the minimum value of nb is "1". Nb is greater than "1" when the source is fit concurrently with other nearby detections (passive deblending), or when a single source is split into two components during the fitting process (active deblending).       
+na              %1d     ----    yes     Active deblending flag. Indicates if a single detection was split into multiple sources in the process of profile-fitting. 
+w1sat           %5.3f   ----    yes     Saturated pixel fraction, W1. The fraction of all pixels within the profile-fitting area in the stack of single-exposure images used to characterize this source that are flagged as saturated. A value larger than 0.0 indicates one or more pixels of saturation. Saturation begins to occur for point sources brighter than [W1]~8 mag. Saturation may occur in fainter sources because of a charged particle strike.         
+w2sat           %5.3f   ----    yes     Saturated pixel fraction, W2. The fraction of all pixels within the profile-fitting area in the stack of single-exposure images used to characterize this source that are flagged as saturated. A value larger than 0.0 indicates one or more pixels of saturation. Saturation begins to occur for point sources brighter than [W2]~7 mag. Saturation may occur in fainter sources because of a charged particle strike.         
+w3sat           %5.3f   ----    yes     Saturated pixel fraction, W3. The fraction of all pixels within the profile-fitting area source in the stack of single-exposure images used to characterize this source that are flagged as saturated. A value larger than 0.0 indicates one or more pixels of saturation. Saturation begins to occur for point sources brighter than [W3]~4 mag. Saturation may occur in fainter sources because of a charged particle strike.  
+w4sat           %5.3f   ----    yes     Saturated pixel fraction, W4. The fraction of all pixels within the profile-fitting area in the stack of single-exposure images used to characterize this source that are flagged as saturated. A value larger than 0.0 indicates one or more pixels of saturation. Saturation begins to occur for point sources brighter than [W4]~0 mag. Saturation may occur in fainter sources because of a charged particle strike.         
+satnum          %4s     ----    no      Minimum sample at which saturation occurs in each band. Four character string, one character per band, that indicates the minimum SUTR sample in which any pixel in the profile-fitting area in all of the single-exposure images used to characterize this source was flagged as having reached the saturation level in the on-board WISE payload processing. If no pixels in a given band are flagged as saturated, the value for that band is "0".    
+cc_flags        %4s     ----    no      Contamination and confusion flag. Four character strong, one character per band [W1/W2/W3/W4], that indicates that the photometry and/or position measurements of a source may be contaminated or biased due to proximity to an image artifact.
+ext_flg         %1d     ----    no      Extended source flag. This flag indicates whether or not the morphology of a source is consistent with the WISE point spread function in any band.
+var_flg         %4s     ----    yes     Variability flag. The variability flag is a four-character string, one character per band, in which the value for each band is related to the probability that the source flux measured on the individual WISE exposures was not constant with time.
+ph_qual         %4s     ----    no      
+
+det_bit         %3d     ----    no      Bit-encoded integer indicating bands in which a source has a w?snr>2 detection. For example, a source detected in W1 only has det_bit=1 (binary 0001). A source detected in W4 only has det_bit=8 (binary 1000). A source detected in all four bands has det_bit=15 (binary 1111).       
+w1nm            %6d     ----    yes     Integer frame detection count. This column gives the number of individual 7.7s exposures on which this source was detected with SNR>3 in the W1 profile-fit measurement. This number can be zero for sources that are well-detected on the coadded Atlas Image, but too faint for detection on the single exposures.     
+w1m             %6d     ----    yes     Integer frame coverage. This column gives the number of individual 7.7s W1 exposures on which a profile-fit measurement of this source was possible.
+w2nm            %6d     ----    yes     Integer frame detection count. This column gives the number of individual 7.7s exposures on which this source was detected with SNR>3 in the W2 profile-fit measurement. This number can be zero for sources that are well-detected on the coadded Atlas Image, but too faint for detection on the single exposures.     
+w2m             %6d     ----    yes     Integer frame coverage. This column gives the number of individual 8.8s W2 exposures on which a profile-fit measurement of this source was possible.
+
+w3nm            %6d     ----    yes     Integer frame detection count. This column gives the number of individual 8.8s exposures on which this source was detected with SNR>3 in the W3 profile-fit measurement. This number can be zero for sources that are well-detected on the coadded Atlas Image, but too faint for detection on the single exposures.     
+w3m             %6d     ----    yes     Integer frame coverage. This column gives the number of individual 7.7s W3 exposures on which a profile-fit measurement of this source was possible.
+w4nm            %6d     ----    yes     Integer frame detection count. This column gives the number of individual 8.8s exposures on which this source was detected with SNR>3 in the W4 profile-fit measurement. This number can be zero for sources that are well-detected on the coadded Atlas Image, but too faint for detection on the single exposures.     
+w4m             %6d     ----    yes     Integer frame coverage. This column gives the number of individual 8.8s W4 exposures on which a profile-fit measurement of this source was possible.
+w1cov           %7.3f   ----    yes     Mean pixel coverage in W1. This column gives the mean pixel value from the W1 Atlas Tile Coverage Map within the "standard" aperture, a circular area with a radius of 8.25" centered on the position of this source.
+
+w2cov           %7.3f   ----    yes     Mean pixel coverage in W2. This column gives the mean pixel value from the W2 Atlas Tile Coverage Map within the "standard" aperture, a circular area with a radius of 8.25" centered on the position of this source.
+w3cov           %7.3f   ----    yes     Mean pixel coverage in W3. This column gives the mean pixel value from the W3 Atlas Tile Coverage Map within the "standard" aperture, a circular area with a radius of 8.25" centered on the position of this source.
+w4cov           %7.3f   ----    yes     Mean pixel coverage in W4. This column gives the mean pixel value from the W4 Atlas Tile Coverage Map within the "standard" aperture, a circular area with a radius of 17.0" centered on the position of this source.
+w1frl           %5.3f   ----    yes     Fraction of pixels affected by latents. This column gives the fraction of all W1 pixels in the stack of individual W1 exposures used to characterize this source that may be affected by the presence of a latent image produced by a bright source. This number is computed by counting the number of pixels in the single exposure Bit Mask images with value "25" that are present within the profile-fitting area, a circular region with radius of 7.25", centered on the position of this source, and dividing by the total number of pixels in the same area that are available for measurement.  
+w2frl           %5.3f   ----    yes     Fraction of pixels affected by latents. This column gives the fraction of all W2 pixels in the stack of individual W2 exposures used to characterize this source that may be affected by the presence of a latent image produced by a bright source. This number is computed by counting the number of pixels in the single exposure Bit Mask images with value "25" that are present within the profile-fitting area, a circular region with radius of 7.25", centered on the position of this source, and dividing by the total number of pixels in the same area that are available for measurement.  
+
+w3frl           %5.3f   ----    yes     Fraction of pixels affected by latents. This column gives the fraction of all W3 pixels in the stack of individual W3 exposures used to characterize this source that may be affected by the presence of a latent image produced by a bright source. This number is computed by counting the number of pixels in the single exposure Bit Mask images with value "25" that are present within the profile-fitting area, a circular region with radius of 7.25", centered on the position of this source, and dividing by the total number of pixels in the same area that are available for measurement.  
+w4frl           %5.3f   ----    yes     Fraction of pixels affected by latents. This column gives the fraction of all W4 pixels in the stack of individual W4 exposures used to characterize this source that may be affected by the presence of a latent image produced by a bright source. This number is computed by counting the number of pixels in the single exposure Bit Mask images with value '25' that are present within the profile-fitting area, a circular region with radius of 15.0", centered on the position of this source, and dividing by the total number of pixels in the same area that are available for measurement.  
+w1cc_map        %3d     ----    no      Contamination and confusion map for this source in W1. This column contains the integer equivalent of the 9-bit binary number that specifies if the W1 measurement is believed to be contaminated by or a spurious detection of an image artifact. The elements of the binary array are:
+w1cc_map_str    %12s    ----    yes     Contamination and confusion string. This column is a character string that denotes all artifacts that may contaminate the W1 measurement of this source, in the priority order D,P,H,O. Lower-case letters denote cases where the source detection in this band is believed to be real but the measurement may be contaminated by the artifact. upper-case letters denote cases where the source detection in this band may be a spurious detection of an artifact.
+w2cc_map        %3d     ----    no      Contamination and confusion map for this source in W2. This column contains the integer equivalent of the 9-bit binary number that specifies if the W2 measurement is believed to be contaminated by or a spurious detection of an image artifact. The elements of the binary array are:
+
+w2cc_map_str    %12s    ----    yes     
+w3cc_map        %3d     ----    no      Contamination and confusion map for this source in W3. This column contains the integer equivalent of the 9-bit binary number that specifies if the W3 measurement is believed to be contaminated by or a spurious detection of an image artifact. The elements of the binary array are:
+w3cc_map_str    %12s    ----    yes     Contamination and confusion string. This column is a character string that denotes all artifacts that may contaminate the W3 measurement of this source, in the priority order D,P,H,O. Lower-case letters denote cases where the source detection in this band is believed to be real but the measurement may be contaminated by the artifact. Upper-case letters denote cases where the source detection in this band may be a spurious detection of an artifact.
+w4cc_map        %3d     ----    no      Contamination and confusion map for this source in W4. This column contains the integer equivalent of the 9-bit binary number that specifies if the W4 measurement is believed to be contaminated by or a spurious detection of an image artifact. The elements of the binary array are:
+w4cc_map_str    %12s    ----    yes     Contamination and confusion string. This column is a character string that denotes all artifacts that may contaminate the W4 measurement of this source, in the priority order D,P,H,O. Lower-case letters denote cases where the source detection in this band is believed to be real but the measurement may be contaminated by the artifact. Upper-case letters denote cases where the source detection in this band may be a spurious detection of an artifact.
+- skip 25 felds
+
+w1flux          %9.3f   dn      yes     The "raw" W1 source flux measured in profile-fit photometry in units of digital numbers. This value may be negative. This column is null if no useful profile-fit measurement of the source is possible because of masked or otherwise unusable pixels.  
+w1sigflux       %9.3f   dn      yes     Uncertainty in the "raw" W1 source flux measurement in profile-fit photometry in units of digital numbers. This column is null if no useful profile-fit measurement of the source is possible because of masked or otherwise unusable pixels.    
+w1sky           %9.3f   dn      yes      The trimmed average of the W1 sky background value in digital numbers measured in an annulus with an inner radius of 50" and outer radius of 70". Both profile-fit and aperture photometry source brightness measurements are made relative to this sky background value.
+w1sigsk         %7.3f   dn      yes     The uncertainty in the W1 sky background value in units of digital numbers measured in an annulus with an inner radius of 50" and outer radius of 70".   
+w1conf          %9.3f   dn      yes     Estimated confusion noise in the W1 sky background annulus, in digital numbers. This number is the difference between the measured noise in the sky background w1sigsk and the noise measured in the same region on the Atlas Uncertainty Maps.  
+w2flux          %9.3f   dn      yes     The "raw" W2 source flux measured in profile-fit photometry in units of digital numbers. This value may be negative. This column is null if no useful profile-fit measurement of the source is possible because of masked or otherwise unusable pixels.  
+w2sigflux       %9.3f   dn      yes     Uncertainty in the "raw" W2 source flux measurement in profile-fit photometry in units of digital numbers. This column is null if no useful profile-fit measurement of the source is possible because of masked or otherwise unusable pixels.    
+w2sky           %9.3f   dn      yes      The trimmed average of the W2 sky background value in digital numbers measured in an annulus with an inner radius of 50" and outer radius of 70". Both profile-fit and aperture photometry source brightness measurements are made relative to this sky background value.
+w2sigsk         %7.3f   dn      yes     The uncertainty in the W2 sky background value in units of digital numbers measured in an annulus with an inner radius of 50" and outer radius of 70".   
+w2conf          %9.3f   dn      yes     Estimated confusion noise in the W2 sky background annulus, in digital numbers. This number is the difference between the measured noise in the sky background w2sigsk and the noise measured in the same region on the Atlas Uncertainty Maps.  
+w3flux          %9.3f   dn      yes     The "raw" W3 source flux measured in profile-fit photometry in units of digital numbers. This value may be negative. This column is null if no useful profile-fit measurement of the source is possible because of masked or otherwise unusable pixels.  
+w3sigflux       %9.3f   dn      yes     Uncertainty in the "raw" W3 source flux measurement in profile-fit photometry in units of digital numbers. This column is null if no useful profile-fit measurement of the source is possible because of masked or otherwise unusable pixels.    
+w3sky           %9.3f   dn      yes      The trimmed average of the W3 sky background value in digital numbers measured in an annulus with an inner radius of 50" and outer radius of 70". Both profile-fit and aperture photometry source brightness measurements are made relative to this sky background value.
+w3sigsk         %7.3f   dn      yes     The uncertainty in the W3 sky background value in units of digital numbers measured in an annulus with an inner radius of 50" and outer radius of 70".   
+w3conf          %9.3f   dn      yes     Estimated confusion noise in the W3 sky background annulus, in digital numbers. This number is the difference between the measured noise in the sky background w3sigsk and the noise measured in the same region on the Atlas Uncertainty Maps.  
+w4flux          %9.3f   dn      yes     The "raw" W1 source flux measured in profile-fit photometry in units of digital numbers. This value may be negative. This column is null if no useful profile-fit measurement of the source is possible because of masked or otherwise unusable pixels.  
+w4sigflux       %9.3f   dn      yes     Uncertainty in the "raw" W1 source flux measurement in profile-fit photometry in units of digital numbers. This column is null if no useful profile-fit measurement of the source is possible because of masked or otherwise unusable pixels.    
+w4sky           %9.3f   dn      yes      The trimmed average of the W1 sky background value in digital numbers measured in an annulus with an inner radius of 50" and outer radius of 70". Both profile-fit and aperture photometry source brightness measurements are made relative to this sky background value.
+w4sigsk         %7.3f   dn      yes     The uncertainty in the W1 sky background value in units of digital numbers measured in an annulus with an inner radius of 50" and outer radius of 70".   
+w4conf          %9.3f   dn      yes     Estimated confusion noise in the W1 sky background annulus, in digital numbers. This number is the difference between the measured noise in the sky background w4sigsk and the noise measured in the same region on the Atlas Uncertainty Maps.  
+- skip 20 fields
+
+w1mag           %7.3f   mag     yes     W1 "standard" aperture magnitude. This is the curve-of-growth corrected source brightness measured in an 8.25" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". The curve-of-growth correction is given in w1mcor.
+w1sigm          %5.3f   mag     yes     Uncertainty in the W1 "standard" aperture magnitude.
+w1flg           %3d     ----    yes      W1 "standard" aperture measurement quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag value is an integer that is the combination of one or more of the following values that signify different conditions: 
+w1mcor          %6.3f   mag     yes      W1 aperture curve-of-growth correction, in magnitudes. This correction is subtracted from the nominal 8.25" aperture photometry brightness, w1mag_2, to give the "standard-aperture" magnitude.         
+
+w2mag           %7.3f   mag     yes     W2 "standard" aperture magnitude. This is the curve-of-growth corrected source brightness measured in an 8.25" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". The curve-of-growth correction is given in w2mcor.
+w2sigm          %5.3f   mag     yes     Uncertainty in the W2 "standard" aperture magnitude.
+w2flg           %3d     ----    yes      W2 "standard" aperture measurement quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag value is an integer that is the combination of one or more of the following values that signify different conditions: 
+w2mcor          %6.3f   mag     yes      W2 aperture curve-of-growth correction, in magnitudes. This correction is subtracted from the nominal 8.25" aperture photometry brightness, w2mag_2, to give the "standard-aperture" magnitude.         
+
+w3mag           %7.3f   mag     yes     W3 "standard" aperture magnitude. This is the curve-of-growth corrected source brightness measured in an 8.25" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". The curve-of-growth correction is given in w3mcor.
+w3sigm          %5.3f   mag     yes     Uncertainty in the W3 "standard" aperture magnitude.
+w3flg           %3d     ----    yes      W3 "standard" aperture measurement quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag value is an integer that is the combination of one or more of the following values that signify different conditions: 
+w3mcor          %6.3f   mag     yes      W3 aperture curve-of-growth correction, in magnitudes. This correction is subtracted from the nominal 8.25" aperture photometry brightness, w3mag_2, to give the "standard-aperture" magnitude.         
+
+w4mag           %7.3f   mag     yes     W4 "standard" aperture magnitude. This is the curve-of-growth corrected source brightness measured in a 16.5" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". The curve-of-growth correction is given in w4mcor.
+w4sigm          %5.3f   mag     yes     Uncertainty in the W4 "standard" aperture magnitude.
+w4flg           %3d     ----    yes      W4 "standard" aperture measurement quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag value is an integer that is the combination of one or more of the following values that signify different conditions: 
+w4mcor          %6.3f   mag     yes      W4 aperture curve-of-growth correction, in magnitudes. This correction is subtracted from the nominal 16.5" aperture photometry brightness, w4mag_2, to give the "standard-aperture" magnitude.         
+- 16 fields
+
+w1mag_1         %7.3f   mag     yes     W1 5.5" radius aperture magnitude. This is the calibrated source brightness measured in a 5.5" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w1sigm_1        %5.3f   mag     yes     Uncertainty in the W1 5.5" radius aperture magnitude.
+w1flg_1         %3d     ----    yes      W1 5.5" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w1mflg.         
+w2mag_1         %7.3f   mag     yes     W2 5.5" radius aperture magnitude. This is the calibrated source brightness measured in a 5.5" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w2sigm_1        %5.3f   mag     yes     Uncertainty in the W2 5.5" radius aperture magnitude.
+w2flg_1         %3d     ----    yes      W2 5.5" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w2mflg.         
+w3mag_1         %7.3f   mag     yes     W3 5.5" radius aperture magnitude. This is the calibrated source brightness measured in a 5.5" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w3sigm_1        %5.3f   mag     yes     Uncertainty in the W3 5.5" radius aperture magnitude.
+w3flg_1         %3d     ----    yes      W3 5.5" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w3mflg.         
+w4mag_1         %7.3f   mag     yes     W4 11.0" radius aperture magnitude. This is the calibrated source brightness measured in a 11.0" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w4sigm_1        %5.3f   mag     yes     Uncertainty in the W4 11.0" radius aperture magnitude.
+w4flg_1         %3d     ----    yes      W4 11.0" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w4mflg.        
+- 12 fields
+w1mag_2         %7.3f   mag     yes     W1 8.25" radius aperture magnitude. This is the calibrated source brightness measured in a 8.25" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w1sigm_2        %5.3f   mag     yes     Uncertainty in the W1 8.25" radius aperture magnitude.
+w1flg_2         %3d     ----    yes      W1 8.25" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w1mflg.        
+w2mag_2         %7.3f   mag     yes     W2 8.25" radius aperture magnitude. This is the calibrated source brightness measured in a 8.25" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w2sigm_2        %5.3f   mag     yes     Uncertainty in the W2 8.25" radius aperture magnitude.
+w2flg_2         %3d     ----    yes      W2 8.25" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w2mflg.        
+w3mag_2         %7.3f   mag     yes     W3 8.25" radius aperture magnitude. This is the calibrated source brightness measured in a 8.25" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w3sigm_2        %5.3f   mag     yes     Uncertainty in the W3 8.25" radius aperture magnitude.
+w3flg_2         %3d     ----    yes      W3 8.25" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w3mflg.        
+w4mag_2         %7.3f   mag     yes     W4 16.5" radius aperture magnitude. This is the calibrated source brightness measured in a 16.5" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w4sigm_2        %5.3f   mag     yes     Uncertainty in the W4 16.5" radius aperture magnitude.
+w4flg_2         %3d     ----    yes      W4 16.5" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w4mflg.        
+- 12 fields
+w1mag_3         %7.3f   mag     yes     W1 11.0" radius aperture magnitude. This is the calibrated source brightness measured in a 11.0" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w1sigm_3        %5.3f   mag     yes     Uncertainty in the W1 11.0" radius aperture magnitude.
+w1flg_3         %3d     ----    yes      W1 11.0" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w1mflg.        
+w2mag_3         %7.3f   mag     yes     W2 11.0" radius aperture magnitude. This is the calibrated source brightness measured in a 11.0" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w2sigm_3        %5.3f   mag     yes     Uncertainty in the W2 11.0" radius aperture magnitude.
+w2flg_3         %3d     ----    yes      W2 11.0" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w2mflg.        
+w3mag_3         %7.3f   mag     yes     W3 11.0" radius aperture magnitude. This is the calibrated source brightness measured in a 11.0" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w3sigm_3        %5.3f   mag     yes     Uncertainty in the W3 11.0" radius aperture magnitude.
+w3flg_3         %3d     ----    yes      W3 11.0" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w3mflg.        
+w4mag_3         %7.3f   mag     yes     W4 22.0" radius aperture magnitude. This is the calibrated source brightness measured in a 22.0" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w4sigm_3        %5.3f   mag     yes     Uncertainty in the W4 22.0" radius aperture magnitude.
+w4flg_3         %3d     ----    yes      W4 22.0" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w4mflg.        
+- 12 fields
+w1mag_4         %7.3f   mag     yes     W1 13.75" radius aperture magnitude. This is the calibrated source brightness measured in a 13.75" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w1sigm_4        %5.3f   mag     yes     Uncertainty in the W1 13.75" radius aperture magnitude.
+w1flg_4         %3d     ----    yes      W1 13.75" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w1mflg.       
+w2mag_4         %7.3f   mag     yes     W2 13.75" radius aperture magnitude. This is the calibrated source brightness measured in a 13.75" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w2sigm_4        %5.3f   mag     yes     Uncertainty in the W2 13.75" radius aperture magnitude.
+w2flg_4         %3d     ----    yes      W2 13.75" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w2mflg.       
+w3mag_4         %7.3f   mag     yes     W3 13.75" radius aperture magnitude. This is the calibrated source brightness measured in a 13.75" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w3sigm_4        %5.3f   mag     yes     Uncertainty in the W3 13.75" radius aperture magnitude.
+w3flg_4         %3d     ----    yes      W3 13.75" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w3mflg.       
+w4mag_4         %7.3f   mag     yes     W4 27.5" radius aperture magnitude. This is the calibrated source brightness measured in a 27.5" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w4sigm_4        %5.3f   mag     yes     Uncertainty in the W4 27.5" radius aperture magnitude.
+w4flg_4         %3d     ----    yes      W4 27.5" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w4mflg.        
+- 12 fields
+w1mag_5         %7.3f   mag     yes     W1 16.5" radius aperture magnitude. This is the calibrated source brightness measured in a 16.5" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w1sigm_5        %5.3f   mag     yes     Uncertainty in the W1 16.5" radius aperture magnitude.
+w1flg_5         %3d     ----    yes      W1 16.5" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w1mflg.        
+w2mag_5         %7.3f   mag     yes     W2 16.5" radius aperture magnitude. This is the calibrated source brightness measured in a 16.5" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w2sigm_5        %5.3f   mag     yes     Uncertainty in the W2 16.5" radius aperture magnitude.
+w2flg_5         %3d     ----    yes      W2 16.5" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w2mflg.        
+w3mag_5         %7.3f   mag     yes     W3 16.5" radius aperture magnitude. This is the calibrated source brightness measured in a 16.5" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w3sigm_5        %5.3f   mag     yes     Uncertainty in the W3 16.5" radius aperture magnitude.
+w3flg_5         %3d     ----    yes      W3 16.5" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w3mflg.        
+w4mag_5         %7.3f   mag     yes     W4 33.0" radius aperture magnitude. This is the calibrated source brightness measured in a 33.0" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w4sigm_5        %5.3f   mag     yes     Uncertainty in the W4 33.0" radius aperture magnitude.
+w4flg_5         %3d     ----    yes      W4 33.0" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w4mflg.        
+- 12 fields
+w1mag_6         %7.3f   mag     yes     W1 19.25" radius aperture magnitude. This is the calibrated source brightness measured in a 19.25" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w1sigm_6        %5.3f   mag     yes     Uncertainty in the W1 19.25" radius aperture magnitude.
+w1flg_6         %3d     ----    yes      W1 19.25" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w1mflg.       
+w2mag_6         %7.3f   mag     yes     W2 19.25" radius aperture magnitude. This is the calibrated source brightness measured in a 19.25" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w2sigm_6        %5.3f   mag     yes     Uncertainty in the W2 19.25" radius aperture magnitude.
+w2flg_6         %3d     ----    yes      W2 19.25" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w2mflg.       
+w3mag_6         %7.3f   mag     yes     W3 19.25" radius aperture magnitude. This is the calibrated source brightness measured in a 19.25" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w3sigm_6        %5.3f   mag     yes     Uncertainty in the W3 19.25" radius aperture magnitude.
+w3flg_6         %3d     ----    yes      W3 19.25" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w3mflg.       
+w4mag_6         %7.3f   mag     yes     W4 38.5" radius aperture magnitude. This is the calibrated source brightness measured in a 38.5" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w4sigm_6        %5.3f   mag     yes     Uncertainty in the W4 38.5" radius aperture magnitude.
+w4flg_6         %3d     ----    yes      W4 38.5" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w4mflg.        
+- 12 fields
+w1mag_7         %7.3f   mag     yes     W1 22.0" radius aperture magnitude. This is the calibrated source brightness measured in a 22.0" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w1sigm_7        %5.3f   mag     yes     Uncertainty in the W1 22.0" radius aperture magnitude.
+w1flg_7         %3d     ----    yes      W1 22.0" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w1mflg.        
+w2mag_7         %7.3f   mag     yes     W2 22.0" radius aperture magnitude. This is the calibrated source brightness measured in a 22.0" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w2sigm_7        %5.3f   mag     yes     Uncertainty in the W2 22.0" radius aperture magnitude.
+w2flg_7         %3d     ----    yes      W2 22.0" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w2mflg.        
+w3mag_7         %7.3f   mag     yes     W3 22.0" radius aperture magnitude. This is the calibrated source brightness measured in a 22.0" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w3sigm_7        %5.3f   mag     yes     Uncertainty in the W3 22.0" radius aperture magnitude.
+w3flg_7         %3d     ----    yes      W3 22.0" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w3mflg.        
+w4mag_7         %7.3f   mag     yes     W4 44.0" radius aperture magnitude. This is the calibrated source brightness measured in a 44.0" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w4sigm_7        %5.3f   mag     yes     Uncertainty in the W4 44.0" radius aperture magnitude.
+w4flg_7         %3d     ----    yes      W4 44.0" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w4mflg.        
+- 12 fields
+w1mag_8         %7.3f   mag     yes     W1 24.75" radius aperture magnitude. This is the calibrated source brightness measured in a 24.75" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w1sigm_8        %5.3f   mag     yes     Uncertainty in the W1 24.75" radius aperture magnitude.
+w1flg_8         %3d     ----    yes      W1 24.75" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w1mflg.       
+w2mag_8         %7.3f   mag     yes     W2 24.75" radius aperture magnitude. This is the calibrated source brightness measured in a 24.75" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w2sigm_8        %5.3f   mag     yes     Uncertainty in the W2 24.75" radius aperture magnitude.
+w2flg_8         %3d     ----    yes      W2 24.75" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w2mflg.       
+w3mag_8         %7.3f   mag     yes     W3 24.75" radius aperture magnitude. This is the calibrated source brightness measured in a 24.75" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w3sigm_8        %5.3f   mag     yes     Uncertainty in the W3 24.75" radius aperture magnitude.
+w3flg_8         %3d     ----    yes      W3 24.75" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w3mflg.       
+w4mag_8         %7.3f   mag     yes     W4 49.5" radius aperture magnitude. This is the calibrated source brightness measured in a 49.5" radius circular aperture centered on the source position on the Atlas Image. If the source is not detected in the aperture measurement, this is the 95% confidence upper limit to the brightness. The background sky reference level is measured in an annular region with inner radius of 50" and outer radius of 70". No curve-of-growth correction has been applied.
+w4sigm_8        %5.3f   mag     yes     Uncertainty in the W4 49.5" radius aperture magnitude.
+w4flg_8         %3d     ----    yes      W4 49.5" radius aperture magnitude quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the standard aperture photometry quality flag, w4mflg.        
+- 12 fields
+w1magp          %6.3f   mag     yes     Inverse-variance-weighted mean W1 magnitude computed from profile-fit measurements on the w1m individual frames covering this source. This differs from w1mpro in that it is computed by combining the profile-fit measurements from individual frames, whereas w1mpro is computed by fitting all W1 frames simultaneously and incorporating a robust error model.
+w1sigp1         %6.3f   mag     yes     Standard deviation of the population of W1 fluxes measured on the w1m individual frames covering this source, in magnitudes. This provides a measure of the characteristic uncertainty of the measurement of this source on individual frames.
+w1sigp2         %6.3f   mag     yes     Standard deviation of the mean of the distribution of W1 fluxes (w1magp) computed from profile-fit measurements on the w1m individual frames covering this source, in magnitudes. This is equivalent to w1sigp1/sqrt(w1m).
+w2magp          %6.3f   mag     yes     Inverse-variance-weighted mean W2 magnitude computed from profile-fit measurements on the w2m individual frames covering this source. This differs from w2mpro in that it is computed by combining the profile-fit measurements from individual frames, whereas w2mpro is computed by fitting all W2 frames simultaneously and incorporating a robust error model.
+w2sigp1         %6.3f   mag     yes     Standard deviation of the population of W2 fluxes measured on the w2m individual frames covering this source, in magnitudes. This provides a measure of the characteristic uncertainty of the measurement of this source on individual frames.
+w2sigp2         %6.3f   mag     yes     Standard deviation of the mean of the distribution of W2 fluxes (w2magp) computed from profile-fit measurements on the w2m individual frames covering this source, in magnitudes. This is equivalent to w2sigp1/sqrt(w2m).
+w3magp          %6.3f   mag     yes     Inverse-variance-weighted mean W3 magnitude computed from profile-fit measurements on the w3m individual frames covering this source. This differs from w3mpro in that it is computed by combining the profile-fit measurements from individual frames, whereas w3mpro is computed by fitting all W3 frames simultaneously and incorporating a robust error model.
+w3sigp1         %6.3f   mag     yes     Standard deviation of the population of W3 fluxes measured on the w3m individual frames covering this source, in magnitudes. This provides a measure of the characteristic uncertainty of the measurement of this source on individual frames.
+w3sigp2         %6.3f   mag     yes     Standard deviation of the mean of the distribution of W3 fluxes (w3magp) computed from profile-fit measurements on the w3m individual frames covering this source, in magnitudes. This is equivalent to w3sigp1/sqrt(w3m).
+w4magp          %6.3f   mag     yes     Inverse-variance-weighted mean W4 magnitude computed from profile-fit measurements on the w4m individual frames covering this source. This differs from w4mpro in that it is computed by combining the profile-fit measurements from individual frames, whereas w4mpro is computed by fitting all W4 frames simultaneously and incorporating a robust error model.
+w4sigp1         %6.3f   mag     yes     Standard deviation of the population of W4 fluxes measured on the w4m individual frames covering this source, in magnitudes. This provides a measure of the characteristic uncertainty of the measurement of this source on individual frames.
+w4sigp2         %6.3f   mag     yes     Standard deviation of the mean of the distribution of W4 fluxes (w4magp) computed from profile-fit measurements on the w4m individual frames covering this source, in magnitudes. This is equivalent to w4sigp1/sqrt(w4m).
+- 12 fields
+xscprox         %8.2f   arcsec  yes     2MASS Extended Source Catalog (XSC) proximity. This column gives the distance between the WISE source position and the position of a nearby 2MASS XSC source, if the separation is less than 1.1 times the Ks isophotal radius size of the XSC source. This identifies WISE sources that are identically the 2MASS XSC source as well as WISE sources that are fragments of large galaxies. This column is "null" if there is no 2MASS XSC source in proximity to the WISE source.
+w1rsemi         %8.2f   arcsec  yes     Semi-major axis of the elliptical aperture used to measure source in W1.         
+w1ba            %4.2f   arcsec  yes     Axis ratio (b/a) of the elliptical aperture used to measure source in W1.        
+w1pa            %7.2f   deg     yes     Position angle (degrees E of N) of the elliptical aperture major axis used to measure source in W1.      
+w1gmag          %7.3f   mag     yes     W1 magnitude of source measured in the elliptical aperture described by w1rsemi, w1ba, and w1pa.         
+w1gerr          %5.3f   mag     yes     Uncertainty in the W1 magnitude of source measured in elliptical aperture.       
+w1gflg          %1d     ----    yes     W1 elliptical aperture measurement quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the "standard" aperture photometry quality flag, w1mflg.       
+w2rsemi         %8.2f   arcsec  yes     Semi-major axis of the elliptical aperture used to measure source in W2.         
+w2ba            %4.2f   arcsec  yes     Axis ratio (b/a) of the elliptical aperture used to measure source in W2.        
+w2pa            %7.2f   deg     yes     Position angle (degrees E of N) of the elliptical aperture major axis used to measure source in W2.      
+w2gmag          %7.3f   mag     yes     W2 magnitude of source measured in the elliptical aperture described by w2rsemi, w2ba, and w2pa.         
+w2gerr          %5.3f   mag     yes     Uncertainty in the W2 magnitude of source measured in elliptical aperture.       
+w2gflg          %1d     ----    yes     W2 elliptical aperture measurement quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the "standard" aperture photometry quality flag, w2mflg.       
+w3rsemi         %8.2f   arcsec  yes     Semi-major axis of the elliptical aperture used to measure source in W3.         
+w3ba            %4.2f   arcsec  yes     Axis ratio (b/a) of the elliptical aperture used to measure source in W3.        
+w3pa            %7.2f   deg     yes     Position angle (degrees E of N) of the elliptical aperture major axis used to measure source in W3.      
+w3gmag          %7.3f   mag     yes     W3 magnitude of source measured in the elliptical aperture described by w3rsemi, w3ba, and w3pa.         
+w3gerr          %5.3f   mag     yes     Uncertainty in the W3 magnitude of source measured in elliptical aperture.       
+w3gflg          %1d     ----    yes     W3 elliptical aperture measurement quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the "standard" aperture photometry quality flag, w3mflg.       
+w4rsemi         %8.2f   arcsec  yes     Semi-major axis of the elliptical aperture used to measure source in W4.         
+w4ba            %4.2f   arcsec  yes     Axis ratio (b/a) of the elliptical aperture used to measure source in W4.        
+w4pa            %7.2f   deg     yes     Position angle (degrees E of N) of the elliptical aperture major axis used to measure source in W4.      
+w4gmag          %7.3f   mag     yes     W4 magnitude of source measured in the elliptical aperture described by w4rsemi, w4ba, and w4pa.         
+w4gerr          %5.3f   mag     yes     Uncertainty in the W4 magnitude of source measured in elliptical aperture.       
+w4gflg          %1d     ----    yes     W4 elliptical aperture measurement quality flag. This flag indicates if one or more image pixels in the measurement aperture for this band is confused with nearby objects, is contaminated by saturated or otherwise unusable pixels, or is an upper limit. The flag values are as described for the "standard" aperture photometry quality flag, w4mflg.       
+- 25 fields
+tmass_key       %10d    ----    yes     2MASS PSC association. Unique identifier of the closest source in the 2MASS Point Source Catalog (PSC) that falls within 3" of the position of this WISE source. This is equivalent to the pts_key in the 2MASS PSC entry. This column is "null" if there is no 2MASS PSC source within 3" of the WISE source position. The number of 2MASS PSC sources within 3" of the WISE position is given in n_2mass
+r_2mass         %7.3f   arcsec  yes     Distance separating the positions of the WISE source and associated 2MASS PSC source within 3". This column is "null" if there is no associated 2MASS PSC source.        
+pa_2mass        %6.1f   deg     yes     Position angle (degrees E of N) of the vector from the WISE source to the associated 2MASS PSC source. This column is "null" if there is no associated 2MASS PSC source.         
+n_2mass         %3d     ----    yes     The number of 2MASS PSC entries found within a 3" radius of the WISE source position. If more than one 2MASS PSC falls within 3" of the WISE position, the closest 2MASS PSC entry is listed. This column is "null" if there is no associated 2MASS PSC source.  
+j_m_2mass       %7.3f   mag     yes     2MASS J-band magnitude or magnitude upper limit of the associated 2MASS PSC source. This column is "null" if there is no associated 2MASS PSC source or if the 2MASS PSC J-band magnitude entry is "null".       
+j_msig_2mass    %5.3f   mag     yes     2MASS J-band corrected photometric uncertainty of the associated 2MASS PSC source. This column is "null" if there is no associated 2MASS PSC source or if the 2MASS PSC J-band uncertainty entry is "null".      
+h_m_2mass       %7.3f   mag     yes     2MASS H-band magnitude or magnitude upper limit of the associated 2MASS PSC source. This column is "null" if there is no associated 2MASS PSC source or if the 2MASS PSC H-band magnitude entry is "null".       
+h_msig_2mass    %5.3f   mag     yes     2MASS H-band corrected photometric uncertainty of the associated 2MASS PSC source. This column is "null" if there is no associated 2MASS PSC source or if the 2MASS PSC H-band uncertainty entry is "null".      
+k_m_2mass       %7.3f   mag     yes     2MASS Ks-band magnitude of the associated 2MASS PSC source. This column is "null" if there is no associated 2MASS PSC source or if the 2MASS PSC Ks-band magnitude entry is "null".      
+k_msig_2mass    %5.3f   mag     yes     2MASS Ks-band corrected photometric uncertainty of the associated 2MASS PSC source. This column is "null" if there is no associated 2MASS PSC source or if the 2MASS PSC Ks-band uncertainty entry is "null".    
+x               %19.16f ----    no      Unit sphere position x value     
+y               %19.16f ----    no      Unit sphere position y value     
+z               %19.16f ----    no      Unit sphere position z value     
+spt_ind         %9d     ----    no      HTM spatial index key    
Index: branches/meh_branches/ppstack_test/Ohana/src/addstar/doc/wise.sample.dat
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/addstar/doc/wise.sample.dat	(revision 33415)
+++ branches/meh_branches/ppstack_test/Ohana/src/addstar/doc/wise.sample.dat	(revision 33415)
@@ -0,0 +1,3 @@
+J070019.93+432212.0|105.0830765|43.3700066|0.0714|0.5048|-0.0096|173.4548334|19.7690989|101.6541116|20.53902|73.341|623.355|1040143901111000001|1040p439_aa11-000001|1040p439_aa11|1|7.149|0.028|38.9|0.869199991|7.294|0.023|48.1|0.935500026|7.13|0.02|54.8|0.841700017|7.115|0.099|11.0|1.074|0.926599979|1|0|0.142|0.0|0.0|0.0|5000|0000|0|1110|AAAA|15|12|12|12|12|12|12|7|12|10.898|12.111|11.879|12.129|0.0|0.0|0.0|0.0|0||0||0||0||219050.0|5638.0|17.205|3.254|0.0|76247.0|1585.40002|45.53|4.079|0.0|14058.0|256.390015|1881.719|22.688|0.0|225.919998|20.566|736.969|10.674|2.705|7.67|0.005|16|0.222|7.313|0.005|0|0.28|7.118|0.008|0|0.665|7.106|0.162|0|0.616|8.338|0.006|16|8.026|0.006|0|8.369|0.009|0|8.211|0.17|0|7.892|0.005|16|7.593|0.005|0|7.783|0.008|0|7.722|0.162|0|7.725|0.005|16|7.428|0.005|0|7.466|0.007|0|7.414|0.167|1|7.654|0.005|16|7.364|0.005|0|7.3|0.008|0|7.228|0.184|1|7.618|0.005|16|7.332|0.005|0|7.212|0.008|0|7.106|0.21|1|7.596|0.005|16|7.314|0.005|0|7.165|0.009|0|7.052|0.247|1|7.58|0.005|16|7.301|0.005|0|7.139|0.009|0|7.075|0.308|1|7.569|0.005|16|7.291|0.005|0|7.124|0.01|0|7.089|0.375|1|7.147|0.037|0.011|7.295|0.03|0.009|7.13|0.027|0.008|7.129|0.34|0.098||||||||||||||||||||||||||389882849|0.009|81.1|1|8.222|0.034|7.451|0.046|7.231|0.021|-0.18916233986809|0.70189102689035|0.686707066766055|223003002|
+J065913.94+431543.9|104.8081131|43.2621994|0.0973|0.5092|-0.012|173.493124|19.5447216|101.4544493|20.4099538|594.029|335.42|1040143901111000051|1040p439_aa11-000051|1040p439_aa11|51|12.893|0.025|42.9|0.7676|12.864|0.03|36.5|0.90079999|12.057||1.7|0.876299977|9.18|||0.902999997|0.857800007|2|0|0.0|0.0|0.0|0.0|0000|0000|0|1100|AAUU|3|13|13|13|13|0|13|0|13|12.628|12.656|12.771|12.717|0.0|0.0|0.0|0.0|0||0||0||0||1103.90002|25.7310009|16.008|3.288|0.0|451.25|12.3649998|44.678|4.026|0.0|69.2180023|40.5629997|1882.381|21.958|0.0|-4.68730021|16.8579998|737.179|10.956|4.392|12.905|0.015|0|0.222|12.907|0.03|0|0.28|11.954||32|0.665|9.494||32|0.616|13.497|0.015|0|13.611|0.031|0|13.002||32|9.6||32|13.127|0.015|0|13.187|0.03|0|12.619||32|10.11||32|12.997|0.016|1|13.048|0.033|1|12.304||32|8.742||32|12.944|0.019|1|13.016|0.04|1|12.015||32|8.286||32|12.914|0.021|1|13.013|0.047|1|11.719||32|7.817||32|12.889|0.024|1|13.007|0.055|1|11.464||32|7.427||32|12.868|0.027|1|12.996|0.063|1|11.264||32|7.166||32|12.853|0.031|1|12.99|0.072|1|11.11||32|7.018||32|12.893|0.023|0.006|12.865|0.068|0.019|12.952|2.605|0.723|||||||||||||||||||||||||||||389862912|0.091|-72.5|1|13.336|0.022|13.016|0.021|13.003|0.028|-0.186121697487002|0.704038676616684|0.685338059319913|223003031|
+J065944.84+432452.7|104.9368439|43.4146538|0.1091|0.5117|-0.0212|173.3730353|19.6833296|101.5360666|20.5717047|352.875|736.982|1040143901111000076|1040p439_aa11-000076|1040p439_aa11|76|13.239|0.025|42.8|1.56500006|13.299|0.037|29.1|1.04999995|11.895||1.8|1.01699996|8.748||0.8|0.957199991|1.13900006|1|0|0.0|0.0|0.0|0.0|0000|h000|0|0100|AAUU|3|11|11|11|11|0|11|0|11|11.257|11.534|10.912|11.402|0.0|0.0|0.0|0.0|8|h|0||0||0||802.429993|18.7660007|16.709|3.838|0.0|302.220001|10.3889999|45.571|5.071|0.0|81.6419983|46.5060005|1883.447|23.228|0.0|14.4219999|17.8859997|736.917|10.331|1.413|13.119|0.023|1|0.222|13.132|0.052|1|0.28|12.004|0.508|1|0.665|8.292||32|0.616|13.802|0.025|0|13.954|0.061|0|12.532||32|9.343||32|13.341|0.023|1|13.412|0.052|1|12.669|0.508|1|8.908||32|13.048|0.022|1|13.081|0.049|1|12.099|0.386|1|8.562||32|12.775|0.022|1|12.793|0.047|1|11.694|0.328|1|8.27||32|12.506|0.021|1|12.523|0.044|1|11.434|0.311|1|8.008||32|12.298|0.02|1|12.317|0.043|1|11.252|0.311|1|7.772||32|12.171|0.021|1|12.183|0.044|1|11.152|0.329|1|7.556||32|12.088|0.022|1|12.09|0.047|1|11.163|0.382|1|7.357||32|13.239|0.03|0.009|13.299|0.078|0.023|12.364|0.996|0.3|9.792|2.153|0.649||||||||||||||||||||||||||389863247|0.051|123.7|1|13.972|0.03|13.451|0.031|13.378|0.03|-0.187232347403342|0.701854285923945|0.687273314930839|223003031|
Index: branches/meh_branches/ppstack_test/Ohana/src/addstar/include/WISE.h
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/addstar/include/WISE.h	(revision 33415)
+++ branches/meh_branches/ppstack_test/Ohana/src/addstar/include/WISE.h	(revision 33415)
@@ -0,0 +1,55 @@
+
+/* structure for data on a catalog region */
+typedef struct {
+  char filename[256];
+  double RA[2];
+  int Nrec;
+} WISE_Region;
+
+typedef struct {
+  double Rmin, Rmax, Dmin, Dmax;
+  int index[20];
+  int Nindex;
+} WISE_Bands;
+
+typedef struct {
+  double R, D;
+  int offset;
+  int flag;
+} WISE_Stars;
+
+short WISE_W1, WISE_W2, WISE_W3, WISE_W4;
+
+SkyTable *get2mass_acc (SkyRegion *patch, char *path, char *accel);
+Stars    *get2mass_2DR_data (SkyRegion *region, char *filename, SkyRegion *patch, int photcode, int *nstars);
+Stars    *get2mass_AS_data (SkyRegion *region, char *filename, SkyRegion *patch, int phocode, int *nstars);
+Stars    *get2mass_AS_rawdata (SkyRegion *region, char *filename, SkyRegion *patch, int phocode, int *nstars);
+
+SkyTable *scan2mass_acc (char *path, char *accel);
+int       scan2mass_as_data (char *filename);
+
+char     *skipNbounds (char *line, char bound, int Nbound, int Nbyte);
+e_time    get2mass_time (char *ptr, int Nbound, int Nbyte);
+e_time    get2mass_date (char *ptr, int Nbound, int Nmax);
+
+int       load2mass_as_rawdata (SkyList *skytable, char *filename, AddstarClientOptions options);
+SkyTable *load2mass_acc (char *path, char *accel);
+int       get2mass_3star (Stars *star, char *line, int Nmax);
+int       load2mass_catalog (Catalog *catalog, Stars *stars, int Nstars);
+
+int       get2mass_setup (int photcode);
+int       get2mass_coords (char *line, double *R, double *D, int Nmax);
+int       get2mass_star (Stars *star, char *line, int Nmax);
+int       get2mass_3star (Stars *star, char *line, int Nmax);
+
+int get2mass_3star_full (Stars **star, char *line, int Nmax);
+char *next2MASSfield (char *line);
+int set2MASS_ph_qual (Stars *star, char qual);
+int set2MASS_rd_flag (Stars *star, char qual);
+int set2MASS_cc_flag (Stars *star, char qual);
+int set2MASS_bl_flag (Stars *star, char qual);
+int set2MASS_gal_flag (Stars *star, char qual);
+int set2MASS_mp_flag (Stars *star, char qual);
+int set2MASS_dup_flag (Stars *star, char qual);
+int set2MASS_use_flag (Stars *star, char qual);
+int get2mass_sortStars (WISE_Stars *tstars, int Ntstars);
Index: branches/meh_branches/ppstack_test/Ohana/src/addstar/src/find_matches_closest.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/addstar/src/find_matches_closest.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/addstar/src/find_matches_closest.c	(revision 33415)
@@ -132,4 +132,11 @@
     }
 
+    /* this block will match a given detection to the closest object within range of that detection.
+       XXX note that this matches ALL detections within range of the single object to that same object 
+       this is bad, but I cannot just go in linear order (ie, mark off each object as they are
+       used).  I should make a list of all Nobj * Ndet pairs in range and choose the matches
+       based on their separations.  UGH
+     */
+    
     /* within match range; look for matches */
     Jmin = -1;
Index: branches/meh_branches/ppstack_test/Ohana/src/addstar/src/loadwise.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/addstar/src/loadwise.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/Ohana/src/addstar/src/loadwise.c	(revision 33415)
@@ -0,0 +1,52 @@
+# include "addstar.h"
+# include "WISE.h"
+
+int main (int argc, char **argv) {
+
+  char *path;
+  int i;
+  SkyTable *sky, *skyWISE;
+  SkyList *skylist = NULL;
+  SkyList *overlap = NULL;
+  AddstarClientOptions options;
+
+  // need to construct these options with args_loadWISE...
+  options = ConfigInit (&argc, argv);
+  options = args_loadWISE (argc, argv, options);
+
+  // load the full sky description table:
+  sky = SkyTableLoadOptimal (CATDIR, SKY_TABLE, GSCFILE, TRUE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  
+  // generate the subset matching the user-selected region
+  skylist = SkyListByPatch (sky, -1, &UserPatch);
+
+  // if we only match to existing (already populated) regions, limit the select to those regions:
+  if (options.existing_regions) {
+    SkyList *tmp;
+    tmp = SkyListExistingSubset (skylist, CATDIR);
+    SkyListFree (skylist);
+    skylist = tmp;
+  }
+
+  path = WISE_DIR_PRELIM;
+
+  // the accel.dat file has the raw filenames
+  // test if the file exists, or else try the .gz version
+  // XXX can we build an accelerator?
+  skyWISE = loadWISE_acc (path, "accel.dat");
+  
+  for (i = 0; i < skyWISE[0].Nregions; i++) {
+    // check if any of the skylist entries overlap this WISE catalog:
+    overlap = SkyListByBounds_List (skylist, -1, skyWISE[0].regions[i].Rmin, skyWISE[0].regions[i].Rmax, skyWISE[0].regions[i].Dmin, skyWISE[0].regions[i].Dmax);
+    if (overlap[0].Nregions == 0) {
+      SkyListFree (overlap);
+      continue;
+    }
+    
+    fprintf (stderr, "loading %s\n", skyWISE[0].filename[i]);
+    loadWISE_as_rawdata (overlap, skyWISE[0].filename[i], options);
+    SkyListFree (overlap);
+  }
+  exit (0);
+}  
Index: branches/meh_branches/ppstack_test/Ohana/src/addstar/src/loadwise_ops.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/addstar/src/loadwise_ops.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/Ohana/src/addstar/src/loadwise_ops.c	(revision 33415)
@@ -0,0 +1,155 @@
+# include "addstar.h"
+# include "WISE.h"
+
+static int FilterSkip;
+static int TimeSkip;
+static int Qentry;
+static int Photcode;
+
+int getWISE_setup () {
+
+  NAMED_PHOTCODE (WISE_W1, "WISE_W1");
+  NAMED_PHOTCODE (WISE_W2, "WISE_W2");
+  NAMED_PHOTCODE (WISE_W3, "WISE_W3");
+  NAMED_PHOTCODE (WISE_W4, "WISE_W4");
+
+  return TRUE;
+}
+
+// fill in the coords for a single star.  takes a pointer to the start of the line
+int getWISE_coords (char *line, double *R, double *D, int Nmax) {
+
+  char *ptr = line;
+
+  ptr = skipNbounds (ptr, '|', 1, Nmax);
+  *R = strtod (ptr, NULL);
+  ptr = skipNbounds (ptr, '|', 1, Nmax);
+  *D = strtod (ptr, NULL);
+  if (*D > 90) Shutdown ("weird DEC value: something is wrong");
+
+  return TRUE;
+}
+
+int getWISE_sortStars (WISE_Stars *tstars, int Ntstars) {
+
+# define SWAPFUNC(A,B){ WISE_Stars temp = tstars[A]; tstars[A] = tstars[B]; tstars[B] = temp; }
+# define COMPARE(A,B)(tstars[A].R < tstars[B].R)
+
+  OHANA_SORT (Ntstars, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+  
+  return TRUE;
+}
+
+// this function retrieves the time from the DATE field
+e_time getWISE_date (char *ptr, int Nbound, int Nmax) {
+
+  e_time time;
+  char *p, *end;
+
+  p = skipNbounds (ptr, '|', Nbound, Nmax);
+  if (p == NULL) Shutdown ("format error in WISE");
+  end = memchr (p, '|', Nmax - (p - ptr));
+  if (end == NULL) Shutdown ("format error in WISE");
+  *end = 0;
+  time = ohana_date_to_sec (ptr);
+  *end = '|';
+
+  return (time);
+}
+
+// this function retrieves the time from the JDATE field (%12.4f)
+e_time getWISE_time (char *ptr, int Nbound, int Nmax) {
+
+  e_time time;
+  double jd;
+  char *p, *end;
+
+  p = skipNbounds (ptr, '|', Nbound, Nmax);
+  if (p == NULL) Shutdown ("format error in WISE");
+  end = memchr (p, '|', Nmax - (p - ptr));
+  if (end == NULL) Shutdown ("format error in WISE");
+  *end = 0;
+  jd = strtod (p, NULL);
+  time = ohana_jd_to_sec (jd);
+  *end = '|';
+
+  return (time);
+}
+
+/* return a pointer to the first char after Nbound of value bound */
+char *skipNbounds (char *line, char bound, int Nbound, int Nbyte) {
+
+  int i;
+  char *p, *q;
+
+  p = line;
+  for (i = 0; i < Nbound; i++) {
+    q = memchr (p, bound, Nbyte - (p - line));
+    if (q == NULL) return (NULL);
+    p = q + 1;
+    if (p - line == Nbyte) return (NULL);
+  }
+  return (p);
+}
+  
+/* watch for patches which cross 0,360 boundary */
+SkyTable *loadWISE_acc (char *path, char *accel) {
+
+  int Nregions, NREGIONS, Nrec;
+  char accelfile[1024], line[256], filename[128], datafile[256], **filenames;
+  FILE *f;
+  double Rs, Re, Ds, De;
+
+  SkyTable *sky;
+  SkyRegion *regions;
+
+  sprintf (accelfile, "%s/%s", path, accel);
+  f = fopen (accelfile, "r");
+  if (f == NULL) Shutdown ("can't read data from accelerator %s", accelfile);
+
+  Nregions = 0;
+  NREGIONS = 200;
+  ALLOCATE (regions, SkyRegion, NREGIONS);
+  ALLOCATE (filenames, char *, NREGIONS);
+
+  /* read in stars line-by-line */
+  while (scan_line (f, line) != EOF) {
+    stripwhite (line);
+    if (line[0] == 0) continue;
+    if (line[0] == '#') continue;
+    sscanf (line, "%s %lf %lf %lf %lf %d", filename, &Rs, &Re, &Ds, &De, &Nrec);
+    Rs *= 15.0;
+    Re *= 15.0;
+
+    // don't restrict by RA, but limit by DEC
+    if (De < UserPatch.Dmin) continue;
+    if (Ds > UserPatch.Dmax) continue;
+
+    regions[Nregions].Rmin = Rs;
+    regions[Nregions].Rmax = Re;
+    regions[Nregions].Dmin = Ds;
+    regions[Nregions].Dmax = De;
+    regions[Nregions].childE = Nrec; // a cheat since WISE only has one depth
+
+    sprintf (datafile, "%s/%s", path, filename);
+    filenames[Nregions] = strcreate (datafile);
+
+    Nregions ++;
+    if (Nregions >= NREGIONS) {
+	NREGIONS += 20;
+	REALLOCATE (regions, SkyRegion, NREGIONS);
+	REALLOCATE (filenames, char *, NREGIONS);
+    }
+  }    
+  fclose (f);
+
+  ALLOCATE (sky, SkyTable, 1);
+  sky[0].regions = regions;
+  sky[0].filename = filenames;
+  sky[0].Nregions = Nregions;
+  return (sky);
+}
+
Index: branches/meh_branches/ppstack_test/Ohana/src/addstar/src/loadwise_prelim_rawdata.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/addstar/src/loadwise_prelim_rawdata.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/Ohana/src/addstar/src/loadwise_prelim_rawdata.c	(revision 33415)
@@ -0,0 +1,241 @@
+# include "addstar.h"
+# include "WISE.h"
+
+/* unlike the DR2 data, the AS data is NOT fixed bytes/row 
+ * we need to handle fractional lines at the end of each read block
+ */
+
+/* read in chunks of ~64MB */
+# define NBYTE 0x4000000
+# define DEBUG 0
+
+int loadWISE_as_rawdata (SkyList *skytable, char *filename, AddstarClientOptions options) {
+  
+  int i, j, verbose;
+  int Nstars, NSTARS, Ntstars, NTSTARS;
+  int Nbyte, Nextra, Ntotal, offset;
+
+  double Rmin, Rmax, Dmin, Dmax;
+
+  FILE *f;
+  char *buffer, *p, *q;
+
+  Stars **stars; // this is an array of pointers to be consistent with input to find_match_refstars
+  WISE_Stars *tstars;
+  SkyList *skylist;
+  SkyRegion *region;
+  Catalog catalog;
+
+  getWISE_setup ();
+
+  ALLOCATE (buffer, char, NBYTE);
+
+  // scan through the entire WISE file
+  f = fopen (filename, "r");
+  if (f == NULL) Shutdown ("can't read WISE data file: %s", filename);
+  // test if this is a raw datafile or gzipped...
+
+  Nextra = 0;  // number excess bytes from lsat partial row
+  Ntotal = 0;  // track the total number of bytes read 
+  while ((Nbyte = fread (&buffer[Nextra], 1, NBYTE-Nextra, f)) != 0) {
+    if (Nbyte == -1) Shutdown ("error reading from raw file %s", filename);
+    if (DEBUG) fprintf (stderr, "read %d bytes", Nbyte);
+
+    Nbyte += Nextra;
+
+    if (VERBOSE) fprintf (stderr, "read .. ");
+
+    /* find bounds on first complete line */
+    p = buffer;
+    q = memchr (p, '\n', Nbyte);
+    if (q == NULL) Shutdown ("incomplete line at end of file\n");
+    offset = p - buffer; // offset within this scan
+
+    Ntstars = 0;
+    NTSTARS = 10000;
+    ALLOCATE (tstars, WISE_Stars, NTSTARS);
+
+    Rmin = 360.0;
+    Rmax =   0.0;
+    Dmin = +90.0;
+    Dmax = -90.0;
+
+    // scan through entire buffer for star coords
+    while (1) {
+      getWISE_coords (p, &tstars[Ntstars].R, &tstars[Ntstars].D, Nbyte - offset);
+      tstars[Ntstars].offset = offset; // offset within scan
+      tstars[Ntstars].flag = FALSE;
+
+      if (VERBOSE) {
+	Rmin = MIN (Rmin, tstars[Ntstars].R);
+	Rmax = MAX (Rmax, tstars[Ntstars].R);
+	Dmin = MIN (Dmin, tstars[Ntstars].D);
+	Dmax = MAX (Dmax, tstars[Ntstars].D);
+      }
+
+      Ntstars ++;
+      CHECK_REALLOCATE (tstars, WISE_Stars, NTSTARS, Ntstars, 10000);
+
+      /* start of the next line */
+      p = q + 1;
+      offset = p - buffer; // offset within this scan
+      if (offset == Nbyte) {
+	// last line in buffer is a complete line
+	Nextra = 0;
+	break;
+      }
+      /* end of the next line */
+      q = memchr (p, '\n', Nbyte - offset);
+      if (q == NULL) {
+	// last, incomplete line in buffer
+	Nextra = Nbyte - offset;
+	break;
+      } 
+    }
+    if (VERBOSE) fprintf (stderr, "scan %d stars (%10.6f - %10.6f, %10.6f - %10.6f) .. ", Ntstars, Rmin, Rmax, Dmin, Dmax);
+
+    // sort the tstars by RA
+    getWISE_sortStars (tstars, Ntstars);
+
+    // scan through the stars, loading the containing catalogs
+    // skip through table for unsaved stars
+    for (i = 0; i < Ntstars; i++) {
+      if (tstars[i].flag) continue;
+
+      // scan forward until we read the UserPatch
+      if (tstars[i].R < UserPatch.Rmin) continue;
+      if (tstars[i].R > UserPatch.Rmax) break;
+      if (tstars[i].D < UserPatch.Dmin) continue;
+      if (tstars[i].D > UserPatch.Dmax) continue;
+
+      // identify the relevant catalog
+      skylist = SkyRegionByPoint_List (skytable, -1, tstars[i].R, tstars[i].D);
+      if (skylist[0].Nregions == 0) {
+	  SkyListFree (skylist);
+	  continue;
+      }
+      region = skylist[0].regions[0];
+      if (DEBUG) fprintf (stderr, "writing to %s\n", skylist[0].filename[0]);
+
+      // collect array of (Stars *) stars in a new output catalog
+      Nstars = 0;
+      NSTARS = 3000;
+      ALLOCATE (stars, Stars *, NSTARS);
+
+      // loop over stars in this WISE region that are also in this output region
+      for (j = i; j < Ntstars; j++) {
+	if (tstars[j].flag) continue;
+
+	// check if in skyregion
+	if (tstars[j].R < region[0].Rmin) continue;
+	if (tstars[j].R > region[0].Rmax) break;
+	if (tstars[j].D < region[0].Dmin) continue;
+	if (tstars[j].D > region[0].Dmax) continue;
+	  
+	// check if in UserPatch
+	if (tstars[j].R < UserPatch.Rmin) continue;
+	if (tstars[j].R > UserPatch.Rmax) break;
+	if (tstars[j].D < UserPatch.Dmin) continue;
+	if (tstars[j].D > UserPatch.Dmax) continue;
+	  
+	offset = tstars[j].offset;
+
+	ALLOCATE (stars[Nstars+0], Stars, 1);
+	ALLOCATE (stars[Nstars+1], Stars, 1);
+	ALLOCATE (stars[Nstars+2], Stars, 1);
+	ALLOCATE (stars[Nstars+3], Stars, 1);
+
+	InitStar (stars[Nstars+0]);
+	InitStar (stars[Nstars+1]);
+	InitStar (stars[Nstars+2]);
+	InitStar (stars[Nstars+3]);
+
+	stars[Nstars+0][0].average.R = tstars[j].R;
+	stars[Nstars+0][0].average.D = tstars[j].D;
+	stars[Nstars+1][0].average.R = tstars[j].R;
+	stars[Nstars+1][0].average.D = tstars[j].D;
+	stars[Nstars+2][0].average.R = tstars[j].R;
+	stars[Nstars+2][0].average.D = tstars[j].D;
+	stars[Nstars+3][0].average.R = tstars[j].R;
+	stars[Nstars+3][0].average.D = tstars[j].D;
+	getWISE_star_full (&stars[Nstars], &buffer[offset], Nbyte - offset);
+
+	tstars[j].flag = TRUE;
+
+	Nstars += 4;
+	if (Nstars >= NSTARS - 4) {
+	  NSTARS += 4000;
+	  REALLOCATE (stars, Stars *, NSTARS);
+	}
+      }
+
+      if (!Nstars) {
+	free (stars);
+	continue;
+      }
+
+      if (DEBUG) fprintf (stderr, "selected %d stars (%10.6f - %10.6f, %10.6f - %10.6f)\n", Nstars, 
+			  region[0].Rmin, region[0].Rmax, region[0].Dmin, region[0].Dmax);
+
+      if (1) {
+	verbose = VERBOSE;
+	VERBOSE = FALSE;
+
+	// now we have all of the loaded stars in this catalog
+	catalog.filename = skylist[0].filename[0];
+	catalog.catformat = dvo_catalog_catformat (CATFORMAT);  // set the default catformat from config data
+	catalog.catmode   = dvo_catalog_catmode (CATMODE);      // set the default catmode from config data
+	catalog.catflags = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+	catalog.Nsecfilt  = GetPhotcodeNsecfilt ();
+
+	// an error exit status here is a significant error
+	if (!dvo_catalog_open (&catalog, skylist[0].regions[0], VERBOSE, "w")) {
+	  fprintf (stderr, "ERROR: failure to open/create catalog file %s\n", catalog.filename);
+	  exit (2);
+	}
+
+	// assume no input star matches an existing star 
+	// simply add to the existing table
+	// loadWISE_catalog (&catalog, stars, Nstars);
+	find_matches_refstars (skylist[0].regions[0], stars, Nstars, &catalog, options);
+	// loadWISE_catalog (&catalog, stars, Nstars);
+
+	dvo_catalog_save (&catalog, VERBOSE);
+	dvo_catalog_unlock (&catalog);
+	dvo_catalog_free (&catalog);
+	// free (catalog.filename);
+	// XXX don't free this! it points to an element of the skytable
+      }
+
+      SkyListFree (skylist);
+      for (j = 0; j < Nstars; j++) free (stars[j]);
+      free (stars);
+      VERBOSE = verbose;
+    }
+    free (tstars);
+    if (VERBOSE) fprintf (stderr, "done\n");
+
+    // at end, p points at the start of last, partial line
+    if (Nextra) memmove (buffer, p, Nextra);
+  }
+
+  if (VERBOSE) fprintf (stderr, "\n");
+  
+  fclose (f);
+  free (buffer);
+  return (TRUE);
+}
+
+/*
+  for each WISE file:
+  for each data block
+  generate a table of: R, D, byte, flag
+  for each unsaved star
+  find containing catalog
+  load catalog
+  find all contained stars
+  add to catalog
+  save catalog 
+  mark all contained stars
+*/
+
Index: branches/meh_branches/ppstack_test/Ohana/src/addstar/src/loadwise_star_full.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/addstar/src/loadwise_star_full.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/Ohana/src/addstar/src/loadwise_star_full.c	(revision 33415)
@@ -0,0 +1,264 @@
+# include "addstar.h"
+# include "WISE.h"
+
+// XXX check to see if desired output format is PS1_V1 or later?  (use 16bit version if not?)
+
+// fill in the data for a WISE quad star.  takes a pointer to the start of the line the
+// RA and DEC have already been set
+int getWISE_star_full (Stars **star, char *line, int Nmax) {
+
+  char *ptr;
+  double dMfull;
+  double jd;
+
+  if (line == NULL) Shutdown ("format error in WISE");
+
+  ptr = skipNbounds (ptr, '|', 3, Nmax); // skip: desig, ra, dec,
+  star[0][0].measure.FWx = ToShortPixels(strtod (ptr, NULL));
+  ptr = nextWISEfield (ptr); // sig_dec
+  star[0][0].measure.FWy = ToShortPixels(strtod (ptr, NULL));
+  ptr = nextWISEfield (ptr); // sig_radec
+  star[0][0].measure.theta = ToShortPixels(strtod (ptr, NULL));
+
+  star[1][0].measure.FWx   = star[0][0].measure.FWx;
+  star[1][0].measure.FWy   = star[0][0].measure.FWy;
+  star[1][0].measure.theta = star[0][0].measure.theta;
+  star[2][0].measure.FWx   = star[0][0].measure.FWx;
+  star[2][0].measure.FWy   = star[0][0].measure.FWy;
+  star[2][0].measure.theta = star[0][0].measure.theta;
+  star[3][0].measure.FWx   = star[0][0].measure.FWx;
+  star[3][0].measure.FWy   = star[0][0].measure.FWy;
+  star[3][0].measure.theta = star[0][0].measure.theta;
+
+  ptr = skipNbounds (ptr, '|', 10, Nmax); // skip: glon, glat, elon, elat, wx, wy, cntr, source_id, coadd_id, src
+
+  // W1
+  star[0][0].measure.M  = strtod (ptr, NULL);
+  ptr = nextWISEfield (ptr); // j_cmsig
+  star[0][0].measure.dM = strtod (ptr, NULL);
+  ptr = skipNbounds (ptr, '|', 2, Nmax); // skip: w1snr, w1rchi2
+
+  // W2
+  star[1][0].measure.M  = strtod (ptr, NULL);
+  ptr = nextWISEfield (ptr); // j_cmsig
+  star[1][0].measure.dM = strtod (ptr, NULL);
+  ptr = skipNbounds (ptr, '|', 2, Nmax); // skip: w2snr, w2rchi2
+
+  // W3
+  star[2][0].measure.M  = strtod (ptr, NULL);
+  ptr = nextWISEfield (ptr); // j_cmsig
+  star[2][0].measure.dM = strtod (ptr, NULL);
+  ptr = skipNbounds (ptr, '|', 2, Nmax); // skip: w3snr, w3rchi2
+
+  // W4
+  star[3][0].measure.M  = strtod (ptr, NULL);
+  ptr = nextWISEfield (ptr); // j_cmsig
+  star[3][0].measure.dM = strtod (ptr, NULL);
+  ptr = skipNbounds (ptr, '|', 2, Nmax); // skip: w4snr, w4rchi2
+
+  star[0][0].measure.photFlags = 0;
+  star[1][0].measure.photFlags = 0;
+  star[2][0].measure.photFlags = 0;
+  star[3][0].measure.photFlags = 0;
+  ptr = skipNbounds (ptr, '|', 8, Nmax); // skip fields
+
+  setWISE_cc_flag (star[0], ptr[0]); // cc_flg
+  setWISE_cc_flag (star[1], ptr[1]); // cc_flg
+  setWISE_cc_flag (star[2], ptr[2]); // cc_flg
+  setWISE_cc_flag (star[3], ptr[2]); // cc_flg
+  ptr = skipNbounds (ptr, '|', 2, Nmax); // skip: ext_flg, var_flg
+
+  setWISE_ph_qual (star[0], ptr[0]); // ph_qual
+  setWISE_ph_qual (star[1], ptr[1]); // ph_qual
+  setWISE_ph_qual (star[2], ptr[2]); // ph_qual
+  setWISE_ph_qual (star[3], ptr[3]); // ph_qual
+  ptr = skipNbounds (ptr, '|', 2, Nmax); // skip: ext_flg, var_flg
+
+  // setWISE_rd_flag (star[0], ptr[0]);
+  // setWISE_bl_flag (star[0], ptr[0]);
+  // setWISE_gal_flag (star[0], ptr[0]);
+  // setWISE_mp_flag (star[0], ptr[0]);
+
+  // ptr = nextWISEfield (ptr); // x_scan
+  // star[0][0].measure.Xccd = strtod (ptr, NULL);
+  // star[2][0].measure.Xccd = star[1][0].measure.Xccd = star[0][0].measure.Xccd;
+  // ptr = nextWISEfield (ptr); // jdate (julian date)
+
+  jd = 2455263.0; // NOTE : WISE prelim release does not contain per-detection time info. 
+  // the release is based on data taken in the period 14 January 2010 to 29 April 2010
+  star[0][0].measure.t = ohana_jd_to_sec (jd);
+  star[1][0].measure.t = star[0][0].measure.t;
+  star[2][0].measure.t = star[0][0].measure.t;
+  star[3][0].measure.t = star[0][0].measure.t;
+
+  // ptr = nextWISEfield (ptr); // j_psfchi
+  // star[0][0].measure.psfChisq = strtod (ptr, NULL);
+  // ptr = nextWISEfield (ptr); // h_psfchi
+  // star[1][0].measure.psfChisq = strtod (ptr, NULL);
+  // ptr = nextWISEfield (ptr); // k_psfchi
+  // star[2][0].measure.psfChisq = strtod (ptr, NULL);
+  // 
+  // ptr = nextWISEfield (ptr); // j_m_stdap
+  // star[0][0].measure.Map = strtod (ptr, NULL);
+  // ptr = nextWISEfield (ptr); // j_msig_stdap (skip?)
+  // 
+  // ptr = nextWISEfield (ptr); // h_m_stdap
+  // star[1][0].measure.Map = strtod (ptr, NULL);
+  // ptr = nextWISEfield (ptr); // h_msig_stdap (skip?)
+  // 
+  // ptr = nextWISEfield (ptr); // k_m_stdap
+  // star[2][0].measure.Map = strtod (ptr, NULL);
+  // ptr = nextWISEfield (ptr); // k_msig_stdap (skip?)
+  // 
+  // ptr = nextWISEfield (ptr); // dist_edge_ns (skip)
+  // ptr = nextWISEfield (ptr); // dist_edge_ew (skip)
+  // ptr = nextWISEfield (ptr); // dist_edge_flg (skip)
+  // 
+  // ptr = nextWISEfield (ptr); // dup_src (one flag for all filters)
+  // setWISE_dup_flag (star[0], ptr[0]);
+  // setWISE_dup_flag (star[1], ptr[0]);
+  // setWISE_dup_flag (star[2], ptr[0]);
+  // 
+  // ptr = nextWISEfield (ptr); // use_src (one flag for all filters)
+  // setWISE_use_flag (star[0], ptr[0]);
+  // setWISE_use_flag (star[1], ptr[0]);
+  // setWISE_use_flag (star[2], ptr[0]);
+
+  star[0][0].measure.photcode  = TM_J;
+  star[0][0].measure.detID   = 0;
+  star[0][0].measure.imageID = 0;
+
+  star[1][0].measure.photcode  = TM_H;
+  star[1][0].measure.detID   = 0;
+  star[1][0].measure.imageID = 0;
+
+  star[2][0].measure.photcode  = TM_K;
+  star[2][0].measure.detID   = 0;
+  star[2][0].measure.imageID = 0;
+
+  return TRUE;
+}
+
+/* return a pointer to the first char after the next field separator (|) */
+char *nextWISEfield (char *line) {
+
+  char *p, *q;
+
+  p = line;
+  q = strchr (p, '|');
+  if (q == NULL) return (NULL);
+  p = q + 1;
+  if (*p == 0) return (NULL);
+  return (p);
+}
+
+int setWISE_cc_flag (Stars *star, char qual) {
+
+  switch (qual) {
+    case 'p': star[0].measure.photFlags |= 0x00010000; break;
+    case 'P': star[0].measure.photFlags |= 0x00010000; break;
+    case 'h': star[0].measure.photFlags |= 0x00020000; break;
+    case 'H': star[0].measure.photFlags |= 0x00020000; break;
+    case 'd': star[0].measure.photFlags |= 0x00040000; break;
+    case 'D': star[0].measure.photFlags |= 0x00040000; break;
+    case 'o': star[0].measure.photFlags |= 0x00020000; break;
+    case 'O': star[0].measure.photFlags |= 0x00020000; break;
+    case '0': break;
+    default: 
+      fprintf (stderr, "error!\n");
+      exit (2);
+  }      
+  return (TRUE);
+}
+
+int setWISE_ph_qual (Stars *star, char qual) {
+
+  switch (qual) {
+    case 'A': star[0].measure.photFlags |= 0x00000001; break; // was: 0x0004
+    case 'B': star[0].measure.photFlags |= 0x00000002; break; // was: 0x0005
+    case 'C': star[0].measure.photFlags |= 0x00000004; break; // was: 0x0006
+    case 'U': star[0].measure.photFlags |= 0x00000040; break; // was: 0x0001
+    case 'X': star[0].measure.photFlags |= 0x00000080; break; // was: 0x0000
+    default: 
+      fprintf (stderr, "error!\n");
+      exit (2);
+  }      
+  return (TRUE);
+}
+
+int setWISE_rd_flag (Stars *star, char qual) {
+
+  switch (qual) {
+    case '0': star[0].measure.photFlags |= 0x00000100; break; // was: 0x0000 
+    case '1': star[0].measure.photFlags |= 0x00000200; break; // was: 0x0010 
+    case '2': star[0].measure.photFlags |= 0x00000400; break; // was: 0x0020 
+    case '3': star[0].measure.photFlags |= 0x00000800; break; // was: 0x0030 
+    case '4': star[0].measure.photFlags |= 0x00001000; break; // was: 0x0040 
+    case '6': star[0].measure.photFlags |= 0x00002000; break; // was: 0x0050 
+    case '9': star[0].measure.photFlags |= 0x00004000; break; // was: 0x0060 
+    default: 
+      fprintf (stderr, "error!\n");
+      exit (2);
+  }      
+  return (TRUE);
+}
+
+int setWISE_bl_flag (Stars *star, char qual) {
+
+  switch (qual) {
+    case '0': star[0].measure.photFlags &= ~0x00300000; break; // was: ~0x0008
+    case '1': star[0].measure.photFlags |=  0x00100000; break; // was: ~0x0008
+    default:  star[0].measure.photFlags |=  0x00200000; break; // was:  0x0008
+  }      
+  return (TRUE);
+}
+
+int setWISE_gal_flag (Stars *star, char qual) {
+
+  switch (qual) {
+    case '0': star[0].measure.photFlags &= ~0x00c00000; break; // was: ~0x0080 
+    case '1': star[0].measure.photFlags |=  0x00400000; break; // was: ~0x0080 
+    default:  star[0].measure.photFlags |=  0x00800000;	       // was:  0x0080
+      star[0].measure.extNsigma = 100.0;
+      break;
+  }      
+  return (TRUE);
+}
+
+int setWISE_mp_flag (Stars *star, char qual) {
+
+  switch (qual) {
+    case '0': star[0].measure.photFlags &= ~0x03000000; break; // was: ~0x0800
+    case '1': star[0].measure.photFlags |=  0x01000000; break; // was: ~0x0800
+    default:  star[0].measure.photFlags |=  0x02000000; break; // was:  0x0800
+  }      
+  return (TRUE);
+}
+
+int setWISE_dup_flag (Stars *star, char qual) {
+
+  switch (qual) {
+    case '0': star[0].measure.photFlags &= ~0x0c000000; break; // was: ~0x1000
+    case '1': star[0].measure.photFlags |=  0x04000000; break; // was: ~0x1000
+    default:  star[0].measure.photFlags |=  0x08000000; break; // was:  0x1000
+  }      
+  return (TRUE);
+}
+
+int setWISE_use_flag (Stars *star, char qual) {
+
+  switch (qual) {
+    case '0': star[0].measure.photFlags &= ~0x10000000; break; // was: ~0x2000
+    case '1': star[0].measure.photFlags |=  0x10000000; break; // was:  0x2000
+    default:  abort();
+  }      
+  return (TRUE);
+}
+
+// unused photFlags:
+// 0x0000.8000
+// 0x0004.0000
+// 0x0008.0000
+// 0x2000.0000
+// 0x4000.0000
+// 0x8000.0000
Index: branches/meh_branches/ppstack_test/Ohana/src/getstar/src/dvoImagesAtCoords.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/getstar/src/dvoImagesAtCoords.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/getstar/src/dvoImagesAtCoords.c	(revision 33415)
@@ -158,5 +158,7 @@
         }
         if  (copy) {
-            free(copy);
+            // XXX: Temporarily leak this copy. free gets redirected to
+            // ohana_free but strdup didn't allocate the memory
+            // free(copy);
         }
     }
Index: branches/meh_branches/ppstack_test/Ohana/src/kapa2/include/prototypes.h
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/kapa2/include/prototypes.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/kapa2/include/prototypes.h	(revision 33415)
@@ -126,4 +126,5 @@
 void 	      PSPixmap24 	  PROTO((Graphic *graphic, KapaImageWidget *image, FILE *f));
 void 	      PSPixmap32 	  PROTO((Graphic *graphic, KapaImageWidget *image, FILE *f));
+void          PSPixmap_3byte      PROTO((Graphic *graphic, KapaImageWidget *image, FILE *f));
 
 /* kapa bDraw Functions */
Index: branches/meh_branches/ppstack_test/Ohana/src/kapa2/include/structures.h
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/kapa2/include/structures.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/kapa2/include/structures.h	(revision 33415)
@@ -26,6 +26,8 @@
   XFontStruct   *font;
   Cursor         cursor;
-  int            x,  y;
-  unsigned int   dx, dy;
+  int            x,  y;	      // corner coord in X world coords
+  unsigned int   dx, dy;      // size of window in X coords
+  int            xwin, ywin;  // corner coord of display subregion (eg, png, ps plot)
+  int            dxwin, dywin; // corner coord of display subregion (eg, png, ps plot)
   CCNode        *cube;
   XColor        *cmap;
Index: branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/DrawFrame.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/DrawFrame.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/DrawFrame.c	(revision 33415)
@@ -150,5 +150,6 @@
 }
 
-# define MIN_RANGE 1e-10
+// do I really want this?  allow an override?
+# define MIN_RANGE 1e-30
 
 void AxisTickScale (Axis *axis, double *major, double *minor, int *nsignif) {
Index: branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/DrawObjects.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/DrawObjects.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/DrawObjects.c	(revision 33415)
@@ -408,4 +408,5 @@
 	  (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy)) {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  XSetForeground (graphic->display, graphic->gc, graphic->cmap[pixel].pixel);
@@ -424,4 +425,5 @@
 	  (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy)) {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  XSetForeground (graphic->display, graphic->gc, graphic->cmap[pixel].pixel);
@@ -440,4 +442,5 @@
 	  (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy)) {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  XSetForeground (graphic->display, graphic->gc, graphic->cmap[pixel].pixel);
@@ -457,4 +460,5 @@
 	  (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy)) {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  XSetForeground (graphic->display, graphic->gc, graphic->cmap[pixel].pixel);
@@ -475,4 +479,5 @@
 	  (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy)) {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  XSetForeground (graphic->display, graphic->gc, graphic->cmap[pixel].pixel);
@@ -495,4 +500,5 @@
 	  (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy)) {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  XSetForeground (graphic->display, graphic->gc, graphic->cmap[pixel].pixel);
@@ -513,4 +519,5 @@
 	  (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy)) {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  XSetForeground (graphic->display, graphic->gc, graphic->cmap[pixel].pixel);
@@ -531,4 +538,5 @@
 	  (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy)) {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  XSetForeground (graphic->display, graphic->gc, graphic->cmap[pixel].pixel);
@@ -547,4 +555,5 @@
 	  (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy)) {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  XSetForeground (graphic->display, graphic->gc, graphic->cmap[pixel].pixel);
@@ -567,4 +576,5 @@
 	  (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy)) {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  XSetForeground (graphic->display, graphic->gc, graphic->cmap[pixel].pixel);
@@ -589,4 +599,5 @@
 	  (sy < graph[0].axis[1].fy) && (sy > graph[0].axis[1].fy + graph[0].axis[1].dfy)) {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  XSetForeground (graphic->display, graphic->gc, graphic->cmap[pixel].pixel);
Index: branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/PSObjects.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/PSObjects.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/PSObjects.c	(revision 33415)
@@ -323,4 +323,5 @@
       {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  fprintf (f, "%4.2f %4.2f %4.2f setrgbcolor\n", pixel1[pixel], pixel2[pixel], pixel3[pixel]);
@@ -340,4 +341,5 @@
       {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  fprintf (f, "%4.2f %4.2f %4.2f setrgbcolor\n", pixel1[pixel], pixel2[pixel], pixel3[pixel]);
@@ -357,4 +359,5 @@
       {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  fprintf (f, "%4.2f %4.2f %4.2f setrgbcolor\n", pixel1[pixel], pixel2[pixel], pixel3[pixel]);
@@ -375,4 +378,5 @@
       {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  fprintf (f, "%4.2f %4.2f %4.2f setrgbcolor\n", pixel1[pixel], pixel2[pixel], pixel3[pixel]);
@@ -393,4 +397,5 @@
       {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  fprintf (f, "%4.2f %4.2f %4.2f setrgbcolor\n", pixel1[pixel], pixel2[pixel], pixel3[pixel]);
@@ -415,4 +420,5 @@
       {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  fprintf (f, "%4.2f %4.2f %4.2f setrgbcolor\n", pixel1[pixel], pixel2[pixel], pixel3[pixel]);
@@ -434,4 +440,5 @@
       {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  fprintf (f, "%4.2f %4.2f %4.2f setrgbcolor\n", pixel1[pixel], pixel2[pixel], pixel3[pixel]);
@@ -453,4 +460,5 @@
       {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  fprintf (f, "%4.2f %4.2f %4.2f setrgbcolor\n", pixel1[pixel], pixel2[pixel], pixel3[pixel]);
@@ -470,4 +478,5 @@
       {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  fprintf (f, "%4.2f %4.2f %4.2f setrgbcolor\n", pixel1[pixel], pixel2[pixel], pixel3[pixel]);
@@ -491,4 +500,5 @@
       {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  fprintf (f, "%4.2f %4.2f %4.2f setrgbcolor\n", pixel1[pixel], pixel2[pixel], pixel3[pixel]);
@@ -514,4 +524,5 @@
       {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  fprintf (f, "%4.2f %4.2f %4.2f setrgbcolor\n", pixel1[pixel], pixel2[pixel], pixel3[pixel]);
Index: branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/PSPixmap.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/PSPixmap.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/PSPixmap.c	(revision 33415)
@@ -1,5 +1,3 @@
 # include "Ximage.h"
-
-// XXX this stuff has been broken by the conversion to the pixmap stuff
 
 void PSPixmap8 (Graphic *graphic, KapaImageWidget *image, FILE *f) {
@@ -146,40 +144,127 @@
 }
 
-# if (0)
-// XXX needs work!
-void PSPixmap32_RGB (Graphic *graphic, KapaImageWidget *image, FILE *f) {
-
-  int i, k, m, val;
-  double Nchar, Npix, start, slope, frac;
-  unsigned int *buff;
-  unsigned long back;
-  unsigned char 
-
-  ALLOCATE (pixelR, unsigned char, graphic[0].Npixels);
-  ALLOCATE (pixelG, unsigned char, graphic[0].Npixels);
-  ALLOCATE (pixelB, unsigned char, graphic[0].Npixels);
+# define WHITE_R 255
+# define WHITE_G 255
+# define WHITE_B 255
+
+void PSPixmap_3byte (Graphic *graphic, KapaImageWidget *image, FILE *f) {
+
+  int i, j, ii, jj;
+  int i_start, i_end, j_start, j_end;
+  int I_start, J_start;
+  int dropback;  /* this is a bit of a kludge... */
+  int dx, dy, DX, DY, inDX, inDY, Xs, Ys;
+  int expand_in, expand_out;
+  double expand, Ix, Iy;
+  unsigned short *in_pix, *in_pix_ref;
+  unsigned char *pixel1, *pixel2, *pixel3;
+
+  if (image == NULL) return;
+
+  ALLOCATE (pixel1, unsigned char, graphic[0].Npixels);
+  ALLOCATE (pixel2, unsigned char, graphic[0].Npixels);
+  ALLOCATE (pixel3, unsigned char, graphic[0].Npixels);
 
   /** cmap[i].pixel must be defined even if X is not used **/
   for (i = 0; i < graphic[0].Npixels; i++) { /* set up pixel array */
-    pixelR[i] = graphic[0].cmap[i].red >> 8;
-    pixelG[i] = graphic[0].cmap[i].green >> 8;
-    pixelB[i] = graphic[0].cmap[i].blue >> 8;
-  }
-
-  for (i = 0; i < image[0].picture.dy; i++) {
-    for (k = 0; k < image[0].picture.dx; k++, buff++) {
-      if (*buff == back) 
-	val = Nchar;
-      else {
-	for (m = 0; (graphic[0].cmap[m].pixel != *buff) && (m < Npix); m++);
-	val = Nchar - frac * MIN (MAX (start + m * slope, 0), Npix);
-      }
-      fprintf (f, "%02x", val);
-      if (!((k+1) % 40)) fprintf (f, "\n"); 
-    }
-    fprintf (f, "\n");
-    buff -= 2*image[0].picture.dx;
-  }
-  return;
-}
-# endif
+    pixel1[i] = graphic[0].cmap[i].red >> 8;
+    pixel2[i] = graphic[0].cmap[i].green >> 8;
+    pixel3[i] = graphic[0].cmap[i].blue >> 8;
+  }
+
+  assert ((image[0].picture.expand >= 1) || (image[0].picture.expand <= -2));
+  expand = expand_in = expand_out = 1.0;
+  if (image[0].picture.expand > 0) {
+    expand = 1 / (1.0*image[0].picture.expand);
+    expand_out = image[0].picture.expand;
+    expand_in  = 1;
+  }
+  if (image[0].picture.expand < 0) {
+    expand = fabs((double)image[0].picture.expand);
+    expand_out = 1;
+    expand_in  = -image[0].picture.expand;
+  }
+
+  Xs = image[0].picture.x;
+  Ys = image[0].picture.y;
+  dx = image[0].picture.dx;
+  dy = image[0].picture.dy;
+  DX = image[0].image[0].matrix.Naxis[0];
+  DY = image[0].image[0].matrix.Naxis[1];
+
+  // i_start, j_start are the closest lit screen pixel to 0,0
+  // I_start, J_start are the image pixel corresponding to i_start, j_start
+  Picture_Lower (&i_start, &j_start, &I_start, &J_start, &image[0].image[0].matrix, &image[0].picture);
+
+  // i_end, j_end are the closest lit screen pixel to dx, dy
+  // I_end, J_end are the image pixel corresponding to i_end, j_end
+  Picture_Upper (&i_end, &j_end, i_start, j_start, &image[0].image[0].matrix, &image[0].picture);
+
+  assert (i_start <= i_end);
+  assert (j_start <= j_end);
+
+  Ix = image[0].picture.flipx ? I_start - 1 : I_start;
+  Iy = image[0].picture.flipy ? J_start - 1 : J_start;
+
+  inDX = image[0].picture.flipx ? -1 : +1;
+  inDY = image[0].picture.flipy ? -1 : +1;
+
+  dropback = expand_out - (i_end - i_start) % expand_out;
+  if ((i_end - i_start) % expand_out == 0) dropback = 0;
+
+  in_pix_ref  = &image[0].pixmap[DX*(int)MAX(Iy,0) + (int)MAX(Ix,0)];
+
+  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/
+
+  // add in occasional return chars
+
+  /**** fill in bottom area ****/
+  for (j = 0; j < j_start; j++) {
+    for (i = 0; i < dx; i++) {
+      fprintf (f, "%02x%02x%02x", WHITE_R, WHITE_G, WHITE_B);
+    }
+  }
+  
+  // probably could do this all smarter with scale operations in PS...
+
+  /*** fill in the image data region ***/
+  for (j = j_start; j < j_end; j+= expand_out, in_pix_ref += inDY*expand_in*DX) {
+    
+    // repeat the section below 'expand_out' times
+    for (jj = 0; jj < expand_out; jj++) {
+
+      /* create one output image line */
+      in_pix = in_pix_ref;
+
+      /**** fill in area to the left of the picture ****/
+      for (i = 0; i < i_start; i++) {
+	fprintf (f, "%02x%02x%02x", WHITE_R, WHITE_G, WHITE_B);
+      }
+    
+      /*** fill in the picture region ***/
+      for (i = i_start; i < i_end; i+=expand_out, in_pix += inDX*expand_in) {
+	for (ii = 0; ii < expand_out; ii++) {
+	  fprintf (f, "%02x%02x%02x", pixel1[*in_pix], pixel2[*in_pix], pixel3[*in_pix]);
+	}
+      }
+    
+      /**** fill in area to the right of the picture ****/
+      for (i = i_end; i < dx; i++) {
+	fprintf (f, "%02x%02x%02x", WHITE_R, WHITE_G, WHITE_B);
+      }
+    }
+  }
+
+  /**** fill in top area ****/
+  for (j = j_end; j < dy; j++) {
+    for (i = 0; i < dx; i++) { 
+      fprintf (f, "%02x%02x%02x", WHITE_R, WHITE_G, WHITE_B);
+    }
+  }
+
+  free (pixel1);
+  free (pixel2);
+  free (pixel3);
+
+  return;
+}
Index: branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/PSimage.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/PSimage.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/PSimage.c	(revision 33415)
@@ -13,4 +13,11 @@
   graphic = GetGraphic();
 
+  // Update this to generate color PS images: I need to test if the image if color or BW (based
+  // on the colormap).  If it is a color image, I need to generate a 24 bit image. the header
+  // should look the same (Nx Ny 8 [1 0 0 1 0 0]) but then it should finish with "false 3
+  // colorimage" instead of just "image" (see ../doc/color.image.s for an example).  the hex
+  // string needs to have RGB represented as chars (2 hex chars per R,G,B). 
+  // the values come from the following map: image[0].pixmap[i] -> cmap[pixel].red -> R (0-255)
+
   fprintf (f, " newpath %d %d moveto %d %d lineto %d %d lineto %d %d lineto closepath\n\n", 
 	   (int) image[0].picture.x,                       graphic->dy - (int) image[0].picture.y, 
@@ -21,23 +28,18 @@
   fprintf (f, "%d %d translate\n", (int) image[0].picture.x, graphic->dy - (int) image[0].picture.y - image[0].picture.dy);
   fprintf (f, "%d %d 8\n", image[0].picture.dx, image[0].picture.dy);
-  fprintf (f, "[1 0 0 1 0 0]\n");
+  fprintf (f, "[1 0 0 -1 0 %d]\n", image[0].picture.dy);
+  // write out the image in normal order, but flip in PS
+
+# if (0)
+
   fprintf (f, "{currentfile %d string readhexstring pop} image\n\n", image[0].picture.dx);
+  PSPixmap_1byte (graphic, image, f);
 
-  /******** First we draw the picture itself ********/
-  /* in !USE_XWINDOW, we'll have to change this to use the JPEG function */
-  switch (graphic[0].Nbits) {
-  case 8:
-    PSPixmap8 (graphic, image, f);
-    break;
-  case 16:
-    PSPixmap16 (graphic, image, f);
-    break;
-  case 24:
-    PSPixmap24 (graphic, image, f);
-    break;
-  case 32:
-    PSPixmap32 (graphic, image, f);
-    break;
-  }
+# else
+
+  fprintf (f, "{currentfile %d string readhexstring pop} false 3 colorimage\n\n", 3*image[0].picture.dx);
+  PSPixmap_3byte (graphic, image, f);
+
+# endif
 
   fprintf (f, "grestore %% end of image\n");
Index: branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/Sections.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/Sections.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/Sections.c	(revision 33415)
@@ -237,2 +237,35 @@
   return (TRUE);
 }
+
+int SectionMinBoundary (Graphic *graphic) {
+
+    int i;
+    int Xs = graphic->dx;
+    int Ys = graphic->dy;
+    int Xe = 0;
+    int Ye = 0;
+
+    // the boundary for a single section should probably be adjusted depending on the 
+    // image status (should not include the imtool portion)
+    for (i = 0; i < Nsections; i++) {
+	Xs = MIN (Xs, sections[i][0].x);
+	Ys = MIN (Ys, sections[i][0].y);
+	Xe = MAX (Xe, sections[i][0].x + sections[i][0].dx);
+	Ye = MAX (Ye, sections[i][0].y + sections[i][0].dy);
+    }
+
+    if ((Xs >= Xe) || (Ys >- Ye)) {
+	// default values for the region window
+	graphic->xwin  = 0;
+	graphic->ywin  = 0;
+	graphic->dxwin = graphic->dx;
+	graphic->dywin = graphic->dy; 
+    }	
+    
+    // set min/max boundary (min window containing max range of sections)
+    graphic->xwin = Xs;
+    graphic->ywin = Ys;
+    graphic->dxwin = Xe - Xs;
+    graphic->dywin = Ye - Ys;
+    return TRUE;
+}
Index: branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/SetUpGraphic.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/SetUpGraphic.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/SetUpGraphic.c	(revision 33415)
@@ -16,4 +16,10 @@
   graphic->dx = 512;
   graphic->dy = 512; 
+
+  // default values for the region window
+  graphic->xwin  = 0;
+  graphic->ywin  = 0;
+  graphic->dxwin = graphic->dx;
+  graphic->dywin = graphic->dy; 
 
   if (!USE_XWINDOW) {
Index: branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/bDrawImage.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/bDrawImage.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/bDrawImage.c	(revision 33415)
@@ -47,6 +47,7 @@
   }
 
-  Xs = image[0].picture.x;
-  Ys = image[0].picture.y;
+  // Xs,Ys are in full-frame coords.  can we trim here?
+  Xs = image[0].picture.x - graphic[0].xwin;
+  Ys = image[0].picture.y - graphic[0].ywin;
   dx = image[0].picture.dx;
   dy = image[0].picture.dy;
@@ -54,4 +55,5 @@
   DY = image[0].image[0].matrix.Naxis[1];
 
+  // the created buffer is supposed to contain the output windows
   if (buffer[0].Nx < Xs + dx) {
     fprintf (stderr, "invalid condition\n");
Index: branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/bDrawIt.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/bDrawIt.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/bDrawIt.c	(revision 33415)
@@ -13,5 +13,9 @@
   black = KapaColorByName ("black");
 
-  buffer = bDrawBufferCreate (graphic->dx, graphic->dy, Nbyte, palette, Npalette);
+  // SectionMinBoundary (graphic);
+
+  // if we want to trim, we'll need to carry about the start in graphic coords and
+  // the dx,dy size.  
+  buffer = bDrawBufferCreate (graphic->dxwin, graphic->dywin, Nbyte, palette, Npalette);
   bDrawSetStyle (buffer, black, 0, 0);
   
Index: branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/bDrawObjects.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/bDrawObjects.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/kapa2/src/bDrawObjects.c	(revision 33415)
@@ -307,4 +307,5 @@
       {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  buffer->bColor_R = pixel1[pixel];
@@ -326,4 +327,5 @@
       {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  buffer->bColor_R = pixel1[pixel];
@@ -345,4 +347,5 @@
       {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  buffer->bColor_R = pixel1[pixel];
@@ -365,4 +368,5 @@
       {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  buffer->bColor_R = pixel1[pixel];
@@ -385,4 +389,5 @@
       {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  buffer->bColor_R = pixel1[pixel];
@@ -405,4 +410,5 @@
       {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  buffer->bColor_R = pixel1[pixel];
@@ -424,4 +430,5 @@
       {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  buffer->bColor_R = pixel1[pixel];
@@ -445,4 +452,5 @@
       {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  buffer->bColor_R = pixel1[pixel];
@@ -464,4 +472,5 @@
       {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  buffer->bColor_R = pixel1[pixel];
@@ -487,4 +496,5 @@
       {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  buffer->bColor_R = pixel1[pixel];
@@ -511,4 +521,5 @@
       {
 	if (scaleColor) {
+	  if (!finite(z[i])) continue;
 	  int pixel = MIN (graphic->Npixels - 2, MAX (0, z[i]*(graphic->Npixels - 1)));
 	  buffer->bColor_R = pixel1[pixel];
Index: branches/meh_branches/ppstack_test/Ohana/src/libdvo/src/dbExtractMeasures.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/libdvo/src/dbExtractMeasures.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/libdvo/src/dbExtractMeasures.c	(revision 33415)
@@ -245,8 +245,8 @@
       break;
     case MEAS_PAR: /* OK */
-      value.Flt = average[0].R;
+      value.Flt = average[0].P;
       break;
     case MEAS_PAR_ERR: /* OK */
-      value.Flt = average[0].D;
+      value.Flt = average[0].dP;
       break;
     case MEAS_CHISQ_POS: /* OK */
Index: branches/meh_branches/ppstack_test/Ohana/src/libdvo/src/dvo_set_catdir.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/libdvo/src/dvo_set_catdir.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/libdvo/src/dvo_set_catdir.c	(revision 33415)
@@ -15,5 +15,6 @@
     }
     if (catdir) {
-        current_catdir = strdup(catdir);
+	// need to keep this in the ohana memory system, so we cannot use strdup
+        current_catdir = strcreate(catdir);
     }
 }
Index: branches/meh_branches/ppstack_test/Ohana/src/libdvo/src/dvo_util.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/libdvo/src/dvo_util.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/libdvo/src/dvo_util.c	(revision 33415)
@@ -276,5 +276,5 @@
   FREE(detections);
 
-  if (NInvalidDetID) fprintf (stderr, "ERROR: Encountered %ld invalid detection IDs\n", NInvalidDetID);
+  if (NInvalidDetID) fprintf (stderr, "ERROR: Encountered %zx invalid detection IDs\n", NInvalidDetID);
 
   return (Ndetect);
Index: branches/meh_branches/ppstack_test/Ohana/src/libkapa/src/PSRotFont.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/libkapa/src/PSRotFont.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/libkapa/src/PSRotFont.c	(revision 33415)
@@ -32,4 +32,24 @@
   dY = currentfont[65].ascent;
   
+  /***
+
+      update this to use the following PS code:
+
+      /ceshow { % (string) fontsize fontname x y 
+      gsave 
+      moveto findfont exch scalefont setfont % s 
+      gsave 
+      dup false charpath flattenpath pathbbox % s x0 y0 x1 y1 
+      grestore 
+      3 -1 roll sub % s x0 x1 dy 
+      3 1 roll sub % s dy -dx 
+      2 div exch % s -dx/2 dy 
+      -2 div % s -dx/2 -dy/2 
+      rmoveto show 
+      grestore 
+      } bind def 
+
+   ***/
+
   /* apply appropriate offset */
   Xoff = Yoff = 0;
Index: branches/meh_branches/ppstack_test/Ohana/src/libohana/include/ohana.h
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/libohana/include/ohana.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/libohana/include/ohana.h	(revision 33415)
@@ -20,5 +20,5 @@
 
 // comment this out to avoid the internal Ohana memory management code
-// # define OHANA_MEMORY
+# define OHANA_MEMORY
 
 // XXX I was including these before, but RHL claims they are not needed
Index: branches/meh_branches/ppstack_test/Ohana/src/libohana/src/ohana_allocate.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/libohana/src/ohana_allocate.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/libohana/src/ohana_allocate.c	(revision 33415)
@@ -112,4 +112,7 @@
   // memblock of supplied pointer
   old = (Memblock *) in - 1;
+
+  if (old->startblock != OHANA_MEMMAGIC) ohana_memabort ("corrupt memory (%s, %d, %s)\n", file, line, func);
+  if (old->endblock != OHANA_MEMMAGIC) ohana_memabort ("corrupt memory (%s, %d, %s)\n", file, line, func);
 
   Nelem = MAX (1, Nelem);
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/cmd.astro/Makefile
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/cmd.astro/Makefile	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/cmd.astro/Makefile	(revision 33415)
@@ -43,4 +43,5 @@
 $(SRC)/galprofiles.$(ARCH).o	   \
 $(SRC)/elliprofile.$(ARCH).o	   \
+$(SRC)/ringflux.$(ARCH).o	   \
 $(SRC)/petrosian.$(ARCH).o	   \
 $(SRC)/multifit.$(ARCH).o	   \
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/cmd.astro/init.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/cmd.astro/init.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/cmd.astro/init.c	(revision 33415)
@@ -29,4 +29,5 @@
 int galradbins              PROTO((int, char **));
 int elliprofile             PROTO((int, char **));
+int ringflux                PROTO((int, char **));
 int petrosian               PROTO((int, char **));
 int mkgauss                 PROTO((int, char **));
@@ -78,4 +79,5 @@
   {1, "galradbins",  galradbins,   "generate radial vectors with interpolation along paths"},
   {1, "elliprofile", elliprofile,  "generate radial vectors with interpolation along paths"},
+  {1, "ringflux",    ringflux,     "mean flux in a ring"},
   {1, "petrosian",   petrosian,    "petrosian parameters given radial bins"},
   {1, "multifit",    multifit,     "fit multi-order spectrum"},
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/cmd.astro/ringflux.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/cmd.astro/ringflux.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/cmd.astro/ringflux.c	(revision 33415)
@@ -0,0 +1,100 @@
+# include "astro.h"
+
+# define FLUX(BUF,X,Y) {						\
+	char valid = TRUE;						\
+	valid &= (X >= 0);						\
+	valid &= (Y >= 0);						\
+	valid &= (X < Nx);						\
+	valid &= (Y < Ny);						\
+	if (valid) {							\
+	    out[Nvec] = BUF[(int)(X) + ((int)(Y))*Nx];			\
+	    Nvec++;							\
+	    if (Nvec >= vec[0].Nelements) abort();			\
+	}								\
+    }
+
+# define C1 0
+
+int ringflux (int argc, char **argv) {
+  
+  int Nx, Ny, x, y, d, radius, Radius, dRadius, Nvec;
+  double *out;
+  float *in;
+  float Xo, Yo;
+  Buffer *buf;
+  Vector *vec;
+
+  if (argc != 7) {
+    gprint (GP_ERR, "USAGE: ringflux (buffer) (vector) (Xo) (Yo) (Radius) (dRadius)\n");
+    return (FALSE);
+  }
+
+  /* select input / output buffers */
+  if ((buf = SelectBuffer (argv[1], OLDBUFFER, TRUE)) == NULL) return (FALSE);
+  if ((vec = SelectVector (argv[2], ANYVECTOR, TRUE)) == NULL) return (FALSE);
+
+  Nx = buf[0].header.Naxis[0];
+  Ny = buf[0].header.Naxis[1];
+
+  Xo = atof(argv[3]);
+  Yo = atof(argv[4]);
+
+  Radius = atoi (argv[5]);
+  dRadius = atoi (argv[6]);
+
+  // just a rough guess at the number needed
+  ResetVector (vec, OPIHI_FLT, MAX (6*Radius*dRadius, 8));
+
+  Nvec = 0;
+  for (radius = Radius; radius < Radius + dRadius; radius ++) {
+
+    x = 0;
+    y = radius;
+
+# if (C1)
+    d = 3 - 2*radius;
+# else
+    d = 5 - 4*radius;
+# endif
+
+    in = (float *) buf[0].matrix.buffer;
+    out = vec[0].elements.Flt;
+
+    while (x <= y) {
+      FLUX(in, (Xo + x), (Yo + y));
+      FLUX(in, (Xo + x), (Yo - y));
+      FLUX(in, (Xo + y), (Yo + x));
+      FLUX(in, (Xo - y), (Yo + x));
+
+      if (x > 0) {
+	FLUX(in, (Xo - x), (Yo + y));
+	FLUX(in, (Xo - x), (Yo - y));
+	FLUX(in, (Xo - y), (Yo - x));
+	FLUX(in, (Xo + y), (Yo - x));
+      }
+      if (Nvec >= vec[0].Nelements - 8) {
+	  // counting error here
+	ResetVector (vec, OPIHI_FLT, vec[0].Nelements + 64);
+	out = vec[0].elements.Flt;
+      }
+
+      if (d < 0) {
+# if (C1)      
+	d = d + 4*x + 6;
+# else
+	d = d + 8*x + 4;
+# endif
+      } else {
+# if (C1)      
+	d = d + 4*(x-y) + 10;
+# else
+	d = d + 8*(x-y) + 8;
+# endif
+	y--;
+      }
+      x++;
+    }
+  }
+  vec[0].Nelements = Nvec;
+  return (TRUE);
+}
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/cmd.basic/echo.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/cmd.basic/echo.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/cmd.basic/echo.c	(revision 33415)
@@ -3,5 +3,11 @@
 int echo (int argc, char **argv) {
   
-  int i;
+  int i, N, RETURN_CHAR;
+
+  RETURN_CHAR = TRUE;
+  if ((N = get_argument (argc, argv, "-no-return"))) {
+    remove_argument (N, &argc, argv);
+    RETURN_CHAR = FALSE;
+  }
 
   for (i = 1; i < argc - 1; i++) {
@@ -9,5 +15,9 @@
   }
   if (argc >= 2) {
+    if (RETURN_CHAR) {
       gprint (GP_LOG, "%s\n", argv[argc - 1]);
+    } else {
+      gprint (GP_LOG, "%s", argv[argc - 1]);
+    }
   }
   return (TRUE);
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/cmd.data/plot.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/cmd.data/plot.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/cmd.data/plot.c	(revision 33415)
@@ -53,4 +53,9 @@
     return (FALSE);
   }
+  if (dypvec && (dypvec->Nelements != xvec->Nelements)) goto mismatch;
+  if (dymvec && (dymvec->Nelements != xvec->Nelements)) goto mismatch;
+  if (dxpvec && (dxpvec->Nelements != xvec->Nelements)) goto mismatch;
+  if (dxmvec && (dxmvec->Nelements != xvec->Nelements)) goto mismatch;
+
   Npts = xvec[0].Nelements;
   if (Npts == 0) return (TRUE);
@@ -69,3 +74,7 @@
   }
   return (TRUE);
+
+mismatch:
+  gprint (GP_ERR, "error and data vector lengths are mismatched\n");
+  return (FALSE);
 }
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/cmd.data/read_vectors.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/cmd.data/read_vectors.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/cmd.data/read_vectors.c	(revision 33415)
@@ -23,5 +23,5 @@
 int read_vectors (int argc, char **argv) {
   
-  int i, j, Nskip, Nvec, *col, done, status, IsCSV;
+    int i, j, Nskip, Nvec, *col, done, status, IsCSV, VERBOSE;
   int Nbytes, nbytes, Nstart, NELEM, N, nread;
   char *colstr, *c0, *c1, *buffer, *extname;
@@ -51,4 +51,10 @@
     remove_argument (N, &argc, argv);
     IsCSV = TRUE;
+  }
+
+  VERBOSE = FALSE;
+  if ((N = get_argument (argc, argv, "-v"))) {
+    remove_argument (N, &argc, argv);
+    VERBOSE = TRUE;
   }
 
@@ -121,7 +127,4 @@
     scan_line (f, buffer);
   }
-
-  int Nfield = 0;
-  int Nline = 0;
 
   Nstart = 0;
@@ -157,17 +160,28 @@
 	    status = dparse (&value, col[i], c0);
 	  }
-	  vec[i][0].elements.Flt[N] = value;
 	  if (status) {
-	      Nfield ++;
+	      vec[i][0].elements.Flt[N] = value;
+	  } else {
+	      vec[i][0].elements.Flt[N] = NAN;
+	      if (VERBOSE) {
+		  if (IsCSV) {
+		      gprint (GP_ERR, "suspect field: %d (%s) in %s\n", col[i], argv[2*i+2], c0);
+		  } else {
+		      gprint (GP_ERR, "suspect field: %d in %s\n", col[i], c0);
+		  }
+	      }
 	  }
-	  if (!status) vec[i][0].elements.Flt[N] = NAN;
-	}
-	if (status) N++;
-      }
-      Nline ++;
-      if (c1) {
-	  // fprintf (stderr, "line %d, chars %ld, fields %d\n", Nline, (c1 - c0), Nfield);
-      }
-      Nfield = 0;
+	}
+	if (status) {
+	    N++;
+	} else {
+	    if (VERBOSE && FALSE) {
+		char temp[32];
+		strncpy (temp, c0, 32);
+		temp[31] = 0;
+		gprint (GP_ERR, "skip line %s\n\n", temp);
+	    }
+	}
+      }
       c0 = c1 + 1;
       if (N == NELEM) {
@@ -177,5 +191,4 @@
 	}
       }
-	
     }
   }
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/cmd.data/vstats.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/cmd.data/vstats.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/cmd.data/vstats.c	(revision 33415)
@@ -169,6 +169,6 @@
 
     // we are going to do another pass: mark the entries to skip
-    pmin = max;
-    pmax = min;
+    pmin = min;
+    pmax = max;
     if (iter < Niter - 1) {
       if (vec[0].type == OPIHI_FLT) {
@@ -208,4 +208,5 @@
 skip:
   FREE(mask);
+  if (mask) mask = NULL;
 
   if (!Quiet) {
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/dvo/skycoverage.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/dvo/skycoverage.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/dvo/skycoverage.c	(revision 33415)
@@ -202,5 +202,5 @@
 	V[ys*Nx + xs] = ShowDensity ?  0 : 2;
       } else {
-	V[ys*Nx + xs] = ShowDensity ? -1 : 0;
+	V[ys*Nx + xs] = ShowDensity ? NAN : 0;
       }
     }
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/include/pantasks.h
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/include/pantasks.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/include/pantasks.h	(revision 33415)
@@ -40,4 +40,5 @@
       TASK_END, 
       TASK_HOST, 
+      TASK_NICE, 
       TASK_STDOUT, 
       TASK_STDERR, 
@@ -115,4 +116,5 @@
 
   int active;
+  int priority;
 
 } Task;
@@ -159,4 +161,5 @@
 
   JobMode     mode;			/* local or controller? */
+  int     priority;
   char   *realhost;
 
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/include/pcontrol.h
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/include/pcontrol.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/include/pcontrol.h	(revision 33415)
@@ -119,4 +119,5 @@
   int          exit_status;
   int          Reset;
+  int          priority;
   JobMode      mode;
   JobStat      state;
@@ -152,4 +153,25 @@
 } Host;
 
+# if (USE_LLIST)
+typedef struct StackItem {
+    StackItem *next;
+    StackItem *prev;
+    void *object;
+    char *name;
+    int   id;
+} StackItem;
+
+/* the Jobs and Hosts are managed in a set of Stacks which define their state */
+typedef struct {
+    StackItem *head;
+    StackItem *tail; // use this?
+    int    Nobject;
+# ifdef THREADED    
+  pthread_mutex_t mutex;
+# endif
+} Stack;
+
+# else
+
 /* the Jobs and Hosts are managed in a set of Stacks which define their state */
 typedef struct {
@@ -163,4 +185,5 @@
 # endif
 } Stack;
+# endif
 
 /* XXX if this is hard-wired, we can't change shell name in StartHost */
@@ -291,5 +314,5 @@
 Job   *PullJobByID (IDtype JobID, int *StackID);
 Job   *PullJobFromStackByID (int StackID, int ID);
-IDtype AddJob (char *hostname, JobMode mode, int timeout, int argc, char **argv, int Nxhosts, char **xhosts);
+IDtype AddJob (char *hostname, JobMode mode, int timeout, int priority, int argc, char **argv, int Nxhosts, char **xhosts);
 void   DelJob (Job *job);
 Host  *UnlinkJobAndHost (Job *job);
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/include/shell.h
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/include/shell.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/include/shell.h	(revision 33415)
@@ -184,7 +184,7 @@
 
 /* socket functions */
-int InitServerSocket (SockAddress *Address);
+int InitServerSocket (SockAddress *Address, char *hostname, char *portinfo);
 int WaitServerSocket (int InitSocket, SockAddress *Address);
-int GetClientSocket (char *hostname);
+int GetClientSocket (char *hostname, char *portinfo);
 int InitServerSocket_Named (char *hostname, SockAddress *Address);
 int DefineValidIP (void);
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/lib.shell/SocketOps.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/lib.shell/SocketOps.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/lib.shell/SocketOps.c	(revision 33415)
@@ -1,3 +1,5 @@
 # include "shell.h"
+# include <unistd.h>
+# define HOST_NAME_MAX 256
 
 # define MY_PORT 2000
@@ -11,34 +13,25 @@
 static int *VALID;
 
-int InitServerSocket (SockAddress *Address) {
-
+int GetPortRange (int *start, int *stop, char *portinfo);
+
+int InitServerSocket (SockAddress *Address, char *hostname, char *portinfo) {
+
+  int start, stop;
   int status, InitSocket, length;
-
-# if (0)
-  struct hostent  *host;
-  char tmpline[80], hostip[80];
-
-  host = gethostbyname (hostname);
-  bzero (hostip, 80);
-  for (i = 0; i < host[0].h_length; i++) {
-    sprintf (tmpline, "%u", (0xff & host[0].h_addr[i]));
-    strcat (hostip, tmpline);
-    if (i < host[0].h_length - 1) strcat (hostip, ".");
-  }
-# endif
-  
+  char myHostname[HOST_NAME_MAX];
+
+  status = gethostname (myHostname, HOST_NAME_MAX);
+
+  fprintf (stderr, "target host: %s, real host: %s\n", hostname, myHostname);
+
+  GetPortRange (&start, &stop, portinfo);
+
+  fprintf (stderr, "using port range %d - %d\n", start, stop);
+
   Address[0].sin_family = AF_INET;
-  Address[0].sin_port   = MY_PORT;
+  Address[0].sin_port   = start;
   Address[0].sin_addr.s_addr = INADDR_ANY; // use this line to bind any address / port?
 
 retry_server:
-
-# if (0)  
-  status = inet_aton (hostip, &Address[0].sin_addr);
-  if (!status) {
-    gprint (GP_ERR, "invalid address\n");
-    exit (4);
-  }
-# endif
 
   length = sizeof(Address[0]);
@@ -74,5 +67,5 @@
     if (errno == EADDRINUSE) {
 	Address[0].sin_port ++;
-	if (Address[0].sin_port > MY_PORT + 10) {
+	if (Address[0].sin_port > stop) {
 	  fprintf (stderr, "failed to find a usable port\n");
 	  exit (6);
@@ -154,7 +147,49 @@
 }
 
-int GetClientSocket (char *hostname) {
-
-  int i, status, InitSocket, length;
+int GetPortRange (int *start, int *stop, char *portinfo) {
+
+  // portinfo is a port or port range of the form NN or NN:MM
+  *start = MY_PORT;
+  *stop = *start + 10;
+  if (!portinfo) return TRUE;
+  if (*portinfo == 0) return TRUE;
+
+  char *endptr;
+  *start = strtol (portinfo, &endptr, 0);
+  *stop = *start + 10; // default range of 10
+  if (!endptr) {
+    gprint (GP_ERR, "error in port range parsing : %s\n", portinfo);
+    exit (20);
+  }
+  if (endptr == portinfo) {
+    gprint (GP_ERR, "error in port range (%s), must be in form NN:MM or NN\n", portinfo);
+    exit (20);
+  }
+  if (*endptr == 0) return TRUE;
+
+  if (*endptr != ':') {
+    gprint (GP_ERR, "error in port range %s (wrong range separator, must be in form NN:MM)\n", portinfo);
+    exit (20);
+  }
+  char *ptr = endptr + 1;
+  *stop = strtol (ptr, &endptr, 0);
+  if (endptr == ptr)  {
+    gprint (GP_ERR, "error in port range (%s), must be in form NN:MM or NN\n", portinfo);
+    exit (20);
+  }
+  if (*stop - *start > 20) {
+    gprint (GP_ERR, "error in port range (%s), range must be 20 or less\n", portinfo);
+    exit (20);
+  }
+  if (*stop < *start) {
+    gprint (GP_ERR, "error in port range (%s), stop must >= start\n", portinfo);
+    exit (20);
+  }
+  return TRUE;
+}
+
+int GetClientSocket (char *hostname, char *portinfo) {
+
+  int i, status, InitSocket, length, start, stop;
   SockAddress Address;
   struct hostent  *host;
@@ -169,10 +204,12 @@
   }
 
+  GetPortRange (&start, &stop, portinfo);
+
   if (DEBUG) {
-    gprint (GP_ERR, "trying %s (%s:%d)...", host[0].h_name, hostip, MY_PORT);
+    gprint (GP_ERR, "trying %s (%s:%d)...", host[0].h_name, hostip, start);
   }
 
   Address.sin_family = AF_INET;
-  Address.sin_port   = MY_PORT;
+  Address.sin_port   = start;
 
 retry_client:
@@ -195,5 +232,5 @@
     if (errno == ECONNREFUSED) {
       Address.sin_port ++;
-      if (Address.sin_port > MY_PORT + 10) exit (12);
+      if (Address.sin_port > stop) exit (12);
       goto retry_client;
     }
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/lib.shell/multicommand.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/lib.shell/multicommand.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/lib.shell/multicommand.c	(revision 33415)
@@ -13,5 +13,5 @@
 void multicommand_InitServer () {
 
-  char hostname[256], PASSWORD[256];
+  char hostname[256], PASSWORD[256], portinfo[256];
 
   if (server != 0) {
@@ -27,6 +27,10 @@
   }
 
+  /* is a port range defined? otherwise use the default */
+  memset (portinfo, 0, 256);
+  VarConfig ("PANTASKS_SERVER_PORT", "%s", portinfo);
+
   /* attempt to connect to the server */
-  server = GetClientSocket (hostname);
+  server = GetClientSocket (hostname, portinfo);
 
   /* here we can perform the security handshaking */
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/ControllerOps.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/ControllerOps.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/ControllerOps.c	(revision 33415)
@@ -258,4 +258,10 @@
       sprintf (cmd, "job -host %s", job[0].task[0].host);
     }
+  }
+  
+  if (job[0].priority) {
+    char tmp[64];
+    snprintf (tmp, 64, " -nice %d", job[0].priority);
+    strcat (cmd, tmp);
   }
 
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/JobOps.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/JobOps.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/JobOps.c	(revision 33415)
@@ -107,4 +107,5 @@
     job[0].mode = JOB_CONTROLLER;
   }
+  job[0].priority = task[0].priority;
 
   /* we need our own copy of task[0].argv argc is the number of valid args, like the usual command line.  we
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/LocalJob.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/LocalJob.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/LocalJob.c	(revision 33415)
@@ -161,4 +161,14 @@
   if (VerboseMode()) gprint (GP_ERR, "local job launched\n");
 
+  /* set nice level for the child process -- maybe I should not exit here... */
+  if (job[0].priority) {
+      status = setpriority (PRIO_PROCESS, pid, job[0].priority);
+      if (status == -1) {
+	  gprint (GP_ERR, "error setting priority\n");
+	  perror ("setpriority: ");
+	  exit (2);
+      }
+  }
+
   /* close the other ends of the pipes */
   close (stdout_fd[1]); stdout_fd[1] = 0;
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/Makefile
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/Makefile	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/Makefile	(revision 33415)
@@ -83,4 +83,5 @@
 $(SRC)/task.$(ARCH).o \
 $(SRC)/task_host.$(ARCH).o \
+$(SRC)/task_nice.$(ARCH).o \
 $(SRC)/task_nmax.$(ARCH).o \
 $(SRC)/task_active.$(ARCH).o \
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/TaskOps.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/TaskOps.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/TaskOps.c	(revision 33415)
@@ -487,4 +487,5 @@
 
   NewTask[0].active = TRUE;
+  NewTask[0].priority = 0;
   return (NewTask);
 }
@@ -595,4 +596,5 @@
   if (!strcasecmp (command, "END"))       hash = TASK_END;
   if (!strcasecmp (command, "HOST"))      hash = TASK_HOST;
+  if (!strcasecmp (command, "NICE"))      hash = TASK_NICE;
   if (!strcasecmp (command, "NMAX"))      hash = TASK_NMAX;
   if (!strcasecmp (command, "ACTIVE"))    hash = TASK_ACTIVE;
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/init.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/init.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/init.c	(revision 33415)
@@ -4,4 +4,5 @@
 int task            PROTO((int, char **));
 int task_host       PROTO((int, char **));
+int task_nice       PROTO((int, char **));
 int task_nmax       PROTO((int, char **));
 int task_npending   PROTO((int, char **));
@@ -33,4 +34,5 @@
   {1, "halt",       halt,          "halt the scheduler (no job harvesting)"},
   {1, "host",       task_host,     "define host machine for a task"},
+  {1, "nice",       task_nice,     "set nice priority level for a task"},
   {1, "ipptool2book", ipptool2book, "convert queue with ipptool output to book"},
   {1, "kill",       kill_job,      "kill job"},
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/init_server.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/init_server.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/init_server.c	(revision 33415)
@@ -4,4 +4,5 @@
 int task            PROTO((int, char **));
 int task_host       PROTO((int, char **));
+int task_nice       PROTO((int, char **));
 int task_nmax       PROTO((int, char **));
 int task_npending   PROTO((int, char **));
@@ -34,4 +35,5 @@
   {1, "delete",     delete_job,    "delete job"},
   {1, "host",       task_host,     "define host machine for a task"},
+  {1, "nice",       task_nice,     "set nice priority level for a task"},
   {1, "ipptool2book", ipptool2book, "convert queue with ipptool output to book"},
   {1, "kill",       kill_job,      "kill job"},
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/pantasks_server.c.in
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/pantasks_server.c.in	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/pantasks_server.c.in	(revision 33415)
@@ -18,4 +18,5 @@
 int main (int argc, char **argv) {
   
+  char hostname[256], portinfo[256];
   char log_stdout[1024], log_stderr[1024];
   pthread_t JobsAndTasksThread;
@@ -100,6 +101,16 @@
      pass them to the ListenClient thread */
 
+  /* find the defined server hostname */
+  if (VarConfig ("PANTASKS_SERVER", "%s", hostname) == NULL) {
+    gprint (GP_ERR, "pantasks server host undefined\n");
+    exit (31);
+  }
+
+  /* is a port range defined? otherwise use the default */
+  memset (portinfo, 0, 256);
+  VarConfig ("PANTASKS_SERVER_PORT", "%s", portinfo);
+
   /* create the listening socket */
-  InitSocket = InitServerSocket (&Address);
+  InitSocket = InitServerSocket (&Address, hostname, portinfo);
   
   InitPassword ();
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/task.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/task.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/task.c	(revision 33415)
@@ -72,4 +72,5 @@
       case TASK_NMAX:
       case TASK_HOST:
+      case TASK_NICE:
       case TASK_EXIT:
       case TASK_EXEC:
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/task_nice.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/task_nice.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/task_nice.c	(revision 33415)
@@ -0,0 +1,34 @@
+# include "pantasks.h"
+
+int task_nice (int argc, char **argv) {
+
+  char *endptr = NULL;
+  int priority;
+  Task *task;
+
+  if (argc != 2) {
+    gprint (GP_ERR, "USAGE: nice (priority)\n");
+    return (FALSE);
+  }
+
+  JobTaskLock();
+  task = GetNewTask ();
+  if (task == NULL) {
+    gprint (GP_ERR, "ERROR: not defining or running a task\n");
+    JobTaskUnlock();
+    return (FALSE);
+  }
+
+  priority = strtol (argv[1], &endptr, 10);
+  if (*endptr) goto fail;
+  if (priority < 0) goto fail;
+  if (priority > 20) goto fail;
+
+  task[0].priority = priority;
+  JobTaskUnlock();
+  return (TRUE);
+
+fail:
+    gprint (GP_ERR, "ERROR: nice (priority) -- priority must be an integer 0 to 20\n");
+    return (FALSE);
+}
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/test/nice.sh
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/test/nice.sh	(revision 33415)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/test/nice.sh	(revision 33415)
@@ -0,0 +1,70 @@
+
+# a basic task which just runs 'sleep 10'
+task	       nicetask_local
+  command      sleep 10
+  host         local
+  nice         10
+
+  periods      -poll 0.1
+  periods      -exec 1.0
+  periods      -timeout 20
+  active       true
+  npending 5
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    echo "nicetask_local start"
+  end
+
+  # success
+  task.exit    0
+    echo "nicetask_local stop"
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "failure: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "timeout"
+  end
+end
+
+# a basic task which just runs 'sleep 10'
+task	       meantask_local
+  command      sleep 10
+  host         local
+
+  periods      -poll 0.1
+  periods      -exec 1.0
+  periods      -timeout 20
+  active       true
+  npending 5
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    echo "meantask_local start"
+  end
+
+  # success
+  task.exit    0
+    echo "meantask_local stop"
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "failure: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "timeout"
+  end
+end
+
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/test/nice_remote.sh
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/test/nice_remote.sh	(revision 33415)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/pantasks/test/nice_remote.sh	(revision 33415)
@@ -0,0 +1,70 @@
+
+# a basic task which just runs 'sleep 10'
+task	       nicetask_local
+  command      sleep 10
+  host         anyhost
+  nice         10
+
+  periods      -poll 0.1
+  periods      -exec 1.0
+  periods      -timeout 20
+  active       true
+  npending 5
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    echo "nicetask_local start"
+  end
+
+  # success
+  task.exit    0
+    echo "nicetask_local stop"
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "failure: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "timeout"
+  end
+end
+
+# a basic task which just runs 'sleep 10'
+task	       meantask_local
+  command      sleep 10
+  host         anyhost
+
+  periods      -poll 0.1
+  periods      -exec 1.0
+  periods      -timeout 20
+  active       true
+  npending 5
+  
+  stdout tmp.txt
+  stderr tmp.txt
+
+  task.exec
+    echo "meantask_local start"
+  end
+
+  # success
+  task.exit    0
+    echo "meantask_local stop"
+  end
+
+  # default exit status
+  task.exit    default
+    echo       "failure: exit status: $EXIT"
+  end
+
+  # operation times out?
+  task.exit    timeout
+    echo       "timeout"
+  end
+end
+
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/pclient/job.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/pclient/job.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/pclient/job.c	(revision 33415)
@@ -3,6 +3,13 @@
 int job (int argc, char **argv) {
 
-  int i, pid, status;
+  int i, N, pid, status, priority;
   char **targv;
+
+  priority = 0;
+  if ((N = get_argument (argc, argv, "-nice"))) {
+    remove_argument (N, &argc, argv);
+    priority = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
 
   if (argc < 2) {
@@ -54,4 +61,14 @@
   }
 
+  /* set nice level for the child process */
+  if (priority) {
+      status = setpriority (PRIO_PROCESS, pid, priority);
+      if (status == -1) {
+	  gprint (GP_ERR, "error setting priority\n");
+	  perror ("setpriority: ");
+	  exit (2);
+      }
+  }
+
   /* free temporary arg list */
   for (i = 0; i < argc - 1; i++) {
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/pcontrol/CheckIdleHost.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/pcontrol/CheckIdleHost.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/pcontrol/CheckIdleHost.c	(revision 33415)
@@ -5,4 +5,27 @@
 // a (temporary?) work-around for the problem that the remote pclient job tends to grow
 // too large over time.
+
+/****
+     
+     queueing strategy:
+
+     We have a problem when the queue contains many jobs of different processing times.  If we
+     start with an equal number of jobs in two classes, fast and slow, eventually, we will end
+     up with all hosts running the slow jobs and the fast jobs completely blocked.  If J1 takes
+     T1 and J2 takes T2, and jobs have equal probability to land in a slot... ??
+
+     It seems like we should boost the probability of the fast jobs over the slow jobs (of
+     course, the end result of that will be all fast jobs draining and the slow jobs hanging
+     around).  
+
+     There is also a problem related to LAP, in that chip stage has a huge number of tasks in
+     the queue (effectively infinite).  Make the probability of the job being selected
+     proportional to something.  In any case, we need a user-controllable way to change the
+     probability of selection
+
+     As things are implemented below, the selection order depends on the queue order.  The
+     easiest way to modify the probabilities is to re-sort based on something.
+
+ ****/
 
 static float MAX_WANTHOST_WAIT = 10.0;
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/pcontrol/JobOps.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/pcontrol/JobOps.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/pcontrol/JobOps.c	(revision 33415)
@@ -195,5 +195,5 @@
 }
 
-IDtype AddJob (char *hostname, JobMode mode, int timeout, int argc, char **argv, int Nxhosts, char **xhosts) {
+IDtype AddJob (char *hostname, JobMode mode, int timeout, int priority, int argc, char **argv, int Nxhosts, char **xhosts) {
 
   int JobID;
@@ -220,4 +220,5 @@
 
   job[0].mode     = mode;
+  job[0].priority = priority;
 
   job[0].state = 0;
@@ -270,2 +271,35 @@
   FREE (job);
 }
+
+/*** ResortJobStack can be used to adjust priorities based on some info we supply.  This
+     is not finished -- to finish this, I need to define the metric of interest and
+     arrange for that to be passed to the jobs in pcontrol
+
+void ResortJobStack (int StackID) {
+
+  Stack *stack = GetJobStack (StackID);
+  LockStack (stack);
+
+# define SWAPFUNC(A,B){				\
+    void *tmpObject = stack[0].object[A];	\
+    stack[0].object[A] = stack[0].object[B];	\
+    stack[0].object[B] = tmpObject;		\
+    void *tmpName = stack[0].name[A];		\
+    stack[0].name[A] = stack[0].name[B];	\
+    stack[0].name[B] = tmpName;			\
+    void *tmpID = stack[0].id[A];		\
+    stack[0].id[A] = stack[0].id[B];		\
+    stack[0].id[B] = tmpID;			\
+  }
+
+# define COMPARE(A,B)(((Job *)stack[0].object[A]).VALUE < ((Job *)stack[0].object[B]).VALUE)
+
+  OHANA_SORT (stack[0].Nobject, COMPARE, SWAPFUNC);
+
+# undef SWAPFUNC
+# undef COMPARE
+
+  UnlockStack (stack);
+}
+
+***/
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/pcontrol/StackOpsLL.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/pcontrol/StackOpsLL.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/pcontrol/StackOpsLL.c	(revision 33415)
@@ -0,0 +1,227 @@
+# include "pcontrol.h"
+
+/* Stacks and thread locks: interacting with the Stacks needs to be thread-safe so that the user may
+ * perform operations which interact with the stacks at the same time that the background loops
+ * check the current status of the jobs and hosts in the different stacks.  The simplest way in
+ * which the stacks are made thread safe is to lock them with a mutex before every interaction
+ */
+
+# define DEBUG 0
+
+void PrintStackInfo (Stack *stack, const char *func) {
+
+  if (!DEBUG) return;
+  fprintf (stderr, "%s: %p  ", func, stack);
+  fprintf (stderr, "objects: %p  ", stack[0].object);
+  fprintf (stderr, "Nobjects: %d (linked list)\n", stack[0].Nobject);
+}
+
+/* allocate stack: create head node (empty node?) */
+Stack *InitStack () {
+
+  Stack *stack;
+
+  ALLOCATE (stack, Stack, 1);
+
+  stack->Nobject = 0;
+  stack->head = NULL;
+  stack->tail = NULL;
+
+# ifdef THREADED
+  pthread_mutex_init (&stack[0].mutex, NULL);
+# endif
+  return (stack);
+}
+
+void FreeStack (Stack *stack) {
+
+  ASSERT (!stack->head, "stack items not freed");
+  ASSERT (!stack->tail, "stack items not freed");
+  free (stack);
+}
+
+/* STACK_TOP == 0, STACK_BOTTOM == -1 
+   in this version, entries by seq number other than head and tail are not allowed
+ */
+
+StackItem InitStackItem (void *object, char *name, int id) {
+
+  StackItem *item;
+  ALLOCATE (item, StackItem, 1);
+  item->object = object;
+  item->name = name;
+  item->id = id;
+
+  return item;
+}
+
+/* push object on stack at given location */
+int PushStack (Stack *stack, int where, void *object, int id, char *name) {
+
+  int i;
+
+  ASSERT (where == STACK_TOP || where == STACK_BOTTOM, "invalid location");
+  ASSERT (stack != NULL, "stack not set");
+
+  PrintStackInfo (stack, __func__);
+  LockStack (stack);
+
+  // make this a StackItem generator
+  StackItem *item = InitStackItem (object, name, id);
+  ALLOCATE (item, StackItem, 1);
+
+  /* there is a special case when the stack is empty.  in this case, we have to update
+     both head and tail.
+   */
+  if (!stack->head && !stack->tail) {
+    stack->head = item;
+    stack->tail = item;
+    stack->Nobject ++;
+    UnlockStack (stack);
+    return (TRUE);
+  }
+
+  if (where == STACK_TOP) {
+    StackHead *oldHead = stack->head;
+    stack->head = item;
+    item->next = oldHead;
+    oldHead->prev = item;
+  } else {
+    StackHead *oldTail = stack->tail;
+    stack->tail = item;
+    item->prev = oldTail;
+    oldTail->next = item;
+  }
+  stack->Nobject ++;
+  UnlockStack (stack);
+  return (TRUE);
+}
+
+/* get object from specified point in stack (negative == distance from end) */
+void *PullStackByLocation (Stack *stack, int where) {
+
+  void *object;
+  
+  ASSERT (where == STACK_TOP || where == STACK_BOTTOM, "invalid location");
+  ASSERT (stack != NULL, "stack not set");
+
+  PrintStackInfo (stack, __func__);
+  LockStack (stack);
+
+  if (
+
+  object = stack[0].object[where];
+  RemoveStackEntry (stack, where);
+  UnlockStack (stack); 
+  return (object);
+}
+
+/* get object from stack which matches name */
+void *PullStackByName (Stack *stack, char *name) {
+
+  int i;
+  void *object;
+
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  LockStack (stack);
+
+  for (i = 0; i < stack[0].Nobject; i++) {
+    if (strcasecmp (stack[0].name[i], name)) continue;
+
+    /* here is the element of interest */
+    object = stack[0].object[i];
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack); 
+    return (object);
+  }
+  UnlockStack (stack); 
+  return (NULL);
+}
+
+/* get object from point in stack (negative == distance from end) */
+void *PullStackByID (Stack *stack, int id) {
+
+  int i;
+  void *object;
+  
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  LockStack (stack);
+
+  for (i = 0; i < stack[0].Nobject; i++) {
+    if (stack[0].id[i] != id) continue;
+
+    /* here is the element of interest */
+    object = stack[0].object[i];
+    RemoveStackEntry (stack, i);
+    UnlockStack (stack); 
+    return (object);
+  }
+  UnlockStack (stack); 
+  return (NULL);
+}
+
+/* should only be called if you know where is a valid entry */
+int RemoveStackEntry (Stack *stack, int where) {
+
+  int i;
+
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+
+  if (where < 0) abort();
+  if (where >= stack[0].Nobject) abort();
+  if (stack[0].Nobject < 1) abort();
+
+  /* shift the remaining entries by one */
+  /* XXX free associated memory */
+  stack[0].Nobject --;
+  for (i = where; i < stack[0].Nobject; i++) {
+    stack[0].object[i] = stack[0].object[i+1];
+    stack[0].name[i]   = stack[0].name[i+1];
+    stack[0].id[i]     = stack[0].id[i+1];
+  }
+  return (TRUE);
+}
+
+/* should only be called if you manually lock the stack */
+void *RemoveStackByID (Stack *stack, int id) {
+
+  int i;
+  void *object;
+  
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  for (i = 0; i < stack[0].Nobject; i++) {
+    if (stack[0].id[i] != id) continue;
+
+    /* here is the element of interest */
+    object = stack[0].object[i];
+    RemoveStackEntry (stack, i);
+    return (object);
+  }
+  return (NULL);
+}
+
+void LockStack (Stack *stack) {
+# ifdef THREADED
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  pthread_mutex_lock (&stack[0].mutex);
+# endif
+  return;
+}
+
+void UnlockStack (Stack *stack) {
+# ifdef THREADED
+  PrintStackInfo (stack, __func__);
+  ASSERT (stack != NULL, "stack not set");
+  pthread_mutex_unlock (&stack[0].mutex);
+# endif
+  return;
+}
+
+/*** I need a function to sort a stack by something.
+     I also need to replace these arrays with linked lists.
+ ***/
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/pcontrol/StartJob.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/pcontrol/StartJob.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/pcontrol/StartJob.c	(revision 33415)
@@ -25,4 +25,9 @@
   bzero (line, Nline);
   strcpy (line, "job");
+  if (job[0].priority) {
+    char tmp[64];
+    snprintf (tmp, 64, " -nice %d", job[0].priority);
+    strcat (line, tmp);
+  }
   for (i = 0; i < job[0].argc; i++) {
     strcat (line, " ");
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/pcontrol/job.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/pcontrol/job.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/pcontrol/job.c	(revision 33415)
@@ -5,5 +5,5 @@
   char *Host = NULL;
   char **targv = NULL;
-  int i, N, Mode, targc, Timeout;
+  int i, N, Mode, targc, Timeout, priority;
   IDtype JobID;
   char **xhosts = NULL;
@@ -43,4 +43,11 @@
   }
 
+  priority = 0;
+  if ((N = get_argument (argc, argv, "-nice"))) {
+    remove_argument (N, &argc, argv);
+    priority = atoi (argv[N]);
+    remove_argument (N, &argc, argv);
+  }
+
   xhosts = NULL;
   Nxhosts = 0;
@@ -60,8 +67,5 @@
   }
 
-  if (argc < 2) {
-    FREE (Host);
-    goto usage;
-  }
+  if (argc < 2) goto usage;
   
   targc = argc - 1;
@@ -72,5 +76,5 @@
 
   // a JobID < 0 mean the job was not accepted
-  JobID = AddJob (Host, Mode, Timeout, targc, targv, Nxhosts, xhosts);
+  JobID = AddJob (Host, Mode, Timeout, priority, targc, targv, Nxhosts, xhosts);
   gprint (GP_LOG, "JobID: %d\n", (int) JobID);
   return (TRUE);
Index: branches/meh_branches/ppstack_test/Ohana/src/opihi/pcontrol/status.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/opihi/pcontrol/status.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/opihi/pcontrol/status.c	(revision 33415)
@@ -59,8 +59,9 @@
     }
 
+    PrintID (GP_LOG, job[0].JobID);
+    gprint (GP_LOG, " %2d ", job[0].priority);
     for (j = 0; j < job[0].argc; j++) {
       gprint (GP_LOG, "%s ", job[0].argv[j]);
     }
-    PrintID (GP_LOG, job[0].JobID);
     gprint (GP_LOG, "\n");
   }
Index: branches/meh_branches/ppstack_test/Ohana/src/relastro/Makefile
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/relastro/Makefile	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/relastro/Makefile	(revision 33415)
@@ -62,4 +62,6 @@
 $(SRC)/high_speed_objects.$(ARCH).o  \
 $(SRC)/high_speed_utils.$(ARCH).o  \
+$(SRC)/relastro_merge_source.$(ARCH).o  \
+$(SRC)/resort_catalog.$(ARCH).o  \
 $(SRC)/relastroVisual.$(ARCH).o
 
Index: branches/meh_branches/ppstack_test/Ohana/src/relastro/doc/2M1835+32_xxxx_astrom_summary.txt
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/relastro/doc/2M1835+32_xxxx_astrom_summary.txt	(revision 33415)
+++ branches/meh_branches/ppstack_test/Ohana/src/relastro/doc/2M1835+32_xxxx_astrom_summary.txt	(revision 33415)
@@ -0,0 +1,9 @@
+#    JD          dT (yr)      RA (deg)        Dec (deg)   dRA_nopm dDe_nopm  dRA_O-C  dDe_O-C  dRA_err  dDe_err Nfr    FWHM     S/N     parms
+2455289.46232  11.952044   278.907656228    32.996014203  212.415   62.135   11.086   45.005   16.240   15.494    1   0.000     -NaN  -0.068662   00    0.7  2.9  94.704
+2450923.97830   0.000000   278.907921440    32.998500058  188.709   67.052   -0.110    8.130   70.000   70.000    1   0.000     -NaN  -0.748337   01   -0.0  0.1   1.537
+2455449.75502  12.390901   278.907522057    32.995890174 -234.674  -55.956  -36.700 -101.438   21.245   21.245    1   0.000     -NaN   0.173250   02   -1.7 -4.8   1.032
+2455449.75984  12.390915   278.907530212    32.995944993 -205.316  141.404   -7.336   95.933   21.164   21.164    1   0.000     -NaN   0.000000   03   -0.3  4.5   1.036
+2455459.75031  12.418267   278.907538552    32.995895676 -173.054  -15.668   34.093  -37.165   21.219   21.219    1   0.000     -NaN   0.000000   04    1.6 -1.8   1.055
+2455459.76131  12.418297   278.907532082    32.995902888 -196.341   10.315   10.812  -11.155   21.191   21.191    1   0.000     -NaN   0.000000   05    0.5 -0.5   1.073
+2455668.07657  12.988633   278.907629935    32.995793586  202.620   43.635    9.726   -6.002   21.200   21.200    1   0.000     -NaN   0.000000   06    0.5 -0.3   1.095
+2455668.08611  12.988659   278.907621238    32.995797113  171.312   56.350  -21.572    6.692   21.256   21.256    1   0.000     -NaN   0.000000   07   -1.0  0.3   1.075
Index: branches/meh_branches/ppstack_test/Ohana/src/relastro/include/relastro.h
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/relastro/include/relastro.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/relastro/include/relastro.h	(revision 33415)
@@ -15,5 +15,5 @@
 typedef enum {FIT_NONE, FIT_AVERAGE, FIT_PM_ONLY, FIT_PAR_ONLY, FIT_PM_AND_PAR} FitMode;
 
-typedef enum {TARGET_NONE, TARGET_OBJECTS, TARGET_SIMPLE, TARGET_CHIPS, TARGET_MOSAICS, TARGET_HIGH_SPEED} FitTarget;
+typedef enum {TARGET_NONE, TARGET_OBJECTS, TARGET_SIMPLE, TARGET_CHIPS, TARGET_MOSAICS, TARGET_HIGH_SPEED, TARGET_MERGE_SOURCE} FitTarget;
 
 typedef struct {
@@ -94,4 +94,9 @@
 char   SKY_TABLE[256];
 int    SKY_DEPTH;  /** XXX EAM : depth of catalog tables, fix usage */
+
+unsigned int OBJ_ID_SRC;
+unsigned int CAT_ID_SRC;
+unsigned int OBJ_ID_DST;
+unsigned int CAT_ID_DST;
 
 double SIGMA_LIM;
@@ -210,10 +215,10 @@
 void          getfullregion       PROTO((Image *image, off_t Nimage, GSCRegion *fullregion));
 Image        *getimage            PROTO((off_t N));
-Image        *getimages           PROTO((off_t *N));
+Image        *getimages           PROTO((off_t *N, off_t **line_number));
 void          global_stats        PROTO((Catalog *catalog, int Ncatalog));
 void          initGrid            PROTO((int dX, int dY));
 void          initGridBins        PROTO((Catalog *catalog, int Ncatalog));
 void          initImageBins       PROTO((Catalog *catalog, int Ncatalog, int FULLINIT));
-void          initImages          PROTO((Image *input, off_t N));
+void          initImages          PROTO((Image *input, off_t *line_number, off_t N));
 void          initMosaicBins      PROTO((Catalog *catalog, int Ncatalog));
 void          initMosaicGrid      PROTO((Image *image, off_t Nimage));
@@ -310,9 +315,9 @@
 int MeasFilterTest(Measure *measure, int applySigmaLim);
 
-int sun_ecliptic (double jd, double *lambda, double *beta, double *epsilon);
-int ParFactor (double *pR, double *pD, double R, double D, double T, double Tmean);
-int FitPM (PMFit *fit, double *X, double *dX, double *Y, double *dY, double *T, int Npts);
+int sun_ecliptic (double jd, double *lambda, double *beta, double *epsilon, double *Radius);
+int ParFactor (double *pR, double *pD, double RA, double Dec, double Time, double Tmean);
+int FitPM (PMFit *fit, double *X, double *dX, double *Y, double *dY, double *T, int Npts, int XVERB);
 int FitPar (PMFit *fit, double *X, double *dX, double *Y, double *dY, double *pR, double *pD, int Npts);
-int FitPMandPar (PMFit *fit, double *X, double *dX, double *Y, double *dY, double *T, double *pR, double *pD, int Npts);
+int FitPMandPar (PMFit *fit, double *X, double *dX, double *Y, double *dY, double *T, double *pR, double *pD, int Npts, int XVERB);
 
 Mosaic *getMosaicForImage (off_t N);
@@ -372,2 +377,5 @@
 int applyConstraintsB(Catalog *catalog, off_t i);
 void setupAreaSelection(SkyRegion *region);
+
+int relastro_merge_source ();
+void resort_catalog (Catalog *catalog);
Index: branches/meh_branches/ppstack_test/Ohana/src/relastro/src/CoordOps.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/relastro/src/CoordOps.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/relastro/src/CoordOps.c	(revision 33415)
@@ -13,5 +13,5 @@
   Image *images;
 
-  images = getimages (&N);
+  images = getimages (&N, NULL);
 
   NoldCoords = N;
Index: branches/meh_branches/ppstack_test/Ohana/src/relastro/src/FitPM.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/relastro/src/FitPM.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/relastro/src/FitPM.c	(revision 33415)
@@ -2,5 +2,5 @@
 
 /* do we want an init function which does the alloc and a clear function to free? */
-int FitPM (PMFit *fit, double *X, double *dX, double *Y, double *dY, double *T, int Npts) {
+int FitPM (PMFit *fit, double *X, double *dX, double *Y, double *dY, double *T, int Npts, int XVERB) {
 
   int i;
@@ -73,8 +73,9 @@
   chisq = 0.0;
   for (i = 0; i < Npts; i++) {
-      Xf = fit[0].Ro + fit[0].uR*T[i];
-      Yf = fit[0].Do + fit[0].uD*T[i];
-      chisq += SQ(X[i] - Xf) / SQ(dX[i]);
-      chisq += SQ(Y[i] - Yf) / SQ(dY[i]);
+    Xf = fit[0].Ro + fit[0].uR*T[i];
+    Yf = fit[0].Do + fit[0].uD*T[i];
+    chisq += SQ(X[i] - Xf) / SQ(dX[i]);
+    chisq += SQ(Y[i] - Yf) / SQ(dY[i]);
+    if (XVERB) fprintf (stderr, "chisq contrib : %f %f : %f %f : %f %f : %f %f : %f\n", Xf, Yf, X[i] - Xf, Y[i] - Yf, dX[i], dY[i], (X[i] - Xf) / dX[i], (Y[i] - Yf) / dY[i], chisq);
   }
   fit[0].Nfit = Npts;
Index: branches/meh_branches/ppstack_test/Ohana/src/relastro/src/FitPMandPar.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/relastro/src/FitPMandPar.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/relastro/src/FitPMandPar.c	(revision 33415)
@@ -2,5 +2,5 @@
 
 /* do we want an init function which does the alloc and a clear function to free? */
-int FitPMandPar (PMFit *fit, double *X, double *dX, double *Y, double *dY, double *T, double *pR, double *pD, int Npts) {
+int FitPMandPar (PMFit *fit, double *X, double *dX, double *Y, double *dY, double *T, double *pR, double *pD, int Npts, int XVERB) {
 
   int i;
@@ -9,4 +9,5 @@
   double wx, wy, Wx, Wy, Tx, Ty, Tx2, Ty2, Xs, Ys, XT, YT;
   double PR, PD, PRT, PDT, PRX, PDY, PR2, PD2;
+  double chisq, Xf, Yf;
 
   A = array_init (5, 5);
@@ -95,4 +96,18 @@
   /* get the chisq from the matrix values */
 
+  // add up the chi square for the fit
+  chisq = 0.0;
+  for (i = 0; i < Npts; i++) {
+    Xf = fit[0].Ro + fit[0].uR*T[i] + fit[0].p*pR[i];
+    Yf = fit[0].Do + fit[0].uD*T[i] + fit[0].p*pD[i];
+    chisq += SQ(X[i] - Xf) / SQ(dX[i]);
+    chisq += SQ(Y[i] - Yf) / SQ(dY[i]);
+    if (XVERB) fprintf (stderr, "chisq contrib : %f %f : %f %f : %f %f : %f %f : %f\n", Xf, Yf, X[i] - Xf, Y[i] - Yf, dX[i], dY[i], (X[i] - Xf) / dX[i], (Y[i] - Yf) / dY[i], chisq);
+
+  }
+  fit[0].Nfit = Npts;
+
+  // the reduced chisq is divided by (Ndof = 2*Npts - 5)
+  fit[0].chisq = chisq / (2.0*Npts - 5.0);
   return (TRUE);
 }
Index: branches/meh_branches/ppstack_test/Ohana/src/relastro/src/FixProblemImages.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/relastro/src/FixProblemImages.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/relastro/src/FixProblemImages.c	(revision 33415)
@@ -15,5 +15,5 @@
   ALLOCATE (sublist.filename, char *, 1);
 
-  image = getimages (&Nimage);
+  image = getimages (&Nimage, NULL);
 
   Nbad = 0;
Index: branches/meh_branches/ppstack_test/Ohana/src/relastro/src/ImageOps.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/relastro/src/ImageOps.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/relastro/src/ImageOps.c	(revision 33415)
@@ -11,4 +11,5 @@
 static Image        *image;   // list of available images
 static off_t        Nimage;   // number of available images
+static off_t       *LineNumber; // match of subset to full image table
 
 static int         *Ncatlist;  // catalogs associated with each image
@@ -27,6 +28,8 @@
 # endif
 
-Image *getimages (off_t *N) {
+Image *getimages (off_t *N, off_t **line_number) {
+
   *N = Nimage;
+  if (line_number) *line_number = LineNumber;
   return (image);
 }
@@ -42,9 +45,10 @@
 }
 
-void initImages (Image *input, off_t N) {
+void initImages (Image *input, off_t *line_number, off_t N) {
 
   off_t i;
 
   image = input;
+  LineNumber = line_number;
   Nimage = N;
 
@@ -909,4 +913,5 @@
   if (!finite(measure[0].dR) || !finite(measure[0].dD)) return FALSE;
   if (!finite(measure[0].M)) return FALSE; //XXX is this necessary for all relastro tasks?
+  if (!finite(measure[0].dM)) return FALSE; //XXX is this necessary for all relastro tasks?
   
   /* select measurements by photcode, or equiv photcode, if specified */
Index: branches/meh_branches/ppstack_test/Ohana/src/relastro/src/ParFactor.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/relastro/src/ParFactor.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/relastro/src/ParFactor.c	(revision 33415)
@@ -24,4 +24,5 @@
 # endif
 
+# if (0)
 /* code borrowed from Skycalc : fix this stuff XXX */
 /* Low precision formulae for the sun, from Almanac p. C24 (1990) */
@@ -40,23 +41,57 @@
   return TRUE;
 }
+# endif
+
+/* Low precision formulae for the sun, from Astro. Almanac p. C5 (2012) */
+int sun_ecliptic (double jd, double *lambda, double *beta, double *epsilon, double *Radius) {
+
+# define J2000 2451545.       /* Julian date at standard epoch */
+
+  double n = jd - J2000;	      // day number
+  double L = 280.460 + 0.9856474 * n; // mean solar longitute (corr. for aberration)
+  double g = (357.528 + 0.9856003 * n)*RAD_DEG; // Mean anomaly
+
+  *lambda = L + 1.915 * sin(g) + 0.020 * sin(2*g); // solar longitude in degrees
+  *beta = 0.0;					   // approx latitude
+  *epsilon = (23.439 - 0.0000004 * n);		   // obliquity of ecliptic in degrees
+  *Radius = 1.00014 - 0.01671*cos(g) - 0.00014*cos(2*g); // earth-to-sun dist in AU
+  return TRUE;
+}
 
 /* given RA, DEC, Time, calculate the parallax factor */
-int ParFactor (double *pR, double *pD, double R, double D, double T, double Tmean) {
+int ParFactor (double *pR, double *pD, double RA, double DEC, double Time, double Tmean) {
 
-  double jd;
-  double L, B, E, e, s, r, d;
+  double jd, lambda, beta, epsilon, Radius;
 
   /* given a time T in UNIX seconds, determine the solar longitude S */
 
-  jd = ohana_sec_to_jd (365.25*86400.0*(T + Tmean));
-  sun_ecliptic (jd, &L, &B, &E);
+  jd = ohana_sec_to_jd (365.25*86400.0*(Time + Tmean));
+  sun_ecliptic (jd, &lambda, &beta, &epsilon, &Radius);
 
-  e = E * RAD_DEG;
-  s = L * RAD_DEG;
-  r = R * RAD_DEG;
-  d = D * RAD_DEG;
-  
-  *pR =  +(cos(e)*sin(s)*cos(r) - cos(s)*sin(r));
-  *pD =  -(cos(e)*sin(s)*sin(r) + cos(s)*cos(r))*sin(d) + sin(e)*sin(s)*cos(d);
+  double lambda_rad = lambda*RAD_DEG;
+  double epsilon_rad = epsilon*RAD_DEG;
+  double RA_rad = RA*RAD_DEG;
+  double DEC_rad = DEC*RAD_DEG;
+
+  double x = Radius*cos(lambda_rad);
+  double y = Radius*cos(epsilon_rad)*sin(lambda_rad);
+  double z = Radius*sin(epsilon_rad)*sin(lambda_rad);
+
+  // original terms:
+  // *pR =  +(cos(e)*sin(s)*cos(r) - cos(s)*sin(r));
+  // *pD =  -(cos(e)*sin(s)*sin(r) + cos(s)*cos(r))*sin(d) + sin(e)*sin(s)*cos(d);
+
+  // convert e->eps, s->lam, etc
+  // *pR =  +(cos(eps)*sin(lam)*cos(ra) - cos(lam)*sin(ra));
+  // *pD =  -(cos(eps)*sin(lam)*sin(ra) + cos(lam)*cos(ra))*sin(dec) + sin(eps)*sin(lam)*cos(dec);
+
+  // convert to x,y,z
+  // *pR = +(y*cos(ra) - x*sin(ra));
+  // *pD = -(y*sin(ra) + x*cos(ra))*sin(dec) + z*cos(dec);
+
+  // NOTE: seems to be identical to the old values, except I now include the varying solar distance
+  *pR = +(y*cos(RA_rad) - x*sin(RA_rad));
+  *pD = -(y*sin(RA_rad) + x*cos(RA_rad))*sin(DEC_rad) + z*cos(DEC_rad);
+
   return TRUE;
 }
Index: branches/meh_branches/ppstack_test/Ohana/src/relastro/src/StarMaps.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/relastro/src/StarMaps.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/relastro/src/StarMaps.c	(revision 33415)
@@ -40,5 +40,5 @@
   off_t i, Nimages;
 
-  images = getimages(&Nimages);
+  images = getimages(&Nimages, NULL);
 
   ALLOCATE (starmap, StarMap, Nimages);
@@ -64,5 +64,5 @@
   gettimeofday (&start, (void *) NULL);
 
-  images = getimages(&Nimages);
+  images = getimages(&Nimages, NULL);
 
   for (i = 0; i < catalog[0].Nmeasure; i++) {
@@ -90,5 +90,5 @@
   int ix, iy;
 
-  images = getimages(&Nimages);
+  images = getimages(&Nimages, NULL);
 
   for (i = 0; i < Nimages; i++) {
@@ -129,5 +129,5 @@
   double L, M, dLmax, dMmax;
 
-  images = getimages(&Nimages);
+  images = getimages(&Nimages, NULL);
 
   dLmax = dMmax = 0.0;
Index: branches/meh_branches/ppstack_test/Ohana/src/relastro/src/UpdateChips.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/relastro/src/UpdateChips.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/relastro/src/UpdateChips.c	(revision 33415)
@@ -18,5 +18,5 @@
   Nskip = Nmosaic = NnewFit = NoldFit = 0;
 
-  image = getimages (&Nimage);
+  image = getimages (&Nimage, NULL);
 
   // save fit results for summary plot
Index: branches/meh_branches/ppstack_test/Ohana/src/relastro/src/UpdateMeasures.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/relastro/src/UpdateMeasures.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/relastro/src/UpdateMeasures.c	(revision 33415)
@@ -13,5 +13,5 @@
     ID_IMAGE_ASTROM_FEW;
 
-  image = getimages (&Nimage);
+  image = getimages (&Nimage, NULL);
 
   for (i = 0; i < Nimage; i++) {
Index: branches/meh_branches/ppstack_test/Ohana/src/relastro/src/UpdateObjects.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/relastro/src/UpdateObjects.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/relastro/src/UpdateObjects.c	(revision 33415)
@@ -84,4 +84,6 @@
       /* calculate the average value of R,D for a single star */
 
+      XVERB = FALSE;
+
       // skip objects which are known to be problematic
       // XXX include this code or not?
@@ -196,5 +198,8 @@
       }
       
-      XVERB = FALSE && (catalog[i].measure[m].dM < 0.01) && (N == 6) && (mode == FIT_PM_ONLY);
+      // XVERB |= (catalog[i].averge[j].objID == 0xc90) && (catalog[i].average[j].catID == 0x2a1e);
+      XVERB |= (catalog[i].average[j].objID == OBJ_ID_SRC) && (catalog[i].average[j].catID == CAT_ID_SRC);
+      XVERB |= (catalog[i].average[j].objID == OBJ_ID_DST) && (catalog[i].average[j].catID == CAT_ID_DST);
+      // XVERB = (catalog[i].measure[m].dM < 0.01) && (N == 6) && (mode == FIT_PM_ONLY);
 
       // to judge the quality of the PM and PAR fits, we need to fit all three models and compare Chisq
@@ -211,7 +216,7 @@
 	}	  
 
-	FitPM (&fitPM, X, dX, Y, dY, T, N);
-
-	if (XVERB) fprintf (stderr, "fitted:  %f - %f : %f %f : %f %f : %f vs %f\n", Tmin, Tmax, fitPM.Ro, fitPM.Do, fitPM.uR, fitPM.uD, fitPM.chisq, fitAve.chisq);
+	FitPM (&fitPM, X, dX, Y, dY, T, N, XVERB);
+
+	if (XVERB) fprintf (stderr, "fitted PM:  %f - %f : %f %f : %f %f : %f vs %f\n", Tmin, Tmax, fitPM.Ro, fitPM.Do, fitPM.uR, fitPM.uD, fitPM.chisq, fitAve.chisq);
 
 	// project Ro, Do back to RA,DEC
@@ -225,11 +230,10 @@
 
       if (mode == FIT_PM_AND_PAR) {
-	fprintf (stderr, "parallax fitting is still untested (%s, %d)", __FILE__, __LINE__);
-	exit (2);
+	// fprintf (stderr, "parallax fitting is still untested (%s, %d)\n", __FILE__, __LINE__);
 
 	for (k = 0; k < N; k++) {
 	  ParFactor (&pX[k], &pY[k], R[k], D[k], T[k], Tmean);
 	}
-	FitPMandPar (&fitPAR, X, dX, Y, dY, T, pX, pY, N);
+	FitPMandPar (&fitPAR, X, dX, Y, dY, T, pX, pY, N, XVERB);
 	XY_to_RD (&fitPAR.Ro, &fitPAR.Do, fitPAR.Ro, fitPAR.Do, &coords);
 	catalog[i].average[j].flags |= ID_STAR_FIT_PAR;
@@ -276,5 +280,5 @@
 	break;
       }
-      if (XVERB) fprintf (stderr, "A%f %f -> %f %f (%f,%f) pm=(%f %f)\n",
+      if (XVERB) fprintf (stderr, "%f %f -> %f %f (%f,%f) pm=(%f %f)\n",
 			  catalog[i].average[j].R, 
 			  catalog[i].average[j].D, 
Index: branches/meh_branches/ppstack_test/Ohana/src/relastro/src/UpdateSimple.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/relastro/src/UpdateSimple.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/relastro/src/UpdateSimple.c	(revision 33415)
@@ -8,5 +8,5 @@
   StarData *raw, *ref;
 
-  image = getimages (&Nimage);
+  image = getimages (&Nimage, NULL);
 
   for (i = 0; i < Nimage; i++) {
Index: branches/meh_branches/ppstack_test/Ohana/src/relastro/src/args.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/relastro/src/args.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/relastro/src/args.c	(revision 33415)
@@ -1,4 +1,6 @@
 # include "relastro.h"
 void usage (void);
+void usage_merge_source (void);
+void usage_merge_source_id (char *name);
 
 int args (int argc, char **argv) {
@@ -6,11 +8,61 @@
   int N;
   double trange;
+  char *endptr;
 
   /* possible operations */
   FIT_TARGET = TARGET_NONE;
   FIT_MODE = FIT_AVERAGE;
+
+  OBJ_ID_SRC = OBJ_ID_DST = 0;
+  CAT_ID_SRC = CAT_ID_DST = 0;
+
+  if ((N = get_argument (argc, argv, "-merge-source"))) {
+    if (N > argc - 6) usage_merge_source();
+    if (strcmp(argv[N+3], "into")) usage_merge_source();
+    FIT_TARGET = TARGET_MERGE_SOURCE;
+    remove_argument (N, &argc, argv);
+    OBJ_ID_SRC = strtol(argv[N], &endptr, 0);
+    if (*endptr) usage_merge_source_id (argv[N]); 
+    remove_argument (N, &argc, argv);
+    CAT_ID_SRC = strtol(argv[N], &endptr, 0);
+    if (*endptr) usage_merge_source_id (argv[N]); 
+    remove_argument (N, &argc, argv);
+    remove_argument (N, &argc, argv); // remove the 'into'
+    OBJ_ID_DST = strtol(argv[N], &endptr, 0);
+    if (*endptr) usage_merge_source_id (argv[N]); 
+    remove_argument (N, &argc, argv);
+    CAT_ID_DST = strtol(argv[N], &endptr, 0);
+    if (*endptr) usage_merge_source_id (argv[N]); 
+    remove_argument (N, &argc, argv);
+
+    if (argc != 1) usage ();
+    return TRUE;
+  }
+
   if ((N = get_argument (argc, argv, "-update-objects"))) {
     remove_argument (N, &argc, argv);
     FIT_TARGET = TARGET_OBJECTS;
+  }
+
+  if ((N = get_argument (argc, argv, "-testobj1"))) {
+    if (N > argc - 3) usage ();
+    remove_argument (N, &argc, argv);
+    OBJ_ID_SRC = strtol(argv[N], &endptr, 0);
+    if (*endptr) usage ();
+    remove_argument (N, &argc, argv);
+    CAT_ID_SRC = strtol(argv[N], &endptr, 0);
+    if (*endptr) usage (); 
+    remove_argument (N, &argc, argv);
+  }
+
+  if ((N = get_argument (argc, argv, "-testobj2"))) {
+    if (N > argc - 3) usage ();
+    remove_argument (N, &argc, argv);
+    OBJ_ID_DST = strtol(argv[N], &endptr, 0);
+    if (*endptr) usage ();
+    remove_argument (N, &argc, argv);
+    CAT_ID_DST = strtol(argv[N], &endptr, 0);
+    if (*endptr) usage (); 
+    remove_argument (N, &argc, argv);
   }
 
@@ -307,4 +359,6 @@
   fprintf (stderr, "ERROR: USAGE: relastro -region RA RA DEC DEC\n");
   fprintf (stderr, "       OR:    relastro -catalog (ra) (dec)\n\n");
+  fprintf (stderr, "       OR:    relastro -high-speed [options]\n\n");
+  fprintf (stderr, "       OR:    relastro -merge-source [options]\n\n");
   fprintf (stderr, "  specify one of the following modes: \n");
   fprintf (stderr, "  -update-objects\n");
@@ -340,2 +394,12 @@
 } 
 
+void usage_merge_source_id (char *name) {
+
+  fprintf (stderr, "ERROR: invalid ID %s (remember to prefix 0x to hex IDs)\n", name);
+  exit (2);
+}
+
+void usage_merge_source () {
+  fprintf (stderr, "ERROR: USAGE: relastro -merge-source (objID) (catID) into (objID) (catID)\n");
+  exit (2);
+}
Index: branches/meh_branches/ppstack_test/Ohana/src/relastro/src/initialize.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/relastro/src/initialize.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/relastro/src/initialize.c	(revision 33415)
@@ -10,4 +10,6 @@
   ConfigInit (&argc, argv);
   args (argc, argv);
+
+  if (FIT_TARGET == TARGET_MERGE_SOURCE) return;
 
   /* build a list of accepted photcodes. these will be used by bcatalog to accept or
Index: branches/meh_branches/ppstack_test/Ohana/src/relastro/src/load_images.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/relastro/src/load_images.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/relastro/src/load_images.c	(revision 33415)
@@ -44,14 +44,5 @@
   MARKTIME("  select images: %f sec\n", dtime);
 
-  // generate db->vtable from db->ftable based on the selection
-  // XXX does this simply duplicate the memory needlessly?  we recreate these lines
-  // in reload_images.  If we had saved the line numbers, we could avoid this
-  // vtable points *another* copy of the subset rows
-  // (the later call to 'reload_images' copies the subset elements back on top of 
-  // the rows of the vtable)
-  // gfits_vtable_from_ftable (&db[0].ftable, &db[0].vtable, LineNumber, Nsubset);
-  // MARKTIME("converted ftable to vtable: %f sec\n", dtime);
-
-  initImages (subset, Nsubset);
+  initImages (subset, LineNumber, Nsubset);
   MARKTIME("  init images: %f sec\n", dtime);
 
@@ -68,9 +59,10 @@
 
   Image     *image;
-  off_t     Nimage, Nx, i;
+  off_t     Nimage, Nx, i, *LineNumber;
   VTable    *vtable;
 
-  image = getimages (&Nimage);
+  image = getimages (&Nimage, &LineNumber);
 
+  gfits_vtable_from_ftable (&db[0].ftable, &db[0].vtable, LineNumber, Nimage);
   vtable = &db[0].vtable;
 
Index: branches/meh_branches/ppstack_test/Ohana/src/relastro/src/relastro.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/relastro/src/relastro.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/relastro/src/relastro.c	(revision 33415)
@@ -23,4 +23,10 @@
   if (FIT_TARGET == TARGET_HIGH_SPEED) {
     high_speed_catalogs ();
+    exit (0);
+  }
+
+  /* the object analysis is a separate process iterating over catalogs */
+  if (FIT_TARGET == TARGET_MERGE_SOURCE) {
+    relastro_merge_source ();
     exit (0);
   }
Index: branches/meh_branches/ppstack_test/Ohana/src/relastro/src/relastro_merge_source.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/relastro/src/relastro_merge_source.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/Ohana/src/relastro/src/relastro_merge_source.c	(revision 33415)
@@ -0,0 +1,138 @@
+# include "relastro.h"
+
+int relastro_merge_source () {
+
+  int i, m;
+  SkyTable *sky = NULL;
+  Catalog catalog_src, catalog_dst;
+
+  // relastro -merge-source (objID) (catID) into (objID) (catID) 
+  // merge the detections of the first source with those of the second source
+  
+  // tell libdvo the CATDIR
+  dvo_set_catdir(CATDIR);
+
+  // load the current sky table (layout of all SkyRegions) 
+  sky = SkyTableLoadOptimal (CATDIR, NULL, NULL, FALSE, SKY_DEPTH, VERBOSE);
+  SkyTableSetFilenames (sky, CATDIR, "cpt");
+  
+  // we are merging the detections of the src into the dst : OBJ_ID_SRC,CAT_ID_SRC -> OBJ_ID_DST,CAT_ID_DST
+
+  // find the catalog containing the src object:
+  dvo_catalog_init (&catalog_src, TRUE);
+  dvo_catalog_init (&catalog_dst, TRUE);
+  SkyRegion *region_src = NULL;
+  SkyRegion *region_dst = NULL;
+
+  // load data from each region file, only use bright stars
+  for (i = 0; i < sky[0].Nregions; i++) {
+    if (sky[0].regions[i].index == CAT_ID_SRC) {
+      catalog_src.filename = sky[0].filename[i];
+      region_src = &sky[0].regions[i];
+    }
+    if (sky[0].regions[i].index == CAT_ID_DST) {
+      catalog_dst.filename = sky[0].filename[i];
+      region_dst = &sky[0].regions[i];
+    }
+  }    
+
+  if (!catalog_src.filename) {
+    fprintf (stderr, "ERROR: cannot find catalog file matching source catID %x\n", CAT_ID_SRC);
+    exit (2);
+  }
+  if (!catalog_dst.filename) {
+    fprintf (stderr, "ERROR: cannot find catalog file matching destination catID %x\n", CAT_ID_DST);
+    exit (2);
+  }
+
+  // load the source object catalog:
+  catalog_src.catformat = dvo_catalog_catformat (CATFORMAT);    // set the default catformat from config data
+  catalog_src.catmode   = dvo_catalog_catmode (CATMODE);        // set the default catmode from config data
+  catalog_src.catflags  = LOAD_AVES | LOAD_MEAS | LOAD_MISS | LOAD_SECF;
+  catalog_src.Nsecfilt  = GetPhotcodeNsecfilt ();
+
+  if (!dvo_catalog_open (&catalog_src, region_src, VERBOSE, "w")) {
+    fprintf (stderr, "ERROR: failure reading catalog %s\n", catalog_src.filename);
+    exit (1);
+  }
+  if (!catalog_src.Naves_disk) {
+    fprintf (stderr, "no data in %s, error in cat ID?\n", catalog_src.filename);
+    exit (1);
+  }
+
+  // find the source object (by objID)
+  int index_src = -1;
+  for (i = 0; i < catalog_src.Naverage; i++) {
+    if (OBJ_ID_SRC != catalog_src.average[i].objID) continue;
+    index_src = i;
+    break;
+  }
+  if (index_src < 0) {
+    fprintf (stderr, "cannot find source object ID %x\n", OBJ_ID_SRC);
+    exit (1);
+  }
+
+  // save the measures from this object 
+  // Measure *measures_src;
+  // ALLOCATE (measures_src, Measure, catalog_src.average[index_src].Nmeasure);
+  // m = catalog_src.average[index_src].measureOffset;
+  // for (i = 0; i < catalog_src.average[index_src].Nmeasure; i++, m++) {
+  //   memcpy (&measures_src[i], &catalog_src.measure[m], sizeof(Measure));
+  // }
+    
+  if (CAT_ID_SRC == CAT_ID_DST) {
+
+    // find the target objects 
+    int index_dst = -1;
+    for (i = 0; i < catalog_src.Naverage; i++) {
+      if (OBJ_ID_DST != catalog_src.average[i].objID) continue;
+      index_dst = i;
+      break;
+    }
+    if (index_dst < 0) {
+      fprintf (stderr, "cannot find source object ID %x\n", OBJ_ID_DST);
+      exit (1);
+    }
+
+    // repoint the src measures at this object
+    Measure *measures_src;
+    ALLOCATE (measures_src, Measure, catalog_src.average[index_src].Nmeasure);
+    m = catalog_src.average[index_src].measureOffset;
+    for (i = 0; i < catalog_src.average[index_src].Nmeasure; i++, m++) {
+
+      // update objID & catID to match the new source
+      catalog_src.measure[m].objID  = catalog_src.average[index_dst].objID;
+      catalog_src.measure[m].catID  = catalog_src.average[index_dst].catID;
+      catalog_src.measure[m].averef = index_dst;
+
+      // get the instantaneous positions:
+      double R = catalog_src.average[index_src].R - catalog_src.measure[m].dR / 3600.0;
+      double D = catalog_src.average[index_src].D - catalog_src.measure[m].dD / 3600.0;
+
+      // update the offset coordinates to match the new source
+      catalog_src.measure[m].dR = 3600.0*(catalog_src.average[index_dst].R - R);
+      catalog_src.measure[m].dD = 3600.0*(catalog_src.average[index_dst].D - D);
+    }
+
+    // update the count? (updated by resort?)
+    catalog_src.average[index_dst].Nmeasure += catalog_src.average[index_src].Nmeasure;
+
+    // for the moment, don't delete this object...
+    catalog_src.average[index_src].Nmeasure = 0;
+    catalog_src.average[index_src].measureOffset = 0;
+
+    catalog_src.sorted = FALSE;
+
+    // resort the measure table and save
+    resort_catalog (&catalog_src);
+
+    dvo_catalog_save (&catalog_src, VERBOSE);
+    dvo_catalog_unlock (&catalog_src);
+    dvo_catalog_free (&catalog_src);
+
+    exit (0);
+  }
+
+  return TRUE;
+}
+
Index: branches/meh_branches/ppstack_test/Ohana/src/relastro/src/resort_catalog.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/relastro/src/resort_catalog.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/Ohana/src/relastro/src/resort_catalog.c	(revision 33415)
@@ -0,0 +1,149 @@
+# include "relastro.h"
+
+# define myAbort(MSG) { fprintf (stderr, "%s\n", MSG); abort(); }
+# define myAssert(LOGIC,MSG) { if (!(LOGIC)) { fprintf (stderr, "%s\n", MSG); abort(); } }
+
+// sort the measure Sequence based on the average Sequence entries
+void SortAveMeasMatch (off_t *MEAS, off_t *AVE, off_t N) {
+
+# define SWAPFUNC(A,B){ off_t tmp_meas; off_t tmp_ave;	\
+    tmp_meas = MEAS[A]; MEAS[A] = MEAS[B]; MEAS[B] = tmp_meas;		\
+    tmp_ave  = AVE[A];  AVE[A]  = AVE[B];  AVE[B]  = tmp_ave;		\
+  }
+# define COMPARE(A,B)(AVE[A] < AVE[B])
+  OHANA_SORT (N, COMPARE, SWAPFUNC);
+# undef SWAPFUNC
+# undef COMPARE
+}
+
+// XXX : where is the time going?  perhaps the ALLOCATE?
+// XXX : I don't thnk his is getting the right answer yet.
+
+void resort_catalog (Catalog *catalog) {
+
+  off_t Naverage, Nmeasure;
+  Measure *measure;
+  Average *average;
+  off_t i, j, N, currentAve;
+
+  off_t *measureSeq = NULL;
+  off_t *averageSeq = NULL;
+  Measure *measureTMP = NULL;
+
+  if (catalog[0].sorted == TRUE) return;
+
+  // struct timeval start, stop;
+  // gettimeofday (&start, NULL);
+
+  /* internal counters */
+  Nmeasure = catalog[0].Nmeasure;
+  Naverage = catalog[0].Naverage;
+
+  measure = catalog[0].measure;
+  average = catalog[0].average;
+  
+  // we have a table of average objects and an unsorted table of measurements.  each measurement
+  // has a reference to the average object sequence (as well as an ID)
+  // measure[i].averef -> average[averef]
+  // measure[i].objID = average[averef].objID
+  // measure[i].catID = average[averef].catID
+
+  // we want a sorted measure array with all averef entries in sequence
+
+  ALLOCATE (measureSeq, off_t,   Nmeasure);
+  ALLOCATE (averageSeq, off_t,   Nmeasure);
+
+  for (i = 0; i < Nmeasure; i++) {
+    measureSeq[i] = i;
+    averageSeq[i] = measure[i].averef;
+    
+    if (catalog[0].catformat >= DVO_FORMAT_PS1_V1) {
+      // earlier formats did not carry the objID or catID, so they are not available (we could assign on load, but we don't)
+      myAssert(average[averageSeq[i]].objID == measure[measureSeq[i]].objID, "object / detection mismatch");
+      myAssert(average[averageSeq[i]].catID == measure[measureSeq[i]].catID, "object / detection mismatch");
+    }
+  }
+  
+  // check that averageSeq is now in order
+  // for (i = 1; i < Nmeasure; i++) {
+  //   if (averageSeq[i] < averageSeq[i-1]) {
+  //     fprintf (stderr, "%d ", (int) i);
+  //   }
+  // }
+  // fprintf (stderr, "\n");
+
+  SortAveMeasMatch(measureSeq, averageSeq, Nmeasure);
+  // MARKTIME("sort : %f sec\n", dtime);
+
+  // check that averageSeq is now in order
+  // for (i = 1; i < Nmeasure; i++) {
+  //   if (averageSeq[i] < averageSeq[i-1]) {
+  //     fprintf (stderr, "%d ", (int) i);
+  //   }
+  // }
+  // fprintf (stderr, "\n");
+
+  // copy the measurements in the sorted order
+  ALLOCATE (measureTMP, Measure, Nmeasure);
+  for (i = 0; i < Nmeasure; i++) {
+    j = measureSeq[i];
+    measureTMP[i] = measure[j];
+  }
+  // MARKTIME("assign measure : %f sec\n", dtime);
+
+  // update the values of average.measureOffset and average.Nmeasure
+  FREE(measure);
+  catalog[0].measure = measureTMP;
+
+  N = 0;
+  currentAve = averageSeq[0];
+  average[currentAve].measureOffset = 0;
+  for (i = 0; i < Nmeasure; i++) {
+    if (averageSeq[i] != currentAve) {
+      // we have hit the next entry in the list
+      average[currentAve].Nmeasure = N;
+      N = 0;
+      currentAve = averageSeq[i];
+      average[currentAve].measureOffset = i;
+    }
+    N++;
+  }
+  // N++;
+  average[currentAve].Nmeasure = N;
+  // MARKTIME("update Nmeasure : %f sec\n", dtime);
+
+  int NmeasureTotal = 0;
+  int measureOffsetOK = TRUE;
+  for (i = 0; i < catalog[0].Naverage; i++) {
+    NmeasureTotal += catalog[0].average[i].Nmeasure;
+    if (VERBOSE && !(NmeasureTotal <= catalog[0].Nmeasure)) {
+      fprintf (stderr, "too few measurements: %d %d %d\n", (int) i, NmeasureTotal, (int) catalog[0].Nmeasure);
+    }
+    measureOffsetOK &= (catalog[0].average[i].measureOffset < catalog[0].Nmeasure);
+    if (VERBOSE && !(catalog[0].average[i].measureOffset < catalog[0].Nmeasure)) {
+      fprintf (stderr, "offset too large: %d %d %d\n", (int) i, catalog[0].average[i].Nmeasure, (int) catalog[0].Nmeasure);
+    }
+    measureOffsetOK &= (catalog[0].average[i].measureOffset + catalog[0].average[i].Nmeasure <= catalog[0].Nmeasure);
+    if (VERBOSE && !(catalog[0].average[i].measureOffset + catalog[0].average[i].Nmeasure <= catalog[0].Nmeasure)) {
+      fprintf (stderr, "orrset + Nmeasure too large: %d + %d > %d %d\n", (int) i, catalog[0].average[i].measureOffset, catalog[0].average[i].Nmeasure, (int) catalog[0].Nmeasure);
+    }
+  }
+
+  if (!measureOffsetOK) {
+    fprintf (stderr, "ERROR: catalog %s has an invalid measureOffset\n", catalog[0].filename);
+  }
+
+  if (NmeasureTotal != catalog[0].Nmeasure) {
+    fprintf (stderr, "ERROR: catalog %s has an invalid Nmeasure\n", catalog[0].filename);
+  }
+
+  // MARKTIME("  match time %9.4f sec for %7lld measures, %6lld average\n", dtime, (long long) Nmeasure, (long long) Naverage);
+
+  catalog[0].sorted = TRUE;
+
+  FREE (measureSeq);
+  FREE (averageSeq);
+
+  return;
+}
+
Index: branches/meh_branches/ppstack_test/Ohana/src/relastro/src/testparallax.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/relastro/src/testparallax.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/Ohana/src/relastro/src/testparallax.c	(revision 33415)
@@ -0,0 +1,31 @@
+# include "relastro.h"
+
+int main (int argc, char **argv) {
+
+    if (argc != N) {
+	fprintf (stderr, "USAGE: testparallax (file.dat)\n");
+	exit (1);
+    }
+
+    // test parallax program
+    FILE *f = fopen (argv[1], "r");
+    if (!f) { fprintf (stderr, "failed to open %s\n", argv[1]); exit (2); }
+
+    char buffer[1024];
+    scan_line(f, line);
+    
+    while (scan_line(f, line) != EOF) {
+	dparse (&jd,   1, line);
+	dparse (&time, 2, line);
+	dparse (&ra,   3, line);
+	dparse (&dec,  4, line);
+
+    }
+
+    // project to local coords
+
+    // run fitter
+    FitPMandPar
+
+
+}
Index: branches/meh_branches/ppstack_test/Ohana/src/relphot/src/load_images.c
===================================================================
--- branches/meh_branches/ppstack_test/Ohana/src/relphot/src/load_images.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/Ohana/src/relphot/src/load_images.c	(revision 33415)
@@ -85,14 +85,4 @@
   }
 
-
-  // generate db->vtable from db->ftable based on the selection
-  // XXX does this simply duplicate the memory needlessly?  we recreate these lines
-  // in reload_images.  If we had saved the line numbers, we could avoid this
-  // vtable points *another* copy of the subset rows
-  // (the later call to 'reload_images' copies the subset elements back on top of 
-  // the rows of the vtable)
-  // gfits_vtable_from_ftable (&db[0].ftable, &db[0].vtable, LineNumber, Nsubset);
-  // MARKTIME("converted ftable to vtable: %f sec\n", dtime);
-
   // save the subset of images in the static reference in ImageOps, set up indexes
   initImages (subset, LineNumber, Nsubset);
Index: branches/meh_branches/ppstack_test/PS-IPP-Config/lib/PS/IPP/Config.pm
===================================================================
--- branches/meh_branches/ppstack_test/PS-IPP-Config/lib/PS/IPP/Config.pm	(revision 32161)
+++ branches/meh_branches/ppstack_test/PS-IPP-Config/lib/PS/IPP/Config.pm	(revision 33415)
@@ -20,4 +20,5 @@
 use Getopt::Long 2.33 qw( GetOptions :config gnu_getopt pass_through ); # Set pass_through so we don't kill @ARGV
 use IPC::Cmd 0.36 qw( can_run run );
+use POSIX qw( strftime );
 
 use base qw( Exporter Class::Accessor::Fast );
@@ -397,5 +398,8 @@
     return $self->{nebulous} if defined $self->{nebulous}; # Already started
 
-    my $server = metadataLookupStr( $self->{_siteConfig}, 'NEB_SERVER' ); # Nebulous server
+    # For consistency with the rest of the IPP, don't use site.config
+    # to set NEB_SERVER use environment variable.
+    # my $server = metadataLookupStr( $self->{_siteConfig}, 'NEB_SERVER' ); # Nebulous server
+    my $server = $ENV{NEB_SERVER};
     unless (defined $server) {
         carp "Unable to find NEB_SERVER in camera configuration file.";
@@ -692,4 +696,6 @@
 }
 
+
+
 # redirect stderr and stdout streams to a file
 sub redirect_output
@@ -722,4 +728,43 @@
 
     return 1;
+}
+
+# redirect stderr and stdout streams to log file. If file already exists rename it and record
+# the name in the new log file.
+sub redirect_to_logfile {
+    my $self = shift;
+    my $name = shift;
+    
+    my $tstring = strftime "%Y%m%dT%H%M%S", gmtime;
+
+    my $old_name;
+    my $scheme = file_scheme($name);
+    if ($scheme and $scheme eq 'neb') {
+        my $neb = $self->nebulous;
+        if ($neb->storage_object_exists($name)) {
+            eval {
+                $old_name = "$name.$tstring";
+                $neb->move($name, $old_name);
+            };
+            if ($@) {
+                carp "nebulous move failed for $name";
+                return undef;
+            }
+        }
+    } else {
+        if ($self->file_exists($name)) {
+            my $resolved = $self->file_resolve($name);
+            $old_name = "$resolved.$tstring";
+            if (!rename($resolved, $old_name)) {
+                carp "Failed to rename $resolved $old_name";
+                return undef;
+            }
+        }
+    }
+    my $result = $self->redirect_output($name);
+    if ($result and $old_name) {
+        print "\nPrevious log file moved to $old_name\n";
+    }
+    return $result;
 }
 
Index: branches/meh_branches/ppstack_test/PS-IPP-PStamp/lib/PS/IPP/PStamp/Job.pm
===================================================================
--- branches/meh_branches/ppstack_test/PS-IPP-PStamp/lib/PS/IPP/PStamp/Job.pm	(revision 32161)
+++ branches/meh_branches/ppstack_test/PS-IPP-PStamp/lib/PS/IPP/PStamp/Job.pm	(revision 33415)
@@ -238,5 +238,5 @@
         # choose the chips containing the center pixel by calling selectComponets() below
         if ($component or $use_imfile_id or !$skycenter) {
-            $command = "$chiptool -processedimfile -dbname $imagedb";
+            $command = "$chiptool -processedimfile -pstamp_order -dbname $imagedb";
             if ($component and $component ne 'all') {
                 $class_id = $component;
@@ -263,5 +263,5 @@
     } elsif ($stage eq "warp") {
         if ($component or $use_imfile_id or !$skycenter) {
-            $command = "$warptool -warped -dbname $imagedb";
+            $command = "$warptool -warped -pstamp_order -dbname $imagedb";
             if ($component and $component ne 'all') {
                 $skycell_id = $component;
@@ -281,5 +281,5 @@
     } elsif ($stage eq "diff") {
         if ($component or $use_imfile_id or !$skycenter) {
-            $command = "$difftool -diffskyfile -dbname $imagedb";
+            $command = "$difftool -diffskyfile -pstamp_order -dbname $imagedb";
             if ($component and $component ne 'all') {
                 $skycell_id = $component;
@@ -302,5 +302,5 @@
         $command = "$stacktool -sumskyfile -dbname $imagedb";
         $id_opt = "-stack_id";
-        $component_args = " -skycell_id $skycell_id" if $skycell_id;
+        $component_args = " -skycell_id $skycell_id" if $skycell_id and $skycell_id ne  'all';
 
         if (!$unconvolved) {
@@ -351,5 +351,5 @@
         $command .= " -dateobs_begin $dateobs_begin" if $dateobs_begin;
         $command .= " -dateobs_end $dateobs_end" if $dateobs_end;
-    } else {
+    } elsif ($req_type ne "byid") {
         $command .= " -mjd_obs_begin $mjd_min" if $mjd_min;
         $command .= " -mjd_obs_end $mjd_max" if $mjd_max;
@@ -438,4 +438,7 @@
         } elsif ($stage eq "stack") {
             $stage_id = $out->{stack_id};
+	    # dquery wants these set
+	    $out->{magicked} = 0;
+	    $out->{data_state} = $out->{state};
         }
         $out->{stage_id} = $stage_id;
@@ -487,10 +490,10 @@
     my $command = "$difftool -dbname $imagedb -pstamp_order";
     
-    my $listrun = 0;
+    my $choose_components = 0;
     if ($byid) {
         if ($skycell_id and ($skycell_id ne 'all')) {
             $command .= " -diffskyfile -diff_id $id -skycell_id $skycell_id";
         } else {
-            $listrun = 1;
+            $choose_components = 1;
             $command .= " -listrun -diff_id $id";
             # the following is a work around for the problem reported in ticket #1394
@@ -512,5 +515,5 @@
 
     my $images;
-    if ($listrun) {
+    if ($choose_components) {
         $images = selectComponents($ipprc, $imagedb, 'byid', 'diff', $rowList, $output, $verbose);
     } else {
@@ -603,5 +606,5 @@
             my @imageList = ($image);
 
-            setRowRefs($rowList, \@imageList);
+            setRowRefs($rowList, \@imageList) unless $choose_components;
             # the $image is going to be returned directly in this case so we need to duplicate
             # some of processing that lookup does for other img_types
@@ -847,4 +850,5 @@
     {
         my $command = "$whichimage $ra $dec";
+        $command .= " --tess_id $requested_tess_id" if $requested_tess_id;
         my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
                     run(command => $command, verbose => $verbose);
Index: branches/meh_branches/ppstack_test/PS-IPP-PStamp/lib/PS/IPP/PStamp/RequestFile.pm
===================================================================
--- branches/meh_branches/ppstack_test/PS-IPP-PStamp/lib/PS/IPP/PStamp/RequestFile.pm	(revision 32161)
+++ branches/meh_branches/ppstack_test/PS-IPP-PStamp/lib/PS/IPP/PStamp/RequestFile.pm	(revision 33415)
@@ -27,6 +27,8 @@
                     $PSTAMP_SELECT_PSF
                     $PSTAMP_SELECT_BACKMDL
+                    $PSTAMP_SELECT_JPEG
                     $PSTAMP_SELECT_INVERSE
                     $PSTAMP_SELECT_UNCONV
+                    $PSTAMP_RESTORE_BACKGROUND
                     $PSTAMP_REQUEST_UNCENSORED
                     $PSTAMP_REQUIRE_UNCENSORED
@@ -63,6 +65,8 @@
 our $PSTAMP_SELECT_PSF       = 16;
 our $PSTAMP_SELECT_BACKMDL   = 32;
+our $PSTAMP_SELECT_JPEG      = 64;
 our $PSTAMP_SELECT_INVERSE   = 1024;
 our $PSTAMP_SELECT_UNCONV    = 2048;
+our $PSTAMP_RESTORE_BACKGROUND = 4096;
 
 our $PSTAMP_USE_IMFILE_ID    = 16384;
Index: branches/meh_branches/ppstack_test/dbconfig/add.md
===================================================================
--- branches/meh_branches/ppstack_test/dbconfig/add.md	(revision 32161)
+++ branches/meh_branches/ppstack_test/dbconfig/add.md	(revision 33415)
@@ -3,5 +3,6 @@
     add_id          S64     0       # Primary Key AUTO_INCREMENT
     stage 	    STR	    64      # what the stage is (warp, cam, diff,
-    stage_id          S64     0       # Key INDEX(add_id,cam_id) fkey(cam_id) ref camRun(cam_id)
+    stage_id          S64     0       # Key INDEX(add_id,cam_id) fkey(cam_id)    ref camRun(cam_id)
+    stage_extra1    S32	    0       
     state           STR     64      # Key
     workdir         STR     255
Index: branches/meh_branches/ppstack_test/dbconfig/changes.txt
===================================================================
--- branches/meh_branches/ppstack_test/dbconfig/changes.txt	(revision 32161)
+++ branches/meh_branches/ppstack_test/dbconfig/changes.txt	(revision 33415)
@@ -2135,2 +2135,122 @@
 
 alter table addRun add key(stage), add key (stage_id);
+
+CREATE TABLE vpRun (
+    vp_id BIGINT AUTO_INCREMENT,
+    exp_id BIGINT,
+    state VARCHAR(64),
+    label VARCHAR(64),
+    data_group VARCHAR(64),
+    workdir VARCHAR(255),
+    note VARCHAR(255),
+    dest_id BIGINT,
+    outroot VARCHAR(255),
+    dtime_script FLOAT,
+    hostname VARCHAR(64),
+    fault SMALLINT NOT NULL DEFAULT 0,
+    PRIMARY KEY(vp_id),
+    KEY(exp_id),
+    KEY(state),
+    KEY(label),
+    KEY(data_group),
+    KEY(fault),
+    INDEX(vp_id, exp_id),
+    FOREIGN KEY(exp_id) REFERENCES rawExp(exp_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE vpProcessedCell (
+    vp_id BIGINT,
+    class_id VARCHAR(64),
+    cell_id VARCHAR(64),
+    dtime_photom FLOAT,
+    quality SMALLINT NOT NULL DEFAULT 0,
+    path_base VARCHAR(255),
+    fault SMALLINT NOT NULL DEFAULT 0,
+    KEY(quality),
+    KEY(fault),
+    FOREIGN KEY(vp_id) REFERENCES vpRun(vp_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+-- changes to prevent repeatedly queueing diffRuns for warps with no overlaps
+ALTER TABLE diffInputSkyfile DROP PRIMARY KEY, ADD PRIMARY KEY(diff_skyfile_id);
+ALTER TABLE diffInputSkyfile CHANGE COLUMN skycell_id  skycell_id VARCHAR(64) DEFAULT NULL;
+
+UPDATE dbversion set schema_version = '1.1.71', updated= CURRENT_TIMESTAMP();
+
+
+-- this is to stuff extra stage related information. 
+-- example - multi filter staticsky stage - this allows selection of 
+-- each filter of cmf staticsky.  
+
+alter table addRun add column stage_extra1 int after stage_id;
+
+
+-- This adds new features to addRun, first add the tables:
+
+CREATE TABLE mergedvodbRun (
+       merge_id BIGINT AUTO_INCREMENT,
+       minidvodb_id BIGINT,
+       mergedvodb VARCHAR(64) NOT NULL,
+       mergedvodb_path VARCHAR(255) NOT NULL,
+       state VARCHAR(64) NOT NULL,
+       creation_date TIMESTAMP,
+       PRIMARY KEY(merge_id), KEY(minidvodb_id), KEY(mergedvodb), INDEX(merge_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+
+CREATE TABLE mergedvodbProcessed (
+      merge_id BIGINT,
+      dtime_verify FLOAT,
+      dtime_merge FLOAT,
+      dtime_script FLOAT,
+      epoch timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+      fault SMALLINT NOT NULL,
+      UNIQUE KEY (merge_id),
+      KEY(merge_id),
+      FOREIGN KEY(merge_id) REFERENCES mergedvodbRun(merge_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE mergedvodbCopy (
+    mergedvodbcopy_id     BIGINT AUTO_INCREMENT,
+    merge_id     BIGINT,
+    mergedvodb_rsync_path VARCHAR(255) NOT NULL,
+    destination_host VARCHAR(255) NOT NULL,
+    fault  SMALLINT NOT NULL,
+    state   VARCHAR(64) NOT NULL,
+    epoch TIMESTAMP,
+    dtime FLOAT,
+    PRIMARY KEY(mergedvodbcopy_id),
+    KEY(merge_id),
+    FOREIGN KEY(merge_id) REFERENCES mergedvodbRun(merge_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+alter table mergedvodbProcessed add column merge_order BIGINT after merge_id;
+
+-- then copy over the info into the 2 tables:
+ insert into mergedvodbRun ( minidvodb_id, mergedvodb,mergedvodb_path,
+   state, creation_date)  select minidvodb_id, minidvodb_group,mergedvodb_path,
+minidvodbRun.state,
+   minidvodbRun.creation_date from minidvodbRun where minidvodb_id >= 0;
+
+insert into mergedvodbProcessed (merge_id, merge_order, dtime_verify,
+dtime_merge, dtime_script, epoch, fault) select merge_id, merge_order,
+minidvodbProcessed.dtime_verify, minidvodbProcessed.dtime_merge,
+minidvodbProcessed.dtime_script, minidvodbProcessed.epoch,
+minidvodbProcessed.fault from minidvodbProcessed join mergedvodbRun using
+(minidvodb_id) where merge_id > -1 ;
+
+--then clean up the tables:
+-- once the tables have had the columns migrated over, then and only then do this:
+
+alter table minidvodbRun drop column mergedvodb_path;
+alter table minidvodbProcessed drop column dtime_verify;
+alter table minidvodbProcessed drop column merge_order;
+alter table minidvodbProcessed drop column dtime_merge;
+alter table minidvodbProcessed drop column mergedvodb_path;
+
+
+--then to keep compatible with old camruns (stage_extra1 = negative means it used a destreaked smf)
+
+--it's commented out as it only needs to be run once (and only on gpc1)
+--update addRun set stage_extra1 = -1 where stage = 'cam' <= 483624; 
+
Index: branches/meh_branches/ppstack_test/dbconfig/config.md
===================================================================
--- branches/meh_branches/ppstack_test/dbconfig/config.md	(revision 32161)
+++ branches/meh_branches/ppstack_test/dbconfig/config.md	(revision 33415)
@@ -2,4 +2,4 @@
     pkg_name        STR     ippdb
     pkg_namespace   STR     ippdb
-    pkg_version     STR     1.1.66
+    pkg_version     STR     1.1.71717171717171
 END
Index: branches/meh_branches/ppstack_test/dbconfig/ipp.m4
===================================================================
--- branches/meh_branches/ppstack_test/dbconfig/ipp.m4	(revision 32161)
+++ branches/meh_branches/ppstack_test/dbconfig/ipp.m4	(revision 33415)
@@ -25,4 +25,5 @@
 include(magic.md)
 include(minidvodb.md)
+include(mergedvodb.md)
 include(calibration.md)
 include(flatcorr.md)
@@ -36,2 +37,3 @@
 include(diffphot.md)
 include(lap.md)
+include(vp.md)
Index: branches/meh_branches/ppstack_test/dbconfig/mergedvodb.md
===================================================================
--- branches/meh_branches/ppstack_test/dbconfig/mergedvodb.md	(revision 33415)
+++ branches/meh_branches/ppstack_test/dbconfig/mergedvodb.md	(revision 33415)
@@ -0,0 +1,29 @@
+mergedvodbRun METADATA
+    merge_id        S64    0
+    minidvodb_id    S64    0	     
+    mergedvodb	    STR    64
+    mergedvodb_path STR    255
+    state           STR    64
+    creation_date   UTC    0001-01-01T00:00:00Z
+END 
+
+mergedvodbProcessed METADATA
+    merge_id	    S64    0
+    merge_order     S64    0
+    dtime_verify    F32    0.0
+    dtime_merge     F32    0.0
+    dtime_script    F32	   0.0
+    epoch           UTC    0001-01-01T00:00:00Z
+    fault	    S16	   0
+END
+
+mergedvodbCopy METADATA
+    mergevodbcopy_id	S64 0
+    merge_id    S64    0
+    mergedvodb_rsync_path   STR    255
+    destination_host	   STR	  255
+    fault           S16	   0
+    state           STR    64
+    epoch           UTC    0001-01-01T00:00:00Z
+    dtime           F32    0.0
+END
Index: branches/meh_branches/ppstack_test/dbconfig/minidvodb.md
===================================================================
--- branches/meh_branches/ppstack_test/dbconfig/minidvodb.md	(revision 32161)
+++ branches/meh_branches/ppstack_test/dbconfig/minidvodb.md	(revision 33415)
@@ -4,5 +4,4 @@
     minidvodb_group STR    64
     minidvodb_path  STR    255
-    mergedvodb_path STR    255
     state           STR    64
     creation_date   UTC    0001-01-01T00:00:00Z
@@ -11,12 +10,8 @@
 minidvodbProcessed METADATA
     minidvodb_id    S64    0
-    merge_order     S64    0
     dtime_resort    F32    0.0
     dtime_relphot   F32    0.0
-    dtime_verify    F32    0.0
-    dtime_merge     F32    0.0
     dtime_script    F32	   0.0
     epoch           UTC    0001-01-01T00:00:00Z
-    mergedvodb_path STR    255
     fault	    S16	   0
 END
Index: branches/meh_branches/ppstack_test/dbconfig/pstamp.md
===================================================================
--- branches/meh_branches/ppstack_test/dbconfig/pstamp.md	(revision 32161)
+++ branches/meh_branches/ppstack_test/dbconfig/pstamp.md	(revision 33415)
@@ -48,4 +48,6 @@
     dep_id      S64         0
     fault_count S32         0
+    parent_id   S64         0
+    is_parent   BOOL        f
 END
 
@@ -67,2 +69,8 @@
     num         S64         0    # Primary Key AUTO_INCREMENT
 END
+
+pstampFile METADATA
+    file_id     S64         0   # Primary Key AUTO_INCREMENT
+    job_id      S64         0
+    path        STR         255
+end
Index: branches/meh_branches/ppstack_test/dbconfig/staticsky.md
===================================================================
--- branches/meh_branches/ppstack_test/dbconfig/staticsky.md	(revision 32161)
+++ branches/meh_branches/ppstack_test/dbconfig/staticsky.md	(revision 33415)
@@ -31,2 +31,32 @@
     quality            S16    0	      # Key
 END
+
+skycalRun METADATA
+    skycal_id     S64         0       # Primary Key AUTO_INCREMENT
+    sky_id      S64         0       # fkey(sky_id) ref staticskyRun(sky_id)
+    stack_id    S64         0       # fkey(stack_id) ref stackRun(stack_id)
+    state       STR         64      # Key
+    workdir     STR         255
+    label       STR         64      # Key
+    data_group  STR         64      # Key
+    dist_group  STR         64      # Key
+    reduction   STR         64      # Reduction class
+    registered  TAI         NULL
+    note        STR         255
+END
+
+skycalResult METADATA
+    skycal_id        S64    0       # Primary Key fkey(skycal_id) ref skycalRun(skycal_id)
+    path_base      STR    255
+    dtime_script   F32    0.0
+    dtime_astrom   F32    0.0
+    sigma_ra       F32    0.0
+    sigma_dec      F32    0.0
+    n_astrom       S32    0
+    zpt_obs        F32    0.0
+    zpt_stdev      F32    0.0
+    quality        S16    0	  # Key
+    software_ver   STR    16
+    hostname       STR    64
+    fault          S16    0       # Key
+END
Index: branches/meh_branches/ppstack_test/dbconfig/vp.md
===================================================================
--- branches/meh_branches/ppstack_test/dbconfig/vp.md	(revision 33415)
+++ branches/meh_branches/ppstack_test/dbconfig/vp.md	(revision 33415)
@@ -0,0 +1,24 @@
+vpRun METADATA
+    vp_id      S64           0      # Primary Key AUTO_INCREMENT
+    exp_id      S64         64      # Key INDEX(vp_id, exp_id) fkey (exp_id) ref rawExp(exp_id)
+    state       STR         64      # Key
+    label       STR         64      # Key
+    data_group  STR         64      # Key
+    workdir     STR         255 
+    note        STR         255
+    dest_id     S64         0
+    outroot     STR         255
+    dtime_script F32        0.0
+    hostname    STR         64
+    fault       S16         0       # Key NOT NULL
+END
+
+vpProcessedCell METADATA
+    vp_id           S64     0       # Primary Key fkey (vp_id) ref vpRun(vp_id)
+    class_id        STR     64      # Primary Key
+    cell_id         STR     64      # Primary Key
+    dtime_photom    F32     0.0
+    quality         S16     0
+    path_base       STR     255
+    fault       S16         0       # Key NOT NULL
+END
Index: branches/meh_branches/ppstack_test/ippMonitor/INSTALL
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/INSTALL	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/INSTALL	(revision 33415)
@@ -13,12 +13,6 @@
  psconfigure --dbhost alala --dbuser ipp --dbpass ipp --htdocs /var/www/kiawe
 
-ipp004 / ipp017 example:
- psconfigure --dbhost ipp017 --dbuser ipp --dbpass ipp --htdocs ~ipp/htdocs
+# note that we have a special version for ippmonitor without $PSCONFIG/$PSCONFDIR values:
+ipp@ippdb03 / ippdb03 example:
+ psconfigure --dbhost ippdb01 --dbuser ipp --dbpass ipp --htdocs /var/www/localhost/htdocs --site ~/ippconfig/ippmonitor.config
 
-ipp004 / ipp004 example:
- psconfigure --dbhost ipp004 --dbuser ipp --dbpass ipp --htdocs ~ipp/htdocs
-
-# note that we have a special version for ippmonitor without $PSCONFIG/$PSCONFDIR values:
-ipp@ipp004 / ippdb01 example:
- psconfigure --dbhost ippdb01 --dbuser ipp --dbpass ipp --htdocs ~/htdocs --site ~/ippconfig/ippmonitor.config
-
Index: branches/meh_branches/ppstack_test/ippMonitor/Makefile.in
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/Makefile.in	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/Makefile.in	(revision 33415)
@@ -60,4 +60,5 @@
 $(DESTWWW)/czartool_getplot.php \
 $(DESTWWW)/czartool_exposures.php \
+$(DESTWWW)/czartool_ipptopsps.php \
 $(DESTWWW)/histogram.php \
 $(DESTWWW)/show_and_delete_image.php \
Index: branches/meh_branches/ppstack_test/ippMonitor/czartool/czarplot.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/czartool/czarplot.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/czartool/czarplot.pl	(revision 33415)
@@ -25,10 +25,9 @@
 my $magicMask = undef;
 my $rateInterval = undef;
-my $deriv = undef;
 my $nebulous = undef;
 my $savingToFile = undef;
-my $analysis = undef;
 my $exposureId = undef;
 my $log = undef;
+my $psps = undef;
 
 GetOptions (
@@ -46,9 +45,8 @@
         "nebulous|n" => \$nebulous,
         "rate|r" => \$rate,
-        "deriv|f" => \$deriv,
-        "analysis|a" => \$analysis,
         "timeseries|t" => \$timeSeries,
         "verbose|v" => \$verbose,
         "log|g" => \$log,
+        "psps|u" => \$psps,
         );
 
@@ -60,7 +58,4 @@
     print "* UNKNKOWN: option                          @ARGV\n";
 }
-if ($analysis && !$stage) {
-    $quit = 1;
-    print "* REQUIRED: choose a stage for analsis      -s <chip|cam|warp|etc>      (default=none)\n";}
 if (!$histogram) {
     print "* OPTIONAL: plot histogram                  -h                          (default=off)\n";}
@@ -73,10 +68,4 @@
 if (!$rate) {
     print "* OPTIONAL: plot histogram of rate          -r                          (default=off)\n";} 
-if (!$deriv) {
-    $deriv = 0;
-    print "* OPTIONAL: plot first derivative           -f                          (default=$deriv)\n";} 
-if (!$analysis) {
-    $analysis = 0;
-    print "* OPTIONAL: include analysis                -a                          (default=$analysis)\n";} 
 if (!$log) {
     $log = 0;
@@ -94,5 +83,5 @@
     print "* OPTIONAL: time interval for rate plot     -i <'1 hour'|'1 day'|etc>   (default=depends on time frame)\n";} 
 if (!$begin) {
-    print "* OPTIONAL: choose a begin time             -b <datetime>               (default=6:35am this morning)\n";} 
+    print "* OPTIONAL: choose a begin time             -b <datetime>               (default=18:00)\n";} 
 if (!$end) {
     print "* OPTIONAL: choose an end time              -e <datetime>               (default=now)\n";} 
@@ -102,4 +91,6 @@
     print "* OPTIONAL: choose an output location       -o <path>                   (default=none)\n";
 } 
+if (!$psps) {
+    print "* OPTIONAL: plot ippToPsps stages           -u                          (default=off)\n";} 
 print "*\n*******************************************************************************\n";
 
@@ -109,5 +100,5 @@
 
 # default values
-if (!$rate && !$magicMask && !$nebulous && !$histogram && !$analysis && !$timeSeries) {$timeSeries = 1;}
+if (!$rate && !$magicMask && !$nebulous && !$histogram && !$timeSeries) {$timeSeries = 1;}
 if (!$verbose) {$verbose = 0;}
 if (!$save_temps) {$save_temps = 0;}
@@ -128,4 +119,6 @@
     $plotter = czartool::Plotter->new_display($config, $save_temps);
 }
+
+if ($psps) {$plotter->setIppToPspsMode();}
 
 # if a single day has been chosen 
@@ -182,7 +175,2 @@
 }
 
-if($analysis) {
-
-    my $stageMetrics = new czartool::StageMetrics($stage, $label, $begin, $end);
-    if ($czarDb->runAnalysis($stageMetrics)) {$stageMetrics->printMe();}
-}
Index: branches/meh_branches/ppstack_test/ippMonitor/czartool/czarpoll.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/czartool/czarpoll.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/czartool/czarpoll.pl	(revision 33415)
@@ -37,6 +37,28 @@
 
 
-my @stages = ("burntool", "chip", "cam", "fake", "warp", "stack", "diff", "magic", "magicDS", "dist", "pub");
-
+my @stages = (
+        "burntool", 
+        "chip", 
+        "cam", 
+        "fake", 
+        "warp", 
+        "stack", 
+        "diff", 
+        "magic", 
+        "magicDS", 
+        "dist", 
+        "pub");
+
+my @ippToPspsStages = (
+        "processed",
+        "loaded_to_datastore",
+        "loaded_to_ODM",
+        "merge_worthy",
+        "deleted_datastore",
+        "deleted_dxlayer",
+        "merged",
+        "deleted_local");
+
+my @allStages = (@stages, @ippToPspsStages);
 
 timePoll($period);
@@ -219,5 +241,5 @@
 
                 updateAllStages($thisServer, $newState, $labels, $begin, $end);
-                createPlots($thisServer, $labels, $begin, $end);
+                if ($thisServer eq "stdscience") {createPlots($thisServer, $labels, $begin, $end);}
             }
             else { print "* WARNING: no $thisServer labels found in Db\n";}
@@ -244,5 +266,29 @@
     print "* Generating plots\n";
 
+    # get ippToPsps labels
+    my $ippToPspsLabels = undef;
+    $czarDb->getCurrentIppToPspsLabels($begin, $end, \$ippToPspsLabels);
+    $plotter->setIppToPspsMode();
+    
+    # create ippToPsps labels
+    foreach $stage (@ippToPspsStages) {
+        foreach $row ( @{$ippToPspsLabels} ) {
+            my ($label) = @{$row};
+            
+            chomp($label);
+            $plotter->createTimeSeries($label, $stage, $begin, $end, 1, 0, 1);
+        }
+    }
+
+    # create plots for each label for all stages for ippToPsps
+    foreach $row ( @{$ippToPspsLabels} ) {
+        my ($label) = @{$row};
+
+        $plotter->createTimeSeries($label, undef, $begin, $end, 1, 1, 1);
+    }
+
+
     # create plots for each label for each stage
+    $plotter->setStandardMode();
     foreach $stage (@stages) {
         foreach $row ( @{$rows} ) {
@@ -250,5 +296,5 @@
 
             chomp($label);
-            $plotter->createTimeSeries($label, $stage, $begin, $end, 1, 1, 1);
+            $plotter->createTimeSeries($label, $stage, $begin, $end, 1, 0, 1);
         }
     }
@@ -265,9 +311,9 @@
     $allServerLabels = "all_".$server."_labels";
 
-    $plotter->createTimeSeries($allServerLabels, undef, $begin, $end, 1, 1, 1);
+    $plotter->createTimeSeries($allServerLabels, undef, $begin, $end, 1, 0, 1);
 
     foreach $stage (@stages) {
 
-        $plotter->createTimeSeries($allServerLabels, $stage, $begin, $end, 1, 1, 1); # TODO must be a neater way...
+        $plotter->createTimeSeries($allServerLabels, $stage, $begin, $end, 1, 0, 1); # TODO must be a neater way...
     }
 }
Index: branches/meh_branches/ppstack_test/ippMonitor/czartool/czartool/CzarDb.pm
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/czartool/czartool/CzarDb.pm	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/czartool/czartool/CzarDb.pm	(revision 33415)
@@ -8,5 +8,28 @@
 use File::Temp;
 
-my @stages = ("burntool", "chip", "cam", "fake", "warp", "stack", "diff", "magic", "magicDS", "dist", "pub"); # TODO put elsewhere
+my @stages = (
+        "burntool", 
+        "chip", 
+        "cam", 
+        "fake", 
+        "warp", 
+        "stack", 
+        "diff", 
+        "magic", 
+        "magicDS", 
+        "dist", 
+        "pub"); # TODO put elsewhere
+
+my @ippToPspsStages = (
+        "processed",
+        "loaded_to_datastore",
+        "loaded_to_ODM",
+        "merge_worthy",
+        "deleted_datastore",
+        "deleted_dxlayer",
+        "merged",
+        "deleted_local");
+
+my @allStages = (@stages, @ippToPspsStages);
 
 use base 'czartool::MySQLDb';
@@ -28,5 +51,4 @@
     bless $self, $class;
 
-    $self->update();    
     return $self;
 }
@@ -34,5 +56,28 @@
 ###########################################################################
 #
-# Gets current_labels table
+# Gets current ippToPsps labels
+#
+###########################################################################
+sub getCurrentIppToPspsLabels {
+    my ($self, $begin, $end, $labels) = @_;
+
+    my $query = $self->{_db}->prepare(<<SQL);
+
+    SELECT DISTINCT label
+        FROM processed
+        WHERE timestamp >= '$begin' 
+        AND timestamp <= '$end' 
+SQL
+
+    if (!$query->execute) {return 0;}
+
+    ${$labels} = $query->fetchall_arrayref();
+
+    return 1;
+}
+
+###########################################################################
+#
+# Gets current labels from the current_labels table
 #
 ###########################################################################
@@ -673,4 +718,6 @@
             # times
             $timeSep = $self->diffTimesInSecs($thisTimestamp, $lastTimestamp);
+            if ($timeSep == 0) {$timeSep = 1;}; # HACK to prevent divide-by-zero
+
             $middleTime = $self->getMiddleTime($lastTimestamp, $thisTimestamp);
             $formattedMiddleTimestamp = $self->getFormattedDate($middleTime);
@@ -1108,5 +1155,5 @@
         my $stage = undef;
         $totalDeleted = 0;
-        foreach $stage (@stages) {
+        foreach $stage (@allStages) {
 
             if (!$self->getLabelsInThisTimePeriod($stage, $fromTime, $toTime, \$labels)) {next;}
@@ -1214,5 +1261,5 @@
 
     my $stage = undef;
-    foreach $stage (@stages) {
+    foreach $stage (@allStages) {
 
         $self->optimizeTable($stage);
@@ -1342,432 +1389,4 @@
 }
 
-###########################################################################
-#
-# Update Db to newest version 
-#
-###########################################################################
-sub update { 
-    my ($self) = @_;
-
-    my $currentRevision = -1;
-
-    while (1) {
-
-        $currentRevision = $self->getRevision();
-        if ($self->{_verbose}) {print "* Current Db revision = $currentRevision\n";}
-
-        if ($currentRevision == 0) {$self->createRevision_1();}
-        elsif ($currentRevision == 1) {$self->createRevision_2();}
-        elsif ($currentRevision == 2) {$self->createRevision_3();}
-        elsif ($currentRevision == 3) {$self->createRevision_4();}
-        elsif ($currentRevision == 4) {$self->createRevision_5();}
-        elsif ($currentRevision == 5) {$self->createRevision_6();}
-        elsif ($currentRevision == 6) {$self->createRevision_7();}
-        elsif ($currentRevision == 7) {$self->createRevision_8();}
-        elsif ($currentRevision == 8) {$self->createRevision_9();}
-        elsif ($currentRevision == 9) {$self->createRevision_10();}
-        elsif ($currentRevision == 10) {$self->createRevision_11();}
-        elsif ($currentRevision == 11) {$self->createRevision_12();}
-        elsif ($currentRevision == 12) {$self->createRevision_13();}
-        else {last;}
-    }
-}
-
-#######################################################################################
-# 
-# Sets current revision of ippToPsps database
-#
-#######################################################################################
-sub setRevision {
-    my ($self, $revision) = @_;
-
-    my $query = $self->{_db}->prepare(<<SQL);
-    INSERT INTO revision (revision) VALUES ($revision);
-SQL
-        $query->execute;
-}
-
-#######################################################################################
-# 
-# Gets current revision of ippToPsps database
-#
-#######################################################################################
-sub getRevision {
-    my ($self) = @_;
-
-    if (!$self->SUPER::doesTableExist("revision")) {return 0;}
-
-    my $query = $self->{_db}->prepare(<<SQL);
-    SELECT revision
-        FROM revision
-        ORDER BY revision DESC LIMIT 1;
-SQL
-
-        $query->execute;
-    my @row = $query->fetchrow_array();
-
-    return $row[0];
-}
-
-#######################################################################################
-# 
-# Create revision 1 of the database 
-#
-#######################################################################################
-sub createRevision_1 {
-    my ($self) = @_;
-
-    print "* Creating revision 1 of '$self->{_dbName}'\n";
-
-    my $query = $self->{_db}->prepare(<<SQL);
-    CREATE TABLE revision (
-            revision INT, 
-            created TIMESTAMP DEFAULT NOW(),
-            primary key (revision)
-            );
-SQL
-        $query->execute;
-
-    my $stage = undef;
-    foreach $stage (@stages) {
-
-        my $query = $self->{_db}->prepare(<<SQL);
-        CREATE TABLE $stage (
-                timestamp TIMESTAMP DEFAULT NOW(),
-                label VARCHAR(128) DEFAULT "NONE", 
-                pending BIGINT NOT NULL,
-                faults BIGINT NOT NULL
-                );
-SQL
-            $query->execute;
-
-    }
-
-    $self->setRevision(1);
-}
-
-#######################################################################################
-# 
-# Create revision 2 of the database 
-#
-#######################################################################################
-sub createRevision_2 {
-    my ($self) = @_;
-
-    print "* Creating revision 2 of '$self->{_dbName}'\n";
-
-
-    my $stage = undef;
-    foreach $stage (@stages) {
-
-        my $query = $self->{_db}->prepare(<<SQL);
-        ALTER TABLE $stage 
-            ADD COLUMN reverting TINYINT NOT NULL DEFAULT 0;
-SQL
-            $query->execute;
-
-    }
-
-    $self->setRevision(2);
-}
-
-#######################################################################################
-# 
-# Create revision 3 of the database 
-#
-#######################################################################################
-sub createRevision_3 {
-    my ($self) = @_;
-
-    print "* Creating revision 3 of '$self->{_dbName}'\n";
-
-
-    my $stage = undef;
-    foreach $stage (@stages) {
-
-        my $query = $self->{_db}->prepare(<<SQL);
-        ALTER TABLE $stage 
-            ADD COLUMN processed BIGINT NOT NULL DEFAULT 0;
-SQL
-
-            $query->execute;
-    }
-
-    $self->setRevision(3);
-}
-
-#######################################################################################
-# 
-# Create revision 4 of the database 
-#
-#######################################################################################
-sub createRevision_4 {
-    my ($self) = @_;
-
-    print "* Creating revision 4 of '$self->{_dbName}'\n";
-
-    my $query = $self->{_db}->prepare(<<SQL);
-    CREATE TABLE current_labels (server VARCHAR(128), label VARCHAR(128));
-SQL
-
-        $query->execute;
-
-    $self->setRevision(4);
-}
-
-#######################################################################################
-# 
-# Create revision 5 of the database 
-#
-#######################################################################################
-sub createRevision_5 {
-    my ($self) = @_;
-
-    print "* Creating revision 5 of '$self->{_dbName}'\n";
-
-    my $query = $self->{_db}->prepare(<<SQL);
-    CREATE TABLE servers (
-            timestamp TIMESTAMP DEFAULT NOW(),
-            server VARCHAR(128), 
-            alive TINYINT, running TINYINT);
-SQL
-
-        $query->execute;
-
-    $self->setRevision(5);
-}
-
-#######################################################################################
-# 
-# Create revision 6 of the database 
-#
-#######################################################################################
-sub createRevision_6 {
-    my ($self) = @_;
-
-    print "* Creating revision 6 of '$self->{_dbName}'\n";
-
-    my $stage = undef;
-    my $index = undef;
-    foreach $stage (@stages) {
-
-        $index = $stage . "Index";
-        my $query = $self->{_db}->prepare(<<SQL);
-        CREATE INDEX $index ON $stage (timestamp, label);
-SQL
-
-            $query->execute;
-    }
-
-    my $query = $self->{_db}->prepare(<<SQL);
-    CREATE INDEX serverIndex ON servers (timestamp, server);
-SQL
-
-        $query->execute;
-
-
-    $self->setRevision(6);
-}
-
-#######################################################################################
-# 
-# Create revision 7 of the database 
-#
-#######################################################################################
-sub createRevision_7 {
-    my ($self) = @_;
-
-    print "* Creating revision 7 of '$self->{_dbName}'\n";
-
-    my $query = $self->{_db}->prepare(<<SQL);
-    ALTER TABLE current_labels 
-        ADD COLUMN priority INT NOT NULL DEFAULT 0;
-SQL
-
-        $query->execute;
-
-    $self->setRevision(7);
-}
-
-#######################################################################################
-# 
-# Create revision 8 of the database 
-#
-#######################################################################################
-sub createRevision_8 {
-    my ($self) = @_;
-
-    print "* Creating revision 8 of '$self->{_dbName}'\n";
-
-    # drop reverting column from all stages tables
-    my $stage = undef;
-    foreach $stage (@stages) {
-
-        my $query = $self->{_db}->prepare(<<SQL);
-        ALTER TABLE $stage 
-            DROP COLUMN reverting;
-SQL
-
-            $query->execute;
-    }
-
-    # create new 'revert' table
-    my $query = $self->{_db}->prepare(<<SQL);
-    CREATE TABLE reverts (
-            stage VARCHAR(128), 
-            reverting TINYINT);
-SQL
-
-        $query->execute;
-
-    # insert stages into revert table
-    foreach $stage (@stages) {
-        my $query = $self->{_db}->prepare(<<SQL);
-        INSERT INTO reverts
-            (stage, reverting)
-            VALUES
-            ('$stage', 0);
-SQL
-            $query->execute;
-    }
-
-    $self->setRevision(8);
-}
-
-#######################################################################################
-# 
-# Create revision 9 of the database 
-#
-#######################################################################################
-sub createRevision_9 {
-    my ($self) = @_;
-
-    print "* Creating revision 9 of '$self->{_dbName}'\n";
-
-    my $query = $self->{_db}->prepare(<<SQL);
-    CREATE TABLE cluster_space (
-            timestamp TIMESTAMP DEFAULT NOW(),
-            total FLOAT, 
-            available FLOAT, 
-            used FLOAT, 
-            hostsOver98 SMALLINT);
-SQL
-
-        $query->execute;
-    $query = $self->{_db}->prepare(<<SQL);
-    CREATE TABLE hosts (
-            host VARCHAR(128), 
-            total FLOAT, 
-            available FLOAT, 
-            used FLOAT); 
-SQL
-
-        $query->execute;
-
-    $query = $self->{_db}->prepare(<<SQL);
-    CREATE INDEX clusterSpaceIndex ON cluster_space (timestamp);
-SQL
-
-        $query->execute;
-
-    $self->setRevision(9);
-}
-
-#######################################################################################
-# 
-# Create revision 10 of the database 
-#
-#######################################################################################
-sub createRevision_10 {
-    my ($self) = @_;
-
-    print "* Creating revision 10 of '$self->{_dbName}'\n";
-
-    my $query = $self->{_db}->prepare(<<SQL);
-    ALTER TABLE hosts 
-        ADD COLUMN writable TINYINT NOT NULL DEFAULT 0,
-            ADD COLUMN readable TINYINT NOT NULL DEFAULT 0;
-SQL
-
-        $query->execute;
-
-    $self->setRevision(10);
-}
-
-#######################################################################################
-# 
-# Create revision 11 of the database 
-#
-#######################################################################################
-sub createRevision_11 {
-    my ($self) = @_;
-
-    print "* Creating revision 11 of '$self->{_dbName}'\n";
-
-    # same shape as other stage tables to enable easy update
-    #my $query = $self->{_db}->prepare(<<SQL);
-    #CREATE TABLE burntool (
-    #        timestamp TIMESTAMP DEFAULT NOW(),
-    #        label VARCHAR(128) DEFAULT "NONE", 
-    #        pending BIGINT NOT NULL,
-    #        processed BIGINT NOT NULL,
-    #        faults BIGINT NOT NULL);
-#SQL
-
-#        $query->execute;
-#    $query = $self->{_db}->prepare(<<SQL);
-#    CREATE INDEX burntoolIndex ON burntool (timestamp, label);
-#SQL
-
-#        $query->execute;
-
-    $self->setRevision(11);
-}
-
-#######################################################################################
-# 
-# Create revision 12 of the database 
-#
-# - adding nightlyscience table
-#
-#######################################################################################
-sub createRevision_12 {
-    my ($self) = @_;
-
-    print "* Creating revision 12 of '$self->{_dbName}'\n";
-
-    my $query = $self->{_db}->prepare(<<SQL);
-    CREATE TABLE nightlyscience (
-            timestamp TIMESTAMP DEFAULT NOW(),
-            status VARCHAR(128) DEFAULT "NONE"); 
-SQL
-
-        $query->execute;
-
-    $self->setRevision(12);
-}
-#######################################################################################
-# 
-# Create revision 13 of the database 
-#
-# adding server_dates table
-#
-#######################################################################################
-sub createRevision_13 {
-    my ($self) = @_;
-
-    print "* Creating revision 13 of '$self->{_dbName}'\n";
-
-    my $query = $self->{_db}->prepare(<<SQL);
-    CREATE TABLE server_dates (
-            timestamp TIMESTAMP DEFAULT NOW(),
-            server VARCHAR(64) DEFAULT "NONE", 
-            date VARCHAR(64) DEFAULT "NONE"); 
-SQL
-
-        $query->execute;
-
-    $self->setRevision(13);
-}
 1;
 
Index: branches/meh_branches/ppstack_test/ippMonitor/czartool/czartool/Gpc1Db.pm
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/czartool/czartool/Gpc1Db.pm	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/czartool/czartool/Gpc1Db.pm	(revision 33415)
@@ -233,4 +233,14 @@
 
     }
+    # 'new' stuff really includes 'update' stuff, at least for LAP. This is a hack, but there you go    
+    elsif ($state eq "new") {
+
+        $query = $self->{_db}->prepare(<<SQL);
+        SELECT COUNT(state)  
+            FROM $table 
+            WHERE label LIKE '$label' 
+            AND (state = 'new' OR state = 'update')
+SQL
+    }
     else {
 
Index: branches/meh_branches/ppstack_test/ippMonitor/czartool/czartool/MetricsIndex.pm
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/czartool/czartool/MetricsIndex.pm	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/czartool/czartool/MetricsIndex.pm	(revision 33415)
@@ -54,6 +54,6 @@
     my $htmlFile = $self->{htmlFile};
 
-    print $htmlFile "<img src=\"masterMask.png\" alt=\"master magic mask fraction plot\" />\n";
-    print $htmlFile "<img src=\"masterPro.png\" alt=\"master MD processing plot\" />\n";
+    #print $htmlFile "<img src=\"masterMask.png\" alt=\"master magic mask fraction plot\" />\n";
+    #print $htmlFile "<img src=\"masterPro.png\" alt=\"master MD processing plot\" />\n";
 
     opendir(DIR, $self->{path}) or die $!;
Index: branches/meh_branches/ppstack_test/ippMonitor/czartool/czartool/Pantasks.pm
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/czartool/czartool/Pantasks.pm	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/czartool/czartool/Pantasks.pm	(revision 33415)
@@ -10,4 +10,5 @@
         "addstarlap", 
         "cleanup", 
+        "deepstack",
         "detrend", 
         "distribution", 
Index: branches/meh_branches/ppstack_test/ippMonitor/czartool/czartool/Plotter.pm
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/czartool/czartool/Plotter.pm	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/czartool/czartool/Plotter.pm	(revision 33415)
@@ -10,5 +10,27 @@
 use czartool::Config;
 
-my @allStages = ("burntool", "chip", "cam", "fake", "warp", "stack", "diff", "magic", "magicDS", "dist", "pub");
+my @allStages = (
+        "burntool", 
+        "chip", 
+        "cam", 
+        "fake", 
+        "warp", 
+        "stack", 
+        "diff", 
+        "magic", 
+        "magicDS", 
+        "dist", 
+        "pub");
+
+my @allIppToPspsStages = (
+        "processed", 
+        "loaded_to_datastore", 
+        "loaded_to_ODM", 
+        "merge_worthy", 
+        "deleted_datastore", 
+        "deleted_dxlayer", 
+        "merged", 
+        "deleted_local");
+
 
 ###########################################################################
@@ -75,4 +97,26 @@
 ###########################################################################
 #
+# Sets plotter to use standard IPP mode, i.e. chip, cam etc 
+#
+###########################################################################
+sub setStandardMode {
+    my ($self) = @_;
+
+    $self->{allStages} = \@allStages;
+}
+
+###########################################################################
+#
+# Sets plotter to use ippToPsps mode, i.e. processed, loaded_to_datastore etc 
+#
+###########################################################################
+sub setIppToPspsMode {
+    my ($self) = @_;
+
+    $self->{allStages} = \@allIppToPspsStages;
+}
+
+###########################################################################
+#
 # Some initialisation code common to all constructors
 #
@@ -83,4 +127,5 @@
     $self->{_gpc1Db} = $self->{_config}->getGpc1Instance();
     $self->{_czarDb} = $self->{_config}->getCzarDbInstance();
+    $self->setStandardMode();
 }
 
@@ -140,5 +185,5 @@
     my $stages = undef;                 
     if (!$selectedStage) {
-        $stages = \@allStages;
+        $stages = \@{$self->{allStages}};
         $selectedStage = 'all_stages';
     }
@@ -203,10 +248,10 @@
         "set style histogram rowstacked;" .
         "set style fill solid border -1;" .
-        "set ylabel \"Exposures processed per $interval\";" .
+        "set ylabel \"Items processed per $interval\";" .
         "set boxwidth 0.75;" .
         "plot ";
 
     my $first = 1;
-    foreach my $stage (@allStages) {
+    foreach my $stage (@{$self->{allStages}}) {
 
 
@@ -226,5 +271,5 @@
 ###########################################################################
 #
-# Plots a time series for all stages for this label
+# Plots a time series for all or one stage for this label
 #
 ###########################################################################
@@ -238,5 +283,5 @@
     my $stages = undef;                 
 
-    if (!$selectedStage) {$stages = \@allStages;}
+    if (!$selectedStage) {$stages = \@{$self->{allStages}};}
     else {$stages = ["$selectedStage"]};        
 
@@ -297,5 +342,5 @@
                 $timeDiff, 
                 "",
-                "Exposures");
+                "Items");
     }
 
@@ -314,5 +359,5 @@
                 $timeDiff, 
                 "",
-                "Log(Exposures)");
+                "Log(Items)");
     }
 
@@ -331,5 +376,5 @@
                 $timeDiff, 
                 "",
-                "Exposures per hour");
+                "Items per hour");
 }    
 }                                                
@@ -351,5 +396,5 @@
     my $stage = undef;
     my $pendingMinusFaults = undef;
-    foreach $stage (@allStages) {
+    foreach $stage (@{$self->{allStages}}) {
 
         if (!$self->{_czarDb}->countProcessedPendingAndFaults(
@@ -381,5 +426,5 @@
         "set style histogram rowstacked;" .
         "set style fill solid border -1;" .
-        "set ylabel \"Exposures\";" .
+        "set ylabel \"Items\";" .
         "set boxwidth 0.75;" .
         "plot '".$tmpFile->filename."' using 2:xtic(1) title \"Faults\" lt 1, '' using 3 title \"Processed\" lt 2, '' using 4 title \"Pending\" lt 7;" . 
@@ -520,5 +565,6 @@
     my $firstIn = 1;
     # loop through stages array so that they are ordered properly (not maintained in hash)
-    foreach my $stage (@allStages) {
+    #foreach my $stage (@allStages) {
+    foreach my $stage (@{$self->{allStages}}) {
 
         if(!$gnuplotFiles->{$stage}) {next;}
@@ -620,4 +666,5 @@
         "set title \"Nebulous disk use across IPP cluster ($totalPercent of total allocated)\";" .
         "set style fill solid 1.00 border -1;" .
+        "set key center top;" .
         "set style histogram rowstacked;" .
         "set style data histograms;" .
Index: branches/meh_branches/ppstack_test/ippMonitor/czartool/czartool/czarconfig.xml
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/czartool/czartool/czarconfig.xml	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/czartool/czartool/czarconfig.xml	(revision 33415)
@@ -9,5 +9,5 @@
     <font>/usr/share/fonts/corefonts/arial.ttf</font>
     <size>8</size>
-    <path>/data/ipp004.0/czartool_plots</path>
+    <path>/data/ipp009.0/czartool_plots</path>
   </gnuplot>
 
@@ -22,6 +22,6 @@
     <name>gpc1</name>
     <host>ippdb01</host>
-    <user>ippuser</user>
-    <password>ippuser</password>
+    <user>ippMonitor</user>
+    <password>ippMonitor</password>
   </gpc1database>
 
Index: branches/meh_branches/ppstack_test/ippMonitor/def/chipProcessedImfile.d
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/def/chipProcessedImfile.d	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/def/chipProcessedImfile.d	(revision 33415)
@@ -9,4 +9,6 @@
 WHERE chipProcessedImfile.chip_id = chipRun.chip_id
 WHERE chipProcessedImfile.fault = 0
+
+#UNRESTRICTED WHERE 0=1
 
 ARGS  ARG1 rawImfile.exp_id=$rawExp.exp_id
Index: branches/meh_branches/ppstack_test/ippMonitor/def/distRun.d
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/def/distRun.d	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/def/distRun.d	(revision 33415)
@@ -7,4 +7,5 @@
 
 WHERE distRun.dist_id = distComponent.dist_id
+UNRESTRICTED AND 0=1
 
 #     field                   size  format  name         show    link to                  extras
Index: branches/meh_branches/ppstack_test/ippMonitor/def/lapExposures.d
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/def/lapExposures.d	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/def/lapExposures.d	(revision 33415)
@@ -6,54 +6,55 @@
 UNRESTRICTED WHERE 0=1
 
-ARGS  ARG1	rawImfile.exp_id=$lapExp.exp_id
-ARGS  ARG2	chipRun.chip_id=$lapExp.chip_id
-ARGS  ARG3	chipRun.chip_id=$lapExp.pair_id
-ARGS  ARG4	camRun.cam_id=$camRun1.cam_id
-ARGS  ARG5	camRun.cam_id=$camRun2.cam_id
-ARGS  ARG6	fakeRun.fake_id=$fakeRun1.fake_id
-ARGS  ARG7	fakeRun.fake_id=$fakeRun2.fake_id
-ARGS  ARG8	warpRun.warp_id=$warpRun1.warp_id
-ARGS  ARG9	warpRun.warp_id=$warpRun2.warp_id
-ARGS  ARG10	diffRun.diff_id=$diffRun.diff_id
+ARGS  ARG1      rawImfile.exp_id=$lapExp.exp_id
+ARGS  ARG2      chipRun.chip_id=$lapExp.chip_id
+ARGS  ARG3      chipRun.chip_id=$lapExp.pair_id
+ARGS  ARG4      camRun.cam_id=$camRun1.cam_id
+ARGS  ARG5      camRun.cam_id=$camRun2.cam_id
+ARGS  ARG6      fakeRun.fake_id=$fakeRun1.fake_id
+ARGS  ARG7      fakeRun.fake_id=$fakeRun2.fake_id
+ARGS  ARG8      warpRun.warp_id=$warpRun1.warp_id
+ARGS  ARG9      warpRun.warp_id=$warpRun2.warp_id
+ARGS  ARG10     diffRun.diff_id=$diffRun.diff_id
 
-#     field                   size  format  name         show    link to                  extras
-FIELD lapRun.seq_id,          6, %d,     seq_id
-FIELD lapExp.lap_id,          6, %d,     lap_id
-FIELD lapExp.data_state,      6, %s,     State (lapExp)
-FIELD lapExp.exp_id,          6, %d,     Exp ID,	value, rawImfile.php, ARG1
-FIELD lapExp.chip_id,         6, %d,     Chip1 (lapExp.chip_id),	value, chipStageExp.php, ARG2
-FIELD chipRun1.state AS ch1state,          6, %s,    Chip1 State
-FIELD camRun1.cam_id AS cam_id_1,         6, %d,     Cam1 ID,	value, camStageExp.php, ARG4
-FIELD camRun1.state AS ca1state,          6, %s,     Cam1 State
-FIELD fakeRun1.fake_id AS fake_id_1,         6, %d,     Fake1 ID,	value, fakeStageExp.php, ARG6
-FIELD fakeRun1.state AS fa1state,          6, %s,     Fake1 State
-FIELD warpRun1.warp_id AS warp_id_1,         6, %d,     Warp1 ID,	value, warpStageExp.php, ARG8
-FIELD warpRun1.state AS wa1state,          6, %s,     Warp1 State
-FIELD lapExp.pair_id,         6, %d,     Chip2 ID (lapExp.chip_id),	value, chipStageExp.php, ARG3
-FIELD chipRun2.state AS ch2state,          6, %s,    Chip2 State
-FIELD camRun2.cam_id AS cam_id_2,         6, %d,     Cam2 ID,	value, camStageExp.php, ARG5
-FIELD camRun2.state AS ca2state,          6, %s,     Cam2 State
-FIELD fakeRun2.fake_id AS fake_id_2,         6, %d,     Fake2 ID,	value, fakeStageExp.php, ARG7
-FIELD fakeRun2.state AS fa2state,          6, %s,     Fake2 State
-FIELD warpRun2.warp_id AS warp_id_2,         6, %d,     Warp2 ID,	value, warpStageExp.php, ARG9
-FIELD warpRun2.state AS wa2state,          6, %s,     Warp2 State
-FIELD diffRun.diff_id,          6, %d,     Diff ID,	value, diffStageExp.php, ARG10
-FIELD diffRun.state AS diState,          6, %s,     Diff State
-FIELD lapRun.tess_id,         8, %s,     tess_id
-FIELD lapRun.projection_cell, 10, %s,    ProjCell
-FIELD lapRun.filter,          7, %s,     Filter
-FIELD lapRun.state,           15, %s,    State (lapRun)
-FIELD lapRun.label,           15, %s,    Label
-FIELD lapRun.dist_group,      15, %s,    Dist Group
-FIELD lapRun.registered,      19, %T,    Registered
-FIELD lapRun.fault,           3, %d,     fault
-FIELD lapRun.quick_sass_id,   6, %d,     quick_sass_id
-FIELD lapRun.final_sass_id,   6, %d,     final_sass_id
-FIELD lapExp.private,         2, %d,     Private
-FIELD lapExp.pairwise,        2, %d,     Pairwise
-FIELD lapExp.active,          2, %d,     Active
-FIELD rawExp.dateobs,         19, %T,     Date/Time
-FIELD rawExp.object, 	      8, %s,     Object
-FIELD rawExp.comment, 	      80, %s,	 Comment
+#     field                        size  format  name                       show   link to           extras
+FIELD lapRun.seq_id,                  6, %d,     seq_id
+FIELD lapExp.lap_id,                  6, %d,     lap_id
+FIELD lapExp.data_state,              6, %s,     State (lapExp)
+FIELD lapExp.exp_id,                  6, %d,     Exp ID,                    value, rawImfile.php,    ARG1
+FIELD lapExp.chip_id,                 6, %d,     Chip1 (lapExp.chip_id),    value, chipStageExp.php, ARG2
+FIELD chipRun1.state AS ch1state,     6, %s,     Chip1 State
+FIELD camRun1.cam_id AS cam_id_1,     6, %d,     Cam1 ID,                   value, camStageExp.php,  ARG4
+FIELD camRun1.state AS ca1state,      6, %s,     Cam1 State
+FIELD fakeRun1.fake_id AS fake_id_1,  6, %d,     Fake1 ID,                  value, fakeStageExp.php, ARG6
+FIELD fakeRun1.state AS fa1state,     6, %s,     Fake1 State
+FIELD warpRun1.warp_id AS warp_id_1,  6, %d,     Warp1 ID,                  value, warpStageExp.php, ARG8
+FIELD warpRun1.state AS wa1state,     6, %s,     Warp1 State
+FIELD lapExp.pair_id,                 6, %d,     Chip2 ID (lapExp.chip_id), value, chipStageExp.php, ARG3
+FIELD chipRun2.state AS ch2state,     6, %s,     Chip2 State
+FIELD camRun2.cam_id AS cam_id_2,     6, %d,     Cam2 ID,                   value, camStageExp.php,  ARG5
+FIELD camRun2.state AS ca2state,      6, %s,     Cam2 State               
+FIELD fakeRun2.fake_id AS fake_id_2,  6, %d,     Fake2 ID,                  value, fakeStageExp.php, ARG7
+FIELD fakeRun2.state AS fa2state,     6, %s,     Fake2 State              
+FIELD warpRun2.warp_id AS warp_id_2,  6, %d,     Warp2 ID,                  value, warpStageExp.php, ARG9
+FIELD warpRun2.state AS wa2state,     6, %s,     Warp2 State              
+FIELD diffRun.diff_id,                6, %d,     Diff ID,                   value, diffStageExp.php, ARG10
+FIELD diffRun.state AS diState,       6, %s,     Diff State
+FIELD lapRun.tess_id,                 8, %s,     tess_id
+FIELD lapRun.projection_cell,        10, %s,     ProjCell
+FIELD diffInputSkyfile.skycell_id,   10, %s,     Skycell ID
+FIELD lapRun.filter,                  7, %s,     Filter
+FIELD lapRun.state,                  15, %s,     State (lapRun)
+FIELD lapRun.label,                  15, %s,     Label
+FIELD lapRun.dist_group,             15, %s,     Dist Group
+FIELD lapRun.registered,             19, %T,     Registered
+FIELD lapRun.fault,                   3, %d,     fault
+FIELD lapRun.quick_sass_id,           6, %d,     quick_sass_id
+FIELD lapRun.final_sass_id,           6, %d,     final_sass_id
+FIELD lapExp.private,                 2, %d,     Private
+FIELD lapExp.pairwise,                2, %d,     Pairwise
+FIELD lapExp.active,                  2, %d,     Active
+FIELD rawExp.dateobs,                19, %T,     Date/Time
+FIELD rawExp.object,                  8, %s,     Object
+FIELD rawExp.comment,                80, %s,     Comment
 
 # MODE summary
Index: branches/meh_branches/ppstack_test/ippMonitor/def/mopsStatus.d
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/def/mopsStatus.d	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/def/mopsStatus.d	(revision 33415)
@@ -5,5 +5,5 @@
 #WHERE rawExp.obs_mode!='ENGINEERING'
 
-UNRESTRICTED WHERE dateobs>=DATE(NOW()) AND rawExp.obs_mode!='ENGINEERING'
+UNRESTRICTED WHERE dateobs>=DATE(NOW()) AND rawExp.obs_mode!='ENGINEERING' AND rawExp.obs_mode!='STS' AND rawExp.obs_mode!='M31' AND rawExp.obs_mode!='CNP'
 
 # OP  OP1   ($ddiff_id > 0 ? $ddiff_id : $tdiff_id)
Index: branches/meh_branches/ppstack_test/ippMonitor/def/stackProcessedSkyfile_Images.d
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/def/stackProcessedSkyfile_Images.d	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/def/stackProcessedSkyfile_Images.d	(revision 33415)
@@ -18,9 +18,10 @@
 
 #        field                   size  format  name           show     link to         extras
-FIELD stackRun.stack_id,            5, %s,     Stack ID
+FIELD stackRun.stack_id,            5, %d,     Stack ID
 FIELD stackRun.skycell_id,          5, %s,     Skycell ID
 FIELD stackRun.label,    	    5, %s,     Label
 FIELD stackRun.data_group,    	    5, %s,     data grp
 FIELD stackRun.dist_group,    	    5, %s,     dist grp
+FIELD stackSumSkyfile.quality,      5, %d,     quality
 FIELD *,    	                    8, %s,     image,         image=JPEG2, stackProcessedSkyfile_Images.php,  ARG7
 FIELD stackRun.filter,    	    5, %s,     Filter
Index: branches/meh_branches/ppstack_test/ippMonitor/def/warpProcessedSkyfiles.d
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/def/warpProcessedSkyfiles.d	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/def/warpProcessedSkyfiles.d	(revision 33415)
@@ -1,13 +1,8 @@
-TABLE warpSkyfile, warpRun, fakeRun, camRun, chipRun, rawExp
+TABLE rawExp JOIN chipRun USING(exp_id) JOIN camRun USING(chip_id) JOIN fakeRun USING(cam_id) JOIN warpRun USING(fake_id) JOIN warpSkyfile USING(warp_id)
 TITLE Warp Processed Skyfiles
 FILE  warpProcessedSkyfiles.php
 MENU  ipp.science.dat
 
-WHERE warpSkyfile.warp_id = warpRun.warp_id
-WHERE warpRun.fake_id = fakeRun.fake_id
-WHERE fakeRun.cam_id = camRun.cam_id
-WHERE camRun.chip_id = chipRun.chip_id
-WHERE chipRun.exp_id = rawExp.exp_id
-WHERE warpSkyfile.fault = 0
+UNRESTRICTED WHERE 0=1
 
 ARGS  ARG1  rawImfile.exp_id=$rawExp.exp_id
Index: branches/meh_branches/ppstack_test/ippMonitor/def/warpProcessedSkyfiles_Images.d
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/def/warpProcessedSkyfiles_Images.d	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/def/warpProcessedSkyfiles_Images.d	(revision 33415)
@@ -1,13 +1,15 @@
-TABLE warpSkyfile, warpRun, fakeRun, camRun, chipRun, rawExp
+TABLE  rawExp JOIN chipRun USING(exp_id) JOIN camRun USING(chip_id) JOIN fakeRun USING(cam_id) JOIN warpRun USING(fake_id) JOIN warpSkyfile USING(warp_id)
 TITLE Warp Processed Skyfiles
 FILE  warpProcessedSkyfiles.php
 MENU  ipp.science.dat
 
-WHERE warpSkyfile.warp_id = warpRun.warp_id
-WHERE warpRun.fake_id = fakeRun.fake_id
-WHERE fakeRun.cam_id = camRun.cam_id
-WHERE camRun.chip_id = chipRun.chip_id
-WHERE chipRun.exp_id = rawExp.exp_id
-WHERE warpSkyfile.fault = 0
+UNRESTRICTED WHERE 0=1
+
+# WHERE warpSkyfile.warp_id = warpRun.warp_id
+# WHERE warpRun.fake_id = fakeRun.fake_id
+# WHERE fakeRun.cam_id = camRun.cam_id
+# WHERE camRun.chip_id = chipRun.chip_id
+# WHERE chipRun.exp_id = rawExp.exp_id
+# WHERE warpSkyfile.fault = 0
 
 # define image names to be used below
Index: branches/meh_branches/ppstack_test/ippMonitor/raw/czartool_getplot.php
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/raw/czartool_getplot.php	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/raw/czartool_getplot.php	(revision 33415)
@@ -18,5 +18,5 @@
 $plottype = $_GET[plottype];
 
-$path = "/data/ipp004.0/czartool_plots/"; # TODO horrible hard-coded path - read from config
+$path = "/data/ipp009.0/czartool_plots"; # TODO horrible hard-coded path - read from config
 
 if ($label == "all") $label = "all_".$mode."_labels";
Index: branches/meh_branches/ppstack_test/ippMonitor/raw/czartool_ipptopsps.php
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/raw/czartool_ipptopsps.php	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippMonitor/raw/czartool_ipptopsps.php	(revision 33415)
@@ -0,0 +1,405 @@
+<?php
+include 'ipp.php';
+include 'site.php';
+
+$ID = checkID();
+
+// require an explicit project
+if (! $ID['proj']) { projectform ($ID); }
+
+//$db = dbconnect($ID['proj']);
+$czardb = dbconnect("czardb"); // HACK to connect to czarDb 
+$lastUpdateTime = getLastUpdateTime($czardb);
+
+$gpc1db = dbconnect($ID['proj']);
+
+
+$PATH = getenv("PATH");
+putenv("PATH=$BINDIR:$PATH");
+
+$LD_LIBRARY_PATH = getenv("LD_LIBRARY_PATH");
+putenv("LD_LIBRARY_PATH=$LIBDIR:$LD_LIBRARY_PATH");
+
+
+if ($ID['menu']) {$myMenu = $ID['menu'];}
+else {$myMenu = "ipp.imfiles.dat";}
+
+menu($myMenu, 'Czartool on '.$lastUpdateTime, 'ipp.css', $ID['link'], $ID['proj']);
+
+$stages=array(
+        "processed",
+        "loaded_to_datastore",
+        "loaded_to_ODM",
+        "merge_worthy",
+        "deleted_datastore",
+        "deleted_dxlayer",
+        "merged",
+        "deleted_local");
+
+$pass = $ID['pass'];
+$proj = $ID['proj'];
+$menu = $ID['menu'];
+
+$selectedStage = $_GET[stage];
+$selectedLabel = $_GET[label];
+$selectedServer = $_GET[server];
+$selectedRevertStage = $_GET[revertstage];
+$selectedRevertMode = $_GET[revertmode];
+$plotType = $_GET[plottype];
+$serverCmd = $_GET[servercmd];
+$allServerCmd = $_GET[allservercmd];
+
+$headerColor = "#0080c0";
+$columnHeaderColor = "lavender";
+
+$labels = getLabels($czardb);
+
+# if no label is selected, use first one in the list
+if ($selectedLabel == "") { 
+    
+    if (sizeof($labels) > 0) $selectedLabel = $labels[0]; 
+    else $selectedLabel = "no labels";
+}
+if ($selectedStage == "") { $selectedStage = "all_stages"; }
+if ($plotType == "") { $plotType = "linear"; }
+
+// deal with reverts: turn on or off if requested and pass current revert state for this stage onto labels table later
+if ($selectedRevertStage && $selectedRevertMode) turnRevertsOnOff($czardb, $selectedRevertStage, $selectedRevertMode);
+
+// tell selected server to stop or run
+if ($selectedServer && $serverCmd) serverStopRun($czardb, $selectedServer,  $serverCmd);
+
+// tell all servers to stop or run
+if ($allServerCmd) {
+
+    foreach ($servers as &$server) serverStopRun($czardb, $server,  $allServerCmd);
+}
+
+
+$debug = 0;
+$table = "<table bgcolor=\"#FFFFFF\" width=\"700\" align=\"middle\" border=\"1\" cellspacing=\"0\" cellpadding=\"4\" style=\"font-size:80%\">\n";
+
+
+if ($debug) {
+    echo "prog returned $status, and output:<br>";
+    for ($i = 0; $i < count($distLabels); $i++) {
+        echo "$distLabels[$i]<br>";
+    }
+}
+
+$isUpdate = ($selectedMode == "update");
+
+// set up the form
+echo "<form action=\"czartool_ipptopsps.php\" method=\"POST\">\n";
+
+echo "<input type=\"hidden\" name=\"pass\" value=\"$pass\">\n";
+echo "<input type=\"hidden\" name=\"proj\" value=\"$proj\">\n";
+echo "<input type=\"hidden\" name=\"menu\" value=\"$menu\">\n";
+echo "</form>\n";
+
+echo "<table border=\"0\">";
+
+echo "<tr>";
+echo "<td colspan=\"3\" style=\"background-color:#0080c0;\">";
+echo "<h1 align=\"middle\">Czartool</h1>";
+echo "<h4 align=\"middle\">IPP to PSPS Monitoring</h4>";
+echo "</td>";
+echo "</tr>";
+
+echo "<tr valign=\"top\">";
+echo "<td style=width:100px;text-align:top;\">";
+ # time series plot
+echo "<br>";
+ echo "<img src=\"czartool_getplot.php?mode=$selectedMode&type=t&label=$selectedLabel&stage=$selectedStage&plottype=$plotType\"><br>";
+ # rate time series plot
+echo "<br>";
+ echo "<img src=\"czartool_getplot.php?mode=$selectedMode&type=rt&label=$selectedLabel&stage=$selectedStage&plottype=linear\"><br>";
+echo "</td>";
+echo "<td style=height:200px;width:10px;text-align:top;\">";
+echo "<td style=height:200px;width:400px;text-align:top;\">";
+
+#echo "<br>";
+
+// status table at top
+$plotTypeLink = ($plotType == "linear") ? "log" : "linear";
+$link = "czartool_ipptopsps.php?pass=" . $pass . "&proj=" . $proj . "&label=" . $selectedLabel . "&stage=" . $selectedStage . "&revertstage=" . $stage . "&plottype=$plotTypeLink";
+echo "<br>";
+echo $table;
+createTableTitle("Status and links", 3);
+echo "<tr>\n";
+echo "<tr><td>Time of last update</td><td>$lastUpdateTime</td></tr>";
+echo "<tr><td>Documentation</td><td><a href=\"http://ps1wiki.ifa.hawaii.edu/trac/wiki/PsPs\"><font color=\"blue\">here</font></a></td></tr>";
+
+echo "</table>\n";
+
+$plotTypeLink = ($plotType == "linear") ? "log" : "linear";
+$link = "czartool_ipptopsps.php?pass=" . $pass . "&proj=" . $proj . "&mode=" . $selectedMode . "&label=" . $selectedLabel . "&stage=" . $selectedStage . "&revertstage=" . $stage . "&plottype=$plotTypeLink";
+
+
+
+echo "<br>";
+
+       createLabelsTable($pass, 
+         $proj, 
+         $czardb, 
+         $selectedMode, 
+         $labels, 
+         $distLabels, 
+         $pubLabels, 
+         $stages, 
+         "new", 
+         $selectedLabel, 
+         $selectedStage, 
+         $plotType);
+
+echo "<br>";
+
+echo "</table>";
+
+menu_end();
+
+###########################################################################
+#
+# Gets labels in use over the last 24 hours
+#
+###########################################################################
+function getLabels($db) {
+
+    # order by descending priority as set in gpc1 database
+    $sql = "SELECT DISTINCT label FROM processed WHERE  timestamp >= now() - INTERVAL 1 DAY ORDER BY label";
+    if ($debug) {echo "$sql<br>";}
+
+    $qry = $db->query($sql);
+    if (dberror($qry)) {echo "<b>error with $sql </b><br>\n";}
+    while ($qry->fetchInto($row)) {
+
+        $labels[] = $row[0];
+    }
+
+    return $labels;
+}
+
+###########################################################################
+#
+# Creates table for all labels showing all stages for given 'state'
+#
+###########################################################################
+function createLabelsTable(
+        $pass, 
+        $proj, 
+        $db, 
+        $selectedMode, 
+        $labels, 
+        $distLabels, 
+        $pubLabels, 
+        $stages, 
+        $selectedState, 
+        $selectedLabel, $selectedStage, $plotType) {
+
+    $isUpdate = ($selectedMode == "update");
+    global $headerColor;
+    global $columnHeaderColor;
+
+    // setup table
+    global $table;
+    echo $table;
+    createTableTitle("Label status", 13);
+
+    echo "<tr>\n";
+    $link = "czartool_ipptopsps.php?pass=".$pass
+        ."&proj=".$proj
+        ."&mode=".$selectedMode
+        ."&label=".$selectedLabel
+        ."&stage=all_stages"
+        ."&plottype=".$plotType;
+    createFormattedTableCell(($selectedStage != "all_stages"), $link, "All stages", 0, "$columnHeaderColor");
+
+    #echo "<th>Label (in order of priority)</th>";
+    foreach ($stages as &$stage) {
+
+        if ($stage == $selectedStage) $showLink = 0;
+        else $showLink = 1;
+        $link = 
+            "czartool_ipptopsps.php?pass=".$pass
+            ."&proj=".$proj 
+            ."&mode=" .$selectedMode
+            ."&label=".$selectedLabel
+            ."&stage=".$stage
+            ."&plottype=".$plotType;
+
+        createFormattedTableCell($showLink, $link, $stage, 0, "$columnHeaderColor");
+#        createTableColumnHeader("Dates");
+    }
+
+    // setup default link
+    $defaultlink = 
+        "czartool_ipptopsps.php?pass=".$pass
+        ."&proj=".$proj
+        ."&mode=" .$selectedMode
+        ."&label=".$selectedLabel
+        ."&stage=".$selectedStage
+        ."&plottype=".$plotType;
+
+    $searchState = "new";
+    if ($selectedMode == "update") $searchState = "update";
+
+    // write rows
+    foreach ($labels as &$thisLabel) {
+
+
+        $distributing = false;
+        $publishing = false;
+
+        // show distribution/publishing status
+        echo "<tr>\n";
+
+        // create link to label summary page for each label
+        if ($thisLabel == $selectedLabel) $showLink = 0;
+        else $showLink = 1;
+        $link = 
+            "czartool_ipptopsps.php?pass=".$pass
+            ."&proj=".$proj
+            ."&mode=" .$selectedMode
+            ."&label=".$thisLabel
+            ."&stage=".$selectedStage
+            ."&plottype=".$plotType;
+        createFormattedTableCell($showLink, $link, $thisLabel, 0, null);
+
+        $str = "";
+        $anyFaults = false; 
+
+        $link = $defaultlink;
+        getStateAndFaults($db, $thisLabel, $selectedState, "processed", $str, $anyFaults);
+        createFormattedTableCell(0, $link, $str, $anyFaults, 0);
+
+        getStateAndFaults($db, $thisLabel, $selectedState, "loaded_to_datastore", $str, $anyFaults);
+        createFormattedTableCell(0, $link, $str, $anyFaults, 0);
+
+        getStateAndFaults($db, $thisLabel, $selectedState, "loaded_to_ODM", $str, $anyFaults);
+        createFormattedTableCell(0, $link, $str, $anyFaults, 0);
+
+        getStateAndFaults($db, $thisLabel, $selectedState, "merge_worthy", $str, $anyFaults);
+        createFormattedTableCell(0, $link, $str, $anyFaults, 0);
+
+        getStateAndFaults($db, $thisLabel, $selectedState, "deleted_datastore", $str, $anyFaults);
+        createFormattedTableCell(0, $link, $str, $anyFaults, 0);
+
+        getStateAndFaults($db, $thisLabel, $selectedState, "deleted_dxlayer", $str, $anyFaults);
+        createFormattedTableCell(0, $link, $str, $anyFaults, 0);
+
+        getStateAndFaults($db, $thisLabel, $selectedState, "merged", $str, $anyFaults);
+        createFormattedTableCell(0, $link, $str, $anyFaults, 0);
+
+        getStateAndFaults($db, $thisLabel, $selectedState, "deleted_local", $str, $anyFaults);
+        createFormattedTableCell(0, $link, $str, $anyFaults, 0);
+
+        echo "</tr>\n";
+    }
+
+    echo "<tr>\n";
+    $link = "czartool_ipptopsps.php?pass=" . $pass . "&proj=" . $proj . "&mode=" . $selectedMode . "&label=all&stage=".$selectedStage."&plottype=".$plotType;
+
+    echo "</tr>\n";
+    if ($selectedLabel == "all") $showLink = 0;
+    else  $showLink = 1;
+    $link = "czartool_ipptopsps.php?pass=".$pass."&proj=".$proj."&mode=".$selectedMode."&label=all&stage=".$selectedStage."&plottype=".$plotType;
+
+    echo "</tr>\n";
+    echo "</table>\n";
+}
+
+
+###########################################################################
+#
+# Creates a table title
+#
+###########################################################################
+function createTableTitle($str, $colSpan) {
+
+    global $headerColor;
+    echo "<th colspan=\"$colSpan\"bgcolor=\"$headerColor\"><h2>$str</h2></th>";
+}
+
+###########################################################################
+#
+# Creates a table header cell
+#
+###########################################################################
+function createTableColumnHeader($str) {
+
+    global $columnHeaderColor;
+    echo "<th bgcolor=\"$columnHeaderColor\"><h3>$str</h3></th>";
+}
+
+###########################################################################
+#
+# Creates table cell with a red background if an error
+#
+###########################################################################
+function createFormattedTableCell($showLink, $link, $str, $error, $color) {
+
+    $fontColor = $error ? "black" : "blue";
+
+    if ($error) echo "<td bgcolor=\"red\">";
+    else if ($color) echo "<td bgcolor=\"$color\">";
+    else  echo "<td>";
+    if ($showLink) echo "<a href=\"$link\"><font color=\"$fontColor\">$str</font></td>";
+    else if ($str) echo "$str</td>";
+    else echo "&nbsp;</td>";
+}
+
+
+###########################################################################
+#
+# Returns state and fault-count (if new) as a string
+#
+###########################################################################
+function getStateAndFaults($db, $label, $state, $stage, &$str, &$anyFaults) {
+
+    $anyFaults = false;
+    $sql = "SELECT pending, faults FROM $stage WHERE label LIKE '$label' ORDER BY timestamp DESC LIMIT 1";
+    if ($debug) {echo "$sql<br>";}
+
+    $qry = $db->query($sql);
+    if (dberror($qry)) {echo "<b>error with $sql </b><br>\n";}
+    $qry->fetchInto($row);
+
+    $pending = $row[0];
+    $faults = $row[1];
+
+    if ($pending == 0)
+        $str = "";
+    else
+        $str = "$pending";
+
+    if ($state == "new") {
+
+        if ($faults > 0) {
+
+            $str = $str."(".$faults.")";
+            $anyFaults = true;
+        }
+    }
+}
+
+###########################################################################
+#
+# Returns time of last czarDb update 
+#
+###########################################################################
+function getLastUpdateTime($db) {
+
+    $anyFaults = false;
+    $sql = "SELECT timestamp FROM processed ORDER BY timestamp DESC LIMIT 1";
+    if ($debug) {echo "$sql<br>";}
+
+    $qry = $db->query($sql);
+    if (dberror($qry)) {echo "<b>error with $sql </b><br>\n";}
+    $qry->fetchInto($row);
+
+    return $row[0];
+}
+
+
+?>
+
Index: branches/meh_branches/ppstack_test/ippMonitor/raw/czartool_labels.php
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/raw/czartool_labels.php	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/raw/czartool_labels.php	(revision 33415)
@@ -27,6 +27,17 @@
 menu($myMenu, 'Czartool on '.$lastUpdateTime, 'ipp.css', $ID['link'], $ID['proj']);
 
-$states=array("full","new","drop","wait");
-$stages=array("burntool", "chip","cam","fake","warp","stack","diff","magic","magicDS","dist", "pub");
+$stages=array(
+        "burntool", 
+        "chip",
+        "cam",
+        "fake",
+        "warp",
+        "stack",
+        "diff",
+        "magic",
+        "magicDS",
+        "dist", 
+        "pub");
+
 $servers=array(
         "addstar",
@@ -41,4 +52,5 @@
         "replication",
         "stack",
+        "deepstack",
         "stdscience",
         "summitcopy");
@@ -149,5 +161,5 @@
 echo "<br>";
 echo $table;
-#"<table width=\"$width\" align=\"middle\" border=\"1\" cellspacing=\"0\" cellpadding=\"4\" style=\"font-size:80%\">\n";
+
 createTableTitle("Status and links", 3);
 echo "<tr>\n";
@@ -157,7 +169,7 @@
 echo "<tr><td>Postage stamp server status</td><td><a href=\"http://pstamp.ipp.ifa.hawaii.edu/status.php\"><font color=\"blue\">here</font></a></td></tr>";
 echo "<tr><td>Documentation</td><td><a href=\"http://svn.pan-starrs.ifa.hawaii.edu/trac/ipp/wiki/Processing\"><font color=\"blue\">here</font></a></td></tr>";
-echo "<tr><td>Cluster load</td><td><a href=\"http://ganglia.pan-starrs.ifa.hawaii.edu/?r=hour&s=descending&hc=6&c=IPP%2520Production\"><font color=\"blue\">here</font></a></td></tr>";
-echo "<tr><td>Who's using the cluster?</td><td><a href=\"http://ipp004.ifa.hawaii.edu/clusterMonitor/top.html\"><font color=\"blue\">here</font></a></td></tr>";
-echo "<tr><td>IPP metrics</td><td><a href=\"http://ipp004.ifa.hawaii.edu/ippMetrics\"><font color=\"blue\">here</font></a></td></tr>";
+echo "<tr><td>Cluster load</td><td><a href=\"http://ganglia.pan-starrs.ifa.hawaii.edu/?r=hour&s=descending&hc=6&c=IPP%2520Production\"><font color=\"blue\">here</font></a> <a href=\"http://ganglia.pan-starrs.ifa.hawaii.edu/?m=cpu_report&r=hour&s=by%2520name&c=IPP+Production&h=&sh=1&hc=8&z=small\"><font color=\"blue\">cpu_report</font></a></td></tr>";
+echo "<tr><td>Who's using the cluster?</td><td><a href=\"http://ippmonitor.ipp.ifa.hawaii.edu/ippMonitor/clusterMonitor/top.html\"><font color=\"blue\">here</font></a></td></tr>";
+echo "<tr><td>IPP metrics</td><td><a href=\"http://ipp0022.ifa.hawaii.edu/ps1sc/ippMetrics\"><font color=\"blue\">here</font></a></td></tr>";
 echo "<tr><td>Czar log pages</td><td><a href=\"http://svn.pan-starrs.ifa.hawaii.edu/trac/ipp/wiki/PS1_IPP_CzarLogs\"><font color=\"blue\">here</font></a></td></tr>";
 echo "<tr><td>Exposure summary</td><td><a href=\"czartool_exposures.php?pass=$pass&proj=$proj\"><font color=\"blue\">here</font></a></td></tr>";
@@ -165,5 +177,4 @@
 $plotTypeLink = ($plotType == "linear") ? "log" : "linear";
 $link = "czartool_labels.php?pass=" . $pass . "&proj=" . $proj . "&mode=" . $selectedMode . "&label=" . $selectedLabel . "&stage=" . $selectedStage . "&revertstage=" . $stage . "&plottype=$plotTypeLink";
-echo "<tr><td>Plot type</td><td><a href=\"$link\"><font color=\"blue\">Change to $plotTypeLink</font></a></td></tr>";
 
 
@@ -180,5 +191,4 @@
          $pubLabels, 
          $stages, 
-         $states, 
          "new", 
          $selectedLabel, 
@@ -201,5 +211,5 @@
       echo "<tr>";
       createTableColumnHeader("Database");
-      createTableColumnHeader("Status");
+      createTableColumnHeader("Status (sec behind master)");
       showReplicationsStatus($REPL_HOST_GPC1, $REPL_USER_GPC1, $REPL_PASSWORD_GPC1, $REPL_DBNAME_GPC1);
       showReplicationsStatus($REPL_HOST_NEBULOUS, $REPL_USER_NEBULOUS, $REPL_PASSWORD_NEBULOUS, $REPL_DBNAME_NEBULOUS);
@@ -349,5 +359,4 @@
         $pubLabels, 
         $stages, 
-        $states, 
         $selectedState, 
         $selectedLabel, $selectedStage, $plotType) {
@@ -483,5 +492,5 @@
     createFormattedTableCell(($selectedLabel != "all"), $link, "All labels", 0, null);
 
-    createFormattedTableCell(0, null, "Reverts:", 0, "$columnHeaderColor");
+    createFormattedTableCell(0, null, "Reverting?", 0, "$columnHeaderColor");
 
     foreach ($stages as &$stage) {
@@ -489,18 +498,21 @@
         if ($stage == "burntool") continue;
         $reverting = getRevertStatus($db, $stage);
-        $link = 
-            "czartool_labels.php?pass=".$pass
-            ."&proj=".$proj
-            ."&mode=" .$selectedMode
-            ."&label=".$selectedLabel
-            ."&stage=".$selectedStage
-            ."&plottype=".$plotType
-            ."&revertstage=".$stage
-            ."&revertmode=";
-
-        if(!$reverting) {$label =  "Start";$link = $link . "on";}
-        if($reverting) {$label = "Stop";$link = $link . "off";}
+        #$link = 
+        #    "czartool_labels.php?pass=".$pass
+        #    ."&proj=".$proj
+        #    ."&mode=" .$selectedMode
+        #    ."&label=".$selectedLabel
+        #    ."&stage=".$selectedStage
+        #    ."&plottype=".$plotType
+        #    ."&revertstage=".$stage
+        #    ."&revertmode=";
+#
+        #if(!$reverting) {$label =  "Start";$link = $link . "on";}
+        #if($reverting) {$label = "Stop";$link = $link . "off";}
+        if(!$reverting) {$label =  "no";}
+        if($reverting) {$label = "yes";}
         unset($reverting);
-        createFormattedTableCell(1, $link, $label, 0, "$columnHeaderColor");
+        #createFormattedTableCell(1, $link, $label, 0, "$columnHeaderColor");
+        createFormattedTableCell(0, null, $label, 0, "$columnHeaderColor");
         #echo "<td><a href=\"$link\"><font color=\"blue\">$label</font></td>";
 
@@ -904,5 +916,5 @@
         //die("MySQL DB connection error: Check the configuration for $replDatabaseName");
         //die("");
-        return;
+        return -1;
 
     }
@@ -911,5 +923,6 @@
         # Have a look for Last_Errno in http://dev.mysql.com/doc/refman/5.0/en/show-slave-status.html
         $errorStatusInMySql = $row[18];
-        $replStatus = ($errorStatusInMySql==0?"OK":"<font color=\"red\">PROBLEM</font>");
+	$sec_behind = $row[32];
+        $replStatus = ($errorStatusInMySql==0?"OK ($sec_behind)":"<font color=\"red\">PROBLEM</font>");
         echo "<tr><td>$replDatabaseName</td><td>$replStatus</td></tr>";
         if ($errorStatusInMySql!=0) {
Index: branches/meh_branches/ppstack_test/ippMonitor/raw/ipp.imfiles.dat
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/raw/ipp.imfiles.dat	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/raw/ipp.imfiles.dat	(revision 33415)
@@ -21,5 +21,6 @@
 menutop   | menutop      | link    | simple plot - chip (to remove?) | simplePlot.php
 menutop   | menutop      | link    | simple plot - cam (to remove?) | simplePlotcam.php
-menutop   | menutop      | link    | czartool                     | czartool_labels.php
+menutop   | menutop      | link    | czartool - standard          | czartool_labels.php
+menutop   | menutop      | link    | czartool - ipptopsps         | czartool_ipptopsps.php
 menutop   | menutop      | link    | mask stats                   | maskStats.php
 
Index: branches/meh_branches/ppstack_test/ippMonitor/raw/nightSummary.php
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/raw/nightSummary.php	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/raw/nightSummary.php	(revision 33415)
@@ -121,4 +121,6 @@
 table_section($db, $WHERE, "magicRun");
 table_section($db, $WHERE, "destreakRun");
+table_section($db, $WHERE, "chipBackgroundRun");
+table_section($db, $WHERE, "warpBackgroundRun");
 table_section($db, $WHERE, "distRun");
 // table_section($db, $WHERE, "publishRun");
Index: branches/meh_branches/ppstack_test/ippMonitor/raw/warpProcessedExp.php
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/raw/warpProcessedExp.php	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/raw/warpProcessedExp.php	(revision 33415)
@@ -117,5 +117,5 @@
 // query the database
 if (basic == "basic") {
-  $sql = "SELECT rawExp.exp_name,rawExp.exp_id,chipRun.chip_id,camRun.cam_id,fakeRun.fake_id,warpRun.warp_id,warpRun.state,warpRun.label,warpRun.data_group,warpRun.dist_group,rawExp.telescope,rawExp.camera,rawExp.dateobs,rawExp.ra,rawExp.decl,rawExp.object,rawExp.filter,rawExp.exp_time,rawExp.airmass,rawExp.bg,rawExp.bg_stdev,rawExp.comment,ifnull(warpSummary.path_base,'noimage'),ifnull(warpSummary.projection_cell,'noimage') FROM fakeRun,camRun,chipRun,rawExp,warpRun LEFT JOIN warpSummary ON warpSummary.warp_id = warpRun.warp_id $WHERE LIMIT $dTABLE OFFSET $rowStart";
+  $sql = "SELECT rawExp.exp_name,rawExp.exp_id,chipRun.chip_id,camRun.cam_id,fakeRun.fake_id,warpRun.warp_id,warpRun.state,warpRun.label,warpRun.data_group,warpRun.dist_group,rawExp.telescope,rawExp.camera,rawExp.dateobs,rawExp.ra,rawExp.decl,rawExp.object,rawExp.filter,rawExp.exp_time,rawExp.airmass,rawExp.bg,rawExp.bg_stdev,rawExp.comment,ifnull(warpSummary.path_base,'noimage'),ifnull(warpSummary.projection_cell,'noimage') FROM warpRun JOIN fakeRun USING(fake_id) JOIN camRun USING(cam_id) JOIN chipRun USING(chip_id) JOIN rawExp USING(exp_id) JOIN warpSummary USING(warp_id) $WHERE LIMIT $dTABLE OFFSET $rowStart";
 }
 
Index: branches/meh_branches/ppstack_test/ippMonitor/raw/warpProcessedExp_Images.php
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/raw/warpProcessedExp_Images.php	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/raw/warpProcessedExp_Images.php	(revision 33415)
@@ -77,12 +77,10 @@
 
 if ($restricted == 0) {
-  if ("none" != "none") {
-    $WHERE = "$WHERE none";
-  }
+   $WHERE = "WHERE 0=1";
 }
 
 // get the result table count
 if (basic == "basic") {
-  $sql = "SELECT count(*) FROM fakeRun,camRun,chipRun,rawExp,warpRun LEFT JOIN warpSummary ON warpSummary.warp_id = warpRun.warp_id $WHERE";
+  $sql = "SELECT count(*) FROM rawExp JOIN chipRun USING(exp_id) JOIN camRun USING (chip_id) JOIN fakeRun USING(cam_id) JOIN warpRun USING(fake_id) LEFT JOIN warpSummary ON warpSummary.warp_id = warpRun.warp_id $WHERE";
 }
 
@@ -111,5 +109,5 @@
 // query the database
 if (basic == "basic") {
-  $sql = "SELECT rawExp.exp_name,rawExp.exp_id,chipRun.chip_id,camRun.cam_id,fakeRun.fake_id,warpRun.warp_id,warpRun.state,warpRun.label,warpRun.data_group,warpRun.dist_group,rawExp.telescope,rawExp.camera,rawExp.dateobs,rawExp.ra,rawExp.decl,rawExp.object,rawExp.filter,rawExp.exp_time,rawExp.airmass,rawExp.bg,rawExp.bg_stdev,rawExp.comment,ifnull(warpSummary.path_base,'noimage'),ifnull(warpSummary.projection_cell,'noimage') FROM fakeRun,camRun,chipRun,rawExp,warpRun LEFT JOIN warpSummary ON warpSummary.warp_id = warpRun.warp_id $WHERE LIMIT $dTABLE OFFSET $rowStart";
+  $sql = "SELECT rawExp.exp_name,rawExp.exp_id,chipRun.chip_id,camRun.cam_id,fakeRun.fake_id,warpRun.warp_id,warpRun.state,warpRun.label,warpRun.data_group,warpRun.dist_group,rawExp.telescope,rawExp.camera,rawExp.dateobs,rawExp.ra,rawExp.decl,rawExp.object,rawExp.filter,rawExp.exp_time,rawExp.airmass,rawExp.bg,rawExp.bg_stdev,rawExp.comment,ifnull(warpSummary.path_base,'noimage'),ifnull(warpSummary.projection_cell,'noimage'),warpRun.tess_id FROM rawExp JOIN chipRun USING(exp_id) JOIN camRun USING (chip_id) JOIN fakeRun USING(cam_id) JOIN warpRun USING(fake_id) LEFT JOIN warpSummary ON warpSummary.warp_id = warpRun.warp_id $WHERE LIMIT $dTABLE OFFSET $rowStart";
 }
 
@@ -175,4 +173,5 @@
 $buttonLink = button_restrict_string ('warpSummary.path_base', $buttonLink);
 $buttonLink = button_restrict_string ('warpSummary.projection_cell', $buttonLink);
+$buttonLink = button_restrict_string ('warpRun.tess_id', $buttonLink);
 navigate_buttons ($rowStart, $rowLast, $dTABLE, 
 		  $rowTotal, $buttonLink, $ID, 'warpProcessedExp_Images.php');
@@ -210,4 +209,5 @@
 write_header_cell ("list", "stdev    ");
 write_header_cell ("list", "Comment");
+write_header_cell ("list", "TessId");
 echo "</tr>\n";
 echo "<tr><td></td>\n";
@@ -236,4 +236,5 @@
 write_sort_cell ("list", "rawExp.bg_stdev", $buttonLink, $ID, 'warpProcessedExp_Images.php');
 write_sort_cell ("list", "rawExp.comment", $buttonLink, $ID, 'warpProcessedExp_Images.php');
+write_sort_cell ("list", "warpRun.tess_id", $buttonLink, $ID, 'warpProcessedExp_Images.php');
 echo "</tr>\n";
 // echo "</tr>\n";
@@ -268,4 +269,5 @@
 write_query_row ('rawExp.bg_stdev', 5, 'min');
 write_query_row ('rawExp.comment', 65, 'string');
+write_query_row ('warpRun.tess_id', 65, 'string');
 echo "</tr><tr><td>&le;</td>\n";
 echo "<td> &nbsp; </td>\n";
@@ -292,4 +294,5 @@
 write_query_row ('rawExp.bg_stdev', 5, 'max');
 echo "<td> &nbsp; </td>\n";
+echo "<td> &nbsp; </td>\n";
 echo "</tr>\n";
 // echo "</tr>\n";
@@ -312,13 +315,14 @@
   //Identify current warpId
   $warpId = $row[5];
-/*   echo "<tr><td>Number of elements in warpIdCollection: ".count($warpIdCollection)."</td></tr>\n"; */
-/*   echo "<tr><td>(warpId=$warpId)</td></tr>\n"; */
+  $tessId = $row[24];
+  /* echo "<tr><td>Number of elements in warpIdCollection: ".count($warpIdCollection)."</td></tr>\n"; */
+  /* echo "<tr><td>(warpId=$warpId)</td></tr>\n"; */
 
   if (!isSet($warpIdCollection[$warpId])) {
-/*     echo "<tr><td>$warpId not in warpIdCollection</td></tr>"; */
+    /* echo "<tr><td>$warpId not in warpIdCollection</td></tr>"; */
     $warpIdCollection[$warpId] = array();
     $skyCellValue = preg_replace("|skycell[.]|","",$row[23]);
     $warpIdCollection[$warpId]["12"] = $skyCellValue;
-/*     echo "<tr><td>Skycell is [$skyCellValue]</td></tr>"; */
+    /* echo "<tr><td>Skycell is [$skyCellValue]</td></tr>"; */
 
     echo "<tr>\n";
@@ -344,10 +348,10 @@
     echo "<td class=\"$class\">\n";
     echo "<table border=\"1\"><tr>\n";
-/*     echo "  <td class=\"ul3x2\" id=\"$warpId.00\">$noImageValue</td>\n"; */
+    /* echo "  <td class=\"ul3x2\" id=\"$warpId.00\">$noImageValue</td>\n"; */
     echo "  <td class=\"ul3x2\" id=\"$warpId.00\"></td>\n";
     echo "  <td class=\"uc3x2\" id=\"$warpId.01\">$noImageValue</td>\n";
     echo "  <td class=\"ur3x2\" id=\"$warpId.02\">$noImageValue</td>\n";
     echo "</tr><tr>\n";
-/*     echo "  <td class=\"ll3x2\" id=\"$warpId.10\">$noImageValue</td>\n"; */
+    /* echo "  <td class=\"ll3x2\" id=\"$warpId.10\">$noImageValue</td>\n"; */
     echo "  <td class=\"ll3x2\" id=\"$warpId.10\"></td>\n";
     echo "  <td class=\"lc3x2\" id=\"$warpId.11\">$noImageValue</td>\n";
@@ -364,10 +368,10 @@
     echo "<td class=\"$class\">\n";
     echo "<table border=\"1\"><tr>\n";
-/*     echo "  <td class=\"ul3x2\" id=\"t$warpId.00\">No image</td>\n"; */
+    /* echo "  <td class=\"ul3x2\" id=\"t$warpId.00\">No image</td>\n"; */
     echo "  <td class=\"ul3x2\" id=\"t$warpId.00\"></td>\n";
     echo "  <td class=\"uc3x2\" id=\"t$warpId.01\">No image</td>\n";
     echo "  <td class=\"ur3x2\" id=\"t$warpId.02\">No image</td>\n";
     echo "</tr><tr>\n";
-/*     echo "  <td class=\"ll3x2\" id=\"t$warpId.10\">No image</td>\n"; */
+    /* echo "  <td class=\"ll3x2\" id=\"t$warpId.10\">No image</td>\n"; */
     echo "  <td class=\"ll3x2\" id=\"t$warpId.10\"></td>\n";
     echo "  <td class=\"lc3x2\" id=\"t$warpId.11\">No image</td>\n";
@@ -376,9 +380,18 @@
     echo "</td>\n";
 
-    $cellContent = "<a href=\\\"$link\\\"> <img src=\\\"getimage.php?name=$row[22].$row[23]&rule=PPSKYCELL.JPEG2&camera=$row[11]&class_id=NONE\\\"> </a>";
-    echo "<script type=\"text/javascript\">changeCell(\"$warpId.12\", \"$cellContent\");</script>";
-    $imageNameValue = ($row[23]=="noimage" ?
-		       "No image" : $row[23]);
-    echo "<script type=\"text/javascript\">changeCell(\"t$warpId.12\", \"$imageNameValue\");</script>";
+    if ($tessId != "RINGS.V3") {
+      $cellContent = "<a href=\\\"$link\\\"> <img src=\\\"getimage.php?name=$row[22].$row[23]&rule=PPSKYCELL.JPEG2&camera=$row[11]&class_id=NONE\\\"> </a>";
+      echo "<script type=\"text/javascript\">changeCell(\"$warpId.12\", \"$cellContent\");</script>";
+      $imageNameValue = ($row[23]=="noimage" ?
+		         "No image" : $row[23]);
+      echo "<script type=\"text/javascript\">changeCell(\"t$warpId.12\", \"$imageNameValue\");</script>";
+    } else {
+      //The cell to update is the upper right one for new tessellation
+      $cellContent = "<a href=\\\"$link\\\"> <img src=\\\"getimage.php?name=$row[22].$row[23]&rule=PPSKYCELL.JPEG2&camera=$row[11]&class_id=NONE\\\"> </a>";
+      echo "<script type=\"text/javascript\">changeCell(\"$warpId.02\", \"$cellContent\");</script>";
+      $imageNameValue = ($row[23]=="noimage" ?
+		         "No image" : $row[23]);
+      echo "<script type=\"text/javascript\">changeCell(\"t$warpId.02\", \"$imageNameValue\");</script>";
+    }
 
     //resume with "regular" columns
@@ -399,4 +412,5 @@
     write_table_cell ($class, '%.2f', "", $row[20]);
     write_table_cell ($class, '%s', "", $row[21]);
+    write_table_cell ($class, '%s', "", $row[24]);
     echo "</tr>\n";
 
@@ -415,36 +429,77 @@
     $cellContent = "<a href=\\\"$link\\\"> <img src=\\\"getimage.php?name=$row[22].$row[23]&rule=PPSKYCELL.JPEG2&camera=$row[11]&class_id=NONE\\\"> </a>";
 
-    if ($skyCellValue == $lrValue + 1) {
-      $warpIdCollection[$warpId]["11"] = $skyCellValue;
-      echo "<script type=\"text/javascript\">changeCell(\"$warpId.11\", \"$cellContent\");</script>";
-      echo "<script type=\"text/javascript\">changeCell(\"t$warpId.11\", \"$row[23]\");</script>";
-    } else {
-      if ($skyCellValue == $lrValue + 2) {
-	$warpIdCollection[$warpId]["10"] = $skyCellValue;
-	echo "<script type=\"text/javascript\">changeCell(\"$warpId.10\", \"$cellContent\");</script>";
-	echo "<script type=\"text/javascript\">changeCell(\"t$warpId.10\", \"$row[23]\");</script>";
+    if ($tessId != "RINGS.V3") {
+      if ($skyCellValue == $lrValue + 1) {
+        $warpIdCollection[$warpId]["11"] = $skyCellValue;
+      	echo "<script type=\"text/javascript\">changeCell(\"$warpId.11\", \"$cellContent\");</script>";
+	echo "<script type=\"text/javascript\">changeCell(\"t$warpId.11\", \"$row[23]\");</script>";
       } else {
-	if (!isSet($warpIdCollection[$warpId]["02"])) {
-	  /* 	echo "<tr><td>Update UpperRight</td></tr>"; */
-	  $warpIdCollection[$warpId]["02"] = $skyCellValue;
-	  echo "<script type=\"text/javascript\">changeCell(\"$warpId.02\", \"$cellContent\");</script>";
-	  echo "<script type=\"text/javascript\">changeCell(\"t$warpId.02\", \"$row[23]\");</script>";
-	} else {
-	  $urValue = $warpIdCollection[$warpId]["02"];
-	  if ($urValue + 1 == $skyCellValue) {
-	    echo "<script type=\"text/javascript\">changeCell(\"$warpId.01\", \"$cellContent\");</script>";
-	    echo "<script type=\"text/javascript\">changeCell(\"t$warpId.01\", \"$row[23]\");</script>";
+        if ($skyCellValue == $lrValue + 2) {
+	  $warpIdCollection[$warpId]["10"] = $skyCellValue;
+	  echo "<script type=\"text/javascript\">changeCell(\"$warpId.10\", \"$cellContent\");</script>";
+	  echo "<script type=\"text/javascript\">changeCell(\"t$warpId.10\", \"$row[23]\");</script>";
+        } else {
+	  if (!isSet($warpIdCollection[$warpId]["02"])) {
+	    /* echo "<tr><td>Update UpperRight</td></tr>"; */
+	    $warpIdCollection[$warpId]["02"] = $skyCellValue;
+	    echo "<script type=\"text/javascript\">changeCell(\"$warpId.02\", \"$cellContent\");</script>";
+	    echo "<script type=\"text/javascript\">changeCell(\"t$warpId.02\", \"$row[23]\");</script>";
 	  } else {
-	    if ($urValue + 2 == $skyCellValue) {
-	      echo "<script type=\"text/javascript\">changeCell(\"$warpId.00\", \"$cellContent\");</script>";
-	      echo "<script type=\"text/javascript\">changeCell(\"t$warpId.00\", \"$row[23]\");</script>";
+	    $urValue = $warpIdCollection[$warpId]["02"];
+	    if ($urValue + 1 == $skyCellValue) {
+	      echo "<script type=\"text/javascript\">changeCell(\"$warpId.01\", \"$cellContent\");</script>";
+	      echo "<script type=\"text/javascript\">changeCell(\"t$warpId.01\", \"$row[23]\");</script>";
 	    } else {
-	      $expected = $urValue + 1;
-	      $actual = $skyCellValue;
-	      echo "<script type=\"text/javascript\">changeCell(\"$warpId.00\", \"Problem in UpperLeft corner: expected $expected but was $actual\");</script>";
-	      echo "<tr><td>Problem in Upper part!</td></tr>";
-	    }
-	  } 
+	      if ($urValue + 2 == $skyCellValue) {
+	        echo "<script type=\"text/javascript\">changeCell(\"$warpId.00\", \"$cellContent\");</script>";
+	        echo "<script type=\"text/javascript\">changeCell(\"t$warpId.00\", \"$row[23]\");</script>";
+	      } else {
+	        $expected = $urValue + 1;
+	        $actual = $skyCellValue;
+	        echo "<script type=\"text/javascript\">changeCell(\"$warpId.00\", \"Problem in UpperLeft corner: expected $expected but was $actual\");</script>";
+	        echo "<tr><td>Problem in Upper part!</td></tr>";
+	      }
+	    } 
+	  }
 	}
+      }
+    } else { /* New tessellation */
+
+      /* echo "<p>$lrValue -  $skyCellValue</p>"; */
+
+      if ($skyCellValue == $lrValue + 1) {
+        $warpIdCollection[$warpId]["11"] = $skyCellValue;
+      	echo "<script type=\"text/javascript\">changeCell(\"$warpId.01\", \"$cellContent\");</script>";
+	echo "<script type=\"text/javascript\">changeCell(\"t$warpId.01\", \"$row[23]\");</script>";
+      } else {
+        if ($skyCellValue == $lrValue + 2) {
+	  $warpIdCollection[$warpId]["10"] = $skyCellValue;
+	  echo "<script type=\"text/javascript\">changeCell(\"$warpId.00\", \"$cellContent\");</script>";
+	  echo "<script type=\"text/javascript\">changeCell(\"t$warpId.00\", \"$row[23]\");</script>";
+        } else {
+	  if (!isSet($warpIdCollection[$warpId]["02"])) {
+	    /* echo "<tr><td>Update UpperRight</td></tr>"; */
+	    $warpIdCollection[$warpId]["02"] = $skyCellValue;
+	    echo "<script type=\"text/javascript\">changeCell(\"$warpId.12\", \"$cellContent\");</script>";
+	    echo "<script type=\"text/javascript\">changeCell(\"t$warpId.12\", \"$row[23]\");</script>";
+	  } else {
+	    $urValue = $warpIdCollection[$warpId]["02"];
+	    if ($urValue + 1 == $skyCellValue) {
+	      echo "<script type=\"text/javascript\">changeCell(\"$warpId.11\", \"$cellContent\");</script>";
+	      echo "<script type=\"text/javascript\">changeCell(\"t$warpId.11\", \"$row[23]\");</script>";
+	    } else {
+	      if ($urValue + 2 == $skyCellValue) {
+	        echo "<script type=\"text/javascript\">changeCell(\"$warpId.10\", \"$cellContent\");</script>";
+	        echo "<script type=\"text/javascript\">changeCell(\"t$warpId.10\", \"$row[23]\");</script>";
+	      } else {
+	        $expected = $urValue + 1;
+	        $actual = $skyCellValue;
+	        echo "<script type=\"text/javascript\">changeCell(\"$warpId.00\", \"Problem in UpperLeft corner: expected $expected but was $actual\");</script>";
+	        echo "<tr><td>Problem in Upper part!</td></tr>";
+	      }
+	    } 
+	  }
+	}
+
       }
     }
Index: branches/meh_branches/ppstack_test/ippMonitor/scripts/skyplot.dvo
===================================================================
--- branches/meh_branches/ppstack_test/ippMonitor/scripts/skyplot.dvo	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippMonitor/scripts/skyplot.dvo	(revision 33415)
@@ -8,4 +8,5 @@
  black
  gold
+ orange
 end
 
@@ -17,4 +18,5 @@
  7
  3
+ 0
 end
 
@@ -26,4 +28,5 @@
  0.9
  1.3
+ 0.5
 end
 
Index: branches/meh_branches/ppstack_test/ippScripts/Build.PL
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/Build.PL	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/Build.PL	(revision 33415)
@@ -65,5 +65,9 @@
         scripts/minidvodb_createdb.pl
         scripts/minidvodb_merge.pl
+        scripts/minidvodb_premerge.pl
         scripts/minidvodb_copy.pl
+        scripts/mergedvodb_copy.pl
+        scripts/mergedvodb_merge.pl
+        scripts/mergedvodb_queue.pl
         scripts/ippdb.pl
         scripts/ipp_cleanup.pl
@@ -71,4 +75,5 @@
         scripts/ipp_inject_expname.pl
         scripts/ipp_serial_inject.pl
+        scripts/ipp_serial_inject_into_nebulous.pl
         scripts/ipp_serial_inject_split.pl
         scripts/ipp_serial_inject_mosaic.pl
@@ -94,4 +99,5 @@
         scripts/dist_bundle.pl
         scripts/dist_advancerun.pl
+        scripts/dist_fix_file.pl
         scripts/dist_make_fileset.pl
         scripts/dist_defineruns.pl
@@ -120,4 +126,5 @@
         scripts/videophot_process.pl
         scripts/listvideocells.pl
+        scripts/skycalibration.pl
     )],
     dist_abstract => 'Scripts for running the Pan-STARRS IPP',
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/addstar_run.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/addstar_run.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/addstar_run.pl	(revision 33415)
@@ -30,4 +30,5 @@
 my $missing_tools;
 my $addtool = can_run('addtool') or (warn "Can't find addtool" and $missing_tools = 1);
+my $camtool = can_run('camtool') or (warn "Can't find camtool" and $missing_tools = 1);
 my $ppConfigDump = can_run('ppConfigDump') or (warn "Can't find ppConfigDump" and $missing_tools = 1);
 my $addstar = can_run('addstar') or (warn "Can't find addstar" and $missing_tools = 1);
@@ -37,5 +38,6 @@
 }
 my $minidvodb_path;
-my ( $add_id, $camera, $stage, $outroot, $stageroot, $dbname, $reduction, $dvodb, $minidvodb, $minidvodb_name, $minidvodb_group, $image_only, $verbose, $no_update,
+my $stage_extra1 ;
+my ( $add_id, $camera, $stage, $stage_id, $multi_num, $outroot, $stageroot, $dbname, $reduction, $dvodb, $minidvodb, $minidvodb_name, $minidvodb_group, $image_only, $verbose, $no_update,
      $no_op, $redirect, $save_temps);
 GetOptions(
@@ -43,5 +45,6 @@
     'camera|c=s'        => \$camera, # Camera
     'stage|s=s'        => \$stage, # Camera
-    
+    'stage_id|w=s'   => \$stage_id,
+    'multi_num|w=s'    => \$multi_num, # the number for a staticskymulti (for finding cmf)
     'dbname|d=s'        => \$dbname, # Database name
     'outroot|w=s'       => \$outroot, # output file base name
@@ -71,4 +74,8 @@
     defined $dvodb and
     defined $camera;
+if ($stage =~ /cam/ && !defined $stage_id) {
+    my_die("cam stage requires -stage_id", $add_id, 3);
+
+}
 if ($minidvodb && !defined($minidvodb_group)) {
                 my_die( "missing minidvodb_group", $add_id, 3 );
@@ -108,12 +115,61 @@
 
 # it was PSASTRO.OUTPUT
-my $fpaObjects = $ipprc->filename("PSASTRO.OUTPUT",     $stageroot) or &my_die("Missing entry from camera config", $add_id, $PS_EXIT_CONFIG_ERROR);
-
+
+my $fpaObjects = $ipprc->filename("PSASTRO.OUTPUT", $stageroot) or &my_die("Missing entry from camera config", $add_id, $PS_EXIT_CONFIG_ERROR);
+
+if ($stage =~ /cam/) {
+    # if it is cam stage we need to be careful when grabbing the filename. 
+    # This breaks down into a few steps: 
+    
+    #get info about the cam_id 
+    my $magicked;
+    {
+	my $mdcParser = PS::IPP::Metadata::Config->new;
+        my $command = "$camtool -processedexp -cam_id " . $stage_id;
+        $command .= " -dbname $dbname" if defined $dbname;
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        &my_die( "Unable to get info on camRun", $add_id, $PS_EXIT_SYS_ERROR) unless $success;
+        if (scalar @$stdout_buf == 0 ) { #it lists nothing if it is the first
+	    &my_die( "empty camRun", $add_id, $PS_EXIT_SYS_ERROR);
+	    print "listing nothing\n";
+	} else {
+	    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+		&my_die("Unable to parse metadata config", $add_id, $PS_EXIT_PROG_ERROR);
+	    #this fails if there is nothing listed. I checked.
+	    my $components = parse_md_list($metadata) or
+		&my_die("Unable to parse metadata list", $add_id, $PS_EXIT_PROG_ERROR);
+	    my $comp = $$components[0];
+	    my  $mparsed = $comp->{magicked};
+	    
+	    if (!defined($mparsed)) {
+		&my_die("Unable to parse magicked", $add_id, $PS_EXIT_PROG_ERROR);
+	    } #but just to make sure, have it grab a minidvodb_name, to make sure it's not junk.
+	    print "found a value for magicked:$mparsed\n";
+	    $magicked = $mparsed;
+	}
+	
+	#is this cam_id magicked or not?
+	if ($magicked) {
+	    $stage_extra1 = $magicked;
+	    $fpaObjects = $ipprc->destreaked_filename("$fpaObjects") or &my_die("Missing entry from camera config", $add_id, $PS_EXIT_CONFIG_ERROR);
+	    print "cam_id is magicked, using $fpaObjects for the cam smf\n";
+	} else {
+	    print "cam_id is NOT magicked, using $fpaObjects for the cam smf\n";
+	}
+    }
+}
 if (($stage =~/staticsky/) || ($stage =~/stack/)) {
     $fpaObjects =~ s/smf$/cmf/;
-    
-}
+}
+if ($stage =~/staticsky_multi/) {
+    $fpaObjects =~ s/smf$/cmf/; #this should do nothing because it was already modded above
+    &my_die( "can't find the filter_num for staticsky_multi, giving up.", $add_id, $PS_EXIT_SYS_ERROR) unless (defined $multi_num);
+    my $nice_num = sprintf ("%03d", $multi_num);
+    $fpaObjects =~ s/cmf$/$nice_num.cmf/;  #this make it look for .001.cmf, etc
+}
+
 my $traceDest  = $ipprc->filename("TRACE.EXP",          $outroot) or &my_die("Missing entry from camera config", $add_id, $PS_EXIT_CONFIG_ERROR);
-
+	
 # convert supplied DVO database name to UNIX filename
 my $dvodbReal;
@@ -122,84 +178,69 @@
     $dvodbReal = $ipprc->convert_filename_absolute( $dvodbReal ) or &my_die("can't get path for dvodb", $add_id, $PS_EXIT_CONFIG_ERROR);
 }
-
-
-
-
 my $dtime_addstar = 0;
-
 if (defined $dvodbReal) {
-        if ($minidvodb) {
-
-            #if (!defined($minidvodb_name)) {
-                #take the active one, if it's not defined on the command line
-                #reverts would have this already set, for example.
-                my $command = "addtool -listminidvodbrun ";
-                $command .= " -minidvodb_group $minidvodb_group" if defined $minidvodb_group;
-                $command .= " -minidvodb_name $minidvodb_name" if defined $minidvodb_name;
-                $command .= " -state 'active' -limit 1";
-                $command .= " -dbname $dbname" if defined $dbname;
-                print $command;
-                my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
-                    run(command => $command, verbose => $verbose);
-                &my_die( "Unable to get active minidvodb_name", $add_id, $PS_EXIT_SYS_ERROR) unless $success;
-                my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
-                    &my_die("Unable to parse metadata config", $add_id, $PS_EXIT_PROG_ERROR);
-
-                my $components = parse_md_list($metadata) or
-                    &my_die("Unable to parse metadata list", $add_id, $PS_EXIT_PROG_ERROR);
-                my $comp = $$components[0];
-                $minidvodb_path = $comp->{minidvodb_path};
-                $minidvodb_name = $comp->{minidvodb_name};
-
-                if (!defined($minidvodb_path)) {
-                    &my_die("Unable to parse minidvodb_path", $add_id, $PS_EXIT_PROG_ERROR);
-                }
-                if (!defined($minidvodb_name)) {
-                    &my_die("Unable to parse minidvodb_name", $add_id, $PS_EXIT_PROG_ERROR);
-                }
-            #}
-            # tack on the minidvodb part to the db.
-#           $dvodbReal = $dvodbReal . '/' . $minidvodb_name . '/';
-#we don't need this now that I fixed the paths
-
-        }
-        else {
-
-            $minidvodb_path = $dvodbReal;
-        }
-
-unless ($no_op) {
+    if ($minidvodb) {
+	my $command = "addtool -listminidvodbrun ";
+	$command .= " -minidvodb_group $minidvodb_group" if defined $minidvodb_group;
+	$command .= " -minidvodb_name $minidvodb_name" if defined $minidvodb_name;
+	$command .= " -state 'active' -limit 1";
+	$command .= " -dbname $dbname" if defined $dbname;
+	print $command;
+	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	    run(command => $command, verbose => $verbose);
+	&my_die( "Unable to get active minidvodb_name", $add_id, $PS_EXIT_SYS_ERROR) unless $success;
+	my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+	    &my_die("Unable to parse metadata config", $add_id, $PS_EXIT_PROG_ERROR);
+
+	my $components = parse_md_list($metadata) or
+	    &my_die("Unable to parse metadata list", $add_id, $PS_EXIT_PROG_ERROR);
+	my $comp = $$components[0];
+	$minidvodb_path = $comp->{minidvodb_path};
+	$minidvodb_name = $comp->{minidvodb_name};
+	
+	if (!defined($minidvodb_path)) {
+	    &my_die("Unable to parse minidvodb_path", $add_id, $PS_EXIT_PROG_ERROR);
+	}
+	if (!defined($minidvodb_name)) {
+	    &my_die("Unable to parse minidvodb_name", $add_id, $PS_EXIT_PROG_ERROR);
+	}
+    }
+    else {
+	$minidvodb_path = $dvodbReal;
+    }
+    
+    unless ($no_op) {
             print $dvodbReal;
-
-        ## addstar can either save the full set of detections, or just
-        ## the image metadata, in the dvodb.  this is set in the
-        ## database table addRun
-
-        # addstar requires the user to have a valid .ptolemyrc which
-        # in turn points at ippconfig/dvo.site
-
-        # get the names for the camera and the real input file
-        my $camdir = $ipprc->dvo_cameradir(); # Camera directory for addstar
-        my $realFile = $ipprc->file_resolve($fpaObjects) or &my_die("Unable to resolve $fpaObjects", $add_id, $PS_EXIT_SYS_ERROR);
-
-        # require a defined output dvo database to run addstar (ie, refuse to use the .ptolemyrc default)
-        my $command  = "$addstar -update"; # XXX optionally set -update?
-        $command .= " -D CAMERA $camdir";
-        $command .= " -D CATDIR $minidvodb_path";
-        $command .= " $realFile";
-        $command .= " -use-name $fpaObjects"; # DVO wants the neb-name as a file reference
+	    
+	    ## addstar can either save the full set of detections, or just
+	    ## the image metadata, in the dvodb.  this is set in the
+	    ## database table addRun
+
+	    # addstar requires the user to have a valid .ptolemyrc which
+	    # in turn points at ippconfig/dvo.site
+
+	    # get the names for the camera and the real input file
+	    my $camdir = $ipprc->dvo_cameradir(); # Camera directory for addstar
+	    my $realFile = $ipprc->file_resolve($fpaObjects) or &my_die("Unable to resolve $fpaObjects", $add_id, $PS_EXIT_SYS_ERROR);
+	    
+	    # require a defined output dvo database to run addstar (ie, refuse to use the .ptolemyrc default)
+	    my $command  = "$addstar -update"; # XXX optionally set -update?
+	    $command .= " -D CAMERA $camdir";
+	    $command .= " -D CATDIR $minidvodb_path";
+	    $command .= " $realFile";
+	    $command .= " -use-name $fpaObjects"; # DVO wants the neb-name as a file reference
 	    $command .= " -image" if $image_only;
-	    if ($stage = ~/staticsky/) {
+	    if ($stage =~ /staticsky/) {
 		$command .= " -accept-astrom ";
+	    }  #careful here - this matches staticsky and staticsky_multi
+	    my $mjd_addstar_start = DateTime->now->mjd;   # MJD of starting script
+
+	    my ( $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);
+		&my_die("Unable to perform addstar: $error_code", $add_id, $error_code);
 	    }
-        my $mjd_addstar_start = DateTime->now->mjd;   # MJD of starting script
-
-        my ( $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);
-            &my_die("Unable to perform addstar: $error_code", $add_id, $error_code);
-        }
-        $dtime_addstar = 86400.0*(DateTime->now->mjd - $mjd_addstar_start);   # MJD of starting script
+	    $dtime_addstar = 86400.0*(DateTime->now->mjd - $mjd_addstar_start);   # MJD of starting script
     }
 }
@@ -212,4 +253,5 @@
 $fpaCommand .= " -dvodb_path $minidvodb_path" if defined $minidvodb_path;
 $fpaCommand .= " -minidvodb_name $minidvodb_name" if defined $minidvodb_name;
+$fpaCommand .= " -stage_extra1 $stage_extra1" if defined $stage_extra1;
 $fpaCommand .= " -dbname $dbname" if defined $dbname;
 print $fpaCommand;
@@ -244,6 +286,8 @@
         $command .= " -path_base $outroot" if defined $outroot;
         $command .= (" -dtime_addstar " . ((DateTime->now->mjd - $mjd_start) * 86400));
-        $fpaCommand .= " -minidvodb_name $minidvodb_name" if defined $minidvodb_name;
+   #     $command .= " -minidvodb_name $minidvodb_name" if defined $minidvodb_name; don't think we want it recorded (not sure)
+	$command .= " -stage_extra1 $stage_extra1" if defined $stage_extra1;
         $command .= " -dbname $dbname" if defined $dbname;
+	print $command;
         system ($command);
     }
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/background_chip.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/background_chip.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/background_chip.pl	(revision 33415)
@@ -42,5 +42,5 @@
 # Parse the command-line arguments
 my ( $chip_bg_id, $class_id, $camera, $outroot, $dbname, $reduction, $verbose,
-     $threads, $no_update, $save_temps, $no_op, $redirect, $deburned );
+     $threads, $no_update, $save_temps, $no_op, $redirect, $chip_path_base, $magicked );
 GetOptions(
     'chip_bg_id=s'      => \$chip_bg_id,    # chipBackgroundRun identifier
@@ -51,4 +51,6 @@
     'reduction=s'       => \$reduction, # Reduction class
     'threads=s'         => \$threads,   # Number of threads to use
+    'chip_path_base=s'  => \$chip_path_base, # optional chip_path_base
+    'magicked=s'        => \$magicked,  # magicked status of input
     'verbose'           => \$verbose,   # Print to stdout
     'no-update'         => \$no_update, # Don't update the database?
@@ -88,6 +90,17 @@
 # Get inputs
 my $in_path;                    # Input path
-my $magicked;                   # Input is magicked?
-{
+my $do_stats;
+my $dump_config;
+if ($chip_path_base and $no_update) {
+    # we are running outside of a chip_bg_run (perhaps by the postage stamp server) don't dump config
+    # or do stats. Get path to input from command line.
+    $in_path = $chip_path_base;
+    $magicked = 0 if !defined $magicked;
+    $do_stats = 0;
+    $dump_config = 0;
+} else {
+    # normal operation. Get input parameters from the database
+    $do_stats = 1;
+    $dump_config = 1;
     my $command = "bgtool -chipinputs -chip_bg_id $chip_bg_id -class_id $class_id";
     $command .= " -dbname $dbname" if defined $dbname;
@@ -153,11 +166,11 @@
     $command .= " -image $in_image";
     $command .= " -mask $in_mask";
-    $command .= " -stats $out_stats";
+    $command .= " -stats $out_stats" if $do_stats;
     $command .= " -background $in_bg" if $apply_bg;
     $command .= " -pattern $in_pattern" if $apply_pattern;
     $command .= " -recipe PPBACKGROUND $recipe_ppBackground";
-    $command .= " -recipe PPSTATS CHIPSTATS";
+    $command .= " -recipe PPSTATS CHIPSTATS" if $do_stats;
     $command .= " -dbname $dbname" if defined $dbname;
-    $command .= " -dumpconfig $out_config";
+    $command .= " -dumpconfig $out_config" if $dump_config;
     $command .= " -tracedest $traceDest -log $logDest";
 
@@ -171,7 +184,7 @@
 
 # Gather command-line arguments from statistics
-my $cmdflags;                   # Command-line flags to add
-my $quality;                    # Quality flag
-{
+my $cmdflags = "";                  # Command-line flags to add
+my $quality = 0;                    # Quality flag
+if ($do_stats) {
     &my_die("Couldn't find expected output file: $out_stats", $chip_bg_id, $class_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($out_stats);
 
@@ -191,7 +204,7 @@
 
 if (!$quality and !$no_op) {
-    &my_die("Couldn't find expected output file: $out_stats", $chip_bg_id, $class_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($out_image);
-    &my_die("Couldn't find expected output file: $out_stats", $chip_bg_id, $class_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($out_mask);
-    &my_die("Couldn't find expected output file: $out_stats", $chip_bg_id, $class_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($out_config);
+    &my_die("Couldn't find expected output file: $out_image", $chip_bg_id, $class_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($out_image);
+    &my_die("Couldn't find expected output file: $out_mask", $chip_bg_id, $class_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($out_mask);
+    &my_die("Couldn't find expected output file: $out_config", $chip_bg_id, $class_id, $PS_EXIT_SYS_ERROR) unless !$dump_config or $ipprc->file_exists($out_config);
 
 }
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/background_warp.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/background_warp.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/background_warp.pl	(revision 33415)
@@ -38,4 +38,5 @@
 
 my ($warp_bg_id, $skycell_id, $tess_dir, $reduction, $camera, $dbname, $outroot, $threads, $verbose, $no_update, $no_op, $redirect, $save_temps);
+my ($astrometry, $imageName, $maskName, $magicked);
 GetOptions(
     'warp_bg_id|i=s'      => \$warp_bg_id, # Warp identifier
@@ -46,4 +47,8 @@
     'reduction=s'         => \$reduction, # Reduction class
     'outroot=s'           => \$outroot, # Output root name
+    'image-list=s'        => \$imageName, # list of input image files
+    'mask-list=s'         => \$maskName, # list of input mask files
+    'astrometry=s'        => \$astrometry,
+    'magicked=s'          => \$magicked,
     'threads=s'           => \$threads,   # Number of threads to use for pswarp
     'verbose'             => \$verbose,   # Print to stdout
@@ -64,5 +69,20 @@
     and defined $outroot;
 
-my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $warp_bg_id, $skycell_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
+if ($imageName) {
+    # we have been invoked in a special mode where the caller tells us all about the inputs
+    # This is used by the postage stamp server to make non-background corrected images without
+    # the existence of a warpBackgroundRun (or chipBackgroundRun)
+    # Make sure all of the inputs were supplied
+    if (!(defined $maskName and defined $astrometry and defined $magicked)) {
+        print STDERR "mask-list, astrometry, and magicked must be supplied if image-list is used\n";
+        if ($warp_bg_id) {
+            my_die( "invalid argument list.", $warp_bg_id, $skycell_id, $PS_EXIT_CONFIG_ERROR );
+        }
+        exit $PS_EXIT_CONFIG_ERROR;
+    }
+}
+
+my $ipprc = PS::IPP::Config->new( $camera ) or 
+my_die( "Unable to set up", $warp_bg_id, $skycell_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
 
 my $logDest = $ipprc->filename("LOG.EXP", $outroot, $skycell_id) or my_die( "Unable to get log filename", $warp_bg_id, $skycell_id, $PS_EXIT_SYS_ERROR );
@@ -97,9 +117,9 @@
 # Get list of filenames
 my $tempOutRoot = "/tmp/background.warp.$warp_bg_id.$skycell_id";
-my ($imageFile, $imageName) = tempfile( "$tempOutRoot.image.list.XXXX",  UNLINK => !$save_temps);
-my ($maskFile, $maskName) = tempfile( "$tempOutRoot.mask.list.XXXX",   UNLINK => !$save_temps);
-my $astrometry;                 # Astrometry filename
-my $magicked;                   # Magicked status
-{
+if (!defined $imageName) {
+    # go find our inputs
+    my ($imageFile, $maskFile);
+    ($imageFile, $imageName) = tempfile( "$tempOutRoot.image.list.XXXX",  UNLINK => !$save_temps);
+    ($maskFile, $maskName) = tempfile( "$tempOutRoot.mask.list.XXXX",   UNLINK => !$save_temps);
     my $command = "$bgtool -warpinputs";
     $command .= " -warp_bg_id $warp_bg_id";
@@ -132,7 +152,7 @@
         $astrometry = $astrom;
     }
-}
-close $imageFile;
-close $maskFile;
+    close $imageFile;
+    close $maskFile;
+}
 
 &my_die("Can't find input astrometry: $astrometry", $warp_bg_id, $skycell_id, $PS_EXIT_PROG_ERROR) unless $ipprc->file_exists($astrometry);
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/camera_exp.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/camera_exp.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/camera_exp.pl	(revision 33415)
@@ -88,5 +88,5 @@
 
 if ($redirect) {
-    $ipprc->redirect_output($logDest) or my_die( "Unable to redirect output", $cam_id, $PS_EXIT_SYS_ERROR );
+    $ipprc->redirect_to_logfile($logDest) or my_die( "Unable to redirect output", $cam_id, $PS_EXIT_SYS_ERROR );
     print "\n\n";
     print "Starting script $0 on $host\n\n";
@@ -156,4 +156,10 @@
     { # Determine if FWHM is too large to bother continuing.
 	my $maxFWHM = 0;
+      # XXX: temporary hack if this is a CNP exposure set maxFWHM to a large
+      # value.
+      # Before the next tag we will put this into a recipe
+      if ($outroot =~ /CNP/) {
+            $maxFWHM = 25;
+      } else {
 	my $command = "$ppConfigDump -camera $camera -get-key MAX_ALLOWED_FWHM";
 	my ($success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
@@ -168,4 +174,5 @@
 	    $maxFWHM = metadataLookupStr($cameraConfig, 'MAX_ALLOWED_FWHM');
 	}
+      }
 	if ($maxFWHM) {
 	    my $expFWHM;
@@ -403,4 +410,18 @@
     &my_die("Couldn't find expected output file: $file",  $cam_id, $PS_EXIT_SYS_ERROR) unless
         $ipprc->file_exists($file);
+
+    # Funpack to confirm we've really made things correctly
+    my $diskfile = $ipprc->file_resolve($file);
+    if ($diskfile =~ /fits/) {
+        my $funpack  = can_run('funpack') or &my_die ("Can't find funpack",  $cam_id, $PS_EXIT_SYS_ERROR);
+	my $check_command = "$funpack -S $diskfile > /dev/null";
+	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	    run(command => $check_command, verbose => $verbose);
+	if (!$success) {
+	    &my_die("Output file not a valid fits file: $file",  $cam_id, $PS_EXIT_SYS_ERROR);
+	}
+    }
+    #####
+
 
     my $scheme = file_scheme($file);
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/chip_imfile.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/chip_imfile.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/chip_imfile.pl	(revision 33415)
@@ -34,4 +34,6 @@
 my $ppConfigDump = can_run('ppConfigDump') or (warn "Can't find ppConfigDump" and $missing_tools = 1);
 my $ppStatsFromMetadata = can_run('ppStatsFromMetadata') or (warn "Can't find ppStatsFromMetadata" and $missing_tools = 1);
+my $nebrepair = can_run('neb-repair') or (warn "Can't find neb-repair" and $missing_tools = 1);
+
 if ($missing_tools) {
     warn("Can't find required tools.");
@@ -44,6 +46,4 @@
 my ( $exp_id, $chip_id, $class_id, $chip_imfile_id, $uri, $camera, $outroot, $dbname, $run_state, $reduction, $threads, $verbose,
      $no_update, $save_temps, $no_op, $redirect, $magicked, $deburned );
-
-my $zaplog = 0;
 
 GetOptions(
@@ -65,5 +65,4 @@
     'no-op'             => \$no_op,     # Don't do any operations?
     'redirect-output'   => \$redirect,
-    'zaplog'            => \$zaplog,
     'save-temps'        => \$save_temps, # Save temporary files?
     ) or pod2usage( 2 );
@@ -92,15 +91,18 @@
 }
 
-my ($logDest, $traceDest);
+my ($logRule, $traceDest);
 if ($run_state eq 'new') {
-    $logDest = prepare_output("LOG.IMFILE", $outroot, $class_id, $zaplog);
+    $logRule = "LOG.IMFILE";
     $traceDest = prepare_output("TRACE.IMFILE",  $outroot, $class_id, 1);
 } else {
-    $logDest = prepare_output("LOG.IMFILE.UPDATE", $outroot, $class_id, 1);
+    $logRule = "LOG.IMFILE.UPDATE";
     $traceDest = prepare_output("TRACE.IMFILE.UPDATE",  $outroot, $class_id, 1);
 }
 
 if ($redirect) {
-    $ipprc->redirect_output($logDest) or my_die( "Unable to redirect output", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR );
+    my $logDest = $ipprc->filename($logRule, $outroot, $class_id);
+
+    $ipprc->redirect_to_logfile($logDest) or my_die( "Unable to redirect output", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR );
+
     print STDOUT "\n\n";
     print STDOUT "Starting script $0 on $host\n\n";
@@ -145,9 +147,10 @@
 my $outputBin2;
 my $dump_config = 1;
+my $do_stats = 1;
+$outputStats   = prepare_output("PPIMAGE.STATS",         $outroot, $class_id, 1);
 my $do_binned_images = 1;
 if ($run_state eq 'new') {
     # prepare the files that are only created for a new run
     $configuration = prepare_output("PPIMAGE.CONFIG",        $outroot, $class_id, 1);
-    $outputStats   = prepare_output("PPIMAGE.STATS",         $outroot, $class_id, 1);
 } else {
     $configuration = $ipprc->filename('PPIMAGE.CONFIG', $outroot, $class_id) 
@@ -194,5 +197,4 @@
 unless ($no_op) {
     my $command;
-    my $do_stats;
 
     ## get the ppImage recipe for this camera and CHIP reduction
@@ -266,27 +268,28 @@
         }
 
-        ## Read camera config to get the current good burntool state
-	## this returns just the values of interest in a tiny mdc file
-        my $camera_config_cmd = "$ppConfigDump -camera $camera -get-key BURNTOOL.STATE.GOOD -get-key BURNTOOL.STATE.GOOD.UPDATE";
-        ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
-            run(command => $camera_config_cmd, verbose => 0);
-        unless ($success) {
-            $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
-            &my_die("Unable to perform ppConfigDump: $error_code", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR);
-        }
-
-        my $camData = $mdcParser->parse(join "", @$stdout_buf) or
-            &my_die("Unable to parse ppConfigDump metadata", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR);
-
-        my $burntoolStateGood;
-        my $burntoolStateGoodUpdate;
-        foreach my $camEntry (@$camData) {
-            if ($camEntry->{name} eq "BURNTOOL.STATE.GOOD") {
-                $burntoolStateGood = $camEntry->{value};
-            }
-            if ($camEntry->{name} eq "BURNTOOL.STATE.GOOD.UPDATE") {
-                $burntoolStateGoodUpdate = $camEntry->{value};
-            }
-        }
+        ## Read camera config to get the current good burntool state :
+        ## XXX This is extremely slow. Any better way to do this?
+        ## my $camera_config_cmd = "$ppConfigDump -camera $camera -dump-camera -";
+        ## ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        ##     run(command => $camera_config_cmd, verbose => 0);
+        ## unless ($success) {
+        ##     $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+        ##     &my_die("Unable to perform ppConfigDump: $error_code", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR);
+        ## }
+        ##
+        ## my $camData = $mdcParser->parse(join "", @$stdout_buf) or
+        ##     &my_die("Unable to parse ppConfigDump metadata", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR);
+
+        ## XXX short term hack until we have a C-based parser
+        my $burntoolStateGood = 14;
+        my $burntoolStateGoodUpdate = 13;
+        ## foreach my $camEntry (@$camData) {
+        ##     if ($camEntry->{name} eq "BURNTOOL.STATE.GOOD") {
+        ##         $burntoolStateGood = $camEntry->{value};
+        ##     }
+        ##     if ($camEntry->{name} eq "BURNTOOL.STATE.GOOD.UPDATE") {
+        ##         $burntoolStateGoodUpdate = $camEntry->{value};
+        ##     }
+        ## }
 
         if ($run_state eq 'new') {
@@ -329,4 +332,14 @@
         # get the UNIX version of the (possible) neb: or path: filename
         my $uriReal = $ipprc->file_resolve( $uri );
+	# Catch errors here
+	if (!$uriReal) {
+	    my $repair_cmd = "$nebrepair $uri";
+	    my ($repair_success, $repair_error_code, $repair_full_buf, $repair_stdout_buf, $repair_stderr_buf ) = run(command => $repair_cmd, verbose => $verbose);
+	    unless ($repair_success) {
+		&my_die("Unable to attempt repair: $uri $repair_error_code", $exp_id,$chip_id, $class_id, $PS_EXIT_SYS_ERROR);
+	    }
+	    $uriReal = $ipprc->file_resolve( $uri );
+	}
+	    
         &my_die("Unable to resolve $uri on $host", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR) if !$uriReal;
 
@@ -335,4 +348,13 @@
         ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) = run(command => $funpack_cmd, verbose => $verbose);
         unless ($success) {
+	    # Catch errors here
+	    my $repair_cmd = "$nebrepair $uri";
+	    my ($repair_success, $repair_error_code, $repair_full_buf, $repair_stdout_buf, $repair_stderr_buf ) = run(command => $repair_cmd, verbose => $verbose);
+	    unless ($repair_success) {
+		&my_die("Unable to attempt repair: $uri $repair_error_code", $exp_id,$chip_id, $class_id, $PS_EXIT_SYS_ERROR);
+	    }
+	    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) = run(command => $funpack_cmd, verbose => $verbose);
+	}
+        unless ($success) {
             $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
             &my_die("Unable to perform funpack: $error_code", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR);
@@ -347,5 +369,14 @@
             $burntoolTable_uri =~ s/fits$/burn.tbl/;
             $burntoolTable_uriReal = $ipprc->file_resolve( $burntoolTable_uri );
+	    if ((!$burntoolTable_uriReal)||(!($ipprc->file_exists($burntoolTable_uri)))) {
+		my $repair_cmd = "$nebrepair $burntoolTable_uri";
+		my ($repair_success, $repair_error_code, $repair_full_buf, $repair_stdout_buf, $repair_stderr_buf ) = run(command => $repair_cmd, verbose => $verbose);
+		unless ($repair_success) {
+		    &my_die("Unable to attempt repair: $uri $repair_error_code", $exp_id,$chip_id, $class_id, $PS_EXIT_SYS_ERROR);
+		}
+		$burntoolTable_uriReal = $ipprc->file_resolve( $burntoolTable_uri );
+	    }
             unless ($ipprc->file_exists($burntoolTable_uri)) {
+		# Catch errors here
                 &my_die("Couldn't find burntool table: $burntoolTable_uri",$exp_id,$chip_id,$class_id, $PS_EXIT_SYS_ERROR);
             }
@@ -439,5 +470,5 @@
         $command .= " -Db PPIMAGE:PHOTOM FALSE";
     }
-    if ($run_state eq "new") {
+    if ($run_state eq "new" or $do_stats) {
         $command .= " -recipe PPSTATS CHIPSTATS";
         $command .= " -stats $outputStats";
@@ -450,5 +481,5 @@
     $command .= " -image_id $chip_imfile_id" if defined $chip_imfile_id;
     $command .= " -source_id $source_id" if defined $source_id;
-    $command .= " -tracedest $traceDest -log $logDest";
+    $command .= " -tracedest $traceDest";
     $command .= " -dbname $dbname" if defined $dbname;
 
@@ -531,14 +562,23 @@
 
 # Add the processed file to the database
-unless ($no_update) {
-    my ( $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 chiptool -addprocessedimfile: $error_code\n");
-        exit($error_code);
-    }
-} else {
-    print "skipping command: $command\n";
+my $tries = 0;
+while (1) {
+    $tries++;
+    unless ($no_update) {
+        my ( $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 chiptool -addprocessedimfile: $error_code\n");
+            exit($error_code) if $tries >= 3;
+            warn("Waiting 10 seconds to try again\n");
+            sleep 10;
+        } else {
+            last;
+        }
+    } else {
+        print "skipping command: $command\n";
+        last;
+    }
 }
 
@@ -726,4 +766,16 @@
         &my_die("Couldn't find expected output file: $file",  $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR);
     }
+    # Funpack to confirm we've really made things correctly
+    my $diskfile = $ipprc->file_resolve($file);
+    if ($diskfile =~ /fits/) {
+        my $funpack  = can_run('funpack') or &my_die ("Can't find funpack", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR);
+	my $check_command = "$funpack -S $diskfile > /dev/null";
+	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	    run(command => $check_command, verbose => $verbose);
+	if (!$success) {
+	    &my_die("Output file not a valid fits file: $file", $exp_id, $chip_id, $class_id, $PS_EXIT_SYS_ERROR);
+	}
+    }
+    #####
 
     if ($replicate and $neb) {
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/diff_skycell.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/diff_skycell.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/diff_skycell.pl	(revision 33415)
@@ -40,5 +40,4 @@
 my ($skycell_id, $diff_skyfile_id);
 my ($use_convolved);
-my $zaplog = 0;
 
 GetOptions(
@@ -57,5 +56,4 @@
     'no-op'             => \$no_op, # Don't do any operations?
     'redirect-output'   => \$redirect,
-    'zaplog'            => \$zaplog, # delete any previous log file
 ) or pod2usage( 2 );
 
@@ -79,15 +77,9 @@
 
 # XXX camera is not known here; cannot use filerules... 
-# bills 2011-03-07 This is unfortunate because it prevents us from using prepare_output ...
-# my $logDest = $ipprc->filename("LOG.EXP", $outroot);
 my $logDest = "$outroot.log";
 if ($run_state eq 'update') {
     $logDest .= '.update';
-    # ... well we can just do the dirty work here
-    $ipprc->kill_file($logDest);
-} elsif ($zaplog) {
-    $ipprc->kill_file($logDest);
-}
-$ipprc->redirect_output($logDest) or my_die( "Unable to redirect output", $diff_id, $skycell_id, $PS_EXIT_SYS_ERROR ) if $redirect;
+}
+$ipprc->redirect_to_logfile($logDest) or my_die( "Unable to redirect output", $diff_id, $skycell_id, $PS_EXIT_SYS_ERROR ) if $redirect;
 
 my $source_id = $ipprc->source_id($dbname, $PS_TABLE_ID_DIFF);
@@ -285,8 +277,16 @@
     $outputStats = prepare_output("SKYCELL.STATS.UPDATE", $outroot, 1);
     $configuration = $ipprc->filename("PPSUB.CONFIG", $outroot);
-    if (!$ipprc->file_exists($configuration)) {
-       print STDERR "WARNING: Config dump file $configuration is missing. Using current recipes and file rules.\n";
-       $configuration = undef;
-    }
+    # XXX: Work around problem with updating old diff runs. Their config files are incompatible
+    # with the current code.
+    if ($camera ne 'GPC1' or $diff_id >= 88268) {
+        if (!$ipprc->file_exists($configuration)) {
+	    print STDERR "WARNING: Config dump file $configuration is missing. Using current recipes and file rules.\n";
+	    $configuration = undef;
+	}
+    } else {
+    	print STDERR "WARNING: Using new recipes because config dump file is too old\n";
+    	$configuration = undef;
+    }
+
     # use WARPSTATS if we're updating, as we don't care about the new stuff from the STACK and DIFF STATS recipes
     $recipe_ppstats = 'WARPSTATS';
@@ -468,4 +468,18 @@
     &my_die("Couldn't find expected output file: $file",  $diff_id, $skycell_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($file);
 
+    # Funpack to confirm we've really made things correctly
+    my $diskfile = $ipprc->file_resolve($file);
+    if ($diskfile =~ /fits/) {
+        my $funpack  = can_run('funpack') or &my_die ("Can't find funpack", $diff_id, $skycell_id, $PS_EXIT_SYS_ERROR);
+	my $check_command = "$funpack -S $diskfile > /dev/null";
+	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	    run(command => $check_command, verbose => $verbose);
+	if (!$success) {
+	    &my_die("Output file not a valid fits file: $file", $diff_id, $skycell_id, $PS_EXIT_SYS_ERROR);
+	}
+    }
+    #####
+
+
     if ($replicate and $neb) {
         $ipprc->replicate_file($file) or &my_die("failed to replicate: $file\n",  $diff_id, $skycell_id, $PS_EXIT_SYS_ERROR);
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/dist_bundle.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/dist_bundle.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/dist_bundle.pl	(revision 33415)
@@ -194,4 +194,8 @@
         # XXX: perhaps only do this for stages where we know that this happens
         next if $file_rule =~ /STATS/;
+	# don't fail on these "non-essential files"
+	next if $file_rule =~ /TRACE/;
+	next if $file_rule =~ /LOG/;
+	next if $file_rule =~ /BIN/;
 
         &my_die("failed to resolve  $file_name", $component, $PS_EXIT_DATA_ERROR);
@@ -636,4 +640,8 @@
         $file{file_rule} = $key;
         $file{name} = $val;
+	if ($val eq "STDERR" or $val eq "STDOUT") {
+	    print STDERR "Skipping $key because filename is $val\n";
+	    next;
+	}
         push @file_list, \%file;
     }
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/dist_component.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/dist_component.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/dist_component.pl	(revision 33415)
@@ -37,5 +37,5 @@
 # Parse the command-line arguments
 my ($dist_id, $camera, $stage, $stage_id, $component, $path_base, $chip_path_base, $clean, $alt_path_base);
-my ($outdir, $run_state, $data_state, $magicked, $no_magic, $poor_quality, $exp_type);
+my ($outdir, $run_state, $data_state, $magicked, $no_magic, $poor_quality, $exp_type, $rerun);
 my ($dbname, $save_temps, $verbose, $no_update, $logfile);
 
@@ -57,4 +57,5 @@
            'outdir=s'       => \$outdir,     # "directory" for outputs
            'clean'          => \$clean,      # create clean distribution
+           'rerun'          => \$rerun,      # recreate an existing component and update the database
            'save-temps'     => \$save_temps, # Save temporary files?
            'dbname=s'       => \$dbname,     # Database name
@@ -76,5 +77,5 @@
 
 my $ipprc = PS::IPP::Config->new($camera); # IPP configuration
-$ipprc->redirect_output($logfile) if $logfile;
+$ipprc->redirect_to_logfile($logfile) if $logfile;
 
 my $temproot = metadataLookupStr($ipprc->{_siteConfig}, "TEMP.DIR");
@@ -146,5 +147,12 @@
 
 {
-    my $command = "$disttool -addprocessedcomponent -dist_id $dist_id -component $component -outdir $outdir";
+    my $command;
+    if (!$rerun) {
+        $command = "$disttool -addprocessedcomponent";
+    } else {
+        $command = "$disttool -updateprocessedcomponent";
+    }
+    
+    $command .= " -dist_id $dist_id -component $component -outdir $outdir";
     $command .= " -name $file_name -bytes $bytes -md5sum $md5sum";
     $command .= " -dbname $dbname" if defined $dbname;
@@ -175,5 +183,10 @@
     $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
 
-    my $command = "$disttool -addprocessedcomponent";
+    my $command;
+    if (!$rerun) {
+        $command = "$disttool -addprocessedcomponent";
+    } else {
+        $command = "$disttool -updateprocessedcomponent";
+    }
     $command   .= " -dist_id $dist_id";
     $command   .= " -component $component";
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/dist_defineruns.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/dist_defineruns.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/dist_defineruns.pl	(revision 33415)
@@ -88,10 +88,19 @@
     push @stages, $stage;
 } else {
-    @stages = qw( raw chip chip_bg camera fake warp warp_bg diff stack SSdiff sky);
+    @stages = qw(chip chip_bg camera fake warp warp_bg diff stack SSdiff sky skysingle);
 }
 
 foreach my $stage (@stages) {
     foreach my $label (@labels) {
-        my $command = "$disttool -definebyquery -stage $stage -workdir $workdir -label $label";
+        my $single;
+        my $cmdstage;
+        if ($stage eq "skysingle") {
+            $cmdstage = "sky";
+            $single = 1;
+        } else {
+            $cmdstage = $stage;
+        }
+        my $command = "$disttool -definebyquery -stage $cmdstage -workdir $workdir -label $label";
+        $command .= " -singlefilter" if $single;
         $command .= " -no_magic" if $no_magic;
         $command .= " -pretend" if $no_update;
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/dist_fix_file.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/dist_fix_file.pl	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/dist_fix_file.pl	(revision 33415)
@@ -0,0 +1,234 @@
+#!/usr/bin/env perl
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+my $date = `date`;
+#print "\n\n";
+#print "Starting script $0 on $host at $date\n\n";
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use File::Temp qw( tempfile );
+use File::Basename qw( basename );
+use Digest::MD5::File qw( file_md5_hex );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config 1.01 qw( :standard );
+
+use DBI;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+# Look for programs we need
+my $missing_tools;
+my $disttool   = can_run('disttool') or (warn "Can't find disttool" and $missing_tools = 1);
+my $regtool   = can_run('regtool') or (warn "Can't find regtool" and $missing_tools = 1);
+my $dist_component   = can_run('dist_component.pl') or (warn "Can't find dist_component.pl" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+# Parse the command-line arguments
+my ($dist_id, $camera, $component );
+my ($outdir, $run_state, $data_state, $magicked, $poor_quality, $exp_type, $rerun);
+my ($dbname, $save_temps, $verbose, $no_update, $logfile);
+
+$camera = 'GPC1';
+
+GetOptions(
+           'dist_id=s'      => \$dist_id,    # distribution run identifier
+           'component=s'    => \$component,  # the class_id or skycell_id
+           'camera=s'       => \$camera,     # the class_id or skycell_id
+           'save-temps'     => \$save_temps, # Save temporary files?
+           'dbname=s'       => \$dbname,     # Database name
+           'verbose'        => \$verbose,    # Print stuff?
+           'no-update'      => \$no_update,  # Don't update the database?
+           'logfile=s'      => \$logfile,
+           ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --dist_id --camera --component",
+           -exitval => 3) unless
+    defined $dist_id and
+    defined $camera and
+    defined $component;
+
+my $ipprc = PS::IPP::Config->new($camera); # IPP configuration
+$ipprc->redirect_to_logfile($logfile) if $logfile;
+
+my $dbh = getDBHandle() unless $no_update;
+
+my $temproot = metadataLookupStr($ipprc->{_siteConfig}, "TEMP.DIR");
+
+$temproot = "/tmp" if !defined $temproot;
+
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+$disttool .= " -dbname $dbname" if $dbname;
+$regtool .= " -dbname $dbname" if $dbname;
+
+my $comp = get_distcomponent($dist_id, $component);
+
+my $stage = $comp->{stage};
+my $stage_id = $comp->{stage_id};
+my $no_magic = $comp->{no_magic};
+my $path_base;
+my $stage_args = "";
+if ($stage eq 'raw') {
+    # need path_base (get from uri) 
+    # exp_type
+    # no_magic
+    # camera path base if !$no_magic
+    my $command = "$regtool -processedimfile -exp_id $stage_id -class_id $component";
+    my ( $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);
+        &my_die("Unable to perform $command: $error_code", $dist_id, $component, $outdir, $error_code);
+    }
+
+    my $metadata = $mdcParser->parse (join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $dist_id, $component, $outdir, $PS_EXIT_PROG_ERROR);
+
+    my $results = parse_md_list($metadata);
+
+    if ((scalar @$results) != 1) {
+        my $n = scalar @$results;
+        &my_die("Unexected number of results from regtool -processedimfile: $n", $dist_id, $component, $outdir, $PS_EXIT_PROG_ERROR);
+    }
+    my $rawfile = $results->[0];
+    $path_base = $rawfile->{uri};
+    $path_base =~ s/.fits//;
+    my $exp_type = $rawfile->{exp_type};
+    if (!$no_magic) {
+        die "don't know how to create magicked raw bundles yet\n";
+    }
+    $stage_args .= " --exp_type $exp_type --chip_path_base NULL";
+    $stage_args .= " --no_magic" if $no_magic;
+} else {
+
+    die "not ready for stage $stage yet\n";
+}
+my $dist_cmd = "$dist_component --rerun --camera $camera --dist_id $dist_id --stage $stage --stage_id $stage_id --component $component --outdir $comp->{outdir} --path_base $path_base $stage_args";
+
+$dist_cmd .= " --dbname $dbname" if $dbname;
+$dist_cmd .= " --verbose" if $verbose;
+$dist_cmd .= " --no-update" if $no_update;
+
+print "$dist_cmd\n";
+
+$comp = get_distcomponent($dist_id, $component);
+
+my $bytes = $comp->{bytes};
+my $md5sum = $comp->{md5sum};
+my $name = $comp->{name};
+
+
+my $query = "UPDATE dsFile join dsFileset using(fileset_id) join dsProduct USING(prod_id) SET dsFile.bytes =?, dsFile.md5sum = ? WHERE prod_name = ? AND fileset_name = ? AND file_name = ?";
+
+my $stmt = $dbh->prepare($query);
+
+my $filesets = get_filesets($dist_id);
+
+foreach my $fs (@$filesets) {
+    my $product = $fs->{product};
+    my $fileset = $fs->{fileset};
+    $stmt->execute($bytes, $md5sum, $product, $fileset, $name)  or
+        &my_die("failed to update dsFile for $product $fileset $name\n", $dist_id, $component, $outdir, $PS_EXIT_PROG_ERROR);
+
+    print "Updated dsFile for $product $fileset $name $bytes $md5sum\n";
+}
+
+
+exit 0;
+
+sub getDBHandle {
+    my $dbserver = metadataLookupStr($ipprc->{_siteConfig}, "DS_DBSERVER");
+    my $dbuser = metadataLookupStr($ipprc->{_siteConfig}, "DS_DBUSER");
+    my $dbpassword = metadataLookupStr($ipprc->{_siteConfig}, "DS_DBPASSWORD");
+    my $dbname = metadataLookupStr($ipprc->{_siteConfig}, "DS_DBNAME");
+
+    &my_die("database configuration not set up", $dist_id, $component, $outdir, $PS_EXIT_UNKNOWN_ERROR) unless defined($dbserver) and defined($dbuser)
+        and defined($dbpassword) and defined($dbname);
+
+    my $dsn = "DBI:mysql:host=$dbserver;database=$dbname";
+
+    my $dbh = DBI->connect($dsn, $dbuser, $dbpassword) 
+        or die "Cannot connect to database $dbname on $dbserver.\n";
+
+    return $dbh;
+}
+
+
+
+sub get_distcomponent {
+    my $dist_id = shift;
+    my $component = shift;
+    my $command = "$disttool -processedcomponent -dist_id $dist_id -component $component";
+    my ( $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);
+        &my_die("Unable to perform $command: $error_code", $dist_id, $component, $outdir, $error_code);
+    }
+
+    my $metadata = $mdcParser->parse (join "", @$stdout_buf) or
+        &my_die("Unable to parse metadata config doc", $dist_id, $component, $outdir, $PS_EXIT_PROG_ERROR);
+
+    my $results = parse_md_list($metadata);
+
+    if ((scalar @$results) != 1) {
+        my $n = scalar @$results;
+        &my_die("Unexected number of results from disttool -processedcomponent: $n", $dist_id, $component, $outdir, $PS_EXIT_PROG_ERROR);
+    }
+    return $results->[0];
+}
+
+sub get_filesets {
+    my $dist_id = shift;
+    my $command = "$disttool -listfilesets -dist_id $dist_id";
+    my ( $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);
+        &my_die("Unable to perform $command: $error_code", $dist_id, $component, $outdir, $error_code);
+    }
+
+    my $output = join "", @$stdout_buf;
+    if (!$output) {
+        return undef;
+    }
+    my $metadata = $mdcParser->parse ($output) or
+        &my_die("Unable to parse metadata config doc", $dist_id, $component, $outdir, $PS_EXIT_PROG_ERROR);
+
+    my $results = parse_md_list($metadata);
+
+    return $results
+}
+
+
+### Pau.
+sub my_die
+{
+    my $msg = shift;            # Warning message on die
+    my $dist_id = shift;        # distRun.dist_id
+    my $component = shift;      # class_id, skycell_id, or exposure
+    my $outdir = shift;         # output directory
+    my $exit_code = shift;      # Exit code to add
+
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
+    carp($msg);
+    exit $exit_code;
+}
+
+__END__
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/ipp_cleanup.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/ipp_cleanup.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/ipp_cleanup.pl	(revision 33415)
@@ -218,4 +218,6 @@
             $command .= " -dbname $dbname" if defined $dbname;
 
+if (0) {
+        # XXX Don't set components to error cleaned anymore
             my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
                     run(command => $command, verbose => $verbose);
@@ -224,4 +226,5 @@
                 &my_die("Unable to perform chiptool: $error_code", "chip", $stage_id, $error_code);
             }
+}
 
             # We want to flag the run as well, to avoid attempting to reprocess the same data over and over again.
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/ipp_filename.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/ipp_filename.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/ipp_filename.pl	(revision 33415)
@@ -10,4 +10,10 @@
 #print "$ENV{'PATH'}\n";
 #print "$ENV{'PERL5LIB'}\n";
+
+# CZW: This is a horrible hack, but I don't want to have to debug all of ippMonitor to figure out why it's not working.
+unless (defined($ENV{'NEB_SERVER'})) {
+    $ENV{'NEB_SERVER'} = 'http://nebserver.ipp.ifa.hawaii.edu:80/nebulous';
+}
+
 
 use PS::IPP::Config;
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/ipp_serial_inject_into_nebulous.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/ipp_serial_inject_into_nebulous.pl	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/ipp_serial_inject_into_nebulous.pl	(revision 33415)
@@ -0,0 +1,169 @@
+#!/usr/bin/env perl
+
+# this program injects a list of single-file exposures into the db,
+# taking the filename (without .fits) as the exp_tag.  the user
+# supplies a temporary telescope and camera name.  these are used
+# for informational purposes only until the registration step can
+# determine the true telescope and camera name from the image
+# headers.
+
+# this program should not fail because of the data format or the
+# configuration, except for the very basic database setup.
+
+use warnings;
+use strict;
+
+use IPC::Cmd 0.36 qw( can_run run );
+use File::Spec;
+use PS::IPP::Config;
+
+my $ipprc = PS::IPP::Config->new(); # this is used for PATH, NEB filename conversions
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+# Parse the command-line arguments
+my ($camera, $telescope, $workdir, $reduction, $dvo_db, $tess_id, $end_stage, $label, $dbname, $no_op, $help);
+GetOptions('camera|i=s'     => \$camera,    # user-supplied camera name
+	   'telescope|t=s'  => \$telescope, # user-supplied telescope name
+	   'workdir|w=s'    => \$workdir,   # working directory for output files
+	   'reduction=s'    => \$reduction, # user-supplied camera name
+	   'dvodb=s'        => \$dvo_db,    # target dvo database 
+	   'tess_id=s'      => \$tess_id,   # tessalation for warping
+	   'end_stage=s'    => \$end_stage, # stop processing at this step
+	   'label=s'        => \$label,     # set chip label
+	   'dbname|d=s'     => \$dbname,    # Database name
+	   'no-op'          => \$no_op,     # pretend but don't actually inject
+	   'help'           => \$help       # give help listing
+) or pod2usage( 2 );
+
+pod2usage( -msg => "inject one or many files into the IPP pipeline database", 
+	   -exitval => 2) if 
+    defined $help;
+
+pod2usage( -msg => "Usage: $0 --telescope (name) --camera (name) [--workdir path] [--reduction class] [--dvodb db] [--tess_id tess] [--end_stage stage] [--label label] [--dbname dbname] (files)", 
+	   -exitval => 2 ) if 
+    scalar @ARGV == 0;
+
+pod2usage( -msg => "Required options: --telescope (name) --camera (name)",
+	   -exitval => 3) unless
+    defined $telescope and
+    defined $camera;
+
+my $pxinject = can_run('pxinject') or die "Can't find pxinject\n";
+
+# if workdir is not defined, assign the current path
+# XXX we need to handle relative paths for workdir (not allowed)
+if (! $workdir) {
+    $workdir = File::Spec->rel2abs( "." );
+}
+
+my $num = 0;
+foreach my $file ( @ARGV ) {
+    # check for file existence
+    if (! -e $file) { die "file $file not found\n"; }
+    my $absfile = File::Spec->rel2abs( $file );
+
+    #copy into nebulous
+    my $neb_part=$absfile;
+    $neb_part =~s/.*\///g;
+    my $base = $neb_part;
+    $base =~s/.fits//;
+    
+    print "$workdir/$base/$neb_part\n";
+    
+    
+    `neb-touch $workdir/$base/$neb_part`;
+    my @paths = `neb-locate --path $workdir/$base/$neb_part`;
+    foreach my $path (@paths) {
+	chomp $path;
+	my @cperr = `cp $absfile $path  2>&1`;
+	if ($#cperr > 0) {
+	    print "error copying $absfile $path\n";
+	}
+    }
+    `neb-replicate $workdir/$base/$neb_part --set_copies 2`;
+    my $numofcopies = `neb-stat $workdir/$base/$neb_part | grep available`;
+    $numofcopies =~ s/available instances:  //;
+    if ($numofcopies < 2) {
+	print "problem: less than 2 copies of $workdir/$base/$neb_part\n";
+    }
+
+    inject("$workdir/$base/$neb_part", $workdir, $dbname, $telescope, $camera);
+    $num ++;
+}
+
+print "$num files injected.\n";
+
+sub inject
+{
+    my $absfile = shift;	# absolute path for this file
+    my $workdir  = shift;	# absolute path for output directory
+    my $dbname = shift;		# IPP database to use
+    my $telescope = shift;	# user-specified telescope
+    my $camera = shift;	# user-specified camera
+
+    # XXX provide an option for an alternative extension
+    my ( $vol, $path, $name ) = File::Spec->splitpath( $absfile );
+    my ( $exp_name ) = $name =~ /(.*)\.(fits|fit|fts)(|.gz)/;
+    $exp_name =~ s|\s|_|g;
+
+    my $relfile = $ipprc->convert_filename_relative( $absfile );
+
+    # the telescope, instrument, and exp_name used here are temporary : register replaces them with the true values
+    my $command_exp = "$pxinject -newExp";
+    $command_exp .= " -tmp_exp_name $exp_name";
+    $command_exp .= " -tmp_inst $camera";
+    $command_exp .= " -tmp_telescope $telescope";
+    $command_exp .= " -workdir $workdir";
+    $command_exp .= " -reduction $reduction" if defined $reduction;
+    $command_exp .= " -dvodb $dvo_db"       if defined $dvo_db;
+    $command_exp .= " -tess_id $tess_id"     if defined $tess_id;
+    $command_exp .= " -end_stage $end_stage" if defined $end_stage;
+    $command_exp .= " -label $label"         if defined $label;
+    $command_exp .= " -dbname $dbname"       if defined $dbname;
+
+    my $exp_id = 0;
+    unless ($no_op) {
+	my ( $success_exp, $error_code_exp, $full_buf_exp, $stdout_buf_exp, $stderr_buf_exp ) =
+	    run( command => $command_exp, verbose => 1 );
+	die "Unable to inject $exp_name: $error_code_exp\n" if not $success_exp;
+	
+	my @line = split(/\s+/, $$stdout_buf_exp[0]); # The output line, containing the exposure tag
+	$exp_id = $line[2];	# The exposure tag
+    } else {
+	print "skipping command: $command_exp\n";
+    }
+    
+    # the class_id used here is temporary : register replaces it with the true class_id
+    my $command_imfile = "$pxinject -newImfile";
+    $command_imfile .= " -exp_id $exp_id";
+    $command_imfile .= " -tmp_class_id fpa";
+    $command_imfile .= " -uri '$relfile'";
+    $command_imfile .= " -dbname $dbname" if defined ($dbname);
+    
+    unless ($no_op) {
+	my ( $success_imfile, $error_code_imfile, $full_buf_imfile, $stdout_buf_imfile, $stderr_buf_imfile ) = run( command => $command_imfile, verbose => 1 );
+	die "Unable to inject $exp_name imfile: $error_code_imfile\n" if not $success_imfile;
+    } else {
+	print "skipping command: $command_imfile\n";
+    }
+
+    # the class_id used here is temporary : register replaces it with the true class_id
+    my $command_update = "$pxinject -updatenewExp";
+    $command_update .= " -exp_id $exp_id";
+    $command_update .= " -state run";
+    $command_update .= " -dbname $dbname" if defined ($dbname);
+    
+    unless ($no_op) {
+	my ( $success_update, $error_code_update, $full_buf_update, $stdout_buf_update, $stderr_buf_update ) = run( command => $command_update, verbose => 1 );
+	die "Unable to update $exp_name: $error_code_update\n" if not $success_update;
+    } else {
+	print "skipping command: $command_update\n";
+    }
+
+    return 1;
+}
+
+
+__END__
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/lap_science.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/lap_science.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/lap_science.pl	(revision 33415)
@@ -27,4 +27,5 @@
 my ( $camera, $dbname);
 my ( $lap_id );
+my ( $queue_list );
 my ( $chip_mode, $monitor_mode, $cleanup_mode);
 
@@ -32,5 +33,5 @@
 # Global configuration constants that probably should be read from elsewhere.  
 my $qstack_threshold     = 0.05; # Only make a quickstack if more than 5% of the exposures require it.
-my $minimum_stack_inputs = 6;    # We can avoid magicking stack inputs if we have more than this number.
+my $minimum_stack_inputs = 4;    # We can avoid magicking stack inputs if we have more than this number.
 
 GetOptions(
@@ -44,4 +45,5 @@
     
     'lap_id=s'     => \$lap_id,
+    'queue_list=s' => \$queue_list,
 
     'chip_mode'    => \$chip_mode,
@@ -200,4 +202,122 @@
     return($exposure);
 }
+
+sub remake_this_exposure_by_update {
+    my $exposure = shift;
+
+    my @utctime = gmtime();
+    $utctime[5] += 1900;
+    $utctime[4] += 1;
+
+    my $label = $exposure->{label};
+
+    my $date = sprintf("%4d%02d%02d",$utctime[5],$utctime[4],$utctime[3]);
+    my $workdir_date = sprintf("%4d/%02d/%02d",$utctime[5],$utctime[4],$utctime[3]);
+    my $workdir = "neb://\@HOST\@.0/${dbname}/${label}/${workdir_date}";
+    my $data_group = "${label}.${date}";
+
+    my $chiptool_info_cmd = "chiptool -listrun -exp_id $exposure->{exp_id} -chip_id $exposure->{chip_id} ";
+    $chiptool_info_cmd   .= " -dbname $dbname " if defined $dbname;
+
+    my ($success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $chiptool_info_cmd, verbose => $verbose);
+    unless ($success) {
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	&my_die("Unable to perform chiptool -listrun: $error_code", $exposure->{exp_id}, $data_group);
+    }
+    my $chips = $mdcParser->parse_list(join "", @$stdout_buf) or
+	&my_die("Unable to parse metadata from chiptool -listrun", $exposure->{exp_id}, $data_group);
+    # There should be only one.
+    my $chip = ${ $chips }[0];
+    my $chip_magicDS_id = 0;
+    if ($chip) {
+	$chip_magicDS_id = $chip->{magic_ds_id};
+    }
+    if ($chip_magicDS_id == 0) {
+#	return(&remake_this_exposure($exposure));
+    }
+
+    my $warptool_info_cmd = "warptool -listrun -exp_id $exposure->{exp_id} -chip_id $exposure->{chip_id} ";
+    $warptool_info_cmd   .= " -dbname $dbname " if defined $dbname;
+    ($success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $warptool_info_cmd, verbose => $verbose);
+    unless ($success) {
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	&my_die("Unable to perform warptool -listrun: $error_code", $exposure->{exp_id}, $data_group);
+    }
+    my $warps = $mdcParser->parse_list(join "", @$stdout_buf) or
+	&my_die("Unable to parse metadata from warptool -listrun", $exposure->{exp_id}, $data_group);
+    # There should be only one.
+    my $warp = ${ $warps }[0];
+    my $warp_id = 0;
+    if ($warp) {
+	$warp_id = $warp->{warp_id};
+    }
+    else {
+	$exposure->{data_state} = 'drop';
+	return($exposure);
+    }	
+    if ($warp_id == 0) {
+	# Handle this correctly. Probably want to drop the exposure here.
+	$exposure->{data_state} = 'drop';
+	return($exposure);
+    }
+
+    if (($chip->{state} eq 'goto_cleaned')||
+	($warp->{state} eq 'goto_cleaned')||
+	($chip->{dsRun_state} eq 'goto_cleaned')) {
+	$exposure->{data_state} = 'pending_update';
+	&update_this_exposure($exposure);
+	return($exposure);
+    }
+
+    
+    my $chiptool_update_cmd = "chiptool -setimfiletoupdate -chip_id $exposure->{chip_id} -set_label $label";
+    $chiptool_update_cmd   .= " -dbname $dbname " if defined $dbname;
+    my $magicDS_update_cmd  = "magicdstool -setfiletoupdate -magic_ds_id $chip_magicDS_id -set_label $label";
+    $magicDS_update_cmd    .= " -dbname $dbname " if defined $dbname;
+    my $warptool_update_cmd = "warptool -setskyfiletoupdate -warp_id $warp_id -set_label $label";
+    $warptool_update_cmd   .= " -dbname $dbname " if defined $dbname;
+
+    ($success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $chiptool_update_cmd, verbose => $verbose);
+    unless ($success) {
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	&my_die("Unable to perform chiptool -setimfiletoupdate: $error_code", $exposure->{exp_id}, $data_group);
+    }
+#     if ($chip_magicDS_id != 0) {    
+# 	($success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+# 	    run(command => $magicDS_update_cmd, verbose => $verbose);
+# 	unless ($success) {
+# 	    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+# 	    &my_die("Unable to perform magicdstool -setfiletoupdate: $error_code", $exposure->{exp_id}, $data_group);
+# 	}
+#     }
+
+    ($success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $warptool_update_cmd, verbose => $verbose);
+    unless ($success) {
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	&my_die("Unable to perform warptool -setskyfiletoupdate: $error_code", $exposure->{exp_id}, $data_group);
+    }
+    
+    $exposure->{active} = 1;
+    return($exposure);
+}
+        
+sub determine_if_can_update {
+    my $exposure = shift;
+    
+    if (S64_IS_NOT_NULL($exposure->{chip_id})) {
+	if (($exposure->{chip_state} eq 'cleaned')||
+	    ($exposure->{chip_state} eq 'goto_cleaned')||
+	    ($exposure->{chip_state} eq 'error_cleaned')) {
+	    return(1);
+	}
+    }
+
+    return(0);
+}
+	
 
 # This is the "user level" subroutine.
@@ -240,5 +360,7 @@
 	my $comment = $exposure->{comment};
 
-	# This is a hack to fix old exposures that have no object.
+	my $updateable = determine_if_can_update($exposure);
+
+	# This is a hack to fix old exposures that have no usable object/comment data.
 	unless(defined($comment)) {
 	    $comment = '';
@@ -247,22 +369,41 @@
 	    if ($comment =~ /3pi_/) {
 		$object = $comment;
-		$object =~ s/^.*?(3pi_\d\d_\d\d\d\d).*?$/$1/;
+		$object =~ s/^.*?(3pi_\d\d_\d\d\d\d).*?$/F1 $1/;
 	    }
 	    elsif ($comment =~ / ps1_/) {
 		$object = $comment;
-		$object =~ s/^.*?(ps1_\d\d_\d\d\d\d).*$/$1/;
-	    }
-	    else {
-		$exposure->{data_state} = 'new';
+		$object =~ s/^.*?(ps1_\d\d_\d\d\d\d).*$/F2 $1/;
+	    }
+	    elsif ($comment =~ /ThreePi.*3pi_/) {
+		$object = $comment;
+		$object =~ s/^ThreePi . (\S+? 3pi_\d\d_\d\d\d\d) .*$/F3 $1/;
+	    }           
+	    elsif ($comment =~ /ThreePi.*ps1_/) {
+		$object = $comment;
+		$object =~ s/^ThreePi . (\S+? ps1_\d\d_\d\d\d\d) .*$/F4 $1/;
+	    }           
+	    elsif ($comment =~ /ThreePi /) {
+		$object = $comment;
+		$object =~ s/^ThreePi . (\S+? \d\d\d\d) .*$/F5 $1/;
+	    }           
+	    elsif (($comment =~ /focus/i)||
+		   ($comment =~ /test/i)||
+		   ($comment =~ /hyster/i)||
+		   ($comment =~ /dither/i)||
+		   ($comment =~ /camera/i)
+		){
+		# This is junk that shouldn't exist.
+		$object = 'DROP';
+		$exposure->{data_state} = 'drop';
 		$exposure->{pairwise} = 0;
-		$exposure->{private} = 1;
-		$exposure->{pair_id} = 9223372036854775807;
-		update_this_exposure($exposure);
-		$exposure = remake_this_exposure($exposure); 
-		$counter++;
+		$exposure->{pair_id} =  9223372036854775807;
+		$exposure->{active} = 0;
+		&update_this_exposure($exposure);
+		$counter++; # To ensure everyone else is consistent
 		next;
 	    }
 	}
 
+	# Determine the current state of chipRuns for these exposures, and update/remake as needed.
 	if (S64_IS_NOT_NULL($chip_id)) { # We already have a defined chip_id
 	    if (($pairwise) && !($pair_id)) {
@@ -270,18 +411,25 @@
 		&my_die("Exposure $exp_id for $lap_id is declared pairwise without a defined pair", $lap_id);
 	    }
+	    $exposure->{data_state} = 'exists';
+	    if ($updateable) { # We know about this run, but the data needs to be regenerated.
+		$exposure->{data_state} = 'update';
+		$exposure = remake_this_exposure_by_update($exposure);
+	    }
+	    &update_this_exposure($exposure);
 	    $matching{$object}{$comment} = $exp_id;
 	    $indexing{$exp_id} = $counter;
 	    $counter++; 
-	    next;
 	}
 	else { # We do not already have a chip_id.  
 	    # Make a chipRun, and update the exposure.
+	    $exposure->{data_state} = 'run';
 	    $exposure = remake_this_exposure($exposure); 
-	    
 	    # Save our information for diff pairing.
+	    &update_this_exposure($exposure);
 	    $matching{$object}{$comment} = $exp_id;
 	    $indexing{$exp_id} = $counter;
 	    $counter++;
 	}
+
 	if ($verbose) {
 	    print "ZZ: $exp_id $object $comment $matching{$object}{$comment}\n";
@@ -296,5 +444,5 @@
 	    print "$object $comment $matching{$object}{$comment} $indexing{$matching{$object}{$comment}} $exp_ids_to_diff[-1]\n";
 	}
-	@exp_ids_to_diff = sort { $indexing{$a} <=> $indexing{$b} } @exp_ids_to_diff;
+	@exp_ids_to_diff = sort { $indexing{$a} <=> $indexing{$b} } @exp_ids_to_diff; # This is effectively a sort by dateobs.
 	
 	if (( $#exp_ids_to_diff + 1) % 2 != 0) { # We have an odd number of exposures, even after comment filtering
@@ -309,14 +457,17 @@
 	    my $exp_B = ${ $exposures }[$indexing{$exp_id_B}];
 	    print "$exp_A $exp_B $exp_id_A $exp_id_B $indexing{$exp_id_A} $indexing{$exp_id_B}\n";
-	    $exp_A->{pairwise} = 1;
-	    $exp_A->{private} = 0;
-	    $exp_A->{pair_id} = $exp_B->{chip_id};
-	    
-	    $exp_B->{pairwise} = 1;
-	    $exp_B->{private} = 0;
-	    $exp_B->{pair_id} = $exp_A->{chip_id};
-	    
-	    if ($verbose) {
-		print "LAP_DIFFS: $object: $exp_A->{exp_id} and $exp_B->{exp_id} are a pair\n";
+	    if ($exp_A->{diff_id} == $exp_B->{diff_id}) {
+		$exp_A->{pairwise} = 1;
+		$exp_A->{pair_id} = $exp_B->{chip_id};
+		
+		$exp_B->{pairwise} = 1;
+		$exp_B->{pair_id} = $exp_A->{chip_id};
+		if ($verbose) {
+		    print "LAP_DIFFS: $object: $exp_A->{exp_id} and $exp_B->{exp_id} are a pair\n";
+		}
+	    }
+	    else {
+		$exp_A->{pairwise} = 0;
+		$exp_B->{pairwise} = 0;
 	    }
 	}
@@ -331,8 +482,5 @@
 	    $exposure->{pairwise} = 0; # We marked it for pairwise diffs, but didn't match it. Probably an error.
 	}
-	if (!($exposure->{pairwise})) {
-	    $exposure->{private} = 1; # If this isn't being pairwise diffed, it needs to be private
-	}
-
+	
 	update_this_exposure($exposure);
     }
@@ -403,4 +551,8 @@
 	    $complete_fstacks++;
 	}
+	elsif (($stack->{final_state})&&
+	       ($stack->{final_state} eq 'drop')) {
+	    $complete_fstacks++;
+	}
 	elsif (($stack->{final_state})&&($stack->{final_state} eq 'new')&&
 	       ($stack->{final_fault} >= 4)&&($stack->{final_fault} != 32767)) {
@@ -457,6 +609,6 @@
     # Things I want to know before I'm through
 #     my $needs_qstack = 0;
-    my $needs_something_remade = 0;
-    my $needs_something_private = 0;
+    my $needs_something_remade = 0;  # I never need something remade, because we're committed to one-exp=one-chip
+    my $needs_something_private = 0; # I never need something private, as that is calculated by the difference engine.
 #     my $can_qstack = 0;
      my $have_diff = 0;
@@ -468,5 +620,4 @@
     my $are_warped = 0;
     my $are_magicked = 0;
-
     foreach my $exposure (@$exposures) {
 	$total_exposures++;
@@ -480,5 +631,4 @@
 	    else { # We claimed to be pairwise, but do not have a valid pair_id.
 		$exposure->{pairwise} = 0;
-		$exposure->{private} = 1;
 		&update_this_exposure($exposure);
 	    }
@@ -487,22 +637,25 @@
 	if  ($exposure->{data_state} eq 'drop') { # This exposure is impossible, so fudge the counts so we get through.
 	    $are_warped++;
-#	    $can_qstack ++;
 	    $can_diff ++;
 	    $have_diff ++;
 	    $are_magicked ++;
-#	    $can_fstack ++;	    
+	    next;
+	}
+
+	if ($exposure->{data_state} eq 'pending_update') {
+	    $exposure->{data_state} = 'update';
+	    $exposure = &remake_this_exposure_by_update($exposure);
+	    &update_this_exposure($exposure);
 	    next;
 	}
 	
 	if ($exposure->{private}) { # I've declared this exposure private to this lapRun.
-#	    $needs_qstack++;
 	    push @lonely_exposures, $exposure;
-#	    $lonely_exposures++;
 	}
 	
-	if ($exposure->{needs_remade}) { # This does the check that private = false for other lapRun
-	    $needs_something_remade = 1;
-	    $exposure = remake_this_exposure($exposure);
-	}
+# 	if ($exposure->{needs_remade}) { # This does the check that private = false for other lapRun
+# 	    $needs_something_remade = 1;
+# 	    $exposure = remake_this_exposure($exposure);
+# 	}
 	# Do quality checks here
 	my $is_bad_quality = 0;
@@ -536,18 +689,14 @@
 		    ($exposure->{diffRun_state} eq 'full')) {
 		$are_warped++;
-#		$needs_qstack++;
-		$needs_something_private = 1;
 		if ($companion) {
-		    $companion->{private} = 1;
 		    $companion->{pairwise} = 0;
 		    &update_this_exposure($companion);
-		    push @lonely_exposures, $companion;
-#		    $lonely_exposures++;
+#		    push @lonely_exposures, $companion;
 		}
-		$exposure->{private} = 1;
 		$exposure->{pairwise} = 0;
 	    }
 	    $exposure->{data_state} = 'drop';
 	    &update_this_exposure($exposure);
+	    next;
 	}
 	
@@ -558,12 +707,28 @@
 #	    $can_qstack ++;
 	    $can_diff ++;
-	}
-	if (($exposure->{magicked}&&
-	     &S64_IS_NOT_NULL($exposure->{magicked}))) {  # This exposure has been magicked, so it is through with diff.
+	    $exposure->{data_state} = 'to_diff';
+	}
+	if (($exposure->{diff_id})&&
+	    (&S64_IS_NOT_NULL($exposure->{diff_id}))&&
+	    (($exposure->{diffRun_state} eq 'full')||
+	     ($exposure->{diffRun_state} eq 'error_cleaned')||
+	     ($exposure->{diffRun_state} eq 'cleaned'))
+	    ) {
 	    $are_magicked++;
-#	    $can_fstack ++;
-	}
-	if (($exposure->{diff_id})&&(&S64_IS_NOT_NULL($exposure->{diff_id}))) {
 	    $have_diff ++;
+#	    $exposure->{data_state} = 'to_magic';
+	    $exposure->{data_state} = 'full';
+	}
+	# We no longer need to care about magic
+# 	if (($exposure->{magicked})&&
+# 	    ($exposure->{warpRun_state})&&
+# 	    ($exposure->{warpRun_state} eq 'full')&&
+# 	    (&S64_IS_NOT_NULL($exposure->{magicked}))) { # This exposure has been magicked, so it is through with diff.
+# 	    $are_magicked++;
+# 	    $exposure->{data_state} = 'full';
+# #	    $can_fstack ++;
+# 	}
+	unless ($debug) {
+	    &update_this_exposure($exposure);
 	}
     }
@@ -575,13 +740,10 @@
     print "STATUS: HAVE_FSTACK:     $have_fstack\n";
 
-#    print "STATUS: NEEDS_QSTACK:    $needs_qstack\n";
     print "STATUS: NEEDS_REMADE:    $needs_something_remade\n";
     print "STATUS: NEEDS PRIVATIZE: $needs_something_private\n";
     print "STATUS: ARE WARPED:      $are_warped\n";
     print "STATUS: ARE MAGICKED:    $are_magicked\n";
-#    print "STATUS: CAN_QSTACK:      $can_qstack\n";
     print "STATUS: CAN_DIFF:        $can_diff\n";
     print "STATUS: HAVE_DIFF:       $have_diff\n";
-#    print "STATUS: CAN_FSTACK:      $can_fstack\n";
 
     print "STATUS: TOTAL_EXPOSURES: $total_exposures\n";
@@ -595,5 +757,8 @@
 	if ($defined_fstack == 0) {        # We have not yet made any stacks.
 	    print "STATUS: We can attempt to queue deep final stacks.\n";
-	    my $Nstacks_made = &queue_muggle_finalstack($exposures);
+	    my $Nstacks_made;
+	    unless ($debug) {
+		$Nstacks_made = &queue_muggle_finalstack($exposures);
+	    }
 	    print "STATUS: Made $Nstacks_made final stacks.\n";
 	    exit($Nstacks_made);
@@ -603,12 +768,15 @@
 	    if ($have_fstack == $defined_fstack) { # We have made all the stacks we have asked for (so far)
 		if ($are_magicked < $total_exposures) { # But we have not yet made diffs
+#		if ($have_diff < $total_exposures) { # We have not yet made diffs
 		    if (($can_diff == $total_exposures)&& # And we have exposures that should be diffed.
 			($have_diff < $can_diff)) {       # and we haven't made them all yet, either.
 			print "STATUS: Dropping unpaired exposures and making pairwise diffs.\n";
-			foreach my $exposure (@lonely_exposures) {
-			    $exposure->{data_state} = 'drop';
-			    &update_this_exposure($exposure);
+# 			foreach my $exposure (@lonely_exposures) {
+# 			    $exposure->{data_state} = 'drop';
+# 			    &update_this_exposure($exposure);
+# 			}
+			unless ($debug) {
+			    &queue_diffs($exposures);
 			}
-			&queue_diffs($exposures);
 			exit(0);
 		    }
@@ -621,24 +789,30 @@
 		}
 		elsif ($are_magicked == $total_exposures) { # We have made all of the diffs, and propagated through magic
-		    print "STATUS: We can create all remaining stacks possible.\n";
-		    my $Nstacks_made = &queue_magic_finalstack($exposures);
-		    if ($Nstacks_made == 0) { # We have made all the stacks that we will ever make.
+		    print "STATUS: We would create all remaining stacks possible, but no longer require magic.\n";
+# 		    my $Nstacks_made = -1;
+# 		    unless ($debug) {
+# 			$Nstacks_made = &queue_muggle_finalstack($exposures);
+# 		    }
+# 		    if ($Nstacks_made == 0) { # We have made all the stacks that we will ever make.
 			print "STATUS: No more stacks generated. Deactivating exposures for this complete lapRun.\n";
-			&deactivate_exposures($exposures);
-			$command = "$laptool -updaterun -lap_id $lap_id";
-			$command .= " -dbname $dbname " if defined $dbname;
-			$command .= " -set_state full ";
-			my ($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);
-			    &my_die("Unable to perform laptool -updaterun: $error_code", $lap_id);
-			}
+			unless ($debug) {
+			    &deactivate_exposures($exposures);
+			
+			    $command = "$laptool -updaterun -lap_id $lap_id";
+			    $command .= " -dbname $dbname " if defined $dbname;
+			    $command .= " -set_state full ";
+			    my ($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);
+				&my_die("Unable to perform laptool -updaterun: $error_code", $lap_id);
+			    }
+# 			}
 			exit(0);
 		    }
-		    else { 
-			print "STATUS: $Nstacks_made stacks made.\n";
-			exit(0);
-		    }
+# 		    else { 
+# 			print "STATUS: $Nstacks_made stacks made.\n";
+# 			exit(0);
+# 		    }
 		}
 		else { # What?
@@ -842,5 +1016,5 @@
     foreach $exposure (@$exposures) {
 	if (($exposure->{data_state} ne 'drop')&&
-	    (S64_IS_NOT_NULL($exposure->{magicked}))&&
+#	    (S64_IS_NOT_NULL($exposure->{magicked}))&&
 	    (S64_IS_NOT_NULL($exposure->{warp_id}))) {
 	    $warps .= " -warp_id $exposure->{warp_id} ";
@@ -924,5 +1098,5 @@
 	    next;
 	}
-	if ($exposure->{diff_id}&&S64_IS_NOT_NULL($exposure->{diff_id})) { # Not sure how this would happen, but still. ## This happens when we inherit a complete exposure.
+	if ($exposure->{diff_id}&&S64_IS_NOT_NULL($exposure->{diff_id})) { # This happens when we inherit a complete exposure.
 	    next;
 	}
@@ -964,9 +1138,21 @@
 	}
 	else { # warp-qstack
-	    next; # We no longer wish to make these, and should never get here.
-	    $command .= '-pretend';
-# 	    $command .= " -definewarpstack -set_reduction NOCONVDIFF -available -good_frac 0.2 ";
-# 	    $command .= " -warp_id $exposure->{warp_id} -stack_label ${label}.quick ";
-# 	    $already_queued{$exposure->{warp_id}} = 1;
+	    # We need to decide if we can make a warp-stack diff:
+	    if (&can_warp_stack_diff_be_made($exposure)) {
+#	    next; 
+#	    $command .= '-pretend';
+		$command .= " -definewarpstack -available -good_frac 0.2 ";
+		$command .= " -warp_id $exposure->{warp_id} -stack_label ${label} ";
+		$already_queued{$exposure->{warp_id}} = 1;
+		$exposure->{private} = 0;
+		&update_this_exposure($exposure);
+	    }
+	    else {
+		$exposure->{private} = 1;
+		$exposure->{data_state} = 'drop';
+		$already_queued{$exposure->{warp_id}} = 1;
+		&update_this_exposure($exposure);
+		next;
+	    }
 	}
 	
@@ -1006,10 +1192,52 @@
 }
 
+sub can_warp_stack_diff_be_made {
+    my $exposure = shift;
+    my $lap_id   = $exposure->{lap_id};
+    my $warp_id  = $exposure->{warp_id};
+
+    my $command = "$laptool -diffcheck -lap_id $lap_id -warp_id $warp_id";
+    $command .= " -dbname $dbname " if defined $dbname;
+
+    my ($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);
+	&my_die("Unable to perform laptool -diffcheck: $error_code", $lap_id, $warp_id);
+    }
+    if (@$stdout_buf == 0) {
+	return(0);
+    }
+    my $skycells = $mdcParser->parse_list(join "", @$stdout_buf) or
+	&my_die("Unable to parse metadata from laptool -diffcheck", $lap_id, $warp_id);
+    foreach my $skycell (@$skycells) {
+	unless (defined($skycell->{stack_state})) {
+	    return(0);
+	}
+	if ($skycell->{stack_state} ne 'full') {
+	    return(0);
+	}
+	if ($skycell->{warp_state} ne 'full') {
+	    return(0);
+	}
+	unless (S64_IS_NOT_NULL($skycell->{stack_id})) {
+	    return(0);
+	}
+    }
+    return(1);
+}
+
+
 # Deactivate all exposures in this run.
 sub deactivate_exposures {
     my $exposures = shift;
     foreach my $exposure (@$exposures) {
-	$exposure->{active} = 0;
-	update_this_exposure($exposure);
+	if ($exposure->{private} == 1) {  # This can probably be relaxed since we can update an undestreaked warp.
+	    $exposure->{active} = 1;
+	}
+	else {
+	    $exposure->{active} = 0;
+	}
+	update_this_exposure($exposure);	
     }
 }
@@ -1022,4 +1250,54 @@
 sub cleanup_mode {
     my $lap_id = shift;
+
+    if (defined($queue_list)) {
+	my $successful = 1;
+	open(Q,$queue_list) or ($successful = 0);
+	if ($successful == 1) {
+	    while(<Q>) {
+		chomp;
+		my $cmd = $_;
+		my ($projection_cell,$seq_id,$filter,$label);
+		$projection_cell = $cmd;
+		$projection_cell =~ s/.*?-projection_cell (skycell.\w+?) .*/$1/;
+		$seq_id = $cmd;
+		$seq_id =~ s/.*-seq_id (\d+?) .*/$1/;
+		$filter = $cmd;
+		$filter =~ s/.*-filter (\w\.00000) .*/$1/;
+		if ($filter =~ /\s+?/) {
+		    $filter =~ s/.*-filter (\w\.00000)$/$1/;
+		}
+		$label = $cmd;
+		$label =~ s/.*-label (.+?) .*/$1/;
+		
+		my $response;
+		chomp($response = 
+		      `laptool -dbname gpc1 -listrun -projection_cell $projection_cell -seq_id $seq_id -filter $filter -label $label -simple`);
+		my $state = (split /\s+/, $response)[5];
+		
+		unless (defined($state)) {
+		    if ($verbose) {
+			print "Queuing: $cmd\n";
+		    }
+		    system($cmd);
+		    
+		    my $i = 0;
+		    do {
+			sleep(5);
+			chomp($response = 
+			      `laptool -dbname gpc1 -listrun -projection_cell $projection_cell -seq_id $seq_id -filter $filter -label $label -simple`);
+			$state = (split /\s+/, $response)[5];
+			$i++;
+			unless(defined($state)) {
+			    $successful = 0;
+			}
+		    } while (($state ne 'run')&&($i < 20)&&($successful));
+		    last;
+		}
+	    }
+	    close(Q);
+	}
+    }
+
     my $command = "$laptool -inactiveexp -lap_id $lap_id ";
     $command .= " -dbname $dbname " if defined $dbname;
@@ -1033,4 +1311,14 @@
     if (@$stdout_buf == 0) {
 	# Nothing to do.
+	$command = "$laptool -updaterun -lap_id $lap_id";
+	$command .= " -dbname $dbname " if defined $dbname;
+	$command .= " -set_state done ";
+	($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);
+	    &my_die("Unable to perform laptool -updaterun: $error_code", $lap_id);
+	}
+
 	return(0);
     }
@@ -1043,7 +1331,9 @@
 	'warptool -dbname @DBNAME@ -updaterun -set_state goto_cleaned -set_label goto_cleaned -label @LABEL@ -warp_id @WARP_ID@',
 	'difftool -dbname @DBNAME@ -updaterun -set_state goto_cleaned -set_label goto_cleaned -label @LABEL@ -diff_id @DIFF_ID@',
+	'magictool -dbname @DBNAME@ -updaterun -set_state full -set_label @LABEL@.old -magic_id @MAGIC_ID@',
 	'magicdstool -dbname @DBNAME@ -updaterun -set_state goto_cleaned -state full -set_label goto_cleaned -stage chip -stage_id @CHIP_ID@',
 	'magicdstool -dbname @DBNAME@ -updaterun -set_state goto_cleaned -state full -set_label goto_cleaned -stage warp -stage_id @WARP_ID@',
 	'magicdstool -dbname @DBNAME@ -updaterun -set_state goto_cleaned -state full -set_label goto_cleaned -stage diff -stage_id @DIFF_ID@');
+    
     foreach my $exposure (@$exposures) {
 	if ($exposure->{is_in_use}) {
@@ -1077,4 +1367,13 @@
 		}
 	    }
+	    if ($command =~ /\@MAGIC_ID\@/) {
+		if ((S64_IS_NOT_NULL($exposure->{magicked}))&&
+		     ($exposure->{magicked} > 0)) {
+		    $command =~ s/\@MAGIC_ID\@/$exposure->{magicked}/;
+		}
+		else {
+		    next;
+		}
+	    }
 	    $command =~ s/\@LABEL\@/$exposure->{label}/;
 	    
@@ -1139,5 +1438,5 @@
 	&my_die("Unable to perform laptool -updaterun: $error_code", $lap_id);
     }
-
+    return(0);
 }
     
@@ -1210,10 +1509,12 @@
 	$command .= " -set_data_state $exposure->{data_state} ";
     }
-
-    my ($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);
-	&my_die("Unable to perform laptool -updateexp: $error_code", $exposure->{lap_id}, $exposure->{proj_cell});
+    unless ($do_nothing) {
+	my ($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);
+	    &my_die("Unable to perform laptool -updateexp: $error_code", $exposure->{lap_id}, $exposure->{proj_cell});
+	}
     }
 }    
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/magic_destreak.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/magic_destreak.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/magic_destreak.pl	(revision 33415)
@@ -35,4 +35,5 @@
 my $ppConfigDump = can_run('ppConfigDump') or (warn "Can't find ppConfigDump" and $missing_tools = 1);
 my $ppStatsFromMetadata = can_run('ppStatsFromMetadata') or (warn "Can't find ppStatsFromMetadata" and $missing_tools = 1);
+my $dvoImageOverlaps = can_run('dvoImageOverlaps') or (warn "Can't find dvoImageOverlaps" and $missing_tools = 1);
 if ($missing_tools) {
     warn("Can't find required tools.");
@@ -45,4 +46,5 @@
 my ($outroot, $recoveryroot, $magicked);
 my ($replace, $release);
+my ($diff_tess_id, $mismatched_tess);
 my ($dbname, $save_temps, $verbose, $no_update, $no_op, $logfile);
 
@@ -60,4 +62,6 @@
            'stage_id=s'     => \$stage_id,   # exp_id, chip_id, warp_id, or diff_id
            'component=s'    => \$component,  # the class_id or skycell_id
+           'mismatched_tess'=> \$mismatched_tess, # true if tess_id of input does not match tess_id of the diff used for magic
+           'diff_tess_id=s' => \$diff_tess_id, # tess_id of diffRun used to compute the streaks
            'uri=s'          => \$uri,        # uri of the input image
            'path_base=s'    => \$path_base,  # path_base of the input
@@ -95,5 +99,10 @@
 
 my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $magic_ds_id, $component, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
-$ipprc->redirect_output($logfile) or my_die( "Unable to redirect output", $magic_ds_id, $component, $PS_EXIT_SYS_ERROR ) if $logfile;
+
+if ($logfile) {
+    $ipprc->redirect_to_logfile($logfile) or my_die( "Unable to redirect output", $magic_ds_id, $component, $PS_EXIT_SYS_ERROR );
+    print "\n\n";
+    print "Continuing script $0 on $host at $date\n\n";
+}
 
 $cam_reduction = 'DEFAULT' if !$cam_reduction or ($cam_reduction eq 'NULL');
@@ -106,8 +115,8 @@
 if (($stage eq "raw") or ($stage eq "chip") or $stage eq "chip_bg") {
     $class_id = $component;
-    $skycell_args = " -class_id $component";
+    $skycell_args = " -class_id";
 } elsif ($stage eq "warp" or $stage eq "warp_bg") {
     $skycell_id = $component;
-    $skycell_args = " -skycell_id $component";
+    $skycell_args = " -skycell_id";
 } elsif ($stage eq "diff") {
     $skycell_id = $component;
@@ -176,5 +185,7 @@
 }
 
-my $statsFile = "$outroot/$exp_id.mds.$magic_ds_id.$stage_id.$component.stats";
+# XXX: create a DESTREAK file rule. For now just steal one that will work
+my $error;
+my $statsFile = $ipprc->prepare_output("PSASTRO.STATS", "$outroot/$exp_id.mds.$magic_ds_id.$stage_id.$component", undef, 1, \$error) or &my_die("failed to prepare output for stats", $magic_ds_id, $component, $error);
 
 my $backup_path_base;
@@ -215,41 +226,65 @@
     my ($sfh, $skycell_list);
     if ($skycell_args) {
-        my $command = "$magicdstool -magic_ds_id $magic_ds_id -getskycells $skycell_args";
-        $command .= " -dbname $dbname" if defined $dbname;
-        my ( $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);
-            &my_die("Unable to perform magicdstool -diffskyfile $skycell_args: $error_code", $magic_ds_id, $component, $error_code);
-        }
-
-        my $getskycells_output = join "", @$stdout_buf;
-        if ($getskycells_output) {
-            my $metadata = $mdcParser->parse($getskycells_output) or
-                &my_die("Unable to parse metadata config doc", $magic_ds_id, $component, $PS_EXIT_PROG_ERROR);
-
-            my $skycells = parse_md_list($metadata) or
-                    &my_die("Unable to parse metadata list", $magic_ds_id, $component, $PS_EXIT_PROG_ERROR);
-
-            ($sfh, $skycell_list) = tempfile( "/tmp/skycell_list.XXXX", UNLINK => !$save_temps);
-
-            foreach my $skycell (@$skycells) {
-                my $skycell_uri;
-                if ($skycell->{data_state} eq "full" and $skycell->{quality} = 0) {
-                    $skycell_uri = $ipprc->filename("PPSUB.OUTPUT", $skycell->{path_base});
-                } else {
-                    # diff run must have been cleaned up, need to create this skycell file on the fly
-                    if (!defined $temp_dir ) {
-                        $temp_dir = tempdir( CLEANUP => !$save_temps);
+        my $command = "$magicdstool -magic_ds_id $magic_ds_id -getskycells";
+        $command .= " $skycell_args";
+        $command .= " -dbname $dbname" if $dbname;
+        my @diff_components;
+        if ($mismatched_tess) {
+            # tessellation for this skycell does not match the tessellation used for the magic analysis
+            # used to calculate the streaks file
+            @diff_components = get_overlaps($path_base, $component, $diff_tess_id);
+        } else {
+            @diff_components = ($component);
+        }
+
+        # hash of uris by skycell.
+        my %diff_skycells;
+        foreach my $component (@diff_components) {
+            my $this_command = "$command $component";
+            my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                run(command => $this_command, verbose => $verbose);
+            unless ($success) {
+                $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+                &my_die("Unable to perform magicdstool -diffskyfile $skycell_args: $error_code", $magic_ds_id, $component, $error_code);
+            }
+
+            my $getskycells_output = join "", @$stdout_buf;
+            if ($getskycells_output) {
+                my $metadata = $mdcParser->parse($getskycells_output) or
+                    &my_die("Unable to parse metadata config doc", $magic_ds_id, $component, $PS_EXIT_PROG_ERROR);
+
+                my $skycells = parse_md_list($metadata) or
+                        &my_die("Unable to parse metadata list", $magic_ds_id, $component, $PS_EXIT_PROG_ERROR);
+
+                foreach my $skycell (@$skycells) {
+                    my $skycell_uri;
+                    $skycell->{quality} = undef;
+		    # XXXXXXXX remake all skycells to avoid dead instances (ipp064)
+                    if (0 and $skycell->{data_state} eq "full" and $skycell->{quality} == 0) {
+                        $skycell_uri = $ipprc->filename("PPSUB.OUTPUT", $skycell->{path_base});
+			my $skycell_id = $skycell->{skycell_id};
+                        $diff_skycells{$skycell_id} = $skycell_uri;
+                    } else {
+                        # diff run must have been cleaned up, need to create this skycell file on the fly
+                        if (!defined $temp_dir ) {
+                            $temp_dir = tempdir( CLEANUP => !$save_temps);
+                        }
+                        my $skycell_id = $skycell->{skycell_id};
+                        if (!$diff_skycells{$skycell_id}) {
+                            $skycell_uri = "$temp_dir/$skycell_id";
+                            $ipprc->skycell_file($skycell->{tess_id}, $skycell_id, $skycell_uri, $verbose) or
+                                &my_die("failed to create skycell file for $skycell_id", $magic_ds_id, $component, $PS_EXIT_PROG_ERROR);
+                        }
+                        $diff_skycells{$skycell_id} = $skycell_uri;
                     }
-                    my $skycell_id = $skycell->{skycell_id};
-                    $skycell_uri = "$temp_dir/$skycell_id";
-                    $ipprc->skycell_file($skycell->{tess_id}, $skycell_id, $skycell_uri, $verbose) or
-                        &my_die("failed to create skycell file for $skycell_id", $magic_ds_id, $component, $PS_EXIT_PROG_ERROR);
                 }
-                print $sfh "$skycell_uri\n";
-            }
-            close $sfh
-        }
+            }
+        }
+        # write the skycell list file
+        ($sfh, $skycell_list) = tempfile( "/tmp/skycell_list.XXXX", UNLINK => !$save_temps);
+        foreach my $skycell_id (keys %diff_skycells) {
+            print $sfh $diff_skycells{$skycell_id} . "\n";
+        }
+        close $sfh
     }
 
@@ -360,10 +395,5 @@
         $command .= " -chip_mask $ch_mask" if defined $ch_mask;
         $command .= " -weight $weight" if defined $weight;
-	if ((defined($sources))&&($ipprc->file_exists($sources))) {
-	    $command .= " -sources $sources" if defined $sources;
-	}
-	else {
-	    print "Did not add sources because they do not appear to exist. This may be an error.\n";
-	}
+        $command .= " -sources $sources" if defined $sources;
         $command .= " -skycelllist $skycell_list" if defined $skycell_list;
         $command .= " -replace" if $replace;
@@ -475,5 +505,5 @@
 
 my $statsFlags;
-if ($stage ne "camera") {
+if (!$no_op and $stage ne "camera") {
     file_check($statsFile);
     {
@@ -595,4 +625,35 @@
 }
 
+            # @diff_components = get_overlaps($path_base, $component, $tess_id, $diff_tess_id);
+sub get_overlaps {
+    my $path_base = shift;
+    my $component = shift;
+    my $diff_tess_id = shift;
+
+    my $tess_dir = $ipprc->tessellation_catdir( $diff_tess_id ); 
+    my $catdir = $ipprc->convert_filename_absolute($tess_dir);
+
+    my $cmf = $ipprc->file_resolve("$path_base.cmf");
+    &my_die("Unable to resolve $path_base.$cmf", $magic_ds_id, $component, $PS_EXIT_SYS_ERROR) unless $cmf;
+
+    my $command = "$dvoImageOverlaps -D CATDIR $catdir -accept-astrom $cmf";
+    my ( $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);
+        &my_die("Unable to perform dvoImageOverlaps $error_code", $magic_ds_id, $component,
+            $error_code);
+    }
+    my @list;
+    my $output = join "\n", @$stdout_buf;
+    if ($output) {
+        foreach my $line (split "\n", $output) {
+            my (undef, undef, $skycell_id) = split " ", $line;
+            push @list, $skycell_id;
+        }
+    }
+    return @list
+}
+
 
 sub my_die
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/magic_destreak_cleanup.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/magic_destreak_cleanup.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/magic_destreak_cleanup.pl	(revision 33415)
@@ -61,5 +61,5 @@
 
 my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $magic_ds_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
-$ipprc->redirect_output($logfile) or my_die( "Unable to redirect output", $magic_ds_id, $PS_EXIT_SYS_ERROR ) if $logfile;
+$ipprc->redirect_to_logfile($logfile) or my_die( "Unable to redirect output", $magic_ds_id, $PS_EXIT_SYS_ERROR ) if $logfile;
 
 
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/magic_destreak_revert.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/magic_destreak_revert.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/magic_destreak_revert.pl	(revision 33415)
@@ -9,6 +9,6 @@
 my $host = hostname();
 my $date = `date`;
-print "\n\n";
-print "Starting script $0 on $host at $date\n\n";
+print STDERR "\n\n";
+print STDERR "Starting script $0 on $host at $date\n\n";
 
 use vars qw( $VERSION );
@@ -86,5 +86,5 @@
 my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $magic_ds_id, $component, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
 
-$ipprc->redirect_output($logfile) or my_die( "Unable to redirect output", $magic_ds_id, $component, $PS_EXIT_SYS_ERROR ) if $logfile;
+$ipprc->redirect_to_logfile($logfile) or my_die( "Unable to redirect output", $magic_ds_id, $component, $PS_EXIT_SYS_ERROR ) if $logfile;
 
 &my_die("bytes and md5sum are is required for raw stage stage", $magic_ds_id, $component, $PS_EXIT_CONFIG_ERROR) if ($replace and $stage eq 'raw' and (!$bytes or !$md5sum));
@@ -413,13 +413,26 @@
     if ($backup_is_not_destreaked) {
         if ($original_is_not_destreaked) {
-            # this is unexpected result. throw an error so this can be checked manually
-            print STDERR "both files appear to not be destreaked\n";
+            print STDERR "\nBoth files appear to not be destreaked.\n";
             print STDERR "original: $original\n";
             print STDERR "backup:   $backup\n";
             my $o_basename = basename($o_path);
             my $b_basename = basename($b_path);
-            if (!($o_basename =~ /SR_/) and ($b_basename =~ /SR_/)) {
-                print " basenames are as expected it is safe to revert\n";
+            # if original file does not have SR_ in basename and the backup does then the 
+            # files are in their original state. Just delete back up file
+            # otherwise go to failed_revert for manual inspection.
+	    # XXX: We probably can get by with just checking the basename of the original file
+            if (!($o_basename =~ /SR_/) ) {
+            # if (!($o_basename =~ /SR_/) and ($b_basename =~ /SR_/)) {
+                print STDERR " basenames are as expected it is safe to delete backup file\n";
+                if (! $ipprc->file_delete($backup, $force)) {
+                    print STDERR "failed to delete $backup\n";
+                    return 0;
+                }
+                return 1;
             } else {
+                # this is an unexpected result. throw an error so this can be checked manually
+                print STDERR " basenames for files are as NOT as expected unsafe to revert\n";
+                print STDERR " o_basename: $o_basename\n";
+                print STDERR " b_basename: $b_basename\n";
                 return 0;
             }
@@ -470,9 +483,7 @@
     foreach my $file (@_) {
         next if !$file;
-        if ($ipprc->file_exists($file)) {
-            if (!$ipprc->file_delete($file)) {
-                &my_die("failed to delete recovery file $file", $magic_ds_id, $component, $PS_EXIT_UNKNOWN_ERROR);
-            }
-        }
+	# don't care if this fails or not (it will fail if storage
+	# object doesn't exist
+        $ipprc->kill_file($file);
     }
 }
@@ -500,5 +511,9 @@
     my $command = "$magicdstool -updaterun";
     $command   .= " -magic_ds_id $magic_ds_id";
-    $command   .= " -set_state failed_revert";
+    if ($run_state ne 'update') {
+        $command .= " -set_state failed_revert";
+    } else {
+        $command .= " -set_state failed_revert_ud";
+    }
     $command   .= " -dbname $dbname" if defined $dbname;
 
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/mergedvodb_copy.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/mergedvodb_copy.pl	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/mergedvodb_copy.pl	(revision 33415)
@@ -0,0 +1,189 @@
+#!/usr/bin/env perl
+
+#This is essentially the same as minidvodb_copy.pl - the tables are essentially identical, and the task (to rsync a copy) is essentially identical.  A new script was created for rsyncing the merges in case they should diverge in the future.
+
+use warnings;
+use strict;
+use Carp;
+ 
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use DateTime;
+my $mjd_start = DateTime->now->mjd;   
+
+my $dtime;
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config 1.01 qw( :standard );
+use File::Temp qw( tempfile );
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+# Look for programs we need
+my $missing_tools;
+my $rsync = can_run('rsync') or (warn "Can't find rsync" and $missing_tools = 1);
+my $mergedvodbtool = can_run('mergedvodbtool') or (warn "Can't find mergedvodbtool" and $missing_tools = 1);
+my $ssh = can_run('ssh') or (warn "Can't find ssh" and $missing_tools = 1);
+my $df = can_run('df') or (warn "Can't find df" and $missing_tools = 1);
+
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+
+
+my ( $mergedvodbcopy_id, $merge_id, $mergedvodbrun_path, $mergedvodb_rsync_path, $destination_host, $dbname,$verbose, $logfile, $no_op, $redirect, $save_temps);
+GetOptions(
+    'mergedvodbcopy_id|w=s'     => \$mergedvodbcopy_id, #minidvodb database
+    'merge_id|w=s'  => \$mergedvodb_id, #minidvodb_id
+    'mergedvodbrun_path|w=s'  => \$mergedvodbrun_path, #minidvodb_id
+    'mergedvodb_rsync_path|w=s' => \$mergedvodb_rsync_path,
+    'destination_host|w=s' => \$destination_host, 
+    'dbname|d=s'        => \$dbname, # Database name
+    'verbose'           => \$verbose,   # Print to stdout
+    'no-op'             => \$no_op, # Don't do any operations?
+    'logfile=s'         => \$logfile,
+    'save-temps'        => \$save_temps, # Save temporary files?
+    ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage(
+          -msg => "Required options: --mergedvodbcopy_id --mergedvodb_rsync_path --mergedvodbrun_path --destination_host",
+          -exitval => 3,
+          ) unless
+    defined $mergedvodbcopy_id and
+    defined $mergedvodb_rsync_path and
+    defined $mergedvodbrun_path and
+    defined $destination_host;
+
+
+
+my $ipprc = PS::IPP::Config->new();
+
+my @df;
+
+
+if ($logfile) {
+    $ipprc->redirect_output($logfile) or my_die( "Unable to redirect output", $mergedvodbcopy_id, $PS_EXIT_SYS_ERROR );
+    print "\n\n";
+    print "Starting script $0 on $host\n\n";
+    print "COMMAND IS: @ARGV\n\n";
+}
+
+
+{ #can we rsync?
+
+    my $sizes = `du -sk $mergedvodbrun_path`;
+    my $total = 0;
+    for(split /[\r\n]+/,$sizes) # split on one or more newline characters
+    { 
+	my($number,$file) = split /\t/,$_,2; # split on tab ($file not used here)
+	$total += $number; 
+    }
+#print 'Total: '.$total;
+{    
+    print "Checking available diskspace on $destination_host\n\n";
+
+    my $command = "$ssh $destination_host df $mergedvodb_rsync_path";
+    print "$command\n\n";
+    my ( $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);
+        &my_die("Unable to perform '$ssh $destination_host df $mergedvodb_rsync_path' $error_code", $minidvodbcopy_id, $error_code);
+    }
+
+    my @fullbuf= split(/\n/, @{$stdout_buf}[0]);
+    #   print $#{$stdout_buf};
+#     print "xx $#df xx";
+#    print $fullbuf[0];
+    if ($#fullbuf ==1 ) {
+#	print $fullbuf[1];
+ 	@df = split /\s+/, $fullbuf[1];
+	if ($#df == 5) {
+#	    
+	    #print $df[3], $df[4];
+	    
+	    $df[4]=~ s/\%//;
+
+	    if ($df[4] <  97 && $df[3]-$total) {
+		print "\nThere is enough available space ($total < $df[3]) for the rsync\n";
+	    } else {
+		&my_die("Not enough available disk space: $df[4]% available, $df[3] on destination, $total free required for copy", $mergedvodbcopy_id, $PS_EXIT_PROG_ERROR);
+	    }
+#	    
+
+	} else {
+	    &my_die("Cannot parse df:\n@{$full_buf}[0]\nCan't figure out %free or available", $mergedvodbcopy_id, $PS_EXIT_PROG_ERROR); 
+	} 
+    } else {
+	&my_die("Cannot parse df:\n@{$full_buf}[0]\nToo many lines ($#fullbuf) vs 1", $mergedvodbcopy_id, $PS_EXIT_PROG_ERROR);
+    }
+}
+}
+
+{#do the rsync
+    my $command = "$rsync -rvuaq";
+    $command .= " $mergedvodbrun_path";
+    $command .= " $destination_host";
+    $command .= ":$mergedvodb_rsync_path";
+    print "\nPerforming rsync:\n";
+    print "$command\n\n";
+my ( $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);
+        &my_die("Unable to perform rsync: $error_code", $mergedvodbcopy_id, $error_code);
+    }
+    
+    print "Rsync complete..\n";
+
+}
+
+{#update the database
+    $dtime = 86400.0*(DateTime->now->mjd - $mjd_start);
+
+    my $command = "$mergedvodbtool -updatemergedvodbcopy -mergedvodbcopy_id $mergedvodbcopy_id";
+    $command .= " -set_state full -set_dtime $dtime";
+    $command .= " -dbname $dbname" if defined $dbname;
+    print "$command\n";
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $command, verbose => $verbose);
+    unless ($success) {
+        $error_code = 20;
+        &my_die("Unable to update database - rsync completed without error' $error_code", $mergedvodbcopy_id, $error_code);
+    }
+
+}
+exit 0;
+
+
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $mergedbvodbcopy_id = shift;
+    my $exit_code = shift; # Exit code to add
+
+    print STDERR "$msg $mergedvodbcopy_id\n";
+
+if (defined $mergedvodb_id ) {
+    my $command = "mergedvodbtool  -updatemergedvodbcopy -mergedvodbcopy_id $mergedvodbcopy_id";
+    $command .= " -set_fault $exit_code";
+    $command .= " -set_dtime $dtime" if defined $dtime;
+    $command .= " -dbname $dbname" if defined $dbname;
+    print $command;
+    system ($command);
+    }
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+    exit $exit_code;
+}
+
+__END__
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/mergedvodb_merge.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/mergedvodb_merge.pl	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/mergedvodb_merge.pl	(revision 33415)
@@ -0,0 +1,237 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+use Carp;
+ 
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use DateTime;
+my $mjd_start = DateTime->now->mjd;   # MJD of starting script
+my $dtime_merge;
+my $dtime_verify;
+my $dtime_script;
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config 1.01 qw( :standard );
+use File::Temp qw( tempfile );
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+# Look for programs we need
+my $missing_tools;
+my $dvomerge = can_run('dvomerge') or (warn "Can't find dvomerge" and $missing_tools = 1);
+my $mergetool = can_run('mergetool') or (warn "Can't find mergetool" and $missing_tools = 1);
+my $dvoverify = can_run('dvoverify') or (warn "Can't find dvoverify" and $missing_tools = 1);
+
+
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+my ( $mergedvodb_path, $merge_id, $mergedvodb, $minidvodb_path, $dbname,$camera,$verbose, $logfile, $no_op, $redirect, $save_temps);
+GetOptions(
+    'mergedvodb_path|w=s'         => \$mergedvodb_path,  # output DVO database
+    'minidvodb_path|w=s'     => \$minidvodb_path, #minidvodb database
+    'mergedvodb|w=s'    => \$mergedvodb, #mergedvodb
+    'merge_id|w=s'  => \$merge_id, #minidvodb_id
+    'dbname|d=s'        => \$dbname, # Database name
+    'camera|w=s'        => \$camera, #camera name
+    'verbose'           => \$verbose,   # Print to stdout
+    'no-op'             => \$no_op, # Don't do any operations?
+    'logfile=s'         => \$logfile,
+    'save-temps'        => \$save_temps, # Save temporary files?
+    ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage(
+          -msg => "Required options: --merge_id -- minidvodb_path --mergedvodb_path --mergedvodb --camera",
+          -exitval => 3,
+          ) unless
+    defined $mergedvodb_path and
+    defined $minidvodb_path and
+    defined $mergedvodb and
+    defined $merge_id and
+    defined $camera;
+
+
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $merge_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
+
+if ($logfile) {
+    $ipprc->redirect_output($logfile) or my_die( "Unable to redirect output", $merge_id, $PS_EXIT_SYS_ERROR );
+    print "\n\n";
+    print "Starting script $0 on $host\n\n";
+    print "COMMAND IS: @ARGV\n\n";
+}
+
+# convert supplied DVO database name to UNIX filename
+
+unless ($no_op) {
+
+    #this is chopped into several parts: firstcheck, addstar, relphot, dvoverify, merge
+    
+    #first check that there were no faulted merges before. If there are, fault this merge with PS_EXIT_UNKNOWN_ERROR
+    {
+	my $nothing_faulted = 0;
+	my $mdcParser = PS::IPP::Metadata::Config->new;
+	my $command = "$mergetool -listmerged -faulted -mergedvodb " . $mergedvodb;
+	$command .= " -dbname $dbname" if defined $dbname;
+	print "looking for faulted:\n\n$command\n\n";
+	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	    run(command => $command, verbose => $verbose);
+	&my_die( "Unable to get list of faulted mergedvodbs", $merge_id, $PS_EXIT_SYS_ERROR) unless $success;
+	if (scalar @$stdout_buf == 0 ) { #it lists nothing if nothign has faulted
+	    $nothing_faulted =1;
+	    print "previous merges are okay.\n";
+	} 
+	&my_die( "Previous merges faulted, do not proceed until they are investigated:", $merge_id, $PS_EXIT_UNKNOWN_ERROR) unless $nothing_faulted;
+    }
+    {
+	my $command  = "$dvoverify -s $minidvodb_path";
+	print "$command\n";
+	my $mjd_dvoverify_start = DateTime->now->mjd;   # MJD of starting script
+	my ( $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);
+	    &my_die("Unable to perform dvoverify: $error_code", $merge_id, $error_code);
+	}
+	$dtime_verify = 86400.0*(DateTime->now->mjd - $mjd_dvoverify_start);   # MJD of starting script
+	print "dvoverify time $dtime_verify\n";
+    }
+    #Merge
+    
+    my $this_is_the_first;
+    {
+	
+	my $mdcParser = PS::IPP::Metadata::Config->new;
+	my $command = "$mergetool -listmerged -mergedvodb " . $mergedvodb;
+	$command .= " -dbname $dbname" if defined $dbname;
+	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	    run(command => $command, verbose => $verbose);
+	&my_die( "Unable to get list of mergedvodbruns", $merge_id, $PS_EXIT_SYS_ERROR) unless $success;
+	if (scalar @$stdout_buf == 0 ) { #it lists nothing if it is the first
+	    $this_is_the_first =1;
+	    print "listing nothing\n";
+	} else {
+	    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
+		&my_die("Unable to parse metadata config", $merge_id, $PS_EXIT_PROG_ERROR);
+	    #this fails if there is nothing listed. I checked.
+	    my $components = parse_md_list($metadata) or
+		&my_die("Unable to parse metadata list", $merge_id, $PS_EXIT_PROG_ERROR);
+	    my $comp = $$components[0];
+	    my  $mergedvodb_path_tmp = $comp->{mergedvodb_path};
+	    
+	    if (!defined($mergedvodb_path_tmp)) {
+		&my_die("Unable to parse mergedvodb_path", $merge_id, $PS_EXIT_PROG_ERROR);
+	    } #but just to make sure, have it grab a minidvodb_name, to make sure it's not junk.
+	    print "found at least 1 mergedvodbrun in merged state\n";
+	    $this_is_the_first = 0;
+	}
+    }
+    print "$this_is_the_first $mergedvodb_path/Image.dat\n";
+    if (-e "$mergedvodb_path/Image.dat") {
+	if ($this_is_the_first == 1) {
+	    &my_die("refusing to merge, this is the first, but files already exist in dir", $merge_id, 4);
+	}
+	$this_is_the_first =0;
+    }
+	
+    print "$this_is_the_first $mergedvodb_path/Image.dat\n";
+    {
+	my $merge_command;
+	my $mjd_merge_start = DateTime->now->mjd;   # MJD of starting script
+	if ($this_is_the_first) {
+	    $merge_command = "rsync -rvat $minidvodb_path/* $mergedvodb_path";
+	} else {
+	    $merge_command = "$dvomerge $minidvodb_path into $mergedvodb_path";
+	}
+	print "\n$merge_command\n";
+	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	    run(command => $merge_command, verbose => $verbose);
+	unless ($success) {
+	    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	    &my_die("Unable to merge: $error_code", $merge_id, $error_code);
+	}
+	$dtime_merge = 86400.0*(DateTime->now->mjd - $mjd_merge_start);   # MJD of starting script
+	print "merge time $dtime_merge\n";
+#	}
+    }
+
+    {
+
+	$dtime_script = 86400.0*(DateTime->now->mjd - $mjd_start);
+	my $command = "mergetool -merge_id $merge_id";
+	$command .= " -addmerged";
+	$command .= " -mergedvodb $mergedvodb" if defined $mergedvodb;
+#	$command .= " -mergedvodb_path $mergedvodb_path" if defined $mergedvodb_path;
+	$command .= " -dtime_merge $dtime_merge" if defined $dtime_merge;
+	$command .= " -dtime_script $dtime_script" if defined $dtime_script;
+	$command .= " -dtime_verify $dtime_verify" if defined $dtime_verify;
+	$command .= " -dbname $dbname" if defined $dbname;
+	#print $command;
+	
+	my ( $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);
+	    &my_die("Unable to add to mergedvodbprocessed: $error_code", $merge_id, $error_code);
+	}
+    }
+    
+#} else {
+#    &my_die("dvodb: $mergedvodb_path not found", $merge_id, $PS_EXIT_UNKNOWN_ERROR);
+}
+else {
+    print "skipping processing for $mergedvodb_path\n";
+}
+    
+exit 0;
+
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $merge_id = shift;
+    my $exit_code = shift; # Exit code to add
+
+    print STDERR "$msg $mergedvodb_path\n";
+
+if (defined $merge_id ) {
+
+    my $command = "mergetool -merge_id $merge_id";
+    $command .= " -addmerged";
+    $command .= " -mergedvodb $mergedvodb" if defined $mergedvodb;
+        $command .= " -fault $exit_code";
+        #$command .= " -mergedvodb_path $mergedvodb_path" if defined $mergedvodb_path;
+        $command .= " -dtime_merge $dtime_merge" if defined $dtime_merge;
+    $command .= " -dtime_script $dtime_script" if defined $dtime_script;
+    $command .= " -dtime_verify $dtime_verify" if defined $dtime_verify;
+        $command .= " -dbname $dbname" if defined $dbname;
+
+
+
+    print $command;
+    system ($command);
+    }
+
+
+
+
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
+    exit $exit_code;
+}
+
+__END__
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/mergedvodb_queue.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/mergedvodb_queue.pl	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/mergedvodb_queue.pl	(revision 33415)
@@ -0,0 +1,164 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+use Carp;
+use DateTime;
+
+# Ideally, we want to queue new runs with mergetool -definebyquery.  We can't for the survey task, because we use the value in 'site.config' to find the mergedvodb_path. I do not know how to tell pantasks how to do this, but I know how to do it in perl.
+
+
+use DateTime::Format::Strptime;
+use DateTime::Duration;
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+my $date = `date`;
+print "\n\n";
+print "Starting script $0 on $host at $date\n\n";
+
+
+my $mjd_start = DateTime->now->mjd;   # MJD of starting script
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config 1.01 qw( :standard );
+use File::Temp qw( tempfile );
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+# Look for programs we need
+my $missing_tools;
+my $mergetool = can_run('mergetool') or (warn "Can't find mergetool" and $missing_tools = 1);
+my $ppConfigDump = can_run('ppConfigDump') or (warn "Can't find ppConfigDump" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+my (  $outroot, $dbname, $minidvodb_group, $mergedvodb , $camera,  $verbose, $no_update,
+     $no_op, $redirect, $save_temps);
+
+# we just need any old camera to be able to read site.config. I'm using SIMPLE, unless it is overridden on the commandline (it makes no difference for this case)
+
+$camera = 'SIMPLE';
+
+
+GetOptions(
+    'camera|c=s'        => \$camera, # Camera
+    'dbname|d=s'        => \$dbname, # Database name
+    'minidvodb_group|w=s'       => \$minidvodb_group, # minidvodb_group.
+    'outroot|w=s'       => \$outroot, # output file base name
+    'mergedvodb|w=s'     => \$mergedvodb, # output miniDVODB
+    'verbose'           => \$verbose,   # Print to stdout
+    'no-update'         => \$no_update, # Update the database?
+    'no-op'             => \$no_op, # Don't do any operations?
+    'redirect-output'   => \$redirect,
+    'save-temps'        => \$save_temps, # Save temporary files?
+    ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage(
+          -msg => "Required options: --mergedvodb --minidvodb_group  --outroot",
+          -exitval => 3,
+          ) unless
+    defined $minidvodb_group and
+   # defined $camera and
+    defined $outroot and
+    defined $mergedvodb;
+
+print "camera is $camera\n";
+
+
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $mergedvodb   , $PS_EXIT_CONFIG_ERROR ); # IPP configuration
+
+my $logDest = $ipprc->filename("LOG.EXP", $outroot) or &my_die("Missing entry from camera config", $minidvodb_group, $PS_EXIT_CONFIG_ERROR);
+print "log file is $logDest\n";
+if ($redirect) {
+    $ipprc->redirect_output($logDest) or my_die( "Unable to redirect output", $minidvodb_group, $PS_EXIT_SYS_ERROR );
+    print "\n\n";
+    print "Starting script $0 on $host\n\n";
+    print "COMMAND IS: @ARGV\n\n";
+}
+
+
+# Recipes to use based on reduction class
+
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+# Output products
+$ipprc->outroot_prepare($outroot);
+
+# the camera configurations should define the psastro output to be a single file (MEF), regardless of the inputs
+ my $create_new = 0;
+# convert supplied DVO database name to UNIX filename
+my $mergedvodbReal;
+if (defined $mergedvodb) {
+    $mergedvodbReal = $ipprc->dvo_catdir( $mergedvodb ); # catdir for DVO
+    $mergedvodbReal = $ipprc->convert_filename_absolute( $mergedvodbReal );
+} else {
+    warn("mergedvodb undefined:\n");
+    exit(4);
+}
+if (!defined $mergedvodbReal) {
+    warn("can't parse out mergedvodb_path\n");
+    exit(4);
+}
+
+
+
+print "mergedvodb_path = $mergedvodbReal\n";
+
+unless ($no_op) {
+
+  #create the mergedvodb entry (well, the command for it)
+    my $fpaCommand = "$mergetool -definebyquery";
+    $fpaCommand .= " -mergedvodb $mergedvodb";
+    $fpaCommand .= " -minidvodb_group $minidvodb_group";
+    $fpaCommand .= " -set_mergedvodb_path  $mergedvodbReal" ;
+    $fpaCommand .= " -dbname $dbname" if defined $dbname;
+
+    print "running the following: \n\n";
+    print "$fpaCommand\n\n";
+
+    unless ($no_update) {
+
+
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $fpaCommand, verbose => $verbose);
+        unless ($success) {
+            $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+            warn("Unable to add result to database: $error_code\n");
+            exit($error_code);
+        }
+    } else {
+        print "skipping command: $fpaCommand\n";
+    }
+}
+
+sub my_die
+{#complain if it doesn't work
+    my $msg = shift; # Warning message on die
+    my $mergedvodb = shift; # Camtool identifier
+    my $exit_code = shift; # Exit code to add
+
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
+    carp($msg);
+
+    exit $exit_code;
+}
+
+END {
+    my $status = $?;
+    system("sync") == 0
+        or die "failed to execute sync: $!" ;
+    $? = $status;
+}
+
+__END__
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/minidvodb_createdb.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/minidvodb_createdb.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/minidvodb_createdb.pl	(revision 33415)
@@ -226,5 +226,4 @@
     $fpaCommand .= " -set_minidvodb_group $minidvodb_group";
     $fpaCommand .= " -set_minidvodb_path  $minidvodbReal" if defined $minidvodbReal;
-    $fpaCommand .= " -set_mergedvodb_path $dvodbReal";
     $fpaCommand .= " -dbname $dbname" if defined $dbname;
 
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/minidvodb_merge.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/minidvodb_merge.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/minidvodb_merge.pl	(revision 33415)
@@ -93,6 +93,26 @@
     if (defined $mergedvodbReal) {
 
-	#this is chopped into several parts:  addstar, relphot, dvoverify, merge
-
+	#this is chopped into several parts: firstcheck, addstar, relphot, dvoverify, merge
+        
+        #first check that there were no faulted merges before. If there are, fault this merge with PS_EXIT_UNKNOWN_ERROR
+	{
+	    my $nothing_faulted = 0;
+	    my $mdcParser = PS::IPP::Metadata::Config->new;
+
+            my $command = "$addtool -listminidvodbprocessed -faulted -minidvodb_group " . $minidvodb_group;
+            $command .= " -dbname $dbname" if defined $dbname;
+
+            my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                    run(command => $command, verbose => $verbose);
+            &my_die( "Unable to get list of faulted minidvodbs", $minidvodb_id, $PS_EXIT_SYS_ERROR) unless $success;
+            if (scalar @$stdout_buf == 0 ) { #it lists nothing if nothign has faulted
+                $nothing_faulted =1;
+		print "previous merges are okay.\n";
+            } 
+	    &my_die( "Previous merges faulted, do not proceed until they are investigated:", $minidvodb_group, $PS_EXIT_UNKNOWN_ERROR) unless $nothing_faulted;
+
+
+ 
+	}
 
 	#addstar
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/minidvodb_premerge.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/minidvodb_premerge.pl	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/minidvodb_premerge.pl	(revision 33415)
@@ -0,0 +1,174 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+use Carp;
+ 
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+print "\n\n";
+print "Starting script $0 on $host\n\n";
+
+use DateTime;
+my $mjd_start = DateTime->now->mjd;   # MJD of starting script
+my $dtime_resort;
+my $dtime_relphot;
+my $dtime_script;
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config 1.01 qw( :standard );
+use File::Temp qw( tempfile );
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+# Look for programs we need
+my $missing_tools;
+my $dvomerge = can_run('dvomerge') or (warn "Can't find dvomerge" and $missing_tools = 1);
+my $addtool = can_run('addtool') or (warn "Can't find addtool" and $missing_tools = 1);
+my $addstar = can_run('addstar') or (warn "Can't find addstar" and $missing_tools = 1);
+my $relphot = can_run('relphot') or (warn "Can't find relphot" and $missing_tools = 1);
+my $relastro = can_run('relastro') or (warn "Can't find relastro" and $missing_tools = 1);
+my $dvoverify = can_run('dvoverify') or (warn "Can't find dvoverify" and $missing_tools = 1);
+
+
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+my ( $minidvodb, $minidvodb_id, $minidvodb_group, $camera, $dbname,$verbose, $logfile, $no_op, $redirect, $save_temps);
+GetOptions(
+    'minidvodb|w=s'     => \$minidvodb, #minidvodb database
+    'minidvodb_id|w=s'  => \$minidvodb_id, #minidvodb_id
+    'minidvodb_group|w=s'  => \$minidvodb_group, #minidvodb_id
+    'camera|c=s'        => \$camera, # Camera
+    'dbname|d=s'        => \$dbname, # Database name
+    'verbose'           => \$verbose,   # Print to stdout
+    'no-op'             => \$no_op, # Don't do any operations?
+    'logfile=s'         => \$logfile,
+    'save-temps'        => \$save_temps, # Save temporary files?
+    ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage(
+          -msg => "Required options: --minidvodb --minidvodb_id -- minidvodb_group --camera",
+          -exitval => 3,
+          ) unless
+    defined $minidvodb and
+    defined $minidvodb_id and
+    defined $minidvodb_group and
+    defined $camera;
+
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $minidvodb_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
+
+if ($logfile) {
+    $ipprc->redirect_output($logfile) or my_die( "Unable to redirect output", $minidvodb_id, $PS_EXIT_SYS_ERROR );
+    print "\n\n";
+    print "Starting script $0 on $host\n\n";
+    print "COMMAND IS: @ARGV\n\n";
+}
+
+my $dtime_addstar = 0;
+
+unless ($no_op) {
+    
+	#this is chopped into several parts: addstar, relphot
+	#addstar
+	{
+            my $command  = "$addstar -resort";
+            $command .= " -D CAMERA $camera";
+            $command .= " -D CATDIR $minidvodb";
+            my $mjd_addstar_start = DateTime->now->mjd;   # MJD of starting script
+	    print "\n$command\n";
+            my ( $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);
+                &my_die("Unable to perform addstar: $error_code", $minidvodb_id, $error_code);
+            }
+            $dtime_addstar = 86400.0*(DateTime->now->mjd - $mjd_addstar_start);  $dtime_resort = $dtime_addstar;
+            # MJD of starting script
+	    $dtime_resort = $dtime_addstar;
+            print "addstar -resort time $dtime_addstar\n";
+        }
+
+	#relphot
+
+        {
+            # relphot only takes lower case gpc1
+            my $relphot_camera = lc($camera);
+            my $command  = "$relphot -averages -update";
+            $command .= " -D CAMERA $relphot_camera";
+            $command .= " -D CATDIR $minidvodb";
+	    print "$command\n";
+            my $mjd_relphot_start = DateTime->now->mjd;   # MJD of starting script
+	    
+            my ( $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);
+                &my_die("Unable to perform relphot: $error_code", $minidvodb_id, $error_code);
+            }
+            $dtime_relphot = 86400.0*(DateTime->now->mjd - $mjd_relphot_start);   # MJD of starting script
+            print "relphot time $dtime_relphot\n";
+        }
+
+	{
+	    
+	    $dtime_script = 86400.0*(DateTime->now->mjd - $mjd_start);
+            my $command = "addtool -minidvodb_id $minidvodb_id";
+            $command .= " -addminidvodbprocessed";
+            $command .= " -minidvodb_group $minidvodb_group";
+            $command .= " -dtime_relphot $dtime_relphot"  if defined $dtime_relphot;
+            $command .= " -dtime_resort $dtime_resort" if defined $dtime_resort;
+	    $command .= " -dtime_script $dtime_script" if defined $dtime_script;
+	    $command .= " -dbname $dbname" if defined $dbname;
+            #print $command;
+            my ( $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);
+                &my_die("Unable to add to minidvodbprocessed: $error_code", $minidvodb_id, $error_code);
+            }
+        }
+#} else {
+#    &my_die("dvodb: minidvodb_id = $minidvodb_id not found", $minidvodb_id, $PS_EXIT_UNKNOWN_ERROR);
+#}
+} else {
+    print "skipping processing for minidvodb_id = $minidvodb_id\n";
+}
+
+exit 0;
+
+
+sub my_die
+{
+    my $msg = shift; # Warning message on die
+    my $minidbvodb_id = shift;
+    my $exit_code = shift; # Exit code to add
+    print STDERR "$msg $minidvodb_id\n";
+
+if (defined $minidvodb_id ) {
+
+    my $command = "addtool -minidvodb_id $minidvodb_id";
+    $command .= " -addminidvodbprocessed";
+    $command .= " -fault $exit_code";
+    $command .= " -minidvodb_group $minidvodb_group";
+    $command .= " -dtime_relphot $dtime_relphot" if defined $dtime_relphot;
+    $command .= " -dtime_resort $dtime_resort" if defined $dtime_resort;
+    $command .= " -dtime_script $dtime_script" if defined $dtime_script;
+    $command .= " -dbname $dbname" if defined $dbname;
+    system ($command);
+    }
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+    exit $exit_code;
+}
+
+__END__
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/nightly_science.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/nightly_science.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/nightly_science.pl	(revision 33415)
@@ -996,5 +996,10 @@
     $cmd .= " -min_num 4";
     $cmd .= " -select_good_frac_min 0.05";
-    $cmd .= " -set_reduction NIGHTLY_STACK ";
+    if (defined($reduction)) {
+	$cmd .= " -set_reduction $reduction ";
+    }
+    else {
+	$cmd .= " -set_reduction NIGHTLY_STACK ";
+    }
     $cmd .= " $select ";
     if ($debug == 1) {
@@ -1410,4 +1415,10 @@
 		}
 	    }
+	    if ((defined($science_config{$target}{SELF_WSDIFFS})) && ($science_config{$target}{SELF_WSDIFFS} == 1)) {
+		foreach my $filter (@filter_list) {
+		    # This one needs to return a state to see if we need to wait on stacking before checking again.
+		    $metadata_out{nsDiffState} = self_WS_diff_queue($date,$target,$filter,$pretend);
+		}
+	    }
 	}
     }
@@ -1702,30 +1713,87 @@
 
     my ($lunation_start,$lunation_end) = get_lunation_extent($date);
-    
+
+
+# Get a list of exposures that could be diffed    
     my $new_data_group = "${data_group}.offnight";
     my $new_dist_group = "${dist_group}.offnight";
-    my $cmd = "$difftool -dbname $dbname  -definewarpwarp ";
-    $cmd .= "-input_label $label  -template_label $label ";
-    $cmd .= "-set_workdir $workdir  -set_dist_group $new_dist_group  -set_data_group $new_data_group ";
-    $cmd .= " -simple  -set_label $label -filter $filter ";
-    $cmd .= " -dateobs_begin ${lunation_start}T00:00:00 -dateobs_end ${lunation_end}T23:59:59 -distance 1.5 ";
-
-    if (defined($pretend)) {
-	$cmd .= ' -pretend ';
-    }
-    if (defined($debug)) {
-	print STDERR "ON_diffs wants to run this command: $cmd\n";
-    }
-    
-    if (($debug == 0)&&(!defined($pretend))) {
-	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
-	    run ( command => $cmd, verbose => $verbose );
-	unless ($success) {
-	    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
-	    &my_die("Unable to perform difftool: $error_code", 0,0,$date, $PS_EXIT_SYS_ERROR);
-	}
-    }
-}
+    my $check_cmd = "$difftool -dbname $dbname  -definewarpwarp ";
+    $check_cmd .= "-input_label $label  -template_label $label ";
+    $check_cmd .= "-set_workdir $workdir  -set_dist_group $new_dist_group  -set_data_group $new_data_group ";
+    $check_cmd .= " -mintimediff 40000 ";
+    $check_cmd .= " -pretend -simple  -rerun -set_label $label -filter $filter ";
+    $check_cmd .= " -dateobs_begin ${lunation_start}T00:00:00 -dateobs_end ${lunation_end}T23:59:59 -distance 1.5 ";
+
+    my ($success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) = 
+	run ( command => $check_cmd, verbose => $verbose );
+    unless ($success) {
+	$error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+	&my_die("Unable to perform difftool: $error_code", 0,0,$date, $PS_EXIT_SYS_ERROR);
+    }
+    
+    # Parse results
+    my $diffs = $mdcParser->parse_list(join "", @$stdout_buf) or
+	&my_die("Unable to parse metadata from difftool -definewarpwarp", 0, 0, $date, $PS_EXIT_PROG_ERROR);
+    foreach my $diff (@$diffs) {
+	unless (multi_date_verify_uniqueness_diff($diff->{input_warp_id},$diff->{template_warp_id},$date,$target)) {
+	    # If we don't already have a diff with these inputs, make a diff with these inputs.
+	    my $cmd = "$difftool -dbname $dbname -definewarpwarp ";
+	    $cmd   .= "-input_label $label -template_label $label ";
+	    $cmd   .= "-warp_id $diff->{input_warp_id} -template_warp_id $diff->{template_warp_id} ";
+	    $cmd .= "-set_workdir $workdir  -set_dist_group $new_dist_group  -set_data_group $new_data_group ";
+	    $cmd .= " -mintimediff 40000 ";
+	    $cmd .= " -simple  -rerun -set_label $label -filter $filter ";
+	    $cmd .= " -dateobs_begin ${lunation_start}T00:00:00 -dateobs_end ${lunation_end}T23:59:59 -distance 1.5 ";
+
+	    if (defined($pretend)) {
+		$cmd .= ' -pretend ';
+	    }
+	    print STDERR "ON_diffs wants to run this command: $cmd\n";
 	    
+	    if (($debug == 0)&&(!defined($pretend))) {
+		my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+		    run ( command => $cmd, verbose => $verbose );
+		unless ($success) {
+		    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+		    &my_die("Unable to perform difftool: $error_code", 0,0,$date, $PS_EXIT_SYS_ERROR);
+		}
+	    }
+	}
+    }
+}
+
+sub intranight_WS_diff_queue {
+    my $date = shift;
+    my $target = shift;
+    my $filter = shift;
+    my $pretend = shift;
+    my ($label,$workdir,$obs_mode,$object,$comment,$tess_id,$dist_group,$data_group,$reduction) = get_tool_parameters($date,$target);
+    my $db = init_gpc_db();
+
+    my ($stacks_to_finish,$stacks_total) = check_stack_count($label);
+    if (($stacks_to_finish == 0)&&($stacks_total != 0)) {
+	my $cmd = "$difftool -dbname $dbname -definewarpstack ";
+	$cmd .= " -good_frac 0.2 ";
+	$cmd .= " -warp_label $label -stack_label $label -set_label $label ";
+	$cmd .= " -set_workdir $workdir -available -set_reduction WARPSTACK -set_dist_group $dist_group ";
+	$cmd .= " -rerun ";
+    }
+}	    
+
+sub check_stack_count {
+    my $label = shift;
+
+    my $stacks_to_finish = 0;
+    my $stacks_total = 0;
+
+    my $sth = "SELECT DISTINCT stack_id from stackRun where label = '$label' AND state = 'new'";
+    my $data_ref = $db->selectall_arrayref( $sth );
+    $stacks_to_finish = $#{ $data_ref } + 1;
+
+    $sth = "SELECT DISTINCT stack_id from stackRun where label = '$label'";
+    $data_ref = $db->selectall_arrayref( $sth );
+    $stacks_total = $#{ $data_ref } + 1;
+    return($stacks_to_finish,$stacks_total);
+}
 
 
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/queue_chips_by_night.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/queue_chips_by_night.pl	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/queue_chips_by_night.pl	(revision 33415)
@@ -0,0 +1,102 @@
+#!/usr/bin/env perl
+
+use Carp;
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+my $stdate = `date`;
+use DateTime;
+my $mjd_start = DateTime->now->mjd;   # MJD of starting script
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use PS::IPP::Config 1.01 qw( :standard );
+use File::Temp qw( tempfile );
+use File::Basename;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+# Look for programs we need
+my $missing_tools;
+my $chiptool = can_run('chiptool') or (warn "Can't find chiptool" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+my @ARGS = @ARGV;
+
+my ( $date, $data_group_base, $label, $tess_id, $end_stage, $workdir_base, $dbname );
+
+GetOptions(
+    'date=s'             => \$date,       # date, like this 2011-02-12
+    'data_group_base=s'  => \$data_group_base,    # data_group_base, like this: isp
+    'label=s'            => \$label,   # label
+    'dbname|d=s'         => \$dbname,    # Database name
+    'tess_id=s'          => \$tess_id, # tess_id
+    'end_stage=s'        => \$end_stage, # end_stage
+    'workdir_base=s'          => \$workdir_base,  # workdir
+     ) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --dbname --label --tess_id --date \n Optional: --data_group_base, end_stage, workdir_base",
+           -exitval => 3) unless
+    defined $dbname and
+    defined $label and
+    defined $tess_id and
+    defined $date;
+
+my $truncdate = $date;
+    $truncdate =~ s/-//g;
+if (defined $data_group_base) {
+    print "found data_group_base = $data_group_base\n";
+} else {
+    $data_group_base = $label;
+    print "using $label as data_group_base\n";
+}
+if (defined $end_stage) {
+    print "found end_stage = $end_stage\n";
+} else {
+    $end_stage = "camera";
+    print "using camera as end_stage\n";
+}
+if (defined $workdir_base) {
+    print "found workdir_base = $workdir_base\n";
+} else {
+    $workdir_base = "neb://\@HOST\@.0/$dbname/$label";
+    print "using $workdir_base as workdir\n";
+}
+
+# we have all the pieces, now build up the chiptool:
+
+my $command = "$chiptool -definebyquery -unique";
+$command .= " -dbname $dbname";
+$command .= " -dateobs_begin '$date"."T00:00:00'";
+$command .= " -dateobs_end '$date"."T23:59:59'";
+$command .= " -set_label $label";
+$command .= " -set_data_group $data_group_base.$truncdate";
+$command .= " -set_workdir $workdir_base/$truncdate";
+$command .= " -set_end_stage $end_stage";
+$command .= " -set_tess_id $tess_id";
+
+print "\nrunning this command:\n\n$command\n\n";
+
+
+my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+    run(command => $command, verbose => 0);
+unless ($success) {
+    $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+    warn("Unable to perform chiptool -definebyquery: $error_code\n");
+    exit($error_code);
+    
+}
+
+ 
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/register_exp.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/register_exp.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/register_exp.pl	(revision 33415)
@@ -54,5 +54,5 @@
 
 my $ipprc = PS::IPP::Config->new() or my_die( "Unable to set up", $exp_id, $PS_EXIT_CONFIG_ERROR );
-$ipprc->redirect_output($logfile) or my_die( "Unable to redirect output", $exp_id, $PS_EXIT_SYS_ERROR ) if $logfile;
+$ipprc->redirect_to_logfile($logfile) or my_die( "Unable to redirect output", $exp_id, $PS_EXIT_SYS_ERROR ) if $logfile;
 
 pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
@@ -121,5 +121,6 @@
 my ($data_group,$dist_group,$chip_workdir,$reduction);
 ($label,$data_group,$dist_group,$end_stage,
- $tess_id,$chip_workdir,$reduction) = advance_decisions(&value_for_flag($cmdflags,"-dateobs"),
+ $tess_id,$chip_workdir,$reduction) = advance_decisions(&value_for_flag($cmdflags,"-exp_name"),
+							&value_for_flag($cmdflags,"-dateobs"),
 							&value_for_flag($cmdflags,"-exp_type"),
 							&value_for_flag($cmdflags,"-obs_mode"),
@@ -170,4 +171,5 @@
 ### Pau.
 sub advance_decisions {
+    my $exp_name = shift;
     my $dateobs = shift;
     my $exptype = shift;
@@ -178,4 +180,5 @@
 
     # The strings come pre-quoted.
+    $exp_name =~ s/\'//g;
     $dateobs =~ s/\'//g;
     $exptype =~ s/\'//g;
@@ -188,4 +191,9 @@
     my $target;
 
+    if ($exp_name =~ /^c/) {
+	print "Skipping because this is a camera commanded exposure: $exp_name\n";
+	return(undef,undef,undef,"reg",undef,undef,undef);
+    }
+    
     if ($exptype ne 'OBJECT') {
 	print "Skipping because exptype doesn't claim to be OBJECT: $exptype\n";
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/register_imfile.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/register_imfile.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/register_imfile.pl	(revision 33415)
@@ -60,5 +60,5 @@
 
 my $ipprc = PS::IPP::Config->new() or my_die_for_add( "Unable to set up", $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
-$ipprc->redirect_output($logfile) or my_die_for_add( "Unable to redirect output", $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $PS_EXIT_SYS_ERROR ) if $logfile;
+$ipprc->redirect_to_logfile($logfile) or my_die_for_add( "Unable to redirect output", $exp_id, $tmp_exp_name, $tmp_class_id, $uri, $PS_EXIT_SYS_ERROR ) if $logfile;
 
 pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
@@ -404,4 +404,7 @@
 	}
     }
+    if ($exp_name =~ /a$/) {
+	return(1);
+    }
     return(0);
 }
@@ -517,9 +520,10 @@
 
     carp($msg);
-    if (defined $exp_id && defined $tmp_class_id and not $no_update) {
+    if (defined $exp_id && defined $class_id and not $no_update) {
         my $command = "$regtool -updateprocessedimfile";
         $command .= " -exp_id $exp_id";
         $command .= " -class_id $class_id";
         $command .= " -fault $exit_code";
+	$command .= " -set_state pending_burntool ";
         $command .= " -hostname $host" if defined $host;
         $command .= " -dbname $dbname" if defined $dbname;
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/skycalibration.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/skycalibration.pl	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/skycalibration.pl	(revision 33415)
@@ -0,0 +1,298 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+## report the program and machine
+use Sys::Hostname;
+my $host = hostname();
+my $date = `date`;
+print "\n\n";
+print "Starting script $0 on $host at $date\n\n";
+
+use DateTime;
+my $mjd_start = DateTime->now->mjd;   # MJD of starting script
+
+use vars qw( $VERSION );
+$VERSION = '0.01';
+
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Metadata::List qw( parse_md_list );
+use Data::Dumper;
+use File::Temp qw( tempfile );
+use File::Basename;
+use PS::IPP::Config 1.01 qw( :standard );
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+
+# Look for programs we need
+my $missing_tools;
+my $staticskytool = can_run('staticskytool') or (warn "Can't find staticskytool" and $missing_tools = 1);
+my $psastro = can_run('psastro') or (warn "Can't find psastro" and $missing_tools = 1);
+my $fhead = can_run('fhead') or (warn "Can't find fhead" and $missing_tools = 1);
+my $ppStatsFromMetadata = can_run('ppStatsFromMetadata') or (warn "Can't find ppStatsFromMetadata" and $missing_tools = 1);
+my $ppConfigDump = can_run('ppConfigDump') or (warn "Can't find ppConfigDump" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+sub HelpMessage {
+    print "USAGE: skycalibration.pl --skycal_id (skycal_id) --outroot (root) --camera (camera) [options]\n";
+    print "  --skycal_id (skycal_id) : run identifier\n";
+    print "  --camera (camera)       : Camera name\n";
+    print "  --dbname (dbname)       : Database name\n";
+    print "  --threads (N)           : Number of threads to use\n";
+    print "  --outroot (root)        : Output root name\n";
+    print "  --path_base (path_base) : path_base of input\n";
+    print "  --filter (filter)       : filter of input\n";
+    print "  --singlefilter          : the input is the result of a single filter analysis";
+    print "  --reduction (class)     : Reduction class\n";
+    print "  --verbose               : Be extra verbose\n";
+    print "  --no-update             : Don't update the database?\n";
+    print "  --no-op                 : Don't do any operations?\n";
+    print "  --redirect-output       : send output to the log file,\n";
+    print "  --save-temps            : Save temporary files?\n";
+    print "  --help                  : show this message\n";
+    exit 2;
+}
+
+my ($skycal_id, $stack_id, $camera, $dbname, $threads, $outroot, $path_base, $filter, $singlefilter, $reduction, $verbose, $no_update, $no_op, $redirect, $save_temps);
+GetOptions(
+    'skycal_id=s'       => \$skycal_id, # sky calibration run id
+    'stack_id=s'        => \$stack_id,    # static sky run id
+    'camera|c=s'        => \$camera,    # Camera name
+    'dbname|d=s'        => \$dbname,    # Database name
+    'threads=s'         => \$threads,   # Number of threads to use
+    'outroot=s'         => \$outroot,   # Output root name
+    'path_base=s'       => \$path_base, # path_base of input
+    'filter=s'          => \$filter,    # filter for input stack
+    'reduction=s'       => \$reduction, # Reduction class
+    'singlefilter'      => \$singlefilter, # input was from single filter analysis
+    'verbose'           => \$verbose,   # Print to stdout
+    'no-update'         => \$no_update, # Don't update the database?
+    'no-op'             => \$no_op, # Don't do any operations?
+    'redirect-output'   => \$redirect,
+    'save-temps'        => \$save_temps, # Save temporary files?
+    'help'              => sub { HelpMessage() },
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+
+pod2usage(
+    -msg => "Required options: --skycal_id --stack_id --outroot --camera --path_base",
+    -exitval => 3,
+          ) unless 
+    defined $skycal_id and
+    defined $stack_id and
+    defined $path_base and
+    defined $outroot and
+    defined $camera;
+
+my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $skycal_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
+
+my $logDest = $ipprc->filename("LOG.EXP", $outroot);
+$ipprc->redirect_to_logfile($logDest) or my_die( "Unable to redirect output", $skycal_id, $PS_EXIT_SYS_ERROR ) if $redirect;
+
+my $traceDest  = $ipprc->filename("TRACE.EXP", $outroot);
+my $stats      = $ipprc->filename("PSASTRO.STATS", $outroot);
+
+my $outbase = basename($outroot);
+
+# Get list of input images to stack photometry
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+my $files;
+
+# Recipes to use based on reduction class
+$reduction = 'DEFAULT' unless defined $reduction;
+
+# generate the input 
+my $cmdflags = "";
+
+my $recipe_psastro  = $ipprc->reduction($reduction, 'STATICSKY_CALIBRATION'); # Recipe to use for psastro
+unless ($recipe_psastro) {
+    &my_die("Couldn't find selected reduction for STATICSKY_CALIBRATION: $reduction\n", $skycal_id, $PS_EXIT_CONFIG_ERROR);
+}
+
+# XXX: We are making the bad assumption here that the staticksky run used the same reduction class
+# as this skycalRun
+my $recipe_psphot = $ipprc->reduction($reduction, "STACKPHOT_PSPHOT");
+unless ($recipe_psphot) {
+    &my_die("Couldn't find selected reduction for STATCKPHOT_PSPHOT: $reduction\n", $skycal_id, $PS_EXIT_CONFIG_ERROR);
+}
+
+print "reduction:      $reduction\n";
+print "recipe_psastro: $recipe_psastro\n";
+print "recipe_psphot:  $recipe_psphot\n";
+
+my $configuration = $ipprc->filename("PSASTRO.CONFIG", $outroot);
+
+# find input file
+# First check the expected file name where the stack_id is the FILE_ID
+my $file;
+if (!$singlefilter) {
+    $file = $ipprc->filename('PSPHOT.STACK.OUTPUT', $path_base, $stack_id);
+    if (! $ipprc->file_resolve($file)) {
+        # XXX: Beginning of section that can be removed eventually
+
+        # no file with the expected name found
+        # assume that the input is from an early staticsky run that did not use stack_id as the FILE_ID
+        # but instead used FILE_ID = [0 .. num_filters-1]
+        print "\nfailed to resolve $file\n";
+        if (!$filter) {
+            &my_die("filter not supplied unable to identify appropriate staticsky input.", $skycal_id, $PS_EXIT_SYS_ERROR);
+        }
+        print "Trying old style FILE_ID\n";
+        my $max_filters = 5;
+        for (my $i=0; $i < $max_filters; $i++) {
+            my $file_id = sprintf "%03d", $i;
+            $file  = $ipprc->filename('PSPHOT.STACK.OUTPUT', $path_base, $file_id);
+            my $resolved = $ipprc->file_resolve($file);
+            if (!$resolved) {
+                # we fail here assumming that if file_id N doesn't exist, neither to N+1
+                print "\nfailed to resolve $file\n\n";
+                &my_die("unable to identify appropriate staticsky input.", $skycal_id, $PS_EXIT_SYS_ERROR);
+            }
+            # Check the filter id for this file
+            my $command = "$fhead $resolved | grep FILTERID";
+            my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                run(command => $command, verbose => 0);
+            unless ($success) {
+                $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
+                &my_die("Unable to perform $command: $error_code", $skycal_id, $PS_EXIT_SYS_ERROR);
+            }
+            # Expected output: HIERARCH FPA.FILTERID = 'r.00000 ' / Filter used (parsed, abstract name)
+            my ($undef, undef, undef, $filt) = split " ", join("", @$stdout_buf);
+            my $this_filter = substr $filt, 1, 7;
+
+            # if it matches we're done
+            last if $this_filter eq $filter;
+
+            # nope loop around to try the next one
+            print "Input file for $filter is not $file ($this_filter)\n";
+            $file = undef;
+        }
+        print "\nInput file for $stack_id filter: $filter: $file\n";
+
+        # XXX: End of section that can be removed eventually
+    }
+} else {
+    # input is from a single filter static sky run use a different file rule
+    # XXX: can't we just make staticsky.pl use the same file rule?
+    $file = $ipprc->filename('PSPHOT.OUT.CMF.MEF', $path_base);
+    &my_die("Unable to find input for $stack_id $filter", $skycal_id, $PS_EXIT_SYS_ERROR) 
+        unless $ipprc->file_exists($file);
+}
+
+if (!$file) {
+    &my_die("Unable to find input for $stack_id $filter", $skycal_id, $PS_EXIT_SYS_ERROR);
+}
+
+# Perform psastro analysis
+my $output_sources_filerule =  'PSASTRO.OUTPUT.CMF';
+{
+    my $command = "$psastro $outroot";
+    $command .= " -file $file";
+#     $command .= " -threads $threads" if defined $threads;  # psastro doesn't currently accept -threads
+    $command .= " -recipe PSASTRO  $recipe_psastro";
+    $command .= " -recipe PSPHOT   $recipe_psphot";
+    $command .= " -F PSASTRO.OUTPUT $output_sources_filerule";
+    $command .= " -stats $stats";
+    $command .= " -recipe PPSTATS CAMSTATS";
+    $command .= " -dumpconfig $configuration" if $configuration;
+    $command .= " -tracedest $traceDest";
+
+    unless ($no_op) {
+        my ( $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);
+            &my_die("Unable to perform psastro: $error_code", $skycal_id, $error_code);
+        }
+
+        &my_die("Couldn't find stats file: $stats", $skycal_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($stats);
+
+        {
+            my $resolved = $ipprc->file_resolve($stats) 
+                or  &my_die("failed to resolve stats file: $stats", $skycal_id, $PS_EXIT_SYS_ERROR) ;
+            my $command = "$ppStatsFromMetadata $resolved - STATICSKY_CAL";
+            my ( $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);
+                &my_die("Unable to perform psastro: $error_code", $skycal_id, $error_code);
+            }
+            $cmdflags = join "", @$stdout_buf;
+            chomp $cmdflags;
+        }
+
+        my $quality;
+        ($quality) =  $cmdflags =~ /-quality (\d+)/;
+        if (!$quality) {
+            my $outputSources  = $ipprc->filename($output_sources_filerule, $outroot);
+            &my_die("Couldn't find expected output file: $outputSources", $skycal_id, $PS_EXIT_SYS_ERROR)
+                unless $ipprc->file_exists($outputSources);
+
+            &my_die("Couldn't find expected output file: $configuration", $skycal_id, $PS_EXIT_SYS_ERROR) 
+                unless $ipprc->file_exists($configuration);
+        }
+    } else {
+        print "Not executing: $command\n";
+    }
+}
+
+
+# Add the result to the database
+{
+    my $command = "$staticskytool -skycal_id $skycal_id";
+    $command .= " -addskycalresult -path_base $outroot";
+    $command .= " $cmdflags";
+    $command .= (" -dtime_script " . ((DateTime->now->mjd - $mjd_start) * 86400));
+    $command .= " -hostname $host" if defined $host;
+    $command .= " -dbname $dbname" if defined $dbname;
+
+    unless($no_update) {
+        my ( $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);
+            my $err_message = "Unable to perform staticskytool -addresult";
+	    warn($err_message);
+	    exit $error_code;
+        }
+    } else {
+        print "skipping $command\n";
+    }
+}
+
+exit 0;
+
+
+sub my_die
+{
+    my $msg = shift;            # Warning message on die
+    my $skycal_id = shift;        # Diff identifier
+    my $exit_code = shift;      # Exit code to add
+
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
+    warn($msg);
+    if (defined $skycal_id and not $no_update) {
+        my $command = "$staticskytool -skycal_id $skycal_id -fault $exit_code";
+	$command .= " -addskycalresult";
+	$command .= (" -dtime_script " . ((DateTime->now->mjd - $mjd_start) * 86400));
+	$command .= " -hostname $host" if defined $host;
+	$command .= " -path_base $outroot" if defined $outroot;
+	$command .= " -dbname $dbname" if defined $dbname;
+        run(command => $command, verbose => $verbose);
+    }
+    exit $exit_code;
+}
+
+END {
+    my $exit = $?;
+    system("sync") == 0 or die "failed to execute sync: $!";
+    $? = $exit;
+}
+
+__END__
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/stack_skycell.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/stack_skycell.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/stack_skycell.pl	(revision 33415)
@@ -104,5 +104,5 @@
 }
 
-$ipprc->redirect_output($logDest) or my_die( "Unable to redirect output", $stack_id, $PS_EXIT_SYS_ERROR ) if $redirect;
+$ipprc->redirect_to_logfile($logDest) or my_die( "Unable to redirect output", $stack_id, $PS_EXIT_SYS_ERROR ) if $redirect;
 
 my $neb;
@@ -243,4 +243,5 @@
     print $listFile "END\n\n";
 }
+close($listFile);
 
 print "I've checked everything: $stack_id\n";
@@ -491,4 +492,17 @@
     &my_die("Couldn't find expected output file: $file",  $stack_id, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($file);
 
+    # Funpack to confirm we've really made things correctly
+    my $diskfile = $ipprc->file_resolve($file);
+    if ($diskfile =~ /fits/) {
+        my $funpack  = can_run('funpack') or &my_die ("Can't find funpack", $stack_id, $PS_EXIT_SYS_ERROR);
+	my $check_command = "$funpack -S $diskfile > /dev/null";
+	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	    run(command => $check_command, verbose => $verbose);
+	if (!$success) {
+	    &my_die("Output file not a valid fits file: $file", $stack_id, $PS_EXIT_SYS_ERROR);
+	}
+    }
+    #####
+
     if ($replicate and $neb) {
         $ipprc->replicate_file($file) or &my_die("failed to replicate: $file\n",  $stack_id, $PS_EXIT_SYS_ERROR);
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/videophot_process.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/videophot_process.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/videophot_process.pl	(revision 33415)
@@ -8,7 +8,4 @@
 my $host = hostname();
 my $date = `date`;
-print "\n\n";
-print "Starting script $0 on $host at $date\n\n";
-
 use DateTime;
 my $mjd_start = DateTime->now->mjd;   # MJD of starting script
@@ -25,24 +22,34 @@
 use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
 use Pod::Usage qw( pod2usage );
+use File::Temp qw( tempfile );
+use File::Basename qw( basename );
 
 # Look for programs we need
 my $missing_tools;
 my $vptool = can_run('vptool') or (warn "Can't find vptool" and $missing_tools = 1);
-my $regtool = can_run('regtool') or (warn "Can't find regtool" and $missing_tools = 1);
 my $psvideophot = can_run('psvideophot') or (warn "Can't find psvideophot" and $missing_tools = 1);
+my $dumpvideo = can_run('dumpvideo') or (warn "Can't find dumpvideo" and $missing_tools = 1);
 my $listvideocells = can_run('listvideocells.pl') or (warn "Can't find listvideocells.pl" and $missing_tools = 1);
+my $dsreg = can_run('dsreg') or (warn "Can't find dsreg" and $missing_tools = 1);
+my $nebrepair = can_run('neb-repair') or (warn "Can't find neb-repair" and $missing_tools = 1);
 if ($missing_tools) {
     warn("Can't find required tools.");
     exit($PS_EXIT_CONFIG_ERROR);
 }
-my ($vp_id, $exp_id, $camera, $outroot, $dbname, $verbose, $no_update, $no_op, $redirect);
+my ($vp_id, $camera, $outroot, $dest_id, $dbname, $verbose, $no_update, $no_op, $redirect, $save_temps);
+my ($product, $save_video_cube, $ds_dbname, $ds_dbhost);
 
 GetOptions(
     'vp_id=s'           => \$vp_id,
-    'exp_id=s'          => \$exp_id,
     'camera=s'          => \$camera, 
     'outroot=s'         => \$outroot,
+    'dest_id=s'         => \$dest_id,
+    'product=s'         => \$product,
+    'save-video-cube'   => \$save_video_cube,
+    'ds_dbname=s'       => \$ds_dbname,
+    'ds_dbhost=s'       => \$ds_dbhost,
     'dbname|d=s'        => \$dbname,
     'verbose'           => \$verbose,
+    'save-temps'        => \$save_temps,
     'no-update'         => \$no_update, # Don't update the database?
     'no-op'             => \$no_op,     # Don't do any operations?
@@ -52,41 +59,50 @@
 pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
 pod2usage(
-    -msg => "Required options: --vp_id --exp_id --outroot --camera",
+    -msg => "Required options: --vp_id --outroot --camera",
     -exitval => 3,
           ) unless defined $vp_id
-            and defined $exp_id
             and defined $camera
             and defined $outroot;
 
+pod2usage(
+    -msg => " --product -ds_dbname and --dsdbhost are required if --dest_id",
+    -exitval => 3,
+          ) if ($dest_id and !( defined $product and defined $ds_dbname and defined $ds_dbhost));
+
 
 $no_update = 1 if $no_op;
-
-my $ipprc = PS::IPP::Config->new($camera) or my_die( "Unable to set up", $vp_id, 'nochip', 'nocell', $PS_EXIT_CONFIG_ERROR ); # IPP configuration
+$vptool .= " -dbname $dbname" if $dbname;
+
+my $ipprc = PS::IPP::Config->new($camera) or my_die( "Unable to set up", $vp_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
 
 my $logDest = $ipprc->filename("LOG.EXP", $outroot);
-$ipprc->redirect_output($logDest) or my_die( "Unable to redirect output", $vp_id, 'nochip', 'nocell', $PS_EXIT_SYS_ERROR ) if $redirect;
-
-my $source_id = $ipprc->source_id($dbname, $PS_TABLE_ID_DIFF);
-
-# Get list of components for subtraction
+if ($redirect) {
+    $ipprc->redirect_to_logfile($logDest) or my_die( "Unable to redirect output", $vp_id, $PS_EXIT_SYS_ERROR );
+
+    print "\n\n";
+    print "Starting script $0 on $host at $date\n\n";
+}
+
+# Get list of chips with pending video cells
 my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
 my $files;
 {
-#    my $command = "$vptool -pendingimfile -vp_id $vp_id";
-#    $command .= " -dbname $dbname" if defined $dbname;
-    my $command = "$regtool -processedimfile -video_cells -exp_id $exp_id";
-    $command .= " -dbname $dbname" if defined $dbname;
+    my $command = "$vptool -pendingimfile -vp_id $vp_id";
     my ( $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);
-        &my_die("Unable to perform vptool -pendingimfile: $error_code", $vp_id, 'nochip', 'nocell', $error_code);
-    }
-
-    my $metadata = $mdcParser->parse(join "", @$stdout_buf) or
-        &my_die("Unable to parse metadata config doc", $vp_id, 'nochip', 'nocell', $PS_EXIT_PROG_ERROR);
-    $files = parse_md_list($metadata) or
-        &my_die("Unable to parse metadata list", $vp_id, 'nochip', 'nocell', $PS_EXIT_PROG_ERROR);
-}
+        &my_die("Unable to perform vptool -pendingimfile: $error_code", $vp_id, $error_code);
+    }
+
+    my $output = join "", @$stdout_buf;
+    if ($output) {
+        my $metadata = $mdcParser->parse($output) or
+            &my_die("Unable to parse metadata config doc", $vp_id, $PS_EXIT_PROG_ERROR);
+        $files = parse_md_list($metadata) or
+        &my_die("Unable to parse metadata list", $vp_id, $PS_EXIT_PROG_ERROR);
+    }
+}
+
 
 foreach my $file (@$files) {
@@ -95,5 +111,5 @@
 
     my $resolved = $ipprc->file_resolve($uri);
-    &my_die("Unable to resolve $uri", $vp_id, $class_id, 'nocell', $PS_EXIT_UNKNOWN_ERROR) unless $resolved;
+    &my_die("Unable to resolve $uri", $vp_id, $PS_EXIT_UNKNOWN_ERROR) unless $resolved;
 
     my $command = "$listvideocells --file $resolved";
@@ -101,18 +117,22 @@
         run(command => $command, verbose => $verbose);
     unless ($success) {
+        check_input_file($uri, $vp_id, $class_id, 'nocell');
         $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
-        &my_die("Unable to perform $command: $error_code", $vp_id, $class_id, 
-            'nocell', $error_code);
+        &my_die("Unable to perform $command: $error_code", $vp_id, $error_code);
     }
 
     my @video_cells = split "\n", (join "", @$stdout_buf);
-    &my_die("No video cells found in $uri", $vp_id, $class_id, 
-            'nocell', $PS_EXIT_UNKNOWN_ERROR) unless scalar @video_cells;
+    &my_die("No video cells found in $uri", $vp_id, $PS_EXIT_UNKNOWN_ERROR) unless scalar @video_cells;
 
     foreach my $cell_id (@video_cells) {
-        my $command = "$psvideophot $outroot";
+        my $path_base =  "$outroot.$class_id.$cell_id";
+        my $error;
+        my $output = $ipprc->prepare_output("PSVIDEOPHOT.OUTPUT", $path_base, undef, 1, \$error)
+            or &my_die("failed to prepare output file for PSVIDEOPHOT.OUTPUT", $vp_id, $error);
+        my $command = "$psvideophot $output";
         $command .= " -file $uri";
+        $command .= " -class_id $class_id";
         $command .= " -cell_id $cell_id";
-        $command .= " -dbname $dbname" if defined $dbname;
+        my $vpstart = DateTime->now->mjd;
         unless ($no_op) {
             my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
@@ -120,16 +140,31 @@
             unless ($success) {
                 $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
-                &my_die("Unable to perform ppSub: $error_code", $vp_id, $class_id, $cell_id, $error_code);
+                &my_die("Unable to perform psvideophot: $error_code", $vp_id, $error_code);
             }
-
-            # check_output($outputStats, 1);
         } else {
             print "Not executing: $command\n";
         }
+        # dump the video cell and the video table to a file
+        if ($save_video_cube) {
+            my $output = $path_base . ".fits";
+            my $extname = "$cell_id";
+            my $command = "$dumpvideo $uri $extname $output -includetable";
+            unless ($no_op) {
+                my ( $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);
+                    &my_die("Unable to perform dumpvideo: $error_code", $vp_id, $error_code);
+                }
+            } else {
+                print "Not executing: $command\n";
+            }
+        }
+
         unless ($no_update) {
-            my $command = "$vptool -addvpcell -vp_id $vp_id -class_id $class_id -cell_id $cell_id";
-            $command .= " -path_base $outroot";
-
-            $command .= " -dbname $dbname" if defined $dbname;
+            my $command = "$vptool -addprocessedcell -vp_id $vp_id -class_id $class_id -cell_id $cell_id";
+            $command .= " -path_base $path_base";
+            $command .= " -hostname $host";
+            $command .= " -dtime_photom " . ((DateTime->now->mjd - $vpstart) * 86400);
 
             my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
@@ -138,18 +173,113 @@
                 $error_code = (($error_code >> 8) or $PS_EXIT_PROG_ERROR);
                 my $err_message = "Unable to perform vptool -addvpcell";
-                &my_die("$err_message: $error_code", $vp_id, $class_id, $cell_id, $error_code);
+                &my_die("$err_message: $error_code", $vp_id, $error_code);
             }
         }
     }
 }
+if ($dest_id) {
+    my $command = "$vptool -processedcell -vp_id $vp_id";
+    my ( $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);
+        &my_die("Unable to perform vptool -pendingimfile: $error_code", $vp_id, $error_code);
+    }
+
+    my $output = join "", @$stdout_buf;
+    my $cells;
+    if ($output) {
+        my $metadata = $mdcParser->parse($output) or
+            &my_die("Unable to parse metadata config doc", $vp_id, $PS_EXIT_PROG_ERROR);
+        $cells = parse_md_list($metadata) or
+            &my_die("Unable to parse metadata list", $vp_id, $PS_EXIT_PROG_ERROR);
+    }
+    my ($reglist, $reglistName) = tempfile("/tmp/filelist.$vp_id.XXXX", UNLINK => !$save_temps);
+
+    if ($redirect) {
+        print $reglist "$logDest|||text|\n";
+    }
+    # list of extensions and types for files to distribute
+    my @exts = qw( vpt );
+    my @types = qw( table );
+    if ($save_video_cube) {
+        push @exts, 'fits';
+        push @types, 'fits';
+    }
+    foreach my $cell (@$cells) {
+        my $path_base = $cell->{path_base};
+        for (my $i = 0; $i < scalar @exts; $i++) {
+            my $file = "$path_base.$exts[$i]";
+            my $type = $types[$i];
+
+            print $reglist "$file|||$type|\n";
+        }
+    }
+    close $reglist or &my_die("failed to close $reglistName", $vp_id, $PS_EXIT_UNKNOWN_ERROR);
+    unless ($no_update or $no_op) {
+        my $fileset = basename($outroot);
+        my $command = "$dsreg --add $fileset --product $product --type dump --list $reglistName";
+        $command .= " --abspath --link";
+        $command .= " --dbname $ds_dbname --dbhost $ds_dbhost";
+        my ( $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);
+             &my_die("Unable to perform $command error_code: $error_code", $vp_id, $error_code);
+        }
+    }
+}
+
+unless ($no_update) {
+    my $command = "$vptool -updaterun -vp_id $vp_id -set_state full"; 
+    $command .= " -set_outroot $outroot";
+    $command .= " -set_hostname $host";
+    $command .= " -set_dtime_script " . ((DateTime->now->mjd - $mjd_start) * 86400);
+
+    my ( $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);
+        my $err_message = "Unable to perform vptool -updaterun";
+        &my_die("$err_message: $error_code", $vp_id, $error_code);
+    }
+}
 
 exit 0;
+
+sub check_input_file {
+    my $uri = shift;
+    my $vp_id = shift;
+    my $class_id = shift;
+    my $cell_id = shift;
+    my $resolved = $ipprc->file_resolve($uri);
+
+    my $tryrepair = 0;
+    if (!-e $resolved) {
+        printf STDERR "instance $resolved for $uri does not exist\n";
+        $tryrepair = 1;
+    } elsif (-s $resolved == 0) {
+        printf STDERR "instance $resolved for $uri is empty\n";
+        $tryrepair = 1;
+    }
+    if ($tryrepair) {
+        my $scheme = file_scheme($uri);
+        if ($scheme and ($scheme = 'neb')) {
+            my $command = "$nebrepair $uri";
+            my ( $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);
+                my $err_message = "Unable to perform nebrepair";
+                &my_die("$err_message: $error_code", $vp_id, $error_code);
+            }
+       }
+    }
+}
 
 sub my_die
 {
     my $msg = shift;            # Warning message on die
-    my $vp_id = shift;
-    my $class_id = shift;       # chip identifier
-    my $cell_id = shift;       # chip identifier
+    my $vp_id = shift;          # vpRun id
     my $exit_code = shift;      # Exit code to add
 
@@ -157,11 +287,12 @@
 
     warn($msg);
-    if (defined $vp_id and defined $class_id and not $no_update) {
-        my $command = "$vptool -vp_id $vp_id -class_id $class_id -cell_id -fault $exit_code";
-        $command .= " -addvpcell";
-        $command .= (" -dtime_script " . ((DateTime->now->mjd - $mjd_start) * 86400));
-        $command .= " -hostname $host" if defined $host;
-        $command .= " -path_base $outroot" if defined $outroot;
-        $command .= " -dbname $dbname" if defined $dbname;
+    if (defined $vp_id and not $no_update) {
+        my $command = "$vptool -vp_id $vp_id";
+        $command .= " -updaterun";
+        $command .= " -set_fault $exit_code";
+        $command .= " -set_hostname $host" if defined $host;
+        $command .= " -set_outroot $outroot" if defined $outroot;
+        $command .= " -set_dtime_script " . ((DateTime->now->mjd - $mjd_start) * 86400);
+
         run(command => $command, verbose => $verbose);
     }
@@ -169,9 +300,3 @@
 }
 
-END {
-    my $exit = $?;
-    system("sync") == 0 or die "failed to execute sync: $!";
-    $? = $exit;
-}
-
 __END__
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/warp_overlap.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/warp_overlap.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/warp_overlap.pl	(revision 33415)
@@ -57,5 +57,5 @@
 
 my $ipprc = PS::IPP::Config->new( $camera ) or my_die( "Unable to set up", $warp_id, $PS_EXIT_CONFIG_ERROR ); # IPP configuration
-$ipprc->redirect_output($logfile) or my_die( "Unable to redirect output", $warp_id, $PS_EXIT_SYS_ERROR ) if $logfile;
+$ipprc->redirect_to_logfile($logfile) or my_die( "Unable to redirect output", $warp_id, $PS_EXIT_SYS_ERROR ) if $logfile;
 
 &my_die("Tessellation identifier not provided: $tess_dir", $warp_id, $PS_EXIT_SYS_ERROR) unless $tess_dir ne "NULL";
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/warp_skycell.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/warp_skycell.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/warp_skycell.pl	(revision 33415)
@@ -38,5 +38,4 @@
 
 my ($warp_id, $skycell_id, $warp_skyfile_id, $tess_dir, $reduction, $camera, $dbname, $outroot, $threads, $run_state, $magicked, $verbose, $no_update, $no_op, $redirect, $save_temps);
-my $zaplog = 0;
 
 GetOptions(
@@ -55,5 +54,4 @@
     'no-update'           => \$no_update, # Don't update the database?
     'no-op'               => \$no_op, # Don't do any operations?
-    'zaplog'              => \$zaplog, # delete previous logfile
     'redirect-output'     => \$redirect,
     'save-temps'          => \$save_temps, # Save temporary files?
@@ -77,9 +75,9 @@
 my $do_stats;
 if ($run_state eq 'new') {
-    $logDest = prepare_output("LOG.EXP", $outroot, $skycell_id, $zaplog);
+    $logDest = $ipprc->filename("LOG.EXP", $outroot, $skycell_id);
     $traceDest = prepare_output("TRACE.EXP", $outroot, $skycell_id, 1);
     $do_stats = 1;
 } elsif ($run_state eq 'update')  {
-    $logDest = prepare_output("LOG.EXP.UPDATE", $outroot, $skycell_id, 1);
+    $logDest = $ipprc->filename("LOG.EXP.UPDATE", $outroot, $skycell_id);
     $traceDest = prepare_output("TRACE.EXP.UPDATE", $outroot, $skycell_id, 1);
 } else {
@@ -93,5 +91,5 @@
 }
 
-$ipprc->redirect_output($logDest) or my_die( "Unable to redirect output", $warp_id, $skycell_id, $tess_dir, $PS_EXIT_SYS_ERROR ) if $redirect;
+$ipprc->redirect_to_logfile($logDest) or my_die( "Unable to redirect output", $warp_id, $skycell_id, $tess_dir, $PS_EXIT_SYS_ERROR ) if $redirect;
 
 # Recipes to use based on reduction class
@@ -333,5 +331,5 @@
             }
         }
-
+	print "Quality: $quality\n";
         unless ($no_update) {
             # XXX change -tess_id to -tess_dir when db is updated
@@ -358,18 +356,42 @@
         }
     } else {
+
+	my $quality = 0;
+
+	unless ($ipprc->file_exists($outputPSF)) {
+	    $quality = 8006; # bad data.
+	}
+#         if (!$quality) {
+#             check_output($outputImage, 0);
+#             check_output($outputMask, 0);
+#             check_output($outputWeight, 0);
+#             check_output($outputSources, 1);
+#             check_output($outputPSF, 1) if metadataLookupBool($recipe, 'PSF')  ;
+#         }
+	print "Quality: $quality\n";
+
         # $run_state eq 'update'
         unless ($no_update) {
-            my $command = "$warptool -tofullskyfile";
-            $command .= " -warp_id $warp_id";
-            $command .= " -skycell_id $skycell_id";
-            $command .= " -set_magicked $magicked" if $magicked;
-            $command .= " -dbname $dbname"   if defined $dbname;
-
-            my ( $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 warptool -addwarped: $error_code\n");
-                exit($error_code);
+	    my $try = 0;
+	    while (1) {
+                $try++;
+                my $command = "$warptool -tofullskyfile";
+                $command .= " -warp_id $warp_id";
+                $command .= " -skycell_id $skycell_id";
+                $command .= " -set_magicked $magicked" if $magicked;
+                $command .= " -set_quality $quality" if $quality;
+                $command .= " -dbname $dbname"   if defined $dbname;
+
+                my ( $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 warptool -addwarped: $error_code\n");
+                    exit($error_code) if $try >= 2;
+                    sleep 10;
+                    warn("Trying again\n");
+                } else {
+                    last;
+                }
             }
         }
@@ -407,4 +429,17 @@
 
     &my_die("Couldn't find expected output file: $file",  $warp_id, $skycell_id, $tess_dir, $PS_EXIT_SYS_ERROR) unless $ipprc->file_exists($file);
+
+    # Funpack to confirm we've really made things correctly
+    my $diskfile = $ipprc->file_resolve($file);
+    if ($diskfile =~ /fits/) {
+        my $funpack  = can_run('funpack') or &my_die ("Can't find funpack", $warp_id, $skycell_id, $tess_dir, $PS_EXIT_SYS_ERROR);
+	my $check_command = "$funpack -S $diskfile > /dev/null";
+	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	    run(command => $check_command, verbose => $verbose);
+	if (!$success) {
+	    &my_die("Output file not a valid fits file: $file", $warp_id, $skycell_id, $tess_dir, $PS_EXIT_SYS_ERROR);
+	}
+    }
+    #####
 
     if ($replicate and $neb) {
Index: branches/meh_branches/ppstack_test/ippScripts/scripts/whichimage
===================================================================
--- branches/meh_branches/ppstack_test/ippScripts/scripts/whichimage	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippScripts/scripts/whichimage	(revision 33415)
@@ -60,5 +60,5 @@
 
     my $sign = 1;
-    if ($h < 0) {
+    if ($h =~ /-/) {
         $sign = -1;
         $h = - $h;
@@ -76,5 +76,5 @@
 
     my $sign = 1;
-    if ($d < 0) {
+    if ($d =~ /-/) {
         $sign = -1;
         $d = - $d;
Index: branches/meh_branches/ppstack_test/ippTasks/Makefile.am
===================================================================
--- branches/meh_branches/ppstack_test/ippTasks/Makefile.am	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTasks/Makefile.am	(revision 33415)
@@ -21,4 +21,5 @@
 	destreak.cleanup.pro \
 	diff.pro \
+	skycal.pro \
 	stack.pro \
 	staticsky.pro \
@@ -37,4 +38,5 @@
 	minidvodb.pro \
 	minidvodbcopy.pro \
+	mergedvodb.pro \
 	nightly_science.pro \
 	burntool.pro \
@@ -42,5 +44,6 @@
 	background.pro \
 	diffphot.pro \
-	lap.pro
+	lap.pro \
+	vp.pro
 
 other_files = \
Index: branches/meh_branches/ppstack_test/ippTasks/addstar.pro
===================================================================
--- branches/meh_branches/ppstack_test/ippTasks/addstar.pro	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTasks/addstar.pro	(revision 33415)
@@ -26,4 +26,7 @@
     active true
   end
+  task addstar.exp.load.staticsky_multi
+    active true
+  end  
   task addstar.exp.run
     active true
@@ -41,4 +44,7 @@
     active false
   end
+  task addstar.exp.load.staticsky_multi
+    active false
+  end
   task addstar.exp.run
     active false
@@ -47,10 +53,28 @@
 
 macro addstar.revert.off
-  task addstar.revert
+  task addstar.revert.cam
+    active false
+  end
+ task addstar.revert.stack
+    active false
+  end
+ task addstar.revert.staticsky
+    active false
+  end
+ task addstar.revert.staticsky_multi
     active false
   end
 end
 macro addstar.revert.on
-  task addstar.revert
+  task addstar.revert.cam
+    active true
+  end
+  task addstar.revert.stack
+    active true
+  end
+  task addstar.revert.staticsky_multi
+    active true
+  end
+  task addstar.revert.staticsky
     active true
   end
@@ -59,6 +83,10 @@
 
 # this variable will cycle through the known database names
-$addstar_DB = 0
-$addstar_revert_DB = 0
+$addstar_DB = 0 
+#this may not work for more databases (addstar) will do that later)
+$addstar_revert_DB_C = 0
+$addstar_revert_DB_S = 0
+$addstar_revert_DB_SS = 0
+$addstar_revert_DB_SSM = 0
 
 
@@ -175,4 +203,5 @@
   end
 end
+
 task	       addstar.exp.load.staticsky
   host         local
@@ -229,4 +258,61 @@
   end
 end
+
+task	       addstar.exp.load.staticsky_multi
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/addstar.exp.log
+
+  task.exec
+   # if ($LABEL:n == 0) break
+    $run = addtool -pendingexp -stage staticsky_multi
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$addstar_DB
+      $run = $run -dbname $DB:$addstar_DB
+      $addstar_DB ++
+      if ($addstar_DB >= $DB:n) set addstar_DB = 0
+    end
+    add_poll_args run
+    add_poll_labels run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout addPendingExp -key add_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook addPendingExp
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup addPendingExp
+  end
+
+  # default exit status
+  task.exit    default
+    showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+
 # run the addstar script on pending exposures
 task	       addstar.exp.run
@@ -252,6 +338,8 @@
     book getword addPendingExp $pageName exp_tag -var EXP_TAG
     book getword addPendingExp $pageName add_id -var ADD_ID
+    book getword addPendingExp $pageName stage_extra1 -var STAGE_EXTRA1
     book getword addPendingExp $pageName stageroot -var STAGEROOT
-    book getword addPendingExp $pageName stage -var STAGE  
+    book getword addPendingExp $pageName stage -var STAGE
+    book getword addPendingExp $pageName stage_id -var STAGE_ID
     book getword addPendingExp $pageName workdir -var WORKDIR_TEMPLATE
     book getword addPendingExp $pageName reduction -var REDUCTION
@@ -285,4 +373,7 @@
 	sprintf outroot "%s.add.%s" $STAGEROOT $ADD_ID
     end
+    if ("$STAGE" == "staticsky_multi")
+	sprintf outroot "%s.add.%s" $STAGEROOT $ADD_ID
+    end
     if ("$STAGE" == "stack")
 	sprintf outroot "%s.add.%s" $STAGEROOT $ADD_ID
@@ -296,4 +387,10 @@
     if ("$REDUCTION" != "NULL")
       $run = $run --reduction $REDUCTION
+    end
+    if ("$STAGE" == "staticsky_multi")
+      $run = $run --multi_num $STAGE_EXTRA1
+    end
+    if ("$STAGE" == "cam") 
+      $run = $run --stage_id $STAGE_ID
     end
     if ("$IMAGE_ONLY" == "T")
@@ -340,5 +437,5 @@
 end
 
-task addstar.revert
+task addstar.revert.cam
   host         local
 
@@ -354,13 +451,13 @@
   task.exec
     if ($LABEL:n == 0) break
-    $run = addtool -revertprocessedexp
-    if ($DB:n == 0)
-      option DEFAULT
-    else
-      # save the DB name for the exit tasks
-      option $DB:$addstar_revert_DB
-      $run = $run -dbname $DB:$addstar_revert_DB
-      $addstar_revert_DB ++
-      if ($addstar_revert_DB >= $DB:n) set addstar_revert_DB = 0
+    $run = addtool -revertprocessedexp -stage cam
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$addstar_revert_DB_C
+      $run = $run -dbname $DB:$addstar_revert_DB_C
+      $addstar_revert_DB_C ++
+      if ($addstar_revert_DB_C >= $DB:n) set addstar_revert_DB_C = 0
     end
     add_poll_labels run
@@ -386,2 +483,140 @@
   end
 end
+task addstar.revert.stack
+  host         local
+
+  periods      -poll 5.0
+  periods      -exec 60.0
+  periods      -timeout 1200
+  npending     1
+  active        false
+
+  stdout NULL
+  stderr $LOGDIR/revert.log
+
+  task.exec
+    if ($LABEL:n == 0) break
+    $run = addtool -revertprocessedexp -stage stack
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$addstar_revert_DB_S
+      $run = $run -dbname $DB:$addstar_revert_DB_S
+      $addstar_revert_DB_S ++
+      if ($addstar_revert_DB_S >= $DB:n) set addstar_revert_DB_S = 0
+    end
+    add_poll_labels run
+    command $run
+  end
+
+  # success
+  task.exit    0
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+task addstar.revert.staticsky
+  host         local
+
+  periods      -poll 5.0
+  periods      -exec 60.0
+  periods      -timeout 1200
+  npending     1
+  active        false
+
+  stdout NULL
+  stderr $LOGDIR/revert.log
+
+  task.exec
+    if ($LABEL:n == 0) break
+    $run = addtool -revertprocessedexp -stage staticsky
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$addstar_revert_DB_SS
+      $run = $run -dbname $DB:$addstar_revert_DB_SS
+      $addstar_revert_DB_SS ++
+      if ($addstar_revert_DB_SS >= $DB:n) set addstar_revert_DB_SS = 0
+    end
+    add_poll_labels run
+    command $run
+  end
+
+  # success
+  task.exit    0
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+task addstar.revert.staticsky_multi
+  host         local
+
+  periods      -poll 5.0
+  periods      -exec 60.0
+  periods      -timeout 1200
+  npending     1
+  active        false
+
+  stdout NULL
+  stderr $LOGDIR/revert.log
+
+  task.exec
+    if ($LABEL:n == 0) break
+    $run = addtool -revertprocessedexp -stage staticsky_multi
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$addstar_revert_DB_SSM
+      $run = $run -dbname $DB:$addstar_revert_DB_SSM
+      $addstar_revert_DB_SSM ++
+      if ($addstar_revert_DB_SSM >= $DB:n) set addstar_revert_DB_SSM = 0
+    end
+    add_poll_labels run
+    command $run
+  end
+
+  # success
+  task.exit    0
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
Index: branches/meh_branches/ppstack_test/ippTasks/destreak.pro
===================================================================
--- branches/meh_branches/ppstack_test/ippTasks/destreak.pro	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTasks/destreak.pro	(revision 33415)
@@ -206,5 +206,6 @@
     book getword magicToDS $pageName magicked -var MAGICKED
     book getword magicToDS $pageName dbname -var DBNAME
-
+    book getword magicToDS $pageName diff_tess_id -var DIFF_TESS_ID
+    book getword magicToDS $pageName mismatched_tess -var MISMATCHED_TESS
 
     substr $COMPONENT 0 3 COMP_HEAD
@@ -224,4 +225,8 @@
     $run = magic_destreak.pl --magic_ds_id $MAGIC_DS_ID --camera $CAMERA --exp_id $EXP_ID --streaks_path_base $STREAKS_PATH_BASE --inv_streaks_path_base $INV_STREAKS_PATH_BASE --streaks $STREAKS --inv_streaks $INV_STREAKS --stage $STAGE --stage_id $STAGE_ID --component $COMPONENT --uri $URI --path_base $PATH_BASE --cam_path_base $CAM_PATH_BASE --cam_reduction $CAM_REDUCTION --outroot $WORKDIR --logfile $logfile --recoveryroot $RECROOT --replace $REPLACE --magicked $MAGICKED --run-state $RUN_STATE
 
+    if ($MISMATCHED_TESS) 
+        book getword magicToDS $pageName diff_tess_id -var DIFF_TESS_ID
+        $run = $run --mismatched_tess --diff_tess_id $DIFF_TESS_ID
+    end
     add_standard_args run
 
Index: branches/meh_branches/ppstack_test/ippTasks/diskbalance.pro
===================================================================
--- branches/meh_branches/ppstack_test/ippTasks/diskbalance.pro	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTasks/diskbalance.pro	(revision 33415)
@@ -15,4 +15,8 @@
 book init balanceControl
 
+
+book init targetPending
+book init targetControl
+
 macro balance.reset
   book init balancePending
@@ -41,4 +45,30 @@
 end
 
+macro target.reset
+  book init targetPending
+end
+
+macro target.status
+  book listbook targetPending
+end
+
+macro target.on
+  task target.load
+    active true
+  end
+  task target.run
+    active true
+  end
+end
+
+macro target.off
+  task target.load
+    active false
+  end
+  task target.run
+    active false
+  end
+end
+ 
 macro set.host.for.balance
   if ($0 != 2)
@@ -71,4 +101,15 @@
   book setword balanceControl control mDestinations $2
 end
+
+macro set.balance.range
+  if ($0 != 3)
+    echo "USAGE: set.balance.range (SO_ID_START) (SO_ID_RANGE)"
+    break
+  end
+
+  $BALANCE_SO_ID_START = $1
+  $BALANCE_SO_ID_RANGE = $2
+end
+
 
 macro show.balance.numbers
@@ -92,4 +133,25 @@
 end
 
+macro set.target.range
+  if ($0 != 3)
+    echo "USAGE: set.target.range (SO_ID_START) (SO_ID_RANGE)"
+    break
+  end
+
+  $TARGET_SO_ID_START = $1
+  $TARGET_SO_ID_RANGE = $2
+end
+
+
+macro show.target.numbers
+  echo "target so_ids: $TARGET_SO_ID_START $TARGET_SO_ID_RANGE"
+end
+
+macro show.targetPending
+  book npages targetPending -var N
+  echo "$N pages in book"
+  book listbook targetPending
+end
+
 # the balance process interacts with only the single Nebulous server
 
@@ -104,5 +166,5 @@
 $BALANCE_LIMIT       = 500
 $BALANCE_OFFSET      = 0
-$BALANCE_TARGET      = "SOURCE"
+$BALANCE_TARGET      = SOURCE
 # Select Nebulous objects which should be shuffled
 task           balance.load
@@ -250,5 +312,155 @@
 
 
-
-    
-
+# This value will need to be incremented periodically.  It looks like we grow by about 5e7 so_ids per month.
+$TARGET_BIG_SO_ID   = 6e8
+$TARGET_SO_ID_START = int($TARGET_BIG_SO_ID * rnd(0)) 
+$TARGET_SO_ID_RANGE = 500000
+$TARGET_LIMIT       = 500
+$TARGET_OFFSET      = 0
+$TARGET_TARGET      = SOURCE
+# Select Nebulous objects which should be shuffled
+task           target.load
+  host         local
+
+  periods      -poll 60
+  periods      -exec 60
+  periods      -timeout 1500
+  npending     1
+
+  # logs
+  stdout NULL
+  stderr $LOGSUBDIR/target.log
+    
+  task.exec
+    book npages targetPending -var N
+    if ($N > 2000)
+      process_cleanup targetPending
+      break
+    end
+
+    command neb-admin --host $NEB_HOST --db $NEB_DB --user $NEB_USER --pass $NEB_PASS --pendingtarget --limit $TARGET_LIMIT --offset $TARGET_OFFSET --so_id_start $TARGET_SO_ID_START --so_id_range $TARGET_SO_ID_RANGE  
+  end
+
+   # success : 0 -- we did not hit the limit, advance so_id counter
+  task.exit 0
+    # advance the so_id counter
+    $TARGET_SO_ID_START = $TARGET_SO_ID_START + $TARGET_SO_ID_RANGE
+    $TARGET_OFFSET = 0
+    # convert 'stdout' to book format
+    ipptool2book stdout targetPending -key key -uniq -setword pantaskState INIT
+
+    if ($VERBOSE > 2)
+      book listbook targetPending
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup targetPending
+  end
+
+  # success : 1 -- we DID hit the limit, do NOT advance so_id counter 
+  task.exit 1
+    # convert 'stdout' to book format
+    ipptool2book stdout targetPending -key key -uniq -setword pantaskState INIT
+    $TARGET_OFFSET = $TARGET_OFFSET + $TARGET_LIMIT
+    if ($VERBOSE > 2)
+      book listbook targetPending
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup targetPending
+  end
+
+  # out of so_id range, reset
+  task.exit 10
+    # advance the so_id counter
+    $TARGET_SO_ID_START = 0
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+# task to execute shuffles
+task            target.run
+  periods       -poll 0.5
+  periods       -exec 5
+  periods       -timeout 5
+  npending      200 
+
+  task.exec
+    # if we are unable to run the 'exec', use a long retry time
+    periods -exec 5
+
+    book npages targetPending -var N
+    if ($NETWORK == 0) break
+    if ($N == 0) break
+
+    # look for new objects in targetPending
+    book getpage targetPending 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword targetPending $pageName pantaskState RUN
+
+    book getword targetPending $pageName key              -var KEY
+    book getword targetPending $pageName source_name      -var SOURCE
+    book getword targetPending $pageName source_host      -var SOURCE_HOST
+    book getword targetPending $pageName destination_name -var DESTINATION
+    book getword targetPending $pageName destination_host -var DEST_HOST
+    book getword targetPending $pageName destination_uri  -var DEST_URI
+    book getword targetPending $pageName ins_id           -var INS_ID
+
+    # Fix this with multihost restriction when possible.
+    if ("$TARGET_TARGET" == "DESTINATION")
+       set.host.for.balance $DEST_HOST
+    else 
+       set.host.for.balance $SOURCE_HOST
+    end
+    
+
+    stdout NULL
+    stderr $LOGSUBDIR/target.log
+
+    $run = neb-insedit --key $KEY --ins_id $INS_ID --uri $DEST_URI --volume $DESTINATION 
+    $run = $run --db $NEB_DB --host $NEB_HOST --user $NEB_USER --pass $NEB_PASS
+    $run = $run --neb_host http://ippc04/nebulous
+    
+    # save the pageName for future reference below
+    options $pageName
+
+    # create the command line
+    if ($VERBOSE > 1)
+	echo command $run
+    end
+    periods -exec 0.05
+    command $run
+  end
+
+  # default exit status
+  task.exit     default
+    process_exit targetPending $options:0 $JOB_STATUS
+  end
+
+  task.exit    crash
+    showcommand crash
+    book setword targetPending $options:0 pantaskState CRASH
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword targetPending $options:0 pantaskState TIMEOUT
+  end
+end
+
+
Index: branches/meh_branches/ppstack_test/ippTasks/fake.pro
===================================================================
--- branches/meh_branches/ppstack_test/ippTasks/fake.pro	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTasks/fake.pro	(revision 33415)
@@ -146,5 +146,7 @@
 
     # specify choice of local or remote host based on camera and fake (class_id)
-    set.host.for.camera $CAMERA $CLASS_ID
+    # XXX: this is not worth the trouble. use anyhost
+    # set.host.for.camera $CAMERA $CLASS_ID
+    host anyhost
 
     # set the WORKDIR variable
Index: branches/meh_branches/ppstack_test/ippTasks/ipphosts.mhpcc.config
===================================================================
--- branches/meh_branches/ppstack_test/ippTasks/ipphosts.mhpcc.config	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTasks/ipphosts.mhpcc.config	(revision 33415)
@@ -6,135 +6,151 @@
 # randomly.  the only exception is ipp004, which is the DVO db host
 
-# NOTE: wave 1 machines have 10TB; wave 2 & 3 machines have 20TB
-# for balance, use 1 host per 10 TB in the partition
+# I've allocated hosts based on disk size. 
+# 40TB => 2 units of skycells
+# 20TB => 1 unit of skycells
+# Each host gets one OTA/chip of data.
+# These hosts are skipped:
+# ipp004 : dvo
+# ipp005 : dvo
+# ipp026 : crashes
+# ipp029 : crashes
+# ipp045 : reference cat
 ipphosts METADATA
   camera STR skycell
-  count S32  76
-  sky00 STR  ipp005
+  count S32  86
+  sky00 STR  ipp006
   sky01 STR  ipp006 
   sky02 STR  ipp007
-  sky03 STR  ipp008
-  sky04 STR  ipp009
-  sky05 STR  ipp010
-  sky06 STR  ipp011
-  sky07 STR  ipp012
-  sky08 STR  ipp012
-  sky09 STR  ipp013
-  sky10 STR  ipp014
-  sky11 STR  ipp014 
-  sky12 STR  ipp015
-  sky13 STR  ipp016
-  sky14 STR  ipp016
-  sky15 STR  ipp017
-  sky16 STR  ipp018
-  sky17 STR  ipp019
-  sky18 STR  ipp020
-  sky19 STR  ipp021
-  sky20 STR  ipp023
-  sky21 STR  ipp024 
-  sky22 STR  ipp025
-  sky23 STR  ipp026
-  sky24 STR  ipp027
-  sky25 STR  ipp027
-  sky26 STR  ipp028
-  sky27 STR  ipp028
-  sky28 STR  ipp029
-  sky29 STR  ipp029
-  sky30 STR  ipp030
-  sky31 STR  ipp030 
-  sky32 STR  ipp031
-  sky33 STR  ipp031
-  sky34 STR  ipp032
-  sky35 STR  ipp032
-  sky36 STR  ipp033
-  sky37 STR  ipp033
-  sky38 STR  ipp034
-  sky39 STR  ipp034
-  sky40 STR  ipp035
-  sky41 STR  ipp035 
-  sky42 STR  ipp036
+  sky03 STR  ipp007
+  sky04 STR  ipp008
+  sky05 STR  ipp008
+  sky06 STR  ipp009
+  sky07 STR  ipp009
+  sky08 STR  ipp010
+  sky09 STR  ipp010
+  sky10 STR  ipp011
+  sky11 STR  ipp011 
+  sky12 STR  ipp012
+  sky13 STR  ipp012
+  sky14 STR  ipp013
+  sky15 STR  ipp013
+  sky16 STR  ipp014
+  sky17 STR  ipp014
+  sky18 STR  ipp015
+  sky19 STR  ipp015
+  sky20 STR  ipp016
+  sky21 STR  ipp016 
+  sky22 STR  ipp017
+  sky23 STR  ipp017
+  sky24 STR  ipp018
+  sky25 STR  ipp018
+  sky26 STR  ipp019
+  sky27 STR  ipp019
+  sky28 STR  ipp020
+  sky29 STR  ipp020
+  sky30 STR  ipp021
+  sky31 STR  ipp021 
+  sky32 STR  ipp023
+  sky33 STR  ipp024
+  sky34 STR  ipp025
+  sky35 STR  ipp027
+  sky36 STR  ipp028
+  sky37 STR  ipp030
+  sky38 STR  ipp031
+  sky39 STR  ipp032
+  sky40 STR  ipp033
+  sky41 STR  ipp034 
+  sky42 STR  ipp035
   sky43 STR  ipp036
   sky44 STR  ipp037
-  sky45 STR  ipp037
-  sky46 STR  ipp038
-  sky47 STR  ipp038
-  sky48 STR  ipp039
-  sky49 STR  ipp039
-  sky50 STR  ipp040
-  sky51 STR  ipp040
-  sky52 STR  ipp041 
-  sky53 STR  ipp041
-  sky54 STR  ipp042
-  sky55 STR  ipp042
-  sky56 STR  ipp043
-  sky57 STR  ipp043
-  sky58 STR  ipp044
-  sky59 STR  ipp044
-  sky60 STR  ipp046
-  sky61 STR  ipp046
-  sky62 STR  ipp047 
-  sky63 STR  ipp047
-  sky64 STR  ipp048
-  sky65 STR  ipp048
-  sky66 STR  ipp049
-  sky67 STR  ipp049
-  sky68 STR  ipp050
-  sky69 STR  ipp050
-  sky70 STR  ipp051
-  sky71 STR  ipp051
-  sky72 STR  ipp052 
-  sky73 STR  ipp052
-  sky74 STR  ipp053
-  sky75 STR  ipp053
+  sky45 STR  ipp038
+  sky46 STR  ipp039
+  sky47 STR  ipp040
+  sky48 STR  ipp041
+  sky49 STR  ipp042
+  sky50 STR  ipp043
+  sky51 STR  ipp044
+  sky52 STR  ipp046 
+  sky53 STR  ipp047
+  sky54 STR  ipp048
+  sky55 STR  ipp049
+  sky56 STR  ipp050
+  sky57 STR  ipp051
+  sky58 STR  ipp052
+  sky59 STR  ipp053
+  sky60 STR  ipp054
+  sky61 STR  ipp054
+  sky62 STR  ipp055 
+  sky63 STR  ipp055
+  sky64 STR  ipp056
+  sky65 STR  ipp056
+  sky66 STR  ipp057
+  sky67 STR  ipp057
+  sky68 STR  ipp058
+  sky69 STR  ipp058
+  sky70 STR  ipp059
+  sky71 STR  ipp059
+  sky72 STR  ipp060 
+  sky73 STR  ipp060
+  sky74 STR  ipp061
+  sky75 STR  ipp061
+  sky76 STR  ipp062
+  sky77 STR  ipp062
+  sky78 STR  ipp063
+  sky79 STR  ipp063
+  sky80 STR  ipp064
+  sky81 STR  ipp064
+  sky82 STR  ipp065
+  sky83 STR  ipp065
+  sky84 STR  ipp066
+  sky85 STR  ipp066
 END
 
-# we have 18 10TB nodes and 30 20TB nodes (ipp037 is used for dvo)
-# 1 x W1 + 1 x W2 + 1 x W3 + 12 more nodes...
-
+# These are ordered oddly to prevent excessive targetting shuffles.
 ipphosts METADATA
   camera STR GPC1
 
-  XY01  STR  ipp005
-  XY02  STR  ipp005
+  XY01  STR  ipp010
+  XY02  STR  ipp011
   XY03  STR  ipp006
-  XY04  STR  ipp006
+  XY04  STR  ipp054
 
   XY05  STR  ipp007
-  XY06  STR  ipp007
+  XY06  STR  ipp055
   XY10  STR  ipp008
-  XY11  STR  ipp008
+  XY11  STR  ipp056
 
   XY12  STR  ipp009
-  XY13  STR  ipp009
-  XY14  STR  ipp010
-  XY15  STR  ipp011
+  XY13  STR  ipp057
+  XY14  STR  ipp058
+  XY15  STR  ipp059
 
   XY16  STR  ipp012
-  XY17  STR  ipp012
+  XY17  STR  ipp060
   XY20  STR  ipp013
   XY21  STR  ipp014
 
-  XY22  STR  ipp014
+  XY22  STR  ipp061
   XY23  STR  ipp015
   XY24  STR  ipp016
-  XY25  STR  ipp016
-
-  XY26  STR  ipp017
-  XY27  STR  ipp018
-  XY30  STR  ipp019
+  XY25  STR  ipp062
+
+  XY26  STR  ipp064
+  XY27  STR  ipp065
+  XY30  STR  ipp066
   XY31  STR  ipp020
 
-  XY32  STR  ipp021
+  XY32  STR  ipp063
   XY33  STR  ipp021
   XY34  STR  ipp023
-  XY35  STR  ipp023
+  XY35  STR  ipp013
 
   XY36  STR  ipp024
-  XY37  STR  ipp024
+  XY37  STR  ipp019
   XY40  STR  ipp025
-  XY41  STR  ipp025
-
-  XY42  STR  ipp026
-  XY43  STR  ipp026
+  XY41  STR  ipp018
+
+  XY42  STR  ipp017
+  XY43  STR  ipp015
   XY44  STR  ipp027
   XY45  STR  ipp028
@@ -174,74 +190,74 @@
   camera STR gpc1
 
-  ota01  STR  ipp005
-  ota02  STR  ipp005
+  ota01  STR  ipp010
+  ota02  STR  ipp011
   ota03  STR  ipp006
-  ota04  STR  ipp006
-	            
+  ota04  STR  ipp054
+
   ota05  STR  ipp007
-  ota06  STR  ipp007
+  ota06  STR  ipp055
   ota10  STR  ipp008
-  ota11  STR  ipp008
-	            
+  ota11  STR  ipp056
+
   ota12  STR  ipp009
-  ota13  STR  ipp009
-  ota14  STR  ipp010
-  ota15  STR  ipp011
-	            
+  ota13  STR  ipp057
+  ota14  STR  ipp058
+  ota15  STR  ipp059
+
   ota16  STR  ipp012
-  ota17  STR  ipp012
+  ota17  STR  ipp060
   ota20  STR  ipp013
   ota21  STR  ipp014
-	            
-  ota22  STR  ipp014
+
+  ota22  STR  ipp061
   ota23  STR  ipp015
   ota24  STR  ipp016
-  ota25  STR  ipp016
-	            
-  ota26  STR  ipp017
-  ota27  STR  ipp018
-  ota30  STR  ipp019
+  ota25  STR  ipp062
+
+  ota26  STR  ipp064
+  ota27  STR  ipp065
+  ota30  STR  ipp066
   ota31  STR  ipp020
-	            
-  ota32  STR  ipp021
+
+  ota32  STR  ipp063
   ota33  STR  ipp021
   ota34  STR  ipp023
-  ota35  STR  ipp023
-	            
+  ota35  STR  ipp013
+
   ota36  STR  ipp024
-  ota37  STR  ipp024
+  ota37  STR  ipp019
   ota40  STR  ipp025
-  ota41  STR  ipp025
-	            
-  ota42  STR  ipp026
-  ota43  STR  ipp026
+  ota41  STR  ipp018
+
+  ota42  STR  ipp017
+  ota43  STR  ipp015
   ota44  STR  ipp027
   ota45  STR  ipp028
-	            
+
   ota46  STR  ipp029
   ota47  STR  ipp030
   ota50  STR  ipp031
   ota51  STR  ipp032
-	            
+
   ota52  STR  ipp033
   ota53  STR  ipp034
   ota54  STR  ipp035
   ota55  STR  ipp036
-	            
+
   ota56  STR  ipp037
   ota57  STR  ipp038
   ota60  STR  ipp039
   ota61  STR  ipp040
-	            
+
   ota62  STR  ipp041
   ota63  STR  ipp042
   ota64  STR  ipp043
   ota65  STR  ipp044
-	            
+
   ota66  STR  ipp046
   ota67  STR  ipp047
   ota71  STR  ipp048
   ota72  STR  ipp049
-	            
+
   ota73  STR  ipp050
   ota74  STR  ipp051
@@ -285,74 +301,74 @@
   count S32   60
 
-  XY01  STR  ipp005
-  XY02  STR  ipp005
+  XY01  STR  ipp010
+  XY02  STR  ipp011
   XY03  STR  ipp006
-  XY04  STR  ipp006
-	           
+  XY04  STR  ipp054
+
   XY05  STR  ipp007
-  XY06  STR  ipp007
+  XY06  STR  ipp055
   XY10  STR  ipp008
-  XY11  STR  ipp008
-	           
+  XY11  STR  ipp056
+
   XY12  STR  ipp009
-  XY13  STR  ipp009
-  XY14  STR  ipp010
-  XY15  STR  ipp011
-	           
+  XY13  STR  ipp057
+  XY14  STR  ipp058
+  XY15  STR  ipp059
+
   XY16  STR  ipp012
-  XY17  STR  ipp012
+  XY17  STR  ipp060
   XY20  STR  ipp013
   XY21  STR  ipp014
-	           
-  XY22  STR  ipp014
+
+  XY22  STR  ipp061
   XY23  STR  ipp015
   XY24  STR  ipp016
-  XY25  STR  ipp016
-	           
-  XY26  STR  ipp017
-  XY27  STR  ipp018
-  XY30  STR  ipp019
+  XY25  STR  ipp062
+
+  XY26  STR  ipp064
+  XY27  STR  ipp065
+  XY30  STR  ipp066
   XY31  STR  ipp020
-	           
-  XY32  STR  ipp021
+
+  XY32  STR  ipp063
   XY33  STR  ipp021
   XY34  STR  ipp023
-  XY35  STR  ipp023
-	           
+  XY35  STR  ipp013
+
   XY36  STR  ipp024
-  XY37  STR  ipp024
+  XY37  STR  ipp019
   XY40  STR  ipp025
-  XY41  STR  ipp025
-	           
-  XY42  STR  ipp026
-  XY43  STR  ipp026
+  XY41  STR  ipp018
+
+  XY42  STR  ipp017
+  XY43  STR  ipp015
   XY44  STR  ipp027
   XY45  STR  ipp028
-	           
+
   XY46  STR  ipp029
   XY47  STR  ipp030
   XY50  STR  ipp031
   XY51  STR  ipp032
-	           
+
   XY52  STR  ipp033
   XY53  STR  ipp034
   XY54  STR  ipp035
   XY55  STR  ipp036
-	           
+
   XY56  STR  ipp037
   XY57  STR  ipp038
   XY60  STR  ipp039
   XY61  STR  ipp040
-	           
+
   XY62  STR  ipp041
   XY63  STR  ipp042
   XY64  STR  ipp043
   XY65  STR  ipp044
-	           
+
   XY66  STR  ipp046
   XY67  STR  ipp047
   XY71  STR  ipp048
   XY72  STR  ipp049
-	           
+
   XY73  STR  ipp050
   XY74  STR  ipp051
@@ -363,80 +379,250 @@
 ipphosts METADATA
   camera STR dist_skycell
-  count S32  76
-  sky00 STR  ipp005
+  count S32  86
+  sky00 STR  ipp006
   sky01 STR  ipp006 
   sky02 STR  ipp007
-  sky03 STR  ipp008
-  sky04 STR  ipp009
-  sky05 STR  ipp010
-  sky06 STR  ipp011
-  sky07 STR  ipp012
-  sky08 STR  ipp012
-  sky09 STR  ipp013
-  sky10 STR  ipp014
-  sky11 STR  ipp014 
-  sky12 STR  ipp015
-  sky13 STR  ipp016
-  sky14 STR  ipp016
-  sky15 STR  ipp017
-  sky16 STR  ipp018
-  sky17 STR  ipp019
-  sky18 STR  ipp020
-  sky19 STR  ipp021
-  sky20 STR  ipp023
-  sky21 STR  ipp024 
-  sky22 STR  ipp025
-  sky23 STR  ipp026
-  sky24 STR  ipp027
-  sky25 STR  ipp027
-  sky26 STR  ipp028
-  sky27 STR  ipp028
-  sky28 STR  ipp029
-  sky29 STR  ipp029
-  sky30 STR  ipp030
-  sky31 STR  ipp030 
-  sky32 STR  ipp031
-  sky33 STR  ipp031
-  sky34 STR  ipp032
-  sky35 STR  ipp032
-  sky36 STR  ipp033
-  sky37 STR  ipp033
-  sky38 STR  ipp034
-  sky39 STR  ipp034
-  sky40 STR  ipp035
-  sky41 STR  ipp035 
-  sky42 STR  ipp036
+  sky03 STR  ipp007
+  sky04 STR  ipp008
+  sky05 STR  ipp008
+  sky06 STR  ipp009
+  sky07 STR  ipp009
+  sky08 STR  ipp010
+  sky09 STR  ipp010
+  sky10 STR  ipp011
+  sky11 STR  ipp011 
+  sky12 STR  ipp012
+  sky13 STR  ipp012
+  sky14 STR  ipp013
+  sky15 STR  ipp013
+  sky16 STR  ipp014
+  sky17 STR  ipp014
+  sky18 STR  ipp015
+  sky19 STR  ipp015
+  sky20 STR  ipp016
+  sky21 STR  ipp016 
+  sky22 STR  ipp017
+  sky23 STR  ipp017
+  sky24 STR  ipp018
+  sky25 STR  ipp018
+  sky26 STR  ipp019
+  sky27 STR  ipp019
+  sky28 STR  ipp020
+  sky29 STR  ipp020
+  sky30 STR  ipp021
+  sky31 STR  ipp021 
+  sky32 STR  ipp023
+  sky33 STR  ipp024
+  sky34 STR  ipp025
+  sky35 STR  ipp027
+  sky36 STR  ipp028
+  sky37 STR  ipp030
+  sky38 STR  ipp031
+  sky39 STR  ipp032
+  sky40 STR  ipp033
+  sky41 STR  ipp034 
+  sky42 STR  ipp035
   sky43 STR  ipp036
   sky44 STR  ipp037
-  sky45 STR  ipp037
-  sky46 STR  ipp038
-  sky47 STR  ipp038
-  sky48 STR  ipp039
-  sky49 STR  ipp039
-  sky50 STR  ipp040
-  sky51 STR  ipp040
-  sky52 STR  ipp041 
-  sky53 STR  ipp041
-  sky54 STR  ipp042
-  sky55 STR  ipp042
-  sky56 STR  ipp043
-  sky57 STR  ipp043
-  sky58 STR  ipp044
-  sky59 STR  ipp044
-  sky60 STR  ipp046
-  sky61 STR  ipp046
-  sky62 STR  ipp047 
-  sky63 STR  ipp047
-  sky64 STR  ipp048
-  sky65 STR  ipp048
-  sky66 STR  ipp049
-  sky67 STR  ipp049
-  sky68 STR  ipp050
-  sky69 STR  ipp050
-  sky70 STR  ipp051
-  sky71 STR  ipp051
-  sky72 STR  ipp052 
-  sky73 STR  ipp052
-  sky74 STR  ipp053
-  sky75 STR  ipp053
+  sky45 STR  ipp038
+  sky46 STR  ipp039
+  sky47 STR  ipp040
+  sky48 STR  ipp041
+  sky49 STR  ipp042
+  sky50 STR  ipp043
+  sky51 STR  ipp044
+  sky52 STR  ipp046 
+  sky53 STR  ipp047
+  sky54 STR  ipp048
+  sky55 STR  ipp049
+  sky56 STR  ipp050
+  sky57 STR  ipp051
+  sky58 STR  ipp052
+  sky59 STR  ipp053
+  sky60 STR  ipp054
+  sky61 STR  ipp054
+  sky62 STR  ipp055 
+  sky63 STR  ipp055
+  sky64 STR  ipp056
+  sky65 STR  ipp056
+  sky66 STR  ipp057
+  sky67 STR  ipp057
+  sky68 STR  ipp058
+  sky69 STR  ipp058
+  sky70 STR  ipp059
+  sky71 STR  ipp059
+  sky72 STR  ipp060 
+  sky73 STR  ipp060
+  sky74 STR  ipp061
+  sky75 STR  ipp061
+  sky76 STR  ipp062
+  sky77 STR  ipp062
+  sky78 STR  ipp063
+  sky79 STR  ipp063
+  sky80 STR  ipp064
+  sky81 STR  ipp064
+  sky82 STR  ipp065
+  sky83 STR  ipp065
+  sky84 STR  ipp066
+  sky85 STR  ipp066
 END
+
+ipphosts METADATA
+  camera STR STARE
+
+  ota01  STR  stare00
+  ota02  STR  stare01
+  ota03  STR  stare02
+  ota04  STR  stare04
+	            
+  ota05  STR  stare00
+  ota06  STR  stare01
+  ota10  STR  stare02
+  ota11  STR  stare04
+	            
+  ota12  STR  stare00
+  ota13  STR  stare01
+  ota14  STR  stare02
+  ota15  STR  stare04
+	            
+  ota16  STR  stare00
+  ota17  STR  stare01
+  ota20  STR  stare02
+  ota21  STR  stare04
+	            
+  ota22  STR  stare00
+  ota23  STR  stare01
+  ota24  STR  stare02
+  ota25  STR  stare04
+	            
+  ota26  STR  stare00
+  ota27  STR  stare01
+  ota30  STR  stare02
+  ota31  STR  stare04
+	            
+  ota32  STR  stare00
+  ota33  STR  stare01
+  ota34  STR  stare02
+  ota35  STR  stare04
+	            
+  ota36  STR  stare00
+  ota37  STR  stare01
+  ota40  STR  stare02
+  ota41  STR  stare04
+	            
+  ota42  STR  stare00
+  ota43  STR  stare01
+  ota44  STR  stare02
+  ota45  STR  stare04
+	            
+  ota46  STR  stare00
+  ota47  STR  stare01
+  ota50  STR  stare02
+  ota51  STR  stare04
+	            
+  ota52  STR  stare00
+  ota53  STR  stare01
+  ota54  STR  stare02
+  ota55  STR  stare04
+	            
+  ota56  STR  stare00
+  ota57  STR  stare01
+  ota60  STR  stare02
+  ota61  STR  stare04
+	            
+  ota62  STR  stare00
+  ota63  STR  stare01
+  ota64  STR  stare02
+  ota65  STR  stare04
+	            
+  ota66  STR  stare00
+  ota67  STR  stare01
+  ota71  STR  stare02
+  ota72  STR  stare04
+	            
+  ota73  STR  stare00
+  ota74  STR  stare01
+  ota75  STR  stare02
+  ota76  STR  stare04
+END
+
+
+# Backup of old stare allocation setup.
+# ipphosts METADATA
+#   camera STR STARE
+
+#   ota01  STR  ipp010
+#   ota02  STR  ipp010
+#   ota03  STR  ipp010
+#   ota04  STR  ipp010
+	            
+#   ota05  STR  ipp011
+#   ota06  STR  ipp011
+#   ota10  STR  ipp011
+#   ota11  STR  ipp011
+	            
+#   ota12  STR  ipp012
+#   ota13  STR  ipp012
+#   ota14  STR  ipp012
+#   ota15  STR  ipp012
+	            
+#   ota16  STR  ipp013
+#   ota17  STR  ipp013
+#   ota20  STR  ipp013
+#   ota21  STR  ipp013
+	            
+#   ota22  STR  ipp014
+#   ota23  STR  ipp014
+#   ota24  STR  ipp014
+#   ota25  STR  ipp014
+	            
+#   ota26  STR  ipp015
+#   ota27  STR  ipp015
+#   ota30  STR  ipp015
+#   ota31  STR  ipp015
+	            
+#   ota32  STR  ipp016
+#   ota33  STR  ipp016
+#   ota34  STR  ipp016
+#   ota35  STR  ipp016
+	            
+#   ota36  STR  ipp017
+#   ota37  STR  ipp017
+#   ota40  STR  ipp017
+#   ota41  STR  ipp017
+	            
+#   ota42  STR  ipp018
+#   ota43  STR  ipp018
+#   ota44  STR  ipp018
+#   ota45  STR  ipp018
+	            
+#   ota46  STR  ipp019
+#   ota47  STR  ipp019
+#   ota50  STR  ipp019
+#   ota51  STR  ipp019
+	            
+#   ota52  STR  ipp020
+#   ota53  STR  ipp020
+#   ota54  STR  ipp020
+#   ota55  STR  ipp020
+	            
+#   ota56  STR  ipp021
+#   ota57  STR  ipp021
+#   ota60  STR  ipp021
+#   ota61  STR  ipp021
+	            
+#   ota62  STR  ipp010
+#   ota63  STR  ipp011
+#   ota64  STR  ipp012
+#   ota65  STR  ipp013
+	            
+#   ota66  STR  ipp014
+#   ota67  STR  ipp015
+#   ota71  STR  ipp016
+#   ota72  STR  ipp017
+	            
+#   ota73  STR  ipp018
+#   ota74  STR  ipp019
+#   ota75  STR  ipp020
+#   ota76  STR  ipp021
+# END
Index: branches/meh_branches/ppstack_test/ippTasks/lap.pro
===================================================================
--- branches/meh_branches/ppstack_test/ippTasks/lap.pro	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTasks/lap.pro	(revision 33415)
@@ -10,4 +10,6 @@
 $lap_RunPage = 0
 $lap_DonePage = 0
+
+$LAP_QUEUE = NULL
 
 book init lapNewRuns
@@ -135,4 +137,8 @@
  
 
+macro lap.define.queue
+  $LAP_QUEUE = $1
+end
+
 task           lap.initial.load
   host         local
@@ -388,5 +394,5 @@
     stderr $LOGDIR/lap.load.log
 
-    $run = laptool -pendingrun -state done
+    $run = laptool -pendingrun -state full
 
     if ($lapSeq:n != 0)
@@ -463,4 +469,8 @@
     option $LAP_ID
     $run = lap_science.pl --cleanup_mode --dbname $DBNAME --lap_id $LAP_ID
+    if ("$LAP_QUEUE" != "NULL") 
+      $run = $run --queue_list $LAP_QUEUE
+    end
+    
 
     command $run
Index: branches/meh_branches/ppstack_test/ippTasks/mergedvodb.pro
===================================================================
--- branches/meh_branches/ppstack_test/ippTasks/mergedvodb.pro	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTasks/mergedvodb.pro	(revision 33415)
@@ -0,0 +1,226 @@
+## addstar.pro : globals and support macros : -*- sh -*-
+## this file contains the tasks for running the addstar analysis stage
+## these tasks use the book addPendingExp
+
+# test for required global variables
+check.globals
+
+
+#There is a book for each task, because I don't use labels. 
+
+if (not($?havemergedvodbBooks))
+  book create MERGEDVODB_MERGE
+  $havemergedvodbBooks = TRUE
+end
+
+$MERGEDVODB_DB = 0
+$MERGEDVODB_MERGE_DB = 0
+
+book init mergedvodbMergelist
+
+macro mergedvodb.merge.status
+  book listbook mergedvodbMergelist
+end
+
+macro mergedvodb.merge.reset
+  book init mergedvodbMergelist
+end
+
+macro mergedvodb.merge.on
+  task mergedvodb.merge.load
+    active true
+  end
+  task mergedvodb.merge.run
+    active true
+  end  
+end
+macro mergedvodb.merge.off
+  task mergedvodb.merge.load
+    active false
+  end
+  task mergedvodb.merge.run
+    active false
+  end  
+end
+
+## you get no choice - you add all of them in at the same time. you can always turn off the tasks you don't want to run.
+macro add.mergedvodb
+  if ($0 != 2)
+    echo "USAGE: add.mergedvodb (mergedvodb)"
+    break
+  end
+    #merge  
+  book newpage MERGEDVODB_MERGE $1
+  book setword MERGEDVODB_MERGE $1 MERGEDVODB $1
+  book setword MERGEDVODB_MERGE $1 STATE PENDING
+end
+
+macro del.mergedvodb
+  if ($0 != 2)
+    echo "USAGE: del.mergedvodb (mergedvodb)"
+    break
+  end
+  book delpage MERGEDVODB_MERGE $1
+end
+
+macro show.mergedvodb
+  if ($0 != 1)
+    echo "USAGE: show.mergedvodb"
+    break
+  end
+  echo "mergedvodb merge"
+  book listbook MERGEDVODB_MERGE
+  echo "n pages"
+  book npages MERGEDVODB_MERGE -var N
+  echo $N
+end
+
+
+task           mergedvodb.merge.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/mergedvodb.merge.load.log
+
+  task.exec
+    book npages MERGEDVODB_MERGE -var N
+    if ($N == 0)
+      echo "No labels for processing"
+      break
+    endif
+    book getpage MERGEDVODB_MERGE 0 -var mergedvodb -key STATE NEW
+    if ("$mergedvodb" == "NULL")
+      # All labels have been done --- reset
+      for i 0 $N
+        book getpage MERGEDVODB_MERGE $i -var mergedvodb
+        book setword MERGEDVODB_MERGE $mergedvodb STATE NEW
+      end
+      book getpage MERGEDVODB_MERGE 0 -var mergedvodb -key STATE NEW
+
+      # Select different database
+      $MERGEDVODB_DB ++
+      if ($MERGEDVODB_DB >= $DB:n) set MERGEDVODB_DB = 0
+    end
+    book setword MERGEDVODB_MERGE $mergedvodb STATE DONE
+    
+    $run = mergetool -pendingmerge -limit 1
+    $run = $run -mergedvodb $mergedvodb
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$MERGEDVODB_DB
+      $run = $run -dbname $DB:$MERGEDVODB_DB
+      $MERGEDVODB_DB ++
+      if ($MERGEDVODB_DB >= $DB:n) set MERGEDVODB_DB = 0
+    end
+    #add_poll_args run
+    #echo $run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    #echo "success"
+    # convert 'stdout' to book format
+    ipptool2book stdout mergedvodbMergelist -key merge_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook mergedvodbMergelist
+    end
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup mergedvodbMergelist
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+task           mergedvodb.merge.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 60000
+  
+  #we only want one running at a time
+
+  host         local
+  npending     1
+
+  task.exec
+    # if we are unable to run the 'exec', use a long retry time
+    periods -exec $RUNEXEC
+    book npages mergedvodbMergelist -var N
+    if ($N == 0) break
+    # look for new images in minidvodbWaitlist (pantaskState == INIT)
+    book getpage mergedvodbMergelist 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+    book setword mergedvodbMergelist $pageName pantaskState RUN
+    book getword mergedvodbMergelist $pageName merge_id -var MERGE_ID
+    book getword mergedvodbMergelist $pageName mergedvodb -var MERGEDVODB
+    book getword mergedvodbMergelist $pageName mergedvodb_path -var MERGEDVODB_PATH
+    book getword mergedvodbMergelist $pageName minidvodb_path -var MINIDVODB_PATH
+    book getword mergedvodbMergelist $pageName state -var STATE
+    stdout $LOGDIR/mergedvodb.merge.run.log
+    stderr $LOGDIR/mergedvodb.merge.run.log
+    $run = mergedvodb_merge.pl --camera GPC1 
+    $run = $run --mergedvodb_path $MERGEDVODB_PATH 
+    $run = $run --minidvodb_path $MINIDVODB_PATH 
+    $run = $run --merge_id $MERGE_ID --mergedvodb $MERGEDVODB
+    echo $run
+  if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$MERGEDVODB_DB
+      $run = $run --dbname $DB:$MERGEDVODB_DB
+      $MERGEDVODB_DB ++
+      if ($MERGEDVODB_DB >= $DB:n) set MERGEDVODB_DB = 0
+    end
+  # save the pageName for future reference below
+    options $pageName
+
+    # create the command line
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    # if we are unable to run the 'exec', use a long retry time
+    periods -exec 0.05
+    command $run
+  end
+  # default exit status
+    task.exit    default
+    process_exit mergedvodbMergelist $options:0 $JOB_STATUS
+  end
+
+  # locked list
+  task.exit    crash
+  
+    echo "hostname: $JOB_HOSTNAME"
+
+    # Set a fault code in the database
+    
+    process_exit mergedvodbMergelist $options:0 $EXIT_CRASH_ERR
+  end
+
+  # operation timed out?
+    task.exit    timeout
+	showcommand timeout
+    book setword mergedvodbMergelist $options:0 pantaskState TIMEOUT
+  end
+end
+
Index: branches/meh_branches/ppstack_test/ippTasks/minidvodb.pro
===================================================================
--- branches/meh_branches/ppstack_test/ippTasks/minidvodb.pro	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTasks/minidvodb.pro	(revision 33415)
@@ -11,5 +11,5 @@
 if (not($?haveminidvodbBooks))
  book create MINIDVODB
- book create MINIDVODB_MERGE
+ book create MINIDVODB_PREMERGE
  book create MINIDVODB_CREATE
  book create MINIDVODB_ACTIVE   
@@ -20,10 +20,10 @@
 
 $MINIDVODB_WAIT_DB = 0
-$MINIDVODB_MERGE_DB = 0
+$MINIDVODB_PREMERGE_DB = 0
 $MINIDVODB_CREATE_DB = 0
 $MINIDVODB_ACTIVE_DB = 0
 
 book init minidvodbWaitlist
-book init minidvodbMergelist
+book init minidvodbPreMergelist
 book init minidvodbCreatelist
 book init minidvodbActivelist
@@ -54,10 +54,10 @@
 end
 
-macro minidvodb.merge.status
-  book listbook minidvodbMergelist
-end
-
-macro minidvodb.merge.reset
-  book init minidvodbMergelist
+macro minidvodb.premerge.status
+  book listbook minidvodbPreMergelist
+end
+
+macro minidvodb.premerge.reset
+  book init minidvodbPreMergelist
 end
 
@@ -112,17 +112,17 @@
 ##these merge the dbs
 
-macro minidvodb.merge.on
-  task minidvodb.merge.load
+macro minidvodb.premerge.on
+  task minidvodb.premerge.load
     active true
   end
-  task minidvodb.merge.run
+  task minidvodb.premerge.run
     active true
   end  
 end
-macro minidvodb.merge.off
-  task minidvodb.merge.load
+macro minidvodb.premerge.off
+  task minidvodb.premerge.load
     active false
   end
-  task minidvodb.merge.run
+  task minidvodb.premerge.run
     active false
   end  
@@ -142,8 +142,8 @@
   book setword MINIDVODB $1 STATE PENDING
   #merge  
-  book newpage MINIDVODB_MERGE $1
-  book setword MINIDVODB_MERGE $1 MINIDVODB_GROUP $1
-  book setword MINIDVODB_MERGE $1 DVODB $3
-  book setword MINIDVODB_MERGE $1 STATE PENDING
+  book newpage MINIDVODB_PREMERGE $1
+  book setword MINIDVODB_PREMERGE $1 MINIDVODB_GROUP $1
+  book setword MINIDVODB_PREMERGE $1 DVODB $3
+  book setword MINIDVODB_PREMERGE $1 STATE PENDING
   #active  
   book newpage MINIDVODB_ACTIVE $1
@@ -168,5 +168,5 @@
   end
   book delpage MINIDVODB $1
-  book delpage MINIDVODB_MERGE $1
+  book delpage MINIDVODB_PREMERGE $1
   book delpage MINIDVODB_CREATE $1  
   book delpage MINIDVODB_ACTIVE $1
@@ -180,6 +180,6 @@
   echo "minidvodb wait"
   book listbook MINIDVODB
-  echo "minidvodb merge"
-  book listbook MINIDVODB_MERGE
+  echo "minidvodb premerge"
+  book listbook MINIDVODB_PREMERGE
   echo "minidvodb create"
   book listbook MINIDVODB_CREATE 
@@ -339,5 +339,5 @@
 
 
-task           minidvodb.merge.load
+task           minidvodb.premerge.load
   host         local
 
@@ -348,8 +348,8 @@
 
   stdout NULL
-  stderr $LOGDIR/minidvodb.merge.load.log
+  stderr $LOGDIR/minidvodb.premerge.load.log
 
   task.exec
-    book npages MINIDVODB_MERGE -var N
+    book npages MINIDVODB_PREMERGE -var N
     if ($N == 0)
 #      echo "No labels for processing"
@@ -357,13 +357,13 @@
     endif
 
-    book getpage MINIDVODB_MERGE 0 -var minidvodb_group -key STATE NEW
+    book getpage MINIDVODB_PREMERGE 0 -var minidvodb_group -key STATE NEW
     if ("$minidvodb_group" == "NULL")
       # All labels have been done --- reset
 #      echo "Resetting labels"
       for i 0 $N
-        book getpage MINIDVODB_MERGE $i -var minidvodb_group
-        book setword MINIDVODB_MERGE $minidvodb_group STATE NEW
+        book getpage MINIDVODB_PREMERGE $i -var minidvodb_group
+        book setword MINIDVODB_PREMERGE $minidvodb_group STATE NEW
       end
-      book getpage MINIDVODB_MERGE 0 -var minidvodb_group -key STATE NEW
+      book getpage MINIDVODB_PREMERGE 0 -var minidvodb_group -key STATE NEW
 
       # Select different database
@@ -372,5 +372,5 @@
     end
     #finds the minidvodbs in a state of 'to_be_merged' 
-    book setword MINIDVODB_MERGE $minidvodb_group STATE DONE
+    book setword MINIDVODB_PREMERGE $minidvodb_group STATE DONE
     $run = addtool -listminidvodbrun -state to_be_merged -limit 1
     $run = $run -minidvodb_group $minidvodb_group
@@ -392,10 +392,10 @@
   task.exit    0
     # convert 'stdout' to book format
-    ipptool2book stdout minidvodbMergelist -key minidvodb_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    ipptool2book stdout minidvodbPreMergelist -key minidvodb_id -uniq -setword dbname $options:0 -setword pantaskState INIT
     if ($VERBOSE > 2)
-      book listbook minidvodbMergelist
+      book listbook minidvodbPreMergelist
     end
     # delete existing entries in the appropriate pantaskStates
-    process_cleanup minidvodbMergelist
+    process_cleanup minidvodbPreMergelist
   end
 
@@ -418,5 +418,5 @@
 
 
-task           minidvodb.merge.run
+task           minidvodb.premerge.run
   periods      -poll $RUNPOLL
   periods      -exec $RUNEXEC
@@ -432,21 +432,20 @@
     periods -exec $RUNEXEC
 
-    book npages minidvodbMergelist -var N
+    book npages minidvodbPreMergelist -var N
     if ($N == 0) break
     
     # look for new images in minidvodbWaitlist (pantaskState == INIT)
-    book getpage minidvodbMergelist 0 -var pageName -key pantaskState INIT
+    book getpage minidvodbPreMergelist 0 -var pageName -key pantaskState INIT
     if ("$pageName" == "NULL") break
 
-    book setword minidvodbMergelist $pageName pantaskState RUN
-    book getword minidvodbMergelist $pageName minidvodb_id -var MINIDVODB_ID
-    book getword minidvodbMergelist $pageName minidvodb_group -var MINIDVODB_GROUP
-    book getword minidvodbMergelist $pageName mergedvodb_path -var MERGEDVODB_PATH
-    book getword minidvodbMergelist $pageName minidvodb_path -var MINIDVODB_PATH
-    book getword minidvodbMergelist $pageName state -var STATE
-    stdout $LOGDIR/minidvodb.merge.run.log
-    stderr $LOGDIR/minidvodb.merge.run.log
-
-    $run = minidvodb_merge.pl --camera GPC1 --mergedvodb $MERGEDVODB_PATH --minidvodb $MINIDVODB_PATH --minidvodb_group $MINIDVODB_GROUP --minidvodb_id $MINIDVODB_ID
+    book setword minidvodbPreMergelist $pageName pantaskState RUN
+    book getword minidvodbPreMergelist $pageName minidvodb_id -var MINIDVODB_ID
+    book getword minidvodbPreMergelist $pageName minidvodb_group -var MINIDVODB_GROUP
+    book getword minidvodbPreMergelist $pageName minidvodb_path -var MINIDVODB_PATH
+    book getword minidvodbPreMergelist $pageName state -var STATE
+    stdout $LOGDIR/minidvodb.premerge.run.log
+    stderr $LOGDIR/minidvodb.premerge.run.log
+
+    $run = minidvodb_premerge.pl --camera GPC1 --minidvodb $MINIDVODB_PATH --minidvodb_group $MINIDVODB_GROUP --minidvodb_id $MINIDVODB_ID
     
   if ($DB:n == 0)
@@ -472,5 +471,5 @@
   # default exit status
     task.exit    default
-    process_exit minidvodbMergelist $options:0 $JOB_STATUS
+    process_exit minidvodbPreMergelist $options:0 $JOB_STATUS
   end
 
@@ -482,5 +481,5 @@
     # Set a fault code in the database
     
-    process_exit minidvodbMergelist $options:0 $EXIT_CRASH_ERR
+    process_exit minidvodbPreMergelist $options:0 $EXIT_CRASH_ERR
   end
 
@@ -488,5 +487,5 @@
     task.exit    timeout
 	showcommand timeout
-    book setword minidvodbMergelist $options:0 pantaskState TIMEOUT
+    book setword minidvodbPreMergelist $options:0 pantaskState TIMEOUT
   end
 end
Index: branches/meh_branches/ppstack_test/ippTasks/nightly_science.pro
===================================================================
--- branches/meh_branches/ppstack_test/ippTasks/nightly_science.pro	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTasks/nightly_science.pro	(revision 33415)
@@ -178,5 +178,5 @@
   periods         -poll $LOADPOLL
   periods         -exec $LOADEXEC
-  periods         -timeout 30
+  periods         -timeout 300
   trange          0:00:00 1:00:00 -nmax 1
   npending        1
@@ -260,5 +260,5 @@
   periods         -poll 3600
   periods         -exec $LOADEXEC
-  periods         -timeout 30
+  periods         -timeout 300
   trange          22:00:00 23:59:59
   trange          00:00:00 10:00:00
Index: branches/meh_branches/ppstack_test/ippTasks/nightly_stacks.pro
===================================================================
--- branches/meh_branches/ppstack_test/ippTasks/nightly_stacks.pro	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTasks/nightly_stacks.pro	(revision 33415)
@@ -290,7 +290,7 @@
 task              ns.dqstats.load
   host            local
-  periods         -poll 3600
+  periods         -poll 10
   periods         -exec $LOADEXEC
-  periods         -timeout 30
+  periods         -timeout 600
   trange          22:00:00 23:59:59
   trange          00:00:00 10:00:00
Index: branches/meh_branches/ppstack_test/ippTasks/pstamp.pro
===================================================================
--- branches/meh_branches/ppstack_test/ippTasks/pstamp.pro	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTasks/pstamp.pro	(revision 33415)
@@ -626,5 +626,5 @@
     periods     -poll $LOADPOLL
     # XXX: create a macro for this time
-    periods     -exec 10
+    periods     -exec 15
 #    periods     -exec $LOADEXEC
     periods     -timeout 300
@@ -676,9 +676,9 @@
 task pstamp.dependent.run
     periods     -poll $RUNPOLL
-    periods     -exec $RUNEXEC
+    periods     -exec 0.5
     periods     -timeout 300
 
     task.exec
-        periods -exec $RUNEXEC
+        # periods -exec $RUNEXEC
 
         book npages pstampDependent -var N
@@ -726,5 +726,5 @@
             echo command $run
         end
-        periods -exec 0.05
+#        periods -exec 0.05
         command $run
     end
Index: branches/meh_branches/ppstack_test/ippTasks/rcserver.pro
===================================================================
--- branches/meh_branches/ppstack_test/ippTasks/rcserver.pro	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTasks/rcserver.pro	(revision 33415)
@@ -108,4 +108,5 @@
   periods      -exec $RUNEXEC
   periods      -timeout 60
+  npending     5
 
   task.exec
Index: branches/meh_branches/ppstack_test/ippTasks/skycal.pro
===================================================================
--- branches/meh_branches/ppstack_test/ippTasks/skycal.pro	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTasks/skycal.pro	(revision 33415)
@@ -0,0 +1,246 @@
+## skycal.pro : tasks for static sky calibration analysis : -*- sh -*-
+
+## This file contains panTasks definitions for performing the
+## skycal analysis.  After a skycalRun entry (with
+## associated skycal_id) is defined, psastro is performed (via script
+## skycalibration.pl) (tasks in skycalRun).
+
+# test for required global variables
+check.globals
+
+### Initialise the books containing the tasks to do
+book init skycalRun
+
+### Database lists
+$skycal_DB = 0
+$skycal_revert_DB = 0
+
+### Check status of skycal tasks
+macro skycal.status
+  book listbook skycalRun
+end
+
+### Reset skycal tasks
+macro skycal.reset
+  book init skycalRun
+end
+
+### Turn skycal tasks on
+macro skycal.on
+  task skycal.load
+    active true
+  end
+  task skycal.run
+    active true
+  end
+end
+
+### Turn skycal tasks off
+macro skycal.off
+  task skycal.load
+    active false
+  end
+  task skycal.run
+    active false
+  end
+end
+
+macro skycal.revert.on
+  task skycal.revert
+    active true
+  end
+end
+
+macro skycal.revert.off
+  task skycal.revert
+    active false
+  end
+end
+
+### Load tasks for skycal
+### Tasks are loaded into skycalRun.
+task	       skycal.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGDIR/skycal.log
+
+  task.exec
+    if ($LABEL:n == 0) break
+    $run = staticskytool -pendingskycalrun
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$skycal_DB
+      $run = $run -dbname $DB:$skycal_DB -limit 40
+      $skycal_DB ++
+      if ($skycal_DB >= $DB:n) set skycal_DB = 0
+    end
+    add_poll_args run
+    add_poll_labels run
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout skycalRun -key skycal_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook skycalRun
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup skycalRun
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+### Run tasks for the skycal analysis
+### Tasks are taken from skycalRun.
+task	       skycal.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 10800
+
+  task.exec
+    book npages skycalRun -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    if ($BURNTOOLING == 1) break
+
+    # look for new entries in skycalRun (pantaskState == INIT)
+    book getpage skycalRun 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword skycalRun $pageName pantaskState RUN
+    book getword skycalRun $pageName skycal_id -var SKYCAL_ID
+    book getword skycalRun $pageName stack_id -var STACK_ID
+    book getword skycalRun $pageName tess_id -var TESS_DIR
+    book getword skycalRun $pageName skycell_id -var SKYCELL_ID
+    book getword skycalRun $pageName camera -var CAMERA
+    book getword skycalRun $pageName filter -var FILTER
+    book getword skycalRun $pageName workdir -var WORKDIR_TEMPLATE
+    book getword skycalRun $pageName path_base -var PATH_BASE
+    book getword skycalRun $pageName reduction -var REDUCTION
+    book getword skycalRun $pageName singlefilter -var SINGLEFILTER
+    book getword skycalRun $pageName dbname -var DBNAME
+    book getword skycalRun $pageName state -var RUN_STATE
+
+    # set the host and workdir based on the skycell hash
+    set.host.for.skycell $SKYCELL_ID
+    set.workdir.by.skycell $SKYCELL_ID $WORKDIR_TEMPLATE $default_host WORKDIR
+
+    basename $TESS_DIR -var TESS_ID
+    sprintf outroot "%s/%s/%s/%s.%s.stk.%s.skycal.%s" $WORKDIR $TESS_ID $SKYCELL_ID $TESS_ID $SKYCELL_ID $STACK_ID $SKYCAL_ID
+
+    stdout $LOGDIR/skycal.log
+    stderr $LOGDIR/skycal.log
+
+    $run = skycalibration.pl --skycal_id $SKYCAL_ID --outroot $outroot --redirect-output --camera $CAMERA --path_base $PATH_BASE --stack_id $STACK_ID --filter $FILTER
+    if ("$REDUCTION" != "NULL")
+      $run = $run --reduction $REDUCTION
+    end
+    if ("$SINGLEFILTER" != "0")
+        $run = $run --singlefilter
+    end
+    add_standard_args run
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # create the command line
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # default exit status
+  task.exit    default
+    process_exit skycalRun $options:0 $JOB_STATUS
+  end
+
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword skycalRun $options:0 pantaskState CRASH
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword skycalRun $options:0 pantaskState TIMEOUT
+  end
+end
+
+task skycal.revert
+  host         local
+
+  periods      -poll 60.0
+  periods      -exec 1800.0
+  periods      -timeout 120.0
+  npending     1
+  active false
+  
+  stdout NULL
+  stderr $LOGDIR/revert.log
+
+  task.exec
+    if ($LABEL:n == 0) break
+    # Only revert failures with fault=2 (SYS_ERROR), which tend to be
+    # temporary filesystem problems.  Every other fault type is
+    # interesting and should be kept for debugging (and so it does not
+    # continue to occur).
+    $run = staticskytool -revertskycal -fault 2
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$skycal_revert_DB
+      $run = $run -dbname $DB:$skycal_revert_DB
+      $skycal_revert_DB ++
+      if ($skycal_revert_DB >= $DB:n) set skycal_revert_DB = 0
+    end
+    add_poll_labels run
+    command $run
+  end
+
+  # success
+  task.exit    0
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
Index: branches/meh_branches/ppstack_test/ippTasks/summit.copy.pro
===================================================================
--- branches/meh_branches/ppstack_test/ippTasks/summit.copy.pro	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTasks/summit.copy.pro	(revision 33415)
@@ -29,4 +29,8 @@
 # list of pzDownloadExps that have completed downloading
 book init pzPendingAdvance
+
+macro copy.status
+    book listbook pzPendingImfile
+end
 
 macro copy.on
@@ -442,5 +446,18 @@
         # this function uses workdir_template, default_host, volume_template, volume_default,
         # it sets workdir and volume
-       	set.workdir.by.camera $CAMERA $CLASS_ID $workdir_template $default_host workdir_base
+
+        # Look to see if we have a stare type observation, and redirect it to an appropriate host
+	substr $EXP_NAME 10 1 EXPTYPE_KEY
+	if ("$EXPTYPE_KEY" == "a") 
+	  if ($NEBULOUS == 1) 
+	    $workdir_template = neb://@HOST@.1
+	  end
+	  set.workdir.by.camera STARE $CLASS_ID $workdir_template $default_host workdir_base
+        else 
+	  if ($NEBULOUS == 1)
+	    $workdir_template = neb://@HOST@.0
+	  end
+          set.workdir.by.camera $CAMERA $CLASS_ID $workdir_template $default_host workdir_base
+        end
 
         # figure out filename
Index: branches/meh_branches/ppstack_test/ippTasks/survey.pro
===================================================================
--- branches/meh_branches/ppstack_test/ippTasks/survey.pro	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTasks/survey.pro	(revision 33415)
@@ -3,22 +3,4 @@
 # test for required global variables
 check.globals
-
-# there is a bug in 'getbook', use a different method below
-# book getbook SURVEY_DIFF -var isBook
-# if ("$isBook" != "SURVEY_DIFF")
-#   book create SURVEY_DIFF
-# end
-# book getbook SURVEY_MAGIC -var isBook
-# if ("$isBook" == "NULL")
-#   book create SURVEY_MAGIC
-# end
-# book getbook SURVEY_DESTREAK -var isBook
-# if ("$isBook" == "NULL")
-#   book create SURVEY_DESTREAK
-# end
-# book getbook SURVEY_DIST -var isBook
-# if ("$isBook" == "NULL")
-#   book create SURVEY_DIST
-# end
 
 if (not($?haveSurveyBooks))
@@ -30,8 +12,9 @@
  book create SURVEY_DIST
  book create SURVEY_ADDSTAR   
+ book create SURVEY_MERGEDVODB
  book create SURVEY_CHIP_BG
  book create SURVEY_WARP_BG
  book create SURVEY_PUBLISH
- book create SURVEY_STATICSKYSINGLE   
+ book create SURVEY_STATICSKYSINGLE 
  $haveSurveyBooks = TRUE
 end
@@ -44,9 +27,9 @@
 $SURVEY_DIST_DB = 0
 $SURVEY_ADDSTAR_DB = 0
+$SURVEY_MERGEDVODB_DB = 0
 $SURVEY_CHIP_BG_DB = 0
 $SURVEY_WARP_BG_DB = 0
 $SURVEY_PUBLISH_DB = 0
 $SURVEY_STATICSKYSINGLE_DB = 0
-
 $SURVEY_EXEC = 120
 $SURVEY_POLL = 10
@@ -69,4 +52,7 @@
     active true
   end
+  task survey.mergedvodb
+    active true
+  end
   task survey.destreak
     active true
@@ -105,4 +91,7 @@
     active false
   end
+  task survey.mergedvodb
+    active false
+  end  
   task survey.destreak
     active false
@@ -213,10 +202,15 @@
 # user functions to manipulate magic labels
 macro survey.add.magic
-  if ($0 != 3)
-    echo "USAGE: survey.add.magic (label) (workdir base)"
+  if (($0 != 3)&&($0 != 4))
+    echo "USAGE: survey.add.magic (label) (workdir base) (multidiff)"
     break
   end
   book newpage SURVEY_MAGIC $1
   book setword SURVEY_MAGIC $1 WORKDIR $2
+  if ($0 == 3)
+     book setword SURVEY_MAGIC $1 MULTIDIFF FALSE
+  else 
+     book setword SURVEY_MAGIC $1 MULTIDIFF $3
+  end
   book setword SURVEY_MAGIC $1 STATE PENDING
 end
@@ -267,4 +261,31 @@
 end
 
+macro survey.add.mergedvodb
+  if ($0 != 4)
+    echo "USAGE: survey.add.mergedvodb (tag) (mergedvodb) (minidvodb_group)"
+    break
+  end
+  book newpage SURVEY_MERGEDVODB $1
+  book setword SURVEY_MERGEDVODB $1 MERGEDVODB $2
+  book setword SURVEY_MERGEDVODB $1 MINIDVODB_GROUP $3
+end
+
+macro survey.del.mergedvodb
+  if ($0 != 2)
+    echo "USAGE: survey.del.mergedvodb (tag)"
+    break
+  end
+  book delpage SURVEY_MERGEDVODB $1
+end
+
+macro survey.show.mergedvodb
+  if ($0 != 1)
+    echo "USAGE: survey.show.mergedvodb"
+    break
+  end
+  book listbook SURVEY_MERGEDVODB
+end
+
+
 # user functions to manipulate destreak labels
 macro survey.add.destreak
@@ -297,10 +318,11 @@
 # user functions to manipulate dist labels
 macro survey.add.dist
-  if ($0 != 3)
-    echo "USAGE: survey.add.dist (label) (workdir)"
+  if ($0 != 4)
+    echo "USAGE: survey.add.dist (label) (workdir) (muggle)"
     break
   end
   book newpage SURVEY_DIST $1
   book setword SURVEY_DIST $1 WORKDIR $2
+  book setword SURVEY_DIST $1 MUGGLE $3
   book setword SURVEY_DIST $1 STATE PENDING
 end
@@ -324,10 +346,11 @@
 # user functions to manipulate chip_bg labels
 macro survey.add.chip.bg
-  if ($0 != 3)
-    echo "USAGE: survey.add.chip.bg (label) (dist_group)"
+  if ($0 != 4)
+    echo "USAGE: survey.add.chip.bg (label) (dist_group) (muggle)"
     break
   end
   book newpage SURVEY_CHIP_BG $1
   book setword SURVEY_CHIP_BG $1 DIST_GROUP $2
+  book setword SURVEY_CHIP_BG $1 MUGGLE $3
   book setword SURVEY_CHIP_BG $1 STATE PENDING
 end
@@ -351,10 +374,11 @@
 # user functions to manipulate warp_bg labels
 macro survey.add.warp.bg
-  if ($0 != 3)
-    echo "USAGE: survey.add.warp.bg (label) (dist_group)"
+  if ($0 != 4)
+    echo "USAGE: survey.add.warp.bg (label) (dist_group) (muggle)"
     break
   end
   book newpage SURVEY_WARP_BG $1
   book setword SURVEY_WARP_BG $1 DIST_GROUP $2
+  book setword SURVEY_WARP_BG $1 MUGGLE $3
   book setword SURVEY_WARP_BG $1 STATE PENDING
 end
@@ -610,5 +634,8 @@
   periods      -exec $SURVEY_EXEC
   periods      -timeout $SURVEY_TIMEOUT
-  trange        13:00:00 14:00:00
+# 13:00 was being done in early morning rather than afternoon HST (so UT value), want both times to have SSdiff ASAP?
+# changing earlier for QUB to fetch @10pm for nightly processing there, should be enough time to finish MD stacks
+#  trange        13:00:00 14:00:00
+  trange	20:00:00 21:00:00
   npending      1
 
@@ -725,6 +752,11 @@
     book setword SURVEY_MAGIC $label STATE DONE
     book getword SURVEY_MAGIC $label WORKDIR -var workdir
-  
+    book getword SURVEY_MAGIC $label MULTIDIFF -var multidiff
+
     $run = magictool -rerun -definebyquery -label $label -diff_label $label -workdir $workdir/$label
+    
+    if (("$multidiff" == "TRUE")||("$multidiff" == "MULTIDIFF"))
+       $run = $run -multidiff
+    end
 
     if ($DB:n == 0)
@@ -735,5 +767,5 @@
     end
     
-    # echo $run
+    echo $run
     command $run
   end
@@ -814,11 +846,12 @@
     if ("$stage" == "cam") 
         #only queue destreaked cams. stacks and staticsky don't need this
-        if ("$DB:$SURVEY_ADDSTAR_DB" == "isp")
+    #    if ("$DB:$SURVEY_ADDSTAR_DB" == "isp")
             #this is the only way I can think of how to handle this (but it is messy). If it is a database that we KNOW doesn't use magicked (ie, isp), then do not queue destreaked
+           # now with no magic, all are uncensored
            $run = $run -uncensored
-	else
+#	else
 	   #if not isp, then run destreaked version only
-	   $run = $run -destreaked
-	end
+#	   $run = $run -destreaked
+#	end
     end
     if ("$stage" == "staticsky") 
@@ -826,4 +859,9 @@
         $run = $run -uncensored
     end
+    if ("$stage" == "staticsky_multi") 
+        #only queue uncensored staticsky (multi filter)
+        $run = $run -uncensored
+    end
+
     if ("$stage" == "stack") 
         #only queue uncensored stacks
@@ -858,4 +896,82 @@
   end
 end
+
+
+task survey.mergedvodb
+  host local
+ 
+  periods      -poll $SURVEY_POLL
+  periods      -exec $SURVEY_EXEC
+  periods      -timeout $SURVEY_TIMEOUT
+  npending     1
+
+  stdout $LOGDIR/survey.mergedvodb.log
+  stderr $LOGDIR/survey.mergedvodb.log
+
+  task.exec
+    book npages SURVEY_MERGEDVODB -var N
+    if ($N == 0)
+#      echo "No labels for processing"
+      break
+    endif
+
+    book getpage SURVEY_MERGEDVODB 0 -var tag -key STATE NEW
+    if ("$tag" == "NULL")
+      # All labels have been done --- reset
+    # echo "Resetting labels"
+      for i 0 $N
+        book getpage SURVEY_MERGEDVODB $i -var tag
+	book setword SURVEY_MERGEDVODB $tag STATE NEW
+      end
+      book getpage SURVEY_MERGEDVODB 0 -var tag -key STATE NEW
+
+      # Select different database
+      $SURVEY_MERGEDVODB_DB ++
+      if ($SURVEY_MERGEDVODB_DB >= $DB:n) set SURVEY_MERGEDVODB_DB = 0
+    end
+   book setword SURVEY_MERGEDVODB $tag STATE DONE
+   book getword SURVEY_MERGEDVODB $tag MERGEDVODB -var mergedvodb
+   book getword SURVEY_MERGEDVODB $tag MINIDVODB_GROUP -var minidvodb_group
+     
+ #   $run = addtool -definebyquery -destreaked -label $label -set_dvodb $dbodb
+    #$run = mergetool 
+    $run = mergedvodb_queue.pl --outroot $mergedvodb --mergedvodb $mergedvodb --minidvodb_group $minidvodb_group
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      $run = $run --dbname $DB:$SURVEY_MERGEDVODB_DB
+      option $DB:$SURVEY_MERGEDVODB_DB
+    end
+    #this run doesn't work because there's no workdir 
+    #$run = $run -definebyquery -mergedvodb $mergedvodb -minidvodb_group $minidvodb_group 
+    
+    
+    # echo $run
+
+  echo $run
+    command $run
+  end
+
+  # success
+   
+  task.exit    0
+#    echo "Success"
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
 
 
@@ -974,8 +1090,13 @@
     $workdir = $workdir_base/$label/$year/$month/$day
 
+    book getword SURVEY_DIST $label MUGGLE -var muggle
+
     book setword SURVEY_DIST $label STATE DONE
   
     # note workdir is set by the script based on site.config
     $run = dist_defineruns.pl --label $label --workdir $workdir
+    if ($muggle != 0)
+        $run = $run --no_magic 
+    end
 
     if ($DB:n == 0)
@@ -1048,6 +1169,10 @@
     book setword SURVEY_CHIP_BG $label STATE DONE
     book getword SURVEY_CHIP_BG $label DIST_GROUP -var dist_group
+    book getword SURVEY_CHIP_BG $label MUGGLE -var muggle
   
     $run = bgtool -definechip -label $label -set_dist_group $dist_group
+    if ($muggle == 0)
+        $run = $run -destreaked
+    end
 
     if ($DB:n == 0)
@@ -1058,5 +1183,7 @@
     end
     
-    # echo $run
+    if ($VERBOSE > 1) 
+        echo $run
+    end
     command $run
   end
@@ -1118,6 +1245,11 @@
     book setword SURVEY_WARP_BG $label STATE DONE
     book getword SURVEY_WARP_BG $label DIST_GROUP -var dist_group
+    book getword SURVEY_WARP_BG $label MUGGLE -var muggle
   
     $run = bgtool -definewarp -warp_label $label -set_dist_group $dist_group
+    if ($muggle == 0)
+        $run = $run -destreaked
+    end
+
 
     if ($DB:n == 0)
@@ -1128,5 +1260,7 @@
     end
     
-    # echo $run
+    if ($VERBOSE > 1) 
+        echo $run
+    end
     command $run
   end
@@ -1248,5 +1382,5 @@
     book npages SURVEY_STATICSKYSINGLE -var N
     if ($N == 0)
-      echo "No STATICSKYSINGLE labels for processing"
+       # echo "No STATICSKYSINGLE labels for processing"
        break
     endif
Index: branches/meh_branches/ppstack_test/ippTasks/vp.pro
===================================================================
--- branches/meh_branches/ppstack_test/ippTasks/vp.pro	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTasks/vp.pro	(revision 33415)
@@ -0,0 +1,249 @@
+## vp.pro : tasks for video photometry : -*- sh -*-
+
+# test for required global variables
+check.globals
+
+$LOGSUBDIR = $LOGDIR/vp
+mkdir $LOGSUBDIR
+
+### Initialise the books containing the tasks to do
+book init vpPendingRun
+
+### Database lists
+$vp_DB = 0
+$vp_revert_DB = 0
+
+$VP_POLL_LIMIT = 20
+
+### Check status of vping tasks
+macro vp.status
+  book listbook vpPendingRun
+end
+
+### Reset vping tasks
+macro vp.reset
+  book init vpPendingRun
+end
+
+### Turn vp tasks on
+macro vp.on
+  task vp.load
+    active true
+  end
+  task vp.run
+    active true
+  end
+end
+
+### Turn vp tasks off
+macro vp.off
+  task vp.load
+    active false
+  end
+  task vp.run
+    active false
+  end
+end
+
+macro vp.revert.on
+  task vp.revert
+    active true
+  end
+end
+
+macro vp.revert.off
+  task vp.revert
+    active false
+  end
+end
+
+macro set.vp.poll
+    $VP_POLL_LIMIT = $1
+end
+macro get.vp.poll
+    echo $VP_POLL_LIMIT
+end
+
+
+### Load tasks for doing video photometry
+### Tasks are loaded into vpPendingRun.
+task	       vp.load
+  host         local
+
+  periods      -poll $LOADPOLL
+  periods      -exec $LOADEXEC
+  periods      -timeout 30
+  npending     1
+
+  stdout NULL
+  stderr $LOGSUBDIR/vp.load.log
+
+  task.exec
+    if ($LABEL:n == 0) break
+    $run = vptool -pendingrun
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$vp_DB
+      $run = $run -dbname $DB:$vp_DB
+      $vp_DB ++
+      if ($vp_DB >= $DB:n) set vp_DB = 0
+    end
+    add_poll_args run
+    add_poll_labels run
+    $run = $run -limit $VP_POLL_LIMIT
+    command $run
+  end
+
+  # success
+  task.exit    0
+    # convert 'stdout' to book format
+    ipptool2book stdout vpPendingRun -key vp_id -uniq -setword dbname $options:0 -setword pantaskState INIT
+    if ($VERBOSE > 2)
+      book listbook vpPendingRun
+    end
+
+    # delete existing entries in the appropriate pantaskStates
+    process_cleanup vpPendingRun
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
+
+
+
+
+### Run video photometery tasks
+### Tasks are taken from vpPendingRun.
+task	       vp.run
+  periods      -poll $RUNPOLL
+  periods      -exec $RUNEXEC
+  periods      -timeout 300
+
+  task.exec
+    book npages vpPendingRun -var N
+    if ($N == 0) break
+    if ($NETWORK == 0) break
+    if ($BURNTOOLING == 1) break
+
+    # look for new runs in vpPendingRun (pantaskState == INIT)
+    book getpage vpPendingRun 0 -var pageName -key pantaskState INIT
+    if ("$pageName" == "NULL") break
+
+    book setword vpPendingRun $pageName pantaskState RUN
+    book getword vpPendingRun $pageName vp_id -var VP_ID
+    book getword vpPendingRun $pageName camera -var CAMERA
+    book getword vpPendingRun $pageName exp_tag -var EXP_TAG
+    book getword vpPendingRun $pageName workdir -var WORKDIR
+    book getword vpPendingRun $pageName dest_id -var DEST_ID
+    book getword vpPendingRun $pageName dest_name -var DEST_NAME
+    book getword vpPendingRun $pageName ds_dbname -var DS_DBNAME
+    book getword vpPendingRun $pageName ds_dbhost -var DS_DBHOST
+    book getword vpPendingRun $pageName dbname -var DBNAME
+
+    # set the host and workdir based on the skycell hash
+#    set.host.for.skycell $SKYCELL_ID
+#    set.workdir.by.skycell $SKYCELL_ID $WORKDIR_TEMPLATE $default_host WORKDIR
+
+    sprintf outroot "%s/%s.vp.%d" $WORKDIR $EXP_TAG $VP_ID
+
+    stdout $LOGSUBDIR/vp.log
+    stderr $LOGSUBDIR/vp.log
+
+    host anyhost
+
+    $run = videophot_process.pl --vp_id $VP_ID --outroot $outroot --redirect-output --camera $CAMERA 
+    if ($DEST_ID) 
+     $run = $run --dest_id $DEST_ID --product $DEST_NAME --ds_dbname $DS_DBNAME --ds_dbhost $DS_DBHOST
+    end
+    add_standard_args run
+
+    # save the pageName for future reference below
+    options $pageName
+
+    # create the command line
+    if ($VERBOSE > 1)
+      echo command $run
+    end
+    command $run
+  end
+
+  # default exit status
+  task.exit    default
+    process_exit vpPendingRun $options:0 $JOB_STATUS
+  end
+
+  # locked list
+  task.exit    crash
+    showcommand crash
+    echo "hostname: $JOB_HOSTNAME"
+    book setword vpPendingRun $options:0 pantaskState CRASH
+  end
+
+  # operation timed out?
+  task.exit    timeout
+    showcommand timeout
+    book setword vpPendingRun $options:0 pantaskState TIMEOUT
+  end
+end
+
+
+task vp.revert
+  host         local
+
+  periods      -poll 5
+  periods      -exec 600
+  periods      -exec 30
+  periods      -timeout 120
+  npending     1
+  
+  stdout NULL
+  stderr $LOGSUBDIR/vp.revert.log
+
+  task.exec
+    if ($LABEL:n == 0) break
+    $run = vptool -revertrun
+    if ($DB:n == 0)
+      option DEFAULT
+    else
+      # save the DB name for the exit tasks
+      option $DB:$vp_revert_DB
+      $run = $run -dbname $DB:$vp_revert_DB
+      $vp_revert_DB ++
+      if ($vp_revert_DB >= $DB:n) set vp_revert_DB = 0
+    end
+    add_poll_labels run
+    command $run
+  end
+
+  # success
+  task.exit    0
+  end
+
+  # locked list
+  task.exit    default
+    showcommand failure
+  end
+
+  task.exit    crash
+    showcommand crash
+  end
+
+  # operation times out?
+  task.exit    timeout
+    showcommand timeout
+  end
+end
Index: branches/meh_branches/ppstack_test/ippToPsps/config/IN/tables.vot
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/config/IN/tables.vot	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/config/IN/tables.vot	(revision 33415)
@@ -22,5 +22,4 @@
         <TR><TD>4</TD><TD>z</TD></TR>
         <TR><TD>5</TD><TD>y</TD></TR>
-        <TR><TD>6</TD><TD>z</TD></TR>
         </TABLEDATA>
       </DATA>
@@ -98996,3077 +98995,3077 @@
       <DATA>
         <TABLEDATA>
-          <TR><TD>1</TD><TD>-999</TD></TR>
-          <TR><TD>2</TD><TD>-999</TD></TR>
-          <TR><TD>3</TD><TD>-999</TD></TR>
-          <TR><TD>4</TD><TD>-999</TD></TR>
-          <TR><TD>5</TD><TD>-999</TD></TR>
-          <TR><TD>6</TD><TD>-999</TD></TR>
-          <TR><TD>7</TD><TD>-999</TD></TR>
-          <TR><TD>8</TD><TD>-999</TD></TR>
-          <TR><TD>9</TD><TD>-999</TD></TR>
-          <TR><TD>10</TD><TD>-999</TD></TR>
-          <TR><TD>11</TD><TD>-999</TD></TR>
-          <TR><TD>12</TD><TD>-999</TD></TR>
-          <TR><TD>13</TD><TD>-999</TD></TR>
-          <TR><TD>14</TD><TD>-999</TD></TR>
-          <TR><TD>15</TD><TD>-999</TD></TR>
-          <TR><TD>16</TD><TD>-999</TD></TR>
-          <TR><TD>17</TD><TD>-999</TD></TR>
-          <TR><TD>18</TD><TD>-999</TD></TR>
-          <TR><TD>19</TD><TD>-999</TD></TR>
-          <TR><TD>20</TD><TD>-999</TD></TR>
-          <TR><TD>21</TD><TD>-999</TD></TR>
-          <TR><TD>22</TD><TD>-999</TD></TR>
-          <TR><TD>23</TD><TD>-999</TD></TR>
-          <TR><TD>24</TD><TD>-999</TD></TR>
-          <TR><TD>25</TD><TD>-999</TD></TR>
-          <TR><TD>26</TD><TD>-999</TD></TR>
-          <TR><TD>27</TD><TD>-999</TD></TR>
-          <TR><TD>28</TD><TD>-999</TD></TR>
-          <TR><TD>29</TD><TD>-999</TD></TR>
-          <TR><TD>30</TD><TD>-999</TD></TR>
-          <TR><TD>31</TD><TD>-999</TD></TR>
-          <TR><TD>32</TD><TD>-999</TD></TR>
-          <TR><TD>33</TD><TD>-999</TD></TR>
-          <TR><TD>34</TD><TD>-999</TD></TR>
-          <TR><TD>35</TD><TD>-999</TD></TR>
-          <TR><TD>36</TD><TD>-999</TD></TR>
-          <TR><TD>37</TD><TD>-999</TD></TR>
-          <TR><TD>38</TD><TD>-999</TD></TR>
-          <TR><TD>39</TD><TD>-999</TD></TR>
-          <TR><TD>40</TD><TD>-999</TD></TR>
-          <TR><TD>41</TD><TD>-999</TD></TR>
-          <TR><TD>42</TD><TD>-999</TD></TR>
-          <TR><TD>43</TD><TD>-999</TD></TR>
-          <TR><TD>44</TD><TD>-999</TD></TR>
-          <TR><TD>45</TD><TD>-999</TD></TR>
-          <TR><TD>46</TD><TD>-999</TD></TR>
-          <TR><TD>47</TD><TD>-999</TD></TR>
-          <TR><TD>48</TD><TD>-999</TD></TR>
-          <TR><TD>49</TD><TD>-999</TD></TR>
-          <TR><TD>50</TD><TD>-999</TD></TR>
-          <TR><TD>51</TD><TD>-999</TD></TR>
-          <TR><TD>52</TD><TD>-999</TD></TR>
-          <TR><TD>53</TD><TD>-999</TD></TR>
-          <TR><TD>54</TD><TD>-999</TD></TR>
-          <TR><TD>55</TD><TD>-999</TD></TR>
-          <TR><TD>56</TD><TD>-999</TD></TR>
-          <TR><TD>57</TD><TD>-999</TD></TR>
-          <TR><TD>58</TD><TD>-999</TD></TR>
-          <TR><TD>59</TD><TD>-999</TD></TR>
-          <TR><TD>60</TD><TD>-999</TD></TR>
-          <TR><TD>61</TD><TD>-999</TD></TR>
-          <TR><TD>62</TD><TD>-999</TD></TR>
-          <TR><TD>63</TD><TD>-999</TD></TR>
-          <TR><TD>64</TD><TD>-999</TD></TR>
-          <TR><TD>65</TD><TD>-999</TD></TR>
-          <TR><TD>66</TD><TD>-999</TD></TR>
-          <TR><TD>67</TD><TD>-999</TD></TR>
-          <TR><TD>68</TD><TD>-999</TD></TR>
-          <TR><TD>69</TD><TD>-999</TD></TR>
-          <TR><TD>70</TD><TD>-999</TD></TR>
-          <TR><TD>71</TD><TD>-999</TD></TR>
-          <TR><TD>72</TD><TD>-999</TD></TR>
-          <TR><TD>73</TD><TD>-999</TD></TR>
-          <TR><TD>74</TD><TD>-999</TD></TR>
-          <TR><TD>75</TD><TD>-999</TD></TR>
-          <TR><TD>76</TD><TD>-999</TD></TR>
-          <TR><TD>77</TD><TD>-999</TD></TR>
-          <TR><TD>78</TD><TD>-999</TD></TR>
-          <TR><TD>79</TD><TD>-999</TD></TR>
-          <TR><TD>80</TD><TD>-999</TD></TR>
-          <TR><TD>81</TD><TD>-999</TD></TR>
-          <TR><TD>82</TD><TD>-999</TD></TR>
-          <TR><TD>83</TD><TD>-999</TD></TR>
-          <TR><TD>84</TD><TD>-999</TD></TR>
-          <TR><TD>85</TD><TD>-999</TD></TR>
-          <TR><TD>86</TD><TD>-999</TD></TR>
-          <TR><TD>87</TD><TD>-999</TD></TR>
-          <TR><TD>88</TD><TD>-999</TD></TR>
-          <TR><TD>89</TD><TD>-999</TD></TR>
-          <TR><TD>90</TD><TD>-999</TD></TR>
-          <TR><TD>91</TD><TD>-999</TD></TR>
-          <TR><TD>92</TD><TD>-999</TD></TR>
-          <TR><TD>93</TD><TD>-999</TD></TR>
-          <TR><TD>94</TD><TD>-999</TD></TR>
-          <TR><TD>95</TD><TD>-999</TD></TR>
-          <TR><TD>96</TD><TD>-999</TD></TR>
-          <TR><TD>97</TD><TD>-999</TD></TR>
-          <TR><TD>98</TD><TD>-999</TD></TR>
-          <TR><TD>99</TD><TD>-999</TD></TR>
-          <TR><TD>100</TD><TD>-999</TD></TR>
-          <TR><TD>101</TD><TD>-999</TD></TR>
-          <TR><TD>102</TD><TD>-999</TD></TR>
-          <TR><TD>103</TD><TD>-999</TD></TR>
-          <TR><TD>104</TD><TD>-999</TD></TR>
-          <TR><TD>105</TD><TD>-999</TD></TR>
-          <TR><TD>106</TD><TD>-999</TD></TR>
-          <TR><TD>107</TD><TD>-999</TD></TR>
-          <TR><TD>108</TD><TD>-999</TD></TR>
-          <TR><TD>109</TD><TD>-999</TD></TR>
-          <TR><TD>110</TD><TD>-999</TD></TR>
-          <TR><TD>111</TD><TD>-999</TD></TR>
-          <TR><TD>112</TD><TD>-999</TD></TR>
-          <TR><TD>113</TD><TD>-999</TD></TR>
-          <TR><TD>114</TD><TD>-999</TD></TR>
-          <TR><TD>115</TD><TD>-999</TD></TR>
-          <TR><TD>116</TD><TD>-999</TD></TR>
-          <TR><TD>117</TD><TD>-999</TD></TR>
-          <TR><TD>118</TD><TD>-999</TD></TR>
-          <TR><TD>119</TD><TD>-999</TD></TR>
-          <TR><TD>120</TD><TD>-999</TD></TR>
-          <TR><TD>121</TD><TD>-999</TD></TR>
-          <TR><TD>122</TD><TD>-999</TD></TR>
-          <TR><TD>123</TD><TD>-999</TD></TR>
-          <TR><TD>124</TD><TD>-999</TD></TR>
-          <TR><TD>125</TD><TD>-999</TD></TR>
-          <TR><TD>126</TD><TD>-999</TD></TR>
-          <TR><TD>127</TD><TD>-999</TD></TR>
-          <TR><TD>128</TD><TD>-999</TD></TR>
-          <TR><TD>129</TD><TD>-999</TD></TR>
-          <TR><TD>130</TD><TD>-999</TD></TR>
-          <TR><TD>131</TD><TD>-999</TD></TR>
-          <TR><TD>132</TD><TD>-999</TD></TR>
-          <TR><TD>133</TD><TD>-999</TD></TR>
-          <TR><TD>134</TD><TD>-999</TD></TR>
-          <TR><TD>135</TD><TD>-999</TD></TR>
-          <TR><TD>136</TD><TD>-999</TD></TR>
-          <TR><TD>137</TD><TD>-999</TD></TR>
-          <TR><TD>138</TD><TD>-999</TD></TR>
-          <TR><TD>139</TD><TD>-999</TD></TR>
-          <TR><TD>140</TD><TD>-999</TD></TR>
-          <TR><TD>141</TD><TD>-999</TD></TR>
-          <TR><TD>142</TD><TD>-999</TD></TR>
-          <TR><TD>143</TD><TD>-999</TD></TR>
-          <TR><TD>144</TD><TD>-999</TD></TR>
-          <TR><TD>145</TD><TD>-999</TD></TR>
-          <TR><TD>146</TD><TD>-999</TD></TR>
-          <TR><TD>147</TD><TD>-999</TD></TR>
-          <TR><TD>148</TD><TD>-999</TD></TR>
-          <TR><TD>149</TD><TD>-999</TD></TR>
-          <TR><TD>150</TD><TD>-999</TD></TR>
-          <TR><TD>151</TD><TD>-999</TD></TR>
-          <TR><TD>152</TD><TD>-999</TD></TR>
-          <TR><TD>153</TD><TD>-999</TD></TR>
-          <TR><TD>154</TD><TD>-999</TD></TR>
-          <TR><TD>155</TD><TD>-999</TD></TR>
-          <TR><TD>156</TD><TD>-999</TD></TR>
-          <TR><TD>157</TD><TD>-999</TD></TR>
-          <TR><TD>158</TD><TD>-999</TD></TR>
-          <TR><TD>159</TD><TD>-999</TD></TR>
-          <TR><TD>160</TD><TD>-999</TD></TR>
-          <TR><TD>161</TD><TD>-999</TD></TR>
-          <TR><TD>162</TD><TD>-999</TD></TR>
-          <TR><TD>163</TD><TD>-999</TD></TR>
-          <TR><TD>164</TD><TD>-999</TD></TR>
-          <TR><TD>165</TD><TD>-999</TD></TR>
-          <TR><TD>166</TD><TD>-999</TD></TR>
-          <TR><TD>167</TD><TD>-999</TD></TR>
-          <TR><TD>168</TD><TD>-999</TD></TR>
-          <TR><TD>169</TD><TD>-999</TD></TR>
-          <TR><TD>170</TD><TD>-999</TD></TR>
-          <TR><TD>171</TD><TD>-999</TD></TR>
-          <TR><TD>172</TD><TD>-999</TD></TR>
-          <TR><TD>173</TD><TD>-999</TD></TR>
-          <TR><TD>174</TD><TD>-999</TD></TR>
-          <TR><TD>175</TD><TD>-999</TD></TR>
-          <TR><TD>176</TD><TD>-999</TD></TR>
-          <TR><TD>177</TD><TD>-999</TD></TR>
-          <TR><TD>178</TD><TD>-999</TD></TR>
-          <TR><TD>179</TD><TD>-999</TD></TR>
-          <TR><TD>180</TD><TD>-999</TD></TR>
-          <TR><TD>181</TD><TD>-999</TD></TR>
-          <TR><TD>182</TD><TD>-999</TD></TR>
-          <TR><TD>183</TD><TD>-999</TD></TR>
-          <TR><TD>184</TD><TD>-999</TD></TR>
-          <TR><TD>185</TD><TD>-999</TD></TR>
-          <TR><TD>186</TD><TD>-999</TD></TR>
-          <TR><TD>187</TD><TD>-999</TD></TR>
-          <TR><TD>188</TD><TD>-999</TD></TR>
-          <TR><TD>189</TD><TD>-999</TD></TR>
-          <TR><TD>190</TD><TD>-999</TD></TR>
-          <TR><TD>191</TD><TD>-999</TD></TR>
-          <TR><TD>192</TD><TD>-999</TD></TR>
-          <TR><TD>193</TD><TD>-999</TD></TR>
-          <TR><TD>194</TD><TD>-999</TD></TR>
-          <TR><TD>195</TD><TD>-999</TD></TR>
-          <TR><TD>196</TD><TD>-999</TD></TR>
-          <TR><TD>197</TD><TD>-999</TD></TR>
-          <TR><TD>198</TD><TD>-999</TD></TR>
-          <TR><TD>199</TD><TD>-999</TD></TR>
-          <TR><TD>200</TD><TD>-999</TD></TR>
-          <TR><TD>201</TD><TD>-999</TD></TR>
-          <TR><TD>202</TD><TD>-999</TD></TR>
-          <TR><TD>203</TD><TD>-999</TD></TR>
-          <TR><TD>204</TD><TD>-999</TD></TR>
-          <TR><TD>205</TD><TD>-999</TD></TR>
-          <TR><TD>206</TD><TD>-999</TD></TR>
-          <TR><TD>207</TD><TD>-999</TD></TR>
-          <TR><TD>208</TD><TD>-999</TD></TR>
-          <TR><TD>209</TD><TD>-999</TD></TR>
-          <TR><TD>210</TD><TD>-999</TD></TR>
-          <TR><TD>211</TD><TD>-999</TD></TR>
-          <TR><TD>212</TD><TD>-999</TD></TR>
-          <TR><TD>213</TD><TD>-999</TD></TR>
-          <TR><TD>214</TD><TD>-999</TD></TR>
-          <TR><TD>215</TD><TD>-999</TD></TR>
-          <TR><TD>216</TD><TD>-999</TD></TR>
-          <TR><TD>217</TD><TD>-999</TD></TR>
-          <TR><TD>218</TD><TD>-999</TD></TR>
-          <TR><TD>219</TD><TD>-999</TD></TR>
-          <TR><TD>220</TD><TD>-999</TD></TR>
-          <TR><TD>221</TD><TD>-999</TD></TR>
-          <TR><TD>222</TD><TD>-999</TD></TR>
-          <TR><TD>223</TD><TD>-999</TD></TR>
-          <TR><TD>224</TD><TD>-999</TD></TR>
-          <TR><TD>225</TD><TD>-999</TD></TR>
-          <TR><TD>226</TD><TD>-999</TD></TR>
-          <TR><TD>227</TD><TD>-999</TD></TR>
-          <TR><TD>228</TD><TD>-999</TD></TR>
-          <TR><TD>229</TD><TD>-999</TD></TR>
-          <TR><TD>230</TD><TD>-999</TD></TR>
-          <TR><TD>231</TD><TD>-999</TD></TR>
-          <TR><TD>232</TD><TD>-999</TD></TR>
-          <TR><TD>233</TD><TD>-999</TD></TR>
-          <TR><TD>234</TD><TD>-999</TD></TR>
-          <TR><TD>235</TD><TD>-999</TD></TR>
-          <TR><TD>236</TD><TD>-999</TD></TR>
-          <TR><TD>237</TD><TD>-999</TD></TR>
-          <TR><TD>238</TD><TD>-999</TD></TR>
-          <TR><TD>239</TD><TD>-999</TD></TR>
-          <TR><TD>240</TD><TD>-999</TD></TR>
-          <TR><TD>241</TD><TD>-999</TD></TR>
-          <TR><TD>242</TD><TD>-999</TD></TR>
-          <TR><TD>243</TD><TD>-999</TD></TR>
-          <TR><TD>244</TD><TD>-999</TD></TR>
-          <TR><TD>245</TD><TD>-999</TD></TR>
-          <TR><TD>246</TD><TD>-999</TD></TR>
-          <TR><TD>247</TD><TD>-999</TD></TR>
-          <TR><TD>248</TD><TD>-999</TD></TR>
-          <TR><TD>249</TD><TD>-999</TD></TR>
-          <TR><TD>250</TD><TD>-999</TD></TR>
-          <TR><TD>251</TD><TD>-999</TD></TR>
-          <TR><TD>252</TD><TD>-999</TD></TR>
-          <TR><TD>253</TD><TD>-999</TD></TR>
-          <TR><TD>254</TD><TD>-999</TD></TR>
-          <TR><TD>255</TD><TD>-999</TD></TR>
-          <TR><TD>256</TD><TD>-999</TD></TR>
-          <TR><TD>257</TD><TD>-999</TD></TR>
-          <TR><TD>258</TD><TD>-999</TD></TR>
-          <TR><TD>259</TD><TD>-999</TD></TR>
-          <TR><TD>260</TD><TD>-999</TD></TR>
-          <TR><TD>261</TD><TD>-999</TD></TR>
-          <TR><TD>262</TD><TD>-999</TD></TR>
-          <TR><TD>263</TD><TD>-999</TD></TR>
-          <TR><TD>264</TD><TD>-999</TD></TR>
-          <TR><TD>265</TD><TD>-999</TD></TR>
-          <TR><TD>266</TD><TD>-999</TD></TR>
-          <TR><TD>267</TD><TD>-999</TD></TR>
-          <TR><TD>268</TD><TD>-999</TD></TR>
-          <TR><TD>269</TD><TD>-999</TD></TR>
-          <TR><TD>270</TD><TD>-999</TD></TR>
-          <TR><TD>271</TD><TD>-999</TD></TR>
-          <TR><TD>272</TD><TD>-999</TD></TR>
-          <TR><TD>273</TD><TD>-999</TD></TR>
-          <TR><TD>274</TD><TD>-999</TD></TR>
-          <TR><TD>275</TD><TD>-999</TD></TR>
-          <TR><TD>276</TD><TD>-999</TD></TR>
-          <TR><TD>277</TD><TD>-999</TD></TR>
-          <TR><TD>278</TD><TD>-999</TD></TR>
-          <TR><TD>279</TD><TD>-999</TD></TR>
-          <TR><TD>280</TD><TD>-999</TD></TR>
-          <TR><TD>281</TD><TD>-999</TD></TR>
-          <TR><TD>282</TD><TD>-999</TD></TR>
-          <TR><TD>283</TD><TD>-999</TD></TR>
-          <TR><TD>284</TD><TD>-999</TD></TR>
-          <TR><TD>285</TD><TD>-999</TD></TR>
-          <TR><TD>286</TD><TD>-999</TD></TR>
-          <TR><TD>287</TD><TD>-999</TD></TR>
-          <TR><TD>288</TD><TD>-999</TD></TR>
-          <TR><TD>289</TD><TD>-999</TD></TR>
-          <TR><TD>290</TD><TD>-999</TD></TR>
-          <TR><TD>291</TD><TD>-999</TD></TR>
-          <TR><TD>292</TD><TD>-999</TD></TR>
-          <TR><TD>293</TD><TD>-999</TD></TR>
-          <TR><TD>294</TD><TD>-999</TD></TR>
-          <TR><TD>295</TD><TD>-999</TD></TR>
-          <TR><TD>296</TD><TD>-999</TD></TR>
-          <TR><TD>297</TD><TD>-999</TD></TR>
-          <TR><TD>298</TD><TD>-999</TD></TR>
-          <TR><TD>299</TD><TD>-999</TD></TR>
-          <TR><TD>300</TD><TD>-999</TD></TR>
-          <TR><TD>301</TD><TD>-999</TD></TR>
-          <TR><TD>302</TD><TD>-999</TD></TR>
-          <TR><TD>303</TD><TD>-999</TD></TR>
-          <TR><TD>304</TD><TD>-999</TD></TR>
-          <TR><TD>305</TD><TD>-999</TD></TR>
-          <TR><TD>306</TD><TD>-999</TD></TR>
-          <TR><TD>307</TD><TD>-999</TD></TR>
-          <TR><TD>308</TD><TD>-999</TD></TR>
-          <TR><TD>309</TD><TD>-999</TD></TR>
-          <TR><TD>310</TD><TD>-999</TD></TR>
-          <TR><TD>311</TD><TD>-999</TD></TR>
-          <TR><TD>312</TD><TD>-999</TD></TR>
-          <TR><TD>313</TD><TD>-999</TD></TR>
-          <TR><TD>314</TD><TD>-999</TD></TR>
-          <TR><TD>315</TD><TD>-999</TD></TR>
-          <TR><TD>316</TD><TD>-999</TD></TR>
-          <TR><TD>317</TD><TD>-999</TD></TR>
-          <TR><TD>318</TD><TD>-999</TD></TR>
-          <TR><TD>319</TD><TD>-999</TD></TR>
-          <TR><TD>320</TD><TD>-999</TD></TR>
-          <TR><TD>321</TD><TD>-999</TD></TR>
-          <TR><TD>322</TD><TD>-999</TD></TR>
-          <TR><TD>323</TD><TD>-999</TD></TR>
-          <TR><TD>324</TD><TD>-999</TD></TR>
-          <TR><TD>325</TD><TD>-999</TD></TR>
-          <TR><TD>326</TD><TD>-999</TD></TR>
-          <TR><TD>327</TD><TD>-999</TD></TR>
-          <TR><TD>328</TD><TD>-999</TD></TR>
-          <TR><TD>329</TD><TD>-999</TD></TR>
-          <TR><TD>330</TD><TD>-999</TD></TR>
-          <TR><TD>331</TD><TD>-999</TD></TR>
-          <TR><TD>332</TD><TD>-999</TD></TR>
-          <TR><TD>333</TD><TD>-999</TD></TR>
-          <TR><TD>334</TD><TD>-999</TD></TR>
-          <TR><TD>335</TD><TD>-999</TD></TR>
-          <TR><TD>336</TD><TD>-999</TD></TR>
-          <TR><TD>337</TD><TD>-999</TD></TR>
-          <TR><TD>338</TD><TD>-999</TD></TR>
-          <TR><TD>339</TD><TD>-999</TD></TR>
-          <TR><TD>340</TD><TD>-999</TD></TR>
-          <TR><TD>341</TD><TD>-999</TD></TR>
-          <TR><TD>342</TD><TD>-999</TD></TR>
-          <TR><TD>343</TD><TD>-999</TD></TR>
-          <TR><TD>344</TD><TD>-999</TD></TR>
-          <TR><TD>345</TD><TD>-999</TD></TR>
-          <TR><TD>346</TD><TD>-999</TD></TR>
-          <TR><TD>347</TD><TD>-999</TD></TR>
-          <TR><TD>348</TD><TD>-999</TD></TR>
-          <TR><TD>349</TD><TD>-999</TD></TR>
-          <TR><TD>350</TD><TD>-999</TD></TR>
-          <TR><TD>351</TD><TD>-999</TD></TR>
-          <TR><TD>352</TD><TD>-999</TD></TR>
-          <TR><TD>353</TD><TD>-999</TD></TR>
-          <TR><TD>354</TD><TD>-999</TD></TR>
-          <TR><TD>355</TD><TD>-999</TD></TR>
-          <TR><TD>356</TD><TD>-999</TD></TR>
-          <TR><TD>357</TD><TD>-999</TD></TR>
-          <TR><TD>358</TD><TD>-999</TD></TR>
-          <TR><TD>359</TD><TD>-999</TD></TR>
-          <TR><TD>360</TD><TD>-999</TD></TR>
-          <TR><TD>361</TD><TD>-999</TD></TR>
-          <TR><TD>362</TD><TD>-999</TD></TR>
-          <TR><TD>363</TD><TD>-999</TD></TR>
-          <TR><TD>364</TD><TD>-999</TD></TR>
-          <TR><TD>365</TD><TD>-999</TD></TR>
-          <TR><TD>366</TD><TD>-999</TD></TR>
-          <TR><TD>367</TD><TD>-999</TD></TR>
-          <TR><TD>368</TD><TD>-999</TD></TR>
-          <TR><TD>369</TD><TD>-999</TD></TR>
-          <TR><TD>370</TD><TD>-999</TD></TR>
-          <TR><TD>371</TD><TD>-999</TD></TR>
-          <TR><TD>372</TD><TD>-999</TD></TR>
-          <TR><TD>373</TD><TD>-999</TD></TR>
-          <TR><TD>374</TD><TD>-999</TD></TR>
-          <TR><TD>375</TD><TD>-999</TD></TR>
-          <TR><TD>376</TD><TD>-999</TD></TR>
-          <TR><TD>377</TD><TD>-999</TD></TR>
-          <TR><TD>378</TD><TD>-999</TD></TR>
-          <TR><TD>379</TD><TD>-999</TD></TR>
-          <TR><TD>380</TD><TD>-999</TD></TR>
-          <TR><TD>381</TD><TD>-999</TD></TR>
-          <TR><TD>382</TD><TD>-999</TD></TR>
-          <TR><TD>383</TD><TD>-999</TD></TR>
-          <TR><TD>384</TD><TD>-999</TD></TR>
-          <TR><TD>385</TD><TD>-999</TD></TR>
-          <TR><TD>386</TD><TD>-999</TD></TR>
-          <TR><TD>387</TD><TD>-999</TD></TR>
-          <TR><TD>388</TD><TD>-999</TD></TR>
-          <TR><TD>389</TD><TD>-999</TD></TR>
-          <TR><TD>390</TD><TD>-999</TD></TR>
-          <TR><TD>391</TD><TD>-999</TD></TR>
-          <TR><TD>392</TD><TD>-999</TD></TR>
-          <TR><TD>393</TD><TD>-999</TD></TR>
-          <TR><TD>394</TD><TD>-999</TD></TR>
-          <TR><TD>395</TD><TD>-999</TD></TR>
-          <TR><TD>396</TD><TD>-999</TD></TR>
-          <TR><TD>397</TD><TD>-999</TD></TR>
-          <TR><TD>398</TD><TD>-999</TD></TR>
-          <TR><TD>399</TD><TD>-999</TD></TR>
-          <TR><TD>400</TD><TD>-999</TD></TR>
-          <TR><TD>401</TD><TD>-999</TD></TR>
-          <TR><TD>402</TD><TD>-999</TD></TR>
-          <TR><TD>403</TD><TD>-999</TD></TR>
-          <TR><TD>404</TD><TD>-999</TD></TR>
-          <TR><TD>405</TD><TD>-999</TD></TR>
-          <TR><TD>406</TD><TD>-999</TD></TR>
-          <TR><TD>407</TD><TD>-999</TD></TR>
-          <TR><TD>408</TD><TD>-999</TD></TR>
-          <TR><TD>409</TD><TD>-999</TD></TR>
-          <TR><TD>410</TD><TD>-999</TD></TR>
-          <TR><TD>411</TD><TD>-999</TD></TR>
-          <TR><TD>412</TD><TD>-999</TD></TR>
-          <TR><TD>413</TD><TD>-999</TD></TR>
-          <TR><TD>414</TD><TD>-999</TD></TR>
-          <TR><TD>415</TD><TD>-999</TD></TR>
-          <TR><TD>416</TD><TD>-999</TD></TR>
-          <TR><TD>417</TD><TD>-999</TD></TR>
-          <TR><TD>418</TD><TD>-999</TD></TR>
-          <TR><TD>419</TD><TD>-999</TD></TR>
-          <TR><TD>420</TD><TD>-999</TD></TR>
-          <TR><TD>421</TD><TD>-999</TD></TR>
-          <TR><TD>422</TD><TD>-999</TD></TR>
-          <TR><TD>423</TD><TD>-999</TD></TR>
-          <TR><TD>424</TD><TD>-999</TD></TR>
-          <TR><TD>425</TD><TD>-999</TD></TR>
-          <TR><TD>426</TD><TD>-999</TD></TR>
-          <TR><TD>427</TD><TD>-999</TD></TR>
-          <TR><TD>428</TD><TD>-999</TD></TR>
-          <TR><TD>429</TD><TD>-999</TD></TR>
-          <TR><TD>430</TD><TD>-999</TD></TR>
-          <TR><TD>431</TD><TD>-999</TD></TR>
-          <TR><TD>432</TD><TD>-999</TD></TR>
-          <TR><TD>433</TD><TD>-999</TD></TR>
-          <TR><TD>434</TD><TD>-999</TD></TR>
-          <TR><TD>435</TD><TD>-999</TD></TR>
-          <TR><TD>436</TD><TD>-999</TD></TR>
-          <TR><TD>437</TD><TD>-999</TD></TR>
-          <TR><TD>438</TD><TD>-999</TD></TR>
-          <TR><TD>439</TD><TD>-999</TD></TR>
-          <TR><TD>440</TD><TD>-999</TD></TR>
-          <TR><TD>441</TD><TD>-999</TD></TR>
-          <TR><TD>442</TD><TD>-999</TD></TR>
-          <TR><TD>443</TD><TD>-999</TD></TR>
-          <TR><TD>444</TD><TD>-999</TD></TR>
-          <TR><TD>445</TD><TD>-999</TD></TR>
-          <TR><TD>446</TD><TD>-999</TD></TR>
-          <TR><TD>447</TD><TD>-999</TD></TR>
-          <TR><TD>448</TD><TD>-999</TD></TR>
-          <TR><TD>449</TD><TD>-999</TD></TR>
-          <TR><TD>450</TD><TD>-999</TD></TR>
-          <TR><TD>451</TD><TD>-999</TD></TR>
-          <TR><TD>452</TD><TD>-999</TD></TR>
-          <TR><TD>453</TD><TD>-999</TD></TR>
-          <TR><TD>454</TD><TD>-999</TD></TR>
-          <TR><TD>455</TD><TD>-999</TD></TR>
-          <TR><TD>456</TD><TD>-999</TD></TR>
-          <TR><TD>457</TD><TD>-999</TD></TR>
-          <TR><TD>458</TD><TD>-999</TD></TR>
-          <TR><TD>459</TD><TD>-999</TD></TR>
-          <TR><TD>460</TD><TD>-999</TD></TR>
-          <TR><TD>461</TD><TD>-999</TD></TR>
-          <TR><TD>462</TD><TD>-999</TD></TR>
-          <TR><TD>463</TD><TD>-999</TD></TR>
-          <TR><TD>464</TD><TD>-999</TD></TR>
-          <TR><TD>465</TD><TD>-999</TD></TR>
-          <TR><TD>466</TD><TD>-999</TD></TR>
-          <TR><TD>467</TD><TD>-999</TD></TR>
-          <TR><TD>468</TD><TD>-999</TD></TR>
-          <TR><TD>469</TD><TD>-999</TD></TR>
-          <TR><TD>470</TD><TD>-999</TD></TR>
-          <TR><TD>471</TD><TD>-999</TD></TR>
-          <TR><TD>472</TD><TD>-999</TD></TR>
-          <TR><TD>473</TD><TD>-999</TD></TR>
-          <TR><TD>474</TD><TD>-999</TD></TR>
-          <TR><TD>475</TD><TD>-999</TD></TR>
-          <TR><TD>476</TD><TD>-999</TD></TR>
-          <TR><TD>477</TD><TD>-999</TD></TR>
-          <TR><TD>478</TD><TD>-999</TD></TR>
-          <TR><TD>479</TD><TD>-999</TD></TR>
-          <TR><TD>480</TD><TD>-999</TD></TR>
-          <TR><TD>481</TD><TD>-999</TD></TR>
-          <TR><TD>482</TD><TD>-999</TD></TR>
-          <TR><TD>483</TD><TD>-999</TD></TR>
-          <TR><TD>484</TD><TD>-999</TD></TR>
-          <TR><TD>485</TD><TD>-999</TD></TR>
-          <TR><TD>486</TD><TD>-999</TD></TR>
-          <TR><TD>487</TD><TD>-999</TD></TR>
-          <TR><TD>488</TD><TD>-999</TD></TR>
-          <TR><TD>489</TD><TD>-999</TD></TR>
-          <TR><TD>490</TD><TD>-999</TD></TR>
-          <TR><TD>491</TD><TD>-999</TD></TR>
-          <TR><TD>492</TD><TD>-999</TD></TR>
-          <TR><TD>493</TD><TD>-999</TD></TR>
-          <TR><TD>494</TD><TD>-999</TD></TR>
-          <TR><TD>495</TD><TD>-999</TD></TR>
-          <TR><TD>496</TD><TD>-999</TD></TR>
-          <TR><TD>497</TD><TD>-999</TD></TR>
-          <TR><TD>498</TD><TD>-999</TD></TR>
-          <TR><TD>499</TD><TD>-999</TD></TR>
-          <TR><TD>500</TD><TD>-999</TD></TR>
-          <TR><TD>501</TD><TD>-999</TD></TR>
-          <TR><TD>502</TD><TD>-999</TD></TR>
-          <TR><TD>503</TD><TD>-999</TD></TR>
-          <TR><TD>504</TD><TD>-999</TD></TR>
-          <TR><TD>505</TD><TD>-999</TD></TR>
-          <TR><TD>506</TD><TD>-999</TD></TR>
-          <TR><TD>507</TD><TD>-999</TD></TR>
-          <TR><TD>508</TD><TD>-999</TD></TR>
-          <TR><TD>509</TD><TD>-999</TD></TR>
-          <TR><TD>510</TD><TD>-999</TD></TR>
-          <TR><TD>511</TD><TD>-999</TD></TR>
-          <TR><TD>512</TD><TD>-999</TD></TR>
-          <TR><TD>513</TD><TD>-999</TD></TR>
-          <TR><TD>514</TD><TD>-999</TD></TR>
-          <TR><TD>515</TD><TD>-999</TD></TR>
-          <TR><TD>516</TD><TD>-999</TD></TR>
-          <TR><TD>517</TD><TD>-999</TD></TR>
-          <TR><TD>518</TD><TD>-999</TD></TR>
-          <TR><TD>519</TD><TD>-999</TD></TR>
-          <TR><TD>520</TD><TD>-999</TD></TR>
-          <TR><TD>521</TD><TD>-999</TD></TR>
-          <TR><TD>522</TD><TD>-999</TD></TR>
-          <TR><TD>523</TD><TD>-999</TD></TR>
-          <TR><TD>524</TD><TD>-999</TD></TR>
-          <TR><TD>525</TD><TD>-999</TD></TR>
-          <TR><TD>526</TD><TD>-999</TD></TR>
-          <TR><TD>527</TD><TD>-999</TD></TR>
-          <TR><TD>528</TD><TD>-999</TD></TR>
-          <TR><TD>529</TD><TD>-999</TD></TR>
-          <TR><TD>530</TD><TD>-999</TD></TR>
-          <TR><TD>531</TD><TD>-999</TD></TR>
-          <TR><TD>532</TD><TD>-999</TD></TR>
-          <TR><TD>533</TD><TD>-999</TD></TR>
-          <TR><TD>534</TD><TD>-999</TD></TR>
-          <TR><TD>535</TD><TD>-999</TD></TR>
-          <TR><TD>536</TD><TD>-999</TD></TR>
-          <TR><TD>537</TD><TD>-999</TD></TR>
-          <TR><TD>538</TD><TD>-999</TD></TR>
-          <TR><TD>539</TD><TD>-999</TD></TR>
-          <TR><TD>540</TD><TD>-999</TD></TR>
-          <TR><TD>541</TD><TD>-999</TD></TR>
-          <TR><TD>542</TD><TD>-999</TD></TR>
-          <TR><TD>543</TD><TD>-999</TD></TR>
-          <TR><TD>544</TD><TD>-999</TD></TR>
-          <TR><TD>545</TD><TD>-999</TD></TR>
-          <TR><TD>546</TD><TD>-999</TD></TR>
-          <TR><TD>547</TD><TD>-999</TD></TR>
-          <TR><TD>548</TD><TD>-999</TD></TR>
-          <TR><TD>549</TD><TD>-999</TD></TR>
-          <TR><TD>550</TD><TD>-999</TD></TR>
-          <TR><TD>551</TD><TD>-999</TD></TR>
-          <TR><TD>552</TD><TD>-999</TD></TR>
-          <TR><TD>553</TD><TD>-999</TD></TR>
-          <TR><TD>554</TD><TD>-999</TD></TR>
-          <TR><TD>555</TD><TD>-999</TD></TR>
-          <TR><TD>556</TD><TD>-999</TD></TR>
-          <TR><TD>557</TD><TD>-999</TD></TR>
-          <TR><TD>558</TD><TD>-999</TD></TR>
-          <TR><TD>559</TD><TD>-999</TD></TR>
-          <TR><TD>560</TD><TD>-999</TD></TR>
-          <TR><TD>561</TD><TD>-999</TD></TR>
-          <TR><TD>562</TD><TD>-999</TD></TR>
-          <TR><TD>563</TD><TD>-999</TD></TR>
-          <TR><TD>564</TD><TD>-999</TD></TR>
-          <TR><TD>565</TD><TD>-999</TD></TR>
-          <TR><TD>566</TD><TD>-999</TD></TR>
-          <TR><TD>567</TD><TD>-999</TD></TR>
-          <TR><TD>568</TD><TD>-999</TD></TR>
-          <TR><TD>569</TD><TD>-999</TD></TR>
-          <TR><TD>570</TD><TD>-999</TD></TR>
-          <TR><TD>571</TD><TD>-999</TD></TR>
-          <TR><TD>572</TD><TD>-999</TD></TR>
-          <TR><TD>573</TD><TD>-999</TD></TR>
-          <TR><TD>574</TD><TD>-999</TD></TR>
-          <TR><TD>575</TD><TD>-999</TD></TR>
-          <TR><TD>576</TD><TD>-999</TD></TR>
-          <TR><TD>577</TD><TD>-999</TD></TR>
-          <TR><TD>578</TD><TD>-999</TD></TR>
-          <TR><TD>579</TD><TD>-999</TD></TR>
-          <TR><TD>580</TD><TD>-999</TD></TR>
-          <TR><TD>581</TD><TD>-999</TD></TR>
-          <TR><TD>582</TD><TD>-999</TD></TR>
-          <TR><TD>583</TD><TD>-999</TD></TR>
-          <TR><TD>584</TD><TD>-999</TD></TR>
-          <TR><TD>585</TD><TD>-999</TD></TR>
-          <TR><TD>586</TD><TD>-999</TD></TR>
-          <TR><TD>587</TD><TD>-999</TD></TR>
-          <TR><TD>588</TD><TD>-999</TD></TR>
-          <TR><TD>589</TD><TD>-999</TD></TR>
-          <TR><TD>590</TD><TD>-999</TD></TR>
-          <TR><TD>591</TD><TD>-999</TD></TR>
-          <TR><TD>592</TD><TD>-999</TD></TR>
-          <TR><TD>593</TD><TD>-999</TD></TR>
-          <TR><TD>594</TD><TD>-999</TD></TR>
-          <TR><TD>595</TD><TD>-999</TD></TR>
-          <TR><TD>596</TD><TD>-999</TD></TR>
-          <TR><TD>597</TD><TD>-999</TD></TR>
-          <TR><TD>598</TD><TD>-999</TD></TR>
-          <TR><TD>599</TD><TD>-999</TD></TR>
-          <TR><TD>600</TD><TD>-999</TD></TR>
-          <TR><TD>601</TD><TD>-999</TD></TR>
-          <TR><TD>602</TD><TD>-999</TD></TR>
-          <TR><TD>603</TD><TD>-999</TD></TR>
-          <TR><TD>604</TD><TD>-999</TD></TR>
-          <TR><TD>605</TD><TD>-999</TD></TR>
-          <TR><TD>606</TD><TD>-999</TD></TR>
-          <TR><TD>607</TD><TD>-999</TD></TR>
-          <TR><TD>608</TD><TD>-999</TD></TR>
-          <TR><TD>609</TD><TD>-999</TD></TR>
-          <TR><TD>610</TD><TD>-999</TD></TR>
-          <TR><TD>611</TD><TD>-999</TD></TR>
-          <TR><TD>612</TD><TD>-999</TD></TR>
-          <TR><TD>613</TD><TD>-999</TD></TR>
-          <TR><TD>614</TD><TD>-999</TD></TR>
-          <TR><TD>615</TD><TD>-999</TD></TR>
-          <TR><TD>616</TD><TD>-999</TD></TR>
-          <TR><TD>617</TD><TD>-999</TD></TR>
-          <TR><TD>618</TD><TD>-999</TD></TR>
-          <TR><TD>619</TD><TD>-999</TD></TR>
-          <TR><TD>620</TD><TD>-999</TD></TR>
-          <TR><TD>621</TD><TD>-999</TD></TR>
-          <TR><TD>622</TD><TD>-999</TD></TR>
-          <TR><TD>623</TD><TD>-999</TD></TR>
-          <TR><TD>624</TD><TD>-999</TD></TR>
-          <TR><TD>625</TD><TD>-999</TD></TR>
-          <TR><TD>626</TD><TD>-999</TD></TR>
-          <TR><TD>627</TD><TD>-999</TD></TR>
-          <TR><TD>628</TD><TD>-999</TD></TR>
-          <TR><TD>629</TD><TD>-999</TD></TR>
-          <TR><TD>630</TD><TD>-999</TD></TR>
-          <TR><TD>631</TD><TD>-999</TD></TR>
-          <TR><TD>632</TD><TD>-999</TD></TR>
-          <TR><TD>633</TD><TD>-999</TD></TR>
-          <TR><TD>634</TD><TD>-999</TD></TR>
-          <TR><TD>635</TD><TD>-999</TD></TR>
-          <TR><TD>636</TD><TD>-999</TD></TR>
-          <TR><TD>637</TD><TD>-999</TD></TR>
-          <TR><TD>638</TD><TD>-999</TD></TR>
-          <TR><TD>639</TD><TD>-999</TD></TR>
-          <TR><TD>640</TD><TD>-999</TD></TR>
-          <TR><TD>641</TD><TD>-999</TD></TR>
-          <TR><TD>642</TD><TD>-999</TD></TR>
-          <TR><TD>643</TD><TD>-999</TD></TR>
-          <TR><TD>644</TD><TD>-999</TD></TR>
-          <TR><TD>645</TD><TD>-999</TD></TR>
-          <TR><TD>646</TD><TD>-999</TD></TR>
-          <TR><TD>647</TD><TD>-999</TD></TR>
-          <TR><TD>648</TD><TD>-999</TD></TR>
-          <TR><TD>649</TD><TD>-999</TD></TR>
-          <TR><TD>650</TD><TD>-999</TD></TR>
-          <TR><TD>651</TD><TD>-999</TD></TR>
-          <TR><TD>652</TD><TD>-999</TD></TR>
-          <TR><TD>653</TD><TD>-999</TD></TR>
-          <TR><TD>654</TD><TD>-999</TD></TR>
-          <TR><TD>655</TD><TD>-999</TD></TR>
-          <TR><TD>656</TD><TD>-999</TD></TR>
-          <TR><TD>657</TD><TD>-999</TD></TR>
-          <TR><TD>658</TD><TD>-999</TD></TR>
-          <TR><TD>659</TD><TD>-999</TD></TR>
-          <TR><TD>660</TD><TD>-999</TD></TR>
-          <TR><TD>661</TD><TD>-999</TD></TR>
-          <TR><TD>662</TD><TD>-999</TD></TR>
-          <TR><TD>663</TD><TD>-999</TD></TR>
-          <TR><TD>664</TD><TD>-999</TD></TR>
-          <TR><TD>665</TD><TD>-999</TD></TR>
-          <TR><TD>666</TD><TD>-999</TD></TR>
-          <TR><TD>667</TD><TD>-999</TD></TR>
-          <TR><TD>668</TD><TD>-999</TD></TR>
-          <TR><TD>669</TD><TD>-999</TD></TR>
-          <TR><TD>670</TD><TD>-999</TD></TR>
-          <TR><TD>671</TD><TD>-999</TD></TR>
-          <TR><TD>672</TD><TD>-999</TD></TR>
-          <TR><TD>673</TD><TD>-999</TD></TR>
-          <TR><TD>674</TD><TD>-999</TD></TR>
-          <TR><TD>675</TD><TD>-999</TD></TR>
-          <TR><TD>676</TD><TD>-999</TD></TR>
-          <TR><TD>677</TD><TD>-999</TD></TR>
-          <TR><TD>678</TD><TD>-999</TD></TR>
-          <TR><TD>679</TD><TD>-999</TD></TR>
-          <TR><TD>680</TD><TD>-999</TD></TR>
-          <TR><TD>681</TD><TD>-999</TD></TR>
-          <TR><TD>682</TD><TD>-999</TD></TR>
-          <TR><TD>683</TD><TD>-999</TD></TR>
-          <TR><TD>684</TD><TD>-999</TD></TR>
-          <TR><TD>685</TD><TD>-999</TD></TR>
-          <TR><TD>686</TD><TD>-999</TD></TR>
-          <TR><TD>687</TD><TD>-999</TD></TR>
-          <TR><TD>688</TD><TD>-999</TD></TR>
-          <TR><TD>689</TD><TD>-999</TD></TR>
-          <TR><TD>690</TD><TD>-999</TD></TR>
-          <TR><TD>691</TD><TD>-999</TD></TR>
-          <TR><TD>692</TD><TD>-999</TD></TR>
-          <TR><TD>693</TD><TD>-999</TD></TR>
-          <TR><TD>694</TD><TD>-999</TD></TR>
-          <TR><TD>695</TD><TD>-999</TD></TR>
-          <TR><TD>696</TD><TD>-999</TD></TR>
-          <TR><TD>697</TD><TD>-999</TD></TR>
-          <TR><TD>698</TD><TD>-999</TD></TR>
-          <TR><TD>699</TD><TD>-999</TD></TR>
-          <TR><TD>700</TD><TD>-999</TD></TR>
-          <TR><TD>701</TD><TD>-999</TD></TR>
-          <TR><TD>702</TD><TD>-999</TD></TR>
-          <TR><TD>703</TD><TD>-999</TD></TR>
-          <TR><TD>704</TD><TD>-999</TD></TR>
-          <TR><TD>705</TD><TD>-999</TD></TR>
-          <TR><TD>706</TD><TD>-999</TD></TR>
-          <TR><TD>707</TD><TD>-999</TD></TR>
-          <TR><TD>708</TD><TD>-999</TD></TR>
-          <TR><TD>709</TD><TD>-999</TD></TR>
-          <TR><TD>710</TD><TD>-999</TD></TR>
-          <TR><TD>711</TD><TD>-999</TD></TR>
-          <TR><TD>712</TD><TD>-999</TD></TR>
-          <TR><TD>713</TD><TD>-999</TD></TR>
-          <TR><TD>714</TD><TD>-999</TD></TR>
-          <TR><TD>715</TD><TD>-999</TD></TR>
-          <TR><TD>716</TD><TD>-999</TD></TR>
-          <TR><TD>717</TD><TD>-999</TD></TR>
-          <TR><TD>718</TD><TD>-999</TD></TR>
-          <TR><TD>719</TD><TD>-999</TD></TR>
-          <TR><TD>720</TD><TD>-999</TD></TR>
-          <TR><TD>721</TD><TD>-999</TD></TR>
-          <TR><TD>722</TD><TD>-999</TD></TR>
-          <TR><TD>723</TD><TD>-999</TD></TR>
-          <TR><TD>724</TD><TD>-999</TD></TR>
-          <TR><TD>725</TD><TD>-999</TD></TR>
-          <TR><TD>726</TD><TD>-999</TD></TR>
-          <TR><TD>727</TD><TD>-999</TD></TR>
-          <TR><TD>728</TD><TD>-999</TD></TR>
-          <TR><TD>729</TD><TD>-999</TD></TR>
-          <TR><TD>730</TD><TD>-999</TD></TR>
-          <TR><TD>731</TD><TD>-999</TD></TR>
-          <TR><TD>732</TD><TD>-999</TD></TR>
-          <TR><TD>733</TD><TD>-999</TD></TR>
-          <TR><TD>734</TD><TD>-999</TD></TR>
-          <TR><TD>735</TD><TD>-999</TD></TR>
-          <TR><TD>736</TD><TD>-999</TD></TR>
-          <TR><TD>737</TD><TD>-999</TD></TR>
-          <TR><TD>738</TD><TD>-999</TD></TR>
-          <TR><TD>739</TD><TD>-999</TD></TR>
-          <TR><TD>740</TD><TD>-999</TD></TR>
-          <TR><TD>741</TD><TD>-999</TD></TR>
-          <TR><TD>742</TD><TD>-999</TD></TR>
-          <TR><TD>743</TD><TD>-999</TD></TR>
-          <TR><TD>744</TD><TD>-999</TD></TR>
-          <TR><TD>745</TD><TD>-999</TD></TR>
-          <TR><TD>746</TD><TD>-999</TD></TR>
-          <TR><TD>747</TD><TD>-999</TD></TR>
-          <TR><TD>748</TD><TD>-999</TD></TR>
-          <TR><TD>749</TD><TD>-999</TD></TR>
-          <TR><TD>750</TD><TD>-999</TD></TR>
-          <TR><TD>751</TD><TD>-999</TD></TR>
-          <TR><TD>752</TD><TD>-999</TD></TR>
-          <TR><TD>753</TD><TD>-999</TD></TR>
-          <TR><TD>754</TD><TD>-999</TD></TR>
-          <TR><TD>755</TD><TD>-999</TD></TR>
-          <TR><TD>756</TD><TD>-999</TD></TR>
-          <TR><TD>757</TD><TD>-999</TD></TR>
-          <TR><TD>758</TD><TD>-999</TD></TR>
-          <TR><TD>759</TD><TD>-999</TD></TR>
-          <TR><TD>760</TD><TD>-999</TD></TR>
-          <TR><TD>761</TD><TD>-999</TD></TR>
-          <TR><TD>762</TD><TD>-999</TD></TR>
-          <TR><TD>763</TD><TD>-999</TD></TR>
-          <TR><TD>764</TD><TD>-999</TD></TR>
-          <TR><TD>765</TD><TD>-999</TD></TR>
-          <TR><TD>766</TD><TD>-999</TD></TR>
-          <TR><TD>767</TD><TD>-999</TD></TR>
-          <TR><TD>768</TD><TD>-999</TD></TR>
-          <TR><TD>769</TD><TD>-999</TD></TR>
-          <TR><TD>770</TD><TD>-999</TD></TR>
-          <TR><TD>771</TD><TD>-999</TD></TR>
-          <TR><TD>772</TD><TD>-999</TD></TR>
-          <TR><TD>773</TD><TD>-999</TD></TR>
-          <TR><TD>774</TD><TD>-999</TD></TR>
-          <TR><TD>775</TD><TD>-999</TD></TR>
-          <TR><TD>776</TD><TD>-999</TD></TR>
-          <TR><TD>777</TD><TD>-999</TD></TR>
-          <TR><TD>778</TD><TD>-999</TD></TR>
-          <TR><TD>779</TD><TD>-999</TD></TR>
-          <TR><TD>780</TD><TD>-999</TD></TR>
-          <TR><TD>781</TD><TD>-999</TD></TR>
-          <TR><TD>782</TD><TD>-999</TD></TR>
-          <TR><TD>783</TD><TD>-999</TD></TR>
-          <TR><TD>784</TD><TD>-999</TD></TR>
-          <TR><TD>785</TD><TD>-999</TD></TR>
-          <TR><TD>786</TD><TD>-999</TD></TR>
-          <TR><TD>787</TD><TD>-999</TD></TR>
-          <TR><TD>788</TD><TD>-999</TD></TR>
-          <TR><TD>789</TD><TD>-999</TD></TR>
-          <TR><TD>790</TD><TD>-999</TD></TR>
-          <TR><TD>791</TD><TD>-999</TD></TR>
-          <TR><TD>792</TD><TD>-999</TD></TR>
-          <TR><TD>793</TD><TD>-999</TD></TR>
-          <TR><TD>794</TD><TD>-999</TD></TR>
-          <TR><TD>795</TD><TD>-999</TD></TR>
-          <TR><TD>796</TD><TD>-999</TD></TR>
-          <TR><TD>797</TD><TD>-999</TD></TR>
-          <TR><TD>798</TD><TD>-999</TD></TR>
-          <TR><TD>799</TD><TD>-999</TD></TR>
-          <TR><TD>800</TD><TD>-999</TD></TR>
-          <TR><TD>801</TD><TD>-999</TD></TR>
-          <TR><TD>802</TD><TD>-999</TD></TR>
-          <TR><TD>803</TD><TD>-999</TD></TR>
-          <TR><TD>804</TD><TD>-999</TD></TR>
-          <TR><TD>805</TD><TD>-999</TD></TR>
-          <TR><TD>806</TD><TD>-999</TD></TR>
-          <TR><TD>807</TD><TD>-999</TD></TR>
-          <TR><TD>808</TD><TD>-999</TD></TR>
-          <TR><TD>809</TD><TD>-999</TD></TR>
-          <TR><TD>810</TD><TD>-999</TD></TR>
-          <TR><TD>811</TD><TD>-999</TD></TR>
-          <TR><TD>812</TD><TD>-999</TD></TR>
-          <TR><TD>813</TD><TD>-999</TD></TR>
-          <TR><TD>814</TD><TD>-999</TD></TR>
-          <TR><TD>815</TD><TD>-999</TD></TR>
-          <TR><TD>816</TD><TD>-999</TD></TR>
-          <TR><TD>817</TD><TD>-999</TD></TR>
-          <TR><TD>818</TD><TD>-999</TD></TR>
-          <TR><TD>819</TD><TD>-999</TD></TR>
-          <TR><TD>820</TD><TD>-999</TD></TR>
-          <TR><TD>821</TD><TD>-999</TD></TR>
-          <TR><TD>822</TD><TD>-999</TD></TR>
-          <TR><TD>823</TD><TD>-999</TD></TR>
-          <TR><TD>824</TD><TD>-999</TD></TR>
-          <TR><TD>825</TD><TD>-999</TD></TR>
-          <TR><TD>826</TD><TD>-999</TD></TR>
-          <TR><TD>827</TD><TD>-999</TD></TR>
-          <TR><TD>828</TD><TD>-999</TD></TR>
-          <TR><TD>829</TD><TD>-999</TD></TR>
-          <TR><TD>830</TD><TD>-999</TD></TR>
-          <TR><TD>831</TD><TD>-999</TD></TR>
-          <TR><TD>832</TD><TD>-999</TD></TR>
-          <TR><TD>833</TD><TD>-999</TD></TR>
-          <TR><TD>834</TD><TD>-999</TD></TR>
-          <TR><TD>835</TD><TD>-999</TD></TR>
-          <TR><TD>836</TD><TD>-999</TD></TR>
-          <TR><TD>837</TD><TD>-999</TD></TR>
-          <TR><TD>838</TD><TD>-999</TD></TR>
-          <TR><TD>839</TD><TD>-999</TD></TR>
-          <TR><TD>840</TD><TD>-999</TD></TR>
-          <TR><TD>841</TD><TD>-999</TD></TR>
-          <TR><TD>842</TD><TD>-999</TD></TR>
-          <TR><TD>843</TD><TD>-999</TD></TR>
-          <TR><TD>844</TD><TD>-999</TD></TR>
-          <TR><TD>845</TD><TD>-999</TD></TR>
-          <TR><TD>846</TD><TD>-999</TD></TR>
-          <TR><TD>847</TD><TD>-999</TD></TR>
-          <TR><TD>848</TD><TD>-999</TD></TR>
-          <TR><TD>849</TD><TD>-999</TD></TR>
-          <TR><TD>850</TD><TD>-999</TD></TR>
-          <TR><TD>851</TD><TD>-999</TD></TR>
-          <TR><TD>852</TD><TD>-999</TD></TR>
-          <TR><TD>853</TD><TD>-999</TD></TR>
-          <TR><TD>854</TD><TD>-999</TD></TR>
-          <TR><TD>855</TD><TD>-999</TD></TR>
-          <TR><TD>856</TD><TD>-999</TD></TR>
-          <TR><TD>857</TD><TD>-999</TD></TR>
-          <TR><TD>858</TD><TD>-999</TD></TR>
-          <TR><TD>859</TD><TD>-999</TD></TR>
-          <TR><TD>860</TD><TD>-999</TD></TR>
-          <TR><TD>861</TD><TD>-999</TD></TR>
-          <TR><TD>862</TD><TD>-999</TD></TR>
-          <TR><TD>863</TD><TD>-999</TD></TR>
-          <TR><TD>864</TD><TD>-999</TD></TR>
-          <TR><TD>865</TD><TD>-999</TD></TR>
-          <TR><TD>866</TD><TD>-999</TD></TR>
-          <TR><TD>867</TD><TD>-999</TD></TR>
-          <TR><TD>868</TD><TD>-999</TD></TR>
-          <TR><TD>869</TD><TD>-999</TD></TR>
-          <TR><TD>870</TD><TD>-999</TD></TR>
-          <TR><TD>871</TD><TD>-999</TD></TR>
-          <TR><TD>872</TD><TD>-999</TD></TR>
-          <TR><TD>873</TD><TD>-999</TD></TR>
-          <TR><TD>874</TD><TD>-999</TD></TR>
-          <TR><TD>875</TD><TD>-999</TD></TR>
-          <TR><TD>876</TD><TD>-999</TD></TR>
-          <TR><TD>877</TD><TD>-999</TD></TR>
-          <TR><TD>878</TD><TD>-999</TD></TR>
-          <TR><TD>879</TD><TD>-999</TD></TR>
-          <TR><TD>880</TD><TD>-999</TD></TR>
-          <TR><TD>881</TD><TD>-999</TD></TR>
-          <TR><TD>882</TD><TD>-999</TD></TR>
-          <TR><TD>883</TD><TD>-999</TD></TR>
-          <TR><TD>884</TD><TD>-999</TD></TR>
-          <TR><TD>885</TD><TD>-999</TD></TR>
-          <TR><TD>886</TD><TD>-999</TD></TR>
-          <TR><TD>887</TD><TD>-999</TD></TR>
-          <TR><TD>888</TD><TD>-999</TD></TR>
-          <TR><TD>889</TD><TD>-999</TD></TR>
-          <TR><TD>890</TD><TD>-999</TD></TR>
-          <TR><TD>891</TD><TD>-999</TD></TR>
-          <TR><TD>892</TD><TD>-999</TD></TR>
-          <TR><TD>893</TD><TD>-999</TD></TR>
-          <TR><TD>894</TD><TD>-999</TD></TR>
-          <TR><TD>895</TD><TD>-999</TD></TR>
-          <TR><TD>896</TD><TD>-999</TD></TR>
-          <TR><TD>897</TD><TD>-999</TD></TR>
-          <TR><TD>898</TD><TD>-999</TD></TR>
-          <TR><TD>899</TD><TD>-999</TD></TR>
-          <TR><TD>900</TD><TD>-999</TD></TR>
-          <TR><TD>901</TD><TD>-999</TD></TR>
-          <TR><TD>902</TD><TD>-999</TD></TR>
-          <TR><TD>903</TD><TD>-999</TD></TR>
-          <TR><TD>904</TD><TD>-999</TD></TR>
-          <TR><TD>905</TD><TD>-999</TD></TR>
-          <TR><TD>906</TD><TD>-999</TD></TR>
-          <TR><TD>907</TD><TD>-999</TD></TR>
-          <TR><TD>908</TD><TD>-999</TD></TR>
-          <TR><TD>909</TD><TD>-999</TD></TR>
-          <TR><TD>910</TD><TD>-999</TD></TR>
-          <TR><TD>911</TD><TD>-999</TD></TR>
-          <TR><TD>912</TD><TD>-999</TD></TR>
-          <TR><TD>913</TD><TD>-999</TD></TR>
-          <TR><TD>914</TD><TD>-999</TD></TR>
-          <TR><TD>915</TD><TD>-999</TD></TR>
-          <TR><TD>916</TD><TD>-999</TD></TR>
-          <TR><TD>917</TD><TD>-999</TD></TR>
-          <TR><TD>918</TD><TD>-999</TD></TR>
-          <TR><TD>919</TD><TD>-999</TD></TR>
-          <TR><TD>920</TD><TD>-999</TD></TR>
-          <TR><TD>921</TD><TD>-999</TD></TR>
-          <TR><TD>922</TD><TD>-999</TD></TR>
-          <TR><TD>923</TD><TD>-999</TD></TR>
-          <TR><TD>924</TD><TD>-999</TD></TR>
-          <TR><TD>925</TD><TD>-999</TD></TR>
-          <TR><TD>926</TD><TD>-999</TD></TR>
-          <TR><TD>927</TD><TD>-999</TD></TR>
-          <TR><TD>928</TD><TD>-999</TD></TR>
-          <TR><TD>929</TD><TD>-999</TD></TR>
-          <TR><TD>930</TD><TD>-999</TD></TR>
-          <TR><TD>931</TD><TD>-999</TD></TR>
-          <TR><TD>932</TD><TD>-999</TD></TR>
-          <TR><TD>933</TD><TD>-999</TD></TR>
-          <TR><TD>934</TD><TD>-999</TD></TR>
-          <TR><TD>935</TD><TD>-999</TD></TR>
-          <TR><TD>936</TD><TD>-999</TD></TR>
-          <TR><TD>937</TD><TD>-999</TD></TR>
-          <TR><TD>938</TD><TD>-999</TD></TR>
-          <TR><TD>939</TD><TD>-999</TD></TR>
-          <TR><TD>940</TD><TD>-999</TD></TR>
-          <TR><TD>941</TD><TD>-999</TD></TR>
-          <TR><TD>942</TD><TD>-999</TD></TR>
-          <TR><TD>943</TD><TD>-999</TD></TR>
-          <TR><TD>944</TD><TD>-999</TD></TR>
-          <TR><TD>945</TD><TD>-999</TD></TR>
-          <TR><TD>946</TD><TD>-999</TD></TR>
-          <TR><TD>947</TD><TD>-999</TD></TR>
-          <TR><TD>948</TD><TD>-999</TD></TR>
-          <TR><TD>949</TD><TD>-999</TD></TR>
-          <TR><TD>950</TD><TD>-999</TD></TR>
-          <TR><TD>951</TD><TD>-999</TD></TR>
-          <TR><TD>952</TD><TD>-999</TD></TR>
-          <TR><TD>953</TD><TD>-999</TD></TR>
-          <TR><TD>954</TD><TD>-999</TD></TR>
-          <TR><TD>955</TD><TD>-999</TD></TR>
-          <TR><TD>956</TD><TD>-999</TD></TR>
-          <TR><TD>957</TD><TD>-999</TD></TR>
-          <TR><TD>958</TD><TD>-999</TD></TR>
-          <TR><TD>959</TD><TD>-999</TD></TR>
-          <TR><TD>960</TD><TD>-999</TD></TR>
-          <TR><TD>961</TD><TD>-999</TD></TR>
-          <TR><TD>962</TD><TD>-999</TD></TR>
-          <TR><TD>963</TD><TD>-999</TD></TR>
-          <TR><TD>964</TD><TD>-999</TD></TR>
-          <TR><TD>965</TD><TD>-999</TD></TR>
-          <TR><TD>966</TD><TD>-999</TD></TR>
-          <TR><TD>967</TD><TD>-999</TD></TR>
-          <TR><TD>968</TD><TD>-999</TD></TR>
-          <TR><TD>969</TD><TD>-999</TD></TR>
-          <TR><TD>970</TD><TD>-999</TD></TR>
-          <TR><TD>971</TD><TD>-999</TD></TR>
-          <TR><TD>972</TD><TD>-999</TD></TR>
-          <TR><TD>973</TD><TD>-999</TD></TR>
-          <TR><TD>974</TD><TD>-999</TD></TR>
-          <TR><TD>975</TD><TD>-999</TD></TR>
-          <TR><TD>976</TD><TD>-999</TD></TR>
-          <TR><TD>977</TD><TD>-999</TD></TR>
-          <TR><TD>978</TD><TD>-999</TD></TR>
-          <TR><TD>979</TD><TD>-999</TD></TR>
-          <TR><TD>980</TD><TD>-999</TD></TR>
-          <TR><TD>981</TD><TD>-999</TD></TR>
-          <TR><TD>982</TD><TD>-999</TD></TR>
-          <TR><TD>983</TD><TD>-999</TD></TR>
-          <TR><TD>984</TD><TD>-999</TD></TR>
-          <TR><TD>985</TD><TD>-999</TD></TR>
-          <TR><TD>986</TD><TD>-999</TD></TR>
-          <TR><TD>987</TD><TD>-999</TD></TR>
-          <TR><TD>988</TD><TD>-999</TD></TR>
-          <TR><TD>989</TD><TD>-999</TD></TR>
-          <TR><TD>990</TD><TD>-999</TD></TR>
-          <TR><TD>991</TD><TD>-999</TD></TR>
-          <TR><TD>992</TD><TD>-999</TD></TR>
-          <TR><TD>993</TD><TD>-999</TD></TR>
-          <TR><TD>994</TD><TD>-999</TD></TR>
-          <TR><TD>995</TD><TD>-999</TD></TR>
-          <TR><TD>996</TD><TD>-999</TD></TR>
-          <TR><TD>997</TD><TD>-999</TD></TR>
-          <TR><TD>998</TD><TD>-999</TD></TR>
-          <TR><TD>999</TD><TD>-999</TD></TR>
-          <TR><TD>1000</TD><TD>-999</TD></TR>
-          <TR><TD>1001</TD><TD>-999</TD></TR>
-          <TR><TD>1002</TD><TD>-999</TD></TR>
-          <TR><TD>1003</TD><TD>-999</TD></TR>
-          <TR><TD>1004</TD><TD>-999</TD></TR>
-          <TR><TD>1005</TD><TD>-999</TD></TR>
-          <TR><TD>1006</TD><TD>-999</TD></TR>
-          <TR><TD>1007</TD><TD>-999</TD></TR>
-          <TR><TD>1008</TD><TD>-999</TD></TR>
-          <TR><TD>1009</TD><TD>-999</TD></TR>
-          <TR><TD>1010</TD><TD>-999</TD></TR>
-          <TR><TD>1011</TD><TD>-999</TD></TR>
-          <TR><TD>1012</TD><TD>-999</TD></TR>
-          <TR><TD>1013</TD><TD>-999</TD></TR>
-          <TR><TD>1014</TD><TD>-999</TD></TR>
-          <TR><TD>1015</TD><TD>-999</TD></TR>
-          <TR><TD>1016</TD><TD>-999</TD></TR>
-          <TR><TD>1017</TD><TD>-999</TD></TR>
-          <TR><TD>1018</TD><TD>-999</TD></TR>
-          <TR><TD>1019</TD><TD>-999</TD></TR>
-          <TR><TD>1020</TD><TD>-999</TD></TR>
-          <TR><TD>1021</TD><TD>-999</TD></TR>
-          <TR><TD>1022</TD><TD>-999</TD></TR>
-          <TR><TD>1023</TD><TD>-999</TD></TR>
-          <TR><TD>1024</TD><TD>-999</TD></TR>
-          <TR><TD>1025</TD><TD>-999</TD></TR>
-          <TR><TD>1026</TD><TD>-999</TD></TR>
-          <TR><TD>1027</TD><TD>-999</TD></TR>
-          <TR><TD>1028</TD><TD>-999</TD></TR>
-          <TR><TD>1029</TD><TD>-999</TD></TR>
-          <TR><TD>1030</TD><TD>-999</TD></TR>
-          <TR><TD>1031</TD><TD>-999</TD></TR>
-          <TR><TD>1032</TD><TD>-999</TD></TR>
-          <TR><TD>1033</TD><TD>-999</TD></TR>
-          <TR><TD>1034</TD><TD>-999</TD></TR>
-          <TR><TD>1035</TD><TD>-999</TD></TR>
-          <TR><TD>1036</TD><TD>-999</TD></TR>
-          <TR><TD>1037</TD><TD>-999</TD></TR>
-          <TR><TD>1038</TD><TD>-999</TD></TR>
-          <TR><TD>1039</TD><TD>-999</TD></TR>
-          <TR><TD>1040</TD><TD>-999</TD></TR>
-          <TR><TD>1041</TD><TD>-999</TD></TR>
-          <TR><TD>1042</TD><TD>-999</TD></TR>
-          <TR><TD>1043</TD><TD>-999</TD></TR>
-          <TR><TD>1044</TD><TD>-999</TD></TR>
-          <TR><TD>1045</TD><TD>-999</TD></TR>
-          <TR><TD>1046</TD><TD>-999</TD></TR>
-          <TR><TD>1047</TD><TD>-999</TD></TR>
-          <TR><TD>1048</TD><TD>-999</TD></TR>
-          <TR><TD>1049</TD><TD>-999</TD></TR>
-          <TR><TD>1050</TD><TD>-999</TD></TR>
-          <TR><TD>1051</TD><TD>-999</TD></TR>
-          <TR><TD>1052</TD><TD>-999</TD></TR>
-          <TR><TD>1053</TD><TD>-999</TD></TR>
-          <TR><TD>1054</TD><TD>-999</TD></TR>
-          <TR><TD>1055</TD><TD>-999</TD></TR>
-          <TR><TD>1056</TD><TD>-999</TD></TR>
-          <TR><TD>1057</TD><TD>-999</TD></TR>
-          <TR><TD>1058</TD><TD>-999</TD></TR>
-          <TR><TD>1059</TD><TD>-999</TD></TR>
-          <TR><TD>1060</TD><TD>-999</TD></TR>
-          <TR><TD>1061</TD><TD>-999</TD></TR>
-          <TR><TD>1062</TD><TD>-999</TD></TR>
-          <TR><TD>1063</TD><TD>-999</TD></TR>
-          <TR><TD>1064</TD><TD>-999</TD></TR>
-          <TR><TD>1065</TD><TD>-999</TD></TR>
-          <TR><TD>1066</TD><TD>-999</TD></TR>
-          <TR><TD>1067</TD><TD>-999</TD></TR>
-          <TR><TD>1068</TD><TD>-999</TD></TR>
-          <TR><TD>1069</TD><TD>-999</TD></TR>
-          <TR><TD>1070</TD><TD>-999</TD></TR>
-          <TR><TD>1071</TD><TD>-999</TD></TR>
-          <TR><TD>1072</TD><TD>-999</TD></TR>
-          <TR><TD>1073</TD><TD>-999</TD></TR>
-          <TR><TD>1074</TD><TD>-999</TD></TR>
-          <TR><TD>1075</TD><TD>-999</TD></TR>
-          <TR><TD>1076</TD><TD>-999</TD></TR>
-          <TR><TD>1077</TD><TD>-999</TD></TR>
-          <TR><TD>1078</TD><TD>-999</TD></TR>
-          <TR><TD>1079</TD><TD>-999</TD></TR>
-          <TR><TD>1080</TD><TD>-999</TD></TR>
-          <TR><TD>1081</TD><TD>-999</TD></TR>
-          <TR><TD>1082</TD><TD>-999</TD></TR>
-          <TR><TD>1083</TD><TD>-999</TD></TR>
-          <TR><TD>1084</TD><TD>-999</TD></TR>
-          <TR><TD>1085</TD><TD>-999</TD></TR>
-          <TR><TD>1086</TD><TD>-999</TD></TR>
-          <TR><TD>1087</TD><TD>-999</TD></TR>
-          <TR><TD>1088</TD><TD>-999</TD></TR>
-          <TR><TD>1089</TD><TD>-999</TD></TR>
-          <TR><TD>1090</TD><TD>-999</TD></TR>
-          <TR><TD>1091</TD><TD>-999</TD></TR>
-          <TR><TD>1092</TD><TD>-999</TD></TR>
-          <TR><TD>1093</TD><TD>-999</TD></TR>
-          <TR><TD>1094</TD><TD>-999</TD></TR>
-          <TR><TD>1095</TD><TD>-999</TD></TR>
-          <TR><TD>1096</TD><TD>-999</TD></TR>
-          <TR><TD>1097</TD><TD>-999</TD></TR>
-          <TR><TD>1098</TD><TD>-999</TD></TR>
-          <TR><TD>1099</TD><TD>-999</TD></TR>
-          <TR><TD>1100</TD><TD>-999</TD></TR>
-          <TR><TD>1101</TD><TD>-999</TD></TR>
-          <TR><TD>1102</TD><TD>-999</TD></TR>
-          <TR><TD>1103</TD><TD>-999</TD></TR>
-          <TR><TD>1104</TD><TD>-999</TD></TR>
-          <TR><TD>1105</TD><TD>-999</TD></TR>
-          <TR><TD>1106</TD><TD>-999</TD></TR>
-          <TR><TD>1107</TD><TD>-999</TD></TR>
-          <TR><TD>1108</TD><TD>-999</TD></TR>
-          <TR><TD>1109</TD><TD>-999</TD></TR>
-          <TR><TD>1110</TD><TD>-999</TD></TR>
-          <TR><TD>1111</TD><TD>-999</TD></TR>
-          <TR><TD>1112</TD><TD>-999</TD></TR>
-          <TR><TD>1113</TD><TD>-999</TD></TR>
-          <TR><TD>1114</TD><TD>-999</TD></TR>
-          <TR><TD>1115</TD><TD>-999</TD></TR>
-          <TR><TD>1116</TD><TD>-999</TD></TR>
-          <TR><TD>1117</TD><TD>-999</TD></TR>
-          <TR><TD>1118</TD><TD>-999</TD></TR>
-          <TR><TD>1119</TD><TD>-999</TD></TR>
-          <TR><TD>1120</TD><TD>-999</TD></TR>
-          <TR><TD>1121</TD><TD>-999</TD></TR>
-          <TR><TD>1122</TD><TD>-999</TD></TR>
-          <TR><TD>1123</TD><TD>-999</TD></TR>
-          <TR><TD>1124</TD><TD>-999</TD></TR>
-          <TR><TD>1125</TD><TD>-999</TD></TR>
-          <TR><TD>1126</TD><TD>-999</TD></TR>
-          <TR><TD>1127</TD><TD>-999</TD></TR>
-          <TR><TD>1128</TD><TD>-999</TD></TR>
-          <TR><TD>1129</TD><TD>-999</TD></TR>
-          <TR><TD>1130</TD><TD>-999</TD></TR>
-          <TR><TD>1131</TD><TD>-999</TD></TR>
-          <TR><TD>1132</TD><TD>-999</TD></TR>
-          <TR><TD>1133</TD><TD>-999</TD></TR>
-          <TR><TD>1134</TD><TD>-999</TD></TR>
-          <TR><TD>1135</TD><TD>-999</TD></TR>
-          <TR><TD>1136</TD><TD>-999</TD></TR>
-          <TR><TD>1137</TD><TD>-999</TD></TR>
-          <TR><TD>1138</TD><TD>-999</TD></TR>
-          <TR><TD>1139</TD><TD>-999</TD></TR>
-          <TR><TD>1140</TD><TD>-999</TD></TR>
-          <TR><TD>1141</TD><TD>-999</TD></TR>
-          <TR><TD>1142</TD><TD>-999</TD></TR>
-          <TR><TD>1143</TD><TD>-999</TD></TR>
-          <TR><TD>1144</TD><TD>-999</TD></TR>
-          <TR><TD>1145</TD><TD>-999</TD></TR>
-          <TR><TD>1146</TD><TD>-999</TD></TR>
-          <TR><TD>1147</TD><TD>-999</TD></TR>
-          <TR><TD>1148</TD><TD>-999</TD></TR>
-          <TR><TD>1149</TD><TD>-999</TD></TR>
-          <TR><TD>1150</TD><TD>-999</TD></TR>
-          <TR><TD>1151</TD><TD>-999</TD></TR>
-          <TR><TD>1152</TD><TD>-999</TD></TR>
-          <TR><TD>1153</TD><TD>-999</TD></TR>
-          <TR><TD>1154</TD><TD>-999</TD></TR>
-          <TR><TD>1155</TD><TD>-999</TD></TR>
-          <TR><TD>1156</TD><TD>-999</TD></TR>
-          <TR><TD>1157</TD><TD>-999</TD></TR>
-          <TR><TD>1158</TD><TD>-999</TD></TR>
-          <TR><TD>1159</TD><TD>-999</TD></TR>
-          <TR><TD>1160</TD><TD>-999</TD></TR>
-          <TR><TD>1161</TD><TD>-999</TD></TR>
-          <TR><TD>1162</TD><TD>-999</TD></TR>
-          <TR><TD>1163</TD><TD>-999</TD></TR>
-          <TR><TD>1164</TD><TD>-999</TD></TR>
-          <TR><TD>1165</TD><TD>-999</TD></TR>
-          <TR><TD>1166</TD><TD>-999</TD></TR>
-          <TR><TD>1167</TD><TD>-999</TD></TR>
-          <TR><TD>1168</TD><TD>-999</TD></TR>
-          <TR><TD>1169</TD><TD>-999</TD></TR>
-          <TR><TD>1170</TD><TD>-999</TD></TR>
-          <TR><TD>1171</TD><TD>-999</TD></TR>
-          <TR><TD>1172</TD><TD>-999</TD></TR>
-          <TR><TD>1173</TD><TD>-999</TD></TR>
-          <TR><TD>1174</TD><TD>-999</TD></TR>
-          <TR><TD>1175</TD><TD>-999</TD></TR>
-          <TR><TD>1176</TD><TD>-999</TD></TR>
-          <TR><TD>1177</TD><TD>-999</TD></TR>
-          <TR><TD>1178</TD><TD>-999</TD></TR>
-          <TR><TD>1179</TD><TD>-999</TD></TR>
-          <TR><TD>1180</TD><TD>-999</TD></TR>
-          <TR><TD>1181</TD><TD>-999</TD></TR>
-          <TR><TD>1182</TD><TD>-999</TD></TR>
-          <TR><TD>1183</TD><TD>-999</TD></TR>
-          <TR><TD>1184</TD><TD>-999</TD></TR>
-          <TR><TD>1185</TD><TD>-999</TD></TR>
-          <TR><TD>1186</TD><TD>-999</TD></TR>
-          <TR><TD>1187</TD><TD>-999</TD></TR>
-          <TR><TD>1188</TD><TD>-999</TD></TR>
-          <TR><TD>1189</TD><TD>-999</TD></TR>
-          <TR><TD>1190</TD><TD>-999</TD></TR>
-          <TR><TD>1191</TD><TD>-999</TD></TR>
-          <TR><TD>1192</TD><TD>-999</TD></TR>
-          <TR><TD>1193</TD><TD>-999</TD></TR>
-          <TR><TD>1194</TD><TD>-999</TD></TR>
-          <TR><TD>1195</TD><TD>-999</TD></TR>
-          <TR><TD>1196</TD><TD>-999</TD></TR>
-          <TR><TD>1197</TD><TD>-999</TD></TR>
-          <TR><TD>1198</TD><TD>-999</TD></TR>
-          <TR><TD>1199</TD><TD>-999</TD></TR>
-          <TR><TD>1200</TD><TD>-999</TD></TR>
-          <TR><TD>1201</TD><TD>-999</TD></TR>
-          <TR><TD>1202</TD><TD>-999</TD></TR>
-          <TR><TD>1203</TD><TD>-999</TD></TR>
-          <TR><TD>1204</TD><TD>-999</TD></TR>
-          <TR><TD>1205</TD><TD>-999</TD></TR>
-          <TR><TD>1206</TD><TD>-999</TD></TR>
-          <TR><TD>1207</TD><TD>-999</TD></TR>
-          <TR><TD>1208</TD><TD>-999</TD></TR>
-          <TR><TD>1209</TD><TD>-999</TD></TR>
-          <TR><TD>1210</TD><TD>-999</TD></TR>
-          <TR><TD>1211</TD><TD>-999</TD></TR>
-          <TR><TD>1212</TD><TD>-999</TD></TR>
-          <TR><TD>1213</TD><TD>-999</TD></TR>
-          <TR><TD>1214</TD><TD>-999</TD></TR>
-          <TR><TD>1215</TD><TD>-999</TD></TR>
-          <TR><TD>1216</TD><TD>-999</TD></TR>
-          <TR><TD>1217</TD><TD>-999</TD></TR>
-          <TR><TD>1218</TD><TD>-999</TD></TR>
-          <TR><TD>1219</TD><TD>-999</TD></TR>
-          <TR><TD>1220</TD><TD>-999</TD></TR>
-          <TR><TD>1221</TD><TD>-999</TD></TR>
-          <TR><TD>1222</TD><TD>-999</TD></TR>
-          <TR><TD>1223</TD><TD>-999</TD></TR>
-          <TR><TD>1224</TD><TD>-999</TD></TR>
-          <TR><TD>1225</TD><TD>-999</TD></TR>
-          <TR><TD>1226</TD><TD>-999</TD></TR>
-          <TR><TD>1227</TD><TD>-999</TD></TR>
-          <TR><TD>1228</TD><TD>-999</TD></TR>
-          <TR><TD>1229</TD><TD>-999</TD></TR>
-          <TR><TD>1230</TD><TD>-999</TD></TR>
-          <TR><TD>1231</TD><TD>-999</TD></TR>
-          <TR><TD>1232</TD><TD>-999</TD></TR>
-          <TR><TD>1233</TD><TD>-999</TD></TR>
-          <TR><TD>1234</TD><TD>-999</TD></TR>
-          <TR><TD>1235</TD><TD>-999</TD></TR>
-          <TR><TD>1236</TD><TD>-999</TD></TR>
-          <TR><TD>1237</TD><TD>-999</TD></TR>
-          <TR><TD>1238</TD><TD>-999</TD></TR>
-          <TR><TD>1239</TD><TD>-999</TD></TR>
-          <TR><TD>1240</TD><TD>-999</TD></TR>
-          <TR><TD>1241</TD><TD>-999</TD></TR>
-          <TR><TD>1242</TD><TD>-999</TD></TR>
-          <TR><TD>1243</TD><TD>-999</TD></TR>
-          <TR><TD>1244</TD><TD>-999</TD></TR>
-          <TR><TD>1245</TD><TD>-999</TD></TR>
-          <TR><TD>1246</TD><TD>-999</TD></TR>
-          <TR><TD>1247</TD><TD>-999</TD></TR>
-          <TR><TD>1248</TD><TD>-999</TD></TR>
-          <TR><TD>1249</TD><TD>-999</TD></TR>
-          <TR><TD>1250</TD><TD>-999</TD></TR>
-          <TR><TD>1251</TD><TD>-999</TD></TR>
-          <TR><TD>1252</TD><TD>-999</TD></TR>
-          <TR><TD>1253</TD><TD>-999</TD></TR>
-          <TR><TD>1254</TD><TD>-999</TD></TR>
-          <TR><TD>1255</TD><TD>-999</TD></TR>
-          <TR><TD>1256</TD><TD>-999</TD></TR>
-          <TR><TD>1257</TD><TD>-999</TD></TR>
-          <TR><TD>1258</TD><TD>-999</TD></TR>
-          <TR><TD>1259</TD><TD>-999</TD></TR>
-          <TR><TD>1260</TD><TD>-999</TD></TR>
-          <TR><TD>1261</TD><TD>-999</TD></TR>
-          <TR><TD>1262</TD><TD>-999</TD></TR>
-          <TR><TD>1263</TD><TD>-999</TD></TR>
-          <TR><TD>1264</TD><TD>-999</TD></TR>
-          <TR><TD>1265</TD><TD>-999</TD></TR>
-          <TR><TD>1266</TD><TD>-999</TD></TR>
-          <TR><TD>1267</TD><TD>-999</TD></TR>
-          <TR><TD>1268</TD><TD>-999</TD></TR>
-          <TR><TD>1269</TD><TD>-999</TD></TR>
-          <TR><TD>1270</TD><TD>-999</TD></TR>
-          <TR><TD>1271</TD><TD>-999</TD></TR>
-          <TR><TD>1272</TD><TD>-999</TD></TR>
-          <TR><TD>1273</TD><TD>-999</TD></TR>
-          <TR><TD>1274</TD><TD>-999</TD></TR>
-          <TR><TD>1275</TD><TD>-999</TD></TR>
-          <TR><TD>1276</TD><TD>-999</TD></TR>
-          <TR><TD>1277</TD><TD>-999</TD></TR>
-          <TR><TD>1278</TD><TD>-999</TD></TR>
-          <TR><TD>1279</TD><TD>-999</TD></TR>
-          <TR><TD>1280</TD><TD>-999</TD></TR>
-          <TR><TD>1281</TD><TD>-999</TD></TR>
-          <TR><TD>1282</TD><TD>-999</TD></TR>
-          <TR><TD>1283</TD><TD>-999</TD></TR>
-          <TR><TD>1284</TD><TD>-999</TD></TR>
-          <TR><TD>1285</TD><TD>-999</TD></TR>
-          <TR><TD>1286</TD><TD>-999</TD></TR>
-          <TR><TD>1287</TD><TD>-999</TD></TR>
-          <TR><TD>1288</TD><TD>-999</TD></TR>
-          <TR><TD>1289</TD><TD>-999</TD></TR>
-          <TR><TD>1290</TD><TD>-999</TD></TR>
-          <TR><TD>1291</TD><TD>-999</TD></TR>
-          <TR><TD>1292</TD><TD>-999</TD></TR>
-          <TR><TD>1293</TD><TD>-999</TD></TR>
-          <TR><TD>1294</TD><TD>-999</TD></TR>
-          <TR><TD>1295</TD><TD>-999</TD></TR>
-          <TR><TD>1296</TD><TD>-999</TD></TR>
-          <TR><TD>1297</TD><TD>-999</TD></TR>
-          <TR><TD>1298</TD><TD>-999</TD></TR>
-          <TR><TD>1299</TD><TD>-999</TD></TR>
-          <TR><TD>1300</TD><TD>-999</TD></TR>
-          <TR><TD>1301</TD><TD>-999</TD></TR>
-          <TR><TD>1302</TD><TD>-999</TD></TR>
-          <TR><TD>1303</TD><TD>-999</TD></TR>
-          <TR><TD>1304</TD><TD>-999</TD></TR>
-          <TR><TD>1305</TD><TD>-999</TD></TR>
-          <TR><TD>1306</TD><TD>-999</TD></TR>
-          <TR><TD>1307</TD><TD>-999</TD></TR>
-          <TR><TD>1308</TD><TD>-999</TD></TR>
-          <TR><TD>1309</TD><TD>-999</TD></TR>
-          <TR><TD>1310</TD><TD>-999</TD></TR>
-          <TR><TD>1311</TD><TD>-999</TD></TR>
-          <TR><TD>1312</TD><TD>-999</TD></TR>
-          <TR><TD>1313</TD><TD>-999</TD></TR>
-          <TR><TD>1314</TD><TD>-999</TD></TR>
-          <TR><TD>1315</TD><TD>-999</TD></TR>
-          <TR><TD>1316</TD><TD>-999</TD></TR>
-          <TR><TD>1317</TD><TD>-999</TD></TR>
-          <TR><TD>1318</TD><TD>-999</TD></TR>
-          <TR><TD>1319</TD><TD>-999</TD></TR>
-          <TR><TD>1320</TD><TD>-999</TD></TR>
-          <TR><TD>1321</TD><TD>-999</TD></TR>
-          <TR><TD>1322</TD><TD>-999</TD></TR>
-          <TR><TD>1323</TD><TD>-999</TD></TR>
-          <TR><TD>1324</TD><TD>-999</TD></TR>
-          <TR><TD>1325</TD><TD>-999</TD></TR>
-          <TR><TD>1326</TD><TD>-999</TD></TR>
-          <TR><TD>1327</TD><TD>-999</TD></TR>
-          <TR><TD>1328</TD><TD>-999</TD></TR>
-          <TR><TD>1329</TD><TD>-999</TD></TR>
-          <TR><TD>1330</TD><TD>-999</TD></TR>
-          <TR><TD>1331</TD><TD>-999</TD></TR>
-          <TR><TD>1332</TD><TD>-999</TD></TR>
-          <TR><TD>1333</TD><TD>-999</TD></TR>
-          <TR><TD>1334</TD><TD>-999</TD></TR>
-          <TR><TD>1335</TD><TD>-999</TD></TR>
-          <TR><TD>1336</TD><TD>-999</TD></TR>
-          <TR><TD>1337</TD><TD>-999</TD></TR>
-          <TR><TD>1338</TD><TD>-999</TD></TR>
-          <TR><TD>1339</TD><TD>-999</TD></TR>
-          <TR><TD>1340</TD><TD>-999</TD></TR>
-          <TR><TD>1341</TD><TD>-999</TD></TR>
-          <TR><TD>1342</TD><TD>-999</TD></TR>
-          <TR><TD>1343</TD><TD>-999</TD></TR>
-          <TR><TD>1344</TD><TD>-999</TD></TR>
-          <TR><TD>1345</TD><TD>-999</TD></TR>
-          <TR><TD>1346</TD><TD>-999</TD></TR>
-          <TR><TD>1347</TD><TD>-999</TD></TR>
-          <TR><TD>1348</TD><TD>-999</TD></TR>
-          <TR><TD>1349</TD><TD>-999</TD></TR>
-          <TR><TD>1350</TD><TD>-999</TD></TR>
-          <TR><TD>1351</TD><TD>-999</TD></TR>
-          <TR><TD>1352</TD><TD>-999</TD></TR>
-          <TR><TD>1353</TD><TD>-999</TD></TR>
-          <TR><TD>1354</TD><TD>-999</TD></TR>
-          <TR><TD>1355</TD><TD>-999</TD></TR>
-          <TR><TD>1356</TD><TD>-999</TD></TR>
-          <TR><TD>1357</TD><TD>-999</TD></TR>
-          <TR><TD>1358</TD><TD>-999</TD></TR>
-          <TR><TD>1359</TD><TD>-999</TD></TR>
-          <TR><TD>1360</TD><TD>-999</TD></TR>
-          <TR><TD>1361</TD><TD>-999</TD></TR>
-          <TR><TD>1362</TD><TD>-999</TD></TR>
-          <TR><TD>1363</TD><TD>-999</TD></TR>
-          <TR><TD>1364</TD><TD>-999</TD></TR>
-          <TR><TD>1365</TD><TD>-999</TD></TR>
-          <TR><TD>1366</TD><TD>-999</TD></TR>
-          <TR><TD>1367</TD><TD>-999</TD></TR>
-          <TR><TD>1368</TD><TD>-999</TD></TR>
-          <TR><TD>1369</TD><TD>-999</TD></TR>
-          <TR><TD>1370</TD><TD>-999</TD></TR>
-          <TR><TD>1371</TD><TD>-999</TD></TR>
-          <TR><TD>1372</TD><TD>-999</TD></TR>
-          <TR><TD>1373</TD><TD>-999</TD></TR>
-          <TR><TD>1374</TD><TD>-999</TD></TR>
-          <TR><TD>1375</TD><TD>-999</TD></TR>
-          <TR><TD>1376</TD><TD>-999</TD></TR>
-          <TR><TD>1377</TD><TD>-999</TD></TR>
-          <TR><TD>1378</TD><TD>-999</TD></TR>
-          <TR><TD>1379</TD><TD>-999</TD></TR>
-          <TR><TD>1380</TD><TD>-999</TD></TR>
-          <TR><TD>1381</TD><TD>-999</TD></TR>
-          <TR><TD>1382</TD><TD>-999</TD></TR>
-          <TR><TD>1383</TD><TD>-999</TD></TR>
-          <TR><TD>1384</TD><TD>-999</TD></TR>
-          <TR><TD>1385</TD><TD>-999</TD></TR>
-          <TR><TD>1386</TD><TD>-999</TD></TR>
-          <TR><TD>1387</TD><TD>-999</TD></TR>
-          <TR><TD>1388</TD><TD>-999</TD></TR>
-          <TR><TD>1389</TD><TD>-999</TD></TR>
-          <TR><TD>1390</TD><TD>-999</TD></TR>
-          <TR><TD>1391</TD><TD>-999</TD></TR>
-          <TR><TD>1392</TD><TD>-999</TD></TR>
-          <TR><TD>1393</TD><TD>-999</TD></TR>
-          <TR><TD>1394</TD><TD>-999</TD></TR>
-          <TR><TD>1395</TD><TD>-999</TD></TR>
-          <TR><TD>1396</TD><TD>-999</TD></TR>
-          <TR><TD>1397</TD><TD>-999</TD></TR>
-          <TR><TD>1398</TD><TD>-999</TD></TR>
-          <TR><TD>1399</TD><TD>-999</TD></TR>
-          <TR><TD>1400</TD><TD>-999</TD></TR>
-          <TR><TD>1401</TD><TD>-999</TD></TR>
-          <TR><TD>1402</TD><TD>-999</TD></TR>
-          <TR><TD>1403</TD><TD>-999</TD></TR>
-          <TR><TD>1404</TD><TD>-999</TD></TR>
-          <TR><TD>1405</TD><TD>-999</TD></TR>
-          <TR><TD>1406</TD><TD>-999</TD></TR>
-          <TR><TD>1407</TD><TD>-999</TD></TR>
-          <TR><TD>1408</TD><TD>-999</TD></TR>
-          <TR><TD>1409</TD><TD>-999</TD></TR>
-          <TR><TD>1410</TD><TD>-999</TD></TR>
-          <TR><TD>1411</TD><TD>-999</TD></TR>
-          <TR><TD>1412</TD><TD>-999</TD></TR>
-          <TR><TD>1413</TD><TD>-999</TD></TR>
-          <TR><TD>1414</TD><TD>-999</TD></TR>
-          <TR><TD>1415</TD><TD>-999</TD></TR>
-          <TR><TD>1416</TD><TD>-999</TD></TR>
-          <TR><TD>1417</TD><TD>-999</TD></TR>
-          <TR><TD>1418</TD><TD>-999</TD></TR>
-          <TR><TD>1419</TD><TD>-999</TD></TR>
-          <TR><TD>1420</TD><TD>-999</TD></TR>
-          <TR><TD>1421</TD><TD>-999</TD></TR>
-          <TR><TD>1422</TD><TD>-999</TD></TR>
-          <TR><TD>1423</TD><TD>-999</TD></TR>
-          <TR><TD>1424</TD><TD>-999</TD></TR>
-          <TR><TD>1425</TD><TD>-999</TD></TR>
-          <TR><TD>1426</TD><TD>-999</TD></TR>
-          <TR><TD>1427</TD><TD>-999</TD></TR>
-          <TR><TD>1428</TD><TD>-999</TD></TR>
-          <TR><TD>1429</TD><TD>-999</TD></TR>
-          <TR><TD>1430</TD><TD>-999</TD></TR>
-          <TR><TD>1431</TD><TD>-999</TD></TR>
-          <TR><TD>1432</TD><TD>-999</TD></TR>
-          <TR><TD>1433</TD><TD>-999</TD></TR>
-          <TR><TD>1434</TD><TD>-999</TD></TR>
-          <TR><TD>1435</TD><TD>-999</TD></TR>
-          <TR><TD>1436</TD><TD>-999</TD></TR>
-          <TR><TD>1437</TD><TD>-999</TD></TR>
-          <TR><TD>1438</TD><TD>-999</TD></TR>
-          <TR><TD>1439</TD><TD>-999</TD></TR>
-          <TR><TD>1440</TD><TD>-999</TD></TR>
-          <TR><TD>1441</TD><TD>-999</TD></TR>
-          <TR><TD>1442</TD><TD>-999</TD></TR>
-          <TR><TD>1443</TD><TD>-999</TD></TR>
-          <TR><TD>1444</TD><TD>-999</TD></TR>
-          <TR><TD>1445</TD><TD>-999</TD></TR>
-          <TR><TD>1446</TD><TD>-999</TD></TR>
-          <TR><TD>1447</TD><TD>-999</TD></TR>
-          <TR><TD>1448</TD><TD>-999</TD></TR>
-          <TR><TD>1449</TD><TD>-999</TD></TR>
-          <TR><TD>1450</TD><TD>-999</TD></TR>
-          <TR><TD>1451</TD><TD>-999</TD></TR>
-          <TR><TD>1452</TD><TD>-999</TD></TR>
-          <TR><TD>1453</TD><TD>-999</TD></TR>
-          <TR><TD>1454</TD><TD>-999</TD></TR>
-          <TR><TD>1455</TD><TD>-999</TD></TR>
-          <TR><TD>1456</TD><TD>-999</TD></TR>
-          <TR><TD>1457</TD><TD>-999</TD></TR>
-          <TR><TD>1458</TD><TD>-999</TD></TR>
-          <TR><TD>1459</TD><TD>-999</TD></TR>
-          <TR><TD>1460</TD><TD>-999</TD></TR>
-          <TR><TD>1461</TD><TD>-999</TD></TR>
-          <TR><TD>1462</TD><TD>-999</TD></TR>
-          <TR><TD>1463</TD><TD>-999</TD></TR>
-          <TR><TD>1464</TD><TD>-999</TD></TR>
-          <TR><TD>1465</TD><TD>-999</TD></TR>
-          <TR><TD>1466</TD><TD>-999</TD></TR>
-          <TR><TD>1467</TD><TD>-999</TD></TR>
-          <TR><TD>1468</TD><TD>-999</TD></TR>
-          <TR><TD>1469</TD><TD>-999</TD></TR>
-          <TR><TD>1470</TD><TD>-999</TD></TR>
-          <TR><TD>1471</TD><TD>-999</TD></TR>
-          <TR><TD>1472</TD><TD>-999</TD></TR>
-          <TR><TD>1473</TD><TD>-999</TD></TR>
-          <TR><TD>1474</TD><TD>-999</TD></TR>
-          <TR><TD>1475</TD><TD>-999</TD></TR>
-          <TR><TD>1476</TD><TD>-999</TD></TR>
-          <TR><TD>1477</TD><TD>-999</TD></TR>
-          <TR><TD>1478</TD><TD>-999</TD></TR>
-          <TR><TD>1479</TD><TD>-999</TD></TR>
-          <TR><TD>1480</TD><TD>-999</TD></TR>
-          <TR><TD>1481</TD><TD>-999</TD></TR>
-          <TR><TD>1482</TD><TD>-999</TD></TR>
-          <TR><TD>1483</TD><TD>-999</TD></TR>
-          <TR><TD>1484</TD><TD>-999</TD></TR>
-          <TR><TD>1485</TD><TD>-999</TD></TR>
-          <TR><TD>1486</TD><TD>-999</TD></TR>
-          <TR><TD>1487</TD><TD>-999</TD></TR>
-          <TR><TD>1488</TD><TD>-999</TD></TR>
-          <TR><TD>1489</TD><TD>-999</TD></TR>
-          <TR><TD>1490</TD><TD>-999</TD></TR>
-          <TR><TD>1491</TD><TD>-999</TD></TR>
-          <TR><TD>1492</TD><TD>-999</TD></TR>
-          <TR><TD>1493</TD><TD>-999</TD></TR>
-          <TR><TD>1494</TD><TD>-999</TD></TR>
-          <TR><TD>1495</TD><TD>-999</TD></TR>
-          <TR><TD>1496</TD><TD>-999</TD></TR>
-          <TR><TD>1497</TD><TD>-999</TD></TR>
-          <TR><TD>1498</TD><TD>-999</TD></TR>
-          <TR><TD>1499</TD><TD>-999</TD></TR>
-          <TR><TD>1500</TD><TD>-999</TD></TR>
-          <TR><TD>1501</TD><TD>-999</TD></TR>
-          <TR><TD>1502</TD><TD>-999</TD></TR>
-          <TR><TD>1503</TD><TD>-999</TD></TR>
-          <TR><TD>1504</TD><TD>-999</TD></TR>
-          <TR><TD>1505</TD><TD>-999</TD></TR>
-          <TR><TD>1506</TD><TD>-999</TD></TR>
-          <TR><TD>1507</TD><TD>-999</TD></TR>
-          <TR><TD>1508</TD><TD>-999</TD></TR>
-          <TR><TD>1509</TD><TD>-999</TD></TR>
-          <TR><TD>1510</TD><TD>-999</TD></TR>
-          <TR><TD>1511</TD><TD>-999</TD></TR>
-          <TR><TD>1512</TD><TD>-999</TD></TR>
-          <TR><TD>1513</TD><TD>-999</TD></TR>
-          <TR><TD>1514</TD><TD>-999</TD></TR>
-          <TR><TD>1515</TD><TD>-999</TD></TR>
-          <TR><TD>1516</TD><TD>-999</TD></TR>
-          <TR><TD>1517</TD><TD>-999</TD></TR>
-          <TR><TD>1518</TD><TD>-999</TD></TR>
-          <TR><TD>1519</TD><TD>-999</TD></TR>
-          <TR><TD>1520</TD><TD>-999</TD></TR>
-          <TR><TD>1521</TD><TD>-999</TD></TR>
-          <TR><TD>1522</TD><TD>-999</TD></TR>
-          <TR><TD>1523</TD><TD>-999</TD></TR>
-          <TR><TD>1524</TD><TD>-999</TD></TR>
-          <TR><TD>1525</TD><TD>-999</TD></TR>
-          <TR><TD>1526</TD><TD>-999</TD></TR>
-          <TR><TD>1527</TD><TD>-999</TD></TR>
-          <TR><TD>1528</TD><TD>-999</TD></TR>
-          <TR><TD>1529</TD><TD>-999</TD></TR>
-          <TR><TD>1530</TD><TD>-999</TD></TR>
-          <TR><TD>1531</TD><TD>-999</TD></TR>
-          <TR><TD>1532</TD><TD>-999</TD></TR>
-          <TR><TD>1533</TD><TD>-999</TD></TR>
-          <TR><TD>1534</TD><TD>-999</TD></TR>
-          <TR><TD>1535</TD><TD>-999</TD></TR>
-          <TR><TD>1536</TD><TD>-999</TD></TR>
-          <TR><TD>1537</TD><TD>-999</TD></TR>
-          <TR><TD>1538</TD><TD>-999</TD></TR>
-          <TR><TD>1539</TD><TD>-999</TD></TR>
-          <TR><TD>1540</TD><TD>-999</TD></TR>
-          <TR><TD>1541</TD><TD>-999</TD></TR>
-          <TR><TD>1542</TD><TD>-999</TD></TR>
-          <TR><TD>1543</TD><TD>-999</TD></TR>
-          <TR><TD>1544</TD><TD>-999</TD></TR>
-          <TR><TD>1545</TD><TD>-999</TD></TR>
-          <TR><TD>1546</TD><TD>-999</TD></TR>
-          <TR><TD>1547</TD><TD>-999</TD></TR>
-          <TR><TD>1548</TD><TD>-999</TD></TR>
-          <TR><TD>1549</TD><TD>-999</TD></TR>
-          <TR><TD>1550</TD><TD>-999</TD></TR>
-          <TR><TD>1551</TD><TD>-999</TD></TR>
-          <TR><TD>1552</TD><TD>-999</TD></TR>
-          <TR><TD>1553</TD><TD>-999</TD></TR>
-          <TR><TD>1554</TD><TD>-999</TD></TR>
-          <TR><TD>1555</TD><TD>-999</TD></TR>
-          <TR><TD>1556</TD><TD>-999</TD></TR>
-          <TR><TD>1557</TD><TD>-999</TD></TR>
-          <TR><TD>1558</TD><TD>-999</TD></TR>
-          <TR><TD>1559</TD><TD>-999</TD></TR>
-          <TR><TD>1560</TD><TD>-999</TD></TR>
-          <TR><TD>1561</TD><TD>-999</TD></TR>
-          <TR><TD>1562</TD><TD>-999</TD></TR>
-          <TR><TD>1563</TD><TD>-999</TD></TR>
-          <TR><TD>1564</TD><TD>-999</TD></TR>
-          <TR><TD>1565</TD><TD>-999</TD></TR>
-          <TR><TD>1566</TD><TD>-999</TD></TR>
-          <TR><TD>1567</TD><TD>-999</TD></TR>
-          <TR><TD>1568</TD><TD>-999</TD></TR>
-          <TR><TD>1569</TD><TD>-999</TD></TR>
-          <TR><TD>1570</TD><TD>-999</TD></TR>
-          <TR><TD>1571</TD><TD>-999</TD></TR>
-          <TR><TD>1572</TD><TD>-999</TD></TR>
-          <TR><TD>1573</TD><TD>-999</TD></TR>
-          <TR><TD>1574</TD><TD>-999</TD></TR>
-          <TR><TD>1575</TD><TD>-999</TD></TR>
-          <TR><TD>1576</TD><TD>-999</TD></TR>
-          <TR><TD>1577</TD><TD>-999</TD></TR>
-          <TR><TD>1578</TD><TD>-999</TD></TR>
-          <TR><TD>1579</TD><TD>-999</TD></TR>
-          <TR><TD>1580</TD><TD>-999</TD></TR>
-          <TR><TD>1581</TD><TD>-999</TD></TR>
-          <TR><TD>1582</TD><TD>-999</TD></TR>
-          <TR><TD>1583</TD><TD>-999</TD></TR>
-          <TR><TD>1584</TD><TD>-999</TD></TR>
-          <TR><TD>1585</TD><TD>-999</TD></TR>
-          <TR><TD>1586</TD><TD>-999</TD></TR>
-          <TR><TD>1587</TD><TD>-999</TD></TR>
-          <TR><TD>1588</TD><TD>-999</TD></TR>
-          <TR><TD>1589</TD><TD>-999</TD></TR>
-          <TR><TD>1590</TD><TD>-999</TD></TR>
-          <TR><TD>1591</TD><TD>-999</TD></TR>
-          <TR><TD>1592</TD><TD>-999</TD></TR>
-          <TR><TD>1593</TD><TD>-999</TD></TR>
-          <TR><TD>1594</TD><TD>-999</TD></TR>
-          <TR><TD>1595</TD><TD>-999</TD></TR>
-          <TR><TD>1596</TD><TD>-999</TD></TR>
-          <TR><TD>1597</TD><TD>-999</TD></TR>
-          <TR><TD>1598</TD><TD>-999</TD></TR>
-          <TR><TD>1599</TD><TD>-999</TD></TR>
-          <TR><TD>1600</TD><TD>-999</TD></TR>
-          <TR><TD>1601</TD><TD>-999</TD></TR>
-          <TR><TD>1602</TD><TD>-999</TD></TR>
-          <TR><TD>1603</TD><TD>-999</TD></TR>
-          <TR><TD>1604</TD><TD>-999</TD></TR>
-          <TR><TD>1605</TD><TD>-999</TD></TR>
-          <TR><TD>1606</TD><TD>-999</TD></TR>
-          <TR><TD>1607</TD><TD>-999</TD></TR>
-          <TR><TD>1608</TD><TD>-999</TD></TR>
-          <TR><TD>1609</TD><TD>-999</TD></TR>
-          <TR><TD>1610</TD><TD>-999</TD></TR>
-          <TR><TD>1611</TD><TD>-999</TD></TR>
-          <TR><TD>1612</TD><TD>-999</TD></TR>
-          <TR><TD>1613</TD><TD>-999</TD></TR>
-          <TR><TD>1614</TD><TD>-999</TD></TR>
-          <TR><TD>1615</TD><TD>-999</TD></TR>
-          <TR><TD>1616</TD><TD>-999</TD></TR>
-          <TR><TD>1617</TD><TD>-999</TD></TR>
-          <TR><TD>1618</TD><TD>-999</TD></TR>
-          <TR><TD>1619</TD><TD>-999</TD></TR>
-          <TR><TD>1620</TD><TD>-999</TD></TR>
-          <TR><TD>1621</TD><TD>-999</TD></TR>
-          <TR><TD>1622</TD><TD>-999</TD></TR>
-          <TR><TD>1623</TD><TD>-999</TD></TR>
-          <TR><TD>1624</TD><TD>-999</TD></TR>
-          <TR><TD>1625</TD><TD>-999</TD></TR>
-          <TR><TD>1626</TD><TD>-999</TD></TR>
-          <TR><TD>1627</TD><TD>-999</TD></TR>
-          <TR><TD>1628</TD><TD>-999</TD></TR>
-          <TR><TD>1629</TD><TD>-999</TD></TR>
-          <TR><TD>1630</TD><TD>-999</TD></TR>
-          <TR><TD>1631</TD><TD>-999</TD></TR>
-          <TR><TD>1632</TD><TD>-999</TD></TR>
-          <TR><TD>1633</TD><TD>-999</TD></TR>
-          <TR><TD>1634</TD><TD>-999</TD></TR>
-          <TR><TD>1635</TD><TD>-999</TD></TR>
-          <TR><TD>1636</TD><TD>-999</TD></TR>
-          <TR><TD>1637</TD><TD>-999</TD></TR>
-          <TR><TD>1638</TD><TD>-999</TD></TR>
-          <TR><TD>1639</TD><TD>-999</TD></TR>
-          <TR><TD>1640</TD><TD>-999</TD></TR>
-          <TR><TD>1641</TD><TD>-999</TD></TR>
-          <TR><TD>1642</TD><TD>-999</TD></TR>
-          <TR><TD>1643</TD><TD>-999</TD></TR>
-          <TR><TD>1644</TD><TD>-999</TD></TR>
-          <TR><TD>1645</TD><TD>-999</TD></TR>
-          <TR><TD>1646</TD><TD>-999</TD></TR>
-          <TR><TD>1647</TD><TD>-999</TD></TR>
-          <TR><TD>1648</TD><TD>-999</TD></TR>
-          <TR><TD>1649</TD><TD>-999</TD></TR>
-          <TR><TD>1650</TD><TD>-999</TD></TR>
-          <TR><TD>1651</TD><TD>-999</TD></TR>
-          <TR><TD>1652</TD><TD>-999</TD></TR>
-          <TR><TD>1653</TD><TD>-999</TD></TR>
-          <TR><TD>1654</TD><TD>-999</TD></TR>
-          <TR><TD>1655</TD><TD>-999</TD></TR>
-          <TR><TD>1656</TD><TD>-999</TD></TR>
-          <TR><TD>1657</TD><TD>-999</TD></TR>
-          <TR><TD>1658</TD><TD>-999</TD></TR>
-          <TR><TD>1659</TD><TD>-999</TD></TR>
-          <TR><TD>1660</TD><TD>-999</TD></TR>
-          <TR><TD>1661</TD><TD>-999</TD></TR>
-          <TR><TD>1662</TD><TD>-999</TD></TR>
-          <TR><TD>1663</TD><TD>-999</TD></TR>
-          <TR><TD>1664</TD><TD>-999</TD></TR>
-          <TR><TD>1665</TD><TD>-999</TD></TR>
-          <TR><TD>1666</TD><TD>-999</TD></TR>
-          <TR><TD>1667</TD><TD>-999</TD></TR>
-          <TR><TD>1668</TD><TD>-999</TD></TR>
-          <TR><TD>1669</TD><TD>-999</TD></TR>
-          <TR><TD>1670</TD><TD>-999</TD></TR>
-          <TR><TD>1671</TD><TD>-999</TD></TR>
-          <TR><TD>1672</TD><TD>-999</TD></TR>
-          <TR><TD>1673</TD><TD>-999</TD></TR>
-          <TR><TD>1674</TD><TD>-999</TD></TR>
-          <TR><TD>1675</TD><TD>-999</TD></TR>
-          <TR><TD>1676</TD><TD>-999</TD></TR>
-          <TR><TD>1677</TD><TD>-999</TD></TR>
-          <TR><TD>1678</TD><TD>-999</TD></TR>
-          <TR><TD>1679</TD><TD>-999</TD></TR>
-          <TR><TD>1680</TD><TD>-999</TD></TR>
-          <TR><TD>1681</TD><TD>-999</TD></TR>
-          <TR><TD>1682</TD><TD>-999</TD></TR>
-          <TR><TD>1683</TD><TD>-999</TD></TR>
-          <TR><TD>1684</TD><TD>-999</TD></TR>
-          <TR><TD>1685</TD><TD>-999</TD></TR>
-          <TR><TD>1686</TD><TD>-999</TD></TR>
-          <TR><TD>1687</TD><TD>-999</TD></TR>
-          <TR><TD>1688</TD><TD>-999</TD></TR>
-          <TR><TD>1689</TD><TD>-999</TD></TR>
-          <TR><TD>1690</TD><TD>-999</TD></TR>
-          <TR><TD>1691</TD><TD>-999</TD></TR>
-          <TR><TD>1692</TD><TD>-999</TD></TR>
-          <TR><TD>1693</TD><TD>-999</TD></TR>
-          <TR><TD>1694</TD><TD>-999</TD></TR>
-          <TR><TD>1695</TD><TD>-999</TD></TR>
-          <TR><TD>1696</TD><TD>-999</TD></TR>
-          <TR><TD>1697</TD><TD>-999</TD></TR>
-          <TR><TD>1698</TD><TD>-999</TD></TR>
-          <TR><TD>1699</TD><TD>-999</TD></TR>
-          <TR><TD>1700</TD><TD>-999</TD></TR>
-          <TR><TD>1701</TD><TD>-999</TD></TR>
-          <TR><TD>1702</TD><TD>-999</TD></TR>
-          <TR><TD>1703</TD><TD>-999</TD></TR>
-          <TR><TD>1704</TD><TD>-999</TD></TR>
-          <TR><TD>1705</TD><TD>-999</TD></TR>
-          <TR><TD>1706</TD><TD>-999</TD></TR>
-          <TR><TD>1707</TD><TD>-999</TD></TR>
-          <TR><TD>1708</TD><TD>-999</TD></TR>
-          <TR><TD>1709</TD><TD>-999</TD></TR>
-          <TR><TD>1710</TD><TD>-999</TD></TR>
-          <TR><TD>1711</TD><TD>-999</TD></TR>
-          <TR><TD>1712</TD><TD>-999</TD></TR>
-          <TR><TD>1713</TD><TD>-999</TD></TR>
-          <TR><TD>1714</TD><TD>-999</TD></TR>
-          <TR><TD>1715</TD><TD>-999</TD></TR>
-          <TR><TD>1716</TD><TD>-999</TD></TR>
-          <TR><TD>1717</TD><TD>-999</TD></TR>
-          <TR><TD>1718</TD><TD>-999</TD></TR>
-          <TR><TD>1719</TD><TD>-999</TD></TR>
-          <TR><TD>1720</TD><TD>-999</TD></TR>
-          <TR><TD>1721</TD><TD>-999</TD></TR>
-          <TR><TD>1722</TD><TD>-999</TD></TR>
-          <TR><TD>1723</TD><TD>-999</TD></TR>
-          <TR><TD>1724</TD><TD>-999</TD></TR>
-          <TR><TD>1725</TD><TD>-999</TD></TR>
-          <TR><TD>1726</TD><TD>-999</TD></TR>
-          <TR><TD>1727</TD><TD>-999</TD></TR>
-          <TR><TD>1728</TD><TD>-999</TD></TR>
-          <TR><TD>1729</TD><TD>-999</TD></TR>
-          <TR><TD>1730</TD><TD>-999</TD></TR>
-          <TR><TD>1731</TD><TD>-999</TD></TR>
-          <TR><TD>1732</TD><TD>-999</TD></TR>
-          <TR><TD>1733</TD><TD>-999</TD></TR>
-          <TR><TD>1734</TD><TD>-999</TD></TR>
-          <TR><TD>1735</TD><TD>-999</TD></TR>
-          <TR><TD>1736</TD><TD>-999</TD></TR>
-          <TR><TD>1737</TD><TD>-999</TD></TR>
-          <TR><TD>1738</TD><TD>-999</TD></TR>
-          <TR><TD>1739</TD><TD>-999</TD></TR>
-          <TR><TD>1740</TD><TD>-999</TD></TR>
-          <TR><TD>1741</TD><TD>-999</TD></TR>
-          <TR><TD>1742</TD><TD>-999</TD></TR>
-          <TR><TD>1743</TD><TD>-999</TD></TR>
-          <TR><TD>1744</TD><TD>-999</TD></TR>
-          <TR><TD>1745</TD><TD>-999</TD></TR>
-          <TR><TD>1746</TD><TD>-999</TD></TR>
-          <TR><TD>1747</TD><TD>-999</TD></TR>
-          <TR><TD>1748</TD><TD>-999</TD></TR>
-          <TR><TD>1749</TD><TD>-999</TD></TR>
-          <TR><TD>1750</TD><TD>-999</TD></TR>
-          <TR><TD>1751</TD><TD>-999</TD></TR>
-          <TR><TD>1752</TD><TD>-999</TD></TR>
-          <TR><TD>1753</TD><TD>-999</TD></TR>
-          <TR><TD>1754</TD><TD>-999</TD></TR>
-          <TR><TD>1755</TD><TD>-999</TD></TR>
-          <TR><TD>1756</TD><TD>-999</TD></TR>
-          <TR><TD>1757</TD><TD>-999</TD></TR>
-          <TR><TD>1758</TD><TD>-999</TD></TR>
-          <TR><TD>1759</TD><TD>-999</TD></TR>
-          <TR><TD>1760</TD><TD>-999</TD></TR>
-          <TR><TD>1761</TD><TD>-999</TD></TR>
-          <TR><TD>1762</TD><TD>-999</TD></TR>
-          <TR><TD>1763</TD><TD>-999</TD></TR>
-          <TR><TD>1764</TD><TD>-999</TD></TR>
-          <TR><TD>1765</TD><TD>-999</TD></TR>
-          <TR><TD>1766</TD><TD>-999</TD></TR>
-          <TR><TD>1767</TD><TD>-999</TD></TR>
-          <TR><TD>1768</TD><TD>-999</TD></TR>
-          <TR><TD>1769</TD><TD>-999</TD></TR>
-          <TR><TD>1770</TD><TD>-999</TD></TR>
-          <TR><TD>1771</TD><TD>-999</TD></TR>
-          <TR><TD>1772</TD><TD>-999</TD></TR>
-          <TR><TD>1773</TD><TD>-999</TD></TR>
-          <TR><TD>1774</TD><TD>-999</TD></TR>
-          <TR><TD>1775</TD><TD>-999</TD></TR>
-          <TR><TD>1776</TD><TD>-999</TD></TR>
-          <TR><TD>1777</TD><TD>-999</TD></TR>
-          <TR><TD>1778</TD><TD>-999</TD></TR>
-          <TR><TD>1779</TD><TD>-999</TD></TR>
-          <TR><TD>1780</TD><TD>-999</TD></TR>
-          <TR><TD>1781</TD><TD>-999</TD></TR>
-          <TR><TD>1782</TD><TD>-999</TD></TR>
-          <TR><TD>1783</TD><TD>-999</TD></TR>
-          <TR><TD>1784</TD><TD>-999</TD></TR>
-          <TR><TD>1785</TD><TD>-999</TD></TR>
-          <TR><TD>1786</TD><TD>-999</TD></TR>
-          <TR><TD>1787</TD><TD>-999</TD></TR>
-          <TR><TD>1788</TD><TD>-999</TD></TR>
-          <TR><TD>1789</TD><TD>-999</TD></TR>
-          <TR><TD>1790</TD><TD>-999</TD></TR>
-          <TR><TD>1791</TD><TD>-999</TD></TR>
-          <TR><TD>1792</TD><TD>-999</TD></TR>
-          <TR><TD>1793</TD><TD>-999</TD></TR>
-          <TR><TD>1794</TD><TD>-999</TD></TR>
-          <TR><TD>1795</TD><TD>-999</TD></TR>
-          <TR><TD>1796</TD><TD>-999</TD></TR>
-          <TR><TD>1797</TD><TD>-999</TD></TR>
-          <TR><TD>1798</TD><TD>-999</TD></TR>
-          <TR><TD>1799</TD><TD>-999</TD></TR>
-          <TR><TD>1800</TD><TD>-999</TD></TR>
-          <TR><TD>1801</TD><TD>-999</TD></TR>
-          <TR><TD>1802</TD><TD>-999</TD></TR>
-          <TR><TD>1803</TD><TD>-999</TD></TR>
-          <TR><TD>1804</TD><TD>-999</TD></TR>
-          <TR><TD>1805</TD><TD>-999</TD></TR>
-          <TR><TD>1806</TD><TD>-999</TD></TR>
-          <TR><TD>1807</TD><TD>-999</TD></TR>
-          <TR><TD>1808</TD><TD>-999</TD></TR>
-          <TR><TD>1809</TD><TD>-999</TD></TR>
-          <TR><TD>1810</TD><TD>-999</TD></TR>
-          <TR><TD>1811</TD><TD>-999</TD></TR>
-          <TR><TD>1812</TD><TD>-999</TD></TR>
-          <TR><TD>1813</TD><TD>-999</TD></TR>
-          <TR><TD>1814</TD><TD>-999</TD></TR>
-          <TR><TD>1815</TD><TD>-999</TD></TR>
-          <TR><TD>1816</TD><TD>-999</TD></TR>
-          <TR><TD>1817</TD><TD>-999</TD></TR>
-          <TR><TD>1818</TD><TD>-999</TD></TR>
-          <TR><TD>1819</TD><TD>-999</TD></TR>
-          <TR><TD>1820</TD><TD>-999</TD></TR>
-          <TR><TD>1821</TD><TD>-999</TD></TR>
-          <TR><TD>1822</TD><TD>-999</TD></TR>
-          <TR><TD>1823</TD><TD>-999</TD></TR>
-          <TR><TD>1824</TD><TD>-999</TD></TR>
-          <TR><TD>1825</TD><TD>-999</TD></TR>
-          <TR><TD>1826</TD><TD>-999</TD></TR>
-          <TR><TD>1827</TD><TD>-999</TD></TR>
-          <TR><TD>1828</TD><TD>-999</TD></TR>
-          <TR><TD>1829</TD><TD>-999</TD></TR>
-          <TR><TD>1830</TD><TD>-999</TD></TR>
-          <TR><TD>1831</TD><TD>-999</TD></TR>
-          <TR><TD>1832</TD><TD>-999</TD></TR>
-          <TR><TD>1833</TD><TD>-999</TD></TR>
-          <TR><TD>1834</TD><TD>-999</TD></TR>
-          <TR><TD>1835</TD><TD>-999</TD></TR>
-          <TR><TD>1836</TD><TD>-999</TD></TR>
-          <TR><TD>1837</TD><TD>-999</TD></TR>
-          <TR><TD>1838</TD><TD>-999</TD></TR>
-          <TR><TD>1839</TD><TD>-999</TD></TR>
-          <TR><TD>1840</TD><TD>-999</TD></TR>
-          <TR><TD>1841</TD><TD>-999</TD></TR>
-          <TR><TD>1842</TD><TD>-999</TD></TR>
-          <TR><TD>1843</TD><TD>-999</TD></TR>
-          <TR><TD>1844</TD><TD>-999</TD></TR>
-          <TR><TD>1845</TD><TD>-999</TD></TR>
-          <TR><TD>1846</TD><TD>-999</TD></TR>
-          <TR><TD>1847</TD><TD>-999</TD></TR>
-          <TR><TD>1848</TD><TD>-999</TD></TR>
-          <TR><TD>1849</TD><TD>-999</TD></TR>
-          <TR><TD>1850</TD><TD>-999</TD></TR>
-          <TR><TD>1851</TD><TD>-999</TD></TR>
-          <TR><TD>1852</TD><TD>-999</TD></TR>
-          <TR><TD>1853</TD><TD>-999</TD></TR>
-          <TR><TD>1854</TD><TD>-999</TD></TR>
-          <TR><TD>1855</TD><TD>-999</TD></TR>
-          <TR><TD>1856</TD><TD>-999</TD></TR>
-          <TR><TD>1857</TD><TD>-999</TD></TR>
-          <TR><TD>1858</TD><TD>-999</TD></TR>
-          <TR><TD>1859</TD><TD>-999</TD></TR>
-          <TR><TD>1860</TD><TD>-999</TD></TR>
-          <TR><TD>1861</TD><TD>-999</TD></TR>
-          <TR><TD>1862</TD><TD>-999</TD></TR>
-          <TR><TD>1863</TD><TD>-999</TD></TR>
-          <TR><TD>1864</TD><TD>-999</TD></TR>
-          <TR><TD>1865</TD><TD>-999</TD></TR>
-          <TR><TD>1866</TD><TD>-999</TD></TR>
-          <TR><TD>1867</TD><TD>-999</TD></TR>
-          <TR><TD>1868</TD><TD>-999</TD></TR>
-          <TR><TD>1869</TD><TD>-999</TD></TR>
-          <TR><TD>1870</TD><TD>-999</TD></TR>
-          <TR><TD>1871</TD><TD>-999</TD></TR>
-          <TR><TD>1872</TD><TD>-999</TD></TR>
-          <TR><TD>1873</TD><TD>-999</TD></TR>
-          <TR><TD>1874</TD><TD>-999</TD></TR>
-          <TR><TD>1875</TD><TD>-999</TD></TR>
-          <TR><TD>1876</TD><TD>-999</TD></TR>
-          <TR><TD>1877</TD><TD>-999</TD></TR>
-          <TR><TD>1878</TD><TD>-999</TD></TR>
-          <TR><TD>1879</TD><TD>-999</TD></TR>
-          <TR><TD>1880</TD><TD>-999</TD></TR>
-          <TR><TD>1881</TD><TD>-999</TD></TR>
-          <TR><TD>1882</TD><TD>-999</TD></TR>
-          <TR><TD>1883</TD><TD>-999</TD></TR>
-          <TR><TD>1884</TD><TD>-999</TD></TR>
-          <TR><TD>1885</TD><TD>-999</TD></TR>
-          <TR><TD>1886</TD><TD>-999</TD></TR>
-          <TR><TD>1887</TD><TD>-999</TD></TR>
-          <TR><TD>1888</TD><TD>-999</TD></TR>
-          <TR><TD>1889</TD><TD>-999</TD></TR>
-          <TR><TD>1890</TD><TD>-999</TD></TR>
-          <TR><TD>1891</TD><TD>-999</TD></TR>
-          <TR><TD>1892</TD><TD>-999</TD></TR>
-          <TR><TD>1893</TD><TD>-999</TD></TR>
-          <TR><TD>1894</TD><TD>-999</TD></TR>
-          <TR><TD>1895</TD><TD>-999</TD></TR>
-          <TR><TD>1896</TD><TD>-999</TD></TR>
-          <TR><TD>1897</TD><TD>-999</TD></TR>
-          <TR><TD>1898</TD><TD>-999</TD></TR>
-          <TR><TD>1899</TD><TD>-999</TD></TR>
-          <TR><TD>1900</TD><TD>-999</TD></TR>
-          <TR><TD>1901</TD><TD>-999</TD></TR>
-          <TR><TD>1902</TD><TD>-999</TD></TR>
-          <TR><TD>1903</TD><TD>-999</TD></TR>
-          <TR><TD>1904</TD><TD>-999</TD></TR>
-          <TR><TD>1905</TD><TD>-999</TD></TR>
-          <TR><TD>1906</TD><TD>-999</TD></TR>
-          <TR><TD>1907</TD><TD>-999</TD></TR>
-          <TR><TD>1908</TD><TD>-999</TD></TR>
-          <TR><TD>1909</TD><TD>-999</TD></TR>
-          <TR><TD>1910</TD><TD>-999</TD></TR>
-          <TR><TD>1911</TD><TD>-999</TD></TR>
-          <TR><TD>1912</TD><TD>-999</TD></TR>
-          <TR><TD>1913</TD><TD>-999</TD></TR>
-          <TR><TD>1914</TD><TD>-999</TD></TR>
-          <TR><TD>1915</TD><TD>-999</TD></TR>
-          <TR><TD>1916</TD><TD>-999</TD></TR>
-          <TR><TD>1917</TD><TD>-999</TD></TR>
-          <TR><TD>1918</TD><TD>-999</TD></TR>
-          <TR><TD>1919</TD><TD>-999</TD></TR>
-          <TR><TD>1920</TD><TD>-999</TD></TR>
-          <TR><TD>1921</TD><TD>-999</TD></TR>
-          <TR><TD>1922</TD><TD>-999</TD></TR>
-          <TR><TD>1923</TD><TD>-999</TD></TR>
-          <TR><TD>1924</TD><TD>-999</TD></TR>
-          <TR><TD>1925</TD><TD>-999</TD></TR>
-          <TR><TD>1926</TD><TD>-999</TD></TR>
-          <TR><TD>1927</TD><TD>-999</TD></TR>
-          <TR><TD>1928</TD><TD>-999</TD></TR>
-          <TR><TD>1929</TD><TD>-999</TD></TR>
-          <TR><TD>1930</TD><TD>-999</TD></TR>
-          <TR><TD>1931</TD><TD>-999</TD></TR>
-          <TR><TD>1932</TD><TD>-999</TD></TR>
-          <TR><TD>1933</TD><TD>-999</TD></TR>
-          <TR><TD>1934</TD><TD>-999</TD></TR>
-          <TR><TD>1935</TD><TD>-999</TD></TR>
-          <TR><TD>1936</TD><TD>-999</TD></TR>
-          <TR><TD>1937</TD><TD>-999</TD></TR>
-          <TR><TD>1938</TD><TD>-999</TD></TR>
-          <TR><TD>1939</TD><TD>-999</TD></TR>
-          <TR><TD>1940</TD><TD>-999</TD></TR>
-          <TR><TD>1941</TD><TD>-999</TD></TR>
-          <TR><TD>1942</TD><TD>-999</TD></TR>
-          <TR><TD>1943</TD><TD>-999</TD></TR>
-          <TR><TD>1944</TD><TD>-999</TD></TR>
-          <TR><TD>1945</TD><TD>-999</TD></TR>
-          <TR><TD>1946</TD><TD>-999</TD></TR>
-          <TR><TD>1947</TD><TD>-999</TD></TR>
-          <TR><TD>1948</TD><TD>-999</TD></TR>
-          <TR><TD>1949</TD><TD>-999</TD></TR>
-          <TR><TD>1950</TD><TD>-999</TD></TR>
-          <TR><TD>1951</TD><TD>-999</TD></TR>
-          <TR><TD>1952</TD><TD>-999</TD></TR>
-          <TR><TD>1953</TD><TD>-999</TD></TR>
-          <TR><TD>1954</TD><TD>-999</TD></TR>
-          <TR><TD>1955</TD><TD>-999</TD></TR>
-          <TR><TD>1956</TD><TD>-999</TD></TR>
-          <TR><TD>1957</TD><TD>-999</TD></TR>
-          <TR><TD>1958</TD><TD>-999</TD></TR>
-          <TR><TD>1959</TD><TD>-999</TD></TR>
-          <TR><TD>1960</TD><TD>-999</TD></TR>
-          <TR><TD>1961</TD><TD>-999</TD></TR>
-          <TR><TD>1962</TD><TD>-999</TD></TR>
-          <TR><TD>1963</TD><TD>-999</TD></TR>
-          <TR><TD>1964</TD><TD>-999</TD></TR>
-          <TR><TD>1965</TD><TD>-999</TD></TR>
-          <TR><TD>1966</TD><TD>-999</TD></TR>
-          <TR><TD>1967</TD><TD>-999</TD></TR>
-          <TR><TD>1968</TD><TD>-999</TD></TR>
-          <TR><TD>1969</TD><TD>-999</TD></TR>
-          <TR><TD>1970</TD><TD>-999</TD></TR>
-          <TR><TD>1971</TD><TD>-999</TD></TR>
-          <TR><TD>1972</TD><TD>-999</TD></TR>
-          <TR><TD>1973</TD><TD>-999</TD></TR>
-          <TR><TD>1974</TD><TD>-999</TD></TR>
-          <TR><TD>1975</TD><TD>-999</TD></TR>
-          <TR><TD>1976</TD><TD>-999</TD></TR>
-          <TR><TD>1977</TD><TD>-999</TD></TR>
-          <TR><TD>1978</TD><TD>-999</TD></TR>
-          <TR><TD>1979</TD><TD>-999</TD></TR>
-          <TR><TD>1980</TD><TD>-999</TD></TR>
-          <TR><TD>1981</TD><TD>-999</TD></TR>
-          <TR><TD>1982</TD><TD>-999</TD></TR>
-          <TR><TD>1983</TD><TD>-999</TD></TR>
-          <TR><TD>1984</TD><TD>-999</TD></TR>
-          <TR><TD>1985</TD><TD>-999</TD></TR>
-          <TR><TD>1986</TD><TD>-999</TD></TR>
-          <TR><TD>1987</TD><TD>-999</TD></TR>
-          <TR><TD>1988</TD><TD>-999</TD></TR>
-          <TR><TD>1989</TD><TD>-999</TD></TR>
-          <TR><TD>1990</TD><TD>-999</TD></TR>
-          <TR><TD>1991</TD><TD>-999</TD></TR>
-          <TR><TD>1992</TD><TD>-999</TD></TR>
-          <TR><TD>1993</TD><TD>-999</TD></TR>
-          <TR><TD>1994</TD><TD>-999</TD></TR>
-          <TR><TD>1995</TD><TD>-999</TD></TR>
-          <TR><TD>1996</TD><TD>-999</TD></TR>
-          <TR><TD>1997</TD><TD>-999</TD></TR>
-          <TR><TD>1998</TD><TD>-999</TD></TR>
-          <TR><TD>1999</TD><TD>-999</TD></TR>
-          <TR><TD>2000</TD><TD>-999</TD></TR>
-          <TR><TD>2001</TD><TD>-999</TD></TR>
-          <TR><TD>2002</TD><TD>-999</TD></TR>
-          <TR><TD>2003</TD><TD>-999</TD></TR>
-          <TR><TD>2004</TD><TD>-999</TD></TR>
-          <TR><TD>2005</TD><TD>-999</TD></TR>
-          <TR><TD>2006</TD><TD>-999</TD></TR>
-          <TR><TD>2007</TD><TD>-999</TD></TR>
-          <TR><TD>2008</TD><TD>-999</TD></TR>
-          <TR><TD>2009</TD><TD>-999</TD></TR>
-          <TR><TD>2010</TD><TD>-999</TD></TR>
-          <TR><TD>2011</TD><TD>-999</TD></TR>
-          <TR><TD>2012</TD><TD>-999</TD></TR>
-          <TR><TD>2013</TD><TD>-999</TD></TR>
-          <TR><TD>2014</TD><TD>-999</TD></TR>
-          <TR><TD>2015</TD><TD>-999</TD></TR>
-          <TR><TD>2016</TD><TD>-999</TD></TR>
-          <TR><TD>2017</TD><TD>-999</TD></TR>
-          <TR><TD>2018</TD><TD>-999</TD></TR>
-          <TR><TD>2019</TD><TD>-999</TD></TR>
-          <TR><TD>2020</TD><TD>-999</TD></TR>
-          <TR><TD>2021</TD><TD>-999</TD></TR>
-          <TR><TD>2022</TD><TD>-999</TD></TR>
-          <TR><TD>2023</TD><TD>-999</TD></TR>
-          <TR><TD>2024</TD><TD>-999</TD></TR>
-          <TR><TD>2025</TD><TD>-999</TD></TR>
-          <TR><TD>2026</TD><TD>-999</TD></TR>
-          <TR><TD>2027</TD><TD>-999</TD></TR>
-          <TR><TD>2028</TD><TD>-999</TD></TR>
-          <TR><TD>2029</TD><TD>-999</TD></TR>
-          <TR><TD>2030</TD><TD>-999</TD></TR>
-          <TR><TD>2031</TD><TD>-999</TD></TR>
-          <TR><TD>2032</TD><TD>-999</TD></TR>
-          <TR><TD>2033</TD><TD>-999</TD></TR>
-          <TR><TD>2034</TD><TD>-999</TD></TR>
-          <TR><TD>2035</TD><TD>-999</TD></TR>
-          <TR><TD>2036</TD><TD>-999</TD></TR>
-          <TR><TD>2037</TD><TD>-999</TD></TR>
-          <TR><TD>2038</TD><TD>-999</TD></TR>
-          <TR><TD>2039</TD><TD>-999</TD></TR>
-          <TR><TD>2040</TD><TD>-999</TD></TR>
-          <TR><TD>2041</TD><TD>-999</TD></TR>
-          <TR><TD>2042</TD><TD>-999</TD></TR>
-          <TR><TD>2043</TD><TD>-999</TD></TR>
-          <TR><TD>2044</TD><TD>-999</TD></TR>
-          <TR><TD>2045</TD><TD>-999</TD></TR>
-          <TR><TD>2046</TD><TD>-999</TD></TR>
-          <TR><TD>2047</TD><TD>-999</TD></TR>
-          <TR><TD>2048</TD><TD>-999</TD></TR>
-          <TR><TD>2049</TD><TD>-999</TD></TR>
-          <TR><TD>2050</TD><TD>-999</TD></TR>
-          <TR><TD>2051</TD><TD>-999</TD></TR>
-          <TR><TD>2052</TD><TD>-999</TD></TR>
-          <TR><TD>2053</TD><TD>-999</TD></TR>
-          <TR><TD>2054</TD><TD>-999</TD></TR>
-          <TR><TD>2055</TD><TD>-999</TD></TR>
-          <TR><TD>2056</TD><TD>-999</TD></TR>
-          <TR><TD>2057</TD><TD>-999</TD></TR>
-          <TR><TD>2058</TD><TD>-999</TD></TR>
-          <TR><TD>2059</TD><TD>-999</TD></TR>
-          <TR><TD>2060</TD><TD>-999</TD></TR>
-          <TR><TD>2061</TD><TD>-999</TD></TR>
-          <TR><TD>2062</TD><TD>-999</TD></TR>
-          <TR><TD>2063</TD><TD>-999</TD></TR>
-          <TR><TD>2064</TD><TD>-999</TD></TR>
-          <TR><TD>2065</TD><TD>-999</TD></TR>
-          <TR><TD>2066</TD><TD>-999</TD></TR>
-          <TR><TD>2067</TD><TD>-999</TD></TR>
-          <TR><TD>2068</TD><TD>-999</TD></TR>
-          <TR><TD>2069</TD><TD>-999</TD></TR>
-          <TR><TD>2070</TD><TD>-999</TD></TR>
-          <TR><TD>2071</TD><TD>-999</TD></TR>
-          <TR><TD>2072</TD><TD>-999</TD></TR>
-          <TR><TD>2073</TD><TD>-999</TD></TR>
-          <TR><TD>2074</TD><TD>-999</TD></TR>
-          <TR><TD>2075</TD><TD>-999</TD></TR>
-          <TR><TD>2076</TD><TD>-999</TD></TR>
-          <TR><TD>2077</TD><TD>-999</TD></TR>
-          <TR><TD>2078</TD><TD>-999</TD></TR>
-          <TR><TD>2079</TD><TD>-999</TD></TR>
-          <TR><TD>2080</TD><TD>-999</TD></TR>
-          <TR><TD>2081</TD><TD>-999</TD></TR>
-          <TR><TD>2082</TD><TD>-999</TD></TR>
-          <TR><TD>2083</TD><TD>-999</TD></TR>
-          <TR><TD>2084</TD><TD>-999</TD></TR>
-          <TR><TD>2085</TD><TD>-999</TD></TR>
-          <TR><TD>2086</TD><TD>-999</TD></TR>
-          <TR><TD>2087</TD><TD>-999</TD></TR>
-          <TR><TD>2088</TD><TD>-999</TD></TR>
-          <TR><TD>2089</TD><TD>-999</TD></TR>
-          <TR><TD>2090</TD><TD>-999</TD></TR>
-          <TR><TD>2091</TD><TD>-999</TD></TR>
-          <TR><TD>2092</TD><TD>-999</TD></TR>
-          <TR><TD>2093</TD><TD>-999</TD></TR>
-          <TR><TD>2094</TD><TD>-999</TD></TR>
-          <TR><TD>2095</TD><TD>-999</TD></TR>
-          <TR><TD>2096</TD><TD>-999</TD></TR>
-          <TR><TD>2097</TD><TD>-999</TD></TR>
-          <TR><TD>2098</TD><TD>-999</TD></TR>
-          <TR><TD>2099</TD><TD>-999</TD></TR>
-          <TR><TD>2100</TD><TD>-999</TD></TR>
-          <TR><TD>2101</TD><TD>-999</TD></TR>
-          <TR><TD>2102</TD><TD>-999</TD></TR>
-          <TR><TD>2103</TD><TD>-999</TD></TR>
-          <TR><TD>2104</TD><TD>-999</TD></TR>
-          <TR><TD>2105</TD><TD>-999</TD></TR>
-          <TR><TD>2106</TD><TD>-999</TD></TR>
-          <TR><TD>2107</TD><TD>-999</TD></TR>
-          <TR><TD>2108</TD><TD>-999</TD></TR>
-          <TR><TD>2109</TD><TD>-999</TD></TR>
-          <TR><TD>2110</TD><TD>-999</TD></TR>
-          <TR><TD>2111</TD><TD>-999</TD></TR>
-          <TR><TD>2112</TD><TD>-999</TD></TR>
-          <TR><TD>2113</TD><TD>-999</TD></TR>
-          <TR><TD>2114</TD><TD>-999</TD></TR>
-          <TR><TD>2115</TD><TD>-999</TD></TR>
-          <TR><TD>2116</TD><TD>-999</TD></TR>
-          <TR><TD>2117</TD><TD>-999</TD></TR>
-          <TR><TD>2118</TD><TD>-999</TD></TR>
-          <TR><TD>2119</TD><TD>-999</TD></TR>
-          <TR><TD>2120</TD><TD>-999</TD></TR>
-          <TR><TD>2121</TD><TD>-999</TD></TR>
-          <TR><TD>2122</TD><TD>-999</TD></TR>
-          <TR><TD>2123</TD><TD>-999</TD></TR>
-          <TR><TD>2124</TD><TD>-999</TD></TR>
-          <TR><TD>2125</TD><TD>-999</TD></TR>
-          <TR><TD>2126</TD><TD>-999</TD></TR>
-          <TR><TD>2127</TD><TD>-999</TD></TR>
-          <TR><TD>2128</TD><TD>-999</TD></TR>
-          <TR><TD>2129</TD><TD>-999</TD></TR>
-          <TR><TD>2130</TD><TD>-999</TD></TR>
-          <TR><TD>2131</TD><TD>-999</TD></TR>
-          <TR><TD>2132</TD><TD>-999</TD></TR>
-          <TR><TD>2133</TD><TD>-999</TD></TR>
-          <TR><TD>2134</TD><TD>-999</TD></TR>
-          <TR><TD>2135</TD><TD>-999</TD></TR>
-          <TR><TD>2136</TD><TD>-999</TD></TR>
-          <TR><TD>2137</TD><TD>-999</TD></TR>
-          <TR><TD>2138</TD><TD>-999</TD></TR>
-          <TR><TD>2139</TD><TD>-999</TD></TR>
-          <TR><TD>2140</TD><TD>-999</TD></TR>
-          <TR><TD>2141</TD><TD>-999</TD></TR>
-          <TR><TD>2142</TD><TD>-999</TD></TR>
-          <TR><TD>2143</TD><TD>-999</TD></TR>
-          <TR><TD>2144</TD><TD>-999</TD></TR>
-          <TR><TD>2145</TD><TD>-999</TD></TR>
-          <TR><TD>2146</TD><TD>-999</TD></TR>
-          <TR><TD>2147</TD><TD>-999</TD></TR>
-          <TR><TD>2148</TD><TD>-999</TD></TR>
-          <TR><TD>2149</TD><TD>-999</TD></TR>
-          <TR><TD>2150</TD><TD>-999</TD></TR>
-          <TR><TD>2151</TD><TD>-999</TD></TR>
-          <TR><TD>2152</TD><TD>-999</TD></TR>
-          <TR><TD>2153</TD><TD>-999</TD></TR>
-          <TR><TD>2154</TD><TD>-999</TD></TR>
-          <TR><TD>2155</TD><TD>-999</TD></TR>
-          <TR><TD>2156</TD><TD>-999</TD></TR>
-          <TR><TD>2157</TD><TD>-999</TD></TR>
-          <TR><TD>2158</TD><TD>-999</TD></TR>
-          <TR><TD>2159</TD><TD>-999</TD></TR>
-          <TR><TD>2160</TD><TD>-999</TD></TR>
-          <TR><TD>2161</TD><TD>-999</TD></TR>
-          <TR><TD>2162</TD><TD>-999</TD></TR>
-          <TR><TD>2163</TD><TD>-999</TD></TR>
-          <TR><TD>2164</TD><TD>-999</TD></TR>
-          <TR><TD>2165</TD><TD>-999</TD></TR>
-          <TR><TD>2166</TD><TD>-999</TD></TR>
-          <TR><TD>2167</TD><TD>-999</TD></TR>
-          <TR><TD>2168</TD><TD>-999</TD></TR>
-          <TR><TD>2169</TD><TD>-999</TD></TR>
-          <TR><TD>2170</TD><TD>-999</TD></TR>
-          <TR><TD>2171</TD><TD>-999</TD></TR>
-          <TR><TD>2172</TD><TD>-999</TD></TR>
-          <TR><TD>2173</TD><TD>-999</TD></TR>
-          <TR><TD>2174</TD><TD>-999</TD></TR>
-          <TR><TD>2175</TD><TD>-999</TD></TR>
-          <TR><TD>2176</TD><TD>-999</TD></TR>
-          <TR><TD>2177</TD><TD>-999</TD></TR>
-          <TR><TD>2178</TD><TD>-999</TD></TR>
-          <TR><TD>2179</TD><TD>-999</TD></TR>
-          <TR><TD>2180</TD><TD>-999</TD></TR>
-          <TR><TD>2181</TD><TD>-999</TD></TR>
-          <TR><TD>2182</TD><TD>-999</TD></TR>
-          <TR><TD>2183</TD><TD>-999</TD></TR>
-          <TR><TD>2184</TD><TD>-999</TD></TR>
-          <TR><TD>2185</TD><TD>-999</TD></TR>
-          <TR><TD>2186</TD><TD>-999</TD></TR>
-          <TR><TD>2187</TD><TD>-999</TD></TR>
-          <TR><TD>2188</TD><TD>-999</TD></TR>
-          <TR><TD>2189</TD><TD>-999</TD></TR>
-          <TR><TD>2190</TD><TD>-999</TD></TR>
-          <TR><TD>2191</TD><TD>-999</TD></TR>
-          <TR><TD>2192</TD><TD>-999</TD></TR>
-          <TR><TD>2193</TD><TD>-999</TD></TR>
-          <TR><TD>2194</TD><TD>-999</TD></TR>
-          <TR><TD>2195</TD><TD>-999</TD></TR>
-          <TR><TD>2196</TD><TD>-999</TD></TR>
-          <TR><TD>2197</TD><TD>-999</TD></TR>
-          <TR><TD>2198</TD><TD>-999</TD></TR>
-          <TR><TD>2199</TD><TD>-999</TD></TR>
-          <TR><TD>2200</TD><TD>-999</TD></TR>
-          <TR><TD>2201</TD><TD>-999</TD></TR>
-          <TR><TD>2202</TD><TD>-999</TD></TR>
-          <TR><TD>2203</TD><TD>-999</TD></TR>
-          <TR><TD>2204</TD><TD>-999</TD></TR>
-          <TR><TD>2205</TD><TD>-999</TD></TR>
-          <TR><TD>2206</TD><TD>-999</TD></TR>
-          <TR><TD>2207</TD><TD>-999</TD></TR>
-          <TR><TD>2208</TD><TD>-999</TD></TR>
-          <TR><TD>2209</TD><TD>-999</TD></TR>
-          <TR><TD>2210</TD><TD>-999</TD></TR>
-          <TR><TD>2211</TD><TD>-999</TD></TR>
-          <TR><TD>2212</TD><TD>-999</TD></TR>
-          <TR><TD>2213</TD><TD>-999</TD></TR>
-          <TR><TD>2214</TD><TD>-999</TD></TR>
-          <TR><TD>2215</TD><TD>-999</TD></TR>
-          <TR><TD>2216</TD><TD>-999</TD></TR>
-          <TR><TD>2217</TD><TD>-999</TD></TR>
-          <TR><TD>2218</TD><TD>-999</TD></TR>
-          <TR><TD>2219</TD><TD>-999</TD></TR>
-          <TR><TD>2220</TD><TD>-999</TD></TR>
-          <TR><TD>2221</TD><TD>-999</TD></TR>
-          <TR><TD>2222</TD><TD>-999</TD></TR>
-          <TR><TD>2223</TD><TD>-999</TD></TR>
-          <TR><TD>2224</TD><TD>-999</TD></TR>
-          <TR><TD>2225</TD><TD>-999</TD></TR>
-          <TR><TD>2226</TD><TD>-999</TD></TR>
-          <TR><TD>2227</TD><TD>-999</TD></TR>
-          <TR><TD>2228</TD><TD>-999</TD></TR>
-          <TR><TD>2229</TD><TD>-999</TD></TR>
-          <TR><TD>2230</TD><TD>-999</TD></TR>
-          <TR><TD>2231</TD><TD>-999</TD></TR>
-          <TR><TD>2232</TD><TD>-999</TD></TR>
-          <TR><TD>2233</TD><TD>-999</TD></TR>
-          <TR><TD>2234</TD><TD>-999</TD></TR>
-          <TR><TD>2235</TD><TD>-999</TD></TR>
-          <TR><TD>2236</TD><TD>-999</TD></TR>
-          <TR><TD>2237</TD><TD>-999</TD></TR>
-          <TR><TD>2238</TD><TD>-999</TD></TR>
-          <TR><TD>2239</TD><TD>-999</TD></TR>
-          <TR><TD>2240</TD><TD>-999</TD></TR>
-          <TR><TD>2241</TD><TD>-999</TD></TR>
-          <TR><TD>2242</TD><TD>-999</TD></TR>
-          <TR><TD>2243</TD><TD>-999</TD></TR>
-          <TR><TD>2244</TD><TD>-999</TD></TR>
-          <TR><TD>2245</TD><TD>-999</TD></TR>
-          <TR><TD>2246</TD><TD>-999</TD></TR>
-          <TR><TD>2247</TD><TD>-999</TD></TR>
-          <TR><TD>2248</TD><TD>-999</TD></TR>
-          <TR><TD>2249</TD><TD>-999</TD></TR>
-          <TR><TD>2250</TD><TD>-999</TD></TR>
-          <TR><TD>2251</TD><TD>-999</TD></TR>
-          <TR><TD>2252</TD><TD>-999</TD></TR>
-          <TR><TD>2253</TD><TD>-999</TD></TR>
-          <TR><TD>2254</TD><TD>-999</TD></TR>
-          <TR><TD>2255</TD><TD>-999</TD></TR>
-          <TR><TD>2256</TD><TD>-999</TD></TR>
-          <TR><TD>2257</TD><TD>-999</TD></TR>
-          <TR><TD>2258</TD><TD>-999</TD></TR>
-          <TR><TD>2259</TD><TD>-999</TD></TR>
-          <TR><TD>2260</TD><TD>-999</TD></TR>
-          <TR><TD>2261</TD><TD>-999</TD></TR>
-          <TR><TD>2262</TD><TD>-999</TD></TR>
-          <TR><TD>2263</TD><TD>-999</TD></TR>
-          <TR><TD>2264</TD><TD>-999</TD></TR>
-          <TR><TD>2265</TD><TD>-999</TD></TR>
-          <TR><TD>2266</TD><TD>-999</TD></TR>
-          <TR><TD>2267</TD><TD>-999</TD></TR>
-          <TR><TD>2268</TD><TD>-999</TD></TR>
-          <TR><TD>2269</TD><TD>-999</TD></TR>
-          <TR><TD>2270</TD><TD>-999</TD></TR>
-          <TR><TD>2271</TD><TD>-999</TD></TR>
-          <TR><TD>2272</TD><TD>-999</TD></TR>
-          <TR><TD>2273</TD><TD>-999</TD></TR>
-          <TR><TD>2274</TD><TD>-999</TD></TR>
-          <TR><TD>2275</TD><TD>-999</TD></TR>
-          <TR><TD>2276</TD><TD>-999</TD></TR>
-          <TR><TD>2277</TD><TD>-999</TD></TR>
-          <TR><TD>2278</TD><TD>-999</TD></TR>
-          <TR><TD>2279</TD><TD>-999</TD></TR>
-          <TR><TD>2280</TD><TD>-999</TD></TR>
-          <TR><TD>2281</TD><TD>-999</TD></TR>
-          <TR><TD>2282</TD><TD>-999</TD></TR>
-          <TR><TD>2283</TD><TD>-999</TD></TR>
-          <TR><TD>2284</TD><TD>-999</TD></TR>
-          <TR><TD>2285</TD><TD>-999</TD></TR>
-          <TR><TD>2286</TD><TD>-999</TD></TR>
-          <TR><TD>2287</TD><TD>-999</TD></TR>
-          <TR><TD>2288</TD><TD>-999</TD></TR>
-          <TR><TD>2289</TD><TD>-999</TD></TR>
-          <TR><TD>2290</TD><TD>-999</TD></TR>
-          <TR><TD>2291</TD><TD>-999</TD></TR>
-          <TR><TD>2292</TD><TD>-999</TD></TR>
-          <TR><TD>2293</TD><TD>-999</TD></TR>
-          <TR><TD>2294</TD><TD>-999</TD></TR>
-          <TR><TD>2295</TD><TD>-999</TD></TR>
-          <TR><TD>2296</TD><TD>-999</TD></TR>
-          <TR><TD>2297</TD><TD>-999</TD></TR>
-          <TR><TD>2298</TD><TD>-999</TD></TR>
-          <TR><TD>2299</TD><TD>-999</TD></TR>
-          <TR><TD>2300</TD><TD>-999</TD></TR>
-          <TR><TD>2301</TD><TD>-999</TD></TR>
-          <TR><TD>2302</TD><TD>-999</TD></TR>
-          <TR><TD>2303</TD><TD>-999</TD></TR>
-          <TR><TD>2304</TD><TD>-999</TD></TR>
-          <TR><TD>2305</TD><TD>-999</TD></TR>
-          <TR><TD>2306</TD><TD>-999</TD></TR>
-          <TR><TD>2307</TD><TD>-999</TD></TR>
-          <TR><TD>2308</TD><TD>-999</TD></TR>
-          <TR><TD>2309</TD><TD>-999</TD></TR>
-          <TR><TD>2310</TD><TD>-999</TD></TR>
-          <TR><TD>2311</TD><TD>-999</TD></TR>
-          <TR><TD>2312</TD><TD>-999</TD></TR>
-          <TR><TD>2313</TD><TD>-999</TD></TR>
-          <TR><TD>2314</TD><TD>-999</TD></TR>
-          <TR><TD>2315</TD><TD>-999</TD></TR>
-          <TR><TD>2316</TD><TD>-999</TD></TR>
-          <TR><TD>2317</TD><TD>-999</TD></TR>
-          <TR><TD>2318</TD><TD>-999</TD></TR>
-          <TR><TD>2319</TD><TD>-999</TD></TR>
-          <TR><TD>2320</TD><TD>-999</TD></TR>
-          <TR><TD>2321</TD><TD>-999</TD></TR>
-          <TR><TD>2322</TD><TD>-999</TD></TR>
-          <TR><TD>2323</TD><TD>-999</TD></TR>
-          <TR><TD>2324</TD><TD>-999</TD></TR>
-          <TR><TD>2325</TD><TD>-999</TD></TR>
-          <TR><TD>2326</TD><TD>-999</TD></TR>
-          <TR><TD>2327</TD><TD>-999</TD></TR>
-          <TR><TD>2328</TD><TD>-999</TD></TR>
-          <TR><TD>2329</TD><TD>-999</TD></TR>
-          <TR><TD>2330</TD><TD>-999</TD></TR>
-          <TR><TD>2331</TD><TD>-999</TD></TR>
-          <TR><TD>2332</TD><TD>-999</TD></TR>
-          <TR><TD>2333</TD><TD>-999</TD></TR>
-          <TR><TD>2334</TD><TD>-999</TD></TR>
-          <TR><TD>2335</TD><TD>-999</TD></TR>
-          <TR><TD>2336</TD><TD>-999</TD></TR>
-          <TR><TD>2337</TD><TD>-999</TD></TR>
-          <TR><TD>2338</TD><TD>-999</TD></TR>
-          <TR><TD>2339</TD><TD>-999</TD></TR>
-          <TR><TD>2340</TD><TD>-999</TD></TR>
-          <TR><TD>2341</TD><TD>-999</TD></TR>
-          <TR><TD>2342</TD><TD>-999</TD></TR>
-          <TR><TD>2343</TD><TD>-999</TD></TR>
-          <TR><TD>2344</TD><TD>-999</TD></TR>
-          <TR><TD>2345</TD><TD>-999</TD></TR>
-          <TR><TD>2346</TD><TD>-999</TD></TR>
-          <TR><TD>2347</TD><TD>-999</TD></TR>
-          <TR><TD>2348</TD><TD>-999</TD></TR>
-          <TR><TD>2349</TD><TD>-999</TD></TR>
-          <TR><TD>2350</TD><TD>-999</TD></TR>
-          <TR><TD>2351</TD><TD>-999</TD></TR>
-          <TR><TD>2352</TD><TD>-999</TD></TR>
-          <TR><TD>2353</TD><TD>-999</TD></TR>
-          <TR><TD>2354</TD><TD>-999</TD></TR>
-          <TR><TD>2355</TD><TD>-999</TD></TR>
-          <TR><TD>2356</TD><TD>-999</TD></TR>
-          <TR><TD>2357</TD><TD>-999</TD></TR>
-          <TR><TD>2358</TD><TD>-999</TD></TR>
-          <TR><TD>2359</TD><TD>-999</TD></TR>
-          <TR><TD>2360</TD><TD>-999</TD></TR>
-          <TR><TD>2361</TD><TD>-999</TD></TR>
-          <TR><TD>2362</TD><TD>-999</TD></TR>
-          <TR><TD>2363</TD><TD>-999</TD></TR>
-          <TR><TD>2364</TD><TD>-999</TD></TR>
-          <TR><TD>2365</TD><TD>-999</TD></TR>
-          <TR><TD>2366</TD><TD>-999</TD></TR>
-          <TR><TD>2367</TD><TD>-999</TD></TR>
-          <TR><TD>2368</TD><TD>-999</TD></TR>
-          <TR><TD>2369</TD><TD>-999</TD></TR>
-          <TR><TD>2370</TD><TD>-999</TD></TR>
-          <TR><TD>2371</TD><TD>-999</TD></TR>
-          <TR><TD>2372</TD><TD>-999</TD></TR>
-          <TR><TD>2373</TD><TD>-999</TD></TR>
-          <TR><TD>2374</TD><TD>-999</TD></TR>
-          <TR><TD>2375</TD><TD>-999</TD></TR>
-          <TR><TD>2376</TD><TD>-999</TD></TR>
-          <TR><TD>2377</TD><TD>-999</TD></TR>
-          <TR><TD>2378</TD><TD>-999</TD></TR>
-          <TR><TD>2379</TD><TD>-999</TD></TR>
-          <TR><TD>2380</TD><TD>-999</TD></TR>
-          <TR><TD>2381</TD><TD>-999</TD></TR>
-          <TR><TD>2382</TD><TD>-999</TD></TR>
-          <TR><TD>2383</TD><TD>-999</TD></TR>
-          <TR><TD>2384</TD><TD>-999</TD></TR>
-          <TR><TD>2385</TD><TD>-999</TD></TR>
-          <TR><TD>2386</TD><TD>-999</TD></TR>
-          <TR><TD>2387</TD><TD>-999</TD></TR>
-          <TR><TD>2388</TD><TD>-999</TD></TR>
-          <TR><TD>2389</TD><TD>-999</TD></TR>
-          <TR><TD>2390</TD><TD>-999</TD></TR>
-          <TR><TD>2391</TD><TD>-999</TD></TR>
-          <TR><TD>2392</TD><TD>-999</TD></TR>
-          <TR><TD>2393</TD><TD>-999</TD></TR>
-          <TR><TD>2394</TD><TD>-999</TD></TR>
-          <TR><TD>2395</TD><TD>-999</TD></TR>
-          <TR><TD>2396</TD><TD>-999</TD></TR>
-          <TR><TD>2397</TD><TD>-999</TD></TR>
-          <TR><TD>2398</TD><TD>-999</TD></TR>
-          <TR><TD>2399</TD><TD>-999</TD></TR>
-          <TR><TD>2400</TD><TD>-999</TD></TR>
-          <TR><TD>2401</TD><TD>-999</TD></TR>
-          <TR><TD>2402</TD><TD>-999</TD></TR>
-          <TR><TD>2403</TD><TD>-999</TD></TR>
-          <TR><TD>2404</TD><TD>-999</TD></TR>
-          <TR><TD>2405</TD><TD>-999</TD></TR>
-          <TR><TD>2406</TD><TD>-999</TD></TR>
-          <TR><TD>2407</TD><TD>-999</TD></TR>
-          <TR><TD>2408</TD><TD>-999</TD></TR>
-          <TR><TD>2409</TD><TD>-999</TD></TR>
-          <TR><TD>2410</TD><TD>-999</TD></TR>
-          <TR><TD>2411</TD><TD>-999</TD></TR>
-          <TR><TD>2412</TD><TD>-999</TD></TR>
-          <TR><TD>2413</TD><TD>-999</TD></TR>
-          <TR><TD>2414</TD><TD>-999</TD></TR>
-          <TR><TD>2415</TD><TD>-999</TD></TR>
-          <TR><TD>2416</TD><TD>-999</TD></TR>
-          <TR><TD>2417</TD><TD>-999</TD></TR>
-          <TR><TD>2418</TD><TD>-999</TD></TR>
-          <TR><TD>2419</TD><TD>-999</TD></TR>
-          <TR><TD>2420</TD><TD>-999</TD></TR>
-          <TR><TD>2421</TD><TD>-999</TD></TR>
-          <TR><TD>2422</TD><TD>-999</TD></TR>
-          <TR><TD>2423</TD><TD>-999</TD></TR>
-          <TR><TD>2424</TD><TD>-999</TD></TR>
-          <TR><TD>2425</TD><TD>-999</TD></TR>
-          <TR><TD>2426</TD><TD>-999</TD></TR>
-          <TR><TD>2427</TD><TD>-999</TD></TR>
-          <TR><TD>2428</TD><TD>-999</TD></TR>
-          <TR><TD>2429</TD><TD>-999</TD></TR>
-          <TR><TD>2430</TD><TD>-999</TD></TR>
-          <TR><TD>2431</TD><TD>-999</TD></TR>
-          <TR><TD>2432</TD><TD>-999</TD></TR>
-          <TR><TD>2433</TD><TD>-999</TD></TR>
-          <TR><TD>2434</TD><TD>-999</TD></TR>
-          <TR><TD>2435</TD><TD>-999</TD></TR>
-          <TR><TD>2436</TD><TD>-999</TD></TR>
-          <TR><TD>2437</TD><TD>-999</TD></TR>
-          <TR><TD>2438</TD><TD>-999</TD></TR>
-          <TR><TD>2439</TD><TD>-999</TD></TR>
-          <TR><TD>2440</TD><TD>-999</TD></TR>
-          <TR><TD>2441</TD><TD>-999</TD></TR>
-          <TR><TD>2442</TD><TD>-999</TD></TR>
-          <TR><TD>2443</TD><TD>-999</TD></TR>
-          <TR><TD>2444</TD><TD>-999</TD></TR>
-          <TR><TD>2445</TD><TD>-999</TD></TR>
-          <TR><TD>2446</TD><TD>-999</TD></TR>
-          <TR><TD>2447</TD><TD>-999</TD></TR>
-          <TR><TD>2448</TD><TD>-999</TD></TR>
-          <TR><TD>2449</TD><TD>-999</TD></TR>
-          <TR><TD>2450</TD><TD>-999</TD></TR>
-          <TR><TD>2451</TD><TD>-999</TD></TR>
-          <TR><TD>2452</TD><TD>-999</TD></TR>
-          <TR><TD>2453</TD><TD>-999</TD></TR>
-          <TR><TD>2454</TD><TD>-999</TD></TR>
-          <TR><TD>2455</TD><TD>-999</TD></TR>
-          <TR><TD>2456</TD><TD>-999</TD></TR>
-          <TR><TD>2457</TD><TD>-999</TD></TR>
-          <TR><TD>2458</TD><TD>-999</TD></TR>
-          <TR><TD>2459</TD><TD>-999</TD></TR>
-          <TR><TD>2460</TD><TD>-999</TD></TR>
-          <TR><TD>2461</TD><TD>-999</TD></TR>
-          <TR><TD>2462</TD><TD>-999</TD></TR>
-          <TR><TD>2463</TD><TD>-999</TD></TR>
-          <TR><TD>2464</TD><TD>-999</TD></TR>
-          <TR><TD>2465</TD><TD>-999</TD></TR>
-          <TR><TD>2466</TD><TD>-999</TD></TR>
-          <TR><TD>2467</TD><TD>-999</TD></TR>
-          <TR><TD>2468</TD><TD>-999</TD></TR>
-          <TR><TD>2469</TD><TD>-999</TD></TR>
-          <TR><TD>2470</TD><TD>-999</TD></TR>
-          <TR><TD>2471</TD><TD>-999</TD></TR>
-          <TR><TD>2472</TD><TD>-999</TD></TR>
-          <TR><TD>2473</TD><TD>-999</TD></TR>
-          <TR><TD>2474</TD><TD>-999</TD></TR>
-          <TR><TD>2475</TD><TD>-999</TD></TR>
-          <TR><TD>2476</TD><TD>-999</TD></TR>
-          <TR><TD>2477</TD><TD>-999</TD></TR>
-          <TR><TD>2478</TD><TD>-999</TD></TR>
-          <TR><TD>2479</TD><TD>-999</TD></TR>
-          <TR><TD>2480</TD><TD>-999</TD></TR>
-          <TR><TD>2481</TD><TD>-999</TD></TR>
-          <TR><TD>2482</TD><TD>-999</TD></TR>
-          <TR><TD>2483</TD><TD>-999</TD></TR>
-          <TR><TD>2484</TD><TD>-999</TD></TR>
-          <TR><TD>2485</TD><TD>-999</TD></TR>
-          <TR><TD>2486</TD><TD>-999</TD></TR>
-          <TR><TD>2487</TD><TD>-999</TD></TR>
-          <TR><TD>2488</TD><TD>-999</TD></TR>
-          <TR><TD>2489</TD><TD>-999</TD></TR>
-          <TR><TD>2490</TD><TD>-999</TD></TR>
-          <TR><TD>2491</TD><TD>-999</TD></TR>
-          <TR><TD>2492</TD><TD>-999</TD></TR>
-          <TR><TD>2493</TD><TD>-999</TD></TR>
-          <TR><TD>2494</TD><TD>-999</TD></TR>
-          <TR><TD>2495</TD><TD>-999</TD></TR>
-          <TR><TD>2496</TD><TD>-999</TD></TR>
-          <TR><TD>2497</TD><TD>-999</TD></TR>
-          <TR><TD>2498</TD><TD>-999</TD></TR>
-          <TR><TD>2499</TD><TD>-999</TD></TR>
-          <TR><TD>2500</TD><TD>-999</TD></TR>
-          <TR><TD>2501</TD><TD>-999</TD></TR>
-          <TR><TD>2502</TD><TD>-999</TD></TR>
-          <TR><TD>2503</TD><TD>-999</TD></TR>
-          <TR><TD>2504</TD><TD>-999</TD></TR>
-          <TR><TD>2505</TD><TD>-999</TD></TR>
-          <TR><TD>2506</TD><TD>-999</TD></TR>
-          <TR><TD>2507</TD><TD>-999</TD></TR>
-          <TR><TD>2508</TD><TD>-999</TD></TR>
-          <TR><TD>2509</TD><TD>-999</TD></TR>
-          <TR><TD>2510</TD><TD>-999</TD></TR>
-          <TR><TD>2511</TD><TD>-999</TD></TR>
-          <TR><TD>2512</TD><TD>-999</TD></TR>
-          <TR><TD>2513</TD><TD>-999</TD></TR>
-          <TR><TD>2514</TD><TD>-999</TD></TR>
-          <TR><TD>2515</TD><TD>-999</TD></TR>
-          <TR><TD>2516</TD><TD>-999</TD></TR>
-          <TR><TD>2517</TD><TD>-999</TD></TR>
-          <TR><TD>2518</TD><TD>-999</TD></TR>
-          <TR><TD>2519</TD><TD>-999</TD></TR>
-          <TR><TD>2520</TD><TD>-999</TD></TR>
-          <TR><TD>2521</TD><TD>-999</TD></TR>
-          <TR><TD>2522</TD><TD>-999</TD></TR>
-          <TR><TD>2523</TD><TD>-999</TD></TR>
-          <TR><TD>2524</TD><TD>-999</TD></TR>
-          <TR><TD>2525</TD><TD>-999</TD></TR>
-          <TR><TD>2526</TD><TD>-999</TD></TR>
-          <TR><TD>2527</TD><TD>-999</TD></TR>
-          <TR><TD>2528</TD><TD>-999</TD></TR>
-          <TR><TD>2529</TD><TD>-999</TD></TR>
-          <TR><TD>2530</TD><TD>-999</TD></TR>
-          <TR><TD>2531</TD><TD>-999</TD></TR>
-          <TR><TD>2532</TD><TD>-999</TD></TR>
-          <TR><TD>2533</TD><TD>-999</TD></TR>
-          <TR><TD>2534</TD><TD>-999</TD></TR>
-          <TR><TD>2535</TD><TD>-999</TD></TR>
-          <TR><TD>2536</TD><TD>-999</TD></TR>
-          <TR><TD>2537</TD><TD>-999</TD></TR>
-          <TR><TD>2538</TD><TD>-999</TD></TR>
-          <TR><TD>2539</TD><TD>-999</TD></TR>
-          <TR><TD>2540</TD><TD>-999</TD></TR>
-          <TR><TD>2541</TD><TD>-999</TD></TR>
-          <TR><TD>2542</TD><TD>-999</TD></TR>
-          <TR><TD>2543</TD><TD>-999</TD></TR>
-          <TR><TD>2544</TD><TD>-999</TD></TR>
-          <TR><TD>2545</TD><TD>-999</TD></TR>
-          <TR><TD>2546</TD><TD>-999</TD></TR>
-          <TR><TD>2547</TD><TD>-999</TD></TR>
-          <TR><TD>2548</TD><TD>-999</TD></TR>
-          <TR><TD>2549</TD><TD>-999</TD></TR>
-          <TR><TD>2550</TD><TD>-999</TD></TR>
-          <TR><TD>2551</TD><TD>-999</TD></TR>
-          <TR><TD>2552</TD><TD>-999</TD></TR>
-          <TR><TD>2553</TD><TD>-999</TD></TR>
-          <TR><TD>2554</TD><TD>-999</TD></TR>
-          <TR><TD>2555</TD><TD>-999</TD></TR>
-          <TR><TD>2556</TD><TD>-999</TD></TR>
-          <TR><TD>2557</TD><TD>-999</TD></TR>
-          <TR><TD>2558</TD><TD>-999</TD></TR>
-          <TR><TD>2559</TD><TD>-999</TD></TR>
-          <TR><TD>2560</TD><TD>-999</TD></TR>
-          <TR><TD>2561</TD><TD>-999</TD></TR>
-          <TR><TD>2562</TD><TD>-999</TD></TR>
-          <TR><TD>2563</TD><TD>-999</TD></TR>
-          <TR><TD>2564</TD><TD>-999</TD></TR>
-          <TR><TD>2565</TD><TD>-999</TD></TR>
-          <TR><TD>2566</TD><TD>-999</TD></TR>
-          <TR><TD>2567</TD><TD>-999</TD></TR>
-          <TR><TD>2568</TD><TD>-999</TD></TR>
-          <TR><TD>2569</TD><TD>-999</TD></TR>
-          <TR><TD>2570</TD><TD>-999</TD></TR>
-          <TR><TD>2571</TD><TD>-999</TD></TR>
-          <TR><TD>2572</TD><TD>-999</TD></TR>
-          <TR><TD>2573</TD><TD>-999</TD></TR>
-          <TR><TD>2574</TD><TD>-999</TD></TR>
-          <TR><TD>2575</TD><TD>-999</TD></TR>
-          <TR><TD>2576</TD><TD>-999</TD></TR>
-          <TR><TD>2577</TD><TD>-999</TD></TR>
-          <TR><TD>2578</TD><TD>-999</TD></TR>
-          <TR><TD>2579</TD><TD>-999</TD></TR>
-          <TR><TD>2580</TD><TD>-999</TD></TR>
-          <TR><TD>2581</TD><TD>-999</TD></TR>
-          <TR><TD>2582</TD><TD>-999</TD></TR>
-          <TR><TD>2583</TD><TD>-999</TD></TR>
-          <TR><TD>2584</TD><TD>-999</TD></TR>
-          <TR><TD>2585</TD><TD>-999</TD></TR>
-          <TR><TD>2586</TD><TD>-999</TD></TR>
-          <TR><TD>2587</TD><TD>-999</TD></TR>
-          <TR><TD>2588</TD><TD>-999</TD></TR>
-          <TR><TD>2589</TD><TD>-999</TD></TR>
-          <TR><TD>2590</TD><TD>-999</TD></TR>
-          <TR><TD>2591</TD><TD>-999</TD></TR>
-          <TR><TD>2592</TD><TD>-999</TD></TR>
-          <TR><TD>2593</TD><TD>-999</TD></TR>
-          <TR><TD>2594</TD><TD>-999</TD></TR>
-          <TR><TD>2595</TD><TD>-999</TD></TR>
-          <TR><TD>2596</TD><TD>-999</TD></TR>
-          <TR><TD>2597</TD><TD>-999</TD></TR>
-          <TR><TD>2598</TD><TD>-999</TD></TR>
-          <TR><TD>2599</TD><TD>-999</TD></TR>
-          <TR><TD>2600</TD><TD>-999</TD></TR>
-          <TR><TD>2601</TD><TD>-999</TD></TR>
-          <TR><TD>2602</TD><TD>-999</TD></TR>
-          <TR><TD>2603</TD><TD>-999</TD></TR>
-          <TR><TD>2604</TD><TD>-999</TD></TR>
-          <TR><TD>2605</TD><TD>-999</TD></TR>
-          <TR><TD>2606</TD><TD>-999</TD></TR>
-          <TR><TD>2607</TD><TD>-999</TD></TR>
-          <TR><TD>2608</TD><TD>-999</TD></TR>
-          <TR><TD>2609</TD><TD>-999</TD></TR>
-          <TR><TD>2610</TD><TD>-999</TD></TR>
-          <TR><TD>2611</TD><TD>-999</TD></TR>
-          <TR><TD>2612</TD><TD>-999</TD></TR>
-          <TR><TD>2613</TD><TD>-999</TD></TR>
-          <TR><TD>2614</TD><TD>-999</TD></TR>
-          <TR><TD>2615</TD><TD>-999</TD></TR>
-          <TR><TD>2616</TD><TD>-999</TD></TR>
-          <TR><TD>2617</TD><TD>-999</TD></TR>
-          <TR><TD>2618</TD><TD>-999</TD></TR>
-          <TR><TD>2619</TD><TD>-999</TD></TR>
-          <TR><TD>2620</TD><TD>-999</TD></TR>
-          <TR><TD>2621</TD><TD>-999</TD></TR>
-          <TR><TD>2622</TD><TD>-999</TD></TR>
-          <TR><TD>2623</TD><TD>-999</TD></TR>
-          <TR><TD>2624</TD><TD>-999</TD></TR>
-          <TR><TD>2625</TD><TD>-999</TD></TR>
-          <TR><TD>2626</TD><TD>-999</TD></TR>
-          <TR><TD>2627</TD><TD>-999</TD></TR>
-          <TR><TD>2628</TD><TD>-999</TD></TR>
-          <TR><TD>2629</TD><TD>-999</TD></TR>
-          <TR><TD>2630</TD><TD>-999</TD></TR>
-          <TR><TD>2631</TD><TD>-999</TD></TR>
-          <TR><TD>2632</TD><TD>-999</TD></TR>
-          <TR><TD>2633</TD><TD>-999</TD></TR>
-          <TR><TD>2634</TD><TD>-999</TD></TR>
-          <TR><TD>2635</TD><TD>-999</TD></TR>
-          <TR><TD>2636</TD><TD>-999</TD></TR>
-          <TR><TD>2637</TD><TD>-999</TD></TR>
-          <TR><TD>2638</TD><TD>-999</TD></TR>
-          <TR><TD>2639</TD><TD>-999</TD></TR>
-          <TR><TD>2640</TD><TD>-999</TD></TR>
-          <TR><TD>2641</TD><TD>-999</TD></TR>
-          <TR><TD>2642</TD><TD>-999</TD></TR>
-          <TR><TD>2643</TD><TD>-999</TD></TR>
-          <TR><TD>2644</TD><TD>-999</TD></TR>
-          <TR><TD>2645</TD><TD>-999</TD></TR>
-          <TR><TD>2646</TD><TD>-999</TD></TR>
-          <TR><TD>2647</TD><TD>-999</TD></TR>
-          <TR><TD>2648</TD><TD>-999</TD></TR>
-          <TR><TD>2649</TD><TD>-999</TD></TR>
-          <TR><TD>2650</TD><TD>-999</TD></TR>
-          <TR><TD>2651</TD><TD>-999</TD></TR>
-          <TR><TD>2652</TD><TD>-999</TD></TR>
-          <TR><TD>2653</TD><TD>-999</TD></TR>
-          <TR><TD>2654</TD><TD>-999</TD></TR>
-          <TR><TD>2655</TD><TD>-999</TD></TR>
-          <TR><TD>2656</TD><TD>-999</TD></TR>
-          <TR><TD>2657</TD><TD>-999</TD></TR>
-          <TR><TD>2658</TD><TD>-999</TD></TR>
-          <TR><TD>2659</TD><TD>-999</TD></TR>
-          <TR><TD>2660</TD><TD>-999</TD></TR>
-          <TR><TD>2661</TD><TD>-999</TD></TR>
-          <TR><TD>2662</TD><TD>-999</TD></TR>
-          <TR><TD>2663</TD><TD>-999</TD></TR>
-          <TR><TD>2664</TD><TD>-999</TD></TR>
-          <TR><TD>2665</TD><TD>-999</TD></TR>
-          <TR><TD>2666</TD><TD>-999</TD></TR>
-          <TR><TD>2667</TD><TD>-999</TD></TR>
-          <TR><TD>2668</TD><TD>-999</TD></TR>
-          <TR><TD>2669</TD><TD>-999</TD></TR>
-          <TR><TD>2670</TD><TD>-999</TD></TR>
-          <TR><TD>2671</TD><TD>-999</TD></TR>
-          <TR><TD>2672</TD><TD>-999</TD></TR>
-          <TR><TD>2673</TD><TD>-999</TD></TR>
-          <TR><TD>2674</TD><TD>-999</TD></TR>
-          <TR><TD>2675</TD><TD>-999</TD></TR>
-          <TR><TD>2676</TD><TD>-999</TD></TR>
-          <TR><TD>2677</TD><TD>-999</TD></TR>
-          <TR><TD>2678</TD><TD>-999</TD></TR>
-          <TR><TD>2679</TD><TD>-999</TD></TR>
-          <TR><TD>2680</TD><TD>-999</TD></TR>
-          <TR><TD>2681</TD><TD>-999</TD></TR>
-          <TR><TD>2682</TD><TD>-999</TD></TR>
-          <TR><TD>2683</TD><TD>-999</TD></TR>
-          <TR><TD>2684</TD><TD>-999</TD></TR>
-          <TR><TD>2685</TD><TD>-999</TD></TR>
-          <TR><TD>2686</TD><TD>-999</TD></TR>
-          <TR><TD>2687</TD><TD>-999</TD></TR>
-          <TR><TD>2688</TD><TD>-999</TD></TR>
-          <TR><TD>2689</TD><TD>-999</TD></TR>
-          <TR><TD>2690</TD><TD>-999</TD></TR>
-          <TR><TD>2691</TD><TD>-999</TD></TR>
-          <TR><TD>2692</TD><TD>-999</TD></TR>
-          <TR><TD>2693</TD><TD>-999</TD></TR>
-          <TR><TD>2694</TD><TD>-999</TD></TR>
-          <TR><TD>2695</TD><TD>-999</TD></TR>
-          <TR><TD>2696</TD><TD>-999</TD></TR>
-          <TR><TD>2697</TD><TD>-999</TD></TR>
-          <TR><TD>2698</TD><TD>-999</TD></TR>
-          <TR><TD>2699</TD><TD>-999</TD></TR>
-          <TR><TD>2700</TD><TD>-999</TD></TR>
-          <TR><TD>2701</TD><TD>-999</TD></TR>
-          <TR><TD>2702</TD><TD>-999</TD></TR>
-          <TR><TD>2703</TD><TD>-999</TD></TR>
-          <TR><TD>2704</TD><TD>-999</TD></TR>
-          <TR><TD>2705</TD><TD>-999</TD></TR>
-          <TR><TD>2706</TD><TD>-999</TD></TR>
-          <TR><TD>2707</TD><TD>-999</TD></TR>
-          <TR><TD>2708</TD><TD>-999</TD></TR>
-          <TR><TD>2709</TD><TD>-999</TD></TR>
-          <TR><TD>2710</TD><TD>-999</TD></TR>
-          <TR><TD>2711</TD><TD>-999</TD></TR>
-          <TR><TD>2712</TD><TD>-999</TD></TR>
-          <TR><TD>2713</TD><TD>-999</TD></TR>
-          <TR><TD>2714</TD><TD>-999</TD></TR>
-          <TR><TD>2715</TD><TD>-999</TD></TR>
-          <TR><TD>2716</TD><TD>-999</TD></TR>
-          <TR><TD>2717</TD><TD>-999</TD></TR>
-          <TR><TD>2718</TD><TD>-999</TD></TR>
-          <TR><TD>2719</TD><TD>-999</TD></TR>
-          <TR><TD>2720</TD><TD>-999</TD></TR>
-          <TR><TD>2721</TD><TD>-999</TD></TR>
-          <TR><TD>2722</TD><TD>-999</TD></TR>
-          <TR><TD>2723</TD><TD>-999</TD></TR>
-          <TR><TD>2724</TD><TD>-999</TD></TR>
-          <TR><TD>2725</TD><TD>-999</TD></TR>
-          <TR><TD>2726</TD><TD>-999</TD></TR>
-          <TR><TD>2727</TD><TD>-999</TD></TR>
-          <TR><TD>2728</TD><TD>-999</TD></TR>
-          <TR><TD>2729</TD><TD>-999</TD></TR>
-          <TR><TD>2730</TD><TD>-999</TD></TR>
-          <TR><TD>2731</TD><TD>-999</TD></TR>
-          <TR><TD>2732</TD><TD>-999</TD></TR>
-          <TR><TD>2733</TD><TD>-999</TD></TR>
-          <TR><TD>2734</TD><TD>-999</TD></TR>
-          <TR><TD>2735</TD><TD>-999</TD></TR>
-          <TR><TD>2736</TD><TD>-999</TD></TR>
-          <TR><TD>2737</TD><TD>-999</TD></TR>
-          <TR><TD>2738</TD><TD>-999</TD></TR>
-          <TR><TD>2739</TD><TD>-999</TD></TR>
-          <TR><TD>2740</TD><TD>-999</TD></TR>
-          <TR><TD>2741</TD><TD>-999</TD></TR>
-          <TR><TD>2742</TD><TD>-999</TD></TR>
-          <TR><TD>2743</TD><TD>-999</TD></TR>
-          <TR><TD>2744</TD><TD>-999</TD></TR>
-          <TR><TD>2745</TD><TD>-999</TD></TR>
-          <TR><TD>2746</TD><TD>-999</TD></TR>
-          <TR><TD>2747</TD><TD>-999</TD></TR>
-          <TR><TD>2748</TD><TD>-999</TD></TR>
-          <TR><TD>2749</TD><TD>-999</TD></TR>
-          <TR><TD>2750</TD><TD>-999</TD></TR>
-          <TR><TD>2751</TD><TD>-999</TD></TR>
-          <TR><TD>2752</TD><TD>-999</TD></TR>
-          <TR><TD>2753</TD><TD>-999</TD></TR>
-          <TR><TD>2754</TD><TD>-999</TD></TR>
-          <TR><TD>2755</TD><TD>-999</TD></TR>
-          <TR><TD>2756</TD><TD>-999</TD></TR>
-          <TR><TD>2757</TD><TD>-999</TD></TR>
-          <TR><TD>2758</TD><TD>-999</TD></TR>
-          <TR><TD>2759</TD><TD>-999</TD></TR>
-          <TR><TD>2760</TD><TD>-999</TD></TR>
-          <TR><TD>2761</TD><TD>-999</TD></TR>
-          <TR><TD>2762</TD><TD>-999</TD></TR>
-          <TR><TD>2763</TD><TD>-999</TD></TR>
-          <TR><TD>2764</TD><TD>-999</TD></TR>
-          <TR><TD>2765</TD><TD>-999</TD></TR>
-          <TR><TD>2766</TD><TD>-999</TD></TR>
-          <TR><TD>2767</TD><TD>-999</TD></TR>
-          <TR><TD>2768</TD><TD>-999</TD></TR>
-          <TR><TD>2769</TD><TD>-999</TD></TR>
-          <TR><TD>2770</TD><TD>-999</TD></TR>
-          <TR><TD>2771</TD><TD>-999</TD></TR>
-          <TR><TD>2772</TD><TD>-999</TD></TR>
-          <TR><TD>2773</TD><TD>-999</TD></TR>
-          <TR><TD>2774</TD><TD>-999</TD></TR>
-          <TR><TD>2775</TD><TD>-999</TD></TR>
-          <TR><TD>2776</TD><TD>-999</TD></TR>
-          <TR><TD>2777</TD><TD>-999</TD></TR>
-          <TR><TD>2778</TD><TD>-999</TD></TR>
-          <TR><TD>2779</TD><TD>-999</TD></TR>
-          <TR><TD>2780</TD><TD>-999</TD></TR>
-          <TR><TD>2781</TD><TD>-999</TD></TR>
-          <TR><TD>2782</TD><TD>-999</TD></TR>
-          <TR><TD>2783</TD><TD>-999</TD></TR>
-          <TR><TD>2784</TD><TD>-999</TD></TR>
-          <TR><TD>2785</TD><TD>-999</TD></TR>
-          <TR><TD>2786</TD><TD>-999</TD></TR>
-          <TR><TD>2787</TD><TD>-999</TD></TR>
-          <TR><TD>2788</TD><TD>-999</TD></TR>
-          <TR><TD>2789</TD><TD>-999</TD></TR>
-          <TR><TD>2790</TD><TD>-999</TD></TR>
-          <TR><TD>2791</TD><TD>-999</TD></TR>
-          <TR><TD>2792</TD><TD>-999</TD></TR>
-          <TR><TD>2793</TD><TD>-999</TD></TR>
-          <TR><TD>2794</TD><TD>-999</TD></TR>
-          <TR><TD>2795</TD><TD>-999</TD></TR>
-          <TR><TD>2796</TD><TD>-999</TD></TR>
-          <TR><TD>2797</TD><TD>-999</TD></TR>
-          <TR><TD>2798</TD><TD>-999</TD></TR>
-          <TR><TD>2799</TD><TD>-999</TD></TR>
-          <TR><TD>2800</TD><TD>-999</TD></TR>
-          <TR><TD>2801</TD><TD>-999</TD></TR>
-          <TR><TD>2802</TD><TD>-999</TD></TR>
-          <TR><TD>2803</TD><TD>-999</TD></TR>
-          <TR><TD>2804</TD><TD>-999</TD></TR>
-          <TR><TD>2805</TD><TD>-999</TD></TR>
-          <TR><TD>2806</TD><TD>-999</TD></TR>
-          <TR><TD>2807</TD><TD>-999</TD></TR>
-          <TR><TD>2808</TD><TD>-999</TD></TR>
-          <TR><TD>2809</TD><TD>-999</TD></TR>
-          <TR><TD>2810</TD><TD>-999</TD></TR>
-          <TR><TD>2811</TD><TD>-999</TD></TR>
-          <TR><TD>2812</TD><TD>-999</TD></TR>
-          <TR><TD>2813</TD><TD>-999</TD></TR>
-          <TR><TD>2814</TD><TD>-999</TD></TR>
-          <TR><TD>2815</TD><TD>-999</TD></TR>
-          <TR><TD>2816</TD><TD>-999</TD></TR>
-          <TR><TD>2817</TD><TD>-999</TD></TR>
-          <TR><TD>2818</TD><TD>-999</TD></TR>
-          <TR><TD>2819</TD><TD>-999</TD></TR>
-          <TR><TD>2820</TD><TD>-999</TD></TR>
-          <TR><TD>2821</TD><TD>-999</TD></TR>
-          <TR><TD>2822</TD><TD>-999</TD></TR>
-          <TR><TD>2823</TD><TD>-999</TD></TR>
-          <TR><TD>2824</TD><TD>-999</TD></TR>
-          <TR><TD>2825</TD><TD>-999</TD></TR>
-          <TR><TD>2826</TD><TD>-999</TD></TR>
-          <TR><TD>2827</TD><TD>-999</TD></TR>
-          <TR><TD>2828</TD><TD>-999</TD></TR>
-          <TR><TD>2829</TD><TD>-999</TD></TR>
-          <TR><TD>2830</TD><TD>-999</TD></TR>
-          <TR><TD>2831</TD><TD>-999</TD></TR>
-          <TR><TD>2832</TD><TD>-999</TD></TR>
-          <TR><TD>2833</TD><TD>-999</TD></TR>
-          <TR><TD>2834</TD><TD>-999</TD></TR>
-          <TR><TD>2835</TD><TD>-999</TD></TR>
-          <TR><TD>2836</TD><TD>-999</TD></TR>
-          <TR><TD>2837</TD><TD>-999</TD></TR>
-          <TR><TD>2838</TD><TD>-999</TD></TR>
-          <TR><TD>2839</TD><TD>-999</TD></TR>
-          <TR><TD>2840</TD><TD>-999</TD></TR>
-          <TR><TD>2841</TD><TD>-999</TD></TR>
-          <TR><TD>2842</TD><TD>-999</TD></TR>
-          <TR><TD>2843</TD><TD>-999</TD></TR>
-          <TR><TD>2844</TD><TD>-999</TD></TR>
-          <TR><TD>2845</TD><TD>-999</TD></TR>
-          <TR><TD>2846</TD><TD>-999</TD></TR>
-          <TR><TD>2847</TD><TD>-999</TD></TR>
-          <TR><TD>2848</TD><TD>-999</TD></TR>
-          <TR><TD>2849</TD><TD>-999</TD></TR>
-          <TR><TD>2850</TD><TD>-999</TD></TR>
-          <TR><TD>2851</TD><TD>-999</TD></TR>
-          <TR><TD>2852</TD><TD>-999</TD></TR>
-          <TR><TD>2853</TD><TD>-999</TD></TR>
-          <TR><TD>2854</TD><TD>-999</TD></TR>
-          <TR><TD>2855</TD><TD>-999</TD></TR>
-          <TR><TD>2856</TD><TD>-999</TD></TR>
-          <TR><TD>2857</TD><TD>-999</TD></TR>
-          <TR><TD>2858</TD><TD>-999</TD></TR>
-          <TR><TD>2859</TD><TD>-999</TD></TR>
-          <TR><TD>2860</TD><TD>-999</TD></TR>
-          <TR><TD>2861</TD><TD>-999</TD></TR>
-          <TR><TD>2862</TD><TD>-999</TD></TR>
-          <TR><TD>2863</TD><TD>-999</TD></TR>
-          <TR><TD>2864</TD><TD>-999</TD></TR>
-          <TR><TD>2865</TD><TD>-999</TD></TR>
-          <TR><TD>2866</TD><TD>-999</TD></TR>
-          <TR><TD>2867</TD><TD>-999</TD></TR>
-          <TR><TD>2868</TD><TD>-999</TD></TR>
-          <TR><TD>2869</TD><TD>-999</TD></TR>
-          <TR><TD>2870</TD><TD>-999</TD></TR>
-          <TR><TD>2871</TD><TD>-999</TD></TR>
-          <TR><TD>2872</TD><TD>-999</TD></TR>
-          <TR><TD>2873</TD><TD>-999</TD></TR>
-          <TR><TD>2874</TD><TD>-999</TD></TR>
-          <TR><TD>2875</TD><TD>-999</TD></TR>
-          <TR><TD>2876</TD><TD>-999</TD></TR>
-          <TR><TD>2877</TD><TD>-999</TD></TR>
-          <TR><TD>2878</TD><TD>-999</TD></TR>
-          <TR><TD>2879</TD><TD>-999</TD></TR>
-          <TR><TD>2880</TD><TD>-999</TD></TR>
-          <TR><TD>2881</TD><TD>-999</TD></TR>
-          <TR><TD>2882</TD><TD>-999</TD></TR>
-          <TR><TD>2883</TD><TD>-999</TD></TR>
-          <TR><TD>2884</TD><TD>-999</TD></TR>
-          <TR><TD>2885</TD><TD>-999</TD></TR>
-          <TR><TD>2886</TD><TD>-999</TD></TR>
-          <TR><TD>2887</TD><TD>-999</TD></TR>
-          <TR><TD>2888</TD><TD>-999</TD></TR>
-          <TR><TD>2889</TD><TD>-999</TD></TR>
-          <TR><TD>2890</TD><TD>-999</TD></TR>
-          <TR><TD>2891</TD><TD>-999</TD></TR>
-          <TR><TD>2892</TD><TD>-999</TD></TR>
-          <TR><TD>2893</TD><TD>-999</TD></TR>
-          <TR><TD>2894</TD><TD>-999</TD></TR>
-          <TR><TD>2895</TD><TD>-999</TD></TR>
-          <TR><TD>2896</TD><TD>-999</TD></TR>
-          <TR><TD>2897</TD><TD>-999</TD></TR>
-          <TR><TD>2898</TD><TD>-999</TD></TR>
-          <TR><TD>2899</TD><TD>-999</TD></TR>
-          <TR><TD>2900</TD><TD>-999</TD></TR>
-          <TR><TD>2901</TD><TD>-999</TD></TR>
-          <TR><TD>2902</TD><TD>-999</TD></TR>
-          <TR><TD>2903</TD><TD>-999</TD></TR>
-          <TR><TD>2904</TD><TD>-999</TD></TR>
-          <TR><TD>2905</TD><TD>-999</TD></TR>
-          <TR><TD>2906</TD><TD>-999</TD></TR>
-          <TR><TD>2907</TD><TD>-999</TD></TR>
-          <TR><TD>2908</TD><TD>-999</TD></TR>
-          <TR><TD>2909</TD><TD>-999</TD></TR>
-          <TR><TD>2910</TD><TD>-999</TD></TR>
-          <TR><TD>2911</TD><TD>-999</TD></TR>
-          <TR><TD>2912</TD><TD>-999</TD></TR>
-          <TR><TD>2913</TD><TD>-999</TD></TR>
-          <TR><TD>2914</TD><TD>-999</TD></TR>
-          <TR><TD>2915</TD><TD>-999</TD></TR>
-          <TR><TD>2916</TD><TD>-999</TD></TR>
-          <TR><TD>2917</TD><TD>-999</TD></TR>
-          <TR><TD>2918</TD><TD>-999</TD></TR>
-          <TR><TD>2919</TD><TD>-999</TD></TR>
-          <TR><TD>2920</TD><TD>-999</TD></TR>
-          <TR><TD>2921</TD><TD>-999</TD></TR>
-          <TR><TD>2922</TD><TD>-999</TD></TR>
-          <TR><TD>2923</TD><TD>-999</TD></TR>
-          <TR><TD>2924</TD><TD>-999</TD></TR>
-          <TR><TD>2925</TD><TD>-999</TD></TR>
-          <TR><TD>2926</TD><TD>-999</TD></TR>
-          <TR><TD>2927</TD><TD>-999</TD></TR>
-          <TR><TD>2928</TD><TD>-999</TD></TR>
-          <TR><TD>2929</TD><TD>-999</TD></TR>
-          <TR><TD>2930</TD><TD>-999</TD></TR>
-          <TR><TD>2931</TD><TD>-999</TD></TR>
-          <TR><TD>2932</TD><TD>-999</TD></TR>
-          <TR><TD>2933</TD><TD>-999</TD></TR>
-          <TR><TD>2934</TD><TD>-999</TD></TR>
-          <TR><TD>2935</TD><TD>-999</TD></TR>
-          <TR><TD>2936</TD><TD>-999</TD></TR>
-          <TR><TD>2937</TD><TD>-999</TD></TR>
-          <TR><TD>2938</TD><TD>-999</TD></TR>
-          <TR><TD>2939</TD><TD>-999</TD></TR>
-          <TR><TD>2940</TD><TD>-999</TD></TR>
-          <TR><TD>2941</TD><TD>-999</TD></TR>
-          <TR><TD>2942</TD><TD>-999</TD></TR>
-          <TR><TD>2943</TD><TD>-999</TD></TR>
-          <TR><TD>2944</TD><TD>-999</TD></TR>
-          <TR><TD>2945</TD><TD>-999</TD></TR>
-          <TR><TD>2946</TD><TD>-999</TD></TR>
-          <TR><TD>2947</TD><TD>-999</TD></TR>
-          <TR><TD>2948</TD><TD>-999</TD></TR>
-          <TR><TD>2949</TD><TD>-999</TD></TR>
-          <TR><TD>2950</TD><TD>-999</TD></TR>
-          <TR><TD>2951</TD><TD>-999</TD></TR>
-          <TR><TD>2952</TD><TD>-999</TD></TR>
-          <TR><TD>2953</TD><TD>-999</TD></TR>
-          <TR><TD>2954</TD><TD>-999</TD></TR>
-          <TR><TD>2955</TD><TD>-999</TD></TR>
-          <TR><TD>2956</TD><TD>-999</TD></TR>
-          <TR><TD>2957</TD><TD>-999</TD></TR>
-          <TR><TD>2958</TD><TD>-999</TD></TR>
-          <TR><TD>2959</TD><TD>-999</TD></TR>
-          <TR><TD>2960</TD><TD>-999</TD></TR>
-          <TR><TD>2961</TD><TD>-999</TD></TR>
-          <TR><TD>2962</TD><TD>-999</TD></TR>
-          <TR><TD>2963</TD><TD>-999</TD></TR>
-          <TR><TD>2964</TD><TD>-999</TD></TR>
-          <TR><TD>2965</TD><TD>-999</TD></TR>
-          <TR><TD>2966</TD><TD>-999</TD></TR>
-          <TR><TD>2967</TD><TD>-999</TD></TR>
-          <TR><TD>2968</TD><TD>-999</TD></TR>
-          <TR><TD>2969</TD><TD>-999</TD></TR>
-          <TR><TD>2970</TD><TD>-999</TD></TR>
-          <TR><TD>2971</TD><TD>-999</TD></TR>
-          <TR><TD>2972</TD><TD>-999</TD></TR>
-          <TR><TD>2973</TD><TD>-999</TD></TR>
-          <TR><TD>2974</TD><TD>-999</TD></TR>
-          <TR><TD>2975</TD><TD>-999</TD></TR>
-          <TR><TD>2976</TD><TD>-999</TD></TR>
-          <TR><TD>2977</TD><TD>-999</TD></TR>
-          <TR><TD>2978</TD><TD>-999</TD></TR>
-          <TR><TD>2979</TD><TD>-999</TD></TR>
-          <TR><TD>2980</TD><TD>-999</TD></TR>
-          <TR><TD>2981</TD><TD>-999</TD></TR>
-          <TR><TD>2982</TD><TD>-999</TD></TR>
-          <TR><TD>2983</TD><TD>-999</TD></TR>
-          <TR><TD>2984</TD><TD>-999</TD></TR>
-          <TR><TD>2985</TD><TD>-999</TD></TR>
-          <TR><TD>2986</TD><TD>-999</TD></TR>
-          <TR><TD>2987</TD><TD>-999</TD></TR>
-          <TR><TD>2988</TD><TD>-999</TD></TR>
-          <TR><TD>2989</TD><TD>-999</TD></TR>
-          <TR><TD>2990</TD><TD>-999</TD></TR>
-          <TR><TD>2991</TD><TD>-999</TD></TR>
-          <TR><TD>2992</TD><TD>-999</TD></TR>
-          <TR><TD>2993</TD><TD>-999</TD></TR>
-          <TR><TD>2994</TD><TD>-999</TD></TR>
-          <TR><TD>2995</TD><TD>-999</TD></TR>
-          <TR><TD>2996</TD><TD>-999</TD></TR>
-          <TR><TD>2997</TD><TD>-999</TD></TR>
-          <TR><TD>2998</TD><TD>-999</TD></TR>
-          <TR><TD>2999</TD><TD>-999</TD></TR>
-          <TR><TD>3000</TD><TD>-999</TD></TR>
-          <TR><TD>3001</TD><TD>-999</TD></TR>
-          <TR><TD>3002</TD><TD>-999</TD></TR>
-          <TR><TD>3003</TD><TD>-999</TD></TR>
-          <TR><TD>3004</TD><TD>-999</TD></TR>
-          <TR><TD>3005</TD><TD>-999</TD></TR>
-          <TR><TD>3006</TD><TD>-999</TD></TR>
-          <TR><TD>3007</TD><TD>-999</TD></TR>
-          <TR><TD>3008</TD><TD>-999</TD></TR>
-          <TR><TD>3009</TD><TD>-999</TD></TR>
-          <TR><TD>3010</TD><TD>-999</TD></TR>
-          <TR><TD>3011</TD><TD>-999</TD></TR>
-          <TR><TD>3012</TD><TD>-999</TD></TR>
-          <TR><TD>3013</TD><TD>-999</TD></TR>
-          <TR><TD>3014</TD><TD>-999</TD></TR>
-          <TR><TD>3015</TD><TD>-999</TD></TR>
-          <TR><TD>3016</TD><TD>-999</TD></TR>
-          <TR><TD>3017</TD><TD>-999</TD></TR>
-          <TR><TD>3018</TD><TD>-999</TD></TR>
-          <TR><TD>3019</TD><TD>-999</TD></TR>
-          <TR><TD>3020</TD><TD>-999</TD></TR>
-          <TR><TD>3021</TD><TD>-999</TD></TR>
-          <TR><TD>3022</TD><TD>-999</TD></TR>
-          <TR><TD>3023</TD><TD>-999</TD></TR>
-          <TR><TD>3024</TD><TD>-999</TD></TR>
-          <TR><TD>3025</TD><TD>-999</TD></TR>
-          <TR><TD>3026</TD><TD>-999</TD></TR>
-          <TR><TD>3027</TD><TD>-999</TD></TR>
-          <TR><TD>3028</TD><TD>-999</TD></TR>
-          <TR><TD>3029</TD><TD>-999</TD></TR>
-          <TR><TD>3030</TD><TD>-999</TD></TR>
-          <TR><TD>3031</TD><TD>-999</TD></TR>
-          <TR><TD>3032</TD><TD>-999</TD></TR>
-          <TR><TD>3033</TD><TD>-999</TD></TR>
-          <TR><TD>3034</TD><TD>-999</TD></TR>
-          <TR><TD>3035</TD><TD>-999</TD></TR>
-          <TR><TD>3036</TD><TD>-999</TD></TR>
-          <TR><TD>3037</TD><TD>-999</TD></TR>
-          <TR><TD>3038</TD><TD>-999</TD></TR>
-          <TR><TD>3039</TD><TD>-999</TD></TR>
-          <TR><TD>3040</TD><TD>-999</TD></TR>
-          <TR><TD>3041</TD><TD>-999</TD></TR>
-          <TR><TD>3042</TD><TD>-999</TD></TR>
-          <TR><TD>3043</TD><TD>-999</TD></TR>
-          <TR><TD>3044</TD><TD>-999</TD></TR>
-          <TR><TD>3045</TD><TD>-999</TD></TR>
-          <TR><TD>3046</TD><TD>-999</TD></TR>
-          <TR><TD>3047</TD><TD>-999</TD></TR>
-          <TR><TD>3048</TD><TD>-999</TD></TR>
-          <TR><TD>3049</TD><TD>-999</TD></TR>
-          <TR><TD>3050</TD><TD>-999</TD></TR>
-          <TR><TD>3051</TD><TD>-999</TD></TR>
-          <TR><TD>3052</TD><TD>-999</TD></TR>
-          <TR><TD>3053</TD><TD>-999</TD></TR>
-          <TR><TD>3054</TD><TD>-999</TD></TR>
-          <TR><TD>3055</TD><TD>-999</TD></TR>
-          <TR><TD>3056</TD><TD>-999</TD></TR>
-          <TR><TD>3057</TD><TD>-999</TD></TR>
-          <TR><TD>3058</TD><TD>-999</TD></TR>
-          <TR><TD>3059</TD><TD>-999</TD></TR>
-          <TR><TD>3060</TD><TD>-999</TD></TR>
-          <TR><TD>3061</TD><TD>-999</TD></TR>
-          <TR><TD>3062</TD><TD>-999</TD></TR>
-          <TR><TD>3063</TD><TD>-999</TD></TR>
-          <TR><TD>3064</TD><TD>-999</TD></TR>
-          <TR><TD>3065</TD><TD>-999</TD></TR>
-          <TR><TD>3066</TD><TD>-999</TD></TR>
-          <TR><TD>3067</TD><TD>-999</TD></TR>
-          <TR><TD>3068</TD><TD>-999</TD></TR>
-          <TR><TD>3069</TD><TD>-999</TD></TR>
-          <TR><TD>3070</TD><TD>-999</TD></TR>
-          <TR><TD>3071</TD><TD>-999</TD></TR>
-          <TR><TD>3072</TD><TD>-999</TD></TR>
-          <TR><TD>3073</TD><TD>-999</TD></TR>
+          <TR><TD>1</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>4</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>5</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>6</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>7</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>8</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>9</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>10</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>11</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>12</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>13</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>14</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>15</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>16</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>17</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>18</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>19</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>20</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>21</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>22</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>23</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>24</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>25</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>26</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>27</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>28</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>29</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>30</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>31</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>32</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>33</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>34</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>35</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>36</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>37</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>38</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>39</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>40</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>41</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>42</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>43</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>44</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>45</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>46</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>47</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>48</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>49</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>50</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>51</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>52</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>53</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>54</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>55</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>56</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>57</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>58</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>59</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>60</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>61</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>62</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>63</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>64</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>65</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>66</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>67</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>68</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>69</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>70</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>71</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>72</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>73</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>74</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>75</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>76</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>77</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>78</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>79</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>80</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>81</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>82</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>83</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>84</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>85</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>86</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>87</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>88</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>89</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>90</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>91</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>92</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>93</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>94</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>95</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>96</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>97</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>98</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>99</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>100</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>101</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>102</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>103</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>104</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>105</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>106</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>107</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>108</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>109</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>110</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>111</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>112</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>113</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>114</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>115</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>116</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>117</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>118</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>119</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>120</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>121</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>122</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>123</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>124</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>125</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>126</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>127</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>128</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>129</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>130</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>131</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>132</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>133</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>134</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>135</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>136</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>137</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>138</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>139</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>140</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>141</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>142</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>143</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>144</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>145</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>146</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>147</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>148</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>149</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>150</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>151</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>152</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>153</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>154</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>155</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>156</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>157</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>158</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>159</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>160</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>161</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>162</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>163</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>164</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>165</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>166</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>167</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>168</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>169</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>170</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>171</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>172</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>173</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>174</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>175</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>176</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>177</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>178</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>179</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>180</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>181</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>182</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>183</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>184</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>185</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>186</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>187</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>188</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>189</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>190</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>191</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>192</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>193</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>194</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>195</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>196</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>197</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>198</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>199</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>200</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>201</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>202</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>203</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>204</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>205</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>206</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>207</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>208</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>209</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>210</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>211</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>212</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>213</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>214</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>215</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>216</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>217</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>218</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>219</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>220</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>221</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>222</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>223</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>224</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>225</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>226</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>227</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>228</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>229</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>230</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>231</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>232</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>233</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>234</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>235</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>236</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>237</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>238</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>239</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>240</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>241</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>242</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>243</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>244</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>245</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>246</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>247</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>248</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>249</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>250</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>251</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>252</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>253</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>254</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>255</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>256</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>257</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>258</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>259</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>260</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>261</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>262</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>263</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>264</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>265</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>266</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>267</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>268</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>269</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>270</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>271</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>272</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>273</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>274</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>275</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>276</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>277</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>278</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>279</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>280</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>281</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>282</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>283</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>284</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>285</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>286</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>287</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>288</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>289</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>290</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>291</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>292</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>293</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>294</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>295</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>296</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>297</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>298</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>299</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>300</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>301</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>302</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>303</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>304</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>305</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>306</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>307</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>308</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>309</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>310</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>311</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>312</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>313</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>314</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>315</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>316</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>317</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>318</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>319</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>320</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>321</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>322</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>323</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>324</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>325</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>326</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>327</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>328</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>329</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>330</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>331</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>332</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>333</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>334</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>335</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>336</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>337</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>338</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>339</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>340</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>341</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>342</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>343</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>344</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>345</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>346</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>347</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>348</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>349</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>350</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>351</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>352</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>353</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>354</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>355</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>356</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>357</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>358</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>359</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>360</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>361</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>362</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>363</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>364</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>365</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>366</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>367</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>368</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>369</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>370</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>371</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>372</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>373</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>374</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>375</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>376</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>377</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>378</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>379</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>380</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>381</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>382</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>383</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>384</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>385</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>386</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>387</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>388</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>389</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>390</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>391</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>392</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>393</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>394</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>395</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>396</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>397</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>398</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>399</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>400</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>401</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>402</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>403</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>404</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>405</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>406</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>407</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>408</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>409</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>410</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>411</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>412</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>413</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>414</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>415</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>416</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>417</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>418</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>419</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>420</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>421</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>422</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>423</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>424</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>425</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>426</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>427</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>428</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>429</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>430</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>431</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>432</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>433</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>434</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>435</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>436</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>437</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>438</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>439</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>440</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>441</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>442</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>443</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>444</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>445</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>446</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>447</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>448</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>449</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>450</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>451</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>452</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>453</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>454</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>455</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>456</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>457</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>458</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>459</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>460</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>461</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>462</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>463</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>464</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>465</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>466</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>467</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>468</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>469</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>470</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>471</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>472</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>473</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>474</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>475</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>476</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>477</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>478</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>479</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>480</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>481</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>482</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>483</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>484</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>485</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>486</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>487</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>488</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>489</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>490</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>491</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>492</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>493</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>494</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>495</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>496</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>497</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>498</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>499</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>500</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>501</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>502</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>503</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>504</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>505</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>506</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>507</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>508</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>509</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>510</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>511</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>512</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>513</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>514</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>515</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>516</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>517</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>518</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>519</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>520</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>521</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>522</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>523</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>524</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>525</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>526</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>527</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>528</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>529</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>530</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>531</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>532</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>533</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>534</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>535</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>536</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>537</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>538</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>539</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>540</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>541</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>542</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>543</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>544</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>545</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>546</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>547</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>548</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>549</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>550</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>551</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>552</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>553</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>554</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>555</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>556</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>557</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>558</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>559</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>560</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>561</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>562</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>563</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>564</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>565</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>566</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>567</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>568</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>569</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>570</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>571</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>572</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>573</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>574</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>575</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>576</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>577</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>578</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>579</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>580</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>581</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>582</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>583</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>584</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>585</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>586</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>587</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>588</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>589</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>590</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>591</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>592</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>593</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>594</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>595</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>596</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>597</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>598</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>599</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>600</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>601</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>602</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>603</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>604</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>605</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>606</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>607</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>608</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>609</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>610</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>611</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>612</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>613</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>614</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>615</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>616</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>617</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>618</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>619</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>620</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>621</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>622</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>623</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>624</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>625</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>626</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>627</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>628</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>629</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>630</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>631</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>632</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>633</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>634</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>635</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>636</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>637</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>638</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>639</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>640</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>641</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>642</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>643</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>644</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>645</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>646</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>647</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>648</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>649</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>650</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>651</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>652</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>653</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>654</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>655</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>656</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>657</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>658</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>659</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>660</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>661</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>662</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>663</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>664</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>665</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>666</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>667</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>668</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>669</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>670</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>671</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>672</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>673</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>674</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>675</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>676</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>677</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>678</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>679</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>680</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>681</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>682</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>683</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>684</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>685</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>686</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>687</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>688</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>689</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>690</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>691</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>692</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>693</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>694</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>695</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>696</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>697</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>698</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>699</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>700</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>701</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>702</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>703</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>704</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>705</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>706</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>707</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>708</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>709</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>710</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>711</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>712</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>713</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>714</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>715</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>716</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>717</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>718</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>719</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>720</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>721</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>722</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>723</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>724</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>725</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>726</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>727</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>728</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>729</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>730</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>731</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>732</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>733</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>734</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>735</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>736</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>737</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>738</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>739</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>740</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>741</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>742</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>743</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>744</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>745</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>746</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>747</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>748</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>749</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>750</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>751</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>752</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>753</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>754</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>755</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>756</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>757</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>758</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>759</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>760</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>761</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>762</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>763</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>764</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>765</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>766</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>767</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>768</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>769</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>770</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>771</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>772</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>773</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>774</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>775</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>776</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>777</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>778</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>779</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>780</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>781</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>782</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>783</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>784</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>785</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>786</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>787</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>788</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>789</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>790</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>791</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>792</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>793</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>794</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>795</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>796</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>797</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>798</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>799</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>800</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>801</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>802</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>803</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>804</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>805</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>806</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>807</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>808</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>809</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>810</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>811</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>812</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>813</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>814</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>815</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>816</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>817</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>818</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>819</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>820</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>821</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>822</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>823</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>824</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>825</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>826</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>827</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>828</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>829</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>830</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>831</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>832</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>833</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>834</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>835</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>836</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>837</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>838</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>839</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>840</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>841</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>842</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>843</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>844</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>845</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>846</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>847</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>848</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>849</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>850</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>851</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>852</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>853</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>854</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>855</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>856</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>857</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>858</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>859</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>860</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>861</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>862</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>863</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>864</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>865</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>866</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>867</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>868</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>869</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>870</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>871</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>872</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>873</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>874</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>875</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>876</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>877</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>878</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>879</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>880</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>881</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>882</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>883</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>884</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>885</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>886</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>887</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>888</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>889</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>890</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>891</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>892</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>893</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>894</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>895</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>896</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>897</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>898</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>899</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>900</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>901</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>902</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>903</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>904</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>905</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>906</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>907</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>908</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>909</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>910</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>911</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>912</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>913</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>914</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>915</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>916</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>917</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>918</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>919</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>920</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>921</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>922</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>923</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>924</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>925</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>926</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>927</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>928</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>929</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>930</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>931</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>932</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>933</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>934</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>935</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>936</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>937</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>938</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>939</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>940</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>941</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>942</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>943</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>944</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>945</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>946</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>947</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>948</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>949</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>950</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>951</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>952</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>953</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>954</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>955</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>956</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>957</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>958</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>959</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>960</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>961</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>962</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>963</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>964</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>965</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>966</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>967</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>968</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>969</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>970</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>971</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>972</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>973</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>974</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>975</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>976</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>977</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>978</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>979</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>980</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>981</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>982</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>983</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>984</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>985</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>986</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>987</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>988</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>989</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>990</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>991</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>992</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>993</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>994</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>995</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>996</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>997</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>998</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>999</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1000</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1001</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1002</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1003</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1004</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1005</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1006</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1007</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1008</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1009</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1010</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1011</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1012</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1013</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1014</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1015</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1016</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1017</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1018</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1019</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1020</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1021</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1022</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1023</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1024</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1025</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1026</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1027</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1028</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1029</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1030</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1031</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1032</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1033</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1034</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1035</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1036</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1037</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1038</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1039</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1040</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1041</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1042</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1043</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1044</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1045</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1046</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1047</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1048</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1049</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1050</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1051</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1052</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1053</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1054</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1055</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1056</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1057</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1058</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1059</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1060</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1061</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1062</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1063</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1064</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1065</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1066</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1067</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1068</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1069</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1070</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1071</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1072</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1073</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1074</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1075</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1076</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1077</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1078</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1079</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1080</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1081</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1082</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1083</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1084</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1085</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1086</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1087</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1088</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1089</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1090</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1091</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1092</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1093</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1094</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1095</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1096</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1097</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1098</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1099</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1100</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1101</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1102</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1103</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1104</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1105</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1106</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1107</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1108</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1109</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1110</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1111</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1112</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1113</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1114</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1115</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1116</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1117</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1118</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1119</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1120</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1121</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1122</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1123</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1124</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1125</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1126</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1127</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1128</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1129</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1130</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1131</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1132</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1133</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1134</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1135</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1136</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1137</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1138</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1139</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1140</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1141</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1142</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1143</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1144</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1145</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1146</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1147</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1148</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1149</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1150</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1151</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1152</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1153</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1154</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1155</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1156</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1157</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1158</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1159</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1160</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1161</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1162</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1163</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1164</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1165</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1166</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1167</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1168</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1169</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1170</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1171</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1172</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1173</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1174</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1175</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1176</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1177</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1178</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1179</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1180</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1181</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1182</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1183</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1184</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1185</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1186</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1187</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1188</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1189</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1190</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1191</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1192</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1193</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1194</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1195</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1196</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1197</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1198</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1199</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1200</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1201</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1202</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1203</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1204</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1205</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1206</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1207</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1208</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1209</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1210</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1211</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1212</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1213</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1214</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1215</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1216</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1217</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1218</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1219</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1220</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1221</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1222</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1223</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1224</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1225</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1226</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1227</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1228</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1229</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1230</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1231</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1232</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1233</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1234</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1235</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1236</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1237</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1238</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1239</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1240</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1241</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1242</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1243</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1244</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1245</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1246</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1247</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1248</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1249</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1250</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1251</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1252</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1253</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1254</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1255</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1256</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1257</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1258</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1259</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1260</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1261</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1262</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1263</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1264</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1265</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1266</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1267</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1268</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1269</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1270</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1271</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1272</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1273</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1274</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1275</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1276</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1277</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1278</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1279</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1280</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1281</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1282</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1283</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1284</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1285</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1286</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1287</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1288</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1289</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1290</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1291</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1292</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1293</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1294</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1295</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1296</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1297</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1298</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1299</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1300</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1301</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1302</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1303</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1304</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1305</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1306</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1307</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1308</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1309</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1310</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1311</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1312</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1313</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1314</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1315</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1316</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1317</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1318</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1319</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1320</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1321</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1322</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1323</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1324</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1325</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1326</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1327</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1328</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1329</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1330</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1331</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1332</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1333</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1334</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1335</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1336</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1337</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1338</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1339</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1340</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1341</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1342</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1343</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1344</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1345</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1346</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1347</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1348</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1349</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1350</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1351</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1352</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1353</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1354</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1355</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1356</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1357</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1358</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1359</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1360</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1361</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1362</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1363</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1364</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1365</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1366</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1367</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1368</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1369</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1370</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1371</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1372</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1373</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1374</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1375</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1376</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1377</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1378</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1379</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1380</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1381</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1382</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1383</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1384</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1385</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1386</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1387</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1388</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1389</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1390</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1391</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1392</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1393</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1394</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1395</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1396</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1397</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1398</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1399</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1400</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1401</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1402</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1403</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1404</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1405</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1406</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1407</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1408</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1409</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1410</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1411</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1412</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1413</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1414</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1415</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1416</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1417</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1418</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1419</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1420</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1421</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1422</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1423</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1424</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1425</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1426</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1427</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1428</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1429</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1430</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1431</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1432</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1433</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1434</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1435</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1436</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1437</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1438</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1439</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1440</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1441</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1442</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1443</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1444</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1445</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1446</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1447</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1448</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1449</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1450</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1451</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1452</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1453</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1454</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1455</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1456</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1457</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1458</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1459</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1460</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1461</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1462</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1463</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1464</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1465</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1466</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1467</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1468</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1469</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1470</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1471</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1472</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1473</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1474</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1475</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1476</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1477</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1478</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1479</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1480</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1481</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1482</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1483</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1484</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1485</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1486</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1487</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1488</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1489</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1490</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1491</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1492</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1493</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1494</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1495</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1496</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1497</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1498</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1499</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1500</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1501</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1502</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1503</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1504</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1505</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1506</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1507</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1508</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1509</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1510</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1511</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1512</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1513</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1514</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1515</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1516</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1517</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1518</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1519</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1520</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1521</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1522</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1523</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1524</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1525</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1526</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1527</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1528</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1529</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1530</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1531</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1532</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1533</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1534</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1535</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1536</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1537</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1538</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1539</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1540</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1541</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1542</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1543</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1544</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1545</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1546</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1547</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1548</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1549</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1550</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1551</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1552</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1553</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1554</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1555</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1556</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1557</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1558</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1559</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1560</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1561</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1562</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1563</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1564</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1565</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1566</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1567</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1568</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1569</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1570</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1571</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1572</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1573</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1574</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1575</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1576</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1577</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1578</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1579</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1580</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1581</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1582</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1583</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1584</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1585</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1586</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1587</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1588</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1589</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1590</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1591</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1592</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1593</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1594</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1595</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1596</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1597</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1598</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1599</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1600</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1601</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1602</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1603</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1604</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1605</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1606</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1607</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1608</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1609</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1610</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1611</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1612</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1613</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1614</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1615</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1616</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1617</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1618</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1619</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1620</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1621</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1622</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1623</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1624</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1625</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1626</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1627</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1628</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1629</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1630</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1631</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1632</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1633</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1634</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1635</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1636</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1637</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1638</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1639</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1640</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1641</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1642</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1643</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1644</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1645</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1646</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1647</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1648</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1649</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1650</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1651</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1652</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1653</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1654</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1655</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1656</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1657</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1658</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1659</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1660</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1661</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1662</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1663</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1664</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1665</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1666</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1667</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1668</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1669</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1670</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1671</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1672</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1673</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1674</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1675</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1676</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1677</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1678</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1679</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1680</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1681</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1682</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1683</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1684</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1685</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1686</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1687</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1688</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1689</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1690</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1691</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1692</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1693</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1694</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1695</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1696</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1697</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1698</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1699</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1700</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1701</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1702</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1703</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1704</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1705</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1706</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1707</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1708</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1709</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1710</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1711</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1712</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1713</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1714</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1715</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1716</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1717</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1718</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1719</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1720</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1721</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1722</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1723</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1724</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1725</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1726</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1727</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1728</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1729</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1730</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1731</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1732</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1733</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1734</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1735</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1736</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1737</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1738</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1739</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1740</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1741</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1742</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1743</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1744</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1745</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1746</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1747</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1748</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1749</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1750</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1751</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1752</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1753</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1754</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1755</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1756</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1757</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1758</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1759</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1760</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1761</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1762</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1763</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1764</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1765</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1766</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1767</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1768</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1769</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1770</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1771</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1772</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1773</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1774</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1775</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1776</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1777</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1778</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1779</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1780</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1781</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1782</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1783</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1784</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1785</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1786</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1787</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1788</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1789</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1790</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1791</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1792</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1793</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1794</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1795</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1796</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1797</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1798</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1799</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1800</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1801</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1802</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1803</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1804</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1805</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1806</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1807</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1808</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1809</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1810</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1811</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1812</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1813</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1814</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1815</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1816</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1817</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1818</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1819</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1820</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1821</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1822</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1823</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1824</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1825</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1826</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1827</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1828</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1829</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1830</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1831</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1832</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1833</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1834</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1835</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1836</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1837</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1838</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1839</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1840</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1841</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1842</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1843</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1844</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1845</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1846</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1847</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1848</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1849</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1850</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1851</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1852</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1853</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1854</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1855</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1856</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1857</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1858</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1859</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1860</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1861</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1862</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1863</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1864</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1865</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1866</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1867</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1868</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1869</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1870</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1871</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1872</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1873</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1874</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1875</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1876</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1877</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1878</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1879</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1880</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1881</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1882</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1883</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1884</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1885</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1886</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1887</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1888</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1889</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1890</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1891</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1892</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1893</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1894</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1895</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1896</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1897</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1898</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1899</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1900</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1901</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1902</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1903</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1904</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1905</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1906</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1907</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1908</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1909</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1910</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1911</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1912</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1913</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1914</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1915</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1916</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1917</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1918</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1919</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1920</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1921</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1922</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1923</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1924</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1925</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1926</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1927</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1928</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1929</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1930</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1931</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1932</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1933</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1934</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1935</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1936</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1937</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1938</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1939</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1940</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1941</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1942</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1943</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1944</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1945</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1946</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1947</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1948</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1949</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1950</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1951</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1952</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1953</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1954</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1955</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1956</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1957</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1958</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1959</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1960</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1961</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1962</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1963</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1964</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1965</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1966</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1967</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1968</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1969</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1970</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1971</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1972</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1973</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1974</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1975</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1976</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1977</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1978</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1979</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1980</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1981</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1982</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1983</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1984</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1985</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1986</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1987</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1988</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1989</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1990</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1991</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1992</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1993</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1994</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1995</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1996</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1997</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1998</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>1999</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2000</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2001</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2002</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2003</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2004</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2005</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2006</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2007</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2008</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2009</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2010</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2011</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2012</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2013</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2014</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2015</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2016</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2017</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2018</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2019</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2020</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2021</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2022</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2023</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2024</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2025</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2026</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2027</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2028</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2029</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2030</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2031</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2032</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2033</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2034</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2035</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2036</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2037</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2038</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2039</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2040</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2041</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2042</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2043</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2044</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2045</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2046</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2047</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2048</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2049</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2050</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2051</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2052</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2053</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2054</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2055</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2056</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2057</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2058</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2059</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2060</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2061</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2062</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2063</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2064</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2065</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2066</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2067</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2068</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2069</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2070</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2071</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2072</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2073</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2074</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2075</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2076</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2077</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2078</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2079</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2080</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2081</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2082</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2083</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2084</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2085</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2086</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2087</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2088</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2089</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2090</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2091</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2092</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2093</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2094</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2095</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2096</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2097</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2098</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2099</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2100</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2101</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2102</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2103</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2104</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2105</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2106</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2107</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2108</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2109</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2110</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2111</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2112</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2113</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2114</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2115</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2116</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2117</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2118</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2119</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2120</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2121</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2122</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2123</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2124</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2125</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2126</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2127</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2128</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2129</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2130</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2131</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2132</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2133</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2134</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2135</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2136</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2137</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2138</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2139</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2140</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2141</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2142</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2143</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2144</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2145</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2146</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2147</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2148</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2149</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2150</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2151</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2152</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2153</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2154</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2155</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2156</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2157</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2158</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2159</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2160</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2161</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2162</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2163</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2164</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2165</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2166</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2167</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2168</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2169</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2170</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2171</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2172</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2173</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2174</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2175</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2176</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2177</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2178</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2179</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2180</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2181</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2182</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2183</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2184</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2185</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2186</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2187</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2188</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2189</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2190</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2191</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2192</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2193</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2194</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2195</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2196</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2197</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2198</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2199</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2200</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2201</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2202</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2203</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2204</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2205</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2206</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2207</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2208</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2209</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2210</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2211</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2212</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2213</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2214</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2215</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2216</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2217</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2218</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2219</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2220</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2221</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2222</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2223</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2224</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2225</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2226</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2227</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2228</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2229</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2230</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2231</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2232</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2233</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2234</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2235</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2236</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2237</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2238</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2239</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2240</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2241</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2242</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2243</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2244</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2245</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2246</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2247</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2248</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2249</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2250</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2251</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2252</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2253</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2254</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2255</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2256</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2257</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2258</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2259</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2260</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2261</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2262</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2263</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2264</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2265</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2266</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2267</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2268</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2269</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2270</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2271</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2272</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2273</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2274</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2275</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2276</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2277</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2278</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2279</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2280</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2281</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2282</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2283</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2284</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2285</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2286</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2287</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2288</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2289</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2290</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2291</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2292</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2293</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2294</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2295</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2296</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2297</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2298</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2299</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2300</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2301</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2302</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2303</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2304</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2305</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2306</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2307</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2308</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2309</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2310</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2311</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2312</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2313</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2314</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2315</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2316</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2317</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2318</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2319</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2320</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2321</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2322</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2323</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2324</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2325</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2326</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2327</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2328</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2329</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2330</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2331</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2332</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2333</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2334</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2335</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2336</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2337</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2338</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2339</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2340</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2341</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2342</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2343</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2344</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2345</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2346</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2347</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2348</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2349</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2350</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2351</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2352</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2353</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2354</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2355</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2356</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2357</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2358</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2359</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2360</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2361</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2362</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2363</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2364</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2365</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2366</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2367</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2368</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2369</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2370</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2371</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2372</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2373</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2374</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2375</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2376</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2377</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2378</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2379</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2380</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2381</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2382</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2383</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2384</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2385</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2386</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2387</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2388</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2389</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2390</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2391</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2392</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2393</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2394</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2395</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2396</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2397</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2398</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2399</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2400</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2401</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2402</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2403</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2404</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2405</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2406</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2407</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2408</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2409</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2410</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2411</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2412</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2413</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2414</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2415</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2416</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2417</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2418</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2419</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2420</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2421</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2422</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2423</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2424</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2425</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2426</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2427</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2428</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2429</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2430</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2431</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2432</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2433</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2434</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2435</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2436</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2437</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2438</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2439</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2440</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2441</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2442</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2443</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2444</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2445</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2446</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2447</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2448</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2449</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2450</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2451</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2452</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2453</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2454</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2455</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2456</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2457</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2458</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2459</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2460</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2461</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2462</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2463</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2464</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2465</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2466</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2467</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2468</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2469</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2470</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2471</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2472</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2473</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2474</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2475</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2476</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2477</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2478</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2479</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2480</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2481</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2482</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2483</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2484</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2485</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2486</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2487</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2488</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2489</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2490</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2491</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2492</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2493</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2494</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2495</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2496</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2497</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2498</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2499</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2500</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2501</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2502</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2503</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2504</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2505</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2506</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2507</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2508</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2509</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2510</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2511</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2512</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2513</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2514</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2515</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2516</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2517</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2518</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2519</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2520</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2521</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2522</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2523</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2524</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2525</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2526</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2527</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2528</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2529</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2530</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2531</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2532</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2533</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2534</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2535</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2536</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2537</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2538</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2539</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2540</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2541</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2542</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2543</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2544</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2545</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2546</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2547</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2548</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2549</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2550</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2551</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2552</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2553</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2554</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2555</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2556</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2557</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2558</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2559</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2560</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2561</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2562</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2563</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2564</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2565</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2566</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2567</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2568</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2569</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2570</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2571</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2572</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2573</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2574</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2575</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2576</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2577</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2578</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2579</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2580</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2581</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2582</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2583</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2584</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2585</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2586</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2587</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2588</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2589</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2590</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2591</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2592</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2593</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2594</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2595</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2596</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2597</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2598</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2599</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2600</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2601</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2602</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2603</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2604</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2605</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2606</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2607</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2608</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2609</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2610</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2611</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2612</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2613</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2614</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2615</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2616</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2617</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2618</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2619</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2620</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2621</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2622</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2623</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2624</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2625</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2626</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2627</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2628</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2629</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2630</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2631</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2632</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2633</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2634</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2635</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2636</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2637</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2638</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2639</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2640</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2641</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2642</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2643</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2644</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2645</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2646</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2647</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2648</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2649</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2650</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2651</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2652</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2653</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2654</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2655</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2656</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2657</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2658</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2659</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2660</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2661</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2662</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2663</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2664</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2665</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2666</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2667</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2668</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2669</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2670</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2671</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2672</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2673</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2674</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2675</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2676</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2677</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2678</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2679</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2680</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2681</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2682</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2683</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2684</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2685</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2686</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2687</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2688</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2689</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2690</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2691</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2692</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2693</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2694</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2695</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2696</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2697</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2698</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2699</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2700</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2701</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2702</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2703</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2704</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2705</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2706</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2707</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2708</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2709</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2710</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2711</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2712</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2713</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2714</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2715</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2716</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2717</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2718</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2719</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2720</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2721</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2722</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2723</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2724</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2725</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2726</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2727</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2728</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2729</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2730</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2731</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2732</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2733</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2734</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2735</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2736</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2737</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2738</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2739</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2740</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2741</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2742</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2743</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2744</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2745</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2746</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2747</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2748</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2749</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2750</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2751</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2752</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2753</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2754</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2755</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2756</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2757</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2758</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2759</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2760</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2761</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2762</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2763</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2764</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2765</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2766</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2767</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2768</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2769</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2770</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2771</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2772</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2773</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2774</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2775</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2776</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2777</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2778</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2779</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2780</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2781</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2782</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2783</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2784</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2785</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2786</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2787</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2788</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2789</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2790</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2791</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2792</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2793</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2794</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2795</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2796</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2797</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2798</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2799</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2800</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2801</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2802</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2803</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2804</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2805</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2806</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2807</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2808</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2809</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2810</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2811</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2812</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2813</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2814</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2815</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2816</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2817</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2818</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2819</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2820</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2821</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2822</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2823</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2824</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2825</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2826</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2827</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2828</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2829</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2830</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2831</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2832</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2833</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2834</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2835</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2836</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2837</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2838</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2839</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2840</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2841</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2842</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2843</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2844</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2845</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2846</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2847</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2848</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2849</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2850</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2851</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2852</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2853</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2854</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2855</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2856</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2857</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2858</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2859</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2860</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2861</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2862</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2863</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2864</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2865</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2866</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2867</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2868</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2869</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2870</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2871</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2872</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2873</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2874</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2875</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2876</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2877</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2878</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2879</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2880</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2881</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2882</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2883</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2884</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2885</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2886</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2887</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2888</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2889</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2890</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2891</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2892</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2893</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2894</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2895</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2896</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2897</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2898</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2899</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2900</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2901</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2902</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2903</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2904</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2905</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2906</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2907</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2908</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2909</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2910</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2911</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2912</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2913</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2914</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2915</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2916</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2917</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2918</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2919</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2920</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2921</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2922</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2923</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2924</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2925</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2926</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2927</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2928</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2929</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2930</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2931</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2932</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2933</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2934</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2935</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2936</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2937</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2938</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2939</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2940</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2941</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2942</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2943</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2944</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2945</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2946</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2947</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2948</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2949</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2950</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2951</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2952</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2953</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2954</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2955</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2956</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2957</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2958</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2959</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2960</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2961</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2962</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2963</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2964</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2965</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2966</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2967</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2968</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2969</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2970</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2971</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2972</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2973</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2974</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2975</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2976</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2977</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2978</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2979</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2980</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2981</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2982</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2983</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2984</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2985</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2986</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2987</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2988</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2989</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2990</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2991</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2992</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2993</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2994</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2995</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2996</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2997</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2998</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>2999</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3000</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3001</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3002</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3003</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3004</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3005</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3006</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3007</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3008</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3009</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3010</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3011</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3012</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3013</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3014</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3015</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3016</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3017</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3018</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3019</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3020</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3021</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3022</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3023</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3024</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3025</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3026</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3027</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3028</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3029</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3030</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3031</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3032</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3033</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3034</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3035</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3036</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3037</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3038</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3039</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3040</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3041</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3042</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3043</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3044</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3045</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3046</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3047</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3048</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3049</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3050</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3051</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3052</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3053</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3054</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3055</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3056</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3057</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3058</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3059</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3060</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3061</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3062</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3063</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3064</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3065</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3066</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3067</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3068</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3069</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3070</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3071</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3072</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
+          <TR><TD>3073</TD><TD>-999</TD><TD>a type</TD><TD>a comment</TD><TD>-999.0</TD><TD> </TD></TR>
         </TABLEDATA>
       </DATA>
Index: branches/meh_branches/ppstack_test/ippToPsps/config/settings.xml
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/config/settings.xml	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippToPsps/config/settings.xml	(revision 33415)
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- Global database settings for ippToPsps -->
+
+<ippToPsps>
+
+  <!-- path for storing logs -->
+  <logPath>/data/ipp005.0/rhenders</logPath>
+
+  <!-- local scratch Db section -->
+  <localdatabase>
+    <name>ipptopsps_scratch</name>
+    <host>localhost</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </localdatabase>
+
+  <!-- GPC1 Db section -->
+  <gpc1database>
+    <name>gpc1</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </gpc1database>
+
+  <!-- ippToPsps Db section -->
+  <ipptopspsdatabase>
+    <name>ipptopsps</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </ipptopspsdatabase>
+
+  <!-- Czar Db section -->
+  <czardatabase>
+    <name>czardb</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </czardatabase>
+
+</ippToPsps>
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/batch.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/batch.py	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/batch.py	(revision 33415)
@@ -21,5 +21,5 @@
 
 '''
-Base class of all batch types.
+Abstract base class of all batch types.
 '''
 class Batch(object):
@@ -27,25 +27,22 @@
     '''
     Constructor
-
-    >>> batch = Batch(1,2,3,4,5,6,7)
-    >>> print batch.pspsVoTableFilePath
-    "../config/2/tables.vot"
     '''
     def __init__(self, 
                  logger, 
-                 configPath,
-                 doc,
+                 config,
                  gpc1Db,
                  ippToPspsDb,
+                 scratchDb,
                  id,
+                 batchID,
                  batchType, 
-                 fits): 
+                 fits,
+                 useFullTables): 
 
         self.everythingOK = False
         self.readHeader = False
-        self.configPath = configPath
-        self.doc = doc
+        self.config = config
         self.fits = fits
-        if self.fits: self.header = self.fits.getPrimaryHeader()
+        self.useFullTables = useFullTables
 
         # set up logging
@@ -56,55 +53,50 @@
         # set up class variables
         self.id = id
+        self.batchID = batchID
         self.gpc1Db = gpc1Db
         self.ippToPspsDb = ippToPspsDb
+        self.scratchDb = scratchDb
         self.batchType = batchType;
         self.pspsVoTableFilePath = "../config/" + batchType + "/tables.vot"
 
-        # get info from config
-        self.survey = self.doc.find("options/survey").text
-        self.pspsSurvey = self.doc.find("options/pspsSurvey").text
-        dvoGpc1Label = self.doc.find("dvo/gpc1Label").text
-        self.dvoLocation = self.doc.find("dvo/location").text
-        self.useFullTables = int(self.doc.find("dvo/useFullTables").text)
-        self.scratchDb = ScratchDb(logger, self.doc, self.useFullTables)
-
-        if not self.scratchDb.everythingOK: return
+        # check FITS file is ok
+        if self.fits: 
+            self.header = self.fits.getPrimaryHeader()
+
+            # now check that the fits header is readable
+            if not self.header: 
+                logger.errorPair("Could not read FITS for id", "%d" % id)
+                return
+
+        # if no fits file, and this is not an IN batch, then get out of here
+        else:
+           if batchType != "IN": 
+               logger.errorPair("Could not read FITS for id", "%d" % id)
+               return
+
+        self.scratchDb.setUseFullTables(self.useFullTables)
 
         # TODO
         self.tablesToExport = []
 
-        if self.survey != "":
-            self.surveyID = self.scratchDb.getSurveyID(self.survey)
+        if self.config.survey != "":
+            self.surveyID = self.scratchDb.getSurveyID(self.config.survey)
         else:
             self.surveyID = -1;
-
        
-        # get some options from the config
-        self.testMode = int(self.doc.find("options/testMode").text)
-        self.dataRelease = int(self.doc.find("metadata/dataRelease").text)
-
         # create datastore object
-        self.datastore = Datastore(self.logger, self.doc)
-
-        # create a new batch
-        self.batchID = self.ippToPspsDb.createNewBatch( 
-                self.batchType, 
-                self.id,
-                self.survey,
-                dvoGpc1Label, 
-                self.datastore.product)
+        self.datastore = Datastore(self.logger, self.config, self.ippToPspsDb)
 
         # get local storage location from config
         self.batchName = Batch.getNameFromID(self.batchID)
-        basePath = self.doc.find("localOutPath").text
         self.subDir = Batch.getSubDir(
-                basePath,
+                self.config.basePath,
                 self.batchType, 
-                dvoGpc1Label)
+                self.config.dvoLabel)
 
         self.localOutPath = Batch.getOutputPath(
-                basePath,
+                self.config.basePath,
                 self.batchType, 
-                dvoGpc1Label,
+                self.config.dvoLabel,
                 self.batchID)
 
@@ -121,8 +113,5 @@
         self.logger.infoTitle("New " + self.batchType + " batch")
         self.logger.infoPair("Batch name", self.batchName)
-        self.logger.infoPair("Survey", self.survey)
         self.logger.infoPair("Survey ID", "%d" % self.surveyID)
-        self.logger.infoPair("Publishing to PSPS as survey", self.pspsSurvey)
-        self.logger.infoPair("DVO location", self.dvoLocation)
         self.logger.infoBool("Use full DVO tables?", self.useFullTables)
 
@@ -157,4 +146,54 @@
 
     '''
+    Static method to get name of tar file
+    '''
+    @staticmethod
+    def getTarFile(id):
+        return Batch.getNameFromID(id) + ".tar"
+
+    '''
+    Static method to get path of tar file
+    '''
+    @staticmethod
+    def getTarPath(basePath, batchType, dvoLabel, id):
+        return Batch.getSubDir(basePath, batchType, dvoLabel) + "/" + Batch.getTarFile(id)
+
+    '''
+    Static method to get name of tarball file
+    '''
+    @staticmethod
+    def getTarballFile(id):
+        return Batch.getTarFile(id) + ".gz"
+
+    '''
+    Static method to get path of tarball file
+    '''
+    @staticmethod
+    def getTarballPath(basePath, batchType, dvoLabel, id):
+        return Batch.getSubDir(basePath, batchType, dvoLabel) + "/" + Batch.getTarballFile(id)
+
+    '''  
+    Static method to delete this batch from local disk
+    '''
+    @staticmethod
+    def deleteFromDisk(logger, basePath, batchType, dvoLabel, id):
+       
+        tarballPath = Batch.getTarballPath(basePath, batchType, dvoLabel, id)
+        dirPath = Batch.getOutputPath(basePath, batchType, dvoLabel, id)
+
+        if not os.path.exists(tarballPath): 
+            logger.errorPair("Could not find", tarballPath)
+            return 1
+
+        try:
+            os.remove(tarballPath)
+            logger.infoPair("Deleted", tarballPath)
+        except:
+            logger.errorPair("Could not delete", tarballPath)
+            return 0
+   
+        return 1
+
+    '''
     Destructor
     '''
@@ -173,5 +212,5 @@
          if value != "NULL": return value
          else:
-             if not self.testMode: return "NULL"
+             if not self.config.test: return "NULL"
              header[key] = default
              self.logger.infoPair("Hardcoding " + key + " to", header[key])
@@ -194,5 +233,5 @@
 
         outPath = self.localOutPath + "/BatchManifest.xml"
-        tmpPath = "./tmp.xml"
+        tmpPath = self.localOutPath + "/tmp.xml"
         self.logger.infoPair("Creating manifest", outPath)
         root = Element('manifest')
@@ -202,5 +241,5 @@
         root.attrib['type'] = self.batchType
         root.attrib['timestamp'] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") 
-        if self.batchType != "IN": root.attrib['survey'] = self.pspsSurvey
+        if self.batchType != "IN": root.attrib['survey'] = self.config.pspsSurvey
         try: self.minObjID
         except: pass
@@ -210,19 +249,8 @@
         else: root.attrib['maxObjId'] = str(self.maxObjID)
 
-        # get md5sum
-        p = Popen("md5sum " + self.outputFitsPath, shell=True, stdout=PIPE)
-        p.wait()
-        out = p.stdout.read()
-        md5sum = out[0:out.rfind(" ")]
-
-        # get file size
-        fileSize = os.path.getsize(self.outputFitsPath)
-
         # file information
         child = Element('file')
         root.append(child)
         child.attrib['name'] = self.outputFitsFile
-        child.attrib['bytes'] = str(fileSize)
-        child.attrib['md5'] = md5sum
 
         # now create doc and write to file
@@ -240,22 +268,34 @@
     Publishes this batch to the datastore
     '''
-    def publishToDatastore(self):
+    def tarAndZip(self):
       
         # set up filenams and paths
-        tarFile = self.batchName + ".tar"
-        tarPath = self.subDir + "/" + tarFile
-
-        tarballFile = tarFile + ".gz"
-        tarballPath = self.subDir + "/" + tarballFile
+        tarFile = Batch.getTarFile(self.batchID)
+        tarPath = Batch.getTarPath(self.config.basePath, self.batchType, self.config.dvoLabel, self.batchID)
+        tarballFile = Batch.getTarballFile(self.batchID)
+        tarballPath = Batch.getTarballPath(self.config.basePath, self.batchType, self.config.dvoLabel, self.batchID)
 
         # tar directory
-        p = Popen("tar -cvf " + tarPath + "\
-                -C " + self.subDir + " \
-                " + self.batchName, shell=True, stdout=PIPE)
+        cmd = "tar -cvf " + tarPath + " -C " + self.subDir + " " + self.batchName
+        self.logger.infoPair("Creating tar archive", cmd)
+        p = Popen(cmd, shell=True, stdout=PIPE)
         p.wait()
 
+        if p.returncode != 0:
+            self.logger.errorPair("tar command", "failed")
+            return False
+
         # zip tar archive
-        p = Popen("gzip -c " + tarPath + " > " + tarballPath, shell=True, stdout=PIPE)
+        cmd = "gzip -c " + tarPath + " > " + tarballPath
+        self.logger.infoPair("Compressing tar archive", cmd)
+        p = Popen(cmd, shell=True, stdout=PIPE)
         p.wait()
+
+        if p.returncode != 0:
+            self.logger.errorPair("gzip command", "failed")
+            return False
+
+        # only now can we report that the batch has successfully processed
+        self.ippToPspsDb.updateProcessed(self.batchID, 1)
 
         # delete tar file and original directory
@@ -263,7 +303,12 @@
         shutil.rmtree(self.localOutPath)
 
-        # now publish to the datastore
-        if self.datastore.publish(self.batchName, self.subDir, tarballFile, "tgz"):
-            self.ippToPspsDb.updateLoadedToDatastore(self.batchID, 1)
+        return True
+
+    '''
+    Static method to publish a batch to the datastore
+    '''
+    @staticmethod
+    def publishToDatastore(datastore, batchID, batchName, subDir, tarballFile):
+        datastore.publish(batchName, subDir, tarballFile, "tgz")
 
     '''
@@ -293,5 +338,5 @@
             rs.close()
 
-        self.ippToPspsDb.updateMinMaxObjID(self.batchID, self.minObjID, self.maxObjID)
+        self.ippToPspsDb.updateDetectionStats(self.batchID, self.minObjID, self.maxObjID, self.totalDetections)
         self.logger.infoPair("Total detections", "%ld" % self.totalDetections)
         self.logger.infoPair("Min objID", "%ld" % self.minObjID)
@@ -309,5 +354,5 @@
              self.tablesToExport.append(table.name)
 
-         self.alterPspsTables();
+         return self.alterPspsTables();
 
     '''
@@ -329,8 +374,13 @@
     Accepts a regular expression filter so not all tables need to be imported
     '''
-    def importIppTables(self, filter=""):
+    def importIppTables(self, columns="*", filter=""):
 
       self.logger.infoPair("Importing tables with filter", filter)
-      tables = stilts.treads(self.fits.getPath())
+
+      try:
+          tables = stilts.treads(self.fits.getPath())
+      except:
+          self.logger.errorPair("STILTS could not import from", self.fits.getPath())
+          return False
 
       count = 0
@@ -347,4 +397,5 @@
           # IPP FITS files are littered with infinities, so remove these
           self.logger.debug("Removing Infinity values from all columns")
+          table = stilts.tpipe(table, cmd='keepcols "' + columns + '"')
           table = stilts.tpipe(table, cmd='replaceval -Infinity null *')
           table = stilts.tpipe(table, cmd='replaceval Infinity null *')
@@ -396,7 +447,7 @@
         try:
             stilts.twrites(_tables, self.outputFitsPath, fmt='fits')
-            self.ippToPspsDb.updateProcessed(self.batchID, 1)
         except:
             self.logger.exception("Could not write to FITS")
+            self.ippToPspsDb.updateProcessed(self.batchID, -1)
             return False
 
@@ -432,14 +483,13 @@
     def getIDsFromDVO(self):
 
-        if self.scratchDb.getRowCount("dvoMeta") < 1:
-            self.logger.error("No DVO IDs found in dvoMeta")
+        if self.scratchDb.getRowCount(self.scratchDb.dvoImagesTable) < 1:
+            self.logger.errorInfo("No DVO images found in", self.scratchDb.dvoImagesTable)
             return False
 
         # TODO path to DVO prog hardcoded temporarily
-        cmd = "../src/dvograbber " + self.configPath + " " + self.dvoLocation
+        cmd = "../src/dvograbber " + self.config.settingsPath + " " + self.scratchDb.dbName + " " + self.config.dvoLocation
         self.logger.infoPair("Running DVO", cmd)
         p = Popen(cmd, shell=True, stdout=PIPE)
         p.wait()
-        #        out = p.stdout.read()
 
         rowCount = self.scratchDb.getRowCount("dvoDetection")
@@ -453,12 +503,6 @@
 
     '''
-    Checks whether this batch has already been processed and published. To be implemented by all subclasses
-    '''
-    def alreadyProcessed(self):
-        self.logger.warn("alreadyProcessed not implemented")
-
-
-    '''
     Creates and publishes a batch
+    TODO all methods call below should throw exceptions on failure
     '''
     def run(self):
@@ -466,15 +510,27 @@
         if not self.everythingOK:
             self.logger.error("Aborting this batch due to (hopefully) previously reported errors")
+            self.ippToPspsDb.updateProcessed(self.batchID, -1)
             return
 
-        self.createEmptyPspsTables()
+        if not self.createEmptyPspsTables():
+            self.logger.error("Aborting this batch due to (hopefully) previously reported errors")
+            self.ippToPspsDb.updateProcessed(self.batchID, -1)
+            return
+
         self.importIppTables()
-        if self.populatePspsTables():
-            if self.exportPspsTablesToFits():
+        if not self.populatePspsTables():
+            self.logger.errorPair("Aborting this batch", "unable to populate PSPS tables")
+            self.ippToPspsDb.updateProcessed(self.batchID, -1)
+        else:
+            if not self.exportPspsTablesToFits():
+                self.logger.errorPair("Aborting this batch", "unable to export tables to FITS file")
+            else:
                 self.writeBatchManifest()
-                if int(self.doc.find("options/publishToDatastore").text): self.publishToDatastore()
-                if int(self.doc.find("options/reportNulls").text): self.reportNullsInAllPspsTables(False)
-
-        self.logger.infoPair("Batch......", "complete")
+                if self.config.datastorePublishing: 
+                    # tar and zip ready for publication to datastore
+                    if self.tarAndZip():
+                        # now publish to the datastore
+                        tarballFile = Batch.getTarballFile(self.batchID)
+                        Batch.publishToDatastore(self.datastore, self.batchID, self.batchName, self.subDir, tarballFile)
 
 from datastore import Datastore
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/cleanup.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/cleanup.py	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/cleanup.py	(revision 33415)
@@ -6,51 +6,133 @@
 import re
 import sys
+import time
+import logging.config
 
-import logging.config
-from pslogger import PSLogger
-from ipptopspsdb import IppToPspsDb
+from ipptopsps import IppToPsps
 from batch import Batch
+from datastore import Datastore
+from dxlayer import DXLayer
 
 
-if len(sys.argv) > 1: CONFIG  = sys.argv[1]
-else:
-    print "\n** Usage: " + sys.argv[0] + " <configPath>\n"
-    sys.exit(1)
+'''
+Cleanup class
+'''
+class Cleanup(IppToPsps):
 
-# open config file
-configDoc = ElementTree(file=CONFIG)
+    '''
+    Constructor
+    '''
+    def __init__(self, argv):
+        super(Cleanup, self).__init__(argv)
 
-# set up logging
-logging.setLoggerClass(PSLogger)
-logger = logging.getLogger(sys.argv[0])
-logger.setup(configDoc, sys.argv[0])
-
-ippToPspsDb = IppToPspsDb(logger, configDoc)
-
-DVOLABEL = configDoc.find("dvo/gpc1Label").text
-EPOCH = configDoc.find("options/epoch").text
-
-ids = ippToPspsDb.getMergedButNotDeletedBatchIDs(EPOCH, DVOLABEL)
-
-logger.infoPair("Merged but un-deleted batches", "%d" % len(ids))
+        if len(argv) > 2: self.parsePollTimeArg(sys.argv[2]) 
 
 
+    '''
+    Overrides base-class version
+    '''
+    def refreshConfig(self):
+        ret = super(Cleanup, self).refreshConfig()
+        self.dxlayer = DXLayer(self.logger)
+        self.datastore = Datastore(self.logger, self.config, self.ippToPspsDb)
+        return ret
 
-logger.info("+-----------+-------------------------+-------------------------+-------------------------+")
-logger.info("|   Batch   | Removed from datastore  | Removed from local disk |   Removed from DXLayer  |")
-logger.info("+-----------+-------------------------+-------------------------+-------------------------+")
+    '''
+    Runs...
+    '''
+    def run(self):
 
-for id in ids:
+        if self.SECONDS: self.logger.infoPair("Performing clean-up every", "%f hours" % (self.SECONDS/60.0/60.0))
+        while True:
+
+            self.config.printDeletionPolicy()
+            self.clean("P2")
+            self.clean("ST")
+
+            self.checkClientStatus()
+            if not self.waitForPollTime(): break
+   
+    '''
+    Finds stuff to delete for this batch type, then:
+
+    - removes from the datastore
+    - deletes from local disk
+    - deletes files from DXLayer
+
+    '''
+    def clean(self, batchType):
+
+        self.logger.infoSeparator()
+        self.logger.infoTitle("Cleaning up batch type %s" % batchType)
+
+        loadedDatastoreIDs = self.ippToPspsDb.getLoadedToODMButNotDeletedFromDatastore(batchType, self.config.epoch, self.config.dvoLabel)
+        loadedDxlayerIDs = self.ippToPspsDb.getLoadedToODMButNotDeletedFromDXLayer(batchType, self.config.epoch, self.config.dvoLabel)
+        mergedLocalIDs = self.ippToPspsDb.getMergedButNotDeletedFromLocalDisk(batchType, self.config.epoch, self.config.dvoLabel)
+
+        purgedDatastoreIDs = self.ippToPspsDb.getPurgedButNotDeletedFromDatastore(batchType, self.config.epoch, self.config.dvoLabel)
+        purgedLocalIDs = self.ippToPspsDb.getPurgedButNotDeletedFromLocalDisk(batchType, self.config.epoch, self.config.dvoLabel)
+
+        self.logger.infoPair("Loaded to ODM but still on datastore", "%d" % len(loadedDatastoreIDs))
+        self.logger.infoPair("Loaded to ODM but still in DXLayer", "%d" % len(loadedDxlayerIDs))
+        self.logger.infoPair("Merged but still on local disk", "%d" % len(mergedLocalIDs))
+        self.logger.infoPair("Purged but still on datastore", "%d" % len(purgedDatastoreIDs))
+        self.logger.infoPair("Purged but still on local disk", "%d" % len(purgedLocalIDs))
     
-    batchName = Batch.getNameFromID(id)
-    removedFromDatastore = 0
-    removedFromLocalDisk = 0
-    removedFromDXLayer = 0
+        if self.config.deleteLocal:
+            self.logger.infoSeparator()
+            count = 0
+            ids = mergedLocalIDs + purgedLocalIDs
+            for id in ids:
+        
+                if Batch.deleteFromDisk(self.logger, self.config.basePath, batchType, self.config.dvoLabel, id):
+                    self.ippToPspsDb.updateDeletedLocal(id, 1)
+                    count = count + 1
+    
+            self.logger.infoSeparator()
+            self.logger.infoPair("Removed from local disk", "%d" % count)
+            self.logger.infoPair("Remaining on local disk", "%d" % (len(ids) - count))
+        
+    
+        if self.config.deleteDatastore:
+            self.logger.infoSeparator()
+            count = 0
+            ids = loadedDatastoreIDs + purgedDatastoreIDs
+            for id in ids:
+        
+                batchName = Batch.getNameFromID(id)
+    
+                if self.datastore.remove(batchName):
+                    count = count + 1
+    
+            self.logger.infoSeparator()
+            self.logger.infoPair("Removed from datastore", "%d" % count)
+            self.logger.infoPair("Remaining on datastore", "%d" % (len(ids) - count))
+        
+        if self.config.deleteDxLayer:
+            self.logger.infoSeparator()
+            count = 0
+            for id in loadedDxlayerIDs:
+        
+                if self.dxlayer.deleteBatch(id):
+                    self.ippToPspsDb.updateDeletedDxlayer(id, 1)
+                    count = count + 1
+    
+            self.logger.infoSeparator()
+            self.logger.infoPair("Removed from DXLayer", "%d" % count)
+            self.logger.infoPair("Remaining on DXLayer", "%d" % (len(loadedDxlayerIDs) - count))
+       
 
-    logger.info("| %9s | %23d | %23d | %23d |",
-            batchName,
-            removedFromDatastore,
-            removedFromLocalDisk,
-            removedFromDXLayer)
+    '''
+    Overrides base-class version
+    '''
+    def printUsage(self):
+        super(Cleanup, self).printUsage()
+        print " [<poll time in hours>]"
 
-logger.info("+-----------+-------------------------+-------------------------+-------------------------+")
+
+'''
+Start of program
+'''
+cleanup = Cleanup(sys.argv)
+cleanup.run()
+cleanup.exitProgram("finished")
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/config.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/config.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/config.py	(revision 33415)
@@ -0,0 +1,131 @@
+#!/usr/bin/env jython
+
+import logging
+from xml.etree.ElementTree import ElementTree, Element, tostring
+
+from pslogger import PSLogger
+
+
+'''
+A class encapsulating the ippToPsps config file
+'''
+class Config(object):
+
+
+    '''
+    Constructor
+
+    Basically reads the entire config and stores values to class variables
+    '''
+    def __init__(self, programName, name):
+
+        self.programName = programName
+        self.name = name
+        self.settingsPath = "../config/settings.xml" # TODO
+        self.logger = None
+
+        self.settingsDoc = ElementTree(file=self.settingsPath)
+        self.logPath = self.settingsDoc.find("logPath").text
+
+
+        # this is the border (in degrees) that we place around any loading box of 
+        # PS1 pointings to ensure we pull a large enough area out of DVO
+        self.BORDER = 1.6
+        self.isLoaded = False
+
+    '''
+    Prints everything for this config
+    '''
+    def printAll(self):
+
+        try:
+            self.logger.infoSeparator()
+            self.logger.infoPair("Config name", self.name)
+            self.logger.infoPair("Survey", self.survey)
+            self.logger.infoPair("Publishing to PSPS as survey", self.pspsSurvey)
+            self.logger.infoPair("Loading epoch", self.epoch)
+            self.logger.infoPair("Data release", "%d" % self.dataRelease)
+            self.logger.infoBool("Test mode?", self.test)
+            self.logger.infoBool("Forcing?", self.force)
+            self.logger.infoBool("Reporting NULLS?", self.reportNulls)
+            for batchType in self.batchTypes: self.logger.infoPair("Queuing batch type", batchType)
+        except:
+            pass
+
+        self.printDvoInfo()
+        self.printDatastoreInfo()
+        self.printBoxCoords()
+        self.printDeletionPolicy()
+        try: self.logger.infoSeparator()
+        except: pass
+
+
+    '''
+    Creates a logger object and returns it
+    '''
+    def getLogger(self, host, pid, stdout=1, sendToFile=0):
+        
+        logging.setLoggerClass(PSLogger)
+        self.logger = logging.getLogger(self.programName)
+        self.logger.setup(self.programName, self.logPath, self.name, host, pid, stdout, sendToFile)
+
+        return self.logger
+
+    '''
+    Prints the currently set DVO info
+    '''
+    def printDvoInfo(self):
+
+        try:
+            self.logger.infoPair("DVO label", self.dvoLabel)
+            self.logger.infoPair("DVO location", self.dvoLocation)
+        except:
+            pass
+   
+    '''
+    Prints datastore info
+    '''
+    def printDatastoreInfo(self):
+
+        try:
+            self.logger.infoBool("Datastore publishing?", self.datastorePublishing)
+            if self.datastorePublishing:
+                self.logger.infoPair("Datastore type", self.datastoreProduct)
+                self.logger.infoPair("Datastore product", self.datastoreType)
+        except:
+            pass
+   
+    '''
+    Prints the current deletion policy
+    '''
+    def printDeletionPolicy(self):
+
+        try:
+            self.logger.infoBool("Deleting local?", self.deleteLocal)
+            self.logger.infoBool("Deleting datastore?", self.deleteDatastore)
+            self.logger.infoBool("Deleting DXLayer?", self.deleteDxLayer)
+        except:
+            pass
+   
+    '''
+    Prints the currently set RA/Dec bounding box
+    '''
+    def printBoxCoords(self):
+
+        try:
+            self.logger.infoPair("RA limits", "%.1f -> %.1f" % (self.minRa, self.maxRa))
+            self.logger.infoPair("Dec limits", "%.1f -> %.1f" % (self.minDec, self.maxDec))
+            self.logger.infoPair("Loading box size", "%.1f degrees" % self.boxSize)
+        except:
+            pass
+   
+
+    '''
+    Various db metadata getters
+    '''
+    def getDbName(self, dbType): return self.settingsDoc.find(dbType +"/name").text
+    def getDbHost(self, dbType): return self.settingsDoc.find(dbType +"/host").text
+    def getDbUser(self, dbType): return self.settingsDoc.find(dbType +"/user").text
+    def getDbPassword(self, dbType): return self.settingsDoc.find(dbType +"/password").text
+
+
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/configs/init.xml
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/configs/init.xml	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/configs/init.xml	(revision 33415)
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- Global config for ippToPsps -->
+
+<ippToPsps>
+
+  <!-- local scratch Db section -->
+  <localdatabase>
+    <name>ipptopsps_scratch</name>
+    <host>localhost</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </localdatabase>
+
+  <!-- GPC1 Db section -->
+  <gpc1database>
+    <name>gpc1</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </gpc1database>
+
+  <!-- ippToPsps Db section -->
+  <ipptopspsdatabase>
+    <name>ipptopsps</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </ipptopspsdatabase>
+
+  <!-- datastore section -->
+  <datastore>
+    <product>BOGUS</product>
+    <type>BOGUS</type>
+  </datastore>
+
+  <!-- DVO section -->
+  <dvo>
+    <useFullTables>0</useFullTables>
+    <gpc1Label>BOGUS</gpc1Label>
+    <location>BOGUS</location> 
+  </dvo>
+
+  <!-- local storage locations -->
+  <localOutPath>/data/ipp005.0/rhenders</localOutPath>
+
+  <metadata>
+    <dataRelease>0</dataRelease>
+  </metadata>
+
+  <options>
+    <epoch>2011-07-01-15:34</epoch>
+    <survey>3PI</survey>
+    <pspsSurvey>3PI</pspsSurvey>
+    <queueP2>0</queueP2>
+    <queueST>0</queueST>
+    <testMode>0</testMode>
+    <force>0</force>
+    <publishToDatastore>0</publishToDatastore>
+    <reportNulls>0</reportNulls>
+    <!--
+     Put stage IDs here (cam_id or stack_id) to force procesing of particular things
+     eg
+
+     <ids>
+       <id>225707</id>
+       <id>230645</id>
+     </ids>
+
+    -->
+    <ids>
+    </ids>
+  </options>
+
+</ippToPsps>
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/configs/lap20110621.xml
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/configs/lap20110621.xml	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/configs/lap20110621.xml	(revision 33415)
@@ -49,11 +49,18 @@
   </metadata>
 
+  <!-- deletion policy -->
+  <deletion>
+    <local>1</local>
+    <datastore>1</datastore>
+    <dxlayer>1</dxlayer>
+  </deletion>
+
   <options>
     <epoch>2011-07-01-15:34</epoch>
     <survey>3PI</survey>
     <pspsSurvey>3PI</pspsSurvey>
-    <queueP2>0</queueP2>
+    <queueP2>1</queueP2>
     <queueST>1</queueST>
-    <testMode>1</testMode>
+    <testMode>0</testMode>
     <force>1</force>
     <publishToDatastore>0</publishToDatastore>
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/configs/lap20110809.xml
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/configs/lap20110809.xml	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/configs/lap20110809.xml	(revision 33415)
@@ -7,5 +7,5 @@
   <!-- local scratch Db section -->
   <localdatabase>
-    <name>ipptopsps_scratch3</name>
+    <name>ipptopsps_scratch</name>
     <host>localhost</host>
     <user>ipp</user>
@@ -29,7 +29,15 @@
   </ipptopspsdatabase>
 
+  <!-- Czar Db section -->
+  <czardatabase>
+    <name>czardb</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </czardatabase>
+
   <!-- datastore section -->
   <datastore>
-    <product>PSPS_test</product>
+    <product>PSPS_JHU</product>
     <type>IPP_PSPS</type>
   </datastore>
@@ -49,13 +57,20 @@
   </metadata>
 
+  <!-- deletion policy -->
+  <deletion>
+    <local>1</local>
+    <datastore>1</datastore>
+    <dxlayer>1</dxlayer>
+  </deletion>
+
   <options>
     <epoch>2011-08-16</epoch>
     <survey>3PI</survey>
     <pspsSurvey>3PI</pspsSurvey>
-    <queueP2>0</queueP2>
-    <queueST>1</queueST>
-    <testMode>1</testMode>
-    <force>1</force>
-    <publishToDatastore>0</publishToDatastore>
+    <queueP2>1</queueP2>
+    <queueST>0</queueST>
+    <testMode>0</testMode>
+    <force>0</force>
+    <publishToDatastore>1</publishToDatastore>
     <reportNulls>0</reportNulls>
     <!--
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/configs/lap20110809_2.xml
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/configs/lap20110809_2.xml	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/configs/lap20110809_2.xml	(revision 33415)
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- Global config for ippToPsps -->
+
+<ippToPsps>
+
+  <!-- local scratch Db section -->
+  <localdatabase>
+    <name>ipptopsps_scratch2</name>
+    <host>localhost</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </localdatabase>
+
+  <!-- GPC1 Db section -->
+  <gpc1database>
+    <name>gpc1</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </gpc1database>
+
+  <!-- ippToPsps Db section -->
+  <ipptopspsdatabase>
+    <name>ipptopsps</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </ipptopspsdatabase>
+
+  <!-- datastore section -->
+  <datastore>
+    <product>PSPS_JHU</product>
+    <type>IPP_PSPS</type>
+  </datastore>
+
+  <!-- DVO section -->
+  <dvo>
+    <useFullTables>0</useFullTables>
+    <gpc1Label>LAP.ThreePi.20110809</gpc1Label>
+    <location>/data/ipp005.0/gpc1/catdirs/LAP.ThreePi.20110809</location> 
+  </dvo>
+
+  <!-- local storage locations -->
+  <localOutPath>/data/ipp005.0/rhenders</localOutPath>
+
+  <metadata>
+    <dataRelease>0</dataRelease>
+  </metadata>
+
+  <!-- deletion policy -->
+  <deletion>
+    <local>0</local>
+    <datastore>1</datastore>
+    <dxlayer>1</dxlayer>
+  </deletion>
+
+  <options>
+    <epoch>2011-08-16</epoch>
+    <survey>3PI</survey>
+    <pspsSurvey>3PI</pspsSurvey>
+    <queueP2>1</queueP2>
+    <queueST>0</queueST>
+    <testMode>0</testMode>
+    <force>0</force>
+    <publishToDatastore>1</publishToDatastore>
+    <reportNulls>0</reportNulls>
+    <!--
+     Put stage IDs here (cam_id or stack_id) to force procesing of particular things
+     eg
+
+     <ids>
+       <id>225707</id>
+       <id>230645</id>
+     </ids>
+
+    -->
+    <ids>
+    </ids>
+  </options>
+
+</ippToPsps>
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/configs/lap20110809_3.xml
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/configs/lap20110809_3.xml	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/configs/lap20110809_3.xml	(revision 33415)
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- Global config for ippToPsps -->
+
+<ippToPsps>
+
+  <!-- local scratch Db section -->
+  <localdatabase>
+    <name>ipptopsps_scratch3</name>
+    <host>localhost</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </localdatabase>
+
+  <!-- GPC1 Db section -->
+  <gpc1database>
+    <name>gpc1</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </gpc1database>
+
+  <!-- ippToPsps Db section -->
+  <ipptopspsdatabase>
+    <name>ipptopsps</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </ipptopspsdatabase>
+
+  <!-- datastore section -->
+  <datastore>
+    <product>PSPS_JHU</product>
+    <type>IPP_PSPS</type>
+  </datastore>
+
+  <!-- DVO section -->
+  <dvo>
+    <useFullTables>0</useFullTables>
+    <gpc1Label>LAP.ThreePi.20110809</gpc1Label>
+    <location>/data/ipp005.0/gpc1/catdirs/LAP.ThreePi.20110809</location> 
+  </dvo>
+
+  <!-- local storage locations -->
+  <localOutPath>/data/ipp005.0/rhenders</localOutPath>
+
+  <metadata>
+    <dataRelease>0</dataRelease>
+  </metadata>
+
+  <!-- deletion policy -->
+  <deletion>
+    <local>0</local>
+    <datastore>1</datastore>
+    <dxlayer>1</dxlayer>
+  </deletion>
+
+  <options>
+    <epoch>2011-08-16</epoch>
+    <survey>3PI</survey>
+    <pspsSurvey>3PI</pspsSurvey>
+    <queueP2>1</queueP2>
+    <queueST>0</queueST>
+    <testMode>0</testMode>
+    <force>0</force>
+    <publishToDatastore>1</publishToDatastore>
+    <reportNulls>0</reportNulls>
+    <!--
+     Put stage IDs here (cam_id or stack_id) to force procesing of particular things
+     eg
+
+     <ids>
+       <id>225707</id>
+       <id>230645</id>
+     </ids>
+
+    -->
+    <ids>
+    </ids>
+  </options>
+
+</ippToPsps>
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/configs/newmd4.xml
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/configs/newmd4.xml	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/configs/newmd4.xml	(revision 33415)
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- Config for re-load of MD4 survey -->
+
+<ippToPsps>
+
+  <!-- local scratch Db section -->
+  <localdatabase>
+    <name>ipptopsps_scratch</name>
+    <host>localhost</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </localdatabase>
+
+  <!-- GPC1 Db section -->
+  <gpc1database>
+    <name>gpc1</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </gpc1database>
+
+  <!-- ippToPsps Db section -->
+  <ipptopspsdatabase>
+    <name>ipptopsps</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </ipptopspsdatabase>
+
+  <!-- Czar Db section -->
+  <czardatabase>
+    <name>czardb</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </czardatabase>
+
+  <!-- datastore section -->
+  <datastore>
+    <product>PSPS_JHU</product>
+    <type>IPP_PSPS</type>
+  </datastore>
+
+  <!-- DVO section -->
+  <dvo>
+    <useFullTables>1</useFullTables>
+    <gpc1Label>MD04.20111128</gpc1Label>
+    <location>/data/ipp005.0/gpc1/catdirs/MD04.20111128</location> 
+  </dvo>
+
+  <!-- local storage locations -->
+  <localOutPath>/data/ipp005.0/rhenders</localOutPath>
+
+  <metadata>
+    <dataRelease>0</dataRelease>
+  </metadata>
+
+  <!-- deletion policy -->
+  <deletion>
+    <local>1</local>
+    <datastore>1</datastore>
+    <dxlayer>1</dxlayer>
+  </deletion>
+
+  <options>
+    <epoch>2011-12-03</epoch>
+    <survey>MD04</survey>
+    <pspsSurvey>MDF</pspsSurvey>
+    <queueP2>1</queueP2>
+    <queueST>1</queueST>
+    <testMode>0</testMode>
+    <force>0</force>
+    <publishToDatastore>1</publishToDatastore>
+    <reportNulls>0</reportNulls>
+    <!--
+     Put stage IDs here (cam_id or stack_id) to force procesing of particular things
+     eg
+
+     <ids>
+       <id></id>
+       <id>230645</id>
+     </ids>
+
+    -->
+    <ids>
+    </ids>
+  </options>
+
+</ippToPsps>
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/configs/newsas.xml
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/configs/newsas.xml	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/configs/newsas.xml	(revision 33415)
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- Config for re-load of SAS 3-year survey -->
+
+<ippToPsps>
+
+  <!-- local scratch Db section -->
+  <localdatabase>
+    <name>ipptopsps_scratch</name>
+    <host>localhost</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </localdatabase>
+
+  <!-- GPC1 Db section -->
+  <gpc1database>
+    <name>gpc1</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </gpc1database>
+
+  <!-- ippToPsps Db section -->
+  <ipptopspsdatabase>
+    <name>ipptopsps</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </ipptopspsdatabase>
+
+  <!-- Czar Db section -->
+  <czardatabase>
+    <name>czardb</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </czardatabase>
+
+  <!-- datastore section -->
+  <datastore>
+    <product>PSPS_JHU</product>
+    <type>IPP_PSPS</type>
+  </datastore>
+
+  <!-- DVO section -->
+  <dvo>
+    <useFullTables>0</useFullTables>
+    <gpc1Label>SAS.20111201</gpc1Label>
+    <location>/data/ipp005.0/gpc1/catdirs/SAS.20111201</location> 
+  </dvo>
+
+  <!-- local storage locations -->
+  <localOutPath>/data/ipp005.0/rhenders</localOutPath>
+
+  <metadata>
+    <dataRelease>0</dataRelease>
+  </metadata>
+
+  <!-- deletion policy -->
+  <deletion>
+    <local>1</local>
+    <datastore>1</datastore>
+    <dxlayer>1</dxlayer>
+  </deletion>
+
+  <options>
+    <epoch>2011-12-01</epoch>
+    <survey>3PI</survey>
+    <pspsSurvey>SA3</pspsSurvey>
+    <queueP2>1</queueP2>
+    <queueST>1</queueST>
+    <testMode>0</testMode>
+    <force>0</force>
+    <publishToDatastore>1</publishToDatastore>
+    <reportNulls>0</reportNulls>
+    <!--
+     Put stage IDs here (cam_id or stack_id) to force procesing of particular things
+     eg
+
+     <ids>
+       <id></id>
+       <id>230645</id>
+     </ids>
+
+    -->
+    <ids>
+    </ids>
+  </options>
+
+</ippToPsps>
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/configs/oldthreepi.xml
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/configs/oldthreepi.xml	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/configs/oldthreepi.xml	(revision 33415)
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- Global config for ippToPsps -->
+
+<ippToPsps>
+
+  <!-- local scratch Db section -->
+  <localdatabase>
+    <name>ipptopsps_scratch</name>
+    <host>localhost</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </localdatabase>
+
+  <!-- GPC1 Db section -->
+  <gpc1database>
+    <name>gpc1</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </gpc1database>
+
+  <!-- ippToPsps Db section -->
+  <ipptopspsdatabase>
+    <name>ipptopsps</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </ipptopspsdatabase>
+
+  <!-- Czar Db section -->
+  <czardatabase>
+    <name>czardb</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </czardatabase>
+
+  <!-- datastore section -->
+  <datastore>
+    <product>PSPS_JHU</product>
+    <type>IPP_PSPS</type>
+  </datastore>
+
+  <!-- DVO section -->
+  <dvo>
+    <useFullTables>0</useFullTables>
+    <gpc1Label>ThreePi.V3</gpc1Label>
+    <location>/data/ipp005.0/gpc1/catdirs/ThreePi.V3</location> 
+    <minRA>90</minRA>
+    <maxRA>270</maxRA>
+    <minDec>-30</minDec>
+    <maxDec>90</maxDec>
+  </dvo>
+
+  <!-- local storage locations -->
+  <localOutPath>/data/ipp005.0/rhenders</localOutPath>
+
+  <metadata>
+    <dataRelease>0</dataRelease>
+  </metadata>
+
+  <!-- deletion policy -->
+  <deletion>
+    <local>0</local>
+    <datastore>1</datastore>
+    <dxlayer>1</dxlayer>
+  </deletion>
+
+  <options>
+    <epoch>2011-10-27</epoch>
+    <survey>3PI</survey>
+    <pspsSurvey>OLD</pspsSurvey>
+    <queueP2>1</queueP2>
+    <queueST>0</queueST>
+    <testMode>0</testMode>
+    <force>0</force>
+    <publishToDatastore>1</publishToDatastore>
+    <reportNulls>0</reportNulls>
+    <!--
+     Put stage IDs here (cam_id or stack_id) to force procesing of particular things
+     eg
+
+     <ids>
+       <id>225707</id>
+       <id>230645</id>
+     </ids>
+
+    -->
+    <ids>
+    </ids>
+  </options>
+
+</ippToPsps>
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/configs/oldthreepi2.xml
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/configs/oldthreepi2.xml	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/configs/oldthreepi2.xml	(revision 33415)
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- Global config for ippToPsps -->
+
+<ippToPsps>
+
+  <!-- local scratch Db section -->
+  <localdatabase>
+    <name>ipptopsps_scratch2</name>
+    <host>localhost</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </localdatabase>
+
+  <!-- GPC1 Db section -->
+  <gpc1database>
+    <name>gpc1</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </gpc1database>
+
+  <!-- ippToPsps Db section -->
+  <ipptopspsdatabase>
+    <name>ipptopsps</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </ipptopspsdatabase>
+
+  <!-- datastore section -->
+  <datastore>
+    <product>PSPS_JHU</product>
+    <type>IPP_PSPS</type>
+  </datastore>
+
+  <!-- DVO section -->
+  <dvo>
+    <useFullTables>0</useFullTables>
+    <gpc1Label>ThreePi.V3</gpc1Label>
+    <location>/data/ipp005.0/gpc1/catdirs/ThreePi.V3</location> 
+    <minRA>90</minRA>
+    <maxRA>270</maxRA>
+    <minDec>-100</minDec>
+    <maxDec>100</maxDec>
+  </dvo>
+
+  <!-- local storage locations -->
+  <localOutPath>/data/ipp005.0/rhenders</localOutPath>
+
+  <metadata>
+    <dataRelease>0</dataRelease>
+  </metadata>
+
+  <!-- deletion policy -->
+  <deletion>
+    <local>0</local>
+    <datastore>1</datastore>
+    <dxlayer>1</dxlayer>
+  </deletion>
+
+  <options>
+    <epoch>2011-10-27</epoch>
+    <survey>3PI</survey>
+    <pspsSurvey>OLD</pspsSurvey>
+    <queueP2>1</queueP2>
+    <queueST>0</queueST>
+    <testMode>0</testMode>
+    <force>0</force>
+    <publishToDatastore>1</publishToDatastore>
+    <reportNulls>0</reportNulls>
+    <!--
+     Put stage IDs here (cam_id or stack_id) to force procesing of particular things
+     eg
+
+     <ids>
+       <id>225707</id>
+       <id>230645</id>
+     </ids>
+
+    -->
+    <ids>
+    </ids>
+  </options>
+
+</ippToPsps>
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/configs/oldthreepi3.xml
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/configs/oldthreepi3.xml	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/configs/oldthreepi3.xml	(revision 33415)
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- Global config for ippToPsps -->
+
+<ippToPsps>
+
+  <!-- local scratch Db section -->
+  <localdatabase>
+    <name>ipptopsps_scratch3</name>
+    <host>localhost</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </localdatabase>
+
+  <!-- GPC1 Db section -->
+  <gpc1database>
+    <name>gpc1</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </gpc1database>
+
+  <!-- ippToPsps Db section -->
+  <ipptopspsdatabase>
+    <name>ipptopsps</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </ipptopspsdatabase>
+
+  <!-- datastore section -->
+  <datastore>
+    <product>PSPS_JHU</product>
+    <type>IPP_PSPS</type>
+  </datastore>
+
+  <!-- DVO section -->
+  <dvo>
+    <useFullTables>0</useFullTables>
+    <gpc1Label>ThreePi.V3</gpc1Label>
+    <location>/data/ipp005.0/gpc1/catdirs/ThreePi.V3</location> 
+    <minRA>90</minRA>
+    <maxRA>270</maxRA>
+    <minDec>-100</minDec>
+    <maxDec>100</maxDec>
+  </dvo>
+
+  <!-- local storage locations -->
+  <localOutPath>/data/ipp005.0/rhenders</localOutPath>
+
+  <metadata>
+    <dataRelease>0</dataRelease>
+  </metadata>
+
+  <!-- deletion policy -->
+  <deletion>
+    <local>0</local>
+    <datastore>1</datastore>
+    <dxlayer>1</dxlayer>
+  </deletion>
+
+  <options>
+    <epoch>2011-10-27</epoch>
+    <survey>3PI</survey>
+    <pspsSurvey>OLD</pspsSurvey>
+    <queueP2>1</queueP2>
+    <queueST>0</queueST>
+    <testMode>0</testMode>
+    <force>0</force>
+    <publishToDatastore>1</publishToDatastore>
+    <reportNulls>0</reportNulls>
+    <!--
+     Put stage IDs here (cam_id or stack_id) to force procesing of particular things
+     eg
+
+     <ids>
+       <id>225707</id>
+       <id>230645</id>
+     </ids>
+
+    -->
+    <ids>
+    </ids>
+  </options>
+
+</ippToPsps>
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/configs/oldthreepiTest.xml
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/configs/oldthreepiTest.xml	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/configs/oldthreepiTest.xml	(revision 33415)
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- Global config for ippToPsps -->
+
+<ippToPsps>
+
+  <!-- local scratch Db section -->
+  <localdatabase>
+    <name>ipptopsps_scratch</name>
+    <host>localhost</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </localdatabase>
+
+  <!-- GPC1 Db section -->
+  <gpc1database>
+    <name>gpc1</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </gpc1database>
+
+  <!-- ippToPsps Db section -->
+  <ipptopspsdatabase>
+    <name>ipptopsps_test</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </ipptopspsdatabase>
+
+  <!-- Czar Db section -->
+  <czardatabase>
+    <name>czardb</name>
+    <host>ippdb01</host>
+    <user>ipp</user>
+    <password>ipp</password>
+  </czardatabase>
+
+  <!-- datastore section -->
+  <datastore>
+    <product>PSPS_JHU</product>
+    <type>IPP_PSPS</type>
+  </datastore>
+
+  <!-- DVO section -->
+  <dvo>
+    <useFullTables>0</useFullTables>
+    <gpc1Label>ThreePi.V3</gpc1Label>
+    <location>/data/ipp005.0/gpc1/catdirs/ThreePi.V3</location> 
+    <minRA>90</minRA>
+    <maxRA>270</maxRA>
+    <minDec>-30</minDec>
+    <maxDec>90</maxDec>
+  </dvo>
+
+  <!-- local storage locations -->
+  <localOutPath>/data/ipp005.0/rhenders/test</localOutPath>
+
+  <metadata>
+    <dataRelease>0</dataRelease>
+  </metadata>
+
+  <!-- deletion policy -->
+  <deletion>
+    <local>0</local>
+    <datastore>1</datastore>
+    <dxlayer>1</dxlayer>
+  </deletion>
+
+  <options>
+    <epoch>2011-10-27</epoch>
+    <survey>3PI</survey>
+    <pspsSurvey>OLD</pspsSurvey>
+    <queueP2>1</queueP2>
+    <queueST>0</queueST>
+    <testMode>1</testMode>
+    <force>0</force>
+    <publishToDatastore>0</publishToDatastore>
+    <reportNulls>0</reportNulls>
+    <!--
+     Put stage IDs here (cam_id or stack_id) to force procesing of particular things
+     eg
+
+     <ids>
+       <id>225707</id>
+       <id>230645</id>
+     </ids>
+
+    -->
+    <ids>
+      <id>72203</id>
+    </ids>
+  </options>
+
+</ippToPsps>
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/configs/sas12.xml
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/configs/sas12.xml	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/configs/sas12.xml	(revision 33415)
@@ -49,4 +49,11 @@
   </metadata>
 
+  <!-- deletion policy -->
+  <deletion>
+    <local>1</local>
+    <datastore>1</datastore>
+    <dxlayer>1</dxlayer>
+  </deletion>
+
   <options>
     <epoch>2011-08-09</epoch>
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/configs/sas123.xml
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/configs/sas123.xml	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/configs/sas123.xml	(revision 33415)
@@ -49,4 +49,11 @@
   </metadata>
 
+  <!-- deletion policy -->
+  <deletion>
+    <local>1</local>
+    <datastore>1</datastore>
+    <dxlayer>1</dxlayer>
+  </deletion>
+
   <options>
     <epoch>2011-08-09</epoch>
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/configs/sasFootprint.xml
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/configs/sasFootprint.xml	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/configs/sasFootprint.xml	(revision 33415)
@@ -49,4 +49,11 @@
   </metadata>
 
+  <!-- deletion policy -->
+  <deletion>
+    <local>1</local>
+    <datastore>1</datastore>
+    <dxlayer>1</dxlayer>
+  </deletion>
+
   <options>
     <epoch>2011-08-09</epoch>
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/czardb.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/czardb.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/czardb.py	(revision 33415)
@@ -0,0 +1,50 @@
+#!/usr/bin/env jython
+
+import re
+import sys
+import os
+import logging
+
+from mysql import MySql
+from java.sql import *
+
+'''
+Class for CzarDb database connectivity
+'''
+class CzarDb(MySql):
+
+    '''
+    Constructor
+    '''
+    def __init__(self, logger, config):
+        super(CzarDb, self).__init__(logger, config, "czardatabase")
+
+
+    '''
+    Updates a stage table with pending, processed, faults and label
+    '''
+    def insertStats(self, stage, label, batchType, pending, processed, faults):
+
+        sql = "INSERT INTO " + stage + " \
+               (label, pending, processed, faults) \
+               VALUES ('" + label + "_" + batchType + "', \
+                       " + str(pending) + ", \
+                       " + str(processed) + ", \
+                       " + str(faults) + ")"
+
+        try:
+            self.execute(sql)
+        except:
+            pass
+
+    '''
+    Destructor
+    '''
+    def __del__(self):
+
+        self.logger.debug("CzarDb Desstructor")
+        self.stmt.close()
+        self.con.close()
+
+
+
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/datastore.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/datastore.py	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/datastore.py	(revision 33415)
@@ -5,5 +5,8 @@
 import logging
 import sys
+import re
 from xml.etree.ElementTree import ElementTree, Element, tostring
+
+from ipptopspsdb import IppToPspsDb
 
 '''
@@ -16,16 +19,10 @@
 
     '''
-    def __init__(self, logger, doc):
+    def __init__(self, logger, config, ippToPspsDb):
     
         # setup logger
         self.logger = logger
-        self.doc = doc
-        self.logger.debug("Datastore constructor")
-
-        # open config
-        self.product = doc.find("datastore/product").text
-        self.type = doc.find("datastore/type").text
-
-        self.logger.debug("Using product: '" + self.product + "' and type: '" + self.type + "'")
+        self.config = config
+        self.ippToPspsDb = ippToPspsDb
 
     '''
@@ -34,4 +31,7 @@
     def publish(self, name, path, file, fileType):
 
+        # TODO should handle this better
+        batchID = int(name[1:])
+
         self.logger.infoPair("Attempting to publish",  path + "/" + file)
         tempFile = tempfile.NamedTemporaryFile(mode='w+b')
@@ -39,9 +39,9 @@
         tempFile.seek(0)
 
-        # build dsreg command command
+        # build dsreg command
         command  = "dsreg --add " + name + "\
-                    --copy --datapath " + path + "\
-                    --type " + self.type + "\
-                    --product " + self.product + "\
+                    --link --datapath " + path + "\
+                    --type " + self.config.datastoreType + "\
+                    --product " + self.config.datastoreProduct + "\
                     --list " + tempFile.name
 
@@ -52,32 +52,45 @@
             self.logger.errorPair("Datastore publish",  "failed")
             ret = False
+            self.ippToPspsDb.updateLoadedToDatastore(batchID, -1)
         else:
             self.logger.infoPair("Datastore publish", "successful")
             ret = True
+            self.ippToPspsDb.updateLoadedToDatastore(batchID, 1)
             
+        tempFile.close()
 
-        tempFile.close()
         return ret
-
 
     '''
     Removes an item from the datastore
+
+    NB this will return a success if the batch is not found on the datastore
     '''
     def remove(self, name):
+
+        # TODO should handle this better
+        batchID = int(name[1:])
 
         command = "dsreg \
                    --del " + name + " \
                    --rm \
-                   --product " + self.product
+                   --product " + self.config.datastoreProduct
 
-        p = Popen(command, shell=True, stdout=PIPE)
+        p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE)
         p.wait()
+        output, errors = p.communicate()
 
         if p.returncode != 0:
-            self.logger.errorPair("Datastore removal failed",  name)
-            ret = False
+            if re.search("not found in", errors):
+                self.logger.infoPair("Datastore batch not found", name)
+                ret = True
+                self.ippToPspsDb.updateDeletedDatastore(batchID, 1)
+            else:
+                self.logger.infoPair("Datastore removal FAILED", name)
+                ret = False
         else:
+            self.logger.infoPair("Datastore removal successful", name)
             ret = True
-            self.logger.infoPair("Datastore removal successful", name)
+            self.ippToPspsDb.updateDeletedDatastore(batchID, 1)
             
         return ret
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/datastoreDupeChecker.sh
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/datastoreDupeChecker.sh	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/datastoreDupeChecker.sh	(revision 33415)
@@ -0,0 +1,11 @@
+rm index.txt
+wget http://datastore.ipp.ifa.hawaii.edu/PSPS_JHU/index.txt
+
+echo "Dupes:"
+echo ""
+
+more index.txt | awk '{print $1}' | uniq -d
+echo ""
+echo "Done"
+
+#wget http://datastore.ipp.ifa.hawaii.edu/PSPS_JHU/B00263471/index.txt
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/detectionbatch.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/detectionbatch.py	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/detectionbatch.py	(revision 33415)
@@ -22,4 +22,7 @@
 '''
 DetectionBatch class
+
+This class, a sub-class of Batch, processes single exposures in the form of IPP smf files
+
 '''
 class DetectionBatch(Batch):
@@ -30,19 +33,23 @@
     def __init__(self, 
                  logger,
-                 configPath,
-                 configDoc,
+                 config,
                  gpc1Db,
                  ippToPspsDb,
-                 camID):
+                 scratchDb,
+                 camID,
+                 batchID,
+                 useFullTables):
 
        super(DetectionBatch, self).__init__(
                logger,
-               configPath,
-               configDoc,
+               config,
                gpc1Db,
                ippToPspsDb,
+               scratchDb,
                camID,
+               batchID,
                "P2", 
-               gpc1Db.getCameraStageSmf(camID))
+               gpc1Db.getCameraStageSmf(camID),
+               useFullTables)
 
        if not self.everythingOK: return
@@ -50,4 +57,8 @@
        # get camera meta data
        meta = self.gpc1Db.getCameraStageMeta(self.id)
+       if not meta:
+           self.everythingOK = False
+           return
+
        self.expID = meta[0];
        self.expName = meta[1];
@@ -64,5 +75,5 @@
 
        # if test mode
-       if self.testMode:
+       if self.config.test:
            self.startX = 3
            self.endX = 4
@@ -101,4 +112,5 @@
 
        self.filter = self.header['FILTERID'][0:1]
+       self.filterID = self.scratchDb.getFilterID(self.filter) 
 
        # insert what we know about this stack batch into the stack table
@@ -209,5 +221,5 @@
         self.scratchDb.updateFilterID("FrameMeta", self.filter)
         self.scratchDb.updateAllRows("FrameMeta", "calibModNum", str(self.calibModNum))
-        self.scratchDb.updateAllRows("FrameMeta", "dataRelease", str(self.dataRelease))
+        self.scratchDb.updateAllRows("FrameMeta", "dataRelease", str(self.config.dataRelease))
 
     '''
@@ -312,5 +324,5 @@
                ," + self.safeDictionaryAccess(header, 'DAPMIFIT') + " \
                ,'" + self.safeDictionaryAccess(header, 'DETECTOR') + "' \
-               ," + str(self.scratchDb.getDvoImageFlags(header['SOURCEID'], header['IMAGEID'])) + " \
+               ," + str(self.scratchDb.getDvoImageFlags(header['IMAGEID'])) + " \
                ,'" + self.safeDictionaryAccess(header, 'DETREND.MASK') + "' \
                ,'" + self.safeDictionaryAccess(header, 'DETREND.DARK') + "' \
@@ -356,5 +368,5 @@
         self.scratchDb.updateFilterID(tableName, self.filter)
         self.scratchDb.updateAllRows(tableName, "calibModNum", str(self.calibModNum))
-        self.scratchDb.updateAllRows(tableName, "dataRelease", str(self.dataRelease))
+        self.scratchDb.updateAllRows(tableName, "dataRelease", str(self.config.dataRelease))
         if 'NASTRO' in header: self.totalNumPhotoRef = self.totalNumPhotoRef + int(header['NASTRO'])
         self.scratchDb.replaceNullsInThisColumn(tableName, "polyOrder", "0")
@@ -367,5 +379,7 @@
         pspsTableName = "Detection_" + ota
         ippTableName = ota + "_psf"
-        
+       
+        results['ORIGINALTOTAL'] = self.scratchDb.getRowCount(ippTableName)
+
         # drop then re-create table
         self.scratchDb.dropTable(pspsTableName)
@@ -385,4 +399,7 @@
         sql = "INSERT IGNORE INTO " + pspsTableName + " ( \
                ippDetectID \
+               ,filterID \
+               ,surveyID \
+               ,obsTime \
                ,xPos \
                ,yPos \
@@ -395,4 +412,5 @@
                ,psfWidMinor \
                ,psfTheta \
+               ,psfLikelihood \
                ,psfCf \
                ,momentXX \
@@ -400,13 +418,18 @@
                ,momentYY \
                ,apMag \
-               ,kronFlux \
-               ,kronFluxErr \
                ,infoFlag \
                ,sky \
                ,skyErr \
                ,sgSep \
+               ,activeFlag \
+               ,assocDate \
+               ,historyModNum \
+               ,dataRelease \
                ) \
                SELECT \
                IPP_IDET \
+               , " + str(self.filterID) + "\
+               , " + str(self.surveyID) + " \
+               ," + str(self.obsTime) + " \
                ,X_PSF \
                ,Y_PSF \
@@ -419,4 +442,5 @@
                ,PSF_MINOR \
                ,PSF_THETA \
+               ,psfLikelihood(EXT_NSIGMA) \
                ,PSF_QF \
                ,MOMENTS_XX \
@@ -424,23 +448,26 @@
                ,MOMENTS_YY \
                ,AP_MAG \
-               ,KRON_FLUX \
-               ,KRON_FLUX_ERR \
                ,FLAGS\
                ,SKY \
                ,SKY_SIGMA \
                ,EXT_NSIGMA \
+               , 0 \
+               , '" + self.dateStr + "' \
+               , 0 \
+               , " + str(self.config.dataRelease) + "\
                FROM " + ippTableName
         self.scratchDb.execute(sql)
 
-        # set obsTime
-        sql = "UPDATE " + pspsTableName + " SET obsTime = %f, assocDate = '%s', activeFlag = 0" % (self.obsTime, self.dateStr)
-        self.scratchDb.execute(sql)
-        self.scratchDb.updateAllRows(pspsTableName, "dataRelease", str(self.dataRelease))
-        self.scratchDb.updateAllRows(pspsTableName, "historyModNum", "0")
-
-        self.scratchDb.updateAllRows(pspsTableName, "surveyID", str(self.surveyID))
-        self.scratchDb.updateFilterID(pspsTableName, self.filter)
-
         # now delete bad flux and bad chip positions
+        sql="DELETE FROM " + pspsTableName + " WHERE instFlux < 0.0000001" # TODO clearly a hack = 0 not allowed for instFlux
+        self.scratchDb.execute(sql)
+
+        # update cosmic ray and extended likelihoods
+        sql="UPDATE " + pspsTableName + " SET extendedLikelihood = 0, crLikelihood = 1.0 - psfLikelihood WHERE sgSep <= 0"
+        self.scratchDb.execute(sql)
+        sql="UPDATE " + pspsTableName + " SET crLikelihood = 0, extendedLikelihood = 1.0 - psfLikelihood WHERE sgSep > 0"
+        self.scratchDb.execute(sql)
+
+        # remove detections will NULL inst flux or NULL peak ADU
         results['NULLINSTFLUX'] = self.scratchDb.reportAndDeleteRowsWithNULLS(pspsTableName, "instFlux")
         results['NULLPEAKADU'] = self.scratchDb.reportAndDeleteRowsWithNULLS(pspsTableName, "peakADU")
@@ -472,5 +499,5 @@
         self.scratchDb.execute(sql)
 
-        self.scratchDb.updateAllRows(tableName, "dataRelease", str(self.dataRelease))
+        self.scratchDb.updateAllRows(tableName, "dataRelease", str(self.config.dataRelease))
 
     '''
@@ -501,5 +528,5 @@
 
         self.scratchDb.updateAllRows(tableName, "calibModNum", str(self.calibModNum))
-        self.scratchDb.updateAllRows(tableName, "dataRelease", str(self.dataRelease))
+        self.scratchDb.updateAllRows(tableName, "dataRelease", str(self.config.dataRelease))
 
 
@@ -514,82 +541,8 @@
         self.scratchDb.makeColumnPrimaryKey("Detection", "ippDetectID")
 
-    '''
-    Applies indexes to the IPP tables
-    '''
-    def indexIppTables(self):
-
-        self.logger.infoPair("Creating indexes on", "IPP tables")
-
-        for x in range(self.startX, self.endX):
-            for y in range(self.startY, self.endY):
-
-                # dodge the corners
-                if x==0 and y==0: continue
-                if x==0 and y==7: continue
-                if x==7 and y==0: continue
-                if x==7 and y==7: continue
-
-                extension = "XY%d%d_psf" % (x, y)
-                self.scratchDb.createIndex(extension, "IPP_IDET")
-
-    '''
-    Updates provided table with DVO IDs from DVO table
-    '''
-    def updateDvoIDs(self, table, sourceID, externID):
-
-        imageID = self.scratchDb.getImageIDFromExternID(sourceID, externID)
-        self.logger.debug("Updating table '" + table + "' with DVO IDs using imageID = %d" % imageID)
-        sql = "UPDATE IGNORE " + table + " AS a, " + self.scratchDb.dvoDetectionTable + " AS b SET \
-               a.ippObjID = b.ippObjID, \
-               a.detectID = b.detectID, \
-               a.objID = b.objID, \
-               a.infoFlag = b.flags << 32 | a.infoFlag \
-               WHERE a.ippDetectID = b.ippDetectID \
-               AND b.sourceID = " + str(sourceID) + " \
-               AND b.imageID = " + str(imageID)
-
-        self.scratchDb.execute(sql)
-
-    '''
-    Generates psf, cosmic ray and extended source likelihoods
-    '''
-    def populateLikelihoods(self, tableName):
-
-        sql = "SELECT ippDetectID, psfLikelihood, crLikelihood, extendedLikelihood, sgSep FROM " + tableName
-        rs = self.scratchDb.executeUpdatableQuery(sql)
-
-        sqrt2 = Math.sqrt(2)
-
-        while rs.next():
-
-            sgSep = rs.getDouble(5)
-            psfLikelihood = Erf.erfc(Math.abs(sgSep)/sqrt2)
-        
-            if sgSep > 0:
-                crLikelihood = 0
-                extendedLikelihood = 1.0 - psfLikelihood
-            else:
-                crLikelihood = 1.0 - psfLikelihood
-                extendedLikelihood = 0
-
-            # update columns
-            rs.updateDouble(2, psfLikelihood )
-            rs.updateDouble(3, crLikelihood )
-            rs.updateDouble(4, extendedLikelihood )
-
-            # now 'commit' to database
-            rs.updateRow();
-
-
-    '''
-    Does the processing, i.e. pulling stuff from IPP tables into PSPS tables
-    '''
-    def populatePspsTables(self):
-
         self.populateFrameMeta()
      
-        # dictionary objects to hold sourceIDs and imageIDs for later
-        sourceIDs = {}
-        imageIDs = {}
+        # dictionary objects to hold imageIDs for later
+        self.imageIDs = {}
 
         # loop through all OTAs and populate ImageMeta extensions
@@ -614,8 +567,6 @@
 
                 # check we have valid sourceID/imageID pair from the header
-                if self.safeDictionaryAccess(header, 'SOURCEID') == "NULL":
-                    continue
-                if self.safeDictionaryAccess(header, 'IMAGEID') == "NULL":
-                    continue
+                if self.safeDictionaryAccess(header, 'SOURCEID') == "NULL": continue
+                if self.safeDictionaryAccess(header, 'IMAGEID') == "NULL": continue
 
                 # store sourceID/imageID combo in Db so DVO can look up later
@@ -624,6 +575,5 @@
 
                 # store these for later
-                sourceIDs[ota] = header['SOURCEID']
-                imageIDs[ota] = header['IMAGEID']
+                self.imageIDs[ota] = header['IMAGEID']
 
                 # populate ImageMeta
@@ -632,5 +582,51 @@
              
         # now run DVO code to get all IDs
-        if not self.useFullTables: self.getIDsFromDVO()
+        if not self.useFullTables: 
+            if not self.getIDsFromDVO(): return False
+
+        return True
+
+
+    '''
+    Applies indexes to the IPP tables
+    '''
+    def indexIppTables(self):
+
+        self.logger.infoPair("Creating indexes on", "IPP tables")
+
+        for x in range(self.startX, self.endX):
+            for y in range(self.startY, self.endY):
+
+                # dodge the corners
+                if x==0 and y==0: continue
+                if x==0 and y==7: continue
+                if x==7 and y==0: continue
+                if x==7 and y==7: continue
+
+                extension = "XY%d%d_psf" % (x, y)
+                self.scratchDb.createIndex(extension, "IPP_IDET")
+
+    '''
+    Updates provided table with DVO IDs from DVO table
+    '''
+    def updateDvoIDs(self, table, externID):
+
+        imageID = self.scratchDb.getImageIDFromExternID(externID)
+        self.logger.debug("Updating table '" + table + "' with DVO IDs using imageID = %d" % imageID)
+        sql = "UPDATE IGNORE " + table + " AS a, " + self.scratchDb.dvoDetectionTable + " AS b SET \
+               a.ippObjID = b.ippObjID, \
+               a.detectID = b.detectID, \
+               a.objID = b.objID, \
+               a.infoFlag = b.flags << 32 | a.infoFlag \
+               WHERE a.ippDetectID = b.ippDetectID \
+               AND b.imageID = " + str(imageID)
+
+        self.scratchDb.execute(sql)
+
+
+    '''
+    Does the processing, i.e. pulling stuff from IPP tables into PSPS tables
+    '''
+    def populatePspsTables(self):
 
         # loop through all OTAs again to update with DVO IDs
@@ -640,7 +636,8 @@
         otaCount = 0
         results = {}
-        self.logger.info("+-------+---------------+---------------+---------------+---------------+---------------+")
-        self.logger.info("|  OTA  |   Sat Det     | NULL instFlux | NULL peak ADU | NULL obj ID   |  Remainder    |")
-        self.logger.info("+-------+---------------+---------------+---------------+---------------+---------------+")
+        totalOriginal = totalSatDet = totalNulIInstFlux = totalNullPeakFlux = totalNullObjID = totalDetections = 0
+        self.logger.info("+-------+---------------+---------------+---------------+---------------+---------------+---------------+")
+        self.logger.info("|  OTA  | Initial total |   Sat Det     | NULL instFlux | NULL peak ADU | NULL obj ID   |  Remainder    |")
+        self.logger.info("+-------+---------------+---------------+---------------+---------------+---------------+---------------+")
         for x in range(self.startX, self.endX):
             for y in range(self.startY, self.endY):
@@ -653,7 +650,10 @@
 
                 ota = "XY%d%d" % (x, y)
-                if ota not in sourceIDs: continue
+                if ota not in self.imageIDs: continue
 
                 #self.logger.infoTitle("Processing " + ota)
+                if not self.scratchDb.astrometricSolutionOK(ota):
+                    self.logger.info("| %5s |            ------------- Bad astrometric solution : rejecting ------------                    |" % ota)
+                    continue
 
                 # populate remainder of tables
@@ -661,11 +661,11 @@
 
                 # now add DVO IDs
-                self.updateDvoIDs("Detection_" + ota, sourceIDs[ota], imageIDs[ota])
+                self.updateDvoIDs("Detection_" + ota, self.imageIDs[ota])
                 results['NULLOBJID'] = self.scratchDb.reportAndDeleteRowsWithNULLS("Detection_" + ota, "objID")
                 self.updateImageID("Detection_" + ota, x, y) 
-                self.populateLikelihoods("Detection_" + ota)
                 rowCount = self.scratchDb.getRowCount("Detection_" + ota) 
-                self.logger.info("| %5s | %13d | %13d | %13d | %13d | %13d |", 
+                self.logger.info("| %5s | %13d | %13d | %13d | %13d | %13d | %13d |", 
                         ota, 
+                        results['ORIGINALTOTAL'], 
                         results['SATDET'], 
                         results['NULLINSTFLUX'], 
@@ -674,4 +674,11 @@
                         rowCount)
 
+                totalOriginal = totalOriginal + results['ORIGINALTOTAL']
+                totalSatDet = totalSatDet + results['SATDET']
+                totalNulIInstFlux = totalNulIInstFlux + results['NULLINSTFLUX']
+                totalNullPeakFlux = totalNullPeakFlux + results['NULLPEAKADU']
+                totalNullObjID = totalNullObjID + results['NULLOBJID']
+                totalDetections = totalDetections + rowCount
+
                 # check we have something in this Detection table TODO add this to table above
                 if rowCount < 1:
@@ -681,5 +688,5 @@
                 # update ImageMeta with count of detections for this OTA and photoCodeID
                 sql = "UPDATE ImageMeta_" + ota + " \
-                       SET nDetect = %d, photoCalID = %d" % (self.scratchDb.getRowCount("Detection_" + ota), self.scratchDb.getPhotoCalID(sourceIDs[ota], imageIDs[ota]))
+                       SET nDetect = %d, photoCalID = %d" % (self.scratchDb.getRowCount("Detection_" + ota), self.scratchDb.getPhotoCalID(self.imageIDs[ota]))
                 self.scratchDb.execute(sql)
 
@@ -696,6 +703,14 @@
                 otaCount = otaCount + 1
 
-        # TODO print totals here
-        self.logger.info("+-------+---------------+---------------+---------------+---------------+---------------+")
+        # print totals
+        self.logger.info("+-------+---------------+---------------+---------------+---------------+---------------+---------------+")
+        self.logger.info("| Total | %13d | %13d | %13d | %13d | %13d | %13d |", 
+                totalOriginal, 
+                totalSatDet, 
+                totalNulIInstFlux, 
+                totalNullPeakFlux, 
+                totalNullObjID,
+                totalDetections)
+        self.logger.info("+-------+---------------+---------------+---------------+---------------+---------------+---------------+")
 
         # if we only have one table export, i.e. FrameMeta, then get out of here
@@ -737,8 +752,10 @@
     def importIppTables(self, filter=""):
 
-       if self.testMode: regex = "XY33.psf"
+       if self.config.test: regex = "XY33.psf"
        else : regex = ".*.psf"
-
-       return super(DetectionBatch, self).importIppTables(regex)
+  
+       columns = "IPP_IDET X_PSF Y_PSF X_PSF_SIG Y_PSF_SIG PSF_INST_MAG PSF_INST_MAG_SIG PEAK_FLUX_AS_MAG PSF_MAJOR PSF_MINOR PSF_THETA EXT_NSIGMA PSF_QF MOMENTS_XX MOMENTS_XY MOMENTS_YY AP_MAG FLAGS SKY SKY_SIGMA EXT_NSIGMA"
+
+       return super(DetectionBatch, self).importIppTables(columns, regex)
 
 
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/dvo.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/dvo.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/dvo.py	(revision 33415)
@@ -0,0 +1,516 @@
+#!/usr/bin/env jython
+
+import stilts
+import datetime
+import re
+import sys
+import os
+import logging
+import glob
+from subprocess import call, PIPE, Popen
+
+from pslogger import PSLogger
+from scratchdb import ScratchDb
+
+import time
+from java.lang import *
+from java.sql import *
+
+
+'''
+A class that encapsulates a DVO database
+
+Pulls relavant bits and pieces into the scratch MySQL database and then provides access queries
+'''
+class Dvo(object):
+
+    '''
+    Constructor
+
+    - sets up logging
+    - checks we are using the correct DVO
+    - checks we have up-to-date Images.dat
+    - checks we have up-to-date SkyTable.fits
+
+    '''
+    def __init__(self, logger, config):
+
+        # set up logging
+        self.logger = logger
+        self.config = config
+
+        # set up empty lists
+        self.regionsToIngest = []
+        self.regionsToPurge = []
+        self.allPopulatedRegions = []
+        self.regionsAlreadyIngested = []
+        self.regionsIngestedButOutOfDate = []
+        
+        #self.correctDvo = False
+
+
+    '''
+    Sets to scratch database
+    '''
+    def setScratchDb(self, dbName):
+
+        self.scratchDb = ScratchDb(self.logger, self.config, dbName)
+        self.scratchDb.setUseFullTables(True)
+
+        # or decide if we are using the right DVO
+        self.correctDvo = self.scratchDb.isCorrectDvo(self.config.dvoLocation)
+ 
+        if not self.correctDvo:
+            print "*******************************************************************************"
+            response = raw_input("**** Wrong or no DVO in use. Do you want to reset and use '" + self.config.dvoLocation + "' (y/n)? ")
+            if response == "y":
+                response = raw_input("**** Are you ABSOLUTELY sure you want to do this? (y/n)? ")
+                if response == "y":
+                    self.resetAllTables()
+
+
+    '''
+    Destructor
+    '''
+    def __del__(self):
+        self.logger.debug("Dvo destructor")
+
+    '''
+    Check we are importing the DVO currenly ingested
+    '''
+    def isThisDvoCurrentlyIngested(self):
+        return self.correctDvo
+
+    '''
+    Resets all detections. Be careful
+    '''
+    def resetDetections(self):
+
+        self.logger.infoPair("Resetting", "just detections")
+        self.scratchDb.resetDvoDetectionTables()
+
+    '''
+    Resets all dvo tables. Be very careful....
+    '''
+    def resetAllTables(self):
+
+        self.logger.infoPair("Resetting", "all tables")
+        self.scratchDb.resetAllDvoTables()
+        self.correctDvo = True
+
+    '''
+    Loads the DVO Images.dat FITS file
+    '''
+    def loadImages(self):
+
+        # go no further if we've already partly ingested a different DVO
+        if not self.correctDvo: return
+
+        # check if we have up-to-date version
+        path = self.config.dvoLocation + "/Images.dat"
+        if self.scratchDb.alreadyImportedThisDvoTable(path): 
+            self.logger.debugPair("DVO Images.dat file", "up-to-date")
+            return
+        
+        self.logger.infoSeparator()
+        self.logger.infoPair("DVO Images.dat file", "NOT up-to-date")
+
+        # first, delete old dvoMeta table
+        #self.logger.infoPair("Deleting from table", self.scratchDb.dvoMetaTable)
+        #sql = "DELETE FROM " + self.scratchDb.dvoMetaTable
+        #self.scratchDb.execute(sql)
+        #self.scratchDb.optimizeTable(self.scratchDb.dvoMetaTable)
+    
+        self.importFits(
+                path,
+                "SOURCE_ID IMAGE_ID EXTERN_ID FLAGS PHOTCODE",
+                self.scratchDb.dvoImagesTable)
+        self.logger.infoPair("Adding primary key to", self.scratchDb.dvoImagesTable)
+        self.scratchDb.execute("ALTER TABLE " + self.scratchDb.dvoImagesTable + " ADD PRIMARY KEY (IMAGE_ID, EXTERN_ID)")
+
+        # insert into dvoMetaFull
+        # NB what we and smf files call IMAGE_ID, DVO calls EXTERN_ID. We are sticking
+        # with the smf name
+        '''
+        self.logger.infoPair("Populating", "all image meta data")
+        sql = "INSERT INTO " + self.scratchDb.dvoMetaTable + " ( \
+               imageID, \
+               externID, \
+               flags, \
+               photcode \
+               ) SELECT \
+               IMAGE_ID, \
+               EXTERN_ID, \
+               FLAGS, \
+               PHOTCODE \
+               FROM " + self.scratchDb.dvoImagesTable
+
+        try:
+            self.scratchDb.execute(sql)
+        except:
+            self.logger.errorPair("Could not insert into", self.scratchDb.dvoMetaTable)
+            return
+        '''
+
+        self.scratchDb.setImportedThisDvoTable(path)
+
+    '''
+    Loads the DVO SkyTable into MySQL and then applies an index
+    '''
+    def loadSkyTable(self):
+       
+        # go no further if we've already partly ingested a different DVO
+        if not self.correctDvo: return
+
+        path =  self.config.dvoLocation + "/SkyTable.fits"
+        if self.scratchDb.alreadyImportedThisDvoTable(path): 
+            self.logger.debugPair("DVO SkyTable.fits file", "up-to-date")        
+            return
+
+        self.logger.infoSeparator()
+        self.logger.infoPair("DVO SkyTable.fits file", "NOT up-to-date")        
+        self.importFits(
+                path,
+                "R_MIN R_MAX D_MIN D_MAX INDEX NAME",
+                self.scratchDb.dvoSkyTable)
+
+        self.logger.infoPair("Adding index to", self.scratchDb.dvoSkyTable)
+
+        self.scratchDb.createIndex(self.scratchDb.dvoSkyTable, "INDEX")
+        
+        self.scratchDb.setImportedThisDvoTable(path)
+        self.logger.infoPair("Finished importing SkyTable at", self.config.dvoLocation)
+
+    '''
+    Sets a bunch of region files from DVO for a RA/Dec box centered with these coords and with this side length
+    '''
+    def setSkyAreaAsBox(self, ra=180., dec=0., side=181.0):
+
+        halfSide = side/2.0
+        self.setSkyArea(ra-halfSide, ra+halfSide, dec-halfSide, dec+halfSide)
+
+    '''
+    Sets a bunch of region files from DVO for this RA/Dec box
+    '''
+    def setSkyArea(self, minRa=-1., maxRa=361., minDec=-91., maxDec=91):
+    
+        # go no further if we've already partly ingested a different DVO
+        if not self.correctDvo: return
+
+        if (maxRa - minRa <= 0) or (maxDec - minDec) <= 0:
+            self.logger.errorPair("Zero range in either RA or Dec", 
+                    "RA: %.2f -> %.2f, Dec: %.2f -> %.2f" % (minRa, maxRa, minDec, maxDec))
+            return False        
+
+        # update the two meta tables, Images and SkyTable
+        #self.loadImages() dont need a new Images table at this point
+        self.loadSkyTable()
+
+        # set bounding-box coords
+        self.minRa = minRa
+        self.maxRa = maxRa
+        self.minDec = minDec
+        self.maxDec = maxDec
+
+        # reset all lists
+        allRegions = self.scratchDb.getDvoRegions(minRa, maxRa, minDec, maxDec)
+        allIngestedRegions = self.scratchDb.getIngestedDvoRegions()
+        self.regionsToIngest = []
+        self.regionsToPurge = []
+        self.allPopulatedRegions = []
+        self.regionsAlreadyIngested = []
+        self.regionsIngestedButOutOfDate = []
+
+        # loop through all regions in DVO inside this bounding-box
+        for region in allRegions:
+
+           cpmPath = self.config.dvoLocation + "/" + region + ".cpm"
+           cptPath = self.config.dvoLocation + "/" + region + ".cpt"
+
+           # check for existence of cpm and cpt files
+           if not os.path.isfile(cpmPath): continue
+           if not os.path.isfile(cptPath): continue
+
+           # ok, have a real, populated file so add it to the list
+           self.allPopulatedRegions.append(region)
+
+           # if we have already imported up-to-date versions of both the cpm and cpt then we skip this region
+           if self.scratchDb.alreadyImportedThisDvoTable(cpmPath) and self.scratchDb.alreadyImportedThisDvoTable(cptPath): 
+               self.regionsAlreadyIngested.append(region)
+               continue
+
+           # check if we have an out-of-date version of either region file, if so, add it to the purge list
+           if self.scratchDb.haveOutOfDateVersionOfThisDvoTable(cpmPath) or self.scratchDb.haveOutOfDateVersionOfThisDvoTable(cptPath):
+               self.regionsIngestedButOutOfDate.append(region)
+
+           self.regionsToIngest.append(region)
+
+        # add out-of-range regions for purge list
+        items = list(set(allIngestedRegions) - set(self.allPopulatedRegions))
+        for item in items: self.regionsToPurge.append(item)
+
+        # now add out-of-date items to purge list
+        for item in self.regionsIngestedButOutOfDate: self.regionsToPurge.append(item)
+
+        self.sizeOfAllPopulatedRegions = self.getDiskSizeOfDvoRegions(self.allPopulatedRegions)
+        self.sizeOfRegionsAlreadyIngested = self.getDiskSizeOfDvoRegions(self.regionsAlreadyIngested)
+        self.sizeOfRegionsIngestedButOutOfDate = self.getDiskSizeOfDvoRegions(self.regionsIngestedButOutOfDate)
+        self.sizeOfRegionsToPurge = self.getDiskSizeOfDvoRegions(self.regionsToPurge)
+        self.sizeOfRegionsToIngest = self.getDiskSizeOfDvoRegions(self.regionsToIngest)
+
+    '''
+    Prints out a summary of the currently set regions
+    '''
+    def printSummary(self):
+
+        self.logger.infoTitle("Summary for currently set DVO region") 
+
+        # go no further if we've already partly ingested a different DVO
+        if not self.correctDvo:
+            self.logger.infoPair("Wrong DVO in use", self.config.dvoLocation)
+            return
+
+        self.logger.infoPair("RA range", "%.2f -> %.2f" % (self.minRa, self.maxRa))
+        self.logger.infoPair("Dec range", "%.2f -> %.2f" % (self.minDec, self.maxDec))
+        self.logger.infoPair("Total regions", "%d (%s)" % (len(self.allPopulatedRegions), self.getFileSizeStr(self.sizeOfAllPopulatedRegions))) 
+        self.logger.infoPair("Up-to-date regions already ingested", "%d (%s)" % (len(self.regionsAlreadyIngested), self.getFileSizeStr(self.sizeOfRegionsAlreadyIngested))) 
+        self.logger.infoPair("Out-of-date regions ingested", "%d (%s)" % (len(self.regionsIngestedButOutOfDate), self.getFileSizeStr(self.sizeOfRegionsIngestedButOutOfDate))) 
+        self.logger.infoPair("Total regions to purge", "%d (%s)" % (len(self.regionsToPurge), self.getFileSizeStr(self.sizeOfRegionsToPurge))) 
+        self.logger.infoPair("Total regions to ingest", "%d (%s)" % (len(self.regionsToIngest), self.getFileSizeStr(self.sizeOfRegionsToIngest))) 
+
+
+    '''
+    Returns the size on disk of the regions that need to be ingested in GB
+    '''
+    def getDiskSizeOfRegionsToBeIngested(self):
+
+        try:
+            return self.sizeOfRegionsToIngest/1073741824.0
+        except:
+            return 0.0
+
+    '''
+    Purges the provided regions from the database
+    '''
+    def purgeRegions(self):
+
+        # go no further if we've already partly ingested a different DVO
+        if not self.correctDvo: return
+
+        if len(self.regionsToPurge) < 1: return
+
+        self.logger.infoPair("Purging", "%d regions" % len(self.regionsToPurge)) 
+        deleteCount = self.scratchDb.purgeTheseDvoRegions(self.regionsToPurge)
+        self.logger.infoPair("Deleted", "%d detections" % deleteCount) 
+
+    '''
+    Formats a nice file size string
+    Accepts a size in bytes
+    '''
+    def getFileSizeStr(self, byteSize):
+
+        # format a string for the user -smaller than one MB
+        if byteSize < 1048576: return "%.1f K" % (byteSize/1024.0)
+        # smaller than one GB
+        elif byteSize < 1073741824: return "%.1fM" % (byteSize/1048576.0)
+        # more than a GB
+        else: return "%.1fG" % (byteSize/1073741824.0)
+
+    '''
+    Returns the disk size of this file
+    '''
+    def getDiskSize(self, path):
+
+        return os.stat(path).st_size
+
+    '''
+    Returns the disk size of these DVO regions, i.e. the size on disk of all cpm and cpt files
+    '''
+    def getDiskSizeOfDvoRegions(self, regions):
+
+        size = 0.0
+        for region in regions:
+
+            # get combined size of cpm and cpt files
+            size = size + self.getDiskSize(self.config.dvoLocation + "/" + region + ".cpm")
+            size = size + self.getDiskSize(self.config.dvoLocation + "/" + region + ".cpt")
+
+        return size
+
+    '''
+    Synchronises MySQL database with the sky area currently set from DVO
+    Populates dvoDetections and dvoDone with FITS tables from DVO for the defined sky area, this
+    includes purging detections outside sky area
+    '''
+    def sync(self):
+
+        # go no further if we've already partly ingested a different DVO
+        if not self.correctDvo: return False
+
+        # check we have something to do
+        if len(self.regionsToPurge) < 1 and len(self.regionsToIngest) < 1:
+            self.logger.infoPair("Nothing to ingest or purge", "finished ingest")
+            return True
+
+        # make sure we have an up-to-date Images table
+        self.loadImages()
+
+        # purge stuff
+        self.purgeRegions()
+
+        totalRegions = len(self.regionsToIngest)
+        if totalRegions > 0: self.logger.infoSeparator()
+
+        # now loop through all regions and ingest to MySQL
+        regionCount = 0
+        for region in self.regionsToIngest:
+
+           cpmPath = self.config.dvoLocation + "/" + region + ".cpm"
+           cptPath = self.config.dvoLocation + "/" + region + ".cpt"
+
+           cpmTableName = self.scratchDb.getDbFriendlyTableName(region + ".cpm")
+           cptTableName = self.scratchDb.getDbFriendlyTableName(region + ".cpt")
+
+           self.logger.infoTitle("Region %d of %d: %s" % (regionCount+1, totalRegions, region))
+
+           # if we have not already ingested the cpm table, then do it now
+           if not self.scratchDb.alreadyImportedThisDvoTable(cpmPath):
+   
+               # import cpm table and index
+               self.importFits(
+                       cpmPath,
+                       "IMAGE_ID DET_ID OBJ_ID CAT_ID EXT_ID DB_FLAGS",
+                       cpmTableName)
+
+               self.scratchDb.createIndex(cpmTableName, "IMAGE_ID")
+               self.scratchDb.createIndex(cpmTableName, "CAT_ID")
+               self.scratchDb.createIndex(cpmTableName, "OBJ_ID")
+
+               # shove SOURCE_IDs into measurement table
+               #self.logger.infoPair("Adding", "SOURCE_IDs")
+               #sql = "ALTER TABLE " + cpmTableName + " ADD COLUMN (SOURCE_ID SMALLINT)"
+               #self.scratchDb.execute(sql)
+               #sql = "UPDATE " + cpmTableName + " AS a, " + self.scratchDb.dvoImagesTable + " AS b \
+               #       SET a.SOURCE_ID = b.SOURCE_ID \
+               #       WHERE a.IMAGE_ID = b.IMAGE_ID"
+               #self.scratchDb.execute(sql)
+
+               # we can now report that we have imported this table
+               self.scratchDb.setImportedThisDvoTable(cpmPath, region)
+
+           # get the id for this cpm file so we can insert it in the dvoDetection table
+           fileId = self.scratchDb.getDvoFileId(cpmPath)
+
+           # import cpt table and index
+           self.importFits(
+                   cptPath,
+                   "OBJ_ID CAT_ID EXT_ID",
+                   cptTableName)
+
+           self.scratchDb.createIndex(cptTableName, "IMAGE_ID")
+           self.scratchDb.createIndex(cptTableName, "CAT_ID")
+           self.scratchDb.createIndex(cptTableName, "OBJ_ID")
+
+           self.logger.infoPair("Adding","PSPS objIDs")
+           # first try to add a column for PSPS_OBJ_ID. catch failure and continue
+           try:
+               sql = "ALTER TABLE " + cpmTableName + " ADD COLUMN (PSPS_OBJ_ID BIGINT)"
+               self.scratchDb.execute(sql)
+           except:
+               self.logger.infoPair("Already added PSPS_OBJ_ID column to", cpmTableName)
+               
+           # shove PSPS objIDs from cpt table into measurement table
+           sql = "UPDATE " + cpmTableName + " AS a, " + cptTableName + " AS b \
+                  SET a.PSPS_OBJ_ID = b.EXT_ID \
+                  WHERE a.CAT_ID = b.CAT_ID \
+                  AND a.OBJ_ID = b.OBJ_ID"
+           self.scratchDb.execute(sql)
+
+
+           # NB we use an INSERT IGNORE here. This is because of a known issue where multiple DVO cpm
+           # files can include the same detection, with the same IMAGE_ID/DET_ID pairing, but different
+           # PSPS object IDs assigned in the corresponding cpt file. This is believed to be a chip-boundary 
+           # issue within DVO. So, for now, we take the first IMAGE_ID/DET_ID detection we find, and ignore the rest
+           self.logger.infoPair("Merging into", self.scratchDb.dvoDetectionTable)
+           sql = "INSERT IGNORE INTO " + self.scratchDb.dvoDetectionTable + " (\
+                  imageID \
+                  ,ippDetectID \
+                  ,detectID \
+                  ,ippObjID \
+                  ,objID \
+                  ,flags \
+                  ,fileID \
+                  ) SELECT \
+                  IMAGE_ID \
+                  ,DET_ID \
+                  ,EXT_ID \
+                  ,CAT_ID * 1000000000 + OBJ_ID \
+                  ,PSPS_OBJ_ID \
+                  ,DB_FLAGS \
+                  , " + str(fileId) + " \
+                  FROM " + cpmTableName
+           try:
+               self.scratchDb.execute(sql)
+           except:
+               self.logger.error("FAILED: " + sql)
+               continue
+
+           # now drop what we don't need
+           self.logger.infoPair("Dropping tables", "%s and %s" % (cpmTableName, cptTableName))
+           self.scratchDb.dropTable(cpmTableName)
+           self.scratchDb.dropTable(cptTableName)
+
+           self.scratchDb.setImportedThisDvoTable(cptPath, region)
+           regionCount = regionCount + 1
+
+        self.logger.infoSeparator()
+
+        self.logger.infoPair("Ingested", "%d DVO regions" % regionCount)
+
+        # update lists after attempted sync
+        self.setSkyArea(self.minRa, self.maxRa, self.minDec, self.maxDec)
+        self.printSummary()
+
+        return True
+
+    '''
+    Imports a FITS file. A regex filter lets you choose which tables to pull from the file
+    An optional final argument lets you name the resulatant database table
+    '''
+    def importFits(self, path, columns, tableName):
+
+      self.logger.infoPair("Importing file", "%s (%s)" % (path, self.getFileSizeStr(self.getDiskSize(path))))
+      self.logger.debugPair("Writing to database table", tableName)
+
+      tables = stilts.treads(path)
+
+      count = 0
+      for table in tables:
+
+          self.logger.debugPair("Reading IPP table", table.name)
+          table = stilts.tpipe(table, cmd='explodeall')
+     
+          # IPP FITS files are littered with infinities, so remove these
+          self.logger.debugPair("Removing", "infinity values")
+          table = stilts.tpipe(table, cmd='replaceval -Infinity null *')
+          table = stilts.tpipe(table, cmd='replaceval Infinity null *')
+
+          attempts = 0
+          self.logger.debugPair("Writing table to database",  table.name)
+          while True:
+    
+              try:
+                  if attempts > 0: self.logger.infoPair("Attempt %d to write" % attempts, table.name)
+                  table.cmd_keepcols(columns).write(self.scratchDb.url + '#' + tableName)
+                  break
+              except:
+                  # wait a few secs then try again
+                  time.sleep(2)
+                  attempts = attempts + 1
+
+          count = count + 1
+
+      self.logger.debugPair("Finished importing", tableName)
+
+      return tableName
+
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/dxlayer.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/dxlayer.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/dxlayer.py	(revision 33415)
@@ -0,0 +1,74 @@
+#!/usr/bin/env jython
+
+import re
+import sys
+import tempfile
+from xml.etree.ElementTree import ElementTree, Element, tostring
+from subprocess import call, PIPE, Popen
+
+from batch import Batch
+
+'''
+Class for using DXLayer webservice
+'''
+class DXLayer(object):
+
+    '''
+    Constructor
+    '''
+    def __init__(self, logger):
+
+        self.logger = logger
+
+    '''
+    Delete's a batch from the DXLayer
+    '''
+    def deleteBatch(self, batchID):
+
+        batch = Batch.getNameFromID(batchID)
+
+        tempFile = tempfile.NamedTemporaryFile(mode='w+b')
+
+        p = Popen("../perl/deleteFromDXLayer.pl -b " + batch + " -f " + tempFile.name, 
+                shell=True, 
+                stdout=PIPE)
+        p.wait()
+       
+        if p.returncode != 0:
+           self.logger.debugPair("DXLayer webservice", "failed")
+           return 0
+        else:
+           self.logger.debugPair("DXLayer webservice", "successful")
+
+        ret = self.decodeDXLayerResponse(tempFile.name)
+
+        tempFile.close()
+        p.stdout.close()
+
+        if ret: self.logger.infoPair("Deleted batch from DXLayer", batch) 
+        else: self.logger.errorPair("Unable to delete batch from DXLayer", batch)
+
+        return ret
+
+    '''
+    Parses the XML returned from the DXLayer webservice call
+    '''
+    def decodeDXLayerResponse(self, dxLayerResponseFile):
+
+        doc = ElementTree(file=dxLayerResponseFile)
+
+        ret = False
+        try:
+            MESSAGE = doc.find("Rows/Row/Value").text
+
+            #print "MESSAGE " + MESSAGE
+            if re.search("Path not found", MESSAGE):
+                ret = True
+            elif re.search("Deleted", MESSAGE): 
+                ret = True
+                
+        except:
+            self.logger.error("Cannot read DXLayer response file")
+            ret = False
+ 
+        return ret
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/fits.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/fits.py	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/fits.py	(revision 33415)
@@ -15,11 +15,11 @@
 
     '''
-    def __init__(self, logger, doc, originalPath):
+    def __init__(self, logger, config, originalPath):
 
        # set class variables
        self.originalPath = originalPath
        self.logger = logger
-       self.doc = doc
-       self.localDir = self.doc.find("localOutPath").text
+       self.config = config
+       self.header = None
 
        # does this file even exist?
@@ -30,11 +30,12 @@
        # ok, we have a file, now copy it locally to save on NFS overhead
        self.logger.debugPair("FITS file", self.originalPath)
-       self.localCopyPath = self.localDir + "/temp.fits"
-       shutil.copy2(self.originalPath, self.localCopyPath)
+       #self.localCopyPath = self.config.basePath + "/temp.fits"
+       #shutil.copy2(self.originalPath, self.localCopyPath)
 
        # open the local copy and parse the ridiculou plain-text header
-       self.file = open(self.localCopyPath)
+       #self.file = open(self.localCopyPath)
+       self.file = open(self.originalPath)
        self.header = self.parseHeader()
-       self.logger.debugPair("FITS local copy", self.localCopyPath)
+       #self.logger.debugPair("FITS local copy", self.localCopyPath)
        self.logger.debugPair("FITS primary header", "%d cards found" % len(self.header))
 
@@ -52,5 +53,6 @@
     '''
     def getPath(self):
-        return self.localCopyPath
+        #return self.localCopyPath
+        return self.originalPath
 
     '''
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/gpc1db.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/gpc1db.py	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/gpc1db.py	(revision 33415)
@@ -20,6 +20,6 @@
     Constructor
     '''
-    def __init__(self, logger, doc):
-        super(Gpc1Db, self).__init__(logger, doc, "gpc1database")
+    def __init__(self, logger, config):
+        super(Gpc1Db, self).__init__(logger, config, "gpc1database")
 
     '''
@@ -32,35 +32,21 @@
 
     '''
-    TODO
-    '''
-    def getIDsInThisDVODbForThisStageFudge(self):
-
-        sql = "SELECT staticskyRun.sky_id \
-               FROM staticskyInput, staticskyRun, stackRun, staticskyResult \
-               WHERE staticskyRun.sky_id = staticskyInput.sky_id \
-               AND staticskyInput.stack_id = stackRun.stack_id \
-               AND staticskyInput.sky_id = staticskyResult.sky_id \
-               AND staticskyRun.label like 'MD04.staticsky' \
-               AND stackRun.filter like 'i%'"
-
-        try:
-            rs = self.executeQuery(sql)
-        except:
-            self.logger.exception("Can't query for ids in DVO")
+    Gets a list of ids in this DVO database for this stage, could be cam or staticsky, for a box with defined center and side length
+    '''
+    def getIDsInThisDVODbForThisStageInThisBox(self, dvoDb, batchType, ra=180., dec=0., side=181.):
+
+        halfSide = side/2.0
+        return self.getIDsInThisDVODbForThisStage(dvoDb, batchType, ra-halfSide, ra+halfSide, dec-halfSide, dec+halfSide)
+
+
+    '''
+    Gets a list of ids in this DVO database for this stage, could be cam or staticsky, and a box can be defined with equatorial coordinates
+    '''
+    def getIDsInThisDVODbForThisStage(self, dvoDb, batchType, minRA=-999, maxRA=999, minDec=-999, maxDec=999):
+
+        self.logger.debugPair("GPC1 RA range", "%.2f -> %.2f" % (minRA, maxRA))
+        self.logger.debugPair("GPC1 Dec range", "%.2f -> %.2f" % (minDec, maxDec))
 
         ids = []
-        while (rs.next()):
-            ids.append(rs.getInt(1))
-
-        rs.close()
-
-        self.logger.debug("Found %d items in DVO database '" % (len(ids)))
-
-        return ids
-
-    '''
-    Gets a list of ids in this DVO database for this stage, could be cam or staticsky (so far)
-    '''
-    def getIDsInThisDVODbForThisStage(self, dvoDb, batchType):
 
         if batchType == "P2": # TODO define these someplace 
@@ -71,15 +57,21 @@
                    JOIN minidvodbRun USING(minidvodb_name) \
                    JOIN minidvodbProcessed USING(minidvodb_id) \
-                   WHERE minidvodbRun.minidvodb_group = '" + dvoDb + "' \
+                   JOIN camRun ON (stage_id = cam_id) \
+                   JOIN chipRun USING (chip_id) \
+                   JOIN rawExp USING (exp_id) \
+                   JOIN mergedvodbRun using (minidvodb_id) \
+                   WHERE mergedvodbRun.mergedvodb = '" + dvoDb + "' \
                    AND minidvodbRun.state = 'merged' \
+                   AND mergedvodbRun.state = 'full' \
                    AND minidvodbProcessed.fault = 0 \
                    AND addRun.stage = '" + stage + "' \
-                   AND addRun.state = 'full'"
+                   AND addRun.state = 'full' \
+                   AND decl BETWEEN RADIANS(" + str(minDec) + ") AND RADIANS(" + str(maxDec) + ") \
+                   AND ra BETWEEN RADIANS(" + str(minRA) + ") AND RADIANS(" + str(maxRA) + ")"
 
         elif batchType == "ST":       
 
-            stage = "staticsky"
-            sql = "SELECT DISTINCT stack_id \
-                   FROM staticskyInput \
+            stage = "staticsky_multi"
+            sql = "SELECT DISTINCT stack_id FROM staticskyInput \
                    JOIN addRun ON(staticskyInput.sky_id = addRun.stage_id) \
                    JOIN minidvodbRun USING(minidvodb_name) \
@@ -90,15 +82,13 @@
                    AND addRun.stage = '" + stage + "' \
                    AND addRun.state = 'full'"
-            
-        try:
-            rs = self.executeQuery(sql)
+
+        try:
+            rs = self.executeQuery(sql)
+            while (rs.next()): ids.append(rs.getInt(1))
+            rs.close()
         except:
             self.logger.exception("Can't query for ids in DVO")
-
-        ids = []
-        while (rs.next()):
-            ids.append(rs.getInt(1))
-
-        rs.close()
+            return ids
+
 
         self.logger.debug("Found %d items in DVO database '%s' for stage='%s'" % (len(ids), dvoDb, stage))
@@ -154,10 +144,14 @@
             rs = self.executeQuery(sql)
             rs.first()
+        except:
+            self.logger.errorPair("Can't query for", "stack meta data")
+           
+        try:
             meta.append(rs.getString(1))
             meta.append(rs.getString(2))
             meta.append(rs.getString(3))
         except:
-            self.logger.exception("Can't query for stack meta")
-
+            self.logger.errorPair("getStackStageMeta()", "empty meta data")
+     
         return meta
 
@@ -178,4 +172,8 @@
             rs = self.executeQuery(sql)
             rs.first()
+        except:
+            self.logger.errorPair("Can't query for", "camera meta data")
+
+        try:
             meta.append(rs.getInt(1))
             meta.append(rs.getString(2))
@@ -185,5 +183,5 @@
             meta.append(rs.getString(6))
         except:
-            self.logger.exception("Can't query for camera meta")
+            self.logger.errorPair("getCameraStageMeta()", "empty meta data")
 
         return meta
@@ -205,9 +203,13 @@
             rs.first()
         except:
-            self.logger.exception("Can't query for camera smfs")
-
-        # get path to base dir of cmf files
-        path = rs.getString(1)
-        path = path[0:path.rfind("/")]
+            self.logger.errorPair("Can't query for smf file for cam_id",  str(camID))
+            return None
+
+        try:
+            # get path to base dir of cmf files
+            path = rs.getString(1)
+        except:
+            self.logger.errorPair("No smf files found for cam_id", str(camID) )
+            return None
        
         # list all smf files if a neb path
@@ -215,5 +217,5 @@
         if path.startswith("neb"):
 
-            f=os.popen("neb-ls -p "+path+"/%smf")
+            f=os.popen("neb-ls -p "+path+"%smf")
             for i in f.readlines():
                 files.append(i.rstrip())
@@ -221,24 +223,64 @@
         # or not a neb path
         else:
-            files = glob.glob(path + "/*.smf")
+            files = glob.glob(path + ".smf")
 
         if len(files) < 1: return None
 
-        return Fits(self.logger, self.doc, files[0]) # TODO just returning first file - check
+        return Fits(self.logger, self.config, files[0]) # TODO just returning first file - check
 
 
     '''
     Gets the path to the cmf file for this stack_id
-    '''
-    def getStackStageCmf(self, stackID):
-      
-        sql = "SELECT staticskyResult.path_base \
-               FROM staticskyRun \
-               JOIN staticskyInput USING(sky_id) \
+    
+    The logic here is fairly ridiculous due to issues with how GPC1 stores info regarding multi-filter stacks. In partcular, it
+    is impossible to find a full path for a cmf file for a given stack_id
+
+    Instead, we get a path_base to a directory with a collection of cmfs. neb-ls will list 5 files, but a number < 5 may actually exist
+    meaning that neb-ls -p will fail.
+
+    our logic is as follows:
+    
+    - use stack_id and the DVO label to find a path base for all cmfs
+    - loop through all the potential cmfs, run neb-ls -p on each
+    - if neb-ls -p fails, then the file does not exists and we ignore it
+    - if neb-ls -p succeeds, we open that cmf file and check the STK_ID value in its header to see if it is the one we want
+    - if it is, great, exit
+    - if not, try the next one in the list
+
+    '''
+    def getStackStageCmf(self, dvoDb, stackID):
+     
+        '''
+        sql = "SELECT DISTINCT staticskyResult.path_base \
+               FROM staticskyRun  \
+               JOIN staticskyInput USING(sky_id)  \
                JOIN staticskyResult USING(sky_id) \
-               JOIN stackRun USING(stack_id) \
+               JOIN stackRun USING(stack_id)  \
                JOIN stackSumSkyfile USING(stack_id) \
-               WHERE stack_id = %s" % stackID
-
+               JOIN addRun ON sky_id = stage_id \
+               JOIN minidvodbRun USING (minidvodb_name) \
+               WHERE stack_id = " + str(stackID) + " \
+               AND minidvodbRun.minidvodb_group = '" + dvoDb + "' \
+               AND minidvodbRun.state = 'merged' \
+               AND stage = 'staticsky_multi'"
+        '''
+
+        sql = "SELECT DISTINCT staticskyResult.path_base \
+               FROM staticskyRun  \
+               JOIN staticskyInput USING(sky_id)  \
+               JOIN staticskyResult USING(sky_id) \
+               JOIN stackRun USING(stack_id)  \
+               JOIN stackSumSkyfile USING(stack_id) \
+               JOIN addRun ON sky_id = stage_id \
+               JOIN minidvodbRun USING (minidvodb_name) \
+               JOIN mergedvodbRun using (minidvodb_id) \
+               WHERE stack_id = " + str(stackID) + " \
+               AND minidvodbRun.state = 'merged' \
+               AND mergedvodbRun.state = 'full' \
+               AND mergedvodbRun.mergedvodb = '" + dvoDb + "' \
+               AND stage = 'staticsky_multi'"
+
+
+        # get single path base for the directory containing all cmf files, one of which is hopefully for our stack_id
         try:
             rs = self.executeQuery(sql)
@@ -246,19 +288,38 @@
             pathBase = rs.getString(1)
 
-            # now find ALL cmf files at this path_base as there can be more than one
-            f=os.popen("neb-ls -p " + pathBase + "%cmf")
-            for i in f.readlines():
-
-                path = i.rstrip()
-                fits = Fits(self.logger, self.doc, path)
-
-                # we need to check if this is the correct cmf file, and if so then break and return
-                if fits.getPrimaryHeaderValue("STK_ID") == str(stackID):
-                    return fits
-
-        except:
-            self.logger.exception("Can't query for stack cmfs")
-
-        self.logger.error("Could not find stack cmf")
+            # now find and loop through all cmf files at this path_base
+            files=os.popen("neb-ls " + pathBase + "%cmf")
+            for i in files.readlines():
+
+                nebPath = i.rstrip()
+
+                # now attempt to run 'neb-ls -p' to actually read this cmf file
+                try:
+                    self.logger.debug("Trying to neb-ls -p on '" + nebPath + "'")
+                    paths=os.popen("neb-ls -p " + nebPath)
+                    path = paths.readline().rstrip()
+                    if len(path) < 1:
+                       self.logger.debug("zero length path - skipping")
+                       raise
+
+                    # if we get here, then the cmf is readable, now check the stack_id
+                    fits = Fits(self.logger, self.config, path)
+                    if fits.getPrimaryHeaderValue("STK_ID") == str(stackID):
+                        # we have the right file!
+                        self.logger.debug("SUCCESS!: %s == %s"% (fits.getPrimaryHeaderValue("STK_ID"), str(stackID)))
+                        return fits
+                    else:
+                        # this is not the correct file
+                        self.logger.debug("STACK ID: %s != %s"% (fits.getPrimaryHeaderValue("STK_ID"), str(stackID)))
+
+                # an exception here means that nebulous could not read this file, so we just skip it
+                except:
+                    self.logger.debug("NEB FAILED SKIPPING")
+                    pass
+
+        except:
+            self.logger.errorPair("Can't query for stack cmfs", "stack_id = %d" % stackID)
+
+        self.logger.errorPair("Couldn't find stack cmf for", "stack_id = %d" % stackID)
         return None
 
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/initbatch.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/initbatch.py	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/initbatch.py	(revision 33415)
@@ -27,16 +27,19 @@
     def __init__(self, 
             logger, 
-            configPath,
-            configDoc,
+            config,
             gpc1Db, 
-            ippToPspsDb):
+            ippToPspsDb,
+            scratchDb,
+            batchID):
        super(InitBatch, self).__init__(logger, 
-               configPath, 
-               configDoc, 
+               config,
                gpc1Db, 
                ippToPspsDb, 
+               scratchDb,
                0, 
+               batchID,
                "IN", 
-               None)
+               None,
+               0)
 
        self.outputFitsFile = "00000000.FITS";
@@ -49,4 +52,9 @@
         self.logger.debug("importIppTables method here to satidfy base-class")
 
+    '''
+    Here to satidfy the base-class
+    '''
+    def alterPspsTables(self):
+        return True
 
     '''
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/ipptopsps.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/ipptopsps.py	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/ipptopsps.py	(revision 33415)
@@ -1,171 +1,152 @@
 #!/usr/bin/env jython
 
-# The starting point for ippTopsps. See usage below.
+import signal
+import sys
+import os
+import time
+import logging
+import socket
 
-import time
-import sys
-import logging.config
-from xml.etree.ElementTree import ElementTree, Element, tostring
-
+from config import Config
 from pslogger import PSLogger
-from gpc1db import Gpc1Db
 from ipptopspsdb import IppToPspsDb
-
-from initbatch import InitBatch
-from stackbatch import StackBatch
-from detectionbatch import DetectionBatch
-
-'''
-Queues up all available stuff for this batch type
-'''
-def queueItems(batchType):
-
-    logger.infoTitle("Queuing " + batchType + " items")
-
-    allIDs = []
-
-    # see if there are any IDs listed in the config
-    try:
-        for element in configDoc.findall('options/ids/id'):
-            allIDs.append(int(element.text))
-    except:
-        logger.errorPair("Problem loading IDs from config under", "options/ids/id") 
-
-    # no config dictated IDs, so get from DVO instead
-    if len(allIDs) > 0:
-        logger.infoPair("Using config IDs (not DVO)", "%d" % len(allIDs))
-    else:
-        allIDs = gpc1Db.getIDsInThisDVODbForThisStage(DVOGPC1LABEL, batchType)
-        logger.infoPair("All %s items in DVO" % batchType, "%d" % len(allIDs))
-
-    # if in FORCE mode, then queue full list
-    if FORCE: ids = allIDs
-
-    # if not in TEST mode, then only queue un-processed items
-    else:
-        processedIDs = ippToPspsDb.getProcessedIDs(batchType, EPOCH, DVOGPC1LABEL)
-        logger.infoPair("Processed %s items" % batchType, "%d" % len(processedIDs))
-        ids = list(set(allIDs) - set(processedIDs))
-        logger.infoPair("Unprocessed %s items" % batchType, "%d" % len(ids))
-
-    logger.infoPair("%s items queued" % batchType, "%d" % len(ids))
-
-    # loop round IDs of all items to be processed
-    for id in ids:
-
-        if not FORCE and ippToPspsDb.alreadyProcessed(id): continue
-
-        if batchType == "P2":
-            batch = DetectionBatch(logger,
-                    CONFIG,
-                    configDoc,
-                    gpc1Db,
-                    ippToPspsDb,
-                    id)
-        elif batchType == "ST":
-            batch = StackBatch(logger,
-                    CONFIG,
-                    configDoc,
-                    gpc1Db,
-                    ippToPspsDb,
-                    id)
-
-        batch.run()
-
-        # if in TEST mode, then quit after one batch
-        if TEST: break
+from dxlayer import DXLayer
+from datastore import Datastore
+from dvo import Dvo
+from odm import Odm
 
 
 '''
-Start of program.
+Abstract base class for all ippToPsps programs
+handle command-line args, setting up of Config and logger objects, clean exiting etc 
 '''
+class IppToPsps(object):
 
-if len(sys.argv) > 1: CONFIG = sys.argv[1]
-else:
-    print "** Usage: " + sys.argv[0] + " <configPath> [init]"
-    sys.exit(1)
+    '''
+    Constructor
+    '''
+    def __init__(self, argv):
 
-if len(sys.argv) > 2 and sys.argv[2] == "init":
-    QUEUE_IN = 1
-else:
-    QUEUE_IN = 0
+        # are the arsg ok? all programs require a config name
+        if len(sys.argv) < 2: 
+            self.printUsage()
+            self.exitProgram("Wrong args")
+            
+        # some class constants
+        self.PROGNAME = sys.argv[0]
+        self.CONFIGNAME = sys.argv[1]
+        self.HOST = socket.gethostname()
+        self.PID = os.getpid()
+        self.PAUSEPERIOD = 60
+        self.HOURS = None
+        self.MINUTES = None
+        self.SECONDS = None
 
-# open config file
-configDoc = ElementTree(file=CONFIG)
-TEST = int(configDoc.find("options/testMode").text)
+        # set up config object
+        self.config = Config(self.PROGNAME, self.CONFIGNAME)
+        self.logger = self.config.getLogger(self.HOST, self.PID, 1, 1)
 
-# set up logging
-logging.setLoggerClass(PSLogger)
-logger = logging.getLogger("ippToPspsLog")
+        # create connection to databases database
+        self.ippToPspsDb = IppToPspsDb(self.logger, self.config)
 
-# if in test mode, print log to screen, otherwise, only to file
-logger.setup(configDoc, "ippToPsps", TEST, not TEST)
+        self.checkClientStatus()
 
-# create database objects
-gpc1Db = Gpc1Db(logger, configDoc)
-ippToPspsDb = IppToPspsDb(logger, configDoc)
+        # catch Ctrl-C signal
+        signal.signal(signal.SIGINT, self.signal_handler)
 
-# check we connected ok
-if not gpc1Db.everythingOK: sys.exit(1)
-if not ippToPspsDb.everythingOK: sys.exit(1)
+        # title for log
+        self.logger.infoSeparator()
+        self.logger.infoTitle("ippToPsps '" + self.PROGNAME + "' started")
 
-# get values from the configutaion file
-POLLPERIOD = 600
-DVOGPC1LABEL = configDoc.find("dvo/gpc1Label").text
-FORCE = int(configDoc.find("options/force").text)
-EPOCH = configDoc.find("options/epoch").text
-PUBLISH = int(configDoc.find("options/publishToDatastore").text)
-QUEUE_P2 = int(configDoc.find("options/queueP2").text)
-QUEUE_ST = int(configDoc.find("options/queueST").text)
+    '''
+    Parses a poll-time argument string and stored as class variables
+    '''
+    def parsePollTimeArg(self, hours):
 
-# prompt user: FORCE and PUBLISH is a dangerous combination
-if FORCE and PUBLISH and not QUEUE_IN:
-   response = raw_input("\n*** Are you sure you want to publish data with the 'force' option enabled (y/n)? ")
-   if response != "y": sys.exit(1)
-
-# prompt user: TEST and PUBLISH is a dangerous combination
-if TEST and PUBLISH and not QUEUE_IN:
-   response = raw_input("\n*** Are you sure you want to publish data with the 'testMode' option enabled (y/n)? ")
-   if response != "y": sys.exit(1)
-
-# print info to log
-logger.infoSeparator()
-logger.infoTitle("ippToPsps started")
-logger.infoPair("Configuration file", CONFIG)
-logger.infoPair("Loading epoch", EPOCH)
-logger.infoPair("DVO gpc1 label", DVOGPC1LABEL)
-logger.infoBool("Queuing P2 batches?", QUEUE_P2)
-logger.infoBool("Queuing ST batches?", QUEUE_ST)
-logger.infoBool("Forcing?", "%d" % FORCE)
-logger.infoBool("Test mode?", "%d" % TEST)
-logger.infoBool("Publishing?", PUBLISH)
-
-'''
-Main processing loop:
-
- 1) creates an IN batch if requested
- 2) queues available P2 batches (if requested)
- 3) queues available ST batches (if requested)
- 4) if in test mode, quits
- 5) otherwise, waits then repeats from 2
-
-'''
-while True:
-
-    # queue all items for all batch types, sequentially
-    if QUEUE_IN:
-       batch = InitBatch(logger, CONFIG, configDoc, gpc1Db, ippToPspsDb)
-       batch.run()
-       break;
-
-    if QUEUE_P2: queueItems("P2")
-    if QUEUE_ST: queueItems("ST")
-
-    # in the TEST mode, we quit after submitting one batch
-    if TEST: break
-
-    # wait for the POLLPERIOD before checking for new ids
-    logger.infoPair("Finished. Waiting for", "%.1f minutes before checking DVO for new items" % (POLLPERIOD/60.0))
-    time.sleep(POLLPERIOD)
+        try:
+            self.HOURS = float(hours)
+            self.MINUTES = self.HOURS * 60.0
+            self.SECONDS = self.MINUTES * 60.0
+        except:
+            pass
 
 
+    '''
+    Waits for user defined poll time
+    '''
+    def waitForPollTime(self):
+
+        # if not POLL time set, then return False
+        if not self.SECONDS: return False
+
+        if self.HOURS < 1.0:
+            str = "%.1f minutes" % self.MINUTES
+        else:
+            str = "%.1f hours" % self.HOURS
+
+        self.logger.infoPair("Waiting for", str)
+        time.sleep(self.SECONDS)
+
+        return True
+
+    '''
+    Prints usage to stdout
+    '''
+    def printUsage(self):
+        # using sys.stdout to avoid newline in case sub-classes need to add further args
+        sys.stdout.write("** Usage: " + sys.argv[0] + " <configPath>")
+
+
+    '''
+    Refreshes the config then recreates objects with new settings
+    '''
+    def refreshConfig(self):
+        return self.ippToPspsDb.readConfig(self.HOST, self.PID)
+
+    '''
+    All implementing subclasses (programs) should call this method once in a while to see if it should be paused or killed, otherwise this updates the clients table with current time and reloads the config data
+    '''
+    def checkClientStatus(self):
+
+        self.ippToPspsDb.updateClient(self.PROGNAME, self.HOST, self.PID, self.config.name)
+
+        if self.ippToPspsDb.isKilled(self.HOST, self.PID): 
+            self.exitProgram("killed via Db")
+       
+        # this loop pauses the process if we have no config or we have set it to pause
+        while not self.refreshConfig() or self.ippToPspsDb.isPaused(self.HOST, self.PID):
+
+            self.ippToPspsDb.updateClient(self.PROGNAME, self.HOST, self.PID,  self.config.name)
+            if self.ippToPspsDb.isKilled(self.HOST, self.PID): self.exitProgram("killed while paused")
+            self.logger.infoPair("Paused. Will check again in", "%d secs" % self.PAUSEPERIOD)
+            time.sleep(self.PAUSEPERIOD)
+
+
+    '''
+    Handler for Ctrl-C signal
+    '''
+    def signal_handler(self, signal, frame):
+
+       self.exitProgram("Ctrl-C")
+
+
+    '''
+    Exits the program cleanly, removes info from ipptopsps database
+    '''
+    def exitProgram(self, exitReason):
+
+        # write message to log or stdout if no logger set up
+        try:
+            self.logger.infoPair("Program exited", exitReason)
+        except: 
+            print "** program exited: " + exitReason
+
+        # remove from database if possible
+        try:
+            self.ippToPspsDb.removeClient(self.HOST, self.PID)
+        except:
+            pass
+
+        sys.exit(0)
+   
+
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/ipptopspsdb.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/ipptopspsdb.py	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/ipptopspsdb.py	(revision 33415)
@@ -17,54 +17,142 @@
     Constructor
     '''
-    def __init__(self, logger, doc):
-        super(IppToPspsDb, self).__init__(logger, doc, "ipptopspsdatabase")
-
-    '''
-    Creates a new batch
-    '''
-    def createNewBatch(self, batchType, stageID, survey, dvoDb, datastoreProduct):
-
-        sql = "INSERT INTO batch ( \
-               batch_type, \
-               stage_id, \
-               survey, \
-               dvo_db, \
-               datastore_product \
-               ) VALUES ( \
-               '" + batchType + "', \
-               " + str(stageID) + ", \
-               '" + survey + "', \
-               '" + dvoDb + "', \
-               '" + datastoreProduct + "' \
-               )"
-
-        self.execute(sql)
-
-        sql = "SELECT MAX(batch_id) FROM batch"
-
-        batchID = -1;
-
-        try:
-            rs = self.executeQuery(sql)
-            rs.first()
-            batchID = rs.getInt(1)
-        except:
-            self.logger.exception("Unable to get batch ID")
-
-        self.logger.debug("Created new batch in ippToPsps database with batchID = %d" % batchID)
-
-        return batchID;
-
-    '''
-    Returns a list of processed batch IDs that are merged but not yet deleted
-    '''
-    def getMergedButNotDeletedBatchIDs(self, epoch, dvoGpc1Label):
+    def __init__(self, logger, config):
+        super(IppToPspsDb, self).__init__(logger, config, "ipptopspsdatabase")
+
+        self.MAX_FAILS = 5
+
+    '''
+    Returns a list of merged batch IDs that are merged but not yet deleted
+    '''
+    def getMergedButNotDeletedBatchIDs(self, batchType, epoch, dvoGpc1Label, column):
 
         sql = "SELECT DISTINCT batch_id \
                FROM batch \
                WHERE timestamp > '" + epoch + "' \
-               AND dvo_db = '" + dvoGpc1Label + "' \
-               AND merged \
-               AND !deleted"
+               AND batch_type = '" + batchType + "' \
+               AND dvo_db = '" + dvoGpc1Label + "' \
+               AND merged = 1 \
+               AND " + column + " = 0"
+
+        ids = []
+        try:
+            rs = self.executeQuery(sql)
+            while (rs.next()): ids.append(rs.getInt(1))
+            rs.close()
+        except:
+            self.logger.exception("Can't query for merged batch ids in ipptopsps Db")
+
+        self.logger.debug("Found %d merged but un-deleted items" % len(ids))
+
+        return ids
+
+    '''
+    Returns a list of batch IDs marked as 'purged' but not yet deleted
+    '''
+    def getPurgedButNotDeletedBatchIDs(self, batchType, epoch, dvoGpc1Label, column):
+
+        sql = "SELECT DISTINCT batch_id \
+               FROM batch \
+               WHERE timestamp > '" + epoch + "' \
+               AND batch_type = '" + batchType + "' \
+               AND dvo_db = '" + dvoGpc1Label + "' \
+               AND purged = 1 \
+               AND " + column + " = 0"
+
+        ids = []
+        try:
+            rs = self.executeQuery(sql)
+            while (rs.next()): ids.append(rs.getInt(1))
+            rs.close()
+        except:
+            self.logger.exception("Can't query for merged batch ids in ipptopsps Db")
+
+
+        self.logger.debug("Found %d merged but un-deleted items" % len(ids))
+
+        return ids
+
+    '''
+    Returns a list of processed batch IDs that have been loaded to the ODM but not yet deleted
+    '''
+    def getLoadedToODMButNotDeletedBatchIDs(self, batchType, epoch, dvoGpc1Label, column):
+
+        sql = "SELECT DISTINCT batch_id \
+               FROM batch \
+               WHERE timestamp > '" + epoch + "' \
+               AND batch_type = '" + batchType + "' \
+               AND dvo_db = '" + dvoGpc1Label + "' \
+               AND (loaded_to_ODM = -1 OR merge_worthy = 1) \
+               AND " + column + " = 0"
+
+        ids = []
+        try:
+            rs = self.executeQuery(sql)
+            while (rs.next()): ids.append(rs.getInt(1))
+            rs.close()
+        except:
+            self.logger.exception("Can't query for merged batch ids in ipptopsps Db")
+
+        self.logger.debug("Found %d merged but un-deleted items" % len(ids))
+
+        return ids
+
+    '''
+    Returns a list of merged batch IDs that are not deleted from local disk
+    '''
+    def getMergedButNotDeletedFromLocalDisk(self, batchType, epoch, dvoGpc1Label):
+        return self.getMergedButNotDeletedBatchIDs(batchType, epoch, dvoGpc1Label, "deleted_local")
+
+    '''
+    Returns a list of processed batch IDs that are loaded to the ODM, but not deleted from local disk
+    '''
+    def getLoadedToODMButNotDeletedFromLocalDisk(self, batchType, epoch, dvoGpc1Label):
+        return self.getLoadedToODMButNotDeletedBatchIDs(batchType, epoch, dvoGpc1Label, "deleted_local")
+
+    '''
+    Returns a list of processed batch IDs that are loaded to the ODM, but not deleted from datastore
+    '''
+    def getLoadedToODMButNotDeletedFromDatastore(self, batchType, epoch, dvoGpc1Label):
+        return self.getLoadedToODMButNotDeletedBatchIDs(batchType, epoch, dvoGpc1Label, "deleted_datastore")
+
+    '''
+    Returns a list of processed batch IDs that are loaded to the ODM, but not deleted from DXLayer
+    '''
+    def getLoadedToODMButNotDeletedFromDXLayer(self, batchType, epoch, dvoGpc1Label):
+        return self.getLoadedToODMButNotDeletedBatchIDs(batchType, epoch, dvoGpc1Label, "deleted_dxlayer")
+
+    '''
+    Returns a list of purged  batch IDs that not deleted from local disk
+    '''
+    def getPurgedButNotDeletedFromLocalDisk(self, batchType, epoch, dvoGpc1Label):
+        return self.getPurgedButNotDeletedBatchIDs(batchType, epoch, dvoGpc1Label, "deleted_local")
+
+    '''
+    Returns a list of purged batch IDs that are not deleted from datastore
+    '''
+    def getPurgedButNotDeletedFromDatastore(self, batchType, epoch, dvoGpc1Label):
+        return self.getPurgedButNotDeletedBatchIDs(batchType, epoch, dvoGpc1Label, "deleted_datastore")
+
+
+    '''
+    Returns a list of processed batch IDs that are not loaded to the datastore
+    NB we have a date constraint here because, before this time, it is possible that
+    a test batch would be labelled as processed but not loaded to datastore, and we would NOT
+    want to load a test batch to PSPS
+
+    We also check that these batches are more than 4 HOURs old, so that we don't attempt to 
+    publish something simulataneously with the client that it producing it
+    '''
+    def getProcessedButFailedDatastoreBatchIDs(self, epoch, dvoGpc1Label, batchType):
+
+        sql = "SELECT DISTINCT batch_id \
+               FROM batch \
+               WHERE timestamp > '" + epoch + "' \
+               AND batch_type = '" + batchType + "' \
+               AND dvo_db = '" + dvoGpc1Label + "' \
+               AND processed = 1\
+               AND loaded_to_datastore != 1 \
+               AND timestamp > '2011-10-27' \
+               AND timestamp < now() -  INTERVAL 4 HOUR"
 
         ids = []
@@ -74,23 +162,26 @@
                 ids.append(rs.getInt(1))
         except:
-            self.logger.exception("Can't query for merged batch ids in ipptopsps Db")
+            self.logger.exception("Can't query for processed batch ids not loaded to datastore ipptopsps Db")
 
         rs.close()
 
-        self.logger.debug("Found %d merged but un-deleted items" % len(ids))
-
-        return ids
-
-    '''
-    Returns a list of processed batch IDs that are not yet merged for this epoch and dvo label
-    '''
-    def getProcessedButUnmergedBatchIDs(self, epoch, dvoGpc1Label):
-
-        sql = "SELECT DISTINCT batch_id \
+        self.logger.debug("Found %d processed but-not-on-datastore items" % len(ids))
+
+        return ids
+
+    '''
+    Returns a list of processed batch IDs that are not yet loaded to the ODM
+    NOTE we exclude batches already deleted from the datastore, as sometimes we delete stuff prior to loading to ODM if there is a known problem with the batch
+    '''
+    def getUnloadedBatchIDs(self, epoch, dvoGpc1Label, batchType):
+
+        sql = "SELECT batch_id \
                FROM batch \
                WHERE timestamp > '" + epoch + "' \
                AND dvo_db = '" + dvoGpc1Label + "' \
-               AND loaded_to_datastore \
-               AND !merged"
+               AND loaded_to_datastore = 1 \
+               AND batch_type = '" + batchType + "' \
+               AND loaded_to_ODM = 0 \
+               AND deleted_datastore = 0"
 
         ids = []
@@ -100,24 +191,133 @@
                 ids.append(rs.getInt(1))
         except:
-            self.logger.exception("Can't query for processed batch ids in ipptopsps Db")
+            self.logger.exception("Can't query for unloaded batch ids in ipptopsps Db")
 
         rs.close()
 
-        self.logger.debug("Found %d processed and un-merged items" % len(ids))
-
-        return ids
-
-    '''
-    Returns a list of IDs for this batch type, epoch and dvo label (i.e. either 
-            cam_ids or stack_ids) for which the spefified column is true (=1)
-    '''
-    def getStageIDs(self, batchType, epoch, dvoGpc1Label, column):
-
-        sql = "SELECT DISTINCT stage_id \
+        self.logger.debug("Found %d unloaded items" % len(ids))
+
+        return ids
+
+    '''
+    Returns a list of processed batch IDs that are not yet merge_worthy and that have not failed to load
+    NOTE we exclude batches already deleted from the datastore, as sometimes we delete stuff prior to loading to ODM if there is a known problem with the batch
+    '''
+    def getUnmergeWorthyBatchIDs(self, epoch, dvoGpc1Label, batchType):
+
+        sql = "SELECT batch_id \
+               FROM batch \
+               WHERE timestamp > '" + epoch + "' \
+               AND dvo_db = '" + dvoGpc1Label + "' \
+               AND loaded_to_datastore = 1 \
+               AND batch_type = '" + batchType + "' \
+               AND merge_worthy = 0 \
+               AND loaded_to_ODM != -1 \
+               AND deleted_datastore = 0"
+
+        ids = []
+        try:
+            rs = self.executeQuery(sql)
+            while (rs.next()):
+                ids.append(rs.getInt(1))
+        except:
+            self.logger.exception("Can't query for un-mergeworthy batch ids in ipptopsps Db")
+
+        rs.close()
+
+        self.logger.debug("Found %d unloaded items" % len(ids))
+
+        return ids
+
+    '''
+    Returns a list of processed batch IDs that are merge_worthy, but not yet merged, for this epoch and dvo label
+    '''
+    def getUnmergedBatchIDs(self, epoch, dvoGpc1Label, batchType):
+
+        sql = "SELECT batch_id \
+               FROM batch \
+               WHERE timestamp > '" + epoch + "' \
+               AND dvo_db = '" + dvoGpc1Label + "' \
+               AND merge_worthy = 1 \
+               AND batch_type = '" + batchType + "' \
+               AND merged != 1"
+
+        ids = []
+        try:
+            rs = self.executeQuery(sql)
+            while (rs.next()):
+                ids.append(rs.getInt(1))
+        except:
+            self.logger.exception("Can't query for unmerged batch ids in ipptopsps Db")
+
+        rs.close()
+
+        self.logger.debug("Found %d un-merged items" % len(ids))
+
+        return ids
+
+    '''
+    Returns a list of batch IDs for this epoch and dvo label for which the specified column is set to value
+    '''
+    def getBatchIDs(self, epoch, dvoGpc1Label, column, value):
+
+        sql = "SELECT DISTINCT batch_id \
+               FROM batch \
+               WHERE timestamp > '" + epoch + "' \
+               AND dvo_db = '" + dvoGpc1Label + "' \
+               AND " + column + " = " + str(value)
+
+        ids = []
+        try:
+            rs = self.executeQuery(sql)
+            while (rs.next()):
+                ids.append(rs.getInt(1))
+        except:
+            self.logger.exception("Can't query for batch ids with " + column + " = " + str(value) + " in ipptopsps Db")
+
+        rs.close()
+
+        self.logger.debug("Found %d batches with %s = %d" % (len(ids), column, value))
+
+        return ids
+
+    '''
+    Returns the total detections published to the datastore for this epoch, dvo label and batch type
+    '''
+    def getTotalDetectionsPublished(self, batchType, epoch, dvoGpc1Label):
+
+        sql = "SELECT SUM(total_detections) \
                FROM batch \
                WHERE batch_type = '" + batchType + "' \
                AND timestamp > '" + epoch + "' \
                AND dvo_db = '" + dvoGpc1Label + "' \
-               AND " + column + " = 1"
+               AND processed = 1 \
+               AND loaded_to_datastore = 1"
+
+        total = -1
+        try:
+            rs = self.executeQuery(sql)
+            rs.first()
+            total =  rs.getLong(1)
+        except:
+            self.logger.exception("Can't query for total detections published")
+
+        rs.close()
+
+        self.logger.debug("Found %d detections" % total)
+
+        return total
+
+    '''
+    Returns a list of IDs for this batch type, epoch and dvo label (i.e. either 
+            cam_ids or stack_ids) for which the specified column is set to value
+    '''
+    def getStageIDs(self, batchType, epoch, dvoGpc1Label, column, value):
+
+        sql = "SELECT DISTINCT stage_id \
+               FROM batch \
+               WHERE batch_type = '" + batchType + "' \
+               AND timestamp > '" + epoch + "' \
+               AND dvo_db = '" + dvoGpc1Label + "' \
+               AND " + column + " = " + str(value)
 
         ids = []
@@ -127,5 +327,5 @@
                 ids.append(rs.getInt(1))
         except:
-            self.logger.exception("Can't query for ids with " + column + " = 1 in ipptopsps Db")
+            self.logger.exception("Can't query for ids with " + column + " = " + str(value) + " in ipptopsps Db")
 
         rs.close()
@@ -136,16 +336,61 @@
 
     '''
+    Series of getter methods that utilise getBatchIDs() method above
+    '''
+    def getloadedToDatastoreBatchIDs(self, epoch, dvoGpc1Label):
+        return self.getBatchIDs(epoch, dvoGpc1Label, "loaded_to_datastore", 1)
+    def getFailedLoadedToODMBatchIDs(self, epoch, dvoGpc1Label):
+        return self.getBatchIDs(epoch, dvoGpc1Label, "loaded_to_ODM", -1)
+
+    '''
+    TODO remove these
     Series of getter methods that utilise getStageIDs() method above
     '''
     def getProcessedIDs(self, batchType, epoch, dvoGpc1Label):
-        return self.getStageIDs(batchType, epoch, dvoGpc1Label, "processed")
-    def getloadedToDatastoreIDs(self, batchType, epoch, dvoGpc1Label):
-        return self.getStageIDs(batchType, epoch, dvoGpc1Label, "loaded_to_datastore")
+        return self.getStageIDs(batchType, epoch, dvoGpc1Label, "processed", 1)
+    def getFailedProcessedIDs(self, batchType, epoch, dvoGpc1Label):
+        return self.getStageIDs(batchType, epoch, dvoGpc1Label, "processed", -1)
+    def getLoadedToDatastoreIDs(self, batchType, epoch, dvoGpc1Label):
+        return self.getStageIDs(batchType, epoch, dvoGpc1Label, "loaded_to_datastore", 1)
+    def getFailedLoadedToDatastoreIDs(self, batchType, epoch, dvoGpc1Label):
+        return self.getStageIDs(batchType, epoch, dvoGpc1Label, "loaded_to_datastore", -1)
     def getLoadedToODMIDs(self, batchType, epoch, dvoGpc1Label):
-        return self.getStageIDs(batchType, epoch, dvoGpc1Label, "loaded_to_ODM")
-    def getMergeWothyIDs(self, batchType, epoch, dvoGpc1Label):
-        return self.getStageIDs(batchType, epoch, dvoGpc1Label, "merge_worthy")
+        return self.getStageIDs(batchType, epoch, dvoGpc1Label, "loaded_to_ODM", 1)
+    def getFailedLoadedToODMIDs(self, batchType, epoch, dvoGpc1Label):
+        return self.getStageIDs(batchType, epoch, dvoGpc1Label, "loaded_to_ODM", -1)
+    def getMergeWorthyIDs(self, batchType, epoch, dvoGpc1Label):
+        return self.getStageIDs(batchType, epoch, dvoGpc1Label, "merge_worthy", 1)
+    def getFailedMergeWorthyIDs(self, batchType, epoch, dvoGpc1Label):
+        return self.getStageIDs(batchType, epoch, dvoGpc1Label, "merge_worthy", -1)
     def getMergedIDs(self, batchType, epoch, dvoGpc1Label):
-        return self.getStageIDs(batchType, epoch, dvoGpc1Label, "merged")
+        return self.getStageIDs(batchType, epoch, dvoGpc1Label, "merged", 1)
+    def getFailedMergedIDs(self, batchType, epoch, dvoGpc1Label):
+        return self.getStageIDs(batchType, epoch, dvoGpc1Label, "merged", -1)
+
+    '''
+    Returns a list of stages
+    '''
+    def getStages(self):
+        return ['processed', 'loaded_to_datastore', 'loaded_to_ODM', 'merge_worthy', 'deleted_datastore', 'deleted_dxlayer', 'merged', 'deleted_local' ]
+
+    '''
+    Returns a count of batches processed in the specified time period.
+    '''
+    def countBatchesInLastPeriod(self, batchType, epoch, dvoGpc1Label, interval):
+
+        sql = "SELECT COUNT(batch_id) \
+               FROM batch \
+               WHERE batch_type = '" + batchType + "' \
+               AND processed = 1 \
+               AND timestamp > '" + epoch + "' \
+               AND dvo_db = '" + dvoGpc1Label + "' \
+               AND timestamp BETWEEN now() - INTERVAL " + interval + " AND now()"
+
+        try:
+            rs = self.executeQuery(sql)
+            if rs.first(): return rs.getInt(1)
+        except:
+            self.logger.exception("Unable to count batches in interval")
+            return 0
 
     '''
@@ -155,10 +400,10 @@
     def getTimeOfLastBatchPublished(self, batchType, epoch, dvoGpc1Label):
 
-        minutes = None
-
-        sql = "SELECT TIMESTAMPDIFF(MINUTE, timestamp, now()) \
+        seconds = None
+
+        sql = "SELECT TIMESTAMPDIFF(SECOND, timestamp, now()) \
                FROM batch \
                WHERE batch_type = '" + batchType + "' \
-               AND loaded_to_datastore \
+               AND loaded_to_datastore = 1 \
                AND timestamp > '" + epoch + "' \
                AND dvo_db = '" + dvoGpc1Label + "' \
@@ -167,15 +412,17 @@
         try:
             rs = self.executeQuery(sql)
-            if rs.first(): minutes = rs.getFloat(1)
+            if rs.first(): seconds = rs.getFloat(1)
         except:
             self.logger.exception("Unable to get last batch published")
 
-        if not minutes: return "Never"
-
+        if not seconds: return "Never"
+
+        minutes = seconds/60.0
         hours = minutes/60.0
-        days = float(hours)/24.0
-        weeks = float(days)/7.0
-
-        if minutes < 60: return "%.1f minutes ago" % minutes
+        days = hours/24.0
+        weeks = days/7.0
+
+        if seconds < 60: return "%.1f secs ago" % seconds
+        elif minutes < 60: return "%.1f mins ago" % minutes
         elif hours < 48: return "%.1f hours ago" % hours
         elif days < 7: return "%.1f days ago" % days
@@ -183,63 +430,12 @@
 
     '''
-    TODO
-    '''
-    def getBatchesPerDay(self, batchType, startTime, endTime=""):
-
-        bph = self.getBatchesPerHour(batchType, startTime, endTime)
-        return bph * 24
-
-    '''
-    TODO
-    '''
-    def getBatchesPerHour(self, batchType, startTime, endTime=""):
-
-        bpm = self.getBatchesPerMinute(batchType, startTime, endTime)
-        return bpm * 60
-
-    '''
-    TODO
-    '''
-    def getAverageTimePerBatch(self, batchType, startTime, endTime=""):
-
-        sql = "SELECT (TIMESTAMPDIFF(MINUTE, min(timestamp), max(timestamp)) / COUNT(*)) \
-               FROM batch \
-               WHERE timestamp > '" + startTime + "' and batch_type = '" + batchType + "'"
-
-        try:
-            rs = self.executeQuery(sql)
-            rs.first()
-            time = rs.getDouble(1)
-        except:
-            self.logger.exception("Unable to get batches per minute")
-
-        return time
-
-    '''
-    TODO
-    '''
-    def getBatchesPerMinute(self, batchType, startTime, endTime=""):
-
-        sql = "SELECT (count(*) / TIMESTAMPDIFF(MINUTE, min(timestamp), max(timestamp)) ) \
-               FROM batch \
-               WHERE timestamp > '" + startTime + "' and batch_type = '" + batchType + "'"
-
-        try:
-            rs = self.executeQuery(sql)
-            rs.first()
-            bpm = rs.getDouble(1)
-        except:
-            self.logger.exception("Unable to get batches per minute")
-
-        return bpm
-
-    '''
     Updates min/max object ID on this table and batch 
     '''
-    def updateMinMaxObjID(self, batchID, minObjID, maxObjID):
+    def updateDetectionStats(self, batchID, minObjID, maxObjID, totalDetections):
 
         sql = "UPDATE batch SET \
                min_obj_id = " + str(minObjID) + ", \
-               max_obj_id = " + str(maxObjID) + " \
+               max_obj_id = " + str(maxObjID) + ", \
+               total_detections = " + str(totalDetections) + " \
                WHERE batch_id = " + str(batchID)
 
@@ -257,5 +453,4 @@
         self.execute(sql)
 
-
     '''
     Updates batch processed field 
@@ -265,8 +460,20 @@
 
     '''
-    Updates batch deleted field 
-    '''
-    def updateDeleted(self, batchID, deleted):
-        self.updateColumn(batchID, "deleted", deleted)
+    Updates batch deleted local field 
+    '''
+    def updateDeletedLocal(self, batchID, value):
+        self.updateColumn(batchID, "deleted_local", value)
+
+    '''
+    Updates batch deleted datastore field 
+    '''
+    def updateDeletedDatastore(self, batchID, value):
+        self.updateColumn(batchID, "deleted_datastore", value)
+
+    '''
+    Updates batch deleted dxlayer field 
+    '''
+    def updateDeletedDxlayer(self, batchID, value):
+        self.updateColumn(batchID, "deleted_dxlayer", value)
 
     '''
@@ -290,13 +497,14 @@
 
     '''
-    Have we already processed and published this batch?
-    '''
-    def alreadyProcessed(self, stage_id):
+    Is someone processing this item right now? Checks whether this was attempted within the last 2 HOUR
+    '''
+    def processingNow(self, batchType, stage_id, epoch, dvoGpc1Label):
 
         sql = "SELECT COUNT(*) \
                FROM batch \
                WHERE stage_id = " + str(stage_id) + " \
-               AND processed \
-               AND loaded_to_datastore"
+               AND batch_type = '" + batchType + "' \
+               AND dvo_db = '" + dvoGpc1Label + "' \
+               AND timestamp BETWEEN now() - INTERVAL 4 HOUR AND now()"
 
         try:
@@ -304,11 +512,151 @@
             rs.first()
             if rs.getInt(1) > 0: 
-                self.logger.error("Batch with stage_id = "+str(stage_id) + " has already been processed and published to datastore")
+                self.logger.debugPair(str(stage_id) + " is already being processed", "skipping")
                 return True
             else: 
                 return False
         except:
+            self.logger.exception("Unable to check whether this batch is being processed")
+
+    '''
+    Gets the batch type of this batch
+    '''
+    def getBatchType(self, batch_id):
+
+        sql = "SELECT batch_type \
+               FROM batch \
+               WHERE batch_id = " + str(batch_id)
+
+        batch_type = "NA"
+        try:
+            rs = self.executeQuery(sql)
+            rs.first()
+            batch_type = rs.getString(1) 
+        except:
             self.logger.exception("Unable to check whether this batch has already been processed")
-
+ 
+        return batch_type
+
+    '''
+    Have we already processed this stage_id?
+    '''
+    def alreadyProcessed(self, batchType, stage_id, epoch, dvoGpc1Label):
+
+        sql = "SELECT COUNT(*) \
+               FROM batch \
+               WHERE stage_id = " + str(stage_id) + " \
+               AND batch_type = '" + batchType + "' \
+               AND timestamp > '" + epoch + "' \
+               AND dvo_db = '" + dvoGpc1Label + "' \
+               AND processed = 1"
+
+        try:
+            rs = self.executeQuery(sql)
+            rs.first()
+            if rs.getInt(1) > 0: 
+                self.logger.debugPair(str(stage_id) + " has already been published", "skipping")
+                return True
+            else: 
+                return False
+        except:
+            self.logger.exception("Unable to check whether this batch has already been processed")
+
+    '''
+    Has this stage_id consistently failed to process?
+    '''
+    def consistentlyFailed(self, batchType, stage_id, epoch, dvoGpc1Label):
+
+        sql = "SELECT COUNT(*) \
+               FROM batch \
+               WHERE stage_id = " + str(stage_id) + " \
+               AND batch_type = '" + batchType + "' \
+               AND timestamp > '" + epoch + "' \
+               AND dvo_db = '" + dvoGpc1Label + "' \
+               AND processed = -1"
+
+        try:
+            rs = self.executeQuery(sql)
+            rs.first()
+            if rs.getInt(1) >= self.MAX_FAILS: 
+                self.logger.errorPair(str(stage_id) + " has failed %d times" % self.MAX_FAILS, "skipping")
+                return True
+            else: 
+                return False
+        except:
+            self.logger.exception("Unable to check whether this batch has consistently failed")
+
+    '''
+    Returns a list of stage_ids that have failed the max number of times
+    '''
+    def getConsistentlyFailedIDs(self, batchType, epoch, dvoGpc1Label):
+
+        sql = "SELECT DISTINCT stage_id, COUNT(stage_id) AS numoccur \
+               FROM batch \
+               WHERE batch_type = '" + batchType + "' \
+               AND timestamp > '" + epoch + "' \
+               AND dvo_db = '" + dvoGpc1Label + "' \
+               AND processed = -1 \
+               GROUP BY stage_id HAVING numoccur >= " + str(self.MAX_FAILS)
+
+        ids = []
+        try:
+            rs = self.executeQuery(sql)
+            while (rs.next()):
+                ids.append(rs.getInt(1))
+        except:
+            self.logger.exception("Can't query for consistantly failed ids in ipptopsps Db")
+
+        rs.close()
+
+        self.logger.debug("Found %d consistantly failed stage_ids for batch type='%s'" % (len(ids), batchType))
+
+        return ids
+
+    '''
+    Locks the batch table. This will wait if the lock is held by someone else
+    '''
+    def lockBatchTable(self):
+        self.lockTable("batch")
+
+    '''
+    Creates a new batch. This is done with a locked table in order that no two clients can start work on the same item
+    '''
+    def createNewBatch(self, batchType, stageID, config):
+
+        batchID = -1;
+
+        if config.force or \
+            (not self.alreadyProcessed(batchType, stageID, config.epoch, config.dvoLabel) \
+            and not self.processingNow(batchType, stageID, config.epoch, config.dvoLabel) \
+            and not self.consistentlyFailed(batchType, stageID, config.epoch, config.dvoLabel)): 
+
+            sql = "INSERT INTO batch ( \
+                   batch_type, \
+                   stage_id, \
+                   survey, \
+                   dvo_db, \
+                   datastore_product \
+                   ) VALUES ( \
+                       '" + batchType + "', \
+                       " + str(stageID) + ", \
+                       '" + config.survey + "', \
+                       '" + config.dvoLabel + "', \
+                       '" + config.datastoreProduct + "' \
+                       )"
+
+            self.execute(sql)
+
+            sql = "SELECT MAX(batch_id) FROM batch"
+
+            try:
+                rs = self.executeQuery(sql)
+                rs.first()
+                batchID = rs.getInt(1)
+                self.logger.debug("Created new batch in ippToPsps database with batchID = %d" % batchID)
+            except:
+                self.logger.exception("Unable to get batch ID")
+                batchID = -1
+
+        return batchID;
      
 
@@ -346,4 +694,326 @@
         self.execute(sql)
 
+    '''
+    Resets a batch ready for re-loading to the datastore
+    '''
+    def resetBatch(self, batchID):
+
+        sql = "UPDATE batch SET \
+               loaded_to_datastore = 0, \
+               loaded_to_ODM = 0, \
+               merge_worthy = 0, \
+               merged = 0, \
+               deleted_datastore = 0, \
+               deleted_dxlayer = 0, \
+               comment = 'This batch was reset, ready for re-load to datastore' \
+               WHERE batch_id = " + str(batchID)
+
+        self.execute(sql)
+
+    '''
+    Remove client
+    '''
+    def removeClient(self, host, pid):
+        self.execute("DELETE FROM clients WHERE host = '" + host + "' AND pid = " + str(pid))
+
+    '''
+    Insert client
+    '''
+    def insertClient(self, type, host, pid, config):
+        self.execute("INSERT INTO clients (timestamp, type, host, pid, config) VALUES (now(), '" + type + "', '" + host + "', " + str(pid) + ", '" + config + "')")
+
+    '''
+    Update clients, or inserts it for the first time
+    '''
+    def updateClient(self, type, host, pid, config):
+
+        try:
+            self.insertClient(type, host, pid, config)
+        except:
+            self.execute("UPDATE clients SET timestamp = now() WHERE host = '" + host + "' AND pid = " + str(pid))
+
+    '''
+    Is this client paused?
+    '''
+    def isPaused(self, host, pid):
+        return self.getClientsColumnBoolean("paused", host, pid)
+
+    '''
+    Is this client killed?
+    '''
+    def isKilled(self, host, pid):
+        return self.getClientsColumnBoolean("killed", host, pid)
+
+    '''
+    Returns boolean value for this column in clients table
+    '''
+    def getClientsColumnBoolean(self, col, host, pid):
+
+        sql = "SELECT " + col + " \
+               FROM clients \
+               WHERE host = '" + host + "' AND pid = " + str(pid)
+
+        try:
+            rs = self.executeQuery(sql)
+            rs.first()
+            if rs.getInt(1) == 1: return True
+            else: return False
+        except:
+            self.logger.exception("Unable to check whether " + col + " is true or false in clients table")
+
+        return False
+
+
+    '''
+    Populates Config object
+    '''
+    def readConfig(self, host, pid):
+
+        # first get config defined for this client
+        sql = "SELECT config \
+               FROM clients \
+               WHERE host = '" + host + "' \
+               AND pid = " + str(pid)
+
+        try:
+            rs = self.executeQuery(sql)
+            rs.first()
+            self.config.name = rs.getString(1)
+        except:
+            self.logger.errorPair("No config set for", "%s (pid=%d)" % (host, pid))
+            self.config.isLoaded = False
+            return False
+
+        # now load that config
+        sql = "SELECT \
+        datastore_product, \
+        datastore_type, \
+        datastore_publish, \
+        dvo_label, \
+        dvo_location, \
+        min_ra, \
+        max_ra, \
+        min_dec, \
+        max_dec, \
+        box_size, \
+        base_path, \
+        data_release, \
+        delete_local, \
+        delete_datastore, \
+        delete_dxlayer, \
+        epoch, \
+        survey, \
+        psps_survey, \
+        queue_P2, \
+        queue_ST \
+        FROM config \
+        WHERE name = '" + self.config.name + "'"
+
+        self.config.batchTypes = []
+        try:
+            rs = self.executeQuery(sql)
+            rs.first()
+            self.config.datastoreProduct = rs.getString(1)
+            self.config.datastoreType = rs.getString(2)
+            if rs.getInt(3) == 1: self.config.datastorePublishing = True
+            else: self.config.datastorePublishing = False
+            self.config.dvoLabel = rs.getString(4)
+            self.config.dvoLocation = rs.getString(5)
+            self.config.minRa = rs.getDouble(6)
+            self.config.maxRa = rs.getDouble(7)
+            self.config.minDec = rs.getDouble(8)
+            self.config.maxDec = rs.getDouble(9)
+
+            self.config.boxSize = rs.getDouble(10)
+            self.config.halfBox = self.config.boxSize/2.0
+            self.config.boxSizeWithBorder = self.config.boxSize + (self.config.BORDER * 2)
+
+            self.config.basePath = rs.getString(11)
+            self.config.dataRelease = rs.getInt(12)
+            self.config.deleteLocal = rs.getInt(13)
+            self.config.deleteDatastore = rs.getInt(14)
+            self.config.deleteDxLayer = rs.getInt(15)
+            self.config.epoch = rs.getString(16)
+            self.config.survey = rs.getString(17)
+            self.config.pspsSurvey = rs.getString(18)
+            if rs.getInt(19) == 1: self.config.batchTypes.append("P2")
+            if rs.getInt(20) == 1: self.config.batchTypes.append("ST")
+            self.config.force = False # TODO
+            self.config.test = False # TODO
+            self.config.isLoaded = True
+        except:
+            self.logger.errorPair("Could not read config with name", self.config.name)
+            self.config.isLoaded = False
+
+        return self.config.isLoaded
+
+    '''
+    Gets id of box with these coords
+    '''
+    def getBoxId(self, config, ra, dec, boxSide):
+
+        sql = "SELECT id FROM box \
+               WHERE config  = '" + config + "' \
+               AND ra_center = " + str(ra) + " \
+               AND dec_center = " + str(dec) + " \
+               AND box_side = " + str(boxSide)
+
+        id = -1
+        try:
+            rs = self.executeQuery(sql)
+            rs.first()
+            id = rs.getInt(1)
+        except:
+            pass
+
+        return id
+
+
+    '''
+    Remove all boxes and associated ids in pending table
+    '''
+    def removeAllBoxes(self, config):
+        self.execute("DELETE FROM box WHERE config = '" + config + "'")
+
+    '''
+    Removes a box and it's associated ids in the pending table
+    '''
+    def removeBox(self, config, ra, dec, boxSide):
+        self.execute("DELETE FROM box \
+                WHERE config = '" + config + "' \
+                AND ra_center = " + str(ra) + " \
+                AND dec_center = " + str(dec) + " \
+                AND box_side = " + str(boxSide))
+
+    '''
+    Inserts new box into box table. If it already exisits, it returns existing box id 
+    '''
+    def insertBox(self, config, ra, dec, boxSide):
+
+        id = self.getBoxId(config, ra, dec, boxSide)
+
+        # does not already exists so insert
+        if id < 0:
+
+            sql = "INSERT INTO box \
+                   (config, ra_center, dec_center, box_side) \
+                   VALUES \
+                   ('" + config + "', " + str(ra) + ", " + str(dec) + ", " + str(boxSide) + ")"
+
+            self.execute(sql)
+            id = self.getBoxId(config, ra, dec, boxSide)
+
+        return id
+
+    '''
+    Inserts new box ids into the pending table 
+    '''
+    def insertPending(self, box_id, batchType, ids):
+
+        # first delete old pending items
+        self.execute("DELETE FROM pending WHERE box_id = " + str(box_id))
+
+        for id in ids:
+            sql = "INSERT INTO pending \
+               (box_id, batch_type, stage_id) \
+               VALUES \
+               (" + str(box_id) + ", '" + batchType + "', " + str(id) + ")"
+
+            self.execute(sql)
+
+    '''
+    Returns ids for all boxes with pending items
+    '''
+    def getBoxIds(self, config):
+
+        sql = "SELECT DISTINCT id \
+               FROM box \
+               JOIN pending ON (id = box_id)  \
+               WHERE config = '" + config + "' \
+               ORDER BY ra_center, dec_center"
+
+        ids = []
+        try:
+            rs = self.executeQuery(sql)
+            while (rs.next()): ids.append(rs.getInt(1))
+            rs.close()
+        except:
+            self.logger.errorPair("Can't get boxes", sql)
+
+        return ids
+
+    '''
+    Returns dimensions of this box
+    '''
+    def getBoxDimensions(self, boxId):
+
+        sql = "SELECT ra_center, dec_center, box_side FROM box WHERE id = " + str(boxId)
+
+        boxDim = {}
+        rs = self.executeQuery(sql)
+        rs.first()
+
+        boxDim['RA'] = rs.getFloat(1)
+        boxDim['DEC'] = rs.getFloat(2)
+        boxDim['SIDE'] = rs.getFloat(3)
+
+        return boxDim
+
+    '''
+    Returns ids for pending items in this box for this batch type
+    '''
+    def getPendingIds(self, boxId, batchType):
+
+        sql = "SELECT stage_id FROM pending WHERE batch_type = '" + batchType + "' AND box_id = " + str(boxId)
+
+        ids = []
+        try:
+            rs = self.executeQuery(sql)
+            while (rs.next()): ids.append(rs.getInt(1))
+            rs.close()
+        except:
+            self.logger.errorPair("Can't get ids from this box", sql)
+
+        return ids
+
+    '''
+    Returns max number of pensing items in a box for this config
+    '''
+    def getMaxPendingInBox(self, config):
+
+        sql = "SELECT MAX(pending) \
+               FROM (SELECT COUNT(stage_id) AS pending \
+                       FROM pending \
+                       JOIN box ON (id = box_id) \
+                       WHERE config = '" + config + "' \
+                       GROUP BY box_id) AS t"
+
+        try:
+            rs = self.executeQuery(sql)
+            rs.first()
+            return rs.getInt(1)
+        except:
+            self.logger.errorPair("Could not count max pending", sql)
+            return 100
+
+    '''
+    Writes density-plot data to file for pending stuff over sky for given config
+    '''
+    def createPendingDensityPlotData(self, config, DATFILE):
+
+        sql = "SELECT ra_center, dec_center, COUNT(stage_id) \
+               FROM box \
+               LEFT JOIN pending ON (id = box_id) \
+               WHERE config = '" + config + "' \
+               GROUP BY id \
+               ORDER BY ra_center, dec_center"
+
+        try:
+            rs = self.executeQuery(sql)
+            while (rs.next()):
+                print >> DATFILE, rs.getFloat(1), rs.getFloat(2), rs.getInt(3)
+            rs.close()
+        except:
+            self.logger.errorPair("Can't get density plot data", sql)
 
     '''
@@ -352,5 +1022,5 @@
     def __del__(self):
 
-        self.logger.debug("IppToPspsDb Desstructor")
+        self.logger.debug("IppToPspsDb Destructor")
         self.stmt.close()
         self.con.close()
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/loader.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/loader.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/loader.py	(revision 33415)
@@ -0,0 +1,219 @@
+#!/usr/bin/env jython
+
+#
+# The main loading program for ippToPsps. See usage below.
+#
+
+import time
+import sys
+import os
+import traceback
+import logging.config
+
+from ipptopsps import IppToPsps
+from scratchdb import ScratchDb
+from gpc1db import Gpc1Db
+from datastore import Datastore
+from batch import Batch
+from dvo import Dvo
+
+from initbatch import InitBatch
+from stackbatch import StackBatch
+from detectionbatch import DetectionBatch
+
+'''
+Loader class
+'''
+class Loader(IppToPsps):
+
+    '''
+    Constructor
+    '''
+    def __init__(self, argv):
+        super(Loader, self).__init__(argv)
+
+        # create gpc1 database objects
+        self.gpc1Db = Gpc1Db(self.logger, self.config)
+
+        # connect to scratch database
+        scratchDbs = ['ipptopsps_scratch', 'ipptopsps_scratch2', 'ipptopsps_scratch3']
+        for dbName in scratchDbs:
+            self.scratchDb = ScratchDb(self.logger, self.config, dbName)
+            if self.scratchDb.anyOtherConnections():
+                self.logger.errorPair("This scratch Db is already in use", dbName)
+                self.scratchDb.disconnect()
+                continue
+            self.logger.infoPair("Using scratch Db", self.scratchDb.dbName)
+            break
+
+        if not self.scratchDb.connected: 
+            self.logger.errorPair("Cannot connect to a", "scratch database")
+            raise Exception("No scratch Db")
+        
+        # now pass scratch Db to dvo object
+        self.dvo.setScratchDb(self.scratchDb.dbName)
+
+        # if an IN batch is requested, create and quit
+        if len(sys.argv) > 2 and sys.argv[2] == "init":
+           batchID = self.ippToPspsDb.createNewBatch("IN", 0, self.config)
+           if batchID > 0:
+               batch = InitBatch(self.logger, 
+                       self.config, 
+                       self.gpc1Db, 
+                       self.ippToPspsDb, 
+                       self.scratchDb, 
+                       batchID)
+               batch.run()
+    
+           self.exitProgram("init batch created")
+    
+
+        # set a poll time of about 1 minutes
+        self.parsePollTimeArg("0.0166")
+
+        self.config.printAll()
+
+    '''
+    Overrides base-class version
+    '''
+    def refreshConfig(self):
+
+        ret = super(Loader, self).refreshConfig()
+        self.dvo = Dvo(self.logger, self.config)
+        try: self.dvo.setScratchDb(self.scratchDb.dbName)
+        except: pass
+        self.datastore = Datastore(self.logger, self.config, self.ippToPspsDb)
+
+        return ret
+
+    '''
+    Main processing loop
+    '''
+    def run(self):
+
+        while True:
+
+            boxIds = self.ippToPspsDb.getBoxIds(self.config.name)
+   
+            self.logger.infoPair("Boxes with unprocessed data", "%d" % len(boxIds))
+
+            for boxId in boxIds:
+      
+                self.checkClientStatus()
+
+                # get box info. if boxes have changed, break and start again
+                try:
+                    boxDim = self.ippToPspsDb.getBoxDimensions(boxId)
+                except:
+                   self.logger.infoPair("Can't get details for this box", "%d" % boxId)
+                   break
+                 
+                for batchType in self.config.batchTypes:
+                    
+                    ids = self.ippToPspsDb.getPendingIds(boxId, batchType)
+            
+                    if len(ids) < 1: 
+                        self.logger.debugPair("No " + batchType + " items found in this box", "skipping")
+                        continue
+            
+                    self.logger.infoSeparator()
+                    self.logger.infoPair("Box dimensions", "%.1f / %.1f / %.1f" % (boxDim['RA'], boxDim['DEC'], boxDim['SIDE']))
+                    self.logger.infoPair(batchType + " items found in this box", "%d" % len(ids))
+                    boxSizeWithBorder = boxDim['SIDE'] + (self.config.BORDER * 2)
+   
+                    # look in DVO for this box (with extra border)
+                    self.dvo.setSkyAreaAsBox(boxDim['RA'], boxDim['DEC'], boxSizeWithBorder)
+                    sizeToBeIngested = self.dvo.getDiskSizeOfRegionsToBeIngested()
+                    if sizeToBeIngested == 0.0: smfsPerGB = 999999999
+                    else: smfsPerGB = len(ids)/sizeToBeIngested
+                    self.logger.infoPair("DVO to be ingested", "%.1f GB" % sizeToBeIngested)
+                    self.logger.infoPair("smfs-per-GB", "%.1f" % smfsPerGB)
+            
+                    # should do we pre-ingest stuff from DVO?
+                    if smfsPerGB > 40:
+                        if not self.dvo.sync():
+                            self.logger.errorPair("Could not sync DVO with MySQL", "skipping")
+                            continue
+     
+                        useFullTables = 1
+                    else: useFullTables = 0
+            
+                    self.logger.infoBool("Using pre-ingested DVO data?", useFullTables)
+                    self.processTheseItems(batchType, ids, useFullTables)
+
+            self.checkClientStatus()
+            if not self.waitForPollTime(): break
+
+        
+    '''
+    Actually loops through items and processes them
+    '''
+    def processTheseItems(self, batchType, ids, useFullTables):
+    
+        self.logger.infoPair("%s items queued" % batchType, "%d" % len(ids))
+    
+        # loop round IDs of all items to be processed
+        self.ippToPspsDb.lockBatchTable()
+        unattemptedCount = 0
+        for id in ids:
+    
+            batchID = self.ippToPspsDb.createNewBatch(batchType, id, self.config)
+            
+            if batchID < 0: 
+                unattemptedCount += 1
+                continue
+    
+            self.ippToPspsDb.unlockTables()
+    
+            if batchType == "P2":
+                batch = DetectionBatch(self.logger,
+                        self.config,
+                        self.gpc1Db,
+                        self.ippToPspsDb,
+                        self.scratchDb,
+                        id,
+                        batchID,
+                        useFullTables)
+            elif batchType == "ST":
+                batch = StackBatch(self.logger,
+                        self.config,
+                        self.gpc1Db,
+                        self.ippToPspsDb,
+                        self.scratchDb,
+                        id,
+                        batchID,
+                        useFullTables)
+    
+            batch.run()
+    
+            # check that we should continue
+            self.checkClientStatus()
+    
+            self.ippToPspsDb.lockBatchTable()
+    
+            self.logger.infoSeparator()
+    
+            # if in test mode, then quit after one batch
+            if self.config.test: break
+    
+        self.ippToPspsDb.unlockTables()
+        self.logger.infoPair("Unattempted batches", "%d" % unattemptedCount)
+    
+    '''
+    Overrides base-class version
+    '''
+    def printUsage(self):
+        super(Cleanup, self).printUsage()
+        print " [init]"
+
+    
+'''
+Start of program.
+'''
+try:
+    loader = Loader(sys.argv)
+    loader.run()
+    loader.exitProgram("finished")
+except Exception, e:
+    print str(e)
+    traceback.print_exc() 
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/metrics.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/metrics.py	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/metrics.py	(revision 33415)
@@ -1,126 +1,174 @@
 import math
+import time
 import logging
 import smtplib
 import sys
 import os
+import datetime
 
-from java.lang import Math
-from org.apache.commons.math.special import Erf
-from xml.etree.ElementTree import ElementTree, Element, tostring
-
-from pslogger import PSLogger
-from ipptopspsdb import IppToPspsDb
+from ipptopsps import IppToPsps
+from czardb import CzarDb
 from gpc1db import Gpc1Db
 
 '''
-Makes a gnuplot of the data
+Metrics class
 '''
-def plotMe(batchType, file):
+class Metrics(IppToPsps):
 
-    OUTPUTFILE = "plots/" + DVOLABEL + "_" + batchType + ".png"
-    f=os.popen('gnuplot', 'w')
+    '''
+    Constructor
+    '''
+    def __init__(self, argv):
+        super(Metrics, self).__init__(argv)
 
-    if 0:
-        TERM = "X11"
-    else:
-        TERM = "png font \"/usr/share/fonts/corefonts/arial.ttf\" 8"
+        if len(sys.argv) > 2: self.parsePollTimeArg(sys.argv[2])
+    
+        # create database objects
+        self.czarDb = CzarDb(self.logger, self.config)
+        self.gpc1Db = Gpc1Db(self.logger, self.config)
+   
+    '''
+    Run...
+    '''
+    def run(self):
+
+        while True:
+    
+            self.logger.infoTitle("ippToPsps loading summary")
+            now = datetime.datetime.now()
+            self.logger.infoPair("Time now", now.strftime("%Y-%m-%d %H:%M:%S"))
+            self.logger.infoPair("Loading epoch", self.config.epoch)
+            self.logger.infoPair("DVO label", self.config.dvoLabel)
+            self.config.printBoxCoords()
         
-    print >> f, "set term " + TERM + "; \
-              set output \"" + OUTPUTFILE + "\"; \
-              set title \"ippToPsps : " + batchType + " loading for " + DVOLABEL + "\"; \
-              set boxwidth; \
-              set xtic rotate by -90 scale 0; \
-              set style data histogram; \
-              set style histogram rowstacked; \
-              set style fill solid border -1; \
-              set ylabel \"Items processed per loading stage\"; \
-              set boxwidth 0.75; \
-              plot '" + file + "' using 2:xtic(1) notitle lt 2, '' using 3 notitle lt 1, '' using 4 notitle lt rgb \"#808080\""
+            self.logger.info("+----+------------------+---------------+-------------------+------------------+----------------+")
+            self.logger.info("|Type| batches per hour | last 24 hours | per day this week | total detections | last published |")
+            self.logger.info("+----+------------------+---------------+-------------------+------------------+----------------+")
+            rateP2 = self.printStats("P2")
+            rateST = self.printStats("ST")
+            self.logger.info("+----+------------------+---------------+-------------------+------------------+----------------+")
+        
+            self.logger.info("")
+        
+            stages = self.ippToPspsDb.getStages()
+            colCount = len(stages)
+            self.writeTableSeparator(colCount)
+            sys.stdout.write("|Type|  DVO  ")
+        
+            for stage in stages: sys.stdout.write("|%19s" % stage)
+            sys.stdout.write("|\n")
+            sys.stdout.write("|    |       ")
+            for stage in stages: sys.stdout.write("| Pend  Succ  Fail  ")
+            sys.stdout.write("|\n")
+            self.writeTableSeparator(colCount)
+            pendP2Processed = self.printTableRow(stages, "P2")
+            pendSTProcessed = self.printTableRow(stages, "ST")
+            self.writeTableSeparator(colCount)
 
-    f.flush()
+            self.logger.info("")
+            try: self.logger.infoPair("Estimated time for P2s", "%.1f hours" % (pendP2Processed / rateP2)) 
+            except: pass
+            try: self.logger.infoPair("Estimated time for STs", "%.1f hours" % (pendSTProcessed / rateST)) 
+            except: pass
+       
+            self.checkClientStatus()
+            if not self.waitForPollTime(): break
 
-'''
-Prepares a string of the format:
 
-n-processed(n-pending)
+    '''
+    Formats an int to a string
+    '''
+    def getIntAsString(self, value):
+        if value == 0: return ""
+        return str(value)
+    
+    '''
+    Prints (to the log) one row of data (one row being one batch-type)
+    '''
+    def printTableRow(self, stages, batchType):
+    
+        # get a master list of IDs in DVO for this batch type
+        #allIDs = self.gpc1Db.getIDsInThisDVODbForThisStage(self.config.dvoLabel, batchType, self.config.minRa, self.config.maxRa, self.config.minDec, self.config.maxDec)
+        allIDs = self.gpc1Db.getIDsInThisDVODbForThisStage(self.config.dvoLabel, batchType)
+        prevList = allIDs
+        numEverything = len(allIDs)
+    
+        sys.stdout.write("| %2s |%6d " % (batchType, numEverything))
+    
+        tempFilename = batchType + '.dat'
+        DATFILE = open(tempFilename,'w')
+        print >> DATFILE, "DVO", numEverything, 0, 0
+        for stage in stages:
+    
+           # get lists. Use unions with prev list to make sure the right stuff is included
+           success = list(set(self.ippToPspsDb.getStageIDs(batchType, self.config.epoch, self.config.dvoLabel, stage, 1)) & set(prevList)) 
+           fail = list(set(self.ippToPspsDb.getStageIDs(batchType, self.config.epoch, self.config.dvoLabel, stage, -1)) & set(prevList) - set(success))
+           pending = list(set(prevList) - set(success) - set(fail))
+    
+           # count stuff
+           numSuccess = len(success)
+           numFail = len(fail)
+           numPending = len(pending)
+    
+           #if stage == 'loaded_to_datastore' and batchType == "P2":
+           #    for n in fail: print n
+    
+           str = "\033[1;34m%5s\033[1;m \033[1;32m%5s\033[1;m \033[1;31m%5s\033[1;m  " % (self.getIntAsString(numPending), self.getIntAsString(numSuccess), self.getIntAsString(numFail))
+           sys.stdout.write("|%-19s" % str)
+    
+           prevList = success
+           print >> DATFILE, stage, numSuccess, numFail, numPending
+    
+           self.czarDb.insertStats(stage, self.config.dvoLabel, batchType, numPending, numSuccess, numFail)
+    
+           if stage == 'processed': numPendingProcessed = numPending
+    
+        sys.stdout.write("|\n")
+        DATFILE.close()
+    
+        return numPendingProcessed
+    
+    '''
+    Writes table separator
+    '''
+    def writeTableSeparator(self, colCount):
+    
+        sys.stdout.write("+----+-------")
+        for i in range(0, colCount): sys.stdout.write("+-------------------")
+        sys.stdout.write("+\n")
+    
+    '''
+    Prints various timing stats for this batch type
+    '''
+    def printStats(self, batchType):
+    
+        rate = self.ippToPspsDb.countBatchesInLastPeriod(batchType, self.config.epoch, self.config.dvoLabel, "1 HOUR") / 1.0 
+    
+        self.logger.info("| %2s | %16.1f | %13d | %17.1f | %16d | %14s |" % 
+                (batchType,
+                 rate,
+                 self.ippToPspsDb.countBatchesInLastPeriod(batchType, self.config.epoch, self.config.dvoLabel, "24 HOUR"),
+                 self.ippToPspsDb.countBatchesInLastPeriod(batchType, self.config.epoch, self.config.dvoLabel, "1 WEEK") / 7.0, 
+                 self.ippToPspsDb.getTotalDetectionsPublished(batchType, self.config.epoch, self.config.dvoLabel),
+                 self.ippToPspsDb.getTimeOfLastBatchPublished(batchType, self.config.epoch, self.config.dvoLabel)))
+    
+        return rate
+   
 
-'''
-def prepareStr(masterList, prevList, thisList, FILE, name):
+    '''
+    Overrides base-class version
+    '''
+    def printUsage(self):
+        super(Metrics, self).printUsage()
+        print " [hours]"
 
-    unsuccessful = list(set(prevList) - set(thisList))
 
-    countTotal = len(masterList)
-    countSuccessful = len(thisList)
-    countUnsuccessful = len(unsuccessful)
-
-    str = "%d" % countSuccessful
-    if countUnsuccessful: str = str + "(%d)" % countUnsuccessful
-
-    print >> FILE, name, countSuccessful, countUnsuccessful, (countTotal - countSuccessful - countUnsuccessful)
-    #;sys.stdout.softspace=0; 
-    return str
-
-'''
-Prints (to the log) one row of data (one row being one batch-type)
-'''
-def printTableRow(batchType):
-
-    allIDs = gpc1Db.getIDsInThisDVODbForThisStage(DVOLABEL, batchType)
-    processedIDs = ippToPspsDb.getProcessedIDs(batchType, EPOCH, DVOLABEL)
-    datastoreIDs = ippToPspsDb.getloadedToDatastoreIDs(batchType, EPOCH, DVOLABEL)
-    loadedToODMIDs = ippToPspsDb.getLoadedToODMIDs(batchType, EPOCH, DVOLABEL)
-    mergeWorthyIDs = ippToPspsDb.getMergeWothyIDs(batchType, EPOCH, DVOLABEL)
-    mergedIDs = ippToPspsDb.getMergedIDs(batchType, EPOCH, DVOLABEL)
-
-    tempFilename = batchType + '.dat'
-    DATFILE = open(tempFilename,'w')
-    prepareStr(allIDs, allIDs, allIDs, DATFILE, "inDVO"),
-
-    logger.info("|  %2s  |  %-12d  |  %-12s  |  %-12s  |  %-12s  |  %-12s  |  %-12s  |  %-12s  |  %-19s |" % (
-                batchType, 
-                len(allIDs), 
-                prepareStr(allIDs, allIDs, processedIDs, DATFILE, "processed"),
-                prepareStr(allIDs, processedIDs, datastoreIDs, DATFILE, "onDatastore"),
-                prepareStr(allIDs, datastoreIDs, loadedToODMIDs, DATFILE, "loadedToODM"),
-                prepareStr(allIDs, loadedToODMIDs, mergeWorthyIDs, DATFILE, "MergeWorthy"),
-                prepareStr(allIDs, mergeWorthyIDs, mergedIDs, DATFILE, "Merged"),
-                len(list(set(allIDs) - set(mergedIDs))),
-                ippToPspsDb.getTimeOfLastBatchPublished(batchType, EPOCH, DVOLABEL)))
-
-    plotMe(batchType, tempFilename)
-    DATFILE.close()
-    #os.remove(tempFilename)
 
 '''
 Start of program.
 '''
-if len(sys.argv) > 1: CONFIG  = sys.argv[1]
-else:
-    print "** Usage: " + sys.argv[0] + " <configPath>"
-    sys.exit(1)
-
-# open config file
-configDoc = ElementTree(file=CONFIG)
-
-logging.setLoggerClass(PSLogger)
-logger = logging.getLogger("metrics")
-logger.setup(configDoc, "metrics")
-
-ippToPspsDb = IppToPspsDb(logger, configDoc)
-gpc1Db = Gpc1Db(logger, configDoc)
-
-DVOLABEL = configDoc.find("dvo/gpc1Label").text
-EPOCH = configDoc.find("options/epoch").text
-
-logger.infoTitle("ippToPsps loading summary")
-logger.infoPair("Loading epoch", EPOCH)
-logger.infoPair("DVO label", DVOLABEL)
-logger.info("")
-logger.info("+------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------------+")
-logger.info("| Type |  Total in DVO  |   Processed    |  On datastore  | Loaded to ODM  |  Merge-worthy  |     Merged     |  Total missing |    Last published    |")
-logger.info("+------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------------+")
-printTableRow("P2")
-printTableRow("ST")
-logger.info("+------+----------------+----------------+----------------+----------------+----------------+----------------+----------------+----------------------+")
+metrics = Metrics(sys.argv)
+metrics.run()
+metrics.exitProgram("finished")
 
 
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/mysql.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/mysql.py	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/mysql.py	(revision 33415)
@@ -21,16 +21,17 @@
 
     '''
-    def __init__(self, logger, doc, dbType):
+    def __init__(self, logger, config, dbType, dbName=None):
 
         # set up logging
         self.logger = logger
-        self.doc = doc
+        self.config = config
         self.logger.debug("MySql class constructor")
 
         # open config and grab database parameters
-        self.dbName = self.doc.find(dbType +"/name").text
-        self.dbHost = self.doc.find(dbType +"/host").text
-        self.dbUser = self.doc.find(dbType +"/user").text
-        self.dbPass = self.doc.find(dbType +"/password").text
+        if not dbName: self.dbName = config.getDbName(dbType)
+        else: self.dbName = dbName
+        self.dbHost = config.getDbHost(dbType)
+        self.dbUser = config.getDbUser(dbType)
+        self.dbPass = config.getDbPassword(dbType)
 
         # set up JDBC connection
@@ -42,9 +43,10 @@
         except:
             self.logger.error("Unable to connect to " + self.url)
-            self.everythingOK = False
+            self.everythingOK = False # TODO need this?
+            self.connected = False
             return
 
         self.everythingOK = True
-
+        self.connected = True
 
     '''
@@ -53,4 +55,5 @@
     def disconnect(self):
         self.con.close()
+        self.connected = False
 
     '''
@@ -61,4 +64,16 @@
         self.logger.debug("MySql destructor")
         self.disconnect()
+    
+    '''
+    Locks a table. This is wait if the locks is already held by another session
+    '''
+    def lockTable(self, table):
+        self.execute("LOCK TABLES " + table + " WRITE")
+
+    '''
+    Unlocks all tables locked by this session.
+    '''
+    def unlockTables(self):
+        self.execute("UNLOCK TABLES")
 
     '''
@@ -69,9 +84,28 @@
         connectionID = self.getLastConnectionID()
         if connectionID == self.connectionID:
-            self.logger.error("NOT going to kill THIS connection ID")
+            self.logger.debug("NOT going to kill THIS connection ID")
             return
 
         sql = "KILL %d" % connectionID
         self.execute(sql)
+
+    '''
+    Checks whether there are any other connections to this database
+    '''
+    def anyOtherConnections(self):
+
+        sql = "SELECT COUNT(*) \
+               FROM INFORMATION_SCHEMA.PROCESSLIST \
+               WHERE DB='" + self.dbName + "'"
+        try:
+            rs = self.executeQuery(sql)
+            rs.first()
+            if rs.getInt(1) > 1: return True
+            else: return False
+        except:
+            self.logger.errorPair("Could not check for connection", sql)
+            pass
+
+        return True
 
     '''
@@ -97,4 +131,24 @@
 
     '''
+    OPTIMIZEs a table
+    '''
+    def optimizeTable(self, table):
+
+        sql = "OPTIMIZE TABLE " + table
+        try: self.execute(sql)
+        except: return
+
+    '''
+    Creates a MySQL-friendly table name from the provided string
+    '''
+    def getDbFriendlyTableName(self, origName):
+
+        name = origName
+        name = name.replace('.', '_')
+        name = name.replace('/', '_')
+
+        return name
+
+    '''
     Drops a table
     '''
@@ -215,7 +269,9 @@
         nBad = rs.getInt(1)
 
-        sql="DELETE from " + tableName + " WHERE " + columnName + " = " + value
+        sql="DELETE FROM " + tableName + " WHERE " + columnName + " = " + value
         self.execute(sql)
-        self.logger.info("%s '%s' values in %s    %5d deleted" % (columnName, value, tableName, nBad))
+        self.logger.debugPair("%s rows with %s" % (columnName, value), "%d deleted" % nBad)
+
+        return nBad
 
     '''
@@ -229,7 +285,7 @@
         nBad = rs.getInt(1)
 
-        sql="DELETE from " + tableName + " WHERE " + columnName + " IS NULL"
+        sql="DELETE FROM " + tableName + " WHERE " + columnName + " IS NULL"
         self.execute(sql)
-        self.logger.debugPair("NULL %s values" % columnName, "%d deleted" % nBad)
+        self.logger.debugPair("NULL %s values in %s" % (columnName, tableName), "%d deleted" % nBad)
 
         return nBad
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/odm.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/odm.py	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/odm.py	(revision 33415)
@@ -2,4 +2,6 @@
 
 import re
+import os
+import signal
 import sys
 import tempfile
@@ -33,7 +35,8 @@
         p = Popen("../perl/checkOdmStatus.pl -b " + batch + " -f " + tempFile.name, 
                 shell=True, 
-                stdout=PIPE)
+                stdout=PIPE,
+                close_fds=True)
         p.wait()
-        
+       
         if p.returncode != 0:
            self.logger.debugPair("ODM polling", "failed")
@@ -45,4 +48,6 @@
 
         tempFile.close()
+        p.stdout.close()
+
 
         if not ret: self.logger.debugPair("Cannot read ODM file for batch", "%d" % batchID)
@@ -58,5 +63,4 @@
 
         loadedToODM = 0
-        loadFailed = 0
         mergeWorthy = 0
         merged = 0
@@ -66,14 +70,26 @@
             STATE = doc.find("{%s}OdmBatchState/{%s}BatchState" % (NAMESPACE, NAMESPACE)).text
             MESSAGE = doc.find("{%s}OdmBatchState/{%s}Message" % (NAMESPACE, NAMESPACE)).text
+            results['MESSAGE'] = MESSAGE
+            results['DETAILS'] = "None"
 
-            if re.search("failed", STATE): loadFailed = 1
-            if re.search("MergeWorthy", MESSAGE): mergeWorthy = 1
-            if re.search("Merge[1-9]Completed", MESSAGE): merged = 1
-            if mergeWorthy or merged or loadFailed: loadedToODM = 1
+            # we assume that batches 'OnHold' are failed as it is very rare for such
+            # batches to have there state changed
+            if re.search("failed", STATE) or re.search("OnHold", MESSAGE):
+                loadedToODM = -1
+                DETAILS = doc.find("{%s}OdmBatchState/{%s}Details" % (NAMESPACE, NAMESPACE)).text
+                results['DETAILS'] = DETAILS
+            if re.search("processing", STATE): 
+                loadedToODM = 1
+            if re.search("MergeWorthy", MESSAGE): 
+                loadedToODM = 1
+                mergeWorthy = 1
+            if re.search("Merge[1-9]Completed", MESSAGE): 
+                loadedToODM = 1
+                mergeWorthy = 1
+                merged = 1
         except:
-            return 0
+            pass
 
         results['LOADEDTOODM'] = loadedToODM
-        results['LOADFAILED'] = loadFailed
         results['MERGEWORTHY'] = mergeWorthy
         results['MERGED'] = merged
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/plotter.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/plotter.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/plotter.py	(revision 33415)
@@ -0,0 +1,70 @@
+#!/usr/bin/env jython
+
+import tempfile
+import os
+import sys
+
+from ipptopsps import IppToPsps
+
+
+'''
+Plotter class
+'''
+class Plotter(IppToPsps):
+
+    '''
+    Constructor
+    '''
+    def __init__(self, argv):
+        super(Plotter, self).__init__(argv)
+
+    '''
+    Runs...
+    '''
+    def run(self):
+
+        tempFilename = "./plotData.dat"
+        DATFILE = open(tempFilename,'w')
+        self.ippToPspsDb.createPendingDensityPlotData(self.config.name, DATFILE)
+        DATFILE.close()
+
+        OUTPUTFILE = self.config.name + ".png"
+        f = os.popen('gnuplot', 'w')
+            
+        max = self.ippToPspsDb.getMaxPendingInBox(self.config.name)
+
+        if 0:
+            TERM = "X11"
+        else:
+            TERM = "png font \"/usr/share/fonts/corefonts/arial.ttf\" 8"
+                     
+        print >> f, "set term " + TERM + "; \
+            set output \"" + OUTPUTFILE + "\"; \
+            set title \"Density plot of unprocessed stuff\"; \
+            set grid; \
+            set xrange [" + str(self.config.minRa) + ":" + str(self.config.maxRa) + "] reverse; \
+            set yrange [" + str(self.config.minDec) + ":" + str(self.config.maxDec) + "]; \
+            unset key; \
+            set palette rgb 22,13,10; \
+            set ylabel \"Dec\"; \
+            set ylabel \"RA\"; \
+            set cbrange [0:" + str(max) + "]; \
+            set datafile missing \"NaN\"; \
+            plot '" + tempFilename + "' using 1:2:3 with image"
+
+        f.flush()
+
+    '''
+    Overrides base-class version
+    '''
+    def printUsage(self):
+        super(Plotter, self).printUsage()
+        print " [<density>]"
+
+
+'''
+Start of program
+'''
+plotter = Plotter(sys.argv)
+plotter.run()
+plotter.exitProgram("finished")
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/pollOdm.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/pollOdm.py	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/pollOdm.py	(revision 33415)
@@ -6,56 +6,133 @@
 import re
 import sys
+import time
 
-import logging.config
-from pslogger import PSLogger
-from ipptopspsdb import IppToPspsDb
-from gpc1db import Gpc1Db
+from ipptopsps import IppToPsps
+from config import Config
 from odm import Odm
 from batch import Batch
 
 
-if len(sys.argv) > 1: CONFIG  = sys.argv[1]
-else:
-    print "\n** Usage: " + sys.argv[0] + " <configPath>\n"
-    sys.exit(1)
+'''
+PollOdm class
+'''
+class PollOdm(IppToPsps):
 
-# open config file
-configDoc = ElementTree(file=CONFIG)
+    '''
+    Constructor
+    '''
+    def __init__(self, argv):
+        super(PollOdm, self).__init__(argv)
 
-# set up logging
-logging.setLoggerClass(PSLogger)
-logger = logging.getLogger(sys.argv[0])
-logger.setup(configDoc, sys.argv[0])
+        # get batch and stage args
+        if len(sys.argv) > 3: 
+            self.BATCHTYPE = sys.argv[2]
+            self.STAGE = sys.argv[3]
+        else:
+            self.printUsage()
+            self.exitProgram("incorrect args")
 
-ippToPspsDb = IppToPspsDb(logger, configDoc)
-gpc1Db = Gpc1Db(logger, configDoc)
-odm = Odm(logger, configDoc)
+        # get poll time arg
+        if len(sys.argv) > 4: self.parsePollTimeArg(sys.argv[4]) 
 
-DVOLABEL = configDoc.find("dvo/gpc1Label").text
-EPOCH = configDoc.find("options/epoch").text
+        if self.STAGE == "all": self.stages = ['unloaded', 'unmergeworthy', 'unmerged']
+        else: self.stages = [self.STAGE]
 
-ids = ippToPspsDb.getProcessedButUnmergedBatchIDs(EPOCH, DVOLABEL)
+        if self.BATCHTYPE == "all": self.batchTypes = ['P2', 'ST']
+        else: self.batchTypes = [self.BATCHTYPE]
 
-logger.infoPair("Processed but un-merged batches", "%d" % len(ids))
+        for stage in self.stages: self.logger.infoPair("Checking stage", stage)
+        for batchType in self.batchTypes: self.logger.infoPair("Checking batch type", batchType)
 
+    '''
+    Overrides base-class version
+    '''
+    def refreshConfig(self):
+        ret = super(PollOdm, self).refreshConfig()
+        self.odm = Odm(self.logger, self.config)
+        return ret
 
-results = {}
+    '''
+    Run...
+    '''
+    def run(self):
 
-logger.info("+-----------+----------------+----------------+----------------+----------------+")
-logger.info("|   Batch   | Loaded to ODM  |  Load failed   |  Merge worthy  |     Merged     |")
-logger.info("+-----------+----------------+----------------+----------------+----------------+")
-for id in ids:
+        while True:
 
-   if odm.checkBatch(id, results):
-       ippToPspsDb.updateOdmStatus(id, results)
-       logger.info("| %9s | %14d | %14d | %14d | %14d |" %
-               (Batch.getNameFromID(id),
-                results['LOADEDTOODM'],
-                results['LOADFAILED'],
-                results['MERGEWORTHY'],
-                results['MERGED']))
-   else:
-       logger.info("| %9s | failed to poll ODM                                                |" % Batch.getNameFromID(id))
+            for stage in self.stages: 
+                for batchType in self.batchTypes:
+                    self.poll(batchType, stage)
 
-logger.info("+-----------+----------------+----------------+----------------+----------------+")
+            self.checkClientStatus()
+            if not self.waitForPollTime(): break
 
+    '''
+    Method to actually poll ODM
+    '''
+    def poll(self, batchType, stage):
+      
+        if stage == "unloaded":
+            ids = self.ippToPspsDb.getUnloadedBatchIDs(self.config.epoch, self.config.dvoLabel, batchType)
+        elif stage == "unmergeworthy":
+            ids = self.ippToPspsDb.getUnmergeWorthyBatchIDs(self.config.epoch, self.config.dvoLabel, batchType)
+        elif stage == "unmerged":
+            ids = self.ippToPspsDb.getUnmergedBatchIDs(self.config.epoch, self.config.dvoLabel, batchType)
+    
+        self.logger.infoTitle("Checking %d %s %s batches" % (len(ids), stage, batchType))
+    
+        results = {}
+        newLoaded = 0
+        newMergeWorthy = 0
+        newMerged = 0
+    
+        self.logger.info("+-----------+----------------+----------------+----------------+----------------+----------------+----------------+")
+        self.logger.info("|   Batch   | Loaded to ODM  |  Merge worthy  |     Merged     |   New loaded   |New merge worthy|   New merged   |")
+        self.logger.info("+-----------+----------------+----------------+----------------+----------------+----------------+----------------+")
+        for id in ids:
+    
+            if self.odm.checkBatch(id, results):
+               self.ippToPspsDb.updateOdmStatus(id, results)
+    
+               if stage == "unloaded":
+                   if results['LOADEDTOODM'] == 1: newLoaded = newLoaded + 1
+                   if results['MERGEWORTHY'] == 1: newMergeWorthy = newMergeWorthy + 1
+                   if results['MERGED'] == 1: newMerged = newMerged + 1
+    
+               elif stage == "unmergeworthy":
+                   if results['MERGEWORTHY'] == 1: newMergeWorthy = newMergeWorthy + 1
+                   if results['MERGED'] == 1: newMerged = newMerged + 1
+
+               elif stage == "unmerged":
+                   if results['MERGED'] == 1: newMerged = newMerged + 1
+    
+               self.logger.info("| %9s | %14d | %14d | %14d | %14d | %14d | %14d |" %
+                       (Batch.getNameFromID(id),
+                            results['LOADEDTOODM'],
+                    results['MERGEWORTHY'],
+                        results['MERGED'],
+                        newLoaded,
+                        newMergeWorthy,
+                        newMerged))
+    
+    
+            else:
+                    self.logger.info("| %9s | failed to poll ODM                                                                                                   |" % Batch.getNameFromID(id))
+    
+        self.logger.info("+-----------+----------------+----------------+----------------+----------------+----------------+----------------+")
+    
+
+    '''
+    Overrides base-class version
+    '''
+    def printUsage(self):
+        super(PollOdm, self).printUsage()
+        print " <P2|ST|all> <unloaded|unmergeworthy|unmerged|all> [hours]"
+    
+    
+    
+'''
+Program starts here
+'''
+pollodm = PollOdm(sys.argv)
+pollodm.run()
+pollodm.exitProgram("finished")
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/pslogger.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/pslogger.py	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/pslogger.py	(revision 33415)
@@ -3,11 +3,13 @@
 import os
 import sys
+import socket
 
 class PSLogger(logging.getLoggerClass()): 
 
    '''
-   Sets up logging using values from the ippToPsps config file
+   Sets up logging using provided values and this process hostname and PID if sending to file
+   This will default to stout and no file output
    '''
-   def setup(self, doc, name, stdout=1, sendToFile=0):
+   def setup(self, programName, basePath, configName, host, pid, stdout=1, sendToFile=0):
 
        formatter = logging.Formatter('%(asctime)s | %(levelname)7s | %(message)s', '%Y-%m-%d %H:%M:%S')
@@ -15,9 +17,9 @@
 
        if sendToFile:
-           PATH = doc.find("localOutPath").text + "/log"
+
+           # generate path
+           PATH = basePath + "/log"
            if not os.path.exists(PATH): os.makedirs(PATH)
-           DVOLABEL = doc.find("dvo/gpc1Label").text
-           FULLPATH = PATH + "/" + name + "_" + DVOLABEL + ".log"
-
+           FULLPATH = PATH + "/" + programName + "_" + configName + "_" + host + "_" + str(pid) + ".log"
            # opens file to be appended
            hdlr = logging.FileHandler(FULLPATH, "a")
@@ -27,5 +29,5 @@
        if stdout:
            stdout = logging.StreamHandler(sys.__stdout__)
-           #stdout.setFormatter(formatter)
+           stdout.setFormatter(formatter)
            self.addHandler(stdout)
 
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/queue.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/queue.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/queue.py	(revision 33415)
@@ -0,0 +1,137 @@
+#!/usr/bin/env jython
+
+#
+# The main loading program for ippToPsps. See usage below.
+#
+
+import time
+import sys
+import os
+import logging.config
+
+from ipptopsps import IppToPsps
+from gpc1db import Gpc1Db
+from datastore import Datastore
+from batch import Batch
+
+'''
+Queue class
+'''
+class Queue(IppToPsps):
+
+    '''
+    Constructor
+    '''
+    def __init__(self, argv):
+        super(Queue, self).__init__(argv)
+
+        # create various objects
+        self.gpc1Db = Gpc1Db(self.logger, self.config)
+
+        # set a poll time in hours
+        self.parsePollTimeArg("12")
+    
+
+    '''
+    Main processing loop.
+    Tiles area defined in config and looks for available stage_ids for each tile and writes them to ippToPsps database ready for processing
+    by one or more loading clients that may be running on any host
+    '''
+    def run(self):
+
+        # this outer while loop simply waits a few minutes then starts queuing again as more stuff may appear in DVO over time
+        while True:
+        
+            # queue up batches that are processed but not loaded to datastore
+            self.logger.infoTitle("Previous failed datastore loads")
+            for batchType in self.config.batchTypes: self.publishAnyUnpublishedBatches(batchType)
+
+            # get totals for whole area to check if there is anything to do
+            processedIDs = self.ippToPspsDb.getProcessedIDs(batchType, self.config.epoch, self.config.dvoLabel)
+            consistantlyFailedIDs = self.ippToPspsDb.getConsistentlyFailedIDs(batchType, self.config.epoch, self.config.dvoLabel)
+            allIDs = self.gpc1Db.getIDsInThisDVODbForThisStage(self.config.dvoLabel, batchType, self.config.minRa, self.config.maxRa, self.config.minDec, self.config.maxDec)
+            #allIDs = self.gpc1Db.getIDsInThisDVODbForThisStage(self.config.dvoLabel, batchType)
+            ids = list(set(allIDs) - set(processedIDs) - set(consistantlyFailedIDs))
+
+            # loop through full range of RA/Dec queueing stuff in boxes of size self.config.boxSize
+            self.logger.info("+---------------------------------------------------------------------+")
+            self.logger.info("|                All unprocessed items in area: %12d          |" % len(ids))
+
+            if len(ids) > 0:
+
+                self.logger.info("+-------------+-------------+-------------+-------------+-------------+")
+                self.logger.info("|     RA      |     Dec     |   box size  |    in DVO   | unprocessed |")
+                self.logger.info("+-------------+-------------+-------------+-------------+-------------+")
+    
+                # starting positions
+                ra = self.config.minRa + self.config.halfBox
+                dec = self.config.minDec + self.config.halfBox
+    
+                while ra <= self.config.maxRa:
+                   while dec <= self.config.maxDec:
+            
+                       self.checkClientStatus()
+                       box_id = self.ippToPspsDb.insertBox(self.config.name, ra, dec, self.config.boxSize)
+            
+                       # for each batch type look for items to queue in this box
+                       for batchType in self.config.batchTypes: 
+            
+                           allIDs = self.gpc1Db.getIDsInThisDVODbForThisStageInThisBox(
+                                   self.config.dvoLabel, 
+                                   batchType, 
+                                   ra, 
+                                   dec, 
+                                   self.config.boxSize)
+    
+                           if len(allIDs) < 1:
+                               self.logger.info("|    %5.1f    |    %5.1f    |  %9d  |  %9d  |  %9d  |" % (
+                                           ra, 
+                                           dec, 
+                                           self.config.boxSize, 
+                                           len(allIDs), 
+                                           0))
+                               continue    
+    
+                           ids = list(set(allIDs) - set(processedIDs) - set(consistantlyFailedIDs))
+                           self.logger.info("|    %5.1f    |    %5.1f    |  %9d  |  %9d  |  %9d  |" % (
+                                       ra, 
+                                       dec, 
+                                       self.config.boxSize, 
+                                       len(allIDs), 
+                                       len(ids)))
+                           if len(ids) < 1: continue
+            
+                           #self.logger.infoPair("write IDs to database", "NOW")
+                           self.ippToPspsDb.insertPending(box_id, batchType, ids)
+
+                       dec = dec + self.config.boxSize
+                   dec = self.config.minDec + self.config.halfBox
+                   ra = ra + self.config.boxSize
+            
+            self.logger.info("+-------------+-------------+-------------+-------------+-------------+")
+
+            if not self.waitForPollTime(): break
+
+    '''
+    Finds batches that processed ok, but did not get loaded to the datastore, then tries to load them
+    '''
+    def publishAnyUnpublishedBatches(self, batchType):
+    
+        batchIDs = self.ippToPspsDb.getProcessedButFailedDatastoreBatchIDs(self.config.epoch, self.config.dvoLabel, batchType)
+        self.logger.infoPair("%s batches" % batchType, "%d" % len(batchIDs))
+    
+        for batchID in batchIDs:
+    
+            batchName = Batch.getNameFromID(batchID)
+            subDir = Batch.getSubDir(self.config.basePath, batchType, self.config.dvoLabel)
+            tarballFile = Batch.getTarballFile(batchID)
+            self.logger.infoPair("Batch name", batchName)
+    
+            Batch.publishToDatastore(self.datastore, batchID, batchName, subDir, tarballFile)
+    
+'''
+Start of program.
+'''
+queue = Queue(sys.argv)
+queue.run()
+queue.exitProgram("finished")                             
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/regionTest.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/regionTest.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/regionTest.py	(revision 33415)
@@ -0,0 +1,96 @@
+from pslogger import PSLogger
+from ipptopspsdb import IppToPspsDb
+from gpc1db import Gpc1Db
+from dvo import Dvo
+
+import sys
+from xml.etree.ElementTree import ElementTree, Element, tostring
+import logging
+
+'''
+Start of program.
+'''
+
+if len(sys.argv) > 1: CONFIG  = sys.argv[1]
+else:
+    print "** Usage: " + sys.argv[0] + " <configPath>"
+    sys.exit(1)
+
+# open config file
+configDoc = ElementTree(file=CONFIG)
+
+logging.setLoggerClass(PSLogger)
+logger = logging.getLogger("regionTest")
+logger.setup(configDoc, "regionTest")
+
+
+gpc1Db = Gpc1Db(logger, configDoc)
+dvo = Dvo(logger, configDoc)
+#dvo.resetAllTables()
+
+DVOLABEL = configDoc.find("dvo/gpc1Label").text
+
+# get equatorial coord constraints, if any
+try:
+    MINRA = float(configDoc.find("dvo/minRA").text)
+    MAXRA = float(configDoc.find("dvo/maxRA").text)
+    MINDEC = float(configDoc.find("dvo/minDec").text)
+    MAXDEC = float(configDoc.find("dvo/maxDec").text)
+except:
+    MINRA = 0.0
+    MAXRA = 360.0
+    MINDEC = -30.0
+    MAXDEC = 90.0
+
+BORDER = 1.60
+#BOXSIZE = 4
+BOXSIZE = 1.0
+HALFBOX = BOXSIZE/2.0
+
+
+
+ra = MINRA + HALFBOX
+dec = MINDEC + HALFBOX
+
+while ra <= MAXRA:
+   while dec <= MAXDEC:
+    
+       logger.infoSeparator()
+       logger.infoPair("BOXSIZE", "%d" % BOXSIZE)
+       allIDs = gpc1Db.getIDsInThisDVODbForThisStageInThisBox(DVOLABEL, "P2", ra, dec, BOXSIZE)
+       logger.infoPair("Found", "%d exposures" % len(allIDs))
+
+       dvo.loadRegionInThisBoxWithBorder(ra, dec, BOXSIZE, BORDER)
+
+       '''
+       regionsToIngest = []
+       regionsToPurge = []
+
+       dvo.getRegionsInThisBoxWithBorder(regionsToIngest, regionsToPurge, ra, dec, BOXSIZE, BORDER)
+
+       size = dvo.getDiskSizeOfDvoRegions(regionsToIngest)/1073741824.0
+       if size == 0: smfsPerGB = 0
+       else: smfsPerGB = len(allIDs)/size
+       logger.infoPair("smf files per GB", "%f" % smfsPerGB)
+       '''
+       dec = dec + BOXSIZE
+       break
+   ra = ra + BOXSIZE
+   break
+
+#regionsToIngest = []
+#regionsToPurge = []
+#dvo.getRegions(regionsToIngest, regionsToPurge)
+#dvo.getRegions(regionsToIngest, regionsToPurge, MINRA-BORDER, MAXRA+BORDER, MINDEC-BORDER, MAXDEC+BORDER)
+#size = dvo.getSizeOfDvoRegionFromFiles(files)
+#logger.infoPair("Size of region in DVO", dvo.getFileSizeStr(size))
+#logger.infoPair("Average region file size", dvo.getFileSizeStr(size/len(files)))
+
+#dvo.loadRegion(329, 331.1, 1.4, 2)
+#dvo.loadRegion(MINRA-BORDER, MAXRA+BORDER, MINDEC-BORDER, MAXDEC+BORDER)
+#dvo.loadRegion()
+
+logger.infoPair("Program...", "complete")
+
+
+
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/removeFromDatastore.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/removeFromDatastore.py	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/removeFromDatastore.py	(revision 33415)
@@ -5,7 +5,10 @@
 import sys
 import getopt
+
+
+from config import Config
 from pslogger import PSLogger
+from ipptopspsdb import IppToPspsDb
 
-from xml.etree.ElementTree import ElementTree, Element, tostring
 
 
@@ -14,12 +17,11 @@
     sys.exit(1)
 
-CONFIG = sys.argv[1]
-configDoc = ElementTree(file=CONFIG)
+CONFIGFILE = sys.argv[1]
 
-logging.setLoggerClass(PSLogger)
-logger = logging.getLogger("removeFromDatastore")
-logger.setup(configDoc, "removeFromDatastore")
-
-datastore = Datastore(logger, configDoc)
+# create objects
+config = Config(CONFIGPATH)
+logger = config.getLogger(sys.argv[0])
+ippToPspsDb = IppToPspsDb(logger, config)
+datastore = Datastore(logger, config, ippToPspsDb)
 
 if len(sys.argv) < 4:
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/reportloadfailures.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/reportloadfailures.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/reportloadfailures.py	(revision 33415)
@@ -0,0 +1,50 @@
+#!/usr/bin/env jython
+
+from subprocess import call, PIPE, Popen
+import tempfile
+from xml.etree.ElementTree import ElementTree, Element, tostring
+import re
+import sys
+
+import logging.config
+from pslogger import PSLogger
+from ipptopspsdb import IppToPspsDb
+from odm import Odm
+from batch import Batch
+
+
+if len(sys.argv) > 1: CONFIG  = sys.argv[1]
+else:
+    print "\n** Usage: " + sys.argv[0] + " <configPath>\n"
+    sys.exit(1)
+
+# open config file
+configDoc = ElementTree(file=CONFIG)
+
+# set up logging
+logging.setLoggerClass(PSLogger)
+logger = logging.getLogger(sys.argv[0])
+logger.setup(configDoc, sys.argv[0])
+
+ippToPspsDb = IppToPspsDb(logger, configDoc)
+odm = Odm(logger, configDoc)
+
+DVOLABEL = configDoc.find("dvo/gpc1Label").text
+EPOCH = configDoc.find("options/epoch").text
+
+ids = ippToPspsDb.getFailedLoadedToODMBatchIDs(EPOCH, DVOLABEL)
+
+
+logger.infoPair("Batches failed load to ODM", "%d" % len(ids))
+
+
+results = {}
+
+for id in ids:
+
+   if odm.checkBatch(id, results):
+       logger.info("  %9s : %s" % (Batch.getNameFromID(id), results['DETAILS']))
+   else:
+       logger.info("  %9s : failed to poll ODM" % Batch.getNameFromID(id))
+
+
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/republishbatch.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/republishbatch.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/republishbatch.py	(revision 33415)
@@ -0,0 +1,60 @@
+import sys
+
+from pslogger import PSLogger
+import logging
+from xml.etree.ElementTree import ElementTree, Element, tostring
+
+from ipptopspsdb import IppToPspsDb
+from datastore import Datastore
+from batch import Batch
+
+
+'''
+A program that republishes a bath to the datastore.
+
+It first 'resets' the batch in the ippToPsps database, i.e. pretends it has never been published, then re-publishes it to the datastore
+'''
+if len(sys.argv) > 2: 
+    CONFIG  = sys.argv[1]
+    BATCHID  = int(sys.argv[2])
+else:
+    print "** Usage: " + sys.argv[0] + " <configPath> <batchid>"
+    sys.exit(1)
+
+# open config file
+configDoc = ElementTree(file=CONFIG)
+
+logging.setLoggerClass(PSLogger)
+logger = logging.getLogger(sys.argv[0])
+logger.setup(configDoc, sys.argv[0])
+
+ippToPspsDb = IppToPspsDb(logger, configDoc)
+datastore = Datastore(logger, configDoc, ippToPspsDb)
+
+DVOLABEL = configDoc.find("dvo/gpc1Label").text
+EPOCH = configDoc.find("options/epoch").text
+BASEPATH = configDoc.find("localOutPath").text
+DATASTOREPRODUCT = configDoc.find("datastore/product").text
+
+logger.infoTitle("Batch republisher")
+logger.infoPair("Loading epoch", EPOCH)
+logger.infoPair("DVO label", DVOLABEL)
+
+
+batchName = Batch.getNameFromID(BATCHID)
+BATCHTYPE = ippToPspsDb.getBatchType(BATCHID)
+subDir = Batch.getSubDir(BASEPATH, BATCHTYPE, DVOLABEL)
+tarballFile = Batch.getTarballFile(BATCHID)
+
+logger.infoPair("batch ID", "%d" % BATCHID)
+logger.infoPair("batchname", batchName)
+logger.infoPair("batch type", BATCHTYPE)
+logger.infoPair("subdir", subDir)
+logger.infoPair("tarballfile", tarballFile)
+logger.infoPair("Reseting batch in database", "%d" % BATCHID)
+ippToPspsDb.resetBatch(BATCHID)
+logger.infoPair("publishing to", DATASTOREPRODUCT)
+Batch.publishToDatastore(datastore, BATCHID, batchName, subDir, tarballFile)
+
+
+
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/scratchdb.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/scratchdb.py	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/scratchdb.py	(revision 33415)
@@ -19,24 +19,70 @@
     Constructor
     '''
-    def __init__(self, logger, doc, useFull=0):
-        super(ScratchDb, self).__init__(logger, doc, "localdatabase")
+    def __init__(self, logger, config, dbName=None):
+        super(ScratchDb, self).__init__(logger, config, "localdatabase", dbName)
+
+        self.dvoDoneTable = "dvoDone"
+        self.dvoSkyTable = "dvoSkyTable"
+
+
+    '''
+    Destructor
+    '''
+    def __del__(self):
+
+        self.logger.debug("ScratchDb destructor")
+
+
+    '''
+    Sets whether we are using normal or pre-ingested DVO tables
+    '''
+    def setUseFullTables(self, useFull):
 
         if useFull:
-            self.dvoMetaTable = "dvoMetaFull"
+            self.dvoImagesTable = "dvoImagesFull"
             self.dvoDetectionTable = "dvoDetectionFull"
-            self.dvoDoneTable = "dvoDone"
         else:
-            self.dvoMetaTable = "dvoMeta"
+            self.dvoImagesTable = "dvoImages"
             self.dvoDetectionTable = "dvoDetection"
 
-        self.logger.debugPair("Using DVO meta table", self.dvoMetaTable)
+        self.logger.debugPair("Using DVO images table", self.dvoImagesTable)
         self.logger.debugPair("Using DVO detection table", self.dvoDetectionTable)
 
     '''
-    Destructor
-    '''
-    def __del__(self):
-
-        self.logger.debug("ScratchDb destructor")
+    Creates a stored procedure for psfLikelihood. This uses an implementation of the complimentary error function taken from Numerical Recipes in C.
+    '''
+    def createPsfLikelihoodFunction(self):
+
+        sql = "DROP FUNCTION IF EXISTS `psfLikelihood`"
+        try:
+            self.execute(sql)
+        except:
+            self.logger.errorPair("Could not drop stored procedure", "psfLikelihood")
+            return False
+
+        sql = "CREATE FUNCTION `psfLikelihood`(x REAL) RETURNS REAL \
+               DETERMINISTIC \
+               COMMENT 'Returns the psfLikelihood using the complimentary error function with fractional error everywhere less than 1.2 x 10-7. From Numerical Recipes in C' \
+               BEGIN \
+               DECLARE ans REAL; \
+               DECLARE t REAL; \
+               DECLARE z REAL; \
+               SET x = ABS(x) / 1.4142135623731; \
+               SET z=ABS(x); \
+               SET t=1.0 / (1.0 + 0.5*z); \
+               SET ans = t * EXP(-z*z-1.26551223+t*(1.00002368+t*(.37409196+t*(.09678418+t*(-.18628806+t*(.27886807+t*(-1.13520398+t*(1.48851587+t*(-.82215223+t*.17087277))))))))); \
+               IF x < 0.0 THEN \
+               SET ans = 2.0 - ans; \
+               END IF; \
+               RETURN ans; \
+               END"
+
+        try:
+            self.execute(sql)
+            self.logger.debugPair("Installed stored procedure", "psfLikelihood")
+            return True
+        except:
+            self.logger.errorPair("Could not install stored procedure", "psfLikelihood")
+            return False
 
     '''
@@ -56,12 +102,28 @@
         return surveyID
 
+    '''
+    Gets filter ID for this filter name
+    '''
+    def getFilterID(self, name):
+     
+        sql = "SELECT filterID FROM Filter WHERE filterType = '" + name + "'"
+        try:
+            rs = self.executeQuery(sql)
+            rs.first()
+            filterID = rs.getInt(1)
+        except:
+            self.logger.errorPair("No filter ID found for filter", name)
+            filterID = -1
+
+        return filterID
+
     ''' 
     Gets imageID from extern ID 
     ''' 
-    def getImageIDFromExternID(self, sourceID, externID): 
+    def getImageIDFromExternID(self, externID): 
                
         imageID = -1 
                
-        sql = "SELECT imageID FROM " + self.dvoMetaTable + " WHERE sourceID = %s AND externID = %s" % (sourceID, externID) 
+        sql = "SELECT IMAGE_ID FROM " + self.dvoImagesTable + " WHERE EXTERN_ID = %s" % (externID) 
         try: 
             rs = self.executeQuery(sql)   
@@ -76,9 +138,9 @@
     Gets DVO image flags
     '''
-    def getDvoImageFlags(self, sourceID, externID):
+    def getDvoImageFlags(self, externID):
 
         flags = 0
 
-        sql = "SELECT flags FROM " + self.dvoMetaTable + " WHERE sourceID = %s AND externID = %s" % (sourceID, externID)
+        sql = "SELECT FLAGS FROM " + self.dvoImagesTable + " WHERE EXTERN_ID = %s" % (externID)
         try:
             rs = self.executeQuery(sql)  
@@ -92,9 +154,9 @@
     Gets photcode (aka photoCalID from dvo table)
     '''
-    def getPhotoCalID(self, sourceID, externID):
+    def getPhotoCalID(self, externID):
 
         photcode = -1
 
-        sql = "SELECT photcode FROM " + self.dvoMetaTable + " WHERE sourceID = %s AND externID = %s" % (sourceID, externID)
+        sql = "SELECT PHOTCODE FROM " + self.dvoImagesTable + " WHERE EXTERN_ID = %s" % (externID)
         try:
             rs = self.executeQuery(sql)  
@@ -115,11 +177,11 @@
 
     '''
-    Inserts a new sourceID/imageID combo into dvoMeta
+    Inserts a new sourceID/imageID combo into dvoImages
     '''
     def insertNewDvoExternID(self, sourceID, externID):
 
-        sql = "INSERT INTO dvoMeta ( \
-               sourceID, \
-               externID \
+        sql = "INSERT INTO " + self.dvoImagesTable + " ( \
+               SOURCE_ID, \
+               EXTERN_ID \
                ) VALUES (\
                " + str(sourceID) + ", \
@@ -129,17 +191,11 @@
 
     '''
-    Updates dvoDone table with this DVO table
-    '''
-    def setImportedThisDvoTable(self, name):
-
-        sql = "INSERT INTO dvoDone (name) VALUES ('" + name + "')"
-        self.execute(sql)
-        
-    '''
-    Have we already imported this DVO table?
-    '''
-    def alreadyImportedThisDvoTable(self, name):
-
-        sql = "SELECT COUNT(*) FROM dvoDone WHERE name = '" + name + "'"
+    Do we have an out-of-date version of this file?
+    '''
+    def haveOutOfDateVersionOfThisDvoTable(self, path):
+
+        fileStat = os.stat(path)
+
+        sql = "SELECT COUNT(*) FROM " + self.dvoDoneTable + " WHERE path = '" + path + "' AND modifiedDate != " + str(fileStat.st_mtime) 
 
         try:
@@ -147,34 +203,181 @@
             rs.first()
             if rs.getInt(1) > 0:
-                self.logger.errorPair("Already imported DVO tables for",  name)
+                self.logger.debugPair("Have out-to-date version of",  path)
                 return True
             else:
                 return False
         except:
+            self.logger.exception("Unable to check whether this DVO table is out-of-date")
+
+    '''
+    Have we already imported this DVO table?
+    Checks path and modified date to be sure
+    '''
+    def alreadyImportedThisDvoTable(self, path):
+
+        fileStat = os.stat(path)
+
+        sql = "SELECT COUNT(*) FROM " + self.dvoDoneTable + " WHERE path = '" + path + "' AND modifiedDate = " + str(fileStat.st_mtime) 
+
+        try:
+            rs = self.executeQuery(sql)
+            rs.first()
+            if rs.getInt(1) > 0:
+                self.logger.debugPair("Already imported up-to-date version of",  path)
+                return True
+            else:
+                return False
+        except:
             self.logger.exception("Unable to check whether this DVO table has been imported")
 
 
     '''
+    Updates dvoDone table with this DVO table
+    '''
+    def setImportedThisDvoTable(self, path, region="null"):
+
+        fileStat = os.stat(path)
+
+        # first delete any old version we have ingested
+        sql = "DELETE FROM " + self.dvoDoneTable + " WHERE path = '" + path + "'"
+        self.execute(sql)
+
+        # now insert new version with up-to-date size and date
+        sql = "INSERT INTO " + self.dvoDoneTable + " (region, path, modifiedDate, size) VALUES ('" + region + "', '" + path + "', " + str(str(fileStat.st_mtime)) + ", " + str(str(fileStat.st_size)) + ")"
+        self.execute(sql)
+
+
+    '''
+    Gets the id for the cpm file for the provided region
+    '''
+    def getDvoCpmFileIdForRegion(self, region):
+
+        id = -1
+        sql = "SELECT id FROM " + self.dvoDoneTable + " WHERE region = '" + region + "' AND path LIKE '%.cpm'";
+        try:
+            rs = self.executeQuery(sql)
+            rs.first()
+            id = rs.getInt(1)
+        except:
+            self.logger.errorPair("Could not get file id from " + self.dvoDoneTable, "for region: " + region)
+
+        return id
+
+
+    '''
+    Gets the id for the provided file
+    '''
+    def getDvoFileId(self, path):
+
+        id = -1
+        sql = "SELECT id FROM " + self.dvoDoneTable + " WHERE path = '" + path + "'"
+        try:
+            rs = self.executeQuery(sql)
+            rs.first()
+            id = rs.getInt(1)
+        except:
+            self.logger.errprPair("Could not get file id from " + self.dvoDoneTable + "for", path)
+
+        return id
+                                                                                                
+    '''
+    Returns a list of DVO regions currently ingested
+    '''
+    def getIngestedDvoRegions(self):
+
+        regions = []
+
+        sql = "SELECT DISTINCT region FROM dvoDone WHERE region != 'null'"
+        try:
+            rs = self.executeQuery(sql)
+            while (rs.next()):
+                regions.append(rs.getString(1))
+            rs.close()
+        except:
+            self.logger.errorPai("Can't query for ingested regions", sql)
+
+        return regions
+
+    
+    '''
+    Gets total size of DVO files imported to this database
+    Counts only cpm and cpt files
+    '''
+    def getTotalSizeOfIngestedDvoFiles(self):
+
+        size = 0
+        sql = "SELECT SUM(size) FROM " + self.dvoDoneTable + " WHERE path LIKE '%cpm' OR path LIKE '%cpt'"
+        try:
+            rs = self.executeQuery(sql)  
+            rs.first()
+            size = rs.getLong(1)
+        except:
+            self.logger.errorPair("Unable to get total ingested DVO size", sql)
+
+        return size
+
+    '''
+    Checks whether DVO files we have ingested are form the DVO path provided
+    '''
+    def isCorrectDvo(self, path):
+
+        sql = "SELECT path FROM " + self.dvoDoneTable + " where path LIKE '%Images.dat'"
+        try:
+            rs = self.executeQuery(sql)  
+            rs.first()
+
+            head,tail = os.path.split(rs.getString(1))
+
+            if head == path: 
+                self.logger.debugPair("Already ingested stuff from DVO at", path)
+                return True
+            else: 
+                self.logger.errorPair("DVO does not match. We have", head)
+                return False
+
+        except:
+            self.logger.errorPair("No DVO files ingested for this path", path)
+            return False
+
+    '''
+    Checks whether the astrometric solution is ok for this chip
+    TODO the value of 50 for numAstroRef is hardcoded here, but this should be temporary anyway
+    '''
+    def astrometricSolutionOK(self, ota):
+
+        sql = "SELECT numAstroRef FROM ImageMeta_" + ota
+
+        try:
+            rs = self.executeQuery(sql)
+            rs.first()
+            if rs.getInt(1) < 50:
+                self.logger.debug("Bad astrometric solution for",  ota)
+                return False
+            else:
+                return True
+        except:
+            self.logger.debug("Unable to check astrometric solution")
+
+        return True
+
+
+    '''
     Creates a table for for ID matching
     '''
     def createDvoTables(self):
-
-        self.logger.debugPair("Creating DVO table", "dvoMeta")
-
-        sql = "DROP TABLE dvoMeta"
+    
+        # drop and create Images table
+        self.logger.debugPair("Creating DVO table", self.dvoImagesTable)
+        sql = "DROP TABLE " + self.dvoImagesTable
         try: self.execute(sql)
         except: pass
         
-        sql = "DROP TABLE dvoDetection"
-        try: self.execute(sql)
-        except: pass
-
-        sql = "CREATE TABLE dvoMeta ( \
-               sourceID INT, \
-               imageID INT, \
-               externID INT, \
-               flags INT, \
-               photcode INT, \
-               PRIMARY KEY (sourceID, imageID, externID) \
+        sql = "CREATE TABLE " + self.dvoImagesTable + " ( \
+               SOURCE_ID SMALLINT, \
+               IMAGE_ID INT, \
+               EXTERN_ID INT, \
+               FLAGS INT, \
+               PHOTCODE SMALLINT, \
+               PRIMARY KEY (IMAGE_ID, EXTERN_ID) \
                )"
 
@@ -183,7 +386,12 @@
             self.logger.error("Unable to create DVO meta-data database table")
 
+        # now detection table
+        self.logger.debugPair("Creating DVO table", self.dvoDetectionTable)
+        sql = "DROP TABLE " + self.dvoDetectionTable
+        try: self.execute(sql)
+        except: pass
+
         self.logger.debugPair("Creating DVO table", "dvoDetection")
         sql = "CREATE TABLE dvoDetection ( \
-               sourceID INT, \
                imageID INT, \
                ippDetectID BIGINT, \
@@ -192,5 +400,5 @@
                objID BIGINT, \
                flags INT, \
-               PRIMARY KEY (sourceID, imageID, ippDetectID) \
+               PRIMARY KEY (imageID, ippDetectID) \
                )"
 
@@ -201,30 +409,119 @@
 
     '''
-    Drops and recreates tables necessary for dvoToMySQL program. Be very careful before using this...
-    '''
-    def resetDvoToMysqlTables(self):
-
-       self.logger.infoPair("Dropping table", self.dvoMetaTable)
-       self.dropTable(self.dvoMetaTable)
+    Removes the provided DVO FITS files from from dvoDone and dvoDetection tables
+    Returns a count of the number of detections deleted
+    '''
+    def purgeTheseDvoRegions(self, regions):
+
+        detectionCount = self.getRowCount(self.dvoDetectionTable)
+        for region in regions:
+           
+           fileId = self.getDvoCpmFileIdForRegion(region)
+
+           if fileId < 0:
+               self.logger.errorPair("Unable to get file ID to delete " + region + " from", self.dvoDoneTable)
+               continue
+
+           sql = "DELETE FROM " + self.dvoDetectionTable + " WHERE fileID = " + str(fileId)
+           try: self.execute(sql)
+           except: 
+               self.logger.errorPair("Unable to delete from " + self.dvoDetectionTable, "for file ID: %d" % fileId)
+               return False
+
+           sql = "DELETE FROM " + self.dvoDoneTable + " WHERE region = '" + region + "'"
+           try: self.execute(sql)
+           except:
+               self.logger.errorPair("Unable to delete " + region + " from", self.dvoDoneTable)
+
+        finalCount = detectionCount - self.getRowCount(self.dvoDetectionTable)
+
+        # after a big delete, we should OPTIMIZE the table
+        self.logger.infoPair("Running OPTIMIZE on ", self.dvoDetectionTable)
+        if finalCount > 0: self.optimizeTable(self.dvoDetectionTable)
+
+        return finalCount
+           
+    '''
+    Drops DVO stuff pertaining to detections
+    '''
+    def resetDvoDetectionTables(self):
+
+       self.logger.infoPair("Deleting detections from", self.dvoDoneTable)
+       sql = "DELETE FROM " + self.dvoDoneTable + " WHERE path LIKE '%cpm' OR path LIKE '%cpt'"
+       try: self.execute(sql)
+       except: 
+           self.logger.errorPair("Unable to delete from", self.dvoDoneTable)
+           return False
+
        self.logger.infoPair("Dropping table", self.dvoDetectionTable)
        self.dropTable(self.dvoDetectionTable)
-       self.logger.infoPair("Dropping table", self.dvoDoneTable)
-       self.dropTable(self.dvoDoneTable) 
-
-       self.logger.infoPair("Creating table", self.dvoMetaTable)
-       sql = "CREATE TABLE " + self.dvoMetaTable + " LIKE dvoMeta"
-       try: self.execute(sql)
-       except: self.logger.errorPair("Unable to create table", self.dvoMetaTable)
 
        self.logger.infoPair("Creating table", self.dvoDetectionTable)
        sql = "CREATE TABLE " + self.dvoDetectionTable + " LIKE dvoDetection"
        try: self.execute(sql)
-       except: self.logger.errorPair("Unable to create table", self.dvoDetectionTable)
-
+       except:
+           self.logger.errorPair("Unable to create table", self.dvoDetectionTable)
+           return False
+
+       # TODO
+       sql = "ALTER TABLE " + self.dvoDetectionTable + " ADD fileID INT NOT NULL"
+       try: self.execute(sql)
+       except: 
+           self.logger.errorPair("Unable to add fileID column to ", self.dvoDetectionTable)
+           return False
+
+    '''
+    Drops and recreates tables necessary for dvoToMySQL program. Be very careful before using this...
+    '''
+    def resetAllDvoTables(self):
+
+       self.logger.infoPair("Dropping table", self.dvoImagesTable)
+       self.dropTable(self.dvoImagesTable) 
+       self.logger.infoPair("Dropping table", self.dvoSkyTable)
+       self.dropTable(self.dvoSkyTable) 
+       self.logger.infoPair("Dropping table", self.dvoDoneTable)
+       self.dropTable(self.dvoDoneTable) 
+
+       # create dvoDone table
        self.logger.infoPair("Creating table", self.dvoDoneTable )
-       sql = "CREATE TABLE " + self.dvoDoneTable + " (name VARCHAR(100))"
+       sql = "CREATE TABLE " + self.dvoDoneTable + " (id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, region VARCHAR(100), path VARCHAR(1000), modifiedDate BIGINT, size BIGINT)"
        try: self.execute(sql)
        except: self.logger.errorPair("Unable to create table", self.dvoDoneTable)
 
-
-
+       self.resetDvoDetectionTables()
+
+    '''
+    Gets a list of DVO regions that overlap with the defined box
+    '''
+    def getDvoRegions(self, minRa, maxRa, minDec, maxDec):
+
+        self.logger.debug("Querying DVO SkyTable for FITS files in this region")
+
+        regions = []
+        self.getDvoRegionsForThisBox(regions, minRa, maxRa, minDec, maxDec)
+
+        # deal with 0/360 boundary
+        if minRa < 0: self.getDvoRegionsForThisBox(regions, minRa + 360, 361 ,minDec, maxDec)
+        if maxRa > 360: self.getDvoRegionsForThisBox(regions, 0, maxRa - 360 ,minDec, maxDec)
+
+        return regions
+
+    '''
+    Gets a list of DVO regions that overlap with the defined box
+    '''
+    def getDvoRegionsForThisBox(self, regions, minRa, maxRa, minDec, maxDec):
+
+        sql = "SELECT name FROM " + self.dvoSkyTable + " \
+               WHERE NOT ((" + str(maxRa) + " <= R_MIN) OR \
+                       (" + str(minRa) + " >= R_MAX) OR \
+                       (" + str(maxDec) + " <= D_MIN) OR \
+                       (" + str(minDec) + " >= D_MAX)) "
+
+        try:
+            rs = self.executeQuery(sql)
+            while (rs.next()): regions.append(rs.getString(1))
+            rs.close()
+        except:
+            self.logger.exception("Can't query for DVO regions")
+
+
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/setupScratchDb.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/setupScratchDb.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/setupScratchDb.py	(revision 33415)
@@ -0,0 +1,49 @@
+#!/usr/bin/env jython
+
+import os.path
+import sys
+import time
+import stilts
+from java.lang import *
+from java.sql import *
+import logging.config
+
+from java.lang import Math
+from org.apache.commons.math.special import Erf
+from xml.etree.ElementTree import ElementTree, Element, tostring
+
+from pslogger import PSLogger
+from scratchdb import ScratchDb
+
+'''
+Start of program.
+'''
+
+if len(sys.argv) > 1: CONFIG = sys.argv[1]
+else:
+    print "** Usage: " + sys.argv[0] + " <configPath> [init]"
+    sys.exit(1)
+
+# open config file
+configDoc = ElementTree(file=CONFIG)
+
+# set up logging
+logging.setLoggerClass(PSLogger)
+logger = logging.getLogger("setupScratchDb")
+logger.setup(configDoc, "setupScratchDb")
+
+scratchDb = ScratchDb(logger, configDoc)
+
+# install stored procedure
+logger.info("Installing psfLikelihood() stored procedure")
+scratchDb.createPsfLikelihoodFunction()
+
+# install IN tables
+logger.info("Installing initialization tables")
+tables = stilts.treads("../config/IN/tables.vot")
+for table in tables:
+    logger.debug("Creating IN table: " + table.name)
+    table.write(scratchDb.url + '#' + table.name)
+
+logger.info("Done")
+
Index: branches/meh_branches/ppstack_test/ippToPsps/jython/stackbatch.py
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/jython/stackbatch.py	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/jython/stackbatch.py	(revision 33415)
@@ -19,4 +19,7 @@
 '''
 StackBatch class
+
+This class, a sub-class of Batch, processes single stack skycells in the form of IPP cmf files
+
 '''
 class StackBatch(Batch):
@@ -27,19 +30,23 @@
     def __init__(self, 
                  logger, 
-                 configPath,
-                 configDoc,
+                 config,
                  gpc1Db,
                  ippToPspsDb,
-                 stackID):
+                 scratchDb,
+                 stackID,
+                 batchID,
+                 useFullTables):
 
        super(StackBatch, self).__init__(
                logger,
-               configPath,
-               configDoc,
+               config,
                gpc1Db,
                ippToPspsDb,
+               scratchDb,
                stackID,
+               batchID,
                "ST", 
-               gpc1Db.getStackStageCmf(stackID))
+               gpc1Db.getStackStageCmf(config.dvoLabel, stackID),
+               useFullTables)
 
        if not self.everythingOK: return
@@ -49,11 +56,26 @@
        # get stack meta data
        meta = self.gpc1Db.getStackStageMeta(self.id)
+       if not meta:
+           self.everythingOK = False
+           return
+
        self.filter = meta[0];
        self.filter = self.filter[0:1]
+       self.filterID = self.scratchDb.getFilterID(self.filter)
        self.skycell = meta[1];
-       # TODO HACK fix this
+
+       # skycell is, eg "skycell.1133.081"
+       # we need 1133.081 for skycell, but store as an int, so instead store 1133081
+       # projection cell is just the 1133 bit
+       # this is how it should be done
+       #self.projectioncell = self.skycell[8:12]
+       #self.skycell = self.skycell[8:]
+       #self.skycell = self.skycell.replace(".", "")
+       #self.projectioncell = self.skycell[8:12]
+
+       # TODO this is the wrong way, but is consistent with SkyCell table in IN data
        self.skycell = self.skycell[8:12]
-       # mangling e.g. 0683.043 into 0683043 for now until we have a schema change
-       #self.skycell = self.skycell.replace(".", "")
+       self.projectioncell = self.skycell
+
        self.analysisVer = meta[2];
 
@@ -77,5 +99,5 @@
        self.scratchDb.dropTable("SkyChip_xsrc")
 
-       # create an output filename, which is {filterID}{skycellID}.FITS
+       # create an output filename, which is {stackID}.FITS
        self.outputFitsFile = "%08d.FITS" % self.id
        self.outputFitsPath = "%s/%s" % (self.localOutPath, self.outputFitsFile)
@@ -90,5 +112,4 @@
        if not self.useFullTables:
            self.scratchDb.insertNewDvoExternID(self.header['SOURCEID'], self.header['IMAGEID'])
-
 
        # dump stuff to log
@@ -96,7 +117,6 @@
        self.logger.infoPair("Stack type", "%s" % self.stackType)
        self.logger.infoPair("Skycell", "%s" % self.skycell)
+       self.logger.infoPair("Projectioncell", "%s" % self.projectioncell)
        self.logger.infoPair("Filter", "%s" % self.filter)
-
-
 
     '''
@@ -247,4 +267,6 @@
         ,expTime \
         ,psfModelID \
+        ,psfFwhm_mean \
+        ,psfFwhm_max \
         ,ctype1 \
         ,ctype2 \
@@ -266,4 +288,6 @@
         ," + str(self.expTime) + " \
         ,'" + self.safeDictionaryAccess(self.header, 'PSFMODEL') + "' \
+        ,'" + self.safeDictionaryAccess(self.header, 'FWHM_MAJ') + "' \
+        ,'" + self.safeDictionaryAccess(self.header, 'FW_MJ_UQ') + "' \
         ,'" + self.header['CTYPE1'] + "' \
         ,'" + self.header['CTYPE2'] + "' \
@@ -283,5 +307,5 @@
         self.scratchDb.updateAllRows("StackMeta", "surveyID", str(self.surveyID))
         self.scratchDb.updateFilterID("StackMeta", self.filter)
-        self.scratchDb.updateAllRows("StackMeta", "dataRelease", str(self.dataRelease))
+        self.scratchDb.updateAllRows("StackMeta", "dataRelease", str(self.config.dataRelease))
         self.updateStackTypeID("StackMeta")
 
@@ -297,4 +321,5 @@
                ippDetectID \
                ,skyCellID \
+               ,projectionCellID \
                ,obsTime \
                ,xPos \
@@ -311,4 +336,5 @@
                ,psfWidMinor \
                ,psfTheta \
+               ,psfLikelihood \
                ,infoFlag \
                ,psfCf \
@@ -335,4 +361,5 @@
                IPP_IDET \
                ," + self.skycell + " \
+               ," + self.projectioncell + " \
                ," + self.header['MJD-OBS'] + " \
                ,X_PSF \
@@ -349,4 +376,5 @@
                ,PSF_MINOR \
                ,PSF_THETA \
+               ,psfLikelihood(EXT_NSIGMA) \
                ,FLAGS << 32 | FLAGS2 \
                ,PSF_QF \
@@ -375,5 +403,5 @@
         self.scratchDb.updateAllRows("StackDetection", "surveyID", str(self.surveyID))
         self.scratchDb.updateFilterID("StackDetection", self.filter)
-        self.scratchDb.updateAllRows("StackDetection", "dataRelease", str(self.dataRelease))
+        self.scratchDb.updateAllRows("StackDetection", "dataRelease", str(self.config.dataRelease))
         self.scratchDb.updateAllRows("StackDetection", "primaryF", "0")
         self.scratchDb.updateAllRows("StackDetection", "activeFlag", "0")
@@ -381,7 +409,7 @@
         self.updateStackTypeID("StackDetection")
         self.updateDvoIDs("StackDetection")
-        sql = "ALTER TABLE StackDetection ADD PRIMARY KEY (objID, stackDetectID)"
-        self.scratchDb.execute(sql)
-
+        sql = "ALTER IGNORE TABLE StackDetection ADD PRIMARY KEY (objID)"
+        self.scratchDb.execute(sql)
+  
         if self.stackType == "DEEP_STACK": 
 
@@ -390,7 +418,7 @@
                    SET instFlux = 2*b.PSF_INST_FLUX_SIG \
                    WHERE instFlux IS NULL \
+                   AND a.ippDetectID = b.IPP_IDET  \
                    AND b.PSF_INST_FLUX_SIG IS NOT NULL"
             self.scratchDb.execute(sql)
-            #    instFlux = 2*PSF_INST_FLUX_SIG
             
         self.scratchDb.reportAndDeleteRowsWithNULLS("StackDetection", "instFlux")
@@ -417,5 +445,5 @@
         # TODO temporarily loading 1st convolved fluxes into unconvolved fields
         self.logger.infoPair("Adding fluxes", "un-convolved")
-        self.updateApFlxs("", "< 7.0")
+        self.updateApFlxs("", "IS NULL")
         self.logger.infoPair("Adding fluxes", "1st convolved")
         self.updateApFlxs("c1", "< 7.0")
@@ -433,4 +461,5 @@
         ,petR90=b.PETRO_RADIUS_90 \
         ,petR90Err=b.PETRO_RADIUS_90_ERR \
+        ,petCf=b.PETRO_FILL \
         WHERE a.ippDetectID=b.IPP_IDET"
         self.scratchDb.execute(sql)
@@ -438,5 +467,5 @@
         self.scratchDb.updateAllRows("StackApFlx", "surveyID", str(self.surveyID))
         self.scratchDb.updateFilterID("StackApFlx", self.filter)
-        self.scratchDb.updateAllRows("StackApFlx", "dataRelease", str(self.dataRelease))
+        self.scratchDb.updateAllRows("StackApFlx", "dataRelease", str(self.config.dataRelease))
         self.scratchDb.updateAllRows("StackApFlx", "primaryF", "0")
         self.scratchDb.updateAllRows("StackApFlx", "activeFlag", "0")
@@ -490,5 +519,4 @@
         rs.first()
         nMissing = rs.getInt(1)
-        self.logger.infoPair("Detections deleted from %s that are not in StackDetection" + table, "%5d" % nMissing)
   
         if nMissing < 1: return
@@ -496,4 +524,5 @@
         sql = "DELETE FROM " + table + " WHERE ippDetectID NOT IN (SELECT ippDetectID FROM StackDetection)"
         self.scratchDb.execute(sql)
+        self.logger.infoPair("Detections not in StackDetection", "%d deleted from %s" % (nMissing, table))
        
 
@@ -527,8 +556,10 @@
                objID \
                ,ippObjID \
+               ,projectionCellID \
                ) \
                SELECT \
                DISTINCT objID \
                ,ippObjID \
+               ,projectionCellID \
                FROM StackDetection"
         self.scratchDb.execute(sql)
@@ -569,4 +600,6 @@
         self.scratchDb.createIndex("StackModelFit", "ippDetectID")
 
+        return True
+
     '''
     Applies indexes to the IPP tables
@@ -580,4 +613,6 @@
         self.scratchDb.createIndex("SkyChip_xsrc", "IPP_IDET")
 
+        return True
+
     '''
     Updates provided table with DVO IDs from DVO table
@@ -587,7 +622,7 @@
         imageID = self.scratchDb.getImageIDFromExternID(self.header['SOURCEID'], self.header['IMAGEID'])
         self.logger.debug("Updating table '" + table + "' with DVO IDs...")
-        sql = "UPDATE IGNORE " + table + " AS a, " + self.scratchDb.dvoDetectionTable + " AS b SET \
+        sql = "UPDATE " + table + " AS a, " + self.scratchDb.dvoDetectionTable + " AS b SET \
                a.ippObjID = b.ippObjID, \
-               a.stackDetectID = b.detectID, \
+               a.stackDetectID = 10 * b.detectID + " + str(self.filterID) + ", \
                a.objID = b.objID \
                WHERE a.ippDetectID = b.ippDetectID \
Index: branches/meh_branches/ppstack_test/ippToPsps/perl/checkOdmStatus.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/perl/checkOdmStatus.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/perl/checkOdmStatus.pl	(revision 33415)
@@ -22,9 +22,4 @@
 $ua->env_proxy;
 
-my $loadedToOdm;
-my $loadFailed;
-my $mergeWorthy;
-my $merged;
-
 my $statusFilter = "*";
 
@@ -42,56 +37,13 @@
     exit(1);
 }
-#        print( "HTTP response status: ".$response->content."\n" );
 
 open (DAT, ">".$file);
-#my ($tempFile, $tempName) = tempfile( "/tmp/ippToPsps_odmXml.XXXX", UNLINK => 0);
 print DAT $response->content;
-#print $response->content;
-#close($tempFile);
 
-#${$loadedToOdm} = 0;
-#${$loadFailed} = 0;
-#${$mergeWorthy} = 0;
-#${$merged} = 0;
 
 print "$file\n";
-#parseXml($tempName, $loadedToOdm, $loadFailed, $mergeWorthy, $merged);
 
 exit(0);
 
-    #unlink($tempFile);
 
 
-########################################################################################
-# 
-# Parses ODM XML
-# 
-########################################################################################
-    sub parseXml {
-        my ($xmlFile, $loadedToOdm, $loadFailed, $mergeWorthy, $merged) = @_;
-
-        my $parser = XML::LibXML->new;
-        my $doc = $parser->parse_file($xmlFile);
-        my $xc = XML::LibXML::XPathContext->new($doc);
-        $xc->registerNs('ArrayOfOdmBatchState', 'PanSTARRS.Services.OdmWebService');
-        my $result = $xc->findvalue('//ArrayOfOdmBatchState:BatchState');
-
-        if ($result =~ m/failed/) { 
-            ${$loadFailed} = 1;
-            ${$loadedToOdm} = 1;
-            return;
-        }
-
-        $result = $xc->findvalue('//ArrayOfOdmBatchState:Message');
-        ${$loadedToOdm} = 0;
-        ${$mergeWorthy} = 0;
-        ${$merged} = 0;
-
-        if ($result =~ m/None/) { ${$loadedToOdm} = 1;}
-        if ($result =~ m/LoadStarted/) { ${$loadedToOdm} = 1;}
-        if ($result =~ m/MergeWorthy/) { ${$loadedToOdm} = 1; ${$mergeWorthy} = 1;}
-        if ($result =~ m/Merge[1-9]Completed/) { ${$loadedToOdm} = 1; ${$mergeWorthy} = 1; ${$merged} = 1;}
-
-
-
-    }
Index: branches/meh_branches/ppstack_test/ippToPsps/perl/deleteFromDXLayer.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/perl/deleteFromDXLayer.pl	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippToPsps/perl/deleteFromDXLayer.pl	(revision 33415)
@@ -0,0 +1,61 @@
+#!/usr/bin/env perl
+
+use warnings;
+use strict;
+
+use LWP::UserAgent;
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use XML::LibXML;
+use File::Temp qw(tempfile);
+
+
+my $batchName = undef;
+my $file = undef;
+
+GetOptions(
+        'batch|b=s' => \$batchName,
+        'file|f=s' => \$file,
+        );
+
+my $dxWebServiceUrl = "http://lsb01.psps.ifa.hawaii.edu/cgi-bin/dxlayer/dxwebservice.cgi";
+#my $dxWebServiceUrl = "http://166.122.172.121/cgi-bin/dxlayer/dxwebservice.cgi";
+my $ua = LWP::UserAgent->new;
+$ua->timeout(15);
+$ua->env_proxy;
+
+    my $doc = XML::LibXML::Document->new(); # Global document variable.
+    my $rootNode = $doc->createElement('AdminQueryParams');
+$doc->setDocumentElement($rootNode);
+
+my $paramNode = $doc->createElement('Param');
+$paramNode->setAttribute('Name', "BatchID"); # default FirstRow is 1
+my $text=XML::LibXML::Text->new($batchName);
+$paramNode->appendChild($text); #append
+$rootNode->appendChild($paramNode); # append a node to the xml tree
+
+my $xmlParams = $doc->toString; 
+
+print  $xmlParams;
+
+my $response = $ua->post($dxWebServiceUrl,
+        [op => "delete_dxbatch_files",
+        xmlParams => $xmlParams ]
+        );
+
+# '200' is the 'OK' response
+if ($response->code != 200) {
+
+    print "Problem connecting to DXLayerweb service, HTTP response status: ".$response->status_line."\n";
+    exit(1);
+}
+
+open (DAT, ">".$file);
+print DAT $response->content;
+
+
+print "$file\n";
+
+exit(0);
+
+
+
Index: branches/meh_branches/ppstack_test/ippToPsps/perl/exposureSummary.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/perl/exposureSummary.pl	(revision 32161)
+++ 	(revision )
@@ -1,236 +1,0 @@
-#!/usr/bin/env perl
-
-# script to provide a summary of loading status given an upper and lower exposure ID limit
-
-use warnings;
-use strict;
-
-use LWP::UserAgent;
-use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
-use File::Temp qw(tempfile);
-use ippToPsps::IppToPspsDb;
-use ippToPsps::Gpc1Db;
-use ippToPsps::DetectionBatch;
-
-my $dvoDb = undef;
-my $beginExp = undef;
-my $endExp = undef;
-my $verbose = undef;
-my $checkNeb = undef;
-my $save_temps = undef;
-
-GetOptions(
-        'startexp|b=s' => \$beginExp,
-        'endexpi|e=s' => \$endExp,
-        'dvodb|d=s' => \$dvoDb,
-        'checkNeb|n' => \$checkNeb,
-        'verbose|v' => \$verbose,
-        'save_temps|s' => \$save_temps
-        );
-
-
-my $quit = 0;
-print "\n*******************************************************************************\n";
-print "* \n";
-if (@ARGV) {
-    $quit=1;
-    print "* UNKNKOWN: option                          @ARGV\n";
-}
-if (!defined $beginExp) {
-    $quit = 1;
-    print "* REQUIRED: a begin exp_id                              -b <name>\n";
-}
-if (!defined $endExp) {
-    $quit = 1;
-    print "* REQUIRED: an end exp_id                               -e <name>\n";
-}
-if (!defined $dvoDb) {
-    $quit = 1;
-    print "* REQUIRED: a dvo Db name                               -d <name>\n";
-}
-if (!defined $checkNeb) {
-    $checkNeb = 0;
-    print "* OPTIONAL: check nebulous for unprocessed exposures    -n      (default = $checkNeb)\n";
-}
-if (!defined $verbose) {
-    $verbose = 0;
-    print "* OPTIONAL: run in verbose mode                         -v      (default = $verbose)\n";
-}
-if (!defined $save_temps) {
-    $save_temps = 0;
-    print "* OPTIONAL: keep temp files                             -t      (default = $save_temps)\n";
-}
-print "*\n*******************************************************************************\n";
-
-if ($quit) { exit; }
-
-my $gpc1Db = new ippToPsps::Gpc1Db("gpc1", "ippdb01", "ippuser", "ippuser", $verbose, $save_temps);
-my $ippToPspsDb = new ippToPsps::IppToPspsDb("ippToPsps", "ippdb01", "ipp", "ipp", $verbose, $save_temps);
-
-my $exposures;
-#if (!$gpc1Db->getExposureListFromDvoDb($dvoDb, \$exposures, 0)) {exit;}
-
-my $totalExposures = $ippToPspsDb->countExposures($beginExp, $endExp, "PSPS_test", "P2", $dvoDb);
-my $totalMerged = $ippToPspsDb->countMergedExposures($beginExp, $endExp, "PSPS_test", "P2", $dvoDb);
-
-if(!$ippToPspsDb->getUnmergedExposures(\$exposures, $beginExp, $endExp, "PSPS_test", "P2", $dvoDb)) {
-
-    print "No unmerged exposures\n";
-    exit;
-}
-
-my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
-my $timeStamp = sprintf "%4d-%02d-%02d %02d:%02d:%02d", $year+1900,$mon+1,$mday,$hour,$min,$sec;
-
-my $exposure;
-my $totalUnmerged = 0;
-my $totalMergeWorthy = 0;
-my $totalNotMergeWorthy = 0;
-my $totalProcessed = 0;
-my $totalOnDatastore = 0;
-my $totalLoadedToOdm = 0;
-my $totalLoadFailed = 0;
-my $totalMergeWorthy = 0;
-my $totalUnderObjIdLimit = 0;
-my $totalUnprocessed = 0;
-my $totalUnprocessedAvailableSmfs = 0;
-my $remainingOdmFailures = 0;
-my $totalUnprocessedMissingSmfs = 0;
-my $totalProcessedNotOnDatastore = 0;
-
-
-open (UNPRO, ">unprocessedExposures.txt") or print "* Problem opening data file for unprocessed exposures\n";
-open (ODMFAIL, ">odmFailures.txt") or print "* Problem opening data file for ODM failures\n";
-open (NOTONDATASTORE, ">notOnDatastore.txt") or print "* Problem opening data file for exposures not on datastore\n";
-
-if ($checkNeb) {
-
-    open (MISSSMF, ">missingSmfs.txt") or print "* Problem opening data file for missing smfs\n";
-    open (NOTMISSSMF, ">notMissingSmfs.txt") or print "* Problem opening data file for missing smfs\n";
-}
-
-foreach $exposure ( @{$exposures} ) {
-    my ($batchId, $expId) = @{$exposure};
-
-    if ($ippToPspsDb->isExposureMerged($expId)) {next;}
-
-    $totalUnmerged++;
-    if ($verbose) {print "$totalUnmerged | $expId \n"; }
-
-    # processed stuff
-    if ($ippToPspsDb->isExposureProcessed($expId)) {
-
-        $totalProcessed++;
-
-        if (!$ippToPspsDb->isExposureAlreadyPublished($expId)) {
-
-            $totalProcessedNotOnDatastore++;
-            print NOTONDATASTORE "$expId\n";
-        }
-        else {
-
-            $totalOnDatastore++;
-
-            if ($ippToPspsDb->isExposureLoadedToOdm($expId)) {
-
-                $totalLoadedToOdm++;
-
-                if ($ippToPspsDb->isMergeWorthy($expId)) {
-
-                    $totalMergeWorthy++;
-                }
-                # not mergeworthy
-                else {
-
-                    $totalNotMergeWorthy++;
-
-                    #if ($ippToPspsDb->didLoadFail($expId)) {
-
-                    #    $totalLoadFailed++;
-                    #}
-                    if ($ippToPspsDb->isMinObjIdUnderLimit($expId)) {
-
-                        $totalUnderObjIdLimit++;
-                    }
-                    else {
-
-                        $remainingOdmFailures++;
-                        print ODMFAIL "$expId\n";
-                    }
-                }
-            }
-        }
-    }
-    # unprocessed stuff
-    else {
-
-
-        $totalUnprocessed++;
-        print UNPRO "$expId\n";
-
-        # if flag is set, then attempt to get smf file from nebulous for this unprocessed exposure
-        if ($checkNeb) {
-
-            my $detectionBatch = ippToPsps::DetectionBatch->existing(
-                    "GPC1",
-                    $gpc1Db,
-                    $batchId,
-                    $ippToPspsDb,
-                    ".",
-                    $verbose,
-                    $save_temps);
-
-
-            my $path = $detectionBatch->getSmfFile();
-            if (!$path) {
-                print "no smf file for $expId\n";
-
-                $totalUnprocessedMissingSmfs++;
-                print MISSSMF "$expId\n";
-            }
-            else {
-
-                $totalUnprocessedAvailableSmfs++;
-                print NOTMISSSMF "$expId\n";
-
-            }
-        }
-
-    }
-}
-
-close(UNPRO);
-close(ODMFAIL);
-close(NOTONDATASTORE);
-if ($checkNeb) {
-
-    close(MISSSMF);
-    close(NOTMISSSMF);
-}
-my $totalOnDatastoreNotLoaded = $totalOnDatastore - $totalLoadedToOdm;
-print "***********************************************************\n";
-print "*    Summary of IPP->PSPS loading as of $timeStamp\n"; 
-print "*    DVO Db = '$dvoDb'\n";
-print "*    Exposures between $beginExp and $endExp\n";
-print "*\n";
-print "* Total Exposures                            = $totalExposures\n";
-print "*   Merged into PSPS                         = $totalMerged\n";
-print "*   Un-merged                                = $totalUnmerged\n";
-print "*     Unprocessed by ippToPsps               = $totalUnprocessed\n";
-if ($checkNeb) {
-    print "*       Missing SMF files                    = $totalUnprocessedMissingSmfs\n";
-    print "*       Available SMF files                  = $totalUnprocessedAvailableSmfs\n";
-}
-print "*     Processed by ippToPsps                 = $totalProcessed\n";
-print "*       Not on datastore                     = $totalProcessedNotOnDatastore\n";
-print "*       On datastore                         = $totalOnDatastore\n";
-print "*         Not loaded to ODM                  = $totalOnDatastoreNotLoaded\n";
-print "*         Loaded to ODM                      = $totalLoadedToOdm\n";
-print "*           MergeWorthy                      = $totalMergeWorthy\n";
-print "*           Not MergeWorthy                  = $totalNotMergeWorthy\n";
-print "*             Under -30 dec limit            = $totalUnderObjIdLimit\n";
-print "*             Remaining failures             = $remainingOdmFailures\n";
-print "*\n";
-print "***********************************************************\n";
-
-
Index: branches/meh_branches/ppstack_test/ippToPsps/perl/ippToPsps_run.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/perl/ippToPsps_run.pl	(revision 32161)
+++ 	(revision )
@@ -1,554 +1,0 @@
-#!/usr/bin/env perl
-
-use warnings;
-use strict;
-
-use PS::IPP::Config 1.01 qw( :standard );
-use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
-use IPC::Cmd 0.36 qw( can_run run );
-use File::Temp qw(tempfile);
-use XML::LibXML;
-use File::Basename;
-
-# local classes
-use ippToPsps::Gpc1Db;
-use ippToPsps::IppToPspsDb;
-use ippToPsps::Datastore;
-#use ippToPsps::BatchManager;
-
-# globals
-my $camera = undef;
-my $batchType = undef;
-my $dvoLocation = undef;
-my $dvoDb = undef;
-my $fullDvoPath = undef;
-my $verbose = undef;
-my $save_temps = undef; 
-my $output = undef;
-my $singleExpId = undef;
-my $datastoreProduct = undef;
-my $force = undef;
-my $initBatch = undef;
-my $dontTarNZip = undef;
-
-# get user args
-GetOptions(
-        'camera|c' => \$camera,
-        'output|o=s' => \$output,
-        'batch|b=s' => \$batchType,
-        'dvo|d=s' => \$fullDvoPath,
-        'expid|e=s' => \$singleExpId,
-        'product|p=s' => \$datastoreProduct,
-        'verbose|v' => \$verbose,
-        'save_temps|t' => \$save_temps,
-        'force|f' => \$force,
-        'tarnzip|z' => \$dontTarNZip,
-        );
-
-my $quit = 0;
-print "\n*******************************************************************************\n";
-print "* \n";
-if (@ARGV) {
-    $quit=1;
-    print "* UNKNKOWN: option                          @ARGV\n";
-}
-if (!defined $output) {
-    $quit=1;
-    print "* REQUIRED: need to provide an output path  -o <path>\n";
-}
-if (!defined $batchType) {
-    $quit=1;
-    print "* REQUIRED: need to define a batch type     -b <init|det|diff|stack>\n";
-}
-if (!defined $fullDvoPath) {
-    $quit=1;
-    print "* REQUIRED: need to provide a DVO Db        -d <pathToDVO>\n";
-}
-if (!defined $camera) {
-    $camera = "GPC1";
-    print "* OPTIONAL: select a camera                 -c                   (default = $camera)\n";
-}
-if (!defined $singleExpId) {
-
-    print "* OPTIONAL: a single exposure ID            -e <expID>           (default = none)\n";
-}
-if (!defined $datastoreProduct) {
-
-    print "* OPTIONAL: datastore product               -p <product>         (default = none, i.e. data will not be published)\n";
-}
-if (!defined $verbose) {
-    $verbose = 0;
-    print "* OPTIONAL: run in verbose mode             -v                   (default = $verbose)\n";
-}
-if (!defined $save_temps) {
-    $save_temps = 0;
-    print "* OPTIONAL: keep temp files                 -t                   (default = $save_temps)\n";
-}
-if (!defined $force) {
-    $force = 0;
-    print "* OPTIONAL: force if already processed      -f                   (default = $force)\n";
-}
-if (!defined $dontTarNZip) {
-    $dontTarNZip = 0;
-    print "* OPTIONAL: don't tar and zip output        -z                   (default = $dontTarNZip)\n";
-}
-print "*\n*******************************************************************************\n";
-
-if ($quit) { exit; }
-
-# determine PSPS batch 'type'
-my $pspsBatchType;
-if ($batchType eq 'init') {$pspsBatchType = "IN";}
-elsif ($batchType eq 'det') {$pspsBatchType = "P2";}
-elsif ($batchType eq 'stack') {$pspsBatchType = "ST";}
-elsif ($batchType eq 'diff') {$pspsBatchType = "OB";}
-else {$pspsBatchType = "UNKNOWN";}
-
-# spilt full DVO path into Db and path
-$fullDvoPath =~ s/\/$//; # strip off trailing '\'
-($dvoDb, $dvoLocation) = fileparse($fullDvoPath);
-
-my $gpc1Db = new ippToPsps::Gpc1Db("gpc1", "ippdb01", "ippuser", "ippuser", $verbose, $save_temps);
-my $ippToPspsDb = new ippToPsps::IppToPspsDb("ippToPsps", "ippdb01", "ipp", "ipp", $verbose, $save_temps);
-my $datastore = undef;
-if ($datastoreProduct) {$datastore = new ippToPsps::Datastore($datastoreProduct, $verbose, $save_temps);}
-#my $batchManager = new ippToPsps::BatchManager($ippToPspsDb, $output, $verbose, $save_temps);
-
-# check we can run programs and get camera config
-my $ippToPsps = can_run('ippToPsps') or (warn "Can't find 'ippToPsps' program" and exit($PS_EXIT_CONFIG_ERROR));
-my $ipprc = PS::IPP::Config->new($camera) or (warn "Can't get camera configuration" and exit($PS_EXIT_CONFIG_ERROR)); 
-
-if ($batchType eq "init") {$initBatch = 1;}
-else {$initBatch = 0;}
-
-if (!process()) {print "* Finished unsuccessfully\n";}
-else {print "* Finished successfully\n";}
-
-#######################################################################################
-# 
-# Generates a PSPS-suitable survey 'type' from the IPP distribution group
-#
-#######################################################################################
-sub getSurveyTypeFromDistGroup {
-    my ($distGroup) = @_;
-
-    if ($distGroup =~ m/^MD([0-1][0-9])$/i) {return "MD$1";}
-    if ($distGroup =~ m/^M31$/i) {return "M31";}
-    if ($distGroup =~ m/^sts$/i) {return "STS1";}
-    if ($distGroup =~ m/^SweetSpot$/i) {return "SSS";}
-    if ($distGroup =~ m/^(3PI)|(ThreePi)|(SAS)$/i) {return "3PI";}
-
-    print "* Unknown distribution group: '$distGroup'\n";
-    return undef;
-}
-
-#######################################################################################
-# 
-# Finds all exposures for the provided TODO and generates PSPS FITS data for them
-#
-#######################################################################################
-sub process {
-
-    my ($resultsFile, $resultsFilePath) = tempfile( "/tmp/ippToPsps_results.XXXX", UNLINK => !$save_temps);
-    my $lastExpId = $ippToPspsDb->getLastExpId();
-    my $exposures;
-
-    my $query;
-    if ($initBatch) {
-
-        my @exposure = (0, 'NULL', 'ThreePi');
-        @{$exposures} = (\@exposure);
-    }
-    # get single exposure
-    elsif ($singleExpId) {
-       
-        if (!$gpc1Db->getSingleExposureFromDvoDb($dvoDb, $singleExpId, \$exposures)) {return 0;}
-    }
-    # get all exposures in this DVO Db
-    else {
-        
-        if (!$gpc1Db->getExposureListFromDvoDb($dvoDb, \$exposures)) {return 0;}
-    }
-
-    my $batchId = 0;
-    my $batchesPerJob = 1;
-    my $published = 0;
-
-    #my $batchId = -1;
-    my $exposure;
-    foreach $exposure ( @{$exposures} ) {
-        my ($expId, $expName, $distGroup) = @{$exposure};
-
-        if (!$initBatch && $ippToPspsDb->isExposureAlreadyProcessed($expId)) {
-
-            if ($force) {print "* Already processed '$expId', but forcing....\n";} 
-            else {
-            
-                print "* Exposure ID '$expId' has already been processed\n";
-                next
-            };
-        }
-
-        my $surveyType = getSurveyTypeFromDistGroup($distGroup);
-        if (!$surveyType) {next;}
-
-        $batchId = $ippToPspsDb->createNewBatch($expId, $distGroup, $pspsBatchType, $dvoDb, (defined $datastore) ? $datastoreProduct : "NONE");
-
-        # TODO quit here if no sensible batch ID
-
-        # generate batch path from batch IDs
-        my $batch = sprintf("B%08d", $batchId);
-        my $dvoDir = "$output/$dvoDb";
-        my $batchDir = "$dvoDir/$batch";
-
-        # make directories
-        unless(-d $dvoDir) {mkdir($dvoDir, 0777);}
-        mkdir($batchDir, 0777);
-
-        $published = 0;
-
-        print "* Preparing exposure $expId as batch '$batch'...\n";
-
-        # run IppToPsps program
-        if (runIppToPsps($expId, $expName, $surveyType, $batchDir, $batchType, $resultsFilePath)) {
-
-            my ($filename, $minObjId, $maxObjId, $totalDetections);
-            parseResults($resultsFilePath, \$filename, \$minObjId, \$maxObjId, \$totalDetections);
-
-            # write batch manifest and tar and zip up
-            if (writeBatchManifest($batchDir, $batch, $batchType, $surveyType, $filename, $minObjId, $maxObjId)) {
-
-                # tar n' zip
-                if (!$dontTarNZip) {
-                    my $tarball = tarAndZipBatch($dvoDir, $batch);
-
-                    # and publish
-                    if ($tarball && defined $datastore ) {
-                        
-                        $published = $datastore->register($batch, $dvoDir, $tarball, "IPP_PSPS", "tgz");
-                    }
-                }
-            }
-
-            $ippToPspsDb->updateBatch($batchId, $expId, 1, $published, $totalDetections, $minObjId, $maxObjId);
-        }
-
-        if ($initBatch) {print "* Wrote initialisation batch\n";}
-
-        print "*\n*******************************************************************************\n\n";
-    }
-
-    close $resultsFile;
-
-    return 1;
-}
-
-#######################################################################################
-#
-# run IppToPsps 
-#
-#######################################################################################
-sub runIppToPsps {
-    my ($expId, $expName, $surveyType, $batchDir, $batchType, $resultsFilePath) = @_;
-
-    my $success = 0;
-
-    if ($batchType =~ /init/) {
-        $success = produceInit($batchDir, $resultsFilePath);
-    }
-    elsif ($batchType =~ /det/) {
-        $success = produceDetections($expId, $expName, $surveyType, $batchDir, $resultsFilePath);
-    }
-    elsif ($batchType =~ /diff/) {
-        $success = produceDiffs($expId, $expName, $surveyType, $batchDir, $resultsFilePath);
-    }
-    elsif ($batchType =~ /test/) {
-        $success = produceDetections($expId, $expName, $surveyType, $batchDir, $resultsFilePath);
-    }
-
-    return $success;
-}
-
-#######################################################################################
-#
-# tar 'n zip a batch TODO handle errors
-#
-#######################################################################################
-sub tarAndZipBatch {
-    my ($path,$batch) = @_;
-
-    my $batchDir = "$path/$batch";
-    my $tar = "$batch.tar";
-    my $tarball = "$tar.gz";
-    my $batchTar = "$path/$tar";
-    my $batchZip = "$path/$tarball";
-
-    # tar
-    my $command = "tar -cvf $batchTar -C $path $batch";
-    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
-        run(command => $command, verbose => $verbose);
-    if (!$success) { print "* Unable to tar up contents of dir: $batchDir\n" and return undef; }
-
-    # zip
-    $command = "gzip -c $batchTar  > $batchZip";
-    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
-        run(command => $command, verbose => $verbose);
-    if (!$success) { print "* Unable to gzip: $batchTar\n" and return undef; }
-
-    # remove tar file
-    $command = "rm $batchTar";
-    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
-        run(command => $command, verbose => $verbose);
-    if (!$success) { print "* Unable to remove: $batchTar\n" };
-
-    # remove batch dir
-    $command = "rm -r $batchDir";
-    ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
-        run(command => $command, verbose => $verbose);
-    if (!$success) { print "* Unable to remove: $batch\n"};
-
-    return $tarball;
-}
-
-#######################################################################################
-#
-# zip a file then delete it 
-#
-#######################################################################################
-sub zipFile {
-    my ($path,$origFile) = @_;
-
-    my $file;
-
-    # zip
-    my $zippedName =  "$origFile.gz";
-    my $command = "gzip -c $path/$origFile  > $path/$zippedName";
-    my ($success, $error_code, $full_buf, $stdout_buf, $stderr_buf) =
-        run(command => $command, verbose => $verbose);
-
-    if (!$success) { 
-
-        $file = $origFile;
-        print "* Unable to gzip: $path/$origFile\n"; 
-    }
-    else {
-
-        $file = $zippedName;
-
-        # now delete original
-        $command = "rm -r $path/$origFile";
-        ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
-            run(command => $command, verbose => $verbose);
-
-        if (!$success) { print "* Unable to remove: $path/$origFile\n"};
-    }
-
-    return $file;
-}
-
-
-#######################################################################################
-#
-# parse results XML
-#
-#######################################################################################
-sub parseResults {
-    my ($resultsFilePath, $filename, $minObjId, $maxObjId, $totalDetections) = @_;
-
-    my $parser = XML::LibXML->new;
-    my $doc = $parser->parse_file($resultsFilePath);
-    ${$filename} = $doc->findvalue('//filename');
-    ${$minObjId} = $doc->findvalue('//minObjID');
-    ${$maxObjId} = $doc->findvalue('//maxObjID');
-    ${$totalDetections} = $doc->findvalue('//totalDetections');
-}
-
-
-#######################################################################################
-#
-# writes a batch manifest file for the one FITS file that should be in the path passed in
-#
-#######################################################################################
-sub writeBatchManifest {
-    my ($path,$batch,$batchType,$surveyType,$filename,$minObjId,$maxObjId) = @_;
-
-    use XML::Writer;
-    use Digest::MD5::File qw( file_md5_hex );
-
-    # sort out paths
-    my $outputPath = "$path/BatchManifest.xml";
-    my $output = new IO::File(">$outputPath");
-
-    # create a timestamp
-    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
-    my $timeStamp = sprintf "%4d-%02d-%02d %02d:%02d:%02d", $year+1900,$mon+1,$mday,$hour,$min,$sec;
-
-    # create XML file
-    my $writer = new XML::Writer(OUTPUT => $output, DATA_MODE => 1, DATA_INDENT=>2);
-    $writer->xmlDecl('UTF-8');
-    $writer->doctype('manifest', "", "psps-manifest.dtd");
-
-    # open directory to hunt for FITS files
-    opendir(DIR, $path) or print "* Cannot open '$path' to write batch manifest\n" and return 0;
-    my @thefiles= readdir(DIR);
-    closedir(DIR);
-
-    my $foundFits = 0;
-    foreach my $f (@thefiles) {
-
-        if ($f =~ /\.FITS/) {
-
-            if ($foundFits) { print "* More than one FITS file found for this batch\n"; return 0;}
-
-            # my $file = zipFile($path, $f); 
-
-            # get md5sum of file
-            my $md5sum  = file_md5_hex("$path/$f");
-
-            # get size of file in bytes
-            my $filesize = -s "$path/$f";
-
-            # write manifest element TODO untidy
-            if($batchType eq 'det' && $filename eq $f) {
-
-                my $pspsSurvey = undef;
-                if ($surveyType =~ m/^MD[0-1][0-9]$/i) {$pspsSurvey = "MDF";}
-                else {$pspsSurvey = $surveyType;}
-
-                $writer->startTag('manifest',
-                        "name" => "$batch",
-                        "type" => $pspsBatchType,
-                        "survey" => $pspsSurvey,
-                        "timestamp" => "$timeStamp",
-                        "minObjId" => $minObjId,
-                        "maxObjId" => $maxObjId);
-            }
-            else {
-
-                $writer->startTag('manifest',
-                        "name" => "$batch",
-                        "type" => $pspsBatchType,
-                        "timestamp" => "$timeStamp");
-            }
-            # write the tag for this batch
-            $writer->startTag("file", 
-                    "name" => $f,
-                    "bytes" => $filesize,
-                    "md5" => $md5sum);
-
-            $writer->endTag();
-            $writer->endTag();
-            $writer->end();
-            $foundFits = 1;
-        }
-
-    }
-
-    if (!$foundFits) { print "* Could not find any FITS files for this batch\n"; return 0;}
-    return 1;
-}
-
-#######################################################################################
-# 
-# runs ippToPsps to produce init batch 
-#
-#######################################################################################
-sub produceInit {
-    my ($outputPath, $resultsFilePath) = @_;
-
-    my $success = runProgram("NULL", $outputPath, 0, "NULL", "NULL", $batchType, $resultsFilePath);
-    return $success;
-}
-
-#######################################################################################
-# 
-# runs ippToPsps for detections for this exposure 
-#
-#######################################################################################
-sub produceDetections {
-    my ($expId, $expName, $surveyType, $outputPath, $resultsFilePath) = @_;
-
-    my $nebPath = $gpc1Db->getCameraStageSmfForThisDvoDb($dvoDb, $expId);
-    if (!$nebPath) { return 0; }
-
-    my $success = 0;
-
-    # get real filename from neb 'key'
-    my $fpaObjects = $ipprc->filename("PSASTRO.OUTPUT",$nebPath) or return 0;
-    my $realFile = $ipprc->file_resolve($fpaObjects) or return 0;
-
-    # now write the path to this file out to temp file
-    my ($tempFile, $tempName) = tempfile( "/tmp/ippToPsps_inputFileList.XXXX", UNLINK => !$save_temps);
-    print $tempFile $realFile . "\n";
-    close $tempFile;
-
-    $success = runProgram($tempName, $outputPath, $expId, $expName, $surveyType, $batchType, $resultsFilePath);
-    return $success;
-}
-
-#######################################################################################
-# 
-# runs ippToPsps for diffs for this exposure 
-#
-#######################################################################################
-sub produceDiffs {
-    my ($expId, $expName, $surveyType, $outputPath, $resultsFilePath) = @_;
-
-    my $rows = $gpc1Db->getDiffStageCmfs($expId);
-
-    my $size = @{$rows};
-    if ($size < 1) {print "* No cmf files found for this exposure\n"; return 0;}
-
-    my ($tempFile, $tempName) = tempfile( "inputFileList.XXXX", UNLINK => !$save_temps);
-
-    # loop round db results should be one file per skycell
-    my $success = 0;
-
-    my $row;
-    foreach $row ( @{$rows} ) {
-        my ($nebPath) = @{$row};
-
-        print "NEB PATH $nebPath\n";
-
-        # get real filename from neb 'key'
-        my $fpaObjects = $ipprc->filename("PPSUB.OUTPUT.SOURCES",$nebPath) or return 0;
-        my $realFile = $ipprc->file_resolve($fpaObjects) or return 0;
-        #print "REALFILE $realFile\n";
-
-        # now write the path to this file out to temp file
-        print $tempFile $realFile . "\n";
-    }
-
-    my $ret = runProgram($tempName, $outputPath, $expId, $expName, $surveyType, $batchType, $resultsFilePath);
-
-    close $tempFile;
-
-    return $ret;
-}
-
-#######################################################################################
-#
-# runs the ippToPsps program
-#
-#######################################################################################
-sub runProgram {
-    my ($input, $output, $expid, $expName, $surveyType, $batchType, $resultsFilePath) = @_;
-
-    # build command
-    my $command = "$ippToPsps"; 
-    $command .= " -input $input";
-    $command .= " -output $output";
-    $command .= " -D CATDIR $fullDvoPath";
-    $command .= " -config ../config"; # TODO
-        $command .= " -expid $expid";
-    $command .= " -expname $expName";
-    $command .= " -survey $surveyType";
-    $command .= " -batch $batchType";
-    $command .= " -results $resultsFilePath";
-
-    # run command
-    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf )
-        = run(command => $command, verbose => $verbose);
-
-    return $success;
-}
Index: branches/meh_branches/ppstack_test/ippToPsps/perl/removeFromDatastore.pl
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/perl/removeFromDatastore.pl	(revision 32161)
+++ 	(revision )
@@ -1,56 +1,0 @@
-#!/usr/bin/env perl
-
-use warnings;
-use strict;
-use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
-use Pod::Usage qw( pod2usage );
-
-use ippToPsps::Datastore;
-
-my $start = undef;
-my $end = undef;
-my $product = undef;
-
-GetOptions( 
-        'start|s=s' => \$start,
-        'end|e=s' => \$end,
-        'product|p=s' => \$product,
-        );
-
-my $quit = 0;
-print "\n*******************************************************************************\n";
-print "* \n";
-if (@ARGV) {
-    $quit=1;
-    print "* UNKNKOWN: option                          @ARGV\n";
-}
-if (!defined $product) {
-    $quit=1;
-    print "* REQUIRED: a datastore product name          -p <name>\n";
-}
-if (!defined $start) {
-    $quit=1;
-    print "* REQUIRED: starting batch number             -s <number>\n";
-}
-if (!defined $end) {
-    $end=99999999999;
-    print "* OPTIONAL: ending batch number               -e <number>    (default=$end)\n";
-}
-print "*\n*******************************************************************************\n";
-
-if ($quit) { exit; }
-
-my $datastore = new ippToPsps::Datastore($product, 0, 0);
-
-my $i;
-
-for ($i=$start; $i<$end+1; $i++) {
-
-    my $name = sprintf("B%08d", $i);
-    if ($datastore->remove($name)) {
-        print "* Successfully removed $name from datastore\n";
-    }
-    else {
-        print "* Unable to remove $name from datastore\n";
-    }
-}
Index: branches/meh_branches/ppstack_test/ippToPsps/src/Dvo.c
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/src/Dvo.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/src/Dvo.c	(revision 33415)
@@ -35,5 +35,5 @@
     mysql_query(this->mysql, "DELETE FROM dvoDetection");
 
-    mysql_query(this->mysql, "SELECT sourceID, externID FROM dvoMeta");
+    mysql_query(this->mysql, "SELECT SOURCE_ID, EXTERN_ID FROM dvoImages");
     MYSQL_RES* result = mysql_store_result(this->mysql);
 
@@ -61,5 +61,5 @@
 
         sprintf(sql, 
-                "UPDATE dvoMeta SET imageID = %d,flags = %d, photcode = %d WHERE sourceID = %d AND externID = %d",
+                "UPDATE dvoImages SET IMAGE_ID = %d, FLAGS = %d, PHOTCODE = %d WHERE SOURCE_ID = %d AND EXTERN_ID = %d",
                 image->imageID,
                 image->flags,
@@ -91,6 +91,5 @@
             else {
                 sprintf(sql, 
-                        "INSERT INTO dvoDetection (sourceID, imageID, ippDetectID, detectID, ippObjID, objID, flags) VALUES (%d, %d, %u, %lu, %lu, %lu, %u)",
-                        sourceID,   // sourceID
+                        "INSERT INTO dvoDetection (imageID, ippDetectID, detectID, ippObjID, objID, flags) VALUES (%d, %u, %lu, %lu, %lu, %u)",
                         image->imageID,    // imageID
                         dvoDetections[i].meas.detID,   // ippDetectID
@@ -117,5 +116,5 @@
   Constructor.
   */
-Dvo* new_Dvo(Logger* logger, const char* configFile, const char* dvoDatabase) {
+Dvo* new_Dvo(Logger* logger, const char* configFile, const char* dbname, const char* dvoDatabase) {
 
     Dvo* this = (Dvo*)calloc(1, sizeof(Dvo));
@@ -130,5 +129,4 @@
     char user[20];
     char passwd[20];
-    char dbname[20];
 
 
@@ -157,6 +155,5 @@
                     sprintf(name, "%s", dbParams->name);
                     value = xmlNodeListGetString(doc, dbParams->xmlChildrenNode, 1);
-                    if (strcmp(name, "name") == 0) sprintf(dbname, "%s", value);
-                    else if (strcmp(name, "user") == 0) sprintf(user, "%s", value);
+                    if (strcmp(name, "user") == 0) sprintf(user, "%s", value);
                     else if (strcmp(name, "host") == 0) sprintf(host, "%s", value);
                     else if (strcmp(name, "password") == 0) sprintf(passwd, "%s", value);
@@ -225,13 +222,12 @@
     Logger* logger = new_Logger(NULL, true);
 
-    if (argc < 3) {
+    if (argc < 4) {
 
         logger->print(logger, MSG_ERROR, "Dvo", "Incorrect arguments\n");
-        logger->print(logger, MSG_INFO, "Dvo", "Usage: ./dvo <pathToConfig> <pathToDvo>\n");
+        logger->print(logger, MSG_INFO, "Dvo", "Usage: ./dvo <pathToConfig> <dbName> <pathToDvo>\n");
     }
     else {
-        //Dvo * dvo = new_Dvo(logger, "/data/ipp004.0/gpc1/catdirs/ThreePi.V1");
-        Dvo * dvo = new_Dvo(logger, argv[1], argv[2]);
-        //dvo->getDetections(dvo, 33, 8345290);
+
+        Dvo * dvo = new_Dvo(logger, argv[1], argv[2], argv[3]);
         dvo->getDetections(dvo);
         dvo->destroy(dvo);
Index: branches/meh_branches/ppstack_test/ippToPsps/src/Dvo.h
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/src/Dvo.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/src/Dvo.h	(revision 33415)
@@ -42,5 +42,5 @@
 
 // constructor
-Dvo* new_Dvo(Logger* logger, const char* configFile, const char* dvoDatabase);
+Dvo* new_Dvo(Logger* logger, const char* configFile, const char* dbname, const char* dvoDatabase);
 
 #endif // IPPTOPSPS_DVO_H
Index: branches/meh_branches/ppstack_test/ippToPsps/xmlschema/BatchManifest.xsd
===================================================================
--- branches/meh_branches/ppstack_test/ippToPsps/xmlschema/BatchManifest.xsd	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippToPsps/xmlschema/BatchManifest.xsd	(revision 33415)
@@ -51,5 +51,10 @@
     <xs:enumeration value="SSS" />
     <xs:enumeration value="3PI" />
-    <xs:enumeration value="STS1" />
+    <xs:enumeration value="STS" />
+    <xs:enumeration value="SA1" />
+    <xs:enumeration value="SA2" />
+    <xs:enumeration value="SA3" />
+    <xs:enumeration value="SAS" />
+    <xs:enumeration value="OLD" />
    </xs:restriction>
   </xs:simpleType>
Index: branches/meh_branches/ppstack_test/ippTools/configure.ac
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/configure.ac	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/configure.ac	(revision 33415)
@@ -1,5 +1,5 @@
 AC_PREREQ(2.61)
 
-AC_INIT([ipptools], [1.1.66], [ipp-support@ifa.hawaii.edu])
+AC_INIT([ipptools], [1.1.71], [ipp-support@ifa.hawaii.edu])
 AC_CONFIG_SRCDIR([autogen.sh])
 
@@ -18,5 +18,5 @@
 PKG_CHECK_MODULES([PSLIB], [pslib >= 1.1.0])
 PKG_CHECK_MODULES([PSMODULES], [psmodules >= 1.1.0])
-PKG_CHECK_MODULES([IPPDB], [ippdb >= 1.1.66]) 
+PKG_CHECK_MODULES([IPPDB], [ippdb >= 1.1.71]) 
 PKG_CHECK_MODULES([PPSTAMP], [ppstamp >= 0.1.1]) 
 
Index: branches/meh_branches/ppstack_test/ippTools/share/Makefile.am
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/Makefile.am	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/Makefile.am	(revision 33415)
@@ -10,4 +10,6 @@
 	addtool_find_sky_id_dvo.sql \
 	addtool_find_sky_id.sql \
+	addtool_find_sky_id_multi_dvo.sql \
+	addtool_find_sky_id_multi.sql \
 	addtool_find_stack_id_dvo.sql \
 	addtool_find_stack_id.sql \
@@ -16,8 +18,10 @@
 	addtool_find_pendingexp_stack.sql \
 	addtool_find_pendingexp_staticsky.sql \
+	addtool_find_pendingexp_staticsky_multi.sql \
 	addtool_find_pendingmergeprocess.sql \
 	addtool_find_processedexp_cam.sql \
 	addtool_find_processedexp_stack.sql \
 	addtool_find_processedexp_staticsky.sql \
+	addtool_find_processedexp_staticsky_multi.sql \
 	addtool_find_minidvodbprocessed.sql \
 	addtool_find_minidvodbrun.sql \
@@ -27,4 +31,5 @@
 	addtool_queue_stack_id.sql \
 	addtool_queue_sky_id.sql \
+	addtool_queue_sky_id_multi.sql \
 	addtool_queue_minidvodbrun.sql \
 	addtool_revertminidvodbprocessed.sql \
@@ -32,4 +37,5 @@
 	addtool_revertprocessedexp_stack.sql \
 	addtool_revertprocessedexp_staticsky.sql \
+	addtool_revertprocessedexp_staticsky_multi.sql \
 	bgtool_advancechip.sql \
 	bgtool_advancewarp.sql \
@@ -76,4 +82,6 @@
 	chiptool_definecopy.sql \
 	chiptool_donecleanup.sql \
+	chiptool_dropprocessedimfile_update.sql \
+	chiptool_dropprocessedimfile_delete.sql \
 	chiptool_find_rawexp.sql \
 	chiptool_listrun.sql \
@@ -179,4 +187,5 @@
 	disttool_definebyquery_raw_no_magic.sql \
 	disttool_definebyquery_sky.sql \
+	disttool_definebyquery_sky_singlefilter.sql \
 	disttool_definebyquery_stack.sql \
 	disttool_definebyquery_warp.sql \
@@ -244,4 +253,5 @@
 	magictool_definebyquery_insert.sql \
 	magictool_definebyquery_select.sql \
+	magictool_definebyquery_select_multidiff.sql \
 	magictool_inputs.sql \
 	magictool_inputskyfile.sql \
@@ -274,4 +284,5 @@
 	magicdstool_definebyquery_warp_bg.sql \
 	magicdstool_definebyquery_diff.sql \
+	magicdstool_definecopy_camera.sql \
 	magicdstool_definecopy_chip.sql \
 	magicdstool_definecopy_warp.sql \
@@ -299,8 +310,19 @@
 	minidvodbtool_queue_minidvodb_id.sql \
 	minidvodbtool_revertminidvodbcopy.sql \
+	mergetool_find_minidvodb_id.sql \
+	mergetool_find_mergedvodbcopy.sql \
+	mergetool_find_merge_id_dvo.sql \
+	mergetool_find_pendingmergeprocess.sql \
+	mergetool_find_pending_merge.sql \
+	mergetool_find_processed.sql \
+	mergetool_queue_mergecopy_id.sql \
+	mergetool_queue_merge_id.sql \
+	mergetool_revertmergedvodbcopy.sql \
+	mergetool_revertmergedvodbprocessed.sql \
 	pstamptool_completedreq.sql \
 	pstamptool_datastore.sql \
 	pstamptool_getdependent.sql \
 	pstamptool_listjob.sql \
+	pstamptool_listfile.sql \
 	pstamptool_pendingcleanup.sql \
 	pstamptool_pendingdependent.sql \
@@ -373,4 +395,8 @@
 	staticskytool_definebyquery_select_by_dg.sql \
 	staticskytool_definebyquery_inputs.sql \
+	staticskytool_defineskycalrun.sql \
+	staticskytool_pendingskycalrun.sql \
+	staticskytool_revertskycal.sql \
+	staticskytool_skycalresult.sql \
 	staticskytool_export_run.sql \
 	staticskytool_export_input.sql \
@@ -418,9 +444,15 @@
 	laptool_definerun.sql \
 	laptool_exposures.sql \
-	laptool_inactiveexp.sql \ 
+	laptool_inactiveexp.sql \
 	laptool_listsequence.sql \
 	laptool_pendingchipexp.sql \
 	laptool_pendingexp.sql \
 	laptool_pendingrun.sql \
-	laptool_stacks.sql
+	laptool_WSdiff_check.sql \
+	laptool_stacks.sql \
+	vptool_find_rawexp.sql \
+	vptool_pendingimfile.sql \
+	vptool_pendingrun.sql \
+	vptool_processedcell.sql \
+	vptool_revertrun.sql
 
Index: branches/meh_branches/ppstack_test/ippTools/share/addtool_checkminidvodbaddrun.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/addtool_checkminidvodbaddrun.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/addtool_checkminidvodbaddrun.sql	(revision 33415)
@@ -1,4 +1,4 @@
 -- this selects the minidvodbs that have complete addRuns
-SELECT minidvodb_id, minidvodb_name, minidvodb_path, mergedvodb_path, state, minidbg 
+SELECT minidvodb_id, minidvodb_name, minidvodb_path, state, minidbg 
 AS minidvodb_group, (cnt - cnt2) as not_full, cnt as addRun_count
 FROM minidvodbRun 
Index: branches/meh_branches/ppstack_test/ippTools/share/addtool_find_minidvodbprocessed.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/addtool_find_minidvodbprocessed.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/addtool_find_minidvodbprocessed.sql	(revision 33415)
@@ -1,5 +1,5 @@
 SELECT
     minidvodbProcessed.*,
-    minidvodbRun.minidvodb_name	
+    minidvodbRun.minidvodb_name,	
     minidvodbRun.minidvodb_group
 FROM minidvodbProcessed
Index: branches/meh_branches/ppstack_test/ippTools/share/addtool_find_minidvodbrun2.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/addtool_find_minidvodbrun2.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/addtool_find_minidvodbrun2.sql	(revision 33415)
@@ -1,4 +1,4 @@
 -- this selects the minidvodbs that have complete addRuns
-SELECT minidvodb_name, minidvodb_path, mergedvodb_path, state, minidbg 
+SELECT minidvodb_name, minidvodb_path, state, minidbg 
 AS minidvodb_group 
 FROM minidvodbRun 
Index: branches/meh_branches/ppstack_test/ippTools/share/addtool_find_pendingexp_staticsky_multi.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/addtool_find_pendingexp_staticsky_multi.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/addtool_find_pendingexp_staticsky_multi.sql	(revision 33415)
@@ -0,0 +1,32 @@
+SELECT
+    addRun.*,
+    staticskyResult.path_base as stageroot,
+    rawExp.camera,
+    rawExp.telescope
+FROM addRun
+JOIN staticskyRun 
+    ON sky_id = stage_id
+JOIN staticskyResult
+     USING (sky_id)
+JOIN staticskyInput
+     USING (sky_id)
+JOIN stackRun
+    USING (stack_id)
+JOIN stackInputSkyfile 
+     USING(stack_id)
+JOIN warpRun using(warp_id)
+JOIN fakeRun using(fake_id)
+JOIN camRun using(cam_id)
+JOIN chipRun using(chip_id)
+JOIN rawExp using (exp_id)
+LEFT JOIN addProcessedExp using (add_id)
+LEFT JOIN addMask
+    ON addRun.label = addMask.label
+WHERE
+    staticskyRun.state = 'full'
+    AND stage = 'staticsky_multi'
+    AND ((addRun.state = 'new' AND addProcessedExp.add_id IS NULL) OR addRun.state = 'update')
+    AND addRun.dvodb IS NOT NULL
+    AND addRun.workdir IS NOT NULL
+    AND addMask.label IS NULL
+
Index: branches/meh_branches/ppstack_test/ippTools/share/addtool_find_processedexp_staticsky_multi.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/addtool_find_processedexp_staticsky_multi.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/addtool_find_processedexp_staticsky_multi.sql	(revision 33415)
@@ -0,0 +1,6 @@
+SELECT
+    addProcessedExp.*,
+    addRun.workdir
+FROM addProcessedExp
+JOIN addRun
+    USING(add_id)
Index: branches/meh_branches/ppstack_test/ippTools/share/addtool_find_sky_id_multi.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/addtool_find_sky_id_multi.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/addtool_find_sky_id_multi.sql	(revision 33415)
@@ -0,0 +1,24 @@
+SELECT
+     distinct(sky_id) as stage_id,
+     staticskyRun.*,
+    staticskyResult.num_inputs
+FROM 
+     staticskyResult
+join staticskyRun using (sky_id)
+join staticskyInput using(sky_id)
+JOIN stackRun USING(stack_id)
+
+LEFT JOIN (SELECT sky_id       AS added_sky_id,
+                  addRun.dvodb AS previous_dvodb
+           FROM addRun
+JOIN staticskyRun on sky_id = stage_id
+          ) as foo
+     ON sky_id = added_sky_id 
+     AND stage = 'staticsky_multi'
+     -- hook for qualifying the join on the previous_dvodb
+     AND %s
+WHERE
+    staticskyRun.state = 'full'
+    AND staticskyResult.quality = 0
+    AND added_exp_id IS NULL
+    -- addtool adds checks on exposure being added to the dvodb previously
Index: branches/meh_branches/ppstack_test/ippTools/share/addtool_find_sky_id_multi_dvo.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/addtool_find_sky_id_multi_dvo.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/addtool_find_sky_id_multi_dvo.sql	(revision 33415)
@@ -0,0 +1,12 @@
+SELECT distinct(sky_id) as stage_id, staticskyRun.*, staticskyResult.num_inputs FROM staticskyResult
+JOIN staticskyRun USING(sky_id)
+join staticskyInput using(sky_id)
+JOIN stackRun USING(stack_id)
+
+WHERE staticskyRun.state = 'full' and staticskyResult.quality = 0
+    AND sky_id NOT IN (SELECT sky_id
+       FROM addRun
+       JOIN staticskyResult on staticskyResult.sky_id = addRun.stage_id
+       JOIN staticskyRun USING(sky_id)
+       WHERE addRun.stage = 'staticsky_multi' AND %s
+      )
Index: branches/meh_branches/ppstack_test/ippTools/share/addtool_queue_cam_id.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/addtool_queue_cam_id.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/addtool_queue_cam_id.sql	(revision 33415)
@@ -4,4 +4,5 @@
 	'cam',		-- stage
         cam_id,         -- stage_id
+	%d,		-- stage_extra1
         '%s',           -- state
         '%s',           -- workdir
Index: branches/meh_branches/ppstack_test/ippTools/share/addtool_queue_minidvodbrun.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/addtool_queue_minidvodbrun.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/addtool_queue_minidvodbrun.sql	(revision 33415)
@@ -4,5 +4,4 @@
     minidvodb_name,
     minidvodb_path,
-    mergedvodb_path,
     'new'
 
Index: branches/meh_branches/ppstack_test/ippTools/share/addtool_queue_sky_id.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/addtool_queue_sky_id.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/addtool_queue_sky_id.sql	(revision 33415)
@@ -4,4 +4,5 @@
         'staticsky',		-- stage
         sky_id,         -- stage_id
+	%d,		-- stage_extra1
         '%s',           -- state
         '%s',           -- workdir
Index: branches/meh_branches/ppstack_test/ippTools/share/addtool_queue_sky_id_multi.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/addtool_queue_sky_id_multi.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/addtool_queue_sky_id_multi.sql	(revision 33415)
@@ -0,0 +1,22 @@
+INSERT INTO addRun
+    SELECT
+        0,              -- add_id
+        'staticsky_multi',		-- stage
+        sky_id,         -- stage_id
+	%d,		-- stage_extra1
+        '%s',           -- state
+        '%s',           -- workdir
+	'%s',           -- workdir_state
+        '%s',           -- reduction
+        '%s',           -- label
+        '%s',           -- data_group
+        '%s',           -- dvodb 
+        '%s',           -- note
+	%d,		-- image_only
+	%d,		-- minidvodb
+	'%s',           -- minidvodb_group 
+ 	'%s'	        -- minidvodb_name
+    FROM staticskyRun
+    WHERE
+        staticskyRun.state = 'full'
+        AND staticskyRun.sky_id = %lld
Index: branches/meh_branches/ppstack_test/ippTools/share/addtool_queue_stack_id.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/addtool_queue_stack_id.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/addtool_queue_stack_id.sql	(revision 33415)
@@ -4,4 +4,5 @@
         'stack',		-- stage
         stack_id,         -- stage_id
+	%d,		  --stage_extra1
         '%s',           -- state
         '%s',           -- workdir
Index: branches/meh_branches/ppstack_test/ippTools/share/addtool_revertprocessedexp_staticsky_multi.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/addtool_revertprocessedexp_staticsky_multi.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/addtool_revertprocessedexp_staticsky_multi.sql	(revision 33415)
@@ -0,0 +1,7 @@
+DELETE FROM addProcessedExp
+USING addProcessedExp, addRun
+WHERE
+    addRun.add_id = addProcessedExp.add_id
+    AND addProcessedExp.fault != 0
+    AND addRun.state = 'new'
+    AND addRun.stage = 'staticsky_multi'
Index: branches/meh_branches/ppstack_test/ippTools/share/bgtool_definechip.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/bgtool_definechip.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/bgtool_definechip.sql	(revision 33415)
@@ -5,3 +5,2 @@
 LEFT JOIN chipBackgroundRun USING(chip_id)
 WHERE chipRun.state = 'full'
-    AND chipRun.magicked > 0 -- require destreaked to stop race condition in destreaking
Index: branches/meh_branches/ppstack_test/ippTools/share/camtool_find_pendingimfile.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/camtool_find_pendingimfile.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/camtool_find_pendingimfile.sql	(revision 33415)
@@ -2,6 +2,7 @@
     camRun.cam_id,
     chipProcessedImfile.*,
-    (IF(exp_time IS NOT NULL AND deteff_magref IS NOT NULL,
-       (2.5 * LOG10(exp_time) + chipProcessedImfile.deteff_magref),  NULL))
+    (IF(rawExp.exp_time IS NOT NULL AND deteff_magref IS NOT NULL,
+       (2.5 * LOG10(rawExp.exp_time) + chipProcessedImfile.deteff_magref),
+        NULL))
         AS deteff_inst,
     rawExp.exp_name,
@@ -13,7 +14,9 @@
     USING (chip_id)
 JOIN chipProcessedImfile
-    USING (chip_id)
+    USING (chip_id, exp_id)
 JOIN rawExp
-  ON chipProcessedImfile.exp_id = rawExp.exp_id
+    USING(exp_id)
+JOIN rawImfile
+    USING(exp_id, class_id)
 LEFT JOIN camProcessedExp
     USING(cam_id)
@@ -22,2 +25,3 @@
 WHERE
     camMask.label IS NULL
+    AND rawImfile.ignored = 0
Index: branches/meh_branches/ppstack_test/ippTools/share/camtool_find_processedexp.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/camtool_find_processedexp.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/camtool_find_processedexp.sql	(revision 33415)
@@ -3,4 +3,5 @@
     camRun.workdir,
     camRun.label,
+    camRun.data_group,
     camRun.state,
     camRun.magicked,
Index: branches/meh_branches/ppstack_test/ippTools/share/chiptool_change_exp_state.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/chiptool_change_exp_state.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/chiptool_change_exp_state.sql	(revision 33415)
@@ -14,4 +14,5 @@
         WHERE
             chipProcessedImfile.chip_id = chipRun.chip_id
+            AND chipProcessedImfile.quality = 0
             AND data_state != '%s'
         ) = 0
Index: branches/meh_branches/ppstack_test/ippTools/share/chiptool_definecopy.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/chiptool_definecopy.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/chiptool_definecopy.sql	(revision 33415)
@@ -1,5 +1,8 @@
 SELECT
     rawExp.*,
-    newExp.label
+    newExp.label,
+    chipOld.workdir AS old_workdir,
+    chipOld.data_group AS old_data_group,
+    chipOld.state AS old_state
 FROM chipRun AS chipOld
 JOIN rawExp USING(exp_id)
Index: branches/meh_branches/ppstack_test/ippTools/share/chiptool_dropprocessedimfile_delete.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/chiptool_dropprocessedimfile_delete.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/chiptool_dropprocessedimfile_delete.sql	(revision 33415)
@@ -0,0 +1,7 @@
+DELETE FROM magicDSFile
+    USING magicDSRun, magicDSFile, chipRun, chipProcessedImfile
+WHERE chipRun.chip_id = chipProcessedImfile.chip_id
+    AND magicDSRun.stage ='chip'
+    AND magicDSRun.stage_id = chipRun.chip_id
+    AND magicDSRun.magic_ds_id = magicDSFile.magic_ds_id
+    AND magicDSFile.component = chipProcessedImfile.class_id
Index: branches/meh_branches/ppstack_test/ippTools/share/chiptool_dropprocessedimfile_update.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/chiptool_dropprocessedimfile_update.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/chiptool_dropprocessedimfile_update.sql	(revision 33415)
@@ -0,0 +1,5 @@
+UPDATE chipProcessedImfile 
+    SET quality = %d,
+        data_state = 'full',
+        magicked = 0,
+        fault = 0
Index: branches/meh_branches/ppstack_test/ippTools/share/chiptool_pendingcleanupimfile.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/chiptool_pendingcleanupimfile.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/chiptool_pendingcleanupimfile.sql	(revision 33415)
@@ -14,5 +14,5 @@
 WHERE
     ((chipRun.state = 'goto_cleaned' AND (chipProcessedImfile.data_state = 'full'
-                                       OR chipProcessedImfile.data_state = 'update'))
+                                       OR chipProcessedImfile.data_state = 'update') AND chipProcessedImfile.quality = 0)
 OR 
     (chipRun.state = 'goto_scrubbed' AND chipProcessedImfile.data_state != 'scrubbed')
Index: branches/meh_branches/ppstack_test/ippTools/share/chiptool_setimfiletoupdate.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/chiptool_setimfiletoupdate.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/chiptool_setimfiletoupdate.sql	(revision 33415)
@@ -1,7 +1,7 @@
 UPDATE chipRun
     JOIN chipProcessedImfile USING(chip_id)
-    LEFT JOIN magicDSRun ON (stage_id = chip_id AND stage = 'chip')
-    LEFT JOIN magicDSFile ON (magicDSRun.magic_ds_id = magicDSFile.magic_ds_id
-                              AND component = class_id)
+    -- LEFT JOIN magicDSRun ON (stage_id = chip_id AND stage = 'chip')
+    -- LEFT JOIN magicDSFile ON (magicDSRun.magic_ds_id = magicDSFile.magic_ds_id
+      --                         AND component = class_id)
 SET chipRun.state = 'update', 
     chipProcessedImfile.data_state = 'update',
@@ -11,7 +11,8 @@
     AND (chipRun.state = 'cleaned' OR chipRun.state = 'update')
     AND (chipProcessedImfile.data_state = 'cleaned')
+    AND (chipProcessedImfile.quality = 0)
     -- don't queue update if the associated magicDSFile exists and isn't cleaned
-    AND (chipRun.magicked = 0 
-      OR ((magicDSRun.state = 'cleaned' OR magicDSRun.state = 'update')
-            AND (magicDSFile.data_state = 'cleaned' OR magicDSFile.data_state = 'update'))
-    )
+    -- AND (chipRun.magicked = 0 
+    --   OR ((magicDSRun.state = 'cleaned' OR magicDSRun.state = 'update')
+    --         AND (magicDSFile.data_state = 'cleaned' OR magicDSFile.data_state = 'update'))
+    -- )
Index: branches/meh_branches/ppstack_test/ippTools/share/difftool_change_run_state.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/difftool_change_run_state.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/difftool_change_run_state.sql	(revision 33415)
@@ -14,3 +14,4 @@
             diffSkyfile.diff_id = diffRun.diff_id
             AND data_state != '%s'
+            AND diffSkyfile.quality = 0
         ) = 0
Index: branches/meh_branches/ppstack_test/ippTools/share/difftool_pendingcleanuprun.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/difftool_pendingcleanuprun.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/difftool_pendingcleanuprun.sql	(revision 33415)
@@ -2,5 +2,5 @@
 -- all of this is just to get the camera used for the diff run
 SELECT DISTINCT * FROM 
-   (SELECT 
+   (SELECT DISTINCT
 	    diffRun.diff_id,
 	    rawExp.camera,
@@ -29,6 +29,7 @@
              diffRun.state = 'goto_scrubbed' OR 
 	     diffRun.state = 'goto_purged')
+	@INNERLIMITS@
      UNION
-     SELECT
+     SELECT DISTINCT
 	    diffRun.diff_id,
 	    rawExp.camera,
@@ -57,6 +58,7 @@
              diffRun.state = 'goto_scrubbed' OR 
 	     diffRun.state = 'goto_purged')
+	@INNERLIMITS@
      UNION
-     SELECT
+     SELECT DISTINCT 
 	    diffRun.diff_id,
 	    rawExp.camera,
@@ -85,6 +87,7 @@
              diffRun.state = 'goto_scrubbed' OR 
 	     diffRun.state = 'goto_purged')
+	@INNERLIMITS@
      UNION
-     SELECT
+     SELECT DISTINCT
 	    diffRun.diff_id,
 	    rawExp.camera,
@@ -113,4 +116,5 @@
              diffRun.state = 'goto_scrubbed' OR 
 	     diffRun.state = 'goto_purged')
+	@INNERLIMITS@
 	) as Foo
 	WHERE 1
Index: branches/meh_branches/ppstack_test/ippTools/share/difftool_pendingcleanupskyfile.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/difftool_pendingcleanupskyfile.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/difftool_pendingcleanupskyfile.sql	(revision 33415)
@@ -9,5 +9,5 @@
     USING(diff_id)
 WHERE
-   ((diffRun.state = 'goto_cleaned'  AND (diffSkyfile.data_state = 'full' OR diffSkyfile.data_state = 'update'))
+   ((diffRun.state = 'goto_cleaned'  AND (diffSkyfile.data_state = 'full' OR diffSkyfile.data_state = 'update') AND diffSkyfile.quality = 0)
     OR
     (diffRun.state = 'goto_scrubbed' AND diffSkyfile.data_state != 'scrubbed')
Index: branches/meh_branches/ppstack_test/ippTools/share/difftool_setskyfiletoupdate.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/difftool_setskyfiletoupdate.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/difftool_setskyfiletoupdate.sql	(revision 33415)
@@ -8,2 +8,3 @@
     AND (diffRun.state = 'cleaned' OR diffRun.state = 'update')
     AND (diffSkyfile.data_state = 'cleaned' OR diffSkyfile.data_state = 'update')
+    AND (diffSkyfile.quality = 0)
Index: branches/meh_branches/ppstack_test/ippTools/share/difftool_todiffskyfile.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/difftool_todiffskyfile.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/difftool_todiffskyfile.sql	(revision 33415)
@@ -77,5 +77,5 @@
     OR (warpSkyfile.data_state = 'full'
     AND warpSkyfile.fault = 0
-    AND warpSkyfile.magicked >= 0
+--    AND warpSkyfile.magicked >= 0
     AND warpSkyfile.quality = 0))
 -- Ensure input stacks are available
@@ -88,5 +88,5 @@
     OR (warpTemplateSkyfile.data_state = 'full'
     AND warpTemplateSkyfile.fault = 0
-    AND warpSkyfile.magicked >= 0
+--    AND warpSkyfile.magicked >= 0
     AND warpTemplateSkyfile.quality = 0))
 -- Ensure template stacks are available
Index: branches/meh_branches/ppstack_test/ippTools/share/disttool_definebyquery_raw_no_magic.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/disttool_definebyquery_raw_no_magic.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/disttool_definebyquery_raw_no_magic.sql	(revision 33415)
@@ -14,5 +14,4 @@
 JOIN rcInterest USING(target_id)
 LEFT JOIN distRun ON distRun.stage_id = exp_id AND distRun.target_id = distTarget.target_id
-WHERE distRun.dist_id IS NULL           -- no existing distRun for this exposure
-    AND distTarget.state = 'enabled'    -- target and intrest are enabled
+WHERE distTarget.state = 'enabled'    -- target and intrest are enabled
     AND rcInterest.state = 'enabled'
Index: branches/meh_branches/ppstack_test/ippTools/share/disttool_definebyquery_sky.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/disttool_definebyquery_sky.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/disttool_definebyquery_sky.sql	(revision 33415)
@@ -22,4 +22,5 @@
 WHERE  distTarget.state = 'enabled'
     AND rcInterest.state = 'enabled'
+    AND distTarget.filter = 'multi'
     AND distRun.dist_id IS NULL
     AND ((staticskyRun.state = 'full') OR (distTarget.clean AND staticskyRun.state = 'cleaned'))
Index: branches/meh_branches/ppstack_test/ippTools/share/disttool_definebyquery_sky_singlefilter.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/disttool_definebyquery_sky_singlefilter.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/disttool_definebyquery_sky_singlefilter.sql	(revision 33415)
@@ -0,0 +1,28 @@
+SELECT
+    'sky' as stage,
+    staticskyRun.sky_id AS stage_id,
+    CAST(0 AS SIGNED) AS magicked,
+    -- run tag in the form 'sky.$skycell_id.$stack_id'
+    CONCAT_WS('.', 'sky', stackRun.skycell_id, convert(staticskyRun.sky_id, CHAR)) as run_tag,
+    staticskyRun.label,
+    staticskyRun.data_group,
+    distTarget.dist_group,
+    distTarget.target_id,
+    distTarget.clean
+FROM staticskyRun
+JOIN staticskyResult USING(sky_id)
+JOIN staticskyInput USING(sky_id)
+JOIN stackRun using(stack_id)
+JOIN distTarget ON distTarget.stage = 'sky'
+    AND staticskyRun.dist_group = distTarget.dist_group
+JOIN rcInterest USING(target_id)
+LEFT JOIN distRun ON (distRun.stage_id = sky_id)
+    AND distRun.target_id = distTarget.target_id
+    -- JOIN hook %s
+WHERE  distTarget.state = 'enabled'
+    AND rcInterest.state = 'enabled'
+    AND distRun.dist_id IS NULL
+    AND ((staticskyRun.state = 'full') OR (distTarget.clean AND staticskyRun.state = 'cleaned'))
+    AND stackRun.filter = distTarget.filter
+    -- we shouldn't need to check fault. If faulted it shouldn't be full
+    AND (staticskyResult.fault = 0 AND staticskyResult.quality = 0)
Index: branches/meh_branches/ppstack_test/ippTools/share/disttool_listinterests.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/disttool_listinterests.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/disttool_listinterests.sql	(revision 33415)
@@ -9,4 +9,5 @@
     stage,
     clean,
+    target_id,
     distTarget.state as target_state
 FROM rcInterest 
Index: branches/meh_branches/ppstack_test/ippTools/share/disttool_pending_chip_bg.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/disttool_pending_chip_bg.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/disttool_pending_chip_bg.sql	(revision 33415)
@@ -14,5 +14,5 @@
     chipBackgroundRun.state,
     chipBackgroundRun.state AS data_state,
-    0 AS quality,
+    chipBackgroundImfile.quality AS quality,
     distRun.no_magic,
     chipBackgroundImfile.magicked,
Index: branches/meh_branches/ppstack_test/ippTools/share/disttool_pending_raw.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/disttool_pending_raw.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/disttool_pending_raw.sql	(revision 33415)
@@ -43,4 +43,5 @@
     -- AND (rawExp.magicked OR distRun.no_magic)
     AND rawExp.magicked
+    AND rawImfile.ignored = 0
     -- need to have magicked the chip image which makes the camera mask
     AND chipProcessedImfile.magicked != 0
@@ -86,4 +87,5 @@
     AND distComponent.dist_id IS NULL
     AND (distRun.no_magic)
+    AND rawImfile.ignored = 0
     AND (Label.active OR Label.active IS NULL)
 UNION
@@ -131,4 +133,5 @@
     AND chipProcessedImfile.magicked != 0
     AND camRun.magicked > 0 
+    AND rawImfile.ignored = 0
     AND (Label.active OR Label.active IS NULL)
 UNION
Index: branches/meh_branches/ppstack_test/ippTools/share/disttool_processedcomponent.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/disttool_processedcomponent.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/disttool_processedcomponent.sql	(revision 33415)
@@ -10,4 +10,5 @@
     name,
     distComponent.state,
+    no_magic,
     distComponent.fault
 FROM    distRun
Index: branches/meh_branches/ppstack_test/ippTools/share/disttool_toadvance.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/disttool_toadvance.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/disttool_toadvance.sql	(revision 33415)
@@ -26,4 +26,5 @@
         AND distRun.fault = 0
         AND distRun.stage = 'raw'
+        AND rawImfile.ignored = 0
     GROUP BY
         distRun.dist_id,
Index: branches/meh_branches/ppstack_test/ippTools/share/laptool_WSdiff_check.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/laptool_WSdiff_check.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/laptool_WSdiff_check.sql	(revision 33415)
@@ -0,0 +1,24 @@
+SELECT warp_id,warp_state,W.skycell_id,stack_id,stack_state,S.seq_id
+  FROM
+( SELECT warp_id,skycell_id,seq_id,warpRun.state AS warp_state
+   FROM warpSkyfile
+   JOIN warpRun USING(warp_id)
+   JOIN fakeRun USING(fake_id)
+   JOIN camRun USING(cam_id)
+   JOIN chipRun USING(chip_id)
+   JOIN rawExp USING(exp_id)
+   JOIN lapExp USING(exp_id,chip_id)
+   JOIN lapRun USING(lap_id)
+   WHERE 1
+   @WHERE@
+) AS W
+LEFT JOIN
+( SELECT stack_id,skycell_id,seq_id,stackRun.state AS stack_state
+   FROM stackRun
+   JOIN stackAssociationMap USING(stack_id)
+   JOIN lapRun ON (stackAssociationMap.sass_id = lapRun.final_sass_id)
+   ) AS S
+ON (    (W.seq_id = S.seq_id)
+    AND (W.skycell_id = S.skycell_id) )
+
+
Index: branches/meh_branches/ppstack_test/ippTools/share/laptool_definerun.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/laptool_definerun.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/laptool_definerun.sql	(revision 33415)
@@ -1,3 +1,8 @@
-SELECT DISTINCT want.exp_id, have.chip_id, false as private, true as active, false as pairwise
+SELECT DISTINCT 
+  want.exp_id, 
+  have.chip_id, 
+  COALESCE(have.private,false) as private, 
+  true as active, 
+  false as pairwise
   FROM
   (SELECT exp_id FROM rawExp 
@@ -8,15 +13,21 @@
   ) AS want
   LEFT JOIN 
-  (SELECT exp_id,MAX(chip_id) AS chip_id, chipRun.state
+  (SELECT 
+     exp_id,
+     MAX(chip_id) AS chip_id, 
+     chipRun.state,
+     lapExp.private
      FROM lapExp JOIN chipRun USING(exp_id,chip_id) 
-     where private IS FALSE 
-     AND chip_id IS NOT NULL
+     JOIN lapRun USING(lap_id)
+     where chip_id IS NOT NULL
      AND 
-     	 (
+-- This is the restriction to only draw from the current lapSequence
+     @CHIPWHERE@
+--     	 (
 	  -- (active = TRUE) OR 
-	  (chipRun.state = 'full') OR
-	  (chipRun.state = 'new')
+	  -- (chipRun.state = 'full') OR
+	  -- (chipRun.state = 'new')
 	  -- when we can do updates, put that here.
-	  )
+--	  )
      GROUP BY exp_id
 ) AS have USING(exp_id)
Index: branches/meh_branches/ppstack_test/ippTools/share/laptool_pendingexp.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/laptool_pendingexp.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/laptool_pendingexp.sql	(revision 33415)
@@ -3,7 +3,9 @@
        lapRun.dist_group, lapRun.registered, lapRun.fault, lapRun.quick_sass_id, lapRun.final_sass_id,
        exp_id, chip_id, pair_id, private, pairwise, active, lapExp.data_state,
-       dateobs, object, comment
+       dateobs, object, comment,
+       chipRun.state AS chip_state
   FROM lapRun JOIN lapExp USING(lap_id)
   JOIN rawExp USING(exp_id,filter)
+  LEFT JOIN chipRun USING(exp_id,chip_id)
 WHERE active IS TRUE AND lapRun.fault = 0
 -- lap_id restriction here.
Index: branches/meh_branches/ppstack_test/ippTools/share/magicdstool_definecopy_camera.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/magicdstool_definecopy_camera.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/magicdstool_definecopy_camera.sql	(revision 33415)
@@ -0,0 +1,31 @@
+SELECT DISTINCT       -- DISTINCT because we are going though diffInputSkyfile
+    magicRun.magic_id,
+    chipRun.exp_id,
+    'camera' AS stage,
+    camRun.cam_id AS stage_id,
+    camRun.data_group,
+    camRun.cam_id,
+    camRun.label,
+    CAST(NULL AS SIGNED) AS inv_magic_id
+FROM magicRun
+    JOIN magicMask USING(magic_id)
+    JOIN diffRun USING(diff_id)
+    JOIN diffInputSkyfile USING(diff_id)
+    JOIN warpRun AS oldWarpRun
+        ON (!magicRun.inverse AND diffInputSkyfile.warp1 = oldWarpRun.warp_id)
+        OR ( magicRun.inverse AND diffInputSkyfile.warp2 = oldWarpRun.warp_id)
+    -- This ends the usual JOIN down to the raw level
+    JOIN rawExp
+        ON rawExp.exp_id = magicRun.exp_id
+    JOIN chipRun
+        ON chipRun.exp_id = rawExp.exp_id
+    JOIN camRun
+        ON camRun.chip_id = chipRun.chip_id
+    LEFT JOIN magicDSRun
+        ON magicRun.magic_id = magicDSRun.magic_id
+        AND magicDSRun.stage  = 'camera'
+        AND magicDSRun.label = '%s'
+WHERE magicRun.state = 'full'
+    -- HOOK: NEWLINE HERE TO ACTIVATE %s AND magicDSRun.magic_ds_id IS NULL
+    AND camRun.magicked  = 0
+    AND camRun.state = 'full'
Index: branches/meh_branches/ppstack_test/ippTools/share/magicdstool_getskycells.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/magicdstool_getskycells.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/magicdstool_getskycells.sql	(revision 33415)
@@ -5,5 +5,6 @@
     diffSkyfile.skycell_id,
     diffSkyfile.path_base,
-    diffSkyfile.data_state
+    diffSkyfile.data_state,
+    diffSkyfile.quality
 FROM magicDSRun
 JOIN magicRun USING(magic_id)
@@ -37,5 +38,6 @@
     diffSkyfile.skycell_id,
     diffSkyfile.path_base,
-    diffSkyfile.data_state
+    diffSkyfile.data_state,
+    diffSkyfile.quality
 FROM magicDSRun
 JOIN magicRun USING(magic_id)
Index: branches/meh_branches/ppstack_test/ippTools/share/magicdstool_todestreak_camera.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/magicdstool_todestreak_camera.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/magicdstool_todestreak_camera.sql	(revision 33415)
@@ -14,4 +14,5 @@
     stage_id,
     'exposure' AS component,
+    0 AS mismatched_tess,
     CAST(NULL AS CHAR(255)) AS uri,
     camProcessedExp.path_base,
Index: branches/meh_branches/ppstack_test/ippTools/share/magicdstool_todestreak_chip.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/magicdstool_todestreak_chip.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/magicdstool_todestreak_chip.sql	(revision 33415)
@@ -14,4 +14,5 @@
     stage_id,
     class_id AS component,
+    0 AS mismatched_tess,
     chipProcessedImfile.uri,
     chipProcessedImfile.path_base,
Index: branches/meh_branches/ppstack_test/ippTools/share/magicdstool_todestreak_diff.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/magicdstool_todestreak_diff.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/magicdstool_todestreak_diff.sql	(revision 33415)
@@ -15,4 +15,5 @@
     diffRun.magicked,
     diffSkyfile.skycell_id AS component,
+    0 AS mismatched_tess,
     CAST(NULL AS CHAR(255)) AS uri,
     diffSkyfile.path_base,
@@ -63,4 +64,5 @@
     diffRun.magicked,
     diffSkyfile.skycell_id AS component,
+    0 AS mismatched_tess,
     CAST(NULL AS CHAR(255)) AS uri,
     diffSkyfile.path_base,
Index: branches/meh_branches/ppstack_test/ippTools/share/magicdstool_todestreak_raw.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/magicdstool_todestreak_raw.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/magicdstool_todestreak_raw.sql	(revision 33415)
@@ -14,4 +14,5 @@
     rawExp.magicked,
     class_id as component,
+    0 AS mismatched_tess,
     rawImfile.uri AS uri,
     -- XXX: replace this with rawImfile.path_base once it exists
Index: branches/meh_branches/ppstack_test/ippTools/share/magicdstool_todestreak_warp.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/magicdstool_todestreak_warp.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/magicdstool_todestreak_warp.sql	(revision 33415)
@@ -14,4 +14,6 @@
     warpRun.magicked,
     warpSkyfile.skycell_id as component,
+    diffRun.tess_id != warpRun.tess_id AS mismatched_tess,
+    diffRun.tess_id as diff_tess_id,
     warpSkyfile.uri,
     warpSkyfile.path_base,
@@ -27,4 +29,5 @@
 JOIN magicMask USING (magic_id)
 JOIN magicRun USING (magic_id)
+JOIN diffRun USING(diff_id)
 JOIN warpRun ON warp_id = stage_id
 JOIN warpSkyfile USING(warp_id)
Index: branches/meh_branches/ppstack_test/ippTools/share/magictool_definebyquery_select_multidiff.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/magictool_definebyquery_select_multidiff.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/magictool_definebyquery_select_multidiff.sql	(revision 33415)
@@ -0,0 +1,53 @@
+-- Get a list of exposures on which magic may be performed
+SELECT DISTINCT
+    chipRun.exp_id,
+    MAX(diffWarps.diff_id) AS diff_id,
+    -- The following trick pulls out the 'inverse' value for the maximum diff_id
+    CONVERT(SUBSTRING_INDEX(GROUP_CONCAT(diffWarps.inverse ORDER BY diffWarps.diff_id DESC), ',', 1), UNSIGNED) AS inverse,
+    diff_data_group
+FROM (
+    -- Forward diffs
+    SELECT
+        diffRun.diff_id,
+        warp1 AS warp_id,
+        0 AS inverse,
+        diffRun.data_group AS diff_data_group
+    FROM diffRun
+    JOIN diffInputSkyfile USING(diff_id)
+    JOIN diffSkyfile USING(diff_id, skycell_id)
+    WHERE diffInputSkyfile.warp1 IS NOT NULL
+        AND diffRun.exposure = 1
+        AND diffRun.magicked = 0
+        AND diffSkyfile.quality = 0
+    -- diff WHERE hook %s
+    UNION
+    -- Backward diffs
+    SELECT
+        diffRun.diff_id,
+        warp2 AS warp_id,
+        1 AS inverse,
+        diffRun.data_group AS diff_data_group
+    FROM diffRun
+    JOIN diffInputSkyfile USING(diff_id)
+    JOIN diffSkyfile USING(diff_id, skycell_id)
+    WHERE diffInputSkyfile.warp2 IS NOT NULL
+        AND diffRun.exposure = 1
+        AND diffRun.bothways = 1
+        AND diffRun.magicked = 0
+        AND diffSkyfile.quality = 0
+    -- diff WHERE hook %s
+    ) AS diffWarps
+JOIN warpRun USING(warp_id)
+JOIN fakeRun USING(fake_id)
+JOIN camRun USING(cam_id)
+JOIN chipRun USING(chip_id)
+JOIN rawExp USING(exp_id)
+LEFT JOIN
+    (SELECT magic_id, exp_id, diff_id, label
+        FROM magicRun
+        -- rerun hook %s
+    ) AS oldMagicRun
+    ON oldMagicRun.exp_id = chipRun.exp_id
+    AND oldMagicRun.diff_id = diffWarps.diff_id
+-- WHERE hook %s
+GROUP BY chipRun.exp_id
Index: branches/meh_branches/ppstack_test/ippTools/share/mergetool_find_merge_id_dvo.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/mergetool_find_merge_id_dvo.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/mergetool_find_merge_id_dvo.sql	(revision 33415)
@@ -0,0 +1,9 @@
+SELECT mergedvodbRun.* FROM mergedvodbRun
+JOIN mergedvodbProcessed USING(merge_id)
+WHERE ( mergedvodbRun.state = 'merged' OR mergedvodbRun.state = 'full')
+      AND mergedvodbProcessed.fault = 0
+    AND merge_id NOT IN (SELECT merge_id
+       FROM mergedvodbCopy
+       JOIN mergedvodbRun USING(mergedvodb_id)
+       WHERE %s
+      )
Index: branches/meh_branches/ppstack_test/ippTools/share/mergetool_find_mergedvodbcopy.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/mergetool_find_mergedvodbcopy.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/mergetool_find_mergedvodbcopy.sql	(revision 33415)
@@ -0,0 +1,7 @@
+SELECT
+    mergedvodbCopy.*,
+    mergedvodbRun.mergedvodb_path
+FROM mergedvodbCopy
+    JOIN mergedvodbRun using (mergedvodb_id)
+
+
Index: branches/meh_branches/ppstack_test/ippTools/share/mergetool_find_minidvodb_id.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/mergetool_find_minidvodb_id.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/mergetool_find_minidvodb_id.sql	(revision 33415)
@@ -0,0 +1,8 @@
+SELECT minidvodbRun.* FROM minidvodbRun
+JOIN minidvodbProcessed USING(minidvodb_id)
+WHERE minidvodbRun.state = 'merged' and minidvodbProcessed.fault = 0
+    AND minidvodb_id NOT IN (SELECT minidvodb_id
+       FROM mergedvodbRun
+       JOIN minidvodbRun USING(minidvodb_id)
+       WHERE %s
+      )
Index: branches/meh_branches/ppstack_test/ippTools/share/mergetool_find_pending_merge.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/mergetool_find_pending_merge.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/mergetool_find_pending_merge.sql	(revision 33415)
@@ -0,0 +1,14 @@
+SELECT
+    mergedvodbRun.*,
+    minidvodbRun.minidvodb_path
+FROM minidvodbRun 
+JOIN mergedvodbRun 
+    USING(minidvodb_id)
+LEFT JOIN mergedvodbProcessed
+    USING(merge_id)
+WHERE
+  (mergedvodbRun.state = 'new' AND mergedvodbProcessed.merge_id IS NULL)
+    AND mergedvodbRun.mergedvodb IS NOT NULL
+    AND mergedvodbRun.mergedvodb_path IS NOT NULL
+  
+
Index: branches/meh_branches/ppstack_test/ippTools/share/mergetool_find_pendingmergeprocess.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/mergetool_find_pendingmergeprocess.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/mergetool_find_pendingmergeprocess.sql	(revision 33415)
@@ -0,0 +1,5 @@
+SELECT mergedvodbRun.* 
+FROM mergedvodbRun 
+LEFT JOIN mergedvodbProcessed
+USING(merge_id)
+WHERE (mergedvodbRun.state = 'new' AND mergedvodbProcessed.merge_id IS NULL)
Index: branches/meh_branches/ppstack_test/ippTools/share/mergetool_find_processed.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/mergetool_find_processed.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/mergetool_find_processed.sql	(revision 33415)
@@ -0,0 +1,7 @@
+SELECT
+    mergedvodbProcessed.*,
+    mergedvodbRun.mergedvodb_path
+FROM mergedvodbProcessed
+JOIN mergedvodbRun
+    USING(merge_id)
+
Index: branches/meh_branches/ppstack_test/ippTools/share/mergetool_queue_merge_id.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/mergetool_queue_merge_id.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/mergetool_queue_merge_id.sql	(revision 33415)
@@ -0,0 +1,11 @@
+INSERT INTO mergedvodbRun
+    SELECT
+        0,              -- merge_id
+	minidvodb_id,   -- minidvodb_id
+        '%s',           -- mergedvodb
+        '%s',           -- mergedvodb_path
+	'%s',           -- state
+    FROM minidvodbRun join minidvodbProcessed using (minidvodb_id)
+    WHERE
+        minidvodbRun.state = 'merged' and minidvodbProcessed.fault = 0
+        AND minidvodbRun.minidvodb_id = %lld
Index: branches/meh_branches/ppstack_test/ippTools/share/mergetool_queue_mergecopy_id.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/mergetool_queue_mergecopy_id.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/mergetool_queue_mergecopy_id.sql	(revision 33415)
@@ -0,0 +1,16 @@
+INSERT INTO mergedvodbCopy
+    SELECT
+        0,              -- mergedvodbcopy_id
+        merge_id,         -- merge_id
+        '%s',           -- mergedvodb_rsync_path
+        '%s',           -- destination_host
+	%d,           -- fault
+        '%s',           -- state
+        %s,           -- epoch
+        %f           -- dtime
+        
+    FROM mergedvodbRun
+    WHERE
+        (mergedvodbRun.state = 'merged'
+ 	OR mergedvodbRun.state = 'full')
+        AND mergedvodbRun.merge_id = %lld
Index: branches/meh_branches/ppstack_test/ippTools/share/mergetool_queue_minidvodb_id.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/mergetool_queue_minidvodb_id.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/mergetool_queue_minidvodb_id.sql	(revision 33415)
@@ -0,0 +1,15 @@
+INSERT INTO minidvodbCopy
+    SELECT
+        0,              -- minidvodbcopy_id
+        minidvodb_id,         -- minidvodb_id
+        '%s',           -- minidvodb_rsync_path
+        '%s',           -- destination_host
+	%d,           -- fault
+        '%s',           -- state
+        %s,           -- epoch
+        %f           -- dtime
+        
+    FROM minidvodbRun
+    WHERE
+        minidvodbRun.state = 'merged'
+        AND minidvodbRun.minidvodb_id = %lld
Index: branches/meh_branches/ppstack_test/ippTools/share/mergetool_revertmergedvodbcopy.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/mergetool_revertmergedvodbcopy.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/mergetool_revertmergedvodbcopy.sql	(revision 33415)
@@ -0,0 +1,7 @@
+UPDATE mergedvodbCopy
+ --   SET minidvodbCopy.state = 'new',
+    SET mergedvodbCopy.fault = 0
+WHERE
+    mergedvodbCopy.state = 'new'
+    AND mergedvodbCopy.fault >0    
+
Index: branches/meh_branches/ppstack_test/ippTools/share/mergetool_revertmergedvodbprocessed.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/mergetool_revertmergedvodbprocessed.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/mergetool_revertmergedvodbprocessed.sql	(revision 33415)
@@ -0,0 +1,6 @@
+DELETE FROM mergedvodbProcessed
+USING mergedvodbProcessed, mergedvodbRun
+WHERE
+    mergedvodbProcessed.merge_id = mergedvodbRun.merge_id
+    AND mergedvodbProcessed.fault != 0
+    AND mergedvodbRun.state = 'new'
Index: branches/meh_branches/ppstack_test/ippTools/share/minidvodbtool_find_minidvodb_id_dvo.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/minidvodbtool_find_minidvodb_id_dvo.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/minidvodbtool_find_minidvodb_id_dvo.sql	(revision 33415)
@@ -1,5 +1,6 @@
 SELECT minidvodbRun.* FROM minidvodbRun
 JOIN minidvodbProcessed USING(minidvodb_id)
-WHERE minidvodbRun.state = 'merged'
+WHERE minidvodbRun.state = 'merged'	
+      AND minidvodbProcessed.fault = 0
     AND minidvodb_id NOT IN (SELECT minidvodb_id
        FROM minidvodbCopy
Index: branches/meh_branches/ppstack_test/ippTools/share/pstamptool_listfile.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/pstamptool_listfile.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/pstamptool_listfile.sql	(revision 33415)
@@ -0,0 +1,5 @@
+SELECT
+    pstampFile.*,
+    pstampJob.req_id
+FROM pstampFile
+    JOIN pstampJob using(job_id)
Index: branches/meh_branches/ppstack_test/ippTools/share/pstamptool_pendingjob.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/pstamptool_pendingjob.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/pstamptool_pendingjob.sql	(revision 33415)
@@ -1,4 +1,7 @@
-SELECT pstampJob.*,
-    IFNULL(Label.priority, 10000) AS priority
+SELECT * FROM
+(SELECT pstampJob.*,
+    IFNULL(Label.priority, 10000) AS priority,
+    0 AS myParent,
+    0 AS runningChildren
 FROM pstampJob
     JOIN pstampRequest USING(req_id)
@@ -9,4 +12,37 @@
     AND pstampJob.state = 'run'
     AND pstampJob.fault = 0
+    AND NOT is_parent
     AND (dep_id = 0 OR pstampDependent.state = 'full')
     AND (Label.active OR Label.active IS NULL)
+    -- where hook 1 %s
+UNION
+SELECT * FROM
+  (SELECT pstampJob.*,
+        IFNULL(Label.priority, 10000) AS priority
+    FROM pstampJob
+        JOIN pstampRequest USING(req_id)
+        LEFT JOIN pstampDependent USING(dep_id)
+        LEFT JOIN Label ON pstampRequest.label = Label.label
+    WHERE pstampRequest.state = 'run'
+        AND pstampRequest.fault = 0
+        AND pstampJob.state = 'run'
+        AND pstampJob.fault = 0
+        AND is_parent
+        AND (dep_id = 0 OR pstampDependent.state = 'full')
+        AND (Label.active OR Label.active IS NULL)
+        -- where hook 2 %s
+    ) as parentJob
+    LEFT JOIN
+    (SELECT parent_id AS myParent,
+            count(job_id) as runningChildren
+        FROM pstampRequest JOIN pstampJob USING(req_id)
+        WHERE pstampRequest.state ='run'
+            AND pstampJob.state = 'run'
+            AND pstampJob.parent_id
+        GROUP BY parent_id
+    ) as childJobs
+    ON childJobs.myParent = parentJob.job_id
+    WHERE runningChildren IS NULL
+) AS theQuery
+
+
Index: branches/meh_branches/ppstack_test/ippTools/share/pstamptool_stopdependentjob.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/pstamptool_stopdependentjob.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/pstamptool_stopdependentjob.sql	(revision 33415)
@@ -5,5 +5,6 @@
 SET pstampJob.state = 'stop', 
     pstampJob.fault = %d,
-    pstampDependent.fault = %d 
+    pstampDependent.fault = %d, 
+    pstampDependent.state = '%s'
 WHERE pstampJob.state = 'run'
     AND pstampDependent.state = 'new'
Index: branches/meh_branches/ppstack_test/ippTools/share/pubtool_definerun.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/pubtool_definerun.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/pubtool_definerun.sql	(revision 33415)
@@ -15,4 +15,5 @@
     JOIN diffRun
     JOIN diffInputSkyfile USING(diff_id)
+    JOIN diffSkyfile using(diff_id)
     JOIN warpRun ON warpRun.warp_id = diffInputSkyfile.warp1 -- Only JOINing input, not reference!
     JOIN fakeRun USING(fake_id)
@@ -30,4 +31,5 @@
               )
            )
+        AND diffSkyfile.quality = 0
     -- WHERE hook %s
     UNION
Index: branches/meh_branches/ppstack_test/ippTools/share/pxadmin_create_tables.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/pxadmin_create_tables.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/pxadmin_create_tables.sql	(revision 33415)
@@ -190,7 +190,8 @@
     magicked BIGINT,
     PRIMARY KEY(exp_id),
+    KEY(end_stage),
+    KEY(fault),
+    KEY(state),
     KEY(exp_name),
-    KEY(end_stage),
-    KEY(fault),
     FOREIGN KEY(exp_id) REFERENCES newExp(exp_id)
 ) ENGINE=innodb DEFAULT CHARSET=latin1;
@@ -554,6 +555,5 @@
     KEY(workdir_state),
     KEY(label),
-    KEY(data_group),
-    INDEX(add_id, cam_id)
+    KEY(data_group)
 ) ENGINE=innodb DEFAULT CHARSET=latin1;
 
@@ -1157,6 +1157,6 @@
         tess_id VARCHAR(64),
         diff_skyfile_id BIGINT AUTO_INCREMENT,
+        PRIMARY KEY(diff_skyfile_id),
         KEY(diff_id, skycell_id),
-        KEY(diff_skyfile_id),
         KEY(warp1),
         KEY(warp2),
@@ -1442,5 +1442,8 @@
         fault SMALLINT,
         PRIMARY KEY(req_id),
-        KEY(req_id)
+        KEY(req_id),
+        KEY(state),
+        KEY(fault),
+        KEY(label)
 ) ENGINE=innodb DEFAULT CHARSET=latin1;
 
@@ -1456,6 +1459,15 @@
         options BIGINT,
         dep_id BIGINT,
+        fault_count INT,
+        parent_id BIGINT DEFAULT NULL,
+        is_parent TINYINT DEFAULT 0,
         PRIMARY KEY(job_id, req_id),
         KEY(job_id),
+        KEY(req_id),
+        KEY(state),
+        KEY(fault),
+        KEY(dep_id),
+        KEY(parent_id),
+        KEY(is_parent),
         FOREIGN KEY(req_id) REFERENCES pstampRequest(req_id)
 ) ENGINE=innodb DEFAULT CHARSET=latin1;
@@ -1468,7 +1480,7 @@
         component  VARCHAR(64),
         imagedb    VARCHAR(64),
+        outdir     VARCHAR(255),
         rlabel     VARCHAR(64),
         need_magic TINYINT,
-        outdir     VARCHAR(255),
         fault      SMALLINT,
         fault_count INT,
@@ -1477,5 +1489,15 @@
         KEY(stage),
         KEY(stage_id),
+        KEY(component),
+        KEY(imagedb),
         KEY(fault)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE pstampFile (
+    file_id BIGINT AUTO_INCREMENT,
+    job_id BIGINT NOT NULL,
+    path VARCHAR(255),
+    PRIMARY KEY(file_id),
+    KEY(job_id)
 ) ENGINE=innodb DEFAULT CHARSET=latin1;
 
@@ -1956,5 +1978,4 @@
        minidvodb_group VARCHAR(64) NOT NULL,
        minidvodb_path VARCHAR(255) NOT NULL,
-       mergedvodb_path VARCHAR(255) NOT NULL,
        state VARCHAR(64) NOT NULL,
        creation_date TIMESTAMP,
@@ -1964,14 +1985,12 @@
 CREATE TABLE minidvodbProcessed (
       minidvodb_id BIGINT,
-      merge_order BIGINT,
       dtime_resort FLOAT,
       dtime_relphot FLOAT,
-      dtime_verify FLOAT,
-      dtime_merge FLOAT,
-      dtime_script,
-      epoch TIMESTAMP,
-      mergedvodb_path VARCHAR(255) NOT NULL,
+      dtime_script FLOAT,
+      epoch timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
       fault SMALLINT NOT NULL,
-       KEY(minidvodb_id), CONSTRAINT UNIQUE(minidvodb_id), FOREIGN KEY(minidvodb_id) REFERENCES minidvodbRun (minidvodb_id)
+      UNIQUE KEY (minidvodb_id),
+      KEY(minidvodb_id),
+      FOREIGN KEY(minidvodb_id) REFERENCES minidvodbRun(minidvodb_id)
 ) ENGINE=innodb DEFAULT CHARSET=latin1;
 
@@ -1991,5 +2010,120 @@
 ) ENGINE=innodb DEFAULT CHARSET=latin1;
 
-
+CREATE TABLE mergedvodbRun (
+       merge_id BIGINT AUTO_INCREMENT,
+       minidvodb_id BIGINT,
+       mergedvodb VARCHAR(64) NOT NULL,
+       mergedvodb_path VARCHAR(255) NOT NULL,
+       state VARCHAR(64) NOT NULL,
+       creation_date TIMESTAMP,
+       PRIMARY KEY(merge_id), KEY(minidvodb_id), KEY(mergedvodb), INDEX(merge_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE mergedvodbProcessed (
+      merge_id BIGINT,
+      merge_order BIGINT,
+      dtime_verify FLOAT,
+      dtime_merge FLOAT,
+      dtime_script FLOAT,
+      epoch timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+      fault SMALLINT NOT NULL,
+      UNIQUE KEY (merge_id),
+      KEY(merge_id),
+      FOREIGN KEY(merge_id) REFERENCES mergedvodbRun(merge_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE mergedvodbCopy (
+    mergedvodbcopy_id     BIGINT AUTO_INCREMENT,
+    merge_id     BIGINT,
+    mergedvodb_rsync_path VARCHAR(255) NOT NULL,
+    destination_host VARCHAR(255) NOT NULL,
+    fault  SMALLINT NOT NULL,
+    state   VARCHAR(64) NOT NULL,
+    epoch TIMESTAMP,
+    dtime FLOAT,
+    PRIMARY KEY(mergedvodbcopy_id),
+    KEY(merge_id),
+    FOREIGN KEY(merge_id) REFERENCES mergedvodbRun(merge_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE vpRun (
+    vp_id BIGINT AUTO_INCREMENT,
+    exp_id BIGINT,
+    state VARCHAR(64),
+    label VARCHAR(64),
+    data_group VARCHAR(64),
+    workdir VARCHAR(255),
+    note VARCHAR(255),
+    dest_id BIGINT,
+    outroot VARCHAR(255),
+    dtime_script FLOAT,
+    hostname VARCHAR(64),
+    fault SMALLINT NOT NULL DEFAULT 0,
+    PRIMARY KEY(vp_id),
+    KEY(exp_id),
+    KEY(state),
+    KEY(label),
+    KEY(data_group),
+    KEY(fault),
+    INDEX(vp_id, exp_id),
+    FOREIGN KEY(exp_id) REFERENCES rawExp(exp_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE vpProcessedCell (
+    vp_id BIGINT,
+    class_id VARCHAR(64),
+    cell_id VARCHAR(64),
+    dtime_photom FLOAT,
+    quality SMALLINT NOT NULL DEFAULT 0,
+    path_base VARCHAR(255),
+    fault SMALLINT NOT NULL DEFAULT 0,
+    KEY(quality),
+    KEY(fault),
+    FOREIGN KEY(vp_id) REFERENCES vpRun(vp_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+CREATE TABLE skycalRun (
+      skycal_id BIGINT AUTO_INCREMENT, -- unique identifier
+      sky_id BIGINT,
+      stack_id BIGINT,
+      state VARCHAR(64) NOT NULL,  -- state of run (new, full, etc.)
+      workdir VARCHAR(255) NOT NULL, -- working directory
+      label VARCHAR(64),             -- processing label
+      data_group VARCHAR(64),        -- group for data
+      dist_group VARCHAR(64),        -- group for distribution
+      reduction VARCHAR(64),         -- reduction class (for altering recipe)
+      registered TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- time run was registered
+      note VARCHAR(255),             -- note
+      PRIMARY KEY(skycal_id),
+      KEY(state),
+      KEY(label),
+      KEY(data_group),
+      KEY(dist_group),
+      KEY(sky_id),
+      KEY(stack_id),
+      FOREIGN KEY(sky_id) REFERENCES staticskyRun(sky_id),
+      FOREIGN KEY(stack_id) REFERENCES stackRun(stack_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
+
+-- Result of sky calibration analysis
+CREATE TABLE skycalResult (
+      skycal_id BIGINT,
+      path_base VARCHAR(255) NOT NULL,
+      dtime_script FLOAT,
+      dtime_astrom FLOAT,
+      sigma_ra FLOAT,
+      sigma_dec FLOAT,
+      n_astrom INT,
+      zpt_obs FLOAT,
+      zpt_stdev FLOAT,
+      quality SMALLINT NOT NULL,
+      hostname VARCHAR(64) NOT NULL,
+      software_ver VARCHAR(64),
+      fault SMALLINT NOT NULL,
+      PRIMARY KEY(skycal_id),
+      KEY(fault),
+      KEY(quality),
+      FOREIGN KEY(skycal_id) REFERENCES skycalRun(skycal_id)
+) ENGINE=innodb DEFAULT CHARSET=latin1;
 
 
Index: branches/meh_branches/ppstack_test/ippTools/share/pxadmin_drop_tables.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/pxadmin_drop_tables.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/pxadmin_drop_tables.sql	(revision 33415)
@@ -86,4 +86,5 @@
 DROP TABLE IF EXISTS publishDone;
 DROP TABLE IF EXISTS pstampDependent;
+DROP TABLE IF EXISTS pstampFile;
 DROP TABLE IF EXISTS staticskyInput;
 DROP TABLE IF EXISTS staticskyResult;
@@ -97,4 +98,14 @@
 DROP TABLE IF EXISTS diffPhotRun;
 DROP TABLE IF EXISTS diffPhotSkyfile;
+DROP TABLE IF EXISTS lapExp;
+DROP TABLE IF EXISTS lapRun;
+DROP TABLE IF EXISTS lapSequence;
+DROP TABLE IF EXISTS minidvodbRun;
+DROP TABLE IF EXISTS minidvodbCopy;
+DROP TABLE IF EXISTS minidvodbProcessed;
+DROP TABLE IF EXISTS vpProcessedCell;
+DROP TABLE IF EXISTS vpRun;
+DROP TABLE IF EXISTS skycalRun;
+DROP TABLE IF EXISTS skycalResult;
 
 SET FOREIGN_KEY_CHECKS=1
Index: branches/meh_branches/ppstack_test/ippTools/share/pztool_pendingimfile.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/pztool_pendingimfile.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/pztool_pendingimfile.sql	(revision 33415)
@@ -19,3 +19,24 @@
         AND pzDownloadImfile.class IS NULL
         AND pzDownloadImfile.class_id IS NULL
+	AND summitImfile.exp_name NOT LIKE '%a'
+UNION
+    SELECT
+        summitImfile.*,
+        summitExp.dateobs
+    FROM summitImfile
+    JOIN pzDownloadExp
+    	USING(summit_id)
+    LEFT JOIN pzDownloadImfile
+        USING(summit_id, class, class_id)
+    JOIN summitExp
+    	USING(summit_id)
+    WHERE 
+        pzDownloadExp.state = 'run'
+	AND pzDownloadImfile.summit_id IS NULL
+        AND pzDownloadImfile.exp_name IS NULL
+        AND pzDownloadImfile.camera IS NULL
+        AND pzDownloadImfile.telescope IS NULL
+        AND pzDownloadImfile.class IS NULL
+        AND pzDownloadImfile.class_id IS NULL
+	AND summitImfile.exp_name LIKE '%a'
 ) as Foo
Index: branches/meh_branches/ppstack_test/ippTools/share/stacktool_inputskyfile.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/stacktool_inputskyfile.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/stacktool_inputskyfile.sql	(revision 33415)
@@ -1,5 +1,10 @@
 SELECT
     warpSkyfile.*,
-    rawExp.camera
+    rawExp.camera,
+    rawExp.exp_id,
+    rawExp.exp_name,
+    rawExp.object,
+    rawExp.dateobs,
+    rawExp.comment
 FROM stackRun
 JOIN stackInputSkyfile USING(stack_id)
Index: branches/meh_branches/ppstack_test/ippTools/share/staticskytool_defineskycalrun.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/staticskytool_defineskycalrun.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/staticskytool_defineskycalrun.sql	(revision 33415)
@@ -0,0 +1,16 @@
+SELECT
+    sky_id,
+    stack_id,
+    staticskyRun.workdir,
+    staticskyRun.label,
+    staticskyRun.data_group,
+    staticskyRun.dist_group
+FROM staticskyRun 
+    JOIN staticskyResult USING(sky_id)
+    JOIN staticskyInput USING(sky_id)
+    JOIN stackRun USING(stack_id)
+    JOIN stackSumSkyfile USING(stack_id)
+    LEFT JOIN skycalRun USING(sky_id, stack_id)
+WHERE staticskyRun.state = 'full'
+    AND staticskyResult.quality = 0
+
Index: branches/meh_branches/ppstack_test/ippTools/share/staticskytool_pendingskycalrun.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/staticskytool_pendingskycalrun.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/staticskytool_pendingskycalrun.sql	(revision 33415)
@@ -0,0 +1,21 @@
+SELECT
+    skycalRun.*,
+    stackRun.tess_id,
+    stackRun.skycell_id,
+    stackRun.filter,
+    staticskyResult.path_base,
+    rawExp.camera,
+    staticskyResult.num_inputs = 1 AS singlefilter
+FROM skycalRun
+JOIN staticskyRun USING (sky_id)
+JOIN staticskyResult USING(sky_id)
+JOIN stackRun USING (stack_id)
+JOIN stackInputSkyfile USING(stack_id)
+JOIN warpRun USING(warp_id)
+JOIN fakeRun USING(fake_id)
+JOIN camRun USING(cam_id)
+JOIN chipRun USING (chip_id)
+JOIN rawExp USING(exp_id)
+LEFT JOIN skycalResult USING(skycal_id)
+WHERE
+   (skycalRun.state = 'new' AND skycalResult.skycal_id IS NULL)
Index: branches/meh_branches/ppstack_test/ippTools/share/staticskytool_result.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/staticskytool_result.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/staticskytool_result.sql	(revision 33415)
@@ -1,3 +1,3 @@
-SELECT DISTINCT
+SELECT
     staticskyResult.*,
     staticskyRun.state,
@@ -6,5 +6,5 @@
     stackRun.tess_id,
     stackRun.skycell_id,
-    stackRun.filter
+    count(stackRun.filter) AS num_filters
 FROM staticskyRun
 JOIN staticskyResult USING(sky_id)
Index: branches/meh_branches/ppstack_test/ippTools/share/staticskytool_revertskycal.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/staticskytool_revertskycal.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/staticskytool_revertskycal.sql	(revision 33415)
@@ -0,0 +1,6 @@
+DELETE FROM skycalResult
+USING skycalResult, skycalRun, stackRun
+WHERE skycalRun.skycal_id = skycalResult.skycal_id
+    AND stackRun.stack_id = skycalRun.stack_id
+    AND skycalRun.state = 'new'
+    AND skycalResult.fault != 0
Index: branches/meh_branches/ppstack_test/ippTools/share/staticskytool_skycalresult.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/staticskytool_skycalresult.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/staticskytool_skycalresult.sql	(revision 33415)
@@ -0,0 +1,14 @@
+SELECT 
+    skycalResult.*,
+    skycalRun.stack_id,
+    stackRun.tess_id,
+    stackRun.skycell_id,
+    stackRun.filter,
+    skycalRun.state,
+    skycalRun.label,
+    skycalRun.data_group,
+    skycalRun.sky_id
+FROM skycalRun
+JOIN skycalResult USING(skycal_id)
+JOIN stackRun USING(stack_id)
+
Index: branches/meh_branches/ppstack_test/ippTools/share/staticskytool_todo.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/staticskytool_todo.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/staticskytool_todo.sql	(revision 33415)
@@ -6,12 +6,17 @@
     staticskyRun.state,
     stackRun.tess_id,
-    stackRun.skycell_id
+    stackRun.skycell_id,
+    IFNULL(Label.priority, 10000) AS priority
 FROM staticskyRun
 JOIN staticskyInput USING (sky_id)
 JOIN stackRun USING (stack_id)
 LEFT JOIN staticskyResult USING(sky_id)
+LEFT JOIN Label ON staticskyRun.label = Label.label
 WHERE
    (staticskyRun.state = 'new' AND staticskyResult.sky_id IS NULL)
+   AND (Label.active OR Label.active IS NULL)
+
     -- WHERE hook %s
 GROUP BY sky_id
 HAVING SUM(IF(stackRun.state = 'full', 1, 0)) = COUNT(staticskyInput.sky_id)
+ORDER BY priority DESC, sky_id
Index: branches/meh_branches/ppstack_test/ippTools/share/vptool_find_rawexp.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/vptool_find_rawexp.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/vptool_find_rawexp.sql	(revision 33415)
@@ -0,0 +1,12 @@
+-- this query is used to find potental rawExps to be queued for vptool
+-- processeing
+SELECT DISTINCT 
+    rawExp.*,
+    newExp.label
+FROM rawExp
+JOIN newExp using (exp_id)
+JOIN rawImfile using(exp_id)
+LEFT JOIN vpRun USING (exp_id)
+WHERE
+    rawExp.fault = 0
+    AND rawImfile.video_cells
Index: branches/meh_branches/ppstack_test/ippTools/share/vptool_pendingimfile.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/vptool_pendingimfile.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/vptool_pendingimfile.sql	(revision 33415)
@@ -0,0 +1,12 @@
+SELECT
+    vpRun.*,
+    rawImfile.class_id,
+    rawImfile.uri
+FROM vpRun
+    JOIN rawExp USING(exp_id)
+    JOIN rawImfile USING(exp_id)
+    LEFT JOIN vpProcessedCell USING(vp_id, class_id)
+WHERE rawImfile.video_cells
+    AND vpRun.state = 'new'
+    AND vpProcessedCell.vp_id IS NULL
+
Index: branches/meh_branches/ppstack_test/ippTools/share/vptool_pendingrun.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/vptool_pendingrun.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/vptool_pendingrun.sql	(revision 33415)
@@ -0,0 +1,17 @@
+SELECT vp_id,
+       rawExp.camera,
+       CONCAT_WS('.', exp_name, exp_id) AS exp_tag,
+       vpRun.label,
+       vpRun.workdir,
+       vpRun.dest_id,
+       rcDestination.name AS dest_name,
+       rcDestination.dbname AS ds_dbname,
+       rcDestination.dbhost AS ds_dbhost,
+       IFNULL(Label.priority, 10000) as priority
+FROM vpRun
+    JOIN rawExp USING(exp_id)
+    LEFT JOIN rcDestination USING(dest_id)
+    LEFT JOIN Label ON vpRun.label = Label.label
+WHERE vpRun.state = 'new'
+    AND vpRun.fault = 0
+    AND (Label.active OR Label.active IS NULL)
Index: branches/meh_branches/ppstack_test/ippTools/share/vptool_processedcell.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/vptool_processedcell.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/vptool_processedcell.sql	(revision 33415)
@@ -0,0 +1,22 @@
+SELECT
+    vpProcessedCell.*,
+    vpRun.state,
+    vpRun.label,
+    vpRun.data_group,
+    vpRun.outroot,
+    rawExp.exp_id,
+    rawExp.exp_tag,
+    rawExp.exp_name,
+    rawExp.filter,
+    rawExp.camera,
+    rawExp.telescope,
+    rawExp.filelevel,
+    rawExp.dateobs,
+    rawExp.obs_mode,
+    rawExp.comment,
+    rawExp.ra,
+    rawExp.decl,
+    rawExp.exp_time
+FROM vpRun 
+    JOIN vpProcessedCell USING(vp_id)
+    JOIN rawExp USING(exp_id)
Index: branches/meh_branches/ppstack_test/ippTools/share/vptool_revertrun.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/vptool_revertrun.sql	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/share/vptool_revertrun.sql	(revision 33415)
@@ -0,0 +1,4 @@
+UPDATE vpRun
+    SET fault = 0
+WHERE vpRun.state = 'new'
+    AND fault > 0
Index: branches/meh_branches/ppstack_test/ippTools/share/warptool_change_run_state.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/warptool_change_run_state.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/warptool_change_run_state.sql	(revision 33415)
@@ -17,3 +17,4 @@
             warpSkyfile.warp_id = warpRun.warp_id
             AND warpSkyfile.data_state != '%s'
+            AND warpSkyfile.quality = 0
         ) = 0
Index: branches/meh_branches/ppstack_test/ippTools/share/warptool_pendingcleanupskyfile.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/warptool_pendingcleanupskyfile.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/warptool_pendingcleanupskyfile.sql	(revision 33415)
@@ -11,5 +11,6 @@
 WHERE
    ((warpRun.state = 'goto_cleaned'  AND (warpSkyfile.data_state = 'full'
-                                      OR  warpSkyfile.data_state = 'update'))
+                                      OR  warpSkyfile.data_state = 'update')
+    AND warpSkyfile.quality = 0)
     OR
     (warpRun.state = 'goto_scrubbed' AND warpSkyfile.data_state != 'scrubbed')
Index: branches/meh_branches/ppstack_test/ippTools/share/warptool_scmap.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/warptool_scmap.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/warptool_scmap.sql	(revision 33415)
@@ -29,4 +29,5 @@
     ON chipRun.chip_id = chipProcessedImfile.chip_id
     AND warpSkyCellMap.class_id = chipProcessedImfile.class_id
+    AND chipProcessedImfile.quality = 0
 JOIN rawImfile
     ON chipRun.exp_id = rawImfile.exp_id
Index: branches/meh_branches/ppstack_test/ippTools/share/warptool_setskyfiletoupdate.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/warptool_setskyfiletoupdate.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/warptool_setskyfiletoupdate.sql	(revision 33415)
@@ -8,2 +8,3 @@
     AND (warpRun.state = 'cleaned' OR warpRun.state = 'update')
     AND (warpSkyfile.data_state = 'cleaned' OR warpSkyfile.data_state = 'update')
+    AND (warpSkyfile.quality = 0)
Index: branches/meh_branches/ppstack_test/ippTools/share/warptool_towarped.sql
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/share/warptool_towarped.sql	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/share/warptool_towarped.sql	(revision 33415)
@@ -100,10 +100,11 @@
         AND warpSkyfile.fault = 0
         AND camRun.state = 'full'
+        AND chipProcessedImfile.quality = 0
+        -- Maybe add this to solve the if any chip is bad a whole warp can't be updated problem?
+        -- AND (chipProcessedImfile.fault = 0 OR chipProcessedImfile.fault = 26)
         AND (Label.active OR Label.active IS NULL)
         -- where hook 2 %s
     GROUP BY warp_id, skycell_id
-    -- if warpSkyfile was magicked previously require inputs to be magicked
-    -- this blocks processing until all the chip inputs have been destreaked
-    HAVING COUNT(warpSkyCellMap.class_id) = SUM(IF(chipProcessedImfile.data_state ='full' and (chipRun.magicked = 0 OR chipProcessedImfile.magicked > 0), 1, 0))
+    HAVING COUNT(warpSkyCellMap.class_id) = SUM(IF(chipProcessedImfile.data_state ='full', 1, 0))
     -- limit hook 2 %s
     )
Index: branches/meh_branches/ppstack_test/ippTools/src/Makefile.am
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/Makefile.am	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/Makefile.am	(revision 33415)
@@ -29,5 +29,7 @@
 	diffphottool \
 	minidvodbtool \
-	laptool
+	mergetool \
+	laptool \
+	vptool
 
 pkginclude_HEADERS = \
@@ -42,4 +44,5 @@
 	pxmagic.h \
 	pxminidvodb.h \
+	pxmerge.h \
 	pxregister.h \
 	pxspace.h \
@@ -78,5 +81,7 @@
 	diffphottool.h \
 	minidvodbtool.h \
-	laptool.h
+	mergetool.h \
+	laptool.h \
+	vptool.h
 
 lib_LTLIBRARIES = libpxtools.la
@@ -96,4 +101,5 @@
 	pxmagic.c \
 	pxminidvodb.c \
+	pxmerge.c \
 	pxregister.c \
 	pxspace.c \
@@ -292,4 +298,10 @@
     minidvodbtoolConfig.c
 
+mergetool_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+mergetool_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+mergetool_SOURCES = \
+    mergetool.c \
+    mergetoolConfig.c
+
 laptool_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
 laptool_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
@@ -298,4 +310,10 @@
     laptoolConfig.c
 
+vptool_CFLAGS = $(PSLIB_CFLAGS) $(PSMODULES_CFLAGS) $(IPPDB_CFLAGS)
+vptool_LDADD = $(PSLIB_LIBS) $(PSMODULES_LIBS) $(IPPDB_LIBS) libpxtools.la
+vptool_SOURCES = \
+    vptool.c \
+    vptoolConfig.c
+
 clean-local:
 	-rm -f TAGS
Index: branches/meh_branches/ppstack_test/ippTools/src/addtool.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/addtool.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/addtool.c	(revision 33415)
@@ -143,5 +143,11 @@
     pxAddLabelSearchArgs (config, where, "-filter",     "stackRun.filter", "=="); // define using camRun label
     }
-    
+        if (strcmp(stage, "staticsky_multi")== 0) {
+
+    pxAddLabelSearchArgs (config, where, "-label",     "staticskyRun.label", "=="); // define using camRun label
+    pxAddLabelSearchArgs (config, where, "-data_group","staticskyRun.data_group", "=="); // define using camRun label
+    PXOPT_COPY_STR(config->args, where,  "-reduction", "staticskyyRun.reduction", "==");
+    //no filter here
+    }
     if (!psListLength(where->list)) {
         psFree(where);
@@ -237,5 +243,19 @@
     }
 
-
+    if (strcmp(stage,"staticsky_multi") == 0) {
+      if (dvodb ) {
+	psTrace("addtool.c", PS_LOG_INFO, "dvodb argument found (%s) using addtool_find_sky_id_multi_dvo.sql\n%s\n", dvodb,stage);
+        // find the cam_id of all the exposures that we want to queue up.
+        bare_query = pxDataGet("addtool_find_sky_id_multi_dvo.sql");
+	// user supplied dvodb
+	psStringAppend(&dvodb_string, "addRun.dvodb = '%s'", dvodb);
+    } else {
+      psTrace("addtool.c", PS_LOG_INFO, "dvodb argument not found using addtool_find_sky_id_multi.sql\n%s\n",stage);
+        // find the cam_id of all the exposures that we want to queue up.
+        bare_query = pxDataGet("addtool_find_sky_id_multi.sql");
+        // inherit dvodb from camRun, avoid matching NULL
+        psStringAppend(&dvodb_string, "(staticskyRun.dvodb IS NOT NULL AND previous_dvodb = staticskyRun.dvodb)");
+    }
+    }
 
     if (!bare_query) {
@@ -272,7 +292,8 @@
     } else {
       //This picks only the unmagicked/uncensored ones
-      if (strcmp(stage,"cam") == 0) {
-        psStringAppend(&query, " AND (camRun.magicked = 0)");
-      }
+      //if (strcmp(stage,"cam") == 0) {
+	//we can now properly handle the magicked case in ippScripts, so we queue camRuns in any magic state now if -uncensored.
+        //psStringAppend(&query, " AND (camRun.magicked = 0)");
+	//}
       if (strcmp(stage,"stack") == 0) {
 	psStringAppend(&query, " AND (stackRun.magicked = 0)");
@@ -280,4 +301,20 @@
 
     }
+
+    //if we grab a group of camRun/stackRuns that have multiple camRun/stackRun pointing to the same exp_id/stack_id, and that exp_id/stack_id has never before been added to addRun, we need to group by exp_id/stack_id to ensure that only 1 of the camRun/stackRun for that exp_id is added to the addRun stage.
+
+    if (strcmp(stage,"cam") == 0) {
+        psStringAppend(&query, " GROUP BY exp_id"); 
+   }
+      if (strcmp(stage,"stack") == 0) {
+	psStringAppend(&query, " GROUP BY stack_id");
+      }
+    if (strcmp(stage,"staticsky") == 0) {
+	psStringAppend(&query, " GROUP BY stack_id");
+      }
+    if (strcmp(stage,"staticsky_multi") == 0) {
+      psStringAppend(&query, " GROUP BY sky_id");  //some reason it needs this
+      }
+
 
     psTrace("addtool.c", PS_LOG_INFO,"query: \n\n%s\n\n",query);
@@ -391,4 +428,43 @@
     }
     }
+    if (strcmp(stage,"staticsky_multi") == 0) {
+    for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *md = output->data[i];
+	bool status = false;
+	psS32 num_inputs = psMetadataLookupS32(&status, md, "num_inputs");
+	if (!status) {
+	  psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item num_inputs");
+	  return false;
+	}
+	if (num_inputs < 0) {
+	  psError(PS_ERR_UNKNOWN, true, "invalid value for num_inputs");
+	  return false;
+	}
+
+	staticskyRunRow *row = staticskyRunObjectFromMetadata(md);
+	
+        if (!row) {
+            psError(PS_ERR_UNKNOWN, false, "failed to convert metadata into camRun");
+            psFree(output);
+            return false;
+        }
+
+        if (!dvodb) {  //there's no staticsky.dvodb
+            psError(PS_ERR_UNKNOWN, false, "cannot queue addstar run without a defined dvodb: label: %s, sky_id %" PRId64, row->label, row->sky_id);
+            psFree(output);
+            return false;
+        }
+        if (!workdir && !row->workdir) {
+            psError(PS_ERR_UNKNOWN, false, "cannot queue addstar run without a defined workdir: label: %s, sky_id %" PRId64, row->label, row->sky_id);
+            psFree(output);
+            return false;
+        }
+
+        psFree(row);
+    }
+    }
+
+
+
 
     // start a transaction so we don't end up with an exp without any associted
@@ -424,4 +500,5 @@
 			       stage,
                                stage_id,
+			       0,
                                workdir     ? workdir   : row->workdir,
                                reduction   ? reduction : row->reduction,
@@ -465,4 +542,5 @@
 			       stage,
                                stage_id,
+			       0,
                                workdir     ? workdir   : row->workdir,
                                reduction   ? reduction : row->reduction,
@@ -506,4 +584,5 @@
 			       stage,
                                stage_id,
+			       0,
                                workdir     ? workdir   : row->workdir,
                                reduction   ? reduction : row->reduction,
@@ -530,4 +609,57 @@
       }
 
+    if (strcmp(stage,"staticsky_multi") == 0) {
+    for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *md = output->data[i];
+	psS64 stage_id =0; 
+	bool status = false;
+	psS32 num_inputs = psMetadataLookupS32(&status, md, "num_inputs");
+	if (!status) {
+	  psError(PS_ERR_UNKNOWN, true, "failed to lookup value for item num_inputs");
+	  return false;
+	}
+	staticskyRunRow *row = staticskyRunObjectFromMetadata(md);
+	stage_id = row->sky_id;
+	
+        if (!row) {
+            psError(PS_ERR_UNKNOWN, false, "failed to convert metadata into camRun");
+            psFree(output);
+            return false;
+        }
+
+        // queue the exp
+	for (int filter_id = 0; filter_id < num_inputs; filter_id++) {  
+        if (!pxaddQueueByCamID(config,
+			       stage,
+                               stage_id,
+			       filter_id,
+                               workdir     ? workdir   : row->workdir,
+                               reduction   ? reduction : row->reduction,
+                               label       ? label     : row->label,
+                               data_group  ? data_group : (row->data_group ? row->data_group :  (label ? label : row->label)),
+                               dvodb       ? dvodb     : NULL,
+                               note        ? note      : NULL,
+                               image_only,
+                               minidvodb,
+                               minidvodb_group,
+                               minidvodb_name
+        )) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error sfg");
+            }
+            psError(PS_ERR_UNKNOWN, false,
+                    "failed to trying to queue stage %s %" PRId64,stage, stage_id);
+            psFree(row);
+            psFree(output);
+            return false;
+        }
+	}
+        psFree(row);
+    }
+      }
+
+
+
+
     psFree(output);
 
@@ -571,4 +703,7 @@
     }
     if (strcmp(stage, "staticsky")==0) {
+    query = psStringCopy("UPDATE addRun JOIN staticskyRun on sky_id = stage_id");
+    }
+    if (strcmp(stage, "staticsky_multi")==0) {
     query = psStringCopy("UPDATE addRun JOIN staticskyRun on sky_id = stage_id");
     }
@@ -612,5 +747,8 @@
     query = pxDataGet("addtool_find_pendingexp_staticsky.sql");
     }
-
+    if (strcmp(stage, "staticsky_multi")==0) { 
+    query = pxDataGet("addtool_find_pendingexp_staticsky_multi.sql");
+    }
+    
 
     if (!query) {
@@ -634,5 +772,8 @@
       psStringAppend(&query, " GROUP BY %s", "sky_id");
     }
-
+    if (strcmp(stage, "staticsky_multi") == 0) {
+      //this group by is needed to join against all the warps (to get camera)
+      psStringAppend(&query, " GROUP BY %s", "sky_id, stage_extra1");
+    }
     // treat limit == 0 as "no limit"
     if (limit) {
@@ -684,5 +825,5 @@
     PXOPT_LOOKUP_STR(minidvodb_name, config->args, "-minidvodb_name", false, false);
     PXOPT_LOOKUP_S16(fault,         config->args, "-fault", false, false);
-
+    PXOPT_LOOKUP_S64(stage_extra1, config->args, "-stage_extra1", false, false);
     // generate restrictions
     psMetadata *where = psMetadataAlloc();
@@ -762,5 +903,19 @@
       }
     }
-
+    //if there is a stage_extra1, set it in addRun (it's not known until it is processed)
+    if (stage_extra1) {
+      psString setExtra = NULL;
+      psStringAppend (&setExtra, "UPDATE addRun set stage_extra1 = %" PRId64, stage_extra1);
+      psStringAppend (&setExtra, " where add_id = %" PRId64, row->add_id);
+
+      if (!p_psDBRunQuery(config->dbh, setExtra)) {
+        if (!psDBRollback(config->dbh)) {
+          psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+
+        return false;
+      }
+    }
 
 
@@ -824,4 +979,8 @@
     query = pxDataGet("addtool_find_processedexp_staticsky.sql");
     }
+    if (strcmp (stage,"staticsky_multi") == 0) {
+    query = pxDataGet("addtool_find_processedexp_staticsky_multi.sql");
+    }
+
    
     if (!query) {
@@ -905,5 +1064,5 @@
     PXOPT_COPY_S64(config->args, where, "-add_id",    "addRun.add_id",         "==");
     PXOPT_COPY_S64(config->args, where, "-stage_id",    "addRun.stage_id",         "==");
-    PXOPT_LOOKUP_STR(stage,       config->args, "-stage", false, false);
+    PXOPT_LOOKUP_STR(stage,       config->args, "-stage", true, false);
     pxcamGetSearchArgs (config, where);
     pxAddLabelSearchArgs (config, where, "-label",    "addRun.label",     "==");
@@ -934,4 +1093,10 @@
 	 query = pxDataGet("addtool_revertprocessedexp_staticsky.sql");
       }
+   if (strcmp(stage, "staticsky_multi") == 0) {
+	 query = pxDataGet("addtool_revertprocessedexp_staticsky_multi.sql");
+      }
+
+
+     
       if (!query) {
             // rollback
@@ -1071,7 +1236,5 @@
 
   PXOPT_LOOKUP_STR(minidvodb_group, config->args, "-set_minidvodb_group", true, false);
-  PXOPT_LOOKUP_STR(mergedvodb_path, config->args, "-set_mergedvodb_path", true, false);
-
-  //optional
+    //optional
   PXOPT_LOOKUP_STR(minidvodb_name, config->args, "-set_minidvodb_name", false, false);
   PXOPT_LOOKUP_STR(minidvodb_path, config->args, "-set_minidvodb_path", false, false);
@@ -1088,4 +1251,7 @@
   if (minidvodb_path) {
     minidvodbpath = minidvodb_path;
+  } else {
+    psError(PS_ERR_UNKNOWN, false, "require minidvodb_path");
+    return false;
   }
 
@@ -1095,6 +1261,5 @@
             minidvodb_group,
             minidvodbpath,
-            mergedvodb_path,
-            "new",
+	    "new",
             0
             )) {
@@ -1118,9 +1283,4 @@
     psStringAppend(&minidvodb_name, "%s.%" PRIu64,minidvodb_group,minidvodb_id);
   }
-
-  if (!minidvodb_path) {
-    psStringAppend(&minidvodb_path,"%s/%s",mergedvodb_path,minidvodb_name);
-  }
-
   if (minidvodb_path) {
     psStringAppend(&minidvodb_path,"/%s",minidvodb_name);
@@ -1154,5 +1314,4 @@
     PXOPT_COPY_STR(config->args, where, "-state",     "state", "==");
     PXOPT_COPY_STR(config->args, where, "-minidvodb_path", "minidvodb_path", "==");
-    PXOPT_COPY_STR(config->args, where, "-mergedvodb_path", "mergedvodb_path", "==");
     PXOPT_COPY_STR(config->args, where, "-minidvodb_group",     "minidvodb_group", "==");
 
@@ -1160,5 +1319,4 @@
     PXOPT_LOOKUP_STR(minidvodb_path,  config->args, "-set_minidvodb_path", false, false);
     PXOPT_LOOKUP_STR(state,  config->args, "-set_state", false, false);
-    PXOPT_LOOKUP_STR(mergedvodb_path,  config->args, "-set_mergedvodb_path", false, false);
     PXOPT_LOOKUP_STR(minidvodb_group,  config->args, "-set_minidvodb_group", false, false);
 
@@ -1195,12 +1353,4 @@
     }
 
-    if (mergedvodb_path) {
-      if (cnt) {
-        psStringAppend(&query, "%s", comma);
-      }
-      psStringAppend(&query, " mergedvodb_path = '%s'", mergedvodb_path);
-      cnt++;
-    }
-
     if (minidvodb_group) {
       if (cnt) {
@@ -1246,5 +1396,5 @@
   }
 
-  psString firstquery = psStringCopy("SELECT * from  minidvodbRun where state = 'new' and minidvodb_name is NOT NULL and minidvodb_group IS NOT NULL and minidvodb_path IS NOT NULL and mergedvodb_path IS NOT NULL");
+  psString firstquery = psStringCopy("SELECT * from  minidvodbRun where state = 'new' and minidvodb_name is NOT NULL and minidvodb_group IS NOT NULL and minidvodb_path IS NOT NULL");
 
   psString firstwhereClause = psDBGenerateWhereConditionSQL(where, NULL);
@@ -1294,5 +1444,5 @@
 
   //now flip new -> active
-  psString query2 = psStringCopy("UPDATE minidvodbRun SET state = 'active' WHERE state = 'new' AND minidvodb_name is NOT NULL and minidvodb_group IS NOT NULL and minidvodb_path IS NOT NULL and mergedvodb_path IS NOT NULL ");
+  psString query2 = psStringCopy("UPDATE minidvodbRun SET state = 'active' WHERE state = 'new' AND minidvodb_name is NOT NULL and minidvodb_group IS NOT NULL and minidvodb_path IS NOT NULL ");
   psStringAppend(&query2, " AND minidvodb_group = '%s' limit 1;", minidvodb_group);
   if (!p_psDBRunQuery(config->dbh, query2)) {
@@ -1471,13 +1621,9 @@
     // required
   PXOPT_LOOKUP_U64(minidvodb_id, config->args, "-minidvodb_id", true, false);
-  PXOPT_LOOKUP_STR(mergedvodb_path, config->args, "-mergedvodb_path", true, false);
   PXOPT_LOOKUP_STR(minidvodb_group, config->args, "-minidvodb_group", true, false);
 
     // optional
-  PXOPT_LOOKUP_U64(merge_order,     config->args, "-merge_order", false, false);
   PXOPT_LOOKUP_F32(dtime_relphot, config->args, "-dtime_relphot", false, false);
   PXOPT_LOOKUP_F32(dtime_resort, config->args, "-dtime_resort", false, false);
-  PXOPT_LOOKUP_F32(dtime_merge, config->args, "-dtime_merge", false, false);
-  PXOPT_LOOKUP_F32(dtime_verify, config->args, "-dtime_verify", false, false);
   PXOPT_LOOKUP_F32(dtime_script, config->args, "-dtime_script", false, false);
   
@@ -1530,12 +1676,8 @@
   minidvodbProcessedRow *row = minidvodbProcessedRowAlloc(
                pendingRow->minidvodb_id,
-               merge_order,
                dtime_resort,
                dtime_relphot,
-	       dtime_verify,
-               dtime_merge,
 	       dtime_script,
                epoch,
-               mergedvodb_path,
                fault
                );
@@ -1552,67 +1694,4 @@
   }
 
-
-
-
-  //this finds the # of merged things (for the merge order)
-  psString query3 = NULL;
-  psStringAppend(&query3, "select count(*) from minidvodbRun join minidvodbProcessed using (minidvodb_id) where state = 'merged' and minidvodb_group = '%s';", minidvodb_group);
-
-  if (!p_psDBRunQuery(config->dbh, query3)) {
-    // rollback
-    if (!psDBRollback(config->dbh)) {
-      psError(PS_ERR_UNKNOWN, false, "database error");
-    }
-    psError(PS_ERR_UNKNOWN, false, "database error");
-    psFree(query3);
-    return false;
-  }
-  psArray *output2 = p_psDBFetchResult(config->dbh);
-  if (!output2) {
-    psError(PS_ERR_UNKNOWN, false, "database error");
-    return false;
-  }
-  if (!psArrayLength(output2)) {
-    psTrace("addtool", PS_LOG_INFO, "no rows found");
-    psFree(output);
-    return true;
-  }
-  bool status;
-  psS64 m_order = psMetadataLookupS64(&status, output2->data[0], "count(*)");
-  if (!status) {
-
-    psAbort("failed to lookup value for count column");
-    return false;
-  }
-  psString final = NULL;
-  psStringAppend(&final, "%" PRIu64, m_order);
-    //return false;
-  psFree(query3);
-  psFree(output2);
-
-
-
-  //update the merge_order
-
-  psString query4 = NULL;
-  psStringAppend(&query4, "update minidvodbProcessed set merge_order = %"PRIu64,m_order);
-  psStringAppend(&query4," where minidvodb_id = %" PRIu64,  minidvodb_id);
-  //printf("%s", query4);
-  if (!p_psDBRunQuery(config->dbh, query4)) {
-    // rollback
-    if (!psDBRollback(config->dbh)) {
-      psError(PS_ERR_UNKNOWN, false, "database error");
-    }
-    psError(PS_ERR_UNKNOWN, false, "database error");
-    psFree(query4);
-    return false;
-  }
-  //
-psFree(query4);
-
-
-
-// since there is only one exp per 'new' set mindvodbRun.state = 'merged'
-
   psString query2 = NULL ;
   psStringAppend(&query2, "UPDATE minidvodbRun SET state = 'merged' WHERE minidvodb_id = %'" PRIu64, row->minidvodb_id);
@@ -1627,12 +1706,4 @@
     return false;
   }
-
-
-
-
-
-
-
-
   psFree(row);
   psFree(pendingRow);
@@ -1647,10 +1718,4 @@
     return false;
   }
-
-
-  //print the merge_order (why not!)
-  printf("%s", final);
-  psFree(final);
-
   return true;
 }
@@ -1661,6 +1726,6 @@
   psMetadata *where = psMetadataAlloc();
   PXOPT_COPY_S64(config->args, where, "-minidvodb_id", "minidvodbProcessed.minidvodb_id", "==");
-  PXOPT_COPY_STR(config->args, where, "-minidvodb_name", "minidvodbProcessed.minidvodb_name", "==");
-  PXOPT_COPY_STR(config->args, where, "-minidvodb_group", "minidvodbProcessed.minidvodb_group", "==");
+  PXOPT_COPY_STR(config->args, where, "-minidvodb_name", "minidvodbRun.minidvodb_name", "==");
+  PXOPT_COPY_STR(config->args, where, "-minidvodb_group", "minidvodbRun.minidvodb_group", "==");
   PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
   PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
@@ -1811,10 +1876,7 @@
 
   PXOPT_LOOKUP_U64(minidvodb_id,  config->args, "-minidvodb_id", true, false);
-  PXOPT_LOOKUP_U64(merge_order,  config->args, "-set_merge_order", false, false);
   PXOPT_LOOKUP_S16(fault,  config->args, "-set_fault", false, false);
   PXOPT_LOOKUP_F32(dtime_relphot,  config->args, "-set_dtime_relphot", false, false);
   PXOPT_LOOKUP_F32(dtime_resort,  config->args, "-set_dtime_resort", false, false);
-  PXOPT_LOOKUP_F32(dtime_merge,  config->args, "-set_dtime_merge", false, false);
-PXOPT_LOOKUP_F32(dtime_verify,  config->args, "-set_dtime_verify", false, false);
 PXOPT_LOOKUP_F32(dtime_script,  config->args, "-set_dtime_script", false, false);
   PXOPT_COPY_S64(config->args, where, "-minidvodb_id",     "minidvodbProcessed.minidvodb_id", "==");
@@ -1836,13 +1898,4 @@
   }
 
-  if (merge_order) {
-    if (cnt) {
-      psStringAppend(&query, "%s", comma);
-    }
-
-    psStringAppend(&query, " merge_order = %" PRId64, merge_order);
-    cnt++;
-  }
-
   if (dtime_relphot) {
     if (cnt) {
@@ -1861,19 +1914,5 @@
   }
 
-  if (dtime_merge) {
-    if (cnt) {
-      psStringAppend(&query, "%s", comma);
-    }
-    psStringAppend(&query, " dtime_merge = %f", dtime_merge);
-    cnt++;
- }
-if (dtime_verify) {
-    if (cnt) {
-      psStringAppend(&query, "%s", comma);
-    }
-    psStringAppend(&query, " dtime_verify = %f", dtime_verify);
-    cnt++;
- }
-if (dtime_script) {
+  if (dtime_script) {
     if (cnt) {
       psStringAppend(&query, "%s", comma);
Index: branches/meh_branches/ppstack_test/ippTools/src/addtoolConfig.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/addtoolConfig.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/addtoolConfig.c	(revision 33415)
@@ -115,4 +115,6 @@
     psMetadataAddF32(addprocessedexpArgs, PS_LIST_TAIL, "-dtime_addstar", 0, "define elapsed time for DVO insertion (seconds)", NAN);
     psMetadataAddS16(addprocessedexpArgs, PS_LIST_TAIL, "-fault",  0,            "set fault code", 0);
+psMetadataAddS64(addprocessedexpArgs, PS_LIST_TAIL, "-stage_extra1",  0,            "set stage_extra1", 0);
+
 
     // -processedexp
@@ -165,5 +167,4 @@
     psMetadataAddStr(addminidvodbrunArgs, PS_LIST_TAIL, "-set_minidvodb_group",        0, "define minidvodb_group (required)", NULL);
     psMetadataAddStr(addminidvodbrunArgs, PS_LIST_TAIL, "-set_minidvodb_path",        0, "define path for minidvodb", NULL);
-    psMetadataAddStr(addminidvodbrunArgs, PS_LIST_TAIL, "-set_mergedvodb_path",        0, "define path for the merged dvodb (required)", NULL);
     psMetadataAddStr(addminidvodbrunArgs, PS_LIST_TAIL, "-set_state",        0, "define state", NULL);
 
@@ -174,9 +175,7 @@
     psMetadataAddStr(updateminidvodbrunArgs, PS_LIST_TAIL, "-minidvodb_name",        0, "search by minidvodb_name (LIKE)", NULL);
     psMetadataAddStr(updateminidvodbrunArgs, PS_LIST_TAIL, "-minidvodb_path",        0, "search by path for minidvodb", NULL);
-    psMetadataAddStr(updateminidvodbrunArgs, PS_LIST_TAIL, "-mergedvodb_path",        0, "search by path for the merged dvodb", NULL);
     psMetadataAddStr(updateminidvodbrunArgs, PS_LIST_TAIL, "-state",        0, "search by state", NULL);
     psMetadataAddStr(updateminidvodbrunArgs, PS_LIST_TAIL, "-set_minidvodb_name",        0, "define minidvodb_name", NULL);
     psMetadataAddStr(updateminidvodbrunArgs, PS_LIST_TAIL, "-set_minidvodb_path",        0, "define path for minidvodb", NULL);
-    psMetadataAddStr(updateminidvodbrunArgs, PS_LIST_TAIL, "-set_mergedvodb_path",        0, "define path for minidvodb", NULL);
     psMetadataAddStr(updateminidvodbrunArgs, PS_LIST_TAIL, "-set_minidvodb_group",        0, "define path for the merged dvodb", NULL);
     psMetadataAddStr(updateminidvodbrunArgs, PS_LIST_TAIL, "-set_state",        0, "define state", NULL);
@@ -205,12 +204,8 @@
     psMetadata *addminidvodbprocessedArgs = psMetadataAlloc();
     psMetadataAddU64(addminidvodbprocessedArgs, PS_LIST_TAIL, "-minidvodb_id", 0,    "define minidvodb_id (required)", 0);
-    psMetadataAddU64(addminidvodbprocessedArgs, PS_LIST_TAIL, "-merge_order",    0,    "define merge order", 0);
     psMetadataAddF32(addminidvodbprocessedArgs, PS_LIST_TAIL, "-dtime_relphot",  0,    "define elapsed time for relphot (seconds)", NAN);
     psMetadataAddF32(addminidvodbprocessedArgs, PS_LIST_TAIL, "-dtime_resort", 0,    "define elapsed time for resort (seconds)", NAN);
-    psMetadataAddF32(addminidvodbprocessedArgs, PS_LIST_TAIL, "-dtime_verify",    0,    "define elapsed time for DVO verify (seconds)", NAN);
-    psMetadataAddF32(addminidvodbprocessedArgs, PS_LIST_TAIL, "-dtime_merge",    0,    "define elapsed time for DVO merge (seconds)", NAN);
     psMetadataAddF32(addminidvodbprocessedArgs, PS_LIST_TAIL, "-dtime_script",    0,    "define elapsed time for script (seconds)", NAN);
     psMetadataAddTime(addminidvodbprocessedArgs, PS_LIST_TAIL, "-epoch",         0,    "time merge is finished", NULL);
-    psMetadataAddStr(addminidvodbprocessedArgs, PS_LIST_TAIL, "-mergedvodb_path",0,    "path of merged dvodb", NULL);
     psMetadataAddStr(addminidvodbprocessedArgs, PS_LIST_TAIL, "-minidvodb_group",0,    "minidvodb_group", NULL);
     psMetadataAddS16(addminidvodbprocessedArgs, PS_LIST_TAIL, "-fault",          0,    "set fault code", 0);
@@ -232,9 +227,6 @@
     psMetadataAddStr(updateminidvodbprocessedArgs, PS_LIST_TAIL, "-minidvodb_name",        0, "search by minidvodb_name", NULL);
     psMetadataAddS16(updateminidvodbprocessedArgs, PS_LIST_TAIL, "-set_fault",  0,            "set fault code", 0);
-    psMetadataAddU64(updateminidvodbprocessedArgs, PS_LIST_TAIL, "-set_merge_order",    0,    "define merge order", 0);
     psMetadataAddF32(updateminidvodbprocessedArgs, PS_LIST_TAIL, "-set_dtime_relphot",  0,    "define elapsed time for relphot (seconds)", 0);
     psMetadataAddF32(updateminidvodbprocessedArgs, PS_LIST_TAIL, "-set_dtime_resort", 0,    "define elapsed time for resort (seconds)", 0);
-    psMetadataAddF32(updateminidvodbprocessedArgs, PS_LIST_TAIL, "-set_dtime_verify",    0,    "define elapsed time for DVO verify (seconds)", 0);
-    psMetadataAddF32(updateminidvodbprocessedArgs, PS_LIST_TAIL, "-set_dtime_merge",    0,    "define elapsed time for DVO merge (seconds)", 0);
     psMetadataAddF32(updateminidvodbprocessedArgs, PS_LIST_TAIL, "-set_dtime_script",    0,    "define elapsed time for script (seconds)", 0);
     psMetadata *argSets = psMetadataAlloc();
Index: branches/meh_branches/ppstack_test/ippTools/src/bgtoolConfig.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/bgtoolConfig.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/bgtoolConfig.c	(revision 33415)
@@ -113,4 +113,6 @@
     psMetadataAddTime(updatechipArgs, PS_LIST_TAIL, "-registered_end", 0, "search by registration time (<)", NULL);
     psMetadataAddBool(updatechipArgs, PS_LIST_TAIL, "-destreaked", 0, "search for runs that have been destreaked", false);
+    psMetadataAddS64(updatechipArgs, PS_LIST_TAIL, "-magicked",  0,        "define magicked status", 0); 
+    psMetadataAddS64(updatechipArgs, PS_LIST_TAIL, "-not_destreaked",  0,        "define magicked status", 0); 
     psMetadataAddBool(updatechipArgs, PS_LIST_TAIL, "-pretend", 0, "only pretend to run the query", false);
     psMetadataAddStr(updatechipArgs, PS_LIST_TAIL, "-set_state", 0, "define new state", NULL);
@@ -298,4 +300,6 @@
     psMetadataAddTime(updatewarpArgs, PS_LIST_TAIL, "-registered_end", 0, "search by registration time (<)", NULL);
     psMetadataAddBool(updatewarpArgs, PS_LIST_TAIL, "-destreaked", 0, "search for runs that have been destreaked", false);
+    psMetadataAddBool(updatewarpArgs, PS_LIST_TAIL, "-not_destreaked", 0, "search for runs that have been destreaked", false);
+    psMetadataAddS64(updatewarpArgs, PS_LIST_TAIL, "-magicked",  0,        "define magicked status", 0); 
     psMetadataAddBool(updatewarpArgs, PS_LIST_TAIL, "-pretend", 0, "only pretend to run the query", false);
     psMetadataAddStr(updatewarpArgs, PS_LIST_TAIL, "-set_state", 0, "define new state", NULL);
Index: branches/meh_branches/ppstack_test/ippTools/src/camtool.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/camtool.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/camtool.c	(revision 33415)
@@ -112,4 +112,5 @@
     PXOPT_COPY_STR(config->args, where, "-reduction", "chipRun.reduction", "==");
     PXOPT_COPY_STR(config->args, where, "-data_group", "chipRun.data_group", "==");
+    PXOPT_COPY_STR(config->args, where, "-obs_mode", "rawExp.obs_mode", "==");
 
     if (!psListLength(where->list) &&
Index: branches/meh_branches/ppstack_test/ippTools/src/camtoolConfig.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/camtoolConfig.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/camtoolConfig.c	(revision 33415)
@@ -55,4 +55,5 @@
     psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-reduction",          0, "search by chipRun reduction class", NULL);
     psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-data_group",         0, "search by chipRun data_group", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-obs_mode",           0, "search by rawExp obs_mode", NULL);
 
     psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_workdir",        0, "define workdir", NULL);
Index: branches/meh_branches/ppstack_test/ippTools/src/chiptool.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/chiptool.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/chiptool.c	(revision 33415)
@@ -43,4 +43,5 @@
 static bool revertprocessedimfileMode(pxConfig *config);
 static bool updateprocessedimfileMode(pxConfig *config);
+static bool dropprocessedimfileMode(pxConfig *config);
 static bool advanceexpMode(pxConfig *config);
 static bool blockMode(pxConfig *config);
@@ -88,4 +89,5 @@
         MODECASE(CHIPTOOL_MODE_REVERTPROCESSEDIMFILE,   revertprocessedimfileMode);
         MODECASE(CHIPTOOL_MODE_UPDATEPROCESSEDIMFILE,   updateprocessedimfileMode);
+        MODECASE(CHIPTOOL_MODE_DROPPROCESSEDIMFILE,     dropprocessedimfileMode);
         MODECASE(CHIPTOOL_MODE_ADVANCEEXP,              advanceexpMode);
         MODECASE(CHIPTOOL_MODE_BLOCK,                   blockMode);
@@ -196,4 +198,11 @@
         GET_VALUE (psS64,    S64, exp_id,        "exp_id");
         GET_VALUE (psString, Str, raw_workdir,   "workdir");
+#ifdef notyet
+        GET_VALUE (psString, Str, old_workdir,   "old_workdir");
+        GET_VALUE (psString, Str, old_data_group, "old_data_group");
+#else   
+        const char *old_workdir = NULL;
+        const char *old_data_group = NULL;
+#endif
         GET_VALUE (psString, Str, raw_label,     "label");
         GET_VALUE (psString, Str, raw_reduction, "reduction");
@@ -211,7 +220,7 @@
         if (!pxchipQueueByExpTag(config,
                                  exp_id,
-                                 workdir     ? workdir   : raw_workdir,
+                                 workdir     ? workdir   : (old_workdir ? old_workdir : raw_workdir),
                                  label       ? label     : raw_label,
-                                 data_group  ? data_group : (label ? label : raw_label),
+                                 data_group  ? data_group : (old_data_group ? old_data_group : (label ? label : raw_label)),
                                  dist_group,
                                  reduction   ? reduction : raw_reduction,
@@ -343,4 +352,5 @@
     pxAddLabelSearchArgs (config, where, "-label", "chipOld.label", "LIKE");
     pxAddLabelSearchArgs (config, where, "-data_group", "chipOld.data_group", "LIKE");
+    PXOPT_COPY_STR(config->args,  where, "-state",   "chipOld.state",   "==");
 
     // psListLength(where->list) is at least 1 because exp_type defaults to "object"
@@ -352,5 +362,5 @@
     }
 
-    PXOPT_LOOKUP_STR(workdir, config->args, "-set_workdir", true, false);
+    PXOPT_LOOKUP_STR(workdir, config->args, "-set_workdir", false, false);
     PXOPT_LOOKUP_STR(label, config->args, "-set_label", true, false);
     PXOPT_LOOKUP_STR(reduction, config->args, "-set_reduction", false, false);
@@ -466,4 +476,5 @@
     PXOPT_COPY_S64(config->args, where, "-chip_id", "chipRun.chip_id", "==");
     pxAddLabelSearchArgs (config, where, "-label", "chipRun.label", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "rawImfile.class_id", "==");
 
     psString query = pxDataGet("chiptool_pendingimfile.sql");
@@ -883,4 +894,6 @@
     }
     psFree(query);
+    int numDeleted = psDBAffectedRows(config->dbh);
+    psLogMsg("chiptool", PS_LOG_INFO, "Deleted %d chipProcessedImfiles", numDeleted);
 
     if (!fault) {
@@ -896,4 +909,7 @@
     }
     psFree(query_update);
+    int numUpdated = psDBAffectedRows(config->dbh);
+    psLogMsg("chiptool", PS_LOG_INFO, "Updated %d chipProcessedImfiles", numUpdated);
+
 
     return true;
@@ -962,5 +978,5 @@
     PXOPT_COPY_S64(config->args, where, "-chip_id", "chip_id", "==");
     PXOPT_COPY_STR(config->args, where, "-class_id", "class_id", "==");
-    PXOPT_LOOKUP_STR(state, config->args, "-set_state", false, NULL);
+    PXOPT_LOOKUP_STR(state, config->args, "-set_state", false, false);
     if (psListLength(where->list) == 0) {
         psError(PS_ERR_UNKNOWN, true, "search parameters are required");
@@ -986,4 +1002,76 @@
 
 
+    return true;
+}
+
+// "drop" previously processed components as though the original processing
+// had poor quality. Sets quality, data_state, clears fault and magicked
+// Deletes corresponding magicDSFile (if one exists)
+static bool dropprocessedimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-chip_id", "chipProcessedImfile.chip_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "chipProcessedImfile.class_id", "==");
+    PXOPT_LOOKUP_S64(chip_id, config->args, "-chip_id", true, false);
+    PXOPT_LOOKUP_S64(class_id, config->args, "-class_id", true, false);
+    PXOPT_LOOKUP_S16(quality, config->args, "-set_quality", true, false);
+
+    if (!quality) {
+        psError(PXTOOLS_ERR_CONFIG, true, "non-zero quality value is required");
+        return false;
+    }
+
+    if (psListLength(where->list) == 0) {
+        // won't get here
+        psError(PXTOOLS_ERR_CONFIG, true, "-chip_id and -class_id are required");
+        return false;
+    }
+
+    psString query = pxDataGet("chiptool_dropprocessedimfile_update.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+    psString delete_query = pxDataGet("chiptool_dropprocessedimfile_delete.sql");
+    if (!delete_query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, " WHERE %s", whereClause);
+    psFree(where);
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!p_psDBRunQueryF(config->dbh, query, quality)) {
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error on rollback");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psStringAppend(&delete_query, " AND %s", whereClause);
+    psFree(whereClause);
+    if (!p_psDBRunQuery(config->dbh, delete_query)) {
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error on rollback");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(delete_query);
+        return false;
+    }
+    psFree(delete_query);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
     return true;
 }
@@ -1418,5 +1506,5 @@
     psMetadata *where = psMetadataAlloc();
     pxAddLabelSearchArgs (config, where, "-label", "label", "==");
-
+    PXOPT_COPY_S64(config->args, where,  "-chip_id",    "chip_id", "==");
     // look for completed chipPendingExp
     // migrate them to chipProccessedExp & camPendingExp
@@ -1432,4 +1520,6 @@
         psFree(whereClause);
     }
+
+    psTrace("chiptool", 10, "%s\n", query);
     psFree(where);
 
@@ -1943,7 +2033,16 @@
         return false;
     }
-
     psFree(setHook);
     psFree(query);
+
+    // if we are updating a whole chipRun set lingering chips with poor quality to full data_state
+    if (!class_id) {
+        query = "UPDATE chipProcessedImfile SET data_state ='full', fault = 0 WHERE chip_id = %" PRId64 " AND quality != 0 AND (data_state ='cleaned' OR data_state = 'update')";
+        
+        if (!p_psDBRunQueryF(config->dbh, query, chip_id)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return false;
+        }
+    }
 
     return true;
Index: branches/meh_branches/ppstack_test/ippTools/src/chiptool.h
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/chiptool.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/chiptool.h	(revision 33415)
@@ -33,4 +33,5 @@
     CHIPTOOL_MODE_REVERTPROCESSEDIMFILE,
     CHIPTOOL_MODE_UPDATEPROCESSEDIMFILE,
+    CHIPTOOL_MODE_DROPPROCESSEDIMFILE,
     CHIPTOOL_MODE_SETIMFILETOUPDATE,
     CHIPTOOL_MODE_ADVANCEEXP,
Index: branches/meh_branches/ppstack_test/ippTools/src/chiptoolConfig.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/chiptoolConfig.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/chiptoolConfig.c	(revision 33415)
@@ -72,6 +72,7 @@
 
     psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-exp_type", PS_META_REPLACE, "search by exp_type", "object");
-
-    psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-set_workdir", 0, "define workdir (required)", NULL);
+    psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-state", 0, "search by chipRun.state", NULL);
+
+    psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-set_workdir", 0, "define workdir", NULL);
     psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-set_label", 0, "define label (required)", NULL);
     psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-set_reduction", 0, "define reduction class", NULL);
@@ -111,4 +112,5 @@
     psMetadataAddS64(pendingimfileArgs, PS_LIST_TAIL, "-chip_id",  0,            "search by chip ID", 0);
     psMetadataAddStr(pendingimfileArgs, PS_LIST_TAIL, "-label",  PS_META_DUPLICATE_OK, "search by label", 0);
+    psMetadataAddStr(pendingimfileArgs, PS_LIST_TAIL, "-class_id",  0,          "search by class_id", 0);
     pxchipSetSearchArgs(pendingimfileArgs);
     psMetadataAddU64(pendingimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
@@ -237,5 +239,12 @@
     psMetadataAddStr(updateprocessedimfileArgs, PS_LIST_TAIL, "-set_state", 0,         "set state", NULL);
     psMetadataAddS16(updateprocessedimfileArgs, PS_LIST_TAIL, "-set_quality",  0,            "set quality", 0);
-    // -processedExp
+
+    // -dropprocessedimfile
+    psMetadata *dropprocessedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(dropprocessedimfileArgs, PS_LIST_TAIL, "-chip_id",  0,            "search by chip ID (required)", 0);
+    psMetadataAddStr(dropprocessedimfileArgs,  PS_LIST_TAIL, "-class_id",           0, "search by class ID (required)", NULL);
+    psMetadataAddS16(dropprocessedimfileArgs, PS_LIST_TAIL, "-set_quality",  0,        "set quality (required)", 0);
+
+    // -listrun
     psMetadata *listrunArgs = psMetadataAlloc();
     pxchipSetSearchArgs(listrunArgs);
@@ -372,4 +381,5 @@
     PXOPT_ADD_MODE("-processedimfile",      "show processed imfiles",               CHIPTOOL_MODE_PROCESSEDIMFILE,      processedimfileArgs);
     PXOPT_ADD_MODE("-updateprocessedimfile","change procesed imfile properties",    CHIPTOOL_MODE_UPDATEPROCESSEDIMFILE,updateprocessedimfileArgs);
+    PXOPT_ADD_MODE("-dropprocessedimfile","drop previously procesed imfile",        CHIPTOOL_MODE_DROPPROCESSEDIMFILE,  dropprocessedimfileArgs);
     PXOPT_ADD_MODE("-revertprocessedimfile","undo a processed imfile",              CHIPTOOL_MODE_REVERTPROCESSEDIMFILE,revertprocessedimfileArgs);
     PXOPT_ADD_MODE("-runstate",             "list the states of chip run",          CHIPTOOL_MODE_RUNSTATE,             runstateArgs);
Index: branches/meh_branches/ppstack_test/ippTools/src/difftool.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/difftool.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/difftool.c	(revision 33415)
@@ -1634,4 +1634,6 @@
     PXOPT_COPY_F32(config->args, selectWhere, "-timediff",
                    "ABS(TIME_TO_SEC(TIMEDIFF(inputRawExp.dateobs, templateRawExp.dateobs)))", "<=");
+    PXOPT_COPY_F32(config->args, selectWhere, "-mintimediff",
+                   "ABS(TIME_TO_SEC(TIMEDIFF(inputRawExp.dateobs, templateRawExp.dateobs)))", ">=");
 
     // other where restrictions:
@@ -1867,5 +1869,5 @@
         const char *tess_id = psMetadataLookupStr(NULL, row, "tess_id");
 	psString input_data_group = psMetadataLookupStr(NULL, row, "input_data_group");
-	if (input_data_group) {
+	if (!data_group) {
 	  data_group = input_data_group;
 	}
@@ -1934,19 +1936,70 @@
             return false;
         }
+        psS64 numSkycells = psDBAffectedRows(config->dbh);
+        if (numSkycells > 0) {
+            if (!setdiffRunState(config, run->diff_id, "new", false)) {
+                psError(PS_ERR_UNKNOWN, false, "Failed to change diffRun.state for diff_id: %" PRId64,
+                        run->diff_id);
+                psFree(input);
+                psFree(diff);
+                psFree(run);
+                psFree(list);
+                psFree(insert);
+                psFree(results);
+                if (!psDBRollback(config->dbh)) {
+                    psError(PS_ERR_UNKNOWN, false, "database error");
+                }
+                return false;
+            }
+            run->state = psStringCopy("new");
+        } else {
+            // No overlap between the warps. Insert a dummy skycell which will complete this pair of warps
+            // XXX: we'd like to use diffInputSkyfileRowAlloc and diffInputSkyfileInsert but there doesn't
+            // seem to be a way to pass in NULL for skycell_id, stack1 and stack2
+            psString dummyQuery = NULL;
+            psStringAppend(&dummyQuery, 
+              "INSERT INTO diffInputSkyfile VALUES(%" PRId64 ", NULL, %s, NULL, %s, NULL, '%s', 0)",
+                    run->diff_id, 
+                    input,                      // warp1
+                    template,                   // warp2
+                    tess_id);
+            if (!p_psDBRunQuery(config->dbh, dummyQuery)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+                psFree(dummyQuery);
+                psFree(input);
+                psFree(diff);
+                psFree(run);
+                psFree(list);
+                psFree(insert);
+                psFree(results);
+                if (!psDBRollback(config->dbh)) {
+                    psError(PS_ERR_UNKNOWN, false, "database error");
+                }
+                return false;
+            }
+            psFree(dummyQuery);
+            psString finishQuery = NULL;
+            psStringAppend( &finishQuery, 
+                "UPDATE diffRun set state ='full', dist_group = NULL, note = 'dummy run - no overlap' WHERE diff_id = %" PRId64,
+                run->diff_id);
+            if (!p_psDBRunQuery(config->dbh, finishQuery)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+                psFree(finishQuery);
+                psFree(input);
+                psFree(diff);
+                psFree(run);
+                psFree(list);
+                psFree(insert);
+                psFree(results);
+                if (!psDBRollback(config->dbh)) {
+                    psError(PS_ERR_UNKNOWN, false, "database error");
+                }
+                return false;
+            }
+            psFree(finishQuery);
+            run->state = psStringCopy("full");
+        }
         psFree(input);
         psFree(diff);
-
-        if (!setdiffRunState(config, run->diff_id, "new", false)) {
-            psError(PS_ERR_UNKNOWN, false, "Failed to change diffRun.state for diff_id: %" PRId64,
-                    run->diff_id);
-            psFree(run);
-            psFree(list);
-            psFree(insert);
-            psFree(results);
-            if (!psDBRollback(config->dbh)) {
-                psError(PS_ERR_UNKNOWN, false, "database error");
-            }
-            return false;
-        }
 
         psArrayAdd(list, list->n, run);
@@ -2461,5 +2514,10 @@
         psString limitString = psDBGenerateLimitSQL(limit);
         psStringAppend(&query, " %s", limitString);
+	psStringSubstitute(&query,limitString,"@INNERLIMITS@");
         psFree(limitString);
+
+    }
+    else {
+      psStringSubstitute(&query,NULL,"@INNERLIMITS@");
     }
     //    fprintf(stderr,"%s",query);
Index: branches/meh_branches/ppstack_test/ippTools/src/difftoolConfig.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/difftoolConfig.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/difftoolConfig.c	(revision 33415)
@@ -283,4 +283,5 @@
     psMetadataAddStr(definewarpwarpArgs, PS_LIST_TAIL, "-obs_mode", 0, "search by observation mode", NULL);
     psMetadataAddF32(definewarpwarpArgs, PS_LIST_TAIL, "-timediff", 0, "limit time difference between input and template", NAN);
+    psMetadataAddF32(definewarpwarpArgs, PS_LIST_TAIL, "-mintimediff", 0, "limit time difference between input and template to be greater than this", NAN);
     psMetadataAddBool(definewarpwarpArgs, PS_LIST_TAIL, "-backwards", 0, "Template comes after input?", false);
     psMetadataAddF32(definewarpwarpArgs, PS_LIST_TAIL, "-rotdiff", 0, "limit rotator difference between input and template", NAN);
Index: branches/meh_branches/ppstack_test/ippTools/src/disttool.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/disttool.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/disttool.c	(revision 33415)
@@ -34,7 +34,9 @@
 static bool updaterunMode(pxConfig *config);
 static bool revertrunMode(pxConfig *config);
-static bool rerunMode(pxConfig *config);
+static bool startoverMode(pxConfig *config);
 static bool pendingcomponentMode(pxConfig *config);
 static bool addprocessedcomponentMode(pxConfig *config);
+static bool updateprocessedcomponentMode(pxConfig *config);
+static bool revertcomponentMode(pxConfig *config);
 static bool revertcomponentMode(pxConfig *config);
 static bool processedcomponentMode(pxConfig *config);
@@ -84,7 +86,8 @@
         MODECASE(DISTTOOL_MODE_UPDATERUN, updaterunMode);
         MODECASE(DISTTOOL_MODE_REVERTRUN, revertrunMode);
-        MODECASE(DISTTOOL_MODE_RERUN, rerunMode);
+        MODECASE(DISTTOOL_MODE_STARTOVER, startoverMode);
         MODECASE(DISTTOOL_MODE_PENDINGCOMPONENT, pendingcomponentMode);
         MODECASE(DISTTOOL_MODE_ADDPROCESSEDCOMPONENT, addprocessedcomponentMode);
+        MODECASE(DISTTOOL_MODE_UPDATEPROCESSEDCOMPONENT, updateprocessedcomponentMode);
         MODECASE(DISTTOOL_MODE_PROCESSEDCOMPONENT, processedcomponentMode);
         MODECASE(DISTTOOL_MODE_REVERTCOMPONENT, revertcomponentMode);
@@ -141,5 +144,4 @@
     PXOPT_LOOKUP_BOOL(use_alternate, config->args, "-use_alternate", false);
     PXOPT_LOOKUP_BOOL(no_magic, config->args, "-no_magic", false);
-    PXOPT_LOOKUP_BOOL(i_am_sure, config->args, "-i_am_sure", false);
     PXOPT_LOOKUP_STR(set_label, config->args, "-set_label", false, false);
     PXOPT_LOOKUP_STR(set_data_group, config->args, "-set_data_group", false, false);
@@ -165,10 +167,7 @@
     PXOPT_LOOKUP_STR(dist_group, config->args, "-dist_group", false, false);
 
-    // queuing no_magic runs can be quite dangerous. Require confirmation
-    if (no_magic && !pretend && !i_am_sure) {
-        psError(PXTOOLS_ERR_SYS, true, "-i_am_sure required to queue no_magic distribution runs");
-        return false;
-    }
-   
+    PXOPT_LOOKUP_BOOL(single, config->args, "-singlefilter", false);
+    PXOPT_LOOKUP_BOOL(rerun, config->args, "-rerun", false);
+
     if (use_alternate) {
         if (strcmp(stage, "raw")) {
@@ -361,5 +360,9 @@
     } else if (!strcmp(stage, "sky")) {
         magicRunType = "staticskyRun";
-        query = pxDataGet("disttool_definebyquery_sky.sql");
+        if (single) {
+            query = pxDataGet("disttool_definebyquery_sky_singlefilter.sql");
+        } else {
+            query = pxDataGet("disttool_definebyquery_sky.sql");
+        }
         if (!query) {
             psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
@@ -406,4 +409,7 @@
         }
     }
+    if (!rerun) {
+        psStringAppend(&query, " AND (distRun.dist_id IS NULL)");
+    }
 
     if (psListLength(where->list)) {
@@ -418,4 +424,12 @@
     if (!no_magic) {
         psStringAppend(&query, " AND (%s.magicked > 0)", magicRunType);
+    }
+
+    if (!strcmp(stage, "sky")) {
+        if (single) {
+            psStringAppend(&query, "\nGROUP BY sky_id HAVING count(stack_id) = 1");
+        } else {
+            psStringAppend(&query, "\nGROUP BY sky_id HAVING count(stack_id) > 1");
+        }
     }
 
@@ -636,5 +650,5 @@
 }
 
-static bool rerunMode(pxConfig *config)
+static bool startoverMode(pxConfig *config)
 {
     psMetadata *where = psMetadataAlloc();
@@ -646,5 +660,5 @@
 
     // require data_group or dist_id to be supplied
-    PXOPT_LOOKUP_STR(data_group, config->args, "-data_group", true, false);
+    PXOPT_LOOKUP_STR(data_group, config->args, "-data_group", false, false);
     PXOPT_LOOKUP_S64(dist_id, config->args, "-dist_id", false, false);
     if (!data_group && !dist_id) {
@@ -771,5 +785,5 @@
     PXOPT_COPY_S64(config->args, where, "-stage_id", "stage_id", "==");
     PXOPT_COPY_STR(config->args, where, "-state", "state", "==");
-    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+    pxAddLabelSearchArgs(config, where, "-label", "distRun.label", "==");
 
     PXOPT_COPY_S16(config->args, where,  "-fault", "distRun.fault", "==");
@@ -962,4 +976,60 @@
     return true;
 }
+static bool updateprocessedcomponentMode(pxConfig *config)
+{
+
+    // required values
+    PXOPT_LOOKUP_S64(dist_id, config->args, "-dist_id", true, false);
+    PXOPT_LOOKUP_STR(component, config->args, "-component", true, false);
+
+    PXOPT_LOOKUP_S16(fault, config->args, "-fault", false, false);
+    PXOPT_LOOKUP_BOOL(clearfault, config->args, "-clearfault", false);
+    PXOPT_LOOKUP_S32(bytes, config->args, "-bytes", false, false);
+    PXOPT_LOOKUP_STR(md5sum, config->args, "-md5sum", false, false);
+    PXOPT_LOOKUP_STR(outdir, config->args, "-outdir", false, false);
+    PXOPT_LOOKUP_STR(name, config->args, "-name", false, false);
+
+    bool setfault = clearfault || fault;
+
+    if (!bytes && !md5sum && !outdir && !name && !setfault && !fault) {
+        psError(PS_ERR_UNKNOWN, true, "at least one of bytes md5sum outdir name fault or setfault is required");
+        return false;
+    }
+
+    char *sep = "";
+    psString query = psStringCopy("UPDATE distComponent SET ");
+    if (setfault) {
+        psStringAppend(&query, "%s fault = %d", sep, fault ? 1 : 0);
+        sep = ", ";
+    }
+    if (bytes) {
+        psStringAppend(&query, "%s bytes = %d", sep, bytes);
+        sep = ", ";
+    }
+    if (md5sum) {
+        psStringAppend(&query, "%s md5sum = '%s'", sep, md5sum);
+        sep = ", ";
+    }
+    if (outdir) {
+        psStringAppend(&query, "%s outdir = '%s'", sep, outdir);
+        sep = ", ";
+    }
+    if (name) {
+        psStringAppend(&query, "%s name = '%s'", sep, name);
+        sep = ", ";
+    }
+
+    psStringAppend(&query, "\nWHERE dist_id = %"PRId64 " AND component = '%s'",
+        dist_id, component);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    return true;
+}
 
 static bool toadvanceMode(pxConfig *config)
@@ -1041,4 +1111,5 @@
     psMetadata *where = psMetadataAlloc();
     PXOPT_COPY_S64(config->args, where, "-dist_id", "dist_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-component", "component", "==");
 
     PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
@@ -2178,4 +2249,5 @@
     psMetadata *where = psMetadataAlloc();
     PXOPT_COPY_S64(config->args, where, "-fs_id", "fs_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-dist_id", "dist_id", "==");
 
     if (!psListLength(where->list)) {
Index: branches/meh_branches/ppstack_test/ippTools/src/disttool.h
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/disttool.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/disttool.h	(revision 33415)
@@ -28,9 +28,10 @@
     DISTTOOL_MODE_UPDATERUN,
     DISTTOOL_MODE_REVERTRUN,
-    DISTTOOL_MODE_RERUN,
+    DISTTOOL_MODE_STARTOVER,
     DISTTOOL_MODE_PENDINGCOMPONENT,
     DISTTOOL_MODE_ADDPROCESSEDCOMPONENT,
     DISTTOOL_MODE_REVERTCOMPONENT,
     DISTTOOL_MODE_PROCESSEDCOMPONENT,
+    DISTTOOL_MODE_UPDATEPROCESSEDCOMPONENT,
     DISTTOOL_MODE_TOADVANCE,
     DISTTOOL_MODE_PENDINGCLEANUP,
Index: branches/meh_branches/ppstack_test/ippTools/src/disttoolConfig.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/disttoolConfig.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/disttoolConfig.c	(revision 33415)
@@ -49,5 +49,4 @@
 
     psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-no_magic", 0, "magic is not needed", false);
-    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-i_am_sure", 0, "confirmation needed when -no_magic is supplied", false);
     psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-use_alternate", 0, "use alternate inputs", false);
     psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_label",    0, "define label for run", NULL);
@@ -64,5 +63,7 @@
     psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-label",         0, "select by run label", NULL);
     psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-dist_group",    0, "select by dist_group", NULL);
-
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-singlefilter", 0, "select single filter runs (sky stage only)", false);
+
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-rerun", 0, "queue a new distRun even if one already exists", false);
     psMetadataAddU64(definebyqueryArgs, PS_LIST_TAIL, "-limit",  0,  "limit result set to N items", 0);
     psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
@@ -87,15 +88,15 @@
     psMetadataAddS16(updaterunArgs, PS_LIST_TAIL, "-fault",      0, "define fault code", 0);
 
-    // -rerun
-    psMetadata *rerunArgs = psMetadataAlloc();
-    psMetadataAddStr(rerunArgs, PS_LIST_TAIL, "-data_group",    0, "define data_group (required)", NULL);
-    psMetadataAddS64(rerunArgs, PS_LIST_TAIL, "-dist_id", 0, "search by dist_id", 0);
-    psMetadataAddStr(rerunArgs, PS_LIST_TAIL, "-stage",    0, "search by stage", NULL);
-    psMetadataAddS64(rerunArgs, PS_LIST_TAIL, "-stage_id", 0, "search by stage_id", 0);
-    psMetadataAddStr(rerunArgs, PS_LIST_TAIL, "-label",    0, "search by label", NULL);
-    psMetadataAddStr(rerunArgs, PS_LIST_TAIL, "-set_label",    0, "define new label", NULL);
-    psMetadataAddBool(rerunArgs, PS_LIST_TAIL, "-pretend", 0, "don't queue runs just display what would be selected", false);
-    psMetadataAddU64(rerunArgs, PS_LIST_TAIL, "-limit",  0,  "limit result set to N items", 0);
-    psMetadataAddBool(rerunArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+    // -startover
+    psMetadata *startoverArgs = psMetadataAlloc();
+    psMetadataAddStr(startoverArgs, PS_LIST_TAIL, "-data_group",    0, "define data_group", NULL);
+    psMetadataAddS64(startoverArgs, PS_LIST_TAIL, "-dist_id", 0, "search by dist_id", 0);
+    psMetadataAddStr(startoverArgs, PS_LIST_TAIL, "-stage",    0, "search by stage", NULL);
+    psMetadataAddS64(startoverArgs, PS_LIST_TAIL, "-stage_id", 0, "search by stage_id", 0);
+    psMetadataAddStr(startoverArgs, PS_LIST_TAIL, "-label",    0, "search by label", NULL);
+    psMetadataAddStr(startoverArgs, PS_LIST_TAIL, "-set_label",    0, "define new label", NULL);
+    psMetadataAddBool(startoverArgs, PS_LIST_TAIL, "-pretend", 0, "don't queue runs just display what would be selected", false);
+    psMetadataAddU64(startoverArgs, PS_LIST_TAIL, "-limit",  0,  "limit result set to N items", 0);
+    psMetadataAddBool(startoverArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
 
     // -revertrun
@@ -105,5 +106,5 @@
     psMetadataAddS64(revertrunArgs, PS_LIST_TAIL, "-stage_id", 0, "define stage_id", 0);
     psMetadataAddStr(revertrunArgs, PS_LIST_TAIL, "-state",    0, "define state", NULL);
-    psMetadataAddStr(revertrunArgs, PS_LIST_TAIL, "-label",    0, "define label", NULL);
+    psMetadataAddStr(revertrunArgs, PS_LIST_TAIL, "-label",    PS_META_DUPLICATE_OK, "define label", NULL);
     psMetadataAddS16(revertrunArgs, PS_LIST_TAIL, "-fault", 0, "define fault code", 0);
     psMetadataAddBool(revertrunArgs, PS_LIST_TAIL, "-all",    0, "revert all faulted runs", NULL);
@@ -151,6 +152,18 @@
     psMetadata *processedcomponentArgs = psMetadataAlloc();
     psMetadataAddS64(processedcomponentArgs, PS_LIST_TAIL, "-dist_id", 0, "define dist_id", 0);
+    psMetadataAddStr(processedcomponentArgs, PS_LIST_TAIL, "-component", 0, "define component", NULL);
     psMetadataAddU64(processedcomponentArgs, PS_LIST_TAIL, "-limit",  0,  "limit result set to N items", 0);
     psMetadataAddBool(processedcomponentArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    // -updateprocessedcomponentArgs
+    psMetadata *updateprocessedcomponentArgs = psMetadataAlloc();
+    psMetadataAddS64(updateprocessedcomponentArgs, PS_LIST_TAIL, "-dist_id", 0, "define dist_id", 0);
+    psMetadataAddStr(updateprocessedcomponentArgs, PS_LIST_TAIL, "-component", 0, "define component (required)", NULL);
+    psMetadataAddStr(updateprocessedcomponentArgs, PS_LIST_TAIL, "-outdir", 0, "define output directory", NULL);
+    psMetadataAddStr(updateprocessedcomponentArgs, PS_LIST_TAIL, "-name", 0, "define file name", NULL);
+    psMetadataAddS32(updateprocessedcomponentArgs, PS_LIST_TAIL, "-bytes", 0, "define file size", 0);
+    psMetadataAddStr(updateprocessedcomponentArgs, PS_LIST_TAIL, "-md5sum", 0, "define stage for bundle", NULL);
+    psMetadataAddS32(updateprocessedcomponentArgs, PS_LIST_TAIL, "-fault", 0, "define fault code", 0);
+    psMetadataAddBool(updateprocessedcomponentArgs, PS_LIST_TAIL, "-clearfault", 0, "set fault to zero", false);
 
     // -toadvance
@@ -180,5 +193,5 @@
     psMetadata *updatefilesetArgs = psMetadataAlloc();
     psMetadataAddS64(updatefilesetArgs, PS_LIST_TAIL, "-fs_id", 0, "define fs_id", 0);
-//    psMetadataAddS64(updatefilesetArgs, PS_LIST_TAIL, "-dist_id", 0, "define dist_id", 0);
+    psMetadataAddS64(updatefilesetArgs, PS_LIST_TAIL, "-dist_id", 0, "define dist_id", 0);
     psMetadataAddStr(updatefilesetArgs, PS_LIST_TAIL, "-set_state",0, "new value for state", NULL);
     psMetadataAddS32(updatefilesetArgs, PS_LIST_TAIL, "-fault",   0, "define fault code", 0);
@@ -343,8 +356,9 @@
     PXOPT_ADD_MODE("-definebyquery",    "define distribution run", DISTTOOL_MODE_DEFINEBYQUERY, definebyqueryArgs);
     PXOPT_ADD_MODE("-updaterun",    "update distRun", DISTTOOL_MODE_UPDATERUN, updaterunArgs);
-    PXOPT_ADD_MODE("-rerun",        "reprocess a cleaned distribution run", DISTTOOL_MODE_RERUN, rerunArgs);
+    PXOPT_ADD_MODE("-startover",        "reprocess a cleaned distribution run", DISTTOOL_MODE_STARTOVER, startoverArgs);
     PXOPT_ADD_MODE("-revertrun",    "revert distRun.faults", DISTTOOL_MODE_REVERTRUN, revertrunArgs);
     PXOPT_ADD_MODE("-pendingcomponent",   "", DISTTOOL_MODE_PENDINGCOMPONENT,    pendingcomponentArgs);
     PXOPT_ADD_MODE("-addprocessedcomponent", "", DISTTOOL_MODE_ADDPROCESSEDCOMPONENT, addprocessedcomponentArgs);
+    PXOPT_ADD_MODE("-updateprocessedcomponent", "", DISTTOOL_MODE_UPDATEPROCESSEDCOMPONENT, updateprocessedcomponentArgs);
     PXOPT_ADD_MODE("-revertcomponent",    "revert faulted components", DISTTOOL_MODE_REVERTCOMPONENT, revertcomponentArgs);
     PXOPT_ADD_MODE("-processedcomponent", "", DISTTOOL_MODE_PROCESSEDCOMPONENT, processedcomponentArgs);
Index: branches/meh_branches/ppstack_test/ippTools/src/faketool.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/faketool.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/faketool.c	(revision 33415)
@@ -335,4 +335,5 @@
     PXOPT_COPY_F32(config->args, where, "-sun_angle_max", "sun_angle", "<");
     PXOPT_COPY_STR(config->args, where, "-label", "fakeRun.label", "==");
+    PXOPT_COPY_STR(config->args, where, "-state", "fakeRun.state", "==");
     PXOPT_COPY_STR(config->args, where, "-data_group", "fakeRun.data_group", "==");
     PXOPT_COPY_STR(config->args, where, "-dist_group", "fakeRun.dist_group", "==");
@@ -625,9 +626,9 @@
     psMetadata *where = psMetadataAlloc();
     pxAddLabelSearchArgs (config, where, "-label", "fakeRun.label", "==");
-    PXOPT_COPY_S64(config->args, where, "-fake_id", "fake_id", "==");
-    PXOPT_COPY_S64(config->args, where, "-exp_id", "exp_id", "==");
-    PXOPT_COPY_STR(config->args, where, "-exp_name", "exp_name", "==");
-    PXOPT_COPY_STR(config->args, where, "-inst", "camera", "==");
-    PXOPT_COPY_STR(config->args, where, "-telescope", "telescope", "==");
+    PXOPT_COPY_S64(config->args, where, "-fake_id", "fakeRun.fake_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id", "rawExp.exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-exp_name", "rawExp.exp_name", "==");
+    PXOPT_COPY_STR(config->args, where, "-inst", "rawExp..camera", "==");
+    PXOPT_COPY_STR(config->args, where, "-telescope", "rawExp.telescope", "==");
     PXOPT_COPY_TIME(config->args, where, "-dateobs_begin", "dateobs", ">=");
     PXOPT_COPY_TIME(config->args, where, "-dateobs_end", "dateobs", "<=");
Index: branches/meh_branches/ppstack_test/ippTools/src/flatcorr.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/flatcorr.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/flatcorr.c	(revision 33415)
@@ -666,4 +666,5 @@
 		"cam",
                 row->cam_id,
+		0,
                 row->workdir,
                 row->reduction,
Index: branches/meh_branches/ppstack_test/ippTools/src/laptool.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/laptool.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/laptool.c	(revision 33415)
@@ -30,4 +30,6 @@
 static bool updateexpMode(pxConfig *config);
 
+static bool diffcheckMode(pxConfig *config);
+
 static bool inactiveexpMode(pxConfig *config);
 
@@ -62,4 +64,6 @@
     MODECASE(LAPTOOL_MODE_UPDATEEXP,     updateexpMode);
 
+    MODECASE(LAPTOOL_MODE_DIFFCHECK,     diffcheckMode);
+    
     MODECASE(LAPTOOL_MODE_INACTIVEEXP,   inactiveexpMode);
   default:
@@ -276,4 +280,7 @@
   PXOPT_COPY_STR(config->args, where, "-filter", "rawExp.filter", "==");
 
+  psMetadata *chipWhere = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, chipWhere, "-seq_id", "lapRun.seq_id", "==");
+  
   // This seems unnecessarily clunky.
   if (!all_obsmode) {
@@ -296,8 +303,13 @@
   }
 
+  psString chipWhereClause = psDBGenerateWhereConditionSQL(chipWhere,NULL);
   if (whereClause) {
     psStringSubstitute(&query,whereClause,"@WHERE@");
   }
+  if (chipWhereClause) {
+    psStringSubstitute(&query,chipWhereClause,"@CHIPWHERE@");
+  }
   psFree(where);
+  psFree(chipWhere);
 
   // Fetch exposures
@@ -378,4 +390,5 @@
 
   PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+  PXOPT_LOOKUP_U64(limit,   config->args, "-limit",  false, false);
   
   psMetadata *where = psMetadataAlloc();
@@ -399,4 +412,9 @@
     psStringAppend(&query, " WHERE %s", whereClause);
     psFree(whereClause);
+  }
+  if (limit) {
+    psString limitString = psDBGenerateLimitSQL(limit);
+    psStringAppend(&query, "\n %s", limitString);
+    psFree(limitString);
   }
   
@@ -755,6 +773,68 @@
 }
 
-
-    
+static bool diffcheckMode(pxConfig *config)
+{
+  PS_ASSERT_PTR_NON_NULL(config, false);
+  PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+  
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-lap_id", "lapRun.lap_id", "==");
+  PXOPT_COPY_S64(config->args, where, "-exp_id",  "exp_id", "==");
+  PXOPT_COPY_S64(config->args, where, "-chip_id", "chip_id", "==");
+  PXOPT_COPY_S64(config->args, where, "-warp_id", "warp_id", "==");
+  PXOPT_COPY_S64(config->args, where, "-seq_id",  "seq_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-skycell_id", "skycell_id", "==");
+
+  psString query = pxDataGet("laptool_WSdiff_check.sql");
+  if (!query) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retrieve SQL statement");
+    return(false);
+  }
+  
+  if (psListLength(where->list)) {
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringPrepend(&whereClause, "\n AND ");
+    psStringSubstitute(&query,whereClause,"@WHERE@");
+    psFree(whereClause);
+  }
+
+  if (!p_psDBRunQuery(config->dbh, query)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(query);
+    return false;
+  }
+  psFree(query);
+
+  psArray *output = p_psDBFetchResult(config->dbh);
+  if (!output) {
+    psErrorCode err = psErrorCodeLast();
+    switch (err) {
+    case PS_ERR_DB_CLIENT:
+      psError(PXTOOLS_ERR_SYS, false, "database error");
+    case PS_ERR_DB_SERVER:
+      psError(PXTOOLS_ERR_PROG, false, "database error");
+    default:
+      psError(PXTOOLS_ERR_PROG, false, "unknown error %d",err);
+    }
+
+    return false;
+  }
+  if (!psArrayLength(output)) {
+    psTrace("laptool", PS_LOG_INFO, "no rows found");
+    psFree(output);
+    return true;
+  }
+
+  if (psArrayLength(output)) {
+    if (!ippdbPrintMetadatas(stdout, output, "lapRunWarpStackSkycells", !simple)) {
+      psError(PS_ERR_UNKNOWN, false, "failed to print array");
+      psFree(output);
+      return false;
+    }
+  }
+
+  psFree(output);
+  return(true);
+}    
 
 static bool inactiveexpMode(pxConfig *config)
Index: branches/meh_branches/ppstack_test/ippTools/src/laptool.h
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/laptool.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/laptool.h	(revision 33415)
@@ -19,4 +19,5 @@
   LAPTOOL_MODE_STACKS,
   LAPTOOL_MODE_UPDATEEXP,
+  LAPTOOL_MODE_DIFFCHECK,
   LAPTOOL_MODE_INACTIVEEXP
 } laptoolMode;
Index: branches/meh_branches/ppstack_test/ippTools/src/laptoolConfig.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/laptoolConfig.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/laptoolConfig.c	(revision 33415)
@@ -74,4 +74,5 @@
   ADD_OPT(Str, pendingrunArgs, "-fault",                      "search by LAP run fault", NULL);
   ADD_OPT(Bool,pendingrunArgs, "-simple",                     "use the simple output format", false);
+  ADD_OPT(U64, pendingrunArgs, "-limit",                      "limit result set to N items", 0);
 
   // -listrun
@@ -86,4 +87,5 @@
   ADD_OPT(Str, listrunArgs, "-fault",                      "search by LAP run fault", NULL);
   ADD_OPT(Bool,listrunArgs, "-simple",                     "use the simple output format", false);
+  ADD_OPT(U64, listrunArgs, "-limit",                      "limit result set to N items", 0);
 
   // -updaterun
@@ -132,4 +134,14 @@
   ADD_OPT(Bool,updateexpArgs, "-inactive",                    "set this exposure to active", 0);
 
+  // -diffcheck
+  psMetadata *diffcheckArgs = psMetadataAlloc();
+  ADD_OPT(S64, diffcheckArgs, "-lap_id",                      "search by lap run ID", 0);
+  ADD_OPT(S64, diffcheckArgs, "-exp_id",                      "search by exposure ID", 0);
+  ADD_OPT(S64, diffcheckArgs, "-chip_id",                     "search by chip ID", 0);
+  ADD_OPT(S64, diffcheckArgs, "-warp_id",                     "search by warp ID", 0);
+  ADD_OPT(S64, diffcheckArgs, "-seq_id",                      "search by lap sequence ID", 0);
+  ADD_OPT(Str, diffcheckArgs, "-skycell_id",                  "search by skycell ID", 0);
+  ADD_OPT(Bool, diffcheckArgs, "-simple",                     "use simple output format", 0);
+  
   // -inactiveexp
   psMetadata *inactiveexpArgs = psMetadataAlloc();
@@ -152,4 +164,5 @@
   PXOPT_ADD_MODE("-stacks",                  "", LAPTOOL_MODE_STACKS,           stacksArgs);
   PXOPT_ADD_MODE("-updateexp",               "", LAPTOOL_MODE_UPDATEEXP,        updateexpArgs);
+  PXOPT_ADD_MODE("-diffcheck",               "", LAPTOOL_MODE_DIFFCHECK,        diffcheckArgs);
   PXOPT_ADD_MODE("-inactiveexp",             "", LAPTOOL_MODE_INACTIVEEXP,      inactiveexpArgs);
   
Index: branches/meh_branches/ppstack_test/ippTools/src/magicdstool.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/magicdstool.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/magicdstool.c	(revision 33415)
@@ -53,4 +53,5 @@
 
 static bool setmagicDSRunState(pxConfig *config, psS64 magic_id, psString extraSetString, psMetadata *where, const char *state);
+static bool validDSRunState(const char *state);
 
 # define MODECASE(caseName, func) \
@@ -350,9 +351,9 @@
     PXOPT_LOOKUP_STR(stage, config->args, "-stage", true, false);
     PXOPT_LOOKUP_STR(workdir, config->args, "-workdir", true, false);
+    PXOPT_LOOKUP_STR(set_label, config->args, "-set_label", true, false);
 
     // Optional
     PXOPT_LOOKUP_STR(recoveryroot, config->args, "-recoveryroot", false, false);
     PXOPT_LOOKUP_BOOL(noreplace, config->args, "-noreplace", false);
-    PXOPT_LOOKUP_STR(set_label, config->args, "-set_label", false, false);
     PXOPT_LOOKUP_STR(set_data_group, config->args, "-set_data_group", false, false);
     PXOPT_LOOKUP_STR(note, config->args, "-set_note", false, false);
@@ -374,5 +375,9 @@
     pxAddLabelSearchArgs (config, where, "-magic_label", "magicRun.label", "=="); // define magic label
     psString labelName = NULL;                                                    // Name of label
-    psStringAppend(&labelName, "%sRun.label", stage);
+    if (strcmp(stage, "camera")) {
+        psStringAppend(&labelName, "%sRun.label", stage);
+    } else {
+        psStringAppend(&labelName, "camRun.label");
+    }
     pxAddLabelSearchArgs (config, where, "-stage_label", labelName, "=="); // define stageRun label
     psFree(labelName);
@@ -392,4 +397,6 @@
         break;
       case IPP_STAGE_CAMERA:
+        query = pxDataGet("magicdstool_definecopy_camera.sql");
+        break;
       case IPP_STAGE_DIFF:
       case IPP_STAGE_FAKE:
@@ -1206,6 +1213,6 @@
 
     psMetadata *where = psMetadataAlloc();
-    // new state (required
-    PXOPT_LOOKUP_STR(new_state, config->args, "-set_state", true, false);
+    // new state
+    PXOPT_LOOKUP_STR(new_state, config->args, "-set_state", false, false);
     // old state (required)
     PXOPT_LOOKUP_STR(state, config->args, "-state", true, false);
@@ -1231,4 +1238,6 @@
         if (!strcmp(state, "failed_revert")) {
             new_state = "new";
+        } else if (!strcmp(state, "failed_revert_ud")) {
+            new_state = "update";
         } else if (!strcmp(state, "failed_cleanup")) {
             new_state = "goto_cleaned";
@@ -1237,7 +1246,12 @@
             return false;
         }
+    } else {
+        if (!validDSRunState(new_state)) {
+            psError(PS_ERR_UNKNOWN, true, "unexpected value for new state: %s", new_state);
+            return false;
+        }
     }
     if (!p_psDBRunQueryF(config->dbh, query, new_state)) {
-        psError(PS_ERR_UNKNOWN, false, "failed to revert");
+        psError(PS_ERR_UNKNOWN, false, "failed to clear state faults");
         psFree(query);
         return false;
@@ -1397,4 +1411,5 @@
           (strcmp(state, "wait") == 0) ||
           (strcmp(state, "failed_revert") == 0) ||
+          (strcmp(state, "failed_revert_ud") == 0) ||
           (strcmp(state, "error_cleaned") == 0) ||
           (strcmp(state, "restored") == 0) ||
Index: branches/meh_branches/ppstack_test/ippTools/src/magicdstoolConfig.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/magicdstoolConfig.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/magicdstoolConfig.c	(revision 33415)
@@ -79,5 +79,5 @@
     psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-recoveryroot", 0, "define recovery directory", NULL);
     psMetadataAddBool(definecopyArgs, PS_LIST_TAIL, "-noreplace", 0, "do not replace input files with the destreaked versions", false);
-    psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-set_label",    0, "define label", NULL);
+    psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-set_label",    0, "define label (required)", NULL);
     psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-set_data_group", 0, "define data_group", NULL);
     psMetadataAddStr(definecopyArgs, PS_LIST_TAIL, "-set_note", 0, "define note", NULL);
@@ -163,5 +163,5 @@
     // -clearstatefaults
     psMetadata *clearstatefaultsArgs = psMetadataAlloc();
-    psMetadataAddStr(clearstatefaultsArgs, PS_LIST_TAIL, "-set_state", 0, "new value for state (required)", NULL);
+    psMetadataAddStr(clearstatefaultsArgs, PS_LIST_TAIL, "-set_state", 0, "new value for state", NULL);
     psMetadataAddStr(clearstatefaultsArgs, PS_LIST_TAIL, "-state", 0, "search by state (required)", NULL);
     psMetadataAddS64(clearstatefaultsArgs, PS_LIST_TAIL, "-magic_ds_id", 0, "search by magictool de-streak ID", 0);
Index: branches/meh_branches/ppstack_test/ippTools/src/magictool.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/magictool.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/magictool.c	(revision 33415)
@@ -131,5 +131,5 @@
     PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
     PXOPT_LOOKUP_BOOL(pretend, config->args, "-pretend", false);
-
+    PXOPT_LOOKUP_BOOL(multidiff, config->args, "-multidiff", false);
     psMetadata *diffWhere = psMetadataAlloc(); // WHERE conditions for diffRuns
     PXOPT_COPY_STR(config->args, diffWhere, "-diff_label", "diffRun.label", "==");
@@ -142,5 +142,11 @@
     // Get list of exposures ready to magic
     {
-        psString query = pxDataGet("magictool_definebyquery_select.sql");
+        psString query;
+        if (multidiff) {
+	  query = pxDataGet("magictool_definebyquery_select_multidiff.sql");
+        }
+        else {
+	  query = pxDataGet("magictool_definebyquery_select.sql");
+        }
         if (!query) {
             psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
Index: branches/meh_branches/ppstack_test/ippTools/src/magictoolConfig.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/magictoolConfig.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/magictoolConfig.c	(revision 33415)
@@ -61,4 +61,5 @@
     psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-available", 0, "process what's immediately available?", false);
     psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-rerun", 0, "generate new run even if existing?", false);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-multidiff", 0, "allow multiple diffs to be magicked with the same label", false);
     psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
     psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-pretend", 0, "list results but do not queue", false);
Index: branches/meh_branches/ppstack_test/ippTools/src/mergetool.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/mergetool.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/src/mergetool.c	(revision 33415)
@@ -0,0 +1,1227 @@
+/*
+ * addtool.c
+ *
+ * Copyright (C) 2006  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#include "pxtools.h"
+#include "pxmerge.h"
+#include "mergetool.h"
+
+static bool definebyqueryMode(pxConfig *config);
+static bool updaterunMode(pxConfig *config);
+static bool pendingmergeMode(pxConfig *config);
+static bool addmergedMode(pxConfig *config);
+static bool listmergedMode(pxConfig *config);
+static bool revertmergedMode(pxConfig *config);
+static bool updatemergedMode(pxConfig *config);
+static bool definebyquerymergecopyMode(pxConfig *config);
+static bool listmergedvodbcopyMode(pxConfig *config);
+static bool revertmergedvodbcopyMode(pxConfig *config);
+static bool updatemergedvodbcopyMode(pxConfig *config);
+
+
+
+# define MODECASE(caseName, func) \
+    case caseName: \
+    if (!func(config)) { \
+        goto FAIL; \
+    } \
+    break;
+
+int main(int argc, char **argv)
+{
+    psLibInit(NULL);
+
+    pxConfig *config = mergetoolConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        MODECASE(MERGETOOL_MODE_DEFINEBYQUERY,        definebyqueryMode);
+	MODECASE(MERGETOOL_MODE_UPDATERUN, updaterunMode);
+        MODECASE(MERGETOOL_MODE_PENDINGMERGE, pendingmergeMode);
+        MODECASE(MERGETOOL_MODE_ADDMERGED, addmergedMode);
+        MODECASE(MERGETOOL_MODE_LISTMERGED, listmergedMode);
+        MODECASE(MERGETOOL_MODE_REVERTMERGED, revertmergedMode);
+        MODECASE(MERGETOOL_MODE_UPDATEMERGED, updatemergedMode);
+	MODECASE(MERGETOOL_MODE_DEFINEBYQUERYMERGECOPY,        definebyquerymergecopyMode);
+        MODECASE(MERGETOOL_MODE_LISTMERGEDVODBCOPY, listmergedvodbcopyMode);
+        MODECASE(MERGETOOL_MODE_REVERTMERGEDVODBCOPY, revertmergedvodbcopyMode);
+        MODECASE(MERGETOOL_MODE_UPDATEMERGEDVODBCOPY, updatemergedvodbcopyMode);
+
+
+
+        default:
+            psAbort("invalid option (this should not happen)");
+    }
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(EXIT_SUCCESS);
+
+FAIL:
+    psErrorStackPrint(stderr, "\n");
+    int exit_status = pxerrorGetExitStatus();
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exit_status);
+}
+
+
+static bool definebyqueryMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-minidvodb_id", "minidvodbRun.minidvodb_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-minidvodb_group", "minidvodbRun.minidvodb_group", "==");
+    //   PXOPT_COPY_STR(config->args, where, "-mergedvodb", "mergedvodbRun.mergedvodb", "==");
+
+
+
+    if (!psListLength(where->list)) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+    PXOPT_LOOKUP_STR(state, config->args, "-set_state", false, false);
+    PXOPT_LOOKUP_STR(mergedvodb_path, config->args, "-set_mergedvodb_path", false, false);
+    PXOPT_LOOKUP_STR(mergedvodb, config->args, "-mergedvodb", true, false);
+
+    PXOPT_LOOKUP_BOOL(pretend,    config->args, "-pretend", false);
+    PXOPT_LOOKUP_BOOL(simple,     config->args, "-simple", false);
+
+    //find the items to queue (uniq minidvodb_id)
+ psString dvodb_string = NULL;
+    psString bare_query = NULL;
+
+bare_query = pxDataGet("mergetool_find_minidvodb_id.sql");
+ psStringAppend(&dvodb_string, "mergedvodbRun.mergedvodb = '%s'", mergedvodb);
+
+if (!bare_query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retrieve SQL statement");
+        psFree(where);
+        return false;
+    }
+    // Take the bare query and add the dvodb selector
+    psString query = NULL;
+    psStringAppend(&query, bare_query, dvodb_string);
+    psFree(dvodb_string);
+    psFree(bare_query);
+
+       // use psDBGenerateWhereConditionSQL because the SQL ends in a WHERE
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    } else {
+        psError(PS_ERR_UNKNOWN, true, "search parameters are required");
+        return false;
+    }
+    psFree(where);
+
+
+    psStringAppend(&query, " GROUP BY minidvodb_id");
+
+
+    psTrace("mergetool.c", PS_LOG_INFO,"query: \n\n%s\n\n",query);
+
+if (!p_psDBRunQuery(config->dbh, query)) {
+      psError(PS_ERR_UNKNOWN, false, "database error, \n%s\n", query);
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+      psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("addtool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (pretend) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "addRun", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+        psFree(output);
+        return true;
+    }
+
+for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *md = output->data[i];
+
+        minidvodbRunRow *row = minidvodbRunObjectFromMetadata(md);
+        
+        if (!row) {
+            psError(PS_ERR_UNKNOWN, false, "failed to convert metadata into minidvodbRun");
+            psFree(output);
+            return false;
+        }
+
+        if (!mergedvodb) {
+            psError(PS_ERR_UNKNOWN, false, "cannot queue merge run without a defined mergedvodb:  minidvodb_id %" PRId64,row->minidvodb_id);
+            psFree(output);
+            return false;
+        }
+        if (!mergedvodb_path) {
+            psError(PS_ERR_UNKNOWN, false, "cannot queue merge run without a defined mergedvodb_path: minidvodb_id %" PRId64,row->minidvodb_id);
+            psFree(output);
+            return false;
+        }
+
+        psFree(row);
+    
+}
+
+    // start a transaction so we don't end up with an exp without any associted
+    // imfiles
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(output);
+        return false;
+    }
+for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *md = output->data[i];
+        psS64 minidvodb_id =0; 
+        
+                  minidvodbRunRow *row = minidvodbRunObjectFromMetadata(md);
+          minidvodb_id = row->minidvodb_id;
+        
+        if (!row) {
+            psError(PS_ERR_UNKNOWN, false, "failed to convert metadata into minidvodbRun");
+            psFree(output);
+            return false;
+        }
+
+        // queue the exp
+        if (!pxmergeQueueByMinidvodbID(config,
+				       minidvodb_id,
+				       mergedvodb,
+				       mergedvodb_path
+                              
+        )) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error sfg");
+            }
+            psError(PS_ERR_UNKNOWN, false,
+                    "failed to trying to queue mergedvodb %" PRId64, minidvodb_id);
+            psFree(row);
+            psFree(output);
+            return false;
+        }
+        psFree(row);
+ 
+      }
+
+psFree(output);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+
+
+
+
+    return true;
+}
+
+static bool updaterunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-merge_id",    "mergedvodbRun.merge_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-mergedvodb", "mergedvodbRun.mergedvodb", "==");
+    
+    if (!psListLength(where->list)) {
+	  psFree(where);
+	  psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+	  return false;
+	}
+	
+	PXOPT_LOOKUP_STR(state, config->args, "-set_state", false, false);
+	PXOPT_LOOKUP_STR(mergedvodb_path, config->args, "-set_mergedvodb_path", false, false);
+	psString query = NULL;
+
+	query = psStringCopy("UPDATE mergedvodbRun SET");
+	
+	int cnt = 0;
+	psString comma = ",";
+	if (state) {
+	  psStringAppend(&query, " state = '%s'", state);
+	  cnt++;
+	}
+
+	if (mergedvodb_path) {
+	  if (cnt) {
+	    psStringAppend(&query, "%s", comma);
+	  }
+	  psStringAppend(&query, " mergedvodb_path = '%s'", mergedvodb_path);
+	  cnt++;
+	}
+
+
+	psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+	psStringAppend(&query, " WHERE %s", whereClause);
+
+	if (!p_psDBRunQuery(config->dbh, query)) {
+	  psError(PS_ERR_UNKNOWN, false, "database error");
+	  psFree(query);
+	  return false;
+	}
+
+	psFree(query);
+	psFree(where);
+	
+  
+	return true;
+}
+
+static bool pendingmergeMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-minidvodb_id",    "mergedvodbRun.minidvodb_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-merge_id",    "mergedvodbRun.merge_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-mergedvodb",    "mergedvodbRun.mergedvodb", "==");
+
+
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+   
+    psString query = NULL;
+    query = pxDataGet("mergetool_find_pending_merge.sql");
+    if (!query) {
+      psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+      return false;
+    }
+
+    // use psDBGenerateWhereSQL because the SQL yields an intermediate table
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+       // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+        psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("addtool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negate simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "mergedvodbPendingMerge", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+    
+    return true;
+}
+
+static bool addmergedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+
+ PXOPT_LOOKUP_U64(merge_id, config->args, "-merge_id", true, false);
+ PXOPT_LOOKUP_STR(mergedvodb, config->args, "-mergedvodb", true, false);
+
+   // optional
+
+//PXOPT_LOOKUP_U64(merge_order,     config->args, "-merge_order", false, false);
+ PXOPT_LOOKUP_F32(dtime_merge, config->args, "-dtime_merge", false, false);
+ PXOPT_LOOKUP_F32(dtime_verify, config->args, "-dtime_verify", false, false);
+ PXOPT_LOOKUP_F32(dtime_script, config->args, "-dtime_script", false, false);
+  
+ PXOPT_LOOKUP_TIME(epoch, config->args, "-epoch", false, false);
+ PXOPT_LOOKUP_S16(fault,         config->args, "-fault", false, false);
+
+ PXOPT_COPY_S64(config->args, where, "-merge_id",   "mergedvodbRun.merge_id",   "==");
+ PXOPT_COPY_STR(config->args, where, "-mergedvodb",   "mergedvodbRun.mergedvodb",   "==");
+
+
+
+  if (!psDBTransaction(config->dbh)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    return false;
+  }
+ psString query = pxDataGet("mergetool_find_pendingmergeprocess.sql");
+  if (!query) {
+    psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+    return false;
+  }
+  // use psDBGenerateWhereSQL because the SQL yields an intermediate table
+  if (psListLength(where->list)) {
+    psString whereClaus = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, " AND %s", whereClaus);
+    psFree(whereClaus);
+  }
+  psFree(where);
+  psTrace("mergetool.c", PS_LOG_INFO, "query is:\n%s", query);
+  
+  if (!p_psDBRunQuery(config->dbh, query)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(query);
+    return false;
+  }
+  psFree(query);
+
+psArray *output = p_psDBFetchResult(config->dbh);
+  if (!output) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    return false;
+  }
+  if (!psArrayLength(output)) {
+    psTrace("mergetool", PS_LOG_INFO, "no rows found");
+    psFree(output);
+    return true;
+  }
+
+  mergedvodbRunRow *pendingRow = mergedvodbRunObjectFromMetadata(output->data[0]);
+  psFree(output);
+  mergedvodbProcessedRow *row = mergedvodbProcessedRowAlloc(
+               pendingRow->merge_id,
+               0,
+	       dtime_verify ? dtime_verify : 0,
+               dtime_merge ? dtime_merge : 0,
+               dtime_script ? dtime_script : 0,
+               epoch,
+	       fault
+               );
+
+  if (!mergedvodbProcessedInsertObject(config->dbh, row)) {
+    // rollback
+    if (!psDBRollback(config->dbh)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+    }
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(row);
+    psFree(pendingRow);
+    return false;
+  }
+
+//this finds the # of merged things (for the merge order)
+  psString query3 = NULL;
+  psStringAppend(&query3, "select count(*) from mergedvodbRun join mergedvodbProcessed using (merge_id) where state = 'full' and mergedvodb = '%s';", mergedvodb);
+
+  if (!p_psDBRunQuery(config->dbh, query3)) {
+    // rollback
+    if (!psDBRollback(config->dbh)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+    }
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(query3);
+    return false;
+  }
+  psArray *output2 = p_psDBFetchResult(config->dbh);
+  if (!output2) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    return false;
+  }
+  if (!psArrayLength(output2)) {
+    psTrace("mergetool", PS_LOG_INFO, "no rows found");
+    psFree(output);
+    return true;
+  }
+  bool status;
+  psS64 m_order = psMetadataLookupS64(&status, output2->data[0], "count(*)");
+  if (!status) {
+
+    psAbort("failed to lookup value for count column");
+    return false;
+  }
+  psString final = NULL;
+  psStringAppend(&final, "%" PRIu64, m_order);
+    //return false;
+  psFree(query3);
+  psFree(output2);
+
+
+
+  //update the merge_order
+
+  psString query4 = NULL;
+  psStringAppend(&query4, "update mergedvodbProcessed set merge_order = %"PRIu64,m_order);
+  psStringAppend(&query4," where merge_id = %" PRIu64,  merge_id);
+  //printf("%s", query4);
+  if (!p_psDBRunQuery(config->dbh, query4)) {
+    // rollback
+    if (!psDBRollback(config->dbh)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+    }
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    psFree(query4);
+    return false;
+  }
+  //
+psFree(query4);
+
+// since there is only one exp per 'new' set mergedvodbRun.state = 'full'
+
+  psString query2 = NULL ;
+
+  if (!fault) {
+    psStringAppend(&query2, "UPDATE mergedvodbRun SET state = 'full' WHERE merge_id = %'" PRIu64, row->merge_id);
+  
+    if (!p_psDBRunQuery(config->dbh, query2)) {
+      // rollback
+      if (!psDBRollback(config->dbh)) {
+	psError(PS_ERR_UNKNOWN, false, "database error");
+      }
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      psFree(query2);
+      return false;
+    }
+    
+  }
+
+
+
+
+
+
+  psFree(row);
+  psFree(pendingRow);
+
+
+
+
+
+  //commit the changes
+  if (!psDBCommit(config->dbh)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    return false;
+  }
+
+
+
+
+ //print the merge_order (why not!)
+  printf("%s", final);
+  psFree(final);
+
+
+
+
+
+
+
+
+
+
+
+    return true;
+}
+
+
+static bool listmergedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_S64(config->args, where, "-minidvodb_id",    "mergedvodbRun.minidvodb_id",    "==");
+    PXOPT_COPY_S64(config->args, where, "-merge_id",    "mergedvodbRun.merge_id",    "==");
+    PXOPT_COPY_STR(config->args, where, "-mergedvodb",    "mergedvodbRun.mergedvodb",    "==");
+
+    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);
+
+    psString query = NULL;
+    query = pxDataGet("mergetool_find_processed.sql");
+
+if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+    }
+
+    
+
+    // we either add AND (condition) or WHERE (condition):
+    if (psListLength(where->list) && faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", " AND fault != 0");
+    }
+      if (psListLength(where->list) && !faulted) {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", " AND fault = 0");
+    }
+	if (!psListLength(where->list) && faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", " WHERE fault != 0");
+    }
+	if (!psListLength(where->list) && !faulted) {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", " WHERE fault = 0");
+    }
+
+psFree(where);
+
+    // order by merge_id
+    psStringAppend(&query, " ORDER BY merge_id");
+    
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("addtool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+ // negate simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "addProcessedExp", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+
+
+    return true;
+}
+
+
+static bool revertmergedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-merge_id",    "mergedvodbRun.merge_id",         "==");
+    PXOPT_COPY_S16(config->args, where, "-fault",    "mergedvodbRun.fault",         "==");
+    PXOPT_COPY_STR(config->args, where, "-mergedvodb",    "mergedvodbRun.mergedvodb",         "==");
+    
+    if (!psListLength(where->list)) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+      
+      if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(where);
+        return false;
+      }
+
+      {
+      psString query = NULL;
+      query = pxDataGet("mergetool_revertmergedvodbprocessed.sql");
+
+if (!query) {
+            // rollback
+        if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+            psFree(where);
+            return false;
+        }
+
+        // use psDBGenerateWhereConditionalSQL with AND ... because the SQL ends in a WHERE
+        if (where && psListLength(where->list)) {
+            psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+            psStringAppend(&query, " AND %s", whereClause);
+            psFree(whereClause);
+        }
+
+        if (!p_psDBRunQuery(config->dbh, query)) {
+            // rollback
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(query);
+            psFree(where);
+            return false;
+        }
+        psFree(query);
+    }
+    psFree(where);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+
+
+    return true;
+}
+
+static bool updatemergedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_LOOKUP_U64(minidvodb_id,  config->args, "-merge_id", true, false);
+    PXOPT_COPY_S64(config->args, where, "-merge_id",     "mergedvodbProcessed.merge_id", "==");
+    PXOPT_LOOKUP_F32(dtime_merge,  config->args, "-set_dtime_merge", false, false);
+    PXOPT_LOOKUP_F32(dtime_verify,  config->args, "-set_dtime_verify", false, false);
+    PXOPT_LOOKUP_F32(dtime_script,  config->args, "-set_dtime_script", false, false);
+    PXOPT_LOOKUP_S16(fault,  config->args, "-set_fault", false, false);
+    
+    PXOPT_LOOKUP_STR(state,  config->args, "-set_state", false, false);
+
+ if (!psListLength(where->list)) {
+    psFree(where);
+    psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+    return false;
+  }
+ psString query = psStringCopy("UPDATE mergedvodbProcessed JOIN mergedvodbRun USING (merge_id) SET ");
+int cnt = 0;
+  psString comma = ",";
+  if (fault) {
+    psStringAppend(&query, " fault = %d", fault);
+  cnt++;
+  }
+
+
+
+  if (dtime_verify) {
+    if (cnt) {
+      psStringAppend(&query, "%s", comma);
+    }
+    psStringAppend(&query, " dtime_verify = %f", dtime_verify);
+    cnt++;
+  }
+
+  if (dtime_merge) {
+    if (cnt) {
+      psStringAppend(&query, "%s", comma);
+    }
+    psStringAppend(&query, " dtime_merge = %f", dtime_merge);
+    cnt++;
+ }
+
+if (dtime_script) {
+    if (cnt) {
+      psStringAppend(&query, "%s", comma);
+    }
+    psStringAppend(&query, " dtime_script = %f", dtime_script);
+    cnt++;
+ }
+if (state) {
+    if (cnt) {
+      psStringAppend(&query, "%s", comma);
+    }
+    psStringAppend(&query, " state = '%s'", state);
+    cnt++;
+ }
+
+
+
+
+  psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+  psStringAppend(&query, " WHERE %s", whereClause);
+
+  if (!p_psDBRunQuery(config->dbh, query)) {
+    psError(PS_ERR_UNKNOWN, false, "database error \n%s\n",query );
+   psFree(query);
+   return false;
+  }
+
+  psFree(query);
+  psFree(where);
+
+    return true;
+}
+
+static bool definebyquerymergecopyMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where,  "-merge_id",    "mergedvodbRun.merge_id", "==");
+    pxAddLabelSearchArgs (config, where, "-mergedvodb",     "mergedvodbRun.mergedvodb", "=="); // define using camRun label
+   
+
+
+    if (!psListLength(where->list)) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+
+    PXOPT_LOOKUP_STR(destination_host,     config->args, "-set_destination_host", false, false);
+    PXOPT_LOOKUP_STR(mergedvodb_rsync_path, config->args, "-set_mergedvodb_rsync_path", false, false);
+    PXOPT_LOOKUP_BOOL(pretend,    config->args, "-pretend", false);
+    PXOPT_LOOKUP_BOOL(simple,     config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(last_merged, config->args, "-last_merged", false);
+    // prevent queueing an addRun if a given exposure has already been added to
+    // the given dvo database
+    psString dvodb_string = NULL;
+    psString bare_query = NULL;
+    if (destination_host) {
+      psTrace("mergedvodbtool.c", PS_LOG_INFO, "destination_host argument found (%s) using mergedvodbtool_find_merge_id_dvo.sql\n", destination_host);
+        // find the cam_id of all the exposures that we want to queue up.
+        bare_query = pxDataGet("mergedvodbtool_find_merge_id_dvo.sql");
+        // user supplied dvodb
+        psStringAppend(&dvodb_string, "mergedvodbCopy.destination_host = '%s'", destination_host);
+    } else {
+      psError(PS_ERR_UNKNOWN, false, "cannot queue mergedvodbcopy run without a defined destination_host");
+       
+            return false;
+         }
+
+   if (!bare_query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retrieve SQL statement");
+        psFree(where);
+        return false;
+    }
+    // Take the bare query and add the dvodb selector
+    psString query = NULL;
+    psStringAppend(&query, bare_query, dvodb_string);
+    psFree(dvodb_string);
+    psFree(bare_query);
+
+    // use psDBGenerateWhereConditionSQL because the SQL ends in a WHERE
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    } else {
+        psError(PS_ERR_UNKNOWN, true, "search parameters are required");
+        return false;
+    }
+    if (last_merged) {
+      psStringAppend(&query, " ORDER BY merge_order DESC LIMIT 1");
+    }
+
+    psFree(where);
+
+   if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+   if (!psArrayLength(output)) {
+        psTrace("addtool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (pretend) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "mergedvodbCopy", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+        psFree(output);
+        return true;
+    }
+
+    // loop over our list of camRun rows to check the supplied and selected dvodb and workdir values:
+    for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *md = output->data[i];
+
+        mergedvodbRunRow *row = mergedvodbRunObjectFromMetadata(md);
+        if (!row) {
+            psError(PS_ERR_UNKNOWN, false, "failed to convert metadata into mergedvodbRun");
+            psFree(output);
+            return false;
+        }
+
+        if (!mergedvodb_rsync_path) {
+            psError(PS_ERR_UNKNOWN, false, "cannot queue mergedvodbcopy run without a defined mergedvodb_rsync_path: merge_id %" PRId64, row->merge_id);
+            psFree(output);
+            return false;
+        }
+        if (!destination_host) {
+            psError(PS_ERR_UNKNOWN, false, "cannot queue mergedvodbcopy run without a defined destination_host: merge_id %" PRId64, row->merge_id);
+            psFree(output);
+            return false;
+        }
+
+        psFree(row);
+    }
+
+    // start a transaction so we don't end up with an exp without any associted
+    // imfiles
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(output);
+        return false;
+    }
+
+  for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *md = output->data[i];
+
+        mergedvodbRunRow *row = mergedvodbRunObjectFromMetadata(md);
+        if (!row) {
+            psError(PS_ERR_UNKNOWN, false, "failed to convert metadata into camRun");
+            psFree(output);
+            return false;
+        }
+
+        // queue the exp
+        if (!pxaddQueueByMergeID(config,
+                               row->merge_id,
+                                     destination_host ? destination_host : "NULL",
+                                     mergedvodb_rsync_path ?mergedvodb_rsync_path : "NULL"
+        )) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error sfg");
+            }
+            psError(PS_ERR_UNKNOWN, false,
+                    "failed to trying to queue merge_id: %" PRId64, row->merge_id);
+            psFree(row);
+            psFree(output);
+            return false;
+        }
+        psFree(row);
+    }
+    psFree(output);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool listmergedvodbcopyMode(pxConfig *config) {
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-merge_id", "mergedvodbCopy.merge_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-mergedvodbcopy_id", "mergedvodbCopy.mergedvodbcopy_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-destination_host", "mergedvodbCopy.destination_host", "==");
+  PXOPT_COPY_STR(config->args, where, "-mergedvodb", "mergedvodbRun.mergedvodb", "==");
+  PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+  PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+  PXOPT_LOOKUP_BOOL(pending, config->args, "-pending", false);
+  PXOPT_LOOKUP_BOOL(faulted, config->args, "-faulted", false);
+  if (!psListLength(where->list)) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+
+  psString query = pxDataGet("mergedvodbtool_find_mergedvodbcopy.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+    }
+
+// we either add AND (condition) or WHERE (condition):
+    if (where->list && faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", " AND mergedvodbCopy.fault != 0");
+    }
+    if (where->list && !faulted) {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", " AND mergedvodbCopy.fault = 0");
+    }
+    if (!where->list && faulted) {
+
+       // list only faulted rows
+        psStringAppend(&query, " %s", " AND mergedvodbCopy.fault != 0");
+    }
+    if (where->list && !faulted) {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", " AND mergedvodbCopy.fault = 0");
+    }
+    if (!where->list && faulted) {
+        // list only faulted rows
+        psStringAppend(&query, " %s", " WHERE mergedvodbCopy.fault != 0");
+    }
+    if (!where->list && !faulted) {
+        // don't list faulted rows
+        psStringAppend(&query, " %s", " WHERE mergedvodbCopy.fault = 0");
+    }
+    psFree(where);
+
+    if (pending) {
+       //add the cuts for pending (state new, no faults)
+      psStringAppend(&query, " %s", " AND mergedvodbCopy.state = 'new' AND mergedvodbCopy.fault = 0 AND mergedvodbCopy.destination_host IS NOT NULL AND mergedvodbCopy.mergedvodb_rsync_path IS NOT NULL");
+    }
+
+
+    // order by epoch
+    psStringAppend(&query, " ORDER BY mergedvodbcopy_id");
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+      psError(PS_ERR_UNKNOWN, false, "database error %s ", query);
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("addtool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negate simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "mergedvodbCopy", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+return true;
+}
+
+static bool revertmergedvodbcopyMode(pxConfig *config) {
+  psMetadata *where = psMetadataAlloc();
+  PS_ASSERT_PTR_NON_NULL(config, false);
+  PXOPT_COPY_S64(config->args, where, "-mergedvodbcopy_id", "mergedvodbCopy.mergedvodbcopy_id", "==");
+  PXOPT_COPY_S64(config->args, where, "-merge_id", "mergedvodbCopy.merge_id", "==");
+  PXOPT_COPY_STR(config->args, where, "-destination_host", "destination_host", "==");
+  PXOPT_COPY_S16(config->args, where, "-fault", "mergedvodbCopy.fault", "==");
+
+  if (!psListLength(where->list) && !psMetadataLookupBool(NULL, config->args, "-all")) {
+    psFree(where);
+    psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+    return false;
+  }
+
+  if (!psDBTransaction(config->dbh)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      psFree(where);
+      return false;
+  }
+
+  {
+    psString query = pxDataGet("mergedvodbtool_revertmergedvodbcopy.sql");
+    if (!query) {
+      // rollback
+      if (!psDBRollback(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+      }
+      psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+      psFree(where);
+      return false;
+    }
+
+    // use psDBGenerateWhereConditionalSQL with AND ... because the SQL ends in a WHERE
+    if (where && psListLength(where->list)) {
+      psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+      psStringAppend(&query, " AND %s", whereClause);
+      psFree(whereClause);
+    }
+   if (!p_psDBRunQuery(config->dbh, query)) {
+      // rollback
+      if (!psDBRollback(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error %s", query);
+      }
+      psError(PS_ERR_UNKNOWN, false, "database error %s", query );
+      psFree(query);
+      psFree(where);
+            return false;
+    }
+    psFree(query);
+  }
+  psFree(where);
+
+  if (!psDBCommit(config->dbh)) {
+    psError(PS_ERR_UNKNOWN, false, "database error");
+    return false;
+  }
+
+  return true;
+}
+
+static bool updatemergedvodbcopyMode(pxConfig *config) {
+  PS_ASSERT_PTR_NON_NULL(config, false);
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_LOOKUP_U64(mergedvodbcopy_id,  config->args, "-mergedvodbcopy_id", false, false);
+  PXOPT_LOOKUP_U64(merge_id,  config->args, "-merge_id", false, false);
+  PXOPT_LOOKUP_STR(state,  config->args, "-state", false, false);
+  PXOPT_LOOKUP_STR(host,  config->args, "-host", false, false);
+  PXOPT_LOOKUP_STR(rsync_path,  config->args, "-mergedvodb_rsync_path", false, false);
+  PXOPT_LOOKUP_S16(fault,  config->args, "-fault", false, false);
+  
+  PXOPT_LOOKUP_S16(set_fault,  config->args, "-set_fault", false, false);
+  PXOPT_LOOKUP_STR(set_rsync_path,  config->args, "-set_mergedvodb_rsync_path", false, false);
+  PXOPT_LOOKUP_STR(set_host,  config->args, "-set_destination_host", false, false);
+  PXOPT_LOOKUP_STR(set_state,  config->args, "-set_state", false, false);
+  PXOPT_LOOKUP_F32(dtime,  config->args, "-set_dtime", false, false);
+  PXOPT_COPY_S64(config->args, where, "-mergedvodbcopy_id",     "mergedvodbCopy.mergedvodbcopy_id", "==");
+  PXOPT_COPY_S64(config->args, where, "-merge_id",     "mergedvodbCopy.merge_id", "==");
+  
+  PXOPT_COPY_STR(config->args, where, "-state",     "mergedvodbCopy.state", "==");
+  PXOPT_COPY_STR(config->args, where, "-host",     "mergedvodbCopy.destination_host", "==");
+  PXOPT_COPY_STR(config->args, where, "-mergedvodb_rsync_path",     "mergedvodbCopy.mergedvodb_rsync_path", "==");
+
+
+  if (!psListLength(where->list)) {
+    psFree(where);
+    psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+    return false;
+  }
+
+  psString query = psStringCopy("UPDATE mergedvodbCopy JOIN mergedvodbRun USING (merge_id) SET ");
+  int cnt = 0;
+  psString comma = ",";
+  if (set_fault) {
+    psStringAppend(&query, " mergedvodbCopy.fault = %d", set_fault);
+  cnt++;
+  }
+  if (set_rsync_path) {
+    if (cnt) {
+      psStringAppend(&query, "%s", comma);
+    }
+
+    psStringAppend(&query, " mergedvodbCopy.mergedvodb_rsync_path = '%s'" , set_rsync_path);
+    cnt++;
+  }
+
+  if (set_host) {
+    if (cnt) {
+      psStringAppend(&query, "%s", comma);
+    }
+
+    psStringAppend(&query, " mergedvodbCopy.destination_host = '%s'" , set_host);
+    cnt++;
+  }
+
+
+  if (set_state) {
+    if (cnt) {
+      psStringAppend(&query, "%s", comma);
+    }
+
+    psStringAppend(&query, " mergedvodbCopy.state = '%s'" , set_state);
+    cnt++;
+  }
+
+  if (dtime) {
+    if (cnt) {
+      psStringAppend(&query, "%s", comma);
+    }
+    psStringAppend(&query, " mergedvodbCopy.dtime = %f", dtime);
+    cnt++;
+  }
+
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+  psStringAppend(&query, " WHERE %s", whereClause);
+
+
+  if (!p_psDBRunQuery(config->dbh, query)) {
+    psError(PS_ERR_UNKNOWN, false, "database error %s", query);
+   psFree(query);
+   return false;
+  }
+
+  psFree(query);
+  psFree(where);
+
+  return true;
+}
+
+
+
Index: branches/meh_branches/ppstack_test/ippTools/src/mergetool.h
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/mergetool.h	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/src/mergetool.h	(revision 33415)
@@ -0,0 +1,41 @@
+/*
+ * mergetool.h
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MERGETOOL_H
+#define MERGETOOL_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+  MERGETOOL_MODE_NONE      = 0x0,
+  MERGETOOL_MODE_DEFINEBYQUERY,
+  MERGETOOL_MODE_UPDATERUN, 
+  MERGETOOL_MODE_PENDINGMERGE, 
+  MERGETOOL_MODE_ADDMERGED, 
+  MERGETOOL_MODE_LISTMERGED,
+  MERGETOOL_MODE_REVERTMERGED,
+  MERGETOOL_MODE_UPDATEMERGED,
+  MERGETOOL_MODE_DEFINEBYQUERYMERGECOPY,
+  MERGETOOL_MODE_LISTMERGEDVODBCOPY,
+  MERGETOOL_MODE_REVERTMERGEDVODBCOPY,
+  MERGETOOL_MODE_UPDATEMERGEDVODBCOPY
+} mergetoolMode;
+
+pxConfig *mergetoolConfig(pxConfig *config, int argc, char **argv);
+
+#endif // MERGETOOL_H
Index: branches/meh_branches/ppstack_test/ippTools/src/mergetoolConfig.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/mergetoolConfig.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/src/mergetoolConfig.c	(revision 33415)
@@ -0,0 +1,196 @@
+/*
+ * addtoolConfig.c
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include <stdint.h>
+
+#include <psmodules.h>
+
+#include "pxtools.h"
+#include "pxmerge.h"
+#include "mergetool.h"
+
+pxConfig *mergetoolConfig(pxConfig *config, int argc, char **argv)
+{
+    if (!config) {
+        config = pxConfigAlloc();
+    }
+
+    pmConfigReadParamsSet(false);
+
+    // setup site config
+    config->modules = pmConfigRead(&argc, argv, NULL);
+    if (!config->modules) {
+        psError(psErrorCodeLast(), false, "Can't find site configuration");
+        psFree(config);
+        return NULL;
+    }
+
+    // -definebyquery
+    psMetadata *definebyqueryArgs = psMetadataAlloc();
+    psMetadataAddS64(definebyqueryArgs, PS_LIST_TAIL, "-minidvodb_id",             0, "search by minidvodb_id", 0);
+    // pxcamSetSearchArgs(definebyqueryArgs);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-mergedvodb", PS_META_DUPLICATE_OK, "search by mergedvodbRun.mergedvodb and use as mergedvodb", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-minidvodb_group", PS_META_DUPLICATE_OK, "search by minidvodbRun.minidvodb_group", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_state",        0, "define state", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_mergedvodb_path",        0, "define workdir", NULL);
+    //  psMetadataAddStr(definebyqueryArge, PS_LIST_TAIL, "-set_mergedvodb", 0, "define mergedvodb");
+
+        //    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_reduction",      0, "define reduction class", NULL);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-pretend",           0, "do not actually modify the database", false);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-simple",            0, "use the simple output format", false);
+   
+    psMetadata *updaterunArgs = psMetadataAlloc(); 
+
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-mergedvodb",        0, "search by mergedvodb", 0);
+    psMetadataAddU64(updaterunArgs, PS_LIST_TAIL, "-merge_id",        0, "search by merge_id", 0);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_state",        0, "change the state", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_mergedvodb_path",        0, "define workdir", NULL);
+   
+
+    psMetadata *pendingmergeArgs = psMetadataAlloc();
+    psMetadataAddU64(pendingmergeArgs, PS_LIST_TAIL, "-minidvodb_id",        0, "search by minidvodb_id", 0);
+    psMetadataAddStr(pendingmergeArgs, PS_LIST_TAIL, "-merge_id",        0, "search by merge_id", NULL);
+    psMetadataAddStr(pendingmergeArgs, PS_LIST_TAIL, "-mergedvodb",         0, "search by mergedvodb", NULL);
+ 
+    psMetadataAddU64(pendingmergeArgs, PS_LIST_TAIL, "-limit",        0, "limit to N items", 0);
+    psMetadataAddBool(pendingmergeArgs, PS_LIST_TAIL, "-simple",        0, "simple output", false);
+   
+
+    psMetadata *addmergedArgs = psMetadataAlloc();
+    psMetadataAddU64(addmergedArgs, PS_LIST_TAIL, "-merge_id", 0, "search by merge_id (required)", 0);
+ psMetadataAddStr(addmergedArgs, PS_LIST_TAIL, "-mergedvodb", 0, "search by mergedvodb (required)", 0);
+    psMetadataAddF32(addmergedArgs, PS_LIST_TAIL, "-dtime_verify",    0,    "define elapsed time for DVO verify (seconds)", NAN);
+    psMetadataAddF32(addmergedArgs, PS_LIST_TAIL, "-dtime_merge",    0,    "define elapsed time for DVO merge (seconds)", NAN);
+    psMetadataAddF32(addmergedArgs, PS_LIST_TAIL, "-dtime_script",    0,    "define elapsed time for script (seconds)", NAN);
+    psMetadataAddTime(addmergedArgs, PS_LIST_TAIL, "-epoch",         0,    "time merge is finished", NULL);
+    psMetadataAddS16(addmergedArgs, PS_LIST_TAIL, "-fault",          0,    "set fault code", 0);
+
+
+
+
+    psMetadata *listmergedArgs = psMetadataAlloc();
+    psMetadataAddU64(listmergedArgs, PS_LIST_TAIL, "-minidvodb_id",        0, "search by minidvodb_id", 0);
+    psMetadataAddStr(listmergedArgs, PS_LIST_TAIL, "-merge_id",        0, "search by merge_id", NULL);
+    psMetadataAddStr(listmergedArgs, PS_LIST_TAIL, "-mergedvodb",         0, "search by minidvodbRun.minidvodb_group", NULL);
+    psMetadataAddU64(listmergedArgs, PS_LIST_TAIL, "-limit",        0, "limit to N items", 0);
+    psMetadataAddBool(listmergedArgs, PS_LIST_TAIL, "-simple",        0, "simple output", false);
+    psMetadataAddBool(listmergedArgs, PS_LIST_TAIL, "-faulted",        0, "limit to faulted state", false);
+
+
+    psMetadata *revertmergedArgs = psMetadataAlloc();
+    psMetadataAddU64(revertmergedArgs, PS_LIST_TAIL, "-merge_id",        0, "search by merge_id", 0);
+    psMetadataAddS16(revertmergedArgs, PS_LIST_TAIL, "-fault",        0, "search by minidvodbCopyfault", 0);
+    psMetadataAddStr(revertmergedArgs, PS_LIST_TAIL, "-mergedvodb",        0, "search by mergedvodb", NULL);
+
+    psMetadata *updatemergedArgs = psMetadataAlloc();
+
+    psMetadataAddU64(updatemergedArgs, PS_LIST_TAIL, "-merge_id",        0, "search by merge_id", 0);
+    psMetadataAddS16(updatemergedArgs, PS_LIST_TAIL, "-set_fault",  0,            "set fault code", 0);
+    psMetadataAddF32(updatemergedArgs, PS_LIST_TAIL, "-set_dtime_verify",    0,    "define elapsed time for DVO verify (seconds)", 0);
+    psMetadataAddF32(updatemergedArgs, PS_LIST_TAIL, "-set_dtime_merge",    0,    "define elapsed time for DVO merge (seconds)", 0);
+    psMetadataAddF32(updatemergedArgs, PS_LIST_TAIL, "-set_dtime_script",    0,    "define elapsed time for script (seconds)", 0);
+    psMetadataAddStr(updatemergedArgs, PS_LIST_TAIL, "-set_state",        0, "change the state", NULL);
+  // -definebyquerymergecopy
+    psMetadata *definebyquerymergecopyArgs = psMetadataAlloc();
+    psMetadataAddS64(definebyquerymergecopyArgs, PS_LIST_TAIL, "-merge_id",             0, "search by merge_id", 0);
+    psMetadataAddStr(definebyquerymergecopyArgs, PS_LIST_TAIL, "-mergedvodb", PS_META_DUPLICATE_OK, "search by mergedvodb", NULL);
+    psMetadataAddStr(definebyquerymergecopyArgs, PS_LIST_TAIL, "-set_mergedvodb_rsync_path",        0, "define workdir", NULL);
+    psMetadataAddStr(definebyquerymergecopyArgs, PS_LIST_TAIL, "-set_destination_host",          0, "set destination host", NULL);
+    psMetadataAddBool(definebyquerymergecopyArgs, PS_LIST_TAIL, "-pretend",           0, "do not actually modify the database", false);
+    psMetadataAddBool(definebyquerymergecopyArgs, PS_LIST_TAIL, "-simple",            0, "use the simple output format", false);
+    psMetadataAddBool(definebyquerymergecopyArgs, PS_LIST_TAIL, "-last_merged",            0, "if multiple minidvodbs have been dvomerged between rsyncs, then only queue the most recent merged dvodb for copying", false);
+
+   psMetadata *listmergedvodbcopyArgs = psMetadataAlloc();
+    psMetadataAddU64(listmergedvodbcopyArgs, PS_LIST_TAIL, "-merge_id",        0, "search by mergedvodb_id", 0);
+    psMetadataAddStr(listmergedvodbcopyArgs, PS_LIST_TAIL, "-mergedvodbcopy_id",        0, "search by mergedvodbcopy_id", NULL);
+    psMetadataAddStr(listmergedvodbcopyArgs, PS_LIST_TAIL, "-mergedvodb",         0, "search by mergedvodbRun.mergedvodb", NULL);
+    psMetadataAddStr(listmergedvodbcopyArgs, PS_LIST_TAIL, "-destination_host",        0, "search by mergedvodbCopy.destination_host", NULL);
+    psMetadataAddBool(listmergedvodbcopyArgs, PS_LIST_TAIL, "-pending",        0, "limit to pending items", false);
+    psMetadataAddU64(listmergedvodbcopyArgs, PS_LIST_TAIL, "-limit",        0, "limit to N items", 0);
+    psMetadataAddBool(listmergedvodbcopyArgs, PS_LIST_TAIL, "-simple",        0, "simple output", false);
+    psMetadataAddBool(listmergedvodbcopyArgs, PS_LIST_TAIL, "-faulted",        0, "limit to faulted state", false);
+
+    psMetadata *revertmergedvodbcopyArgs = psMetadataAlloc();
+    psMetadataAddU64(revertmergedvodbcopyArgs, PS_LIST_TAIL, "-mergedvodbcopy_id",        0, "search by mergedvodbcopy_id", 0);
+    psMetadataAddU64(revertmergedvodbcopyArgs, PS_LIST_TAIL, "-merge_id",        0, "search by merge_id", 0);
+    psMetadataAddStr(revertmergedvodbcopyArgs, PS_LIST_TAIL, "-destination_host",        0, "search by destination_host", NULL);
+    psMetadataAddS16(revertmergedvodbcopyArgs, PS_LIST_TAIL, "-fault",        0, "search by mergedvodbCopyfault", 0);
+    psMetadata *updatemergedvodbcopyArgs = psMetadataAlloc();
+    psMetadataAddU64(updatemergedvodbcopyArgs, PS_LIST_TAIL, "-mergedvodbcopy_id",        0, "search by mergedvodbcopy_id", 0);
+    psMetadataAddU64(updatemergedvodbcopyArgs, PS_LIST_TAIL, "-merge_id",        0, "search by merge_id", 0);
+    psMetadataAddStr(updatemergedvodbcopyArgs, PS_LIST_TAIL, "-state",        0, "search by state", NULL);
+    psMetadataAddStr(updatemergedvodbcopyArgs, PS_LIST_TAIL, "-host",        0, "search by host", NULL);
+    psMetadataAddStr(updatemergedvodbcopyArgs, PS_LIST_TAIL, "-mergedvodb_rsync_path",        0, "search by rsync", NULL);
+    psMetadataAddS16(updatemergedvodbcopyArgs, PS_LIST_TAIL, "-fault",  0,            "search by fault code", 0);
+  
+
+    psMetadataAddS16(updatemergedvodbcopyArgs, PS_LIST_TAIL, "-set_fault",  0,            "set fault code", 0);
+  
+    psMetadataAddF32(updatemergedvodbcopyArgs, PS_LIST_TAIL, "-set_dtime",  0,    "set elapsed time for transfer", 0);
+    psMetadataAddStr(updatemergedvodbcopyArgs, PS_LIST_TAIL, "-set_mergedvodb_rsync_path",        0, "change the mergedvodb_rsync_path", NULL); 
+    psMetadataAddStr(updatemergedvodbcopyArgs, PS_LIST_TAIL, "-set_destination_host",        0, "change the host", NULL);
+    psMetadataAddStr(updatemergedvodbcopyArgs, PS_LIST_TAIL, "-set_state",        0, "change the state", NULL);
+
+
+
+
+
+
+    psMetadata *argSets = psMetadataAlloc();
+    psMetadata *modes = psMetadataAlloc();
+
+    PXOPT_ADD_MODE("-definebyquery",        "create runs from minindvodbRun",           MERGETOOL_MODE_DEFINEBYQUERY, definebyqueryArgs);
+    PXOPT_ADD_MODE("-updaterun",        "update existing mergedvodbRuns",           MERGETOOL_MODE_UPDATERUN, updaterunArgs);
+    PXOPT_ADD_MODE("-pendingmerge",        "list pending mergedvodbRuns",           MERGETOOL_MODE_PENDINGMERGE, pendingmergeArgs);
+    PXOPT_ADD_MODE("-addmerged",        "add processed mergedvodbRun",           MERGETOOL_MODE_ADDMERGED, addmergedArgs);
+    PXOPT_ADD_MODE("-listmerged","list merged dvodbs",           MERGETOOL_MODE_LISTMERGED, listmergedArgs);
+    PXOPT_ADD_MODE("-revertmerged","revert merged dvodbs",        MERGETOOL_MODE_REVERTMERGED,     revertmergedArgs);
+    PXOPT_ADD_MODE("-updatemerged","change merged dvodb properties",MERGETOOL_MODE_UPDATEMERGED,  updatemergedArgs); 
+ PXOPT_ADD_MODE("-definebyquerymergecopy",   "create runs from mergedvodbRun",  MERGETOOL_MODE_DEFINEBYQUERYMERGECOPY, definebyquerymergecopyArgs);
+ PXOPT_ADD_MODE("-listmergedvodbcopy","list copy mergedvodbs", MERGETOOL_MODE_LISTMERGEDVODBCOPY, listmergedvodbcopyArgs);
+    PXOPT_ADD_MODE("-revertmergedvodbcopy","revert copy mergedvobs",MERGETOOL_MODE_REVERTMERGEDVODBCOPY,     revertmergedvodbcopyArgs);
+    PXOPT_ADD_MODE("-updatemergedvodbcopy","change mergedvodb copy properties",MERGETOOL_MODE_UPDATEMERGEDVODBCOPY,  updatemergedvodbcopyArgs);
+
+
+
+    if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
+        psError(PS_ERR_UNKNOWN, false, "option parsing failed");
+        psFree(argSets);
+        psFree(modes);
+        psFree(config);
+        return NULL;
+    }
+
+    psFree(argSets);
+    psFree(modes);
+
+    // define Database handle, if used
+    config->dbh = psMemIncrRefCounter(pmConfigDB(config->modules));
+    if (!config->dbh) {
+        psError(PS_ERR_UNKNOWN, false, "Can't configure database");
+        psFree(config);
+        return NULL;
+    }
+
+    return config;
+}
Index: branches/meh_branches/ppstack_test/ippTools/src/minidvodbtool.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/minidvodbtool.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/minidvodbtool.c	(revision 33415)
@@ -89,5 +89,5 @@
     psMetadata *where = psMetadataAlloc();
     pxcamGetSearchArgs (config, where);
-    PXOPT_COPY_S64(config->args, where,  "-minidvodb_id",    "minidvodbRun.minidvodbRun_id", "==");
+    PXOPT_COPY_S64(config->args, where,  "-minidvodb_id",    "minidvodbRun.minidvodb_id", "==");
     pxAddLabelSearchArgs (config, where, "-minidvodb_group",     "minidvodbRun.minidvodb_group", "=="); // define using camRun label
    
Index: branches/meh_branches/ppstack_test/ippTools/src/pstamptool.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/pstamptool.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/pstamptool.c	(revision 33415)
@@ -56,4 +56,7 @@
 static bool revertdependentMode(pxConfig *config);
 static bool getwebrequestnumMode(pxConfig *config);
+static bool addfileMode(pxConfig *config);
+static bool listfileMode(pxConfig *config);
+static bool deletefileMode(pxConfig *config);
 
 # define MODECASE(caseName, func) \
@@ -103,4 +106,7 @@
         MODECASE(PSTAMPTOOL_MODE_REVERTDEPENDENT, revertdependentMode);
         MODECASE(PSTAMPTOOL_MODE_GETWEBREQUESTNUM, getwebrequestnumMode);
+        MODECASE(PSTAMPTOOL_MODE_ADDFILE, addfileMode);
+        MODECASE(PSTAMPTOOL_MODE_LISTFILE, listfileMode);
+        MODECASE(PSTAMPTOOL_MODE_DELETEFILE, deletefileMode);
         default:
             psAbort("invalid option (this should not happen)");
@@ -173,6 +179,6 @@
 
     if (psListLength(where->list)) {
-        psString whereClause = psDBGenerateWhereSQL(where, NULL);
-        psStringAppend(&query, " %s", whereClause);
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
         psFree(whereClause);
     }
@@ -698,6 +704,8 @@
     PXOPT_LOOKUP_S16(fault,       config->args, "-fault",      false, false);
     PXOPT_LOOKUP_S64(exp_id,      config->args, "-exp_id",     false, false);
-    PXOPT_LOOKUP_S64(options,     config->args, "-options",     false, false);
+    PXOPT_LOOKUP_S64(options,     config->args, "-options",    false, false);
     PXOPT_LOOKUP_S64(dep_id,      config->args, "-dep_id",     false, false);
+    PXOPT_LOOKUP_S64(parent_id,   config->args, "-parent_id",  false, false);
+    PXOPT_LOOKUP_BOOL(is_parent,  config->args, "-is_parent",  false);
 
     // unless the job is being inserted with stop state require outputBase
@@ -710,4 +718,19 @@
     if (!strcmp(job_type, "get_image") || !strcmp(job_type, "detect_query") || !strcmp(job_type, "none")) {
         stampJob = false;
+    } else if (!strcmp(job_type, "child")) {
+        // job_type child's only action is to resolve a dependent
+        // XXX: IS this necessary?
+        if (!dep_id) {
+            psError(PS_ERR_UNKNOWN, true, "dep_id required for child job\n");
+            return false;
+        }
+        if (is_parent) {
+            psError(PS_ERR_UNKNOWN, true, "job type child can not be a parent job\n");
+            return false;
+        }
+        stampJob = false;
+    } else if (!strcmp(job_type, "mosaic")) {
+        stampJob = false;
+        is_parent = true;
     } else if (!strcmp(job_type, "stamp")) {
         stampJob = true;
@@ -717,5 +740,5 @@
     }
     if (!pstampJobInsert(config->dbh,
-            0, // job_id
+            0,          // job_id
             req_id,
             rownum,
@@ -727,5 +750,7 @@
             options,
             dep_id,
-            0   // fault_count
+            0,          // fault_count
+            parent_id,
+            is_parent
             )) {
         psError(PS_ERR_UNKNOWN, false, "database error");
@@ -755,4 +780,5 @@
     PXOPT_COPY_S64(config->args, where, "-req_id", "req_id", "==");
     PXOPT_COPY_S64(config->args, where, "-dep_id", "dep_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-jobType", "jobType", "==");
     PXOPT_COPY_S64(config->args, where, "-fault",  "fault", "==");
 
@@ -836,8 +862,11 @@
 
     // use psDBGenerateWhereSQL because the SQL yields an intermediate table
+    psString whereStr = NULL;
     if (psListLength(where->list)) {
         psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
-        psStringAppend(&query, " AND %s", whereClause);
+        psStringAppend(&whereStr, "\n AND %s", whereClause);
         psFree(whereClause);
+    } else {
+        psStringAppend(&whereStr, "%s", "");
     }
     psFree(where);
@@ -852,9 +881,11 @@
     }
 
-    if (!p_psDBRunQuery(config->dbh, query)) {
-        psError(PS_ERR_UNKNOWN, false, "database error");
-        psFree(query);
-        return false;
-    }
+    if (!p_psDBRunQueryF(config->dbh, query, whereStr, whereStr)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(whereStr);
+        psFree(query);
+        return false;
+    }
+    psFree(whereStr);
     psFree(query);
 
@@ -950,4 +981,5 @@
 
     PXOPT_LOOKUP_S32(fault,  config->args, "-set_fault",  true, false);
+    PXOPT_LOOKUP_STR(state,  config->args, "-set_state",  false, false);
 
     psMetadata *where = psMetadataAlloc();
@@ -980,5 +1012,5 @@
     psFree(where);
 
-    if (!p_psDBRunQueryF(config->dbh, query, fault, fault)) {
+    if (!p_psDBRunQueryF(config->dbh, query, fault, fault, state)) {
         psError(PS_ERR_UNKNOWN, false, "database error");
         psFree(query);
@@ -1476,2 +1508,122 @@
     return true;
 }
+
+static bool addfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(job_id, config->args, "-job_id", true, false);
+    PXOPT_LOOKUP_STR(path,   config->args, "-path",   true, false);
+
+    if (!pstampFileInsert(config->dbh,
+            0, // file_id
+            job_id,
+            path
+            )) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psU64 affected = psDBAffectedRows(config->dbh);
+    if (affected != 1) {
+        psError(PS_ERR_UNKNOWN, false,
+            "should have affected one row but %" PRIu64 " rows were modified",
+            affected);
+        return false;
+    }
+
+    psS64 file_id = psDBLastInsertID(config->dbh);
+    printf("%" PRId64 "\n", file_id);
+
+    return true;
+}
+
+static bool listfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-job_id", "job_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-req_id", "req_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-file_id", "file_id", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    if (!psListLength(where->list)) {
+        fprintf(stderr, "search arguments are required\n");
+        exit (1);
+    }
+
+    psString query = pxDataGet("pstamptool_listfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    // use psDBGenerateWhereSQL because the SQL yields an intermediate table
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("pstamptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "pstampFile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool deletefileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S64(job_id, config->args, "-job_id", true, false);
+
+    psString query = NULL; 
+    psStringAppend(&query, "DELETE FROM pstampFile WHERE job_id = %" PRId64, job_id);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psU64 affected = psDBAffectedRows(config->dbh);
+    psLogMsg("pstamptool", PS_LOG_INFO, "Deleted %" PRIu64 " rows from pstampFile", affected);
+
+    return true;
+}
Index: branches/meh_branches/ppstack_test/ippTools/src/pstamptool.h
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/pstamptool.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/pstamptool.h	(revision 33415)
@@ -50,4 +50,7 @@
     PSTAMPTOOL_MODE_REVERTDEPENDENT,
     PSTAMPTOOL_MODE_GETWEBREQUESTNUM,
+    PSTAMPTOOL_MODE_ADDFILE,
+    PSTAMPTOOL_MODE_LISTFILE,
+    PSTAMPTOOL_MODE_DELETEFILE,
 } pstamptoolMode;
 
Index: branches/meh_branches/ppstack_test/ippTools/src/pstamptoolConfig.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/pstamptoolConfig.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/pstamptoolConfig.c	(revision 33415)
@@ -144,4 +144,6 @@
     psMetadataAddS64(addjobArgs, PS_LIST_TAIL, "-options", 0,          "define options", 0);
     psMetadataAddS64(addjobArgs, PS_LIST_TAIL, "-dep_id", 0,           "define job dep_id", 0);
+    psMetadataAddS64(addjobArgs, PS_LIST_TAIL, "-parent_id", 0,        "define parent's job_id", 0);
+    psMetadataAddBool(addjobArgs, PS_LIST_TAIL, "-is_parent", 0,       "define whether job has children", false);
     psMetadataAddS16(addjobArgs, PS_LIST_TAIL, "-fault", 0,            "define job result", 0);
 
@@ -152,4 +154,5 @@
     psMetadataAddS64(listjobArgs, PS_LIST_TAIL, "-dep_id", 0,          "select by dependent ID", 0);
     psMetadataAddS16(listjobArgs, PS_LIST_TAIL, "-fault", 0,           "select by fault", 0);
+    psMetadataAddStr(listjobArgs, PS_LIST_TAIL, "-jobType", 0,         "select by jobType", 0);
     psMetadataAddU64(listjobArgs, PS_LIST_TAIL, "-limit",  0,          "limit result set to N items", 0);
     psMetadataAddBool(listjobArgs, PS_LIST_TAIL, "-simple", 0,         "use the simple output format", false);
@@ -186,4 +189,5 @@
     psMetadataAddS32(stopdependentjobArgs, PS_LIST_TAIL,  "-fault_count", 0,   "select by fault_count (>=)", 0);
     psMetadataAddS16(stopdependentjobArgs, PS_LIST_TAIL,  "-set_fault", 0,    "new fault value for job and dependent (required)", 0);
+    psMetadataAddStr(stopdependentjobArgs, PS_LIST_TAIL, "-set_state", 0,            "new pstampDependent.state", "new");
     psMetadataAddStr(stopdependentjobArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "search by pstampJob label (LIKE comparision)", NULL);
     psMetadataAddU64(stopdependentjobArgs, PS_LIST_TAIL, "-limit", 0,      "not used", 0);
@@ -274,4 +278,21 @@
     psMetadata *modes = psMetadataAlloc();
 
+    // -addfile
+    psMetadata *addfileArgs = psMetadataAlloc();
+    psMetadataAddS64(addfileArgs, PS_LIST_TAIL, "-job_id",       0, "define job ID for file (required)", 0);
+    psMetadataAddStr(addfileArgs, PS_LIST_TAIL, "-path",         0, "define path for file (required)", NULL);
+
+    // -listfile
+    psMetadata *listfileArgs = psMetadataAlloc();
+    psMetadataAddS64(listfileArgs, PS_LIST_TAIL, "-file_id",     0, "select by file ID", 0);
+    psMetadataAddS64(listfileArgs, PS_LIST_TAIL, "-job_id",      0, "select by job ID", 0);
+    psMetadataAddS64(listfileArgs, PS_LIST_TAIL, "-req_id",      0, "select by request ID", 0);
+    psMetadataAddU64(listfileArgs, PS_LIST_TAIL, "-limit",       0, "limit result set to N items", 0);
+    psMetadataAddBool(listfileArgs, PS_LIST_TAIL, "-simple",     0, "use the simple output format", false);
+
+    // -deletefile
+    psMetadata *deletefileArgs = psMetadataAlloc();
+    psMetadataAddS64(deletefileArgs, PS_LIST_TAIL, "-job_id",      0, "select by job ID (required)", 0);
+
     PXOPT_ADD_MODE("-addreq",          "", PSTAMPTOOL_MODE_ADDREQ,       addreqArgs);
     PXOPT_ADD_MODE("-pendingreq",      "", PSTAMPTOOL_MODE_PENDINGREQ,   pendingreqArgs);
@@ -302,4 +323,7 @@
     PXOPT_ADD_MODE("-project",         "", PSTAMPTOOL_MODE_PROJECT,    projectArgs);
     PXOPT_ADD_MODE("-getwebrequestnum","", PSTAMPTOOL_MODE_GETWEBREQUESTNUM,   getwebrequestnumArgs);
+    PXOPT_ADD_MODE("-addfile",         "", PSTAMPTOOL_MODE_ADDFILE,   addfileArgs);
+    PXOPT_ADD_MODE("-listfile",        "", PSTAMPTOOL_MODE_LISTFILE,   listfileArgs);
+    PXOPT_ADD_MODE("-deletefile",      "", PSTAMPTOOL_MODE_DELETEFILE,   deletefileArgs);
 
     if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
Index: branches/meh_branches/ppstack_test/ippTools/src/pxadd.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/pxadd.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/pxadd.c	(revision 33415)
@@ -131,4 +131,5 @@
 		       char *stage,
                        psS64 stage_id,
+		       psS32 stage_extra1,
                        char *workdir,
                        char *reduction,
@@ -159,4 +160,8 @@
         psMemSetPersistent(query, true);
       }
+      if (strcmp(stage,"staticsky_multi") == 0) {
+	query = pxDataGet("addtool_queue_sky_id_multi.sql");
+        psMemSetPersistent(query, true);
+      }
         if (!query) {
             psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
@@ -170,4 +175,5 @@
     // on 32/64
     if (!p_psDBRunQueryF(config->dbh, query,
+			 stage_extra1 ? stage_extra1 : 0,
 			 "new", // state
                          workdir  ? workdir   : "NULL",
Index: branches/meh_branches/ppstack_test/ippTools/src/pxadd.h
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/pxadd.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/pxadd.h	(revision 33415)
@@ -33,4 +33,5 @@
 		       char *stage,
 		       psS64 stage_id,
+		       psS32 stage_extra1,
 		       char *workdir,
 		       char *reduction,
Index: branches/meh_branches/ppstack_test/ippTools/src/pxchip.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/pxchip.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/pxchip.c	(revision 33415)
@@ -71,5 +71,5 @@
     psMetadataAddF32(md,  PS_LIST_TAIL, "-sun_angle_min",      0, "search by min solar angle", NAN);
     psMetadataAddF32(md,  PS_LIST_TAIL, "-sun_angle_max",      0, "search by max solar angle", NAN);
-    psMetadataAddStr(md,  PS_LIST_TAIL, "-object",             0, "search by exposure object", NULL);
+    psMetadataAddStr(md,  PS_LIST_TAIL, "-object",             0, "search by exposure object (LIKE comparison)", NULL);
     psMetadataAddStr(md,  PS_LIST_TAIL, "-comment",            0, "search by comment field (LIKE comparison)", NULL);
     psMetadataAddStr(md,  PS_LIST_TAIL, "-obs_mode",           0, "search by observation mode", NULL);
Index: branches/meh_branches/ppstack_test/ippTools/src/pxmerge.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/pxmerge.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/src/pxmerge.c	(revision 33415)
@@ -0,0 +1,97 @@
+/*
+ * pxadd.c
+ *
+ * Copyright (C) 2007  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <ippdb.h>
+#include <string.h>
+
+#include "pxtools.h"
+#include "pxmerge.h"
+
+bool pxmergeQueueByMinidvodbID(pxConfig *config,
+                       psS64 minidvodb_id,
+                       char *mergedvodb,
+                       char *mergedvodb_path
+  )
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // load the SQL to enqueue our exp_ids from disk once
+    static psString query = NULL;
+    if (!query) {
+        query = pxDataGet("mergetool_queue_merge_id.sql");
+        psMemSetPersistent(query, true);
+        if (!query) {
+            psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+            return false;
+        }
+    }
+    if (!p_psDBRunQueryF(config->dbh,"INSERT INTO mergedvodbRun SELECT         0,   minidvodb_id, '%s', '%s', 'new', NULL FROM minidvodbRun     WHERE    minidvodbRun.minidvodb_id = %lld and minidvodbRun.state = 'merged'", mergedvodb, mergedvodb_path, (long long) minidvodb_id))
+    {
+      psError(PS_ERR_UNKNOWN, false, "database error %s", query);
+        return false;
+    }
+    // just to be safe, we should have changed at least one row
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false,
+                "no rows affected - should have changed at least one row \n%s", query);
+        return false;
+    }
+    return true;
+}
+
+bool pxaddQueueByMergeID(pxConfig *config,
+                       psS64 merge_id,
+                       char *destination_host,
+                       char *mergedvodb_rsync_path
+  )
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // load the SQL to enqueue our exp_ids from disk once
+    static psString query = NULL;
+    if (!query) {
+        query = pxDataGet("mergetool_queue_mergecopy_id.sql");
+        psMemSetPersistent(query, true);
+        if (!query) {
+            psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+            return false;
+        }
+    }
+
+    if (!p_psDBRunQueryF(config->dbh,"INSERT INTO mergedvodbCopy SELECT         0,   merge_id, '%s', '%s', 0, 'new', NULL, 0 FROM mergedvodbRun     WHERE    mergedvodbRun.merge_id = %lld and (mergedvodbRun.state = 'merged' or mergedvodbRun.state = 'full')", mergedvodb_rsync_path, destination_host, (long long) merge_id))
+
+    {
+      psError(PS_ERR_UNKNOWN, false, "database error %s", query);
+        return false;
+    }
+
+    // just to be safe, we should have changed at least one row
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false,
+                "no rows affected - should have changed at least one row \n%s", query);
+        return false;
+    }
+
+    return true;
+}
Index: branches/meh_branches/ppstack_test/ippTools/src/pxmerge.h
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/pxmerge.h	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/src/pxmerge.h	(revision 33415)
@@ -0,0 +1,41 @@
+/*
+ * pxadd.h
+ *
+ * Copyright (C) 2009  Joshua Hoblitt, Chris Waters
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PXMERGE_H
+#define PXMERGE_H 1
+
+#include <pslib.h>
+
+#include "pxtools.h"
+
+
+bool pxmergeQueueByMinidvodbID(pxConfig *config,
+		       psS64 minidvodb_id,
+		       char *mergedvodbb,
+		       char *mergedvodb_path
+		       );
+
+bool pxaddQueueByMergeID(pxConfig *config,
+                       psS64 merge_id,
+                       char *destination_host,
+                       char *mergedvodb_rsync_path
+                       );
+
+
+#endif // PXMERGE_H
Index: branches/meh_branches/ppstack_test/ippTools/src/pxtools.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/pxtools.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/pxtools.c	(revision 33415)
@@ -35,4 +35,5 @@
     if (!strcmp(state, "drop")) return true;
     if (!strcmp(state, "wait")) return true;
+    if (!strcmp(state, "keep")) return true;
     if (!strcmp(state, "goto_cleaned")) return true;
     if (!strcmp(state, "error_cleaned")) return true;
Index: branches/meh_branches/ppstack_test/ippTools/src/regtool.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/regtool.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/regtool.c	(revision 33415)
@@ -268,4 +268,8 @@
       }
     }
+    char *is_stareim = strchr(psMetadataLookupStr(NULL,row,"exp_name"),'a');
+    if (is_stareim) {
+      continue;
+    }
     
     if ((psMetadataLookupS32(NULL,row,"is_downloaded") != 1)||
@@ -386,4 +390,9 @@
       }
     }
+    char *is_stareim = strchr(psMetadataLookupStr(NULL,row,"exp_name"),'a');
+    if (is_stareim) {
+      continue;
+    }
+
 
     bool status = false;
@@ -882,12 +891,18 @@
     PXOPT_LOOKUP_STR(set_md5sum, config->args, "-set_md5sum", false, false);
     PXOPT_LOOKUP_STR(set_state, config->args, "-set_state", false, false);
+    PXOPT_LOOKUP_BOOL(set_ignored, config->args, "-set_ignored", false);
+    PXOPT_LOOKUP_BOOL(clear_ignored, config->args, "-clear_ignored", false);
     PXOPT_LOOKUP_STR(hostname, config->args, "-hostname", false, false);
     
-    if ((fault == INT16_MAX) && (burntool_state == INT16_MAX) && !set_state) {
-        psError(PS_ERR_UNKNOWN, false, "one of -fault or -burntool_state or -set_state must be selected");
+    if ((fault == INT16_MAX) && (burntool_state == INT16_MAX) && !set_state && !set_ignored && !clear_ignored) {
+        psError(PS_ERR_UNKNOWN, false, "at least one of -fault or -burntool_state -set_ignored -clear_ignored or -set_state must be selected");
         return false;
     }
     if ((fault != INT16_MAX) && (burntool_state != INT16_MAX)) {
         psError(PS_ERR_UNKNOWN, false, "only one of -fault or -burntool_state must be selected");
+        return false;
+    }
+    if (set_ignored && clear_ignored) {
+        psError(PS_ERR_UNKNOWN, true, "only one of -set_ignored or -clear_ignored may be selected");
         return false;
     }
@@ -898,5 +913,6 @@
 
     if (fault != INT16_MAX) {
-        // this is fairly dangerous : can set all if the where is not set...
+        // this is fairly dangerous : can set all if the where is not set... but since exp_id and class_id
+        // were required above this is safe
         if (!pxSetFaultCode(config->dbh, "rawImfile", where, fault, 0)) {
             psError(PS_ERR_UNKNOWN, false, "failed to set set fault flag");
@@ -930,4 +946,16 @@
       }
       psStringAppend(&setvalues,"rawImfile.data_state = '%s'",set_state);
+    }
+    if (set_ignored) {
+      if (setvalues) {
+        psStringAppend(&setvalues,",");
+      }
+      psStringAppend(&setvalues,"rawImfile.ignored = 1");
+    }
+    if (clear_ignored) {
+      if (setvalues) {
+        psStringAppend(&setvalues,",");
+      }
+      psStringAppend(&setvalues,"rawImfile.ignored = 0");
     }
     if (hostname) {
Index: branches/meh_branches/ppstack_test/ippTools/src/regtoolConfig.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/regtoolConfig.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/regtoolConfig.c	(revision 33415)
@@ -194,4 +194,6 @@
     ADD_OPT(Str, updateprocessedimfileArgs, "-set_md5sum",     "set md5sum", NULL);
     ADD_OPT(Str, updateprocessedimfileArgs, "-set_state",      "set data state", NULL);
+    psMetadataAddBool(updateprocessedimfileArgs, PS_LIST_TAIL, "-set_ignored",  0,        "set imfile to be ignored for processing", false);
+    psMetadataAddBool(updateprocessedimfileArgs, PS_LIST_TAIL, "-clear_ignored",  0,        "set imfile to not be ignored for processing", false);
 
     
Index: branches/meh_branches/ppstack_test/ippTools/src/staticskytool.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/staticskytool.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/staticskytool.c	(revision 33415)
@@ -38,7 +38,15 @@
 static bool resultMode(pxConfig *config);
 static bool revertMode(pxConfig *config);
-static bool updateresult(pxConfig *config);
+static bool updateresultMode(pxConfig *config);
 static bool exportrunMode(pxConfig *config);
 static bool importrunMode(pxConfig *config);
+
+static bool defineskycalrunMode(pxConfig *config);
+static bool updateskycalrunMode(pxConfig *config);
+static bool pendingskycalrunMode(pxConfig *config);
+static bool addskycalresultMode(pxConfig *config);
+static bool skycalresultMode(pxConfig *config);
+static bool revertskycalresultMode(pxConfig *config);
+static bool updateskycalresultMode(pxConfig *config);
 
 static bool setstaticskyRunState(pxConfig *config, psS64 sky_id, const char *state);
@@ -69,7 +77,14 @@
         MODECASE(STATICSKYTOOL_MODE_RESULT,            resultMode);
         MODECASE(STATICSKYTOOL_MODE_REVERT,            revertMode);
-        MODECASE(STATICSKYTOOL_MODE_UPDATERESULT,      updateresult);
+        MODECASE(STATICSKYTOOL_MODE_UPDATERESULT,      updateresultMode);
         MODECASE(STATICSKYTOOL_MODE_EXPORTRUN,         exportrunMode);
         MODECASE(STATICSKYTOOL_MODE_IMPORTRUN,         importrunMode);
+        MODECASE(STATICSKYTOOL_MODE_DEFINESKYCALRUN,   defineskycalrunMode);
+        MODECASE(STATICSKYTOOL_MODE_UPDATESKYCALRUN,   updateskycalrunMode);
+        MODECASE(STATICSKYTOOL_MODE_PENDINGSKYCALRUN,  pendingskycalrunMode);
+        MODECASE(STATICSKYTOOL_MODE_ADDSKYCALRESULT,   addskycalresultMode);
+        MODECASE(STATICSKYTOOL_MODE_UPDATESKYCALRESULT,updateskycalresultMode);
+        MODECASE(STATICSKYTOOL_MODE_REVERTSKYCALRESULT,revertskycalresultMode);
+        MODECASE(STATICSKYTOOL_MODE_SKYCALRESULT,      skycalresultMode);
         default:
             psAbort("invalid option (this should not happen)");
@@ -97,10 +112,8 @@
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
-
-    // required options
-    PXOPT_LOOKUP_STR(workdir, config->args, "-set_workdir", true, false);
+    PXOPT_LOOKUP_STR(workdir,     config->args, "-set_workdir", true, false);
+    PXOPT_LOOKUP_STR(label,       config->args, "-set_label", true, false);
 
     // optional
-    PXOPT_LOOKUP_STR(label,       config->args, "-set_label", true, false);
     PXOPT_LOOKUP_STR(data_group,  config->args, "-set_data_group", false, false);
     PXOPT_LOOKUP_STR(dist_group,  config->args, "-set_dist_group", false, false);
@@ -111,5 +124,6 @@
     psMetadata *whereMD = psMetadataAlloc();
 
-    PXOPT_COPY_STR(config->args, whereMD, "-select_skycell_id",    "stackRun.skycell_id",       "==");
+    PXOPT_COPY_S64(config->args, whereMD, "-select_stack_id",      "stackRun.stack_id",         "==");
+    PXOPT_COPY_STR(config->args, whereMD, "-select_skycell_id",    "stackRun.skycell_id",       "LIKE");
     PXOPT_COPY_STR(config->args, whereMD, "-select_tess_id",       "stackRun.tess_id",          "==");
     PXOPT_COPY_F32(config->args, whereMD, "-select_good_frac_min", "stackSumSkyfile.good_frac", ">=");
@@ -621,5 +635,8 @@
     PXOPT_COPY_STR(config->args, where, "-label",      "staticskyRun.label", "==");
     PXOPT_COPY_STR(config->args, where, "-data_group", "staticskyRun.data_group", "LIKE");
+    PXOPT_COPY_STR(config->args, where, "-tess_id", "stackRun.tess_id", "LIKE");
+    PXOPT_COPY_STR(config->args, where, "-skycell_id", "stackRun.skycell_id", "LIKE");
     PXOPT_COPY_S16(config->args, where, "-fault",      "staticskyResult.fault", "==");
+    PXOPT_LOOKUP_S32(num_filters, config->args, "-num_filters", false, false);
 
     PXOPT_LOOKUP_BOOL(all, config->args, "-all", false);
@@ -645,8 +662,14 @@
     psFree(where);
 
+    psStringAppend(&query, "\nGROUP BY sky_id");
+    if (num_filters) {
+        psStringAppend(&query, "\nHAVING COUNT(filter) >= %d", num_filters);
+    }
+        
+
     // treat limit == 0 as "no limit"
     if (limit) {
         psString limitString = psDBGenerateLimitSQL(limit);
-        psStringAppend(&query, " %s", limitString);
+        psStringAppend(&query, "\n%s", limitString);
         psFree(limitString);
     }
@@ -735,5 +758,5 @@
 }
 
-static bool updateresult(pxConfig *config)
+static bool updateresultMode(pxConfig *config)
 {
     PS_ASSERT_PTR_NON_NULL(config, false);
@@ -978,2 +1001,477 @@
 # endif
 
+static bool defineskycalrunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required options
+    // none required. We get workdir, etc from staticskyRun if not provided
+
+    // optional
+    PXOPT_LOOKUP_STR(label,       config->args, "-set_label", false, false);
+    PXOPT_LOOKUP_STR(workdir,     config->args, "-set_workdir", false, false);
+    PXOPT_LOOKUP_STR(data_group,  config->args, "-set_data_group", false, false);
+    PXOPT_LOOKUP_STR(dist_group,  config->args, "-set_dist_group", false, false);
+    PXOPT_LOOKUP_STR(reduction,   config->args, "-set_reduction", false, false);
+    PXOPT_LOOKUP_STR(note,        config->args, "-set_note", false, false);
+    PXOPT_LOOKUP_TIME(registered, config->args, "-set_registered", false, false);
+
+    psMetadata *whereMD = psMetadataAlloc();
+
+    PXOPT_COPY_S64(config->args, whereMD, "-select_sky_id",        "staticskyRun.sky_id",       "==");
+    PXOPT_COPY_S64(config->args, whereMD, "-select_stack_id",      "stackRun.stack_id",         "==");
+    PXOPT_COPY_STR(config->args, whereMD, "-select_skycell_id",    "stackRun.skycell_id",       "==");
+    PXOPT_COPY_STR(config->args, whereMD, "-select_tess_id",       "stackRun.tess_id",          "==");
+    PXOPT_COPY_F32(config->args, whereMD, "-select_good_frac_min", "stackSumSkyfile.good_frac", ">=");
+    pxAddLabelSearchArgs(config, whereMD, "-select_label",         "stackRun.label",            "LIKE");
+    pxAddLabelSearchArgs(config, whereMD, "-select_data_group",    "stackRun.data_group",       "LIKE");
+    pxAddLabelSearchArgs(config, whereMD, "-select_filter",        "stackRun.filter",           "LIKE");
+
+    PXOPT_LOOKUP_BOOL(rerun, config->args, "-rerun", false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(pretend, config->args, "-pretend", false);
+
+    psString query = pxDataGet("staticskytool_defineskycalrun.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        psFree(whereMD);
+        return false;
+    }
+
+    if (!psListLength(whereMD->list)) {
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        psFree(whereMD);
+        return false;
+    }
+
+    psString whereClause = psDBGenerateWhereConditionSQL(whereMD, NULL);
+    psStringAppend(&query, "\nAND %s", whereClause);
+    psFree(whereClause);
+    psFree(whereMD);
+
+    if (!rerun) {
+        if (label)  {
+            psStringAppend(&query, "\nAND (skycalRun.label IS NULL OR skycalRun.label = '%s')", label);
+        }
+        psStringAppend(&query, "\nAND skycal_id IS NULL");
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    // we now have a list of (tess_id, skycell_id) that (potentially) meet out needs
+    // we now need to loop over all of these and for each pair, select the best set of
+    // inputs
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psWarning("staticskytool: no rows found");
+        psFree(output);
+        return true;
+    }
+    if (pretend) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "skycalRun", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+        psFree(output);
+        return true;
+    }
+
+    for (long i = 0; i < output->n; i++) {
+        psMetadata *row = output->data[i]; // Row from select
+        bool status;
+
+        psS64 sky_id = psMetadataLookupS64(&status, row, "sky_id");
+        psS64 stack_id = psMetadataLookupS64(&status, row, "stack_id");
+        psString sky_workdir = psMetadataLookupStr(&status, row, "workdir");
+        psString sky_label =  psMetadataLookupStr(&status, row, "label");
+        psString sky_data_group =  psMetadataLookupStr(&status, row, "data_group");
+
+	// create a staticskyRun
+	if (!skycalRunInsert(config->dbh,
+				0x0,	     // skycal_id
+                                sky_id,
+                                stack_id,
+				"new",	     // state
+				workdir ? workdir : sky_workdir,
+				label ? label : sky_label,
+				data_group ? data_group : sky_data_group,
+				dist_group,
+				reduction,
+				registered,
+				note
+		)
+	    ) {
+	    psError(PS_ERR_UNKNOWN, false, "database error");
+            psFree(output);
+	    return false;
+	}
+
+    }
+    psFree(output);
+    return true;
+}
+
+static bool updateskycalrunMode(pxConfig *config)
+{
+#ifdef notyet
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-sky_id",  "sky_id",   "==");
+    PXOPT_COPY_STR(config->args, where, "-label",   "label",    "==");
+    PXOPT_COPY_STR(config->args, where, "-state",   "state",    "==");
+    if (!psListLength(where->list)) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+
+    psString query = psStringCopy("UPDATE staticskyRun");
+
+    // pxUpdateRun gets parameters from config->args and updates
+    bool result = pxUpdateRun(config, where, &query, "staticskyRun", "sky_id", "staticskyResult", true, false);
+    psFree(query);
+    psFree(where);
+
+    return result;
+#endif
+    return false;
+}
+static bool pendingskycalrunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *whereMD = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, whereMD, "-skycal_id", "skycal_id", "==");
+    PXOPT_COPY_S64(config->args, whereMD, "-sky_id", "sky_id", "==");
+    pxAddLabelSearchArgs (config, whereMD, "-label", "skycalRun.label", "==");
+    PXOPT_COPY_STR(config->args, whereMD, "-filter", "stackRun.filter", "LIKE");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("staticskytool_pendingskycalrun.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (!psListLength(whereMD->list)) {
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        psFree(whereMD);
+        return false;
+    }
+
+    psString whereClause = psDBGenerateWhereConditionSQL(whereMD, NULL);
+    psStringAppend(&query, "\n AND %s", whereClause);
+    psFree(whereClause);
+    psFree(whereMD);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, "\n%s", limitString);
+        psFree(limitString);
+    }
+
+    // the where clause is required and matches the WHERE hook format string
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("staticskytool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "pendingskycalRun", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+    return true;
+}
+
+static bool addskycalresultMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // required
+    PXOPT_LOOKUP_S64(skycal_id, config->args, "-skycal_id", true, false);
+
+    // optional
+    PXOPT_LOOKUP_F32(sigma_ra, config->args,  "-sigma_ra", false, false);
+    PXOPT_LOOKUP_F32(sigma_dec, config->args, "-sigma_dec", false, false);
+
+    PXOPT_LOOKUP_F32(zpt_obs, config->args,   "-zpt_obs", false, false);
+    PXOPT_LOOKUP_F32(zpt_stdev, config->args, "-zpt_stdev", false, false);
+
+
+    PXOPT_LOOKUP_F32(dtime_script, config->args,   "-dtime_script", false, false);
+    PXOPT_LOOKUP_F32(dtime_astrom, config->args,   "-dtime_astrom", false, false);
+
+    PXOPT_LOOKUP_STR(hostname, config->args,    "-hostname", false, false);
+//    PXOPT_LOOKUP_S32(n_stars, config->args,     "-n_stars", false, false);
+    PXOPT_LOOKUP_S32(n_astrom, config->args,    "-n_astrom", false, false);
+
+    PXOPT_LOOKUP_STR(path_base, config->args,   "-path_base", false, false);
+    PXOPT_LOOKUP_S16(fault, config->args,       "-fault", false, false);
+    PXOPT_LOOKUP_S16(quality, config->args,     "-quality", false, false);
+
+    PXOPT_LOOKUP_STR(ver_pslib, config->args,   "-ver_pslib", false, false);
+    PXOPT_LOOKUP_STR(ver_psmodules, config->args, "-ver_psmodules", false, false);
+    PXOPT_LOOKUP_STR(ver_psphot, config->args,  "-ver_psphot", false, false);
+    PXOPT_LOOKUP_STR(ver_psastro, config->args, "-ver_psastro", false, false);
+    PXOPT_LOOKUP_STR(ver_ppstats, config->args, "-ver_ppstats", false, false);
+
+    psString software_ver = NULL;
+    if ((ver_pslib)&&(ver_psmodules)) {
+      software_ver = pxMergeCodeVersions(ver_pslib,ver_psmodules);
+    }
+    if (ver_psphot) {
+      software_ver = pxMergeCodeVersions(software_ver,ver_psphot);
+    }
+    if (ver_psastro) {
+      software_ver = pxMergeCodeVersions(software_ver,ver_psastro);
+    }
+    if (ver_ppstats) {
+      software_ver = pxMergeCodeVersions(software_ver,ver_ppstats);
+    }
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    skycalResultRow *row = skycalResultRowAlloc(
+        skycal_id,
+        path_base,
+        dtime_script,
+        dtime_astrom,
+        sigma_ra,
+        sigma_dec,
+        n_astrom,
+        zpt_obs,
+        zpt_stdev,
+        quality,
+        software_ver,
+        hostname,
+        fault
+        );
+
+    if (!skycalResultInsertObject(config->dbh, row)) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(row);
+        return false;
+    }
+
+    psFree(row);
+
+    if (fault) {
+        if (!psDBCommit(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return false;
+        }
+        return true;
+    }
+
+    psString query = "UPDATE skycalRun SET state = 'full' WHERE skycal_id = %" PRId64;
+    if (!p_psDBRunQueryF(config->dbh, query, skycal_id)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+static bool updateskycalresultMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_S16(fault, config->args, "-set_fault", true, false);
+    PXOPT_LOOKUP_S16(quality, config->args, "-set_quality", false, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-skycal_id",   "skycal_id",   "==");
+
+    if (!pxSetFaultCode(config->dbh, "skycalResult", where, fault, quality)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to set set fault flag");
+        psFree (where);
+        return false;
+    }
+    psFree (where);
+    return true;
+}
+static bool skycalresultMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-skycal_id",  "skycalRun.skycal_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-sky_id",     "skycalRun.sky_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-stack_id",   "skycalRun.stack_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-tess_id",    "stackRun.tess_id", "LIKE");
+    PXOPT_COPY_STR(config->args, where, "-skycell_id", "stackRun.skycell_id", "LIKE");
+    PXOPT_COPY_STR(config->args, where, "-filter",     "stackRun.filter", "LIKE");
+    PXOPT_COPY_STR(config->args, where, "-label",      "skycalRun.label", "==");
+    PXOPT_COPY_STR(config->args, where, "-data_group", "skycalRun.data_group", "LIKE");
+    PXOPT_COPY_S16(config->args, where, "-fault",      "skycalResult.fault", "==");
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("staticskytool_skycalresult.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (!psListLength(where->list)) {
+        psError(PXTOOLS_ERR_CONFIG, true, "search parameters are required");
+        return false;
+    }
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, " WHERE %s", whereClause);
+    psFree(whereClause);
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("staticskytool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        if (!ippdbPrintMetadatas(stdout, output, "staticskyResult", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+static bool revertskycalresultMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-skycal_id", "staticskyResult.sky_id", "==");
+    pxAddLabelSearchArgs(config, where, "-label", "staticskyRun.label", "==");
+    pxAddLabelSearchArgs(config, where, "-data_group", "staticskyRun.data_group", "==");
+    pxAddLabelSearchArgs(config, where, "-filter", "stackRun.filter", "==");
+    PXOPT_COPY_S16(config->args, where, "-fault", "staticskyResult.fault", "==");
+
+    if (!psListLength(where->list) && !psMetadataLookupBool(NULL, config->args, "-all")) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+
+    // Delete product
+    psString delete = pxDataGet("staticskytool_revertskycal.sql");
+    if (!delete) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&delete, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, delete)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(delete);
+        psFree(where);
+        return false;
+    }
+    psFree(delete);
+
+    int numRows = psDBAffectedRows(config->dbh); // Number of row affected
+    psLogMsg("staticskytool", PS_LOG_INFO, "Deleted %d rows", numRows);
+
+    psFree(where);
+    return true;
+}
Index: branches/meh_branches/ppstack_test/ippTools/src/staticskytool.h
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/staticskytool.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/staticskytool.h	(revision 33415)
@@ -34,5 +34,13 @@
     STATICSKYTOOL_MODE_UPDATERESULT,
     STATICSKYTOOL_MODE_EXPORTRUN,
-    STATICSKYTOOL_MODE_IMPORTRUN
+    STATICSKYTOOL_MODE_IMPORTRUN,
+
+    STATICSKYTOOL_MODE_DEFINESKYCALRUN,
+    STATICSKYTOOL_MODE_PENDINGSKYCALRUN,
+    STATICSKYTOOL_MODE_UPDATESKYCALRUN,
+    STATICSKYTOOL_MODE_ADDSKYCALRESULT,
+    STATICSKYTOOL_MODE_UPDATESKYCALRESULT,
+    STATICSKYTOOL_MODE_SKYCALRESULT,
+    STATICSKYTOOL_MODE_REVERTSKYCALRESULT,
 } staticskytoolMode;
 
Index: branches/meh_branches/ppstack_test/ippTools/src/staticskytoolConfig.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/staticskytoolConfig.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/staticskytoolConfig.c	(revision 33415)
@@ -48,4 +48,5 @@
     // XXX need a 'ra_min,max', 'dec_min,max' : to do this, we need a table of skycell boundaries
     psMetadata *definebyqueryArgs = psMetadataAlloc();
+    psMetadataAddS64(definebyqueryArgs,  PS_LIST_TAIL, "-select_stack_id", 0, "search for stack_id", 0);
     psMetadataAddStr(definebyqueryArgs,  PS_LIST_TAIL, "-select_skycell_id", 0, "search for skycell_id", NULL);
     psMetadataAddStr(definebyqueryArgs,  PS_LIST_TAIL, "-select_tess_id", 0, "search for tess_id", NULL);
@@ -107,8 +108,9 @@
     psMetadata *resultArgs= psMetadataAlloc();
     psMetadataAddS64(resultArgs, PS_LIST_TAIL, "-sky_id", 0, "search by staticsky ID", 0);
-    // psMetadataAddStr(resultArgs, PS_LIST_TAIL, "-tess_id", 0, "search by tess ID", 0);
-    // psMetadataAddStr(resultArgs, PS_LIST_TAIL, "-skycell_id", 0, "search by skycell ID", 0);
+    psMetadataAddStr(resultArgs, PS_LIST_TAIL, "-tess_id", 0, "search by tess ID (LIKE comparison)", 0);
+    psMetadataAddStr(resultArgs, PS_LIST_TAIL, "-skycell_id", 0, "search by skycell ID (LIKE comparison)", 0);
     psMetadataAddStr(resultArgs, PS_LIST_TAIL, "-label", 0, "search by label", NULL);
     psMetadataAddStr(resultArgs, PS_LIST_TAIL, "-data_group", 0, "search by data_group (LIKE comparison)", NULL);
+    psMetadataAddS16(resultArgs, PS_LIST_TAIL, "-num_filters", 0, "search by number of filters in the inputs (>=)", 0);
     psMetadataAddS16(resultArgs, PS_LIST_TAIL, "-fault", 0, "search by fault code", 0);
     psMetadataAddU64(resultArgs, PS_LIST_TAIL, "-limit", 0, "limit result set to N items", 0);
@@ -138,5 +140,102 @@
     psMetadata *importrunArgs = psMetadataAlloc();
     psMetadataAddStr(importrunArgs, PS_LIST_TAIL, "-infile", 0, "import from this file (required)", NULL);
-
+  
+    // -defineskycalrun
+    psMetadata *defineskycalrunArgs = psMetadataAlloc();
+    psMetadataAddS64(defineskycalrunArgs,  PS_LIST_TAIL, "-select_sky_id", 0, "search for sky_id", 0);
+    psMetadataAddS64(defineskycalrunArgs,  PS_LIST_TAIL, "-select_stack_id", 0, "search for stack_id", 0);
+    psMetadataAddStr(defineskycalrunArgs,  PS_LIST_TAIL, "-select_skycell_id", 0, "search for skycell_id", NULL);
+    psMetadataAddStr(defineskycalrunArgs,  PS_LIST_TAIL, "-select_tess_id", 0, "search for tess_id", NULL);
+    psMetadataAddF32(defineskycalrunArgs,  PS_LIST_TAIL, "-select_good_frac_min", 0, "define min good_frac", 0.0);
+    psMetadataAddStr(defineskycalrunArgs,  PS_LIST_TAIL, "-select_label", PS_META_DUPLICATE_OK, "search by stackRun label (LIKE comparison, multiple OK)", NULL);
+    psMetadataAddStr(defineskycalrunArgs,  PS_LIST_TAIL, "-select_data_group", PS_META_DUPLICATE_OK, "search by stackRun data_group (LIKE comparison, multiple OK)", NULL);
+    psMetadataAddStr(defineskycalrunArgs,  PS_LIST_TAIL, "-select_filter", PS_META_DUPLICATE_OK, "search by filter (LIKE comparison, multiple OK)", NULL);
+    psMetadataAddStr(defineskycalrunArgs,  PS_LIST_TAIL, "-set_workdir", 0, "define workdir", NULL);
+    psMetadataAddStr(defineskycalrunArgs,  PS_LIST_TAIL, "-set_label", 0, "define label", NULL);
+    psMetadataAddStr(defineskycalrunArgs,  PS_LIST_TAIL, "-set_data_group", 0, "define data group", NULL);
+    psMetadataAddStr(defineskycalrunArgs,  PS_LIST_TAIL, "-set_dist_group", 0, "define dist group", NULL);
+    psMetadataAddStr(defineskycalrunArgs,  PS_LIST_TAIL, "-set_note", 0, "define note", NULL);
+    psMetadataAddStr(defineskycalrunArgs,  PS_LIST_TAIL, "-set_reduction", 0, "define reduction", NULL);
+    psMetadataAddTime(defineskycalrunArgs, PS_LIST_TAIL, "-set_registered", 0, "time detrend run was registered", now);
+    psMetadataAddBool(defineskycalrunArgs, PS_LIST_TAIL, "-rerun",  0, "queue new run even if one already exists for inputs", false);
+    psMetadataAddBool(defineskycalrunArgs, PS_LIST_TAIL, "-pretend",  0, "do not actually modify the database", false);
+    psMetadataAddBool(defineskycalrunArgs, PS_LIST_TAIL, "-check_inputs",  0, "list inputs, do not modify database", false);
+    psMetadataAddBool(defineskycalrunArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+    // -updateskycalrun
+    psMetadata *updateskycalrunArgs = psMetadataAlloc();
+    psMetadataAddS64(updateskycalrunArgs, PS_LIST_TAIL, "-sky_id", 0, "search by stack ID", 0);
+    psMetadataAddStr(updateskycalrunArgs, PS_LIST_TAIL, "-state", 0, "search by state", NULL);
+    psMetadataAddStr(updateskycalrunArgs, PS_LIST_TAIL, "-label", 0, "search by label", 0);
+    psMetadataAddStr(updateskycalrunArgs, PS_LIST_TAIL, "-set_label", 0, "define new value for label", NULL);
+    psMetadataAddStr(updateskycalrunArgs, PS_LIST_TAIL, "-set_state", 0, "define new state", NULL);
+    psMetadataAddStr(updateskycalrunArgs, PS_LIST_TAIL, "-set_data_group", 0, "define new data_group", NULL);
+    psMetadataAddStr(updateskycalrunArgs, PS_LIST_TAIL, "-set_dist_group", 0, "define new dist_group", NULL);
+    psMetadataAddStr(updateskycalrunArgs, PS_LIST_TAIL, "-set_note", 0, "define new note", NULL);
+
+    // -pendingskycalrun
+    psMetadata *pendingskycalrunArgs = psMetadataAlloc();
+    psMetadataAddS64(pendingskycalrunArgs, PS_LIST_TAIL, "-skycal_id", 0, "search by skycal ID", 0);
+    psMetadataAddS64(pendingskycalrunArgs, PS_LIST_TAIL, "-sky_id", 0, "search by staticsky ID", 0);
+    psMetadataAddStr(pendingskycalrunArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "search by label", NULL);
+    psMetadataAddStr(pendingskycalrunArgs, PS_LIST_TAIL, "-filter", 0, "search by filter", NULL);
+    psMetadataAddU64(pendingskycalrunArgs, PS_LIST_TAIL, "-limit", 0, "limit result set to N items", 0);
+    psMetadataAddBool(pendingskycalrunArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
+
+
+    // -addskycalresult
+    psMetadata *addskycalresultArgs = psMetadataAlloc();
+    psMetadataAddS64(addskycalresultArgs, PS_LIST_TAIL, "-skycal_id", 0,            "define camtool ID (required)", 0);
+    psMetadataAddF32(addskycalresultArgs, PS_LIST_TAIL, "-sigma_ra", 0,            "define exposure E ra", NAN);
+    psMetadataAddF32(addskycalresultArgs, PS_LIST_TAIL, "-sigma_dec", 0,            "define exposure E dec", NAN);
+    psMetadataAddF32(addskycalresultArgs, PS_LIST_TAIL, "-zpt_obs", 0,   "define observed zero point", 0);
+    psMetadataAddF32(addskycalresultArgs, PS_LIST_TAIL, "-zpt_stdev", 0,   "define observed zero point stdandard deviation", 0);
+
+    psMetadataAddF32(addskycalresultArgs, PS_LIST_TAIL, "-dtime_script", 0, "define elapsed time in script (seconds)", NAN);
+    psMetadataAddF32(addskycalresultArgs, PS_LIST_TAIL, "-dtime_astrom", 0, "define elapsed time for astrometry (seconds)", NAN);
+
+    psMetadataAddStr(addskycalresultArgs, PS_LIST_TAIL, "-hostname", 0,            "define hostname", NULL);
+    // psMetadataAddS32(addskycalresultArgs, PS_LIST_TAIL, "-n_stars", 0,            "define number of stars", 0);
+    psMetadataAddS32(addskycalresultArgs, PS_LIST_TAIL, "-n_astrom", 0,            "define number of astrometry reference objects", 0);
+
+    psMetadataAddStr(addskycalresultArgs, PS_LIST_TAIL, "-path_base", 0,            "define base output location", NULL);
+    psMetadataAddS16(addskycalresultArgs, PS_LIST_TAIL, "-fault",  0,            "set fault code", 0);
+    psMetadataAddS16(addskycalresultArgs, PS_LIST_TAIL, "-quality",  0,            "set quality", 0);
+
+    psMetadataAddStr(addskycalresultArgs, PS_LIST_TAIL, "-ver_pslib", 0, "define psLib version", NULL);
+    psMetadataAddStr(addskycalresultArgs, PS_LIST_TAIL, "-ver_psmodules", 0, "define psModules version", NULL);
+    psMetadataAddStr(addskycalresultArgs, PS_LIST_TAIL, "-ver_psphot", 0, "define psphot version", NULL);
+    psMetadataAddStr(addskycalresultArgs, PS_LIST_TAIL, "-ver_psastro", 0, "define psastro version", NULL);
+    psMetadataAddStr(addskycalresultArgs, PS_LIST_TAIL, "-ver_ppstats", 0, "define ppStats version", NULL);
+    psMetadataAddStr(addskycalresultArgs, PS_LIST_TAIL, "-ver_streaks", 0, "define streaksremove version", NULL);
+
+    // -revertskycal
+    psMetadata *revertskycalArgs= psMetadataAlloc();
+    psMetadataAddS64(revertskycalArgs, PS_LIST_TAIL, "-skycal_id", 0, "search by staticsky ID", 0);
+    psMetadataAddStr(revertskycalArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "search by label", 0);
+    psMetadataAddStr(revertskycalArgs, PS_LIST_TAIL, "-data_group", PS_META_DUPLICATE_OK, "search by data_group", 0);
+    psMetadataAddStr(revertskycalArgs, PS_LIST_TAIL, "-filter", PS_META_DUPLICATE_OK, "search by filter", 0);
+    psMetadataAddS16(revertskycalArgs, PS_LIST_TAIL, "-fault", 0, "search by fault code", 0);
+    psMetadataAddBool(revertskycalArgs, PS_LIST_TAIL, "-all", 0, "allow no search terms", false);
+
+    // -updateskycalresult
+    psMetadata *updateskycalresultArgs = psMetadataAlloc();
+    psMetadataAddS64(updateskycalresultArgs, PS_LIST_TAIL, "-skycal_id", 0, "define staticksky ID (required)", 0);
+    psMetadataAddS16(updateskycalresultArgs, PS_LIST_TAIL, "-set_fault", 0, "set fault code (required)", 0);
+    psMetadataAddS16(updateskycalresultArgs, PS_LIST_TAIL, "-set_quality", 0, "set quality code", 0);
+
+    // -skycalresult
+    psMetadata *skycalresultArgs= psMetadataAlloc();
+    psMetadataAddS64(skycalresultArgs, PS_LIST_TAIL, "-skycal_id", 0, "search by skycal ID", 0);
+    psMetadataAddS64(skycalresultArgs, PS_LIST_TAIL, "-sky_id", 0, "search by staticsky ID", 0);
+    psMetadataAddS64(skycalresultArgs, PS_LIST_TAIL, "-stack_id", 0, "search by stack ID", 0);
+    psMetadataAddStr(skycalresultArgs, PS_LIST_TAIL, "-tess_id", 0, "search by tess ID (LIKE comparison)", 0);
+    psMetadataAddStr(skycalresultArgs, PS_LIST_TAIL, "-skycell_id", 0, "search by skycell ID (LIKE comparison)", 0);
+    psMetadataAddStr(skycalresultArgs, PS_LIST_TAIL, "-filter", 0, "search by filter (LIKE comparison)", 0);
+    psMetadataAddStr(skycalresultArgs, PS_LIST_TAIL, "-label", 0, "search by label (LIKE comparison)", NULL);
+    psMetadataAddStr(skycalresultArgs, PS_LIST_TAIL, "-data_group", 0, "search by data_group (LIKE comparison)", NULL);
+    psMetadataAddS16(skycalresultArgs, PS_LIST_TAIL, "-fault", 0, "search by fault code", 0);
+    psMetadataAddU64(skycalresultArgs, PS_LIST_TAIL, "-limit", 0, "limit skycalresult set to N items", 0);
+    psMetadataAddBool(skycalresultArgs, PS_LIST_TAIL, "-simple", 0, "use the simple output format", false);
 
     psFree(now);
@@ -155,4 +254,11 @@
     PXOPT_ADD_MODE("-exportrun",     "Export run",           STATICSKYTOOL_MODE_EXPORTRUN,     exportrunArgs);
     PXOPT_ADD_MODE("-importrun",     "Import run",           STATICSKYTOOL_MODE_IMPORTRUN,     importrunArgs);
+    PXOPT_ADD_MODE("-defineskycalrun", "Define a new skycalrun", STATICSKYTOOL_MODE_DEFINESKYCALRUN, defineskycalrunArgs);
+    PXOPT_ADD_MODE("-updateskycalrun", "Update a skycalrun", STATICSKYTOOL_MODE_UPDATESKYCALRUN,     updateskycalrunArgs);
+    PXOPT_ADD_MODE("-pendingskycalrun", "Get skcal runs to do",       STATICSKYTOOL_MODE_PENDINGSKYCALRUN,          pendingskycalrunArgs);
+    PXOPT_ADD_MODE("-addskycalresult", "add skycal result",       STATICSKYTOOL_MODE_ADDSKYCALRESULT, addskycalresultArgs);
+    PXOPT_ADD_MODE("-revertskycal",  "revert faulted skycal run", STATICSKYTOOL_MODE_REVERTSKYCALRESULT, revertskycalArgs);
+    PXOPT_ADD_MODE("-updateskycal",  "revert faulted skycal run", STATICSKYTOOL_MODE_UPDATESKYCALRESULT, updateskycalresultArgs);
+    PXOPT_ADD_MODE("-skycalresult",  "Get result of skycal run",  STATICSKYTOOL_MODE_SKYCALRESULT, skycalresultArgs);
 
     if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
Index: branches/meh_branches/ppstack_test/ippTools/src/vptool.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/vptool.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/src/vptool.c	(revision 33415)
@@ -0,0 +1,1849 @@
+/*
+ * vptool.c
+ *
+ * Copyright (C) 2011 IfA University of Hawaii
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include "pxtools.h"
+#include "pxdata.h"
+#include "pxchip.h"
+
+#include "vptool.h"
+
+static bool definebyqueryMode(pxConfig *config);
+static bool pendingrunMode(pxConfig *config);
+static bool updaterunMode(pxConfig *config);
+static bool revertrunMode(pxConfig *config);
+static bool listrunMode(pxConfig *config);
+static bool pendingimfileMode(pxConfig *config);
+static bool addprocessedcellMode(pxConfig *config);
+static bool processedcellMode(pxConfig *config);
+
+# define MODECASE(caseName, func) \
+    case caseName: \
+    if (!func(config)) { \
+        goto FAIL; \
+    } \
+    break;
+
+int main(int argc, char **argv) {
+    psLibInit(NULL);
+
+    pxConfig *config = vptoolConfig(NULL, argc, argv);
+    if (!config) {
+        psError(PXTOOLS_ERR_CONFIG, false, "failed to configure");
+        goto FAIL;
+    }
+
+    switch (config->mode) {
+        MODECASE(VPTOOL_MODE_DEFINEBYQUERY,         definebyqueryMode);
+        MODECASE(VPTOOL_MODE_PENDINGRUN,            pendingrunMode);
+        MODECASE(VPTOOL_MODE_UPDATERUN,             updaterunMode);
+        MODECASE(VPTOOL_MODE_REVERTRUN,             revertrunMode);
+        MODECASE(VPTOOL_MODE_LISTRUN,               listrunMode);
+        MODECASE(VPTOOL_MODE_PENDINGIMFILE,         pendingimfileMode);
+        MODECASE(VPTOOL_MODE_ADDPROCESSEDCELL,      addprocessedcellMode);
+        MODECASE(VPTOOL_MODE_PROCESSEDCELL,         processedcellMode);
+        default:
+            psAbort("invalid option (this should not happen)");
+    }
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(EXIT_SUCCESS);
+
+FAIL:
+    psErrorStackPrint(stderr, "\n");
+    int exit_status = pxerrorGetExitStatus();
+
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+
+    exit(exit_status);
+}
+
+// Queue exposures for videophot processing
+static bool queue_exposures(pxConfig *config,  // Configuration
+                            const psArray *exps, // Exposures information
+                            const char *label,   // Label, or NULL to inherit
+                            const char *data_group, // Data group, or NULL to inherit
+                            const char *workdir, // Working directory, or NULL to inherit
+                            const char *note,       // Note
+                            const psS64 dest_id
+    )
+{
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+
+# define GET_VALUE(PTYPE,CTYPE,VALUE,NAME)                              \
+    PTYPE VALUE;                                                        \
+    {                                                                   \
+        bool status;                                                    \
+        VALUE = psMetadataLookup##CTYPE(&status, md, NAME);             \
+        if (!status) {                                                  \
+            psError(PS_ERR_UNKNOWN, false, "failed to lookup value for %s", NAME); \
+            return false;                                               \
+        }                                                               \
+    }
+
+    // loop over our list of exp_ids
+    for (long i = 0; i < psArrayLength(exps); i++) {
+        psMetadata *md = exps->data[i];
+
+        rawExpRow *row = rawExpObjectFromMetadata(md);
+        if (!row) {
+            psError(PS_ERR_UNKNOWN, false, "failed to convert metadata into chipRun");
+            return false;
+        }
+
+        GET_VALUE (psS64,    S64, exp_id,        "exp_id");
+        GET_VALUE (psString, Str, raw_workdir,   "workdir");
+        GET_VALUE (psString, Str, raw_label,     "label");
+        GET_VALUE (psString, Str, raw_reduction, "reduction");
+        // GET_VALUE (psString, Str, raw_expgroup,  "expgroup");
+        GET_VALUE (psString, Str, raw_dvodb,     "dvodb");
+        GET_VALUE (psString, Str, raw_tess_id,   "tess_id");
+        GET_VALUE (psString, Str, raw_end_stage, "end_stage");
+
+        if (!row->exp_id) {
+            psError(PS_ERR_UNKNOWN, false, "failed to find value for exp_id");
+            return false;
+        }
+
+        // queue the exp
+        if (!vpRunInsert(config->dbh,
+                                 0,  //vp_id
+                                 exp_id,
+                                 "new",
+                                 label       ? label     : raw_label,
+                                 data_group  ? data_group : (label ? label : raw_label),
+                                 workdir     ? workdir   : raw_workdir,
+                                 note,
+                                 dest_id,
+                                 NULL,      // outroot
+                                 0,         // dtime_script
+                                 NULL,      // hostname
+                                 0          // fault
+                                 )) {
+            if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false,
+                    "failed to trying to queue exp_id: %" PRId64, exp_id);
+            return false;
+        }
+    }
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool definebyqueryMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_S64(exp_id, config->args, "-exp_id", false, false);
+
+
+    PXOPT_LOOKUP_STR(label, config->args, "-set_label", true, false);
+    PXOPT_LOOKUP_STR(data_group, config->args, "-set_data_group", false, false);
+    PXOPT_LOOKUP_STR(workdir, config->args, "-set_workdir", true, false);
+    PXOPT_LOOKUP_STR(note, config->args, "-set_note", false, false);
+    PXOPT_LOOKUP_S64(dest_id, config->args, "-set_dest_id", false, false);
+
+    // default
+    PXOPT_LOOKUP_BOOL(rerun, config->args, "-rerun", false);
+    PXOPT_LOOKUP_BOOL(pretend, config->args, "-pretend", false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    pxchipGetSearchArgs (config, where); // rawExp only
+    pxAddLabelSearchArgs (config, where, "-label", "newExp.label", "="); // define using newExp label
+
+    if (!psListLength(where->list)) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+
+    // find the exp_id of all the exposures that we want to queue up.
+    psString query = pxDataGet("vptool_find_rawexp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, " AND %s", whereClause);
+
+    psFree(whereClause);
+    psFree(where);
+
+    if (!rerun) {
+      psStringAppend(&query, "\n  AND vp_id IS NULL\n  AND (vpRun.label = '%s' OR vpRun.label IS NULL)", label);
+    }
+    
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("vptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (pretend) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "rawExp", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+        psFree(output);
+        return true;
+    }
+
+    if (!queue_exposures(config, output, label, data_group, workdir, note, dest_id)) {
+        psError(psErrorCodeLast(), false, "Unable to queue exposures for videophot processing.");
+        psFree(output);
+        return false;
+    }
+    psFree(output);
+
+    return true;
+}
+
+static bool pendingrunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    // Do we need pxchipArgs?
+    pxchipGetSearchArgs (config, where); //chipRun, rawExp
+    PXOPT_COPY_S64(config->args, where, "-vp_id", "vpRun.vp_id", "==");
+    pxAddLabelSearchArgs (config, where, "-label", "vpRun.label", "==");
+
+    psString query = pxDataGet("vptool_pendingrun.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    psStringAppend(&query, "\nORDER BY priority DESC, vp_id");
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("vptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "vpPendingRun", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+#ifdef notdef
+static bool definecopyMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_S64(exp_id, config->args, "-exp_id", false, false);
+
+    psMetadata *where = psMetadataAlloc();
+    pxchipGetSearchArgs (config, where); // rawExp only
+    pxAddLabelSearchArgs (config, where, "-label", "chipOld.label", "LIKE");
+    pxAddLabelSearchArgs (config, where, "-data_group", "chipOld.data_group", "LIKE");
+
+    // psListLength(where->list) is at least 1 because exp_type defaults to "object"
+    // so we require a list longer than 1 entry
+    if ((psListLength(where->list) <= 1) && !psMetadataLookupBool(NULL, config->args, "-all")) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+
+    PXOPT_LOOKUP_STR(workdir, config->args, "-set_workdir", true, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-set_label", true, false);
+    PXOPT_LOOKUP_STR(reduction, config->args, "-set_reduction", false, false);
+    PXOPT_LOOKUP_STR(expgroup, config->args, "-set_expgroup", false, false);
+    PXOPT_LOOKUP_STR(dvodb, config->args, "-set_dvodb", false, false);
+    PXOPT_LOOKUP_STR(tess_id, config->args, "-set_tess_id", false, false);
+    PXOPT_LOOKUP_STR(end_stage, config->args, "-set_end_stage", false, false);
+    PXOPT_LOOKUP_STR(dist_group, config->args, "-set_dist_group", false, false);
+    PXOPT_LOOKUP_STR(data_group, config->args, "-set_data_group", false, false);
+    PXOPT_LOOKUP_STR(note, config->args, "-set_note", false, false);
+
+    // default
+    PXOPT_LOOKUP_BOOL(pretend, config->args, "-pretend", false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // find the exp_id of all the exposures that we want to queue up.
+    psString query = pxDataGet("vptool_definecopy.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psStringAppend(&query, " AND %s", whereClause);
+
+    psFree(whereClause);
+    psFree(where);
+
+    if (!p_psDBRunQueryF(config->dbh, query, label)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("vptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (pretend) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "rawExp", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+        psFree(output);
+        return true;
+    }
+
+    if (!queue_exposures(config, output, workdir, label, data_group, dist_group,
+                         note)) {
+        psError(psErrorCodeLast(), false, "Unable to queue exposures for chip.");
+        psFree(output);
+        return false;
+    }
+    psFree(output);
+
+    return true;
+}
+#endif
+
+
+static bool updaterunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    psMetadata *where = psMetadataAlloc();
+//     pxchipGetSearchArgs (config, where); // rawExp, vpRun
+    PXOPT_COPY_S64(config->args,  where, "-vp_id", "vpRun.vp_id", "==");
+    PXOPT_COPY_STR(config->args,  where, "-label",   "vpRun.label",   "LIKE");
+    PXOPT_COPY_STR(config->args,  where, "-state",   "vpRun.state",   "==");
+    PXOPT_COPY_STR(config->args,  where, "-data_group", "vpRun.data_group",   "LIKE");
+
+    if (!psListLength(where->list)) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+
+    PXOPT_LOOKUP_STR(state, config->args, "-set_state", false, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-set_label", false, false);
+    PXOPT_LOOKUP_STR(data_group, config->args, "-set_data_group", false, false);
+    PXOPT_LOOKUP_STR(note, config->args, "-set_note", false, false);
+    PXOPT_LOOKUP_STR(hostname, config->args, "-set_hostname", false, false);
+    PXOPT_LOOKUP_STR(outroot, config->args, "-set_outroot", false, false);
+    PXOPT_LOOKUP_S16(fault, config->args, "-set_fault", false, false);
+    PXOPT_LOOKUP_F32(dtime_script, config->args, "-set_dtime_script", false, false);
+
+    if (!state && !label && !data_group && !note && !hostname && !fault) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, true, "must supply at least one set parameter");
+        return false;
+    }
+
+    psString query = psStringCopy("UPDATE vpRun");
+
+    char *separator = " SET ";
+    char * comma = ",";
+
+#   define addColumn(_tab, _fmt, _val)                                        \
+        do {                                                                \
+            if (_val) {                                                     \
+                psStringAppend(&query, "%s %s.%s =" _fmt, separator, _tab, #_val, _val); \
+                separator = comma;                                          \
+            }                                                               \
+        } while (0)
+
+    addColumn("vpRun", "'\%s'", state);
+    addColumn("vpRun", "'\%s'", label);
+    addColumn("vpRun", "'\%s'", data_group);
+    addColumn("vpRun", "'\%s'", note);
+    addColumn("vpRun", "'\%s'", hostname);
+    addColumn("vpRun", "'\%s'", outroot);
+    addColumn("vpRun", "%d", fault);
+    addColumn("vpRun", "%f", dtime_script);
+
+    psString  whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+    psFree(where);
+    psStringAppend(&query, "\nWHERE %s", whereClause);
+    psFree(whereClause);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+
+    psFree(query);
+
+    return true;
+}
+
+
+static bool pendingimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    // Do we need pxchipArgs?
+    pxchipGetSearchArgs (config, where); //chipRun, rawExp
+    PXOPT_COPY_S64(config->args, where, "-vp_id", "vpRun.vp_id", "==");
+    pxAddLabelSearchArgs (config, where, "-label", "vpRun.label", "==");
+
+    psString query = pxDataGet("vptool_pendingimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("vptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "vpPendingImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool addprocessedcellMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // vp_id, class_id, and cell_id are required
+    PXOPT_LOOKUP_S64(vp_id, config->args, "-vp_id", true, false);
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", true, false);
+    PXOPT_LOOKUP_STR(cell_id, config->args, "-cell_id", true, false);
+
+    // optional
+    PXOPT_LOOKUP_F32(dtime_photom,  config->args,  "-dtime_photom",  false, false);
+    PXOPT_LOOKUP_STR(path_base, config->args,      "-path_base", false, false);
+
+    // default values
+    PXOPT_LOOKUP_S16(fault, config->args, "-fault", false, false);
+    PXOPT_LOOKUP_S16(quality, config->args, "-quality", false, false);
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!vpProcessedCellInsert(config->dbh,
+                                   vp_id,
+                                   class_id,
+                                   cell_id,
+                                   dtime_photom,
+                                   quality,
+                                   path_base,
+                                   fault
+            )) {
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+static bool processedcellMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    pxchipGetSearchArgs (config, where); // chipRun, chipProcessedImfile, rawExp
+    PXOPT_COPY_S64(config->args, where, "-vp_id", "vp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "class_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-cell_id", "cell_id", "==");
+    pxAddLabelSearchArgs (config, where, "-label", "vpRun.label", "LIKE");
+    pxAddLabelSearchArgs (config, where, "-data_group", "vpRun.data_group", "LIKE");
+
+    psString query = pxDataGet("vptool_processedcell.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+    } else {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+    psFree(where);
+
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("vptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "vpProcessedCell", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool revertrunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-vp_id", "vpRun.vp_id", "==");
+    pxAddLabelSearchArgs (config, where, "-label", "vpRun.label", "==");
+    PXOPT_COPY_S16(config->args, where, "-fault", "vpProcessedCell.fault", "==");
+
+    PXOPT_LOOKUP_S16(fault, config->args, "-fault", false, false);
+
+    if (!psListLength(where->list)) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+
+    // find vpRuns with faulted cells
+    psString query = pxDataGet("vptool_revertrun.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+
+    psFree(where);
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+    int numUpdated = psDBAffectedRows(config->dbh);
+    psLogMsg("vptool", PS_LOG_INFO, "Reverted %d vpRuns", numUpdated);
+
+    return true;
+}
+
+#ifdef NOTDEF
+static bool revertcleanupMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-chip_id", "chipRun.chip_id", "==");
+    pxAddLabelSearchArgs (config, where, "-label", "chipRun.label", "LIKE");
+    pxAddLabelSearchArgs (config, where, "-data_group", "chipRun.data_group", "LIKE");
+    PXOPT_LOOKUP_STR(state, config->args, "-state", false, false);
+
+    char* newState = NULL;
+    if (!state) {
+        state = "error_cleaned";
+    }
+    if (!strcmp(state, "error_cleaned")) {
+        newState = "goto_cleaned";
+    } else if (!strcmp(state, "error_purged")) {
+        newState = "goto_purged";
+    } else if (!strcmp(state, "error_scrubbed")) {
+        newState = "goto_scrubbed";
+    } else {
+        psError(PXTOOLS_ERR_CONFIG, true, "-state must be either error_cleaned, error_purged, or error_scrubbed");
+        return false;
+    }
+
+    if (!psListLength(where->list)) {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters are required");
+        return false;
+    }
+
+    psString query = pxDataGet("vptool_revertcleanup.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        psFree(where);
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+
+    psFree(where);
+
+    if (!p_psDBRunQueryF(config->dbh, query, newState, state, state)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    return true;
+}
+
+static bool updateprocessedcellMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-chip_id", "chip_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-class_id", "class_id", "==");
+    PXOPT_LOOKUP_STR(state, config->args, "-set_state", false, NULL);
+    if (psListLength(where->list) == 0) {
+        psError(PS_ERR_UNKNOWN, true, "search parameters are required");
+        return false;
+    }
+
+    if (!state) {
+      PXOPT_LOOKUP_S16(fault, config->args, "-fault", true, false);
+      PXOPT_LOOKUP_S16(quality, config->args, "-set_quality", false, false);
+
+      if (!pxSetFaultCode(config->dbh, "chipProcessedImfile", where, fault, quality)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to set set fault flag");
+        return false;
+      }
+      psFree(where);
+    }
+    else {
+      if (!pxchipProcessedImfileSetStateByQuery(config,where,state)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to set chipProcessedImfile state");
+        return(false);
+      }
+    }
+
+
+    return true;
+}
+
+
+static bool blockMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(label, config->args, "-label", true, false);
+
+    if (!chipMaskInsert(config->dbh, label)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool maskedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    if (where->list->n < 1) {
+        psFree(where);
+        where = NULL;
+    }
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = psStringCopy("SELECT * FROM chipMask");
+
+    if (where) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psFree(where);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("vptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "chipMask", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+// return the list of labels which are NOT blocked
+static bool unmaskedMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+
+    psMetadata *where = psMetadataAlloc();
+
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    if (where->list->n < 1) {
+        psFree(where);
+        where = NULL;
+    }
+
+    psString query = pxDataGet("vptool_unmasked.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where) {
+        psString whereClause = psDBGenerateWhereSQL(where, "chipUnmask");
+        psFree(where);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("vptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "chipUnmask", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool unblockMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_STR(label, config->args, "-label", true, false);
+
+    char *query = "DELETE FROM chipMask WHERE label = '%s'";
+
+    if (!p_psDBRunQueryF(config->dbh, query, label)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+
+
+static bool pendingcleanuprunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    pxAddLabelSearchArgs (config, where, "-label", "label", "==");
+
+    psString query = pxDataGet("vptool_pendingcleanuprun.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("vptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "chipPendingCleanupRun", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool pendingcleanupimfileMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_S64(chip_id, config->args, "-chip_id", false, false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    if (chip_id) {
+        PXOPT_COPY_S64(config->args, where, "-chip_id", "chip_id", "==");
+    }
+    pxAddLabelSearchArgs (config, where, "-label", "label", "==");
+
+    psString query = pxDataGet("vptool_pendingcleanupimfile.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("vptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "chipPendingCleanupImfile", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool donecleanupMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_STR(config->args, where, "-label", "label", "==");
+
+    psString query = pxDataGet("vptool_donecleanup.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " AND %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("vptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "chipDoneCleanup", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+static bool runMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_STR(state, config->args, "-state", true, false);
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    // make sure that the state string is valid
+    if (!pxIsValidState(state)) {
+        psError(PXTOOLS_ERR_CONFIG, false, "%s is not a valid state", state);
+        return false;
+    }
+
+    psMetadata *where = psMetadataAlloc();
+    pxchipGetSearchArgs (config, where); // chipRun, chipProcessedImfile, rawExp
+    PXOPT_COPY_S64(config->args, where, "-chip_id", "chipRun.chip_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-reduction", "chipRun.reduction", "==");
+    pxAddLabelSearchArgs (config, where, "-label", "label", "==");
+    PXOPT_COPY_STR(config->args, where, "-state", "state", "==");
+
+    psString query = pxDataGet("vptool_run.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("vptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "chipDoneCleanup", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+
+static bool advanceexpMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+
+    psMetadata *where = psMetadataAlloc();
+    pxAddLabelSearchArgs (config, where, "-label", "label", "==");
+
+    // look for completed chipPendingExp
+    // migrate them to chipProccessedExp & camPendingExp
+    psString query = pxDataGet("vptool_completely_processed_exp.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (where && psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereSQL(where, NULL);
+        psStringAppend(&query, " %s", whereClause);
+        psFree(whereClause);
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("vptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    for (long i = 0; i < psArrayLength(output); i++) {
+        psMetadata *row = output->data[i];
+
+	psS64 chip_id = psMetadataLookupS64(NULL,row,"chip_id");
+	
+	psString software_ver = NULL;
+	psS64 maskfrac_npix = 0;
+	psF32 maskfrac_static = 0;
+	psF32 maskfrac_dynamic = 0;
+	psF32 maskfrac_magic = 0;
+	psF32 maskfrac_advisory = 0;
+
+	// Calculate run level masking and software state
+	if (!pxCoalesceRunStatus(config,"vptool_coalesce_run.sql",chip_id,
+				 &software_ver,&maskfrac_npix,
+				 &maskfrac_static,&maskfrac_dynamic,
+				 &maskfrac_magic,&maskfrac_advisory)) {
+	  psError(PS_ERR_UNKNOWN, false, "failed to generate run level statistics");
+	  psFree(output);
+	  if (!psDBRollback(config->dbh)) {
+	    psError(PS_ERR_UNKNOWN, false, "database error");
+	  }
+	  return(false);
+	}
+	psMetadataAddStr(row, PS_LIST_TAIL, "software_ver",  0, "Software version", software_ver);
+	psMetadataAddS32(row, PS_LIST_TAIL, "maskfrac_npix",  0, "Number of pixels used for mask stats", (psS32) maskfrac_npix);
+	psMetadataAddF32(row, PS_LIST_TAIL, "maskfrac_static",  0, "Fraction of static masked pixels", maskfrac_static);
+	psMetadataAddF32(row, PS_LIST_TAIL, "maskfrac_dynamic",  0, "Fraction of dynamic masked pixels", maskfrac_dynamic);
+	psMetadataAddF32(row, PS_LIST_TAIL, "maskfrac_magic",  0, "Fraction of magic masked pixels", maskfrac_magic);
+	psMetadataAddF32(row, PS_LIST_TAIL, "maskfrac_advisory",  0, "Fraction of advisory pixels", maskfrac_advisory);
+/* 	psWarning("ADVANCE %ld %s %d %f %f %f %f\n",chip_id,software_ver,maskfrac_npix,maskfrac_static,maskfrac_dynamic,maskfrac_magic,maskfrac_advisory); */
+        chipRunRow *chipRun = chipRunObjectFromMetadata(row);
+        if (!psDBTransaction(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return false;
+        }
+
+	// Set chipRun.software_ver to the appropriate value
+	if (software_ver) {
+	  if (!pxSetRunSoftware(config, "chipRun", "chip_id", chip_id, software_ver)) {
+	    psError(PS_ERR_UNKNOWN, false, "failed to set chipRun.software_ver for chip_id: %" PRId64,
+		    chip_id);
+	    psFree(output);
+	    if (!psDBRollback(config->dbh)) {
+	      psError(PS_ERR_UNKNOWN, false, "database error");
+	    }
+	    return(false);
+	  }
+	}
+	// Set chipRun.maskfrac* to the appropriate values.
+	if (maskfrac_npix) {
+	  if (!pxSetRunMaskfrac(config, "chipRun", "chip_id",chip_id, maskfrac_npix, maskfrac_static,
+				maskfrac_dynamic, maskfrac_magic, maskfrac_advisory)) {
+	    psError(PS_ERR_UNKNOWN, false, "failed to set chipRun.software_ver for chip_id: %" PRId64,
+		    chip_id);
+	    psFree(output);
+	    if (!psDBRollback(config->dbh)) {
+	      psError(PS_ERR_UNKNOWN, false, "database error");
+	    }
+	    return(false);
+	  }
+	}
+        // set chipRun.state to 'stop' and update the magicked state
+        if (!pxchipRunSetState(config, chipRun->chip_id, "full", chipRun->magicked)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to change chipRun.state for chip_id: %" PRId64, chipRun->chip_id);
+            psFree(chipRun);
+            psFree(output);
+            return false;
+        }
+
+        // should we stop here or proceed on to the cam stage?
+        // NULL for end_stage means go as far as possible
+        if (chipRun->end_stage && psStrcasestr(chipRun->end_stage, "chip")) {
+            if (!psDBCommit(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+                return false;
+            }
+
+            psFree(chipRun);
+            continue;
+        }
+        // else continue on...
+
+        // camQueueChipID() can only be run after chipRun.state has been set to
+        // stop
+        if (!pxcamQueueByChipID(config,
+				chipRun->chip_id,
+				chipRun->workdir,
+				chipRun->label,
+				chipRun->data_group,
+				chipRun->dist_group,
+				chipRun->reduction,
+				chipRun->expgroup,
+				chipRun->dvodb,
+				chipRun->tess_id,
+				chipRun->end_stage,
+				chipRun->magicked,
+				NULL    // note does not propagate
+        )) {
+           if (!psDBRollback(config->dbh)) {
+                psError(PS_ERR_UNKNOWN, false, "database error");
+            }
+            psError(PS_ERR_UNKNOWN, false, "failed to queue camPendingExp");
+            psFree(chipRun);
+            psFree(output);
+            return false;
+        }
+        if (!psDBCommit(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return false;
+        }
+        psFree(chipRun);
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+// update chipProcessedImfile.data_state to given value.
+// afterwards, if all imfiles in the exposure have the new state, update the state for the exposure as well
+// shared code for the modes -tocleanedimfile -tofullimfile -topurgedimfile
+
+static bool change_imfile_data_state(pxConfig *config, psString data_state, psString run_state)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    // chip_id, class_id are required
+    PXOPT_LOOKUP_S64(chip_id, config->args, "-chip_id", true, false);
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", true, false);
+
+    psString query = pxDataGet("vptool_change_imfile_data_state.sql");
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psString set_magicked_imfile = psStringCopy("");
+    psString set_magicked_run = psStringCopy("");
+    if (!strcmp(data_state, "full")) {
+        // if (chipProcessedImfile.magicked < 0 and rawImfile.magicked = 0) leave magicked unchanged. This will
+        // block warp processing until destreaking has been done
+        // otherwise copy magicked from the rawImfile
+        // Same thing for chipRun/rawExp
+        psStringAppend(&set_magicked_imfile, "\n , chipProcessedImfile.magicked = IF((chipProcessedImfile.magicked < 0"
+                                      " AND rawImfile.magicked = 0), chipProcessedImfile.magicked, rawImfile.magicked)");
+        psStringAppend(&set_magicked_run, "\n , chipRun.magicked = IF((chipRun.magicked < 0 AND rawExp.magicked = 0), "
+                                      " chipRun.magicked, rawExp.magicked)");
+
+    } else if (!strcmp(data_state, "cleaned") || !strcmp(data_state, "purged")) {
+        // if magicked is non-zero set it to -1
+        // Once one imfile has been cleaned, the chipRun is no longer 'magicked'
+        psStringAppend(&set_magicked_imfile, "\n, chipProcessedImfile.magicked = IF(chipProcessedImfile.magicked = 0, 0, -1),"
+                                             " chipRun.magicked = IF(chipRun.magicked = 0, 0, -1)");
+    }
+
+    if (!p_psDBRunQueryF(config->dbh, query, data_state, set_magicked_imfile, chip_id, class_id)) {
+        psFree(query);
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    psFree(query);
+    psFree(set_magicked_imfile);
+    psFree(set_magicked_run);
+    if (psDBAffectedRows(config->dbh) < 1) {
+        psError(PS_ERR_UNKNOWN, false, "should have affected atleast 1 row");
+        return false;
+    }
+
+    query = pxDataGet("vptool_change_exp_state.sql");
+    if (!p_psDBRunQueryF(config->dbh, query, data_state, set_magicked_run, chip_id, data_state)) {
+        psFree(query);
+        // rollback
+        if (!psDBRollback(config->dbh)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+        }
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    psFree(query);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    return true;
+}
+static bool tocleanedimfileMode(pxConfig *config)
+{
+    return change_imfile_data_state(config, "cleaned", "goto_cleaned");
+}
+static bool tofullimfileMode(pxConfig *config)
+{
+    return change_imfile_data_state(config, "full", "update");
+}
+static bool topurgedimfileMode(pxConfig *config)
+{
+    return change_imfile_data_state(config, "purged", "goto_purged");
+}
+static bool toscrubbedimfileMode(pxConfig *config)
+{
+  return change_imfile_data_state(config, "scrubbed", "goto_scrubbed");
+}
+bool exportrunMode(pxConfig *config)
+{
+  typedef struct ExportTable {
+    char tableName[80];
+    char sqlFilename[80];
+  } ExportTable;
+
+  int numExportTables = 3;
+
+  PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+  PXOPT_LOOKUP_S64(dummy, config->args, "-chip_id", true,  false);
+  PXOPT_LOOKUP_STR(outfile, config->args, "-outfile", true,  false);
+  PXOPT_LOOKUP_U64(limit,   config->args, "-limit",   false, false);
+  PXOPT_LOOKUP_BOOL(clean, config->args, "-clean", false);
+
+
+  FILE *f = fopen (outfile, "w");
+  if (f == NULL) {
+    psError(PS_ERR_UNKNOWN, false, "failed to open output file");
+    return false;
+  }
+
+  if (!pxExportVersion(config, f)) {
+    psError(PS_ERR_UNKNOWN, false, "failed to write dbversion output file");
+    return false;
+  }
+
+  psMetadata *where = psMetadataAlloc();
+  PXOPT_COPY_S64(config->args, where, "-chip_id", "chip_id", "==");
+
+  ExportTable tables [] = {
+    {"chipRun", "vptool_export_run.sql"},
+    {"chipImfile", "vptool_export_imfile.sql"},
+    {"chipProcessedImfile", "vptool_export_processed_imfile.sql"},
+  };
+
+
+  for (int i=0; i < numExportTables; i++) {
+    psString query = pxDataGet(tables[i].sqlFilename);
+    if (!query) {
+      psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+      return false;
+    }
+
+    if (where && psListLength(where->list)) {
+      psString whereClause = psDBGenerateWhereSQL(where, NULL);
+      psStringAppend(&query, " %s", whereClause);
+      psFree(whereClause);
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+      psString limitString = psDBGenerateLimitSQL(limit);
+      psStringAppend(&query, " %s", limitString);
+      psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      psFree(query);
+      return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+      psError(PS_ERR_UNKNOWN, false, "database error");
+      return false;
+    }
+    if (!psArrayLength(output)) {
+      psTrace("vptool", PS_LOG_INFO, "no rows found");
+      psFree(output);
+      return false;
+    }
+
+    if (clean) {
+        bool success = true;
+        if (!strcmp(tables[i].tableName, "chipRun")) {
+            success = pxSetStateCleaned("chipRun", "state", output);
+        } else if (!strcmp(tables[i].tableName, "chipProcessedImfile")) {
+            success = pxSetStateCleaned("chipProcessedImfile", "data_state", output);
+        }
+        if (!success) {
+            psFree(output);
+            psError(PS_ERR_UNKNOWN, false, "pxSetStateClean failed for table %s",  tables[i].tableName);
+            return false;
+        }
+    }
+
+    // we must write the export table in non-simple (true) format
+    if (!ippdbPrintMetadatas(f, output, tables[i].tableName, true)) {
+      psError(PS_ERR_UNKNOWN, false, "failed to print array");
+      psFree(output);
+      return false;
+    }
+    psFree(output);
+  }
+
+  fclose (f);
+
+  return true;
+}
+
+bool importrunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_STR(infile, config->args, "-infile", true, false);
+    unsigned int nFail = 0;               // Number of failed lines
+    psMetadata *input = psMetadataConfigRead(NULL, &nFail, infile, false);
+    if (nFail) {
+        psError(PS_ERR_IO, false, "%d failed lines in input", nFail);
+        psFree(input);
+        return false;
+    }
+
+    psVector *identifiers = psVectorAllocEmpty(16, PS_TYPE_U64); // Identifiers inserted
+
+    psMetadataIterator *iter = psMetadataIteratorAlloc(input, PS_LIST_HEAD, NULL);       // Iterator
+
+    if (!pxCheckImportVersion(config, input)) {
+        psError(PS_ERR_UNKNOWN, false, "pxCheckImportVersion failed");
+        return false;
+    }
+    // first item is the dbversion, skip it
+    psMetadataItem *dbversion =  psMetadataGetAndIncrement(iter);
+    (void) dbversion;
+
+    if (!psDBTransaction(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psMetadataItem *item;               // Item from iteration
+    while ((item = psMetadataGetAndIncrement(iter))) {
+        PXMIRROR_PRIMARY(item, "chipRun", chipRunRow, chipRunObjectFromMetadata, identifiers, chip_id,
+                         chipRunInsertObject, config->dbh,
+                         { psFree(iter); psFree(identifiers); psFree(input); });
+
+        PXMIRROR_OTHER(item, "chipImfile", chipImfileRow, chipImfileObjectFromMetadata, identifiers, chip_id,
+                       chipImfileInsertObject, config->dbh,
+                       { psFree(iter); psFree(identifiers); psFree(input); });
+
+        PXMIRROR_OTHER(item, "chipProcessedImfile", chipProcessedImfileRow,
+                       chipProcessedImfileObjectFromMetadata, identifiers, chip_id,
+                       chipProcessedImfileInsertObject, config->dbh,
+                       { psFree(iter); psFree(identifiers); psFree(input); });
+    }
+    psFree(iter);
+    psFree(input);
+
+    if (!psDBCommit(config->dbh)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psLogMsg("vptool", PS_LOG_INFO, "%ld chipRuns added", identifiers->n);
+    psFree(identifiers);
+
+    return true;
+}
+
+static bool runstateMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, false);
+
+    psMetadata *where = psMetadataAlloc();
+    PXOPT_COPY_S64(config->args, where, "-chip_id",    "chipRun.chip_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-exp_id",     "rawExp.exp_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-exp_name",   "rawExp.exp_name", "==");
+    pxAddLabelSearchArgs (config, where, "-label",     "chipRun.label", "LIKE");
+
+//    PXOPT_LOOKUP_BOOL(all, config->args, "-all", false);
+    PXOPT_LOOKUP_BOOL(no_magic, config->args, "-no_magic", false);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+
+    psString query = pxDataGet("vptool_runstate.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s", whereClause);
+        psFree(whereClause);
+    } else {
+        psError(PXTOOLS_ERR_CONFIG, true, "search parameters or -all are required");
+        return false;
+    }
+    psFree(where);
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psErrorCode err = psErrorCodeLast();
+        switch (err) {
+            case PS_ERR_DB_CLIENT:
+                psError(PXTOOLS_ERR_SYS, false, "database error");
+            case PS_ERR_DB_SERVER:
+                psError(PXTOOLS_ERR_PROG, false, "database error");
+            default:
+                psError(PXTOOLS_ERR_PROG, false, "unknown error");
+        }
+
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("vptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    if (psArrayLength(output)) {
+        // negative simple so the default is true
+        if (!ippdbPrintMetadatas(stdout, output, "chipRunState", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(output);
+            return false;
+        }
+    }
+
+    psFree(output);
+
+    return true;
+}
+
+// a very specfic function to queue a cleaned chipProcessedImfile to be updated
+static bool setimfiletoupdateMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_S64(chip_id, config->args, "-chip_id", true, false);
+    PXOPT_LOOKUP_STR(class_id, config->args, "-class_id", false, false);
+    PXOPT_LOOKUP_STR(label, config->args, "-set_label", false, false);
+
+    psString query = pxDataGet("vptool_setimfiletoupdate.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    psString setHook = psStringCopy("");
+    if (label) {
+        psStringAppend(&setHook, "\n , chipRun.label = '%s'", label);
+    }
+
+    if (class_id) {
+        psStringAppend(&query, " AND (chipProcessedImfile.class_id = '%s')", class_id);
+    }
+
+    // we do not update components with the magic fault value. They are non-updateable
+    psStringAppend(&query, " AND (chipProcessedImfile.fault != %d)", PXTOOL_DO_NOT_REVERT_FAULT);
+
+    if (!p_psDBRunQueryF(config->dbh, query, setHook, chip_id)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+
+    psFree(setHook);
+    psFree(query);
+
+    return true;
+}
+#endif // NOTDEF
+static bool listrunMode(pxConfig *config)
+{
+    PS_ASSERT_PTR_NON_NULL(config, NULL);
+
+    PXOPT_LOOKUP_U64(limit, config->args, "-limit", false, false);
+    PXOPT_LOOKUP_BOOL(simple, config->args, "-simple", false);
+    PXOPT_LOOKUP_BOOL(pstamp_order, config->args, "-pstamp_order", false);
+
+    psMetadata *where = psMetadataAlloc();
+    pxchipGetSearchArgs (config, where); // chipRun, chipProcessedImfile, rawExp
+    PXOPT_COPY_S64(config->args, where, "-chip_id", "chipRun.chip_id", "==");
+    PXOPT_COPY_STR(config->args, where, "-reduction", "chipRun.reduction", "==");
+    PXOPT_COPY_STR(config->args, where, "-state", "chipRun.state", "==");
+    pxAddLabelSearchArgs (config, where, "-label", "chipRun.label", "LIKE");
+    pxAddLabelSearchArgs (config, where, "-data_group", "chipRun.data_group", "LIKE");
+    pxAddLabelSearchArgs (config, where, "-dist_group", "chipRun.dist_group", "LIKE");
+    PXOPT_COPY_S64(config->args, where, "-magicked", "chipRun.magicked", "==");
+
+    psString where2 = NULL;
+    pxmagicAddWhere(config, &where2, "chipRun");
+    // add cuts on ra and decl if supplied
+    if (!pxspaceAddWhere(config, &where2, "rawExp")) {
+        psError(psErrorCodeLast(), false, "pxSpaceAddWhere failed");
+        return false;
+    }
+
+    psString query = pxDataGet("vptool_listrun.sql");
+    if (!query) {
+        psError(PXTOOLS_ERR_SYS, false, "failed to retreive SQL statement");
+        return false;
+    }
+
+    if (psListLength(where->list)) {
+        psString whereClause = psDBGenerateWhereConditionSQL(where, NULL);
+        psStringAppend(&query, " WHERE %s %s", whereClause, where2 ? where2 : "");
+        psFree(whereClause);
+    } else if (psMetadataLookupBool(NULL, config->args, "-all") || where2) {
+        psStringAppend(&query, " WHERE chipRun.chip_id IS NOT NULL %s", where2 ? where2 : "");
+    } else {
+        psFree(where);
+        psError(PXTOOLS_ERR_CONFIG, false, "search parameters (or -all) are required");
+        return false;
+    }
+    psFree(where);
+
+    if (pstamp_order) {
+        // put runs in order of exposure id with newest chip Runs first
+        // The postage stamp parser depends on this behavior
+        psStringAppend(&query, "\nORDER by exp_id, chip_id DESC");
+    }
+
+    // treat limit == 0 as "no limit"
+    if (limit) {
+        psString limitString = psDBGenerateLimitSQL(limit);
+        psStringAppend(&query, " %s", limitString);
+        psFree(limitString);
+    }
+
+    if (!p_psDBRunQuery(config->dbh, query)) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        psFree(query);
+        return false;
+    }
+    psFree(query);
+
+    psArray *output = p_psDBFetchResult(config->dbh);
+    if (!output) {
+        psError(PS_ERR_UNKNOWN, false, "database error");
+        return false;
+    }
+    if (!psArrayLength(output)) {
+        psTrace("vptool", PS_LOG_INFO, "no rows found");
+        psFree(output);
+        return true;
+    }
+
+    // negative simple so the default is true
+    if (!ippdbPrintMetadatas(stdout, output, "chipRun", !simple)) {
+        psError(PS_ERR_UNKNOWN, false, "failed to print array");
+        psFree(output);
+        return false;
+    }
+
+    psFree(output);
+
+    return true;
+}
Index: branches/meh_branches/ppstack_test/ippTools/src/vptool.h
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/vptool.h	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/src/vptool.h	(revision 33415)
@@ -0,0 +1,39 @@
+/*
+ * vptool.h
+ *
+ * Copyright (C) 2011 University of Hawaii IFA 
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef VPTOOL_H
+#define VPTOOL_H 1
+
+#include "pxtools.h"
+
+typedef enum {
+    VPTOOL_MODE_NONE      = 0x0,
+    VPTOOL_MODE_DEFINEBYQUERY,
+    VPTOOL_MODE_PENDINGRUN,
+    VPTOOL_MODE_UPDATERUN,
+    VPTOOL_MODE_REVERTRUN,
+    VPTOOL_MODE_LISTRUN,
+    VPTOOL_MODE_PENDINGIMFILE,
+    VPTOOL_MODE_ADDPROCESSEDCELL,
+    VPTOOL_MODE_PROCESSEDCELL,
+} vptoolMode;
+
+pxConfig *vptoolConfig(pxConfig *config, int argc, char **argv);
+
+#endif // VPTOOL_H
Index: branches/meh_branches/ppstack_test/ippTools/src/vptoolConfig.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/vptoolConfig.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippTools/src/vptoolConfig.c	(revision 33415)
@@ -0,0 +1,297 @@
+/*
+ * vptoolConfig.c
+ *
+ * Copyright (C) 2006-2008  Joshua Hoblitt
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * program; see the file COPYING. If not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+
+#include <psmodules.h>
+
+#include "pxtools.h"
+#include "vptool.h"
+
+pxConfig *vptoolConfig(pxConfig *config, int argc, char **argv)
+{
+    if (!config) {
+        config = pxConfigAlloc();
+    }
+
+    pmConfigReadParamsSet(false);
+
+    config->modules = pmConfigRead(&argc, argv, NULL);
+    if (! config->modules) {
+        psError(psErrorCodeLast(), false, "Can't find site configuration");
+        psFree(config);
+        return NULL;
+    }
+
+    // -definebyquery
+    psMetadata *definebyqueryArgs = psMetadataAlloc();
+    // allow selecting by all of the args used to queue chipRuns
+    pxchipSetSearchArgs (definebyqueryArgs);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "search by newExp label (LIKE comparison)", NULL);
+
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_label",  0,            "define label (required)", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_data_group",  0,      "define data group", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_workdir",  0,            "define workdir (required)", NULL);
+    psMetadataAddStr(definebyqueryArgs, PS_LIST_TAIL, "-set_note",  0,           "define note", NULL);
+    psMetadataAddS64(definebyqueryArgs, PS_LIST_TAIL, "-set_dest_id",  0,      "define destination", 0);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-pretend",  0,            "do not actually modify the database", false);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-rerun",   0,           "queue exposures even if a previous vpRun exists", false);
+    psMetadataAddBool(definebyqueryArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    // -pendingrun
+    psMetadata *pendingrunArgs = psMetadataAlloc();
+    psMetadataAddS64(pendingrunArgs, PS_LIST_TAIL, "-vp_id",  0,            "search by chip ID", 0);
+    psMetadataAddStr(pendingrunArgs, PS_LIST_TAIL, "-label",  PS_META_DUPLICATE_OK, "search by label", 0);
+    pxchipSetSearchArgs(pendingrunArgs);
+    psMetadataAddU64(pendingrunArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(pendingrunArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+
+    // -updaterun
+    psMetadata *updaterunArgs = psMetadataAlloc();
+//     pxchipSetSearchArgs (updaterunArgs);
+    psMetadataAddS64(updaterunArgs, PS_LIST_TAIL, "-vp_id", 0,            "search by vpRun ID", 0);
+    psMetadataAddS64(updaterunArgs, PS_LIST_TAIL, "-exp_id", 0,            "search by exp_id", 0);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-state", 0,            "search by state", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-label",  0,           "search by label (LIKE comparison)", NULL);
+    psMetadataAddS16(updaterunArgs, PS_LIST_TAIL, "-fault",  0,         "search by fault code", 0);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-data_group",  0,      "search by data_group (LIKE comparison)", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_state", 0,        "set state", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_label", 0,        "set label", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_data_group", 0,   "set data group", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_note", 0,         "set note", NULL);
+    psMetadataAddF32(updaterunArgs, PS_LIST_TAIL, "-set_dtime_script",  0,      "define elapsed time for script (seconds)", 0);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_hostname", 0, "set hostname", NULL);
+    psMetadataAddStr(updaterunArgs, PS_LIST_TAIL, "-set_outroot", 0, "set outroot", NULL);
+    psMetadataAddS16(updaterunArgs, PS_LIST_TAIL, "-set_fault",  0,  "set fault code", 0);
+
+    // -revertrun
+    psMetadata *revertrunArgs = psMetadataAlloc();
+    psMetadataAddS64(revertrunArgs, PS_LIST_TAIL, "-vp_id", 0,            "search by chip ID", 0);
+    psMetadataAddStr(revertrunArgs,  PS_LIST_TAIL, "-label",              PS_META_DUPLICATE_OK, "search by vpRun label (LIKE comparison)", NULL);
+    psMetadataAddStr(revertrunArgs,  PS_LIST_TAIL, "-data_group",              PS_META_DUPLICATE_OK, "search by vpRun data_group (LIKE comparison)", NULL);
+    // pxchipSetSearchArgs(revertrunArgs);
+    psMetadataAddS16(revertrunArgs, PS_LIST_TAIL, "-fault",  0,            "search by fault code", 0);
+
+    // -pendingimfile
+    psMetadata *pendingimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(pendingimfileArgs, PS_LIST_TAIL, "-vp_id",  0,            "search by chip ID", 0);
+    psMetadataAddStr(pendingimfileArgs, PS_LIST_TAIL, "-label",  PS_META_DUPLICATE_OK, "search by label", 0);
+    pxchipSetSearchArgs(pendingimfileArgs);
+    psMetadataAddU64(pendingimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(pendingimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    // -addprocessedcell
+    psMetadata *addprocessedcellArgs = psMetadataAlloc();
+    psMetadataAddS64(addprocessedcellArgs, PS_LIST_TAIL, "-vp_id",  0,            "define chip ID (required)", 0);
+    psMetadataAddStr(addprocessedcellArgs, PS_LIST_TAIL, "-class_id",  0,          "define class ID (required)", NULL);
+    psMetadataAddStr(addprocessedcellArgs, PS_LIST_TAIL, "-cell_id",  0,           "define class ID (required)", NULL);
+
+    psMetadataAddStr(addprocessedcellArgs, PS_LIST_TAIL, "-path_base",  0,         "define base output location (required)", NULL);
+    psMetadataAddF32(addprocessedcellArgs, PS_LIST_TAIL, "-dtime_photom",  0,      "define elapsed time for photometry (seconds)", NAN);
+    psMetadataAddStr(addprocessedcellArgs, PS_LIST_TAIL, "-hostname",  0,          "define hostname", NULL);
+
+    psMetadataAddS16(addprocessedcellArgs, PS_LIST_TAIL, "-fault",  0,              "set fault code", 0);
+    psMetadataAddS16(addprocessedcellArgs, PS_LIST_TAIL, "-quality",  0,            "set quality", 0);
+    
+
+    // -processedcell
+    psMetadata *processedcellArgs = psMetadataAlloc();
+    pxchipSetSearchArgs(processedcellArgs);
+    psMetadataAddS64(processedcellArgs, PS_LIST_TAIL,  "-vp_id",  0,         "search by  chip ID", 0);
+    psMetadataAddStr(processedcellArgs,  PS_LIST_TAIL, "-class_id",           0, "search by class ID", NULL);
+    psMetadataAddStr(processedcellArgs,  PS_LIST_TAIL, "-cell_id",           0, "search by class ID", NULL);
+    psMetadataAddStr(processedcellArgs,  PS_LIST_TAIL, "-label",  PS_META_DUPLICATE_OK, "search by vpRun label (LIKE comparison)", NULL);
+    psMetadataAddStr(processedcellArgs,  PS_LIST_TAIL, "-data_group",  PS_META_DUPLICATE_OK, "search by vpRun data_group (LIKE comparison)", NULL);
+
+    psMetadataAddU64(processedcellArgs,  PS_LIST_TAIL, "-limit",  0,           "limit result set to N items", 0);
+    psMetadataAddBool(processedcellArgs, PS_LIST_TAIL, "-simple",  0,         "use the simple output format", false);
+
+    pxspaceAddArguments(processedcellArgs);
+
+#ifdef notdef
+    // -updateprocessedcell
+    psMetadata *updateprocessedcellArgs = psMetadataAlloc();
+    psMetadataAddS64(updateprocessedcellArgs, PS_LIST_TAIL, "-vp_id",  0,            "search by chip ID", 0);
+    psMetadataAddStr(updateprocessedcellArgs,  PS_LIST_TAIL, "-class_id",           0, "search by class ID", NULL);
+    psMetadataAddS16(updateprocessedcellArgs, PS_LIST_TAIL, "-fault",  0,            "set fault code (required)", 0);
+    psMetadataAddStr(updateprocessedcellArgs, PS_LIST_TAIL, "-set_state", 0,         "set state", NULL);
+    psMetadataAddS16(updateprocessedcellArgs, PS_LIST_TAIL, "-set_quality",  0,            "set quality", 0);
+#endif // notdef
+    // -listrun
+    psMetadata *listrunArgs = psMetadataAlloc();
+    pxchipSetSearchArgs(listrunArgs);
+    psMetadataAddS64(listrunArgs, PS_LIST_TAIL, "-vp_id",  0,         "search by  vpRun ID", 0);
+    psMetadataAddStr(listrunArgs,  PS_LIST_TAIL, "-reduction",          0, "search by reduction class", NULL);
+    psMetadataAddStr(listrunArgs,  PS_LIST_TAIL, "-label",  PS_META_DUPLICATE_OK, "search by vpRun label (LIKE comparison)", NULL);
+    psMetadataAddStr(listrunArgs,  PS_LIST_TAIL, "-state",              0, "search by vpRun state", NULL);
+    psMetadataAddStr(listrunArgs,  PS_LIST_TAIL, "-data_group",  PS_META_DUPLICATE_OK, "search by vpRun data_group (LIKE comparison)", NULL);
+    psMetadataAddStr(listrunArgs,  PS_LIST_TAIL, "-dist_group",  PS_META_DUPLICATE_OK, "search by vpRun dist_group (LIKE comparison)", NULL);
+    // pxmagicAddArguments(listrunArgs);
+
+    psMetadataAddBool(listrunArgs, PS_LIST_TAIL, "-pstamp_order",  0,    "order results for postage stamp server", false);
+    psMetadataAddU64(listrunArgs,  PS_LIST_TAIL, "-limit",  0,           "limit result set to N items", 0);
+    psMetadataAddBool(listrunArgs, PS_LIST_TAIL, "-all",  0,            "list everything without search terms", false);
+    psMetadataAddBool(listrunArgs, PS_LIST_TAIL, "-simple",  0,         "use the simple output format", false);
+    pxspaceAddArguments(listrunArgs);
+
+#ifdef notdef
+    // -block
+    psMetadata *blockArgs = psMetadataAlloc();
+    psMetadataAddStr(blockArgs, PS_LIST_TAIL, "-label",  0,            "name of a label to mask out (required)", NULL);
+
+    // -masked
+    psMetadata *maskedArgs = psMetadataAlloc();
+    psMetadataAddStr(maskedArgs, PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
+    psMetadataAddBool(maskedArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(maskedArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -unmasked
+    psMetadata *unmaskedArgs = psMetadataAlloc();
+    psMetadataAddStr(unmaskedArgs, PS_LIST_TAIL, "-label",  0,            "restrict to specified label", NULL);
+    psMetadataAddBool(unmaskedArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(unmaskedArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -unblock
+    psMetadata *unblockArgs = psMetadataAlloc();
+    psMetadataAddStr(unblockArgs, PS_LIST_TAIL, "-label",  0,            "name of a label to unmask (required)", NULL);
+
+    // -pendingcleanuprun
+    psMetadata *pendingcleanuprunArgs = psMetadataAlloc();
+    psMetadataAddStr(pendingcleanuprunArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "list blocks for specified label", NULL);
+    psMetadataAddBool(pendingcleanuprunArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(pendingcleanuprunArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -pendingcleanupimfile
+    psMetadata *pendingcleanupimfileArgs = psMetadataAlloc();
+    psMetadataAddStr(pendingcleanupimfileArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "list blocks for specified label", NULL);
+    psMetadataAddS64(pendingcleanupimfileArgs, PS_LIST_TAIL, "-vp_id", 0,          "search by chip ID", 0);
+    psMetadataAddStr(pendingcleanupimfileArgs, PS_LIST_TAIL, "-exp_id",                 0,            "search by exp_id", NULL);
+    psMetadataAddBool(pendingcleanupimfileArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(pendingcleanupimfileArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -donecleanup
+    psMetadata *donecleanupArgs = psMetadataAlloc();
+    psMetadataAddStr(donecleanupArgs, PS_LIST_TAIL, "-label",  0,            "list blocks for specified label", NULL);
+    psMetadataAddBool(donecleanupArgs, PS_LIST_TAIL, "-simple",  0,            "use the simple output format", false);
+    psMetadataAddU64(donecleanupArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -revertcleanup
+    psMetadata *revertcleanupArgs = psMetadataAlloc();
+    psMetadataAddS64(revertcleanupArgs, PS_LIST_TAIL, "-vp_id", 0,            "search by chip ID", 0);
+    psMetadataAddStr(revertcleanupArgs,  PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "search by vpRun label (LIKE comparison)", NULL);
+    psMetadataAddStr(revertcleanupArgs,  PS_LIST_TAIL, "-data_group",  PS_META_DUPLICATE_OK, "search by vpRun data_group (LIKE comparison)", NULL);
+    psMetadataAddStr(revertcleanupArgs, PS_LIST_TAIL, "-state", 0,             "search by current state", NULL);
+    psMetadataAddS16(revertcleanupArgs, PS_LIST_TAIL, "-fault",  0,            "search by fault code", 0);
+
+    // -run
+    psMetadata *runArgs = psMetadataAlloc();
+    psMetadataAddStr(runArgs, PS_LIST_TAIL, "-label", PS_META_DUPLICATE_OK, "search by label", NULL);
+    psMetadataAddBool(runArgs, PS_LIST_TAIL, "-simple",  0,     "use the simple output format", false);
+    psMetadataAddU64(runArgs, PS_LIST_TAIL, "-limit",  0,       "limit result set to N items", 0);
+    psMetadataAddStr(runArgs, PS_LIST_TAIL, "-state", 0,        "search by state (required)", NULL);
+    pxchipSetSearchArgs(runArgs);
+    psMetadataAddS64(runArgs, PS_LIST_TAIL, "-vp_id",  0,         "search by  chip ID", 0);
+    psMetadataAddStr(runArgs,  PS_LIST_TAIL, "-reduction",          0, "search by reduction class", NULL);
+
+    // -advanceexp
+    psMetadata *advanceexpArgs = psMetadataAlloc();
+    psMetadataAddS64(advanceexpArgs, PS_LIST_TAIL, "-vp_id",  0,          "search by chip ID", 0);
+    psMetadataAddStr(advanceexpArgs, PS_LIST_TAIL, "-label",  PS_META_DUPLICATE_OK, "advance exposures for specified label", NULL);
+    psMetadataAddU64(advanceexpArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+
+    // -tocleanedimfile
+    psMetadata *tocleanedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(tocleanedimfileArgs, PS_LIST_TAIL, "-vp_id", 0,          "chip ID to update", 0);
+    psMetadataAddStr(tocleanedimfileArgs, PS_LIST_TAIL, "-class_id",  0,        "class ID to update", NULL);
+
+    // -tofullimfile
+    psMetadata *tofullimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(tofullimfileArgs, PS_LIST_TAIL, "-vp_id", 0,          "chip ID to update", 0);
+    psMetadataAddStr(tofullimfileArgs, PS_LIST_TAIL, "-class_id",  0,        "class ID to update", NULL);
+
+    // -topurgedimfile
+    psMetadata *topurgedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(topurgedimfileArgs, PS_LIST_TAIL, "-vp_id", 0,          "chip ID to update", 0);
+    psMetadataAddStr(topurgedimfileArgs, PS_LIST_TAIL, "-class_id",  0,        "class ID to update", NULL);
+
+    // -toscrubbedimfile
+    psMetadata *toscrubbedimfileArgs = psMetadataAlloc();
+    psMetadataAddS64(toscrubbedimfileArgs, PS_LIST_TAIL, "-vp_id", 0,        "chip ID to update", 0);
+    psMetadataAddStr(toscrubbedimfileArgs, PS_LIST_TAIL, "-class_id", 0,       "class ID to update", NULL);
+
+    // -exportrun
+    psMetadata *exportrunArgs = psMetadataAlloc();
+    // pxchipSetSearchArgs (exportrunArgs); XXX include search terms?
+    psMetadataAddS64(exportrunArgs, PS_LIST_TAIL, "-vp_id", 0,          "export this chip ID (required)", 0);
+    psMetadataAddStr(exportrunArgs, PS_LIST_TAIL, "-outfile", 0,          "export to this file (required)", NULL);
+    psMetadataAddBool(exportrunArgs, PS_LIST_TAIL, "-clean",  0,          "mark tables as cleaned", false);
+    psMetadataAddU64(exportrunArgs, PS_LIST_TAIL, "-limit",   0,          "limit result set to N items", 0);
+
+    // -importrun
+    psMetadata *importrunArgs = psMetadataAlloc();
+    psMetadataAddStr(importrunArgs, PS_LIST_TAIL, "-infile",  0,          "import from this file (required)", NULL);
+
+    // -runstate
+    psMetadata *runstateArgs = psMetadataAlloc();
+    psMetadataAddS64(runstateArgs, PS_LIST_TAIL, "-vp_id", 0,           "search by chip ID", 0);
+    psMetadataAddS64(runstateArgs, PS_LIST_TAIL, "-exp_id", 0,            "search by exposure tag", 0);
+    psMetadataAddStr(runstateArgs, PS_LIST_TAIL, "-exp_name", 0,          "search by exposure tag", 0);
+    psMetadataAddStr(runstateArgs, PS_LIST_TAIL,  "-label",  PS_META_DUPLICATE_OK, "search by warpRun label", NULL);
+    psMetadataAddBool(runstateArgs, PS_LIST_TAIL, "-no_magic",  0,        "magic is not necessary for result", false);
+
+    psMetadataAddU64(runstateArgs, PS_LIST_TAIL, "-limit",  0,            "limit result set to N items", 0);
+    psMetadataAddBool(runstateArgs, PS_LIST_TAIL, "-simple",  0,          "use the simple output format", false);
+#endif //notdef
+
+    psMetadata *argSets = psMetadataAlloc();
+    psMetadata *modes = psMetadataAlloc();
+
+    PXOPT_ADD_MODE("-definebyquery",        "create runs from raw exposures",       VPTOOL_MODE_DEFINEBYQUERY,        definebyqueryArgs);
+    PXOPT_ADD_MODE("-pendingrun",           "list runs ready to process",           VPTOOL_MODE_PENDINGRUN,           pendingrunArgs);
+    PXOPT_ADD_MODE("-updaterun",            "change vpRun properties",              VPTOOL_MODE_UPDATERUN,            updaterunArgs);
+    PXOPT_ADD_MODE("-revertrun","clear a faulted vpRun",                            VPTOOL_MODE_REVERTRUN, revertrunArgs);
+    PXOPT_ADD_MODE("-pendingimfile",        "list pending imfiles",                 VPTOOL_MODE_PENDINGIMFILE,        pendingimfileArgs);
+
+    PXOPT_ADD_MODE("-addprocessedcell",   "add a processed cell",               VPTOOL_MODE_ADDPROCESSEDCELL,   addprocessedcellArgs);
+    PXOPT_ADD_MODE("-processedcell",      "show processed cells",               VPTOOL_MODE_PROCESSEDCELL,      processedcellArgs);
+    PXOPT_ADD_MODE("-listrun",              "list vpRuns",                        VPTOOL_MODE_LISTRUN,              listrunArgs);
+
+    if (!pxGetOptions(stderr, argc, argv, config, modes, argSets)) {
+        psError(PS_ERR_UNKNOWN, false, "option parsing failed");
+        psFree(argSets);
+        psFree(modes);
+        psFree(config);
+        return NULL;
+    }
+
+    psFree(argSets);
+    psFree(modes);
+
+    // define Database handle, if used
+    config->dbh = psMemIncrRefCounter(pmConfigDB(config->modules));
+    if (!config->dbh) {
+        psError(PS_ERR_UNKNOWN, false, "Can't configure database");
+        psFree(config);
+        return NULL;
+    }
+
+    return config;
+}
Index: branches/meh_branches/ppstack_test/ippTools/src/warptool.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/warptool.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/warptool.c	(revision 33415)
@@ -975,4 +975,5 @@
     psFree(where);
 
+    psArray *allOutput = psArrayAllocEmpty(0);
     // Now loop over the labels and query each individually
     for (long i = 0; i < psArrayLength(labelOutput); i++) {
@@ -983,5 +984,5 @@
 
         psString query = psStringCopy(originalQuery);
-
+	psStringAppend(&query,"\nORDER BY warp_id");
         // treat limit == 0 as "no limit"
         psString limitString = psStringCopy("");
@@ -992,4 +993,5 @@
             psStringAppend(&query, "%s", limitString);
         }
+
         psString thisWhere = NULL;
         if (whereStr) {
@@ -1023,9 +1025,11 @@
         long outputLength = psArrayLength(output);
         if (outputLength) {
-            // negative simple so the default is true
-            if (!ippdbPrintMetadatas(stdout, output, "warpPendingSkyCell", !simple)) {
-                psError(PS_ERR_UNKNOWN, false, "failed to print array");
-                psFree(output);
-                return false;
+            for (int i = 0; i< outputLength; i++) {
+                psPtr ptr = output->data[i];
+                allOutput = psArrayAdd(allOutput, outputLength - i + 10, ptr);
+                if (!allOutput) {
+                    psError(PS_ERR_UNKNOWN, false, "failed to add element to array");
+                    return false;
+                }
             }
         } else {
@@ -1044,4 +1048,13 @@
         }
     }
+    long allLabelsLength = psArrayLength(allOutput);
+    if (allLabelsLength) {
+        if (!ippdbPrintMetadatas(stdout, allOutput, "warpPendingSkyCell", !simple)) {
+            psError(PS_ERR_UNKNOWN, false, "failed to print array");
+            psFree(allOutput);
+            return false;
+        }
+    }
+    psFree(allOutput);
     psFree(labelOutput);
     psFree(whereStr);
@@ -2039,8 +2052,12 @@
         // magicked is only an argument for for -tofullskyfile
         PXOPT_LOOKUP_S64(magicked, config->args, "-set_magicked", false, false);
+	PXOPT_LOOKUP_S16(quality, config->args, "-set_quality", false, false);
         if (magicked) {
             psStringAppend(&set_magicked_skyfile, "\n , warpSkyfile.magicked = %" PRId64, magicked);
             psStringAppend(&set_magicked_run, "\n,  warpRun.magicked = %" PRId64, magicked);
         }
+	if (quality) {
+	  psStringAppend(&set_magicked_skyfile, "\n , warpSkyfile.quality = %"PRId16, quality);
+	}
     } else if (!strcmp(data_state, "cleaned") || !strcmp(data_state, "purged")) {
         // if magicked is currently nonzero set it to -1
@@ -2421,4 +2438,6 @@
     PXOPT_COPY_S64(config->args, where, "-exp_id",     "rawExp.exp_id", "==");
     PXOPT_COPY_STR(config->args, where, "-exp_name",   "rawExp.exp_name", "==");
+    PXOPT_COPY_S64(config->args, where, "-chip_id",    "chipRun.chip_id", "==");
+    PXOPT_COPY_S64(config->args, where, "-cam_id",     "camRun.cam_id", "==");
     PXOPT_COPY_S64(config->args, where, "-fake_id",    "fakeRun.fake_id", "==");
     PXOPT_COPY_TIME(config->args, where, "-dateobs_begin", "rawExp.dateobs",  ">=");
@@ -2562,4 +2581,13 @@
     psFree(query);
 
-    return true;
-}
+    if (!skycell_id) {
+        // If we are updateing a whole warpRun set skycells with bad quality to 'full'
+        query = "UPDATE warpSkyfile SET data_state ='full', fault = 0 WHERE warp_id = %" PRId64 " AND quality != 0 AND (data_state != 'full')";
+        if (!p_psDBRunQueryF(config->dbh, query, warp_id)) {
+            psError(PS_ERR_UNKNOWN, false, "database error");
+            return false;
+        }
+    }
+
+    return true;
+}
Index: branches/meh_branches/ppstack_test/ippTools/src/warptoolConfig.c
===================================================================
--- branches/meh_branches/ppstack_test/ippTools/src/warptoolConfig.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippTools/src/warptoolConfig.c	(revision 33415)
@@ -269,7 +269,9 @@
     psMetadataAddStr(listrunArgs, PS_LIST_TAIL, "-tess_id",  0,          "search by tessellation ID", NULL);
     psMetadataAddStr(listrunArgs, PS_LIST_TAIL, "-state",  0,            "search by state", NULL);
-    psMetadataAddS64(listrunArgs, PS_LIST_TAIL, "-exp_id", 0,            "search by exposure tag", 0);
-    psMetadataAddStr(listrunArgs, PS_LIST_TAIL, "-exp_name", 0,          "search by exposure tag", 0);
-    psMetadataAddS64(listrunArgs, PS_LIST_TAIL, "-fake_id", 0,           "search by phase 3 version of exposure tag", 0);
+    psMetadataAddS64(listrunArgs, PS_LIST_TAIL, "-exp_id", 0,            "search by exposure id", 0);
+    psMetadataAddStr(listrunArgs, PS_LIST_TAIL, "-exp_name", 0,          "search by exposure tag", NULL);
+    psMetadataAddS64(listrunArgs, PS_LIST_TAIL, "-chip_id", 0,           "search by chip id", 0);
+    psMetadataAddS64(listrunArgs, PS_LIST_TAIL, "-cam_id",  0,           "search by cam id", 0);
+    psMetadataAddS64(listrunArgs, PS_LIST_TAIL, "-fake_id", 0,           "search by phase 3 version of exposure tag (fake_id)", 0);
     psMetadataAddTime(listrunArgs, PS_LIST_TAIL, "-dateobs_begin", 0,    "search for exposures by time (>=)", NULL);
     psMetadataAddTime(listrunArgs, PS_LIST_TAIL, "-dateobs_end", 0,      "search for exposures by time (<=)", NULL);
@@ -396,5 +398,6 @@
     psMetadataAddStr(tofullskyfileArgs, PS_LIST_TAIL, "-skycell_id", 0, "skycell ID to update", NULL);
     psMetadataAddS64(tofullskyfileArgs, PS_LIST_TAIL, "-set_magicked",  0, "new value for magicked", 0);
-
+    psMetadataAddS16(tofullskyfileArgs, PS_LIST_TAIL, "-set_quality", 0, "new value for quality", 0);
+    
     // -updateskyfile
     psMetadata *updateskyfileArgs = psMetadataAlloc();
Index: branches/meh_branches/ppstack_test/ippconfig/Makefile.am
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/Makefile.am	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/Makefile.am	(revision 33415)
@@ -18,4 +18,5 @@
 	lbc_red \
 	lulin \
+	ssp \
 	uh8k
 
Index: branches/meh_branches/ppstack_test/ippconfig/configure.ac
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/configure.ac	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/configure.ac	(revision 33415)
@@ -29,4 +29,5 @@
   lbc_red/Makefile
   lulin/Makefile
+  ssp/Makefile
   uh8k/Makefile
 ])
Index: branches/meh_branches/ppstack_test/ippconfig/gpc1/format_20090120.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/gpc1/format_20090120.config	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/gpc1/format_20090120.config	(revision 33415)
@@ -185,5 +185,5 @@
         FPA.ALT         STR     ALT
         FPA.AZ          STR     AZ
-        # FPA.TEMP        STR     DETTEM
+         FPA.TEMP        STR     DETTEM
         FPA.M1X         STR     M1X
         FPA.M1Y         STR     M1Y   
@@ -505,5 +505,5 @@
 DATABASE        METADATA
 	# this rule is fragile : does not match the camera
-        FPA.TEMP        STR     SELECT ccd_temp FROM rawExp WHERE dateobs >= '{FPA.DATE}' and abs(timediff(dateobs, cast('{FPA.TIME}' as datetime))) < 2
+#        FPA.TEMP        STR     SELECT ccd_temp FROM rawExp WHERE dateobs >= '{FPA.DATE}' and abs(timediff(dateobs, cast('{FPA.TIME}' as datetime))) < 2
 END
 
Index: branches/meh_branches/ppstack_test/ippconfig/gpc1/format_20090220.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/gpc1/format_20090220.config	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/gpc1/format_20090220.config	(revision 33415)
@@ -185,5 +185,5 @@
         FPA.ALT         STR     ALT
         FPA.AZ          STR     AZ
-        # FPA.TEMP        STR     DETTEM
+        FPA.TEMP        STR     DETTEM
         FPA.M1X         STR     M1X
         FPA.M1Y         STR     M1Y   
@@ -505,5 +505,5 @@
 DATABASE        METADATA
 	# this rule is fragile : does not match the camera
-        FPA.TEMP        STR     SELECT ccd_temp FROM rawExp WHERE dateobs >= '{FPA.DATE}' and abs(timediff(dateobs, cast('{FPA.TIME}' as datetime))) < 2
+#        FPA.TEMP        STR     SELECT ccd_temp FROM rawExp WHERE dateobs >= '{FPA.DATE}' and abs(timediff(dateobs, cast('{FPA.TIME}' as datetime))) < 2
 END
 
@@ -590,4 +590,6 @@
         XY76    STR     1111111111111111111111111111111111111111111111111111111111111111
 END
+PATTERN.CELL    BOOL    FALSE
+PATTERN.CONTINUITY BOOL TRUE
 PATTERN.CELL.SUBSET      METADATA        # List of chips and whether to do cell pattern correction  
         XY01    BOOL 	FALSE
Index: branches/meh_branches/ppstack_test/ippconfig/gpc1/format_20091209.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/gpc1/format_20091209.config	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/gpc1/format_20091209.config	(revision 33415)
@@ -185,5 +185,5 @@
         FPA.ALT         STR     ALT
         FPA.AZ          STR     AZ
-        # FPA.TEMP        STR     DETTEM
+        FPA.TEMP        STR     DETTEM
         FPA.M1X         STR     M1X
         FPA.M1Y         STR     M1Y   
@@ -505,5 +505,5 @@
 DATABASE        METADATA
 	# this rule is fragile : does not match the camera
-        FPA.TEMP        STR     SELECT ccd_temp FROM rawExp WHERE dateobs >= '{FPA.DATE}' and abs(timediff(dateobs, cast('{FPA.TIME}' as datetime))) < 2
+#        FPA.TEMP        STR     SELECT ccd_temp FROM rawExp WHERE dateobs >= '{FPA.DATE}' and abs(timediff(dateobs, cast('{FPA.TIME}' as datetime))) < 2
 END
 
@@ -525,7 +525,135 @@
 
 # PATTERN.ROW		BOOL 	TRUE	# Do any row pattern correction at all?
+# PATTERN.ROW.SUBSET	METADATA	# List of chips and whether to do row pattern correction
+# 	XY01	BOOL	FALSE
+#         XY02	STR     0000000000000000000000000000000000000000000000000000000000000001 # xy77
+# 	XY03	BOOL	FALSE
+# 	XY04	BOOL	FALSE
+# 	XY05	BOOL	FALSE
+# 	XY06	BOOL	FALSE
+# 	XY10	BOOL	FALSE
+# 	XY11	STR	0011100100000000000000000000000000000000000000000000000000000000
+# 	XY12	BOOL	FALSE
+# 	XY13	BOOL	FALSE
+#         XY14	STR     0000000000000000000100000000000000000000000000000000000000000000 # xy23
+# 	XY15	STR     1111111100000000000000000000000000000000000000000000000000000000 # cols: 0
+# 	XY16	BOOL	FALSE
+# 	XY17	BOOL	FALSE
+# 	XY20	BOOL	FALSE
+# 	XY21	BOOL	FALSE
+# 	XY22	BOOL	FALSE
+# 	XY23	BOOL	FALSE
+# 	XY24	BOOL	FALSE
+# 	XY25	BOOL	FALSE
+# 	XY26	BOOL	FALSE
+# 	XY27	STR     1111111111111111111111111111111100000000000000000000000011111111 # cols: 0,1,2,3,7
+# 	XY30	BOOL	FALSE
+# 	XY31	STR     0000000000000000000000000000000000000000000000000000000011111111 # cols: 7
+# 	XY32	STR     0000000000000000000000001111111100000000000000000000000011111111 # cols: 3,7
+# 	XY33	BOOL	FALSE
+# 	XY34	BOOL	FALSE
+# 	XY35	BOOL	FALSE
+# 	XY36	BOOL	FALSE
+# 	XY37	BOOL	FALSE
+# 	XY40	BOOL	FALSE
+# 	XY41	BOOL	FALSE
+# 	XY42	BOOL	FALSE
+# 	XY43	BOOL	FALSE
+# 	XY44	BOOL	FALSE
+# 	XY45	STR     0000000000000000000000001111111100000000000000000000000011111111 # cols: 3,7
+# 	XY46	BOOL	FALSE
+# 	XY47	STR     1111111100000000000000001111111100000000111111110000000011111111 # cols: 0,3,5,7
+# 	XY50	BOOL	FALSE
+# 	XY51	BOOL	FALSE
+# 	XY52	BOOL	FALSE
+# 	XY53	BOOL	FALSE
+# 	XY54	BOOL	FALSE
+# 	XY55	BOOL	FALSE
+# 	XY56	BOOL	FALSE
+# 	XY57	STR     1111111111111111111111110000000000000000000000001111111111111111 # cols: 0,1,2,6,7
+# 	XY60	STR	0000000000000000000000000000000000000000000001000000000000000000 # cell xy55
+# 	XY61	BOOL	FALSE
+# 	XY62	BOOL	FALSE
+# 	XY63	BOOL	FALSE
+# 	XY64	BOOL	FALSE
+# 	XY65	BOOL	FALSE
+# 	XY66	STR	0000000000000000000000000000000000000000000000000000000000000100 # cell xy75
+# 	XY67	BOOL	FALSE
+# 	XY71	BOOL	FALSE
+# 	XY72	BOOL	FALSE
+# 	XY73	BOOL	FALSE
+# 	XY74	STR     0000000000000000111111110000000000000000000000000000000011111111 # cols: 2,7
+# 	XY75	BOOL	FALSE
+# 	XY76	BOOL	FALSE
+# END
+
+# # PATTERN.CELL	BOOL 	TRUE		# by default, do not do any cell pattern correction
+# PATTERN.CELL.SUBSET	METADATA	# List of chips and whether to do cell pattern correction
+# 	XY01	BOOL	FALSE
+# 	XY02	BOOL	FALSE
+# 	XY03	BOOL	FALSE
+# 	XY04	BOOL	FALSE
+# 	XY05	BOOL	FALSE
+# 	XY06    STR     1101000000010000000110100100000000000000000000000000000000000000
+# 	XY10	BOOL	FALSE
+# 	XY11	BOOL	FALSE
+# 	XY12	BOOL	FALSE
+# 	XY13	BOOL	FALSE
+# 	XY14	STR     0000000000000000111111110000000000000000000000010000000000101101
+# 	XY15	STR     0011111100011000000001000000010000001001000111110001010000011111
+# 	XY16	STR     0000000100000000000000000000000000000001000000000000000000000000
+# 	XY17	BOOL	FALSE
+# 	XY20	BOOL	FALSE
+# 	XY21	BOOL	FALSE
+# 	XY22	BOOL	FALSE
+# 	XY23	BOOL	FALSE
+# 	XY24	STR     0000000000100000000000000110100000100000001000000011100000000000
+# 	XY25	STR     0100010000001001001011000000001000000111000110000000100000000010
+# 	XY26	STR     0000000000000100000000000000000000001000001000000000000000000000
+# 	XY27	BOOL	FALSE
+# 	XY30	BOOL	FALSE
+# 	XY31	BOOL	FALSE
+# 	XY32	BOOL	FALSE
+# 	XY33	BOOL	FALSE
+# 	XY34	STR     0000000000000000000000000000000000000001000011000000101000111111
+# 	XY35	BOOL	FALSE
+# 	XY36	STR     0000000000000000000000000000000000010000000000000000000000000000
+# 	XY37	STR     0000000000000000000000000000000100010001000000001011000010110110
+# 	XY40	STR     0000000000000000000000000010110000000000000001110000001000011111
+# 	XY41	BOOL	FALSE
+# 	XY42	BOOL	FALSE
+# 	XY43	STR     0000000000000000000000010000000100000000000010000100000100000100
+# 	XY44	BOOL	FALSE
+# 	XY45	BOOL	FALSE
+# 	XY46	BOOL	FALSE
+# 	XY47	BOOL	FALSE
+# 	XY50	BOOL	FALSE
+# 	XY51	BOOL	FALSE
+# 	XY52	BOOL	FALSE
+# 	XY53	BOOL	FALSE
+# 	XY54	BOOL	FALSE
+# 	XY55	BOOL	FALSE
+# 	XY56	BOOL	FALSE
+# 	XY57	BOOL	FALSE
+# 	XY60	BOOL	FALSE
+# 	XY61	BOOL	FALSE
+# 	XY62	STR     0000000000000000000000000000000000001000000000000000000100001010
+# 	XY63	BOOL	FALSE
+# 	XY64	BOOL	FALSE
+# 	XY65	BOOL	FALSE
+# 	XY66	BOOL	FALSE
+# 	XY67	BOOL	FALSE
+# 	XY71	BOOL	FALSE
+# 	XY72	STR     0000000000000000000000000000000000000000000000000000000000000001
+# 	XY73	STR     0001100000000000000000000000001000000000000000000000000000000000
+# 	XY74	BOOL	FALSE
+# 	XY75	BOOL	FALSE
+# 	XY76	BOOL	FALSE
+# END
+# PATTERN.ROW		BOOL 	TRUE	# Do any row pattern correction at all?
 PATTERN.ROW.SUBSET	METADATA	# List of chips and whether to do row pattern correction
 	XY01	BOOL	FALSE
-        XY02	STR     0000000000000000000000000000000000000000000000000000000000000001 # xy77
+#	XY02	STR     0000000000000000000000000000000000000000000000000000000000000001 # xy77
+	XY02	BOOL	FALSE
 	XY03	BOOL	FALSE
 	XY04	BOOL	FALSE
@@ -536,5 +664,5 @@
 	XY12	BOOL	FALSE
 	XY13	BOOL	FALSE
-        XY14	STR     0000000000000000000100000000000000000000000000000000000000000000 # xy23
+	XY14	STR     0000000000000000000100000000000000000000000000000000000000000000 # xy23
 	XY15	STR     1111111100000000000000000000000000000000000000000000000000000000 # cols: 0
 	XY16	BOOL	FALSE
@@ -578,5 +706,6 @@
 	XY64	BOOL	FALSE
 	XY65	BOOL	FALSE
-	XY66	STR	0000000000000000000000000000000000000000000000000000000000000100 # cell xy75
+#	XY66	STR	0000000000000000000000000000000000000000000000000000000000000100 # cell xy75
+	XY66	BOOL	FALSE
 	XY67	BOOL	FALSE
 	XY71	BOOL	FALSE
@@ -588,65 +717,82 @@
 END
 
-# PATTERN.CELL	BOOL 	TRUE		# by default, do not do any cell pattern correction
-PATTERN.CELL.SUBSET	METADATA	# List of chips and whether to do cell pattern correction
-	XY01	BOOL	FALSE
-	XY02	BOOL	FALSE
-	XY03	BOOL	FALSE
-	XY04	BOOL	FALSE
-	XY05	BOOL	FALSE
-	XY06    STR     1101000000010000000110100100000000000000000000000000000000000000
-	XY10	BOOL	FALSE
-	XY11	BOOL	FALSE
-	XY12	BOOL	FALSE
-	XY13	BOOL	FALSE
-	XY14	STR     0000000000000000111111110000000000000000000000010000000000101101
-	XY15	STR     0011111100011000000001000000010000001001000111110001010000011111
-	XY16	STR     0000000100000000000000000000000000000001000000000000000000000000
-	XY17	BOOL	FALSE
-	XY20	BOOL	FALSE
-	XY21	BOOL	FALSE
-	XY22	BOOL	FALSE
-	XY23	BOOL	FALSE
-	XY24	STR     0000000000100000000000000110100000100000001000000011100000000000
-	XY25	STR     0100010000001001001011000000001000000111000110000000100000000010
-	XY26	STR     0000000000000100000000000000000000001000001000000000000000000000
-	XY27	BOOL	FALSE
-	XY30	BOOL	FALSE
-	XY31	BOOL	FALSE
-	XY32	BOOL	FALSE
-	XY33	BOOL	FALSE
-	XY34	STR     0000000000000000000000000000000000000001000011000000101000111111
-	XY35	BOOL	FALSE
-	XY36	STR     0000000000000000000000000000000000010000000000000000000000000000
-	XY37	STR     0000000000000000000000000000000100010001000000001011000010110110
-	XY40	STR     0000000000000000000000000010110000000000000001110000001000011111
-	XY41	BOOL	FALSE
-	XY42	BOOL	FALSE
-	XY43	STR     0000000000000000000000010000000100000000000010000100000100000100
-	XY44	BOOL	FALSE
-	XY45	BOOL	FALSE
-	XY46	BOOL	FALSE
-	XY47	BOOL	FALSE
-	XY50	BOOL	FALSE
-	XY51	BOOL	FALSE
-	XY52	BOOL	FALSE
-	XY53	BOOL	FALSE
-	XY54	BOOL	FALSE
-	XY55	BOOL	FALSE
-	XY56	BOOL	FALSE
-	XY57	BOOL	FALSE
-	XY60	BOOL	FALSE
-	XY61	BOOL	FALSE
-	XY62	STR     0000000000000000000000000000000000001000000000000000000100001010
-	XY63	BOOL	FALSE
-	XY64	BOOL	FALSE
-	XY65	BOOL	FALSE
-	XY66	BOOL	FALSE
-	XY67	BOOL	FALSE
-	XY71	BOOL	FALSE
-	XY72	STR     0000000000000000000000000000000000000000000000000000000000000001
-	XY73	STR     0001100000000000000000000000001000000000000000000000000000000000
-	XY74	BOOL	FALSE
-	XY75	BOOL	FALSE
-	XY76	BOOL	FALSE
-END
+PATTERN.CELL    BOOL   FALSE
+PATTERN.CONTINUITY BOOL TRUE
+
+# # PATTERN.CELL	BOOL 	TRUE		# by default, do not do any cell pattern correction
+# PATTERN.CELL.SUBSET	METADATA	# List of chips and whether to do cell pattern correction
+# 	XY01	BOOL	FALSE
+# 	XY02	BOOL	FALSE
+# 	XY03	BOOL	FALSE
+# 	XY04	BOOL	FALSE
+# 	XY05	BOOL	FALSE
+# #	XY06    STR     0101000000010000000110000100000000000000000000000000000000000000
+# 	XY06	BOOL	FALSE
+# 	XY10	BOOL	FALSE
+# 	XY11	BOOL	FALSE
+# 	XY12	BOOL	FALSE
+# 	XY13	BOOL	FALSE
+# 	XY14	STR     0000000000000000111111110000000000000000000000000000000000000000
+# 	XY15	STR     1111111110000000100000000000000000000000000000000000000000000000
+# #	XY16	STR     0000000100000000000000000000000000000001000000000000000000000000
+# 	XY16	BOOL	FALSE
+# 	XY17	BOOL	FALSE
+# 	XY20	BOOL	FALSE
+# 	XY21	BOOL	FALSE
+# 	XY22	BOOL	FALSE
+# 	XY23	BOOL	FALSE
+# #	XY24	STR     0000000000100000000000000110100000100000001000000011100000000000
+# 	XY24	BOOL	FALSE
+# #	XY25	STR     0100010000001001001011000000001000000111000110000000100000000010
+# 	XY25	BOOL	FALSE
+# #	XY26	STR     0000000000000100000000000000000000001000001000000000000000000000
+# 	XY26	BOOL	FALSE
+# 	XY27	BOOL	FALSE
+# 	XY30	BOOL	FALSE
+# 	XY31	BOOL	FALSE
+# 	XY32	BOOL	FALSE
+# 	XY33	BOOL	FALSE
+# #	XY34	STR     0000000000000000000000000000000000000001000011000000101000111111
+# 	XY34	BOOL	FALSE
+# 	XY35	BOOL	FALSE
+# #	XY36	STR     0001000000000000000000000000000000010000000000000000000000000000
+# 	XY36	BOOL	FALSE
+# #	XY37	STR     0000000000000000000000000000000100010001000000001011000010110110 ?
+# 	XY37	BOOL	FALSE
+# #	XY40	STR     0000000000000000000000000010110000000000000001110000001000011111
+# 	XY40	BOOL	FALSE
+# 	XY41	BOOL	FALSE
+# #	XY42	BOOL	FALSE
+# 	XY42	STR	0000000000000000000100000001000000000000000000000000000000000000
+# #	XY43	STR     0000000000000000000000010000000100000000000010000100000100000100
+# 	XY43	STR 	0000000000000000000000000000000000000000000010000000000000000000
+# 	XY44	BOOL	FALSE
+# 	XY45	BOOL	FALSE
+# 	XY46	BOOL	FALSE
+# 	XY47	BOOL	FALSE
+# 	XY50	BOOL	FALSE
+# 	XY51	BOOL	FALSE
+# 	XY52	BOOL	FALSE
+# 	XY53	BOOL	FALSE
+# 	XY54	BOOL	FALSE
+# 	XY55	BOOL	FALSE
+# 	XY56	BOOL	FALSE
+# 	XY57	BOOL	FALSE
+# 	XY60	BOOL	FALSE
+# 	XY61	BOOL	FALSE
+# #	XY62	STR     0000000000000000000000000000000000000000000000000000000000000010
+# 	XY62	BOOL	FALSE
+# 	XY63	BOOL	FALSE
+# 	XY64	BOOL	FALSE
+# 	XY65	BOOL	FALSE
+# 	XY66	BOOL	FALSE
+# 	XY67	BOOL	FALSE
+# 	XY71	BOOL	FALSE
+# #	XY72	STR     0000000000000000000000000000000000000000000000000000000000000001
+# 	XY72	BOOL	FALSE
+# #	XY73	STR     0001100000000000000000000000001000000000000000000000000000000000
+# 	XY73	BOOL	FALSE
+# 	XY74	BOOL	FALSE
+# 	XY75	BOOL	FALSE
+# 	XY76	BOOL	FALSE
+# END
Index: branches/meh_branches/ppstack_test/ippconfig/gpc1/format_20100122.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/gpc1/format_20100122.config	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/gpc1/format_20100122.config	(revision 33415)
@@ -185,5 +185,5 @@
         FPA.ALT         STR     ALT
         FPA.AZ          STR     AZ
-        # FPA.TEMP        STR     DETTEM
+        FPA.TEMP        STR     DETTEM
         FPA.M1X         STR     M1X
         FPA.M1Y         STR     M1Y   
@@ -506,5 +506,5 @@
 DATABASE        METADATA
 	# this rule is fragile : does not match the camera
-        FPA.TEMP        STR     SELECT ccd_temp FROM rawExp WHERE dateobs >= '{FPA.DATE}' and abs(timediff(dateobs, cast('{FPA.TIME}' as datetime))) < 2
+#        FPA.TEMP        STR     SELECT ccd_temp FROM rawExp WHERE dateobs >= '{FPA.DATE}' and abs(timediff(dateobs, cast('{FPA.TIME}' as datetime))) < 2
 END
 
@@ -527,8 +527,136 @@
 # Pattern correction information:
 
+# # PATTERN.ROW		BOOL 	TRUE	# Do any row pattern correction at all?
+# PATTERN.ROW.SUBSET	METADATA	# List of chips and whether to do row pattern correction
+# 	XY01	BOOL	FALSE
+# 	XY02	STR     0000000000000000000000000000000000000000000000000000000000000001 # xy77
+# 	XY03	BOOL	FALSE
+# 	XY04	BOOL	FALSE
+# 	XY05	BOOL	FALSE
+# 	XY06	BOOL	FALSE
+# 	XY10	BOOL	FALSE
+# 	XY11	STR	0011100100000000000000000000000000000000000000000000000000000000
+# 	XY12	BOOL	FALSE
+# 	XY13	BOOL	FALSE
+# 	XY14	STR     0000000000000000000100000000000000000000000000000000000000000000 # xy23
+# 	XY15	STR     1111111100000000000000000000000000000000000000000000000000000000 # cols: 0
+# 	XY16	BOOL	FALSE
+# 	XY17	BOOL	FALSE
+# 	XY20	BOOL	FALSE
+# 	XY21	BOOL	FALSE
+# 	XY22	BOOL	FALSE
+# 	XY23	BOOL	FALSE
+# 	XY24	BOOL	FALSE
+# 	XY25	BOOL	FALSE
+# 	XY26	BOOL	FALSE
+# 	XY27	STR     1111111111111111111111111111111100000000000000000000000011111111 # cols: 0,1,2,3,7
+# 	XY30	BOOL	FALSE
+# 	XY31	STR     0000000000000000000000000000000000000000000000000000000011111111 # cols: 7
+# 	XY32	STR     0000000000000000000000001111111100000000000000000000000011111111 # cols: 3,7
+# 	XY33	BOOL	FALSE
+# 	XY34	BOOL	FALSE
+# 	XY35	BOOL	FALSE
+# 	XY36	BOOL	FALSE
+# 	XY37	BOOL	FALSE
+# 	XY40	BOOL	FALSE
+# 	XY41	BOOL	FALSE
+# 	XY42	BOOL	FALSE
+# 	XY43	BOOL	FALSE
+# 	XY44	BOOL	FALSE
+# 	XY45	STR     0000000000000000000000001111111100000000000000000000000011111111 # cols: 3,7
+# 	XY46	BOOL	FALSE
+# 	XY47	STR     1111111100000000000000001111111100000000111111110000000011111111 # cols: 0,3,5,7
+# 	XY50	BOOL	FALSE
+# 	XY51	BOOL	FALSE
+# 	XY52	BOOL	FALSE
+# 	XY53	BOOL	FALSE
+# 	XY54	BOOL	FALSE
+# 	XY55	BOOL	FALSE
+# 	XY56	BOOL	FALSE
+# 	XY57	STR     1111111111111111111111110000000000000000000000001111111111111111 # cols: 0,1,2,6,7
+# 	XY60	STR	0000000000000000000000000000000000000000000001000000000000000000 # cell xy55
+# 	XY61	BOOL	FALSE
+# 	XY62	BOOL	FALSE
+# 	XY63	BOOL	FALSE
+# 	XY64	BOOL	FALSE
+# 	XY65	BOOL	FALSE
+# 	XY66	STR	0000000000000000000000000000000000000000000000000000000000000100 # cell xy75
+# 	XY67	BOOL	FALSE
+# 	XY71	BOOL	FALSE
+# 	XY72	BOOL	FALSE
+# 	XY73	BOOL	FALSE
+# 	XY74	STR     0000000000000000111111110000000000000000000000000000000011111111 # cols: 2,7
+# 	XY75	BOOL	FALSE
+# 	XY76	BOOL	FALSE
+# END
+
+# # PATTERN.CELL	BOOL 	TRUE		# by default, do not do any cell pattern correction
+# PATTERN.CELL.SUBSET	METADATA	# List of chips and whether to do cell pattern correction
+# 	XY01	BOOL	FALSE
+# 	XY02	BOOL	FALSE
+# 	XY03	BOOL	FALSE
+# 	XY04	BOOL	FALSE
+# 	XY05	BOOL	FALSE
+# 	XY06    STR     1101000000010000000110100100000000000000000000000000000000000000
+# 	XY10	BOOL	FALSE
+# 	XY11	BOOL	FALSE
+# 	XY12	BOOL	FALSE
+# 	XY13	BOOL	FALSE
+# 	XY14	STR     0000000000000000111111110000000000000000000000010000000000101101
+# 	XY15	STR     0011111100011000000001000000010000001001000111110001010000011111
+# 	XY16	STR     0000000100000000000000000000000000000001000000000000000000000000
+# 	XY17	BOOL	FALSE
+# 	XY20	BOOL	FALSE
+# 	XY21	BOOL	FALSE
+# 	XY22	BOOL	FALSE
+# 	XY23	BOOL	FALSE
+# 	XY24	STR     0000000000100000000000000110100000100000001000000011100000000000
+# 	XY25	STR     0100010000001001001011000000001000000111000110000000100000000010
+# 	XY26	STR     0000000000000100000000000000000000001000001000000000000000000000
+# 	XY27	BOOL	FALSE
+# 	XY30	BOOL	FALSE
+# 	XY31	BOOL	FALSE
+# 	XY32	BOOL	FALSE
+# 	XY33	BOOL	FALSE
+# 	XY34	STR     0000000000000000000000000000000000000001000011000000101000111111
+# 	XY35	BOOL	FALSE
+# 	XY36	STR     0000000000000000000000000000000000010000000000000000000000000000
+# 	XY37	STR     0000000000000000000000000000000100010001000000001011000010110110
+# 	XY40	STR     0000000000000000000000000010110000000000000001110000001000011111
+# 	XY41	BOOL	FALSE
+# 	XY42	BOOL	FALSE
+# 	XY43	STR     0000000000000000000000010000000100000000000010000100000100000100
+# 	XY44	BOOL	FALSE
+# 	XY45	BOOL	FALSE
+# 	XY46	BOOL	FALSE
+# 	XY47	BOOL	FALSE
+# 	XY50	BOOL	FALSE
+# 	XY51	BOOL	FALSE
+# 	XY52	BOOL	FALSE
+# 	XY53	BOOL	FALSE
+# 	XY54	BOOL	FALSE
+# 	XY55	BOOL	FALSE
+# 	XY56	BOOL	FALSE
+# 	XY57	BOOL	FALSE
+# 	XY60	BOOL	FALSE
+# 	XY61	BOOL	FALSE
+# 	XY62	STR     0000000000000000000000000000000000001000000000000000000100001010
+# 	XY63	BOOL	FALSE
+# 	XY64	BOOL	FALSE
+# 	XY65	BOOL	FALSE
+# 	XY66	BOOL	FALSE
+# 	XY67	BOOL	FALSE
+# 	XY71	BOOL	FALSE
+# 	XY72	STR     0000000000000000000000000000000000000000000000000000000000000001
+# 	XY73	STR     0001100000000000000000000000001000000000000000000000000000000000
+# 	XY74	BOOL	FALSE
+# 	XY75	BOOL	FALSE
+# 	XY76	BOOL	FALSE
+# END
 # PATTERN.ROW		BOOL 	TRUE	# Do any row pattern correction at all?
 PATTERN.ROW.SUBSET	METADATA	# List of chips and whether to do row pattern correction
 	XY01	BOOL	FALSE
-	XY02	STR     0000000000000000000000000000000000000000000000000000000000000001 # xy77
+#	XY02	STR     0000000000000000000000000000000000000000000000000000000000000001 # xy77
+	XY02	BOOL	FALSE
 	XY03	BOOL	FALSE
 	XY04	BOOL	FALSE
@@ -581,5 +709,6 @@
 	XY64	BOOL	FALSE
 	XY65	BOOL	FALSE
-	XY66	STR	0000000000000000000000000000000000000000000000000000000000000100 # cell xy75
+#	XY66	STR	0000000000000000000000000000000000000000000000000000000000000100 # cell xy75
+	XY66	BOOL	FALSE
 	XY67	BOOL	FALSE
 	XY71	BOOL	FALSE
@@ -591,65 +720,82 @@
 END
 
-# PATTERN.CELL	BOOL 	TRUE		# by default, do not do any cell pattern correction
-PATTERN.CELL.SUBSET	METADATA	# List of chips and whether to do cell pattern correction
-	XY01	BOOL	FALSE
-	XY02	BOOL	FALSE
-	XY03	BOOL	FALSE
-	XY04	BOOL	FALSE
-	XY05	BOOL	FALSE
-	XY06    STR     1101000000010000000110100100000000000000000000000000000000000000
-	XY10	BOOL	FALSE
-	XY11	BOOL	FALSE
-	XY12	BOOL	FALSE
-	XY13	BOOL	FALSE
-	XY14	STR     0000000000000000111111110000000000000000000000010000000000101101
-	XY15	STR     0011111100011000000001000000010000001001000111110001010000011111
-	XY16	STR     0000000100000000000000000000000000000001000000000000000000000000
-	XY17	BOOL	FALSE
-	XY20	BOOL	FALSE
-	XY21	BOOL	FALSE
-	XY22	BOOL	FALSE
-	XY23	BOOL	FALSE
-	XY24	STR     0000000000100000000000000110100000100000001000000011100000000000
-	XY25	STR     0100010000001001001011000000001000000111000110000000100000000010
-	XY26	STR     0000000000000100000000000000000000001000001000000000000000000000
-	XY27	BOOL	FALSE
-	XY30	BOOL	FALSE
-	XY31	BOOL	FALSE
-	XY32	BOOL	FALSE
-	XY33	BOOL	FALSE
-	XY34	STR     0000000000000000000000000000000000000001000011000000101000111111
-	XY35	BOOL	FALSE
-	XY36	STR     0000000000000000000000000000000000010000000000000000000000000000
-	XY37	STR     0000000000000000000000000000000100010001000000001011000010110110
-	XY40	STR     0000000000000000000000000010110000000000000001110000001000011111
-	XY41	BOOL	FALSE
-	XY42	BOOL	FALSE
-	XY43	STR     0000000000000000000000010000000100000000000010000100000100000100
-	XY44	BOOL	FALSE
-	XY45	BOOL	FALSE
-	XY46	BOOL	FALSE
-	XY47	BOOL	FALSE
-	XY50	BOOL	FALSE
-	XY51	BOOL	FALSE
-	XY52	BOOL	FALSE
-	XY53	BOOL	FALSE
-	XY54	BOOL	FALSE
-	XY55	BOOL	FALSE
-	XY56	BOOL	FALSE
-	XY57	BOOL	FALSE
-	XY60	BOOL	FALSE
-	XY61	BOOL	FALSE
-	XY62	STR     0000000000000000000000000000000000001000000000000000000100001010
-	XY63	BOOL	FALSE
-	XY64	BOOL	FALSE
-	XY65	BOOL	FALSE
-	XY66	BOOL	FALSE
-	XY67	BOOL	FALSE
-	XY71	BOOL	FALSE
-	XY72	STR     0000000000000000000000000000000000000000000000000000000000000001
-	XY73	STR     0001100000000000000000000000001000000000000000000000000000000000
-	XY74	BOOL	FALSE
-	XY75	BOOL	FALSE
-	XY76	BOOL	FALSE
-END
+PATTERN.CELL BOOL FALSE
+PATTERN.CONTINUITY BOOL TRUE
+
+# # PATTERN.CELL	BOOL 	TRUE		# by default, do not do any cell pattern correction
+# PATTERN.CELL.SUBSET	METADATA	# List of chips and whether to do cell pattern correction
+# 	XY01	BOOL	FALSE
+# 	XY02	BOOL	FALSE
+# 	XY03	BOOL	FALSE
+# 	XY04	BOOL	FALSE
+# 	XY05	BOOL	FALSE
+# #	XY06    STR     0101000000010000000110000100000000000000000000000000000000000000
+# 	XY06	BOOL	FALSE
+# 	XY10	BOOL	FALSE
+# 	XY11	BOOL	FALSE
+# 	XY12	BOOL	FALSE
+# 	XY13	BOOL	FALSE
+# 	XY14	STR     0000000000000000111111110000000000000000000000000000000000000000
+# 	XY15	STR     1111111110000000100000000000000000000000000000000000000000000000
+# #	XY16	STR     0000000100000000000000000000000000000001000000000000000000000000
+# 	XY16	BOOL	FALSE
+# 	XY17	BOOL	FALSE
+# 	XY20	BOOL	FALSE
+# 	XY21	BOOL	FALSE
+# 	XY22	BOOL	FALSE
+# 	XY23	BOOL	FALSE
+# #	XY24	STR     0000000000100000000000000110100000100000001000000011100000000000
+# 	XY24	BOOL	FALSE
+# #	XY25	STR     0100010000001001001011000000001000000111000110000000100000000010
+# 	XY25	BOOL	FALSE
+# #	XY26	STR     0000000000000100000000000000000000001000001000000000000000000000
+# 	XY26	BOOL	FALSE
+# 	XY27	BOOL	FALSE
+# 	XY30	BOOL	FALSE
+# 	XY31	BOOL	FALSE
+# 	XY32	BOOL	FALSE
+# 	XY33	BOOL	FALSE
+# #	XY34	STR     0000000000000000000000000000000000000001000011000000101000111111
+# 	XY34	BOOL	FALSE
+# 	XY35	BOOL	FALSE
+# #	XY36	STR     0001000000000000000000000000000000010000000000000000000000000000
+# 	XY36	BOOL	FALSE
+# #	XY37	STR     0000000000000000000000000000000100010001000000001011000010110110 ?
+# 	XY37	BOOL	FALSE
+# #	XY40	STR     0000000000000000000000000010110000000000000001110000001000011111
+# 	XY40	BOOL	FALSE
+# 	XY41	BOOL	FALSE
+# #	XY42	BOOL	FALSE
+# 	XY42	STR	0000000000000000000100000001000000000000000000000000000000000000
+# #	XY43	STR     0000000000000000000000010000000100000000000010000100000100000100
+# 	XY43	STR 	0000000000000000000000000000000000000000000010000000000000000000
+# 	XY44	BOOL	FALSE
+# 	XY45	BOOL	FALSE
+# 	XY46	BOOL	FALSE
+# 	XY47	BOOL	FALSE
+# 	XY50	BOOL	FALSE
+# 	XY51	BOOL	FALSE
+# 	XY52	BOOL	FALSE
+# 	XY53	BOOL	FALSE
+# 	XY54	BOOL	FALSE
+# 	XY55	BOOL	FALSE
+# 	XY56	BOOL	FALSE
+# 	XY57	BOOL	FALSE
+# 	XY60	BOOL	FALSE
+# 	XY61	BOOL	FALSE
+# #	XY62	STR     0000000000000000000000000000000000000000000000000000000000000010
+# 	XY62	BOOL	FALSE
+# 	XY63	BOOL	FALSE
+# 	XY64	BOOL	FALSE
+# 	XY65	BOOL	FALSE
+# 	XY66	BOOL	FALSE
+# 	XY67	BOOL	FALSE
+# 	XY71	BOOL	FALSE
+# #	XY72	STR     0000000000000000000000000000000000000000000000000000000000000001
+# 	XY72	BOOL	FALSE
+# #	XY73	STR     0001100000000000000000000000001000000000000000000000000000000000
+# 	XY73	BOOL	FALSE
+# 	XY74	BOOL	FALSE
+# 	XY75	BOOL	FALSE
+# 	XY76	BOOL	FALSE
+# END
Index: branches/meh_branches/ppstack_test/ippconfig/gpc1/format_20100228.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/gpc1/format_20100228.config	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/gpc1/format_20100228.config	(revision 33415)
@@ -185,5 +185,5 @@
         FPA.ALT         STR     ALT
         FPA.AZ          STR     AZ
-        # FPA.TEMP        STR     DETTEM
+         FPA.TEMP        STR     DETTEM
         FPA.M1X         STR     M1X
         FPA.M1Y         STR     M1Y   
@@ -506,5 +506,5 @@
 DATABASE        METADATA
 	# this rule is fragile : does not match the camera
-        FPA.TEMP        STR     SELECT ccd_temp FROM rawExp WHERE dateobs >= '{FPA.DATE}' and abs(timediff(dateobs, cast('{FPA.TIME}' as datetime))) < 2
+#        FPA.TEMP        STR     SELECT ccd_temp FROM rawExp WHERE dateobs >= '{FPA.DATE}' and abs(timediff(dateobs, cast('{FPA.TIME}' as datetime))) < 2
 END
 
@@ -526,8 +526,136 @@
 # Pattern correction information:
 
+# # PATTERN.ROW		BOOL 	TRUE	# Do any row pattern correction at all?
+# PATTERN.ROW.SUBSET	METADATA	# List of chips and whether to do row pattern correction
+# 	XY01	BOOL	FALSE
+# 	XY02	STR     0000000000000000000000000000000000000000000000000000000000000001 # xy77
+# 	XY03	BOOL	FALSE
+# 	XY04	BOOL	FALSE
+# 	XY05	BOOL	FALSE
+# 	XY06	BOOL	FALSE
+# 	XY10	BOOL	FALSE
+# 	XY11	STR	0011100100000000000000000000000000000000000000000000000000000000
+# 	XY12	BOOL	FALSE
+# 	XY13	BOOL	FALSE
+# 	XY14	STR     0000000000000000000100000000000000000000000000000000000000000000 # xy23
+# 	XY15	STR     1111111100000000000000000000000000000000000000000000000000000000 # cols: 0
+# 	XY16	BOOL	FALSE
+# 	XY17	BOOL	FALSE
+# 	XY20	BOOL	FALSE
+# 	XY21	BOOL	FALSE
+# 	XY22	BOOL	FALSE
+# 	XY23	BOOL	FALSE
+# 	XY24	BOOL	FALSE
+# 	XY25	BOOL	FALSE
+# 	XY26	BOOL	FALSE
+# 	XY27	STR     1111111111111111111111111111111100000000000000000000000011111111 # cols: 0,1,2,3,7
+# 	XY30	BOOL	FALSE
+# 	XY31	STR     0000000000000000000000000000000000000000000000000000000011111111 # cols: 7
+# 	XY32	STR     0000000000000000000000001111111100000000000000000000000011111111 # cols: 3,7
+# 	XY33	BOOL	FALSE
+# 	XY34	BOOL	FALSE
+# 	XY35	BOOL	FALSE
+# 	XY36	BOOL	FALSE
+# 	XY37	BOOL	FALSE
+# 	XY40	BOOL	FALSE
+# 	XY41	BOOL	FALSE
+# 	XY42	BOOL	FALSE
+# 	XY43	BOOL	FALSE
+# 	XY44	BOOL	FALSE
+# 	XY45	STR     0000000000000000000000001111111100000000000000000000000011111111 # cols: 3,7
+# 	XY46	BOOL	FALSE
+# 	XY47	STR     1111111100000000000000001111111100000000111111110000000011111111 # cols: 0,3,5,7
+# 	XY50	BOOL	FALSE
+# 	XY51	BOOL	FALSE
+# 	XY52	BOOL	FALSE
+# 	XY53	BOOL	FALSE
+# 	XY54	BOOL	FALSE
+# 	XY55	BOOL	FALSE
+# 	XY56	BOOL	FALSE
+# 	XY57	STR     1111111111111111111111110000000000000000000000001111111111111111 # cols: 0,1,2,6,7
+# 	XY60	STR	0000000000000000000000000000000000000000000001000000000000000000 # cell xy55
+# 	XY61	BOOL	FALSE
+# 	XY62	BOOL	FALSE
+# 	XY63	BOOL	FALSE
+# 	XY64	BOOL	FALSE
+# 	XY65	BOOL	FALSE
+# 	XY66	STR	0000000000000000000000000000000000000000000000000000000000000100 # cell xy75
+# 	XY67	BOOL	FALSE
+# 	XY71	BOOL	FALSE
+# 	XY72	BOOL	FALSE
+# 	XY73	BOOL	FALSE
+# 	XY74	STR     0000000000000000111111110000000000000000000000000000000011111111 # cols: 2,7
+# 	XY75	BOOL	FALSE
+# 	XY76	BOOL	FALSE
+# END
+
+# # PATTERN.CELL	BOOL 	TRUE		# by default, do not do any cell pattern correction
+# PATTERN.CELL.SUBSET	METADATA	# List of chips and whether to do cell pattern correction
+# 	XY01	BOOL	FALSE
+# 	XY02	BOOL	FALSE
+# 	XY03	BOOL	FALSE
+# 	XY04	BOOL	FALSE
+# 	XY05	BOOL	FALSE
+# 	XY06    STR     1101000000010000000110100100000000000000000000000000000000000000
+# 	XY10	BOOL	FALSE
+# 	XY11	BOOL	FALSE
+# 	XY12	BOOL	FALSE
+# 	XY13	BOOL	FALSE
+# 	XY14	STR     0000000000000000111111110000000000000000000000010000000000101101
+# 	XY15	STR     0011111100011000000001000000010000001001000111110001010000011111
+# 	XY16	STR     0000000100000000000000000000000000000001000000000000000000000000
+# 	XY17	BOOL	FALSE
+# 	XY20	BOOL	FALSE
+# 	XY21	BOOL	FALSE
+# 	XY22	BOOL	FALSE
+# 	XY23	BOOL	FALSE
+# 	XY24	STR     0000000000100000000000000110100000100000001000000011100000000000
+# 	XY25	STR     0100010000001001001011000000001000000111000110000000100000000010
+# 	XY26	STR     0000000000000100000000000000000000001000001000000000000000000000
+# 	XY27	BOOL	FALSE
+# 	XY30	BOOL	FALSE
+# 	XY31	BOOL	FALSE
+# 	XY32	BOOL	FALSE
+# 	XY33	BOOL	FALSE
+# 	XY34	STR     0000000000000000000000000000000000000001000011000000101000111111
+# 	XY35	BOOL	FALSE
+# 	XY36	STR     0000000000000000000000000000000000010000000000000000000000000000
+# 	XY37	STR     0000000000000000000000000000000100010001000000001011000010110110
+# 	XY40	STR     0000000000000000000000000010110000000000000001110000001000011111
+# 	XY41	BOOL	FALSE
+# 	XY42	BOOL	FALSE
+# 	XY43	STR     0000000000000000000000010000000100000000000010000100000100000100
+# 	XY44	BOOL	FALSE
+# 	XY45	BOOL	FALSE
+# 	XY46	BOOL	FALSE
+# 	XY47	BOOL	FALSE
+# 	XY50	BOOL	FALSE
+# 	XY51	BOOL	FALSE
+# 	XY52	BOOL	FALSE
+# 	XY53	BOOL	FALSE
+# 	XY54	BOOL	FALSE
+# 	XY55	BOOL	FALSE
+# 	XY56	BOOL	FALSE
+# 	XY57	BOOL	FALSE
+# 	XY60	BOOL	FALSE
+# 	XY61	BOOL	FALSE
+# 	XY62	STR     0000000000000000000000000000000000001000000000000000000100001010
+# 	XY63	BOOL	FALSE
+# 	XY64	BOOL	FALSE
+# 	XY65	BOOL	FALSE
+# 	XY66	BOOL	FALSE
+# 	XY67	BOOL	FALSE
+# 	XY71	BOOL	FALSE
+# 	XY72	STR     0000000000000000000000000000000000000000000000000000000000000001
+# 	XY73	STR     0001100000000000000000000000001000000000000000000000000000000000
+# 	XY74	BOOL	FALSE
+# 	XY75	BOOL	FALSE
+# 	XY76	BOOL	FALSE
+# END
 # PATTERN.ROW		BOOL 	TRUE	# Do any row pattern correction at all?
 PATTERN.ROW.SUBSET	METADATA	# List of chips and whether to do row pattern correction
 	XY01	BOOL	FALSE
-	XY02	STR     0000000000000000000000000000000000000000000000000000000000000001 # xy77
+#	XY02	STR     0000000000000000000000000000000000000000000000000000000000000001 # xy77
+	XY02	BOOL	FALSE
 	XY03	BOOL	FALSE
 	XY04	BOOL	FALSE
@@ -580,5 +708,6 @@
 	XY64	BOOL	FALSE
 	XY65	BOOL	FALSE
-	XY66	STR	0000000000000000000000000000000000000000000000000000000000000100 # cell xy75
+#	XY66	STR	0000000000000000000000000000000000000000000000000000000000000100 # cell xy75
+	XY66	BOOL	FALSE
 	XY67	BOOL	FALSE
 	XY71	BOOL	FALSE
@@ -590,65 +719,82 @@
 END
 
+PATTERN.CELL BOOL FALSE
+PATTERN.CONTINUITY BOOL TRUE
+
 # PATTERN.CELL	BOOL 	TRUE		# by default, do not do any cell pattern correction
-PATTERN.CELL.SUBSET	METADATA	# List of chips and whether to do cell pattern correction
-	XY01	BOOL	FALSE
-	XY02	BOOL	FALSE
-	XY03	BOOL	FALSE
-	XY04	BOOL	FALSE
-	XY05	BOOL	FALSE
-	XY06    STR     1101000000010000000110100100000000000000000000000000000000000000
-	XY10	BOOL	FALSE
-	XY11	BOOL	FALSE
-	XY12	BOOL	FALSE
-	XY13	BOOL	FALSE
-	XY14	STR     0000000000000000111111110000000000000000000000010000000000101101
-	XY15	STR     0011111100011000000001000000010000001001000111110001010000011111
-	XY16	STR     0000000100000000000000000000000000000001000000000000000000000000
-	XY17	BOOL	FALSE
-	XY20	BOOL	FALSE
-	XY21	BOOL	FALSE
-	XY22	BOOL	FALSE
-	XY23	BOOL	FALSE
-	XY24	STR     0000000000100000000000000110100000100000001000000011100000000000
-	XY25	STR     0100010000001001001011000000001000000111000110000000100000000010
-	XY26	STR     0000000000000100000000000000000000001000001000000000000000000000
-	XY27	BOOL	FALSE
-	XY30	BOOL	FALSE
-	XY31	BOOL	FALSE
-	XY32	BOOL	FALSE
-	XY33	BOOL	FALSE
-	XY34	STR     0000000000000000000000000000000000000001000011000000101000111111
-	XY35	BOOL	FALSE
-	XY36	STR     0000000000000000000000000000000000010000000000000000000000000000
-	XY37	STR     0000000000000000000000000000000100010001000000001011000010110110
-	XY40	STR     0000000000000000000000000010110000000000000001110000001000011111
-	XY41	BOOL	FALSE
-	XY42	BOOL	FALSE
-	XY43	STR     0000000000000000000000010000000100000000000010000100000100000100
-	XY44	BOOL	FALSE
-	XY45	BOOL	FALSE
-	XY46	BOOL	FALSE
-	XY47	BOOL	FALSE
-	XY50	BOOL	FALSE
-	XY51	BOOL	FALSE
-	XY52	BOOL	FALSE
-	XY53	BOOL	FALSE
-	XY54	BOOL	FALSE
-	XY55	BOOL	FALSE
-	XY56	BOOL	FALSE
-	XY57	BOOL	FALSE
-	XY60	BOOL	FALSE
-	XY61	BOOL	FALSE
-	XY62	STR     0000000000000000000000000000000000001000000000000000000100001010
-	XY63	BOOL	FALSE
-	XY64	BOOL	FALSE
-	XY65	BOOL	FALSE
-	XY66	BOOL	FALSE
-	XY67	BOOL	FALSE
-	XY71	BOOL	FALSE
-	XY72	STR     0000000000000000000000000000000000000000000000000000000000000001
-	XY73	STR     0001100000000000000000000000001000000000000000000000000000000000
-	XY74	BOOL	FALSE
-	XY75	BOOL	FALSE
-	XY76	BOOL	FALSE
-END
+# PATTERN.CELL.SUBSET	METADATA	# List of chips and whether to do cell pattern correction
+# 	XY01	BOOL	FALSE
+# 	XY02	BOOL	FALSE
+# 	XY03	BOOL	FALSE
+# 	XY04	BOOL	FALSE
+# 	XY05	BOOL	FALSE
+# #	XY06    STR     0101000000010000000110000100000000000000000000000000000000000000
+# 	XY06	BOOL	FALSE
+# 	XY10	BOOL	FALSE
+# 	XY11	BOOL	FALSE
+# 	XY12	BOOL	FALSE
+# 	XY13	BOOL	FALSE
+# 	XY14	STR     0000000000000000111111110000000000000000000000000000000000000000
+# 	XY15	STR     1111111110000000100000000000000000000000000000000000000000000000
+# #	XY16	STR     0000000100000000000000000000000000000001000000000000000000000000
+# 	XY16	BOOL	FALSE
+# 	XY17	BOOL	FALSE
+# 	XY20	BOOL	FALSE
+# 	XY21	BOOL	FALSE
+# 	XY22	BOOL	FALSE
+# 	XY23	BOOL	FALSE
+# #	XY24	STR     0000000000100000000000000110100000100000001000000011100000000000
+# 	XY24	BOOL	FALSE
+# #	XY25	STR     0100010000001001001011000000001000000111000110000000100000000010
+# 	XY25	BOOL	FALSE
+# #	XY26	STR     0000000000000100000000000000000000001000001000000000000000000000
+# 	XY26	BOOL	FALSE
+# 	XY27	BOOL	FALSE
+# 	XY30	BOOL	FALSE
+# 	XY31	BOOL	FALSE
+# 	XY32	BOOL	FALSE
+# 	XY33	BOOL	FALSE
+# #	XY34	STR     0000000000000000000000000000000000000001000011000000101000111111
+# 	XY34	BOOL	FALSE
+# 	XY35	BOOL	FALSE
+# #	XY36	STR     0001000000000000000000000000000000010000000000000000000000000000
+# 	XY36	BOOL	FALSE
+# #	XY37	STR     0000000000000000000000000000000100010001000000001011000010110110 ?
+# 	XY37	BOOL	FALSE
+# #	XY40	STR     0000000000000000000000000010110000000000000001110000001000011111
+# 	XY40	BOOL	FALSE
+# 	XY41	BOOL	FALSE
+# #	XY42	BOOL	FALSE
+# 	XY42	STR	0000000000000000000100000001000000000000000000000000000000000000
+# #	XY43	STR     0000000000000000000000010000000100000000000010000100000100000100
+# 	XY43	STR 	0000000000000000000000000000000000000000000010000000000000000000
+# 	XY44	BOOL	FALSE
+# 	XY45	BOOL	FALSE
+# 	XY46	BOOL	FALSE
+# 	XY47	BOOL	FALSE
+# 	XY50	BOOL	FALSE
+# 	XY51	BOOL	FALSE
+# 	XY52	BOOL	FALSE
+# 	XY53	BOOL	FALSE
+# 	XY54	BOOL	FALSE
+# 	XY55	BOOL	FALSE
+# 	XY56	BOOL	FALSE
+# 	XY57	BOOL	FALSE
+# 	XY60	BOOL	FALSE
+# 	XY61	BOOL	FALSE
+# #	XY62	STR     0000000000000000000000000000000000000000000000000000000000000010
+# 	XY62	BOOL	FALSE
+# 	XY63	BOOL	FALSE
+# 	XY64	BOOL	FALSE
+# 	XY65	BOOL	FALSE
+# 	XY66	BOOL	FALSE
+# 	XY67	BOOL	FALSE
+# 	XY71	BOOL	FALSE
+# #	XY72	STR     0000000000000000000000000000000000000000000000000000000000000001
+# 	XY72	BOOL	FALSE
+# #	XY73	STR     0001100000000000000000000000001000000000000000000000000000000000
+# 	XY73	BOOL	FALSE
+# 	XY74	BOOL	FALSE
+# 	XY75	BOOL	FALSE
+# 	XY76	BOOL	FALSE
+# END
Index: branches/meh_branches/ppstack_test/ippconfig/gpc1/format_20100723.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/gpc1/format_20100723.config	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/gpc1/format_20100723.config	(revision 33415)
@@ -185,5 +185,5 @@
         FPA.ALT         STR     ALT
         FPA.AZ          STR     AZ
-        # FPA.TEMP        STR     DETTEM
+         FPA.TEMP        STR     DETTEM
         FPA.M1X         STR     M1X
         FPA.M1Y         STR     M1Y   
@@ -506,5 +506,5 @@
 DATABASE        METADATA
 	# this rule is fragile : does not match the camera
-        FPA.TEMP        STR     SELECT ccd_temp FROM rawExp WHERE dateobs >= '{FPA.DATE}' and abs(timediff(dateobs, cast('{FPA.TIME}' as datetime))) < 2
+#        FPA.TEMP        STR     SELECT ccd_temp FROM rawExp WHERE dateobs >= '{FPA.DATE}' and abs(timediff(dateobs, cast('{FPA.TIME}' as datetime))) < 2
 END
 
@@ -529,5 +529,6 @@
 PATTERN.ROW.SUBSET	METADATA	# List of chips and whether to do row pattern correction
 	XY01	BOOL	FALSE
-	XY02	STR     0000000000000000000000000000000000000000000000000000000000000001 # xy77
+#	XY02	STR     0000000000000000000000000000000000000000000000000000000000000001 # xy77
+	XY02	BOOL	FALSE
 	XY03	BOOL	FALSE
 	XY04	BOOL	FALSE
@@ -580,5 +581,6 @@
 	XY64	BOOL	FALSE
 	XY65	BOOL	FALSE
-	XY66	STR	0000000000000000000000000000000000000000000000000000000000000100 # cell xy75
+#	XY66	STR	0000000000000000000000000000000000000000000000000000000000000100 # cell xy75
+	XY66	BOOL	FALSE
 	XY67	BOOL	FALSE
 	XY71	BOOL	FALSE
@@ -590,65 +592,80 @@
 END
 
-# PATTERN.CELL	BOOL 	TRUE		# by default, do not do any cell pattern correction
-PATTERN.CELL.SUBSET	METADATA	# List of chips and whether to do cell pattern correction
-	XY01	BOOL	FALSE
-	XY02	BOOL	FALSE
-	XY03	BOOL	FALSE
-	XY04	BOOL	FALSE
-	XY05	BOOL	FALSE
-	XY06    STR     1101000000010000000110100100000000000000000000000000000000000000
-	XY10	BOOL	FALSE
-	XY11	BOOL	FALSE
-	XY12	BOOL	FALSE
-	XY13	BOOL	FALSE
-	XY14	STR     0000000000000000111111110000000000000000000000010000000000101101
-	XY15	STR     0011111100011000000001000000010000001001000111110001010000011111
-	XY16	STR     0000000100000000000000000000000000000001000000000000000000000000
-	XY17	BOOL	FALSE
-	XY20	BOOL	FALSE
-	XY21	BOOL	FALSE
-	XY22	BOOL	FALSE
-	XY23	BOOL	FALSE
-	XY24	STR     0000000000100000000000000110100000100000001000000011100000000000
-	XY25	STR     0100010000001001001011000000001000000111000110000000100000000010
-	XY26	STR     0000000000000100000000000000000000001000001000000000000000000000
-	XY27	BOOL	FALSE
-	XY30	BOOL	FALSE
-	XY31	BOOL	FALSE
-	XY32	BOOL	FALSE
-	XY33	BOOL	FALSE
-	XY34	STR     0000000000000000000000000000000000000001000011000000101000111111
-	XY35	BOOL	FALSE
-	XY36	STR     0000000000000000000000000000000000010000000000000000000000000000
-	XY37	STR     0000000000000000000000000000000100010001000000001011000010110110
-	XY40	STR     0000000000000000000000000010110000000000000001110000001000011111
-	XY41	BOOL	FALSE
-	XY42	BOOL	FALSE
-	XY43	STR     0000000000000000000000010000000100000000000010000100000100000100
-	XY44	BOOL	FALSE
-	XY45	BOOL	FALSE
-	XY46	BOOL	FALSE
-	XY47	BOOL	FALSE
-	XY50	BOOL	FALSE
-	XY51	BOOL	FALSE
-	XY52	BOOL	FALSE
-	XY53	BOOL	FALSE
-	XY54	BOOL	FALSE
-	XY55	BOOL	FALSE
-	XY56	BOOL	FALSE
-	XY57	BOOL	FALSE
-	XY60	BOOL	FALSE
-	XY61	BOOL	FALSE
-	XY62	STR     0000000000000000000000000000000000001000000000000000000100001010
-	XY63	BOOL	FALSE
-	XY64	BOOL	FALSE
-	XY65	BOOL	FALSE
-	XY66	BOOL	FALSE
-	XY67	BOOL	FALSE
-	XY71	BOOL	FALSE
-	XY72	STR     0000000000000000000000000000000000000000000000000000000000000001
-	XY73	STR     0001100000000000000000000000001000000000000000000000000000000000
-	XY74	BOOL	FALSE
-	XY75	BOOL	FALSE
-	XY76	BOOL	FALSE
-END
+PATTERN.CONTINUITY BOOL TRUE
+ PATTERN.CELL	BOOL 	FALSE		# by default, do not do any cell pattern correction
+# PATTERN.CELL.SUBSET	METADATA	# List of chips and whether to do cell pattern correction
+# 	XY01	BOOL	FALSE
+# 	XY02	BOOL	FALSE
+# 	XY03	BOOL	FALSE
+# 	XY04	BOOL	FALSE
+# 	XY05	BOOL	FALSE
+# #	XY06    STR     0101000000010000000110000100000000000000000000000000000000000000
+# 	XY06	BOOL	FALSE
+# 	XY10	BOOL	FALSE
+# 	XY11	BOOL	FALSE
+# 	XY12	BOOL	FALSE
+# 	XY13	BOOL	FALSE
+# 	XY14	STR     0000000000000000111111110000000000000000000000000000000000000000
+# 	XY15	STR     1111111110000000100000000000000000000000000000000000000000000000
+# #	XY16	STR     0000000100000000000000000000000000000001000000000000000000000000
+# 	XY16	BOOL	FALSE
+# 	XY17	BOOL	FALSE
+# 	XY20	BOOL	FALSE
+# 	XY21	BOOL	FALSE
+# 	XY22	BOOL	FALSE
+# 	XY23	BOOL	FALSE
+# #	XY24	STR     0000000000100000000000000110100000100000001000000011100000000000
+# 	XY24	BOOL	FALSE
+# #	XY25	STR     0100010000001001001011000000001000000111000110000000100000000010
+# 	XY25	BOOL	FALSE
+# #	XY26	STR     0000000000000100000000000000000000001000001000000000000000000000
+# 	XY26	BOOL	FALSE
+# 	XY27	BOOL	FALSE
+# 	XY30	BOOL	FALSE
+# 	XY31	BOOL	FALSE
+# 	XY32	BOOL	FALSE
+# 	XY33	BOOL	FALSE
+# #	XY34	STR     0000000000000000000000000000000000000001000011000000101000111111
+# 	XY34	BOOL	FALSE
+# 	XY35	BOOL	FALSE
+# #	XY36	STR     0001000000000000000000000000000000010000000000000000000000000000
+# 	XY36	BOOL	FALSE
+# #	XY37	STR     0000000000000000000000000000000100010001000000001011000010110110 ?
+# 	XY37	BOOL	FALSE
+# #	XY40	STR     0000000000000000000000000010110000000000000001110000001000011111
+# 	XY40	BOOL	FALSE
+# 	XY41	BOOL	FALSE
+# #	XY42	BOOL	FALSE
+# 	XY42	STR	0000000000000000000100000001000000000000000000000000000000000000
+# #	XY43	STR     0000000000000000000000010000000100000000000010000100000100000100
+# 	XY43	STR 	0000000000000000000000000000000000000000000010000000000000000000
+# 	XY44	BOOL	FALSE
+# 	XY45	BOOL	FALSE
+# 	XY46	BOOL	FALSE
+# 	XY47	BOOL	FALSE
+# 	XY50	BOOL	FALSE
+# 	XY51	BOOL	FALSE
+# 	XY52	BOOL	FALSE
+# 	XY53	BOOL	FALSE
+# 	XY54	BOOL	FALSE
+# 	XY55	BOOL	FALSE
+# 	XY56	BOOL	FALSE
+# 	XY57	BOOL	FALSE
+# 	XY60	BOOL	FALSE
+# 	XY61	BOOL	FALSE
+# #	XY62	STR     0000000000000000000000000000000000000000000000000000000000000010
+# 	XY62	BOOL	FALSE
+# 	XY63	BOOL	FALSE
+# 	XY64	BOOL	FALSE
+# 	XY65	BOOL	FALSE
+# 	XY66	BOOL	FALSE
+# 	XY67	BOOL	FALSE
+# 	XY71	BOOL	FALSE
+# #	XY72	STR     0000000000000000000000000000000000000000000000000000000000000001
+# 	XY72	BOOL	FALSE
+# #	XY73	STR     0001100000000000000000000000001000000000000000000000000000000000
+# 	XY73	BOOL	FALSE
+# 	XY74	BOOL	FALSE
+# 	XY75	BOOL	FALSE
+# 	XY76	BOOL	FALSE
+# END
Index: branches/meh_branches/ppstack_test/ippconfig/gpc1/ppImage.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/gpc1/ppImage.config	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/gpc1/ppImage.config	(revision 33415)
@@ -166,5 +166,36 @@
   MASK.BURNTOOL      BOOL    TRUE            # Mask potential burntool trails
   PATTERN.ROW        BOOL    TRUE            # Row pattern correction
-  PATTERN.CELL	     BOOL    TRUE	     # Cell pattern correction
+  PATTERN.CELL	     BOOL    FALSE	     # Cell pattern correction
+  PATTERN.CONTINUITY BOOL    TRUE            # Cell pattern correction using edge continuity
+  FRINGE             BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS          BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS          BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM             BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP        BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC      BOOL    FALSE           # Astrometry for mosaic?
+  BACKGROUND         BOOL    TRUE            # Subtract background?
+  CROSSTALK.MEASURE  BOOL    TRUE            # Subtract model background?
+END
+
+# Chip processing for testing pattern correction
+CHIP_PATTERNTEST METADATA
+  BASE.FITS          BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS     BOOL    FALSE           # Save base detrended image?
+  BASE.VARIANCE.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS          BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS     BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.VARIANCE.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN           BOOL    TRUE            # Overscan subtraction
+  NONLIN             BOOL    TRUE            # apply non-linearity correction 
+  BIAS               BOOL    FALSE           # Bias subtraction
+  DARK               BOOL    TRUE            # Dark subtraction
+  REMNANCE           BOOL    FALSE           # Remnance masking
+  SHUTTER            BOOL    FALSE           # Shutter correction
+  FLAT               BOOL    TRUE            # Flat-field normalisation
+  MASK               BOOL    TRUE            # Mask bad pixels
+  MASK.BUILD	     BOOL    TRUE	     # Build internal mask?
+  MASK.BURNTOOL      BOOL    TRUE            # Mask potential burntool trails
+  PATTERN.ROW        BOOL    FALSE            # Row pattern correction
+  PATTERN.CELL	     BOOL    FALSE	     # Cell pattern correction
   FRINGE             BOOL    TRUE            # Fringe subtraction
   BIN1.FITS          BOOL    TRUE            # Save 1st binned chip image?
@@ -1083,4 +1114,31 @@
 END
 
+PR_NOPATTERN       METADATA
+  BASE.FITS        BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS   BOOL    FALSE           # Save base detrended image?
+  BASE.VARIANCE.FITS BOOL  FALSE           # Save base detrended image?
+  CHIP.FITS        BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS   BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.VARIANCE.FITS BOOL  TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN         BOOL    TRUE            # Overscan subtraction
+  NONLIN           BOOL    TRUE            # apply non-linearity correction 
+  BIAS             BOOL    FALSE           # Bias subtraction
+  DARK             BOOL    TRUE            # Dark subtraction
+  REMNANCE         BOOL    FALSE           # Remnance masking
+  SHUTTER          BOOL    FALSE           # Shutter correction
+  FLAT             BOOL    TRUE            # Flat-field normalisation
+  MASK             BOOL    TRUE            # Mask bad pixels
+  MASK.BUILD	        BOOL    TRUE	          # Build internal mask?
+  FRINGE           BOOL    FALSE           # Fringe subtraction
+  PATTERN.ROW      BOOL    FALSE           # Row pattern correction
+  PATTERN.CELL        BOOL    FALSE	        # Cell pattern correction
+  BIN1.FITS        BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS        BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM           BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP      BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC    BOOL    FALSE           # Astrometry for mosaic?
+  BACKGROUND       BOOL    FALSE            # Subtract background?
+  CROSSTALK.MEASURE  BOOL  TRUE            # Subtract model background?
+END
 
 # Standard chip processing without any background removal
@@ -1102,6 +1160,6 @@
   MASK.BUILD	     BOOL    TRUE	     # Build internal mask?
   MASK.BURNTOOL      BOOL    TRUE            # Mask potential burntool trails
-  PATTERN.ROW        BOOL    FALSE           # Row pattern correction
-  PATTERN.CELL	     BOOL    FALSE	     # Cell pattern correction
+  PATTERN.ROW        BOOL    TRUE           # Row pattern correction
+  PATTERN.CELL	     BOOL    TRUE	     # Cell pattern correction
   FRINGE             BOOL    TRUE            # Fringe subtraction
   BIN1.FITS          BOOL    TRUE            # Save 1st binned chip image?
Index: branches/meh_branches/ppstack_test/ippconfig/gpc1/ppStack.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/gpc1/ppStack.config	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/gpc1/ppStack.config	(revision 33415)
@@ -36,12 +36,12 @@
 END
 
-# for stages that want just unconvolved and deep stacks
+# for just unconvolved and deep stacks
 QUICKSTACK_DEEP    METADATA
-    CONVOLVE        BOOL   FALSE
-    COMBINE.REJ     F32    3.5		# Rejection threshold in combination (sigma). tune?
-    COMBINE.DISCARD F32    0.2		# Discard fraction for Olympic weighted mean. tune?
+    CONVOLVE        BOOL   FALSE    
+    COMBINE.REJ     F32    3.5          # Rejection threshold in combination (sigma). tune?
+    COMBINE.DISCARD F32    0.2          # Discard fraction for Olympic weighted mean. tune?
     OUTPUT.NOCOMP   BOOL    TRUE
     OUTPUT.LOGFLUX  BOOL    FALSE
-    PSF.INPUT.MAX           F32  7.5	# since not convolved limit, relax? to tune.
+    PSF.INPUT.MAX           F32  8.0    # since not convolved limit, relax? tune?
     PSF.INPUT.CLIP.NSIGMA   F32  5.0
 END
Index: branches/meh_branches/ppstack_test/ippconfig/gpc1/psastro.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/gpc1/psastro.config	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/gpc1/psastro.config	(revision 33415)
@@ -344,4 +344,6 @@
   PSASTRO.MATCH.RADIUS.N7 F32    5
   PSASTRO.MATCH.FIT.NITER S32    5
+
+  PSASTRO.CATDIR    	STR 	STS.PP5.REFCAT
 END
 
Index: branches/meh_branches/ppstack_test/ippconfig/gpc1/psphot.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/gpc1/psphot.config	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/gpc1/psphot.config	(revision 33415)
@@ -54,2 +54,6 @@
 	SKY_STAT         STR   FITTED_MEAN_V4  # statistic used to measure background
 END
+
+CHIP_CONSTANT_WEIGHTPHOT METADATA
+        CONSTANT_PHOTOMETRIC_WEIGHTS        BOOL  TRUE
+END
Index: branches/meh_branches/ppstack_test/ippconfig/lbc_red/filerules.mdc
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/lbc_red/filerules.mdc	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/lbc_red/filerules.mdc	(revision 33415)
@@ -136,4 +136,5 @@
 PSASTRO.OUTPUT.CMP    OUTPUT {OUTPUT}.{CHIP.NAME}.smp         CMP       NONE      CHIP       TRUE      NONE
 PSASTRO.OUT.CMF.SPL   OUTPUT {OUTPUT}.{CHIP.NAME}.smf         CMF       NONE      CHIP       TRUE      NONE
+PSASTRO.OUTPUT.CMF   OUTPUT {OUTPUT}.{CHIP.NAME}.smf         CMF       NONE      CHIP       TRUE      NONE
 PSASTRO.OUT.CMF.MEF   OUTPUT {OUTPUT}.smf                     CMF       NONE      FPA        TRUE      NONE
 PSASTRO.OUT.ASTROM    OUTPUT {OUTPUT}.ast                     ASTROM    NONE      FPA        TRUE      NONE
Index: branches/meh_branches/ppstack_test/ippconfig/megacam/camera.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/megacam/camera.config	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/megacam/camera.config	(revision 33415)
@@ -154,4 +154,18 @@
 PHOTCODE.RULE           STR     {DETECTOR}.{FILTER.ID}.{CHIP.N}		# Rule for generating photcode
 
+BURNTOOL.STATE.GOOD        S16  0  # Value for burntool_state with the most recent bt version.
+BURNTOOL.STATE.GOOD.UPDATE S16  0  # for upddate processing accept earlier version
+
+FOV_REF                     F32     0   # Field of view of unvignetted region in FPA pixels.
+FOV_MAX			    F32	    0
+NPIX_REF 		    S32	    0
+NPIX_MAX		    S32	    0
+NPIX_INTERCHIP              S32     0
+
+# This sets the maximum size for the fwhm_major.  This is roughly 3", and 98.67% of all exposures in the database are less than this.
+MAX_ALLOWED_FWHM          F32         25
+
 # don't censor any masked pixels
 MASK.NO.CENSOR          U32 4294967295
+
+METADATA.COMPRESSION         STR      7f # compression mode (nM): n = 1-9, M = f (filtered), h (Huffman), R (run-length)
Index: branches/meh_branches/ppstack_test/ippconfig/recipes/filerules-mef.mdc
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/recipes/filerules-mef.mdc	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/recipes/filerules-mef.mdc	(revision 33415)
@@ -42,4 +42,5 @@
 PSASTRO.INPUT         STR PSASTRO.INPUT.CMF
 PSASTRO.OUTPUT.MEF    STR PSASTRO.OUT.CMF.MEF
+#PSASTRO.OUTPUT.CMF    STR PSASTRO.OUT.CMF
 DVOCORR.OUTPUT        STR DVOCORR.MEF.OUTPUT
 DVOFLAT.OUTPUT        STR DVOFLAT.MEF.OUTPUT
@@ -243,4 +244,6 @@
 PSASTRO.OUT.CMF.SPL     OUTPUT {OUTPUT}.{CHIP.NAME}.smf          CMF       NONE       CHIP       TRUE      SPLIT
 PSASTRO.OUT.CMF.MEF     OUTPUT {OUTPUT}.smf                      CMF       NONE       FPA        TRUE      MEF
+PSASTRO.OUTPUT.CMF      OUTPUT {OUTPUT}.cmf                      CMF       NONE       CHIP       TRUE      NONE
+#PSASTRO.OUT.CMF         OUTPUT {OUTPUT}.smf                      CMF       NONE       FPA        TRUE      NONE
 PSASTRO.OUT.MODEL       OUTPUT {OUTPUT}.asm                ASTROM.MODEL    NONE       FPA        TRUE      NONE
 PSASTRO.OUT.REFSTARS    OUTPUT {OUTPUT}.aref.fits          ASTROM.REFSTARS NONE       FPA        TRUE      NONE
@@ -308,4 +311,6 @@
 PPSTAMP.OUTPUT.MASK     OUTPUT {OUTPUT}.mk.fits                  MASK      COMP_MASK  FPA        TRUE      NONE
 PPSTAMP.OUTPUT.VARIANCE OUTPUT {OUTPUT}.wt.fits                  VARIANCE  COMP_WT    FPA        TRUE      NONE
+PPSTAMP.OUTPUT.JPEG     OUTPUT {OUTPUT}.jpg                      JPEG      NONE       FPA        TRUE      NONE
+PPSTAMP.OUTPUT.JPEG.FLIP OUTPUT {OUTPUT}.jpg                     JPEG      NONE       FPA        TRUE      NONE
 PPSTAMP.CHIP.MEF        OUTPUT {OUTPUT}.ch.fits                  IMAGE     NONE       CHIP       FALSE     MEF
                                                                                      
Index: branches/meh_branches/ppstack_test/ippconfig/recipes/filerules-simple.mdc
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/recipes/filerules-simple.mdc	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/recipes/filerules-simple.mdc	(revision 33415)
@@ -29,4 +29,11 @@
 PPIMAGE.SHUTTER           INPUT    @DETDB        CHIP       IMAGE     
 PPIMAGE.LINEARITY         INPUT    @DETDB        CHIP       LINEARITY
+
+## Files used by psvideophot
+PSVIDEOPHOT.INPUT             INPUT    @FILES        CHIP       IMAGE
+PSVIDEOPHOT.INPUT.MASK        INPUT    @FILES        CHIP       MASK
+PSVIDEOPHOT.INPUT.VARIANCE    INPUT    @FILES        CHIP       VARIANCE
+PSVIDEOPHOT.INPUT.PSF         INPUT    @FILES        CHIP       PSF
+PSVIDEOPHOT.INPUT.SRC         INPUT    @FILES        CHIP       CMF
                           
 ## Files used by ppMerge  
@@ -193,4 +200,8 @@
 PSPHOT.PSF.RAW.SAVE          OUTPUT {OUTPUT}.psf                  PSF             NONE       FPA        TRUE      NONE
 PSPHOT.PSF.SKY.SAVE          OUTPUT {OUTPUT}.psf                  PSF             NONE       FPA        TRUE      NONE
+
+#psvideophot
+PSVIDEOPHOT.OUTPUT           OUTPUT {OUTPUT}.{CHIP.NAME}.vid          OBJ NONE CHIP TRUE NONE
+PSVIDEOPHOT.CHIP             OUTPUT {OUTPUT}.{CHIP.NAME}.vid          OBJ NONE CHIP TRUE NONE
 
 # used by staticsky with single input
@@ -280,4 +291,6 @@
 PPSTAMP.OUTPUT.MASK          OUTPUT {OUTPUT}.mk.fits              MASK            COMP_MASK  FPA        TRUE      NONE
 PPSTAMP.OUTPUT.VARIANCE      OUTPUT {OUTPUT}.wt.fits              VARIANCE        COMP_WT    FPA        TRUE      NONE
+PPSTAMP.OUTPUT.JPEG          OUTPUT {OUTPUT}.jpg                  JPEG            NONE       FPA        TRUE      NONE
+PPSTAMP.OUTPUT.JPEG.FLIP     OUTPUT {OUTPUT}.jpg                  JPEG            NONE       FPA        TRUE      NONE
 PPSTAMP.CHIP                 OUTPUT {OUTPUT}.ch.fits              IMAGE           NONE       CHIP       FALSE     NONE
                                                      
Index: branches/meh_branches/ppstack_test/ippconfig/recipes/filerules-split.mdc
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/recipes/filerules-split.mdc	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/recipes/filerules-split.mdc	(revision 33415)
@@ -13,5 +13,5 @@
 SOURCE.PLOT.APRESID  STR SOURCE.PLOT.RAW.APRESID
 
-# PSASTRO.OUTPUT     STR PSASTRO.OUT.CMF.MEF
+PSASTRO.OUTPUT     STR PSASTRO.OUT.CMF.MEF
 # PSASTRO.OUTPUT.SPL STR PSASTRO.OUT.CMF.SPL
 # PSASTRO.OUTPUT.MEF STR PSASTRO.OUT.CMF.MEF
@@ -220,6 +220,5 @@
 
 #psvideophot
-PSVIDEOPHOT.OUTPUT           OUTPUT {OUTPUT}.{CHIP.NAME}.vid          OBJ NONE CHIP TRUE NONE
-PSVIDEOPHOT.CHIP             OUTPUT {OUTPUT}.{CHIP.NAME}.vid          OBJ NONE CHIP TRUE NONE
+PSVIDEOPHOT.OUTPUT           OUTPUT {OUTPUT}.vpt                      OBJ             NONE       CELL       TRUE      NONE
 
 # used by staticsky with single input
@@ -246,8 +245,8 @@
 # PSASTRO.OUTPUT.CMP         OUTPUT {OUTPUT}.{CHIP.NAME}.smp          CMP             NONE       CHIP       TRUE      NONE
 # PSASTRO.OUT.CMF.SPL        OUTPUT {OUTPUT}.{CHIP.NAME}.smf          CMF             NONE       CHIP       TRUE      NONE
-# PSASTRO.OUT.CMF.MEF        OUTPUT {OUTPUT}.smf                      CMF             NONE       FPA        TRUE      MEF
 PSASTRO.OUT.MODEL            OUTPUT {OUTPUT}.asm                      ASTROM.MODEL    NONE       FPA        TRUE      NONE
 PSASTRO.OUT.REFSTARS         OUTPUT {OUTPUT}.aref.fits                ASTROM.REFSTARS NONE       FPA        TRUE      NONE
-PSASTRO.OUTPUT               OUTPUT {OUTPUT}.smf                      CMF             NONE       FPA        TRUE      MEF
+PSASTRO.OUT.CMF.MEF          OUTPUT {OUTPUT}.smf                      CMF             NONE       FPA        TRUE      MEF
+PSASTRO.OUTPUT.CMF           OUTPUT {OUTPUT}.cmf                      CMF             NONE       FPA        TRUE      NONE
 PSASTRO.STATS                OUTPUT {OUTPUT}.stats                    STATS           NONE       FPA        TRUE      NONE
 PSASTRO.CONFIG               OUTPUT {OUTPUT}.psastro.mdc              TEXT            NONE       FPA        TRUE      NONE
@@ -323,7 +322,8 @@
 PPSTAMP.OUTPUT.MASK          OUTPUT {OUTPUT}.mk.fits                  MASK            COMP_MASK  FPA        TRUE      NONE
 PPSTAMP.OUTPUT.VARIANCE      OUTPUT {OUTPUT}.wt.fits                  VARIANCE        COMP_WT    FPA        TRUE      NONE
+PPSTAMP.OUTPUT.JPEG         OUTPUT {OUTPUT}.jpg                      JPEG            NONE       FPA        TRUE      NONE
+PPSTAMP.OUTPUT.JPEG.FLIP    OUTPUT {OUTPUT}.jpg                      JPEG            NONE       FPA        TRUE      NONE
 # mosaic image used for making stamps of raw images. Not saved.
 PPSTAMP.CHIP                 OUTPUT {OUTPUT}.ch.fits                  IMAGE           NONE       CHIP       FALSE     MEF
-PPSTAMP.OUTPUT.JPEG1         OUTPUT {OUTPUT}.jpg                      JPEG            NONE       FPA        TRUE      NONE
                                                                                                               
 PPSIM.OUTPUT                 OUTPUT {OUTPUT}.{CHIP.NAME}.fits         IMAGE           NONE       CHIP       TRUE      SPLIT
Index: branches/meh_branches/ppstack_test/ippconfig/recipes/jpeg.mdc
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/recipes/jpeg.mdc	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/recipes/jpeg.mdc	(revision 33415)
@@ -254,2 +254,17 @@
 END
 
+PPSTAMP.OUTPUT.JPEG  	        METADATA
+	COLORMAP	STR	greyscale
+	SCALE.MODE	STR	RANGE
+	SCALE.MIN	F32	 -5.0
+	SCALE.MAX	F32	+10.0
+	OPTIONS		STR	-SB,+X,+Y
+END
+
+PPSTAMP.OUTPUT.JPEG.FLIP        METADATA
+	COLORMAP	STR	greyscale
+	SCALE.MODE	STR	RANGE
+	SCALE.MIN	F32	 -5.0
+	SCALE.MAX	F32	+10.0
+	OPTIONS		STR	-SB,-X,+Y
+END
Index: branches/meh_branches/ppstack_test/ippconfig/recipes/nightly_science.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/recipes/nightly_science.config	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/recipes/nightly_science.config	(revision 33415)
@@ -2,15 +2,15 @@
 CLEAN_MODES METADATA
   MODE           STR CHIP
-  COMMAND        STR chiptool -dbname @DBNAME@ -updaterun -set_state goto_cleaned -state full -set_label goto_cleaned -label @LABEL@ -data_group @DATA_GROUP@ -destreaked
+  COMMAND        STR chiptool -dbname @DBNAME@ -updaterun -set_state goto_cleaned -state full -set_label goto_cleaned -label @LABEL@ -data_group @DATA_GROUP@
   RETENTION_TIME U16 21
 END
 CLEAN_MODES METADATA
   MODE           STR WARP
-  COMMAND        STR warptool -dbname @DBNAME@ -updaterun -set_state goto_cleaned -state full -set_label goto_cleaned -label @LABEL@ -data_group @DATA_GROUP@ -destreaked
+  COMMAND        STR warptool -dbname @DBNAME@ -updaterun -set_state goto_cleaned -state full -set_label goto_cleaned -label @LABEL@ -data_group @DATA_GROUP@
   RETENTION_TIME U16 7
 END
 CLEAN_MODES METADATA
   MODE           STR DIFF
-  COMMAND        STR difftool -dbname @DBNAME@ -updaterun -set_state goto_cleaned -state full -set_label goto_cleaned -label @LABEL@ -data_group @DATA_GROUP@ -destreaked
+  COMMAND        STR difftool -dbname @DBNAME@ -updaterun -set_state goto_cleaned -state full -set_label goto_cleaned -label @LABEL@ -data_group @DATA_GROUP@
   RETENTION_TIME S16 21
 END
@@ -93,5 +93,5 @@
   NAME      STR  MD02
   DISTRIBUTION STR MD02
-  TESS      STR MD02.V2
+  TESS      STR MD02.V3
   OBSMODE   STR MD
   OBJECT    STR MD02%
@@ -104,5 +104,5 @@
   NAME      STR MD03
   DISTRIBUTION STR MD03
-  TESS      STR MD03.V2
+  TESS      STR MD03.V3
   OBSMODE   STR MD
   OBJECT    STR MD03%
@@ -115,5 +115,5 @@
   NAME      STR MD04
   DISTRIBUTION STR MD04
-  TESS      STR MD04.V2
+  TESS      STR MD04.V3
   OBSMODE   STR MD
   OBJECT    STR MD04%
@@ -126,5 +126,5 @@
   NAME      STR MD05
   DISTRIBUTION STR MD05
-  TESS      STR MD05.V2
+  TESS      STR MD05.V3
   OBSMODE   STR MD
   OBJECT    STR MD05%
@@ -137,5 +137,5 @@
   NAME      STR MD06
   DISTRIBUTION STR MD06
-  TESS      STR MD06
+  TESS      STR MD06.V3
   OBSMODE   STR MD
   OBJECT    STR MD06%
@@ -148,5 +148,5 @@
   NAME      STR MD07
   DISTRIBUTION STR MD07
-  TESS      STR MD07
+  TESS      STR MD07.V3
   OBSMODE   STR MD
   OBJECT    STR MD07%
@@ -159,5 +159,5 @@
   NAME      STR MD08
   DISTRIBUTION STR MD08
-  TESS      STR MD08.V2
+  TESS      STR MD08.V3
   OBSMODE   STR MD
   OBJECT    STR MD08%
@@ -170,5 +170,5 @@
   NAME      STR MD09
   DISTRIBUTION STR MD09
-  TESS      STR MD09.V2
+  TESS      STR MD09.V3
   OBSMODE   STR MD
   OBJECT    STR MD09%
@@ -192,5 +192,5 @@
   NAME      STR MD11
   DISTRIBUTION STR MD11
-  TESS      STR MD11.V2
+  TESS      STR MD11.V3
   OBSMODE   STR MD
   OBJECT    STR MD11%
@@ -210,7 +210,7 @@
   REDUCTION STR STS_DATASET
   # bump the retention times for the time being
-  CHIP S16 60
-  WARP S16 60
-  DIFF S16 60
+  CHIP S16 7
+  WARP S16 7
+  DIFF S16 7
 END
 TARGETS METADATA
@@ -219,6 +219,8 @@
   TESS      STR RINGS.V3
   OBSMODE   STR SS
-  STACKABLE BOOL FALSE
+  STACKABLE BOOL TRUE
   DIFFABLE  BOOL TRUE
+  REDUCTION STR SWEETSPOT
+#  REDUCTION STR SSTF_4SIG
 #  WARP      S16 60
 END
@@ -228,6 +230,8 @@
   TESS         STR RINGS.V3
   OBSMODE      STR OSS
-  STACKABLE   BOOL FALSE
-  DIFFABLE    BOOL TRUE
+  STACKABLE   BOOL TRUE
+  DIFFABLE    BOOL TRUE
+  REDUCTION STR SWEETSPOT
+#  REDUCTION STR SSTF_4SIG
 END
 TARGETS METADATA
@@ -236,6 +240,8 @@
   TESS         STR RINGS.V3
   OBSMODE      STR ESS
-  STACKABLE   BOOL FALSE
-  DIFFABLE    BOOL TRUE
+  STACKABLE   BOOL TRUE
+  DIFFABLE    BOOL TRUE
+  REDUCTION STR SWEETSPOT
+#  REDUCTION STR SSTF_4SIG
 END
 TARGETS METADATA
@@ -244,6 +250,8 @@
   TESS         STR RINGS.V3
   OBSMODE      STR MSS
-  STACKABLE   BOOL FALSE
-  DIFFABLE    BOOL TRUE
+  STACKABLE   BOOL TRUE
+  DIFFABLE    BOOL TRUE
+  REDUCTION STR SWEETSPOT
+#  REDUCTION STR SSTF_4SIG
 END
 TARGETS METADATA
@@ -262,4 +270,6 @@
   STACKABLE   BOOL FALSE
   DIFFABLE    BOOL FALSE
+  CHIP	      S16  800
+  WARP	      S16  800
 END
 TARGETS METADATA
Index: branches/meh_branches/ppstack_test/ippconfig/recipes/ppImage.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/recipes/ppImage.config	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/recipes/ppImage.config	(revision 33415)
@@ -27,4 +27,7 @@
 PATTERN.SUBSET	   METADATA
 END
+PATTERN.CONTINUITY BOOL    FALSE
+PATTERN.CONTINUITY.WIDTH S32  50
+PATTERN.CONTINUITY.SUBSET BOOL TRUE
 FRINGE             BOOL    FALSE           # Fringe subtraction
 PHOTOM             BOOL    FALSE           # Source identification and photometry
@@ -177,4 +180,31 @@
   BACKGROUND         BOOL    TRUE            # Subtract background?
   MASK.STATS         BOOL    TRUE            # Calculate Mask statistics.
+END
+
+# Standard chip processing
+CHIP_PATTERNTEST	METADATA
+  BASE.FITS          BOOL    FALSE           # Save base detrended image?
+  BASE.MASK.FITS     BOOL    FALSE           # Save base detrended image?
+  BASE.VARIANCE.FITS BOOL    FALSE           # Save base detrended image?
+  CHIP.FITS          BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.MASK.FITS     BOOL    TRUE            # Save chip-mosaic-ed image? 
+  CHIP.VARIANCE.FITS BOOL    TRUE            # Save chip-mosaic-ed image? 
+  OVERSCAN           BOOL    TRUE            # Overscan subtraction
+  NONLIN             BOOL    FALSE           # Non-linearity correction; not implemented
+  BIAS               BOOL    TRUE            # Bias subtraction
+  DARK               BOOL    TRUE            # Dark subtraction
+  SHUTTER            BOOL    FALSE           # Shutter correction
+  FLAT               BOOL    TRUE            # Flat-field normalisation
+  MASK               BOOL    TRUE            # Mask bad pixels
+  FRINGE             BOOL    TRUE            # Fringe subtraction
+  BIN1.FITS          BOOL    TRUE            # Save 1st binned chip image?
+  BIN2.FITS          BOOL    TRUE            # Save 2nd binned chip image?
+  PHOTOM             BOOL    TRUE            # Source identification and photometry
+  ASTROM.CHIP        BOOL    FALSE           # Astrometry per chip?
+  ASTROM.MOSAIC      BOOL    FALSE           # Astrometry for mosaic?
+  BACKGROUND         BOOL    TRUE            # Subtract background?
+  MASK.STATS         BOOL    TRUE            # Calculate Mask statistics.
+  PATTERN.CELL	     BOOL    FALSE
+  PATTERN.ROW	     BOOL    FALSE
 END
 
Index: branches/meh_branches/ppstack_test/ippconfig/recipes/ppSim.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/recipes/ppSim.config	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/recipes/ppSim.config	(revision 33415)
@@ -46,11 +46,11 @@
 FORCED.PHOTCODE STR     r               # source of the forced photometry positions
 
-STARS.REAL	BOOL	TRUE		# Add stars from a catalogue?
-MATCH.DENSITY   BOOL    TRUE            # significance of faintest sources
+STARS.REAL	BOOL	TRUE 		# Add stars from a catalogue?
+MATCH.DENSITY   BOOL    FALSE           # significance of faintest sources
 
 STARS.FAKE      BOOL    TRUE		# Add fake stars, randomly distributed?
-STARS.LUM	F32	0.35		# Stellar luminosity function slope (magnitude slope)
-STARS.MAG	F32	15.5		# Brightest magnitude for fake stars (choose based on real catalog limit)
-STARS.DENSITY	F32      1.0		# Stellar density (per square degree) at the brightest magnitude
+STARS.LUM	F32	0.40		# Stellar luminosity function slope (magnitude slope)
+STARS.MAG	F32	12.5		# Brightest magnitude for fake stars (choose based on real catalog limit)
+STARS.DENSITY	F32    100.0		# Stellar density (per square degree) at the brightest magnitude
 STARS.SIGMA.LIM F32      1.0            # significance of faintest sources
 
@@ -69,5 +69,5 @@
 GALAXY.FAKE     BOOL    FALSE		# Generate fake galaxies?
 
-GALAXY.LUM	  F32	-0.8		# Stellar luminosity function slope
+GALAXY.LUM	  F32	0.8		# Stellar luminosity function slope
 GALAXY.MAG	  F32	15.0		# Brightest magnitude for fake stars
 GALAXY.DENSITY	  F32	5.0		# Galaxy density (per square degree) at the brightest magnitude
@@ -83,11 +83,14 @@
 
 GALAXY.THETA.MIN F32    0.0		# Minimum position angle for galaxy models
-GALAXY.THETA.MAX F32	3.14            # Maximum position angle for galaxy models
+GALAXY.THETA.MAX F32	180.0           # Maximum position angle for galaxy models
 
 GALAXY.GRID        BOOL   FALSE		# Generate a (regular) grid of galaxy models?
-GALAXY.GRID.DX     S32    100             # Spacing between galaxies in grid in x
-GALAXY.GRID.DY     S32    200             # Spacing between galaxies in grid in y
+GALAXY.GRID.DX     S32    100           # Spacing between galaxies in grid in x
+GALAXY.GRID.DY     S32    200           # Spacing between galaxies in grid in y
 GALAXY.GRID.RANDOM BOOL   FALSE		# randomize shape parameters in grid (or sequence?)
-GALAXY.GRID.PEAK   F32    100.0		# peak flux of the grid galaxies
+GALAXY.GRID.PEAK   F32    100.0		# peak flux of the grid galaxies (deprecated)
+GALAXY.GRID.MAG    F32    17.0		# integrated magnitude of the grid galaxies
+GALAXY.GRID.XOFF   F32    0.0           # galaxy centroid offsets
+GALAXY.GRID.YOFF   F32    0.0           # galaxy centroid offsets
 
 GALAXY.MODEL     STR    PS_MODEL_GAUSS
Index: branches/meh_branches/ppstack_test/ippconfig/recipes/ppStack.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/recipes/ppStack.config	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/recipes/ppStack.config	(revision 33415)
@@ -19,5 +19,5 @@
 POOR.FRACTION	F32	0.25		# Maximum fraction of bad variance for poor pixels
 THRESHOLD.MASK	F32	0.5		# Threshold for mask deconvolution (0..1)
-DECONV.LIMIT	F32	2.0		# Deconvolution fraction for rejecting entire image
+DECONV.LIMIT	F32	3.0		# Deconvolution fraction for rejecting entire image
 IMAGE.REJ	F32	0.1		# Rejected pixel fraction threshold for rejecting entire image
 MATCH.REJ	F32	3.0		# Rejection threshold for chi^2 values from matching
Index: branches/meh_branches/ppstack_test/ippconfig/recipes/ppStatsFromMetadata.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/recipes/ppStatsFromMetadata.config	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/recipes/ppStatsFromMetadata.config	(revision 33415)
@@ -536,2 +536,24 @@
 # ENTRY  VAL  REPLICATE_OUTPUTS   F32  CONSTANT          -dtime_replicate  # time to replicate the outputs
 END
+
+STATICSKY_CAL METADATA
+  ENTRY MULTI
+  TYPE   VAL  KEYWORD             TYPE STATISTIC         FLAG 
+  ENTRY  VAL  CERROR              F32  CONSTANT         -sigma_ra          
+  ENTRY  VAL  CERROR              F32  CONSTANT         -sigma_dec         
+  ENTRY  VAL  DT_ASTR             F32  CONSTANT         -dtime_astrom      
+  ENTRY  VAL  NASTRO              S32  CONSTANT         -n_astrom          
+
+  ENTRY  VAL  ZPT_OBS             F32  CONSTANT         -zpt_obs
+  ENTRY  VAL  ZPT_ERR             F32  CONSTANT         -zpt_stdev
+
+  ENTRY  VAL  QUALITY             S32  CONSTANT          -quality             # Bad quality flag
+
+  # Revision values
+  ENTRY	 VAL  PSLIB_V		  STR  CONSTANT		-ver_pslib
+  ENTRY  VAL  MODULE_V		  STR  CONSTANT		-ver_psmodules
+  ENTRY	 VAL  PHOT_V		  STR  CONSTANT		-ver_psphot
+  ENTRY	 VAL  ASTRO_V		  STR  CONSTANT		-ver_psastro
+  ENTRY	 VAL  STATS_V		  STR  CONSTANT		-ver_ppstats
+#   ENTRY	 VAL  IMAGE_V		  STR  CONSTANT		-ver_ppimage
+END
Index: branches/meh_branches/ppstack_test/ippconfig/recipes/ppSub.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/recipes/ppSub.config	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/recipes/ppSub.config	(revision 33415)
@@ -219,4 +219,13 @@
 END
 
+SSTF_DIFF METADATA
+	DUAL		BOOL	FALSE	# Dual convolution?
+	INVERSE		BOOL	FALSE   # Generate inverse subtraction?
+	PHOTOMETRY	BOOL	TRUE	# Perform photometry?
+	NOCONVOLVE      BOOL    TRUE
+	CONVOLVE.TARGET STR     SINGLE2	# convolution direction
+END
+ 
+
 # Gene's choice: multiplying by 1.75
 #@ISIS.WIDTHS	F32	1.8 3.3 5.8 10.2 17.9	# Gaussian FWHMs for ISIS kernels
Index: branches/meh_branches/ppstack_test/ippconfig/recipes/psastro.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/recipes/psastro.config	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/recipes/psastro.config	(revision 33415)
@@ -258,2 +258,9 @@
 END
 
+STATICSKY_CAL   METADATA
+    REFSTAR_MASK                BOOL FALSE
+    PSASTRO.SAVE.REFMATCH       BOOL FALSE
+    PSASTRO.FIX.CHIPS           BOOL FALSE
+    PSASTRO.USE.MODEL           BOOL FALSE
+    PSASTRO.MOSAIC.MODE         BOOL FALSE
+END
Index: branches/meh_branches/ppstack_test/ippconfig/recipes/psphot.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/recipes/psphot.config	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/recipes/psphot.config	(revision 33415)
@@ -199,7 +199,7 @@
  #PGAUSS_PSF  EXTENDED_SOURCE_MODEL  PS_MODEL_PGAUSS   20.0    TRUE
  #QGAUSS_PSF  EXTENDED_SOURCE_MODEL  PS_MODEL_QGAUSS   20.0    TRUE
- EXP_PCM  EXTENDED_SOURCE_MODEL  PS_MODEL_EXP      20.0    TRUE
- DEV_PCM  EXTENDED_SOURCE_MODEL  PS_MODEL_DEV      20.0    TRUE
- SER_PCM  EXTENDED_SOURCE_MODEL  PS_MODEL_SERSIC   20.0    TRUE
+ EXP_PCM  EXTENDED_SOURCE_MODEL  PS_MODEL_EXP      10.0    TRUE
+ DEV_PCM  EXTENDED_SOURCE_MODEL  PS_MODEL_DEV      10.0    TRUE
+ SER_PCM  EXTENDED_SOURCE_MODEL  PS_MODEL_SERSIC   10.0    TRUE
 END
 
@@ -216,7 +216,7 @@
 # @RADIAL.ANNULAR.BINS.UPPER           F32      1.2 3.4 5.2 8.8 15.0 23.2 37.2 57.1 91.0 141.0 221.1 345.0 539.1 841.0 1305.0 #
 
-# PS1 values?
-@RADIAL.ANNULAR.BINS.LOWER           F32      0.0 1.2 3.4 5.2  8.8 15.0 23.2 37.2 57.1  91.0 141.0  # perl parser fails without a comment ticket #1476
-@RADIAL.ANNULAR.BINS.UPPER           F32      1.2 3.4 5.2 8.8 15.0 23.2 37.2 57.1 91.0 141.0 221.1  # perl parser fails without a comment ticket #1476
+# PS1 values? (revised fpr PS1 @0.25 arcsec pixel scales) 
+@RADIAL.ANNULAR.BINS.LOWER           F32      0.0 0.96 2.72 4.16 7.04 12.0 18.56 29.76 45.68 72.80 112.80    # perl parser fails without a comment ticket #1476 still?
+@RADIAL.ANNULAR.BINS.UPPER           F32      0.96 2.72 4.16 7.04 12.0 18.56 29.76 45.68 72.80 112.80 176.88 # perl parser fails without a comment ticket #1476 still?
 
 # Extended source fit parameters
@@ -329,9 +329,9 @@
 
 # stack photometry parameters
-PSPHOT.STACK.MATCH.RADIUS           F32   1.0  # join detections from different images with this correlation radius (pixels)
+PSPHOT.STACK.MATCH.RADIUS           F32   5.0  # join detections from different images with this correlation radius (pixels)
 PSPHOT.STACK.MATCH.PSF              BOOL  T    # convolved input images to common target PSF
 PSPHOT.STACK.MATCH.PSF.SOURCE       STR   AUTO # which inputs to convolve? (RAW, CNV, AUTO)
 PSPHOT.STACK.TARGET.PSF.AUTO        BOOL  F    # automatically determine target PSF size?
-@PSPHOT.STACK.TARGET.PSF.FWHM       F32   6.5 9.0 # FWHM of target PSF (if NOT AUTO sized; pixels)
+@PSPHOT.STACK.TARGET.PSF.FWHM       F32   6.0 8.0 # FWHM of target PSF (if NOT AUTO sized; pixels)
 PSPHOT.STACK.USE.RAW                BOOL  T
 
@@ -343,5 +343,5 @@
   EXTENDED_SOURCE_FITS                BOOL  TRUE  # perform any of the aperture-like measurements?
   EXTENDED_SOURCE_ANALYSIS            BOOL  TRUE  # perform any of the aperture-like measurements?
-  EXTENDED_SOURCE_SN_LIM              F32   20.0
+  EXTENDED_SOURCE_SN_LIM              F32   10.0
   EXTENDED_SOURCE_PETROSIAN           BOOL  TRUE
   EXTENDED_SOURCE_ANNULI              BOOL  TRUE
@@ -349,32 +349,36 @@
   PSPHOT.STACK.TARGET.PSF.AUTO        BOOL  F    # automatically determine target PSF size?
   PSPHOT.STACK.USE.RAW                BOOL  F    # perform photometry & morphology analysis on the raw image
-  @PSPHOT.STACK.TARGET.PSF.FWHM       F32   6.5 8.0 # FWHM of target PSF (if NOT AUTO sized; pixels)
+  @PSPHOT.STACK.TARGET.PSF.FWHM       F32   6.0 8.0 # FWHM of target PSF (if NOT AUTO sized; pixels)
   RADIAL_APERTURES                    BOOL  T    # calculate flux in circular radial apertures?
   RADIAL_APERTURES_SN_LIM             F32   0.0  # S/N limit for radial aperture calculation
   OUTPUT.FORMAT                       STR   PS1_SV1
+  PETROSIAN_FOR_STARS                 BOOL  FALSE
+
+  CONSTANT_PHOTOMETRIC_WEIGHTS        BOOL  TRUE 
+  POISSON.ERRORS.PHOT.LMM             BOOL  TRUE
 
   EXT_FIT_MAX_RADIUS                  F32   50.0
-  PSF_MODEL                           STR   PS_MODEL_QGAUSS 
-  EXT_MODEL                           STR   PS_MODEL_SERSIC
+  PSF_MODEL                           STR   PS_MODEL_GAUSS 
+  EXT_MODEL                           STR   PS_MODEL_QGAUSS
 END
 
 # Extended source fit parameters
 STACKPHOT_TEST                        METADATA
-  EXTENDED_SOURCE_FITS                BOOL  TRUE  # perform any of the aperture-like measurements?
-  EXTENDED_SOURCE_ANALYSIS            BOOL  TRUE  # perform any of the aperture-like measurements?
-  EXTENDED_SOURCE_SN_LIM              F32   20.0
-  EXTENDED_SOURCE_PETROSIAN           BOOL  TRUE
-  EXTENDED_SOURCE_ANNULI              BOOL  TRUE
+  EXTENDED_SOURCE_FITS                BOOL  F  # perform any of the aperture-like measurements?
+  EXTENDED_SOURCE_ANALYSIS            BOOL  F  # perform any of the aperture-like measurements?
+  EXTENDED_SOURCE_SN_LIM              F32   10.0
+  EXTENDED_SOURCE_PETROSIAN           BOOL  F
+  EXTENDED_SOURCE_ANNULI              BOOL  F
   PSPHOT.STACK.MATCH.PSF.SOURCE       STR   AUTO # which inputs to convolve? (RAW, CNV, AUTO)
   PSPHOT.STACK.TARGET.PSF.AUTO        BOOL  F    # automatically determine target PSF size?
   PSPHOT.STACK.USE.RAW                BOOL  F    # perform photometry & morphology analysis on the raw image
-  @PSPHOT.STACK.TARGET.PSF.FWHM       F32   8.0 10.0 # FWHM of target PSF (if NOT AUTO sized; pixels)
-  RADIAL_APERTURES                    BOOL  T    # calculate flux in circular radial apertures?
+  @PSPHOT.STACK.TARGET.PSF.FWHM       F32   6.0 8.0 # FWHM of target PSF (if NOT AUTO sized; pixels)
+  RADIAL_APERTURES                    BOOL  F    # calculate flux in circular radial apertures?
   RADIAL_APERTURES_SN_LIM             F32   0.0  # S/N limit for radial aperture calculation
   OUTPUT.FORMAT                       STR   PS1_SV1
 
   EXT_FIT_MAX_RADIUS                  F32   50.0
-  PSF_MODEL                           STR   PS_MODEL_QGAUSS 
-  EXT_MODEL                           STR   PS_MODEL_SERSIC
+  PSF_MODEL                           STR   PS_MODEL_GAUSS 
+  EXT_MODEL                           STR   PS_MODEL_QGAUSS
 END
 
@@ -481,4 +485,7 @@
 END
 
+CHIP_CONSTANT_WEIGHTPHOT METADATA
+END
+
 # Recipe overrides for PSPHOT in sdssmosaic
 PSPHOT_NOAP_PSF METADATA
@@ -500,4 +507,11 @@
   OUTPUT.FORMAT		            STR  PS1_DV2       # Format of output file
 END
+
+SSTF_DIFF METADATA
+  PEAKS_NSIGMA_LIMIT_2                F32   4.0             # peak significance threshold  
+  DIFF_STATS			    BOOL TRUE	       # Calculate diff-specific stats
+  OUTPUT.FORMAT		            STR  PS1_DV2       # Format of output file
+END
+
 
 # Recipe overrides for PR reduction
@@ -513,2 +527,3 @@
 	SKY_STAT         STR   FITTED_MEAN_V4  # statistic used to measure background
 END
+
Index: branches/meh_branches/ppstack_test/ippconfig/recipes/reductionClasses.mdc
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/recipes/reductionClasses.mdc	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/recipes/reductionClasses.mdc	(revision 33415)
@@ -164,5 +164,8 @@
 	BACKGROUND_PSWARP	STR	BACKGROUND
         PSVIDEOPHOT             STR     PSVIDEOPHOT
-END
+        STATICSKY_CALIBRATION   STR     STATICSKY_CAL
+END
+
+
 
 # basic science analysis
@@ -216,4 +219,52 @@
 END
 
+# Sweetspot reduction class
+SWEETSPOT		METADATA
+	CHIP_PPIMAGE	  STR	  CHIP
+	CHIP_PSPHOT	  STR	  CHIP
+	WARP_PSWARP	  STR	  WARP
+	STACK_PPSTACK	  STR	  QUICKSTACK
+	STACK_PPSUB	  STR	  STACK
+	STACK_PSPHOT	  STR	  STACK
+	DIFF_PPSUB	  STR	  DIFF
+	DIFF_PSPHOT	  STR	  SSTF_DIFF
+	JPEG_BIN1	  STR	  PPIMAGE_J1
+	JPEG_BIN2	  STR	  PPIMAGE_J2
+	FAKEPHOT	  STR	  FAKEPHOT
+	ADDSTAR		  STR	  ADDSTAR
+	PSASTRO		  STR	  DEFAULT_RECIPE
+	STACKPHOT_PSPHOT  STR     STACKPHOT
+	STACKPHOT_PPSUB   STR     STACKPHOT
+	STACKPHOT_PPSTACK STR     STACKPHOT
+	STACKPHOT_SINGLE_PSPHOT  STR     STACKPHOT_SINGLE
+	BACKGROUND_PPBACKGROUND	STR	BACKGROUND
+	BACKGROUND_PSWARP	STR	BACKGROUND
+        PSVIDEOPHOT             STR     PSVIDEOPHOT
+END
+
+
+CONSTANT_WEIGHTPHOT		METADATA
+	CHIP_PPIMAGE	  STR	  CHIP
+	CHIP_PSPHOT	  STR	  CHIP_CONSTANT_WEIGHTPHOT
+	WARP_PSWARP	  STR	  WARP
+	STACK_PPSTACK	  STR	  STACK
+	STACK_PPSUB	  STR	  STACK
+	STACK_PSPHOT	  STR	  STACK
+	DIFF_PPSUB	  STR	  DIFF
+	DIFF_PSPHOT	  STR	  DIFF
+	JPEG_BIN1	  STR	  PPIMAGE_J1
+	JPEG_BIN2	  STR	  PPIMAGE_J2
+	FAKEPHOT	  STR	  FAKEPHOT
+	ADDSTAR		  STR	  ADDSTAR
+	PSASTRO		  STR	  DEFAULT_RECIPE
+	STACKPHOT_PSPHOT  STR     STACKPHOT
+	STACKPHOT_PPSUB   STR     STACKPHOT
+	STACKPHOT_PPSTACK STR     STACKPHOT
+	STACKPHOT_SINGLE_PSPHOT  STR     STACKPHOT_SINGLE
+	BACKGROUND_PPBACKGROUND	STR	BACKGROUND
+	BACKGROUND_PSWARP	STR	BACKGROUND
+END
+
+
 DEFAULT_DARKTEST	METADATA
 	CHIP_PPIMAGE	  STR	  CHIP_DARKTEST
@@ -239,4 +290,5 @@
 
 
+
 DEFAULT_MASKTEST		METADATA
 	CHIP_PPIMAGE	STR	CHIP_MASKTEST
@@ -260,4 +312,76 @@
 END
 
+# SSTF tests
+SSTF_NOCONV		METADATA
+	CHIP_PPIMAGE	  STR	  CHIP
+	CHIP_PSPHOT	  STR	  CHIP
+	WARP_PSWARP	  STR	  WARP
+	STACK_PPSTACK	  STR	  STACK
+	STACK_PPSUB	  STR	  STACK
+	STACK_PSPHOT	  STR	  STACK
+	DIFF_PPSUB	  STR	  SSTF_DIFF
+	DIFF_PSPHOT	  STR	  DIFF
+	JPEG_BIN1	  STR	  PPIMAGE_J1
+	JPEG_BIN2	  STR	  PPIMAGE_J2
+	FAKEPHOT	  STR	  FAKEPHOT
+	ADDSTAR		  STR	  ADDSTAR
+	PSASTRO		  STR	  DEFAULT_RECIPE
+	STACKPHOT_PSPHOT  STR     STACKPHOT
+	STACKPHOT_PPSUB   STR     STACKPHOT
+	STACKPHOT_PPSTACK STR     STACKPHOT
+	STACKPHOT_SINGLE_PSPHOT  STR     STACKPHOT_SINGLE
+	BACKGROUND_PPBACKGROUND	STR	BACKGROUND
+	BACKGROUND_PSWARP	STR	BACKGROUND
+        PSVIDEOPHOT             STR     PSVIDEOPHOT
+END
+
+SSTF_4SIG		METADATA
+	CHIP_PPIMAGE	  STR	  CHIP
+	CHIP_PSPHOT	  STR	  CHIP
+	WARP_PSWARP	  STR	  WARP
+	STACK_PPSTACK	  STR	  STACK
+	STACK_PPSUB	  STR	  STACK
+	STACK_PSPHOT	  STR	  STACK
+	DIFF_PPSUB	  STR	  DIFF
+	DIFF_PSPHOT	  STR	  SSTF_DIFF
+	JPEG_BIN1	  STR	  PPIMAGE_J1
+	JPEG_BIN2	  STR	  PPIMAGE_J2
+	FAKEPHOT	  STR	  FAKEPHOT
+	ADDSTAR		  STR	  ADDSTAR
+	PSASTRO		  STR	  DEFAULT_RECIPE
+	STACKPHOT_PSPHOT  STR     STACKPHOT
+	STACKPHOT_PPSUB   STR     STACKPHOT
+	STACKPHOT_PPSTACK STR     STACKPHOT
+	STACKPHOT_SINGLE_PSPHOT  STR     STACKPHOT_SINGLE
+	BACKGROUND_PPBACKGROUND	STR	BACKGROUND
+	BACKGROUND_PSWARP	STR	BACKGROUND
+        PSVIDEOPHOT             STR     PSVIDEOPHOT
+END
+
+SSTF_NOCONV_4SIG		METADATA
+	CHIP_PPIMAGE	  STR	  CHIP
+	CHIP_PSPHOT	  STR	  CHIP
+	WARP_PSWARP	  STR	  WARP
+	STACK_PPSTACK	  STR	  STACK
+	STACK_PPSUB	  STR	  STACK
+	STACK_PSPHOT	  STR	  STACK
+	DIFF_PPSUB	  STR	  SSTF_DIFF
+	DIFF_PSPHOT	  STR	  SSTF_DIFF
+	JPEG_BIN1	  STR	  PPIMAGE_J1
+	JPEG_BIN2	  STR	  PPIMAGE_J2
+	FAKEPHOT	  STR	  FAKEPHOT
+	ADDSTAR		  STR	  ADDSTAR
+	PSASTRO		  STR	  DEFAULT_RECIPE
+	STACKPHOT_PSPHOT  STR     STACKPHOT
+	STACKPHOT_PPSUB   STR     STACKPHOT
+	STACKPHOT_PPSTACK STR     STACKPHOT
+	STACKPHOT_SINGLE_PSPHOT  STR     STACKPHOT_SINGLE
+	BACKGROUND_PPBACKGROUND	STR	BACKGROUND
+	BACKGROUND_PSWARP	STR	BACKGROUND
+        PSVIDEOPHOT             STR     PSVIDEOPHOT
+END
+
+
+
 # reduction class specifically for warpwarp diffs:
 WARPWARP	METADATA
@@ -337,9 +461,8 @@
 # quick deep stacks (for unconvolved refstacks and full,deep stacks)
 DEEP_QUICKSTACK            METADATA
-      STACK_PPSTACK   STR      QUICKSTACK_DEEP
-      STACK_PPSUB     STR      STACK
-      STACK_PSPHOT    STR      STACK
-END
-
+       STACK_PPSTACK   STR      QUICKSTACK_DEEP
+       STACK_PPSUB     STR      STACK
+       STACK_PSPHOT    STR      STACK
+END
 
 TESTSTACK            METADATA
Index: branches/meh_branches/ppstack_test/ippconfig/ssp/Makefile.am
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/ssp/Makefile.am	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippconfig/ssp/Makefile.am	(revision 33415)
@@ -0,0 +1,18 @@
+
+installdir = $(datadir)/ippconfig/ssp
+
+install_files = \
+	camera.config \
+	format.config \
+	ppImage.config \
+	psastro.config \
+	psphot.config
+
+install_DATA = $(install_files)
+
+install-data-hook:
+	chmod 0755 $(installdir)
+
+EXTRA_DIST = $(install_files)
+
+ACLOCAL_AMFLAGS = -I m4
Index: branches/meh_branches/ppstack_test/ippconfig/ssp/camera.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/ssp/camera.config	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippconfig/ssp/camera.config	(revision 33415)
@@ -0,0 +1,68 @@
+# Camera configuration file for drop-dead boring camera, consisting of a single chip with single cell.
+
+# File formats that we know about
+FORMATS         METADATA
+	SSP	STR	ssp/format.config
+END
+
+# Description of camera --- all the chips and the cells that comprise them
+FPA     METADATA
+        Chip		STR	Cell
+END
+
+# move this elsewhere?  we need a lookup table to go from filter ID to abstract name
+FILTER.ID       METADATA
+	B	STR	B
+	V	STR	V
+	R	STR	R
+	I	STR	I
+	z	STR	z
+END
+
+# Table of strings to use for the class identifier (see ippdb) according to the file level.
+CLASSID		METADATA
+	FPA	STR	fpa
+END
+
+DVO.CAMERADIR	STR	megacam		# Camera directory for DVO
+
+# convert supplied FPA.OBSTYPE values to abstract exptype names
+OBSTYPE.TABLE METADATA
+  bias 	   STR BIAS
+  zero 	   STR BIAS
+  dark 	   STR DARK
+  flat 	   STR SKYFLAT
+  skyflat  STR SKYFLAT
+  domeflat STR DOMEFLAT
+  object   STR OBJECT
+  science  STR OBJECT
+END
+
+# Recipe options
+RECIPES		METADATA
+        PPIMAGE         STR     ssp/ppImage.config   # Default: all (normal) options on
+	PSPHOT		STR	ssp/psphot.config	# psphot details
+END
+
+PHOTCODE.RULE           STR     {DETECTOR}.{FILTER.ID}.{CHIP.NAME}	# Rule for generating photcode
+
+# reduction classes (recipes which are grouped together)
+REDUCTION	STR	recipes/reductionClasses.mdc
+
+FITSTYPES       STR     recipes/fitstypes.mdc
+
+FILERULES       STR     recipes/filerules-simple.mdc
+
+EXTNAME.RULES METADATA
+  CMF.HEAD STR hdr
+  CMF.DATA STR psf
+  CMF.XSRC STR xsrc
+  CMF.XFIT STR xfit
+  CMF.DETEFF STR deteff
+
+  PSF.HEAD  STR hdr
+  PSF.TABLE STR psf_model
+  PSF.RESID STR psf_resid
+END
+
+MASK.FORCE	BOOL	TRUE	# Force reliance on MASKS recipe if headers not present
Index: branches/meh_branches/ppstack_test/ippconfig/ssp/format.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/ssp/format.config	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippconfig/ssp/format.config	(revision 33415)
@@ -0,0 +1,80 @@
+# Drop-dead simple camera with single chip with single cell
+
+# How to identify this type
+RULE	METADATA
+	SIMPLE		BOOL	TRUE
+###	NAXIS		S32	2
+END
+
+# How to read this data
+FILE	METADATA
+	PHU		STR	FPA	# The FITS file represents an entire FPA
+	EXTENSIONS	STR	NONE	# There are no extensions
+	FPA.OBS		STR	NAXIS	# A PHU keyword for unique identifier within the hierarchy level
+END
+
+# What's in the FITS file?
+CONTENTS	STR	Chip:Cell:amplifier
+
+# Specify the cell data
+CELLS	METADATA
+	amplifier	METADATA
+		CELL.TRIMSEC.SOURCE	STR	VALUE
+		#CELL.TRIMSEC		STR	[0:0,0:0]
+		
+		# alternatives for getting these from the headers
+		# CELL.TRIMSEC.SOURCE	STR	HEADER
+		 CELL.BIASSEC.SOURCE	STR	VALUE
+		 CELL.TRIMSEC		STR	DATASEC
+		 CELL.BIASSEC		STR	BIASSEC
+	END
+END
+
+# How to translate PS concepts into FITS headers
+TRANSLATION	METADATA
+	#FPA.TELESCOPE	STR	TELESCOP
+	FPA.INSTRUMENT	STR	INSTRUME
+	FPA.OBSTYPE	STR	OBSTYPE
+	FPA.AIRMASS	STR	AIRMASS
+	FPA.FILTER	STR	FILTER
+	FPA.POSANGLE	STR	POSANGLE
+	FPA.RA		STR	RA
+	FPA.DEC		STR	DEC
+	FPA.OBJECT	STR	OBJECT
+	FPA.TIME	STR	DATE-OBS # UTC-OBS	# Date and time
+	FPA.EXPOSURE	STR	EXPTIME
+	CELL.EXPOSURE	STR	EXPTIME
+	CELL.DARKTIME	STR	DARKTIME
+	FPA.FOCUS	STR	FOCUS
+	FPA.DETECTOR    STR     DETECTOR
+	CELL.GAIN	STR	CCDGAIN
+#	CELL.TIME	STR	DATE-OBS TIME-OBS	# Date and time
+END
+
+# Default PS concepts that may be specified by value
+DEFAULTS	METADATA
+	FPA.TELESCOPE  STR	SSP	
+	CELL.XBIN	S32	1
+	CELL.YBIN	S32	1
+	FPA.TIMESYS	STR	UTC
+	FPA.RADECSYS	STR	ICRS
+	#CELL.GAIN	F32	1.0
+	CELL.READNOISE	F32	2.0
+	CELL.BAD	F32	0
+	CELL.SATURATION	F32	65535
+#	CELL.BAD	F32	-65535
+	CELL.READDIR	S32	1
+	CELL.TIMESYS	STR	UTC
+	CELL.XPARITY	S32	1
+	CELL.YPARITY	S32	1
+	CHIP.XPARITY	S32	1
+	CHIP.YPARITY	S32	1
+	CELL.X0		S32	0
+	CELL.Y0		S32	0
+END
+
+FORMATS		METADATA
+	FPA.RA		STR	HOURS
+	FPA.DEC		STR	DEGREES
+	FPA.TIME	STR	SEPARATE,YEAR.FIRST
+END
Index: branches/meh_branches/ppstack_test/ippconfig/ssp/ppImage.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/ssp/ppImage.config	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippconfig/ssp/ppImage.config	(revision 33415)
@@ -0,0 +1,120 @@
+### ppImage recipe configuration file for drop-dead simple camera
+
+# List of tasks to perform
+SHUTTER         BOOL    FALSE           # Shutter correction
+DARK		BOOL	TRUE		# Dark correction
+
+BASE.FITS       BOOL    TRUE            # Save base detrended image?
+BIN1.FITS	BOOL    TRUE            # Save 1st binned chip image?
+BIN2.FITS	BOOL    TRUE		# Save 2nd binned chip image?
+
+# binned output image options
+BIN1.XBIN               S32     8
+BIN1.YBIN               S32     8
+BIN2.XBIN               S32     64
+BIN2.YBIN               S32     64
+
+# Overscan subtraction
+OVERSCAN.SINGLE		BOOL	FALSE		# Reduce overscan to a single value?
+OVERSCAN.FIT		STR	POLYNOMIAL	# NONE | POLYNOMIAL | SPLINE
+OVERSCAN.ORDER		S32	3		# Order of polynomial fit
+OVERSCAN.STAT		STR	MEAN		# MEAN | MEDIAN
+
+# How to select the appropriate detrend image
+DETREND.CONSTRAINTS  METADATA
+  BIAS METADATA
+  END
+  MASK METADATA
+  END
+  DARK METADATA
+  END
+  FLAT METADATA
+    FILTER  STR FPA.FILTER
+  END
+  FRINGE METADATA
+    FILTER   STR FPA.FILTER
+  END
+  SHUTTER METADATA
+  END	
+END
+
+########################################################################################
+# Need the following in order to turn off overscan and shutter for the site-level recipe
+########################################################################################
+
+# Overscan subtraction only
+PPIMAGE_O         METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+END
+
+# Dark subtraction only
+PPIMAGE_D         METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+END
+
+# Shutter correction only
+PPIMAGE_S         METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    FALSE           # Bias subtraction
+  DARK            BOOL    FALSE           # Dark subtraction
+  SHUTTER         BOOL    FALSE            # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+END
+
+# Overscan, bias, dark
+PPIMAGE_OBD      METADATA
+  OVERSCAN        BOOL    FALSE           # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    FALSE           # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+END
+
+# Overscan, bias, dark, shutter
+PPIMAGE_OBDS      METADATA
+  OVERSCAN        BOOL    FALSE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    FALSE            # Shutter correction
+  FLAT            BOOL    FALSE           # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+END
+
+# Overscan, bias, dark, shutter, flat-field
+PPIMAGE_OBDSF     METADATA
+  OVERSCAN        BOOL    FALSE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    FALSE            # Shutter correction
+  FLAT            BOOL    TRUE            # Flat-field normalisation
+  MASK            BOOL    FALSE            # Mask bad pixels
+  FRINGE          BOOL    FALSE           # Fringe subtraction
+END
+
+# Overscan, bias, dark, shutter, flat-field, fringe
+PPIMAGE_OBDSFR    METADATA
+  OVERSCAN        BOOL    FALSE            # Overscan subtraction
+  BIAS            BOOL    TRUE            # Bias subtraction
+  DARK            BOOL    TRUE            # Dark subtraction
+  SHUTTER         BOOL    FALSE            # Shutter correction
+  FLAT            BOOL    TRUE            # Flat-field normalisation
+  MASK            BOOL    FALSE           # Mask bad pixels
+  FRINGE          BOOL    TRUE            # Fringe subtraction
+END
Index: branches/meh_branches/ppstack_test/ippconfig/ssp/psastro.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/ssp/psastro.config	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippconfig/ssp/psastro.config	(revision 33415)
@@ -0,0 +1,36 @@
+
+# astrometry matching parameters
+
+# nominal plate scale (microns / pixel)
+# PSASTRO.PLATE.SCALE    F32  13.5
+PSASTRO.PLATE.SCALE    F32  1.0
+
+# pmAstromGridMatch:
+PSASTRO.GRID.MIN.ANGLE F32 -0.0851
+PSASTRO.GRID.MAX.ANGLE F32 +0.0851
+PSASTRO.GRID.DEL.ANGLE F32  0.0170
+
+# pmAstromGridAngle
+# max grid offset in FP units (microns)
+# use plate-scale to make this in pixels?
+# PSASTRO.GRID.OFFSET    F32  10000.
+# PSASTRO.GRID.SCALE     F32    500.
+PSASTRO.GRID.OFFSET    F32   1000.
+PSASTRO.GRID.SCALE     F32     50
+
+# these tweak are in FP units (pixels, currently)
+PSASTRO.TWEAK.SCALE     F32      1
+PSASTRO.TWEAK.RANGE     F32     75
+PSASTRO.TWEAK.SMOOTH    F32      2
+PSASTRO.TWEAK.NSIGMA    F32      3
+
+# pmAstromRadiusMatch
+# use plate-scale to make this in pixels?
+PSASTRO.MATCH.RADIUS   F32    8
+
+# pmAstromMatchFit
+PSASTRO.CHIP.NX        S32      1  # fit order
+PSASTRO.CHIP.NY        S32      1  # fit order
+PSASTRO.CHIP.NITER     S32      3  # fit clipping iterations
+PSASTRO.CHIP.NSIGMA    F32      3  # fit clipping sigmas
+
Index: branches/meh_branches/ppstack_test/ippconfig/ssp/psphot.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/ssp/psphot.config	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippconfig/ssp/psphot.config	(revision 33415)
@@ -0,0 +1,2 @@
+
+SAVE.RESID	BOOL 	TRUE
Index: branches/meh_branches/ppstack_test/ippconfig/system.config
===================================================================
--- branches/meh_branches/ppstack_test/ippconfig/system.config	(revision 32161)
+++ branches/meh_branches/ppstack_test/ippconfig/system.config	(revision 33415)
@@ -16,4 +16,5 @@
 	VYSOS5			STR	vysos5/camera.config		# VYSOS 5in
 	VYSOS20			STR	vysos20/camera.config		# VYSOS 5in
+	SSP			STR	ssp/camera.config		# Pan-STARRS spectroscopic sky probe
 	SIMMOSAIC		STR	simmosaic/camera.config		# Simulated mosaic, for testing
 	SIMTEST			STR	simtest/camera.config		# Simulation test camera
Index: branches/meh_branches/ppstack_test/ippdor/IppSiteTest.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/IppSiteTest.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/IppSiteTest.py	(revision 33415)
@@ -0,0 +1,37 @@
+"""
+This module contains the definitions relative to the installation and
+the configuration of the IPP-Condor framework.
+"""
+class IppSite:
+    """
+    This module contains the definitions relative to the installation
+    and the configuration of the IPP-Condor framework.
+    """
+    def __init__(self):
+        """Not to be instantiated
+        """
+        raise Exception('Not to be instantiated')
+
+    SVN_SOURCE_URL = 'https://svn.pan-starrs.ifa.hawaii.edu/repo/ipp/trunk/ippdor/src'
+    TARGET_DIRECTORY = '/home/schastel/dev/ippdor/test/ipp'
+    TARGET_BINARY_DIRECTORY = '/home/schastel/dev/ippdor/test/bin'
+
+    LOCAL_TMP_DIR = '/tmp/ippdor_processing'
+    NFS_TMP_DIR = '~/ippdor_processing'
+    IPP_BIN_PATH = '~/ipp/default.linux'
+
+class Gpc1:
+    """
+    Information relative to the Gpc1 database
+
+    NOT INTENDED TO BE USABLE
+    """
+    def __init__(self):
+        """Not to be instantiated
+        """
+        raise Exception('Not to be instantiated')
+    HOST = 'somehost.ipp.ifa.hawaii.edu'
+    NAME = 'gpc1'
+    USER = 'username'
+    PASSWORD = 'its_password'
+
Index: branches/meh_branches/ppstack_test/ippdor/README
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/README	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/README	(revision 33415)
@@ -0,0 +1,10 @@
+Installation:
+	./deployment/deploy.py -h
+
+Documentation:
+	cd ./doc && make
+
+Code Quality Metrics (pylint 0.25 required):
+	./quality_metrics.sh
+
+
Index: branches/meh_branches/ppstack_test/ippdor/conf/pylintrc
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/conf/pylintrc	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/conf/pylintrc	(revision 33415)
@@ -0,0 +1,310 @@
+# lint Python modules using external checkers.
+# 
+# This is the main checker controlling the other ones and the reports
+# generation. It is itself both a raw checker and an astng checker in order
+# to:
+# * handle message activation / deactivation at the module level
+# * handle some basic but necessary stats'data (number of classes, methods...)
+# 
+[MASTER]
+
+# Specify a configuration file.
+#rcfile=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Profiled execution.
+profile=no
+
+# Add <file or directory> to the black list. It should be a base name, not a
+# path. You may set this option multiple times.
+ignore=CVS
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# Set the cache size for astng objects.
+cache-size=500
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+
+[MESSAGES CONTROL]
+
+# Enable only checker(s) with the given id(s). This option conflicts with the
+# disable-checker option
+#enable-checker=
+
+# Enable all checker(s) except those with the given id(s). This option
+# conflicts with the enable-checker option
+#disable-checker=
+
+# Enable all messages in the listed categories (IRCWEF).
+#enable-msg-cat=
+
+# Disable all messages in the listed categories (IRCWEF).
+disable-msg-cat=I
+
+# Enable the message(s) with the given id(s).
+#enable-msg=
+
+# Disable the message(s) with the given id(s).
+disable-msg=W0704
+
+
+[REPORTS]
+
+# Set the output format. Available formats are text, parseable, colorized, msvs
+# (visual studio) and html
+output-format=text
+
+# Include message's id in output
+include-ids=no
+
+# Put messages in a separate file for each module / package specified on the
+# command line instead of printing them on stdout. Reports (if any) will be
+# written in a file name "pylint_global.[txt|html]".
+files-output=no
+
+# Tells whether to display a full report or only the messages
+reports=yes
+
+# Python expression which should return a note less than 10 (10 is the highest
+# note). You have access to the variables errors warning, statement which
+# respectively contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (R0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Add a comment according to your evaluation note. This is used by the global
+# evaluation report (R0004).
+comment=no
+
+# Enable the report(s) with the given id(s).
+#enable-report=
+
+# Disable the report(s) with the given id(s).
+#disable-report=
+
+
+# checks for :
+# * doc strings
+# * modules / classes / functions / methods / arguments / variables name
+# * number of arguments, local variables, branches, returns and statements in
+# functions, methods
+# * required module attributes
+# * dangerous default values as arguments
+# * redefinition of function / method / class
+# * uses of the global statement
+# 
+[BASIC]
+
+# Required attributes for module, separated by a comma
+required-attributes=
+
+# Regular expression which should only match functions or classes name which do
+# not require a docstring
+no-docstring-rgx=__.*__
+
+# Regular expression which should only match correct module names
+module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Regular expression which should only match correct module level names
+const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
+
+# Regular expression which should only match correct class names
+class-rgx=[A-Z_][a-zA-Z0-9]+$
+
+# Regular expression which should only match correct function names
+function-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct method names
+method-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct instance attribute names
+attr-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct argument names
+argument-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct variable names
+variable-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct list comprehension /
+# generator expression variable names
+inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=i,j,k,ex,Run,_
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=foo,bar,baz,toto,tutu,tata
+
+# List of builtins function names that should not be used, separated by a comma
+bad-functions=map,filter,apply,input
+
+
+# try to find bugs in the code using type inference
+# 
+[TYPECHECK]
+
+# Tells whether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# List of classes names for which member attributes should not be checked
+# (useful for classes with attributes dynamically set).
+ignored-classes=SQLObject
+
+# When zope mode is activated, add a predefined set of Zope acquired attributes
+# to generated-members.
+zope=no
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E0201 when accessed.
+generated-members=REQUEST,acl_users,aq_parent
+
+
+# checks for
+# * unused variables / imports
+# * undefined variables
+# * redefinition of variable from builtins or from an outer scope
+# * use of variable before assignment
+# 
+[VARIABLES]
+
+# Tells whether we should check for unused import in __init__ files.
+init-import=no
+
+# A regular expression matching names used for dummy variables (i.e. not used).
+dummy-variables-rgx=_|dummy
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+
+# checks for :
+# * methods without self as first argument
+# * overridden methods signature
+# * access only to existent members via self
+# * attributes not defined in the __init__ method
+# * supported interfaces implementation
+# * unreachable code
+# 
+[CLASSES]
+
+# List of interface methods to ignore, separated by a comma. This is used for
+# instance to not check methods defines in Zope's Interface base class.
+ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,__new__,setUp
+
+
+# checks for
+# * external modules dependencies
+# * relative / wildcard imports
+# * cyclic imports
+# * uses of deprecated modules
+# 
+[IMPORTS]
+
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report R0402 must not be disabled)
+import-graph=
+
+# Create a graph of external dependencies in the given file (report R0402 must
+# not be disabled)
+ext-import-graph=
+
+# Create a graph of internal dependencies in the given file (report R0402 must
+# not be disabled)
+int-import-graph=
+
+
+# checks for sign of poor/misdesign:
+# * number of methods, attributes, local variables...
+# * size, complexity of functions, methods
+# 
+[DESIGN]
+
+# Maximum number of arguments for function / method
+max-args=8
+
+# Maximum number of locals for function / method body
+max-locals=25
+
+# Maximum number of return / yield for function / method body
+max-returns=6
+
+# Maximum number of branch for function / method body
+max-branchs=15
+
+# Maximum number of statements in function / method body
+max-statements=150
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=7
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=0
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=30
+
+
+# checks for:
+# * warning notes in the code like FIXME, XXX
+# * PEP 263: source code with non ascii character but no encoding declaration
+# 
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,XXX,TODO
+
+
+# checks for similarities and duplicated code. This computation may be
+# memory / CPU intensive, so you should disable it if you experiments some
+# problems.
+# 
+[SIMILARITIES]
+
+# Minimum lines number of a similarity.
+min-similarity-lines=10
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+
+# checks for :
+# * unauthorized constructions
+# * strict indentation
+# * line length
+# * use of <> instead of !=
+# 
+[FORMAT]
+
+# Maximum number of characters on a single line.
+#max-line-length=80
+max-line-length=120
+
+# Maximum number of lines in a module
+max-module-lines=1000
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string='    '
Index: branches/meh_branches/ppstack_test/ippdor/deployment/Deployment.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/deployment/Deployment.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/deployment/Deployment.py	(revision 33415)
@@ -0,0 +1,226 @@
+#!/usr/bin/env python
+
+"""Utility class for Condor IPP software deployent
+"""
+
+import os
+import subprocess
+import tempfile
+import shutil
+import logging
+
+class Deployment:
+    """Utility class for Condor IPP software deployent
+    """
+    logger = logging.getLogger('ippdor')
+
+    CLASSES_FILENAMES = [ '__init__.py',
+                          'ChipManager.py', 
+                          'CamManager.py', 
+                          'FakeManager.py', 
+                          'WarpManager.py',
+                          'StackManager.py',
+                          'Gpc1Manager.py', 
+                          'helpers.py', 
+                          'constants/__init__.py',
+                          'constants/Globals.py',
+                          'constants/Chip.py',
+                          'constants/Camera.py',
+                          'constants/Fake.py',
+                          'constants/Warp.py',
+                          'constants/Stack.py',
+                          'exceptions/__init__.py',
+                          'exceptions/IppException.py',
+                          'exceptions/UninstantiableIppException.py',
+                          ]
+    EXECUTABLES_FILENAMES = [ 'generate_chip_jobs.py', 
+                              'generate_cam_jobs.py', 
+                              'generate_fake_jobs.py',
+                              'generate_warp_jobs.py', 
+                              'generate_stack_jobs.py', 
+                              'generate_exposure_dag.py',
+                              'ippdor_chip_to_warp.py', 
+                              # 'ippdor_chip_to_stack.py', 
+                              'undo_dag_exposure.py',
+                              'send_email.py', 
+                              'encapsulate_chip.sh', 
+                              'encapsulate_camera.sh', 
+                              'encapsulate_fake.sh',
+                              'encapsulate_warp.sh', 
+                              'encapsulate_stack.sh',
+                              ]
+
+    def __init__(self, 
+                 svn_source_url, 
+                 target_directory, 
+                 target_binary_directory, 
+                 logger = None):
+        """
+        Constructor:
+        
+        svn_source_url: SVN Source URL
+        target_directory: Target Python Directory
+        target_binary_directory: Target Executables Directory
+        logger: optional argument (if None: logging to logging.getLogger('ippdor'))
+        """
+        self.svn_source_url = svn_source_url
+        self.target_directory  = target_directory
+        self.target_binary_directory = target_binary_directory
+        self.temporary_directory = None # Set when necessary
+        if logger is not None:
+            Deployment.logger = logger
+
+    def copy_to_targetdir(self, path_to_file, filename):
+        """
+        Copy a file from the SVN temporary directory to the target
+        directory.
+        """
+        Deployment.logger.info('...... Installing %s' % filename)
+        shutil.copyfile('%s/%s/%s' % (self.temporary_directory, 
+                                      path_to_file, 
+                                      filename), 
+                        '%s/%s' % (self.target_directory, filename))
+
+    def copy_to_bindir(self, path_to_file, filename):
+        """
+        Copy a file from the SVN temporary directory to the target
+        binary directory.
+        """
+        Deployment.logger.info('...... Installing %s' % filename)
+        shutil.copyfile('%s/%s/%s' % (self.temporary_directory, 
+                                      path_to_file, 
+                                      filename), 
+                        '%s/%s' % (self.target_binary_directory, filename))
+
+    def check(self):
+        """
+        Check different elements
+        """
+        Deployment.logger.info('Checking Condor-IPP Softwares Configuration')
+        # Configuration
+        from IppSite import IppSite, Gpc1
+        Deployment.logger.info('Checking definition of IppSite.SVN_SOURCE_URL: %s' 
+                               % IppSite.SVN_SOURCE_URL)
+        Deployment.logger.info('Checking definition of IppSite.TARGET_DIRECTORY: %s' 
+                               % IppSite.TARGET_DIRECTORY)
+        Deployment.logger.info('Checking definition of IppSite.TARGET_BINARY_DIRECTORY: %s' 
+                               % IppSite.TARGET_BINARY_DIRECTORY)
+        Deployment.logger.info('Checking definition of IppSite.LOCAL_TMP_DIR: %s' 
+                               % IppSite.LOCAL_TMP_DIR)
+        Deployment.logger.info('Checking definition of IppSite.NFS_TMP_DIR: %s' 
+                               % IppSite.NFS_TMP_DIR)
+        Deployment.logger.info('Checking definition of IppSite.IPP_BIN_PATH: %s' 
+                               % IppSite.IPP_BIN_PATH)
+        Deployment.logger.info('Checking definition of Gpc1.HOST: %s' 
+                               % Gpc1.HOST)
+        Deployment.logger.info('Checking definition of Gpc1.NAME: %s' 
+                               % Gpc1.NAME)
+        Deployment.logger.info('Checking definition of Gpc1.USER: %s' 
+                               % Gpc1.USER)
+        Deployment.logger.info('Checking definition of Gpc1.PASSWORD: %s[Hidden]' 
+                               % Gpc1.PASSWORD[:0])
+        # target_directory
+        Deployment.logger.info('... Checking write permission to %s' % self.target_directory)
+        extra = ''
+        if not os.path.exists(self.target_directory):
+            os.makedirs(self.target_directory)
+            extra = ' (directory was created)'
+        if not os.access(self.target_directory, os.W_OK):
+            raise IOError('Directory [%s] is not writable' % self.target_directory)
+        Deployment.logger.info('...... OK%s' % extra)
+        # target_binary_directory
+        Deployment.logger.info('... Checking write permission to %s' % self.target_binary_directory)
+        extra = ''
+        if not os.path.exists(self.target_binary_directory):
+            os.makedirs(self.target_binary_directory)
+            extra = ' (directory was created)'
+        if not os.access(self.target_binary_directory, os.W_OK):
+            raise IOError('Directory [%s] is not writable' % self.target_binary_directory)
+        Deployment.logger.info('...... OK%s' % extra)
+        # Check deployment software version?
+        # Anything else?
+        Deployment.logger.info('... Check complete')
+
+    def svn_export(self):
+        """
+        Export the software under configuration into the SVN temporary directory
+        """
+        Deployment.logger.info('Updating Condor-IPP Softwares SVN temporary directory')
+        Deployment.logger.info('... Creating temporary directory:')
+        self.temporary_directory = tempfile.mkdtemp()
+        Deployment.logger.info('...... OK [%s]' % self.temporary_directory)
+        Deployment.logger.info('... Exporting Condor source code to temporary directory')
+        process = subprocess.Popen( ('svn export --force %s %s' % (self.svn_source_url, 
+                                                                   self.temporary_directory)).split(' '),
+                                    stdout = open('/dev/null', 'w'), 
+                                    stderr = open('/dev/null', 'w') )
+        status = process.wait()
+        if status != 0:
+            Deployment.logger.error('Error: svn export failed')
+            raise Exception('svn export failed')
+        Deployment.logger.info('...... OK')
+        Deployment.logger.info('... SVN Update complete')
+        
+    def generate(self):
+        """
+        Generate any file depending on the installation target
+        """
+        Deployment.logger.info('Generating Condor-IPP Softwares')
+        Deployment.logger.debug('--- Nothing to generate yet from %s' % self.svn_source_url)
+        Deployment.logger.info('... Generation complete')
+
+    def deploy(self):
+        """
+        Deploy files on the target
+        """
+        Deployment.logger.info('Deploying Condor-IPP Softwares')
+        Deployment.logger.info('... Installing Site definition class to [%s]' 
+                               % self.target_directory)
+        shutil.copyfile('.tmp_site/IppSite.py',
+                        '%s/IppSite.py' % (self.target_directory))
+        Deployment.logger.info('... Installing Python classes to [%s]' % self.target_directory)
+        for filename in Deployment.CLASSES_FILENAMES:
+            path_elements = filename.split('/')
+            if len(path_elements) > 1:
+                try:
+                    os.makedirs('%s/%s' % (self.target_directory,
+                                           path_elements[0]))
+                except OSError, exc:
+                    Deployment.logger.warn(exc)
+                    Deployment.logger.info('Ignored')
+        for filename in Deployment.CLASSES_FILENAMES:
+            self.copy_to_targetdir('ipp', filename)
+        Deployment.logger.info('... Installing executables to [%s]' % self.target_binary_directory)
+        for filename in Deployment.EXECUTABLES_FILENAMES:
+            self.copy_to_bindir('executables', filename)
+        Deployment.logger.info('... Deployment complete')
+        
+    def clean(self, doit = False):
+        """
+        Clean the installation, i.e. remove files and subdirectories
+        if doit is True (default is False). Otherwise, doesn't do
+        anything...
+        """
+        Deployment.logger.info('Cleaning')
+        if not doit:
+            Deployment.logger.warn('!!! NO DELETION WILL BE PERFORMED SINCE doit IS False')
+        try:
+            Deployment.logger.info('... Deleting temporary directory [%s]' % self.temporary_directory)
+            if doit:
+                for root, dirs, files in os.walk(self.temporary_directory, topdown=False):
+                    Deployment.logger.debug('...... Deleting [%s]' % root)
+                    for name in files:
+                        Deployment.logger.debug('...... Deleting [%s] (in files)' % name)
+                        os.remove(os.path.join(root, name))
+                    for name in dirs:
+                        Deployment.logger.debug('...... Deleting [%s] (in dirs)' % name)
+                        os.rmdir(os.path.join(root, name))
+                os.rmdir(self.temporary_directory)
+        except AttributeError:
+            Deployment.logger.info('... Nothing to be cleaned')
+        Deployment.logger.info('... Cleaning complete')
+
+if __name__ == '__main__':
+    print "Running doctest"
+    import doctest
+    doctest.testmod()
Index: branches/meh_branches/ppstack_test/ippdor/deployment/__init__.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/deployment/__init__.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/deployment/__init__.py	(revision 33415)
@@ -0,0 +1,3 @@
+"""
+This is the deployment module: a module to deploy Condor-IPP software.
+"""
Index: branches/meh_branches/ppstack_test/ippdor/deployment/deploy.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/deployment/deploy.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/deployment/deploy.py	(revision 33415)
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+
+"""
+Utility executable for IPP Condor deployment
+"""
+import sys
+import os
+import logging
+#optparse Deprecated in python 2.7. Will be replaced by argparse
+from optparse import OptionParser 
+from Deployment import Deployment
+
+if __name__ == '__main__':
+    if os.getenv('PYTHONPATH') is not None:
+        print 'Error: Unset PYTHONPATH environment variable to run this program'
+        sys.exit(1)
+    USAGE = """usage: %prog [options] <ipp site definition file>
+
+Deploy the Condor-IPP softwares
+"""
+    PARSER = OptionParser(usage = USAGE)
+    PARSER.set_defaults(verbose=True)
+    PARSER.add_option( '-v', action = 'store_true', 
+                      dest = 'verbose',
+                      help = 'Be verbose',
+                      default = False)
+    PARSER.add_option('-q', action='store_true', 
+                      dest = 'quiet',
+                      help = 'Be quiet',
+                      default = False)
+    (OPTIONS, ARGS) = PARSER.parse_args()    # ... the values are stored in options
+    if len(ARGS) != 1:
+        PARSER.print_help()
+        sys.exit(1)
+    IPP_SITE_FILE = ARGS[0]
+    # Logging setup
+    LOGGER = logging.getLogger('ippdor')
+    FORMATTER = logging.Formatter('%(asctime)s | %(levelname)7s | %(message)s', 
+                                  '%Y-%m-%dT%H:%M:%S')
+    STDOUT = logging.StreamHandler(sys.stdout)
+    STDOUT.setFormatter(FORMATTER)
+    LOGGER.addHandler(STDOUT) 
+    if OPTIONS.verbose:
+        LOGGER.setLevel(logging.DEBUG)
+        LOGGER.debug('Logging level set to DEBUG')
+    elif OPTIONS.quiet:
+        LOGGER.setLevel(logging.ERROR)
+        LOGGER.info('Logging level set to ERROR')
+    else:
+        LOGGER.setLevel(logging.INFO)
+    # Import IppSite definition
+    LOGGER.info('Importing local site definitions')
+    try:
+        os.makedirs('.tmp_site')
+    except OSError:
+        LOGGER.warn('Existing .tmp_site directory')
+    import shutil
+    shutil.copy(IPP_SITE_FILE, '.tmp_site/IppSite.py')
+    INITFILE = open('.tmp_site/__init__.py', 'w')
+    INITFILE.close()
+    sys.path.append('.tmp_site')
+    from IppSite import IppSite
+
+    # Deployment
+    try:
+        DEPLOYMENT = Deployment(IppSite.SVN_SOURCE_URL,
+                                IppSite.TARGET_DIRECTORY,
+                                IppSite.TARGET_BINARY_DIRECTORY)
+        DEPLOYMENT.check()
+        DEPLOYMENT.svn_export()
+        DEPLOYMENT.generate()
+        DEPLOYMENT.deploy()
+        DEPLOYMENT.clean(doit = True)
+    except Exception, e:
+        LOGGER.info('Exception caught')
+        DEPLOYMENT.clean(doit = False)
+        LOGGER.error(e)
+        import traceback
+        traceback.print_exc()
+    # Clean temporary site
+    LOGGER.info('Cleaning temporary site definitions')
+    os.remove('.tmp_site/IppSite.py')
+    try:
+        os.remove('.tmp_site/IppSite.pyc')
+    except OSError:
+        pass # Ignore if not compiled
+    os.remove('.tmp_site/__init__.py')
+    os.rmdir('.tmp_site')
Index: branches/meh_branches/ppstack_test/ippdor/doc/Chip2Stack/stack.tex
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/doc/Chip2Stack/stack.tex	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/doc/Chip2Stack/stack.tex	(revision 33415)
@@ -0,0 +1,97 @@
+\documentclass[10pt]{article}
+\usepackage[hmargin=0.5in,vmargin=0.5in]{geometry}
+
+\begin{document}
+
+\textbf{\Large FOR EACH EXPOSURE}
+
+\begin{description}
+\item[Prerequisites] The exposures to be processed are
+  \textbf{labelled} externally in the chipRun table.
+\item[A. Chip stage] Detail prerequisites \mbox{ }
+  \begin{description}
+  \item[PRE A010] (Script) Generate the Condor job file containing the
+    \verb+chip_imfile.pl+ commands to be run on the cluster
+    \hfill{}\mbox{Equivalent to chip.imfile.load}
+  \item[A010] (Condor) Queue that Condor job file (exactly 60
+    commands/jobs: one for each ota) \hfill{}\mbox{Equivalent to
+      chip.imfile.run}
+  \item[POST A010] (Script) Generate the Condor job file containing the
+    appropriate jobs for reverting \hfill{}\mbox{Equivalent to
+      chip.revert}
+  \item[A020] (Condor) Queue that Condor job file (various, possibly
+    nothing to do)
+  \item[POST A020] (Script) Generate the Condor job file containing the
+    appropriate jobs for reverting \hfill{}\mbox{Equivalent to
+      chip.revert}
+  \item[A030] (Condor) Queue that Condor job file (various, possibly
+    nothing to do)
+  \item[POST A030] (Script) Generate the Condor job file containing the
+    appropriate jobs for reverting \hfill{}\mbox{Equivalent to
+      chip.revert}
+  \item[A040] (Condor) Queue that Condor job file (various, possibly
+    nothing to do)
+  \item[POST A040] (Script) If there is failing otas, send e-mail to
+    operator and stop processing. Otherwise generate stage 050 jobs file.
+  \item[A050] (Script) Advance the exposure with
+    \verb+chiptool -advanceexp+ \hfill{}\mbox{Equivalent to
+      chip.advanceexp}
+  \end{description}
+\item[B. Camera stage] Detail prerequisites \mbox{ }
+  \begin{description}
+  \item[PRE B010] (Script) Generate the Condor job file containing the
+    \verb+camera_exp.pl+ command(s?) \hfill{}\mbox{Equivalent to
+      camera.exp.load}
+  \item[B010] (Condor) Queue that Condor job file
+    \hfill{}\mbox{Equivalent to camera.exp.run}
+  \item[PRE B020] (Script) Generate the Condor job file possibly
+    containing the revert command (possibly nothing to revert)
+    \hfill{}\mbox{Equivalent to camera.revert}
+  \item[B020] (Condor) Queue that Condor job file
+  \item[PRE B030] (Script) Generate the Condor job file possibly
+    containing the revert command (possibly nothing to revert)
+    \hfill{}\mbox{Equivalent to camera.revert}
+  \item[B030] (Condor) Queue that Condor job file
+  \item[PRE B040] (Script) Generate the Condor job file possibly
+    containing the revert command (possibly nothing to revert)
+    \hfill{}\mbox{Equivalent to camera.revert}
+  \item[B040] (Condor) Queue that Condor job file
+  \item[POST B040] (Script) If the camera entry could be reverted, send
+    e-mail to operator and stop processing
+  \end{description}
+\item[C. Fake stage] Detail prerequisites \mbox{ }
+  \begin{description}
+  \item[PRE C010] (Script) Generate the Condor job file containing the
+    \verb+fake_imfile.pl+ command(s?) \hfill{}\mbox{Equivalent to
+      fake.imfile.load}
+  \item[C010] (Condor) Queue that Condor job file
+    \hfill{}\mbox{Equivalent to fake.imfile.run}
+  \item[C020] (Script (or Script+Condor)) Run the
+    \verb+faketool -advanceexp+ command(s?) (or: PRE C020: Generate
+    the Condor job file containing the \verb+faketool -advanceexp+
+    command(s?); C020: Queue that Condor job file)
+    \hfill{}\mbox{Equivalent to fake.advanceexp}
+  \end{description}
+\item[D. Warp stage] Detail prerequisites \hrulefill{}\textbf{BARF}\mbox{ }
+  \begin{description}
+  \item[TODO] \verb+warptool -tooverlap+ \hfill{}\mbox{Equivalent to warp.exp.load}
+  \item[TODO] \verb+warp_overlap.pl+ \hfill{}\mbox{Equivalent to warp.exp.run}
+  \item[TODO] \hfill{}\mbox{Equivalent to warp.revert.overlap}
+  \item[TODO] \verb+warptool -towarp+ \hfill{}\mbox{Equivalent to warp.skycell.load}
+  \item[TODO] \verb+warp_skycell.pl+ \hfill{}\mbox{Equivalent to warp.skycell.run}
+  \item[TODO] \verb++ \hfill{}\mbox{Equivalent to warp.revert.warped}
+  \item[TODO] \verb+ warptool -advancerun+ \hfill{}\mbox{Equivalent to warp.advancerun}
+  \item[Check if needed?] \verb++ \hfill{}\mbox{Equivalent to
+      warp.summary.load and/or warp.summary.run}
+  \end{description}
+\item[E. Stack stage] Detail prerequisites? \mbox{ }
+  \begin{description}
+  \item[TODO] \verb+stacktool -tosum+ \hfill{}\mbox{Equivalent to stack.skycell.load}
+  \item[TODO] \verb+stack_skycell.pl + \hfill{}\mbox{Equivalent to stack.skycell.run}
+  \item[TODO] \verb++ \hfill{}\mbox{Equivalent to stack.revert}
+  \item[Check if needed?] \verb++ \hfill{}\mbox{Equivalent to
+      stack.summary.load and/or stack.summary.run}
+  \end{description}
+\end{description}
+
+\end{document}
Index: branches/meh_branches/ppstack_test/ippdor/doc/Full/Makefile
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/doc/Full/Makefile	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/doc/Full/Makefile	(revision 33415)
@@ -0,0 +1,10 @@
+MAIN	= ippdor
+
+all:
+	pdflatex $(MAIN).tex
+	makeindex $(MAIN)
+	pdflatex $(MAIN).tex
+
+clean:
+	/bin/rm -f $(MAIN).pdf
+	CleanTex.sh
Index: branches/meh_branches/ppstack_test/ippdor/doc/Full/administration.tex
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/doc/Full/administration.tex	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/doc/Full/administration.tex	(revision 33415)
@@ -0,0 +1,34 @@
+
+\chapter{Administration}
+\label{chap-adm}
+
+\section{The \texttt{ippdor} user}
+\label{sec-adm-ippdor}
+
+\texttt{ippdor} is the dedicated user for any processing happening in
+Condor. This user uses the \texttt{bash} shell. The ippdor has no
+particular privilege on the system.
+
+The \texttt{bashrc} file contains some definitions and expansions:
+\begin{itemize}
+\item \texttt{PATH} adds \texttt{\$HOME/local/bin};
+\item \texttt{LD\_LIBRARY\_PATH} adds \texttt{\$HOME/local/lib};
+\item \texttt{PYTHONPATH} adds \texttt{\$HOME/local/share/python} and
+  \texttt{\$HOME/local/lib/python2.6/site-packages};
+\item The \texttt{PSCONFIG} value is fixed to \texttt{current}. See
+  section~\ref{sec-link-ippTODO} for details. The
+  \texttt{psconfig.bash} utility is called using that value.
+\end{itemize}
+
+\texttt{cmake} was locally installed to allow Condor build and
+install. It is now the only external software. It was built using the
+\texttt{--prefix=\$HOME/local} option and can be found in the
+\texttt{\$HOME/softwares} directory.
+
+\section{Deploying Condor on a new host}
+\label{sec-adm-newnode}
+
+\section{Updating Condor}
+\label{sec-adm-update}
+
+To be written when it's happening...
Index: branches/meh_branches/ppstack_test/ippdor/doc/Full/annex.tex
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/doc/Full/annex.tex	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/doc/Full/annex.tex	(revision 33415)
@@ -0,0 +1,103 @@
+\chapter{Annex}
+
+\section{Held Jobs}
+\label{annex-heldjobs}
+
+\null\hfill{\parbox{5in}{\scriptsize This example is related to the
+    DAG shown in section~\ref{sec-oper-tasks}. The A.condor and
+    B.condor jobs are defined as "echo A" and "echo B" commands.}}
+
+If \texttt{condor\_submit\_dag} is executed from a local directory
+(e.g. \textbf{/export/ippc01.0/ippdor/simple\_dag}), the jobs will be
+indefinitely held by Condor. 
+
+\begin{verbatim}
+ippdor@ippc01 /export/ippc01.0/ippdor/simple_dag $ condor_submit_dag AB.dag
+-----------------------------------------------------------------------
+File for submitting this DAG to Condor           : AB.dag.condor.sub
+Log of DAGMan debugging messages                 : AB.dag.dagman.out
+Log of Condor library output                     : AB.dag.lib.out
+Log of Condor library error messages             : AB.dag.lib.err
+Log of the life of condor_dagman itself          : AB.dag.dagman.log
+
+Submitting job(s).
+1 job(s) submitted to cluster 71204.
+-----------------------------------------------------------------------
+\end{verbatim}
+
+This can be seen with the
+\texttt{condor\_q} command:
+
+\begin{verbatim}
+$ condor_q
+
+-- Submitter: ippc01.ifa.hawaii.edu : <10.10.20.80:46944> : ippc01.ifa.hawaii.edu
+ ID      OWNER            SUBMITTED     RUN_TIME ST PRI SIZE CMD               
+71204.0   ippdor         11/25 11:41   0+00:01:27 R  0   12.2 condor_dagman     
+71205.0   ippdor         11/25 11:42   0+00:00:01 H  0   0.0  echo A            
+
+2 jobs; 0 idle, 1 running, 1 held
+\end{verbatim}
+
+``In-depth'' analysis can be performed with \texttt{condor\_q -analyze} command:
+
+\begin{verbatim}
+$ condor_q -analyze 71205.0
+
+-- Submitter: ippc01.ifa.hawaii.edu : <10.10.20.80:46944> : ippc01.ifa.hawaii.edu
+---
+71205.000:  Request is held.
+
+Hold reason: Error from slot7@ipp029.ifa.hawaii.edu: Failed to execute 
+'/bin/echo' with arguments A: Cannot access specified iwd 
+"/export/ippc01.0/ippdor/simple_dag" (errno=2: 'No such file or directory')
+\end{verbatim}
+
+The hold reason makes things obvious as soon as you know that
+\texttt{iwd} stands for "Initial Working Directory". There is no way
+that the Execute node \texttt{ipp029} can access the contents of what is
+on the \texttt{ippc01} local file system.
+
+To fix the problem, stop processing with:
+\begin{verbatim}
+$ condor_rm 71204.0
+\end{verbatim}
+
+Note the creation of the \texttt{rescue} file in
+\texttt{/export/ippc01.0/ippdor/simple\_dag}
+
+Now submit the DAG from any NFS directory:
+\begin{verbatim}
+ippdor@ippc01 ~ $ condor_submit_dag /export/ippc01.0/ippdor/simple_dag/AB.dag 
+
+Running rescue DAG 1
+-----------------------------------------------------------------------
+File for submitting this DAG to Condor           : /export/ippc01.0/ippdor/simple_dag/AB.dag.condor.sub
+Log of DAGMan debugging messages                 : /export/ippc01.0/ippdor/simple_dag/AB.dag.dagman.out
+Log of Condor library output                     : /export/ippc01.0/ippdor/simple_dag/AB.dag.lib.out
+Log of Condor library error messages             : /export/ippc01.0/ippdor/simple_dag/AB.dag.lib.err
+Log of the life of condor_dagman itself          : /export/ippc01.0/ippdor/simple_dag/AB.dag.dagman.log
+
+Submitting job(s).
+1 job(s) submitted to cluster 71208.
+-----------------------------------------------------------------------
+\end{verbatim}
+
+Note that the "rescue DAG 1" is used.
+
+... and the DAG should terminate correctly from the \texttt{condor\_q}
+command and the contents of the \texttt{AB.dag.dagman.out} file:
+\begin{verbatim}
+[...]
+11/25/11 12:13:58  Done     Pre   Queued    Post   Ready   Un-Ready   Failed
+11/25/11 12:13:58   ===     ===      ===     ===     ===        ===      ===
+11/25/11 12:13:58     2       0        0       0       0          0        0
+11/25/11 12:13:58 0 job proc(s) currently held
+11/25/11 12:13:58 All jobs Completed!
+11/25/11 12:13:58 Note: 0 total job deferrals because of -MaxJobs limit (0)
+11/25/11 12:13:58 Note: 0 total job deferrals because of -MaxIdle limit (0)
+11/25/11 12:13:58 Note: 0 total job deferrals because of node category throttles
+11/25/11 12:13:58 Note: 0 total PRE script deferrals because of -MaxPre limit (0)
+11/25/11 12:13:58 Note: 0 total POST script deferrals because of -MaxPost limit (0)
+11/25/11 12:13:58 **** condor_scheduniv_exec.71208.0 (condor_DAGMAN) pid 18455 EXITING WITH STATUS 0
+\end{verbatim}
Index: branches/meh_branches/ppstack_test/ippdor/doc/Full/design.tex
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/doc/Full/design.tex	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/doc/Full/design.tex	(revision 33415)
@@ -0,0 +1,4 @@
+\chapter{Software Design}
+\label{chap-software}
+
+TODO
Index: branches/meh_branches/ppstack_test/ippdor/doc/Full/heldjobs.tex
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/doc/Full/heldjobs.tex	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/doc/Full/heldjobs.tex	(revision 33415)
@@ -0,0 +1,66 @@
+\chapter{Annex}
+
+\section{Held Jobs}
+\label{annex-heldjobs}
+
+\nul\hfill{\scriptsize This example is related to the DAG shown in
+  section~\ref{sec-oper-tasks}. The A.condor and B.condor jobs are
+  defined as "echo A" and "echo B" commands.}
+
+If \texttt{condor\_submit\_dag} is executed from a local directory
+(e.g. \textbf{/export/ippc01.0/ippdor/simple_dag}), the jobs will be
+indefinitely held by Condor. 
+
+\begin{verbatim}
+ippdor@ippc01 /export/ippc01.0/ippdor/simple_dag $ condor_submit_dag AB.dag
+-----------------------------------------------------------------------
+File for submitting this DAG to Condor           : AB.dag.condor.sub
+Log of DAGMan debugging messages                 : AB.dag.dagman.out
+Log of Condor library output                     : AB.dag.lib.out
+Log of Condor library error messages             : AB.dag.lib.err
+Log of the life of condor_dagman itself          : AB.dag.dagman.log
+
+Submitting job(s).
+1 job(s) submitted to cluster 71204.
+-----------------------------------------------------------------------
+\end{verbatim}
+
+This can be seen with the
+\texttt{condor\_q} command:
+
+\begin{verbatim}
+$ condor_q
+
+-- Submitter: ippc01.ifa.hawaii.edu : <10.10.20.80:46944> : ippc01.ifa.hawaii.edu
+ ID      OWNER            SUBMITTED     RUN_TIME ST PRI SIZE CMD               
+71204.0   ippdor         11/25 11:41   0+00:01:27 R  0   12.2 condor_dagman     
+71205.0   ippdor         11/25 11:42   0+00:00:01 H  0   0.0  echo A            
+
+2 jobs; 0 idle, 1 running, 1 held
+\end{verbatim}
+
+``In-depth'' analysis can be performed with \texttt{condor\_q -analyze} command:
+
+\begin{verbatim}
+$ condor_q -analyze 71205.0
+
+-- Submitter: ippc01.ifa.hawaii.edu : <10.10.20.80:46944> : ippc01.ifa.hawaii.edu
+---
+71205.000:  Request is held.
+
+Hold reason: Error from slot7@ipp029.ifa.hawaii.edu: Failed to execute 
+'/bin/echo' with arguments A: Cannot access specified iwd 
+"/export/ippc01.0/ippdor/simple_dag" (errno=2: 'No such file or directory')
+\end{verbatim}
+
+The hold reason makes things obvious (as soon as you know that
+\texttt{iwd} stands for "Initial Working Directory")... There is no
+way that the Execute node \texttt{ipp029} can get the contents of what
+is on \texttt{ippc01} local file system.
+
+To fix the problem, stop processing with:
+\begin{verbatim}
+$ condor_rm 71204.0
+\end{verbatim}
+
+Note the creation of the \texttt{rescue} file in \texttt{/export/ippc01.0/ippdor/simple\_dag}
Index: branches/meh_branches/ppstack_test/ippdor/doc/Full/ippdor.tex
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/doc/Full/ippdor.tex	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/doc/Full/ippdor.tex	(revision 33415)
@@ -0,0 +1,100 @@
+\documentclass[10pt]{report}
+
+\usepackage{vmargin}
+\setpapersize{USletter}
+\setmarginsrb{0.5in}{0.5in}{1.25in}{0.5in}{0pt}{0mm}{0pt}{0mm}
+\usepackage{longtable}
+\usepackage{hyperref}
+\usepackage{makeidx}
+\usepackage{enumitem}
+
+\title{Condor in the IPP}
+\author{S.~Chastel\\Version 0.1}
+\date{2011-11-23}
+
+\makeindex
+
+\newcommand{\CondorSubmit}{\texttt{condor\_submit}}
+\newcommand{\CondorSubmitDag}{\texttt{condor\_submit\_dag}}
+
+\newcommand{\IppPython}[1]{\texttt{\~{ }ippdor/local/share/python/ipp/{#1}}}
+
+\newcommand{\definition}[3]{%
+  %#1: What is displayed in the text
+  %#2: What is displayed in the margin
+  %#3: Entry in the index
+  \emph{#1}\marginpar{\scriptsize #2}%
+  \index{#3}%
+}
+
+\begin{document}
+\maketitle
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\chapter*{Introduction}
+
+This is the documentation detailing how the Condor HTC system is used
+to perform processing for the IPP. Note that it is (currently) not
+supposed to replace the \texttt{pantasks} scheduling system (mainly
+because we choose not to use Condor as a scheduler).
+
+This documentation consists of five parts:
+\begin{enumerate}
+\item This introduction;
+\item Operational Users Manual (page~\pageref{chap-operation}): its
+  targets are the operators of the IPP, that is, persons who want to
+  use the Condor framework to process data for the IPP;
+\item Administration Manual (page~\pageref{chap-adm}): for system
+  administrators. Mainly how to install and upgrade the Condor
+  framework;
+\item Link with the IPP (page~\pageref{chap-ipp}): TODO: Should be
+  part of Software Organization;
+\item Software Organization (page~\pageref{chap-software}): Design,
+  Classes. How to add new features.
+\end{enumerate}
+
+\emph{Note:} Be aware that this documentation is not a substitute to
+the Condor documentation\definition{}{Condor
+  documentation}{Condor!Documentation} which is available from the
+Condor Project Homepage\footnote{when this document was released, it
+  was:
+  \href{http://research.cs.wisc.edu/condor/}{http://research.cs.wisc.edu/condor/}}.
+
+\subsection*{Document History}
+\begin{tabular}{|c|c|p{5in}|}
+  \hline
+  \textbf{Version} & \textbf{Date} & \textbf{Comment} \\
+  \hline
+  0.1 & 2011-11-23 & First draft \\
+  \hline
+\end{tabular}
+
+\subsection*{Acronyms}
+\begin{longtable}{lp{5in}}
+  IPP & Image Processing Pipeline \\
+  IWD & Initial Working Directory \\
+  RTCM & Read the Condor Manual \\
+\end{longtable}
+
+\setcounter{tocdepth}{2}
+\tableofcontents
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\input{operations.tex}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\input{administration.tex}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\input{link_ipp.tex}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\input{design.tex}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\input{annex.tex}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\printindex
+
+\end{document}
Index: branches/meh_branches/ppstack_test/ippdor/doc/Full/link_ipp.tex
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/doc/Full/link_ipp.tex	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/doc/Full/link_ipp.tex	(revision 33415)
@@ -0,0 +1,4 @@
+\chapter{Link with the IPP}
+\label{chap-ipp}
+
+TODO
Index: branches/meh_branches/ppstack_test/ippdor/doc/Full/operations.tex
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/doc/Full/operations.tex	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/doc/Full/operations.tex	(revision 33415)
@@ -0,0 +1,622 @@
+\newcommand{\QueueExposuresWarping}{%
+  \texttt{queue\_exposures\_for\_warping.py}}
+\newcommand{\QueueExposuresStacking}{%
+  \texttt{queue\_exposures\_for\_stacking.py}}
+
+\chapter{Operators Manual}
+\label{chap-operation}
+
+\section{Condor Framework Availability on the IPP Production Cluster}
+
+The Condor framework is not available on all hosts of the IPP
+production cluster. The purpose of this section is to give some
+details about the different nodes which are used and their roles in
+Condor. The set of all machines on which Condor is installed is called
+the \definition{Condor pool}{Condor pool}{Condor!Pool}.
+
+The Condor \definition{central manager}{Central
+  manager}{Condor!Central manager} is \texttt{ippdb03}. From the
+Condor documentation (section~3.1), the role of the central manager is
+to "[collect] information, and [negotiate] between resources and
+resource requests". It "plays a very important part in the Condor pool
+and should be reliable" since "[if it] crashes, no further matchmaking
+can be performed within the Condor system". Moreover "[all] queries go
+to the central manager".
+
+The other nodes of the IPP production cluster where Condor is
+installed are all both Execute and Submit Condor nodes. The complete
+list of those nodes is available in the file
+\texttt{\~{ }ippdor/local/share/CondorNodes}. Note that a node on which
+Condor is installed is not necessary available for processing in
+Condor since it can be deactivated (see section~\ref{sec-oper-hosts}
+for details).
+
+An \definition{Execute}{Execute node}{Condor!Execute node} node is a
+node where processing can be performed. From the Condor manual
+section~3.1, "the only resource that might matter is disk
+space"... Until now, it has never been a matter.
+
+A \definition{Submit}{Submit node}{Condor!Submit node} node is a node
+where Condor jobs can be submitted (see
+section~\ref{sec-ippdor-run}). It is suggested (again this is from the
+Condor manual, section~3.1), that since "every job that you submit
+that is currently running on a remote machine generates another
+process on your submit machine [, ...]  if you have lots of jobs
+running, you will need a fair amount of swap space and/or real
+memory". Until now, jobs were generally submitted from one of the
+\texttt{ippc\emph{xy}} nodes.
+
+Note: For Condor gurus, there is no Checkpoint Server since all
+processing is performed in the \emph{vanilla} Condor universe.
+
+\section{Condor Dedicated User: \texttt{ippdor}}
+\label{sec-oper-login}
+
+To use the Condor framework you need to log into the IPP production
+cluster as the user \texttt{ippdor}. Note that since Condor is not
+installed on \texttt{ippc18}, you will have to login again into
+another node for the condor commands to be usable. \texttt{ipp004} or
+\texttt{ippdb03} are other "open" nodes where you can connect.
+
+Note: The \texttt{ippdor} account uses \texttt{bash}.
+
+Theoretically it should be possible to use the Condor framework with
+your own login but 1) It has not been tested; 2) No support will be
+provided for that; 3) So... RTCM.
+
+As detailed in section~\ref{sec-adm-ippdor}, the \texttt{ippdor}
+account uses files that may be localized on the Condor node. If you
+get a \texttt{command not found} error, make sure you are on one of
+the node where Condor is installed\footnote{that is, the command
+  \texttt{"grep \`{ }hostname\`{ } \~{
+    }ippdor/local/share/CondorNodes"} should display the host name. If
+  you don't see anything, read the first paragraph of this section.}.
+
+\section{Managing Condor Execution on the IPP Production Cluster
+  Nodes}
+\label{sec-oper-hosts}
+
+Note 1: To install Condor on a new node, see
+section~\ref{sec-adm-newnode}.
+
+Note 2: Except for a few home-grown commands, information was
+extracted from the Condor Manual section~3.10.
+
+\subsection{Node Availability}
+\label{sec-oper-hosts-availability}
+\definition{}{Node availability}{Node availability}%
+\definition{}{}{Condor!Node availability}%
+To check if a node can be used by Condor, check the result of the
+\texttt{ipp\_condor\_status <hostname>} command. You should get some
+information about the host from the Condor point of view. For
+instance, \texttt{ipp060} potentially has 12 potential processors for
+Condor execution\footnote{To get more information about what the
+  columns mean, RTCM}:
+\begin{verbatim}
+ippdor@ippc20 ~ $ ipp_condor_status ipp060
+Name               OpSys      Arch   State     Activity LoadAv Mem   ActvtyTime
+
+slot10@ipp060.ifa. LINUX      X86_64 Unclaimed Idle     0.000  4032  0+05:54:29
+slot11@ipp060.ifa. LINUX      X86_64 Owner     Idle     0.400  4032  0+00:00:06
+slot12@ipp060.ifa. LINUX      X86_64 Owner     Idle     1.000  4032  0+00:00:07
+slot1@ipp060.ifa.h LINUX      X86_64 Owner     Idle     0.840  4032  0+00:00:04
+slot2@ipp060.ifa.h LINUX      X86_64 Unclaimed Idle     0.000  4032  0+14:34:09
+slot3@ipp060.ifa.h LINUX      X86_64 Unclaimed Idle     0.000  4032  0+06:56:32
+slot4@ipp060.ifa.h LINUX      X86_64 Unclaimed Idle     0.000  4032  0+14:25:44
+slot5@ipp060.ifa.h LINUX      X86_64 Unclaimed Idle     0.000  4032  0+14:33:57
+slot6@ipp060.ifa.h LINUX      X86_64 Claimed   Idle     0.000  4032  3+11:22:07
+slot7@ipp060.ifa.h LINUX      X86_64 Owner     Idle     1.000  4032  0+00:00:29
+slot8@ipp060.ifa.h LINUX      X86_64 Owner     Idle     0.790  4032  0+00:00:03
+slot9@ipp060.ifa.h LINUX      X86_64 Unclaimed Idle     0.000  4032  0+06:55:39
+                     Total Owner Claimed Unclaimed Matched Preempting Backfill
+
+        X86_64/LINUX    12     5       1         6       0          0        0
+
+               Total    12     5       1         6       0          0        0
+\end{verbatim}
+
+If you don't get anything, the node has been deactivated for Condor
+processing). For instance (while the author writes these lines),
+\texttt{ipp065} will not execute any Condor jobs:
+\begin{verbatim}
+ippdor@ippc20 ~ $ ipp_condor_status ipp065
+ippdor@ippc20 ~ $ 
+\end{verbatim}
+
+\emph{Note:} \texttt{ipp\_condor\_status} is an alias of
+\texttt{condor\_status} defined in \texttt{\~{ }ippdor/.bashrc}.
+
+\subsection{Make a Node Unavailable}
+\label{sec-oper-hosts-remove}
+
+To remove a node from the list of execution nodes, execute the command
+\texttt{ipp\_condor\_off <hostname>} from any node where Condor is
+installed. No job will be started/executed and submitted from that
+host. \texttt{ipp\_condor\_off} is a function defined in \texttt{\~{
+  }ippdor/.bashrc}.
+
+To stop Condor execution and submission from \definition{all}{Stopping
+  Condor on stare nodes}{Stopping Condor on stare nodes}stare nodes,
+the \texttt{stare\_nodes\_off} and \texttt{stare\_nodes\_on} scripts
+are provided. As it can be expected, those scripts just call
+\texttt{ipp\_condor\_off} or \texttt{ipp\_condor\_on} for each of the
+stares nodes.
+
+\subsection{Make a Node Available}
+\label{sec-oper-hosts-available}
+
+To make a node available for Condor after it has been stopped, execute
+the command \texttt{ipp\_condor\_on <hostname>} from any node where
+Condor is installed.
+
+\emph{Note:} If the following message is displayed,
+\begin{verbatim}
+bash: condor_master: command not found
+\end{verbatim}
+it simply means that Condor is not installed on the node where you try
+to start it.
+
+\section{Condor Jobs and DAG}
+
+\subsection{Running Jobs in Condor}
+\label{sec-oper-jobs}
+We will call \definition{job}{job}{job} an elementary unit of
+processing. It consists of the execution of one instance of an
+executable (we will assume that an executable is a program that can be
+called directly from the command line). For instance, "/bin/echo One"
+and "/bin/echo Two" are two jobs, each job being an instance of the
+same executable "/bin/echo".
+
+In our case, defining a correct Condor job is as simple (not to say
+simplistic) as defining:
+\begin{itemize}
+\item The name of the executable;
+\item The Condor universe of execution (in the IPP case, to avoid
+  recompilation of programs we use the \emph{vanilla} universe);
+\item The file where the standard output of the executable will go (on
+  an NFS directory since we don't know where the executable will be
+  run);
+\item The file where the error output of the executable will go (on
+  an NFS directory since we don't know where the executable will be
+  run);
+\item The file where Condor will write the log of the job (on a local
+  directory);
+\item The requirements about the node where execution will take place;
+\item Some argument to tell that we want to keep the environment
+  variables;
+\item The arguments for each execution of the executable.
+\end{itemize}
+
+With the example given on \texttt{/bin/echo}, the contents of the
+condor job file (let's call this file \texttt{echo\_test.condor}) are:
+\begin{verbatim}
+Executable   = /bin/echo
+Universe     = vanilla
+Output       = /home/panstarrs/ippdor/tmp/tests/out.echo_test
+Error        = /home/panstarrs/ippdor/tmp/tests/err.echo_test
+Log          = /tmp/echo_test.log
+Requirements = LoadAvg < 0.3
+
+getenv       = True
+
+Arguments    = One
+Queue
+
+Arguments    = Two
+Queue
+\end{verbatim}
+
+To execute those jobs, they just need to be \definition{submitted}{Job
+  submission}{Job submission} from any Submit node to the Condor
+system with:
+
+\texttt{condor\_submit echo\_test.condor}
+
+The jobs are (usually) not executed immediately and there is no way of
+guessing the order on the jobs execution (if the order of jobs
+execution is a matter, look at paragraph~\ref{sec-oper-tasks}).
+
+Note: From what the author experienced, \texttt{condor\_submit} and
+the condor jobs definition file can be executed and placed on any kind
+of file system (local and/or NFS) which is different from
+\texttt{condor\_submit\_dag}.
+
+\subsection{Running tasks}
+\label{sec-oper-tasks}
+
+\subsubsection{DAGMan}
+
+We will call \definition{task}{task}{task} a set of jobs where jobs
+chaining depend on each other. Condor provides a mechanism of
+organizing jobs according to their dependencies:
+\definition{DAGMan}{DAGMan}{DAGMan} (which stands for Directed Acyclic
+Graph Manager). Exhaustive documentation about DAGMan can be found in
+the Condor documentation section 2.10. In this section I will just
+give details about what is used for the IPP.
+
+\subsubsection{DAG Description File}
+
+To make it simple, a DAG is a high-level description of how jobs are
+sequenced. If the job B has to be performed after the job A, the
+dependency is formalized this way:
+\begin{verbatim}
+JOB    A     /some/directory/A.condor
+JOB    B     /some/directory/B.condor
+PARENT A CHILD B
+\end{verbatim}
+
+Assuming the previous text is saved in the file named \texttt{AB.dag},
+the execution of the DAG can be \definition{submitted}{DAG
+  submission}{DAG submission} from any Submit node to the Condor
+system using:
+
+\texttt{condor\_submit\_dag AB.dag}
+
+The output of that command will look like:
+\begin{verbatim}
+-----------------------------------------------------------------------
+File for submitting this DAG to Condor           : AB.dag.condor.sub
+Log of DAGMan debugging messages                 : AB.dag.dagman.out
+Log of Condor library output                     : AB.dag.lib.out
+Log of Condor library error messages             : AB.dag.lib.err
+Log of the life of condor_dagman itself          : AB.dag.dagman.log
+
+Submitting job(s).
+1 job(s) submitted to cluster 71201.
+-----------------------------------------------------------------------
+\end{verbatim}
+
+All those files are created in the directory of the \texttt{AB.dag}
+file. The \texttt{AB.dag.dagman.out} file is the most interesting log
+file. 
+
+\subsubsection{Files and Directories Locations}
+
+The DAG file can be localized on any file system: local or
+NFS. However for performance concerns, I recommend to put it on the
+local file system of the submit node. Monitor to the log growth rate
+(the logs are not huge though... For instance, full processing of 2300
+MD06 exposures from Chip to Warp stages in the IPP created a log of 15
+MB for the \texttt{.out} file and of a total of about 50 kB for the 4
+other files).
+
+The \texttt{/some/directory} directory (where the condor jobs are
+defined) can be placed on any file system. Again, I suggest to put
+them on the local file system of the Submit node (mostly for
+performance concerns).
+
+\begin{center}
+  \fbox{\parbox{6in}{\textbf{Important note:} The
+      \texttt{condor\_submit\_dag} command \textbf{must} be executed
+      \textbf{from a NFS file system} or the jobs will be indefinitely
+      \texttt{Held} (shown with \texttt{H} status in
+      \texttt{condor\_q}. See section~\ref{annex-heldjobs} for more
+      details.}}
+\end{center}
+
+One restriction is that there cannot be cycles, i.e. circular
+dependencies. For instance, job Egg depends on job Chicken, which
+depend on job Egg. If you submit a DAG with a cycle, looking at the
+\texttt{dagman.out} file, you will see a message like:
+\begin{verbatim}
+[...]
+11/25/11 12:22:02 ERROR: DAG finished but not all nodes are complete -- 
+                  checking for a cycle...
+11/25/11 12:22:02 ... ERROR: a cycle exists in the dag, plese check input
+[...]
+11/25/11 12:22:03 **** condor_scheduniv_exec.71211.0 (condor_DAGMAN) 
+                  pid 20996 EXITING WITH STATUS 1
+\end{verbatim}
+
+\subsubsection{Advanced DAGMan Features}
+\label{sec-oper-dagman}
+
+DAGMan offers other convenient functions for a DAG description. As for
+the other Condor features the reader is invited to check the Condor
+documentation for in-depth details about them.
+
+\paragraph{SubDAG:} A graph node of a DAG can be itself a DAG making
+the development and the analysis of a processing chain easier. A DAG
+in a DAG is called a \definition{SubDAG}{SubDAG}{SubDAG} and can be
+added to a DAG using this syntax:
+\begin{verbatim}
+SUBDAG EXTERNAL <job name> <dag file name>
+\end{verbatim}
+
+\paragraph{Script:} Scripts canbe executed before or after a node
+execution. They can be added using the following syntax:
+\begin{verbatim}
+SCRIPT PRE <job name> <Executable name> [<arguments>]
+SCRIPT POST <job name> <Executable name> [<arguments>]
+\end{verbatim}
+
+From the Condor documentation, "the \texttt{<job name>} specifies the
+node to which the script is attached. The \texttt{<Executable name>}
+specifies the script to be executed, and it may be followed by any
+command line \texttt{<arguments>} to that script". In the IPP context
+we currently use only the \texttt{SCRIPT POST} feature.
+
+\subsection{Condor Interrupted}
+\label{sec-oper-condor-interrupted}
+
+If the execution of Condor DAG is interrupted, for instance, because
+of an operator \texttt{condor\_rm} or because of errors, Condor marks
+the jobs or subdags that were successfully completed by adding the
+\texttt{DONE} keyword to the DAG file. The resulting DAG file is saved
+in a \definition{rescue DAG file}{Rescue DAG file}{Rescue DAG file},
+which is simply a copy of the original DAG file with a
+\texttt{rescue<running ID>} file extension (\texttt{<running ID>} is
+an auto-incrementing integer coded on three digits, starting at
+\texttt{001}). 
+
+To \definition{rescue processing}{Rescuing processing}{Rescuing
+  processing} you just need to execute \textbf{exactly} the same
+submission command: Condor will use the last rescue file that was
+produced.
+
+Note: If you know what you are doing you can create you rown rescue
+file or add/remove \texttt{DONE} keywords to/from a rescue file. Some
+utility programs can do that for you for some IPP stages
+(see~\ref{TODO}).
+
+\section{IPP Tasks}
+\label{sec-oper-ipp}
+
+The IPP defines complex tasks. The purpose of this section is to
+provide a user manual about the programs that need to be run to
+prepare the set of Condor files which are needed to perform some of
+the IPP tasks. Developers should look at chapter~\ref{chap-software}
+for in-depth details.
+
+Each subsection of this section details: 1) The software role; 2) Its
+required inputs; 3) Its outputs; 4) Its IPP products.
+
+Currently the IPP parts that are implemented are:
+\begin{itemize}%
+  \setlength{\itemsep}{0pt}\setlength{\topsep}{0pt}\setlength{\partopsep}{0pt}\setlength{\parsep}{0pt}%
+\item Chip to Warp stages (paragraph~\ref{sec-oper-ipp-chip2warp});
+\item Chip to Stack stages (paragraph~\ref{sec-oper-ipp-chip2stack});
+\end{itemize}
+
+\subsection{Chip to Warp Processing: \QueueExposuresWarping{}}
+\label{sec-oper-ipp-chip2warp}
+
+Since this section is supposed to belong to the operators manual, no
+detail about the software will be provided. The developer will read
+section~\ref{sec-TODO} for such details.
+
+\subsubsection{Role}
+The role of \QueueExposuresWarping{} is to prepare the set of Condor
+and IPP elements which are required to process a set of exposures from
+the Chip to the Warp stage.
+
+The \QueueExposuresWarping{} script has four roles:
+\begin{enumerate}[topsep=0pt, partopsep=0pt, itemsep=0pt, parsep=0pt, itemindent=10pt]
+\item Assert that the label has not already been used and that the
+  input exposures exist;
+\item Create entries in the \texttt{chipRun} table;
+\item Create the DAG files which are necessary for the processing of
+  all exposures;
+\item Display the \texttt{submit\_condor\_dag} command that needs to
+  be run by the operator.
+\end{enumerate}
+
+Note that the operator has to run the \texttt{submit\_condor\_dag}
+command.
+
+\subsubsection{Submit Node}
+
+A node needs to be chosen as Submit node. TODO: add details.
+
+\subsubsection{Inputs}
+The inputs are:
+\begin{itemize}
+\item A \textbf{label} The label used for processing. If the label has
+  already been used, the program is aborted. As for the processing in
+  IPP, don't use spaces or non-ASCII characters for the label (for
+  instance, \texttt{My.New\_Label});
+\item An \textbf{e-mail address} who gets the e-mails (for instance,
+  \texttt{someone@mail.com});
+\item A \textbf{tessellation ID} (e.g. \texttt{MD04.V3});
+\item A \textbf{list of exposures} to be processed (for instance,
+  \texttt{o4926g0064o}, \texttt{o4926g0068o}, \texttt{o4926g0069o},
+  \texttt{o4926g0071o}, and \texttt{o4940g0237o}).
+\end{itemize}
+
+The \QueueExposuresWarping{} script can be called in two ways:
+\begin{itemize}
+\item Either they are provided in a file which has the format of a
+  Properties-style file. With the previous parameters:
+\begin{verbatim}
+label     = My.New_Label
+tessId    = MD04.V3
+recipient = someone@mail.com
+exposure  = o4926g0064o
+exposure  = o4926g0068o
+exposure  = o4926g0069o
+exposure  = o4926g0071o
+# Comment line
+exposure  = o4940g0237o  # Line followed by a comment
+\end{verbatim}
+  Assume that the file is stored under the name 
+  \texttt{~{ }ippdor/examples/ChipsToWarps.ippdor} 
+  the script is called with (note the \texttt{-f} option flag):
+\begin{verbatim}
+queue_exposures_for_warping.py -f ~ippdor/examples/01/ChipsToWarps.ippdor
+\end{verbatim}
+\item Or the input parameters are provided as arguments, that is (the
+  arguments have to be in this order):
+\begin{verbatim}
+queue_exposures_for_warping.py My.New_Label MD04.V3 someone@mail.com \
+      o4926g0064o o4926g0068o o4926g0069o o4926g0071o o4940g0237o
+\end{verbatim}
+\end{itemize}
+
+\subsubsection{Outputs. Entries on GPC1 Database}
+The script writes its outputs to the directory named
+\texttt{<LOCAL\_TMP\_DIR>/<label>} where
+\definition{\emph{\texttt{<LOCAL\_TMP\_DIR>}}}{\texttt{LOCAL\_TMP\_DIR}}{\texttt{LOCAL\_TMP\_DIR}}{}
+is defined in \IppPython{Constants.py} and \texttt{<label>} is the
+label which has been provided to the script. For instance, with the
+current configuration if executed on \texttt{ippc10} in the
+\texttt{ippdor} production environment, and for the label
+\texttt{My.New\_Label}, the various files will be written to the
+directory \texttt{/export/ippc10.0/ippdor/processing/My.New\_Label}.
+
+The main file, also called DAG of DAGs, is \texttt{ch2wp.dag}. Its
+name is defined by
+\texttt{ChipWarpBuilder.Constants.DAG\_OF\_DAGS\_FMT}. This DAG lists
+all the steps which are necessary to build the IPP products from the
+list of exposures.
+
+The script also creates one directory for each exposure in
+\texttt{<LOCAL\_TMP\_DIR>}. 
+
+
+\subsubsection{IPP Products}
+
+Upon successfull completion, the IPP Products are those which are
+produced by successfull chip, cam, fake, and warp stages. Look at the
+IPP documentation to find at what they are.
+
+If the execution of a SubDAG fails, an e-mail is sent to the
+\texttt{recipient} address telling about the nature of the failure. To
+avoid too many failures each processing stage is reverted up to 3
+times. The failure e-mail is therefore sent after 3 failed revert
+attempts. It is expected from the operator to fix the problem and
+\textbf{manually} revert \textbf{all} failures before restarting the
+Condor processing.
+
+%%%%%%%%%%%%%%%%%%% BARF
+
+\subsection{Chip to Stack}
+\label{sec-oper-ipp-chip2stack}
+
+
+\paragraph{Required Inputs}
+
+The name of the utility is \QueueExposuresStacking. Running it requires
+parameters. Those parameters can be provided through command line
+arguments or in a file (which filename is provided through the command
+line interface). Those parameters are:
+\begin{itemize}
+\item \texttt{label} The label used for processing. If the label has already
+  been used, the program is aborted. As for the processing in IPP,
+  don't use spaces or non-ASCII characters for the label.
+\item \texttt{tesselation ID} like MD08.V2
+\item \texttt{E-mail address} who gets the e-mails
+\item \texttt{List of exposures}
+\end{itemize}
+
+The file format is a Properties-style file, for instance:
+\begin{verbatim}
+label     = My.New_Label
+tessId    = MD06.V3
+recipient = someone@mail.com
+exposure  = o4926g0064o
+exposure  = o4926g0068o
+exposure  = o4926g0069o
+exposure  = o4926g0071o
+# Comment line
+exposure  = o4940g0237o  # Followed by a comment
+\end{verbatim}
+
+The command-line equivalent is:
+\begin{verbatim}
+queue\_exposures\_for\_stacking.py \
+    My.New_Label MD06.V3 someone@mail.com \
+    o4926g0064o o4926g0068o o4926g0069o o4926g0071o o4940g0237o
+\end{verbatim}
+
+\paragraph{What \QueueExposuresStacking{} does}
+\begin{enumerate}
+\item It checks that each exposure exists;
+\item It creates the appropriate entries in the \texttt{chipRun} table;
+\item It generates the various Condor DAGs: one global DAG and one DAG
+  for each exposure. The files are located
+\item It displays the \CondorSubmitDag{} command that has to be run
+\end{enumerate}
+
+\section{Running a task}
+\label{ippdor-run}
+The \CondorSubmitDag{} command has to be run on the host where
+\QueueExposuresStacking{} was run. But the \CondorSubmitDag{} has to
+be run from a NFS directory (e.g. the \texttt{ippdor} home directory)
+otherwise Condor will hold jobs.
+
+The output of the \CondorSubmitDag{} command looks like\footnote{Long lines were wrapped}: 
+\begin{verbatim}
+Can't open directory "/export/ippc20.0/ippdor/condor_local/config" as \
+   PRIV_UNKNOWN, errno: 2 (No such file or directory)
+-----------------------------------------------------------------------
+File for submitting this DAG to Condor           : \
+  /export/ippc20.0/ippdor/processing/condor_MD06.V3_01/ch2st.dag.condor.sub
+Log of DAGMan debugging messages                 : \
+  /export/ippc20.0/ippdor/processing/condor_MD06.V3_01/ch2st.dag.dagman.out
+Log of Condor library output                     : \
+  /export/ippc20.0/ippdor/processing/condor_MD06.V3_01/ch2st.dag.lib.out
+Log of Condor library error messages             : \
+  /export/ippc20.0/ippdor/processing/condor_MD06.V3_01/ch2st.dag.lib.err
+Log of the life of condor_dagman itself          : \
+  /export/ippc20.0/ippdor/processing/condor_MD06.V3_01/ch2st.dag.dagman.log
+
+Submitting job(s).
+1 job(s) submitted to cluster 62384.
+-----------------------------------------------------------------------
+\end{verbatim}
+
+Don't bother about the warning on the first line. You should have a
+look (or even \texttt{tail~-f}) the \texttt{ch2st.dag.dagman.out} file
+since it is the place where Condor dagman is logging. It tells how the
+jobs progress, what is happening in terms of successes and failures,
+if jobs are held...
+
+Note the \texttt{1 job(s) submitted to cluster 62384} last line. It
+tells the Condor cluster id which is the mother of all other Condor
+jobs in the DAG. More precisely, if you want to stop the Condor
+processing (see~\ref{ippdor-rm}), you can either stop each Condor job
+individually (somewhat annoying when you have 2000 jobs), or stop the
+mother.
+
+\section{Supervising a Task Execution}
+
+As mentioned in the previous paragraph, the
+\texttt{ch2st.dag.dagman.out} file is the file to look at to get
+information from Condor.
+
+The \texttt{condor\_q} command can be used to monitor the
+progress. The \texttt{long\_condor\_q} command is a home-grown command
+that displays more information about the jobs which are running (it
+doesn't display anything about idle or held jobs).
+
+\texttt{condor\_q -analyze <job\_id>} can be useful to get more
+information about a job.
+
+\section{Ending a task}
+
+\subsubsection{Regular}
+\label{ippdor-end}
+
+When \texttt{condor\_q} does not show the Condor cluster ID.
+
+\subsection{Forced Termination}
+\label{ippdor-rm}
+
+The command to stop a Condor job is \texttt{condor\_rm}. 
+
+An argument is necessary. You will generally use the Condor cluster ID
+that you got from the \CondorSubmitDag{} command. For instance, to
+stop all processing from example shown in paragraph~\ref{ippdor-run}:
+\begin{verbatim}
+condor_rm 62384.0
+\end{verbatim}
+
+The \texttt{-all} option also exists. Use at your own risk... You're
+maybe not the only one to run a Condor instance.
+
+\section{FAQ}
+
+\begin{description}
+\item[All jobs are held] 
+\end{description}
+
Index: branches/meh_branches/ppstack_test/ippdor/quality_metrics.sh
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/quality_metrics.sh	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/quality_metrics.sh	(revision 33415)
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+if [ "$PYTHONPATH" != "" ]; then
+    echo "Unset your PYTHONPATH before running this"
+    exit 1
+fi
+
+/bin/rm -rf ./test
+mkdir -p ./test/ipp ./test/bin
+./deployment/deploy.py IppSiteTest.py
+
+echo "\"\"\"Stupido\n\"\"\"" > ./test/__init__.py
+cp ./test/__init__.py ./test/bin
+
+export PYTHONPATH=`pwd`/test:$HOME/local/lib64/python2.5/site-packages
+pylint -f html --rcfile=conf/pylintrc test > report.html
Index: branches/meh_branches/ppstack_test/ippdor/src/IppSiteSample.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/IppSiteSample.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/IppSiteSample.py	(revision 33415)
@@ -0,0 +1,71 @@
+"""
+This module contains an example of the definitions relative to the
+installation and the configuration of the IPP-Condor framework.
+
+It is not intended to be installed on the production cluster
+"""
+class IppSite:
+    """
+    This module contains the definitions relative to the installation
+    and the configuration of the IPP-Condor framework.
+    """
+    def __init__(self):
+        """Not to be instantiated
+        """
+        raise Exception('Not to be instantiated')
+
+    ###########################################
+    #                                         #
+    # S Y S T E M   C O N F I G U R A T I O N #
+    #                                         #
+    ###########################################
+    # The URL of the SVN repository where the Python source code has
+    # been committed
+    SVN_SOURCE_URL = 'https://svn.pan-starrs.ifa.hawaii.edu/repo/ipp/trunk/ippdor/src'
+    # The directory where the Python classes will be copied.
+    # Note:
+    # - Leave the final 'ipp' directory;
+    # - The directory before the 'ipp' part should be in your
+    #   PYTHONPATH (unless you want to modify sys.path)
+    TARGET_DIRECTORY = '/home/schastel/dev/ippdor/test/ipp'
+    # The directory where the Python (and some bash scripts)
+    # executables will be copied.
+    # This directory 
+    TARGET_BINARY_DIRECTORY = '/home/schastel/dev/ippdor/test/bin'
+
+    ###############################################
+    #                                             #
+    # S O F T W A R E   C O N F I G U R A T I O N #
+    #                                             #
+    ###############################################
+    # Where the local Condor files can be stored
+    LOCAL_TMP_DIR = '/tmp/ippdor_processing'
+    # Where the NFS Condor files can be stored
+    NFS_TMP_DIR = '~/ippdor_processing'
+    # Where the IPP binaries are located
+    IPP_BIN_PATH = '~/ipp/default.linux/bin'
+
+###############################################
+#                                             #
+# D A T A B A S E   C O N F I G U R A T I O N #
+#                                             #
+###############################################
+class Gpc1:
+    """
+    Information relative to the Gpc1 database
+
+    NOT INTENDED TO BE USABLE
+    """
+    def __init__(self):
+        """Not to be instantiated
+        """
+        raise Exception('Not to be instantiated')
+    # The database host name
+    HOST = 'somehost.ipp.ifa.hawaii.edu'
+    # The database name
+    NAME = 'gpc1'
+    # The database username
+    USER = 'username'
+    # The database password
+    PASSWORD = 'its_password'
+
Index: branches/meh_branches/ppstack_test/ippdor/src/__init__.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/__init__.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/__init__.py	(revision 33415)
@@ -0,0 +1,2 @@
+"""Added to suppress a warning
+"""
Index: branches/meh_branches/ppstack_test/ippdor/src/executables/encapsulate_camera.sh
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/executables/encapsulate_camera.sh	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/executables/encapsulate_camera.sh	(revision 33415)
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+#
+# Modified to be adapted to camera processing
+#
+TO_BE_RUN="$*"
+`exec $TO_BE_RUN`
+exit 0
+
Index: branches/meh_branches/ppstack_test/ippdor/src/executables/encapsulate_chip.sh
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/executables/encapsulate_chip.sh	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/executables/encapsulate_chip.sh	(revision 33415)
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+#
+# Modified to be adapted to chip processing
+#
+TO_BE_RUN="$*"
+`exec $TO_BE_RUN`
+exit 0
+
Index: branches/meh_branches/ppstack_test/ippdor/src/executables/encapsulate_fake.sh
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/executables/encapsulate_fake.sh	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/executables/encapsulate_fake.sh	(revision 33415)
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+#
+# Modified to be adapted to fake processing
+#
+TO_BE_RUN="$*"
+`exec $TO_BE_RUN`
+exit 0
+
Index: branches/meh_branches/ppstack_test/ippdor/src/executables/encapsulate_stack.sh
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/executables/encapsulate_stack.sh	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/executables/encapsulate_stack.sh	(revision 33415)
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+#
+# Modified to be adapted to stack processing
+#
+TO_BE_RUN="$*"
+`exec $TO_BE_RUN`
+exit 0
+
Index: branches/meh_branches/ppstack_test/ippdor/src/executables/encapsulate_warp.sh
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/executables/encapsulate_warp.sh	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/executables/encapsulate_warp.sh	(revision 33415)
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+#
+# Modified to be adapted to warp processing
+#
+TO_BE_RUN="$*"
+`exec $TO_BE_RUN`
+exit 0
+
Index: branches/meh_branches/ppstack_test/ippdor/src/executables/generate_cam_jobs.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/executables/generate_cam_jobs.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/executables/generate_cam_jobs.py	(revision 33415)
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+
+"""
+Generates the complete Camera stage DAG for one exposure
+"""
+import sys
+from ipp.CamManager import CamManager as CamManager
+
+#################################################################
+if __name__ == '__main__':
+    if len(sys.argv) != 4 and len(sys.argv) != 5:
+        print 'generate_cam_jobs.py <stage> <label> <exposure_name> [<email recipient>]'
+        print '  e.g.: generate_cam_jobs.py B010 MD08.jtrp o5806g0078o me@nospam.com'
+        print '  Acceptable value for <stage>: DAG, B010, B020, B030, B040, B050'
+        sys.exit(1)
+    STAGE = sys.argv[1]
+    LABEL = sys.argv[2]
+    EXPOSURE_NAME = sys.argv[3]
+    if len(sys.argv) == 5:
+        RECIPIENT = sys.argv[4]
+    else:
+        RECIPIENT = 'schastel@ifa.hawaii.edu'
+    CAMQUERIER = CamManager(LABEL, EXPOSURE_NAME, recipient=RECIPIENT)
+    if STAGE == 'B010':
+        CAMQUERIER.build_new_camera_jobs()
+    elif STAGE == 'B020' or STAGE == 'B030' or STAGE == 'B040':
+        CAMQUERIER.build_camera_revert_jobs(STAGE)
+    elif STAGE == 'B050':
+        CAMQUERIER.advance_camera()
+    elif STAGE == 'DAG':
+        DAG_NAME = CAMQUERIER.build_camera_dag()
+        CAMQUERIER.build_new_camera_jobs()
+        print 'DAG file generated now run:'
+        print 'condor_submit_dag %s' % DAG_NAME
+    else:
+        print 'Stage [%s] not supported' % STAGE
+        sys.exit(2)
+    sys.exit(0)
Index: branches/meh_branches/ppstack_test/ippdor/src/executables/generate_chip_jobs.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/executables/generate_chip_jobs.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/executables/generate_chip_jobs.py	(revision 33415)
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+
+"""
+Generates the full chip DAG for one exposure
+"""
+import sys
+from ipp.ChipManager import ChipManager as ChipManager
+from ipp.exceptions.IppException import IppException
+
+#################################################################
+if __name__ == '__main__':
+    if len(sys.argv) != 4 and len(sys.argv) != 5:
+        print len(sys.argv)
+        print 'generate_chip_jobs.py <stage> <label> <exposure_name> [<email recipient>]'
+        print '  e.g.: generate_chip_jobs.py A010 MD08.jtrp o5806g0078o me@nospam.com'
+        print '  Acceptable value for <stage>: DAG, A020, A030, A040, or A050'
+        print '  Default for e-mail recipient is schastel@ifa.hawaii.edu'
+        sys.exit(1)
+    STAGE = sys.argv[1]
+    LABEL = sys.argv[2]
+    EXPOSURE_NAME = sys.argv[3]
+    if len(sys.argv) == 5:
+        RECIPIENT = sys.argv[4]
+    else:
+        RECIPIENT = 'schastel@ifa.hawaii.edu'
+    CHIP_MANAGER = ChipManager(LABEL, EXPOSURE_NAME, 
+                               RECIPIENT)
+    try:
+        if STAGE == 'A010':
+            CHIP_MANAGER.build_new_chips_jobs()
+        elif STAGE == 'A020' or STAGE == 'A030' or STAGE == 'A040':
+            CHIP_MANAGER.build_chip_revert_jobs(STAGE)
+        elif STAGE == 'A050':
+            CHIP_MANAGER.advance_chips()
+        elif STAGE == 'DAG':
+            DAG_NAME = CHIP_MANAGER.build_chip_dag()
+            CHIP_MANAGER.build_new_chips_jobs()
+            print 
+            print 'Now run: condor_submit_dag %s\n' % DAG_NAME
+        else:
+            raise IppException('Stage [%s] not supported' % STAGE)
+    except IppException, e:
+        print 'IppException caught: ', e
+        print 'Aborting'
+        sys.exit(1)
+    sys.exit(0)
Index: branches/meh_branches/ppstack_test/ippdor/src/executables/generate_exposure_dag.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/executables/generate_exposure_dag.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/executables/generate_exposure_dag.py	(revision 33415)
@@ -0,0 +1,216 @@
+#!/usr/bin/env python
+
+"""
+Generate the complete DAG for ONE exposure from chip to warp
+"""
+import sys
+from ipp.helpers import mkdir
+from ipp.ChipManager import ChipManager as ChipManager
+from ipp.CamManager import CamManager as CamManager
+from ipp.FakeManager import FakeManager as FakeManager
+from ipp.WarpManager import WarpManager as WarpManager
+from ipp.constants.Globals import Globals as GlobalConstants
+
+class DagGenerator(ChipManager, CamManager, FakeManager, WarpManager):
+    """
+    Generation of all DAGs needed for the processing of an exposure
+    """
+    def __init__(self, 
+                 label, 
+                 exposure_name, 
+                 recipient):
+        """
+        Create a connector to the gpc1 database and a local directory
+        dedicated to the exposure
+        """
+        ChipManager.__init__(self, label, exposure_name, recipient)
+        CamManager.__init__(self, label, exposure_name, recipient)
+        FakeManager.__init__(self, label, exposure_name, recipient)
+        WarpManager.__init__(self, label, exposure_name, recipient)
+        self.exposure_directory = '%s/%s/%s' % (GlobalConstants.LOCAL_TMP_DIR, 
+                                                label, exposure_name)
+        mkdir(self.exposure_directory)
+        return
+
+    def build_dag(self, need_configuration_file = True):
+        """Build the DAG to generating all stages from chip to warp
+        """
+        dag_name = '%s/%s.dag' % (self.exposure_directory, self.exposure_name)
+        print "Generating Warp file [%s] for exposure %s (label %s)" % (dag_name,
+                                                                        self.exposure_name,
+                                                                        self.label)
+        output = open(dag_name, 'w')
+        output.write('# Generated DAG\n')
+        output.write('\n')
+        if need_configuration_file:
+            output.write('CONFIG %s/%s/configuration' % (GlobalConstants.LOCAL_TMP_DIR, self.label))
+            output.write('\n')
+        output.write('# Chip stages\n')
+        output.write('JOB\tA010\t%s/A010.jobs\n' % self.chip_directory)
+        output.write('SCRIPT POST A010 %s A020 %s %s %s\n' % (ChipManager.SCRIPT_NAME, 
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('JOB\tA020\t%s/A020.jobs\n' % self.chip_directory)
+        output.write('SCRIPT POST A020 %s A030 %s %s %s\n' % (ChipManager.SCRIPT_NAME, 
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('JOB\tA030\t%s/A030.jobs\n' % self.chip_directory)
+        output.write('SCRIPT POST A030 %s A040 %s %s %s\n' % (ChipManager.SCRIPT_NAME, 
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('JOB\tA040\t%s/A040.jobs\n' % self.chip_directory)
+        output.write('SCRIPT POST A040 %s A050 %s %s %s\n' % (ChipManager.SCRIPT_NAME, 
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('JOB\tA050\t%s/A050.jobs\n' % self.chip_directory)
+        output.write('\n')
+        output.write('# Chip-Cam link\n')
+        output.write('SCRIPT POST A050 %s B010 %s %s %s\n' % (CamManager.SCRIPT_NAME, 
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('\n')
+        output.write('# Cam stages\n')
+        output.write('JOB\tB010\t%s/B010.jobs\n' % self.cam_directory)
+        output.write('SCRIPT POST B010 %s B020 %s %s %s\n' % (CamManager.SCRIPT_NAME, 
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('JOB\tB020\t%s/B020.jobs\n' % self.cam_directory)
+        output.write('SCRIPT POST B020 %s B030 %s %s %s\n' % (CamManager.SCRIPT_NAME, 
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('JOB\tB030\t%s/B030.jobs\n' % self.cam_directory)
+        output.write('SCRIPT POST B030 %s B040 %s %s %s\n' % (CamManager.SCRIPT_NAME, 
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('JOB\tB040\t%s/B040.jobs\n' % self.cam_directory)
+        output.write('SCRIPT POST B040 %s B050 %s %s %s\n' % (CamManager.SCRIPT_NAME,
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('JOB\tB050\t%s/B050.jobs\n' % self.cam_directory)
+        output.write('\n')
+        output.write('# Cam-Fake link\n')
+        output.write('SCRIPT POST B050 %s C010 %s %s %s\n' % (FakeManager.SCRIPT_NAME, 
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('\n')
+        output.write('# Fake stages\n')
+        output.write('JOB\tC010\t%s/C010.jobs\n' % self.fake_directory)
+        output.write('SCRIPT POST C010 %s C020 %s %s %s\n' % (FakeManager.SCRIPT_NAME, 
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('JOB\tC020\t%s/C020.jobs\n' % self.fake_directory)
+        output.write('SCRIPT POST C020 %s C030 %s %s %s\n' % (FakeManager.SCRIPT_NAME, 
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('JOB\tC030\t%s/C030.jobs\n' % self.fake_directory)
+        output.write('SCRIPT POST C030 %s C040 %s %s %s\n' % (FakeManager.SCRIPT_NAME, 
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('JOB\tC040\t%s/C040.jobs\n' % self.fake_directory)
+        output.write('SCRIPT POST C040 %s C050 %s %s %s\n' % (FakeManager.SCRIPT_NAME,
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('JOB\tC050\t%s/C050.jobs\n' % self.fake_directory)
+        output.write('\n')
+        output.write('# Fake-Warp link\n')
+        output.write('SCRIPT POST C050 %s D010 %s %s %s\n' % (WarpManager.SCRIPT_NAME,
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('\n')
+        output.write('# Warp stages\n')
+        output.write('JOB\tD010\t%s/D010.jobs\n' % self.warp_directory)
+        output.write('SCRIPT POST D010 %s D020 %s %s %s\n' % (WarpManager.SCRIPT_NAME, 
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('JOB\tD020\t%s/D020.jobs\n' % self.warp_directory)
+        output.write('SCRIPT POST D020 %s D030 %s %s %s\n' % (WarpManager.SCRIPT_NAME, 
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('JOB\tD030\t%s/D030.jobs\n' % self.warp_directory)
+        output.write('SCRIPT POST D030 %s D040 %s %s %s\n' % (WarpManager.SCRIPT_NAME, 
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('JOB\tD040\t%s/D040.jobs\n' % self.warp_directory)
+        output.write('SCRIPT POST D040 %s D110 %s %s %s\n' % (WarpManager.SCRIPT_NAME, 
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('\n')
+        output.write('# Skycell\n')
+        output.write('JOB\tD110\t%s/D110.jobs\n' % self.warp_directory)
+        output.write('SCRIPT POST D110 %s D120 %s %s %s\n' % (WarpManager.SCRIPT_NAME, 
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('JOB\tD120\t%s/D120.jobs\n' % self.warp_directory)
+        output.write('SCRIPT POST D120 %s D130 %s %s %s\n' % (WarpManager.SCRIPT_NAME, 
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('JOB\tD130\t%s/D130.jobs\n' % self.warp_directory)
+        output.write('SCRIPT POST D130 %s D140 %s %s %s\n' % (WarpManager.SCRIPT_NAME, 
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('JOB\tD140\t%s/D140.jobs\n' % self.warp_directory)
+        output.write('SCRIPT POST D140 %s D210 %s %s %s\n' % (WarpManager.SCRIPT_NAME, 
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('\n')
+        output.write('# Summary\n')
+        output.write('JOB\tD210\t%s/D210.jobs\n' % self.warp_directory)
+        output.write('SCRIPT POST D210 %s D310 %s %s %s\n' % (WarpManager.SCRIPT_NAME, 
+                                                              self.label, 
+                                                              self.exposure_name, self.recipient))
+        output.write('\n')
+        output.write('# Advance\n')
+        output.write('JOB\tD310\t%s/D310.jobs\n' % self.warp_directory)
+        output.write('\n')
+
+        output.write('\n')
+        output.write('# Chip dependencies\n')
+        output.write('PARENT A010 CHILD A020\n')
+        output.write('PARENT A020 CHILD A030\n')
+        output.write('PARENT A030 CHILD A040\n')
+        output.write('PARENT A040 CHILD A050\n')
+        output.write('# Chip-Cam dependencies\n')
+        output.write('PARENT A050 CHILD B010\n')
+        output.write('# Cam dependencies\n')
+        output.write('PARENT B010 CHILD B020\n')
+        output.write('PARENT B020 CHILD B030\n')
+        output.write('PARENT B030 CHILD B040\n')
+        output.write('PARENT B040 CHILD B050\n')
+        output.write('# Cam-Fake dependencies\n')
+        output.write('PARENT B050 CHILD C010\n')
+        output.write('# Fake dependencies\n')
+        output.write('PARENT C010 CHILD C020\n')
+        output.write('PARENT C020 CHILD C030\n')
+        output.write('PARENT C030 CHILD C040\n')
+        output.write('PARENT C040 CHILD C050\n')
+        output.write('# Fake-Warp dependencies\n')
+        output.write('PARENT C050 CHILD D010\n')
+        output.write('# Warp dependencies\n')
+        output.write('PARENT D010 CHILD D020\n')
+        output.write('PARENT D020 CHILD D030\n')
+        output.write('PARENT D030 CHILD D040\n')
+        output.write('PARENT D040 CHILD D110\n')
+        output.write('PARENT D110 CHILD D120\n')
+        output.write('PARENT D120 CHILD D130\n')
+        output.write('PARENT D130 CHILD D140\n')
+        output.write('PARENT D140 CHILD D210\n')
+        output.write('PARENT D210 CHILD D310\n')
+        output.write('\n')
+        output.close()
+        return dag_name
+
+if __name__ == '__main__':
+    if len(sys.argv) != 4:
+        print 'generate_exposure_dag.py <label> <recipient> <exposure_name>'
+        print '  e.g.: generate_exposure_dag.py MD08.jtrp schastel@ifa.hawaii.edu o5806g0078o'
+        sys.exit(1)
+    LABEL = sys.argv[1]
+    RECIPIENT = sys.argv[2]
+    EXPOSURE_NAME = sys.argv[3]
+    GENERATOR = DagGenerator(LABEL, EXPOSURE_NAME, RECIPIENT)
+    DAG_NAME = GENERATOR.build_dag()
+    GENERATOR.build_new_chips_jobs()
+    print 'Now run'
+    print 'condor_submit_dag %s' % DAG_NAME
+    print
+    sys.exit(0)
Index: branches/meh_branches/ppstack_test/ippdor/src/executables/generate_fake_jobs.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/executables/generate_fake_jobs.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/executables/generate_fake_jobs.py	(revision 33415)
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+
+"""
+Generates the fake DAG and Condor jobs for one exposure
+"""
+import sys
+from ipp.FakeManager import FakeManager as FakeManager
+
+#################################################################
+if __name__ == '__main__':
+    if len(sys.argv) != 4 and len(sys.argv) != 5:
+        print 'generate_fake_jobs.py <stage> <label> <exposure_name>'
+        print '  e.g.: generate_fake_jobs.py C010 MD08.jtrp o5806g0078o'
+        print '  Acceptable value for <stage>: DAG, C010, C020, C030, C040, C050'
+        sys.exit(1)
+    STAGE = sys.argv[1]
+    LABEL = sys.argv[2]
+    EXPOSURE_NAME = sys.argv[3]
+    if len(sys.argv) == 5:
+        RECIPIENT = sys.argv[4]
+    else:
+        RECIPIENT = 'schastel@ifa.hawaii.edu'
+    FAKE_MANAGER = FakeManager(LABEL, EXPOSURE_NAME, RECIPIENT)
+    if STAGE == 'C010':
+        FAKE_MANAGER.build_new_fake_jobs()
+    elif STAGE == 'C020' or STAGE == 'C030' or STAGE == 'C040':
+        FAKE_MANAGER.build_fake_revert_jobs(STAGE)
+    elif STAGE == 'C050':
+        FAKE_MANAGER.advance_fake()
+    elif STAGE == 'DAG':
+        DAG_NAME = FAKE_MANAGER.build_fake_dag()
+        FAKE_MANAGER.build_new_fake_jobs()
+        print 'DAG file generated now run:'
+        print 'condor_submit_dag %s' % DAG_NAME
+    else:
+        print 'Stage [%s] not supported' % STAGE
+        sys.exit(2)
+    sys.exit(0)
Index: branches/meh_branches/ppstack_test/ippdor/src/executables/generate_stack_jobs.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/executables/generate_stack_jobs.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/executables/generate_stack_jobs.py	(revision 33415)
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+
+"""
+Generates the complete stack stage DAG for one label
+"""
+import sys
+from ipp.StackManager import StackManager as StackManager
+
+#################################################################
+if __name__ == '__main__':
+    if len(sys.argv) != 4:
+        print 'generate_stack_jobs.py <stage> <label> <recipient>'
+        print '  e.g.: generate_stack_jobs.py E010 MD08.jtrp me@nospam.com'
+        print '  Acceptable value for <stage>: DAG, E010-E060'
+        sys.exit(1)
+    STAGE = sys.argv[1]
+    LABEL = sys.argv[2]
+    RECIPIENT = sys.argv[3]
+    STACK_QUERIER = StackManager(LABEL, RECIPIENT)
+    if STAGE == 'E010':
+        STACK_QUERIER.build_create_stack_entries_job()
+    elif STAGE == 'E020':
+        STACK_QUERIER.build_new_stack_jobs()
+    elif STAGE == 'E030' or STAGE == 'E040' or STAGE == 'E050':
+        STACK_QUERIER.build_revert_stack_jobs(STAGE)
+    elif STAGE == 'E060':
+        STACK_QUERIER.build_stack_summary_jobs()
+    elif STAGE == 'DAG':
+        DAG_NAME = STACK_QUERIER.build_dag()
+        STACK_QUERIER.build_create_stack_entries_job()
+        print 'DAG file generated now run:'
+        print 'condor_submit_dag %s' % DAG_NAME
+    else:
+        print 'Stage [%s] not supported' % STAGE
+        sys.exit(2)
+    sys.exit(0)
Index: branches/meh_branches/ppstack_test/ippdor/src/executables/generate_warp_jobs.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/executables/generate_warp_jobs.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/executables/generate_warp_jobs.py	(revision 33415)
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+
+"""
+Generates the complete warp stage DAG for one exposure
+"""
+import sys
+from ipp.WarpManager import WarpManager as WarpManager
+
+#################################################################
+if __name__ == '__main__':
+    if len(sys.argv) != 4 and len(sys.argv) != 5:
+        print 'generate_warp_jobs.py <stage> <label> <exposure_name> [recipient]'
+        print '  e.g.: generate_warp_jobs.py D010 MD08.jtrp o5806g0078o'
+        print '  Acceptable value for <stage>: DAG, D010-D040, D110-D150, D210, D310'
+        sys.exit(1)
+    STAGE = sys.argv[1]
+    LABEL = sys.argv[2]
+    EXPOSURE_NAME = sys.argv[3]
+    if len(sys.argv) == 5:
+        RECIPIENT = sys.argv[4]
+    else:
+        RECIPIENT = 'schastel@ifa.hawaii.edu'
+    WARP_QUERIER = WarpManager(LABEL, EXPOSURE_NAME, RECIPIENT)
+    if STAGE == 'D010':
+        WARP_QUERIER.build_new_exp_warp_jobs()
+    elif STAGE == 'D020' or STAGE == 'D030' or STAGE == 'D040':
+        WARP_QUERIER.build_warp_exp_revert_jobs(STAGE)
+    elif STAGE == 'D110':
+        WARP_QUERIER.build_new_skycell_warp_jobs()
+    elif STAGE == 'D120' or STAGE == 'D130' or STAGE == 'D140':
+        WARP_QUERIER.build_warp_skycell_revert_jobs(STAGE)
+    elif STAGE == 'D210':
+        WARP_QUERIER.build_new_summary_warp_jobs()
+    elif STAGE == 'D310':
+        WARP_QUERIER.advance_warp()
+    elif STAGE == 'DAG':
+        DAG_NAME = WARP_QUERIER.build_warp_dag()
+        WARP_QUERIER.build_new_exp_warp_jobs()
+        print 'DAG file generated now run:'
+        print 'condor_submit_dag %s' % DAG_NAME
+    else:
+        print 'Stage [%s] not supported' % STAGE
+        sys.exit(2)
+    sys.exit(0)
Index: branches/meh_branches/ppstack_test/ippdor/src/executables/ippdor_chip_to_stack.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/executables/ippdor_chip_to_stack.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/executables/ippdor_chip_to_stack.py	(revision 33415)
@@ -0,0 +1,234 @@
+#!/usr/bin/env python
+
+import sys
+import os
+import subprocess
+from ipp.Gpc1Manager import Gpc1Manager as Gpc1Manager
+from ipp.StackManager import StackManager as StackManager
+from ipp.helpers import mkdir
+from ipp.IppException import IppException as IppException
+
+class StackBuilder(Gpc1Manager):
+    def __init__(self,
+                 label,
+                 recipient,
+                 host='ippdb01.ifa.hawaii.edu', 
+                 user='ipp', 
+                 passwd='ipp'):
+        """Create a connector to the gpc1 database
+        """
+        Gpc1Manager.__init__(self, recipient=recipient, host=host, user=user, passwd=passwd)
+        self.label = label
+        if self.isLabelUsed():
+            print 'Label [%s] already used' % label
+            print 'IF YOU KNOW WHAT YOU ARE DOING, delete all chips with label %s in chipRun:' % label
+            print "  DELETE FROM chipImfile USING chipImfile, chipRun WHERE chipImfile.chip_id=chipRun.chip_id AND label = '%s';" % label
+            print "  DELETE FROM chipRun WHERE label = '%s';" % label
+            print 'OR'
+            import datetime
+            print 'Use another label (e.g. %s_%s)' % (label, datetime.datetime.now().strftime("%Y%m%dT%H%M%S"))
+            raise IppException('Label [%s] already used' % label)
+        self.rootDirectory = '%s/%s' % (Constants.ippdorLocalTmp, label)
+        mkdir(self.rootDirectory)
+
+    def isLabelUsed(self):
+        query = """SELECT label from chipRun WHERE label = '%s' LIMIT 1""" % self.label
+        values = self.multipleQuery(query)
+        return values is not None and len(values) != 0
+
+    def isExposureNameValid(self, exposureName):
+        query = """SELECT exp_id FROM rawExp WHERE exp_name='%s'""" % exposureName
+        return int(self.simpleQuery(query)[0])
+
+    def buildCondorFiles(self, exposureNames):
+        # Create DAGs for each exposure
+        for exposureName in exposureNames:
+            print '\tCreating DAG for exposure [%s]' % (exposureName)
+            command = "/home/panstarrs/ippdor/local/bin/generate_exposure_dag.py %s %s %s" % (self.label, 
+                                                                                              self.recipient, 
+                                                                                              exposureName)
+            try:
+                subprocess.call(command.split(' '))
+            except OSError:
+                print 'Cannot find /home/panstarrs/ippdor/local/bin/generate_exposure_dag.py, Trying with anonymous generate_exposure_dag.py'
+                command = "generate_exposure_dag.py %s %s %s" % (self.label, 
+                                                                 self.recipient,
+                                                                 exposureName)
+                subprocess.call(command.split(' '))
+        # Generate stack generation job file
+        self.stackGenerationJob()
+        # Generate configuration file
+        self.generateConfiguration()
+        # Generate DAG of DAGs
+        dagOfdagsName = self.generateDAG(exposureNames)
+        return dagOfdagsName
+
+    def generateConfiguration(self):
+        # Generate configuration file
+        output = open('%s/configuration' % self.rootDirectory, 'w')
+        output.write('# DAGMAN_ABORT_ON_SCARY_SUBMIT = FALSE\n')
+        output.close()
+
+    def stackGenerationJob(self):
+        output = open('%s/stack_generation.job' % self.rootDirectory, 'w')
+        output.write('Executable = %s\n' % StackManager.scriptName)
+        output.write('Universe = vanilla\n')
+        output.write('Output = /home/panstarrs/ippdor/tmp/02test/out.%s.stack_generation\n' % (self.label))
+        output.write('Error = /home/panstarrs/ippdor/tmp/02test/err.%s.stack\n' % (self.label))
+        output.write('Log = /tmp/stack_generation.log\n')
+        output.write('Requirements = %s\n' % Constants.REQUIREMENTS)
+        output.write('\n')
+        output.write('getenv = True\n')
+        output.write('\n')
+        output.write('Arguments = DAG %s %s\n' % (self.label, self.recipient))
+        output.write('Queue\n')
+        output.write('\n')
+        output.close()
+
+    def generateDAG(self, exposureNames):
+        dagName = '%s/ch2st.dag' % self.rootDirectory
+        output = open(dagName, 'w')
+        output.write('# Chip to warp DAGs\n')
+        chip2warpNodesList = ''
+        for exposureName in exposureNames:
+            output.write('SUBDAG EXTERNAL CHIP2WARP_%s %s/%s/%s.dag\n' % (exposureName, 
+                                                                          self.rootDirectory,
+                                                                          exposureName,
+                                                                          exposureName))
+            chip2warpNodesList = '%s CHIP2WARP_%s' % (chip2warpNodesList, exposureName)
+        output.write('\n')
+        output.write('# Generate Stack DAGs\n')
+        output.write('JOB GENERATE_STACK %s/stack_generation.job\n' % self.rootDirectory)
+        output.write('\n')
+        output.write('# Stack DAGs\n')
+        output.write('SUBDAG EXTERNAL STACK %s/stack/stack.dag\n' % (self.rootDirectory))
+        output.write('\n')
+        output.write('# Node Dependencies\n')
+        output.write('PARENT %s CHILD GENERATE_STACK\n' % chip2warpNodesList)
+        output.write('PARENT GENERATE_STACK CHILD STACK\n')
+        output.write('\n')
+        output.write('# Retries\n')
+        for exposureName in exposureNames:
+            output.write('# RETRY CHIP2WARP_%s 3\n' % exposureName)
+        output.write('# RETRY GENERATE_STACK 3\n')
+        output.write('# RETRY STACK 3\n')        
+        output.write('\n')
+        output.close()
+        return dagName
+
+def loadInputFile(fileName):
+    """File format is the following:
+label    = PreferredLabel
+tessId   = MD08.V2
+exposure = exposure1
+exposure = exposure2
+exposure = exposure3
+exposure = exposure4
+# Comment line
+exposure = exposure5    # Followed by a comment
+"""
+    import re
+    exposureNames = []
+    inFile = open(fileName)
+    labelNotDefined = True
+    tessIdNotDefined = True
+    recipientNotDefined = True
+    for originalLine in inFile:
+        line = originalLine[:-1]
+        line = re.sub('#.*', '', line)
+        line = re.sub('\s+', '', line)
+        #print 'Line after replacements: [%s]' % line
+        if line != '':
+            elements = line.split('=')
+            if len(elements) != 2:
+                print 'Cannot interpret line [%s]' % originalLine[:-1]
+                print 'Check your file format:'
+                print loadInputFile.__doc__
+                raise IppException('Error: Input file format [%s]' % fileName)
+            else:
+                if elements[0] == 'label':
+                    if labelNotDefined:
+                        label = elements[1]
+                        labelNotDefined = False
+                    else:
+                        raise IppException('label defined twice in [%s]' % fileName)
+                elif elements[0] == 'tessId':
+                    if tessIdNotDefined:
+                        tessId = elements[1]
+                        tessIdNotDefined = False
+                    else:
+                        raise IppException('tessId defined twice in [%s]' % fileName)
+                elif elements[0] == 'exposure':
+                    exposureNames.append(elements[1])
+                elif elements[0] == 'recipient':
+                    if recipientNotDefined:
+                        recipient = elements[1]
+                        recipientNotDefined = False
+                    else:
+                        raise IppException('recipient defined twice in [%s]' % fileName)
+                else:
+                    print 'Unknown key [%s] in [%s]' % (elements[0], fileName)
+                    print loadInputFile.__doc__
+                    raise IppException('Unknown key [%s] in [%s]' % (elements[0], fileName))
+    return (label, tessId, exposureNames, recipient)
+
+if __name__ == '__main__':
+    if len(sys.argv)<2 or sys.argv[1].startswith('-h') or sys.argv[1].startswith('--h'):
+        print '<Usage>: %s <label> <tessellation> <email recipient> [exposures list]' % sys.argv[0]
+        print '         %s -f <input_file>' % sys.argv[0]
+        print '   If <label> has already been used, the program will abort'
+        print '   [exposures list] must have more than 2 exposures'
+        print
+        print loadInputFile.__doc__
+        sys.exit(1)
+    #TODO: Implement proper arguments management
+    if sys.argv[1] == '-f':
+        (label, tessellation, exposureNames, recipient) = loadInputFile(sys.argv[2])
+    else:
+        argStart = 1
+        label = sys.argv[argStart]
+        argStart += 1
+        tessellation = sys.argv[argStart]
+        argStart += 1
+        recipient = sys.argv[argStart]
+        argStart += 1
+        exposureNames = sys.argv[argStart:]
+        argStart += 1
+    if len(exposureNames) < 2:
+        print 'You need more than 2 exposures to build a stack'
+        print 'Aborting'
+        sys.exit(4)
+    if os.getenv('USER') == 'ippdor':
+        builder = StackBuilder(label, recipient)
+    else:
+        builder = StackBuilder(label, recipient, host='ipp0012.ifa.hawaii.edu')
+    # Check exposures        
+    expIds = dict()
+    for exposureName in exposureNames:
+        print 'Checking exposure [%s]' % exposureName
+        try:
+            expIds[exposureName] = builder.isExposureNameValid(exposureName)
+        except IndexError:
+            print 'Exposure [%s] is not valid' % exposureName
+            print 'Aborting'
+            sys.exit(3)
+    # Create entries in chiptool
+    print 'Stacking %d exposures with label [%s]' % (len(exposureNames), label)
+    for exposureName in exposureNames:
+        expId = expIds[exposureName]
+        print '\tCreating chipRun entry for exposure [%s]/%d' % (exposureName, expId)
+        command = "/home/panstarrs/ippdor/psconfig/20110920/20110920.lin64/bin/chiptool -dbname gpc1 -definebyquery -exp_id %d" % expId
+        command = "%s -set_label %s" % (command, label)
+        command = "%s -set_workdir neb://\@HOST\@.0/gpc1/%s" % (command, label)
+        command = "%s -set_tess_id %s" % (command, tessellation)
+        command = "%s -set_data_group %s_dg" % (command, label)
+        command = "%s -set_note \'chip/stack_reprocessing\' -set_end_stage warp" % (command)
+        try:
+            subprocess.call(command.split(' '))
+        except OSError:
+            print 'System call to [%s] failed. Continuing though...' % command
+    # Generate Condor jobs, subdags and dag
+    dagOfDagsName = builder.buildCondorFiles(exposureNames)
+    # Show the command to be run
+    print 'Now run:'
+    print 'condor_submit_dag -maxidle 1000 -maxjobs 500 %s' % dagOfDagsName
Index: branches/meh_branches/ppstack_test/ippdor/src/executables/ippdor_chip_to_warp.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/executables/ippdor_chip_to_warp.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/executables/ippdor_chip_to_warp.py	(revision 33415)
@@ -0,0 +1,260 @@
+#!/usr/bin/env python
+
+"""
+This is the script that generates:
+- DAG for each stage from chip to warp for each exposure 
+- DAG of all these DAGs
+"""
+
+import sys
+import subprocess
+import datetime
+
+from ipp.Gpc1Manager import Gpc1Manager as Gpc1Manager
+from ipp.helpers import mkdir
+from ipp.constants.Globals import Globals as GlobalConstants
+from ipp.exceptions.IppException import IppException
+from ipp.exceptions.UninstantiableIppException import UninstantiableIppException
+
+class Constants:
+    """Constants which are used only in this script
+    """
+    def __init__(self):
+        raise UninstantiableIppException()
+
+    # In LABEL_ALREADY_USED, replace:
+    # - @LABEL@ with label value 
+    LABEL_ALREADY_USED = """
+Error: Label [@LABEL@] already used.
+
+To fix the problem:
+1) Either use another label (e.g. @LABEL@_%s)
+2) Or __IF YOU KNOW WHAT YOU ARE DOING__, delete all chips with label [@LABEL@] in chipRun:
+  DELETE FROM chipImfile USING chipImfile, chipRun WHERE chipImfile.chip_id=chipRun.chip_id AND label = '@LABEL@';
+  DELETE FROM chipRun WHERE label = '@LABEL@';
+""" % (datetime.datetime.now().strftime("%Y%m%dT%H%M%S"))
+    # Format string for the DAG of DAGs:
+    # - #1: target directory
+    DAG_OF_DAGS_FMT = "%s/ch2wp.dag"
+
+class ChipWarpBuilder(Gpc1Manager):
+    """Main class for DAG/Condor job files building
+    """
+    def __init__(self,
+                 label,
+                 recipient):
+        """
+        The constructor:
+        - creates a connector to the gpc1 database
+        - checks if the label has already been used
+        - creates a local directory
+        """
+        Gpc1Manager.__init__(self, recipient=recipient)
+        self.label = label
+        if self.is_label_used():
+            print Constants.LABEL_ALREADY_USED.replace('@LABEL@', self.label)
+            raise IppException('Label [%s] already used' % label)
+        self.root_directory = '%s/%s' % (GlobalConstants.LOCAL_TMP_DIR, label)
+        mkdir(self.root_directory)
+
+    def build_condor_files(self, exposures_names):
+        """Generate all condor subdag files
+        """
+        # Create DAGs for each exposure
+        for exposure_name in exposures_names:
+            print '\tCreating DAG for exposure [%s]' % (exposure_name)
+            command = GlobalConstants.GENERATE_EXPOSURE_DAG % (self.label, 
+                                                               self.recipient, 
+                                                               exposure_name)
+            try:
+                subprocess.call(command.split(' '))
+            except OSError:
+                print 'Cannot find %s' % GlobalConstants.GENERATE_EXPOSURE_DAG
+                command = "generate_exposure_dag.py %s %s %s" % (self.label, 
+                                                                 self.recipient,
+                                                                 exposure_name)
+                subprocess.call(command.split(' '))
+        # Generate completion job file
+        self.completion_job()
+        # Generate configuration file
+        self.generate_configuration_file()
+        # Generate DAG of DAGs
+        dag_of_dags_name = self.generate_dag(exposures_names)
+        return dag_of_dags_name
+
+    def generate_configuration_file(self):
+        """Method generating the condor configuration file (empty now)
+        """
+        output = open('%s/configuration' % self.root_directory, 'w')
+        output.write('# File empty for now\n')
+        output.close()
+
+    def completion_job(self):
+        """Method generating the condor job file for completion
+        """
+        output = open('%s/chip_to_warp_completion.job' % self.root_directory, 'w')
+        output.write('Executable = %s/send_email.py\n' % GlobalConstants.IPPDOR_BINARY_PATH)
+        output.write('Universe = vanilla\n')
+        output.write('Output = %s/%s.out.ch2wp.completion\n' % (GlobalConstants.NFS_TMP_DIR, 
+                                                                self.label))
+        output.write('Error = %s/%s.err.ch2wp.completion\n' % (GlobalConstants.NFS_TMP_DIR, 
+                                                               self.label))
+        output.write('Log = %s/%s_ch2wp.completion.log\n' % (GlobalConstants.LOCAL_TMP_DIR, 
+                                                             self.label))
+        output.write('Requirements = %s\n' % (GlobalConstants.REQUIREMENTS))
+        output.write('Notification = Error\n')
+        output.write('\n')
+        output.write('getenv = True\n')
+        output.write('\n')
+        output.write('Arguments =  %s %s %s/%s_final_email.txt\n' % (self.label, 
+                                                                     self.recipient,
+                                                                     GlobalConstants.NFS_TMP_DIR,
+                                                                     self.label))
+        output.write('Queue\n')
+        output.write('\n')
+        output.close()
+        # E-mail content
+        output = open('%s/%s_final_email.txt' % (GlobalConstants.NFS_TMP_DIR,
+                                                 self.label), 'w')
+        output.write('Condor DAG complete: label [%s]\n' % self.label)
+        output.close()
+
+    def generate_dag(self, exposures_names):
+        """Method generating the DAG of DAGs
+        """
+        dag_name = Constants.DAG_OF_DAGS_FMT % self.root_directory
+        output = open(dag_name, 'w')
+        output.write('# Chip to warp DAGs\n')
+        chip2warp_nodes_list = ''
+        for exposure_name in exposures_names:
+            output.write('SUBDAG EXTERNAL CHIP2WARP_%s %s/%s/%s.dag\n' % (exposure_name, 
+                                                                          self.root_directory,
+                                                                          exposure_name,
+                                                                          exposure_name))
+            chip2warp_nodes_list = '%s CHIP2WARP_%s' % (chip2warp_nodes_list, exposure_name)
+        output.write('\n')
+        output.write('# Send an e-mail when done\n')
+        output.write('JOB COMPLETION_EMAIL %s/chip_to_warp_completion.job\n' % self.root_directory)
+        output.write('\n')
+        output.write('# Node Dependencies\n')
+        output.write('PARENT %s CHILD COMPLETION_EMAIL\n' % chip2warp_nodes_list)
+        output.write('\n')
+        output.close()
+        return dag_name
+
+def load_input_file(filename):
+    """File format is the following:
+label    = PreferredLabel
+tessId   = MD08.V2
+exposure = exposure1
+exposure = exposure2
+exposure = exposure3
+exposure = exposure4
+# Comment line
+exposure = exposure5    # Followed by a comment
+"""
+    import re
+    exposures_names = []
+    input_file = open(filename)
+    label_not_defined = True
+    tessellation_not_defined = True
+    recipient_not_defined = True
+    for original_line in input_file:
+        line = original_line[:-1]
+        line = re.sub('#.*', '', line)
+        line = re.sub('\s+', '', line)
+        #print 'Line after replacements: [%s]' % line
+        if line != '':
+            elements = line.split('=')
+            if len(elements) != 2:
+                print 'Cannot interpret line [%s]' % original_line[:-1]
+                print 'Check your file format:'
+                print load_input_file.__doc__
+                raise IppException('Error: Input file format [%s]' % filename)
+            else:
+                if elements[0] == 'label':
+                    if label_not_defined:
+                        label = elements[1]
+                        label_not_defined = False
+                    else:
+                        raise IppException('label defined twice in [%s]' % filename)
+                elif elements[0] == 'tessId':
+                    if tessellation_not_defined:
+                        tessellation_id = elements[1]
+                        tessellation_not_defined = False
+                    else:
+                        raise IppException('tessId defined twice in [%s]' % filename)
+                elif elements[0] == 'exposure':
+                    exposures_names.append(elements[1])
+                elif elements[0] == 'recipient':
+                    if recipient_not_defined:
+                        recipient = elements[1]
+                        recipient_not_defined = False
+                    else:
+                        raise IppException('recipient defined twice in [%s]' % filename)
+                else:
+                    print 'Unknown key [%s] in [%s]' % (elements[0], filename)
+                    print load_input_file.__doc__
+                    raise IppException('Unknown key [%s] in [%s]' % (elements[0], filename))
+    input_file.close()
+    return (label, tessellation_id, exposures_names, recipient)
+
+#####################################################################################################
+if __name__ == '__main__':
+    #Implement proper arguments management?
+    if len(sys.argv)<2 or sys.argv[1].startswith('-h') or sys.argv[1].startswith('--h'):
+        print '<Usage>: %s <label> <tessellation> <email recipient> [exposures list]' % sys.argv[0]
+        print '         %s -f <input_file>' % sys.argv[0]
+        print '   If <label> has already been used, the program will abort'
+        print '   [exposures list] must have more than 2 exposures'
+        print
+        print load_input_file.__doc__
+        sys.exit(1)
+    if sys.argv[1] == '-f':
+        (LABEL, TESSELLATION_ID, EXPOSURES_NAMES, RECIPIENT) = load_input_file(sys.argv[2])
+    else:
+        ARG_START = 1
+        LABEL = sys.argv[ARG_START]
+        ARG_START += 1
+        TESSELLATION_ID = sys.argv[ARG_START]
+        ARG_START += 1
+        RECIPIENT = sys.argv[ARG_START]
+        ARG_START += 1
+        EXPOSURES_NAMES = sys.argv[ARG_START:]
+        ARG_START += 1
+    if len(EXPOSURES_NAMES) < 2:
+        print 'You need more than 2 exposures to build a stack'
+        print 'Aborting'
+        sys.exit(4)
+    BUILDER = ChipWarpBuilder(LABEL, RECIPIENT)
+    # Check exposures
+    EXPOSURES_IDS = dict()
+    for _exposure_name in EXPOSURES_NAMES:
+        print 'Checking exposure [%s]' % _exposure_name
+        try:
+            EXPOSURES_IDS[_exposure_name] = BUILDER.is_exposure_name_valid(_exposure_name)
+        except IndexError:
+            print 'Exposure [%s] is not valid' % _exposure_name
+            print 'Aborting'
+            sys.exit(3)
+    # Generate Condor jobs, subdags and dag
+    DAG_OF_DAGS_NAME = BUILDER.build_condor_files(EXPOSURES_NAMES)
+    # Create entries in chiptool
+    print 'Stacking %d exposures with label [%s]' % (len(EXPOSURES_NAMES), LABEL)
+    for _exposure_name in EXPOSURES_NAMES:
+        expId = EXPOSURES_IDS[_exposure_name]
+        print '\tCreating chipRun entry for exposure [%s]/%d' % (_exposure_name, expId)
+        COMMAND = "%s -definebyquery -exp_id %d" % (GlobalConstants.CHIPTOOL, expId)
+        COMMAND = "%s -set_label %s" % (COMMAND, LABEL)
+        COMMAND = "%s -set_workdir neb://\@HOST\@.0/gpc1/%s" % (COMMAND, LABEL)
+        COMMAND = "%s -set_tess_id %s" % (COMMAND, TESSELLATION_ID)
+        COMMAND = "%s -set_data_group %s_dg" % (COMMAND, LABEL)
+        COMMAND = "%s -set_note \'chip/stack_reprocessing\' -set_end_stage warp" % (COMMAND)
+        try:
+            subprocess.call(COMMAND.split(' '))
+        except OSError:
+            print 'System call to [%s] failed.' % COMMAND
+            print '... Continuing though (assuming you know what you are doing)'
+    # Show the command to be run
+    print 'Now run:'
+    print 'condor_submit_dag -maxidle 1000 -maxjobs 500 %s' % DAG_OF_DAGS_NAME
Index: branches/meh_branches/ppstack_test/ippdor/src/executables/send_email.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/executables/send_email.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/executables/send_email.py	(revision 33415)
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+
+"""
+A simple utility program to send an e-mail at the end of the condor
+processing.
+"""
+import sys
+import smtplib
+
+LABEL = sys.argv[1]
+RECIPIENT = sys.argv[2]
+
+from email.mime.text import MIMEText
+BODY_INPUT_FILE = open(sys.argv[3], 'rb')
+MESSAGE = MIMEText(BODY_INPUT_FILE.read())
+BODY_INPUT_FILE.close()
+
+MESSAGE['Subject'] = 'End of Condor processing: %s' % LABEL
+_FROM = 'ippdor@ifa.hawaii.edu'
+MESSAGE['From'] = _FROM
+_TO = [ RECIPIENT, 'schastel@ifa.hawaii.edu' ]
+MESSAGE['To'] = RECIPIENT
+SMTP_CONNECTION = smtplib.SMTP('ippc18.ifa.hawaii.edu')
+SMTP_CONNECTION.sendmail(_FROM, _TO, MESSAGE.as_string())
+SMTP_CONNECTION.quit()
+
Index: branches/meh_branches/ppstack_test/ippdor/src/executables/undo_dag_exposure.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/executables/undo_dag_exposure.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/executables/undo_dag_exposure.py	(revision 33415)
@@ -0,0 +1,36 @@
+#!/usr/bin/env python
+
+"""
+Remove the DONE label from a dag file from a <stage> and after this <stage>
+"""
+
+
+if __name__ == '__main__':
+    import sys
+    FILENAME = sys.argv[1]
+    STAGE = sys.argv[2]
+    # Make a backup
+    BACKUP_FILE = '%s.backup' % FILENAME
+    try:
+        BACKUP = open(BACKUP_FILE)
+        BACKUP.close()
+        print 'Remove/Rename old backup first [%s] since I will not delete it' % BACKUP_FILE
+        sys.exit(1)
+    except IOError:
+        pass
+    print 'Backup file: %s' % BACKUP_FILE
+    import shutil
+    shutil.copyfile(FILENAME, BACKUP_FILE)
+    # Parse and filter
+    INPUT_FILE = open(BACKUP_FILE)
+    OUTPUT = open(FILENAME, 'w')
+    STATE = 0
+    for line in INPUT_FILE:
+        if STATE == 0:
+            if STAGE in line:
+                STATE = 1
+        if STATE == 1:
+            line = line.replace(' DONE', '')
+        OUTPUT.write(line)
+    INPUT_FILE.close()
+    OUTPUT.close()
Index: branches/meh_branches/ppstack_test/ippdor/src/ipp/CamManager.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/ipp/CamManager.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/ipp/CamManager.py	(revision 33415)
@@ -0,0 +1,229 @@
+"""
+Generation of the full cam DAG for one exposure and the jobs
+associated to this stage
+"""
+from ipp.Gpc1Manager import Gpc1Manager as Gpc1Manager
+from ipp.helpers import mkdir
+from ipp.exceptions.IppException import IppException
+from ipp.constants.Globals import Globals as GlobalConstants
+from ipp.constants.Camera import Camera as CameraConstants
+
+import sys
+
+#################################################################
+class CamManager(Gpc1Manager):
+    """Derived from the abstract Gpc1Manager to have database access
+    """
+    SCRIPT_NAME = '/home/panstarrs/ippdor/local/bin/generate_cam_jobs.py'
+
+    def __init__(self,
+                 label,
+                 exposure_name,
+                 recipient):
+        """
+        Creates a connector to the gpc1 database as well as local and
+        nfs directories (their name is cam)
+        """
+        Gpc1Manager.__init__(self, recipient=recipient)
+        self.label = label
+        self.exposure_name = exposure_name
+        self.cam_directory = '%s/%s/%s/cam' % (GlobalConstants.LOCAL_TMP_DIR,
+                                              self.label, 
+                                               self.exposure_name)
+        mkdir(self.cam_directory)
+        self.nfs_cam_directory = '%s/%s/%s/cam' % (GlobalConstants.NFS_TMP_DIR,
+                                                   self.label, 
+                                                   self.exposure_name)
+        mkdir(self.nfs_cam_directory)
+
+    def build_new_camera_jobs(self, stage_name = 'B010'):
+        """Build Condor jobs files for stages B010 (regular new), B020, B030, B040 (3 possible reverts)
+        """
+        query = """
+SELECT
+    rawExp.exp_tag,
+    camRun.cam_id,
+    rawExp.camera,
+    camRun.state,
+    camRun.workdir,
+    camRun.reduction,
+    camRun.dvodb
+FROM camRun
+JOIN chipRun
+    USING(chip_id)
+JOIN rawExp
+    USING(exp_id)
+LEFT JOIN camProcessedExp
+    USING(cam_id)
+LEFT JOIN camMask
+    ON camRun.label = camMask.label
+WHERE
+    chipRun.state = 'full'
+    AND ((camRun.state = 'new' AND camProcessedExp.cam_id IS NULL) OR camRun.state = 'update')
+    AND camMask.label IS NULL
+    AND chipRun.label = '%s'
+    AND rawExp.exp_name = '%s'
+""" % (self.label, self.exposure_name)
+        cameras = self.multiple_query(query)
+        jobs_filename = '%s/%s.jobs' % (self.cam_directory, stage_name)
+        message = 'Generating Condor jobs file [%s]' % (jobs_filename)
+        message = '%s for processing of %d cam (stage %s)' % ( message,
+                                                               len(cameras), 
+                                                               stage_name)
+        print message
+        output = open(jobs_filename, 'w')
+        output.write('Executable = %s/encapsulate_camera.sh\n' % GlobalConstants.IPPDOR_BINARY_PATH)
+        output.write('Universe = vanilla\n')
+        output.write('Output = %s/%s.%s.cam_%s.out\n' % (self.nfs_cam_directory,
+                                                         self.label, self.exposure_name, 
+                                                         stage_name))
+        output.write('Error = %s/%s.%s.cam_%s.err\n' % (self.nfs_cam_directory,
+                                                        self.label, self.exposure_name, 
+                                                        stage_name))
+        output.write('Log = %s/%s.%s.cam.log\n' % (self.cam_directory,
+                                                   self.exposure_name, stage_name))
+        output.write('Requirements = %s\n' % CameraConstants.REQUIREMENTS)
+        output.write('Notification = Error\n')
+        output.write('\n')
+        output.write('getenv = True\n')
+        output.write('\n')
+        if len(cameras) > 0:
+            camera = cameras[0]
+            (exp_tag, cam_id, camera, state, workdir, reduction, dvodb) = camera
+            if len(cameras) > 1:
+                body = CameraConstants.EMAIL_BODY_MULTIPLE_CAM_ENTRIES % (self.label, 
+                                                                          self.exposure_name, 
+                                                                          cam_id)
+                self.send_email('Multiple camRun entries for label %s / exposure %s' 
+                                % (self.label, 
+                                   self.exposure_name),
+                                body)
+            outroot = '%s/%s/%s.cm.%d' % (workdir, exp_tag, exp_tag, cam_id)
+            arguments = "Arguments = %s" % GlobalConstants.CAMEXP
+            arguments = "%s --exp_tag %s" % (arguments, exp_tag)
+            arguments = "%s --cam_id %d" % (arguments, cam_id)
+            arguments = "%s --camera %s" % (arguments, camera)
+            arguments = "%s --outroot %s" % (arguments, outroot)
+            arguments = "%s --run-state %s" % (arguments, state)
+            if reduction is not None and reduction != 'NULL':
+                arguments = '%s --reduction %s' % (arguments, reduction)
+            if dvodb is not None and dvodb != 'NULL':
+                arguments = '%s --dvodb %s' % (arguments, dvodb)
+            output.write('%s\n' % arguments)
+            output.write('Queue\n')
+            output.write('\n')
+        if len(cameras) == 0:
+            arguments = 'Arguments = echo Nothing to do for stage %s' % stage_name
+            output.write('%s\n' % arguments)
+            output.write('Queue\n')
+            output.write('\n')
+        output.close()
+
+    def build_camera_revert_jobs(self, stage_name):
+        """Build the jobs to revert camera
+        """
+        query = """
+DELETE FROM camProcessedExp
+USING camProcessedExp, camRun, chipRun, rawExp
+WHERE
+    camRun.cam_id = camProcessedExp.cam_id
+    AND camRun.chip_id = chipRun.chip_id
+    AND chipRun.exp_id = rawExp.exp_id
+    AND camProcessedExp.fault != 0
+    AND camRun.state = 'new'
+    AND camRun.label = '%s'
+    AND chipRun.exp_id = %d
+""" % (self.label, self.get_exposure_id(self.exposure_name))
+        self.multiple_query(query)
+        self.build_new_camera_jobs(stage_name)
+
+    def advance_camera(self):
+        """Last phase (B050) in camera: 
+        * If there is something to revert (we already reverted 3 times): send 
+          an e-mail to the operator and abort any further processing
+        * If it has quality != 0: send an e-mail to the operator and
+          abort any further processing
+        """
+        query = """
+SELECT camProcessedExp.fault, camProcessedExp.quality
+FROM camProcessedExp 
+  JOIN camRun USING(cam_id) 
+  JOIN chipRun USING(chip_id) 
+  JOIN rawExp USING(exp_id)
+WHERE
+  ( ( camProcessedExp.fault != 0
+      AND camRun.state = 'new' )
+    OR ( camProcessedExp.quality != 0) )
+  AND camRun.label = '%s'
+  AND chipRun.exp_id = %d
+""" % (self.label, self.get_exposure_id(self.exposure_name))
+        cam_status = self.multiple_query(query)
+        if len(cam_status) > 0:
+            # Send e-mail to recipient
+            print 'Default for exposure [%s] at cam level' % (self.exposure_name)
+            body = CameraConstants.EMAIL_BODY_REVERT_FAILURE % (self.exposure_name,
+                                                                self.label, 
+                                                                self.exposure_name)
+            self.send_email('Camera stage failed for label %s / exposure %s)' % (self.label, 
+                                                                                 self.exposure_name), 
+                            body)
+            print 'EMail sent to [%s]' % self.recipient
+            raise IppException('Camera stage failed for label %s / exposure %s)' 
+                               % (self.label, 
+                                  self.exposure_name))
+        # Generate the B050 stage
+        jobs_filename = '%s/B050.jobs' % (self.cam_directory)
+        print "Generating Condor jobs file [%s] for processing of camera (stage B050)" % (jobs_filename)
+        output = open(jobs_filename, 'w')
+        output.write('Executable = %s/encapsulate_camera.sh\n' % GlobalConstants.IPPDOR_BINARY_PATH)
+        output.write('Universe = vanilla\n')
+        output.write('Output = %s/%s.%s.cam_B050.out\n' % (self.nfs_cam_directory,
+                                                           self.label, self.exposure_name))
+        output.write('Error = %s/%s.%s.cam_B050.err\n' % (self.nfs_cam_directory,
+                                                          self.label, self.exposure_name))
+        output.write('Log = %s/%s.%s.cam_B050.log\n' % (self.cam_directory,
+                                                        self.label, self.exposure_name))
+        output.write('Requirements = %s\n' % CameraConstants.REQUIREMENTS)
+        output.write('Notification = Error\n')
+        output.write('\n')
+        output.write('getenv = True\n')
+        output.write('\n')
+        arguments = 'echo Nothing to do for camera exposure %s/%s (stage B050)' % (self.exposure_name, self.label)
+        output.write('Arguments = %s\n' % arguments)
+        output.write('Queue\n')
+        output.write('\n')
+        output.close()
+
+    def build_camera_dag(self):
+        """Generate the camera DAG
+        """
+        dag_name = '%s/camera.dag' % (self.cam_directory)
+        output = open(dag_name, 'w')
+        output.write('# Generated DAG\n')
+        output.write('JOB\tB010\t%s/B010.jobs\n' % self.cam_directory)
+        output.write('SCRIPT POST B010 %s B020 %s %s %s\n' % (CamManager.SCRIPT_NAME, 
+                                                              self.label, self.exposure_name, self.recipient))
+        output.write('JOB\tB020\t%s/B020.jobs\n' % self.cam_directory)
+        output.write('SCRIPT POST B020 %s B030 %s %s %s\n' % (CamManager.SCRIPT_NAME, 
+                                                              self.label, self.exposure_name, self.recipient))
+        output.write('JOB\tB030\t%s/B030.jobs\n' % self.cam_directory)
+        output.write('SCRIPT POST B030 %s B040 %s %s %s\n' % (CamManager.SCRIPT_NAME, 
+                                                              self.label, self.exposure_name, self.recipient))
+        output.write('JOB\tB040\t%s/B040.jobs\n' % self.cam_directory)
+        output.write('SCRIPT POST B040 %s B050 %s %s %s\n' % (CamManager.SCRIPT_NAME, 
+                                                              self.label, self.exposure_name, self.recipient))
+        output.write('JOB\tB050\t%s/B050.jobs\n' % self.cam_directory)
+        output.write('\n')
+        output.write('PARENT B010 CHILD B020\n')
+        output.write('PARENT B020 CHILD B030\n')
+        output.write('PARENT B030 CHILD B040\n')
+        output.write('PARENT B040 CHILD B050\n')
+        output.write('\n')
+        output.close()
+        return dag_name
+
+#################################################################
+if __name__ == '__main__':
+    print '<Error>: Not intended to be used as an independent program'
+    print '<Error>: Use %s' % CamManager.SCRIPT_NAME
+    sys.exit(1)
Index: branches/meh_branches/ppstack_test/ippdor/src/ipp/ChipManager.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/ipp/ChipManager.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/ipp/ChipManager.py	(revision 33415)
@@ -0,0 +1,277 @@
+"""
+Generate the full chip DAG for one exposure
+Generate the Condor job files for chip stage
+"""
+from ipp.Gpc1Manager import Gpc1Manager as Gpc1Manager
+from ipp.helpers import mkdir
+from ipp.exceptions.IppException import IppException
+from ipp.constants.Globals import Globals as GlobalConstants
+from ipp.constants.Chip import Chip as ChipConstants
+
+#################################################################
+class ChipManager(Gpc1Manager):
+    """
+    Generate the full chip DAG for one exposure
+    Generate the Condor job files for chip stage
+    """
+    SCRIPT_NAME = '%s/generate_chip_jobs.py' % GlobalConstants.IPPDOR_BINARY_PATH
+
+    def __init__(self,
+                 label,
+                 exposure_name,
+                 recipient):
+        """
+        Creates a connector to the gpc1 database as well as local and
+        nfs directories (their name is chip)
+        """
+        Gpc1Manager.__init__(self, recipient=recipient)
+        self.label = label
+        self.exposure_name = exposure_name
+        self.chip_directory = '%s/%s/%s/chip' % (GlobalConstants.LOCAL_TMP_DIR, 
+                                                 self.label, self.exposure_name)
+        mkdir(self.chip_directory)
+        self.nfs_chip_directory = '%s/%s/%s/chip' % (GlobalConstants.NFS_TMP_DIR, 
+                                                     self.label, self.exposure_name)
+        mkdir(self.nfs_chip_directory)
+
+    def build_new_chips_jobs(self, stage_name = 'A010'):
+        """Build the jobs file for the A010 node
+        """
+        query = """
+SELECT
+    chipRun.exp_id,
+    chipRun.chip_id,
+    chipImfile.chip_imfile_id,
+    rawImfile.class_id,
+    rawImfile.uri,
+    rawExp.camera,
+    chipRun.state,
+    rawImfile.magicked as raw_magicked,
+    (rawImfile.user_1 is not NULL and rawImfile.user_1 > 0.5) as deburned,
+    chipRun.workdir,
+    rawExp.exp_tag,
+    chipRun.reduction
+FROM chipRun
+JOIN rawExp
+    USING(exp_id)
+JOIN rawImfile
+    ON rawExp.exp_id = rawImfile.exp_id
+    AND rawImfile.ignored = 0
+JOIN chipImfile
+    USING(chip_id, class_id)
+LEFT JOIN chipProcessedImfile
+    ON chipRun.chip_id = chipProcessedImfile.chip_id
+    AND rawImfile.exp_id = chipProcessedImfile.exp_id
+    AND rawImfile.class_id = chipProcessedImfile.class_id
+LEFT JOIN chipMask
+    ON chipRun.label = chipMask.label
+LEFT JOIN Label ON chipRun.label = Label.label
+WHERE
+    chipRun.state = 'new'
+    AND chipProcessedImfile.chip_id IS NULL
+    AND chipProcessedImfile.exp_id IS NULL
+    AND chipProcessedImfile.class_id IS NULL
+    AND chipMask.label IS NULL
+    AND chipRun.exp_id = %d
+    AND chipRun.label = '%s'
+""" % (self.get_exposure_id(self.exposure_name), self.label)
+        chips = self.multiple_query(query)
+        if len(chips) != 60:
+            message = "(Stage %s) There are %d chips (instead of 60) " % (stage_name, 
+                                                                          len(chips))
+            message = "%s for exposure [%s] (label: [%s])" % (message,
+                                                              self.exposure_name, 
+                                                              self.label)
+            print message
+        jobs_filename = '%s/%s.jobs' % (self.chip_directory, stage_name)
+        message = 'Generating Condor jobs file [%s] ' % jobs_filename
+        message = '%s for processing of %d chips (stage %s)' % (message, 
+                                                                len(chips), 
+                                                                stage_name)
+        output = open(jobs_filename, 'w')
+        output.write('Executable = %s/encapsulate_chip.sh\n' % GlobalConstants.IPPDOR_BINARY_PATH)
+        output.write('Universe = vanilla\n')
+        output.write('Output = %s/%s.%s.chip_%s.out\n' % (self.nfs_chip_directory, 
+                                                          self.label, self.exposure_name, stage_name))
+        output.write('Error = %s/%s.%s.chip_%s.err\n' % (self.nfs_chip_directory, 
+                                                         self.label, self.exposure_name, stage_name))
+        output.write('Log = %s/%s.%s.chip_%s.log\n' % (self.chip_directory,
+                                                       self.label, self.exposure_name,
+                                                       stage_name))
+        output.write('Requirements = %s\n' % ChipConstants.REQUIREMENTS)
+        output.write('Notification = Error\n')
+        output.write('\n')
+        output.write('getenv = True\n')
+        output.write('\n')
+        for chip in chips:
+            (exp_id, chip_id, chip_imfile_id, ota, uri, camera, state, raw_magicked, 
+             deburned, work_dir, exp_tag, reduction) = chip
+            outroot = '%s/%s/%s.ch.%d' % (work_dir, exp_tag, exp_tag, chip_id)
+            arguments = 'Arguments = %s' % GlobalConstants.CHIPIMFILE
+            arguments = '%s --exp_id %d' % (arguments, exp_id)
+            arguments = '%s --chip_id %d' % (arguments, chip_id)
+            arguments = '%s --chip_imfile_id %d' % (arguments, chip_imfile_id)
+            arguments = '%s --class_id %s' % (arguments, ota)
+            arguments = '%s --uri %s' % (arguments, uri)
+            arguments = '%s --camera %s' % (arguments, camera)
+            arguments = '%s --run-state %s' % (arguments, state)
+            arguments = '%s --deburned %d' % (arguments, deburned)
+            arguments = '%s --outroot %s' % (arguments, outroot)
+            if raw_magicked > 0:
+                arguments = '%s --magicked %d' % (arguments, raw_magicked)
+            if reduction is not None:
+                arguments = '%s --reduction %s' % (arguments, reduction)
+            output.write('%s\n' % arguments)
+            output.write('Queue\n')
+            output.write('\n')
+        if len(chips) == 0:
+            arguments = 'Arguments = echo Nothing to do for stage %s' % stage_name
+            output.write('%s\n' % arguments)
+            output.write('Queue\n')
+            output.write('\n')
+        output.close()
+
+    def build_chip_dag(self):
+        """Build the full DAG file for the chip (aka A) stage
+        """
+        dag_name = '%s/chip.dag' % (self.chip_directory)
+        output = open(dag_name, 'w')
+        output.write('# Generated DAG\n')
+        output.write('JOB\tA010\t%s/A010.jobs\n' % self.chip_directory)
+        output.write('SCRIPT POST A010 %s A020 %s %s %s\n' % (ChipManager.SCRIPT_NAME, 
+                                                              self.label, self.exposure_name,
+                                                              self.recipient))
+        output.write('JOB\tA020\t%s/A020.jobs\n' % self.chip_directory)
+        output.write('SCRIPT POST A020 %s A030 %s %s %s\n' % (ChipManager.SCRIPT_NAME, 
+                                                              self.label, self.exposure_name,
+                                                              self.recipient))
+        output.write('JOB\tA030\t%s/A030.jobs\n' % self.chip_directory)
+        output.write('SCRIPT POST A030 %s A040 %s %s %s\n' % (ChipManager.SCRIPT_NAME, 
+                                                              self.label, self.exposure_name,
+                                                              self.recipient))
+        output.write('JOB\tA040\t%s/A040.jobs\n' % self.chip_directory)
+        output.write('SCRIPT POST A040 %s A050 %s %s %s\n' % (ChipManager.SCRIPT_NAME, 
+                                                              self.label, self.exposure_name,
+                                                              self.recipient))
+        output.write('JOB\tA050\t%s/A050.jobs\n' % self.chip_directory)
+        output.write('\n')
+        output.write('PARENT A010 CHILD A020\n')
+        output.write('PARENT A020 CHILD A030\n')
+        output.write('PARENT A030 CHILD A040\n')
+        output.write('PARENT A040 CHILD A050\n')
+        output.write('\n')
+        output.close()
+        return dag_name
+
+    def build_chip_revert_jobs(self, stage_name):
+        """Build the jobs to revert chips
+        """
+        query = """
+DELETE FROM chipProcessedImfile
+USING chipProcessedImfile, chipRun, rawExp
+WHERE
+    chipRun.chip_id = chipProcessedImfile.chip_id
+    AND rawExp.exp_id = chipProcessedImfile.exp_id
+    AND chipRun.state = 'new'
+    AND chipProcessedImfile.fault != 0
+    AND chipRun.exp_id = %d
+    AND chipRun.label = '%s'
+""" % (self.get_exposure_id(self.exposure_name), self.label)
+        print "Reverting with %s" % query
+        self.multiple_query(query)
+        self.build_new_chips_jobs(stage_name)
+
+    def advance_chips(self):
+        """
+        Last phase in Chip:
+        * If *all* chips have failed: send an e-mail to the operator
+          and abort any further processing
+          (Matching example: MD08.photfest.20100923 o5331g0350o)
+        * Create the job that runs 'chiptool -advanceexp' for the exposure
+        """
+        query = """
+SELECT class_id
+FROM chipProcessedImfile JOIN chipRun USING(chip_id) JOIN rawExp ON rawExp.exp_id = chipRun.exp_id
+WHERE chipRun.state = 'new'
+    AND chipProcessedImfile.fault != 0
+    AND chipRun.exp_id = %d
+    AND chipRun.label = '%s'""" % (self.get_exposure_id(self.exposure_name), self.label)
+        chips = self.multiple_query(query)
+        if len(chips) != 0:
+            # Send e-mail to recipient
+            body = ChipConstants.EMAIL_BODY_FAILED_REVERT.replace('@COUNT_FAILED_CHIPS@',
+                                                                  len(chips))
+            body = body.replace('@LABEL@', self.label)
+            body = body.replace('@EXPOSURE_NAME@', self.exposure_name)
+            query = 'SELECT exp_name, chip_id, class_id, chipProcessedImfile.fault, path_base'
+            query = '%s FROM chipProcessedImfile JOIN chipRun USING(chip_id) JOIN rawExp' % query
+            query = "%s ON chipRun.exp_id = rawExp.exp_id " % query
+            query = "%s WHERE chipRun.label = '%s'" % (query, self.label)
+            query = "%s  AND exp_name = '%s' " % (query, self.exposure_name)
+            query = "%s AND chipProcessedImfile.fault!=0" % (query)
+            rows = self.multiple_query(query)
+            updateprocessedimfile = ''
+            for row in rows:
+                chip_id = row[1]
+                class_id = row[2]
+                updateprocessedimfile = "%s\tchiptool -dbname gpc1 -updateprocessedimfile" % updateprocessedimfile
+                updateprocessedimfile = "%s -fault 0 -set_quality 42" % updateprocessedimfile
+                updateprocessedimfile = "%s -chip_id %s -class_id %s\n" % (updateprocessedimfile, 
+                                                                           chip_id, 
+                                                                           class_id)
+            body = body.replace('@CHIPTOOL_UPDATEPROCESSEDIMFILE@', updateprocessedimfile)
+            self.send_email(ChipConstants.EMAIL_SUBJECT_FAILED_REVERT % (self.label, 
+                                                                         self.exposure_name), 
+                            body % (GlobalConstants.LOCAL_TMP_DIR))
+            print 'EMail sent to [%s]' % self.recipient
+            raise IppException('Chip stage failed for label %s / exposure %s)' % (self.label, self.exposure_name))
+        # It seems that sometimes we create more than one entry in
+        # camRun. Maybe chiptool is not thread-safe
+        query = "SELECT count(*) FROM camRun JOIN chipRun USING(chip_id)"
+        query = "%s WHERE chipRun.label='%s' AND chipRun.chip_id = %d" % (query,
+                                                                          self.label, 
+                                                                          self.get_chip_id(self.exposure_name, 
+                                                                                           self.label))
+        cam_count = int(self.simple_query(query)[0])
+        if (cam_count != 0):
+            body = 'Error: More than one entry in camRun'
+            body = '%s for chip_id %d' % (body,
+                                          self.get_chip_id(self.exposure_name, 
+                                                           self.label))
+            self.send_email('Cam entries for %s: %d' % (self.exposure_name, cam_count), 
+                            body)
+            raise IppException('Invalid cam entries count for chip %d' 
+                               % self.get_chip_id(self.exposure_name, 
+                                                  self.label) )
+        # Resume to normal case...
+        jobs_filename = '%s/A050.jobs' % (self.chip_directory)
+        print "Generating Condor jobs file [%s] for processing of %d chips (stage A050)" % (jobs_filename, len(chips))
+        output = open(jobs_filename, 'w')
+        output.write('Executable = %s/encapsulate_chip.sh\n' % GlobalConstants.IPPDOR_BINARY_PATH)
+        output.write('Universe = vanilla\n')
+        output.write('Output = %s/%s.%s.chip_A050.out\n' % (self.nfs_chip_directory,
+                                                            self.label, self.exposure_name))
+        output.write('Error = %s/%s.%s.chip_A050.err\n' % (self.nfs_chip_directory,
+                                                           self.label, self.exposure_name))
+        output.write('Log = %s/%s.%s.chip_A050.log\n' % (self.chip_directory,
+                                                        self.label, self.exposure_name))
+        output.write('Requirements = %s\n' % ChipConstants.REQUIREMENTS)
+        output.write('Notification = Error\n')
+        output.write('\n')
+        output.write('getenv = True\n')
+        output.write('\n')
+        arguments = '%s -advanceexp -chip_id %d -label %s' % (GlobalConstants.CHIPTOOL,
+                                                              self.get_chip_id(self.exposure_name, 
+                                                                               self.label), 
+                                                              self.label)
+        output.write('Arguments = %s\n' % arguments)
+        output.write('Queue\n')
+        output.write('\n')
+        output.close()
+
+#################################################################
+if __name__ == '__main__':
+    import sys
+    print '<Error>: Not intended to be used as an independent program'
+    print '  Use %s' % ChipManager.SCRIPT_NAME
+    sys.exit(1)
Index: branches/meh_branches/ppstack_test/ippdor/src/ipp/FakeManager.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/ipp/FakeManager.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/ipp/FakeManager.py	(revision 33415)
@@ -0,0 +1,203 @@
+"""
+Generate the full fake DAG for one exposure
+Generate the jobs for fake stage
+"""
+from ipp.Gpc1Manager import Gpc1Manager as Gpc1Manager
+from ipp.helpers import mkdir
+from ipp.constants.Globals import Globals as GlobalConstants
+from ipp.constants.Fake import Fake as FakeConstants
+
+#################################################################
+class FakeManager(Gpc1Manager):
+    """
+    Fake stage implementation
+    """
+    SCRIPT_NAME = '/home/panstarrs/ippdor/local/bin/generate_fake_jobs.py'
+
+    def __init__(self,
+                 label,
+                 exposure_name,
+                 recipient):
+        """
+        Creates a connector to the gpc1 database as well as local and
+        nfs directories (their name is fake)
+        """
+        Gpc1Manager.__init__(self, recipient=recipient)
+        self.label = label
+        self.exposure_name = exposure_name
+        self.fake_directory = '%s/%s/%s/fake' % (GlobalConstants.LOCAL_TMP_DIR, 
+                                                label, 
+                                                exposure_name)
+        mkdir(self.fake_directory)
+
+    def build_new_fake_jobs(self, stage_name = 'C010'):
+        """Build the jobs file for the C010 node
+        """
+        query = """
+SELECT
+    chipProcessedImfile.exp_id,
+    fakeRun.fake_id,
+    chipProcessedImfile.class_id,
+    chipProcessedImfile.path_base,
+    camProcessedExp.path_base,
+    rawExp.camera,
+    fakeRun.reduction,
+    fakeRun.dvodb,
+    fakeRun.workdir
+FROM fakeRun
+JOIN camRun USING(cam_id)
+JOIN camProcessedExp USING(cam_id)
+JOIN chipRun USING(chip_id)
+JOIN chipProcessedImfile USING(chip_id,exp_id)
+JOIN rawExp USING(exp_id)
+--    ON chipProcessedImfile.exp_id = rawExp.exp_id
+LEFT JOIN fakeProcessedImfile
+    ON fakeRun.fake_id = fakeProcessedImfile.fake_id
+    AND chipProcessedImfile.class_id = fakeProcessedImfile.class_id
+LEFT JOIN fakeMask
+    ON fakeRun.label = fakeMask.label
+WHERE
+    ((fakeRun.state = 'new'
+        AND fakeProcessedImfile.fake_id IS NULL
+        AND fakeProcessedImfile.class_id IS NULL
+    )
+    OR (fakeRun.state = 'update'
+        AND fakeProcessedImfile.data_state = 'cleaned')
+    )
+    AND fakeMask.label IS NULL
+    AND fakeRun.label='%s'
+    AND rawExp.exp_id = %d
+""" % (self.label, self.get_exposure_id(self.exposure_name))
+        fakes = self.multiple_query(query)
+        jobs_filename = '%s/%s.jobs' % (self.fake_directory, stage_name)
+        print "Generating Condor jobs file [%s] for processing of %d fake (stage %s)" % (jobs_filename, 
+                                                                                         len(fakes), 
+                                                                                         stage_name)
+        output = open(jobs_filename, 'w')
+        output.write('Executable = /home/panstarrs/ippdor/local/bin/encapsulate_fake.sh\n')
+        output.write('Universe = vanilla\n')
+        output.write('Output = %s/out.%s.%s.fake_%s\n' % (GlobalConstants.NFS_TMP_DIR,
+                                                          self.label, 
+                                                          self.exposure_name, 
+                                                          stage_name))
+        output.write('Error = %s/err.%s.%s.fake_%s\n' % (GlobalConstants.NFS_TMP_DIR,
+                                                         self.label, 
+                                                         self.exposure_name, 
+                                                         stage_name))
+        output.write('Log = %s/%s_fake_%s.log\n' % (GlobalConstants.LOCAL_TMP_DIR,
+                                                    self.exposure_name, 
+                                                    stage_name))
+        output.write('Requirements = %s\n' % FakeConstants.REQUIREMENTS)
+        output.write('Notification = Error\n')
+        output.write('\n')
+        output.write('getenv = True\n')
+        output.write('\n')
+        for fake in fakes:
+            (exp_id, fake_id, class_id, chip_pathbase, cam_pathbase, camera, reduction, dvodb, workdir) = fake
+            outroot = '%s/%s/%s.fk.%d' % (workdir, exp_id, exp_id, fake_id)
+            arguments = 'Arguments = %s' % GlobalConstants.FAKE_IMFILE
+            arguments = '%s --exp_id %d' % (arguments, exp_id)
+            arguments = '%s --fake_id %d' % (arguments, fake_id)
+            arguments = '%s --class_id %s' % (arguments, class_id)
+            arguments = '%s --chiproot=%s' % (arguments, chip_pathbase)
+            arguments = '%s --camroot=%s' % (arguments, cam_pathbase)
+            arguments = '%s --camera %s' % (arguments, camera)
+            arguments = '%s --outroot %s' % (arguments, outroot)
+            if reduction is not None and reduction != 'NULL':
+                arguments = '%s --reduction %s' % (arguments, reduction)
+            if dvodb is not None and dvodb != 'NULL':
+                print 'dvodb is defined but I won\'t use it'
+            #     arguments = '%s --dvodb %s' % (arguments, dvodb)
+            output.write('%s\n' % arguments)
+            output.write('Queue\n')
+            output.write('\n')
+        if len(fakes) == 0:
+            arguments = "Arguments = echo Nothing to do for stage %s" % stage_name
+            output.write('%s\n' % arguments)
+            output.write('Queue\n')
+            output.write('\n')
+        output.close()
+
+    def build_fake_revert_jobs(self, stage_name):
+        """Revert faulty fakes if any
+        """
+        query = """
+DELETE FROM fakeProcessedImfile
+USING fakeProcessedImfile, fakeRun, camRun, chipRun, rawExp
+WHERE
+    fakeProcessedImfile.fake_id = fakeRun.fake_id
+    AND fakeRun.cam_id = camRun.cam_id
+    AND camRun.chip_id = chipRun.chip_id
+    AND chipRun.exp_id = rawExp.exp_id
+    AND fakeProcessedImfile.fault != 0
+    AND fakeRun.label = '%s'
+    AND chipRun.exp_id = %d
+""" % (self.label, self.get_exposure_id(self.exposure_name))
+        self.multiple_query(query)
+        self.build_new_fake_jobs(stage_name)
+
+    def advance_fake(self):
+        """Last phase (C050) in fake stage:
+        * 
+        """
+        jobs_filename = '%s/C050.jobs' % (self.fake_directory)
+        print "Generating Condor jobs file [%s] for processing of a fake (stage C050)" % (jobs_filename)
+        output = open(jobs_filename, 'w')
+        output.write('Executable = /home/panstarrs/ippdor/local/bin/encapsulate_fake.sh\n')
+        output.write('Universe = vanilla\n')
+        output.write('Output = %s/out.%s.%s.fake_C050\n' % (GlobalConstants.NFS_TMP_DIR,
+                                                            self.label, 
+                                                            self.exposure_name))
+        output.write('Error = %s/err.%s.%s.fake_C050\n' % (GlobalConstants.NFS_TMP_DIR,
+                                                           self.label, 
+                                                           self.exposure_name))
+        output.write('Log = %s/%s_fake_C050.log\n' % (GlobalConstants.LOCAL_TMP_DIR,
+                                                      self.exposure_name))
+        output.write('Requirements = %s\n' % FakeConstants.REQUIREMENTS)
+        output.write('Notification = Error\n')
+        output.write('\n')
+        output.write('getenv = True\n')
+        output.write('\n')
+        arguments = '%s -advanceexp' % GlobalConstants.FAKETOOL
+        arguments = '%s -fake_id %d -label %s' % (arguments,
+                                                  self.get_fake_id(self.exposure_name, 
+                                                                   self.label), 
+                                                  self.label)
+        output.write('Arguments = %s\n' % arguments)
+        output.write('Queue\n')
+        output.write('\n')
+        output.close()
+
+    def build_fake_dag(self):
+        """Build the full DAG file for the fake (aka C) stage
+        """
+        dag_name = '%s/fake.dag' % (self.fake_directory)
+        output = open(dag_name, 'w')
+        output.write('# Generated DAG\n')
+        output.write('JOB\tC010\t%s/C010.jobs\n' % self.fake_directory)
+        output.write('SCRIPT POST C010 %s C020 %s %s\n' % (FakeManager.SCRIPT_NAME, 
+                                                           self.label, self.exposure_name))
+        output.write('JOB\tC020\t%s/C020.jobs\n' % self.fake_directory)
+        output.write('SCRIPT POST C020 %s C030 %s %s\n' % (FakeManager.SCRIPT_NAME, 
+                                                           self.label, self.exposure_name))
+        output.write('JOB\tC030\t%s/C030.jobs\n' % self.fake_directory)
+        output.write('SCRIPT POST C030 %s C040 %s %s\n' % (FakeManager.SCRIPT_NAME, 
+                                                           self.label, self.exposure_name))
+        output.write('JOB\tC040\t%s/C040.jobs\n' % self.fake_directory)
+        output.write('SCRIPT POST C040 %s C050 %s %s\n' % (FakeManager.SCRIPT_NAME, 
+                                                           self.label, self.exposure_name))
+        output.write('JOB\tC050\t%s/C050.jobs\n' % self.fake_directory)
+        output.write('\n')
+        output.write('PARENT C010 CHILD C020\n')
+        output.write('PARENT C020 CHILD C030\n')
+        output.write('PARENT C030 CHILD C040\n')
+        output.write('PARENT C040 CHILD C050\n')
+        output.write('\n')
+        output.close()
+        return dag_name
+
+#################################################################
+if __name__ == '__main__':
+    import sys
+    print '<Error>: Use %s' % FakeManager.SCRIPT_NAME
+    sys.exit(1)
Index: branches/meh_branches/ppstack_test/ippdor/src/ipp/Gpc1Manager.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/ipp/Gpc1Manager.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/ipp/Gpc1Manager.py	(revision 33415)
@@ -0,0 +1,131 @@
+"""
+- Access to the Gpc1 database
+- Definition of high-level values (recipient, label...)
+"""
+import sys
+import MySQLdb
+from ipp.IppSite import Gpc1
+
+#################################################################
+class Gpc1Manager:
+    """
+    Access to the Gpc1 database
+    """
+    def __init__(self,
+                 recipient = 'schastel@ifa.hawaii.edu'):
+        """Create a connector to the gpc1 database
+        """
+        self.recipient = recipient
+        self.connector = MySQLdb.connect(Gpc1.HOST, 
+                                         Gpc1.USER, 
+                                         Gpc1.PASSWORD, 
+                                         db = Gpc1.NAME)
+        self.label = None # Set by derived classes
+
+    ##############################################################
+    def close(self):
+        """Close the connection
+        """
+        self.connector.close()
+
+    ##############################################################
+    def multiple_query(self, query):
+        """Perform a MySQL query ("multiple" possibly empty answers)
+        """
+        cursor = self.connector.cursor()
+        cursor.execute(query)
+        rows = cursor.fetchall()
+        cursor.close()
+        self.connector.commit()
+        return rows
+
+    ##############################################################
+    def simple_query(self, query):
+        """Perform a MySQL query (unique answer)
+        """
+        rows = self.multiple_query(query)
+        self.connector.commit()
+        if rows is None:
+            return None
+        if len(rows) > 1:
+            raise Exception("More than one answer to statement [%s]"
+                            % query)
+        return rows[0]
+
+    ##############################################################
+    def get_exposure_id(self, exposure_name):
+        """Get the exposure id associated to a named exposure
+        """
+        query = "SELECT exp_id FROM rawExp WHERE exp_name = '%s'" % exposure_name
+        return int(self.simple_query(query)[0])
+
+    ##############################################################
+    def get_chip_id(self, exposure_name, label = None):
+        """Get the chip id associated to a named exposure
+        """
+        query = "SELECT chip_id FROM chipRun JOIN rawExp USING(exp_id) "
+        query = "%s WHERE exp_name = '%s'" % (query, exposure_name)
+        if label is not None:
+            query = "%s AND label = '%s'" % (query, label)
+        return int(self.simple_query(query)[0])
+
+    ##############################################################
+    def get_fake_id(self, exposure_name, label = None):
+        """Get the fake id associated to a named exposure
+        """
+        query = "SELECT fake_id FROM fakeRun JOIN camRun USING(cam_id) JOIN chipRun USING(chip_id)"
+        query = "%s JOIN rawExp USING(exp_id) WHERE exp_name = '%s'" % (query,
+                                                                        exposure_name)
+        if label is not None:
+            query = "%s AND fakeRun.label='%s'" % (query, label)
+        return int(self.simple_query(query)[0])
+
+    ##############################################################
+    def get_warp_id(self, exposure_name, label):
+        """Get the warp id associated to a named and labelled exposure
+        """
+        query = "SELECT warp_id FROM warpRun JOIN fakeRun USING(fake_id) JOIN camRun USING(cam_id)"
+        query = "%s JOIN chipRun USING(chip_id) JOIN rawExp USING(exp_id)" % query
+        query = "%s WHERE warpRun.label='%s' AND exp_name='%s'" % (query, label, exposure_name)
+        return int(self.simple_query(query)[0])
+
+    ##############################################################
+    def send_email(self, subject, body):
+        """
+        Send an e-mail to the recipient (which has been previously defined)
+        """
+        import smtplib
+        from email.mime.text import MIMEText
+        message = MIMEText(body)
+        message['Subject'] = subject
+        _from = 'ippdor@ifa.hawaii.edu'
+        message['From'] = _from
+        _to = [ self.recipient, 'schastel@ifa.hawaii.edu' ]
+        message['To'] = self.recipient
+        smtp_connection = smtplib.SMTP('ippc18.ifa.hawaii.edu')
+        smtp_connection.sendmail(_from, _to, message.as_string())
+        smtp_connection.quit()
+
+    ##############################################################
+    def is_label_used(self):
+        """
+        Check if the label is used in the chipRun table
+        """
+        query = """SELECT label from chipRun WHERE label = '%s' LIMIT 1""" % self.label
+        values = self.multiple_query(query)
+        return values is not None and len(values) != 0
+
+    ##############################################################
+    def is_exposure_name_valid(self, exposure_name):
+        """
+        Check if the exposure named exposure_name is in the rawExp table
+        """
+        query = """SELECT exp_id FROM rawExp WHERE exp_name='%s'""" % exposure_name
+        return int(self.simple_query(query)[0])
+
+#################################################################
+if __name__ == '__main__':
+    print 'Not intended to be used as an independent program'
+    GPC1 = Gpc1Manager(recipient = 'schastel@ifa.hawaii.edu')
+    GPC1.send_email("This is a test", "Condor e-mail test")
+    sys.exit(1)
Index: branches/meh_branches/ppstack_test/ippdor/src/ipp/StackManager.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/ipp/StackManager.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/ipp/StackManager.py	(revision 33415)
@@ -0,0 +1,281 @@
+"""
+The purpose of this class is to:
+- Implement the stack.pro pantasks script tasks
+- Generate the associated DAG
+"""
+
+#
+# Generate the stack DAG for a label and (a set of exposures?)
+#
+from ipp.Gpc1Manager import Gpc1Manager as Gpc1Manager
+from ipp.helpers import mkdir
+from ipp.constants.Globals import Globals as GlobalConstants
+from ipp.constants.Stack import Stack as StackConstants
+
+#################################################################
+class StackManager(Gpc1Manager):
+    """
+    Implementation of the stack.pro pantasks script tasks
+    """
+    scriptName = '/home/panstarrs/ippdor/local/bin/generate_stack_jobs.py'
+
+    def __init__(self,
+                 label,
+                 recipient):
+        """
+        Creates a connector to the gpc1 database as well as local and
+        nfs directories (their name is stack)
+        """
+        Gpc1Manager.__init__(self, recipient=recipient)
+        self.label = label
+        # Retrieve exposures names (even if they are known) and their warp_id
+        self.exposures_names = []
+        self.warp_ids = []
+        query = """
+SELECT rawExp.exp_name, warpRun.warp_id, warpRun.data_group
+FROM warpRun
+  JOIN fakeRun USING(fake_id)
+  JOIN camRun USING(cam_id)
+  JOIN chipRun USING(chip_id)
+  JOIN rawExp USING(exp_id)
+WHERE
+  warpRun.state = 'full'
+  AND warpRun.label = '%s'""" % (self.label)
+        print
+        print query
+        print
+        data = self.multiple_query(query)
+        for datum in data:
+            (exposure_name, warp_id, data_group) = datum
+            self.exposures_names.append(exposure_name)
+            self.warp_ids.append(str(warp_id))
+            self.data_group = data_group
+        self.stack_directory = '%s/%s/stack' % (GlobalConstants.LOCAL_TMP_DIR, self.label)
+        mkdir(self.stack_directory)
+
+    def build_create_stack_entries_job(self, stage_name = 'E010'):
+        """
+        Stage E010:
+        stacktool -dbname gpc1 -definebyquery -set_label ${label} 
+                  -set_data_group $data_group  -set_workdir $workdir
+                  -warp_id <w1> -warp_id <w2> [...]
+        """
+        argument_warp_ids =  '-warp_id %s' % ' -warp_id '.join(self.warp_ids)
+        jobs_filenames = '%s/%s.jobs' % (self.stack_directory, stage_name)
+        print "Generating Condor jobs file [%s] for stacking of %d exposures (stage %s)" % (jobs_filenames, 
+                                                                                            len(self.exposures_names), 
+                                                                                            stage_name)
+        output = open(jobs_filenames, 'w')
+        output.write('Executable = /home/panstarrs/ippdor/local/bin/encapsulate_stack.sh\n')
+        output.write('Universe = vanilla\n')
+        output.write('Output = %s/out.%s.stack_%s\n' % (GlobalConstants.NFS_TMP_DIR,
+                                                        self.label, 
+                                                        stage_name))
+        output.write('Error = %s/err.%s.stack_%s\n' % (GlobalConstants.NFS_TMP_DIR,
+                                                       self.label,
+                                                       stage_name))
+        output.write('Log = %s/stack_%s.log\n' % (GlobalConstants.LOCAL_TMP_DIR,
+                                                  stage_name))
+        output.write('Requirements = %s\n' % StackConstants.REQUIREMENTS)
+        output.write('Notification = Error\n')
+        output.write('\n')
+        output.write('getenv = True\n')
+        output.write('\n')
+        arguments = "%s -definebyquery -dbname gpc1" % GlobalConstants.STACKTOOL
+        arguments = "%s -set_dist_group NODIST -min_num 2" % arguments
+        arguments = "%s -set_label %s" % (arguments, self.label)
+        arguments = "%s -set_data_group %s" % (arguments, self.data_group)
+        arguments = "%s -set_workdir neb://@HOST.0@/gpc1/%s/stack" % (arguments, self.label)
+        arguments = "%s %s -min_num 2 " % (arguments, argument_warp_ids)
+        output.write('Arguments = %s\n' % arguments)
+        output.write('Queue\n')
+        output.write('\n')
+        output.close()
+
+    def build_new_stack_jobs(self, stage_name = 'E020'):
+        """Equivalent of stacktool -tosum
+        """
+        query = """
+SELECT
+    stackRun.stack_id,
+    stackRun.tess_id,
+    stackRun.skycell_id,
+    stackRun.workdir,
+    stackRun.reduction,
+    stackRun.state,
+    stackSumSkyfile.path_base
+FROM stackRun
+JOIN stackInputSkyfile USING(stack_id)
+JOIN warpRun USING(warp_id)
+LEFT JOIN stackSumSkyfile USING(stack_id)
+LEFT JOIN Label ON Label.label = stackRun.label
+WHERE
+    ((stackRun.state = 'new' AND stackSumSkyfile.stack_id IS NULL)
+    OR (stackRun.state = 'update' AND stackSumSkyfile.fault = 0 AND stackSumSkyfile.quality = 0))
+    AND (Label.active OR Label.active IS NULL)
+    AND warpRun.label='%s'
+GROUP BY stack_id
+HAVING SUM(IF(warpRun.state = 'full', 1, 0)) = COUNT(stackInputSkyfile.warp_id)
+""" % (self.label)
+        skycells_to_be_processed = self.multiple_query(query)
+        print 'Got %d jobs to generate' % len(skycells_to_be_processed)
+        jobs_filenames = '%s/%s.jobs' % (self.stack_directory, stage_name)
+        message = 'Generating Condor jobs file [%s] ' % (jobs_filenames)
+        message = '%s for stacking of %d skycells (stage %s)' % (message,
+                                                                 len(skycells_to_be_processed), 
+                                                                 stage_name)
+        print message
+        output = open(jobs_filenames, 'w')
+        output.write('Executable = /home/panstarrs/ippdor/local/bin/encapsulate_stack.sh\n')
+        output.write('Universe = vanilla\n')
+        output.write('Output = %s/out.%s.stack_%s\n' % (GlobalConstants.NFS_TMP_DIR,
+                                                        self.label, 
+                                                        stage_name))
+        output.write('Error = %s/err.%s.stack_%s\n' % (GlobalConstants.NFS_TMP_DIR,
+                                                       self.label, 
+                                                       stage_name))
+        output.write('Log = %s/stack_%s.log\n' % (GlobalConstants.LOCAL_TMP_DIR,
+                                                  stage_name))
+        output.write('Requirements = %s\n' % StackConstants.REQUIREMENTS)
+        output.write('Notification = Error\n')
+        output.write('\n')
+        output.write('getenv = True\n')
+        output.write('\n')
+        for skycell_info in skycells_to_be_processed:
+            (stack_id, tess_id, skycell_id, workdir, reduction, state, path_base) = skycell_info
+            outroot = '%s/%s/%s/%s.%s.stk.%d' % (workdir, tess_id, skycell_id, tess_id, skycell_id, stack_id)
+            arguments = "Arguments = %s/stack_skycell.pl --dbname gpc1 --threads 1 " % GlobalConstants.IPP_BIN_PATH
+            arguments = "%s --stack_id %d" % (arguments, stack_id)
+            arguments = "%s --outroot %s --redirect-output " % (arguments, outroot)
+            arguments = "%s --run-state %s" % (arguments, state)
+            output.write('%s\n' % arguments)
+            output.write('Queue\n')
+            output.write('\n')
+            if reduction is not None:
+                print 'reduction defined (%s) but not used' % reduction
+            if path_base is not None:
+                print 'path_base defined (%s) but not used' % path_base
+        if len(skycells_to_be_processed) == 0:
+            arguments = "Arguments = echo No skycell to be processed for label [%s]" % self.label
+            output.write('%s\n' % arguments)
+            output.write('Queue\n')
+            output.write('\n')
+        output.close()
+        return
+
+    def build_revert_stack_jobs(self, stage_name):
+        """-revertsumskyfile
+        """
+        query = """
+DELETE FROM stackSumSkyfile
+USING stackSumSkyfile, stackRun
+WHERE stackSumSkyfile.stack_id = stackRun.stack_id
+    AND stackRun.state = 'new'
+    AND fault != 0
+    AND stackRun.label = '%s'
+""" % (self.label)
+        self.multiple_query(query)
+        self.build_new_stack_jobs(stage_name)
+        return
+
+    def build_stack_summary_jobs(self):
+        """E060:
+        skycell_jpeg.pl --stage stack --stage_id $SASS_ID --camera $CAMERA --outroot $outroot
+        """
+        query = """
+SELECT DISTINCT sass_id,rawExp.camera,stackRun.workdir,stackRun.tess_id,stackRun.state
+       FROM stackRun
+       JOIN stackInputSkyfile ON stackRun.stack_id = stackInputSkyfile.stack_id
+       JOIN stackAssociationMap ON stackRun.stack_id = stackAssociationMap.stack_id
+       JOIN stackAssociation USING(sass_id)
+       JOIN warpRun USING(warp_id)
+       JOIN fakeRun USING(fake_id)
+       JOIN camRun USING(cam_id)
+       JOIN chipRun USING(chip_id)
+       JOIN rawExp USING(exp_id)
+       LEFT JOIN stackSummary USING(sass_id)
+WHERE stackRun.state = 'full' AND
+      stackSummary.projection_cell IS NULL AND
+      stackRun.label = '%s'
+""" % (self.label)
+        entries = self.multiple_query(query)
+        print 'Got %d jobs to generate' % len(entries)
+        stage_name = 'E060'
+        jobs_filenames = '%s/%s.jobs' % (self.stack_directory, stage_name)
+        print "Generating Condor jobs file [%s] for jpeging of %d skycells (stage %s)" % (jobs_filenames, 
+                                                                                          len(entries), 
+                                                                                          stage_name)
+        output = open(jobs_filenames, 'w')
+        output.write('Executable = /home/panstarrs/ippdor/local/bin/encapsulate_stack.sh\n')
+        output.write('Universe = vanilla\n')
+        output.write('Output = %s/out.%s.stack_%s\n' % (GlobalConstants.NFS_TMP_DIR,
+                                                        self.label, 
+                                                        stage_name))
+        output.write('Error = %s/err.%s.stack_%s\n' % (GlobalConstants.NFS_TMP_DIR,
+                                                       self.label, 
+                                                       stage_name))
+        output.write('Log = %s/stack_%s.log\n' % (GlobalConstants.LOCAL_TMP_DIR,
+                                                  stage_name))
+        output.write('Requirements = %s\n' % StackConstants.REQUIREMENTS)
+        output.write('Notification = Error\n')
+        output.write('\n')
+        output.write('getenv = True\n')
+        output.write('\n')
+        for entry in entries:
+            (sass_id, camera, workdir, tess_id, state) = entry
+            outroot = '%s/%s/%s.stk.%d.summary' % (workdir, tess_id, tess_id, sass_id)
+            arguments = 'Arguments = skycell_jpeg.pl --stage stack '
+            arguments = '%s --stage_id %d --camera %s --outroot %s' % (arguments,
+                                                                       sass_id,
+                                                                       camera, 
+                                                                       outroot)
+            output.write('%s\n' % arguments)
+            output.write('Queue\n')
+            output.write('\n')
+            if state is not None:
+                print 'state defined (%s) but not used' % state
+        if len(entries) == 0:
+            arguments = "Arguments = echo No skycell to be jpegged for label [%s]" % self.label
+            output.write('%s\n' % arguments)
+            output.write('Queue\n')
+            output.write('\n')
+        output.close()
+        return
+
+    def build_dag(self):
+        """
+        DAG construction
+        """
+        dag_name = '%s/stack.dag' % (self.stack_directory)
+        print "Generating Stack DAG file [%s] for label %s" % (dag_name,
+                                                               self.label)
+        output = open(dag_name, 'w')
+        output.write('# Generated DAG\n')
+        output.write('\n')
+        output.write('JOB\tE010\t%s/E010.jobs\n' % self.stack_directory)
+        output.write('SCRIPT POST E010 %s E020 %s %s\n' % (StackManager.scriptName, self.label, self.recipient))
+        output.write('JOB\tE020\t%s/E020.jobs\n' % self.stack_directory)
+        output.write('SCRIPT POST E020 %s E030 %s %s\n' % (StackManager.scriptName, self.label, self.recipient))
+        output.write('JOB\tE030\t%s/E030.jobs\n' % self.stack_directory)
+        output.write('SCRIPT POST E030 %s E040 %s %s\n' % (StackManager.scriptName, self.label, self.recipient))
+        output.write('JOB\tE040\t%s/E040.jobs\n' % self.stack_directory)
+        output.write('SCRIPT POST E040 %s E050 %s %s\n' % (StackManager.scriptName, self.label, self.recipient))
+        output.write('JOB\tE050\t%s/E050.jobs\n' % self.stack_directory)
+        output.write('SCRIPT POST E050 %s E060 %s %s\n' % (StackManager.scriptName, self.label, self.recipient))
+        output.write('JOB\tE060\t%s/E060.jobs\n' % self.stack_directory)
+        output.write('\n')
+        output.write('PARENT E010 CHILD E020\n')
+        output.write('PARENT E020 CHILD E030\n')
+        output.write('PARENT E030 CHILD E040\n')
+        output.write('PARENT E040 CHILD E050\n')
+        output.write('PARENT E050 CHILD E060\n')
+        output.write('\n')
+        output.close()
+        return dag_name
+
+#################################################################
+if __name__ == '__main__':
+    import sys
+    print '<Error>: Not intended to be used as an independent program'
+    print 'Use %s' % StackManager.scriptName
+    sys.exit(1)
Index: branches/meh_branches/ppstack_test/ippdor/src/ipp/WarpManager.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/ipp/WarpManager.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/ipp/WarpManager.py	(revision 33415)
@@ -0,0 +1,390 @@
+"""
+Generate the full warp DAG for one exposure
+Generate the condor job files for the warp substages
+"""
+from ipp.Gpc1Manager import Gpc1Manager as Gpc1Manager
+from ipp.helpers import mkdir
+from ipp.constants.Globals import Globals as GlobalConstants
+from ipp.constants.Warp import Warp as WarpConstants
+
+#################################################################
+class WarpManager(Gpc1Manager):
+    """
+    Warp stage in Condor
+    """
+    SCRIPT_NAME = '/home/panstarrs/ippdor/local/bin/generate_warp_jobs.py'
+
+    def __init__(self,
+                 label,
+                 exposure_name,
+                 recipient):
+        """
+        Creates a connector to the gpc1 database as well as local and
+        nfs directories (their name is warp)
+        """
+        Gpc1Manager.__init__(self, recipient=recipient)
+        self.label = label
+        self.exposure_name = exposure_name
+        self.nfs_warp_directory = '%s' % GlobalConstants.NFS_TMP_DIR
+        self.warp_directory = '%s/%s/%s/warp' % (GlobalConstants.LOCAL_TMP_DIR, 
+                                                label, exposure_name)
+        mkdir(self.warp_directory)
+
+    def build_new_exp_warp_jobs(self, stage_name = 'D010'):
+        """Equivalent of warptool -tooverlap and warptool -revert
+        """
+        query = """
+SELECT
+    warpRun.warp_id,
+    warpRun.workdir,
+    warpRun.tess_id,
+    rawExp.camera,
+    exp_tag
+FROM warpRun
+JOIN fakeRun
+    USING(fake_id)
+JOIN camRun
+    USING(cam_id)
+JOIN chipRun
+    USING(chip_id)
+JOIN rawExp
+    USING(exp_id)
+LEFT JOIN warpSkyCellMap
+    USING(warp_id)
+LEFT JOIN warpMask
+    ON warpRun.label = warpMask.label
+WHERE
+    warpRun.state = 'new'
+    AND fakeRun.state = 'full'
+    AND camRun.state = 'full'
+    AND chipRun.state = 'full'
+    AND warpSkyCellMap.warp_id IS NULL
+    AND warpMask.label IS NULL
+    AND warpRun.label = '%s'
+    AND exp_name = '%s'
+""" % (self.label, self.exposure_name)
+        warps = self.multiple_query(query)
+        jobs_filename = '%s/%s.jobs' % (self.warp_directory, stage_name)
+        message = 'Generating Condor jobs file [%s] for processing' % jobs_filename
+        message = "%s %d warps (stage %s)" % (message, len(warps), stage_name)
+        print message
+        output = open(jobs_filename, 'w')
+        output.write('Executable = /home/panstarrs/ippdor/local/bin/encapsulate_warp.sh\n')
+        output.write('Universe = vanilla\n')
+        output.write('Output = %s/out.%s.%s.warp_%s\n' % (self.nfs_warp_directory,
+                                                          self.label, 
+                                                          self.exposure_name, 
+                                                          stage_name))
+        output.write('Error = %s/err.%s.%s.warp_%s\n' % (self.nfs_warp_directory,
+                                                         self.label, 
+                                                         self.exposure_name, 
+                                                         stage_name))
+        output.write('Log = %s/%s_warp_%s.log\n' % (GlobalConstants.LOCAL_TMP_DIR,
+                                                    self.exposure_name, 
+                                                    stage_name))
+        output.write('Requirements = %s\n' % WarpConstants.REQUIREMENTS)
+        output.write('Notification = Error\n')
+        output.write('\n')
+        output.write('getenv = True\n')
+        output.write('\n')
+        for warp in warps:
+            (warp_id, workdir, tess_dir, camera, exp_tag) = warp
+            outlogfile = "%s/%s/%s.wrp.%s.log" % (workdir, exp_tag, exp_tag, warp_id)
+            arguments = "Arguments = %s --warp_id %d" % (GlobalConstants.WARP_OVERLAP, warp_id)
+            arguments = "%s --camera %s --tess_dir %s --logfile %s" % (arguments, camera, 
+                                                                       tess_dir, outlogfile)
+            output.write('%s\n' % arguments)
+            output.write('Queue\n')
+            output.write('\n')
+        if len(warps) == 0:
+            arguments = "Arguments = echo Nothing to do for stage %s" % stage_name
+            output.write('%s\n' % arguments)
+            output.write('Queue\n')
+            output.write('\n')
+        output.close()
+
+    def build_warp_exp_revert_jobs(self, stage_name):
+        """revert the -overlap exp stage
+        """
+        query = """
+DELETE FROM warpSkyCellMap
+USING warpSkyCellMap, warpRun, fakeRun, camRun, chipRun, rawExp
+WHERE warpSkyCellMap.warp_id = warpRun.warp_id
+    AND warpRun.fake_id = fakeRun.fake_id
+    AND fakeRun.cam_id = camRun.cam_id
+    AND camRun.chip_id = chipRun.chip_id
+    AND chipRun.exp_id = rawExp.exp_id
+    AND warpRun.state = 'new'
+    AND warpSkyCellMap.fault != 0
+    AND warpRun.label = '%s'
+    AND rawExp.exp_name = '%s'
+""" % (self.label, self.exposure_name)
+        self.multiple_query(query)
+        self.build_new_exp_warp_jobs(stage_name)
+
+    def build_new_skycell_warp_jobs(self, stage_name = 'D110'):
+        """Equivalent of warp.skycell.[load|run|revert] (warptool -towarp)
+        Dropped the label part. We are not in the general case
+        """
+        query = """
+SELECT DISTINCT
+        warpSkyCellMap.warp_id,
+        warpImfile.warp_skyfile_id,
+        warpSkyCellMap.skycell_id,
+        warpSkyCellMap.tess_id,
+        rawExp.camera,
+        warpRun.workdir,
+        rawExp.exp_tag,
+        warpRun.reduction,
+        chipRun.magicked,
+        warpRun.state
+    FROM warpRun
+    JOIN warpSkyCellMap
+        USING(warp_id)
+    JOIN warpImfile
+        ON warpSkyCellMap.warp_id = warpImfile.warp_id
+        AND warpSkyCellMap.skycell_id = warpImfile.skycell_id
+    JOIN fakeRun
+        USING(fake_id)
+    JOIN camRun
+        USING(cam_id)
+    JOIN chipRun
+        USING(chip_id)
+    JOIN chipProcessedImfile
+        USING(chip_id)
+    JOIN rawExp
+        ON chipRun.exp_id = rawExp.exp_id
+    LEFT JOIN warpSkyfile
+        ON warpRun.warp_id = warpSkyfile.warp_id
+        AND warpSkyCellMap.skycell_id = warpSkyfile.skycell_id
+        AND warpSkyCellMap.tess_id = warpSkyfile.tess_id
+    LEFT JOIN warpMask
+        ON warpRun.label = warpMask.label
+    LEFT JOIN Label ON warpRun.label = Label.label
+    WHERE
+        warpRun.state = 'new'
+        AND warpSkyfile.warp_id IS NULL
+        AND warpSkyfile.skycell_id IS NULL
+        AND warpSkyfile.tess_id IS NULL
+        AND fakeRun.state = 'full'
+        AND camRun.state = 'full'
+        AND chipRun.state = 'full'
+        AND warpMask.label IS NULL
+        AND warpSkyCellMap.fault = 0
+        AND (Label.active OR Label.active IS NULL)
+        AND warpRun.label = '%s'
+        AND rawExp.exp_name = '%s'
+""" % (self.label, self.exposure_name)
+        warps = self.multiple_query(query)
+        jobs_filename = '%s/%s.jobs' % (self.warp_directory, stage_name)
+        message ="Generating Condor jobs file [%s] for processing" % jobs_filename
+        message = "%s of %d warps (stage %s)" % (message, len(warps), stage_name)
+        print message
+        output = open(jobs_filename, 'w')
+        output.write('Executable = /home/panstarrs/ippdor/local/bin/encapsulate_warp.sh\n')
+        output.write('Universe = vanilla\n')
+        output.write('Output = %s/out.%s.%s.warp_%s\n' % (self.nfs_warp_directory,
+                                                          self.label, 
+                                                          self.exposure_name, 
+                                                          stage_name))
+        output.write('Error = %s/err.%s.%s.warp_%s\n' % (self.nfs_warp_directory,
+                                                         self.label, 
+                                                         self.exposure_name, 
+                                                         stage_name))
+        output.write('Log = %s/%s_warp_%s.log\n' % (self.warp_directory,
+                                                    self.exposure_name, stage_name))
+        output.write('Requirements = %s\n' % WarpConstants.REQUIREMENTS)
+        output.write('Notification = Error\n')
+        output.write('\n')
+        output.write('getenv = True\n')
+        output.write('\n')
+        for warp in warps:
+            ( warp_id, warp_skyfile_id, skycell_id, tess_dir, camera, workdir, 
+              exp_tag, reduction, raw_magicked, state ) = warp
+            outroot = '%s/%s/%s.wrp.%d.%s' % (workdir, exp_tag, exp_tag, warp_id, skycell_id)
+            arguments = "Arguments = %s" % GlobalConstants.WARP_SKYCELL
+            arguments = "%s --warp_id %d" % (arguments, warp_id)
+            arguments = "%s --warp_skyfile_id %d" % (arguments, warp_skyfile_id) 
+            arguments = "%s --skycell_id %s" % (arguments, skycell_id)
+            arguments = "%s --tess_dir %s" % (arguments, tess_dir)
+            arguments = "%s --camera %s" % (arguments,  camera)
+            arguments = "%s --outroot %s" % (arguments, outroot)
+            arguments = "%s --run-state %s" % (arguments, state)
+            if raw_magicked is not None and raw_magicked > 0:
+                arguments = '%s --magicked %d' % (arguments, raw_magicked)
+            if reduction is not None and reduction != 'NULL':
+                arguments = '%s --reduction %s' % (arguments, reduction)
+            output.write('%s\n' % arguments)
+            output.write('Queue\n')
+            output.write('\n')
+        if len(warps) == 0:
+            arguments = 'Arguments = echo Nothing to do for stage %s' % stage_name
+            output.write('%s\n' % arguments)
+            output.write('Queue\n')
+            output.write('\n')
+        output.close()
+
+    def build_warp_skycell_revert_jobs(self, stage_name):
+        """Revert stage D110
+        """
+        query = """
+DELETE FROM warpSkyfile
+USING warpSkyfile, warpRun, fakeRun, camRun, chipRun, rawExp
+WHERE warpSkyfile.warp_id = warpRun.warp_id
+    AND warpRun.fake_id = fakeRun.fake_id
+    AND fakeRun.cam_id = camRun.cam_id
+    AND camRun.chip_id = chipRun.chip_id
+    AND chipRun.exp_id = rawExp.exp_id
+    AND warpRun.state = 'new'
+    AND warpSkyfile.fault != 0
+    AND warpRun.label = '%s'
+    AND rawExp.exp_name = '%s'
+""" % (self.label, self.exposure_name)
+        self.multiple_query(query)
+        self.build_new_skycell_warp_jobs(stage_name)
+
+    def build_new_summary_warp_jobs(self):
+        """Stage D210
+        """
+        query = """
+SELECT DISTINCT warp_id,rawExp.camera,warpRun.workdir,warpRun.tess_id,rawExp.exp_tag,warpRun.state
+       FROM warpRun 
+       JOIN fakeRun USING(fake_id) JOIN camRun USING(cam_id)
+       JOIN chipRun USING(chip_id) JOIN rawExp USING(exp_id)
+       LEFT JOIN warpSummary USING(warp_id)
+WHERE warpRun.state = 'full' 
+  AND warpSummary.projection_cell IS NULL
+  AND warpRun.label = '%s'
+  AND rawExp.exp_name = '%s'
+""" % (self.label, self.exposure_name)
+        summaries = self.multiple_query(query)
+        jobs_filename = '%s/%s.jobs' % (self.warp_directory, 'D210')
+        message = "Generating Condor jobs file [%s] for processing of" % jobs_filename
+        message = "%s %d warp summaries (stage D210)" % (message, len(summaries))
+        print message
+        output = open(jobs_filename, 'w')
+        output.write('Executable = /home/panstarrs/ippdor/local/bin/encapsulate_warp.sh\n')
+        output.write('Universe = vanilla\n')
+        output.write('Output = %s/out.%s.%s.warp_D210\n' % (self.nfs_warp_directory,
+                                                            self.label, self.exposure_name))
+        output.write('Error = %s/err.%s.%s.warp_D210\n' % (self.nfs_warp_directory,
+                                                           self.label, self.exposure_name))
+        output.write('Log = %s/%s_warp_D210.log\n' % (self.warp_directory,
+                                                      self.exposure_name))
+        output.write('Requirements = %s\n' % WarpConstants.REQUIREMENTS)
+        output.write('Notification = Error\n')
+        output.write('\n')
+        output.write('getenv = True\n')
+        output.write('\n')
+        for summary in summaries:
+            (warp_id, camera, workdir, tess_id, exp_tag, state) = summary
+            outroot = '%s/%s/%s.wrp.%d.%s' % (workdir, exp_tag, exp_tag, warp_id, tess_id)
+            arguments = 'Arguments = %s -stage_id %d -camera %s -outroot %s' % (GlobalConstants.WARP_JPEG,
+                                                                                warp_id, camera, outroot)
+            output.write('%s\n' % arguments)
+            output.write('Queue\n')
+            output.write('\n')
+            if state is not None:
+                print 'state is defined but not used: %s' % state
+        if len(summaries) == 0:
+            arguments = 'Arguments = echo Nothing to do for stage D210'
+            output.write('%s\n' % arguments)
+            output.write('Queue\n')
+            output.write('\n')
+        output.close()
+
+    def advance_warp(self):
+        """Stage 310 (last). Create the job to 'warptool -advancerun -warp_id -label'
+        """
+        jobs_filename = '%s/%s.jobs' % (self.warp_directory, 'D310')
+        print "Generating Condor jobs file [%s] for advancing of warp related to %s (stage D310)" % (jobs_filename, 
+                                                                                                     self.exposure_name)
+        output = open(jobs_filename, 'w')
+        output.write('Executable = /home/panstarrs/ippdor/local/bin/encapsulate_warp.sh\n')
+        output.write('Universe = vanilla\n')
+        output.write('Output = %s/out.%s.%s.warp_D210\n' % (self.nfs_warp_directory,
+                                                            self.label, self.exposure_name))
+        output.write('Error = %s/err.%s.%s.warp_D210\n' % (self.nfs_warp_directory,
+                                                           self.label, self.exposure_name))
+        output.write('Log = %s/%s_warp_D210.log\n' % (self.warp_directory,
+                                                      self.exposure_name))
+        output.write('Requirements = %s\n' % WarpConstants.REQUIREMENTS)
+        output.write('Notification = Error\n')
+        output.write('\n')
+        output.write('getenv = True\n')
+        output.write('\n')
+        arguments = '%s -advancerun -label %s -warp_id %d' % (GlobalConstants.WARPTOOL, 
+                                                              self.label, 
+                                                              self.get_warp_id(self.exposure_name, 
+                                                                               self.label))
+        output.write('Arguments = %s\n' % arguments)
+        output.write('Queue\n')
+        output.write('\n')
+        output.close()
+
+    def build_warp_dag(self):
+        """Build the warp stage DAG
+        """
+        dag_name = '%s/warp.dag' % (self.warp_directory)
+        print "Generating Warp DAG file [%s] for exposure %s (label %s)" % (dag_name,
+                                                                            self.exposure_name,
+                                                                            self.label)
+        output = open(dag_name, 'w')
+        output.write('# Generated DAG\n')
+        output.write('\n')
+        output.write('# Exp\n')
+        output.write('JOB\tD010\t%s/D010.jobs\n' % self.warp_directory)
+        output.write('SCRIPT POST D010 %s D020 %s %s\n' % (WarpManager.SCRIPT_NAME,
+                                                           self.label, self.exposure_name))
+        output.write('JOB\tD020\t%s/D020.jobs\n' % self.warp_directory)
+        output.write('SCRIPT POST D020 %s D030 %s %s\n' % (WarpManager.SCRIPT_NAME, 
+                                                           self.label, self.exposure_name))
+        output.write('JOB\tD030\t%s/D030.jobs\n' % self.warp_directory)
+        output.write('SCRIPT POST D030 %s D040 %s %s\n' % (WarpManager.SCRIPT_NAME, 
+                                                           self.label, self.exposure_name))
+        output.write('JOB\tD040\t%s/D040.jobs\n' % self.warp_directory)
+        output.write('SCRIPT POST D040 %s D110 %s %s\n' % (WarpManager.SCRIPT_NAME, 
+                                                           self.label, self.exposure_name))
+        output.write('\n')
+        output.write('# Skycell\n')
+        output.write('JOB\tD110\t%s/D110.jobs\n' % self.warp_directory)
+        output.write('SCRIPT POST D110 %s D120 %s %s\n' % (WarpManager.SCRIPT_NAME, 
+                                                           self.label, self.exposure_name))
+        output.write('JOB\tD120\t%s/D120.jobs\n' % self.warp_directory)
+        output.write('SCRIPT POST D120 %s D130 %s %s\n' % (WarpManager.SCRIPT_NAME, 
+                                                           self.label, self.exposure_name))
+        output.write('JOB\tD130\t%s/D130.jobs\n' % self.warp_directory)
+        output.write('SCRIPT POST D130 %s D140 %s %s\n' % (WarpManager.SCRIPT_NAME, 
+                                                           self.label, self.exposure_name))
+        output.write('JOB\tD140\t%s/D140.jobs\n' % self.warp_directory)
+        output.write('SCRIPT POST D140 %s D210 %s %s\n' % (WarpManager.SCRIPT_NAME, 
+                                                           self.label, self.exposure_name))
+        output.write('\n')
+        output.write('# Summary\n')
+        output.write('JOB\tD210\t%s/D210.jobs\n' % self.warp_directory)
+        output.write('SCRIPT POST D210 %s D310 %s %s\n' % (WarpManager.SCRIPT_NAME, 
+                                                           self.label, self.exposure_name))
+        output.write('\n')
+        output.write('# Advance\n')
+        output.write('JOB\tD310\t%s/D310.jobs\n' % self.warp_directory)
+        output.write('\n')
+        output.write('PARENT D010 CHILD D020\n')
+        output.write('PARENT D020 CHILD D030\n')
+        output.write('PARENT D030 CHILD D040\n')
+        output.write('PARENT D040 CHILD D110\n')
+        output.write('\n')
+        output.write('PARENT D110 CHILD D120\n')
+        output.write('PARENT D120 CHILD D130\n')
+        output.write('PARENT D130 CHILD D140\n')
+        output.write('PARENT D140 CHILD D210\n')
+        output.write('\n')
+        output.write('PARENT D210 CHILD D310\n')
+        output.write('\n')
+        output.close()
+        return dag_name
+
+#################################################################
+if __name__ == '__main__':
+    import sys
+    print 'Error: Use %s' % WarpManager.SCRIPT_NAME
+    sys.exit(0)
Index: branches/meh_branches/ppstack_test/ippdor/src/ipp/__init__.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/ipp/__init__.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/ipp/__init__.py	(revision 33415)
@@ -0,0 +1,3 @@
+"""
+Classes dedicated for IPP Processing in the Condor framework
+"""
Index: branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/Camera.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/Camera.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/Camera.py	(revision 33415)
@@ -0,0 +1,63 @@
+"""
+This is the file in which all constants relative to the Condor-IPP
+framework are defined for the Camera stage. 
+
+It defines the following constants:
+- REQUIREMENTS: The Condor requirements to run Cam jobs
+- EMAIL_BODY_MULTIPLE_CAM_ENTRIES: The e-mail body when there is more
+  than one cam entry for one chip (Note: it seems it was useful when
+  chiptool has a bug but it seems to be fixed now (see svn log for
+  revision 32520))
+- EMAIL_BODY_REVERT_FAILURE: The e-mail body which is sent when revert
+  has failed
+"""
+from ipp.exceptions.UninstantiableIppException import UninstantiableIppException
+from ipp.constants.Globals import Globals as GlobalConstants
+
+class Camera:
+    """
+    Class where all cam stage constants are defined
+    """
+    def __init__(self):
+        raise UninstantiableIppException()
+
+    # Change it according to new needs
+    REQUIREMENTS = GlobalConstants.REQUIREMENTS
+
+    # Body of the e-mail which is sent when there is more than one cam
+    # entry for one chip (Note: it seems it was useful when chiptool
+    # has a bug but it seems to be fixed now (see svn log for revision
+    # 32520)).
+    # It is expected to valuate this expression with 3 parameters:
+    # - #1: label (str)
+    # - #2: exposure name (str)
+    # - #3: cam_id (int)
+    EMAIL_BODY_MULTIPLE_CAM_ENTRIES = """Hi,
+
+this is an e-mail generated by the ippdor subsystem.
+
+There is more than one entry for the camera stage for 
+label %s / exposure %s.
+
+I will only process the entry with cam_id = %d and I strongly
+suggest to delete the other entries in the camRun table.
+
+Thanks,
+"""
+    # Body of the e-mail which is sent when revert has failed. It is
+    # expected to valuate this expression with 3 parameters:
+    # - #1: exposure name (str)
+    # - #2: label (str)
+    # - #3: exposure name (str)
+    EMAIL_BODY_REVERT_FAILURE = """Hi,
+
+this is an e-mail generated by the ippdor subsystem.
+
+exposure %s has a fault at camera stage (after 3 revert attempts).
+
+You could identify where the problem comes from by running the query:
+
+SELECT exp_name, rawExp.exp_id, chipRun.chip_id, camRun.cam_id, camRun.state, camProcessedExp.fault, camProcessedExp.quality, camProcessedExp.path_base FROM camRun JOIN camProcessedExp USING(cam_id) JOIN chipRun USING(chip_id) JOIN rawExp USING(exp_id) WHERE camRun.label = '%s' AND exp_name='%s';
+
+Thanks,
+"""
Index: branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/Chip.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/Chip.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/Chip.py	(revision 33415)
@@ -0,0 +1,70 @@
+"""
+This is the file in which all constants relative to the Condor-IPP
+framework are defined for the chip stage.
+
+It defines the following constants:
+- REQUIREMENTS: The Condor requirements to run Chip jobs
+- EMAIL_SUBJECT_FAILED_REVERT: The e-mail subject which is sent when
+  revert has failed
+- EMAIL_BODY_FAILED_REVERT: The e-mail body which is sent when revert
+  has failed
+ 
+"""
+from ipp.exceptions.UninstantiableIppException import UninstantiableIppException
+from ipp.constants.Globals import Globals as GlobalConstants
+
+class Chip:
+    """
+    This class is not intended to be instantiated.
+    All its members are static.
+    """
+    def __init__(self):
+        raise UninstantiableIppException()
+
+    # Change it according to new needs
+    REQUIREMENTS = GlobalConstants.REQUIREMENTS
+
+    # This is the e-mail body which is sent when revert has failed. It
+    # is expected to valuate this expression with two parameters:
+    # - #1: The label (string)
+    # - #2: The exposure name (string)
+    EMAIL_SUBJECT_FAILED_REVERT = 'Chip stage failed for label %s / exposure %s)'
+
+    # This is the e-mail body which is sent when revert has
+    # failed. Some values need to be replaced, namely:
+    # - @COUNT_FAILED_CHIPS@: the number of chips which have failed;
+    # - @LABEL@: the label;
+    # - @EXPOSURE_NAME@: the exposure name;
+    # - @LIST_PROCESSING_LOGS@: the list of log files that should be
+    #   checked by the operator;
+    # - @CHIPTOOL_UPDATEPROCESSEDIMFILE@: the list of commands that
+    #   could be run to ignore some flaky OTAs
+    # - @CHIPTOOL_REVERTPROCESSEDIMFILE@: the command that shoudl be
+    #   run to revert the exposure.
+    # It is also expected to valuate this expression with one argument:
+    # - #1: The path to Globals.LOCAL_TMP_DIR
+    EMAIL_BODY_FAILED_REVERT = """Hi,
+
+this is an e-mail generated by the ippdor subsystem.
+
+@COUNT_FAILED_CHIPS@ chips have a fault after 3 revert attempts.
+
+You can identify them by running the query:
+SELECT exp_name, chip_id, class_id, chipProcessedImfile.fault, path_base FROM chipProcessedImfile JOIN chipRun USING(chip_id) JOIN rawExp ON chipRun.exp_id = rawExp.exp_id WHERE chipRun.label = '@LABEL@' AND exp_name = '@EXPOSURE_NAME@' AND chipProcessedImfile.fault!=0;
+
+You can have a look at the processing logs:
+@LIST_PROCESSING_LOGS@
+
+Please fix those faults.
+
+Note that some people will use:
+@CHIPTOOL_UPDATEPROCESSEDIMFILE@
+and then:
+@CHIPTOOL_REVERTPROCESSEDIMFILE@
+
+Once you have FIXED ALL PROBLEMS FOR ALL EXPOSURES, on the host where the Condor task was submitted, submit the DAG (the host name is shown in the following line) again:
+condor_submit_dag -maxidle 1000 -maxjobs 500 %s/@LABEL@/ch2st.dag
+
+Thanks,
+
+"""
Index: branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/ChipToWarp.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/ChipToWarp.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/ChipToWarp.py	(revision 33415)
@@ -0,0 +1,19 @@
+"""
+This is the file in which all constants relative to the Condor-IPP
+framework are defined for the ippdor_chip_to_warp script. 
+
+It defines the following data members:
+TODO
+"""
+from ipp.exceptions.UninstantiableIppException import UninstantiableIppException
+from ipp.constants.Globals import Globals as GlobalConstants
+
+class ChipToWarp:
+    """
+    Class where all fake stage constants are defined
+    """
+    def __init__(self):
+        raise UninstantiableIppException()
+
+    # Change it according to new needs
+    REQUIREMENTS = GlobalConstants.REQUIREMENTS
Index: branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/Fake.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/Fake.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/Fake.py	(revision 33415)
@@ -0,0 +1,19 @@
+"""
+This is the file in which all constants relative to the Condor-IPP
+framework are defined for the Camera stage. 
+
+It defines the following constants:
+- REQUIREMENTS: The Condor requirements to run Fake jobs
+"""
+from ipp.exceptions.UninstantiableIppException import UninstantiableIppException
+from ipp.constants.Globals import Globals as GlobalConstants
+
+class Fake:
+    """
+    Class where all fake stage constants are defined
+    """
+    def __init__(self):
+        raise UninstantiableIppException()
+
+    # Change it according to new needs
+    REQUIREMENTS = GlobalConstants.REQUIREMENTS
Index: branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/Globals.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/Globals.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/Globals.py	(revision 33415)
@@ -0,0 +1,62 @@
+"""
+This is the file in which all constants relative to the Condor-IPP
+framework are defined. It defines the following constants:
+
+- IPPDOR_BINARY_PATH: the path to ippdor executables (they do not
+  strictly have to be binaries)
+- LOCAL_TMP_DIR: The local file system where some files can be
+  stored. On the IPP production cluster, it is dependent on the host
+  name.
+- NFS_TMP_DIR: The NFS file system where some files are located.
+- IPP_BIN_PATH: The path to the IPP executables. It is recommended to
+  leave it to a fixed value (like current.lin64) and to relink 
+  current.lin64 to any new install.
+"""
+
+from ipp.exceptions.UninstantiableIppException import UninstantiableIppException
+from ipp.IppSite import IppSite
+
+class Globals:
+    """
+    This class is not intended to be instantiated.
+    All its members are static (and constant).
+    """
+    def __init__(self):
+        """We don't want this class to be instantiated"""
+        raise UninstantiableIppException()
+
+    IPPDOR_BINARY_PATH = IppSite.TARGET_BINARY_DIRECTORY
+    LOCAL_TMP_DIR = IppSite.LOCAL_TMP_DIR
+    NFS_TMP_DIR = IppSite.NFS_TMP_DIR
+    IPP_BIN_PATH = IppSite.IPP_BIN_PATH
+
+    # Global requirements
+    REQUIREMENTS     = 'LoadAvg < 0.3'
+
+    # Condor scripts
+    GENERATE_EXPOSURE_DAG = '%s/generate_exposure_dag.py %s %s %s' % (IPPDOR_BINARY_PATH, 
+                                                                      '%s', '%s', '%s')
+
+    ###################
+    # IPP executables
+    ###################
+    # Chip stage
+    CHIPTOOL = '%s/chiptool -dbname gpc1' % IPP_BIN_PATH
+    CHIPIMFILE = '%s/chip_imfile.pl --dbname gpc1 --redirect-output --verbose --threads 1' % IPP_BIN_PATH
+
+    # Cam stage
+    CAMTOOL = '%s/camtool' % IPP_BIN_PATH
+    CAMEXP = '%s/camera_exp.pl --dbname gpc1 --redirect-output --verbose' % IPP_BIN_PATH
+
+    # Fake stage
+    FAKETOOL = '%s/faketool -dbname gpc1' % IPP_BIN_PATH
+    FAKE_IMFILE = '%s/fake_imfile.pl --dbname gpc1 --redirect-output --verbose' % IPP_BIN_PATH
+
+    # Warp stage
+    WARPTOOL = '%s/warptool -dbname gpc1' % IPP_BIN_PATH
+    WARP_OVERLAP = '%s/warp_overlap.pl --dbname gpc1' % IPP_BIN_PATH
+    WARP_SKYCELL = '%s/warp_skycell.pl --dbname gpc1 --threads 1 --redirect-output' % IPP_BIN_PATH
+    WARP_JPEG = '%s/skycell_jpeg.pl -dbname gpc1 -stage warp' % IPP_BIN_PATH
+
+    # Stack stage
+    STACKTOOL = '%s/stacktool' % IPP_BIN_PATH
Index: branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/Stack.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/Stack.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/Stack.py	(revision 33415)
@@ -0,0 +1,19 @@
+"""
+This is the file in which all constants relative to the Condor-IPP
+framework are defined for the Stack stage. 
+
+It defines the following constants:
+- REQUIREMENTS: The Condor requirements to run Stack jobs
+"""
+from ipp.exceptions.UninstantiableIppException import UninstantiableIppException
+from ipp.constants.Globals import Globals as GlobalConstants
+
+class Stack:
+    """
+    Class where all warp stack constants are defined
+    """
+    def __init__(self):
+        raise UninstantiableIppException()
+
+    # Change it according to new needs
+    REQUIREMENTS = GlobalConstants.REQUIREMENTS
Index: branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/Warp.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/Warp.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/Warp.py	(revision 33415)
@@ -0,0 +1,19 @@
+"""
+This is the file in which all constants relative to the Condor-IPP
+framework are defined for the Warp stage. 
+
+It defines the following constants:
+- REQUIREMENTS: The Condor requirements to run Warp jobs
+"""
+from ipp.exceptions.UninstantiableIppException import UninstantiableIppException
+from ipp.constants.Globals import Globals as GlobalConstants
+
+class Warp:
+    """
+    Class where all warp stage constants are defined
+    """
+    def __init__(self):
+        raise UninstantiableIppException()
+
+    # Change it according to new needs
+    REQUIREMENTS = GlobalConstants.REQUIREMENTS
Index: branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/__init__.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/__init__.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/ipp/constants/__init__.py	(revision 33415)
@@ -0,0 +1,21 @@
+"""
+This package is the package where constants are defined.
+
+- The Globals module defines constants which can be used anywhere. It
+  is also the module where path to executables are defined;
+
+- The Chip module defines constants relative to the Chip stage (but
+  the executables)
+
+- The Cam module defines constants relative to the Camera stage (but
+  the executables)
+
+- The Fake module defines constants relative to the Fake stage (but
+  the executables)
+
+- The Warp module defines constants relative to the Warp stage (but
+  the executables)
+
+- The Stack module defines constants relative to the Stack stage (but
+  the executables)
+"""
Index: branches/meh_branches/ppstack_test/ippdor/src/ipp/exceptions/IppException.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/ipp/exceptions/IppException.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/ipp/exceptions/IppException.py	(revision 33415)
@@ -0,0 +1,12 @@
+#!/usr/bin/env python
+
+"""
+A class for the IPP-Condor framework
+"""
+class IppException(Exception):
+    """
+    A class for our own exception
+    """
+    def __init__(self, message = None):
+        Exception.__init__(self, message)
+
Index: branches/meh_branches/ppstack_test/ippdor/src/ipp/exceptions/UninstantiableIppException.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/ipp/exceptions/UninstantiableIppException.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/ipp/exceptions/UninstantiableIppException.py	(revision 33415)
@@ -0,0 +1,15 @@
+"""
+A class for the IPP-Condor framework
+"""
+
+from ipp.exceptions.IppException import IppException
+
+class UninstantiableIppException(IppException):
+    """
+    An exception for classes that we don't want to be instantiated
+    (e.g. constants containers)
+    """
+    def __init__(self, message = None):
+        if message is None:
+            IppException.__init__(self, 
+                                  'Not intended to be instantiated')
Index: branches/meh_branches/ppstack_test/ippdor/src/ipp/exceptions/__init__.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/ipp/exceptions/__init__.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/ipp/exceptions/__init__.py	(revision 33415)
@@ -0,0 +1,5 @@
+"""
+Module containing the exceptions classes for the Condor-IPP framework.
+
+All classes should derive from IppException.
+"""
Index: branches/meh_branches/ppstack_test/ippdor/src/ipp/helpers.py
===================================================================
--- branches/meh_branches/ppstack_test/ippdor/src/ipp/helpers.py	(revision 33415)
+++ branches/meh_branches/ppstack_test/ippdor/src/ipp/helpers.py	(revision 33415)
@@ -0,0 +1,43 @@
+"""
+Various Condor-IPP helpers
+"""
+import os
+import socket
+
+#################################################################
+def mkdir(directory_name):
+    """From http://code.activestate.com/recipes/82465-a-friendly-mkdir/
+
+    Works the way a good mkdir should :)
+        - already exists, silently complete
+        - regular file in the way, raise an exception
+        - parent directory(ies) does not exist, make them as well
+    """
+    print 'Creating directory [%s]' % directory_name
+    if os.path.isdir(directory_name):
+        pass
+    elif os.path.isfile(directory_name):
+        raise OSError("a file with the same name as the desired " \
+                      "dir, '%s', already exists." % directory_name)
+    else:
+        head, tail = os.path.split(directory_name)
+        if head and not os.path.isdir(head):
+            mkdir(head)
+        if tail:
+            os.mkdir(directory_name)
+
+#################################################################
+def hostname():
+    """
+    Get the host name
+    >>> print hostname()
+    neverland
+    """
+    return socket.gethostbyaddr(socket.gethostname())[0].split('.')[0]
+
+#################################################################
+if __name__ == '__main__':
+    print 'Host is [%s]' % hostname()
+    print 'Not intended to be used independently'
+    import sys
+    sys.exit(1)
Index: branches/meh_branches/ppstack_test/magic/Makefile
===================================================================
--- branches/meh_branches/ppstack_test/magic/Makefile	(revision 32161)
+++ branches/meh_branches/ppstack_test/magic/Makefile	(revision 33415)
@@ -18,5 +18,5 @@
 clean:
 	if [ -d magic ]; then (cd magic && make -f Makefile.magic clean); fi
-	if [ -d ssa-core-cpp ]; then (cd ssa-core-cpp && make clean); fi
+	# if [ -d ssa-core-cpp ]; then (cd ssa-core-cpp && make clean); fi
 
 update:
Index: branches/meh_branches/ppstack_test/magic/remove/src/streaksio.c
===================================================================
--- branches/meh_branches/ppstack_test/magic/remove/src/streaksio.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/magic/remove/src/streaksio.c	(revision 33415)
@@ -632,4 +632,5 @@
 
     if (!isImage(in)) {
+        psLogMsg("streaksremove", PS_LOG_INFO, "%s contains no image\n", in->name);
         return;
     }
@@ -752,5 +753,5 @@
         return;
     }
-    psArray *table = psFitsReadTable(in->fits);
+    psFitsTable *table = psFitsReadTableNew(in->fits);
     if (!table) {
         psError(PS_ERR_UNKNOWN, false, "failed to read table in extension %d from in->resolved name", extnum);
@@ -758,8 +759,9 @@
     }
 
-    if (!psFitsWriteTable(out->fits, out->header, table, extname)) {
+    if (!psFitsWriteTableNew(out->fits, out->header, table, extname)) {
         psError(PS_ERR_UNKNOWN, false, "failed to copy table in extension %d", extnum);
         streaksExit("", PS_EXIT_DATA_ERROR);
     }
+    psFree(table);
 }
 
@@ -778,4 +780,9 @@
 setupImageRefs(sFile *out, sFile *recoveryOut, sFile *in, int extnum, bool exciseAll)
 {
+    if (!in->image) {
+        // Input file is likely corrupt. XXX: Shouldn't we have caught this earlier?
+        psError(PS_ERR_UNKNOWN, true, "image is null for extension %d of %s file is likely corrupt", extnum, in->name);
+        streaksExit("", PS_EXIT_UNKNOWN_ERROR);
+    }
     if (!exciseAll) {
         // set output image to be the input image
@@ -839,4 +846,5 @@
     psFree(sfile->name);
     psFree(sfile->resolved_name);
+    psFree(sfile->fits);
     psFree(sfile);
 }
@@ -915,4 +923,8 @@
         sFileFree(sf->outWeight);
         sFileFree(sf->recWeight);
+    }
+    if (sf->inSources) {
+        sFileFree(sf->inSources);
+        sFileFree(sf->outSources);
     }
 }
@@ -1301,4 +1313,5 @@
                 streaksExit("", PS_EXIT_UNKNOWN_ERROR);
             }
+            psFree(compress);
         } else {
             copyTable(out, in, extnum);
Index: branches/meh_branches/ppstack_test/magic/remove/src/streaksremove.c
===================================================================
--- branches/meh_branches/ppstack_test/magic/remove/src/streaksremove.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/magic/remove/src/streaksremove.c	(revision 33415)
@@ -278,5 +278,4 @@
 
     if (statsFileName) {
-    // Write out
         psString resolved = pmConfigConvertFilename(statsFileName, config, true, true); // Resolved filename
         if (!resolved) {
@@ -292,5 +291,5 @@
     }
 
-    // all done. Clean up to look for memory leaks.
+    // all done. Clean up and look for memory leaks.
 
     psFree(sfiles);
@@ -302,5 +301,7 @@
     psLibFinalize();
 
-    fprintf(stderr, "Found %d leaks at %s\n", psMemCheckLeaks (0, NULL, NULL, false), "streaksremove");
+    int leaks = psMemCheckLeaks (0, NULL, stdout, false);
+
+    fprintf(stderr, "Found %d leaks at %s\n", leaks, "streaksremove");
 
     return 0;
@@ -917,5 +918,4 @@
     sFile *out = sfiles->outSources;
 
-
     // Primary header, should be "something.hdr"
     {
@@ -960,45 +960,82 @@
         }
 
-        psArray *inTable = psFitsReadTable(in->fits);
-        if (!inTable) {
+#ifdef CHECK_MEM_USE
+        bool printStats = false;
+        size_t allocated = 0;
+        size_t persistent = 0;
+        size_t allocatedBefore = psMemStats(printStats, &allocated, &persistent);
+        printf("Before: %12lld %12lld %12lld\n", (long long) allocated, (long long) persistent, (long long) allocatedBefore);
+#endif
+
+        psFitsTable *table = psFitsReadTableNew(in->fits);
+
+#ifdef CHECK_MEM_USE
+        size_t allocatedAfter = psMemStats(printStats, &allocated, &persistent);
+        printf("After:  %12lld %12lld %12lld\n", (long long) allocated, (long long) persistent, (long long) allocatedAfter);
+        printf("Change: %12lld\n", (long long) allocatedAfter - allocatedBefore);
+#endif
+
+        if (!table) {
             psErrorStackPrint(stderr, "failed to read table in %s", in->resolved_name);
             streaksExit("", PS_EXIT_DATA_ERROR);
         }
 
-        psArray *outTable = psArrayAllocEmpty(inTable->n);
-        int j = 0;
         int numCensored = 0;
-        for (int i = 0 ; i < inTable->n; i++) {
-            psMetadata *row = inTable->data[i];
-
-            psF32 x = psMetadataLookupF32(NULL, row, "X_PSF");
-            psF32 y = psMetadataLookupF32(NULL, row, "Y_PSF");
-
-            psImageMaskType mask;
-            if ((x >= maskImage->numCols) || (y >= maskImage->numRows) || (x <  0) || (y < 0) || isnan(x) || isnan(y)) {
-                mask = maskStreak;
-            } else {
-                mask = maskImage->data.PS_TYPE_IMAGE_MASK_DATA[(int)y][(int)x];
-            }
-
-            // Key the source if the center pixel is not masked with maskStreak
-            if (!(mask & maskStreak) ) {
-                psArraySet(outTable, j++, row);
-            } else {
-                numCensored++;
-            }
-        }
-
-        // get rid of unused elements (don't know if this is necessary)
-        psArrayRealloc(outTable, j);
-
-        addDestreakKeyword(header, true);
-        if (psArrayLength(outTable) > 0) {
+        bool *rowCensored = NULL;
+        if (table->numRows > 0) {
+            // For speed we get intimate with the table
+            // avoid 2 * numRows lookups of the column number by looking
+            // it up ahead of time and verifying that the type is what we
+            // expect.
+            int xCol = psFitsTableGetColumnNumber(table, "X_PSF");
+            assert(xCol >= 0 && xCol < table->numCols);
+            assert(table->columns[xCol].type == PS_DATA_F32);
+
+            int yCol = psFitsTableGetColumnNumber(table, "Y_PSF");
+            assert(yCol >= 0 && yCol < table->numCols);
+            assert(table->columns[yCol].type == PS_DATA_F32);
+
+            // We mark censored rows in this array.
+            rowCensored = psAlloc(table->numRows * sizeof(bool));
+            memset(rowCensored, 0, table->numRows * sizeof(bool));
+
+            for (int i = 0 ; i < table->numRows; i++) {
+
+#ifndef GO_SLOWER
+                psF32 x = table->columns[xCol].data.F32[i];
+                psF32 y = table->columns[yCol].data.F32[i];
+#else
+                psF32 x = psFitsTableGetF32(NULL, table, i, "X_PSF");
+                psF32 y = psFitsTableGetF32(NULL, table, i, "Y_PSF");
+#endif
+                psImageMaskType mask;
+                if ((x >= maskImage->numCols) || (y >= maskImage->numRows) || (x <  0) || (y < 0) || isnan(x) || isnan(y)) {
+                    mask = maskStreak;
+                } else {
+                    mask = maskImage->data.PS_TYPE_IMAGE_MASK_DATA[(int)y][(int)x];
+                }
+
+                // Key the source if the center pixel is not masked with maskStreak
+                if (mask & maskStreak) {
+                    rowCensored[i] = true;
+                    numCensored++;
+                }
+            }
+
+            // remove the censored rows from the table
+            if (!psFitsTableCensor(table, rowCensored)) {
+                streaksExit("", PS_EXIT_PROG_ERROR);
+            }
+
+            addDestreakKeyword(header, true);
+        }
+
+        if (table->numCols > 0) {
             printf("Censored %d sources\n", numCensored);
-            if (! psFitsWriteTable(out->fits, header, outTable, extname)) {
+            if (! psFitsWriteTableNew(out->fits, header, table, extname)) {
                 psErrorStackPrint(stderr, "failed to write table to %s", out->resolved_name);
                 streaksExit("", PS_EXIT_DATA_ERROR);
             }
-        } else if (psArrayLength(inTable) == 0) {
+        } else {
             printf("No input sources\n");
             // We'd like to write a blank table, but this is as good as it gets without more work to parse the
@@ -1008,14 +1045,8 @@
                 streaksExit("", PS_EXIT_DATA_ERROR);
             }
-        } else {
-            printf("Censored ALL %d sources\n", numCensored);
-            if (! psFitsWriteTableEmpty(out->fits, header, inTable->data[0], extname)) {
-                psErrorStackPrint(stderr, "failed to write empty table to %s", out->resolved_name);
-                streaksExit("", PS_EXIT_DATA_ERROR);
-            }
         }
         psFree(header);
-        psFree(outTable);
-        psFree(inTable);
+        psFree(table);
+        psFree(rowCensored);
     }
 
@@ -1038,15 +1069,16 @@
         }
 
-        psArray *inTable = psFitsReadTable(in->fits);
-        if (!inTable) {
+        psFitsTable *table = psFitsReadTableNew(in->fits);
+        if (!table) {
             psErrorStackPrint(stderr, "failed to read table in %s", in->resolved_name);
             streaksExit("", PS_EXIT_DATA_ERROR);
         }
-        if (! psFitsWriteTable(out->fits, header, inTable, extname)) {
+        if (! psFitsWriteTableNew(out->fits, header, table, extname)) {
             psErrorStackPrint(stderr, "failed to write table to %s", out->resolved_name);
             streaksExit("", PS_EXIT_DATA_ERROR);
         }
-    }
-
+        psFree(header);
+        psFree(table);
+    }
 
     if (!psFitsClose(out->fits)) {
@@ -1054,3 +1086,4 @@
         streaksExit("", PS_EXIT_DATA_ERROR);
     }
-}
+    out->fits = NULL;   // freed by psFitsclose()
+}
Index: branches/meh_branches/ppstack_test/ppImage/src/ppImage.h
===================================================================
--- branches/meh_branches/ppstack_test/ppImage/src/ppImage.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppImage/src/ppImage.h	(revision 33415)
@@ -40,4 +40,5 @@
     bool doPatternRow;                  // Row pattern correction
     bool doPatternCell;                 // Cell pattern correction
+    bool doPatternContinuity;           // Cell continuity correction
     bool doFringe;                      // Fringe subtraction
     bool doPhotom;                      // Source identification and photometry
@@ -109,4 +110,6 @@
     psStatsOptions patternCellMean;        // Statistic for mean
 
+  int patternContinuityEdgeWidth;        // Size of box to use for edge matching.
+  
     int remnanceSize;                   // Size for remnance detection
     float remnanceThresh;               // Threshold for remnance detection
Index: branches/meh_branches/ppstack_test/ppImage/src/ppImageDetrendPattern.c
===================================================================
--- branches/meh_branches/ppstack_test/ppImage/src/ppImageDetrendPattern.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppImage/src/ppImageDetrendPattern.c	(revision 33415)
@@ -18,5 +18,5 @@
     pmCell *cell = NULL;
 
-    assert(options->doPatternRow || options->doPatternCell); // do not call if not needed
+    assert(options->doPatternRow || options->doPatternCell || options->doPatternContinuity); // do not call if not needed
     assert(inputView->chip != -1);
     assert(inputView->cell == -1);
@@ -40,5 +40,5 @@
 	if (psMetadataLookupBool(NULL,hdu->header,"PTRN_ROW")) {
 	  psLogMsg("ppImage", PS_LOG_INFO, "Not performing row pattern correction as it has already been done.");
-	  goto pattern_cell;
+	  goto pattern_continuity;
 	}
 
@@ -98,8 +98,54 @@
     }
 
+ pattern_continuity:
+
+    // see the comment for PATTERN.ROW; the same rules apply for PATTERN.CELL
+
+    if (options->doPatternContinuity) {
+        int numCells = chip->cells->n;       // Number of cells
+        psVector *tweak = psVectorAlloc(numCells, PS_TYPE_U8); // Tweak cell?
+        pmFPAview *view = pmFPAviewAlloc(0); // View for local processing
+        *view = *inputView;
+
+	pmHDU *hdu = pmHDUFromChip(chip);
+	if (psMetadataLookupBool(NULL,hdu->header,"PTRN_CON")) {
+	  psLogMsg("ppImage", PS_LOG_INFO, "Not performing cell continuity correction as it has already been done.");
+	  goto pattern_cell;
+	}
+
+	for (int i = 0; i < chip->cells->n; i++) {
+            view->cell = i;
+
+            pmCell *cell = chip->cells->data[i]; // Cell of interest
+
+            if (cell->readouts->n > 1) {
+                psLogMsg("ppImage", PS_LOG_INFO, "Not performing cell continuity correction on video cell.");
+                continue;
+            }
+
+            bool doPattern = false;
+            if (!doPatternForView(&doPattern, config, chip, view, RECIPE_NAME, "PATTERN.CONTINUITY.SUBSET")) {
+                ESCAPE(false, "Unable to determine whether row pattern matching should be applied.");
+            }
+            if (doPattern) {
+                tweak->data.U8[i] = 0xFF;
+            }
+	}
+
+	// Tweak the cells
+	if (!pmPatternContinuity(chip, tweak, options->patternCellBG, options->patternCellMean,
+				 options->maskValue, options->darkMask,options->patternContinuityEdgeWidth)) {
+	    psFree(tweak);
+	    psFree(view);
+	    return false;
+	}
+	psFree(tweak);
+	psFree(view);
+
+	psMetadataAddBool(hdu->header, PS_LIST_TAIL, "PTRN_CON",PS_META_REPLACE,"PATTERN.CONTINUITY correction applied",true);
+    }
+
  pattern_cell:
-
-    // see the comment for PATTERN.ROW; the same rules apply for PATTERN.CELL
-
+    
     if (options->doPatternCell) {
         int numCells = chip->cells->n;       // Number of cells
@@ -131,5 +177,4 @@
                 tweak->data.U8[i] = 0xFF;
             }
-
 	}
 
Index: branches/meh_branches/ppstack_test/ppImage/src/ppImageLoop.c
===================================================================
--- branches/meh_branches/ppstack_test/ppImage/src/ppImageLoop.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppImage/src/ppImageLoop.c	(revision 33415)
@@ -170,5 +170,5 @@
 
         // Apply the pattern correction
-        if (options->doPatternRow || options->doPatternCell) {
+        if (options->doPatternRow || options->doPatternCell || options->doPatternContinuity) {
           if (!ppImageDetrendPatternApply(config,chip,view,options)) {
             ESCAPE("Unable to apply pattern corrections");
Index: branches/meh_branches/ppstack_test/ppImage/src/ppImageOptions.c
===================================================================
--- branches/meh_branches/ppstack_test/ppImage/src/ppImageOptions.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppImage/src/ppImageOptions.c	(revision 33415)
@@ -34,4 +34,5 @@
     options->doPatternRow    = false;   // Row pattern correction
     options->doPatternCell   = false;   // Cell pattern correction
+    options->doPatternContinuity = false; // Cell continuity correction
     options->doFringe        = false;   // Fringe subtraction
     options->doPhotom        = false;   // Source identification and photometry
@@ -253,4 +254,5 @@
     options->doPatternRow = psMetadataLookupBool(NULL, recipe, "PATTERN.ROW");
     options->doPatternCell = psMetadataLookupBool(NULL, recipe, "PATTERN.CELL");
+    options->doPatternContinuity = psMetadataLookupBool(NULL, recipe, "PATTERN.CONTINUITY");
 
     options->doMaskStats = psMetadataLookupBool(NULL, recipe, "MASK.STATS");
@@ -408,4 +410,11 @@
     }
 
+    if (psMetadataLookup(format, "PATTERN.CONTINUITY.WIDTH")) {
+      options->patternContinuityEdgeWidth = psMetadataLookupS32(NULL, format, "PATTERN.CONTINUITY.WIDTH");
+    }
+    else {
+      options->patternContinuityEdgeWidth = psMetadataLookupS32(NULL, recipe, "PATTERN.CONTINUITY.WIDTH");
+    }
+    
 
     // Remnance options
Index: branches/meh_branches/ppstack_test/ppMerge/src/ppMergeLoop.c
===================================================================
--- branches/meh_branches/ppstack_test/ppMerge/src/ppMergeLoop.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppMerge/src/ppMergeLoop.c	(revision 33415)
@@ -382,5 +382,5 @@
 
             // Wait for the threads to finish and manage results
-            if (!psThreadPoolWait(false)) {
+            if (!psThreadPoolWait(false, true)) {
                 psError(PS_ERR_UNKNOWN, false, "Unable to combine images.");
                 return false;
Index: branches/meh_branches/ppstack_test/ppSim/src/ppSimInsertGalaxies.c
===================================================================
--- branches/meh_branches/ppstack_test/ppSim/src/ppSimInsertGalaxies.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppSim/src/ppSimInsertGalaxies.c	(revision 33415)
@@ -137,5 +137,5 @@
         axes.minor       = galaxy->Rmin;
         axes.theta       = galaxy->theta;
-	pmPSF_AxesToModel (PAR, axes);
+	pmPSF_AxesToModel (PAR, axes, type);
 	psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
 
Index: branches/meh_branches/ppstack_test/ppSim/src/ppSimInsertStars.c
===================================================================
--- branches/meh_branches/ppstack_test/ppSim/src/ppSimInsertStars.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppSim/src/ppSimInsertStars.c	(revision 33415)
@@ -6,4 +6,5 @@
     PTR = NULL;
 
+float imageSum (psImage *image);
 
 bool ppSimInsertStars (pmReadout *readout, psImage *expCorr, psArray *stars, pmConfig *config) {
@@ -111,4 +112,5 @@
 	float radius = model->modelRadius (model->params, nSigmaLim * roughNoise);
 	radius = PS_MAX (radius, 1.0);
+	radius = 20.0;
 
 	// construct a source, with model flux pixels set, based on the model
@@ -117,5 +119,5 @@
 	// XXX set the mag & err values (should this be done in pmSourceFromModel?)
 	// XXX i should be applying the gain and the correct effective area
-	psEllipseAxes axes = pmPSF_ModelToAxes (model->params->data.F32, 20.0);
+	psEllipseAxes axes = pmPSF_ModelToAxes (model->params->data.F32, 20.0, model->type);
 	float Area = 2.0 * M_PI * axes.major * axes.minor;
 
@@ -127,5 +129,5 @@
 
 	float par8 = (model->params->n == 8) ? model->params->data.F32[7] : 0.0;
-	fprintf (outfile, "%8.3f %8.3f %10.2f  %2d  %7.3f %5.3f  %5.3f %5.3f %5.3f %5.3f\n", star->x, star->y, star->flux, 0, source->psfMag, source->psfMagErr, axes.major, axes.minor, axes.theta, par8);
+	float starFlux = 0.0;
 
 	// if psfConvolve is TRUE, we will (elsewhere) convolve the image we a PSF
@@ -135,5 +137,8 @@
 	} else {
 	    // insert the source flux in the image
+	    float sum1 = imageSum(source->pixels);
 	    pmSourceAddWithOffset (source, PM_MODEL_OP_FULL, 0xff, dX, dY);
+	    float sum2 = imageSum(source->pixels);
+	    starFlux = sum2 - sum1;
 	    
 	    // insert the source flux in the noise image
@@ -149,4 +154,5 @@
 	    RESET(source->blends);
 	}
+	fprintf (outfile, "%8.3f %8.3f %10.2f  %2d  %7.3f %5.3f  %5.3f %5.3f %5.3f %5.3f  :  %f %f\n", star->x, star->y, starFlux, 0, source->psfMag, source->psfMagErr, axes.major, axes.minor, axes.theta, par8, model->params->data.F32[PM_PAR_I0], radius);
 
 	// add the sources to the source array
Index: branches/meh_branches/ppstack_test/ppSim/src/ppSimMakeGalaxies.c
===================================================================
--- branches/meh_branches/ppstack_test/ppSim/src/ppSimMakeGalaxies.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppSim/src/ppSimMakeGalaxies.c	(revision 33415)
@@ -11,6 +11,6 @@
     if (!galaxyFake) return true;
 
-    float galaxyLum       = psMetadataLookupF32(&mdok, recipe, "GALAXY.LUM"); // Galaxy luminosity func slope
-    float galaxyMag       = psMetadataLookupF32(&mdok, recipe, "GALAXY.MAG"); // Galaxy brightest magnitude
+    float galaxyAlpha     = psMetadataLookupF32(&mdok, recipe, "GALAXY.LUM"); // Galaxy luminosity func slope
+    float brightMag       = psMetadataLookupF32(&mdok, recipe, "GALAXY.MAG"); // Galaxy brightest magnitude
     float galaxyDensity   = psMetadataLookupF32(&mdok, recipe, "GALAXY.DENSITY"); // Density of fakes
 
@@ -20,5 +20,9 @@
     bool galaxyGridRandom = psMetadataLookupBool(&mdok, recipe, "GALAXY.GRID.RANDOM"); // Density of fakes
     float galaxyGridPeak  = psMetadataLookupF32 (&mdok, recipe, "GALAXY.GRID.PEAK"); // peak flux of fakes
+    float galaxyGridMag   = psMetadataLookupF32 (&mdok, recipe, "GALAXY.GRID.MAG"); // peak flux of fakes
     
+    float galaxyGridXoff  = psMetadataLookupF32 (&mdok, recipe, "GALAXY.GRID.XOFF"); // centroid offset
+    float galaxyGridYoff  = psMetadataLookupF32 (&mdok, recipe, "GALAXY.GRID.YOFF"); // centroid offset
+
     float galaxyRmajorMax = psMetadataLookupF32(&mdok, recipe, "GALAXY.RMAJOR.MAX"); // Density of fakes
     float galaxyRmajorMin = psMetadataLookupF32(&mdok, recipe, "GALAXY.RMAJOR.MIN"); // Density of fakes
@@ -35,8 +39,8 @@
     float galaxyIndexMax  = psMetadataLookupF32(&mdok, recipe, "GALAXY.INDEX.MAX"); // Density of fakes
 
-    float darkRate 	  = psMetadataLookupF32(&mdok, recipe, "DARK.RATE"); // Dark rate
+    // float darkRate 	  = psMetadataLookupF32(&mdok, recipe, "DARK.RATE"); // Dark rate
     float expTime  	  = psMetadataLookupF32(&mdok, recipe, "EXPTIME"); // Exposure time
     float zp       	  = psMetadataLookupF32(&mdok, recipe, "ZEROPOINT"); // Photometric zero point
-    float seeing   	  = psMetadataLookupF32(&mdok, recipe, "SEEING"); // Seeing sigma (pix)
+    // float seeing   	  = psMetadataLookupF32(&mdok, recipe, "SEEING"); // Seeing sigma (pix)
     float scale    	  = psMetadataLookupF32(&mdok, recipe, "PIXEL.SCALE") * M_PI / 3600.0 / 180.0; // Plate scale (radians/pixel)
     float skyRate  	  = psMetadataLookupF32(&mdok, recipe, "SKY.RATE"); // Sky rate
@@ -44,4 +48,12 @@
 	float skyMags = psMetadataLookupF32(&mdok, recipe, "SKY.MAGS");  assert (mdok);
 	skyRate = scale * scale * ppSimMagToFlux (skyMags, zp);
+    }
+
+    // determine the galaxy model
+    char *modelName = psMetadataLookupStr(&mdok, recipe, "GALAXY.MODEL"); // galaxy model name
+    pmModelType type = pmModelClassGetType (modelName);
+    if (type == -1) {
+	psError (PS_ERR_UNKNOWN, false, "invalid model name");
+        return false;
     }
 
@@ -64,31 +76,37 @@
     // Grabbing read noise from the recipe rather than the cell, which is a potential danger, but it
     // shouldn't be too bad.
-    float readnoise = psMetadataLookupF32(&mdok, recipe, "READNOISE"); // Default read noise
-    if (!mdok) {
-	psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find READNOISE in recipe.");
-	psFree(bounds);
-	return false;
-    }
-
-    // Peak fluxes: faintest and brightest levels for random galaxy
-    float faint = 0.1 * 10.0 * sqrt(2.0*M_PI) * seeing * sqrtf(PS_SQR(readnoise) + (darkRate + skyRate) * expTime);
-    float bright = powf(10.0, -0.4 * (galaxyMag - zp)) / sqrt(2.0*M_PI) / seeing * expTime;
-    if (bright < faint) {
-	psLogMsg("ppSim", PS_LOG_INFO,
-		 "Image noise is above brightest random galaxy --- no random galaxy added.");
+    // float readnoise = psMetadataLookupF32(&mdok, recipe, "READNOISE"); // Default read noise
+    // if (!mdok) {
+    // 	psError(PS_ERR_BAD_PARAMETER_VALUE, false, "Unable to find READNOISE in recipe.");
+    // 	psFree(bounds);
+    // 	return false;
+    // }
+
+    // faintest and brightest magnitudes for random galaxies
+    // float brightFlux = ppSimMagToFlux (brightMag, zp) * expTime;
+    float faintMag = brightMag + 5.0;
+    if (brightMag > faintMag) {
+	psLogMsg("ppSim", PS_LOG_INFO, "Image noise is above brightest random galaxy --- no random galaxy added.");
 	return true;
     }
 
     // Normalisation, set by the specified stellar density at the specified bright magnitude
-    float norm = galaxyDensity * xSize * ySize * PS_SQR(scale * 180.0 / M_PI) /
-	powf(bright, galaxyLum);
+    float refSum = galaxyDensity * xSize * ySize * PS_SQR(scale * 180.0 / M_PI);
+    float normLum =  refSum / (galaxyAlpha * logf(10.0) * powf(10.0, (galaxyAlpha * brightMag)));
+    float normScale = normLum * galaxyAlpha * logf(10.0);
 
     // Total number of galaxy down to the faint flux end
-    long num = expf(logf(norm) + galaxyLum * logf(faint)) + 0.5;
+    long nTotal = 0;
+    if (galaxyAlpha < 0.1) {
+	nTotal = 100;
+    } else {
+	nTotal = normScale * powf (10.0, (galaxyAlpha * faintMag));
+    }
 
     if (galaxyGrid) {
-	num = (int)(xSize / galaxyGridDX) * (int)(ySize / galaxyGridDY);
+	fprintf (stderr, "galaxy grid inst mag: %f\n", galaxyGridMag - zp - 2.5*log10(expTime));
+	nTotal = (int)(xSize / galaxyGridDX) * (int)(ySize / galaxyGridDY);
     
-	psLogMsg("ppSim", PS_LOG_INFO, "Adding grid of %ld galaxies\n", num);
+	psLogMsg("ppSim", PS_LOG_INFO, "Adding grid of %ld galaxies\n", nTotal);
 
 	float galaxyIndexSlope = (galaxyIndexMax - galaxyIndexMin);
@@ -98,6 +116,6 @@
 
 	int i = 0;
-	float refNorm = 1.0;
-	float ourNorm = 1.0;
+	// float refNorm = 1.0;
+	// float ourNorm = 1.0;
 
 	for (long iy = 0.5*galaxyGridDY; iy < ySize; iy += galaxyGridDY) {
@@ -106,7 +124,9 @@
 
 		// make fpa center of distribution
-		galaxy->x    = ix;
-		galaxy->y    = iy;
-		galaxy->peak = galaxyGridPeak;
+		// center the galaxy peak on the center of a pixel
+		galaxy->x    = ix + 0.5 + galaxyGridXoff;
+		galaxy->y    = iy + 0.5 + galaxyGridYoff;
+
+		float galaxyGridFlux = expTime * powf(10.0, -0.4 * (galaxyGridMag - zp));
 
 		// galaxyIndex from user should be for function of this form: exp(-r^(1/n))
@@ -115,33 +135,45 @@
 		float rndValue;
 
-		rndValue = galaxyGridRandom ? drand48() : i / (float) num;
+		rndValue = galaxyGridRandom ? drand48() : i / (float) nTotal;
 		float index = (galaxyIndexMin  + rndValue * galaxyIndexSlope);
 		galaxy->index = 0.5/index; // factor of 0.5 because the Sersic model creates exp(-z^n), not exp(-r^n)
 
-		rndValue = galaxyGridRandom ? drand48() : i / (float) num;
+		rndValue = galaxyGridRandom ? drand48() : i / (float) nTotal;
 		float scale = (galaxyRmajorMin + rndValue * galaxyRmajorSlope);
 
-		// for a sersic model, 
-                float bn = 1.9992*index - 0.3271;
-                float fR = 1.0 / (sqrt(2.0) * pow (bn, index));
-                float Io = exp(bn);
-
 		galaxy->Rmaj  = scale;
 
-		rndValue = galaxyGridRandom ? drand48() : i / (float) num;
+		rndValue = galaxyGridRandom ? drand48() : i / (float) nTotal;
 		galaxy->Rmin  = (galaxyARatioMin + rndValue * galaxyARatioSlope) * galaxy->Rmaj;
 
-		rndValue = galaxyGridRandom ? drand48() : i / (float) num;
+		rndValue = galaxyGridRandom ? drand48() : i / (float) nTotal;
 		galaxy->theta = (galaxyThetaMin  + rndValue * galaxyThetaSlope);
 		
-		if (i == 0) {
-		    refNorm = Io*scale*scale;
-		} 
-		ourNorm = refNorm / (Io*scale*scale);
-		galaxy->peak *= ourNorm;
-
-		if (0) {
-		    fprintf (stderr, "Rmaj: %f, scale: %f, index: %f, bn: %f, Ro: %f, Io: %f, theta: %f\n", galaxy->Rmaj, scale, index, bn, fR, Io, galaxy->theta);
+		bool sersicType = false;
+		sersicType |= (type == pmModelClassGetType ("PS_MODEL_SERSIC"));
+		sersicType |= (type == pmModelClassGetType ("PS_MODEL_EXP"));
+		sersicType |= (type == pmModelClassGetType ("PS_MODEL_DEV"));
+		if (sersicType) {
+		    if (type == pmModelClassGetType ("PS_MODEL_DEV")) index = 4.0;
+		    if (type == pmModelClassGetType ("PS_MODEL_EXP")) index = 1.0;
+			
+		    // for a sersic model, 
+		    float bn = 1.9992*index - 0.3271;
+		    float Io = exp(bn);
+		    
+		    // the integral of a Sersic has an analytical form as follows:
+		    float logGamma = lgamma(2.0*index);
+		    float bnFactor = pow(bn, 2.0*index);
+		    float norm = 2.0 * M_PI * PS_SQR(galaxy->Rmaj) * index * Io * exp(logGamma) / bnFactor;
+		    
+		    // XXX probably should limit the allowed thinness of a galaxy
+		    galaxy->peak = galaxyGridFlux / norm / (galaxy->Rmin / galaxy->Rmaj);
+		} else {
+		    galaxy->peak = galaxyGridPeak;
 		}
+
+		// if (0) {
+		//     fprintf (stderr, "Rmaj: %f, scale: %f, index: %f, bn: %f, Ro: %f, Io: %f, theta: %f\n", galaxy->Rmaj, scale, index, bn, fR, Io, galaxy->theta);
+		// }
 
 		psArrayAdd (galaxies, 100, galaxy);
@@ -151,4 +183,5 @@
     } else {    
 
+	fprintf (stderr, "galaxy ref inst mag: %f\n", brightMag - zp - 2.5*log10(expTime));
 	float galaxyIndexSlope = (galaxyIndexMax - galaxyIndexMin);
 	float galaxyThetaSlope = (galaxyThetaMax - galaxyThetaMin);
@@ -156,8 +189,7 @@
 	float galaxyRmajorSlope = (galaxyRmajorMax - galaxyRmajorMin);
 
-	psLogMsg("ppSim", PS_LOG_INFO, "Adding %ld galaxies down to %f mag\n", num,
-		 -2.5 * log10(faint * sqrt(2.0*M_PI) * seeing / expTime) + zp);
-
-	for (long i = 0; i < num; i++) {
+	psLogMsg("ppSim", PS_LOG_INFO, "Adding %ld galaxies down to %f mag\n", nTotal, faintMag);
+
+	for (long i = 0; i < nTotal; i++) {
 	    ppSimGalaxy *galaxy = ppSimGalaxyAlloc ();
 
@@ -170,23 +202,34 @@
 
 	    float scale = (psRandomUniform(rng) * galaxyRmajorSlope + galaxyRmajorMin);
-
-	    // for a sersic model, 
-	    float bn = 1.9992*index - 0.3271;
-	    float fR = 1.0 / (sqrt(2.0) * pow (bn, index));
-	    float Io = exp(bn);
-
 	    galaxy->Rmaj  = scale;
-
 	    galaxy->Rmin  = (PS_SQR(psRandomUniform(rng)) * galaxyARatioSlope + galaxyARatioMin) * galaxy->Rmaj;
-
 	    galaxy->theta = (psRandomUniform(rng) * galaxyThetaSlope  + galaxyThetaMin);
 
-	    // the flux is only approximately correct (scaling of the peak is wrong)
-	    // elsewhere (ppSimInsertGalaxies.c) we calculate the true galaxy flux
-	    galaxy->flux = expf((logf(i + 1) - logf(norm)) / galaxyLum); // Peak flux
-	    galaxy->peak = galaxy->flux / (2.0*M_PI*PS_SQR(seeing));
-
-	    if (0) {
-	      fprintf (stderr, "Rmaj: %f, scale: %f, index: %f, bn: %f, Ro: %f, Io: %f\n", galaxy->Rmaj, scale, index, bn, fR, Io);
+	    bool sersicType = false;
+	    sersicType |= (type == pmModelClassGetType ("PS_MODEL_SERSIC"));
+	    sersicType |= (type == pmModelClassGetType ("PS_MODEL_EXP"));
+	    sersicType |= (type == pmModelClassGetType ("PS_MODEL_DEV"));
+	    if (sersicType) {
+		if (type == pmModelClassGetType ("PS_MODEL_DEV")) index = 4.0;
+		if (type == pmModelClassGetType ("PS_MODEL_EXP")) index = 1.0;
+		
+		// for a sersic model, 
+		float bn = 1.9992*index - 0.3271;
+		float Io = exp(bn);
+		
+		// the integral of a Sersic has an analytical form as follows:
+		float logGamma = lgamma(2.0*index);
+		float bnFactor = pow(bn, 2.0*index);
+		float norm = 2.0 * M_PI * PS_SQR(galaxy->Rmaj) * index * Io * exp(logGamma) / bnFactor;
+		    
+		// XXX probably should limit the allowed thinness of a galaxy
+		float galaxyMag =  PS_MIN (faintMag, PS_MAX (brightMag, (log10((i + 1.0) / normScale) / galaxyAlpha)));
+
+		galaxy->flux = ppSimMagToFlux (galaxyMag, zp) * expTime;
+		galaxy->peak = galaxy->flux / norm / (galaxy->Rmin / galaxy->Rmaj);
+	    }
+
+	    if (1) {
+		fprintf (stderr, "Rmaj: %f, Rmin: %f, theta: %f, scale: %f, index: %f, peak: %f, mag: %f\n", galaxy->Rmaj, galaxy->Rmin, galaxy->theta*180.0/M_PI, scale, index, galaxy->peak, -2.5*log10(galaxy->flux));
 	    }
 
Index: branches/meh_branches/ppstack_test/ppSkycell/src/ppSkycell.c
===================================================================
--- branches/meh_branches/ppstack_test/ppSkycell/src/ppSkycell.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppSkycell/src/ppSkycell.c	(revision 33415)
@@ -31,5 +31,5 @@
     if (!ppSkycellLoop(data)) {
         psErrorStackPrint(stderr, "Unable to process data.");
-        psFree(data);
+        // psFree(data); -- XXX this can cause an error when freeing a file that failed
         return PS_EXIT_DATA_ERROR;
     }
Index: branches/meh_branches/ppstack_test/ppStack/src/ppStackCombineFinal.c
===================================================================
--- branches/meh_branches/ppstack_test/ppStack/src/ppStackCombineFinal.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppStack/src/ppStackCombineFinal.c	(revision 33415)
@@ -71,5 +71,5 @@
     }
 
-    if (!psThreadPoolWait(true)) {
+    if (!psThreadPoolWait(true, true)) {
         psError(psErrorCodeLast(), false, "Unable to do final combination.");
         psFree(reject);
Index: branches/meh_branches/ppstack_test/ppStack/src/ppStackCombineInitial.c
===================================================================
--- branches/meh_branches/ppstack_test/ppStack/src/ppStackCombineInitial.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppStack/src/ppStackCombineInitial.c	(revision 33415)
@@ -2,5 +2,5 @@
 
 // This is the doomsday switch.
-// #define TESTING                         // Enable test output
+ #define TESTING                         // Enable test output
 
 bool ppStackCombineInitial(ppStackThreadData *stack, ppStackOptions *options, pmConfig *config)
@@ -17,4 +17,6 @@
 
     psTimerStart("PPSTACK_INITIAL");
+    //MEH
+    ppStackMemDump("initial:start");
 
     psMetadata *ppsub = psMetadataLookupMetadata(NULL, config->recipes, "PPSUB"); // PPSUB recipe
@@ -22,4 +24,6 @@
                                           "KERNEL.SIZE"); // Overlap by kernel size between consecutive scans
 
+    //MEH
+    ppStackMemDump("initial:configdone");
 
     bool status;                    // Status of read
@@ -27,4 +31,6 @@
     for (numChunk = 0; true; numChunk++) {
         ppStackThread *thread = ppStackThreadRead(&status, stack, config, numChunk, overlap);
+	//MEH -- full mem footprint before fault?
+	//ppStackMemDump("initial:threadread");
         if (!status) {
             // Something went wrong
@@ -36,4 +42,6 @@
             break;
         }
+	//MEH -- need to make so doesn't cause segfault - dump is bad - was full size though 150MB for sim20
+	//	ppStackMemDump("initial:threadchunkread");
 
         psThreadJob *job = psThreadJobAlloc("PPSTACK_INITIAL_COMBINE"); // Job to start
@@ -45,6 +53,8 @@
         }
     }
+    //MEH - also segfault
+    //    ppStackMemDump("initial:threadchunkdone");
 
-    if (!psThreadPoolWait(false)) {
+    if (!psThreadPoolWait(false, true)) {
         psError(psErrorCodeLast(), false, "Unable to do initial combination.");
         return false;
@@ -69,4 +79,7 @@
         psArray *inspect = results->data[0]; // Pixels to inspect
         psArray *reject = results->data[1];  // Pixels to reject
+	//MEH
+	//	ppStackMemDump("initial:threadchunkharvest");
+
         for (int i = 0; i < options->num; i++) {
             if (options->inputMask->data.PS_TYPE_VECTOR_MASK_DATA[i]) {
@@ -75,4 +88,6 @@
             options->inspect->data[i] = psArrayAdd(options->inspect->data[i], 1, inspect->data[i]);
             options->rejected->data[i] = psArrayAdd(options->rejected->data[i], 1, reject->data[i]);
+	    //MEH
+	    //	    ppStackMemDump("initial:threadchunkinspect");
         }
         psFree(job);
Index: branches/meh_branches/ppstack_test/ppStack/src/ppStackConvolve.c
===================================================================
--- branches/meh_branches/ppstack_test/ppStack/src/ppStackConvolve.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppStack/src/ppStackConvolve.c	(revision 33415)
@@ -376,7 +376,8 @@
         float median = numGood % 2 ? values->data.F32[numGood / 2] :
             0.5 * (values->data.F32[numGood / 2 - 1] + values->data.F32[numGood / 2]);
-	// EAM/MEH adding abs
+
+	// EAM/MEH ensure RMS is positive
         float rms = 0.74 * fabsf(values->data.F32[numGood * 3 / 4] -
-	                    values->data.F32[numGood / 4]); // Estimated RMS from interquartile range
+                            values->data.F32[numGood / 4]); // Estimated RMS from interquartile range
         psFree(values);
 
Index: branches/meh_branches/ppstack_test/ppStack/src/ppStackFiles.c
===================================================================
--- branches/meh_branches/ppstack_test/ppStack/src/ppStackFiles.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppStack/src/ppStackFiles.c	(revision 33415)
@@ -70,6 +70,6 @@
     fclose(memFile);
     psFree(leaks);
-
-    fprintf(stderr, "Memdump %s %d: Memory use: %ld, sbrk: %p\n", name, num, total, sbrk(0));
+    //MEH
+    fprintf(stderr, "Memdump %s %d: Memory use(MB): %f, sbrk: %p\n", name, num, total/1000000.0, sbrk(0));
     num++;
 }
Index: branches/meh_branches/ppstack_test/ppStack/src/ppStackLoop.c
===================================================================
--- branches/meh_branches/ppstack_test/ppStack/src/ppStackLoop.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppStack/src/ppStackLoop.c	(revision 33415)
@@ -54,4 +54,6 @@
         }
     }
+    //MEH
+    ppStackMemDump("inputsummary");
 
     // Define threading elements
@@ -61,4 +63,7 @@
         return false;
     }
+    //MEH
+    ppStackMemDump("threaddatasetup");
+
 
     // Prepare for combination
@@ -68,4 +73,7 @@
         return false;
     }
+    //MEH
+    ppStackMemDump("combineprep");
+
 
     // Initial combination
@@ -85,4 +93,6 @@
     }
     psFree(stack);
+    //MEH
+    ppStackMemDump("cellfree");
 
     // Pixel rejection
@@ -94,4 +104,6 @@
     }
     psLogMsg("ppStack", PS_LOG_INFO, "Stage 4: Pixel Rejection: %f sec", psTimerClear("PPSTACK_STEPS"));
+    //MEH
+    ppStackMemDump("reject");
 
     // Check inputs
@@ -106,4 +118,6 @@
         }
     }
+    //MEH
+    ppStackMemDump("summary");
 
     stack = ppStackThreadDataSetup(options, config, true);
@@ -112,4 +126,6 @@
         return false;
     }
+    //MEH
+    ppStackMemDump("threadsetup");
 
     // Final combination.  This one does NOT need to be normalized since the convolution takes care of that
@@ -121,4 +137,6 @@
         return false;
       }
+      //MEH
+      ppStackMemDump("convfinalstack");
     }
     else {
@@ -129,4 +147,6 @@
         return false;
       }
+      //MEH
+      ppStackMemDump("unconvfinalstack");
     }
       
Index: branches/meh_branches/ppstack_test/ppStack/src/ppStackMatch.c
===================================================================
--- branches/meh_branches/ppstack_test/ppStack/src/ppStackMatch.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppStack/src/ppStackMatch.c	(revision 33415)
@@ -6,5 +6,5 @@
 #define COVAR_FRAC 0.01                 // Truncation fraction for covariance matrix
 
-// #define TESTING                         // Enable debugging output
+//#define TESTING                         // Enable debugging output -- this needs cleaning up 
 
 #ifdef TESTING
@@ -259,4 +259,6 @@
             fake->image = psImageCopy(NULL, target, PS_TYPE_F32);
             fake->mask = psImageCopy(NULL, readout->mask, PS_TYPE_IMAGE_MASK);
+	    //MEH add fake->variance just as fake->image for testing passing along fully make difference in soln
+            //fake->variance = psImageCopy(NULL, target, PS_TYPE_F32);
 
 #if 1
@@ -267,5 +269,6 @@
 #endif
 
-#ifdef TESTING
+	    //#ifdef TESTING
+#if 1
             {
                 pmHDU *hdu = pmHDUFromCell(readout->parent);
@@ -313,4 +316,6 @@
 		// we need to register the FWHM values for use downstream 
 		pmSubtractionSetFWHMs(options->targetSeeing, options->inputSeeing->data.F32[index]);
+		//MEH -- seems to not be used now that kron R is in pmSubtractionMatch
+		psLogMsg("ppStack", PS_LOG_INFO, "TESTING:: FWHM %f %f\n",options->targetSeeing,options->inputSeeing->data.F32[index]);
 
                 // Scale the input parameters
@@ -334,5 +339,5 @@
                 // }
 
-                if (!pmSubtractionMatch(NULL, conv, fake, readout, footprint, stride, regionSize, spacing,
+		if (!pmSubtractionMatch(NULL, conv, fake, readout, footprint, stride, regionSize, spacing,
                                         threshold, options->sources, stampsName, type, size, order, widthsCopy,
                                         orders, inner, ringsOrder, binning, penalty,
@@ -354,5 +359,6 @@
 
 
-#ifdef TESTING
+	    //#ifdef TESTING
+#if 1
             {
                 pmHDU *hdu = pmHDUFromCell(readout->parent);
@@ -441,5 +447,5 @@
                 assert(item->type == PS_DATA_UNKNOWN);
                 pmSubtractionKernels *kernels = item->data.V; // Convolution kernels
-		// EAM/MEH sum the RMS instead of the mean now 
+		// EAM/MEH sum the RMS instead of the mean now  
 		sum += kernels->rms;
                 num++;
@@ -534,5 +540,6 @@
     psFree(bg);
 
-#ifdef TESTING
+    //#ifdef TESTING
+#if 1
     {
         pmHDU *hdu = pmHDUFromCell(readout->parent);
Index: branches/meh_branches/ppstack_test/ppStack/src/ppStackReject.c
===================================================================
--- branches/meh_branches/ppstack_test/ppStack/src/ppStackReject.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppStack/src/ppStackReject.c	(revision 33415)
@@ -33,11 +33,11 @@
 	
 	kernel->solution1 = psVectorAlloc(3, PS_TYPE_F64);
+	psVectorSet(kernel->solution1, 0, 1.0);
 	psVectorSet(kernel->solution1, 1, 1.0);
 	psVectorSet(kernel->solution1, 2, 1.0);
-	psVectorSet(kernel->solution1, 3, 1.0);
 	kernel->solution2 = psVectorAlloc(3, PS_TYPE_F64);
+	psVectorSet(kernel->solution2, 0, 1.0);
 	psVectorSet(kernel->solution2, 1, 1.0);
-	psVectorSet(kernel->solution1, 2, 1.0);
-	psVectorSet(kernel->solution1, 3, 1.0);
+	psVectorSet(kernel->solution2, 2, 1.0);
 	
 	options->kernels->data[i] = kernels;
@@ -80,5 +80,5 @@
         }
     }
-    if (!psThreadPoolWait(true)) {
+    if (!psThreadPoolWait(true, true)) {
         psError(psErrorCodeLast(), false, "Unable to concatenate inspection lists.");
         return false;
Index: branches/meh_branches/ppstack_test/ppStack/src/ppStackSources.c
===================================================================
--- branches/meh_branches/ppstack_test/ppStack/src/ppStackSources.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppStack/src/ppStackSources.c	(revision 33415)
@@ -1,5 +1,5 @@
 #include "ppStack.h"
 
-//#define TESTING                         // Enable debugging output
+#define TESTING                         // Enable debugging output
 
 //#define ASTROMETRY                    // Correct astrometry?
@@ -7,6 +7,8 @@
 #ifdef TESTING
 // Size of fake image; set by hand because it's trouble to get it from other places
-#define FAKE_COLS 4861
-#define FAKE_ROWS 4913
+//#define FAKE_COLS 4861
+//#define FAKE_ROWS 4913
+#define FAKE_COLS 1000
+#define FAKE_ROWS 1000
 #endif
 
@@ -57,6 +59,14 @@
     PS_ASSERT_PTR_NON_NULL(config, false);
 
+    //MEH - added for not matchZP option - need to fix logic
+    psLogMsg("ppStack", PS_LOG_WARN, "TESTING:options: %d : %d", options->matchZPs,options->photometry);
+    if (!options->matchZPs) {
+	options->norm = psVectorAlloc(options->num, PS_TYPE_F32);
+        psVectorInit(options->norm, 0.0);
+    }
+    // -----
+
     if (!options->matchZPs && !options->photometry) {
-        options->norm = psVectorAlloc(options->num, PS_TYPE_F32);
+	options->norm = psVectorAlloc(options->num, PS_TYPE_F32);
         psVectorInit(options->norm, 0.0);
         return true;
@@ -71,5 +81,6 @@
     PS_ASSERT_VECTOR_SIZE(inputMask, sourceLists->n, false);
 
-#if defined(TESTING) && 1
+//MEH 1->0 - dont want to add offsets until understand what it doing
+#if defined(TESTING) && 0
     {
         // Deliberately induce a major transparency difference
@@ -159,5 +170,6 @@
         pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPSTACK.INPUT", i); // File of interest
 
-#if defined(TESTING) && 0
+//MEH 0->1
+#if defined(TESTING) && 1
         pmReadout *fake = pmReadoutAlloc(NULL); // Fake readout
         pmPSF *psf = psMetadataLookupPtr(NULL, config->arguments, "PSF.TARGET"); // PSF for fake image
@@ -333,9 +345,13 @@
                 psArray *sources = sourceLists->data[i]; // Sources of interest
                 float magCorr = zp->data.F32[i] - trans->data.F32[i] - 2.5*log10(options->sumExposure) - airmassTerm * airmassTarget;
+		//MEH
+		psLogMsg("ppStack", PS_LOG_INFO,"TESTING:: %d: %f %f %f\n",i,magCorr,trans->data.F32[i],zp->data.F32[i]);
                 if (zpExpNum == numGoodImages) { // case (a)
                     // Using measured zero points, so attempt to set target zero point
 		    // XXX see NOTE above regarding case (b) : this is wrong.  the code should load a nominal zero point and supply it above
 		    // 
-                    magCorr -= zpTarget;
+		    magCorr -= zpTarget;
+		    //MEH
+		    psLogMsg("ppStack", PS_LOG_INFO,"TESTING:if: %d: %f %f %f\n",i,magCorr,trans->data.F32[i],zp->data.F32[i]);
                 }
                 options->norm->data.F32[i] = magCorr;
Index: branches/meh_branches/ppstack_test/ppStack/src/ppStackTarget.c
===================================================================
--- branches/meh_branches/ppstack_test/ppStack/src/ppStackTarget.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppStack/src/ppStackTarget.c	(revision 33415)
@@ -1,3 +1,6 @@
 #include "ppStack.h"
+
+//MEH
+#define TESTING                         // Enable test output
 
 #define SOURCE_MASK (PM_SOURCE_MODE_FAIL | PM_SOURCE_MODE_DEFECT | PM_SOURCE_MODE_SATURATED | \
@@ -229,4 +232,14 @@
     psFree(stampSources);
 
+    //MEH
+#ifdef TESTING
+    {
+      psString name = NULL;
+      psStringAppend(&name, "target.fits");
+      ppStackWriteImage(name, NULL, fake->image, config);
+      psFree(name);
+    }
+#endif
+
     psImage *target = psMemIncrRefCounter(fake->image);
     psFree(fake);
Index: branches/meh_branches/ppstack_test/ppSub/src/ppSubFlagNeighbors.c
===================================================================
--- branches/meh_branches/ppstack_test/ppSub/src/ppSubFlagNeighbors.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppSub/src/ppSubFlagNeighbors.c	(revision 33415)
@@ -18,8 +18,11 @@
 #include "ppSub.h"
 
+# define MIN_ERROR 0.02 /* padding for self-match flux test */
+# define MAX_OFFSET 5.0 /* delta mag greater than this is not a self match */
+
 // match the diff sources to the detections from the positive or negative input images
 // matchRef : false if this is the standard diff image, true if this is the inverse (in-ref vs ref-in)
 // this is needed to choose which value is positive and which is negative relative to the difference
-bool ppSubFlagNeighbors(pmConfig *config, pmFPAview *view, psArray *sources, bool matchRef) {
+bool ppSubFlagNeighbors(pmConfig *config, pmFPAview *view, psArray *sources, bool subInverse) {
 
     bool status;
@@ -106,10 +109,24 @@
 	      psAssert ((sourceM1->imageID == 1) || (sourceM1->imageID == 2), "error in pmPhotObj construction (second entry is not from 1 or 2");
 
+	      // which image gives the match, the positive or negative one?
+	      bool positive = false;
+	      if (subInverse) {
+		  positive = (sourceM1->imageID == 2);
+	      } else {
+		  positive = (sourceM1->imageID == 1);
+	      }
+
+	      // check if source and sourceM1 are likely to be the same flux (ie, isolated detection in one image)
+	      if (positive && isfinite(sourceM1->psfMag) && isfinite(source->psfMag)) {
+		  float dMag = sourceM1->psfMag - source->psfMag;
+		  float nSig = fabs(dMag) / hypot(MIN_ERROR, source->psfMagErr);
+		  if (nSig < MAX_OFFSET) {
+		      source->mode2 |= PM_SOURCE_MODE2_DIFF_SELF_MATCH;
+		  }
+	      }
+
 	      // only one match.  set a flag?
 	      source->mode2 |= PM_SOURCE_MODE2_DIFF_WITH_SINGLE;
 	      if (source->diffStats) {
-		  // which is match need an xor here...
-		  bool positive = !matchRef && (sourceM1->imageID == 1);
-		  positive |= matchRef && (sourceM1->imageID == 2);
 		  float SN1 = isfinite(sourceM1->psfMagErr) ? 1.0 / sourceM1->psfMagErr : NAN;
 		  if (positive) {
@@ -133,9 +150,26 @@
 	      psAssert (sourceM1->imageID != sourceM2->imageID, "error in pmPhotObj construction (case 3.3)");
 
+	      // we don't know which matched source (sourceM1 or sourceM2) is the positive detection,
+	      // and the answer depends on both the ID and the subtraction order (in-ref vs ref-in)
+	      // positive is true if sourceM1 is the positive detection
+	      bool positive = false;
+	      if (subInverse) {
+		  positive = (sourceM1->imageID == 2);
+	      } else {
+		  positive = (sourceM1->imageID == 1);
+	      }
+
+	      // check for self-detection
+	      float magPSF1 = positive ? sourceM1->psfMag : sourceM2->psfMag;
+	      if (isfinite(magPSF1) && isfinite(source->psfMag)) {
+		  float dMag = magPSF1 - source->psfMag;
+		  float nSig = fabs(dMag) / hypot(MIN_ERROR, source->psfMagErr);
+		  if (nSig < MAX_OFFSET) {
+		      source->mode2 |= PM_SOURCE_MODE2_DIFF_SELF_MATCH;
+		  }
+	      }
+
 	      source->mode2 |= PM_SOURCE_MODE2_DIFF_WITH_DOUBLE;
 	      if (source->diffStats) {
-		  // which is match need an xor here...
-		  bool positive = !matchRef && (sourceM1->imageID == 1);
-		  positive |= matchRef && (sourceM1->imageID == 2);
 		  float SN1 = isfinite(sourceM1->psfMagErr) ? 1.0 / sourceM1->psfMagErr : NAN;
 		  float SN2 = isfinite(sourceM2->psfMagErr) ? 1.0 / sourceM2->psfMagErr : NAN;
@@ -187,4 +221,6 @@
         pmPhotObj *obj = objects->data[j]; 
  
+	// fprintf (stderr, "%f, %f vs %f, %f\n", src->peak->xf, src->peak->yf, obj->x, obj->y);
+
         if (!src) NEXT1; 
         if (!src->peak) NEXT1; 
@@ -209,10 +245,10 @@
 	    src = sources->data[I]; 
 	    
-	    if (!src) NEXT1; 
-	    if (!src->peak) NEXT1; 
-	    if (!isfinite(src->peak->xf)) NEXT1; 
-	    if (!isfinite(src->peak->yf)) NEXT1; 
-	    if (!isfinite(src->peak->detValue)) NEXT1; 
-	    if (sqrt(src->peak->detValue) < MIN_SN) NEXT1; 
+	    if (!src) continue; 
+	    if (!src->peak) continue; 
+	    if (!isfinite(src->peak->xf)) continue; 
+	    if (!isfinite(src->peak->yf)) continue; 
+	    if (!isfinite(src->peak->detValue)) continue; 
+	    if (sqrt(src->peak->detValue) < MIN_SN) continue; 
 
 	    dx = src->peak->xf - obj->x; 
Index: branches/meh_branches/ppstack_test/ppSub/src/ppSubLoop.c
===================================================================
--- branches/meh_branches/ppstack_test/ppSub/src/ppSubLoop.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppSub/src/ppSubLoop.c	(revision 33415)
@@ -23,9 +23,9 @@
 bool dumpout(pmConfig *config, char *name) 
 {
-  pmFPAview *view = ppSubViewReadout(); // View to readout
-  pmReadout *out = pmFPAfileThisReadout(config->files, view, "PPSUB.OUTPUT");
-  psphotSaveImage (NULL, out->image, name);
-  psFree(view);
-  return true;
+    pmFPAview *view = ppSubViewReadout(); // View to readout
+    pmReadout *out = pmFPAfileThisReadout(config->files, view, "PPSUB.OUTPUT");
+    psphotSaveImage (NULL, out->image, name);
+    psFree(view);
+    return true;
 }
 
@@ -33,13 +33,17 @@
 bool ppSubLoop(ppSubData *data)
 {
+    bool mdok = false;
+    bool success = true;
+
     psAssert(data, "Require processing data");
     pmConfig *config = data->config;    // Configuration
     psAssert(config, "Require configuration.");
 
-    bool success = true;
+    psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSUB_RECIPE); // Recipe for ppSim
+    psAssert(recipe, "We checked this earlier, so it should be here.");
+    bool noConvolve = psMetadataLookupBool(&mdok, recipe, "NOCONVOLVE"); // Do not use convolved images.
 
     pmConfigCamerasCull(config, NULL);
     pmConfigRecipesCull(config, "PPSUB,PPSTATS,PSPHOT,PSASTRO,MASKS,JPEG");
-
 
     pmFPAfile *input = psMetadataLookupPtr(NULL, config->files, "PPSUB.INPUT");
@@ -75,28 +79,27 @@
     if (data->forcedPhot2) {
         // Change the recipe to use a higher nsigma limit and quit after pass1
-        psMetadata *recipe = psMetadataLookupPtr (NULL, config->recipes, PSPHOT_RECIPE);
-
-        psF32 nsigma_peak_save = psMetadataLookupF32 (NULL, recipe, "PEAKS_NSIGMA_LIMIT");
-        char *breakPt_save =  psMetadataLookupStr (NULL, recipe, "BREAK_POINT");
-
-        bool mdok;
-        psF32 pos2_nsigma_peak = psMetadataLookupF32 (&mdok, recipe, "PEAKS_POS2_NSIGMA_LIMIT");
+        psMetadata *psphotRecipe = psMetadataLookupPtr (NULL, config->recipes, PSPHOT_RECIPE);
+
+        psF32 nsigma_peak_save = psMetadataLookupF32 (NULL, psphotRecipe, "PEAKS_NSIGMA_LIMIT");
+        char *breakPt_save =  psMetadataLookupStr (NULL, psphotRecipe, "BREAK_POINT");
+
+        psF32 pos2_nsigma_peak = psMetadataLookupF32 (&mdok, psphotRecipe, "PEAKS_POS2_NSIGMA_LIMIT");
         if (!mdok) {
-            psWarning("PEAKS_POS2_NSIGMA_LIMIT not found in recipe. Will use 25.\n");
+            psWarning("PEAKS_POS2_NSIGMA_LIMIT not found in psphotRecipe. Will use 25.\n");
             pos2_nsigma_peak = 25.;
         }
-        psMetadataAddF32(recipe, PS_LIST_TAIL, "PEAKS_NSIGMA_LIMIT", PS_META_REPLACE, "", pos2_nsigma_peak);
-        psMetadataAddStr(recipe, PS_LIST_TAIL, "BREAK_POINT", PS_META_REPLACE, "", "PASS1");
+        psMetadataAddF32(psphotRecipe, PS_LIST_TAIL, "PEAKS_NSIGMA_LIMIT", PS_META_REPLACE, "", pos2_nsigma_peak);
+        psMetadataAddStr(psphotRecipe, PS_LIST_TAIL, "BREAK_POINT", PS_META_REPLACE, "", "PASS1");
 
 	bool foundDetections = false;
 	if (!ppSubInputDetections(&foundDetections, "PPSUB.POS2.SOURCES", "PPSUB.REF", data)) {
 	    psError(psErrorCodeLast(), false, "Unable to measure positive detections (2)");
-            psMetadataAddF32(recipe, PS_LIST_TAIL, "PEAKS_NSIGMA_LIMIT", PS_META_REPLACE, "", nsigma_peak_save);
-            psMetadataAddStr(recipe, PS_LIST_TAIL, "BREAK_POINT", PS_META_REPLACE, "", breakPt_save);
-	    success = false;
-            goto ESCAPE;
-	}
-        psMetadataAddF32(recipe, PS_LIST_TAIL, "PEAKS_NSIGMA_LIMIT", PS_META_REPLACE, "", nsigma_peak_save);
-        psMetadataAddStr(recipe, PS_LIST_TAIL, "BREAK_POINT", PS_META_REPLACE, "", breakPt_save);
+            psMetadataAddF32(psphotRecipe, PS_LIST_TAIL, "PEAKS_NSIGMA_LIMIT", PS_META_REPLACE, "", nsigma_peak_save);
+            psMetadataAddStr(psphotRecipe, PS_LIST_TAIL, "BREAK_POINT", PS_META_REPLACE, "", breakPt_save);
+	    success = false;
+            goto ESCAPE;
+	}
+        psMetadataAddF32(psphotRecipe, PS_LIST_TAIL, "PEAKS_NSIGMA_LIMIT", PS_META_REPLACE, "", nsigma_peak_save);
+        psMetadataAddStr(psphotRecipe, PS_LIST_TAIL, "BREAK_POINT", PS_META_REPLACE, "", breakPt_save);
 	// if nothing was found, don't bother doing the forced photometry below
 	if (!foundDetections) {
@@ -107,16 +110,9 @@
 
     // XXX if it exists, use the POS1, POS2 successs for the FWHMs
-    psMetadata *recipe = psMetadataLookupPtr(NULL, config->recipes, PPSUB_RECIPE);
-    bool noConvolve = psMetadataLookupBool(NULL, recipe, "NOCONVOLVE"); // Do not use convolved images.
-    if (noConvolve) {
-      psWarning("not matching PSFs because NOCONVOLVE is TRUE\n");
-    } else {
-      if (!ppSubMatchPSFs(data)) {
-        psError(psErrorCodeLast(), false, "Unable to match PSFs.");
-        success = false;
-        goto ESCAPE;
-      }
-    }
-
+    if (!ppSubMatchPSFs(data)) {
+	psError(psErrorCodeLast(), false, "Unable to match PSFs.");
+	success = false;
+	goto ESCAPE;
+    }
 
     if (data->quality) {
@@ -138,11 +134,12 @@
                      psTimerClear("PPSUB_MATCH"));
 
-    // Close input files
+    // Close input files (freeing up space) : for the noConvolve case, we cannot close the
+    // inputs since we will use them for subtraction below.  wait until later to do the work
     if (!noConvolve) {
-    if (!ppSubFilesIterateUp(config, PPSUB_FILES_INPUT)) {
-        psError(PPSUB_ERR_IO, false, "Unable to close input files.");
-        success = false;
-        goto ESCAPE;
-    }
+	if (!ppSubFilesIterateUp(config, PPSUB_FILES_INPUT)) {
+	    psError(PPSUB_ERR_IO, false, "Unable to close input files.");
+	    success = false;
+	    goto ESCAPE;
+	}
     }
     if (!ppSubLowThreshold(data)) {
@@ -165,5 +162,4 @@
     }
 
-    if (!noConvolve) {
     if (!data->quality && !ppSubMakePSF(data)) {
         psError(psErrorCodeLast(), false, "Unable to generate PSF.");
@@ -171,5 +167,5 @@
         goto ESCAPE;
     }
-    }
+
     // Now we've got a PSF, blow away detections in case they're confused with real output detections
     {
@@ -197,9 +193,9 @@
     // dumpout(config, "diff.2a.fits");
     if (noConvolve) {
-      if (!ppSubFilesIterateUp(config, PPSUB_FILES_INPUT)) {
-	psError(PPSUB_ERR_IO, false, "Unable to close input files.");
-	success = false;
-	goto ESCAPE;
-      }
+	if (!ppSubFilesIterateUp(config, PPSUB_FILES_INPUT)) {
+	    psError(PPSUB_ERR_IO, false, "Unable to close input files.");
+	    success = false;
+	    goto ESCAPE;
+	}
     }
     
@@ -271,12 +267,12 @@
     // Do Mask Stats
     {
-      pmFPAview *view = ppSubViewReadout(); // View to readout
-      if (!ppSubMaskStats(config, view,data->stats)) {
-	psError(psErrorCodeLast(), false, "Unable to generate mask statistics");
-	success = false;
-        psFree(view);
-        goto ESCAPE;
-      }
-      psFree(view);
+	pmFPAview *view = ppSubViewReadout(); // View to readout
+	if (!ppSubMaskStats(config, view,data->stats)) {
+	    psError(psErrorCodeLast(), false, "Unable to generate mask statistics");
+	    success = false;
+	    psFree(view);
+	    goto ESCAPE;
+	}
+	psFree(view);
     }
     // dumpout(config, "diff.4.fits");
@@ -344,5 +340,5 @@
     }
 
-ESCAPE:
+ ESCAPE:
     pmFPAfileDropInternal(config->files, "PSPHOT.BACKGND");
     pmFPAfileDropInternal(config->files, "PSPHOT.BACKMDL");
Index: branches/meh_branches/ppstack_test/ppSub/src/ppSubMakePSF.c
===================================================================
--- branches/meh_branches/ppstack_test/ppSub/src/ppSubMakePSF.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppSub/src/ppSubMakePSF.c	(revision 33415)
@@ -46,23 +46,29 @@
     bool noConvolve = psMetadataLookupBool(&mdok, recipe, "NOCONVOLVE"); // Do not use convolved images.
 
-    if (!noConvolve) {
-      printf("Using Convolved images because NOCONVOLVE is FALSE\n");
-      if (reverse) {
-        minuend = pmFPAfileThisReadout(config->files, view, "PPSUB.REF.CONV");
-	minuendFile = psMetadataLookupPtr(&mdok, config->files, "PPSUB.REF.CONV");
-      } else {
-        minuend = pmFPAfileThisReadout(config->files, view, "PPSUB.INPUT.CONV");
-	minuendFile = psMetadataLookupPtr(&mdok, config->files, "PPSUB.INPUT.CONV");
-      }
-    }
-    else {
-      psWarning("Not using Convolved images because NOCONVOLVE  is TRUE\n");
-      if (reverse) {
-        minuend = pmFPAfileThisReadout(config->files, view, "PPSUB.REF");
-	minuendFile = psMetadataLookupPtr(&mdok, config->files, "PPSUB.REF");
-      } else {
-        minuend = pmFPAfileThisReadout(config->files, view, "PPSUB.INPUT");
-	minuendFile = psMetadataLookupPtr(&mdok, config->files, "PPSUB.INPUT");
-      }
+    if (noConvolve) {
+	// if we do not convolve, we need to copy the detections to the image for analysis
+	pmReadout *psfSourcesRO = NULL;	// readout containing loaded sources for psf model
+	psWarning("Not using Convolved images because NOCONVOLVE  is TRUE\n");
+	if (reverse) {
+	    minuend = pmFPAfileThisReadout(config->files, view, "PPSUB.REF");
+	    minuendFile = psMetadataLookupPtr(&mdok, config->files, "PPSUB.REF");
+	    psfSourcesRO = pmFPAfileThisReadout(config->files, view, "PPSUB.REF.SOURCES");
+	} else {
+	    minuend = pmFPAfileThisReadout(config->files, view, "PPSUB.INPUT");
+	    minuendFile = psMetadataLookupPtr(&mdok, config->files, "PPSUB.INPUT");
+	    psfSourcesRO = pmFPAfileThisReadout(config->files, view, "PPSUB.INPUT.SOURCES");
+	}
+	psAssert (psfSourcesRO, "missing readout with sources");
+	pmDetections *psfDetections = psMetadataLookupPtr(&mdok, psfSourcesRO->analysis,  "PSPHOT.DETECTIONS");
+	psMetadataAddPtr(minuend->analysis,  PS_LIST_TAIL, "PSPHOT.DETECTIONS", PS_META_REPLACE | PS_DATA_UNKNOWN, "Merged source list", psfDetections);
+    } else {
+	printf("Using Convolved images because NOCONVOLVE is FALSE\n");
+	if (reverse) {
+	    minuend = pmFPAfileThisReadout(config->files, view, "PPSUB.REF.CONV");
+	    minuendFile = psMetadataLookupPtr(&mdok, config->files, "PPSUB.REF.CONV");
+	} else {
+	    minuend = pmFPAfileThisReadout(config->files, view, "PPSUB.INPUT.CONV");
+	    minuendFile = psMetadataLookupPtr(&mdok, config->files, "PPSUB.INPUT.CONV");
+	}
     }
 
Index: branches/meh_branches/ppstack_test/ppSub/src/ppSubMatchPSFs.c
===================================================================
--- branches/meh_branches/ppstack_test/ppSub/src/ppSubMatchPSFs.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppSub/src/ppSubMatchPSFs.c	(revision 33415)
@@ -201,4 +201,6 @@
 bool ppSubMatchPSFs(ppSubData *data)
 {
+    bool mdok = false;
+
     psAssert(data, "Require processing data");
     pmConfig *config = data->config;    // Configuration
@@ -208,4 +210,10 @@
     psMetadata *recipe = psMetadataLookupMetadata(NULL, config->recipes, PPSUB_RECIPE); // Recipe for ppSim
     psAssert(recipe, "We checked this earlier, so it should be here.");
+
+    bool noConvolve = psMetadataLookupBool(&mdok, recipe, "NOCONVOLVE"); // Do not use convolved images.
+    if (noConvolve) {
+	psWarning("not matching PSFs because NOCONVOLVE is TRUE\n");
+	return true;
+    }
 
     pmFPAview *view = ppSubViewReadout(); // View to readout
@@ -228,6 +236,4 @@
 	psFree(refConv);
     }
-
-    bool mdok;                          // Status of MD lookup
 
     // Load pre-calculated kernel, if available
Index: branches/meh_branches/ppstack_test/ppSub/src/ppSubReadoutInverse.c
===================================================================
--- branches/meh_branches/ppstack_test/ppSub/src/ppSubReadoutInverse.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppSub/src/ppSubReadoutInverse.c	(revision 33415)
@@ -11,5 +11,8 @@
 {
     psAssert(config, "Require configuration");
-
+    // Check configuration for convolve option
+    psMetadata *recipe = psMetadataLookupPtr(NULL, config->recipes, PPSUB_RECIPE);
+    bool noConvolve = psMetadataLookupBool(NULL, recipe, "NOCONVOLVE"); // Do not use convolved images.
+    
     pmFPAview *view = ppSubViewReadout(); // View to readout
     pmReadout *outRO = pmFPAfileThisReadout(config->files, view, "PPSUB.OUTPUT");
@@ -25,5 +28,11 @@
 
     // Get concepts from reference
-    pmFPAfile *refFile = psMetadataLookupPtr(NULL, config->files, "PPSUB.REF.CONV"); // File with concepts
+    pmFPAfile *refFile;
+    if (noConvolve) {
+      refFile = psMetadataLookupPtr(NULL, config->files, "PPSUB.REF"); // File with concepts
+    }
+    else {
+      refFile = psMetadataLookupPtr(NULL, config->files, "PPSUB.REF.CONV"); // File with concepts
+    }
     pmFPA *invFPA = invRO->parent->parent->parent; // Inverse FPA
     pmConceptsCopyFPA(invFPA, refFile->fpa, true, true);
Index: branches/meh_branches/ppstack_test/ppSub/src/ppSubReadoutPhotometry.c
===================================================================
--- branches/meh_branches/ppstack_test/ppSub/src/ppSubReadoutPhotometry.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppSub/src/ppSubReadoutPhotometry.c	(revision 33415)
@@ -92,6 +92,6 @@
     // detections in the wings (or cores) of bright(er) stars found in both images.
     // flag detections based on their distance from the bright(er) input sources.
-    bool matchRef = !strcasecmp(name, "PPSUB.INVERSE");
-    ppSubFlagNeighbors (config, view, sources, matchRef);
+    bool subInverse = !strcasecmp(name, "PPSUB.INVERSE");
+    ppSubFlagNeighbors (config, view, sources, subInverse);
 
     if (data->stats) {
Index: branches/meh_branches/ppstack_test/ppTranslate/src/ppMops.c
===================================================================
--- branches/meh_branches/ppstack_test/ppTranslate/src/ppMops.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppTranslate/src/ppMops.c	(revision 33415)
@@ -3,4 +3,24 @@
 
 #include "ppMops.h"
+
+void test()
+{
+    psMetadata *md = psMetadataAlloc();
+
+    psVector *vec = psVectorAlloc(42, PS_TYPE_S32);
+
+    psMetadataAddVector(md, PS_LIST_TAIL, "TEST", 0, NULL, vec);
+
+    psFree(vec);
+    psFree(md);
+
+    psLibFinalize();
+
+    fprintf (stderr, "found %d leaks at %s\n", 
+    	psMemCheckLeaks2 (0,
+		NULL, stdout, false, 500), "ppMops");
+
+    exit(0);
+}
 
 /*
@@ -39,4 +59,6 @@
     psLibInit(NULL);
 
+    // test();
+
     ppMopsArguments *args = ppMopsArgumentsParse(argc, argv); // Parsed arguments
     if (!args) {
@@ -45,4 +67,5 @@
     }
 
+
     psArray *detections = ppMopsRead(args); // Detections from each input
     if (!detections) {
@@ -51,20 +74,25 @@
     }
 
-    ppMopsDetections *merged = ppMopsMerge(detections); // Merged detections
-    psFree(detections);
-    if (!merged) {
+    if (!ppMopsPurgeDuplicates(detections)) {
         psErrorStackPrint(stderr, "Unable to merge detections");
         exit(PS_EXIT_SYS_ERROR);
     }
 
-    if (!ppMopsWrite(merged, args)) {
+    if (!ppMopsWrite(detections, args)) {
         psErrorStackPrint(stderr, "Unable to write detections");
         exit(PS_EXIT_SYS_ERROR);
     }
 
-    psFree(merged);
+    for (int i = 0; i < detections->n; i++) {
+        psFree(detections->data[i]);
+    }
+    psFree(detections);
     psFree(args);
 
     psLibFinalize();
+
+    fprintf (stderr, "found %d leaks at %s\n", 
+    	psMemCheckLeaks2 (0,
+		NULL, stdout, false, 500), "ppMops");
 
     return PS_EXIT_SUCCESS;
Index: branches/meh_branches/ppstack_test/ppTranslate/src/ppMops.h
===================================================================
--- branches/meh_branches/ppstack_test/ppTranslate/src/ppMops.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppTranslate/src/ppMops.h	(revision 33415)
@@ -62,4 +62,5 @@
 
 typedef struct {
+  psString component;                 // skycell_id for these detections
   psString raBoresight, decBoresight; // RA,Dec of telescope boresight
   psString filter;                    // Filter for exposure
@@ -70,53 +71,16 @@
   double mjd;                         // Modified Julian Date
   float seeing;                       // Seeing of exposure
+  int   naxis1, naxis2;               // size of the image
   long num;                           // Number of detections
+  long numGood;                       // Number of "good" detections
+  psS64 diffSkyfileId;                // unique id for input skyfile
+  psMetadata *table;                  // Columns from the input file
   psVector *x, *y;                    // Image coordinates
   psVector *ra, *dec;                 // Sky coordinates
   psVector *raErr, *decErr;           // Error in sky coordinates
-  psVector *mag, *magErr;             // Magnitude and associated error
-  psVector *chi2, *dof;               // Chi^2 from fitting, with associated degrees of freedom
-  psVector *cr, *extended;            // Measures of CR-ness and extendedness
-  psVector *psfMajor, *psfMinor, *psfTheta; // PSF major and minor axes, and position angle
-  psVector *quality, *numPix;               // PSF quality factor and number of pixels
-  psVector *xxMoment, *xyMoment, *yyMoment; // Moments
-  psVector *flags;                    // psphot flags
-  psVector *diffSkyfileId;            // Identifier for source image
-  psVector *naxis1, *naxis2;          // Size of image
   psVector *mask;                     // Mask for detections
-  psVector *nPos;                     // Number of positive pixels
-  psVector *fPos;                     // Fraction of positive flux
-  psVector *nRatioBad;                // Fraction of positive pixels to negative
-  psVector *nRatioMask;               // Fraction of positive pixels to masked
-  psVector *nRatioAll;                // Fraction of positive pixels to all
-  psVector *psfInstFlux;              // PSF fit instrumental magnitude
-  psVector *psfInstFluxSig;           // Sigma of PSF instrumental magnitude
-  psVector *apMag;                    // Magnitude in standard aperture
-  psVector *apMagRadius;              // Radius used for aperture mags
-  psVector *apMagRaw;                 // Magnitude in real aperture
-  psVector *apFlux;                   // Instrumental flux in standard aperture
-  psVector *apFluxSig;                // Aperture flux error
-  psVector *peakFluxAsMag;            // Peak flux expressed as magnitude
-  psVector *calPsfMag;                // PSF Magnitude using supplied calibration
-  psVector *calPsfMagSig;             // Measured scatter of zero point calibration
-  psVector *sky;                      // Sky level
-  psVector *skySig;                   // Sigma of sky level
-  psVector *qualityPerfect;           // PSF coverage/quality factor (poor)
-  psVector *momentsR1;                // First radial moment
-  psVector *momentsRH;                // Half radial moment
-  psVector *kronFlux;                 // Kron Flux (in 2.5 R1)
-  psVector *kronFluxErr;              // Kron Flux Error
-  psVector *kronFluxInner;            // Kron Flux (in 1.0 R1)
-  psVector *kronFluxOuter;            // Kron Flux (in 4.0 R1)
-  psVector *diffRP;                   // Distance to positive match source
-  psVector *diffSnP;                  // Signal-to-noise of pos match src
-  psVector *diffRM;                   // Distance to negative match source
-  psVector *diffSnM;                  // Signal-to-noise of neg match src
-  psVector *flags2;                   // psphot flags (group 2)
-  psVector *ippIdet;                  // IPP detection identifier index
-  psVector *nFrames;                  // Number of frames overlapping source center
-  psVector *padding;                  // Padding
 } ppMopsDetections;
 
-ppMopsDetections *ppMopsDetectionsAlloc(long num);
+ppMopsDetections *ppMopsDetectionsAlloc();
 
 /// Copy a detection
@@ -130,8 +94,9 @@
 
 /// Merge detections
-ppMopsDetections *ppMopsMerge(const psArray *detections);
+// ppMopsDetections *ppMopsMerge(const psArray *detections);
+bool ppMopsPurgeDuplicates(const psArray *detections);
 
 /// Write detections
-bool ppMopsWrite(const ppMopsDetections *detections, const ppMopsArguments *args);
+bool ppMopsWrite(const psArray *detections, const ppMopsArguments *args);
 
 /// Get the version contained in EXTTYPE of the "SkyChip.psf" table:
Index: branches/meh_branches/ppstack_test/ppTranslate/src/ppMopsArguments.c
===================================================================
--- branches/meh_branches/ppstack_test/ppTranslate/src/ppMopsArguments.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppTranslate/src/ppMopsArguments.c	(revision 33415)
@@ -108,5 +108,5 @@
     args->output = psStringCopy(argv[2]);
 
-    args->exp_name = psMetadataLookupStr(NULL, arguments, "-exp_name");
+    args->exp_name = psMemIncrRefCounter(psMetadataLookupStr(NULL, arguments, "-exp_name"));
     args->exp_id = psMetadataLookupS64(NULL, arguments, "-exp_id");
     args->chip_id = psMetadataLookupS64(NULL, arguments, "-chip_id");
@@ -123,4 +123,5 @@
     psTrace("ppMops.args", 1, "Done parsing command-line arguments\n");
 
+    psFree(arguments);
     return args;
 }
Index: branches/meh_branches/ppstack_test/ppTranslate/src/ppMopsDetections.c
===================================================================
--- branches/meh_branches/ppstack_test/ppTranslate/src/ppMopsDetections.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppTranslate/src/ppMopsDetections.c	(revision 33415)
@@ -8,7 +8,9 @@
 static void mopsDetectionsFree(ppMopsDetections *det)
 {
+    psFree(det->component);
     psFree(det->raBoresight);
     psFree(det->decBoresight);
     psFree(det->filter);
+    psFree(det->table);
     psFree(det->x);
     psFree(det->y);
@@ -17,62 +19,13 @@
     psFree(det->raErr);
     psFree(det->decErr);
-    psFree(det->mag);
-    psFree(det->magErr);
-    psFree(det->chi2);
-    psFree(det->dof);
-    psFree(det->cr);
-    psFree(det->extended);
-    psFree(det->psfMajor);
-    psFree(det->psfMinor);
-    psFree(det->psfTheta);
-    psFree(det->quality);
-    psFree(det->numPix);
-    psFree(det->xxMoment);
-    psFree(det->xyMoment);
-    psFree(det->yyMoment);
-    psFree(det->flags);
-    psFree(det->diffSkyfileId);
-    psFree(det->naxis1);
-    psFree(det->naxis2);
     psFree(det->mask);
-    psFree(det->nPos);
-    psFree(det->fPos);
-    psFree(det->nRatioBad);
-    psFree(det->nRatioMask);
-    psFree(det->nRatioAll);
-    psFree(det->psfInstFlux);
-    psFree(det->psfInstFluxSig);
-    psFree(det->apMag);
-    psFree(det->apMagRadius);
-    psFree(det->apMagRaw);
-    psFree(det->apFlux);
-    psFree(det->apFluxSig);
-    psFree(det->peakFluxAsMag);
-    psFree(det->calPsfMag);
-    psFree(det->calPsfMagSig);
-    psFree(det->sky);
-    psFree(det->skySig);
-    psFree(det->qualityPerfect);
-    psFree(det->momentsR1);
-    psFree(det->momentsRH);
-    psFree(det->kronFlux);
-    psFree(det->kronFluxErr);
-    psFree(det->kronFluxInner);
-    psFree(det->kronFluxOuter);
-    psFree(det->diffRP);
-    psFree(det->diffSnP);
-    psFree(det->diffRM);
-    psFree(det->diffSnM);
-    psFree(det->flags2);
-    psFree(det->ippIdet);
-    psFree(det->nFrames);
-    psFree(det->padding);
     return;
 }
 
-ppMopsDetections *ppMopsDetectionsAlloc(long num)
+ppMopsDetections *ppMopsDetectionsAlloc()
 {
     ppMopsDetections *det = psAlloc(sizeof(ppMopsDetections)); // Detections, to return
     psMemSetDeallocator(det, (psFreeFunc)mopsDetectionsFree);
+    det->component = NULL;
     det->raBoresight = NULL;
     det->decBoresight = NULL;
@@ -86,396 +39,13 @@
     det->seeing = NAN;
     det->num = 0;
-    det->x = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->y = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->ra = psVectorAllocEmpty(num, PS_TYPE_F64);
-    det->dec = psVectorAllocEmpty(num, PS_TYPE_F64);
-    det->raErr = psVectorAllocEmpty(num, PS_TYPE_F64);
-    det->decErr = psVectorAllocEmpty(num, PS_TYPE_F64);
-    det->mag = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->magErr = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->chi2 = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->dof = psVectorAllocEmpty(num, PS_TYPE_S32);
-    det->cr = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->extended = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->psfMajor = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->psfMinor = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->psfTheta = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->quality = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->numPix = psVectorAllocEmpty(num, PS_TYPE_S32);
-    det->xxMoment = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->xyMoment = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->yyMoment = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->flags = psVectorAllocEmpty(num, PS_TYPE_U32);
-    det->diffSkyfileId = psVectorAllocEmpty(num, PS_TYPE_S64);
-    det->naxis1 = psVectorAllocEmpty(num, PS_TYPE_S32);
-    det->naxis2 = psVectorAllocEmpty(num, PS_TYPE_S32);
-    det->mask = psVectorAllocEmpty(num, PS_TYPE_U8);
-    det->nPos = psVectorAllocEmpty(num, PS_TYPE_S32);
-    det->fPos = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->nRatioBad = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->nRatioMask = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->nRatioAll = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->psfInstFlux = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->psfInstFluxSig = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->apMag = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->apMagRadius  = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->apMagRaw = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->apFlux = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->apFluxSig = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->peakFluxAsMag = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->calPsfMag = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->calPsfMagSig = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->sky = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->skySig = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->qualityPerfect = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->momentsR1 = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->momentsRH = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->kronFlux = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->kronFluxErr = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->kronFluxInner = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->kronFluxOuter = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->diffRP = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->diffSnP = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->diffRM = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->diffSnM = psVectorAllocEmpty(num, PS_TYPE_F32);
-    det->flags2 = psVectorAllocEmpty(num, PS_TYPE_U32);
-    det->ippIdet = psVectorAllocEmpty(num, PS_TYPE_U32);
-    det->nFrames = psVectorAllocEmpty(num, PS_TYPE_U16);
-    det->padding = psVectorAllocEmpty(num, PS_TYPE_S16);
+    det->table = NULL;
+    det->x = NULL;
+    det->y = NULL;
+    det->ra = NULL;
+    det->dec = NULL;
+    det->raErr = NULL;
+    det->decErr = NULL;
+    det->mask = NULL;
+    det->diffSkyfileId = 0;
     return det;
 }
-
-ppMopsDetections *ppMopsDetectionsRealloc(ppMopsDetections *det, long num)
-{
-    det->x = psVectorRealloc(det->x, num);
-    det->y = psVectorRealloc(det->y, num);
-    det->ra = psVectorRealloc(det->ra, num);
-    det->dec = psVectorRealloc(det->dec, num);
-    det->raErr = psVectorRealloc(det->raErr, num);
-    det->decErr = psVectorRealloc(det->decErr, num);
-    det->mag = psVectorRealloc(det->mag, num);
-    det->magErr = psVectorRealloc(det->magErr, num);
-    det->chi2 = psVectorRealloc(det->chi2, num);
-    det->dof = psVectorRealloc(det->dof, num);
-    det->cr = psVectorRealloc(det->cr, num);
-    det->extended = psVectorRealloc(det->extended, num);
-    det->psfMajor = psVectorRealloc(det->psfMajor, num);
-    det->psfMinor = psVectorRealloc(det->psfMinor, num);
-    det->psfTheta = psVectorRealloc(det->psfTheta, num);
-    det->quality = psVectorRealloc(det->quality, num);
-    det->numPix = psVectorRealloc(det->numPix, num);
-    det->xxMoment = psVectorRealloc(det->xxMoment, num);
-    det->xyMoment = psVectorRealloc(det->xyMoment, num);
-    det->yyMoment = psVectorRealloc(det->yyMoment, num);
-    det->flags = psVectorRealloc(det->flags, num);
-    det->diffSkyfileId = psVectorRealloc(det->diffSkyfileId, num);
-    det->naxis1 = psVectorRealloc(det->naxis1, num);
-    det->naxis2 = psVectorRealloc(det->naxis2, num);
-    det->mask = psVectorRealloc(det->mask, num);
-    det->nPos = psVectorRealloc(det->nPos, num);
-    det->fPos = psVectorRealloc(det->fPos, num);
-    det->nRatioBad = psVectorRealloc(det->nRatioBad, num);
-    det->nRatioMask = psVectorRealloc(det->nRatioMask, num);
-    det->nRatioAll = psVectorRealloc(det->nRatioAll, num);
-    det->psfInstFlux = psVectorRealloc(det->psfInstFlux, num);
-    det->psfInstFluxSig = psVectorRealloc(det->psfInstFluxSig, num);
-    det->apMag = psVectorRealloc(det->apMag, num);
-    det->apMagRadius = psVectorRealloc(det->apMagRadius, num);
-    det->apMagRaw = psVectorRealloc(det->apMagRadius, num);
-    det->apFlux = psVectorRealloc(det->apFlux, num);
-    det->apFluxSig = psVectorRealloc(det->apFluxSig, num);
-    det->peakFluxAsMag = psVectorRealloc(det->peakFluxAsMag, num);
-    det->calPsfMag = psVectorRealloc(det->calPsfMag, num);
-    det->calPsfMagSig = psVectorRealloc(det->calPsfMagSig, num);
-    det->sky = psVectorRealloc(det->sky, num);
-    det->skySig = psVectorRealloc(det->skySig, num);
-    det->qualityPerfect = psVectorRealloc(det->qualityPerfect, num);
-    det->momentsR1 = psVectorRealloc(det->momentsR1, num);
-    det->momentsRH = psVectorRealloc(det->momentsRH, num);
-    det->kronFlux = psVectorRealloc(det->kronFlux, num);
-    det->kronFluxErr = psVectorRealloc(det->kronFluxErr, num);
-    det->kronFluxInner = psVectorRealloc(det->kronFluxInner, num);
-    det->kronFluxOuter = psVectorRealloc(det->kronFluxOuter, num);
-    det->diffRP = psVectorRealloc(det->diffRP, num);
-    det->diffSnP = psVectorRealloc(det->diffSnP, num);
-    det->diffRM = psVectorRealloc(det->diffRM, num);
-    det->diffSnM = psVectorRealloc(det->diffSnM, num);
-    det->flags2 = psVectorRealloc(det->flags2, num);
-    det->ippIdet = psVectorRealloc(det->ippIdet, num);
-    det->nFrames = psVectorRealloc(det->nFrames, num);
-    det->padding = psVectorRealloc(det->padding, num);
-    return det;
-}
-
-bool ppMopsDetectionsAdd(ppMopsDetections *det, float x, float y, double ra, double dec,
-                         double raErr, double decErr, float mag, float magErr,
-                         float chi2, int dof, float cr, float extended, float psfMajor,
-                         float psfMinor, float psfTheta, float quality, int numPix,
-                         float xxMoment, float xyMoment, float yyMoment,
-                         psU32 flags, psS64 diffSkyfileId, int naxis1, int naxis2,
-                         int nPos, float fPos, float nRatioBad, float nRatioMask, float nRatioAll,
-			 float psfInstFlux, float psfInstFluxSig,
-			 float apMag, float apMagRadius, float apMagRaw, float apFlux, float apFluxSig,
-			 float peakFluxAsMag, float calPsfMag, float calPsfMagSig,
-			 float sky, float skySig, float qualityPerfect,
-			 float momentsR1, float momentsRH,
-			 float kronFlux, float kronFluxErr, float kronFluxInner, float kronFluxOuter,
-			 float diffRP, float diffSnP, float diffRM, float diffSnM,
-			 psU32 flags2, psU32 ippIdet, psU16 nFrames, psS16 padding)
-{
-    psVectorAppend(det->x, x);
-    psVectorAppend(det->y, y);
-    psVectorAppend(det->ra, ra);
-    psVectorAppend(det->dec, dec);
-    psVectorAppend(det->raErr, raErr);
-    psVectorAppend(det->decErr, decErr);
-    psVectorAppend(det->mag, mag);
-    psVectorAppend(det->magErr, magErr);
-    psVectorAppend(det->chi2, chi2);
-    psVectorAppend(det->dof, dof);
-    psVectorAppend(det->cr, cr);
-    psVectorAppend(det->extended, extended);
-    psVectorAppend(det->psfMajor, psfMajor);
-    psVectorAppend(det->psfMinor, psfMinor);
-    psVectorAppend(det->psfTheta, psfTheta);
-    psVectorAppend(det->quality, quality);
-    psVectorAppend(det->numPix, numPix);
-    psVectorAppend(det->xxMoment, xxMoment);
-    psVectorAppend(det->xyMoment, xyMoment);
-    psVectorAppend(det->yyMoment, yyMoment);
-    psVectorAppend(det->flags, flags);
-    psVectorAppend(det->diffSkyfileId, diffSkyfileId);
-    psVectorAppend(det->naxis1, naxis1);
-    psVectorAppend(det->naxis2, naxis2);
-    psVectorAppend(det->mask, 0);
-    psVectorAppend(det->nPos, nPos);
-    psVectorAppend(det->fPos, fPos);
-    psVectorAppend(det->nRatioBad, nRatioBad);
-    psVectorAppend(det->nRatioMask, nRatioMask);
-    psVectorAppend(det->nRatioAll, nRatioAll);
-    psVectorAppend(det->psfInstFlux, psfInstFlux);
-    psVectorAppend(det->psfInstFluxSig, psfInstFluxSig);
-    psVectorAppend(det->apMag, apMag);
-    psVectorAppend(det->apMagRadius, apMagRadius);
-    psVectorAppend(det->apMagRaw, apMagRaw);
-    psVectorAppend(det->apFlux, apFlux);
-    psVectorAppend(det->apFluxSig, apFluxSig);
-    psVectorAppend(det->peakFluxAsMag, peakFluxAsMag);
-    psVectorAppend(det->calPsfMag, calPsfMag);
-    psVectorAppend(det->calPsfMagSig, calPsfMagSig);
-    psVectorAppend(det->sky, sky);
-    psVectorAppend(det->skySig, skySig);
-    psVectorAppend(det->qualityPerfect, qualityPerfect);
-    psVectorAppend(det->momentsR1, momentsR1);
-    psVectorAppend(det->momentsRH, momentsRH);
-    psVectorAppend(det->kronFlux, kronFlux);
-    psVectorAppend(det->kronFluxErr, kronFluxErr);
-    psVectorAppend(det->kronFluxInner, kronFluxInner);
-    psVectorAppend(det->kronFluxOuter, kronFluxOuter);
-    psVectorAppend(det->diffRP, diffRP);
-    psVectorAppend(det->diffSnP, diffSnP);
-    psVectorAppend(det->diffRM, diffRM);
-    psVectorAppend(det->diffSnM, diffSnM);
-    psVectorAppend(det->flags2, flags2);
-    psVectorAppend(det->ippIdet, ippIdet);
-    psVectorAppend(det->nFrames, nFrames);
-    psVectorAppend(det->padding, padding);
-    return true;
-}
-
-bool ppMopsDetectionsCopySingle(ppMopsDetections *target, const ppMopsDetections *source, long index)
-{
-    psVectorAppend(target->x, source->x->data.F32[index]);
-    psVectorAppend(target->y, source->y->data.F32[index]);
-    psVectorAppend(target->ra, source->ra->data.F64[index]);
-    psVectorAppend(target->dec, source->dec->data.F64[index]);
-    psVectorAppend(target->raErr, source->raErr->data.F64[index]);
-    psVectorAppend(target->decErr, source->decErr->data.F64[index]);
-    psVectorAppend(target->mag, source->mag->data.F32[index]);
-    psVectorAppend(target->magErr, source->magErr->data.F32[index]);
-    psVectorAppend(target->chi2, source->chi2->data.F32[index]);
-    psVectorAppend(target->dof, source->dof->data.S32[index]);
-    psVectorAppend(target->cr, source->cr->data.F32[index]);
-    psVectorAppend(target->extended, source->extended->data.F32[index]);
-    psVectorAppend(target->psfMajor, source->psfMajor->data.F32[index]);
-    psVectorAppend(target->psfMinor, source->psfMinor->data.F32[index]);
-    psVectorAppend(target->psfTheta, source->psfTheta->data.F32[index]);
-    psVectorAppend(target->quality, source->quality->data.F32[index]);
-    psVectorAppend(target->numPix, source->numPix->data.S32[index]);
-    psVectorAppend(target->xxMoment, source->xxMoment->data.F32[index]);
-    psVectorAppend(target->xyMoment, source->xyMoment->data.F32[index]);
-    psVectorAppend(target->yyMoment, source->yyMoment->data.F32[index]);
-    psVectorAppend(target->flags, source->flags->data.U32[index]);
-    psVectorAppend(target->diffSkyfileId, source->diffSkyfileId->data.S64[index]);
-    psVectorAppend(target->naxis1, source->naxis1->data.S32[index]);
-    psVectorAppend(target->naxis2, source->naxis2->data.S32[index]);
-    psVectorAppend(target->mask, 0);
-    psVectorAppend(target->nPos, source->nPos->data.S32[index]);
-    psVectorAppend(target->fPos, source->fPos->data.F32[index]);
-    psVectorAppend(target->nRatioBad, source->nRatioBad->data.F32[index]);
-    psVectorAppend(target->nRatioMask, source->nRatioMask->data.F32[index]);
-    psVectorAppend(target->nRatioAll, source->nRatioAll->data.F32[index]);
-    psVectorAppend(target->psfInstFlux, source->psfInstFlux->data.F32[index]);
-    psVectorAppend(target->psfInstFluxSig, source->psfInstFluxSig->data.F32[index]);
-    psVectorAppend(target->apMag, source->apMag->data.F32[index]);
-    psVectorAppend(target->apMagRadius, source->apMagRadius->data.F32[index]);
-    psVectorAppend(target->apMagRaw, source->apMagRaw->data.F32[index]);
-    psVectorAppend(target->apFlux, source->apFlux->data.F32[index]);
-    psVectorAppend(target->apFluxSig, source->apFluxSig->data.F32[index]);
-    psVectorAppend(target->peakFluxAsMag, source->peakFluxAsMag->data.F32[index]);
-    psVectorAppend(target->calPsfMag, source->calPsfMag->data.F32[index]);
-    psVectorAppend(target->calPsfMagSig, source->calPsfMagSig->data.F32[index]);
-    psVectorAppend(target->sky, source->sky->data.F32[index]);
-    psVectorAppend(target->skySig, source->skySig->data.F32[index]);
-    psVectorAppend(target->qualityPerfect, source->qualityPerfect->data.F32[index]);
-    psVectorAppend(target->momentsR1, source->momentsR1->data.F32[index]);
-    psVectorAppend(target->momentsRH, source->momentsRH->data.F32[index]);
-    psVectorAppend(target->kronFlux, source->kronFlux->data.F32[index]);
-    psVectorAppend(target->kronFluxErr, source->kronFluxErr->data.F32[index]);
-    psVectorAppend(target->kronFluxInner, source->kronFluxInner->data.F32[index]);
-    psVectorAppend(target->kronFluxOuter, source->kronFluxOuter->data.F32[index]);
-    psVectorAppend(target->diffRP, source->diffRP->data.F32[index]);
-    psVectorAppend(target->diffSnP, source->diffSnP->data.F32[index]);
-    psVectorAppend(target->diffRM, source->diffRM->data.F32[index]);
-    psVectorAppend(target->diffSnM, source->diffSnM->data.F32[index]);
-    psVectorAppend(target->flags2, source->flags2->data.U32[index]);
-    psVectorAppend(target->ippIdet, source->ippIdet->data.U32[index]);
-    psVectorAppend(target->nFrames, source->nFrames->data.U16[index]);
-    psVectorAppend(target->padding, source->padding->data.S16[index]);
-
-    target->num++;
-
-    return true;
-}
-
-
-bool ppMopsDetectionsPurge(ppMopsDetections *det)
-{
-    long num = 0;
-    for (long i = 0; i < det->num; i++) {
-        if (!det->mask->data.U8[i]) {
-            if (i == num) {
-                // No need to copy
-                num++;
-                continue;
-            }
-            det->x->data.F32[num] = det->x->data.F32[i];
-            det->y->data.F32[num] = det->y->data.F32[i];
-            det->ra->data.F64[num] = det->ra->data.F64[i];
-            det->dec->data.F64[num] = det->dec->data.F64[i];
-            det->raErr->data.F64[num] = det->raErr->data.F64[i];
-            det->decErr->data.F64[num] = det->decErr->data.F64[i];
-            det->mag->data.F32[num] = det->mag->data.F32[i];
-            det->magErr->data.F32[num] = det->magErr->data.F32[i];
-            det->chi2->data.F32[num] = det->chi2->data.F32[i];
-            det->dof->data.S32[num] = det->dof->data.S32[i];
-            det->cr->data.F32[num] = det->cr->data.F32[i];
-            det->extended->data.F32[num] = det->extended->data.F32[i];
-            det->psfMajor->data.F32[num] = det->psfMajor->data.F32[i];
-            det->psfMinor->data.F32[num] = det->psfMinor->data.F32[i];
-            det->psfTheta->data.F32[num] = det->psfTheta->data.F32[i];
-            det->quality->data.F32[num] = det->quality->data.F32[i];
-            det->numPix->data.S32[num] = det->numPix->data.S32[i];
-            det->xxMoment->data.F32[num] = det->xxMoment->data.F32[i];
-            det->xyMoment->data.F32[num] = det->xyMoment->data.F32[i];
-            det->yyMoment->data.F32[num] = det->yyMoment->data.F32[i];
-            det->flags->data.U32[num] = det->flags->data.U32[i];
-            det->diffSkyfileId->data.S64[num] = det->diffSkyfileId->data.S64[i];
-            det->naxis1->data.S32[num] = det->naxis1->data.S32[i];
-            det->naxis2->data.S32[num] = det->naxis2->data.S32[i];
-            det->mask->data.U8[num] = 0;
-            det->nPos->data.S32[num] = det->nPos->data.S32[i];
-            det->fPos->data.F32[num] = det->fPos->data.F32[i];
-            det->nRatioBad->data.F32[num] = det->nRatioBad->data.F32[i];
-            det->nRatioMask->data.F32[num] = det->nRatioMask->data.F32[i];
-            det->nRatioAll->data.F32[num] = det->nRatioAll->data.F32[i];
-            det->psfInstFlux->data.F32[num] = det->psfInstFlux->data.F32[i];
-            det->psfInstFluxSig->data.F32[num] = det->psfInstFluxSig->data.F32[i];
-            det->apMag->data.F32[num] = det->apMag->data.F32[i];
-            det->apMagRadius->data.F32[num] = det->apMagRadius->data.F32[i];
-            det->apMagRaw->data.F32[num] = det->apMagRaw->data.F32[i];
-            det->apFlux->data.F32[num] = det->apFlux->data.F32[i];
-            det->apFluxSig->data.F32[num] = det->apFluxSig->data.F32[i];
-            det->peakFluxAsMag->data.F32[num] = det->peakFluxAsMag->data.F32[i];
-            det->calPsfMag->data.F32[num] = det->calPsfMag->data.F32[i];
-            det->calPsfMagSig->data.F32[num] = det->calPsfMagSig->data.F32[i];
-            det->sky->data.F32[num] = det->sky->data.F32[i];
-            det->skySig->data.F32[num] = det->skySig->data.F32[i];
-            det->qualityPerfect->data.F32[num] = det->qualityPerfect->data.F32[i];
-            det->momentsR1->data.F32[num] = det->momentsR1->data.F32[i];
-            det->momentsRH->data.F32[num] = det->momentsRH->data.F32[i];
-	    det->kronFlux->data.F32[num] = det->kronFlux->data.F32[i];
-	    det->kronFluxErr->data.F32[num] = det->kronFluxErr->data.F32[i];
-	    det->kronFluxInner->data.F32[num] = det->kronFluxInner->data.F32[i];
-	    det->kronFluxOuter->data.F32[num] = det->kronFluxOuter->data.F32[i];
-	    det->diffRP->data.F32[num] = det->diffRP->data.F32[i];
-	    det->diffSnP->data.F32[num] = det->diffSnP->data.F32[i];
-	    det->diffRM->data.F32[num] = det->diffRM->data.F32[i];
-	    det->diffSnM->data.F32[num] = det->diffSnM->data.F32[i];
-	    det->flags2->data.U32[num] = det->flags2->data.U32[i];
-	    det->ippIdet->data.U32[num] = det->ippIdet->data.U32[i];
-	    det->nFrames->data.U16[num] = det->nFrames->data.U16[i];
-	    det->padding->data.S16[num] = det->padding->data.S16[i];
-            num++;
-        }
-    }
-    det->x->n = num;
-    det->y->n = num;
-    det->ra->n = num;
-    det->dec->n = num;
-    det->raErr->n = num;
-    det->decErr->n = num;
-    det->mag->n = num;
-    det->magErr->n = num;
-    det->chi2->n = num;
-    det->dof->n = num;
-    det->cr->n = num;
-    det->extended->n = num;
-    det->psfMajor->n = num;
-    det->psfMinor->n = num;
-    det->psfTheta->n = num;
-    det->quality->n = num;
-    det->numPix->n = num;
-    det->xxMoment->n = num;
-    det->xyMoment->n = num;
-    det->yyMoment->n = num;
-    det->flags->n = num;
-    det->diffSkyfileId->n = num;
-    det->naxis1->n = num;
-    det->naxis2->n = num;
-    det->mask->n = num;
-    det->num = num;
-    det->nPos->n = num;
-    det->fPos->n = num;
-    det->nRatioBad->n = num;
-    det->nRatioMask->n = num;
-    det->nRatioAll->n = num;
-    det->psfInstFlux->n = num;
-    det->psfInstFluxSig->n = num;
-    det->apMag->n = num;
-    det->apMagRadius->n = num;
-    det->apMagRaw->n = num;
-    det->apFlux->n = num;
-    det->apFluxSig->n = num;
-    det->peakFluxAsMag->n = num;
-    det->calPsfMag->n = num;
-    det->calPsfMagSig->n = num;
-    det->sky->n = num;
-    det->skySig->n = num;
-    det->qualityPerfect->n = num;
-    det->momentsR1->n = num;
-    det->momentsRH->n = num;
-    det->kronFlux->n = num;
-    det->kronFluxErr->n = num;
-    det->kronFluxInner->n = num;
-    det->kronFluxOuter->n = num;
-    det->diffRP->n = num;
-    det->diffSnP->n = num;
-    det->diffRM->n = num;
-    det->diffSnM->n = num;
-    det->flags2->n = num;
-    det->ippIdet->n = num;
-    det->nFrames->n = num;
-    det->padding->n = num;
-    return true;
-}
Index: branches/meh_branches/ppstack_test/ppTranslate/src/ppMopsGetSkyChipPsfVersion.c
===================================================================
--- branches/meh_branches/ppstack_test/ppTranslate/src/ppMopsGetSkyChipPsfVersion.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppTranslate/src/ppMopsGetSkyChipPsfVersion.c	(revision 33415)
@@ -9,9 +9,12 @@
 	  "EXTTYPE value: [%s]\n", version);
   if (strcmp(version, "PS1_DV1") == 0) {
+    psFree(headerSkyChip);
     return 1;
   } else if (strcmp(version, "PS1_DV2") == 0) {
+    psFree(headerSkyChip);
     return 2;
   }
   psWarning("Unsupported EXTTYPE in SkyChip.psf table: [%s]", version);
+  psFree(headerSkyChip);
   return 0;
 }
Index: branches/meh_branches/ppstack_test/ppTranslate/src/ppMopsMerge.c
===================================================================
--- branches/meh_branches/ppstack_test/ppTranslate/src/ppMopsMerge.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppTranslate/src/ppMopsMerge.c	(revision 33415)
@@ -17,4 +17,12 @@
 #define AIRMASS_TOL 1.0e-3              // Tolerance for airmass matching
 
+#if 0
+#undef psTrace
+#define psTrace(facil, level, ...)              \
+    if (level <= 5) {                           \
+        fprintf(stderr, __VA_ARGS__);           \
+    }
+#endif
+
 // Get distance from detection to centre of image
 static float mergeDistance(const ppMopsDetections *detections, // Detections of interest
@@ -22,94 +30,148 @@
     )
 {
-  float dx = (float) (detections->x->data.F32[index] - detections->naxis1->data.S32[index] / 2.0);
-  float dy = (float) (detections->y->data.F32[index] - detections->naxis2->data.S32[index] / 2.0);
-  return PS_SQR(dx) + PS_SQR(dy);
+    float dx = detections->x->data.F32[index] - detections->naxis1 / 2.0;
+    float dy = detections->y->data.F32[index] - detections->naxis2 / 2.0;
+    return PS_SQR(dx) + PS_SQR(dy);
 }
 
 
-ppMopsDetections *ppMopsMerge(const psArray *detections)
+bool ppMopsPurgeDuplicates(const psArray *detections)
 {
     PS_ASSERT_ARRAY_NON_NULL(detections, NULL);
 
-    psTrace("ppMops.merge", 1, "Merging detections from %ld inputs\n", detections->n);
-
-    ppMopsDetections *merged = NULL;    // Merged list
-    int num = 0;                                                         // Number of merged files
+    int numInputs = detections->n;                // Number of inputs
+    psTrace("ppMops.merge", 1, "Checking detections from %d inputs\n", numInputs);
+
+    long total = 0;                                // Total number of sources
+    psArray *duplicates = psArrayAlloc(numInputs); // Vector of duplicate bits for each input
+    psVector *dupNum = psVectorAlloc(numInputs, PS_TYPE_U32); // Number of duplicates for each input
+    psVectorInit(dupNum, 0);
+    for (int i = 0; i < numInputs; i++) {
+        ppMopsDetections *det = detections->data[i]; // Detections from
+        if (!det || det->num == 0) {
+            continue;
+        }
+        psVector *dupes = duplicates->data[i] = psVectorAlloc(det->num, PS_TYPE_U8);
+        psVectorInit(dupes, 0);
+        total += det->num;
+    }
+    psTrace("ppMops.merge", 2, "Total detections: %ld\n", total);
+
+    psVector *raMerged = psVectorAlloc(total, PS_TYPE_F64); // Merged RAs
+    psVector *decMerged = psVectorAlloc(total, PS_TYPE_F64); // Merged Decs
+    psVector *sourceMerged = psVectorAlloc(total, PS_TYPE_U16); // Source image of merged sources
+    psVector *indexMerged = psVectorAlloc(total, PS_TYPE_U32); // Index of merged sources
+    long num = 0;                                                   // Number of merged sources
+
+    const char *raBoresight = NULL, *decBoresight = NULL; // Boresight coordinates
+    const char *filter = NULL;                    // Filter name
+    float airmass = NAN, exptime = NAN;           // Exposure details
+    double posangle = NAN, alt = NAN, az = NAN; // Telescope pointing
+    double mjd = NAN;                           // Time of exposure
+
+    for (int i = 0; i < numInputs; i++) {
+        ppMopsDetections *det = detections->data[i]; // Detections of interest
+        if (!det) {
+            psTrace("ppMops.merge", 3, "Ignoring NULL input %d\n", i);
+            continue;
+        } else if (det->num == 0) {
+            psTrace("ppMops.merge", 3, "Ignoring empty input %d\n", i);
+            continue;
+        }
+
+        // Check exposure characteristics
+        if (num == 0) {
+            raBoresight = det->raBoresight;
+            decBoresight = det->decBoresight;
+            filter = det->filter;
+            airmass = det->airmass;
+            exptime = det->exptime;
+            posangle = det->posangle;
+            alt = det->alt;
+            az = det->az;
+        } else {
+            if (strcmp(raBoresight, det->raBoresight) != 0) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Exposure RA values differ: %s vs %s",
+                        raBoresight, det->raBoresight);
+                return false;
+            }
+            if (strcmp(decBoresight, det->decBoresight) != 0) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Exposure Dec values differ: %s vs %s",
+                        decBoresight, det->decBoresight);
+                return false;
+            }
+            if (strcmp(filter, det->filter) != 0) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Exposure filter values differ: %s vs %s",
+                        filter, det->filter);
+                return false;
+            }
+            if (fabsf(airmass - det->airmass) > AIRMASS_TOL) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Exposure airmass values differ: %f vs %f",
+                        airmass, det->airmass);
+                return false;
+            }
+            if (fabsf(exptime - det->exptime) > EXPTIME_TOL) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Exposure exposure time values differ: %f vs %f",
+                        exptime, det->exptime);
+                return false;
+            }
+            if (fabs(posangle - det->posangle) > POSANGLE_TOL) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Exposure position angle values differ: %f vs %f",
+                        posangle, det->posangle);
+                return false;
+            }
+            if (fabs(alt - det->alt) > BORESIGHT_TOL) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Exposure altitude values differ: %lf vs %lf",
+                        alt, det->alt);
+                return false;
+            }
+            if (fabs(az - det->az) > BORESIGHT_TOL) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Exposure azimuth values differ: %lf vs %lf",
+                        az, det->az);
+                return false;
+            }
+            if (fabs(mjd - det->mjd) > MJD_TOL) {
+                psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Exposure MJD values differ: %lf vs %lf",
+                        mjd, det->mjd);
+                return false;
+            }
+        }
+
+        psTrace("ppMops.merge", 3, "Accepting %ld detections from input %d\n", det->num, i);
+        memcpy(&raMerged->data.F64[num], det->ra->data.F64, det->num * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
+        memcpy(&decMerged->data.F64[num], det->dec->data.F64, det->num * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
+        for (long j = 0, k = num; j < det->num; j++, k++) {
+            sourceMerged->data.U16[k] = i;
+            indexMerged->data.U32[k] = j;
+        }
+        num += det->num;
+    }
+
+    psTrace("ppMops.merge", 3, "Generating kd-tree from %ld sources\n", num);
+    psTree *tree = psTreePlant(2, LEAF_SIZE, PS_TREE_SPHERICAL, raMerged, decMerged); // kd tree
+    if (!tree) {
+        psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate kd tree");
+        return false;
+    }
+
     for (int i = 0; i < detections->n; i++) {
         ppMopsDetections *det = detections->data[i]; // Detections of interest
         if (!det) {
-            psTrace("ppMops.merge", 3, "Ignoring NULL input %d\n", i);
             continue;
         } else if (det->num == 0) {
-            psTrace("ppMops.merge", 3, "Ignoring empty input %d\n", i);
-            continue;
-        }
-        num++;
-        if (!merged) {
-            psTrace("ppMops.merge", 3, "Accepting %ld detections from input %d\n", det->num, i);
-            merged = psMemIncrRefCounter(det);
-            continue;
-        }
-        psTrace("ppMops.merge", 3, "Merging %ld detections from input %d\n", det->num, i);
-
-        // XXX compare exposure properties
-        if (strcmp(merged->raBoresight, det->raBoresight) != 0) {
-            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Exposure RA values differ: %s vs %s",
-                    merged->raBoresight, det->raBoresight);
-            return NULL;
-        }
-        if (strcmp(merged->decBoresight, det->decBoresight) != 0) {
-            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Exposure Dec values differ: %s vs %s",
-                    merged->decBoresight, det->decBoresight);
-            return NULL;
-        }
-        if (strcmp(merged->filter, det->filter) != 0) {
-            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Exposure filter values differ: %s vs %s",
-                    merged->filter, det->filter);
-            return NULL;
-        }
-
-        if (fabsf(merged->airmass - det->airmass) > AIRMASS_TOL) {
-            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Exposure airmass values differ: %f vs %f",
-                    merged->airmass, det->airmass);
-            return NULL;
-        }
-        if (fabsf(merged->exptime - det->exptime) > EXPTIME_TOL) {
-            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Exposure exposure time values differ: %f vs %f",
-                    merged->exptime, det->exptime);
-            return NULL;
-        }
-        if (fabs(merged->posangle - det->posangle) > POSANGLE_TOL) {
-            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Exposure position angle values differ: %f vs %f",
-                    merged->posangle, det->posangle);
-            return NULL;
-        }
-        if (fabs(merged->alt - det->alt) > BORESIGHT_TOL) {
-            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Exposure altitude values differ: %lf vs %lf",
-                    merged->alt, det->alt);
-            return NULL;
-        }
-        if (fabs(merged->az - det->az) > BORESIGHT_TOL) {
-            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Exposure azimuth values differ: %lf vs %lf",
-                    merged->az, det->az);
-            return NULL;
-        }
-        if (fabs(merged->mjd - det->mjd) > MJD_TOL) {
-            psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Exposure MJD values differ: %lf vs %lf",
-                    merged->mjd, det->mjd);
-            return NULL;
-        }
-
-        merged->seeing += det->seeing;  // Taking average
-
-        psTree *tree = psTreePlant(2, LEAF_SIZE, PS_TREE_SPHERICAL, merged->ra, merged->dec); // kd tree
-        if (!tree) {
-            psError(PS_ERR_UNEXPECTED_NULL, false, "Unable to generate kd tree");
-            psFree(merged);
-            return NULL;
-        }
-
+            continue;
+        }
+        psTrace("ppMops.merge", 3, "Checking %ld detections from input %d\n", det->num, i);
+
+        psVector *dupes = duplicates->data[i];            // Duplicates list
         psVector *coords = psVectorAlloc(2, PS_TYPE_F64); // Coordinates of interest
         for (int j = 0; j < det->num; j++) {
+            // XXX: added by Bill
+            if (det->mask->data.U8[j]) {
+                // we marked this source marked as bad when we read it, mark it as a duplicate
+                dupes->data.U8[j] = 0xFF;
+                continue;
+            }
+
             coords->data.F64[0] = det->ra->data.F64[j];
             coords->data.F64[1] = det->dec->data.F64[j];
@@ -119,14 +181,14 @@
                 psFree(coords);
                 psFree(tree);
-                psFree(merged);
-                return NULL;
-            }
-            if (indices->n == 0) {
-                psTrace("ppMops.merge", 9, "No matches for source %d in input %d\n", j, i);
+                return false;
+            }
+            psTrace("ppMops.merge", 5, "%ld matches for source %d from input %d\n", indices->n, j, i);
+            psAssert(indices->n > 0, "Expect at least one match for source %d in input %d", j, i);
+
+            if (indices->n == 1 && sourceMerged->data.U16[indices->data.S64[0]] == i) {
+                // It's myself
                 psFree(indices);
-                ppMopsDetectionsCopySingle(merged, det, j);
                 continue;
             }
-            psTrace("ppMops.merge", 5, "%ld matches for source %d from input %d\n", indices->n, j, i);
 
             // Which one do we keep?
@@ -134,6 +196,16 @@
             long bestIndex = -1;           // Index with best distance
             for (int k = 0; k < indices->n; k++) {
-                long index = indices->data.S64[k]; // Index of point
-                float distance = mergeDistance(merged, index); // Distance to centre of image
+                long mergeIndex = indices->data.S64[k]; // Index of point in merged list
+                int source = sourceMerged->data.U16[mergeIndex]; // Source image
+                if (source == i) {
+                    continue;
+                }
+                long index = indexMerged->data.U32[mergeIndex];  // Index in source
+                psVector *dupes = duplicates->data[source];     // Duplicates list
+                if (dupes->data.U8[index]) {
+                    continue;
+                }
+
+                float distance = mergeDistance(detections->data[source], index); // Distance to centre of image
                 if (distance < bestDistance) {
                     bestDistance = distance;
@@ -142,35 +214,100 @@
             }
 
-            float distance = mergeDistance(det, j); // Distance to centre of image
-            if (distance < bestDistance) {
-                psTrace("ppMops.merge", 6, "New source clobbers old sources\n");
-                // Blow away existing sources
-                for (int k = 0; k < indices->n; k++) {
-                    long index = indices->data.S64[k]; // Index of point
-                    merged->mask->data.U8[index] = 0xFF;
+            if (bestIndex >= 0) {
+                float distance = mergeDistance(det, j); // Distance to centre of image
+                if (bestIndex >= 0 && distance < bestDistance) {
+                    psTrace("ppMops.merge", 6, "New source clobbers old sources\n");
+                    for (int k = 0; k < indices->n; k++) {
+                        long mergeIndex = indices->data.S64[k]; // Index of point
+                        int source = sourceMerged->data.U16[mergeIndex]; // Source image
+                        if (source == i) {
+                            continue;
+                        }
+                        long index = indexMerged->data.U32[mergeIndex];  // Index in source
+                        psVector *dupes = duplicates->data[source];      // Duplicates list
+                        if (!dupes->data.U8[index]) {
+                            dupes->data.U8[index] = 0xFF;
+                            dupNum->data.U32[source]++;
+                        }
+                    }
+                } else {
+                    psTrace("ppMops.merge", 6, "Old sources clobber new source\n");
+                    dupes->data.U8[j] = 0xFF;
+                    dupNum->data.U32[i]++;
                 }
-                ppMopsDetectionsCopySingle(merged, det, j);
-            } else {
-                psTrace("ppMops.merge", 6, "Old sources clobber new source\n");
             }
             psFree(indices);
         }
-
-        psTrace("ppMops.merge", 3, "Done merging input %d, %ld merged sources\n", i, merged->num);
-
-        psFree(tree);
-        ppMopsDetectionsPurge(merged);
-    }
-
-    if (num == 0) {
-    	//All detections were NULL?!
-	psTrace("ppMops.merge", 3, "All %ld detections were NULL\n", detections->n);
-    	return NULL;
-    }
-    psTrace("ppMops.merge", 2, "%ld sources in merged detections list\n", merged->num);
-
-    merged->seeing /= (float) num;
-
-    return merged;
+        psFree(coords);
+    }
+    psFree(tree);
+    psFree(raMerged);
+    psFree(decMerged);
+    psFree(sourceMerged);
+    psFree(indexMerged);
+
+    // Remove duplicates
+    for (int i = 0; i < detections->n; i++) {
+        ppMopsDetections *det = detections->data[i]; // Detections of interest
+        if (!det) {
+            continue;
+        } else if (det->num == 0) {
+            continue;
+        }
+        psTrace("ppMops.merge", 3, "Purging %d duplicates from input %d\n", dupNum->data.U32[i], i);
+
+#define VECTOR_PURGE_CASE(TYPE)                                     \
+        case PS_TYPE_##TYPE:    {                                   \
+          long j = 0;                                               \
+          for (long i = 0; i < vector->n; i++) {                    \
+              if (!dupes->data.U8[i]) {                             \
+                  if (i == j) {                                     \
+                      j++;                                          \
+                      continue;                                     \
+                  }                                                 \
+                  vector->data.TYPE[j++] = vector->data.TYPE[i];    \
+              }                                                     \
+          }                                                         \
+          vector->n = j;                                            \
+        }                                                           \
+        break
+
+        psVector *dupes = duplicates->data[i]; // Duplicates
+        psArray *table = psListToArray(det->table->list); // Table of data
+        long newLength = -1;
+        for (int t = 0; t < table->n; t++) {
+            psMetadataItem *item = table->data[t]; // Table item
+            psAssert(item->type == PS_DATA_VECTOR, "Table column is not a vector: %x", item->type);
+            psVector *vector = item->data.V; // Vector to purge
+            switch (vector->type.type) {
+                VECTOR_PURGE_CASE(U8);
+                VECTOR_PURGE_CASE(U16);
+                VECTOR_PURGE_CASE(U32);
+                VECTOR_PURGE_CASE(U64);
+                VECTOR_PURGE_CASE(S8);
+                VECTOR_PURGE_CASE(S16);
+                VECTOR_PURGE_CASE(S32);
+                VECTOR_PURGE_CASE(S64);
+                VECTOR_PURGE_CASE(F32);
+                VECTOR_PURGE_CASE(F64);
+              default:
+                psAbort("Unrecognised vector type: %x", vector->type.type);
+            }
+            if (newLength == -1) {
+                newLength = vector->n;
+            } else if (vector->n != newLength) {
+                psAbort("Unexpected new length found : %ld expected: %ld",
+                                                vector->n, newLength);
+            }
+                
+        }
+        // XXX IS this safe? Perhaps save in numGood?
+        det->num = newLength;
+        psFree(table);
+    }
+    psFree(dupNum);
+    psFree(duplicates);
+
+    return true;
 }
 
Index: branches/meh_branches/ppstack_test/ppTranslate/src/ppMopsRead.c
===================================================================
--- branches/meh_branches/ppstack_test/ppTranslate/src/ppMopsRead.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppTranslate/src/ppMopsRead.c	(revision 33415)
@@ -20,10 +20,12 @@
     psArray *detections = psArrayAlloc(num); // Array of detections, to return
     for (int i = 0; i < num; i++) {
-        psFits *fits = psFitsOpen(inNames->data[i], "r"); // FITS file
-
+        const char *name = inNames->data[i];
+
+        psFits *fits = psFitsOpen(name,  "r"); // FITS file
         if (!fits) {
             psError(PS_ERR_IO, false, "Unable to open input %d", i);
             return false;
         }
+
         psMetadata *header = psFitsReadHeader(NULL, fits); // Primary header
         if (!header) {
@@ -74,4 +76,8 @@
         }
         ppMopsDetections *det = ppMopsDetectionsAlloc(size);
+        detections->data[i] = det;
+        det->component = psStringNCopy(name, strrchr(name, '.') - name); // Strip off extension
+        det->num = size;
+        det->diffSkyfileId = diffSkyfileId;
 
         psTrace("ppMops.read", 3, "Reading %ld rows from %s\n", size, (const char*)inNames->data[i]);
@@ -90,135 +96,84 @@
 				     psMetadataLookupF32(NULL, header, "FWHM_MIN"));
 
-        int naxis1 = psMetadataLookupS32(NULL, header, "IMNAXIS1"); // Number of columns
-        int naxis2 = psMetadataLookupS32(NULL, header, "IMNAXIS2"); // Number of rows
+        det->naxis1 = psMetadataLookupS32(NULL, header, "IMNAXIS1"); // Number of columns
+        det->naxis2 = psMetadataLookupS32(NULL, header, "IMNAXIS2"); // Number of rows
 
         psFree(header);
 
-        psArray *table = psFitsReadTable(fits); // Table of interest
+        psMetadata *table = psFitsReadTableAllColumns(fits); // Table of interest
         if (!table) {
             psError(PS_ERR_IO, false, "Unable to read table %d", i);
-            return false;
-        }
+            return NULL;
+        }
+        det->table = table;
         psFitsClose(fits);
+        if (args->version == 0) {
+          if (skyChipPsfVersion < 2) {
+            // XXX: TODO: Do we need to add dummy vectors for the missing columns?
+           }
+        }
+
+        psVector *ra = psMetadataLookupVector(NULL, table, "RA_PSF");
+        psVector *dec = psMetadataLookupVector(NULL, table, "DEC_PSF");
+
+        det->raErr = psVectorAlloc(size, PS_TYPE_F64);
+        det->decErr = psVectorAlloc(size, PS_TYPE_F64);
+        det->mask = psVectorAlloc(size, PS_TYPE_U8);
+
+        // convert ra and dec to radians for use in the purge duplicates function
+        det->ra = (psVector*)psBinaryOp(NULL, ra, "*", psScalarAlloc(DEG_TO_RAD(1.0), PS_TYPE_F64));
+        det->dec = (psVector*)psBinaryOp(NULL, dec, "*", psScalarAlloc(DEG_TO_RAD(1.0), PS_TYPE_F64));
+        det->x = psMemIncrRefCounter(psMetadataLookupVector(NULL, table, "X_PSF"));
+        det->y = psMemIncrRefCounter(psMetadataLookupVector(NULL, table, "Y_PSF"));
+        if (!det->ra || !det->dec || !det->x || !det->y) {
+            psError(PS_ERR_UNEXPECTED_NULL, true, "Unable to find all of RA, Dec, X and Y columns");
+            return NULL;
+        }
+
+        // Add our new vectors to the table so that duplicates and masked items may be purged
+        psMetadataAddVector(table, PS_LIST_HEAD, "DEC_ERR", 0, NULL, det->decErr);
+        psMetadataAddVector(table, PS_LIST_HEAD, "RA_ERR", 0, NULL, det->raErr);
+
+        psTrace("ppMops.read", 2, "Read %ld rows from %s\n", det->num, det->component);
+
+        psVector *mag    = psMetadataLookupVector(NULL, table, "PSF_INST_MAG");
+        psVector *magErr = psMetadataLookupVector(NULL, table, "PSF_INST_MAG_SIG");
+        psVector *xErrV  = psMetadataLookupVector(NULL, table, "X_PSF_SIG");
+        psVector *yErrV  = psMetadataLookupVector(NULL, table, "Y_PSF_SIG");
+        psVector *scaleV = psMetadataLookupVector(NULL, table, "PLTSCALE");
+        psVector *angleV = psMetadataLookupVector(NULL, table, "POSANGLE");
+        psVector *flagsV = psMetadataLookupVector(NULL, table, "FLAGS");
 
         double plateScale = 0.0;        // Plate scale
         long numGood = 0;               // Number of good rows
-        for (long j = 0; j < size; j++) {
-            psMetadata *row = table->data[j]; // Row of interest
-
-            psU32 flags = psMetadataLookupU32(NULL, row, "FLAGS");
+        for (long row = 0; row < size; row++) {
+
+            psU32 flags = flagsV->data.U32[row]; // psFitsTableGetU32(NULL, table, row, "FLAGS");
             if (flags & SOURCE_MASK) {
-                psTrace("ppMops.read", 10, "Discarding row %ld from input %d because of flags: %ud", j, i, flags);
+                psTrace("ppMops.read", 10, "Discarding row %ld from input %d because of flags: %ud", row, i, flags);
+                det->mask->data.U8[row] = 0xFF;
                 continue;
             }
 
-            det->x->data.F32[numGood] = psMetadataLookupF32(NULL, row, "X_PSF");
-            det->y->data.F32[numGood] = psMetadataLookupF32(NULL, row, "Y_PSF");
-            det->ra->data.F64[numGood] = DEG_TO_RAD(psMetadataLookupF64(NULL, row, "RA_PSF"));
-            det->dec->data.F64[numGood] = DEG_TO_RAD(psMetadataLookupF64(NULL, row, "DEC_PSF"));
-            det->mag->data.F32[numGood] = psMetadataLookupF32(NULL, row, "PSF_INST_MAG");
-            det->magErr->data.F32[numGood] = psMetadataLookupF32(NULL, row, "PSF_INST_MAG_SIG");
-            det->chi2->data.F32[numGood] = psMetadataLookupF32(NULL, row, "PSF_CHISQ");
-            det->dof->data.S32[numGood] = psMetadataLookupS32(NULL, row, "PSF_NDOF");
-            det->cr->data.F32[numGood] = psMetadataLookupF32(NULL, row, "CR_NSIGMA");
-            det->extended->data.F32[numGood] = psMetadataLookupF32(NULL, row, "EXT_NSIGMA");
-            det->psfMajor->data.F32[numGood] = psMetadataLookupF32(NULL, row, "PSF_MAJOR");
-            det->psfMinor->data.F32[numGood] = psMetadataLookupF32(NULL, row, "PSF_MINOR");
-            det->psfTheta->data.F32[numGood] = psMetadataLookupF32(NULL, row, "PSF_THETA");
-            det->quality->data.F32[numGood] = psMetadataLookupF32(NULL, row, "PSF_QF");
-            det->numPix->data.S32[numGood] = psMetadataLookupS32(NULL, row, "PSF_NPIX");
-            det->xxMoment->data.F32[numGood] = psMetadataLookupF32(NULL, row, "MOMENTS_XX");
-            det->xyMoment->data.F32[numGood] = psMetadataLookupF32(NULL, row, "MOMENTS_XY");
-            det->yyMoment->data.F32[numGood] = psMetadataLookupF32(NULL, row, "MOMENTS_YY");
-            det->flags->data.U32[numGood] = psMetadataLookupU32(NULL, row, "FLAGS");
-            det->diffSkyfileId->data.S64[numGood] = diffSkyfileId;
-            det->naxis1->data.S32[numGood] = naxis1;
-            det->naxis2->data.S32[numGood] = naxis2;
-            det->nPos->data.S32[numGood] = psMetadataLookupS32(NULL, row, "DIFF_NPOS");
-            det->fPos->data.F32[numGood] = psMetadataLookupF32(NULL, row, "DIFF_FRATIO");
-            det->nRatioBad->data.F32[numGood] = psMetadataLookupF32(NULL, row, "DIFF_NRATIO_BAD");
-            det->nRatioMask->data.F32[numGood] = psMetadataLookupF32(NULL, row, "DIFF_NRATIO_MASK");
-            det->nRatioAll->data.F32[numGood] = psMetadataLookupF32(NULL, row, "DIFF_NRATIO_ALL");
-
-	    //Additions of 2010-10-25
-	    if (args->version == 2) {
-	      //Values are set only if the version is 2
-	      if (skyChipPsfVersion == 2) {
-		det->psfInstFlux->data.F32[numGood] = psMetadataLookupF32(NULL, row, "PSF_INST_FLUX");
-		det->psfInstFluxSig->data.F32[numGood] = psMetadataLookupF32(NULL, row, "PSF_INST_FLUX_SIG");
-		det->apMag->data.F32[numGood] = psMetadataLookupF32(NULL, row, "AP_MAG");
-		det->apMagRaw->data.F32[numGood] = psMetadataLookupF32(NULL, row, "AP_MAG_RAW");
-		det->apMagRadius->data.F32[numGood] = psMetadataLookupF32(NULL, row, "AP_MAG_RADIUS");
-		det->apFlux->data.F32[numGood] = psMetadataLookupF32(NULL, row, "AP_FLUX");
-		det->apFluxSig->data.F32[numGood] = psMetadataLookupF32(NULL, row, "AP_FLUX_SIG");
-		det->peakFluxAsMag->data.F32[numGood] = psMetadataLookupF32(NULL, row, "PEAK_FLUX_AS_MAG");
-		det->calPsfMag->data.F32[numGood] = psMetadataLookupF32(NULL, row, "CAL_PSF_MAG");
-		det->calPsfMagSig->data.F32[numGood] = psMetadataLookupF32(NULL, row, "CAL_PSF_MAG_SIG");
-		det->sky->data.F32[numGood] = psMetadataLookupF32(NULL, row, "SKY");
-		det->skySig->data.F32[numGood] = psMetadataLookupF32(NULL, row, "SKY_SIGMA");
-		det->qualityPerfect->data.F32[numGood] = psMetadataLookupF32(NULL, row, "PSF_QF_PERFECT");
-		det->momentsR1->data.F32[numGood] = psMetadataLookupF32(NULL, row, "MOMENTS_R1");
-		det->momentsRH->data.F32[numGood] = psMetadataLookupF32(NULL, row, "MOMENTS_RH");
-		det->kronFlux->data.F32[numGood] = psMetadataLookupF32(NULL, row, "KRON_FLUX");
-		det->kronFluxErr->data.F32[numGood] = psMetadataLookupF32(NULL, row, "KRON_FLUX_ERR");
-		det->kronFluxInner->data.F32[numGood] = psMetadataLookupF32(NULL, row, "KRON_FLUX_INNER");
-		det->kronFluxOuter->data.F32[numGood] = psMetadataLookupF32(NULL, row, "KRON_FLUX_OUTER");
-		det->diffRP->data.F32[numGood] = psMetadataLookupF32(NULL, row, "DIFF_R_P");
-		det->diffSnP->data.F32[numGood] = psMetadataLookupF32(NULL, row, "DIFF_SN_P");
-		det->diffRM->data.F32[numGood] = psMetadataLookupF32(NULL, row, "DIFF_R_M");
-		det->diffSnM->data.F32[numGood] = psMetadataLookupF32(NULL, row, "DIFF_SN_M");
-		det->flags2->data.U32[numGood] = psMetadataLookupU32(NULL, row, "FLAGS2");
-		det->ippIdet->data.U32[numGood] = psMetadataLookupU32(NULL, row, "IPP_IDET");
-		det->nFrames->data.U16[numGood] = psMetadataLookupU16(NULL, row, "N_FRAMES");
-		det->padding->data.S16[numGood] = psMetadataLookupS16(NULL, row, "PADDING");
-	      } else {
-		det->psfInstFlux->data.F32[numGood] = NAN;
-		det->psfInstFluxSig->data.F32[numGood] = NAN;
-		det->apMag->data.F32[numGood] = NAN;
-		det->apMagRaw->data.F32[numGood] = NAN;
-		det->apMagRadius->data.F32[numGood] = NAN;
-		det->apFlux->data.F32[numGood] = NAN;
-		det->apFluxSig->data.F32[numGood] = NAN;
-		det->peakFluxAsMag->data.F32[numGood] = NAN;
-		det->calPsfMag->data.F32[numGood] = NAN;
-		det->calPsfMagSig->data.F32[numGood] = NAN;
-		det->sky->data.F32[numGood] = NAN;
-		det->skySig->data.F32[numGood] = NAN;
-		det->qualityPerfect->data.F32[numGood] = NAN;
-		det->momentsR1->data.F32[numGood] = NAN;
-		det->momentsRH->data.F32[numGood] = NAN;
-		det->kronFlux->data.F32[numGood] = NAN;
-		det->kronFluxErr->data.F32[numGood] = NAN;
-		det->kronFluxInner->data.F32[numGood] = NAN;
-		det->kronFluxOuter->data.F32[numGood] = NAN;
-		det->diffRP->data.F32[numGood] = NAN;
-		det->diffSnP->data.F32[numGood] = NAN;
-		det->diffRM->data.F32[numGood] = NAN;
-		det->diffSnM->data.F32[numGood] = NAN;
-		det->flags2->data.U32[numGood] = 0;
-		det->ippIdet->data.U32[numGood] = 0;
-		det->nFrames->data.U16[numGood] = 0;
-		det->padding->data.S16[numGood] = 0;
-	      }
-	    }
-
             // Calculate error in RA, Dec
-            double xErr = psMetadataLookupF64(NULL, row, "X_PSF_SIG"); //SC: Warning! Promotion of F32
-            double yErr = psMetadataLookupF64(NULL, row, "Y_PSF_SIG"); //SC: Warning! Promotion of F32
-            double scale = psMetadataLookupF64(NULL, row, "PLTSCALE"); //SC: Warning! Promotion of F32
-            double angle = psMetadataLookupF64(NULL, row, "POSANGLE"); //SC: Warning! Promotion of F32
-
-            if (!isfinite(det->x->data.F32[numGood]) || !isfinite(det->y->data.F32[numGood]) ||
-                !isfinite(det->ra->data.F64[numGood]) || !isfinite(det->dec->data.F64[numGood]) ||
-                !isfinite(det->mag->data.F32[numGood]) || !isfinite(det->magErr->data.F32[numGood]) ||
+
+            double xErr = xErrV->data.F32[row];
+            double yErr = yErrV->data.F32[row];
+            double scale = scaleV->data.F32[row];
+            double angle = angleV->data.F32[row];
+
+            if (!isfinite(det->x->data.F32[row]) || !isfinite(det->y->data.F32[row]) ||
+                !isfinite(det->ra->data.F64[row]) || !isfinite(det->dec->data.F64[row]) ||
+                !isfinite(mag->data.F32[row]) || !isfinite(magErr->data.F32[row]) ||
                 !isfinite(xErr) || !isfinite(yErr) || !isfinite(scale) || !isfinite(angle)) {
                 psTrace("ppMops.read", 10,
                         "Discarding row %ld from input %d because of non-finite values: "
                         "%f %f %lf %lf %f %f %f %f %f %f",
-                        j, i,
-                        det->x->data.F32[numGood], det->y->data.F32[numGood],
-                        det->ra->data.F64[numGood], det->dec->data.F64[numGood],
-                        det->mag->data.F32[numGood], det->magErr->data.F32[numGood],
+                        row, i,
+                        det->x->data.F32[row], det->y->data.F32[row],
+                        det->ra->data.F64[row], det->dec->data.F64[row],
+                        mag->data.F32[row], magErr->data.F32[row],
                         xErr, yErr, scale, angle);
+                det->mask->data.U8[row] = 0xFF;
                 continue;
             }
@@ -229,8 +184,8 @@
             double xErr2 = PS_SQR(xErr), yErr2 = PS_SQR(yErr);
             double errScale = scale / 3600.0;
-            det->raErr->data.F64[numGood] = errScale * sqrt(cosAngle2 * xErr2 + sinAngle2 * yErr2);
-            det->decErr->data.F64[numGood] = errScale * sqrt(sinAngle2 * xErr2 + cosAngle2 * yErr2);
-
-            det->mask->data.U8[numGood] = 0;
+            det->raErr->data.F64[row] = errScale * sqrt(cosAngle2 * xErr2 + sinAngle2 * yErr2);
+            det->decErr->data.F64[row] = errScale * sqrt(sinAngle2 * xErr2 + cosAngle2 * yErr2);
+
+            det->mask->data.U8[row] = 0;
             plateScale += scale;
             numGood++;
@@ -238,72 +193,12 @@
         det->seeing *= ((float) plateScale) / ((float) numGood);
 
-        det->x->n = numGood;
-        det->y->n = numGood;
-        det->ra->n = numGood;
-        det->dec->n = numGood;
-        det->raErr->n = numGood;
-        det->decErr->n = numGood;
-        det->mag->n = numGood;
-        det->magErr->n = numGood;
-        det->chi2->n = numGood;
-        det->dof->n = numGood;
-        det->cr->n = numGood;
-        det->extended->n = numGood;
-        det->psfMajor->n = numGood;
-        det->psfMinor->n = numGood;
-        det->psfTheta->n = numGood;
-        det->quality->n = numGood;
-        det->numPix->n = numGood;
-        det->xxMoment->n = numGood;
-        det->xyMoment->n = numGood;
-        det->yyMoment->n = numGood;
-        det->flags->n = numGood;
-        det->diffSkyfileId->n = numGood;
-        det->naxis1->n = numGood;
-        det->naxis2->n = numGood;
-        det->mask->n = numGood;
-        det->nPos->n = numGood;
-        det->fPos->n = numGood;
-        det->nRatioBad->n = numGood;
-        det->nRatioMask->n = numGood;
-        det->nRatioAll->n = numGood;
-	det->psfInstFlux->n = numGood;
-	det->psfInstFluxSig->n = numGood;
-	det->apMag->n = numGood;
-	det->apMagRaw->n = numGood;
-	det->apMagRadius->n = numGood;
-	det->apFlux->n = numGood;
-	det->apFluxSig->n = numGood;
-	det->peakFluxAsMag->n = numGood;
-	det->calPsfMag->n = numGood;
-	det->calPsfMagSig->n = numGood;
-	det->sky->n = numGood;
-	det->skySig->n = numGood;
-	det->qualityPerfect->n = numGood;
-	det->momentsR1->n = numGood;
-	det->momentsRH->n = numGood;
-	det->kronFlux->n = numGood;
-	det->kronFluxErr->n = numGood;
-	det->kronFluxInner->n = numGood;
-	det->kronFluxOuter->n = numGood;
-	det->diffRP->n = numGood;
-	det->diffSnP->n = numGood;
-	det->diffRM->n = numGood;
-	det->diffSnM->n = numGood;
-	det->flags2->n = numGood;
-	det->ippIdet->n = numGood;
-	det->nFrames->n = numGood;
-	det->padding->n = numGood;
-
-        det->num = numGood;
+        // Are we using numGood for anything outside of this function?
+        det->numGood = numGood;
 
         if (isfinite(args->zp) && numGood > 0) {
-            psBinaryOp(det->mag, det->mag, "+", psScalarAlloc(args->zp, PS_TYPE_F32));
-        }
-
-        psTrace("ppMops.read", 2, "Read %ld good rows from %s\n", numGood, (const char*)inNames->data[i]);
-
-        psFree(table);
-        detections->data[i] = det;
+            psBinaryOp(mag, mag, "+", psScalarAlloc(args->zp, PS_TYPE_F32));
+        }
+
+        psTrace("ppMops.read", 2, "Read %ld good rows from %s\n", numGood, (const char*)name);
     }
 
Index: branches/meh_branches/ppstack_test/ppTranslate/src/ppMopsWrite.c
===================================================================
--- branches/meh_branches/ppstack_test/ppTranslate/src/ppMopsWrite.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppTranslate/src/ppMopsWrite.c	(revision 33415)
@@ -9,7 +9,9 @@
 #include "ppTranslateVersion.h"
 
-bool ppMopsWrite(const ppMopsDetections *det, const ppMopsArguments *args)
+static bool addOutputColumn(psMetadata *table, const psArray *detections, long total, char *outColName, char *inColName, bool convertTo32);
+static bool addSkyfileIDColumn(psMetadata *table, const psArray *detections, long total, char *colName);
+
+bool ppMopsWrite(const psArray *detections, const ppMopsArguments *args)
 {
-    psTrace("ppMops.write", 1, "Writing %ld rows to %s", det->num, args->output);
 
     psFits *fits = psFitsOpen(args->output, "w"); // FITS file
@@ -36,24 +38,47 @@
     psMetadataAddBool(header, PS_LIST_TAIL, "DIFF_POS", 0, "Positive subtraction?", args->positive);
 
-    psMetadataAddF64(header, PS_LIST_TAIL, "MJD-OBS", 0, "MJD of exposure midpoint", det->mjd);
-    psMetadataAddStr(header, PS_LIST_TAIL, "RA", 0, "Right Ascension of boresight", det->raBoresight);
-    psMetadataAddStr(header, PS_LIST_TAIL, "DEC", 0, "Declination of boresight", det->decBoresight);
-    psMetadataAddF64(header, PS_LIST_TAIL, "TEL_ALT", 0, "Telescope altitude", det->alt);
-    psMetadataAddF64(header, PS_LIST_TAIL, "TEL_AZ", 0, "Telescope azimuth", det->az);
-    psMetadataAddF64(header, PS_LIST_TAIL, "EXPTIME", 0, "Exposure time (sec)", det->exptime);
-    psMetadataAddF64(header, PS_LIST_TAIL, "ROTANGLE", 0, "Rotator position angle", det->posangle);
-    psMetadataAddStr(header, PS_LIST_TAIL, "FILTER", 0, "Filter name", det->filter);
-    psMetadataAddF32(header, PS_LIST_TAIL, "AIRMASS", 0, "Airmass of exposure", det->airmass);
+    // Get these header words from the first input non null input
+    ppMopsDetections *det = NULL;
+    for (int d = 0; d < detections->n && det == NULL; d++) {
+        det = detections->data[d];
+    }
+    if (det != NULL) {
+        psMetadataAddF64(header, PS_LIST_TAIL, "MJD-OBS", 0, "MJD of exposure midpoint", det->mjd);
+        psMetadataAddStr(header, PS_LIST_TAIL, "RA", 0, "Right Ascension of boresight", det->raBoresight);
+        psMetadataAddStr(header, PS_LIST_TAIL, "DEC", 0, "Declination of boresight", det->decBoresight);
+	psMetadataAddF64(header, PS_LIST_TAIL, "TEL_ALT", 0, "Telescope altitude", det->alt);
+	psMetadataAddF64(header, PS_LIST_TAIL, "TEL_AZ", 0, "Telescope azimuth", det->az);
+	psMetadataAddF64(header, PS_LIST_TAIL, "EXPTIME", 0, "Exposure time (sec)", det->exptime);
+	psMetadataAddF64(header, PS_LIST_TAIL, "ROTANGLE", 0, "Rotator position angle", det->posangle);
+	psMetadataAddStr(header, PS_LIST_TAIL, "FILTER", 0, "Filter name", det->filter);
+	psMetadataAddF32(header, PS_LIST_TAIL, "AIRMASS", 0, "Airmass of exposure", det->airmass);
+        psMetadataAddF32(header, PS_LIST_TAIL, "SEEING", 0, "Mean seeing", det->seeing);
+    } else {
+	psWarning("no inputs with surviving detections. output header will be incomplete");
+    }
     psMetadataAddStr(header, PS_LIST_TAIL, "OBSCODE", 0, "IAU Observatory code", OBSERVATORY_CODE);
-    psMetadataAddF32(header, PS_LIST_TAIL, "SEEING", 0, "Mean seeing", det->seeing);
     psMetadataAddF32(header, PS_LIST_TAIL, "MAGZP", 0, "Magnitude zero point", args->zp);
     psMetadataAddF32(header, PS_LIST_TAIL, "MAGZPERR", 0, "Error in magnitude zero point", args->zpErr);
     psMetadataAddF32(header, PS_LIST_TAIL, "ASTRORMS", 0, "RMS of astrometric fit", args->rmsAstrom);
-    //New field in header that tells about the CMF version
+
+    //field in header that tells about the CMF version
     char cmfVersion[8];
     sprintf(cmfVersion, PS1_DV_FORMAT, args->version);
     psMetadataAddStr(header, PS_LIST_TAIL, "CMFVERSION", 0, "CMF version", cmfVersion);
 
-    if (det->num == 0) {
+    // Find the total number of detections
+
+    long total = 0;
+    for (long i=0; i<detections->n; i++) {
+        ppMopsDetections *det = detections->data[i];
+	if (!det) {
+	    continue;
+	}
+        total += det->num;
+    }
+
+    psTrace("ppMops.write", 1, "Writing %ld rows to %s", total, args->output);
+
+    if (total == 0) {
         // Write dummy table
         psMetadata *row = psMetadataAlloc(); // Output row
@@ -85,5 +110,5 @@
         psMetadataAddF32(row, PS_LIST_TAIL, "RATIO_ALL", 0, "Ratio of positive pixels to all", NAN);
 
-	    if (args->version == 2) {
+	if (args->version == 2) {
 	      // Write data of version 2 (see ICD)
 	      psMetadataAdd (row, PS_LIST_TAIL, "IPP_IDET",         PS_DATA_U32, "IPP detection identifier index",
@@ -141,5 +166,5 @@
 	      psMetadataAdd (row, PS_LIST_TAIL, "PADDING",          PS_DATA_S16, "padding", 
 			     0);
-	    }
+	}
 
         if (!psFitsWriteTableEmpty(fits, header, row, OUT_EXTNAME)) {
@@ -151,114 +176,70 @@
         psFree(row);
     } else {
-        psArray *table = psArrayAlloc(det->num); // Table to write
-        for (long i = 0; i < det->num; i++) {
-            psMetadata *row = psMetadataAlloc(); // Output row
-            psMetadataAddF64(row, PS_LIST_TAIL, "RA", 0, "Right ascension (degrees)",
-                             RAD_TO_DEG(det->ra->data.F64[i]));
-            psMetadataAddF64(row, PS_LIST_TAIL, "RA_ERR", 0, "Right ascension error (degrees)",
-                             det->raErr->data.F64[i]);
-            psMetadataAddF64(row, PS_LIST_TAIL, "DEC", 0, "Declination (degrees)",
-                             RAD_TO_DEG(det->dec->data.F64[i]));
-            psMetadataAddF64(row, PS_LIST_TAIL, "DEC_ERR", 0, "Declination error (degrees)",
-                             det->decErr->data.F64[i]);
-            psMetadataAddF32(row, PS_LIST_TAIL, "MAG", 0, "Magnitude", det->mag->data.F32[i]);
-            psMetadataAddF32(row, PS_LIST_TAIL, "MAG_ERR", 0, "Magnitude error", det->magErr->data.F32[i]);
-            psMetadataAddF32(row, PS_LIST_TAIL, "PSF_CHI2", 0, "chi^2 of PSF fit", det->chi2->data.F32[i]);
-            psMetadataAddS32(row, PS_LIST_TAIL, "PSF_DOF", 0, "Degrees of freedom of PSF fit",
-                             det->dof->data.S32[i]);
-            psMetadataAddF32(row, PS_LIST_TAIL, "CR_SIGNIFICANCE", 0, "Significance of CR",
-                             det->cr->data.F32[i]);
-            psMetadataAddF32(row, PS_LIST_TAIL, "EXT_SIGNIFICANCE", 0, "Significance of extendedness",
-                             det->extended->data.F32[i]);
-            psMetadataAddF32(row, PS_LIST_TAIL, "PSF_MAJOR", 0, "PSF major axis (pixels)", det->psfMajor->data.F32[i]);
-            psMetadataAddF32(row, PS_LIST_TAIL, "PSF_MINOR", 0, "PSF minor axis (pixels)", det->psfMinor->data.F32[i]);
-            psMetadataAddF32(row, PS_LIST_TAIL, "PSF_THETA", 0, "PSF position angle (deg on chip)",
-                             det->psfTheta->data.F32[i]);
-            psMetadataAddF32(row, PS_LIST_TAIL, "PSF_QUALITY", 0, "PSF quality factor",
-                             det->quality->data.F32[i]);
-            psMetadataAddS32(row, PS_LIST_TAIL, "PSF_NPIX", 0, "Number of pixels in PSF",
-                             det->numPix->data.S32[i]);
-            psMetadataAddF32(row, PS_LIST_TAIL, "MOMENTS_XX", 0, "xx moment", det->xxMoment->data.F32[i]);
-            psMetadataAddF32(row, PS_LIST_TAIL, "MOMENTS_XY", 0, "xy moment", det->xyMoment->data.F32[i]);
-            psMetadataAddF32(row, PS_LIST_TAIL, "MOMENTS_YY", 0, "yy moment", det->yyMoment->data.F32[i]);
-            psMetadataAddS32(row, PS_LIST_TAIL, "N_POS", 0, "Number of positive pixels",
-                             det->nPos->data.S32[i]);
-            psMetadataAddF32(row, PS_LIST_TAIL, "F_POS", 0, "Fraction of positive pixels",
-                             det->fPos->data.F32[i]);
-            psMetadataAddF32(row, PS_LIST_TAIL, "RATIO_BAD", 0, "Ratio of positive pixels to negative",
-                             det->nRatioBad->data.F32[i]);
-            psMetadataAddF32(row, PS_LIST_TAIL, "RATIO_MASK", 0, "Ratio of positive pixels to masked",
-                             det->nRatioMask->data.F32[i]);
-            psMetadataAddF32(row, PS_LIST_TAIL, "RATIO_ALL", 0, "Ratio of positive pixels to all",
-                             det->nRatioAll->data.F32[i]);
-            psMetadataAddU32(row, PS_LIST_TAIL, "FLAGS", 0, "Detection bit flags", det->flags->data.U32[i]);
-            psMetadataAddS64(row, PS_LIST_TAIL, "DIFF_SKYFILE_ID", 0, "Identifier for diff skyfile",
-                             det->diffSkyfileId->data.S64[i]);
-
-	    if (args->version == 2) {
-	      // Write data of version 2 (see ICD)
-	      psMetadataAdd (row, PS_LIST_TAIL, "IPP_IDET",         PS_DATA_U32, "IPP detection identifier index",
-			     det->ippIdet->data.U32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_FLUX",    PS_DATA_F32, "PSF fit instrumental magnitude",
-			     det->psfInstFlux->data.F32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_FLUX_SIG",PS_DATA_F32, "Sigma of PSF instrumental magnitude",
-			     det->psfInstFluxSig->data.F32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG",           PS_DATA_F32, "magnitude in standard aperture",
-			     det->apMag->data.F32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RAW",       PS_DATA_F32, "magnitude in real aperture",
-			     det->apMagRaw->data.F32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RADIUS",    PS_DATA_F32, "radius used for aperture mags",
-			     det->apMagRadius->data.F32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "AP_FLUX",          PS_DATA_F32, "instrumental flux in standard aperture",
-			     det->apFlux->data.F32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "AP_FLUX_SIG",      PS_DATA_F32, "aperture flux error",
-			     det->apFluxSig->data.F32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "PEAK_FLUX_AS_MAG", PS_DATA_F32, "Peak flux expressed as magnitude",
-			     det->peakFluxAsMag->data.F32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "CAL_PSF_MAG",      PS_DATA_F32, "PSF Magnitude using supplied calibration",
-			     det->calPsfMag->data.F32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "CAL_PSF_MAG_SIG",  PS_DATA_F32, "measured scatter of zero point calibration",
-			     det->calPsfMagSig->data.F32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "SKY",              PS_DATA_F32, "Sky level",
-			     det->sky->data.F32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "SKY_SIGMA",        PS_DATA_F32, "Sigma of sky level",
-			     det->skySig->data.F32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF_PERFECT",   PS_DATA_F32, "PSF coverage/quality factor (poor)",
-			     det->qualityPerfect->data.F32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_R1",       PS_DATA_F32, "first radial moment",
-			     det->momentsR1->data.F32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_RH",       PS_DATA_F32, "half radial moment",
-			     det->momentsRH->data.F32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX",        PS_DATA_F32, "Kron Flux (in 2.5 R1)",
-			     det->kronFlux->data.F32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_ERR",    PS_DATA_F32, "Kron Flux Error",
-			     det->kronFluxErr->data.F32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_INNER",  PS_DATA_F32, "Kron Flux (in 1.0 R1)", 
-			     det->kronFluxInner->data.F32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_OUTER",  PS_DATA_F32, "Kron Flux (in 4.0 R1)",
-			     det->kronFluxOuter->data.F32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "DIFF_R_P",         PS_DATA_F32, "distance to positive match source",
-			     det->diffRP->data.F32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "DIFF_SN_P",        PS_DATA_F32, "signal-to-noise of pos match src",
-			     det->diffSnP->data.F32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "DIFF_R_M",         PS_DATA_F32, "distance to negative match source",
-			     det->diffRM->data.F32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "DIFF_SN_M",        PS_DATA_F32, "signal-to-noise of neg match src",
-			     det->diffSnM->data.F32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "FLAGS2",           PS_DATA_U32, "psphot analysis flags (group 2)",
-			     det->flags2->data.U32[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "N_FRAMES",         PS_DATA_U16, "Number of frames overlapping source center", 
-			     det->nFrames->data.U16[i]);
-	      psMetadataAdd (row, PS_LIST_TAIL, "PADDING",          PS_DATA_S16, "padding", 
-			     det->padding->data.S16[i]);
-	    }
-
-	    //Update with the table with the current row
-            table->data[i] = row;
-        }
-        if (!psFitsWriteTable(fits, header, table, OUT_EXTNAME)) {
-            psErrorStackPrint(stderr, "Unable to write table.");
-            psFree(header);
-            psFree(table);
+
+#define addColumn(_outName, _inName, _convertTo32) \
+        if (!addOutputColumn(table, detections, total, _outName, _inName, _convertTo32)) { \
+            psError(PS_ERR_UNKNOWN, false, "Failed to add column %s", _outName); \
+            return false; \
+        }
+
+        // Allocate the output table
+        psMetadata *table = psMetadataAlloc();
+        addColumn("RA", "RA_PSF", 0);
+        addColumn("RA_ERR", NULL, 0);      // calculated from various parameters including X_PSF_SIG Y_PSF_SIG and POSANG
+        addColumn("DEC", "DEC_PSF", 0);
+        addColumn("DEC_ERR", NULL, 0);     // calculated from various parameters including X_PSF_SIG Y_PSF_SIG and POSANG
+        addColumn("MAG", "PSF_INST_MAG", 0);
+        addColumn("MAG_ERR", "PSF_INST_MAG_SIG", 0);
+        addColumn("PSF_CHI2", "PSF_CHISQ", 0);
+        addColumn("PSF_DOF", "PSF_NDOF", 1);
+        addColumn("CR_SIGNIFICANCE", "CR_NSIGMA", 0);
+        addColumn("EXT_SIGNIFICANCE", "EXT_NSIGMA", 0);
+        addColumn("PSF_MAJOR", NULL, 0);
+        addColumn("PSF_MINOR", NULL, 0);     
+        addColumn("PSF_THETA", NULL, 0);    
+        addColumn("PSF_QUALITY", "PSF_QF", 0); 
+        addColumn("PSF_NPIX", NULL, 1);        
+        addColumn("MOMENTS_XX", NULL, 0);
+        addColumn("MOMENTS_XY", NULL, 0);
+        addColumn("MOMENTS_YY", NULL, 0);
+        addColumn("N_POS", "DIFF_NPOS", 1);
+        addColumn("F_POS", "DIFF_FRATIO", 0);
+        addColumn("RATIO_BAD", "DIFF_NRATIO_BAD", 0);
+        addColumn("RATIO_MASK", "DIFF_NRATIO_MASK", 0);
+        addColumn("RATIO_ALL", "DIFF_NRATIO_ALL", 0);
+        addColumn("FLAGS", "FLAGS", 1);
+        addSkyfileIDColumn(table, detections, total, "DIFF_SKYFILE_ID");
+        if (args->version == 2) {
+            addColumn("IPP_IDET", NULL, 1);
+            addColumn("PSF_INST_FLUX", NULL, 0);
+            addColumn("PSF_INST_FLUX_SIG", NULL, 0);
+            addColumn("AP_MAG", NULL, 0);
+            addColumn("AP_MAG_RAW", NULL, 0);
+            addColumn("AP_MAG_RADIUS", NULL, 0);
+            addColumn("AP_FLUX", NULL, 0);
+            addColumn("AP_FLUX_SIG", NULL, 0);
+            addColumn("PEAK_FLUX_AS_MAG", NULL, 0);
+            addColumn("CAL_PSF_MAG", NULL, 0);
+            addColumn("CAL_PSF_MAG_SIG", NULL, 0);
+            addColumn("SKY", NULL, 0);
+            addColumn("SKY_SIGMA", NULL, 0);
+            addColumn("PSF_QF_PERFECT", NULL, 0);
+            addColumn("MOMENTS_R1", NULL, 0);
+            addColumn("MOMENTS_RH", NULL, 0);
+            addColumn("KRON_FLUX", NULL, 0);
+            addColumn("KRON_FLUX_ERR", NULL, 0);
+            addColumn("KRON_FLUX_INNER", NULL, 0);
+            addColumn("KRON_FLUX_OUTER", NULL, 0);
+            addColumn("DIFF_R_P", NULL, 0);
+            addColumn("DIFF_SN_P", NULL, 0);
+            addColumn("DIFF_R_M", NULL, 0);
+            addColumn("DIFF_SN_M", NULL, 0);
+            addColumn("FLAGS2", NULL, 1);
+            addColumn("IPP_IDET", NULL, 0);
+            addColumn("N_FRAMES", NULL, 0);
+            addColumn("PADDING", NULL, 0);
+        }
+        if (!psFitsWriteTableAllColumns(fits, header, table, OUT_EXTNAME)) {
+            psError(psErrorCodeLast(), false, "Unable to write table");
             return false;
         }
@@ -273,2 +254,133 @@
     return true;
 }
+
+static bool addOutputColumn(psMetadata *table, const psArray *detections, long outputSize, char *outColumnName, char *inColumnName, bool convertTo32)
+{
+    if (inColumnName == NULL) {
+        inColumnName = outColumnName;
+    }
+
+    psVector *out = NULL;
+    if (convertTo32) {
+        // psFitsReadTableAllColumns reads columns of cfitsio type LONG and ULONG into a 64 bit integers
+        // We want to write 32 bits to the output.
+        int next = 0;
+        for (long i=0; i<detections->n; i++) {
+            ppMopsDetections *det = detections->data[i];
+            if (!det || det->num == 0) {
+                // no detections survived for this input
+                continue;
+            }
+            psVector *in = psMetadataLookupVector(NULL, det->table, inColumnName);
+            if (!in) {
+                psError(PS_ERR_PROGRAMMING, true, "failed to find input column: %s", inColumnName);
+                return false;
+            }
+            if (in->type.type != PS_TYPE_S64 && in->type.type != PS_TYPE_U64) {
+                psError(PS_ERR_PROGRAMMING, true, "input column to convert is not S64 or U64: %s %d",
+                    inColumnName, in->type.type);
+                return false;
+            }
+            if (out == NULL) {
+                // First time through set up the output vector and the copy parameters
+                if (in->type.type == PS_TYPE_S64) {
+                    out = psVectorAlloc(outputSize, PS_TYPE_S32);
+                } else {
+                    out = psVectorAlloc(outputSize, PS_TYPE_U32);
+                }
+            }
+            for (long d=0; d < det->num; d++) {
+                if (in->type.type == PS_TYPE_S64) {
+                    out->data.S32[next++] = in->data.S64[d];
+                } else {
+                    out->data.U32[next++] = in->data.U64[d];
+                }
+            }
+        }
+    } else {
+        void *next = NULL;
+        int elementSize = 0;    // size of elements in vector... We are making assumptions here about the organization of primitives in memory so we can use memcopy
+        for (long i=0; i<detections->n; i++) {
+            ppMopsDetections *det = detections->data[i];
+            if (!det || det->num == 0) {
+                // no detections survived for this input
+                continue;
+            }
+            psVector *in = psMetadataLookupVector(NULL, det->table, inColumnName);
+            if (!in) {
+                psError(PS_ERR_PROGRAMMING, true, "failed to find input column: %s", inColumnName);
+                return false;
+            }
+            if (out == NULL) {
+                // First time through set up the output vector and the copy parameters
+                out = psVectorAlloc(outputSize, in->type.type);
+                next = (void *) out->data.U8;
+                switch (in->type.type) {
+                    case PS_TYPE_S8:
+                    elementSize = sizeof(psS8);
+                    break;
+                case PS_TYPE_U8:
+                    elementSize = sizeof(psU8);
+                    break;
+                case PS_TYPE_S16:
+                    elementSize = sizeof(psS16);
+                    break;
+                case PS_TYPE_U16:
+                    elementSize = sizeof(psU16);
+                    break;
+                case PS_TYPE_S32:
+                    elementSize = sizeof(psS32);
+                    break;
+                case PS_TYPE_U32:
+                    elementSize = sizeof(psU32);
+                    break;
+                case PS_TYPE_S64:
+                    elementSize = sizeof(psS64);
+                    break;
+                case PS_TYPE_U64:
+                    elementSize = sizeof(psU64);
+                    break;
+                case PS_TYPE_F32:
+                    elementSize = sizeof(psF32);
+                    break;
+                case PS_TYPE_F64:
+                    elementSize = sizeof(psF64);
+                    break;
+                default:
+                    psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Unknown vector type %d", in->type.type);
+                    return false;
+
+                }
+            }
+            // We are doing nasty things here so we can use memcpy. 
+            // It would be safer to do a proper loop over the elements.
+            long toCopy = det->num * elementSize;
+            memcpy(next, in->data.U8, toCopy);
+            next += toCopy;
+        }
+    }
+
+    // Finally add the new column to the output table
+    psMetadataAddVector(table, PS_LIST_TAIL, outColumnName, 0, NULL, out);
+    psFree(out);    // drop reference
+
+    return true;
+}
+static bool addSkyfileIDColumn(psMetadata *table, const psArray *detections, long total, char *colName)
+{
+    psVector *out = psVectorAlloc(total, PS_TYPE_S64);
+    long next = 0;
+    for (long i = 0; i<detections->n; i++) {
+        ppMopsDetections *det = detections->data[i];
+	if (!det) {
+	    continue;
+	}
+        psS64 diffSkyfileId = det->diffSkyfileId;
+        for (long j = 0; j < det->num; j++) {
+            out->data.S64[next++] = diffSkyfileId;
+        }
+    }
+    psMetadataAddVector(table, PS_LIST_TAIL, colName, 0, NULL, out);
+    psFree(out);
+    return true;
+}
Index: branches/meh_branches/ppstack_test/ppTranslate/src/ppTranslateVersion.c
===================================================================
--- branches/meh_branches/ppstack_test/ppTranslate/src/ppTranslateVersion.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppTranslate/src/ppTranslateVersion.c	(revision 33415)
@@ -77,4 +77,5 @@
     psFree(version);
     psFree(source);
+    psFree(time);
 
     return true;
Index: branches/meh_branches/ppstack_test/ppViz/src/ppCoord/ppCoordLoop.c
===================================================================
--- branches/meh_branches/ppstack_test/ppViz/src/ppCoord/ppCoordLoop.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/ppViz/src/ppCoord/ppCoordLoop.c	(revision 33415)
@@ -31,6 +31,9 @@
 
     psProject(&tp, &sky, astromFPA->toSky);
+    psTrace("ppCoord",2,"sky2pix tr1: %f %f\n",tp.x,tp.y);
     psPlaneTransformApply(&fp, astromFPA->fromTPA, &tp);
+    psTrace("ppCoord",2,"sky2pix tr2: %f %f\n",fp.x,fp.y);
     psPlaneTransformApply(&pix, astromChip->fromFPA, &fp);
+    psTrace("ppCoord",2,"sky2pix tr3: %f %f\n",pix.x,pix.y);
 
     *x = pix.x;
@@ -261,10 +264,12 @@
 
                 psPlaneTransformApply(fp, chip->toFPA, pix);
+		psTrace("ppCoord",2,"pix2sky tr1: %f %f\n",fp->x,fp->y);
                 psPlaneTransformApply(tp, astromFile->fpa->toTPA, fp);
+		psTrace("ppCoord",2,"pix2sky tr2: %f %f\n",tp->x,tp->y);
                 psDeproject(sky, tp, astromFile->fpa->toSky);
-
+		psTrace("ppCoord",2,"pix2sky tr3: %f %f\n",sky->r,sky->d);
                 if (!data->radians) {
-                    sky->r *= M_PI / 180.0;
-                    sky->d *= M_PI / 180.0;
+		  sky->r *= 180.0 / M_PI;
+		  sky->d *= 180.0 / M_PI;
                 }
 
Index: branches/meh_branches/ppstack_test/psLib/src/fits/Makefile.am
===================================================================
--- branches/meh_branches/ppstack_test/psLib/src/fits/Makefile.am	(revision 32161)
+++ branches/meh_branches/ppstack_test/psLib/src/fits/Makefile.am	(revision 33415)
@@ -9,4 +9,5 @@
 	psFitsImage.c \
 	psFitsTable.c \
+	psFitsTableNew.c \
 	psFitsFloat.c \
 	psFitsFloatFile.c \
@@ -20,4 +21,5 @@
 	psFitsImage.h \
 	psFitsTable.h \
+	psFitsTableNew.h \
 	psFitsFloat.h \
 	psFitsFloatFile.h \
Index: branches/meh_branches/ppstack_test/psLib/src/fits/psFits.c
===================================================================
--- branches/meh_branches/ppstack_test/psLib/src/fits/psFits.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psLib/src/fits/psFits.c	(revision 33415)
@@ -243,5 +243,5 @@
         (&fptr, name, iomode, &status);
         if (fptr == NULL || status != 0) {
-            psFitsDumpErrors(PS_ERR_IO, _("Could not open file,'%s'"), name);
+	    psFitsDumpErrors(PS_ERR_IO, _("Could not open file,'%s'"), name);
             return NULL;
         }
Index: branches/meh_branches/ppstack_test/psLib/src/fits/psFitsImage.c
===================================================================
--- branches/meh_branches/ppstack_test/psLib/src/fits/psFitsImage.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psLib/src/fits/psFitsImage.c	(revision 33415)
@@ -772,5 +772,5 @@
 		       (options->scaling == PS_FITS_SCALE_LOG_STDEV_BOTH)))) {
 	  fits_write_key_dbl(fits->fd, "BOFFSET", boffset, 12,
-			     "Scaling: ORIGINAL = 10**(TRUE) + BOFFSET)", &status);
+			     "Scaling: UNCOMP = 10**(TRUE) + BOFFSET", &status);
 	}	
 	if (options&&(((options->scaling == PS_FITS_SCALE_ASINH_RANGE)||
@@ -779,10 +779,8 @@
 		       (options->scaling == PS_FITS_SCALE_ASINH_STDEV_NEGATIVE)||
 		       (options->scaling == PS_FITS_SCALE_ASINH_STDEV_BOTH)))) {
+	  fits_write_key_dbl(fits->fd, "BSOFTEN", bsoften, 12,
+			     "Scaling: LINEAR = 2 * BSOFTEN * sinh(TRUE/a)",&status);
 	  fits_write_key_dbl(fits->fd, "BOFFSET", boffset, 12,
-			     "Scaling: NORM = DISK + BOFFSET", &status);
-	  fits_write_key_dbl(fits->fd, "BSOFTEN", bsoften, 12,
-			     "Scaling: TRUE = 2 * BSOFTEN * sinh(NORM/a)",&status);
-
-
+			     "Scaling: UNCOMP = BOFFSET + LINEAR",&status);
 	}	
 
Index: branches/meh_branches/ppstack_test/psLib/src/fits/psFitsTableNew.c
===================================================================
--- branches/meh_branches/ppstack_test/psLib/src/fits/psFitsTableNew.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/psLib/src/fits/psFitsTableNew.c	(revision 33415)
@@ -0,0 +1,679 @@
+/** @file  psFitsTableNew.c
+ *
+ *  @brief Contains Memory efficient functions for accessing  Fits tables
+ *
+ *  @ingroup FileIO
+ *
+ *  @author Bill Sweeney IfA
+ *
+ *
+ *  Copyright 2011 Institute for Astronomy, University of Hawaii
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "psFits.h"
+#include "string.h"
+#include "psError.h"
+
+#include "psImageStructManip.h"
+#include "psMemory.h"
+#include "psString.h"
+#include "psLogMsg.h"
+#include "psTrace.h"
+#include "psVector.h"
+#include "psFitsTable.h"
+#include "psFitsTableNew.h"
+#include "psFitsHeader.h"
+#include "psAbort.h"
+#include "psAssert.h"
+
+// #define MAX_STRING_LENGTH 256  // maximum length string for FITS routines
+
+
+// Check if the FITS file is an empty table
+static bool emptyTableCheck(const psFits *fits // FITS file
+                            )
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    if (psFitsGetExtNum(fits) == 0 && !psFitsMoveExtNum(fits, 1, false)) {
+        psError(PS_ERR_IO, false, "Unable to move to first extension to read table.");
+        return false;
+    }
+
+    int status = 0;                     // CFITSIO status
+    int hdutype;                        // Type of HDU
+    fits_get_hdu_type(fits->fd, &hdutype, &status);
+    if (status) {
+        psFitsError(status, true, "Could not determine the HDU type.");
+        return false;
+    }
+    if (hdutype == IMAGE_HDU) {
+        // It could be an empty table
+        int naxis = 0;                  // Dimensions of image
+        if (fits_get_img_dim(fits->fd, &naxis, &status) != 0) {
+            psFitsError(status, true, "Unable to determine dimension for table.");
+            return false;
+        }
+        if (naxis != 0) {
+            psFitsError(PS_ERR_BAD_FITS, true, "Current FITS HDU is not a table.");
+            return false;
+        }
+        return true;
+    }
+
+    return false;
+}
+
+// Check the FITS file in preparation for reading a table
+static bool readTableCheck(const psFits *fits // FITS file
+                           )
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+
+    if (psFitsGetExtNum(fits) == 0 && !psFitsMoveExtNum(fits, 1, false)) {
+        psError(PS_ERR_IO, false, "Unable to move to first extension to read table.");
+        return false;
+    }
+
+    // check that we are positioned on a table HDU
+    int status = 0;                     // CFITSIO status
+    int hdutype;                        // Type of HDU
+    fits_get_hdu_type(fits->fd, &hdutype, &status);
+    if (status) {
+        psFitsError(status, true, "Could not determine the HDU type.");
+        return false;
+    }
+    if (hdutype != ASCII_TBL && hdutype != BINARY_TBL) {
+        return false;
+    }
+    return true;
+}
+
+void
+freeTable(psFitsTable *table)
+{
+    for (int col = 0; col < table->numCols; col++) {
+        psFitsTableColumn *column = &table->columns[col];
+        psFree(column->name);
+        if (column->type == PS_DATA_STRING) {
+            for (int row = 0; row < table->numRows; row++) {
+                psFree(column->data.str);
+            }
+        } else if (column->type == PS_DATA_VECTOR) {
+            for (int row = 0; row < table->numRows; row++) {
+                psFree(column->data.vec);
+            }
+        }
+        // all of the members in the data union are pointers so just pick S32
+        psFree(column->data.S32);
+    }
+    psFree(table->columns);
+    psFree(table->index);
+}
+
+psFitsTable* psFitsTableAlloc(int numCols, int numRows) {
+    if (numCols < 0) {
+        // empty table
+        psError(PS_ERR_BAD_PARAMETER_VALUE, true,
+                "Can't allocate a psFitsTable with %d columns.", numCols);
+        return NULL;
+    }
+
+    psFitsTable *table = psAlloc(sizeof(psFitsTable));
+    table->index = psMetadataAlloc();
+    table->numRows = numRows;
+    table->numCols = numCols;
+    if (numCols > 0) {
+        table->columns = psAlloc(sizeof(psFitsTableColumn) * numCols);
+        memset(table->columns, 0, sizeof(psFitsTableColumn) * numCols);
+    } else {
+        table->columns = NULL;
+    }
+    psMemSetDeallocator(table, (psFreeFunc) freeTable);
+
+    return table;
+}
+
+psFitsTable* psFitsReadTableNew(const psFits* fits)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, NULL);
+    // PS_ASSERT_INT_NONNEGATIVE(row, NULL);
+
+    if (!readTableCheck(fits)) {
+        if (emptyTableCheck(fits)) {
+            psFitsTable *table = psFitsTableAlloc(0, 0);
+            return table;
+        }
+        psError(PS_ERR_BAD_FITS, true, "Extension is not a table");
+        return NULL;
+    }
+
+    // get the size of the FITS table
+    long numRows;
+    int numCols;
+    int status = 0;
+    fits_get_num_rows(fits->fd, &numRows, &status);
+    fits_get_num_cols(fits->fd, &numCols, &status);
+    if (status) {
+        psFitsError(status, true, "Failed to determine the size of the current HDU table.");
+        return NULL;
+    }
+
+    psTrace("psLib.fits",5,"Table size is %ix%li\n",numCols, numRows);
+
+    psFitsTable *table = psFitsTableAlloc(numCols, numRows);
+
+    int hdutype;                        // Type of HDU: need to distinguish ASCII and binary tables
+    fits_get_hdu_type(fits->fd, &hdutype, &status);
+    if (psFitsError(status, true, "Could not determine the HDU type.")) {
+        return false;
+    }
+
+    int typecode;
+    long repeat;
+    long width;
+    char name[60];
+    for (int col = 1; col <= numCols; col++) {
+        // get the column name
+        double tscal;
+        double tzero;
+        if (hdutype == BINARY_TBL) {
+            fits_get_bcolparms(fits->fd, col, name,
+                               NULL, NULL, NULL, &tscal, &tzero, NULL, NULL, &status);
+        } else {
+            fits_get_acolparms(fits->fd, col, name,
+                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, &status);
+        }
+        // get the column type
+        fits_get_coltype(fits->fd, col, &typecode, &repeat, &width, &status);
+        if (psFitsError(status, true, "Unable to get column type for column %d", col)) {
+            psFree(table);
+            return NULL;
+        }
+
+#define READ_TABLE_ROW_CASE(FITSTYPE, NATIVETYPE, TYPE, VECTYPE) \
+        case FITSTYPE: { \
+                column->type = PS_DATA_##TYPE; \
+                column->data.TYPE = psAlloc(sizeof(ps##TYPE)*table->numRows); \
+                if (repeat == 1) { \
+                    column->elementSize = 1; \
+                    NATIVETYPE *values = (NATIVETYPE *) psAlloc(sizeof(NATIVETYPE) * table->numRows); \
+                    int anynul = 0; \
+                    fits_read_col(fits->fd, FITSTYPE, col, 1, \
+                                  1, numRows, NULL, values, &anynul, &status); \
+                    for (int lcv = 0; lcv < table->numRows; lcv++) { \
+                        column->data.TYPE[lcv] = values[lcv]; \
+                    } \
+                    psTrace("psLib.fits",5,"Column #%i, '%s', is type %i, repeat %li\n", \
+                            col, name, typecode, repeat); \
+                    psFree(values); \
+                } else { \
+                    column->elementSize = repeat; \
+                    column->type = PS_DATA_VECTOR; \
+                    column->vectorType = PS_DATA_##TYPE; \
+                    column->data.vec = psAlloc(sizeof(psVector*) * table->numRows); \
+                    for (int row = 0; row < table->numRows; row++) { \
+                        NATIVETYPE* values = psAlloc(sizeof(NATIVETYPE)*repeat); \
+                        psVector* vec = psVectorAlloc(repeat, PS_TYPE_##VECTYPE); \
+                        int anynul = 0; \
+                        fits_read_col(fits->fd, FITSTYPE, col,row+1, \
+                                  1, repeat, NULL, values, &anynul, &status); \
+                        for (int lcv = 0; lcv < repeat; lcv++) { \
+                            vec->data.VECTYPE[lcv] = values[lcv]; \
+                        } \
+                        column->data.vec[row] = vec; \
+                        psFree(values); \
+                    } \
+                    \
+                } \
+                break; \
+            }
+
+        // Handle cases where the data is "really" unsigned
+        if (typecode == TLONG && tzero == 2147483648) {
+            typecode = TULONG;
+        } else if (typecode == TSHORT && tzero == 32768) {
+            typecode = TUSHORT;
+        }
+
+        // Save the column number for this column in the index metadata
+        psMetadataAddS32(table->index, PS_LIST_TAIL, name, 0, NULL, col-1);
+
+        psFitsTableColumn *column = &table->columns[col-1];
+        // This second copy of the name makes it easily available when writing the table
+        column->name = psStringCopy(name);
+        switch (typecode) {
+            READ_TABLE_ROW_CASE(TBYTE, long, S32, S32);
+            READ_TABLE_ROW_CASE(TSHORT, short, S16, S16);
+            READ_TABLE_ROW_CASE(TUSHORT, unsigned short, U16, U16);
+            READ_TABLE_ROW_CASE(TULONG, unsigned long, U32, U32);
+            READ_TABLE_ROW_CASE(TLONG, long, S32, S32);
+            READ_TABLE_ROW_CASE(TLONGLONG, psS64, S64, S64);
+            READ_TABLE_ROW_CASE(TFLOAT, float, F32, F32);
+            READ_TABLE_ROW_CASE(TDOUBLE, double, F64, F64);
+            READ_TABLE_ROW_CASE(TLOGICAL, bool, BOOL, S8);
+          case TSTRING: {
+              column->data.str = psAlloc(sizeof(psString) * table->numRows);
+              column->type = PS_DATA_STRING;
+              column->elementSize = 0;
+              for (int row = 0; row < table->numRows; row++) {
+                  psString value = psStringAlloc(repeat);
+                  int anynul = 0;
+                  fits_read_col(fits->fd, TSTRING, col,row+1, 1, 1, NULL, &value, &anynul, &status);
+                  psTrace("psLib.fits", 5, "Column #%i, '%s', is type %i, repeat %li, value = %s\n",
+                          col, name, typecode, repeat, value);
+                  if (anynul) {
+                      psFree(value);
+                      value = NULL;
+                  }
+                  column->data.str[row] = value;
+                  int len = strlen(value);
+                  if (len > column->elementSize) {
+                    column->elementSize = len;
+                  }
+              }
+              break;
+          }
+          default:
+            psWarning("Data type (%d) not supportted for column %d", typecode, col);
+            psTrace("psLib.fits", 2, "Column %d was of a non primitive type, %d",
+                    col, typecode);
+        }
+
+        if (psFitsError(status, true, "Failed to retrieve table column %d", col)) {
+            psFree(table);
+            return NULL;
+        }
+    }
+
+    return table;
+}
+
+// return the column number with a given name
+int psFitsTableGetColumnNumber(psFitsTable *table, const char *name)
+{
+    bool mdok;
+    int col = psMetadataLookupS32(&mdok, table->index, name);
+
+    if (!mdok) {
+        psFitsError(mdok, true, "Failed to lookup column index for %s", name);
+        return -1;
+    }
+
+    return col;
+}
+
+#define psFitsTableGetNumTYPE(TYPE, NAME) \
+ps##TYPE psFitsTableGet##NAME(bool *status, psFitsTable *table, int row, const char *name) \
+{ \
+    ps##TYPE value = 0; \
+    if (status) { \
+        *status = true; \
+    } \
+    \
+    if (row >= table->numRows || row < 0) { \
+        if (status) { \
+            *status = false; \
+        } \
+        return 0; \
+    } \
+    bool mdok; \
+    int col = psMetadataLookupS32(&mdok, table->index, name); \
+    if (!mdok) { \
+        if (status) { \
+            *status = false; \
+        } \
+        return 0; \
+    } \
+    if (col < 0) { \
+        if (status) { \
+            *status = false; \
+        } \
+        return 0; \
+    } \
+    psFitsTableColumn *column = &table->columns[col]; \
+    switch (column->type) { \
+        case PS_DATA_S8: \
+        value = (ps##TYPE)column->data.S8[row]; \
+        break; \
+    case PS_DATA_S16: \
+        value = (ps##TYPE)column->data.S16[row]; \
+        break; \
+    case PS_DATA_S32: \
+        value = (ps##TYPE)column->data.S32[row]; \
+        break; \
+    case PS_DATA_S64: \
+        value = (ps##TYPE)column->data.S64[row]; \
+        break; \
+    case PS_DATA_U8: \
+        value = (ps##TYPE)column->data.U8[row]; \
+        break; \
+    case PS_DATA_U16: \
+        value = (ps##TYPE)column->data.U16[row]; \
+        break; \
+    case PS_DATA_U32: \
+        value = (ps##TYPE)column->data.U32[row]; \
+        break; \
+    case PS_DATA_U64: \
+        value = (ps##TYPE)column->data.U64[row]; \
+        break; \
+    case PS_DATA_F32: \
+        value = (ps##TYPE)column->data.F32[row]; \
+        break; \
+    case PS_DATA_F64: \
+        value = (ps##TYPE)column->data.F64[row]; \
+        break; \
+    case PS_DATA_BOOL: \
+        if (column->data.BOOL[row]) { \
+            value = 1; \
+        } \
+        break; \
+    default: \
+        /* if you get to this point, the value is not a number. */ \
+        if (status) {  \
+            *status = false; \
+        }  \
+        break; \
+    } \
+    return value; \
+}
+
+// #define psFitsTableGetNumTYPE(TYPE, NAME)
+
+psFitsTableGetNumTYPE(S8, S8);
+psFitsTableGetNumTYPE(U8, U8);
+psFitsTableGetNumTYPE(S16, S16);
+psFitsTableGetNumTYPE(U16, U16);
+psFitsTableGetNumTYPE(S32, S32);
+psFitsTableGetNumTYPE(U32, U32);
+psFitsTableGetNumTYPE(S64, S64);
+psFitsTableGetNumTYPE(U64, U64);
+psFitsTableGetNumTYPE(F32, F32);
+psFitsTableGetNumTYPE(F64, F64);
+psFitsTableGetNumTYPE(Bool, BOOL);
+
+// remove rows from table that are marked in the mask array as censored.
+bool psFitsTableCensor(psFitsTable *table, bool *censorMask)
+{
+    int newRow = 0;
+    for (int row = 0; row < table->numRows; row++) {
+        if (censorMask[row]) {
+            for (int col = 0; col < table->numCols; col++) {
+                psFitsTableColumn *column = &table->columns[col];
+                if (column->type == PS_DATA_VECTOR) {
+                    psFree(column->data.vec[row]);
+                    column->data.vec[row] = NULL;
+                } else if (column->type == PS_DATA_STRING) {
+                    psFree(column->data.str[row]);
+                    column->data.str[row] = NULL;
+                }
+            }
+        } else {
+            if (newRow < row) {
+                // slide data for this row into new destination
+                for (int col = 0; col < table->numCols; col++) {
+                    psFitsTableColumn *column = &table->columns[col];
+                    switch (column->type) {
+                    case PS_DATA_S16:    
+                        column->data.S16[newRow] = column->data.S16[row];
+                        break;
+                    case PS_DATA_U16:    
+                        column->data.U16[newRow] = column->data.U16[row];
+                        break;
+                    case PS_DATA_S32:    
+                        column->data.S32[newRow] = column->data.S32[row];
+                        break;
+                    case PS_DATA_U32:    
+                        column->data.U32[newRow] = column->data.U32[row];
+                        break;
+                    case PS_DATA_S64:    
+                        column->data.S64[newRow] = column->data.S64[row];
+                        break;
+                    case PS_DATA_F32:    
+                        column->data.F32[newRow] = column->data.F32[row];
+                        break;
+                    case PS_DATA_F64:    
+                        column->data.F64[newRow] = column->data.F64[row];
+                        break;
+                    case PS_DATA_BOOL:
+                        column->data.S8[newRow] = column->data.S8[row];
+                        break;
+                    case    PS_DATA_STRING:
+                        // dest pointer has been freed above or moved
+                        column->data.str[newRow] = column->data.str[row];
+                        // zero source to avoid double free
+                        column->data.str[row] = NULL;
+                        break;
+                    case    PS_DATA_VECTOR:
+                        // dest pointer was freed above or zeroed.
+                        column->data.vec[newRow] = column->data.vec[row];
+                        // zero source to avoid double free
+                        column->data.vec[row] = NULL;
+                        break;
+                    default:
+                        psError(PS_ERR_PROGRAMMING, true, "unexpected column type: %d found", column->type);
+                        return false;
+                    }
+                }
+            }
+            newRow++;
+        }
+    }
+
+    // set the new number of rows
+    table->numRows = newRow;
+
+    return true;
+}
+
+// Get the TFORM character, given a PS type
+static inline char getTForm(psDataType type)
+{
+    switch (type) {
+    case PS_TYPE_U8:
+    case PS_TYPE_S8:
+        return 'B';
+    case PS_TYPE_S16:
+        return 'I';
+    case PS_TYPE_S32:
+        return 'J';
+    case PS_TYPE_U64:
+    case PS_TYPE_S64:
+        return 'K';
+    case PS_TYPE_U16:
+        return 'U';
+    case PS_TYPE_U32:
+        return 'V';
+    case PS_TYPE_F32:
+        return 'E';
+    case PS_TYPE_F64:
+        return 'D';
+    case PS_TYPE_BOOL:
+        return 'L';
+    case PS_DATA_STRING:
+        return 'A';
+    default:
+        psError(PS_ERR_UNKNOWN, true, "Unknown type: %x\n", type);
+        return '?';
+    }
+}
+
+static bool fitsInsertTableNew(psFits* fits,          // FITS file
+                            const psMetadata* header, // FITS header to write
+                            psFitsTable *table,       // internal representation of the table
+                            const char *extname,      // Extension name to give table
+                            bool after,               // Write table after current extension?
+                            bool writeData            // Write data?
+                            )
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_FITS_WRITABLE(fits, false);
+    PS_ASSERT_PTR_NON_NULL(table, false);
+
+    int status = 0;
+
+    long numRows = table->numRows;
+    if (writeData && numRows < 1) {
+        // no table data, what can I do?
+        psError(PS_ERR_BAD_PARAMETER_SIZE, true,
+                _("Can't create a table without any rows."));
+        return false;
+    }
+
+    // Create array of column names and types.
+    psArray *columnNames = psArrayAlloc(table->numCols); // Array of column names, for cfitsio
+    psArray *columnTypes = psArrayAlloc(table->numCols); // Array of column types, for cfitsio
+    for (long i = 0; i < table->numCols; i++) {
+        psFitsTableColumn *column = &table->columns[i];
+        columnNames->data[i] = psMemIncrRefCounter(column->name);
+        psString colType = NULL;        // The FITS column type
+        size_t size = column->elementSize;
+        if (column->type == PS_DATA_VECTOR) {
+            psStringAppend(&colType, "%zd%c", size, getTForm(column->vectorType));
+        } else {
+            psStringAppend(&colType, "%zd%c", size, getTForm(column->type));
+        }
+        columnTypes->data[i] = colType;
+    }
+
+    // Create the table HDU
+    int numHDUs = psFitsGetSize(fits);  // Number of HDUs in file
+    if (numHDUs == 0) {
+        // We're creating the first extension
+        fits_create_tbl(fits->fd,
+                        BINARY_TBL,
+                        writeData ? numRows : 0, // number of rows in table
+                        table->numCols, // number of columns in table
+                        (char**)columnNames->data, // names of the columns
+                        (char**)columnTypes->data, // format of the columns
+                        NULL, // physical unit of columns
+                        NULL, // skip extension name: we set the by hand below
+                        &status);
+    } else {
+        if (!after) {
+            if (psFitsGetExtNum(fits) == 0) {
+                // We're creating a replacement primary HDU.
+                // Set status to signal fits_insert_img to insert a new primary HDU
+                status = PREPEND_PRIMARY;
+            } else {
+                // Move back one to perform an insert after the previous HDU
+                psFitsMoveExtNum(fits, -1, true);
+            }
+        }
+        // Insert the table
+        fits_insert_btbl(fits->fd,
+                         writeData ? numRows : 0, // number of rows in table
+                         table->numCols, // number of columns in table
+                         (char**)columnNames->data, // names of the columns
+                         (char**)columnTypes->data, // format of the columns
+                         NULL, // physical unit of columns
+                         NULL, // skip extension name: we set this by hand below
+                         0, &status);
+    }
+    psFree(columnNames);
+    psFree(columnTypes);
+
+    if (status != 0) {
+        psFitsError(status, true, "Unable to create FITS table with %d columns and %d rows",
+                    table->numCols, table->numRows);
+        return false;
+    }
+
+    // Write header
+    if (header && !psFitsWriteHeader(fits, header)) {
+        psError(PS_ERR_IO, false, "Unable to write FITS header.\n");
+        return false;
+    }
+
+    // write the header, if any.
+    if (extname && strlen(extname) > 0) {
+        if (!psFitsSetExtName(fits, extname)) {
+            psError(PS_ERR_IO, false, "Unable to write FITS header extension name.\n");
+            return false;
+        }
+    }
+
+    if (writeData) {
+        // psMetadataIteratorSet(colSpecsIter, PS_LIST_HEAD);
+        for (long colNum = 1; colNum <= table->numCols; colNum++) {
+            // Note: colNum is unit-indexed, because it's for cfitsio
+            psFitsTableColumn *column = &table->columns[colNum - 1];
+            // colSpec *spec = colSpecItem->data.V; // The specification
+            if (PS_DATA_IS_PRIMITIVE(column->type)) {
+                int fitsDataType;           // Data type for cfitsio
+                p_psFitsTypeToCfitsio(column->type, NULL, NULL, &fitsDataType);
+                fits_write_col(fits->fd,
+                               fitsDataType,
+                               colNum, // column number
+                               1, // first row
+                               1, // first element
+                               table->numRows, // number of rows
+                               column->data.U8, // the data
+                               &status);
+                // psFree(columnData);
+            } else {
+                switch (column->type) {
+                  case PS_DATA_STRING: {
+                      fits_write_col_str(fits->fd, colNum, 1, 1, table->numRows, (char**)column->data.str, &status);
+                      // psFree(strings);
+                      break;
+                  }
+                  case PS_DATA_VECTOR: {
+                      size_t dataSize = PSELEMTYPE_SIZEOF(column->vectorType); // Size of data, in bytes
+                      psVector *columnData = psVectorAlloc(column->elementSize * table->numRows * dataSize, PS_TYPE_U8);
+                      psVectorInit(columnData, 0);
+                      for (long i = 0; i < table->numRows; i++) {
+                          psVector *vector = column->data.vec[i];
+                          memcpy(&columnData->data.U8[i * dataSize * column->elementSize], vector->data.U8,
+                                 vector->n * dataSize);
+                      }
+
+                      int fitsDataType;           // Data type for cfitsio
+                      p_psFitsTypeToCfitsio(column->vectorType, NULL, NULL, &fitsDataType);
+                      fits_write_col(fits->fd, fitsDataType, colNum, 1, 1, table->numRows * column->elementSize,
+                                     columnData->data.U8, &status);
+                      psFree(columnData);
+                      break;
+                  }
+                  default:
+                    psAbort("Should never get here.\n");
+                }
+            }
+
+            // Check error status from writing column
+            if (status != 0) {
+                psFitsError(status, true, "Unable to write column %ld of FITS table", colNum);
+                return false;
+            }
+        }
+    }
+
+    // This forces a re-scan of the header to ensure everything's kosher.  We found this occassionally
+    // necessary for compressed images, which are tables, so perhaps it helps here too.  I guess it can't
+    // hurt.
+    ffrdef(fits->fd, &status);
+    if (psFitsError(status, true, "Could not re-scan HDU.")) {
+        return false;
+    }
+
+    return true;
+}
+
+bool psFitsWriteTableNew(psFits* fits,
+                      const psMetadata* header,
+                      psFitsTable* table,
+                      const char *extname)
+{
+    PS_ASSERT_FITS_NON_NULL(fits, false);
+    PS_ASSERT_FITS_WRITABLE(fits, false);
+    if (!psFitsMoveLast(fits)) {
+        psError(PS_ERR_UNKNOWN, false, "Unable to move to last extension to write table");
+        return false;
+    }
+    bool writeData = table->numRows > 0;
+    return fitsInsertTableNew(fits, header, table, extname, true, writeData);
+}
Index: branches/meh_branches/ppstack_test/psLib/src/fits/psFitsTableNew.h
===================================================================
--- branches/meh_branches/ppstack_test/psLib/src/fits/psFitsTableNew.h	(revision 33415)
+++ branches/meh_branches/ppstack_test/psLib/src/fits/psFitsTableNew.h	(revision 33415)
@@ -0,0 +1,69 @@
+/* @file  psFitsTableNew.h
+ * @brief Contains Fits I/O routines
+ *
+ * @author EAM, PAP, JH
+ * @author Robert DeSonia, MHPCC
+ *
+ * @version $Revision: 1.7 $ $Name: not supported by cvs2svn $
+ * @date $Date: 2007-10-09 02:56:23 $
+ * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+
+#ifndef PS_FITSTABLENEW_H
+#define PS_FITSTABLENEW_H
+
+typedef struct {
+    psString    name;
+    psDataType  type;
+    psDataType  vectorType;             // type inside if Vector
+    int         elementSize;            // 1 for primitives, strlen for strings
+                                        // vector length for vectors
+    // only types supported by fits tables will be implemented
+    union {
+        bool *BOOL;                     ///< boolean data
+        psS8 *S8;                       ///< Signed 8-bit integer data.
+        psS16 *S16;                     ///< Signed 16-bit integer data.
+        psS32 *S32;                     ///< Signed 32-bit integer data.
+        psS64 *S64;                     ///< Signed 64-bit integer data.
+        psU8 *U8;                       ///< Unsigned 8-bit integer data.
+        psU16 *U16;                     ///< Unsigned 16-bit integer data.
+        psU32 *U32;                     ///< Unsigned 32-bit integer data.
+        psU64 *U64;                     ///< Unsigned 64-bit integer data.
+        psF32 *F32;                     ///< Single-precision float data.
+        psF64 *F64;                     ///< Double-precision float data.
+        psString *str;                  ///< string data
+        psVector **vec;                 ///< vector
+    } data;                             ///< Union for data types.
+} psFitsTableColumn;
+
+typedef struct {
+    int numRows;
+    int numCols;
+    psMetadata  *index;
+    psFitsTableColumn *columns;
+} psFitsTable;
+
+// return column number of column with name (-1 if not found)
+int psFitsTableGetColumnNumber(psFitsTable *table, const char *name);
+
+psFitsTable *psFitsReadTableNew(const psFits *fits);
+bool psFitsWriteTableNew(psFits *fits, const psMetadata *header, psFitsTable *table, const char *extname);
+
+// remove rows from table whose entry in the supplied array are true
+bool psFitsTableCensor(psFitsTable *table, bool *rowMask);
+
+// Get value for given row and column name
+psBool psFitsTableGetBool(bool *status, psFitsTable *table, int row, const char* name);
+psS8 psFitsTableGetS8(bool *status, psFitsTable *table, int row, const char* name);
+psU8 psFitsTableGetU8(bool *status, psFitsTable *table, int row, const char* name);
+psS16 psFitsTableGetS16(bool *status, psFitsTable *table, int row, const char* name);
+psU16 psFitsTableGetU16(bool *status, psFitsTable *table, int row, const char* name);
+psS32 psFitsTableGetS32(bool *status, psFitsTable *table, int row, const char* name);
+psU32 psFitsTableGetU32(bool *status, psFitsTable *table, int row, const char* name);
+psS64 psFitsTableGetS64(bool *status, psFitsTable *table, int row, const char* name);
+psU64 psFitsTableGetU64(bool *status, psFitsTable *table, int row, const char* name);
+
+psF32 psFitsTableGetF32(bool *status, psFitsTable *table, int row, const char* name);
+psF64 psFitsTableGetF64(bool *status, psFitsTable *table, int row, const char* name);
+
+#endif
Index: branches/meh_branches/ppstack_test/psLib/src/imageops/psImageConvolve.c
===================================================================
--- branches/meh_branches/ppstack_test/psLib/src/imageops/psImageConvolve.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psLib/src/imageops/psImageConvolve.c	(revision 33415)
@@ -569,5 +569,5 @@
 // Generate normalised Gaussian vector
 #define IMAGE_SMOOTH_GAUSS(TARGET, SIZE, SIGMA, TYPE) \
-    psVector *TARGET = psVectorAlloc(2 * SIZE + 1, PS_TYPE_##TYPE); /* Gaussian */ \
+    TARGET = psVectorAlloc(2 * SIZE + 1, PS_TYPE_##TYPE); /* Gaussian */ \
     double sum = 0.0; /* Sum of Gaussian, for normalisation */ \
     double factor = -0.5/PS_SQR(SIGMA); /* Multiplier for exponential */ \
@@ -587,4 +587,5 @@
     psKernel *kernel = psKernelAlloc(-size, size, -size, size); // Kernel to return
 
+    psVector *gaussNorm = NULL;
     IMAGE_SMOOTH_GAUSS(gaussNorm, size, sigma, F32);
     psF32 *gauss = &gaussNorm->data.F32[size]; // Convenient kernel-like indexing for Gaussian
@@ -602,5 +603,5 @@
 bool psImageSmooth(psImage *image, double  sigma, double  Nsigma)
 {
-    PS_ASSERT_IMAGE_NON_NULL(image, NULL);
+    PS_ASSERT_IMAGE_NON_NULL(image, false);
 
     // relevant terms
@@ -612,4 +613,5 @@
   case PS_TYPE_##TYPE: { \
         /* generate normalized gaussian */ \
+        psVector *gaussnorm = NULL; \
         IMAGE_SMOOTH_GAUSS(gaussnorm, Nrange, sigma, TYPE) \
         ps##TYPE *gauss = &gaussnorm->data.TYPE[Nrange]; \
@@ -622,7 +624,7 @@
                 ps##TYPE *vo = calculation->data.TYPE; \
                 int xMax = PS_MIN(Nrange, Nx); \
-                int convRange = PS_MIN(Nrange + 1, Nx); \
                 /* Smooth first Nrange pixels, with renorm */ \
                 for (int i = 0; i < xMax; i++, vi++, vo++) { \
+		    int convRange = PS_MIN(Nrange + 1, Nx - i);  \
                     ps##TYPE *vr = vi - i; \
                     ps##TYPE *vg = gauss - i; \
@@ -648,6 +650,7 @@
                     } \
                     /* Smooth last Nrange pixels, with renorm */ \
-                    /* XXX does this miss the last column? */ \
-                    for (int i = Nx - Nrange; i < Nx; i++, vi++, vo++) { \
+		    /* if Nx < 2*Nrange, this pass starts at i == Nrange */ \
+		    int xMin = PS_MAX(Nx - Nrange, Nrange); \
+                    for (int i = xMin; i < Nx; i++, vi++, vo++) { \
                         ps##TYPE *vr = vi - Nrange; \
                         ps##TYPE *vg = gauss - Nrange; \
@@ -670,6 +673,6 @@
         /* Smooth the first Nrange pixels, with renorm */ \
         int yMax = PS_MIN(Nrange, Ny); \
-        int convRange = PS_MIN(Nrange + 1, Ny); \
         for (int j = 0; j < yMax; j++) { \
+	    int convRange = PS_MIN(Nrange + 1, Ny - j);		       \
             psVector *calculation = psVectorAlloc(Nx, PS_TYPE_##TYPE); \
             /* Zero the output row */ \
@@ -715,6 +718,8 @@
                 calculation = save; \
             } \
-            /* Smooth last Nrange pixels, with renorm */ \
-            for (int j = Ny - Nrange; j < Ny; j++) { \
+            /* Smooth last Nrange pixels, with renorm */		\
+	    /* if Ny < 2*Nrange, this pass starts at j == Nrange */ \
+	    int yMin = PS_MAX(Ny - Nrange, Nrange); \
+            for (int j = yMin; j < Ny; j++) { \
                 /* save the Nrange-offset output row, then zero */ \
                 memset(calculation->data.TYPE, 0, Nx*sizeof(ps##TYPE)); \
@@ -765,4 +770,161 @@
 }
 
+void psImageSmooth_PreAlloc_DataFree (psImageSmooth_PreAlloc_Data *smdata) {
+    psFree (smdata->resultX);
+    psFree (smdata->resultY);
+    psFree (smdata->kernel);
+}
+
+psImageSmooth_PreAlloc_Data *psImageSmooth_PreAlloc_DataAlloc (psImage *image, double sigma, double Nsigma) {
+
+    psImageSmooth_PreAlloc_Data *smdata = psAlloc(sizeof(psImageSmooth_PreAlloc_Data));
+    psMemSetDeallocator(smdata, (psFreeFunc) psImageSmooth_PreAlloc_DataFree);
+
+    if (!image) {
+	// relevant terms
+	smdata->Nrange = sigma*Nsigma + 0.5;    // Number of pixels either side for convolution kernel
+	smdata->Nx = 0;
+	smdata->Ny = 0;
+	smdata->kernel = NULL;
+	smdata->resultX = NULL;
+	smdata->resultY = NULL;
+	return smdata;
+    }
+
+    // relevant terms
+    smdata->Nrange = sigma*Nsigma + 0.5;    // Number of pixels either side for convolution kernel
+    smdata->Nx = image->numCols;            // Number of columns
+    smdata->Ny = image->numRows;            // Number of rows
+
+    IMAGE_SMOOTH_GAUSS(smdata->kernel, smdata->Nrange, sigma, F32);
+       
+    // use a temp running buffer for X and Y directions.
+    smdata->resultX = psAlloc(smdata->Nx * sizeof(psF32));
+    memset (smdata->resultX, 0, smdata->Nx*sizeof(psF32));
+
+    smdata->resultY = psAlloc(smdata->Ny * sizeof(psF32));
+    memset (smdata->resultY, 0, smdata->Ny*sizeof(psF32));
+
+    return smdata;
+}
+
+// we can use the same DATA structure on multiple images of the same size
+bool psImageSmooth_PreAlloc_F32(psImage *image, psImageSmooth_PreAlloc_Data *smdata)
+{
+    PS_ASSERT_IMAGE_NON_NULL(image, false);
+    // assert on data type
+
+    // relevant terms
+    int Nrange = smdata->Nrange;    // Number of pixels either side for convolution kernel
+    int Nx = smdata->Nx;            // Number of columns
+    int Ny = smdata->Ny;            // Number of rows
+
+    psF32 *gauss = &smdata->kernel->data.F32[Nrange];
+    psF32 *resultX = smdata->resultX;
+    psF32 *resultY = smdata->resultY;
+       
+    /* Smooth in X direction */
+    {
+	for (int j = 0; j < Ny; j++) {
+	    psF32 *vi = image->data.F32[j];
+	    int xMax = PS_MIN(Nrange, Nx);
+	    /* Smooth first Nrange pixels, with renorm */
+	    for (int i = 0; i < xMax; i++, vi++) {
+		int convRange = PS_MIN(Nrange + 1, Nx - i);
+		psF32 *vr = vi - i;
+		psF32 *vg = gauss - i;
+		double g = 0.0;
+		double s = 0.0;
+		for (int n = -i; n < convRange; n++, vr++, vg++) {
+		    s += *vg * *vr;
+		    g += *vg;
+		}
+		resultX[i] = s / g;
+	    }
+	    /* If that's all the pixels we have, then we're done already */
+	    if (Nx > Nrange) {
+		/* Smooth middle pixels; if Nx < 2*Nrange, this pass is skipped */
+		for (int i = Nrange; i < Nx - Nrange; i++, vi++) {
+		    psF32 *vr = vi - Nrange;
+		    psF32 *vg = gauss - Nrange;
+		    double s = 0;
+		    for (int n = -Nrange; n < Nrange + 1; n++, vr++, vg++) {
+			s += *vg * *vr;
+		    }
+		    resultX[i] = s;
+		}
+		/* Smooth last Nrange pixels, with renorm */
+		// if Nx < 2*Nrange, this pass starts at i == Nrange
+		int xMin = PS_MAX(Nx - Nrange, Nrange);
+		for (int i = xMin; i < Nx; i++, vi++) {
+		    psF32 *vr = vi - Nrange;
+		    psF32 *vg = gauss - Nrange;
+		    double g = 0.0;
+		    double s = 0.0;
+		    for (int n = -Nrange; n < Nx - i; n++, vr++, vg++) {
+			s += *vg * *vr;
+			g += *vg;
+		    }
+		    resultX[i] = s / g;
+		}
+	    }
+	    memcpy(image->data.F32[j], resultX, Nx*sizeof(psF32));
+	}
+    }
+       
+    // this section probably hits the cache poorly for large images, but is probably OK for small ones
+    /* Smooth in Y direction */
+    {
+	for (int i = 0; i < Nx; i++) {
+	    int yMax = PS_MIN(Nrange, Ny);
+	    /* Smooth first Nrange pixels, with renorm */
+	    for (int j = 0; j < yMax; j++) {
+		int convRange = PS_MIN(Nrange + 1, Ny - j);
+		psF32 *vg = gauss - j;
+		double g = 0.0;
+		double s = 0.0;
+		for (int n = -j; n < convRange; n++, vg++) {
+		    psF32 vr = image->data.F32[j+n][i];
+		    s += *vg * vr;
+		    g += *vg;
+		}
+		resultY[j] = s / g;
+	    }
+	    /* If that's all the pixels we have, then we're done already */
+	    if (Ny > Nrange) {
+		/* Smooth middle pixels */
+		for (int j = Nrange; j < Ny - Nrange; j++) {
+		    psF32 *vg = gauss - Nrange;
+		    double s = 0;
+		    for (int n = -Nrange; n < Nrange + 1; n++, vg++) {
+			psF32 vr = image->data.F32[j+n][i]; 
+			s += *vg * vr;
+		    }
+		    resultY[j] = s;
+		}
+		/* Smooth last Nrange pixels, with renorm */
+		// if Ny < 2*Nrange, this pass starts at j == Nrange
+		int yMin = PS_MAX(Ny - Nrange, Nrange);
+		for (int j = yMin; j < Ny; j++) {
+		    psF32 *vg = gauss - Nrange;
+		    double g = 0.0;
+		    double s = 0.0;
+		    for (int n = -Nrange; n < Ny - j; n++, vg++) {
+			psF32 vr = image->data.F32[j+n][i];
+			s += *vg * vr;
+			g += *vg;
+		    }
+		    resultY[j] = s / g;
+		}
+	    }
+	    // loop here 
+	    for (int j = 0; j < Ny; j++) {
+		image->data.F32[j][i] = resultY[j];
+	    }
+	}
+    }
+    return true;
+}
+
 static bool imageSmoothMaskPixels(psVector *out, const psImage *image, const psImage *mask,
                                   psImageMaskType maskVal, const psVector *x, const psVector *y,
@@ -849,4 +1011,5 @@
 
     // Generate normalized gaussian
+    psVector *gaussNorm = NULL;
     IMAGE_SMOOTH_GAUSS(gaussNorm, size, sigma, F32);
 
@@ -859,11 +1022,11 @@
 
             psThreadJob *job = psThreadJobAlloc("PSLIB_IMAGE_SMOOTHMASK_PIXELS");
-            psArrayAdd(job->args, 1, out);
-            psArrayAdd(job->args, 1, (psImage*)image);
-            psArrayAdd(job->args, 1, (psImage*)mask);
+            psArrayAdd(job->args, 0, out);
+            psArrayAdd(job->args, 0, (psImage*)image);
+            psArrayAdd(job->args, 0, (psImage*)mask);
             PS_ARRAY_ADD_SCALAR(job->args, maskVal, PS_TYPE_IMAGE_MASK);
-            psArrayAdd(job->args, 1, (psVector*)x);
-            psArrayAdd(job->args, 1, (psVector*)y);
-            psArrayAdd(job->args, 1, gaussNorm);
+            psArrayAdd(job->args, 0, (psVector*)x);
+            psArrayAdd(job->args, 0, (psVector*)y);
+            psArrayAdd(job->args, 0, gaussNorm);
             PS_ARRAY_ADD_SCALAR(job->args, minGauss, PS_TYPE_F32);
             PS_ARRAY_ADD_SCALAR(job->args, size, PS_TYPE_S32);
@@ -876,5 +1039,5 @@
             }
         }
-        if (!psThreadPoolWait(true)) {
+        if (!psThreadPoolWait(true, true)) {
             psError(PS_ERR_UNKNOWN, false, "Error waiting for threads.");
             psFree(gaussNorm);
@@ -974,4 +1137,5 @@
 
     // Generate normalized gaussian
+    psVector *gaussNorm = NULL;
     IMAGE_SMOOTH_GAUSS(gaussNorm, size, sigma, F32);
     const psF32 *gauss = &gaussNorm->data.F32[size]; // Gaussian convolution kernel
@@ -1185,4 +1349,5 @@
 
     // Generate normalized gaussian
+    psVector *gaussNorm = NULL;
     IMAGE_SMOOTH_GAUSS(gaussNorm, size, sigma, F32);
 
@@ -1203,10 +1368,10 @@
                   // allocate a job, construct the arguments for this job
                   psThreadJob *job = psThreadJobAlloc("PSLIB_IMAGE_SMOOTHMASK_SCANROWS");
-                  psArrayAdd(job->args, 1, calculation);
-                  psArrayAdd(job->args, 1, calcMask);
-                  psArrayAdd(job->args, 1, (psImage *) image); // cast away const
-                  psArrayAdd(job->args, 1, (psImage *) mask); // cast away const
+                  psArrayAdd(job->args, 0, calculation);
+                  psArrayAdd(job->args, 0, calcMask);
+                  psArrayAdd(job->args, 0, (psImage *) image); // cast away const
+                  psArrayAdd(job->args, 0, (psImage *) mask); // cast away const
                   PS_ARRAY_ADD_SCALAR(job->args, maskVal,  PS_TYPE_IMAGE_MASK);
-                  psArrayAdd(job->args, 1, gaussNorm);
+                  psArrayAdd(job->args, 0, gaussNorm);
                   PS_ARRAY_ADD_SCALAR(job->args, minGauss, PS_TYPE_F32);
                   PS_ARRAY_ADD_SCALAR(job->args, size,     PS_TYPE_S32);
@@ -1225,5 +1390,5 @@
               }
               // wait here for the threaded jobs to finish (NOP if threading is not active)
-              if (!psThreadPoolWait(true)) {
+              if (!psThreadPoolWait(true, true)) {
                   psError(PS_ERR_UNKNOWN, false, "Unable to smooth image");
                   psFree(calculation);
@@ -1250,9 +1415,9 @@
                   // allocate a job, construct the arguments for this job
                   psThreadJob *job = psThreadJobAlloc("PSLIB_IMAGE_SMOOTHMASK_SCANCOLS");
-                  psArrayAdd(job->args, 1, output);
-                  psArrayAdd(job->args, 1, calculation);
-                  psArrayAdd(job->args, 1, calcMask);
+                  psArrayAdd(job->args, 0, output);
+                  psArrayAdd(job->args, 0, calculation);
+                  psArrayAdd(job->args, 0, calcMask);
                   PS_ARRAY_ADD_SCALAR(job->args, maskVal,  PS_TYPE_IMAGE_MASK);
-                  psArrayAdd(job->args, 1, gaussNorm);
+                  psArrayAdd(job->args, 0, gaussNorm);
                   PS_ARRAY_ADD_SCALAR(job->args, minGauss, PS_TYPE_F32);
                   PS_ARRAY_ADD_SCALAR(job->args, size,     PS_TYPE_S32);
@@ -1272,5 +1437,5 @@
 
               // wait here for the threaded jobs to finish (NOP if threading is not active)
-              if (!psThreadPoolWait(true)) {
+              if (!psThreadPoolWait(true, true)) {
                   psError(PS_ERR_UNKNOWN, false, "Unable to smooth image");
                   psFree(calculation);
@@ -1321,4 +1486,5 @@
 
     /* generate normalized gaussian */
+    psVector *gaussnorm = NULL;
     IMAGE_SMOOTH_GAUSS(gaussnorm, Nrange, sigma, F32);
     psF32 *gauss = &gaussnorm->data.F32[Nrange];
@@ -1572,6 +1738,6 @@
 
             psThreadJob *job = psThreadJobAlloc("PSLIB_IMAGE_CONVOLVE_MASK");
-            psArrayAdd(job->args, 1, conv);
-            psArrayAdd(job->args, 1, (psImage*)mask); // Casting away const to put on arguments
+            psArrayAdd(job->args, 0, conv);
+            psArrayAdd(job->args, 0, (psImage*)mask); // Casting away const to put on arguments
             PS_ARRAY_ADD_SCALAR(job->args, start, PS_TYPE_S32);
             PS_ARRAY_ADD_SCALAR(job->args, stop, PS_TYPE_S32);
@@ -1586,5 +1752,5 @@
             }
         }
-        if (!psThreadPoolWait(true)) {
+        if (!psThreadPoolWait(true, true)) {
             psError(PS_ERR_UNKNOWN, false, "Error waiting for threads.");
             psFree(conv);
@@ -1607,6 +1773,6 @@
 
             psThreadJob *job = psThreadJobAlloc("PSLIB_IMAGE_CONVOLVE_MASK");
-            psArrayAdd(job->args, 1, out);
-            psArrayAdd(job->args, 1, conv);
+            psArrayAdd(job->args, 0, out);
+            psArrayAdd(job->args, 0, conv);
             PS_ARRAY_ADD_SCALAR(job->args, start, PS_TYPE_S32);
             PS_ARRAY_ADD_SCALAR(job->args, stop, PS_TYPE_S32);
@@ -1621,5 +1787,5 @@
             }
         }
-        if (!psThreadPoolWait(true)) {
+        if (!psThreadPoolWait(true, true)) {
             psError(PS_ERR_UNKNOWN, false, "Error waiting for threads.");
             psFree(conv);
Index: branches/meh_branches/ppstack_test/psLib/src/imageops/psImageConvolve.h
===================================================================
--- branches/meh_branches/ppstack_test/psLib/src/imageops/psImageConvolve.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/psLib/src/imageops/psImageConvolve.h	(revision 33415)
@@ -25,4 +25,14 @@
 #define PS_TYPE_KERNEL_DATA F32        ///< the data member to use for kernel image */
 #define PS_TYPE_KERNEL_NAME "psF32"    ///< the data type for kernel as a string */
+
+/// a structure to contain data related to image smoothing with a 1D gauss kernel
+typedef struct {
+    int Nx;
+    int Ny;
+    int Nrange;
+    psF32 *resultX;
+    psF32 *resultY;
+    psVector *kernel;
+} psImageSmooth_PreAlloc_Data;
 
 /// A convolution kernel
@@ -277,4 +287,7 @@
 );
 
+psImageSmooth_PreAlloc_Data *psImageSmooth_PreAlloc_DataAlloc (psImage *image, double sigma, double Nsigma);
+bool psImageSmooth_PreAlloc_F32(psImage *image, psImageSmooth_PreAlloc_Data *smdata);
+
 /// Control threading for image convolution functions
 ///
Index: branches/meh_branches/ppstack_test/psLib/src/imageops/psImageCovariance.c
===================================================================
--- branches/meh_branches/ppstack_test/psLib/src/imageops/psImageCovariance.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psLib/src/imageops/psImageCovariance.c	(revision 33415)
@@ -177,7 +177,7 @@
             if (threaded) {
                 psThreadJob *job = psThreadJobAlloc("PSLIB_IMAGE_COVARIANCE_CALCULATE");
-                psArrayAdd(job->args, 1, out);
-                psArrayAdd(job->args, 1, covar);
-                psArrayAdd(job->args, 1, (psKernel*)kernel); // Casting away const
+                psArrayAdd(job->args, 0, out);
+                psArrayAdd(job->args, 0, covar);
+                psArrayAdd(job->args, 0, (psKernel*)kernel); // Casting away const
                 PS_ARRAY_ADD_SCALAR(job->args, x, PS_TYPE_S32);
                 PS_ARRAY_ADD_SCALAR(job->args, y, PS_TYPE_S32);
@@ -193,5 +193,5 @@
     }
 
-    if (threaded && !psThreadPoolWait(true)) {
+    if (threaded && !psThreadPoolWait(true, true)) {
         psError(PS_ERR_UNKNOWN, false, "Error waiting for threads.");
         return false;
@@ -345,6 +345,6 @@
             if (threaded) {
                 psThreadJob *job = psThreadJobAlloc("PSLIB_IMAGE_COVARIANCE_BIN");
-                psArrayAdd(job->args, 1, out);
-                psArrayAdd(job->args, 1, covar);
+                psArrayAdd(job->args, 0, out);
+                psArrayAdd(job->args, 0, covar);
                 PS_ARRAY_ADD_SCALAR(job->args, bin, PS_TYPE_S32);
                 PS_ARRAY_ADD_SCALAR(job->args, binVal, PS_TYPE_F32);
@@ -360,5 +360,5 @@
         }
     }
-    if (threaded && !psThreadPoolWait(true)) {
+    if (threaded && !psThreadPoolWait(true, true)) {
         psError(PS_ERR_UNKNOWN, false, "Error waiting for threads.");
         return false;
Index: branches/meh_branches/ppstack_test/psLib/src/jpeg/psImageJpeg.c
===================================================================
--- branches/meh_branches/ppstack_test/psLib/src/jpeg/psImageJpeg.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psLib/src/jpeg/psImageJpeg.c	(revision 33415)
@@ -8,4 +8,9 @@
 
 #include <kapa.h>
+#undef free
+// note: when using OHANA_MEMORY, kapa.h includes ohana.h, etc re-define free to use the
+// internal function this file does not use ohana memory management directly (ALLOCATE/free),
+// so we can undef it and allow psMemory.h to poison it
+
 #include "psMemory.h"
 #include "psImage.h"
Index: branches/meh_branches/ppstack_test/psLib/src/pslib_strict.h
===================================================================
--- branches/meh_branches/ppstack_test/psLib/src/pslib_strict.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/psLib/src/pslib_strict.h	(revision 33415)
@@ -41,4 +41,5 @@
 #include "psFitsImage.h"
 #include "psFitsTable.h"
+#include "psFitsTableNew.h"
 #include "psFitsFloat.h"
 #include "psFitsFloatFile.h"
Index: branches/meh_branches/ppstack_test/psLib/src/sys/psMemory.c
===================================================================
--- branches/meh_branches/ppstack_test/psLib/src/sys/psMemory.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psLib/src/sys/psMemory.c	(revision 33415)
@@ -56,5 +56,5 @@
 
 # define USE_SPINLOCK 0
-# define USE_HARDLOCK 0
+# define USE_HARDLOCK 1
 
 # if (USE_SPINLOCK) 
@@ -100,4 +100,6 @@
     PS_MEM_ABORT(__func__, "Unsafe to Continue\n"); \
 }
+
+static bool checkingForCorruption = false;
 
 static bool isBadMemBlock(FILE *output, const psMemBlock *memBlock, const char *file, unsigned int lineo, const char *func);
@@ -337,5 +339,5 @@
 /**** Unique ID for allocated blocks and associated accessor functions
  */
-static psMemId memid = 0;
+static volatile psMemId memid = 0;
 
 /* Return memory ID counter for next block to be allocated
@@ -384,5 +386,5 @@
 // pointer to the last mem block that was allocated.
 // This is the root of the entire memory list
-static psMemBlock *lastMemBlockAllocated = NULL;
+static volatile psMemBlock *lastMemBlockAllocated = NULL;
 
 // set lock on lastMemBlockAllocated
@@ -515,18 +517,32 @@
 	    continue;
 	}
-	if (memBlock->nextBlock && memBlock->nextBlock->inFlight) {
-	    // nope, we need to try again
-	    MUTEX_UNLOCK(&memBlockListMutex);
-	    usleep (BLOCK_SLEEP);
-	    retryLock ++;
-	    continue;
-	}
-	if (memBlock->previousBlock && memBlock->previousBlock->inFlight) {
-	    // nope, we need to try again
-	    MUTEX_UNLOCK(&memBlockListMutex);
-	    usleep (BLOCK_SLEEP);
-	    retryLock ++;
-	    continue;
-	}
+        if (memBlock->nextBlock && memBlock->nextBlock->inFlight) {
+            // we reply on the value of 'inFlight'.  we should crash if this block is corrupted
+            if (memBlock->nextBlock->startblock != P_PS_MEMMAGIC) {
+                PS_MEM_ABORT(__func__, "Unsafe to Continue\n");
+            }
+            if (memBlock->nextBlock->endblock != P_PS_MEMMAGIC) {
+                PS_MEM_ABORT(__func__, "Unsafe to Continue\n");
+            }
+            // nope, we need to try again
+            MUTEX_UNLOCK(&memBlockListMutex);
+            usleep (BLOCK_SLEEP);
+            retryLock ++;
+            continue;
+        }
+        if (memBlock->previousBlock && memBlock->previousBlock->inFlight) {
+            // we reply on the value of 'inFlight'.  we should crash if this block is corrupted
+            if (memBlock->previousBlock->startblock != P_PS_MEMMAGIC) {
+                PS_MEM_ABORT(__func__, "Unsafe to Continue\n");
+            }
+            if (memBlock->previousBlock->endblock != P_PS_MEMMAGIC) {
+                PS_MEM_ABORT(__func__, "Unsafe to Continue\n");
+            }
+            // nope, we need to try again
+            MUTEX_UNLOCK(&memBlockListMutex);
+            usleep (BLOCK_SLEEP);
+            retryLock ++;
+            continue;
+        }
 
 	// the markers are ours!
@@ -641,6 +657,7 @@
     memBlock->refCounter = 1;                   // one user so far
 
-    psMemBlock *last0 = lastMemBlockAllocated;
-    int flight0 = (lastMemBlockAllocated) ? lastMemBlockAllocated->inFlight : 10;
+    // XXX these lines are test lines that potentially access invalid memory
+    // XXX psMemBlock *last0 = lastMemBlockAllocated;
+    // XXX int flight0 = (lastMemBlockAllocated) ? lastMemBlockAllocated->inFlight : 10;
 
     // need exclusive access of the memory block list now...
@@ -649,10 +666,10 @@
     BLOCKLAST_LOCK ();
 
-    psMemBlock *last1 = lastMemBlockAllocated;
-    int flight1 = (lastMemBlockAllocated) ? lastMemBlockAllocated->inFlight : 10;
-
-    if (false) {
-	fprintf (stderr, "last 0 : %lld, last 1 : %lld, flight0: %d, flight1: %d\n", (long long int) last0, (long long int) last1, (int) flight0, (int) flight1);
-    }
+    // XXX psMemBlock *last1 = lastMemBlockAllocated;
+    // XXX int flight1 = (lastMemBlockAllocated) ? lastMemBlockAllocated->inFlight : 10;
+
+    // XXX if (false) {
+    // XXX 	fprintf (stderr, "last 0 : %lld, last 1 : %lld, flight0: %d, flight1: %d\n", (long long int) last0, (long long int) last1, (int) flight0, (int) flight1);
+    // XXX }
 
     // increment the memory id only after we've grabbed the memBlockListMutex this value is
@@ -665,5 +682,5 @@
         lastMemBlockAllocated->previousBlock = memBlock;
     }
-    memBlock->nextBlock = lastMemBlockAllocated;
+    memBlock->nextBlock = (psMemBlock *) lastMemBlockAllocated;
     lastMemBlockAllocated = memBlock;
 
@@ -731,4 +748,7 @@
         }
         fprintf(output, _("\n\tMemory block is corrupted; buffer underflow detected.\n"));
+	if (!checkingForCorruption) {
+	  p_psMemCheckCorruption(__FILE__, __LINE__, __func__, output, false);
+	}
         bad = true;
     }
@@ -740,4 +760,7 @@
         }
         fprintf(output, _("\n\tMemory block is corrupted; buffer overflow detected.\n"));
+	if (!checkingForCorruption) {
+	  p_psMemCheckCorruption(__FILE__, __LINE__, __func__, output, false);
+	}
         bad = true;
     }
@@ -889,12 +912,19 @@
                       psMemBlock ***array,
                       FILE * fd,
-                      bool persistence)
+                      bool persistence,
+		      int maxDisplayedLeaksCount          ///< List at most maxDisplayedLeaksCount (-1 for all)
+		  )
 {
     psS32 nleak = 0;
     psS32 j = 0;
-    psMemBlock *topBlock = lastMemBlockAllocated;
+    psMemBlock *topBlock = (psMemBlock *) lastMemBlockAllocated;
 
     // XXX move this elsewhere?
-    fprintf (stderr, "set %d locks, cleared %d locks, retry on %d locks (memID %ld)\n", setLock, clearLock, retryLock, memid);
+    if (fd != NULL) {
+# if (USE_HARDLOCK)
+# else
+      fprintf (fd, "set %d locks, cleared %d locks, retry on %d locks (memID %ld)\n", setLock, clearLock, retryLock, memid);
+# endif
+    }
 
     // make sure that the memblock list is free of corruption before we crawl
@@ -915,4 +945,22 @@
     for (memBlock = topBlock; memBlock->nextBlock != NULL; memBlock = memBlock->nextBlock) { }
 
+    int maxToDisplay = maxDisplayedLeaksCount;
+    psMemBlock *memBlockBackup = memBlock;
+    if (maxToDisplay == -1 ) {
+      for (; memBlock != NULL; memBlock = memBlock->previousBlock) {
+	if ( (memBlock->refCounter > 0) &&
+	     ( (persistence) || (!persistence && !memBlock->persistent) ) &&
+	     (memBlock->id >= id0)) {
+	  nleak++;
+	}
+      }
+      maxToDisplay=nleak;
+    }
+    if (fd != NULL) {
+      fprintf(fd, "Number of leaks to display: %d\n", maxToDisplay);
+    }
+    memBlock = memBlockBackup;
+
+    nleak=0;
     // iterate through the block list starting with the oldest block
     for (; memBlock != NULL; memBlock = memBlock->previousBlock) {
@@ -924,5 +972,5 @@
 
 	    // only print a max of 500 leaks (make this an argument)
-            if ((nleak < 500) && (fd != NULL)) {
+            if ( (nleak <= maxToDisplay) && (fd != NULL) ) {
                 if (nleak == 1) {
                     fprintf(fd, "# func at (file:line)  ID: X  Ref: X\n");
@@ -1168,4 +1216,12 @@
     #endif
 
+# if (PS_TRACE_ON)    
+    // before freeing a particular block of memory, fill the entire block with FF to poison it
+    // this should trigger any bad reactions early.  this may be costly, so on do it for
+    // unoptimzed builds
+    size_t totalSize = sizeof(psMemBlock) + memBlock->userMemorySize + sizeof(void *);
+    memset ((void *) memBlock, 0xff, totalSize);
+# endif
+
     free(memBlock);
 
@@ -1256,6 +1312,9 @@
     MUTEX_LOCK(&memBlockListMutex);
 
+    // this function calls 'isBadMemBlock', which may in turn call this function : avoid nested calls or we will be stuck forever
+    checkingForCorruption = true;
+
     psS32 nbad = 0;               // number of bad blocks
-    for (psMemBlock *memBlock = lastMemBlockAllocated; memBlock != NULL; memBlock = memBlock->nextBlock) {
+    for (psMemBlock *memBlock = (psMemBlock *) lastMemBlockAllocated; memBlock != NULL; memBlock = memBlock->nextBlock) {
         if (isBadMemBlock(output, memBlock, __FILE__, __LINE__, __func__)) {
             nbad++;
@@ -1269,4 +1328,6 @@
     }
 
+    checkingForCorruption = true;
+
     // release the lock on the memblock list
     MUTEX_UNLOCK(&memBlockListMutex);
@@ -1288,5 +1349,5 @@
 
     // loop through the linked list of memBlocks looking for a matching pointer
-    for (psMemBlock *memBlock = lastMemBlockAllocated; memBlock != NULL; memBlock = memBlock->nextBlock) {
+    for (psMemBlock *memBlock = (psMemBlock *) lastMemBlockAllocated; memBlock != NULL; memBlock = memBlock->nextBlock) {
         if (memBlock == addr) {
             // we found the memBlock
@@ -1339,5 +1400,5 @@
     size_t alloc = 0, persist = 0;
     size_t nalloc = 0, npersist = 0;
-    for (psMemBlock* ptr = lastMemBlockAllocated; ptr != NULL; ptr = ptr->nextBlock) {
+    for (psMemBlock* ptr = (psMemBlock *) lastMemBlockAllocated; ptr != NULL; ptr = ptr->nextBlock) {
         assert (ptr->refCounter > 0);
 
Index: branches/meh_branches/ppstack_test/psLib/src/sys/psMemory.h
===================================================================
--- branches/meh_branches/ppstack_test/psLib/src/sys/psMemory.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/psLib/src/sys/psMemory.h	(revision 33415)
@@ -367,4 +367,11 @@
     bool persistence                   ///< make check across all object even persistent ones
 );
+int psMemCheckLeaks2(
+    psMemId id0,                       ///< don't list blocks with id < id0
+    psMemBlock ***array,               ///< pointer to array of pointers to leaked blocks, or NULL
+    FILE * fd,                         ///< print list of leaks to fd (or NULL)
+    bool persistence,                  ///< make check across all object even persistent ones
+    int maxDisplayedLeaksCount         ///< List at most maxDisplayedLeaksCount (-1 for all)
+);
 #else // ifdef DOXYGEN
 int p_psMemCheckLeaks(
@@ -375,9 +382,12 @@
     psMemBlock ***array,                ///< pointer to array of pointers to leaked blocks, or NULL
     FILE * fd,                          ///< print list of leaks to fd (or NULL)
-    bool persistence                    ///< make check across all object even persistent ones
-);
-#ifndef SWIG
+    bool persistence,                   ///< make check across all object even persistent ones
+    int maxDisplayedLeaksCount          ///< List at most maxDisplayedLeaksCount (-1 for all)
+);
+#ifndef SWIG
+#define psMemCheckLeaks2(id0, array, fd, persistence, maxDisplayedLeaksCount) \
+      p_psMemCheckLeaks(__FILE__, __LINE__, __func__, id0, array, fd, persistence, maxDisplayedLeaksCount)
 #define psMemCheckLeaks(id0, array, fd, persistence) \
-      p_psMemCheckLeaks(__FILE__, __LINE__, __func__, id0, array, fd, persistence)
+      p_psMemCheckLeaks(__FILE__, __LINE__, __func__, id0, array, fd, persistence, 500)
 #endif // ifndef SWIG
 #endif // ifdef DOXYGEN
Index: branches/meh_branches/ppstack_test/psLib/src/sys/psThread.c
===================================================================
--- branches/meh_branches/ppstack_test/psLib/src/sys/psThread.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psLib/src/sys/psThread.c	(revision 33415)
@@ -36,6 +36,6 @@
 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // Mutex for locking threads
 
-static psList *pending = NULL;          // queue of pending jobs
-static psList *done = NULL;             // queue of done jobs
+static volatile psList *pending = NULL; // queue of pending jobs
+static volatile psList *done = NULL;    // queue of done jobs
 static pthread_t *threads = NULL;       // array of the POSIX thread handles
 static psArray *pool = NULL;            // array of defined threads
@@ -91,5 +91,5 @@
 
     job->type = psStringCopy(type);
-    job->args = psArrayAlloc(0);
+    job->args = psArrayAllocEmpty(16);
     job->results = NULL;
     return job;
@@ -116,5 +116,5 @@
             done = psListAlloc(NULL);
         }
-        psListAdd(done, PS_LIST_TAIL, job);
+        psListAdd((psList *)done, PS_LIST_TAIL, job);
         psFree(job);
         return task->function(job);
@@ -125,5 +125,5 @@
         pending = psListAlloc(NULL);
     }
-    psListAdd(pending, PS_LIST_TAIL, job);
+    psListAdd((psList *)pending, PS_LIST_TAIL, job);
     psFree(job);
     psThreadUnlock();
@@ -139,5 +139,5 @@
     }
 
-    psThreadJob *job = psListGetAndRemove(pending, PS_LIST_HEAD);
+    psThreadJob *job = psListGetAndRemove((psList *)pending, PS_LIST_HEAD);
     return job;
 }
@@ -150,5 +150,5 @@
     }
 
-    psThreadJob *job = psListGetAndRemove(done, PS_LIST_HEAD);
+    psThreadJob *job = psListGetAndRemove((psList *) done, PS_LIST_HEAD);
     return job;
 }
@@ -179,4 +179,6 @@
     PS_ASSERT_THREAD_TASK_NON_NULL(task, false);
 
+    // fprintf(stderr, "adding task %s\n", task->type);
+
     if (!tasks) {
         tasks = psHashAlloc(TASK_BUCKETS);
@@ -189,4 +191,5 @@
 {
     PS_ASSERT_STRING_NON_EMPTY(type, false);
+    // fprintf(stderr, "removing task %s\n", type);
 
     return psHashRemove(tasks, type);
@@ -223,4 +226,5 @@
 
         psThreadTask *task = psHashLookup(tasks, job->type); // Task to execute job
+        // fprintf(stderr, "launching job %s\n", job->type);
 #ifdef HAVE_BACKTRACE
         if (!task && bt_buffer) {
@@ -246,5 +250,10 @@
                  "invalid number of arguments to %s (%ld supplied, expected %d)",
                  task->type, job->args->n, task->nArgs);
+        // fprintf(stderr, "    thread for %s %p launching on %p\n", job->type, task->function, self);
+
+        // Run the job's function
         bool status = task->function(job); // Status of executing task
+
+        // fprintf(stderr, "    thread for %s %p finished on %p with status %d\n", job->type, task->function, self, status);
 
         // Put the completed job on the 'done' queue
@@ -253,5 +262,5 @@
             done = psListAlloc(NULL);
         }
-        psListAdd(done, PS_LIST_TAIL, job);
+        psListAdd((psList *)done, PS_LIST_TAIL, job);
         psFree(job);
 
@@ -306,6 +315,7 @@
 
 // call this function after you have added jobs to the queue and
-bool psThreadPoolWait(bool harvest)
-{
+bool psThreadPoolWait(bool harvest, bool harvestOnFailure)
+{
+    // fprintf(stderr, "psThreadPoolWait called with harvest: %d\n", harvest);
     if (!pool || pool->n == 0) {
         // No threads initialised, so everything's done
@@ -326,4 +336,6 @@
 #endif
 
+    // accumulate the number of faulted jobs that we encounter
+    int numFaults = 0;
     while (1) {
         // check for an error
@@ -331,5 +343,5 @@
             psThread *thread = pool->data[i];
             if (thread->fault) {
-                return false;
+                numFaults++;
             }
         }
@@ -354,9 +366,9 @@
             // Nothing in the queue and nothing more to add
             // Ensure everything is harvested, if requested
-            if (harvest) {
+            if (harvest || (numFaults && harvestOnFailure)) {
                 psThreadJobHarvest();
             }
             psThreadUnlock();
-            return true;
+            return numFaults == 0;
         }
 
Index: branches/meh_branches/ppstack_test/psLib/src/sys/psThread.h
===================================================================
--- branches/meh_branches/ppstack_test/psLib/src/sys/psThread.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/psLib/src/sys/psThread.h	(revision 33415)
@@ -116,7 +116,9 @@
 /// Wait for the thread pool to finish
 ///
-/// This function blocks (waits in usleep) until either an error is detected on one of the threads or until ll
-/// threads are idle and no jobs are left on the queue
-bool psThreadPoolWait(bool harvest      // Harvest the jobs from the queue?
+/// This function blocks (waits in usleep) until all  threads are idle and no jobs
+/// are left on the queue
+/// returns success if all jobs return success, otherwise returns false
+bool psThreadPoolWait(bool harvest,         // Harvest the jobs from the queue?
+                      bool harvestOnFailure // If harvest is false, harvest the jobs if a failure is encountered
     );
 
Index: branches/meh_branches/ppstack_test/psLib/src/types/psArray.c
===================================================================
--- branches/meh_branches/ppstack_test/psLib/src/types/psArray.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psLib/src/types/psArray.c	(revision 33415)
@@ -35,5 +35,5 @@
 #include "psSort.h"
 
-#define DEFAULT_ARRAY_ADD 10            // Default number to add to an array when not specified
+#define DEFAULT_ARRAY_ADD 8            // Default number to add to an array when not specified
 
 /*****************************************************************************
Index: branches/meh_branches/ppstack_test/psLib/src/types/psArray.h
===================================================================
--- branches/meh_branches/ppstack_test/psLib/src/types/psArray.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/psLib/src/types/psArray.h	(revision 33415)
@@ -171,5 +171,5 @@
 #define PS_ARRAY_ADD_SCALAR(ARRAY, VALUE, TYPE) { \
       psScalar *scalar = psScalarAlloc(VALUE, TYPE); \
-      psArrayAdd(ARRAY, 1, scalar); \
+      psArrayAdd(ARRAY, 0, scalar); \
       psFree(scalar); \
 }
Index: branches/meh_branches/ppstack_test/psLib/test/imageops/Makefile.am
===================================================================
--- branches/meh_branches/ppstack_test/psLib/test/imageops/Makefile.am	(revision 32161)
+++ branches/meh_branches/ppstack_test/psLib/test/imageops/Makefile.am	(revision 33415)
@@ -17,4 +17,5 @@
 	tap_psImagePixelManip \
 	tap_psImageSmooth \
+	tap_psImageSmooth_PreAlloc \
 	tap_psImageStructManip \
 	tap_psImageConvolve \
Index: branches/meh_branches/ppstack_test/psLib/test/imageops/tap_psImageSmooth.c
===================================================================
--- branches/meh_branches/ppstack_test/psLib/test/imageops/tap_psImageSmooth.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psLib/test/imageops/tap_psImageSmooth.c	(revision 33415)
@@ -24,5 +24,5 @@
 #define TS00_IM_F64             0x00000004
 #define TS00_IM_S32             0x00000008
-#define VERBOSE 0
+#define VERBOSE 1
 
 static psBool testImageSmoothGeneric(
@@ -64,8 +64,4 @@
         }
     }
-    if (VERBOSE) {
-        p_psImagePrint(1, img, "The Smoothed Image");
-    }
-
     if (flags & TS00_IM_F64) {
         if (VERBOSE)
@@ -83,5 +79,4 @@
         }
     }
-
     if (flags & TS00_IM_S32) {
         if (VERBOSE)
@@ -100,4 +95,5 @@
     }
     if (VERBOSE) {
+        if (img) p_psImagePrint(1, img, "The Raw Image");
         printf(" %d columns\n", numCols);
         printf(" %d rows\n", numRows);
@@ -160,6 +156,6 @@
     ok(testImageSmoothGeneric(TS00_IM_F32, NUM_COLS, 1, SIGMA, NSIGMA, true), "testImageSmoothGeneric() successful");
     ok(testImageSmoothGeneric(TS00_IM_F32, 1, 1, SIGMA, NSIGMA, true), "testImageSmoothGeneric() successful");
-    ok(testImageSmoothGeneric(TS00_IM_F64, NUM_COLS, NUM_ROWS, SIGMA, NSIGMA, true), "testImageSmoothGeneric() successful");
-    ok(testImageSmoothGeneric(TS00_IM_S32, NUM_COLS, NUM_ROWS, SIGMA, NSIGMA, false), "testImageSmoothGeneric() successful");
+    // ok(testImageSmoothGeneric(TS00_IM_F64, NUM_COLS, NUM_ROWS, SIGMA, NSIGMA, true), "testImageSmoothGeneric() successful");
+    // ok(testImageSmoothGeneric(TS00_IM_S32, NUM_COLS, NUM_ROWS, SIGMA, NSIGMA, false), "testImageSmoothGeneric() successful");
     ok(testImageSmoothGeneric(TS00_IM_NULL, NUM_COLS, NUM_ROWS, SIGMA, NSIGMA, false), "testImageSmoothGeneric() successful");
 }
Index: branches/meh_branches/ppstack_test/psLib/test/imageops/tap_psImageSmooth_PreAlloc.c
===================================================================
--- branches/meh_branches/ppstack_test/psLib/test/imageops/tap_psImageSmooth_PreAlloc.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/psLib/test/imageops/tap_psImageSmooth_PreAlloc.c	(revision 33415)
@@ -0,0 +1,164 @@
+/** @file  tst_psImageConvolve.c
+ *
+ *  This code will test the psImageSmooth() routine.
+ *
+ *  @author GLG, MHPCC
+ *
+ *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
+ *  @date $Date: 2007-02-27 23:56:12 $
+ *
+ *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
+ */
+#include <stdio.h>
+#include <string.h>
+#include <pslib.h>
+#include "tap.h"
+#include "pstap.h"
+
+#define NUM_ROWS 20
+#define NUM_COLS 20
+#define SIGMA 1.0
+#define NSIGMA 5.0
+#define TS00_IM_NULL            0x00000001
+#define TS00_IM_F32             0x00000002
+#define TS00_IM_F64             0x00000004
+#define TS00_IM_S32             0x00000008
+#define VERBOSE 1
+
+static psBool testImageSmoothGeneric(
+    psU32 flags,
+    psS32 numCols,
+    psS32 numRows,
+    psF64 sigma,
+    psF64 Nsigma,
+    psBool expectedRC)
+{
+    psMemId id = psMemGetId();
+    psBool testStatus = true;
+    psImage *img = NULL;
+
+    if (VERBOSE) {
+        if (expectedRC == false) {
+            printf("This test should generate FALSE.\n");
+        } else {
+            printf("This test should generate TRUE.\n");
+        }
+    }
+
+    if (flags & TS00_IM_NULL) {
+        if (VERBOSE) printf("        using a NULL image\n");
+    }
+
+    if (flags & TS00_IM_F32) {
+        if (VERBOSE) printf("        using a psF32 image\n");
+        img = psImageAlloc(numCols, numRows, PS_TYPE_F32);
+        // Set a checkboard pattern
+        for (psS32 i = 0 ; i < numRows ; i++) {
+            for (psS32 j = 0 ; j < numCols ; j++) {
+                if ((i%2) != (j%2)) {
+                    img->data.F32[i][j] = 1.0;
+                } else {
+                    img->data.F32[i][j] = 0.0;
+                }
+            }
+        }
+    }
+    if (flags & TS00_IM_F64) {
+        if (VERBOSE)
+            printf("        using a psF64 image\n");
+        img = psImageAlloc(numCols, numRows, PS_TYPE_F64);
+        // Set a checkboard pattern
+        for (psS32 i = 0 ; i < numRows ; i++) {
+            for (psS32 j = 0 ; j < numCols ; j++) {
+                if ((i%2) != (j%2)) {
+                    img->data.F64[i][j] = 1.0;
+                } else {
+                    img->data.F64[i][j] = 0.0;
+                }
+            }
+        }
+    }
+    if (flags & TS00_IM_S32) {
+        if (VERBOSE)
+            printf("        using a psS32 image\n");
+        img = psImageAlloc(numCols, numRows, PS_TYPE_S32);
+        // Set a checkboard pattern
+        for (psS32 i = 0 ; i < numRows ; i++) {
+            for (psS32 j = 0 ; j < numCols ; j++) {
+                if ((i%2) != (j%2)) {
+                    img->data.S32[i][j] = 1;
+                } else {
+                    img->data.S32[i][j] = 0;
+                }
+            }
+        }
+    }
+
+    if (VERBOSE) {
+        if (img) p_psImagePrint(1, img, "The Raw Image");
+        printf(" %d columns\n", numCols);
+        printf(" %d rows\n", numRows);
+        printf(" sigma is %.2f\n", sigma);
+        printf(" Nsigma is %.2f\n", Nsigma);
+    }
+
+    psImageSmooth_PreAlloc_Data *smdata = psImageSmooth_PreAlloc_DataAlloc(img, sigma, Nsigma);
+    psBool rc = psImageSmooth_PreAlloc_F32(img, smdata);
+    if (rc == false) {
+        if (expectedRC == true) {
+            diag("TEST ERROR: psImageSmooth returned FALSE\n");
+            testStatus = false;
+        }
+    } else {
+        if (expectedRC == false) {
+            diag("TEST ERROR: psImageSmooth returned TRUE\n");
+            testStatus = false;
+        }
+        if (VERBOSE) {
+            p_psImagePrint(1, img, "The Smoothed Image");
+        }
+
+        if (flags & TS00_IM_F32) {
+            for (psS32 i = 1 ; i < numRows-1 ; i++) {
+                for (psS32 j = 1 ; j < numCols-1 ; j++) {
+                    if ((fabs(img->data.F32[i][j] - 0.5) > 0.1)) {
+                        diag("TEST ERROR: img[%d][%d] was %f, expected 0.5\n",
+                             i, j, img->data.F32[i][j]);
+                        testStatus = false;
+                    }
+                }
+            }
+        }
+
+        if (flags & TS00_IM_F64) {
+            for (psS32 i = 1 ; i < numRows-1 ; i++) {
+                for (psS32 j = 1 ; j < numCols-1 ; j++) {
+                    if ((fabs(img->data.F64[i][j] - 0.5) > 0.1)) {
+                        diag("TEST ERROR: img[%d][%d] was %f, expected 0.5\n",
+                             i, j, img->data.F64[i][j]);
+                        testStatus = false;
+                    }
+                }
+            }
+        }
+    }
+    psFree(img);
+    psFree(smdata);
+    ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
+    return(testStatus);
+}
+
+psS32 main(psS32 argc, char* argv[])
+{
+    psLogSetFormat("HLNM");
+    psLogSetLevel(PS_LOG_INFO);
+    plan_tests(14);
+
+    ok(testImageSmoothGeneric(TS00_IM_F32, NUM_COLS, NUM_ROWS, SIGMA, NSIGMA, true), "testImageSmoothGeneric() successful");
+    ok(testImageSmoothGeneric(TS00_IM_F32, 1, NUM_ROWS, SIGMA, NSIGMA, true), "testImageSmoothGeneric() successful");
+    ok(testImageSmoothGeneric(TS00_IM_F32, NUM_COLS, 1, SIGMA, NSIGMA, true), "testImageSmoothGeneric() successful");
+    ok(testImageSmoothGeneric(TS00_IM_F32, 1, 1, SIGMA, NSIGMA, true), "testImageSmoothGeneric() successful");
+    // ok(testImageSmoothGeneric(TS00_IM_F64, NUM_COLS, NUM_ROWS, SIGMA, NSIGMA, true), "testImageSmoothGeneric() successful");
+    // ok(testImageSmoothGeneric(TS00_IM_S32, NUM_COLS, NUM_ROWS, SIGMA, NSIGMA, false), "testImageSmoothGeneric() successful");
+    ok(testImageSmoothGeneric(TS00_IM_NULL, NUM_COLS, NUM_ROWS, SIGMA, NSIGMA, false), "testImageSmoothGeneric() successful");
+}
Index: branches/meh_branches/ppstack_test/psModules/src/astrom/pmAstrometryWCS.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/astrom/pmAstrometryWCS.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/astrom/pmAstrometryWCS.c	(revision 33415)
@@ -487,4 +487,6 @@
 
     if (fpa->toSky == NULL) {
+	psFree(fpa->toTPA);
+	psFree(fpa->fromTPA);
         fpa->toTPA = psPlaneTransformIdentity (1);
         fpa->fromTPA = psPlaneTransformIdentity (1);
Index: branches/meh_branches/ppstack_test/psModules/src/camera/pmReadoutFake.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/camera/pmReadoutFake.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/camera/pmReadoutFake.c	(revision 33415)
@@ -51,5 +51,5 @@
 
     psF32 *params = model->params->data.F32; // Model parameters
-    psEllipseAxes axes = pmPSF_ModelToAxes(params, MAX_AXIS_RATIO); // Ellipse axes
+    psEllipseAxes axes = pmPSF_ModelToAxes(params, MAX_AXIS_RATIO, model->type); // Ellipse axes
     // Curiously, the minor axis can be larger than the major axis, so need to check.
     if (axes.major >= axes.minor) {
@@ -58,5 +58,5 @@
         axes.major = axes.minor;
     }
-    return pmPSF_AxesToModel(params, axes);
+    return pmPSF_AxesToModel(params, axes, model->type);
 }
 
@@ -314,5 +314,5 @@
                 }
             }
-            if (!psThreadPoolWait(true)) {
+            if (!psThreadPoolWait(true, true)) {
                 psError(PS_ERR_UNKNOWN, false, "Error waiting for threads.");
                 psFree(groups);
Index: branches/meh_branches/ppstack_test/psModules/src/concepts/pmConceptsStandard.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/concepts/pmConceptsStandard.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/concepts/pmConceptsStandard.c	(revision 33415)
@@ -751,13 +751,17 @@
   bool has_video_cell = false;
 
-  if (concept->type != PS_DATA_STRING) {
-    psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Type for %s (%x) is not string\n",
-	    concept->name, concept->type);
-    return NULL;
-  }
-
-  char *Vptr = strchr(concept->data.V,'V');
-  if (Vptr) {
-    has_video_cell = true;
+  if (concept->type == PS_DATA_BOOL) {
+    has_video_cell = concept->data.B;
+  } else { 
+    if (concept->type != PS_DATA_STRING) {
+        psError(PS_ERR_BAD_PARAMETER_TYPE, true, "Type for %s (%x) is not string\n",
+                concept->name, concept->type);
+        return NULL;
+      }
+
+      char *Vptr = strchr(concept->data.V,'V');
+      if (Vptr) {
+        has_video_cell = true;
+      }
   }
 
Index: branches/meh_branches/ppstack_test/psModules/src/detrend/pmBias.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/detrend/pmBias.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/detrend/pmBias.c	(revision 33415)
@@ -154,5 +154,5 @@
     if (threaded) {
         // wait here for the threaded jobs to finish
-        if (!psThreadPoolWait(true)) {
+        if (!psThreadPoolWait(true, true)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to apply bias correction.");
             return false;
Index: branches/meh_branches/ppstack_test/psModules/src/detrend/pmDark.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/detrend/pmDark.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/detrend/pmDark.c	(revision 33415)
@@ -601,5 +601,5 @@
     if (threaded) {
         // wait here for the threaded jobs to finish
-        if (!psThreadPoolWait(true)) {
+        if (!psThreadPoolWait(true, true)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to apply dark.");
             psFree(orders);
Index: branches/meh_branches/ppstack_test/psModules/src/detrend/pmFlatField.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/detrend/pmFlatField.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/detrend/pmFlatField.c	(revision 33415)
@@ -161,5 +161,5 @@
     if (threaded) {
         // wait here for the threaded jobs to finish
-        if (!psThreadPoolWait(true)) {
+        if (!psThreadPoolWait(true, true)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to flat-field image.");
             return false;
Index: branches/meh_branches/ppstack_test/psModules/src/detrend/pmPattern.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/detrend/pmPattern.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/detrend/pmPattern.c	(revision 33415)
@@ -6,4 +6,6 @@
 
 #include "pmPattern.h"
+
+#define PATTERN_ROW_BKG_FIX 1
 
 
@@ -89,4 +91,22 @@
     psImageInit(corr, NAN);
 
+#ifdef PATTERN_ROW_BKG_FIX
+    // CZW: 2011-11-30
+    // Define the vectors to hold the "x" and "y" slope trends.
+    // Briefly, the slope trend in the y-axis is a due to variations in the 0-th order term
+    // of the PATTERN.ROW fit between individual rows across the cell.  Similarly, the 1-st
+    // order term of the PATTERN.ROW fit defines the trend in the x-axis (as that's what we
+    // are fitting with PATTERN.ROW in the first place).  However, the thing we're trying to
+    // fix with PATTERN.ROW is the detector level bias wiggles.  These should be overlaid on
+    // the true sky level.  Therefore, simply applying the PATTERN.ROW correction will
+    // introduce cell-to-cell sky variations as these two trends are removed.  To avoid this,
+    // We store the 0th and 1st order values used for each row, and then fit a polynomial to
+    // these results.  By re-adding these systematic trends back, we can remove the row-to-row
+    // variations without improperly removing the real sky trend.
+    psVector *yaxisData = psVectorAlloc(numRows, PS_TYPE_F32); // Data to fit to the constant term
+    psVector *yaxisMask = psVectorAlloc(numRows, PS_TYPE_VECTOR_MASK); // Mask for rows with no fit
+    psVector *xaxisData = psVectorAlloc(numRows, PS_TYPE_F32); // Data to fit to the linear term
+    psVectorInit(yaxisMask, 0);
+#endif
     for (int y = 0; y < numRows; y++) {
         psVectorInit(clipMask, 0);
@@ -105,4 +125,8 @@
             // Not enough points to fit
             patternMaskRow(ro, y, maskBad);
+#ifdef PATTERN_ROW_BKG_FIX
+	    // Ignore this row in our subsequent fits, because the fit failed.
+	    yaxisMask->data.PS_TYPE_VECTOR_MASK_DATA[y] = 0xFF;
+#endif
             continue;
         }
@@ -111,8 +135,22 @@
             psErrorClear();
             patternMaskRow(ro, y, maskBad);
-            continue;
-        }
-
-        poly->coeff[0] -= background;
+#ifdef PATTERN_ROW_BKG_FIX
+	    // Ignore this row in our subsequent fits, because the fit failed.
+	    yaxisMask->data.PS_TYPE_VECTOR_MASK_DATA[y] = 0xFF;
+#endif
+            continue;
+        }
+#ifndef PATTERN_ROW_BKG_FIX
+ 	poly->coeff[0] -= background;
+#else
+	// Store the results we found for this row.
+	yaxisData->data.F32[y] = poly->coeff[0];
+	xaxisData->data.F32[y] = poly->coeff[1];
+	psTrace("pattern",1,"%d %g %g\n",y,poly->coeff[0],poly->coeff[1]);
+	
+	//	yaxisData->data.F32[y] = 0.0;
+/* 	xaxisData->data.F32[y] = 0.0; */
+	
+#endif
         memcpy(corr->data.F64[y], poly->coeff, (order + 1) * PSELEMTYPE_SIZEOF(PS_TYPE_F64));
         psVector *solution = psPolynomial1DEvalVector(poly, indices); // Solution vector
@@ -121,4 +159,7 @@
             psErrorClear();
             patternMaskRow(ro, y, maskBad);
+#ifdef PATTERN_ROW_BKG_FIX
+	    yaxisMask->data.PS_TYPE_VECTOR_MASK_DATA[y] = 0xFF;
+#endif
             continue;
         }
@@ -126,8 +167,94 @@
         for (int x = 0; x < numCols; x++) {
             image->data.F32[y][x] -= solution->data.F32[x];
+	    psTrace("pattern",5,"A: %d %d %g\n",x,y,solution->data.F32[x]);
         }
         psFree(solution);
     }
 
+#ifdef PATTERN_ROW_BKG_FIX
+    // Put the global trends back that were removed by the PATTERN.ROW correction.
+    // Set up the indices for the polynomial
+    psVector *yaxisIndices = psVectorAlloc(numRows, PS_TYPE_F32);
+    norm = 2.0 / (float)numRows;
+    for (int y = 0; y < numRows; y++) {
+      yaxisIndices->data.F32[y] = y * norm - 1.0;
+      psTrace("psModules.detrend.pattern",10,"%d %f %f\n",y,yaxisIndices->data.F32[y],yaxisData->data.F32[y]);
+    }
+
+    // Fit the trend of the constant term, producing the y-axis global trend
+    psStatsInit(clip);
+    psPolynomial1D *yaxisPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1); // Polynomial to fit.
+    if (!psVectorClipFitPolynomial1D(yaxisPoly,clip,yaxisMask,0xFF,yaxisData, NULL, yaxisIndices)) {
+      psWarning("Unable to fit polynomial to y-axis trend");
+      psErrorClear();
+      // If we've failed, we need to do something, so add back in the background level, and
+      // expect that the final image will have background mismatches.
+      for (int y = 0; y < numRows; y++) {
+	for (int x = 0; x < numCols; x++) {
+	  image->data.F32[y][x] += background;
+	  corr->data.F64[y][0]  -= background;
+	}
+      }
+    }
+    else {
+      psVector *solution = psPolynomial1DEvalVector(yaxisPoly,yaxisIndices);
+      if (!solution) {
+	psWarning("Unable to evaluate polynomial");
+	psErrorClear();
+	// If we've failed, we need to do something, so add back in the background level, and
+	// expect that the final image will have background mismatches.
+	for (int y = 0; y < numRows; y++) {
+	  for (int x = 0; x < numCols; x++) {
+	    image->data.F32[y][x] += background;
+	    corr->data.F64[y][0]  -= background;
+	  }
+	}
+      }
+      else {
+	for (int y = 0; y < numRows; y++) {
+	  for (int x = 0; x < numCols; x++) {
+	    image->data.F32[y][x] += solution->data.F32[y];
+	    corr->data.F64[y][0]  -= solution->data.F32[y];
+	    psTrace("pattern",5,"B: %d %d %g\n",x,y,solution->data.F32[x]);
+	  }
+	}
+      }
+      psFree(solution);
+    }      
+
+    // Fit the trend of the linear term, producing the x-axis global trend
+    // We can use the same mask vector, as the same rows failed the row-fit earlier.
+    psStatsInit(clip);
+    psPolynomial1D *xaxisPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1); // Polynomial to fit.
+    if (!psVectorClipFitPolynomial1D(xaxisPoly,clip,yaxisMask,0xFF,xaxisData, NULL, yaxisIndices)) {
+      psWarning("Unable to fit polynomial to x-axis trend");
+      psErrorClear();
+    }
+    else {
+      psVector *solution = psPolynomial1DEvalVector(xaxisPoly,yaxisIndices);
+      if (!solution) {
+	psWarning("Unable to evaluate polynomial");
+	psErrorClear();
+      }
+      else {
+	for (int y = 0; y < numRows; y++) {
+	  for (int x = 0; x < numCols; x++) {
+	    image->data.F32[y][x] += solution->data.F32[y] * indices->data.F32[x];
+	    corr->data.F64[y][1]  -= solution->data.F32[y] ;
+	    psTrace("pattern",5,"C: %d %d %g %g\n",x,y,solution->data.F32[x],indices->data.F32[x]);
+	  }
+	}
+      }
+      psFree(solution);
+    }
+    psFree(yaxisPoly);
+    psFree(xaxisPoly);
+    psFree(yaxisIndices);
+    psFree(yaxisMask);
+    psFree(yaxisData);
+    psFree(xaxisData);
+    // End PATTERN_ROW_BKG_FIX global trend replacement
+#endif 
+    
     psMetadataAddImage(ro->analysis, PS_LIST_TAIL, PM_PATTERN_ROW_CORRECTION, PS_META_REPLACE,
                        "Pattern row correction", corr);
@@ -382,2 +509,432 @@
 
 
+
+bool pmPatternContinuity(pmChip *chip, const psVector *tweak, psStatsOptions bgStat, psStatsOptions cellStat,
+			 psImageMaskType maskVal, psImageMaskType maskBad, int edgeWidth)
+{
+    PS_ASSERT_PTR_NON_NULL(chip, false);
+    PS_ASSERT_VECTOR_NON_NULL(tweak, false);
+    PS_ASSERT_VECTOR_SIZE(tweak, chip->cells->n, false);
+    PS_ASSERT_VECTOR_TYPE(tweak, PS_TYPE_U8, false);
+
+    int numCells = tweak->n;            // Number of cells
+
+    psVector *meanMask = psVectorAlloc(numCells, PS_TYPE_VECTOR_MASK); // Mask for means
+    psVectorInit(meanMask, 0);
+
+    // Mask bits
+    enum {
+        PM_PATTERN_IGNORE = 0x01,       // Ignore this cell
+        PM_PATTERN_TWEAK  = 0x02,       // Tweak this cell
+        PM_PATTERN_ERROR  = 0x04,       // Error in calculating background
+        PM_PATTERN_ALL    = 0xFF,       // All causes
+    };
+
+    // Count number of cells to tweak
+    int numTweak = 0;                   // Number of cells to tweak
+    int numIgnore = 0;                  // Number of cells to ignore
+    for (int i = 0; i < numCells; i++) {
+        pmCell *cell = chip->cells->data[i]; // Cell of interest
+        if (!cell || !cell->data_exists || !cell->process ||
+            cell->readouts->n == 0 || cell->readouts->n > 1 || !cell->readouts->data[0]) {
+            numIgnore++;
+            meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PM_PATTERN_IGNORE;
+            continue;
+        }
+        if (tweak->data.U8[i]) {
+            meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] = PM_PATTERN_TWEAK;
+            numTweak++;
+        }
+    }
+    if (numTweak == 0) {
+        // Nothing to do
+        psFree(meanMask);
+        return true;
+    }
+
+    // Measure mean of each cell edge, and use that to determine the cell offsets.
+
+    psStats *bgStats = psStatsAlloc(bgStat); // Statistics on background
+    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
+
+    psRegion region = {0,0,0,0};
+
+    /* These images hold the edge data for the OTA structure.  */
+    psImage *A = psImageAlloc(8,8,PS_TYPE_F64); // Top edge
+    psImage *B = psImageAlloc(8,8,PS_TYPE_F64); // Bottom edge
+    psImage *C = psImageAlloc(8,8,PS_TYPE_F64); // Right edge
+    psImage *D = psImageAlloc(8,8,PS_TYPE_F64); // Left edge
+    psImageInit(A,0.0);
+    psImageInit(B,0.0);
+    psImageInit(C,0.0);
+    psImageInit(D,0.0);
+    
+    for (int i = 0; i < numCells; i++) {
+        if (meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PM_PATTERN_IGNORE) {
+            continue;
+        }
+        pmCell *cell = chip->cells->data[i]; // Cell of interest
+        pmReadout *ro = cell->readouts->data[0]; // Readout of interest
+
+        psStatsInit(bgStats);
+
+	// Convert cell iterator i into an xy coordinate on the grid of cells
+	int y = (i % 8);
+	int x = (i - y) / 8;
+	
+	for (int j = 0; j < 4; j++) {
+	  if (j == 0) {  // Region B
+	    region = psRegionSet(0,ro->image->numCols,
+				 0,edgeWidth);
+	  }
+	  else if (j == 1) { // Region A
+	    region = psRegionSet(0,ro->image->numCols,
+				 ro->image->numRows - edgeWidth,ro->image->numRows);
+	  }
+	  else if (j == 2) { // Region D
+	    region = psRegionSet(0,edgeWidth,
+				 0,ro->image->numRows);
+	  }
+	  else if (j == 3) { // Region C
+	    region = psRegionSet(ro->image->numCols - edgeWidth,ro->image->numCols,
+				 0,ro->image->numRows);
+	  }
+	  psImage *subset  = psImageSubset(ro->image,region);
+	  psImage *submask = psImageSubset(ro->mask,region);
+
+	  if (!psImageBackground(bgStats, NULL, subset, submask, maskVal, rng)) {
+            psWarning("Unable to measure background for cell %d on edge %d\n", i, j);
+            psErrorClear();
+            meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] |= PM_PATTERN_ERROR;
+	    if (j == 0)      { B->data.F64[y][x] = NAN; }
+	    else if (j == 1) { A->data.F64[y][x] = NAN; }
+	    else if (j == 2) { C->data.F64[y][x] = NAN; }
+	    else if (j == 3) { D->data.F64[y][x] = NAN; }
+	    psFree(subset);
+	    psFree(submask);
+            continue; // Move on to next edge, as only part of this cell may be a problem
+	  }
+ 
+	  // If the returned value is zero, assume something is wrong.  Do I still need this?
+	  if (psStatsGetValue(bgStats,bgStat) < 1e-6) {
+	    if (j == 0)      { B->data.F64[y][x] = NAN; }
+	    else if (j == 1) { A->data.F64[y][x] = NAN; }
+	    else if (j == 2) { C->data.F64[y][x] = NAN; }
+	    else if (j == 3) { D->data.F64[y][x] = NAN; }
+	  }
+	  // If we have an error for this cell/edge, make sure we mask the value
+	  if (meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PM_PATTERN_ERROR) {
+	    if (j == 0)      { B->data.F64[y][x] = NAN; }
+	    else if (j == 1) { A->data.F64[y][x] = NAN; }
+	    else if (j == 2) { C->data.F64[y][x] = NAN; }
+	    else if (j == 3) { D->data.F64[y][x] = NAN; }
+	  }
+	  else { // Set the value to match what we got from the edge box.
+	    if (j == 0)      { B->data.F64[y][x] = psStatsGetValue(bgStats,bgStat); }
+	    else if (j == 1) { A->data.F64[y][x] = psStatsGetValue(bgStats,bgStat); }
+	    else if (j == 2) { C->data.F64[y][x] = psStatsGetValue(bgStats,bgStat); }
+	    else if (j == 3) { D->data.F64[y][x] = psStatsGetValue(bgStats,bgStat); }
+	  }
+
+	  for (int u = 0; u < subset->numCols; u++) {
+	    for (int v = 0; v < subset->numRows; v++) {
+	      psTrace("psModules.detrend.cont",10,"BOX: %d %d (%d %d) (%d %d) %f %d",
+		      i,j,x,y,u,v,subset->data.F32[v][u],submask->data.PS_TYPE_IMAGE_MASK_DATA[v][u]);
+	    }
+	  }	  
+	  
+	  psFree(subset);
+	  psFree(submask);
+
+	}
+	psTrace("psModules.detrend.cont",5, "OTA: %d (%d %d) A: %f B: %f C: %f D: %f",
+		i,x,y,
+		A->data.F64[y][x],B->data.F64[y][x],C->data.F64[y][x],D->data.F64[y][x]);		
+    }
+    psFree(bgStats);
+    psFree(rng);
+
+    // We've now allocated all the edge values, so we can now minimize the offsets.
+    // This involves solving the equation A x = b, where
+    // A is the (64x64 for GPC1) matrix containing the edges that match for each cell
+    // x is the solution vector
+    // b is the combination of offsets across each cell boundary for each cell.
+    // Below "XX" is used as the matrix A, and "solution" is used as both b and x
+    //   (due to the way psMatrixLUSolve operates).
+    psVector *solution = psVectorAlloc(64,PS_TYPE_F64);
+    psImage  *XX       = psImageAlloc(64,64,PS_TYPE_F64);
+    psVectorInit(solution,0.0);
+    psImageInit(XX,0.0);
+    
+    for (int i = 0; i < numCells; i++) {
+      // Accumulate all the possible edge differences we can for this cell.
+      // As we do so, make a note of the correlations by incrementing the element of the matrix.
+      int y = (i % 8);
+      int x = (i - y) / 8;
+      int j;
+      double critical_value = 0.0;
+      if (meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PM_PATTERN_IGNORE) {
+	continue;
+      }
+      if (x + 1 < 8) {  // We have a neighbor adjacent in the +x direction
+	j = 8 * (x + 1) + y; // Determine that neighbor's index
+	if (fabs(C->data.F64[y][x]) > fabs(D->data.F64[y][x+1])) {
+	  critical_value = 2.0 * fabs(D->data.F64[y][x+1]);
+	}
+	else {
+	  critical_value = 2.0 * fabs(C->data.F64[y][x]);
+	}
+	if (critical_value < 25) { critical_value = 25; }
+	psTrace("psModules.detrend.cont",5,"CmD %d %d %d %d %g %g %g", // diagnostic
+		i,x,y,j,
+		C->data.F64[y][x],
+		D->data.F64[y][x+1],
+		critical_value
+		);
+	if (!(meanMask->data.PS_TYPE_VECTOR_MASK_DATA[j] & PM_PATTERN_IGNORE)&&  // If there are no errors with the neighbor,
+	    (isfinite(C->data.F64[y][x]))&&(isfinite(D->data.F64[y][x+1]))&&     // and all edges have valid values,
+	    (fabs(C->data.F64[y][x] - D->data.F64[y][x+1]) < critical_value)     // and there are no large discontinuities,
+	    ) {    
+	  solution->data.F64[i] += C->data.F64[y][x] - D->data.F64[y][x+1];     // Take the difference
+	  XX->data.F64[i][i] += 1;                                              // increment our relation with ourself
+	  XX->data.F64[i][j] += -1;                                             // decrement our relation with the neighbor
+	}
+      }
+      if (x - 1 > -1) { // etc.
+	j = 8 * (x - 1) + y;
+	if (fabs(C->data.F64[y][x-1]) > fabs(D->data.F64[y][x])) {
+	  critical_value = 2.0 * fabs(D->data.F64[y][x]);
+	}
+	else {
+	  critical_value = 2.0 * fabs(C->data.F64[y][x-1]);
+	}
+	if (critical_value < 25) { critical_value = 25; }
+	psTrace("psModules.detrend.cont",5,"DmC %d %d %d %d %g %g %g",
+		i,x,y,j,
+		D->data.F64[y][x],
+		C->data.F64[y][x-1],
+		critical_value
+		);
+
+	if (!(meanMask->data.PS_TYPE_VECTOR_MASK_DATA[j] & PM_PATTERN_IGNORE)&&
+	    (isfinite(D->data.F64[y][x]))&&(isfinite(C->data.F64[y][x-1]))&&
+	    (fabs(D->data.F64[y][x] - C->data.F64[y][x-1]) < critical_value)
+	    ) {
+	  solution->data.F64[i] += D->data.F64[y][x] - C->data.F64[y][x-1];
+	  XX->data.F64[i][i] += 1;
+	  XX->data.F64[i][j] += -1;
+	}
+      }
+      if (y + 1 < 8) {
+	j = 8 * x + (y + 1);
+	psTrace("psModules.detrend.cont",5,"AmB %d %d %d %d %g %g",
+		i,x,y,j,
+		A->data.F64[y][x],
+		B->data.F64[y+1][x]
+		);
+	if (fabs(A->data.F64[y][x]) > fabs(B->data.F64[y+1][x])) {
+	  critical_value = 2.0 * fabs(B->data.F64[y+1][x]);
+	}
+	else {
+	  critical_value = 2.0 * fabs(A->data.F64[y][x]);
+	}
+	if (critical_value < 25) { critical_value = 25; }
+	if (!(meanMask->data.PS_TYPE_VECTOR_MASK_DATA[j] & PM_PATTERN_IGNORE)&&
+	    (isfinite(A->data.F64[y][x]))&&(isfinite(B->data.F64[y+1][x]))&&
+	    (fabs(A->data.F64[y][x] - B->data.F64[y+1][x]) < critical_value)
+	    ) {
+	  solution->data.F64[i] += A->data.F64[y][x] - B->data.F64[y+1][x];
+	  XX->data.F64[i][i] += 1;
+	  XX->data.F64[i][j] += -1;
+	}
+      }
+      if (y - 1 > -1) {
+	j = 8 * x +  (y - 1);
+	psTrace("psModules.detrend.cont",5,"BmA %d %d %d %d %g %g",
+		i,x,y,j,
+		B->data.F64[y][x],
+		A->data.F64[y-1][x]
+		);
+	if (fabs(A->data.F64[y-1][x]) > fabs(B->data.F64[y][x])) {
+	  critical_value = 2.0 * fabs(B->data.F64[y][x]);
+	}
+	else {
+	  critical_value = 2.0 * fabs(A->data.F64[y-1][x]);
+	}
+	if (critical_value < 25) { critical_value = 25; }
+	if (!(meanMask->data.PS_TYPE_VECTOR_MASK_DATA[j] & PM_PATTERN_IGNORE)&&
+	    (isfinite(B->data.F64[y][x]))&&(isfinite(A->data.F64[y-1][x]))&&
+	    (fabs(B->data.F64[y][x] - A->data.F64[y-1][x]) < critical_value)
+	    ) {
+	  solution->data.F64[i] += B->data.F64[y][x] - A->data.F64[y-1][x];
+	  XX->data.F64[i][i] += 1;
+	  XX->data.F64[i][j] += -1;
+	}
+      }
+    }
+    double max_XX = 0;
+    double solution_V = 0;
+    int i_peak = -1;
+    for (int i = 0; i < numCells; i++) { // If any cells have no value of themself, set the matrix to 1.0.
+      if (XX->data.F64[i][i] == 0.0) {
+	XX->data.F64[i][i] = 1.0;
+      }
+      if (XX->data.F64[i][i] > max_XX) {
+	max_XX = XX->data.F64[i][i];
+	solution_V = solution->data.F64[i];
+	i_peak = i;
+      }
+    }
+    psTrace("psModules.detrend.cont",5,"fixed point: %d %g\n",
+	    i_peak,solution_V);
+
+    for (int i = 0; i < numCells; i++) {
+/*        if (!((XX->data.F64[i][i] == 1.0)&& */
+/*  	    (solution->data.F64[i] == 0.0))) { */
+	solution->data.F64[i] -= solution_V;
+	if (i != i_peak) {
+	  for (int j = 0; j < numCells; j++) {
+	    XX->data.F64[i][j] -= XX->data.F64[i_peak][j];
+	  }
+	}
+/*        } */
+    }
+    for (int i = 0; i < numCells; i++) {
+      XX->data.F64[i_peak][i] = 0.0;
+    }
+    XX->data.F64[i_peak][i_peak] = 1.0;
+    
+    
+#if (1)
+    for (int i = 0; i < numCells; i++) { // print matrix A
+      psTrace("psModules.detrend.cont",5,"A: %3d % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f % 2.0f",
+	      i,
+	      XX->data.F64[i][0],	      XX->data.F64[i][1],	      XX->data.F64[i][2],	      XX->data.F64[i][3],
+	      XX->data.F64[i][4],	      XX->data.F64[i][5],	      XX->data.F64[i][6],	      XX->data.F64[i][7],
+	      XX->data.F64[i][8],	      XX->data.F64[i][9],	      XX->data.F64[i][10],	      XX->data.F64[i][11],
+	      XX->data.F64[i][12],	      XX->data.F64[i][13],	      XX->data.F64[i][14],	      XX->data.F64[i][15],
+	      XX->data.F64[i][16],	      XX->data.F64[i][17],	      XX->data.F64[i][18],	      XX->data.F64[i][19],
+	      XX->data.F64[i][20],	      XX->data.F64[i][21],	      XX->data.F64[i][22],	      XX->data.F64[i][23],
+	      XX->data.F64[i][24],	      XX->data.F64[i][25],	      XX->data.F64[i][26],	      XX->data.F64[i][27],
+	      XX->data.F64[i][28],	      XX->data.F64[i][29],	      XX->data.F64[i][30],	      XX->data.F64[i][31],
+	      XX->data.F64[i][32],	      XX->data.F64[i][33],	      XX->data.F64[i][34],	      XX->data.F64[i][35],
+	      XX->data.F64[i][36],	      XX->data.F64[i][37],	      XX->data.F64[i][38],	      XX->data.F64[i][39],
+	      XX->data.F64[i][40],	      XX->data.F64[i][41],	      XX->data.F64[i][42],	      XX->data.F64[i][43],
+	      XX->data.F64[i][44],	      XX->data.F64[i][45],	      XX->data.F64[i][46],	      XX->data.F64[i][47],
+	      XX->data.F64[i][48],	      XX->data.F64[i][49],	      XX->data.F64[i][50],	      XX->data.F64[i][51],
+	      XX->data.F64[i][52],	      XX->data.F64[i][53],	      XX->data.F64[i][54],	      XX->data.F64[i][55],
+	      XX->data.F64[i][56],	      XX->data.F64[i][57],	      XX->data.F64[i][58],	      XX->data.F64[i][59],
+	      XX->data.F64[i][60],	      XX->data.F64[i][61],	      XX->data.F64[i][62],	      XX->data.F64[i][63]
+	      );
+    }
+
+    for (int i = 0; i < numCells; i++) { // print vector b
+      psTrace("psModules.detrend.cont",5,"b: %d %f",
+	      i,
+	      solution->data.F64[i]
+	      );
+    }
+#endif    
+    
+    // Solve the Ax=b equation
+    //    psMatrixLUSolve(XX,solution);
+    psMatrixGJSolve(XX,solution);
+#if (1)
+    for (int i = 0; i < numCells; i++) { // print vector b
+      psTrace("psModules.detrend.cont",5,"x: %d %f",
+	      i,
+	      solution->data.F64[i]
+	      );
+    }
+#endif
+    
+    /* old code to remove the minimum solution value from the set, to give a "minimal set of offsets." Mathematically unnecessary. */
+/*     double min = 99e99; */
+/*     for (int i = 0; i < numCells; i++) { */
+/*       if (solution->data.F64[i] < min) { */
+/* 	min = solution->data.F64[i]; */
+/*       } */
+/*       psTrace("psModules.detrend.cont",5,"x: %d %f %f ", */
+/* 	      i, */
+/* 	      solution->data.F64[i],min */
+/* 	      ); */
+/*     } */
+/*     for (int i = 0; i < numCells; i++) { */
+/* 	if (solution->data.F64[i] != 0.0) { */
+/* 	  solution->data.F64[i] -= min; */
+/* 	} */
+/*     } */
+
+    // Cleanup
+    psFree(XX);
+    psFree(A);
+    psFree(B);
+    psFree(C);
+    psFree(D);
+
+    // Correct cells based on the offsets calculated, and store the result in the analysis metadata.
+    for (int i = 0; i < numCells; i++) {
+        if (meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PM_PATTERN_IGNORE) {
+            continue;
+        }
+        if (!(meanMask->data.PS_TYPE_VECTOR_MASK_DATA[i] & PM_PATTERN_TWEAK)) {
+            continue;
+        }
+        pmCell *cell = chip->cells->data[i]; // Cell of interest
+        pmReadout *ro = cell->readouts->data[0]; // Readout of interest
+
+        float correction = solution->data.F64[i];
+        const char *cellName = psMetadataLookupStr(NULL, cell->concepts, "CELL.NAME"); // Name of cell
+        psLogMsg("psModules.detrend", PS_LOG_DETAIL, "Correcting background of cell %s by %f",
+                 cellName, correction);
+        psBinaryOp(ro->image, ro->image, "-", psScalarAlloc(correction, PS_TYPE_F32));
+        psMetadataAddF32(ro->analysis, PS_LIST_TAIL, PM_PATTERN_CELL_CORRECTION, PS_META_REPLACE,
+                         "Pattern cell correction solution", correction);
+    }
+
+    psFree(solution);
+    psFree(meanMask);
+
+    return true;
+}
+
+bool pmPatternContinuityApply(pmReadout *ro, psImageMaskType maskBad)
+{
+    PM_ASSERT_READOUT_NON_NULL(ro, false);
+    PM_ASSERT_READOUT_IMAGE(ro, false);
+
+    bool mdok;                          // Status of MD lookup
+    float corr = psMetadataLookupF32(&mdok, ro->analysis, PM_PATTERN_CELL_CORRECTION); // Correction to apply
+    if (!mdok) {
+        // No correction to apply
+        return true;
+    }
+
+    psImage *image = ro->image, *mask = ro->mask; // Image and mask of interest
+    int numCols = image->numCols, numRows = image->numRows; // Size of image
+
+    if (!isfinite(corr)) {
+        for (int y = 0; y < numRows; y++) {
+            for (int x = 0; x < numCols; x++) {
+                image->data.F32[y][x] = NAN;
+            }
+        }
+        if (mask) {
+            for (int y = 0; y < numRows; y++) {
+                for (int x = 0; x < numCols; x++) {
+                    mask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] |= maskBad;
+                }
+            }
+        }
+    } else {
+        for (int y = 0; y < numRows; y++) {
+            for (int x = 0; x < numCols; x++) {
+                image->data.F32[y][x] += corr;
+            }
+        }
+    }
+
+    return true;
+}
+
+
Index: branches/meh_branches/ppstack_test/psModules/src/detrend/pmPattern.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/detrend/pmPattern.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/detrend/pmPattern.h	(revision 33415)
@@ -54,4 +54,21 @@
                         );
 
+/// Fix the background on cells known to be troublesome
+bool pmPatternContinuity(
+    pmChip *chip,                       ///< Chip to correct
+    const psVector *tweak,              ///< U8 vector indicating whether to tweak the corresponding cell
+    psStatsOptions bgStat,              ///< Statistic to use for background measurement
+    psStatsOptions cellStat,            ///< Statistic to use for combination of cell background measurements
+    psImageMaskType maskVal,            ///< Mask value to use
+    psImageMaskType maskBad,            ///< Mask value to give bad pixels
+    int edgeWidth                       ///< Size of box to use
+    );
+
+/// Apply previously measured cell pattern correction
+bool pmPatternContinuityApply(pmReadout *ro,          ///< Readout to correct
+                        psImageMaskType maskBad ///< Mask value to give bad pixels
+                        );
+
+
 
 /// @}
Index: branches/meh_branches/ppstack_test/psModules/src/detrend/pmShutterCorrection.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/detrend/pmShutterCorrection.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/detrend/pmShutterCorrection.c	(revision 33415)
@@ -805,5 +805,5 @@
         if (threaded) {
             // wait here for the threaded jobs to finish
-            if (!psThreadPoolWait(true)) {
+            if (!psThreadPoolWait(true, true)) {
                 psError(PS_ERR_UNKNOWN, false, "Unable to apply shutter correction.");
                 psFree(shutterImage);
Index: branches/meh_branches/ppstack_test/psModules/src/imcombine/pmPSFEnvelope.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/imcombine/pmPSFEnvelope.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/imcombine/pmPSFEnvelope.c	(revision 33415)
@@ -36,5 +36,5 @@
 
 
-//#define TESTING                         // Enable test output
+#define TESTING                         // Enable test output
 // #define PEAK_NORM                       // Normalise peaks?
 #define PEAK_FLUX 1.0e4                 // Peak flux for each source
@@ -380,5 +380,5 @@
 
         // measure the source moments: tophat windowing, no pixel S/N cutoff
-        if (!pmSourceMoments(source, maxRadius, 0.0, 0.0, 0.0, maskVal)) {
+        if (!pmSourceMoments(source, maxRadius, 0.25*maxRadius, 0.0, 0.0, maskVal)) {
             // Can't do anything about it; limp along as best we can
             psErrorClear();
Index: branches/meh_branches/ppstack_test/psModules/src/imcombine/pmStackReject.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/imcombine/pmStackReject.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/imcombine/pmStackReject.c	(revision 33415)
@@ -313,5 +313,5 @@
     }
 
-    if (!psThreadPoolWait(false)) {
+    if (!psThreadPoolWait(false, true)) {
         psError(psErrorCodeLast(), false, "Unable to grow bad pixels.");
         psFree(source);
Index: branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtraction.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtraction.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtraction.c	(revision 33415)
@@ -905,5 +905,5 @@
         }
     }
-    if (!psThreadPoolWait(true)) {
+    if (!psThreadPoolWait(true, true)) {
         psError(psErrorCodeLast(), false, "Error waiting for threads.");
         return false;
@@ -1019,5 +1019,13 @@
 		}
 	    }
-	    pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "red");
+
+	    psString ds9text = NULL;
+
+	    psStringAppend(&ds9text, "flux: %.0f ", match->fluxes->data.F32[i]);
+	    psStringAppend(&ds9text, "chi2: %.0f ", match->chisq->data.F32[i]);
+
+	    pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "red", ds9text);
+
+	    psFree(ds9text);	   
 
 	    // Set stamp for replacement
@@ -1041,5 +1049,15 @@
 	} else {
 	    numGood++;
-	    pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green");
+
+	    //MEH bad way to do this - should also add to rej/red one as well
+	    // -- is match from subQuality what want?
+	    psString ds9text = NULL;
+
+	    psStringAppend(&ds9text, "flux: %.0f ", match->fluxes->data.F32[i]);
+	    psStringAppend(&ds9text, "chi2: %.0f ", match->chisq->data.F32[i]);
+
+	    pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green", ds9text);
+
+	    psFree(ds9text);	   
         }
     }
@@ -1427,5 +1445,5 @@
     }
 
-    if (!psThreadPoolWait(false)) {
+    if (!psThreadPoolWait(false, true)) {
         psError(psErrorCodeLast(), false, "Error waiting for threads.");
         return false;
@@ -1487,4 +1505,5 @@
             }
         }
+	psFree(out1->covariance);
         out1->covariance = psImageCovarianceAverage(covars);
         psFree(covars);
@@ -1527,4 +1546,5 @@
             }
         }
+	psFree(out2->covariance);
         out2->covariance = psImageCovarianceAverage(covars);
         psFree(covars);
Index: branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionEquation.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionEquation.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionEquation.c	(revision 33415)
@@ -796,6 +796,6 @@
     stamp->normSquare1 = normSquare1;
     stamp->normSquare2 = normSquare2;
-
-    // psLogMsg ("psModules.imcombine", PS_LOG_DETAIL, "normValue: %f %f %f  (%f %f)\n", normI1, normI2, stamp->norm, normSquare1, normSquare2);
+    
+    //psLogMsg ("psModules.imcombine", PS_LOG_DETAIL, "normValue: %f %f %f  (%f %f)\n", normI1, normI2, stamp->norm, normSquare1, normSquare2);
 
     return true;
@@ -958,5 +958,5 @@
     }
 
-    if (!psThreadPoolWait(true)) {
+    if (!psThreadPoolWait(true, true)) {
         psError(psErrorCodeLast(), false, "Error waiting for threads.");
         return false;
@@ -1037,8 +1037,8 @@
                 (void)psBinaryOp(sumVector, sumVector, "+", stamp->vector);
 
-                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green");
+                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green", "");
                 numStamps++;
             } else if (stamp->status == PM_SUBTRACTION_STAMP_REJECTED) {
-                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "red");
+                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "red", "");
             }
         }
@@ -1139,8 +1139,8 @@
 		normSquare2 += stamp->normSquare2;
 
-                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green");
+                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "green", "");
                 numStamps++;
             } else if (stamp->status == PM_SUBTRACTION_STAMP_REJECTED) {
-                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "red");
+                pmSubtractionStampPrint(ds9, stamp->x, stamp->y, stamps->footprint, "red", "");
             }
         }
@@ -1382,5 +1382,8 @@
 	float Myy = fluxY2 / fluxC1;
 
+        //MEH uncomment and update
 	// fprintf (stderr, "conv1, flux2: %f, Mx: %f, My: %f, Mxx: %f, Myy: %f, chisq: %f, npix: %d\n", flux2, Mx, My, Mxx, Myy, chisq, npix);
+        //fprintf (stderr, "conv1, flux2: %f, fluxC1: %f, Mxx: %f, Myy: %f, chisqR: %f, npix: %d\n", flux2, fluxC1, Mxx, Myy, chisqR, npix);
+
 	moment += Mxx + Myy;
     }
@@ -1554,4 +1557,7 @@
                     residual->kernel[y][x] = difference->kernel[y][x] - convolved1->kernel[y][x];
 		    convolved1->kernel[y][x] += source->kernel[y][x] * norm;
+                    //MEH 
+                    //psLogMsg("psModules.imcombine", PS_LOG_INFO, "xy: %d %d, norm: %6.3f, bg: %6.3f, difference: %6.3f, target: %6.3f, source: %6.3f, residual: %6.3f, convolved1: %6.3f \n",x,y,norm,background,difference->kernel[y][x],target->kernel[y][x],source->kernel[y][x],residual->kernel[y][x],convolved1->kernel[y][x]);
+
                 }
             }
@@ -1652,5 +1658,8 @@
     } else {
 	sumKernel2 = 1.0;
-    }
+	//MEH
+	sumKernel2 = 0.001;
+    }
+    //MEH if sumKernel2 not use essentially, then not need to be 2.0*? but level of orderFactor may need to be adjusted then
 
     // if we modify the chisq value by the (sumKernel1 + sumKernel2), we account for the
@@ -1658,6 +1667,8 @@
     // penalized by increasing the score somewhat.  the 0.01 value is not well-chosen.
     float orderFactor = 0.01 * kernels->spatialOrder;
-    float score = 2.0 * chisqRValue / (sumKernel1 + sumKernel2) + orderFactor;
-    psLogMsg("psModules.imcombine", PS_LOG_INFO, "chisq: %6.3f, chisqD: %6.3f, moment: %6.3f, sumKernel_1: %6.3f, sumKernel_2, score: %6.3f: %6.3f\n", chisqRValue, chisqDValue, momentValue, sumKernel1, sumKernel2, score);
+    //MEH
+    //float score = 2.0 * chisqRValue / (sumKernel1 + sumKernel2) + orderFactor;
+    float score = 2.0 * chisqRValue / (sqrt(sumKernel1) + sqrt(sumKernel2)) + orderFactor;
+    psLogMsg("psModules.imcombine", PS_LOG_INFO, "chisq: %6.3f, chisqD: %6.3f, moment: %6.3f, sumKernel_1: %6.3f, sumKernel_2: %6.3f, score: %6.3f\n", chisqRValue, chisqDValue, momentValue, sumKernel1, sumKernel2, score);
 
     // save this result if it is the first or the best (skip if bestMatch is NULL)
Index: branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionEquation.v0.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionEquation.v0.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionEquation.v0.c	(revision 33415)
@@ -882,5 +882,5 @@
     }
 
-    if (!psThreadPoolWait(true)) {
+    if (!psThreadPoolWait(true, true)) {
         psError(psErrorCodeLast(), false, "Error waiting for threads.");
         return false;
Index: branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionMatch.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionMatch.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionMatch.c	(revision 33415)
@@ -29,6 +29,6 @@
 static bool useFFT = true;              // Do convolutions using FFT
 
-//#define TESTING
-//#define TESTING_MEMORY
+#define TESTING
+#define TESTING_MEMORY
 
 // Output memory usage information
@@ -538,14 +538,21 @@
     // Where does our variance map come from?
     // Getting the variance exactly right is not necessary --- it's just used for weighting.
+    // MEH - helpful to have one used output to log -- at some level want log output for most every step
+
     psImage *variance = NULL;             // Variance image to use
     if (ro1->variance && ro2->variance) {
         variance = (psImage*)psBinaryOp(NULL, ro1->variance, "+", ro2->variance);
+	psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "TESTING:SubMatch variance avail: var1+var2");
     } else if (ro1->variance) {
         variance = psMemIncrRefCounter(ro1->variance);
+	psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "TESTING:SubMatch variance avail: var1");
     } else if (ro2->variance) {
         variance = psMemIncrRefCounter(ro2->variance);
+	psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "TESTING:SubMatch variance avail: var2");
     } else {
         variance = (psImage*)psBinaryOp(NULL, ro1->image, "+", ro2->image);
-    }
+	psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "TESTING:SubMatch variance avail: none ro1+ro2");
+    }
+
 
     // Putting important variable declarations here, since they are freed after a "goto" if there is an error.
@@ -726,4 +733,7 @@
 		float radMoment1 = stamps->normWindow1 / 2.75;
 		float radMoment2 = stamps->normWindow2 / 2.75;
+		//MEH - FWHM vs radialMoment need *2? may mess up other parts
+		//float radMoment1 = stamps->normWindow1 / 2.75*2;
+		//float radMoment2 = stamps->normWindow2 / 2.75*2;
 		pmSubtractionParamsScale(NULL, NULL, isisWidths, radMoment1, radMoment2);
 
@@ -810,5 +820,5 @@
 		    }
 		}
-
+                //MEH probably should comment out but should be ok?
 		// step 0 : calculate the normalizations, pass along to the next steps via stamps->normValue
 		psTrace("psModules.imcombine", 3, "Calculating normalization...\n");
@@ -825,4 +835,12 @@
 		for (int order = 0; order <= N_TEST_ORDER; order++) {
 		    for (int j = 0; j < N_TEST_MODES; j++) {
+                        //MEH: thinking pmSubtractionCalculateNormalization needs to be called here for conv source
+                        // per mode
+                        //psTrace("psModules.imcombine", 3, "Calculating normalization...order: %d  mode: %d\n",order,j);
+			//                        if (!pmSubtractionCalculateNormalization(stamps, TestModes[j])) {
+                        //    psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
+                        //    goto MATCH_ERROR;
+                        //}
+                        //
 			if (!pmSubtractionMatchAttempt(&bestMatch, kernels, stamps, TestModes[j], order, false)) {
 			    goto MATCH_ERROR;
@@ -843,4 +861,11 @@
 	    // apply the best fit so we are ready to roll
 	    psLogMsg("psModules.imcombine", PS_LOG_INFO, "applying order: %d, mode: %d\n", bestMatch->spatialOrder, bestMatch->mode);
+            //MEH need to call normalization one more time for final/best run - 
+            // but what if dual - already checked for in pmSubtractionCalculateNormalization
+            //if (!pmSubtractionCalculateNormalization(stamps, bestMatch->mode)) {
+            //    psError(psErrorCodeLast(), false, "Unable to calculate least-squares equation.");
+            //    goto MATCH_ERROR;
+            //}
+            // 
 	    if (!pmSubtractionMatchAttempt(NULL, kernels, stamps, bestMatch->mode, bestMatch->spatialOrder, true)) {
 		goto MATCH_ERROR;
@@ -1091,5 +1116,5 @@
     }
 
-    if (!psThreadPoolWait(true)) {
+    if (!psThreadPoolWait(true, true)) {
 	psError(psErrorCodeLast(), false, "Error waiting for threads.");
 	psFree(models);
@@ -1331,5 +1356,6 @@
 	*stampSize = *stampSize * scale + 0.5;
     }
-
+    //MEH
+    psLogMsg("psModules.imcombine", PS_LOG_INFO, "TESTING Scaling kernel parameters fwhm1,2,refscale/min/max: %f %f %f %f %f", fwhm1,fwhm2,scaleRefOption,scaleMinOption,scaleMaxOption);
     psLogMsg("psModules.imcombine", PS_LOG_INFO, "Scaling kernel parameters by %f", scale);
     if (kernelSize) psLogMsg("psModules.imcombine", PS_LOG_INFO, " modified kernel size %d", *kernelSize);
Index: branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionStamps.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionStamps.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionStamps.c	(revision 33415)
@@ -127,4 +127,10 @@
                 continue;
             }
+	    //MEH hack to limit bright end test
+//            if ((image1 && image1->data.F32[y][x] > 2000) ||
+//                (image2 && image2->data.F32[y][x] > 2000 )) {
+//		fprintf (stderr, "%f,%f : thresh\n",image1->data.F32[y][x],image2->data.F32[y][x]);
+//                continue;
+//            }
 
             if (subMask && subMask->data.PS_TYPE_IMAGE_MASK_DATA[y][x] &
@@ -188,5 +194,6 @@
 }
 
-void pmSubtractionStampPrint(FILE *ds9, float x, float y, float size, const char *color)
+// MEH adding text to ds9
+void pmSubtractionStampPrint(FILE *ds9, float x, float y, float size, const char *color, const char *ds9text)
 {
     if (!ds9regions || !ds9) {
@@ -196,4 +203,7 @@
     if (color && strlen(color) > 0) {
         fprintf(ds9, " # color=%s", color);
+    }
+    if (ds9text && strlen(ds9text) > 0) {
+        fprintf(ds9, " text={%s}", ds9text);
     }
     fprintf(ds9, "\n");
@@ -486,7 +496,7 @@
                                              normFrac, sysErr, skyErr);
     }
-
+    //MEH - stamps.dat will repeatedly be overwritten - out to trace may be btter -- see end for summary 
     // XXX TEST : dump all stars in the stamps here
-    if (0) {
+    if (1) {
 	FILE *f = fopen ("stamp.dat", "w");
 	for (int i = 0; i < stamps->num; i++) {
@@ -589,6 +599,9 @@
                 stamp->status = PM_SUBTRACTION_STAMP_FOUND;
                 numFound++;
-                psTrace("psModules.imcombine", 5, "Found stamp in subregion %d: %d,%d\n",
-                        i, (int)stamp->x, (int)stamp->y);
+//                psTrace("psModules.imcombine", 5, "Found stamp in subregion %d: %d,%d\n",
+//                        i, (int)stamp->x, (int)stamp->y);
+		//MEH adding int flux to say something about the source 
+                psTrace("psModules.imcombine", 5, "Found stamp in subregion %d: %d,%d  Flux: %d\n",
+                        i, (int)stamp->x, (int)stamp->y, (int)stamp->flux);
             } else {
                 stamp->status = PM_SUBTRACTION_STAMP_NONE;
@@ -666,8 +679,8 @@
             psTrace("psModules.imcombine", 9, "Rejecting input stamp (%d,%d) because outside region",
                     xPix, yPix);
-            pmSubtractionStampPrint(ds9, xPix, yPix, footprint, "red");
+            pmSubtractionStampPrint(ds9, xPix, yPix, footprint, "red", "");
             continue;
         }
-
+	//MEH - are all using -0.5? seen some seem shifted
         // fprintf (stderr, "stamp: %5.1f %5.1f == %d %d\n", xStamp, yStamp, xPix, yPix);
 
@@ -692,5 +705,5 @@
                 psTrace("psModules.imcombine", 9, "Putting input stamp (%d,%d) into subregion %d",
                         xPix, yPix, j);
-                pmSubtractionStampPrint(ds9, xPix, yPix, footprint, "green");
+                pmSubtractionStampPrint(ds9, xPix, yPix, footprint, "green", "");
             }
         }
@@ -699,5 +712,5 @@
             psTrace("psModules.imcombine", 9, "Unable to find subregion for stamp (%d,%d)",
                     xPix, yPix);
-            pmSubtractionStampPrint(ds9, xPix, yPix, footprint, "yellow");
+            pmSubtractionStampPrint(ds9, xPix, yPix, footprint, "yellow", "");
         }
     }
@@ -865,6 +878,6 @@
         }
     }
-
-#if 0
+//MEH
+#if 1
     {
 	psFits *fits = NULL;
@@ -1156,15 +1169,26 @@
                 float sysErr = 0.25 * PS_SQR(stamps->sysErr); // Systematic error
                 float skyErr = stamps->skyErr;
+		//MEH - image2 not used if not adding to additional.. also these are never freed here, but upstream/downstream?
                 psKernel *image1 = stamp->image1, *image2 = stamp->image2; // Input images
+		//psKernel *image2 = stamp->image2; // Input images
                 for (int y = -size; y <= size; y++) {
                     for (int x = -size; x <= size; x++) {
+			//MEH -- if conv to model then what does sys and weight mean for both?
+			// w1+w2 vs 1/var1+1/var2.. depends on what the weight is for?
+			// why additional ^2? 
                         float additional = image1->kernel[y][x] + image2->kernel[y][x];
-                        weight->kernel[y][x] = 1.0 / (skyErr + var->kernel[y][x] + sysErr * PS_SQR(additional));
+                        //float additional = image2->kernel[y][x];
+//                        weight->kernel[y][x] = 1.0 / (skyErr + var->kernel[y][x] + sysErr * PS_SQR(additional));
+                        weight->kernel[y][x] = 1.0 / (skyErr + 1000.0*var->kernel[y][x] + sysErr * PS_SQR(additional));
                     }
                 }
+		psLogMsg("psModules.imcombine", PS_LOG_DETAIL, "TESTING:: Stamp %d (%.1f,%.1f) weight: %f var: %.1f sysErr: %.2f im1: %.0f im2: %.0f \n", 
+			 i, stamp->x, stamp->y, weight->kernel[0][0],var->kernel[0][0],sysErr,image2->kernel[0][0],image1->kernel[0][0]);
+
             } else {
                 for (int y = -size; y <= size; y++) {
                     for (int x = -size; x <= size; x++) {
-                        weight->kernel[y][x] = 1.0 / var->kernel[y][x];
+//                        weight->kernel[y][x] = 1.0 / var->kernel[y][x];
+                        weight->kernel[y][x] = 1.0 / (1000.0*var->kernel[y][x]);
                     }
                 }
Index: branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionStamps.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionStamps.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/imcombine/pmSubtractionStamps.h	(revision 33415)
@@ -163,7 +163,8 @@
                              float x, float y, ///< Position of stamp
                              float size,///< Size of circle
-                             const char *color ///< Colour
+                             const char *color, ///< Colour
+			     const char *ds9text ///< info text
     );
-
+// MEH added text to ds9 file
 
 bool pmSubtractionStampsResetStatus (pmSubtractionStampList *stamps);
Index: branches/meh_branches/ppstack_test/psModules/src/objects/Makefile.am
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/Makefile.am	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/Makefile.am	(revision 33415)
@@ -45,4 +45,5 @@
 	pmSourceIO_CMF_PS1_V2.c \
 	pmSourceIO_CMF_PS1_V3.c \
+	pmSourceIO_CMF_PS1_V4.c \
 	pmSourceIO_CMF_PS1_SV1.c \
 	pmSourceIO_CMF_PS1_DV1.c \
@@ -130,14 +131,17 @@
 
 # pmSourceID_CMF_* functions use a common framework
-BUILT_SOURCES = pmSourceIO_CMF_PS1_V1.v1.c pmSourceIO_CMF_PS1_V2.v1.c pmSourceIO_CMF_PS1_V3.v1.c
+BUILT_SOURCES = pmSourceIO_CMF_PS1_V1.c pmSourceIO_CMF_PS1_V2.c pmSourceIO_CMF_PS1_V3.c pmSourceIO_CMF_PS1_V4.c
 
-pmSourceIO_CMF_PS1_V1.v1.c : pmSourceIO_CMF.c.in mksource.pl
-	mksource.pl pmSourceIO_CMF.c.in PS1_V1 pmSourceIO_CMF_PS1_V1.v1.c
+pmSourceIO_CMF_PS1_V1.c : pmSourceIO_CMF.c.in mksource.pl
+	mksource.pl pmSourceIO_CMF.c.in PS1_V1 pmSourceIO_CMF_PS1_V1.c
 
-pmSourceIO_CMF_PS1_V2.v1.c : pmSourceIO_CMF.c.in mksource.pl
-	mksource.pl pmSourceIO_CMF.c.in PS1_V2 pmSourceIO_CMF_PS1_V2.v1.c
+pmSourceIO_CMF_PS1_V2.c : pmSourceIO_CMF.c.in mksource.pl
+	mksource.pl pmSourceIO_CMF.c.in PS1_V2 pmSourceIO_CMF_PS1_V2.c
 
-pmSourceIO_CMF_PS1_V3.v1.c : pmSourceIO_CMF.c.in mksource.pl
-	mksource.pl pmSourceIO_CMF.c.in PS1_V2 pmSourceIO_CMF_PS1_V3.v1.c
+pmSourceIO_CMF_PS1_V3.c : pmSourceIO_CMF.c.in mksource.pl
+	mksource.pl pmSourceIO_CMF.c.in PS1_V3 pmSourceIO_CMF_PS1_V3.c
+
+pmSourceIO_CMF_PS1_V4.c : pmSourceIO_CMF.c.in mksource.pl
+	mksource.pl pmSourceIO_CMF.c.in PS1_V4 pmSourceIO_CMF_PS1_V4.c
 
 # EXTRA_DIST = pmErrorCodes.h.in pmErrorCodes.dat pmErrorCodes.c.in
Index: branches/meh_branches/ppstack_test/psModules/src/objects/mksource.pl
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/mksource.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/mksource.pl	(revision 33415)
@@ -14,7 +14,11 @@
 
 # see if we can add in PS1_DV* and PS1_SV* as well...
-@cmfmodes = ("PS1_V1", 1,
+%cmfmodes = ("PS1_V1", 1,
 	     "PS1_V2", 2,
-	     "PS1_V3", 3);
+	     "PS1_V3", 3,
+	     "PS1_V4", 4);
+
+print "1: $cmfmodes{1}\n";
+print "PS1_V1: $cmfmodes{'PS1_V1'}\n";
 
 open (FILE, "$template") || die "failed to open template $template\n";
@@ -50,15 +54,19 @@
 
     if ($gtMode) {
+	# print "gtMode : $line\n";
 	$thisLevel = $cmfmodes{$gtMode};
 	$myLevel = $cmfmodes{$cmfmode};
-	if ($thisLevel <= $myLevel) { next; }
-	$line =~ s|\@<\S*\@\s*||;
+	print "gtMode : $gtMode vs $cmfmode, $thisLevel, $myLevel\n";
+	if ($myLevel <= $thisLevel) { next; }
+	$line =~ s|\@>\S*\@\s*||;
     }
 
     if ($ltMode) {
+	# print "ltMode : $line\n";
 	$thisLevel = $cmfmodes{$ltMode};
 	$myLevel = $cmfmodes{$cmfmode};
-	if ($thisLevel >= $myLevel) { next; }
-	$line =~ s|\@>\S*\@\s*||;
+	print "ltMode : $ltMode vs $cmfmode, $thisLevel, $myLevel\n";
+	if ($myLevel >= $thisLevel) { next; }
+	$line =~ s|\@<\S*\@\s*||;
     }
 
Index: branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_DEV.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_DEV.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_DEV.c	(revision 33415)
@@ -89,4 +89,6 @@
 static bool limitsApply = true;         // Apply limits?
 
+# include "pmModel_SERSIC.CP.h"
+
 psF32 PM_MODEL_FUNC (psVector *deriv,
                      const psVector *params,
@@ -94,8 +96,4 @@
 {
     psF32 *PAR = params->data.F32;
-
-    float index = 0.5 / ALPHA;
-    float bn = 1.9992*index - 0.3271;
-    float Io = exp(bn);
 
     psF32 X  = pixcoord->data.F32[0] - PAR[PM_PAR_XPOS];
@@ -105,8 +103,89 @@
     psF32 z  = (PS_SQR(px) + PS_SQR(py) + PAR[PM_PAR_SXY]*X*Y);
 
-    assert (z >= 0);
+    // If the elliptical contour is defined in a valid way, we should never trigger this
+    // assert.  Other models (like PGAUSS) don't use fractional powers, and thus do not have
+    // NaN values for negative values of z
+    psAssert (z >= 0, "do not allow negative z values in model");
+
+    float index = 0.5 / ALPHA;
+    float par7 = ALPHA;
+    float bn = 1.9992*index - 0.3271;
+    float Io = exp(bn);
 
     psF32 f2 = bn*pow(z,ALPHA);
     psF32 f1 = Io*exp(-f2);
+
+    psF32 radius = hypot(X, Y);
+    if (radius < 1.0) {
+
+	// ** use bilinear interpolation to the given location from the 4 surrounding pixels centered on the object center
+
+	// first, use Rmajor and index to find the central pixel flux (fraction of total flux)
+	psEllipseShape shape;
+
+	shape.sx  = PAR[PM_PAR_SXX];
+	shape.sy  = PAR[PM_PAR_SYY];
+	shape.sxy = PAR[PM_PAR_SXY];
+
+	// for a non-circular Sersic, the flux of the Rmajor equivalent is scaled by the AspectRatio
+	psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
+
+	// get the central pixel flux from the lookup table
+	float xPix = (axes.major - centralPixelXo) / centralPixeldX;
+	xPix = PS_MIN (PS_MAX(xPix, 0), centralPixelNX - 1);
+	float yPix = (index - centralPixelYo) / centralPixeldY;
+	yPix = PS_MIN (PS_MAX(yPix, 0), centralPixelNY - 1);
+
+	// the integral of a Sersic has an analytical form as follows:
+	float logGamma = lgamma(2.0*index);
+	float bnFactor = pow(bn, 2.0*index);
+	float norm = 2.0 * M_PI * PS_SQR(axes.major) * index * exp(bn) * exp(logGamma) / bnFactor;
+
+	// XXX interpolate to get the value
+	// XXX for the moment, just integerize
+	// XXX I need to multiply by the integrated flux to get the flux in the central pixel
+	float Vcenter = centralPixel[(int)yPix][(int)xPix] * norm;
+	
+	float px1 = 1.0 / PAR[PM_PAR_SXX];
+	float py1 = 1.0 / PAR[PM_PAR_SYY];
+	float z10 = PS_SQR(px1);
+	float z01 = PS_SQR(py1);
+
+	// which pixels do we need for this interpolation?
+	// (I do not keep state information, so I don't know anything about other evaluations of nearby pixels...)
+	if ((X >= 0) && (Y >= 0)) {
+	    float z11 = z10 + z01 + PAR[PM_PAR_SXY]; // X * Y positive
+	    float V00 = Vcenter;
+	    float V10 = Io*exp(-bn*pow(z10,par7));
+	    float V01 = Io*exp(-bn*pow(z01,par7));
+	    float V11 = Io*exp(-bn*pow(z11,par7));
+	    f1 = interpolatePixels(V00, V10, V01, V11, X, Y);
+	}
+	if ((X < 0) && (Y >= 0)) {
+	    float z11 = z10 + z01 - PAR[PM_PAR_SXY]; // X * Y negative
+	    float V00 = Io*exp(-bn*pow(z10,par7));
+	    float V10 = Vcenter;
+	    float V01 = Io*exp(-bn*pow(z11,par7));
+	    float V11 = Io*exp(-bn*pow(z01,par7));
+	    f1 = interpolatePixels(V00, V10, V01, V11, (1.0 + X), Y);
+	}
+	if ((X >= 0) && (Y < 0)) {
+	    float z11 = z10 + z01 - PAR[PM_PAR_SXY]; // X * Y negative
+	    float V00 = Io*exp(-bn*pow(z01,par7));
+	    float V10 = Io*exp(-bn*pow(z11,par7));
+	    float V01 = Vcenter;
+	    float V11 = Io*exp(-bn*pow(z10,par7));
+	    f1 = interpolatePixels(V00, V10, V01, V11, X, (1.0 + Y));
+	}
+	if ((X < 0) && (Y < 0)) {
+	    float z11 = z10 + z01 + PAR[PM_PAR_SXY]; // X * Y positive
+	    float V00 = Io*exp(-bn*pow(z11,par7));
+	    float V10 = Io*exp(-bn*pow(z10,par7));
+	    float V01 = Io*exp(-bn*pow(z01,par7));
+	    float V11 = Vcenter;
+	    f1 = interpolatePixels(V00, V10, V01, V11, (1.0 + X), (1.0 + Y));
+	}
+    }   
+
     psF32 z0 = PAR[PM_PAR_I0]*f1;
     psF32 f0 = PAR[PM_PAR_SKY] + z0;
@@ -120,9 +199,9 @@
         psF32 *dPAR = deriv->data.F32;
 
+        dPAR[PM_PAR_SKY]  = +1.0;
+        dPAR[PM_PAR_I0]   = +2.0*f1; // XXX extra damping..
+
         // gradient is infinite for z = 0; saturate at z = 0.01
         psF32 z1 = (z < 0.01) ? z0*bn*ALPHA*pow(0.01,ALPHA - 1.0) : z0*bn*ALPHA*pow(z,ALPHA - 1.0);
-
-        dPAR[PM_PAR_SKY]  = +1.0;
-        dPAR[PM_PAR_I0]   = +2.0*f1;
 
         assert (isfinite(z1));
@@ -223,4 +302,5 @@
 
     // set the shape parameters
+    // XXX adjust this?
     if (!pmModelSetShape(&PAR[PM_PAR_SXX], &PAR[PM_PAR_SXY], &PAR[PM_PAR_SYY], source->moments)) {
       return false;
@@ -246,36 +326,28 @@
 }
 
+// A DeVaucouleur model is equivalent to a Sersic with index = 4.0
 psF64 PM_MODEL_FLUX (const psVector *params)
 {
-    float z, norm;
     psEllipseShape shape;
 
     psF32 *PAR = params->data.F32;
 
-    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
-    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sx  = PAR[PM_PAR_SXX];
+    shape.sy  = PAR[PM_PAR_SYY];
     shape.sxy = PAR[PM_PAR_SXY];
 
-    // Area is equivalent to 2 pi sigma^2
+    // for a non-circular DeVaucouleur, the flux of the Rmajor equivalent is scaled by the AspectRatio
     psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
-    psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
-
-    // the area needs to be multiplied by the integral of f(z)
-    norm = 0.0;
-
-    # define DZ 0.25
-
-    float f0 = 1.0;
-    float f1, f2;
-    for (z = DZ; z < 150; z += DZ) {
-	f1 = exp(-pow(z,ALPHA));
-        z += DZ;
-	f2 = exp(-pow(z,ALPHA));
-        norm += f0 + 4*f1 + f2;
-        f0 = f2;
-    }
-    norm *= DZ / 3.0;
-
-    psF64 Flux = PAR[PM_PAR_I0] * Area * norm;
+    float AspectRatio = axes.minor / axes.major;
+
+    float index = 4.0;
+    float bn = 1.9992*index - 0.3271;
+
+    // the integral of a Sersic has an analytical form as follows:
+    float logGamma = lgamma(2.0*index);
+    float bnFactor = pow(bn, 2.0*index);
+    float norm = 2.0 * M_PI * PS_SQR(axes.major) * index * exp(bn) * exp(logGamma) / bnFactor;
+    
+    psF64 Flux = PAR[PM_PAR_I0] * norm * AspectRatio;
 
     return(Flux);
@@ -297,6 +369,6 @@
         return (1.0);
 
-    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
-    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sx  = PAR[PM_PAR_SXX];
+    shape.sy  = PAR[PM_PAR_SYY];
     shape.sxy = PAR[PM_PAR_SXY];
 
Index: branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_EXP.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_EXP.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_EXP.c	(revision 33415)
@@ -81,4 +81,6 @@
 static bool limitsApply = true;         // Apply limits?
 
+# include "pmModel_SERSIC.CP.h"
+
 psF32 PM_MODEL_FUNC (psVector *deriv,
                      const psVector *params,
@@ -93,13 +95,89 @@
     psF32 z  = PS_SQR(px) + PS_SQR(py) + PAR[PM_PAR_SXY]*X*Y;
 
-    // XXX if the elliptical contour is defined in valid way, this step should not be required.
-    // other models (like PGAUSS) don't use fractional powers, and thus do not have NaN values
-    // for negative values of z
-    // XXX use an assert here to force the elliptical parameters to be correctly determined
-    // if (z < 0) z = 0;
-    assert (z >= 0);
-
-    psF32 f2 = sqrt(z);
-    psF32 f1 = exp(-f2);
+    // If the elliptical contour is defined in a valid way, we should never trigger this
+    // assert.  Other models (like PGAUSS) don't use fractional powers, and thus do not have
+    // NaN values for negative values of z
+    psAssert (z >= 0, "do not allow negative z values in model");
+
+    float index = 1.0;
+    float par7 = 0.5;
+    float bn = 1.9992*index - 0.3271;
+    float Io = exp(bn);
+
+    psF32 f2 = bn*sqrt(z);
+    psF32 f1 = Io*exp(-f2);
+
+    psF32 radius = hypot(X, Y);
+    if (radius < 1.0) {
+
+	// ** use bilinear interpolation to the given location from the 4 surrounding pixels centered on the object center
+
+	// first, use Rmajor and index to find the central pixel flux (fraction of total flux)
+	psEllipseShape shape;
+
+	shape.sx  = PAR[PM_PAR_SXX];
+	shape.sy  = PAR[PM_PAR_SYY];
+	shape.sxy = PAR[PM_PAR_SXY];
+
+	// for a non-circular Sersic, the flux of the Rmajor equivalent is scaled by the AspectRatio
+	psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
+
+	// get the central pixel flux from the lookup table
+	float xPix = (axes.major - centralPixelXo) / centralPixeldX;
+	xPix = PS_MIN (PS_MAX(xPix, 0), centralPixelNX - 1);
+	float yPix = (index - centralPixelYo) / centralPixeldY;
+	yPix = PS_MIN (PS_MAX(yPix, 0), centralPixelNY - 1);
+
+	// the integral of a Sersic has an analytical form as follows:
+	float logGamma = lgamma(2.0*index);
+	float bnFactor = pow(bn, 2.0*index);
+	float norm = 2.0 * M_PI * PS_SQR(axes.major) * index * exp(bn) * exp(logGamma) / bnFactor;
+
+	// XXX interpolate to get the value
+	// XXX for the moment, just integerize
+	// XXX I need to multiply by the integrated flux to get the flux in the central pixel
+	float Vcenter = centralPixel[(int)yPix][(int)xPix] * norm;
+	
+	float px1 = 1.0 / PAR[PM_PAR_SXX];
+	float py1 = 1.0 / PAR[PM_PAR_SYY];
+	float z10 = PS_SQR(px1);
+	float z01 = PS_SQR(py1);
+
+	// which pixels do we need for this interpolation?
+	// (I do not keep state information, so I don't know anything about other evaluations of nearby pixels...)
+	if ((X >= 0) && (Y >= 0)) {
+	    float z11 = z10 + z01 + PAR[PM_PAR_SXY]; // X * Y positive
+	    float V00 = Vcenter;
+	    float V10 = Io*exp(-bn*pow(z10,par7));
+	    float V01 = Io*exp(-bn*pow(z01,par7));
+	    float V11 = Io*exp(-bn*pow(z11,par7));
+	    f1 = interpolatePixels(V00, V10, V01, V11, X, Y);
+	}
+	if ((X < 0) && (Y >= 0)) {
+	    float z11 = z10 + z01 - PAR[PM_PAR_SXY]; // X * Y negative
+	    float V00 = Io*exp(-bn*pow(z10,par7));
+	    float V10 = Vcenter;
+	    float V01 = Io*exp(-bn*pow(z11,par7));
+	    float V11 = Io*exp(-bn*pow(z01,par7));
+	    f1 = interpolatePixels(V00, V10, V01, V11, (1.0 + X), Y);
+	}
+	if ((X >= 0) && (Y < 0)) {
+	    float z11 = z10 + z01 - PAR[PM_PAR_SXY]; // X * Y negative
+	    float V00 = Io*exp(-bn*pow(z01,par7));
+	    float V10 = Io*exp(-bn*pow(z11,par7));
+	    float V01 = Vcenter;
+	    float V11 = Io*exp(-bn*pow(z10,par7));
+	    f1 = interpolatePixels(V00, V10, V01, V11, X, (1.0 + Y));
+	}
+	if ((X < 0) && (Y < 0)) {
+	    float z11 = z10 + z01 + PAR[PM_PAR_SXY]; // X * Y positive
+	    float V00 = Io*exp(-bn*pow(z11,par7));
+	    float V10 = Io*exp(-bn*pow(z10,par7));
+	    float V01 = Io*exp(-bn*pow(z01,par7));
+	    float V11 = Vcenter;
+	    f1 = interpolatePixels(V00, V10, V01, V11, (1.0 + X), (1.0 + Y));
+	}
+    }   
+
     psF32 z0 = PAR[PM_PAR_I0]*f1;
     psF32 f0 = PAR[PM_PAR_SKY] + z0;
@@ -118,5 +196,5 @@
         // gradient is infinite for z = 0; saturate at z = 0.01
 	// z1 is -df/dz (the negative sign is canceled by most of dz/dPAR[i]
-        psF32 z1 = (z < 0.01) ? 0.5*z0/sqrt(0.01) : 0.5*z0/sqrt(z);
+        psF32 z1 = (z < 0.01) ? 0.5*bn*z0/sqrt(0.01) : 0.5*bn*z0/sqrt(z);
 
 	// XXX dampen SXX and SYY as in GAUSS?
@@ -216,4 +294,5 @@
 
     // set the shape parameters
+    // XXX adjust this?
     if (!pmModelSetShape(&PAR[PM_PAR_SXX], &PAR[PM_PAR_SXY], &PAR[PM_PAR_SYY], source->moments)) {
       return false;
@@ -233,36 +312,28 @@
 }
 
+// An exponential model is equivalent to a Sersic with index = 1.0
 psF64 PM_MODEL_FLUX (const psVector *params)
 {
-    float z, norm;
     psEllipseShape shape;
 
     psF32 *PAR = params->data.F32;
 
-    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
-    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sx  = PAR[PM_PAR_SXX];
+    shape.sy  = PAR[PM_PAR_SYY];
     shape.sxy = PAR[PM_PAR_SXY];
 
-    // Area is equivalent to 2 pi sigma^2
+    // for a non-circular Exponential, the flux of the Rmajor equivalent is scaled by the AspectRatio
     psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
-    psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
-
-    // the area needs to be multiplied by the integral of f(z) = exp(-sqrt(z)) [0 to infinity]
-    norm = 0.0;
-
-    # define DZ 0.25
-
-    float f0 = 1.0;
-    float f1, f2;
-    for (z = DZ; z < 150; z += DZ) {
-        f1 = exp(-sqrt(z));
-        z += DZ;
-        f2 = exp(-sqrt(z));
-        norm += f0 + 4*f1 + f2;
-        f0 = f2;
-    }
-    norm *= DZ / 3.0;
-
-    psF64 Flux = PAR[PM_PAR_I0] * Area * norm;
+    float AspectRatio = axes.minor / axes.major;
+
+    float index = 1.0;
+    float bn = 1.9992*index - 0.3271;
+
+    // the integral of a Sersic has an analytical form as follows:
+    float logGamma = lgamma(2.0*index);
+    float bnFactor = pow(bn, 2.0*index);
+    float norm = 2.0 * M_PI * PS_SQR(axes.major) * index * exp(bn) * exp(logGamma) / bnFactor;
+    
+    psF64 Flux = PAR[PM_PAR_I0] * norm * AspectRatio;
 
     return(Flux);
@@ -284,6 +355,6 @@
         return (1.0);
 
-    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
-    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sx  = PAR[PM_PAR_SXX];
+    shape.sy  = PAR[PM_PAR_SYY];
     shape.sxy = PAR[PM_PAR_SXY];
 
Index: branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_PGAUSS.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_PGAUSS.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_PGAUSS.c	(revision 33415)
@@ -217,7 +217,8 @@
 }
 
-psF64 PM_MODEL_FLUX(const psVector *params)
-{
-    float norm, z;
+// integrate the model to get the full flux
+psF64 PM_MODEL_FLUX (const psVector *params)
+{
+    float z, norm;
     psEllipseShape shape;
 
@@ -228,25 +229,27 @@
     shape.sxy = PAR[PM_PAR_SXY];
 
-    // Area is equivalent to 2 pi sigma^2
     psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
-    psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
-
-    // the area needs to be multiplied by the integral of f(z)
+    float AspectRatio = axes.minor / axes.major;
+
+    // flux = 2 \pi \int f(r) r dr
     norm = 0.0;
 
-    # define DZ 0.25
-
-    float f0 = 1.0;
+    # define DR 0.25
+
+    // f = f(r) * r
+    float f0 = 0.0;
     float f1, f2;
-    for (z = DZ; z < 150; z += DZ) {
-        f1 = 1.0 / (1 + z + z*z/2.0 + z*z*z/6.0);
-        z += DZ;
-        f2 = 1.0 / (1 + z + z*z/2.0 + z*z*z/6.0);
+    for (float r = DR; r < 150; r += DR) {
+	z = 0.5 * PS_SQR(r / axes.major);
+        f1 = r / (1 + z + z*z/2.0 + z*z*z/6.0);
+        r += DR;
+	z = 0.5 * PS_SQR(r / axes.major);
+        f2 = r / (1 + z + z*z/2.0 + z*z*z/6.0);
         norm += f0 + 4*f1 + f2;
         f0 = f2;
     }
-    norm *= DZ / 3.0;
-
-    psF64 Flux = PAR[PM_PAR_I0] * Area * norm;
+    norm *= DR / 3.0;
+
+    psF64 Flux = PAR[PM_PAR_I0] * norm * 2.0 * M_PI * AspectRatio;
 
     return(Flux);
Index: branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_PS1_V1.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_PS1_V1.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_PS1_V1.c	(revision 33415)
@@ -14,6 +14,6 @@
    * PM_PAR_XPOS 2  - X center of object
    * PM_PAR_YPOS 3  - Y center of object
-   * PM_PAR_SXX 4   - X^2 term of elliptical contour (sqrt(2) / SigmaX)
-   * PM_PAR_SYY 5   - Y^2 term of elliptical contour (sqrt(2) / SigmaY)
+   * PM_PAR_SXX 4   - X^2 term of elliptical contour (SigmaX / sqrt(2))
+   * PM_PAR_SYY 5   - Y^2 term of elliptical contour (SigmaY / sqrt(2))
    * PM_PAR_SXY 6   - X*Y term of elliptical contour
    * PM_PAR_7   7   - amplitude of the linear component (k)
@@ -239,4 +239,5 @@
 }
 
+// integrate the model to get the full flux
 psF64 PM_MODEL_FLUX (const psVector *params)
 {
@@ -250,25 +251,27 @@
     shape.sxy = PAR[PM_PAR_SXY];
 
-    // Area is equivalent to 2 pi sigma^2
     psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
-    psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
-
-    // the area needs to be multiplied by the integral of f(z)
+    float AspectRatio = axes.minor / axes.major;
+
+    // flux = 2 \pi \int f(r) r dr
     norm = 0.0;
 
-    # define DZ 0.25
-
-    float f0 = 1.0;
+    # define DR 0.25
+
+    // f = f(r) * r
+    float f0 = 0.0;
     float f1, f2;
-    for (z = DZ; z < 150; z += DZ) {
-        f1 = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, ALPHA));
-        z += DZ;
-        f2 = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, ALPHA));
+    for (float r = DR; r < 150; r += DR) {
+	z = 0.5 * PS_SQR(r / axes.major);
+        f1 = r / (1 + PAR[PM_PAR_7]*z + pow(z, ALPHA));
+        r += DR;
+	z = 0.5 * PS_SQR(r / axes.major);
+        f2 = r / (1 + PAR[PM_PAR_7]*z + pow(z, ALPHA));
         norm += f0 + 4*f1 + f2;
         f0 = f2;
     }
-    norm *= DZ / 3.0;
-
-    psF64 Flux = PAR[PM_PAR_I0] * Area * norm;
+    norm *= DR / 3.0;
+
+    psF64 Flux = PAR[PM_PAR_I0] * norm * 2.0 * M_PI * AspectRatio;
 
     return(Flux);
Index: branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_QGAUSS.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_QGAUSS.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_QGAUSS.c	(revision 33415)
@@ -240,4 +240,5 @@
 }
 
+// integrate the model to get the full flux
 psF64 PM_MODEL_FLUX (const psVector *params)
 {
@@ -251,25 +252,27 @@
     shape.sxy = PAR[PM_PAR_SXY];
 
-    // Area is equivalent to 2 pi sigma^2
     psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
-    psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
-
-    // the area needs to be multiplied by the integral of f(z)
+    float AspectRatio = axes.minor / axes.major;
+
+    // flux = 2 \pi \int f(r) r dr
     norm = 0.0;
 
-    # define DZ 0.25
-
-    float f0 = 1.0;
+    # define DR 0.25
+
+    // f = f(r) * r
+    float f0 = 0.0;
     float f1, f2;
-    for (z = DZ; z < 150; z += DZ) {
-        f1 = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, ALPHA));
-        z += DZ;
-        f2 = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, ALPHA));
+    for (float r = DR; r < 150; r += DR) {
+	z = 0.5 * PS_SQR(r / axes.major);
+        f1 = r / (1 + PAR[PM_PAR_7]*z + pow(z, ALPHA));
+        r += DR;
+	z = 0.5 * PS_SQR(r / axes.major);
+        f2 = r / (1 + PAR[PM_PAR_7]*z + pow(z, ALPHA));
         norm += f0 + 4*f1 + f2;
         f0 = f2;
     }
-    norm *= DZ / 3.0;
-
-    psF64 Flux = PAR[PM_PAR_I0] * Area * norm;
+    norm *= DR / 3.0;
+
+    psF64 Flux = PAR[PM_PAR_I0] * norm * 2.0 * M_PI * AspectRatio;
 
     return(Flux);
Index: branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_RGAUSS.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_RGAUSS.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_RGAUSS.c	(revision 33415)
@@ -229,7 +229,8 @@
 }
 
+// integrate the model to get the full flux
 psF64 PM_MODEL_FLUX (const psVector *params)
 {
-    float norm, z;
+    float z, norm;
     psEllipseShape shape;
 
@@ -240,25 +241,27 @@
     shape.sxy = PAR[PM_PAR_SXY];
 
-    // Area is equivalent to 2 pi sigma^2
     psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
-    psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
-
-    // the area needs to be multiplied by the integral of f(z)
+    float AspectRatio = axes.minor / axes.major;
+
+    // flux = 2 \pi \int f(r) r dr
     norm = 0.0;
 
-    # define DZ 0.25
-
-    float f0 = 1.0;
+    # define DR 0.25
+
+    // f = f(r) * r
+    float f0 = 0.0;
     float f1, f2;
-    for (z = DZ; z < 150; z += DZ) {
-        f1 = 1.0 / (1 + z + pow(z, PAR[PM_PAR_7]));
-        z += DZ;
-        f2 = 1.0 / (1 + z + pow(z, PAR[PM_PAR_7]));
+    for (float r = DR; r < 150; r += DR) {
+	z = 0.5 * PS_SQR(r / axes.major);
+        f1 = r / (1 + z + pow(z, PAR[PM_PAR_7]));
+        r += DR;
+	z = 0.5 * PS_SQR(r / axes.major);
+        f2 = r / (1 + z + pow(z, PAR[PM_PAR_7]));
         norm += f0 + 4*f1 + f2;
         f0 = f2;
     }
-    norm *= DZ / 3.0;
-
-    psF64 Flux = PAR[PM_PAR_I0] * Area * norm;
+    norm *= DR / 3.0;
+
+    psF64 Flux = PAR[PM_PAR_I0] * norm * 2.0 * M_PI * AspectRatio;
 
     return(Flux);
Index: branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_SERSIC.CP.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_SERSIC.CP.h	(revision 33415)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_SERSIC.CP.h	(revision 33415)
@@ -0,0 +1,30 @@
+static int centralPixelNX = 29;
+static int centralPixelNY = 12;
+static float centralPixelXo = 2.0;
+static float centralPixelYo = 0.5;
+static float centralPixeldX = 0.5;
+static float centralPixeldY = 0.5;
+
+static float centralPixel[12][29] = {
+{  0.0521,  0.0336,  0.0235,  0.0173,  0.0133,  0.0105,  0.0085,  0.0070,  0.0059,  0.0051,  0.0044,  0.0038,  0.0033,  0.0030,  0.0026,  0.0024,  0.0021,  0.0019,  0.0018,  0.0016,  0.0015,  0.0014,  0.0013,  0.0012,  0.0011,  0.0010,  0.0010,  0.0009,  0.0008, }, 
+{  0.0817,  0.0556,  0.0402,  0.0304,  0.0238,  0.0191,  0.0157,  0.0131,  0.0111,  0.0096,  0.0083,  0.0073,  0.0064,  0.0057,  0.0051,  0.0046,  0.0042,  0.0038,  0.0035,  0.0032,  0.0029,  0.0027,  0.0025,  0.0023,  0.0022,  0.0020,  0.0019,  0.0018,  0.0017, }, 
+{  0.1096,  0.0785,  0.0591,  0.0462,  0.0371,  0.0305,  0.0255,  0.0217,  0.0187,  0.0162,  0.0143,  0.0126,  0.0113,  0.0101,  0.0091,  0.0083,  0.0076,  0.0069,  0.0064,  0.0059,  0.0054,  0.0050,  0.0047,  0.0044,  0.0041,  0.0038,  0.0036,  0.0034,  0.0032, }, 
+{  0.1339,  0.0998,  0.0777,  0.0624,  0.0513,  0.0431,  0.0367,  0.0317,  0.0277,  0.0244,  0.0217,  0.0194,  0.0175,  0.0158,  0.0144,  0.0132,  0.0121,  0.0112,  0.0103,  0.0096,  0.0089,  0.0083,  0.0078,  0.0073,  0.0069,  0.0065,  0.0061,  0.0058,  0.0055, }, 
+{  0.1547,  0.1190,  0.0951,  0.0781,  0.0655,  0.0559,  0.0484,  0.0423,  0.0374,  0.0334,  0.0300,  0.0271,  0.0246,  0.0225,  0.0206,  0.0190,  0.0176,  0.0163,  0.0152,  0.0142,  0.0133,  0.0125,  0.0117,  0.0110,  0.0104,  0.0099,  0.0093,  0.0089,  0.0084, }, 
+{  0.1726,  0.1361,  0.1110,  0.0928,  0.0791,  0.0685,  0.0600,  0.0531,  0.0475,  0.0427,  0.0387,  0.0353,  0.0323,  0.0297,  0.0275,  0.0255,  0.0237,  0.0221,  0.0207,  0.0195,  0.0183,  0.0172,  0.0163,  0.0154,  0.0146,  0.0139,  0.0132,  0.0126,  0.0120, }, 
+{  0.1881,  0.1513,  0.1255,  0.1065,  0.0919,  0.0805,  0.0713,  0.0637,  0.0574,  0.0521,  0.0476,  0.0437,  0.0403,  0.0373,  0.0347,  0.0323,  0.0303,  0.0284,  0.0267,  0.0252,  0.0238,  0.0225,  0.0214,  0.0203,  0.0193,  0.0184,  0.0176,  0.0168,  0.0161, }, 
+{  0.2017,  0.1649,  0.1387,  0.1191,  0.1040,  0.0919,  0.0821,  0.0740,  0.0672,  0.0614,  0.0564,  0.0521,  0.0483,  0.0450,  0.0420,  0.0394,  0.0370,  0.0349,  0.0329,  0.0312,  0.0296,  0.0281,  0.0268,  0.0255,  0.0244,  0.0233,  0.0223,  0.0214,  0.0205, }, 
+{  0.2138,  0.1771,  0.1507,  0.1308,  0.1152,  0.1027,  0.0924,  0.0839,  0.0767,  0.0705,  0.0651,  0.0604,  0.0563,  0.0527,  0.0494,  0.0465,  0.0439,  0.0415,  0.0393,  0.0374,  0.0356,  0.0339,  0.0324,  0.0310,  0.0296,  0.0284,  0.0273,  0.0262,  0.0252, }, 
+{  0.2246,  0.1883,  0.1618,  0.1416,  0.1257,  0.1128,  0.1022,  0.0933,  0.0857,  0.0792,  0.0735,  0.0686,  0.0642,  0.0603,  0.0568,  0.0536,  0.0508,  0.0482,  0.0458,  0.0436,  0.0416,  0.0398,  0.0381,  0.0365,  0.0351,  0.0337,  0.0324,  0.0313,  0.0301, }, 
+{  0.2347,  0.1986,  0.1721,  0.1517,  0.1355,  0.1224,  0.1115,  0.1023,  0.0945,  0.0877,  0.0817,  0.0765,  0.0719,  0.0677,  0.0640,  0.0606,  0.0576,  0.0548,  0.0522,  0.0499,  0.0477,  0.0458,  0.0439,  0.0422,  0.0406,  0.0391,  0.0377,  0.0364,  0.0352, }, 
+{  0.2448,  0.2086,  0.1819,  0.1614,  0.1450,  0.1316,  0.1204,  0.1110,  0.1029,  0.0958,  0.0897,  0.0842,  0.0793,  0.0750,  0.0711,  0.0675,  0.0643,  0.0613,  0.0586,  0.0561,  0.0538,  0.0517,  0.0497,  0.0479,  0.0462,  0.0445,  0.0430,  0.0416,  0.0403, }, 
+}; 
+
+static float interpolatePixels (float V00, float V10, float V01, float V11, float dx, float dy) {
+    // bilinear interpolation
+    float rx = 1.0 - dx;
+    float ry = 1.0 - dy;
+    float value = V00*rx*ry + V10*dx*ry + V01*rx*dy + V11*dx*dy;
+    return value;
+}
+
Index: branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_SERSIC.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_SERSIC.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/models/pmModel_SERSIC.c	(revision 33415)
@@ -13,8 +13,14 @@
    * PM_PAR_XPOS 2  - X center of object
    * PM_PAR_YPOS 3  - Y center of object
-   * PM_PAR_SXX 4   - X^2 term of elliptical contour (sqrt(2) / SigmaX)
-   * PM_PAR_SYY 5   - Y^2 term of elliptical contour (sqrt(2) / SigmaY)
+   * PM_PAR_SXX 4   - X^2 term of elliptical contour (SigmaX / sqrt(2))
+   * PM_PAR_SYY 5   - Y^2 term of elliptical contour (SigmaY / sqrt(2))
    * PM_PAR_SXY 6   - X*Y term of elliptical contour
    * PM_PAR_7   7   - normalized sersic parameter
+
+   * note that a Sersic model is usually defined in terms of R_e, the half-light radius.  This
+     construction does not include a factor of 2 in the X^2 term, etc, like for a Gaussian.
+     Conversion from SXX, SYY, SXY to R_major, R_minor, theta can be done by using setting:
+     shape.sx = SXX / sqrt(2), shape.sy = SYY / sqrt(2), shape.sxy = SXY, then calling
+     psEllipseShapeToAxes, and multiplying the values of axes.major, axes.minor by sqrt(2)
 
    note that a standard sersic model uses exp(-K*(z^(1/2n) - 1).  the additional elements (K,
@@ -85,4 +91,6 @@
 static bool limitsApply = true;         // Apply limits?
 
+# include "pmModel_SERSIC.CP.h"
+
 psF32 PM_MODEL_FUNC (psVector *deriv,
                      const psVector *params,
@@ -97,17 +105,89 @@
     psF32 z  = PS_SQR(px) + PS_SQR(py) + PAR[PM_PAR_SXY]*X*Y;
 
-    // XXX if the elliptical contour is defined in valid way, this step should not be required.
-    // other models (like PGAUSS) don't use fractional powers, and thus do not have NaN values
-    // for negative values of z
-    // XXX use an assert here to force the elliptical parameters to be correctly determined
-    // if (z < 0) z = 0;
-    assert (z >= 0);
+    // If the elliptical contour is defined in a valid way, we should never trigger this
+    // assert.  Other models (like PGAUSS) don't use fractional powers, and thus do not have
+    // NaN values for negative values of z
+    psAssert (z >= 0, "do not allow negative z values in model");
 
     float index = 0.5 / PAR[PM_PAR_7];
+    float par7 = PAR[PM_PAR_7];
     float bn = 1.9992*index - 0.3271;
     float Io = exp(bn);
 
-    psF32 f2 = bn*pow(z,PAR[PM_PAR_7]);
+    psF32 f2 = bn*pow(z,par7);
     psF32 f1 = Io*exp(-f2);
+
+    psF32 radius = hypot(X, Y);
+    if (radius < 1.0) {
+
+	// ** use bilinear interpolation to the given location from the 4 surrounding pixels centered on the object center
+
+	// first, use Rmajor and index to find the central pixel flux (fraction of total flux)
+	psEllipseShape shape;
+
+	shape.sx  = PAR[PM_PAR_SXX];
+	shape.sy  = PAR[PM_PAR_SYY];
+	shape.sxy = PAR[PM_PAR_SXY];
+
+	// for a non-circular Sersic, the flux of the Rmajor equivalent is scaled by the AspectRatio
+	psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
+
+	// get the central pixel flux from the lookup table
+	float xPix = (axes.major - centralPixelXo) / centralPixeldX;
+	xPix = PS_MIN (PS_MAX(xPix, 0), centralPixelNX - 1);
+	float yPix = (index - centralPixelYo) / centralPixeldY;
+	yPix = PS_MIN (PS_MAX(yPix, 0), centralPixelNY - 1);
+
+	// the integral of a Sersic has an analytical form as follows:
+	float logGamma = lgamma(2.0*index);
+	float bnFactor = pow(bn, 2.0*index);
+	float norm = 2.0 * M_PI * PS_SQR(axes.major) * index * exp(bn) * exp(logGamma) / bnFactor;
+
+	// XXX interpolate to get the value
+	// XXX for the moment, just integerize
+	// XXX I need to multiply by the integrated flux to get the flux in the central pixel
+	float Vcenter = centralPixel[(int)yPix][(int)xPix] * norm;
+	
+	float px1 = 1.0 / PAR[PM_PAR_SXX];
+	float py1 = 1.0 / PAR[PM_PAR_SYY];
+	float z10 = PS_SQR(px1);
+	float z01 = PS_SQR(py1);
+
+	// which pixels do we need for this interpolation?
+	// (I do not keep state information, so I don't know anything about other evaluations of nearby pixels...)
+	if ((X >= 0) && (Y >= 0)) {
+	    float z11 = z10 + z01 + PAR[PM_PAR_SXY]; // X * Y positive
+	    float V00 = Vcenter;
+	    float V10 = Io*exp(-bn*pow(z10,par7));
+	    float V01 = Io*exp(-bn*pow(z01,par7));
+	    float V11 = Io*exp(-bn*pow(z11,par7));
+	    f1 = interpolatePixels(V00, V10, V01, V11, X, Y);
+	}
+	if ((X < 0) && (Y >= 0)) {
+	    float z11 = z10 + z01 - PAR[PM_PAR_SXY]; // X * Y negative
+	    float V00 = Io*exp(-bn*pow(z10,par7));
+	    float V10 = Vcenter;
+	    float V01 = Io*exp(-bn*pow(z11,par7));
+	    float V11 = Io*exp(-bn*pow(z01,par7));
+	    f1 = interpolatePixels(V00, V10, V01, V11, (1.0 + X), Y);
+	}
+	if ((X >= 0) && (Y < 0)) {
+	    float z11 = z10 + z01 - PAR[PM_PAR_SXY]; // X * Y negative
+	    float V00 = Io*exp(-bn*pow(z01,par7));
+	    float V10 = Io*exp(-bn*pow(z11,par7));
+	    float V01 = Vcenter;
+	    float V11 = Io*exp(-bn*pow(z10,par7));
+	    f1 = interpolatePixels(V00, V10, V01, V11, X, (1.0 + Y));
+	}
+	if ((X < 0) && (Y < 0)) {
+	    float z11 = z10 + z01 + PAR[PM_PAR_SXY]; // X * Y positive
+	    float V00 = Io*exp(-bn*pow(z11,par7));
+	    float V10 = Io*exp(-bn*pow(z10,par7));
+	    float V01 = Io*exp(-bn*pow(z01,par7));
+	    float V11 = Vcenter;
+	    f1 = interpolatePixels(V00, V10, V01, V11, (1.0 + X), (1.0 + Y));
+	}
+    }   
+
     psF32 z0 = PAR[PM_PAR_I0]*f1;
     psF32 f0 = PAR[PM_PAR_SKY] + z0;
@@ -121,10 +201,11 @@
         psF32 *dPAR = deriv->data.F32;
 
-        // gradient is infinite for z = 0; saturate at z = 0.01
-        psF32 z1 = (z < 0.01) ? z0*bn*PAR[PM_PAR_7]*pow(0.01,PAR[PM_PAR_7] - 1.0) : z0*bn*PAR[PM_PAR_7]*pow(z,PAR[PM_PAR_7] - 1.0);
-
         dPAR[PM_PAR_SKY]  = +1.0;
         dPAR[PM_PAR_I0]   = +f1;
-        dPAR[PM_PAR_7]    = (z < 0.01) ? -z0*pow(0.01,PAR[PM_PAR_7])*log(0.01) : -z0*f2*log(z);
+
+        // gradient is infinite for z = 0; saturate at z = 0.01
+        psF32 z1 = (z < 0.01) ? z0*bn*par7*pow(0.01,par7 - 1.0) : z0*bn*par7*pow(z,par7 - 1.0);
+
+        dPAR[PM_PAR_7]    = (z < 0.01) ? -z0*pow(0.01,par7)*log(0.01) : -z0*f2*log(z);
 	dPAR[PM_PAR_7]   *= 3.0;
 
@@ -269,8 +350,4 @@
     float Io = exp(0.5*bn);
 
-    // XXX do we need this factor of sqrt(2)?
-    // float Sxx = PS_MAX(0.5, M_SQRT2*shape.sx);
-    // float Syy = PS_MAX(0.5, M_SQRT2*shape.sy);
-
     float Sxx = PS_MAX(0.5, shape.sx);
     float Syy = PS_MAX(0.5, shape.sy);
@@ -294,37 +371,28 @@
 }
 
+// A sersic model has an integral flux which can be analytically represented
 psF64 PM_MODEL_FLUX (const psVector *params)
 {
-    float z, norm;
     psEllipseShape shape;
 
     psF32 *PAR = params->data.F32;
 
-    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
-    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sx  = PAR[PM_PAR_SXX];
+    shape.sy  = PAR[PM_PAR_SYY];
     shape.sxy = PAR[PM_PAR_SXY];
 
-    // Area is equivalent to 2 pi sigma^2
+    // for a non-circular Sersic, the flux of the Rmajor equivalent is scaled by the AspectRatio
     psEllipseAxes axes = psEllipseShapeToAxes (shape, 20.0);
-    psF64 Area = 2.0 * M_PI * axes.major * axes.minor;
-
-    // the area needs to be multiplied by the integral of f(z)
-    norm = 0.0;
-
-    # define DZ 0.25
-
-    float f0 = 1.0;
-    float f1, f2;
-    for (z = DZ; z < 150; z += DZ) {
-        // f1 = 1.0 / (1 + PAR[PM_PAR_7]*z + pow(z, 2.25));
-	f1 = exp(-pow(z,PAR[PM_PAR_7]));
-        z += DZ;
-	f2 = exp(-pow(z,PAR[PM_PAR_7]));
-        norm += f0 + 4*f1 + f2;
-        f0 = f2;
-    }
-    norm *= DZ / 3.0;
-
-    psF64 Flux = PAR[PM_PAR_I0] * Area * norm;
+    float AspectRatio = axes.minor / axes.major;
+
+    float index = 0.5 / PAR[PM_PAR_7];
+    float bn = 1.9992*index - 0.3271;
+
+    // the integral of a Sersic has an analytical form as follows:
+    float logGamma = lgamma(2.0*index);
+    float bnFactor = pow(bn, 2.0*index);
+    float norm = 2.0 * M_PI * PS_SQR(axes.major) * index * exp(bn) * exp(logGamma) / bnFactor;
+    
+    psF64 Flux = PAR[PM_PAR_I0] * norm * AspectRatio;
 
     return(Flux);
@@ -346,6 +414,6 @@
         return (1.0);
 
-    shape.sx  = PAR[PM_PAR_SXX] / M_SQRT2;
-    shape.sy  = PAR[PM_PAR_SYY] / M_SQRT2;
+    shape.sx  = PAR[PM_PAR_SXX];
+    shape.sy  = PAR[PM_PAR_SYY];
     shape.sxy = PAR[PM_PAR_SXY];
 
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmFootprintCullPeaks.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmFootprintCullPeaks.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmFootprintCullPeaks.c	(revision 33415)
@@ -118,6 +118,8 @@
 	    threshbounds->data.F32[i] = 0.25*beta2*PS_SQR(i) + min_threshold;	    
 	}
-	psAssert(threshbounds->data.F32[threshbounds->n-1] > maxFlux, "upper limit does not include max flux");
-
+        if (threshbounds->data.F32[threshbounds->n-1] > maxFlux) {
+            psWarning ("upper limit: %f does not include max flux: %f",
+                    threshbounds->data.F32[threshbounds->n-1], maxFlux);
+        }
 	psHistogram *threshist = psHistogramAllocGeneric(threshbounds);
 
@@ -181,4 +183,5 @@
 	    if (!myFP->n) {
 		psWarning ("empty footprint?");
+		psFree (myFP);
 		continue;
 	    }
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmModelUtils.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmModelUtils.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmModelUtils.c	(revision 33415)
@@ -122,4 +122,9 @@
     if (!isfinite(axes.theta)) return false;
 
+    // Mxx, Mxy, Myy define the elliptical shape, but Mrf defines the width 
+    float scale = (isfinite(moments->Mrf) && (moments->Mrf > 0.0)) ? moments->Mrf / axes.major : 1.0;
+    axes.major *= scale;
+    axes.minor *= scale;
+
     psEllipseShape shape = psEllipseAxesToShape (axes);
 
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmMoments.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmMoments.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmMoments.c	(revision 33415)
@@ -32,4 +32,8 @@
     tmp->Mrf = 0.0;
     tmp->Mrh = 0.0;
+
+    tmp->KronCore = 0.0;
+    tmp->KronCoreErr = 0.0;
+
     tmp->KronFlux = 0.0;
     tmp->KronFluxErr = 0.0;
@@ -37,4 +41,8 @@
     tmp->KronFinner = 0.0;
     tmp->KronFouter = 0.0;
+
+    tmp->KronFluxPSF = 0.0;
+    tmp->KronFluxPSFErr = 0.0;
+    tmp->KronRadiusPSF = 0.0;
 
     tmp->Mx = 0.0;
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmMoments.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmMoments.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmMoments.h	(revision 33415)
@@ -51,4 +51,8 @@
     int nPixels;  ///< Number of pixels used.
 
+    float KronFluxPSF; ///< Kron Flux using PSF-optimized window
+    float KronFluxPSFErr; ///< Kron Flux Error using PSF-optimized window
+    float KronRadiusPSF; ///< Kron Radius using PSF-optimized window (Flux in 2.5 Radius)
+
     float KronCore;    ///< flux in r < 1.0*Mrf
     float KronCoreErr;    ///< error on flux in r < 1.0*Mrf
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmPCM_MinimizeChisq.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmPCM_MinimizeChisq.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmPCM_MinimizeChisq.c	(revision 33415)
@@ -45,4 +45,5 @@
 # define USE_FFT 1
 # define PRE_CONVOLVE 1
+# define TESTCOPY 0
 
 bool pmPCM_MinimizeChisq (
@@ -93,5 +94,9 @@
 	psFree (pcm->psfFFT);
     }
-    pcm->psfFFT = psImageConvolveKernelInit(pcm->modelFlux, pcm->psf);
+# if (!TESTCOPY)
+    if (!pcm->use1Dgauss) {
+	pcm->psfFFT = psImageConvolveKernelInit(pcm->modelFlux, pcm->psf);
+    }
+# endif
 # endif    
 
@@ -285,6 +290,6 @@
 
             // Convert i/j to image space:
-            coord->data.F32[0] = (psF32) (j + source->pixels->col0);
-            coord->data.F32[1] = (psF32) (i + source->pixels->row0);
+            coord->data.F32[0] = (psF32) (j + 0.5 + source->pixels->col0);
+            coord->data.F32[1] = (psF32) (i + 0.5 + source->pixels->row0);
 
             pcm->modelFlux->data.F32[i][j] = pcm->modelConv->modelFunc (deriv, params, coord);
@@ -309,5 +314,21 @@
 # if (PRE_CONVOLVE)
     // convolve model image and derivative images with pre-convolved kernel
-    psImageConvolveKernel (pcm->modelConvFlux, pcm->modelFlux, NULL, 0, pcm->psfFFT);
+
+// XXX for a test, just copy, rather than convolve
+# if (TESTCOPY)
+    psImageCopy (pcm->modelConvFlux, pcm->modelFlux, pcm->modelFlux->type.type);
+# else
+    if (pcm->use1Dgauss) {
+	// do not use the threaded, mask-aware version of this code (psImageSmoothMaskPixelsThread):
+	// * the model flux is not masked
+	// * threading takes place above this level
+	pcm->modelConvFlux = psImageCopy (pcm->modelConvFlux, pcm->modelFlux, pcm->modelFlux->type.type);
+	psImageSmooth_PreAlloc_F32 (pcm->modelConvFlux, pcm->smdata);
+	// psImageSmooth (pcm->modelConvFlux, pcm->sigma, pcm->nsigma);
+    } else {
+	psImageConvolveKernel (pcm->modelConvFlux, pcm->modelFlux, NULL, 0, pcm->psfFFT);
+    }
+# endif
+
     for (int n = 0; n < pcm->dmodelsFlux->n; n++) {
         if (pcm->dmodelsFlux->data[n] == NULL) continue;
@@ -315,5 +336,18 @@
         psImage *dmodel = pcm->dmodelsFlux->data[n];
         psImage *dmodelConv = pcm->dmodelsConvFlux->data[n];
-        psImageConvolveKernel (dmodelConv, dmodel, NULL, 0, pcm->psfFFT);
+# if (TESTCOPY)
+	psImageCopy (dmodelConv, dmodel, dmodel->type.type);
+# else
+	if (pcm->use1Dgauss) {
+	    // do not use the threaded, mask-aware version of this code (psImageSmoothMaskPixelsThread):
+	    // * the model flux is not masked
+	    // * threading takes place above this level
+	    dmodelConv = psImageCopy (dmodelConv, dmodel, dmodel->type.type);
+	    psImageSmooth_PreAlloc_F32 (dmodelConv, pcm->smdata);
+	    // psImageSmooth (dmodelConv, pcm->sigma, pcm->nsigma);
+	} else {
+	    psImageConvolveKernel (dmodelConv, dmodel, NULL, 0, pcm->psfFFT);
+	}
+# endif
     }
 # else
@@ -325,5 +359,15 @@
         psImage *dmodel = pcm->dmodelsFlux->data[n];
         psImage *dmodelConv = pcm->dmodelsConvFlux->data[n];
-        psImageConvolveFFT (dmodelConv, dmodel, NULL, 0, pcm->psf);
+
+	if (pcm->use1Dgauss) {
+	    // do not use the threaded, mask-aware version of this code (psImageSmoothMaskPixelsThread):
+	    // * the model flux is not masked
+	    // * threading takes place above this level
+	    dmodelConv = psImageCopy (dmodelConv, dmodel, dmodel->type.type);
+	    psImageSmooth_PreAlloc_F32 (dmodelConv, pcm->smdata);
+	    // psImageSmooth (dmodelConv, pcm->sigma, pcm->nsigma);
+	} else {
+	    psImageConvolveFFT (dmodelConv, dmodel, NULL, 0, pcm->psf);
+	}
     }
 # endif // PRE-CONVOLVE
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmPCMdata.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmPCMdata.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmPCMdata.c	(revision 33415)
@@ -41,4 +41,7 @@
 #include "pmPCMdata.h"
 
+# define USE_DELTA_PSF 0
+# define USE_1D_GAUSS 1
+
 static void pmPCMdataFree (pmPCMdata *pcm) {
 
@@ -54,4 +57,5 @@
     psFree (pcm->psfFFT);
     psFree (pcm->constraint);
+    psFree (pcm->smdata); // pre-allocated data for psImageSmooth_PreAlloc
     return;
 }
@@ -88,4 +92,10 @@
     pcm->constraint = NULL;
     pcm->nDOF = 0;
+
+    // full convolution with the PSF is expensive.  if we have to save time, we can do a 1D
+    // convolution with a Gaussian approximation to the kernel
+    pcm->use1Dgauss = false;
+    pcm->nsigma = 3.0; 
+    pcm->sigma = 1.0; // this should be set to something sensible when the psf is known
 
     return pcm;
@@ -257,4 +267,28 @@
     pcm->nDOF = nPix - nParams;
 
+# if (USE_1D_GAUSS)
+    pmModel *modelPSF = source->modelPSF;
+    psAssert (modelPSF, "psf model must be defined");
+    
+    psEllipseShape shape;
+    psEllipseAxes axes;
+
+    shape.sx  = modelPSF->params->data.F32[PM_PAR_SXX];
+    shape.sy  = modelPSF->params->data.F32[PM_PAR_SYY];
+    shape.sxy = modelPSF->params->data.F32[PM_PAR_SXY];
+    axes = psEllipseShapeToAxes (shape, 20.0);
+    
+    float FWHM_MAJOR = 2*modelPSF->modelRadius (modelPSF->params, 0.5*modelPSF->params->data.F32[PM_PAR_I0]);
+    float FWHM_MINOR = FWHM_MAJOR * (axes.minor / axes.major);
+
+    pcm->use1Dgauss = true;
+    pcm->sigma = 0.5 * (FWHM_MAJOR + FWHM_MINOR) / 2.35;
+    pcm->nsigma = 2.0;
+
+    pcm->smdata = psImageSmooth_PreAlloc_DataAlloc (source->pixels, pcm->sigma, pcm->nsigma);
+# else
+    pcm->smdata = NULL;
+# endif
+
     return pcm;
 }
@@ -364,6 +398,68 @@
 	    pcm->dmodelsConvFlux->data[n] = psImageCopy (pcm->dmodelsConvFlux->data[n], source->pixels, PS_TYPE_F32);
 	}
+	psFree(pcm->smdata);
+	pcm->smdata = psImageSmooth_PreAlloc_DataAlloc (source->pixels, pcm->sigma, pcm->nsigma);
     }
 
     return true;
 }
+
+// construct a realization of the source model
+bool pmPCMCacheModel (pmSource *source, psImageMaskType maskVal, int psfSize) {
+
+    PS_ASSERT_PTR_NON_NULL(source, false);
+
+    // select appropriate model
+    pmModel *model = pmSourceGetModel (NULL, source);
+    if (model == NULL) return false;  // model must be defined
+
+    // if we already have a cached image, re-use that memory
+    source->modelFlux = psImageCopy (source->modelFlux, source->pixels, PS_TYPE_F32);
+    psImageInit (source->modelFlux, 0.0);
+
+    // modelFlux always has unity normalization (I0 = 1.0)
+    pmModelAdd (source->modelFlux, source->maskObj, model, PM_MODEL_OP_FULL | PM_MODEL_OP_NORM, maskVal);
+
+    // convolve the model image with the PSF
+    if (USE_1D_GAUSS) {
+	// do not use the threaded, mask-aware version of this code (psImageSmoothMaskPixelsThread):
+	// * the model flux is not masked
+	// * threading takes place above this level
+	
+	// define the Gauss parameters from the psf
+	pmModel *modelPSF = source->modelPSF;
+	psAssert (modelPSF, "psf model must be defined");
+    
+	psEllipseShape shape;
+	psEllipseAxes axes;
+
+	shape.sx  = modelPSF->params->data.F32[PM_PAR_SXX];
+	shape.sy  = modelPSF->params->data.F32[PM_PAR_SYY];
+	shape.sxy = modelPSF->params->data.F32[PM_PAR_SXY];
+	axes = psEllipseShapeToAxes (shape, 20.0);
+    
+	float FWHM_MAJOR = 2*modelPSF->modelRadius (modelPSF->params, 0.5*modelPSF->params->data.F32[PM_PAR_I0]);
+	float FWHM_MINOR = FWHM_MAJOR * (axes.minor / axes.major);
+
+	float sigma = 0.5 * (FWHM_MAJOR + FWHM_MINOR) / 2.35;
+	float nsigma = 2.0;
+
+	psImageSmooth (source->modelFlux, sigma, nsigma);
+    } else {
+	// make sure we save a cached copy of the psf flux
+	pmSourceCachePSF (source, maskVal);
+
+	// convert the cached cached psf model for this source to a psKernel
+	psKernel *psf = pmPCMkernelFromPSF (source, psfSize);
+	if (!psf) {
+	    // NOTE: this only happens if the source is too close to an edge
+	    model->flags |= PM_MODEL_STATUS_BADARGS;
+	    return NULL;
+	}
+
+	// XXX not sure if I can place the output on top of the input
+	psImageConvolveFFT (source->modelFlux, source->modelFlux, NULL, 0, psf);
+    }
+    return true;
+}
+
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmPCMdata.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmPCMdata.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmPCMdata.h	(revision 33415)
@@ -35,4 +35,10 @@
     int nPar;
     int nDOF;
+
+    bool use1Dgauss;
+    float sigma;
+    float nsigma;
+
+    psImageSmooth_PreAlloc_Data *smdata;
 } pmPCMdata;
 
@@ -90,4 +96,5 @@
 bool pmSourceFitPCM (pmPCMdata *pcm, pmSource *source, pmSourceFitOptions *fitOptions, psImageMaskType maskVal, psImageMaskType markVal, int psfSize);
 
+bool pmPCMCacheModel (pmSource *source, psImageMaskType maskVal, int psfSize);
 
 /// @}
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmPSF.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmPSF.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmPSF.c	(revision 33415)
@@ -281,4 +281,5 @@
 // convert the parameters used in the fitted source model
 // to the parameters used in the 2D PSF model
+// XXX this function may be invalid for SERSIC, DEV, EXP models (SQRT2 not used?)
 bool pmPSF_FitToModel (psF32 *fittedPar, float minMinorAxis)
 {
@@ -307,4 +308,5 @@
 // convert the PSF parameters used in the 2D PSF model fit into the
 // parameters used in the source model
+// XXX this function may be invalid for SERSIC, DEV, EXP models (SQRT2 not used?)
 psEllipsePol pmPSF_ModelToFit (psF32 *modelPar)
 {
@@ -329,5 +331,5 @@
 // convert the parameters used in the fitted source model to the psEllipseAxes representation
 // (major,minor,theta)
-psEllipseAxes pmPSF_ModelToAxes (psF32 *modelPar, double maxAR)
+psEllipseAxes pmPSF_ModelToAxes (psF32 *modelPar, double maxAR, pmModelType type)
 {
     psEllipseShape shape;
@@ -336,10 +338,21 @@
     axes.minor = NAN;
     axes.theta = NAN;
-//   XXX: must assert non-NULL input parameter
+    //   XXX: must assert non-NULL input parameter
     PS_ASSERT_PTR_NON_NULL(modelPar, axes);
 
-    shape.sx  = modelPar[PM_PAR_SXX] / M_SQRT2;
-    shape.sy  = modelPar[PM_PAR_SYY] / M_SQRT2;
-    shape.sxy = modelPar[PM_PAR_SXY];
+    bool useReff = true;
+    useReff |= (type == pmModelClassGetType ("PS_MODEL_SERSIC"));
+    useReff |= (type == pmModelClassGetType ("PS_MODEL_DEV"));
+    useReff |= (type == pmModelClassGetType ("PS_MODEL_EXP"));
+
+    if (useReff) {
+	shape.sx  = modelPar[PM_PAR_SXX];
+	shape.sy  = modelPar[PM_PAR_SYY];
+	shape.sxy = modelPar[PM_PAR_SXY];
+    } else {
+	shape.sx  = modelPar[PM_PAR_SXX] / M_SQRT2;
+	shape.sy  = modelPar[PM_PAR_SYY] / M_SQRT2;
+	shape.sxy = modelPar[PM_PAR_SXY];
+    }
 
     if ((shape.sx == 0) || (shape.sy == 0)) {
@@ -357,5 +370,5 @@
 // convert the psEllipseAxes representation (major,minor,theta) to the parameters used in the
 // fitted source model
-bool pmPSF_AxesToModel (psF32 *modelPar, psEllipseAxes axes)
+bool pmPSF_AxesToModel (psF32 *modelPar, psEllipseAxes axes, pmModelType type)
 {
     PS_ASSERT_PTR_NON_NULL(modelPar, false);
@@ -370,8 +383,18 @@
     psEllipseShape shape = psEllipseAxesToShape (axes);
 
-    modelPar[PM_PAR_SXX] = shape.sx * M_SQRT2;
-    modelPar[PM_PAR_SYY] = shape.sy * M_SQRT2;
-    modelPar[PM_PAR_SXY] = shape.sxy;
-
+    bool useReff = true;
+    useReff |= pmModelClassGetType ("PS_MODEL_SERSIC");
+    useReff |= pmModelClassGetType ("PS_MODEL_DEV");
+    useReff |= pmModelClassGetType ("PS_MODEL_EXP");
+
+    if (useReff) {
+	modelPar[PM_PAR_SXX] = shape.sx;
+	modelPar[PM_PAR_SYY] = shape.sy;
+	modelPar[PM_PAR_SXY] = shape.sxy;
+    } else {
+	modelPar[PM_PAR_SXX] = shape.sx * M_SQRT2;
+	modelPar[PM_PAR_SYY] = shape.sy * M_SQRT2;
+	modelPar[PM_PAR_SXY] = shape.sxy;
+    }
     return true;
 }
@@ -438,5 +461,5 @@
 # if (0)
     psF32 *params = model->params->data.F32; // Model parameters
-    psEllipseAxes axes = pmPSF_ModelToAxes(params, MAX_AXIS_RATIO); // Ellipse axes
+    psEllipseAxes axes = pmPSF_ModelToAxes(params, MAX_AXIS_RATIO, model->type); // Ellipse axes
 
     // Curiously, the minor axis can be larger than the major axis, so need to check.
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmPSF.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmPSF.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmPSF.h	(revision 33415)
@@ -106,9 +106,9 @@
 pmPSF *pmPSFBuildSimple (char *typeName, float sxx, float syy, float sxy, ...);
 
-bool pmPSF_AxesToModel (psF32 *modelPar, psEllipseAxes axes);
+bool pmPSF_AxesToModel (psF32 *modelPar, psEllipseAxes axes, pmModelType type);
 bool pmPSF_FitToModel (psF32 *fittedPar, float minMinorAxis);
 
 psEllipsePol pmPSF_ModelToFit (psF32 *modelPar);
-psEllipseAxes pmPSF_ModelToAxes (psF32 *modelPar, double maxAR);
+psEllipseAxes pmPSF_ModelToAxes (psF32 *modelPar, double maxAR, pmModelType type);
 
 /// Calculate FWHM value from a PSF
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmPeaks.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmPeaks.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmPeaks.c	(revision 33415)
@@ -137,5 +137,9 @@
 *****************************************************************************/
 static void peakFree(pmPeak *tmp)
-{} //
+{
+    if (!tmp) return;
+    psFree (tmp->saddlePoints);
+    return;
+}
 
 pmPeak *pmPeakAlloc(psS32 x,
@@ -162,4 +166,5 @@
     tmp->type = type;
     tmp->footprint = NULL;
+    tmp->saddlePoints = NULL;
 
     psMemSetDeallocator(tmp, (psFreeFunc) peakFree);
@@ -185,4 +190,5 @@
     out->assigned        = in->assigned;
     out->type      	 = in->type;
+    out->footprint       = in->footprint;
 
     return true;
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmPeaks.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmPeaks.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmPeaks.h	(revision 33415)
@@ -71,5 +71,6 @@
     bool assigned;                      ///< is peak assigned to a source?
     pmPeakType type;                    ///< Description of peak.
-    pmFootprint *footprint;		///< reference to containing footprint
+    pmFootprint *footprint;		///< reference to containing footprint (just a view, not a memcopy)
+    psArray *saddlePoints;		///< set of saddle points between this peak and near neighbors
 }
 pmPeak;
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSource.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSource.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSource.c	(revision 33415)
@@ -58,4 +58,5 @@
     psFree(tmp->modelEXT);
     psFree(tmp->modelFits);
+    psFree(tmp->extFitPars);
     psFree(tmp->extpars);
     psFree(tmp->moments);
@@ -119,4 +120,6 @@
     source->modelEXT = NULL;
     source->modelFits = NULL;
+    source->extFitPars = NULL;
+
     source->type = PM_SOURCE_TYPE_UNKNOWN;
     source->mode = PM_SOURCE_MODE_DEFAULT;
@@ -136,4 +139,9 @@
     source->apFluxErr 	     = NAN; 
 
+    source->windowRadius     = NAN;
+    source->skyRadius  	     = NAN;
+    source->skyFlux    	     = NAN;
+    source->skySlope   	     = NAN;
+
     source->pixWeightNotBad  = NAN;
     source->pixWeightNotPoor = NAN;
@@ -196,8 +204,11 @@
     // NOTE : because of the const id element, we cannot just assign *source = *in
 
+    source->imageID          = in->imageID;
+
     source->type     	     = in->type;
     source->mode     	     = in->mode;
     source->mode2    	     = in->mode2;
     source->tmpFlags 	     = in->tmpFlags;
+
     source->psfMag     	     = in->psfMag;
     source->psfMagErr 	     = in->psfMagErr;
@@ -210,6 +221,13 @@
     source->apFlux    	     = in->apFlux;
     source->apFluxErr 	     = in->apFluxErr;
+
+    source->windowRadius     = in->windowRadius;
+    source->skyRadius  	     = in->skyRadius;  	
+    source->skyFlux    	     = in->skyFlux;    	
+    source->skySlope   	     = in->skySlope;   	
+
     source->pixWeightNotBad  = in->pixWeightNotBad;
     source->pixWeightNotPoor = in->pixWeightNotPoor;
+
     source->psfChisq         = in->psfChisq;
     source->crNsigma         = in->crNsigma;
@@ -252,4 +270,5 @@
     }
     mySource->region   = srcRegion;
+    mySource->windowRadius = Radius;
 
     return true;
@@ -321,4 +340,5 @@
         mySource->psfImage = NULL;
     }
+    mySource->windowRadius = Radius;
     return extend;
 }
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSource.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSource.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSource.h	(revision 33415)
@@ -36,4 +36,8 @@
     PM_SOURCE_TMPF_MOMENTS_MEASURED  = 0x0010,
     PM_SOURCE_TMPF_CANDIDATE_PSFSTAR = 0x0020,
+    PM_SOURCE_TMPF_RADIAL_KEEP       = 0x0040,
+    PM_SOURCE_TMPF_RADIAL_SKIP       = 0x0080,
+    PM_SOURCE_TMPF_PETRO_KEEP        = 0x0100,
+    PM_SOURCE_TMPF_PETRO_SKIP        = 0x0200,
 } pmSourceTmpF;
 
@@ -76,4 +80,5 @@
     pmModel *modelEXT;                  ///< EXT Model fit used for subtraction (parameters and type)
     psArray *modelFits;                 ///< collection of extended source models (best == modelEXT)
+    psArray *extFitPars;		///< extra extended fit parameters
     pmSourceType type;                  ///< Best identification of object.
     pmSourceMode mode;                  ///< analysis flags set for object.
@@ -91,4 +96,9 @@
     float apFlux;                       ///< apFlux corresponding to psfMag or extMag (depending on type)
     float apFluxErr;                    ///< apFluxErr corresponding to psfMag or extMag (depending on type)
+
+    float windowRadius;			///< size of box used for full analysis
+    float skyRadius;			///< radius at which profile hits local sky (or goes flat)
+    float skyFlux;			///< mean flux per pixel in aperture at which profile hits local sky (or goes flat)
+    float skySlope;			///< mean flux slope at which profile hits local sky (or goes flat)
 
     float pixWeightNotBad;              ///< PSF-weighted coverage of unmasked (not BAD) pixels
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceExtendedPars.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceExtendedPars.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceExtendedPars.c	(revision 33415)
@@ -258,2 +258,27 @@
     return ( psMemGetDeallocator(ptr) == (psFreeFunc) pmSourceExtendedFluxFree);
 }
+
+// *** pmSourceExtFitPars describes extra metadata related to an extended fit
+static void pmSourceExtFitParsFree (pmSourceExtFitPars *pars) {
+    return;
+}
+
+pmSourceExtFitPars *pmSourceExtFitParsAlloc (void) {
+
+    pmSourceExtFitPars *pars = (pmSourceExtFitPars *) psAlloc(sizeof(pmSourceExtFitPars));
+    psMemSetDeallocator(pars, (psFreeFunc) pmSourceExtFitParsFree);
+
+    pars->Mxx = NAN;
+    pars->Mxy = NAN;
+    pars->Myy = NAN;
+
+    pars->Mrf    = NAN;
+    pars->Mrh    = NAN;
+
+    pars->apMag  = NAN;
+    pars->krMag  = NAN;
+    pars->psfMag = NAN;
+    pars->peakMag = NAN;
+
+    return pars;
+}
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceExtendedPars.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceExtendedPars.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceExtendedPars.h	(revision 33415)
@@ -67,4 +67,19 @@
 } pmSourceExtendedPars;
 
+// additional measurements related to the model fits
+typedef struct {
+    float Mxx;
+    float Mxy;
+    float Myy;
+    
+    float Mrf;
+    float Mrh;
+
+    float apMag;
+    float krMag;
+    float psfMag;
+    float peakMag;
+} pmSourceExtFitPars;
+
 pmSourceRadialFlux *pmSourceRadialFluxAlloc();
 bool psMemCheckSourceRadialFlux(psPtr ptr);
@@ -92,5 +107,5 @@
 bool pmSourceRadialProfileSortPair(psVector *index, psVector *extra);
 
-
+pmSourceExtFitPars *pmSourceExtFitParsAlloc (void);
 
 /// @}
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceFitModel.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceFitModel.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceFitModel.c	(revision 33415)
@@ -176,7 +176,9 @@
         break;
       case PM_SOURCE_FIT_EXT:
-        // EXT model fits all params (except sky)
-        nParams = params->n - 1;
+        // EXT model fits all shape params and Io (not Xo, Yo, sky)
+        nParams = params->n - 3;
         psVectorInit (constraint->paramMask, 0);
+        constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_XPOS] = 1;
+        constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_YPOS] = 1;
         constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_SKY] = 1;
         break;
@@ -193,11 +195,13 @@
 	break;
       case PM_SOURCE_FIT_NO_INDEX:
-        // PSF model only fits Io, index (PAR7) -- only Io for models with < 8 params
+        // PSF model only fits Io, Sxx, Sxy, Syy
 	psVectorInit (constraint->paramMask, 0);
+        constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_XPOS] = 1;
+        constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_YPOS] = 1;
 	constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_SKY] = 1;
         if (params->n == 7) {
-	    nParams = params->n - 1;
+	    nParams = params->n - 3;
 	} else {
-	    nParams = params->n - 2;
+	    nParams = params->n - 4;
 	    constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[PM_PAR_7] = 1;
 	}
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceFitPCM.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceFitPCM.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceFitPCM.c	(revision 33415)
@@ -48,6 +48,10 @@
 // convolved model image.
 
+# define TIMING 0
+
 bool pmSourceFitPCM (pmPCMdata *pcm, pmSource *source, pmSourceFitOptions *fitOptions, psImageMaskType maskVal, psImageMaskType markVal, int psfSize) {
     
+    if (TIMING) { psTimerStart ("pmSourceFitPCM"); }
+
     psVector *params  = pcm->modelConv->params;
     psVector *dparams  = pcm->modelConv->dparams;
@@ -63,6 +67,13 @@
 
     psImage *covar = psImageAlloc (params->n, params->n, PS_TYPE_F32);
+    // NOTE : 4 allocs to here
 
+    float t1, t2, t3, t4, t5;
+    if (TIMING) { t1 = psTimerMark ("pmSourceFitPCM"); }
+
+    // NOTE : 996 allocs in here
     bool fitStatus = pmPCM_MinimizeChisq (myMin, covar, params, source, pcm);
+    if (TIMING) { t2 = psTimerMark ("pmSourceFitPCM"); }
+
     for (int i = 0; i < dparams->n; i++) {
         if ((pcm->constraint->paramMask != NULL) && pcm->constraint->paramMask->data.PS_TYPE_VECTOR_MASK_DATA[i])
@@ -76,4 +87,5 @@
     }
     psTrace ("psphot", 4, "niter: %d, chisq: %f", myMin->iter, myMin->value);
+    if (TIMING) { t3 = psTimerMark ("pmSourceFitPCM"); }
 
     // renormalize output model image (generated by fitting process)
@@ -97,4 +109,5 @@
 	pmSourceChisqUnsubtracted (source, pcm->modelConv, maskVal);
     }
+    if (TIMING) { t4 = psTimerMark ("pmSourceFitPCM"); }
 
     // set the model success or failure status
@@ -114,4 +127,9 @@
 
     source->mode |= PM_SOURCE_MODE_FITTED; // XXX is this needed?
+    if (TIMING) { t5 = psTimerMark ("pmSourceFitPCM"); }
+
+     if (TIMING) {
+ 	fprintf (stderr, "nIter: %2d, npix: %5d, t1: %6.4f, t2: %6.4f, t3: %6.4f, t4: %6.4f, t5: %6.4f\n", myMin->iter, pcm->nPix, t1, t2, t3, t4, t5);
+     }
 
     psFree(myMin);
@@ -121,4 +139,5 @@
 }
 
+// XXX deprecate this function or merge with the empirical model
 bool pmSourceModelGuessPCM (pmPCMdata *pcm, pmSource *source, psImageMaskType maskVal, psImageMaskType markVal) {
 
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO.c	(revision 33415)
@@ -57,4 +57,7 @@
 #define BLANK_HEADERS "BLANK.HEADERS"   // Name of metadata in camera configuration containing header names
                                         // for putting values into a blank PHU
+static bool pmReadoutReadXSRC(pmFPAfile *file, char * exttype, psMetadata *hduHeader, psString xsrcname, psArray *sources, long *sourceIndex);
+static bool pmReadoutReadXFIT(pmFPAfile *file, char * exttype, psMetadata *hduHeader, psString xfitname, psArray *sources, long *sourceIndex);
+static bool pmReadoutReadXRAD(pmFPAfile *file, pmReadout *readout, char * exttype, psMetadata *hduHeader, psString xfitname, psArray *sources, long *sourceIndex);
 
 // lookup the EXTNAME values used for table data and image header segments
@@ -569,4 +572,5 @@
 	    PM_SOURCES_WRITE("PS1_V2",    CMF_PS1_V2);
 	    PM_SOURCES_WRITE("PS1_V3",    CMF_PS1_V3);
+	    PM_SOURCES_WRITE("PS1_V4",    CMF_PS1_V4);
 	    PM_SOURCES_WRITE("PS1_SV1",   CMF_PS1_SV1);
 	    PM_SOURCES_WRITE("PS1_DV1",   CMF_PS1_DV1);
@@ -960,5 +964,27 @@
         psString dataname = NULL;
         psString deteffname = NULL;
-        if (!pmSourceIOextnames(&headname, &dataname, &deteffname, NULL, NULL, NULL, file, view)) {
+        psString xsrcname = NULL;
+        psString xfitname = NULL;
+        psString xradname = NULL;
+
+        // determine the output table format. Assume if we need to output extendend source
+        // parameters that they may exist in the input. 
+        // XXX: Perhaps we should use different recipe values.
+        // I.E. EXTENDED_SOURCE_ANALYSIS_READ or something like that
+        psMetadata *recipe = psMetadataLookupMetadata(&status, config->recipes, "PSPHOT");
+        if (!status) {
+	    psError(PS_ERR_UNKNOWN, true, "missing recipe PSPHOT in config data");
+	    return false;
+        }
+        // if this is not TRUE, the output files only contain the psf measurements.
+        bool XSRC_OUTPUT = psMetadataLookupBool(&status, recipe, "EXTENDED_SOURCE_ANALYSIS");
+        bool XFIT_OUTPUT = psMetadataLookupBool(&status, recipe, "EXTENDED_SOURCE_FITS");
+        bool XRAD_OUTPUT = psMetadataLookupBool(&status, recipe, "RADIAL_APERTURES");
+
+        if (!pmSourceIOextnames(&headname, &dataname, &deteffname, 
+                XSRC_OUTPUT ? &xsrcname : NULL, 
+                XFIT_OUTPUT ? &xfitname : NULL, 
+                XRAD_OUTPUT ? &xradname : NULL,
+                file, view)) {
             return false;
         }
@@ -1025,4 +1051,7 @@
                 sources = pmSourcesRead_CMF_PS1_V3 (file->fits, hdu->header);
             }
+            if (!strcmp (exttype, "PS1_V4")) {
+                sources = pmSourcesRead_CMF_PS1_V4 (file->fits, hdu->header);
+            }
             if (!strcmp (exttype, "PS1_SV1")) {
                 sources = pmSourcesRead_CMF_PS1_SV1 (file->fits, hdu->header);
@@ -1034,4 +1063,45 @@
                 sources = pmSourcesRead_CMF_PS1_DV2 (file->fits, hdu->header);
             }
+
+            long *sourceIndex = NULL;
+            if (XSRC_OUTPUT || XFIT_OUTPUT || XRAD_OUTPUT) {
+                long seq_max = -1;
+                for (long i = sources->n -1; i >= 0; i--) {
+                    pmSource *source = sources->data[i];
+                    if (source->seq > seq_max) {
+                        seq_max = source->seq;
+                    }
+                }
+                sourceIndex = psAlloc((seq_max + 1) * sizeof(long));
+                for (long i = 0; i < seq_max; i++) {
+                    sourceIndex[i] = -1;
+                }
+                for (long i = 0; i < sources->n; i++) {
+                    pmSource *source = sources->data[i];
+                    sourceIndex[source->seq] = i;
+                }
+            }
+            if (XSRC_OUTPUT && xsrcname) {
+                if (!pmReadoutReadXSRC(file, exttype, hdu->header, xsrcname, sources, sourceIndex)) {
+                    // XXX: is this an error?
+                    psErrorClear();
+                }
+                psFree(xsrcname);
+            }
+            if (XFIT_OUTPUT && xfitname) {
+                if (!pmReadoutReadXFIT(file, exttype, hdu->header, xfitname, sources, sourceIndex)) {
+                    // XXX: is this an error?
+                    psErrorClear();
+                }
+                psFree(xfitname);
+            }
+            if (XRAD_OUTPUT && xradname) {
+                if (!pmReadoutReadXRAD(file, readout, exttype, hdu->header, xradname, sources, sourceIndex)) {
+                    // XXX: is this an error?
+                    psErrorClear();
+                }
+                psFree(xradname);
+            }
+            psFree(sourceIndex);
 
             if (!pmReadoutReadDetEff(file->fits, readout, deteffname)) {
@@ -1161,3 +1231,86 @@
 }
 
-
+// XXX: We might be able to macroize this and reuse for the other types
+
+static bool pmReadoutReadXSRC(pmFPAfile *file, char *exttype, psMetadata *hduHeader, psString xsrcname, psArray *sources, long *sourceIndex) 
+{
+    if (!psFitsMoveExtName (file->fits, xsrcname)) {
+        psError(PS_ERR_UNKNOWN, false, "cannot find xsrc extension %s in %s", xsrcname, file->filename);
+        return false;
+    }
+
+    psMetadata *tableHeader = psFitsReadHeader(NULL, file->fits); // The FITS header
+    if (!tableHeader) psAbort("cannot read table header");
+
+    char *xtension = psMetadataLookupStr (NULL, tableHeader, "XTENSION");
+    if (!xtension) psAbort("cannot read table type");
+    if (strcmp (xtension, "BINTABLE")) {
+        psWarning ("no binary table in extension %s, skipping\n", xsrcname);
+        return false;
+    }
+
+    // XXX these are case-sensitive since the EXTYPE is case-sensitive
+    bool status = false;
+    if (file->type == PM_FPA_FILE_CMF) {
+        if (!strcmp (exttype, "PS1_SV1")) {
+            status  = pmSourcesRead_CMF_PS1_SV1_XSRC (file->fits, hduHeader, sources, sourceIndex);
+        }
+    }
+    psFree(tableHeader);
+    return status;
+}
+
+static bool pmReadoutReadXFIT(pmFPAfile *file, char *exttype, psMetadata *hduHeader, psString extname, psArray *sources, long *sourceIndex) 
+{
+    if (!psFitsMoveExtName (file->fits, extname)) {
+        psError(PS_ERR_UNKNOWN, false, "cannot find extension %s in %s", extname, file->filename);
+        return false;
+    }
+
+    psMetadata *tableHeader = psFitsReadHeader(NULL, file->fits); // The FITS header
+    if (!tableHeader) psAbort("cannot read table header");
+
+    char *xtension = psMetadataLookupStr (NULL, tableHeader, "XTENSION");
+    if (!xtension) psAbort("cannot read table type");
+    if (strcmp (xtension, "BINTABLE")) {
+        psWarning ("no binary table in extension %s, skipping\n", extname);
+        return false;
+    }
+
+    // XXX these are case-sensitive since the EXTYPE is case-sensitive
+    bool status = false;
+    if (file->type == PM_FPA_FILE_CMF) {
+        if (!strcmp (exttype, "PS1_SV1")) {
+            status  = pmSourcesRead_CMF_PS1_SV1_XFIT (file->fits, hduHeader, sources, sourceIndex);
+        }
+    }
+    psFree(tableHeader);
+    return status;
+}
+static bool pmReadoutReadXRAD(pmFPAfile *file, pmReadout *readout, char *exttype, psMetadata *hduHeader, psString extname, psArray *sources, long *sourceIndex) 
+{
+    if (!psFitsMoveExtName (file->fits, extname)) {
+        psError(PS_ERR_UNKNOWN, false, "cannot find extension %s in %s", extname, file->filename);
+        return false;
+    }
+
+    psMetadata *tableHeader = psFitsReadHeader(NULL, file->fits); // The FITS header
+    if (!tableHeader) psAbort("cannot read table header");
+
+    char *xtension = psMetadataLookupStr (NULL, tableHeader, "XTENSION");
+    if (!xtension) psAbort("cannot read table type");
+    if (strcmp (xtension, "BINTABLE")) {
+        psWarning ("no binary table in extension %s, skipping\n", extname);
+        return false;
+    }
+
+    // XXX these are case-sensitive since the EXTYPE is case-sensitive
+    bool status = false;
+    if (file->type == PM_FPA_FILE_CMF) {
+        if (!strcmp (exttype, "PS1_SV1")) {
+            status  = pmSourcesRead_CMF_PS1_SV1_XRAD (file->fits, readout, hduHeader, sources, sourceIndex);
+        }
+    }
+    psFree(tableHeader);
+    return status;
+}
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO.h	(revision 33415)
@@ -62,4 +62,9 @@
 bool pmSourcesWrite_CMF_PS1_V3_XRAD(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
 
+bool pmSourcesWrite_CMF_PS1_V4(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_CMF_PS1_V4_XSRC(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
+bool pmSourcesWrite_CMF_PS1_V4_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname);
+bool pmSourcesWrite_CMF_PS1_V4_XRAD(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
+
 bool pmSourcesWrite_CMF_PS1_SV1(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe);
 bool pmSourcesWrite_CMF_PS1_SV1_XSRC(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe);
@@ -86,5 +91,9 @@
 psArray *pmSourcesRead_CMF_PS1_V2 (psFits *fits, psMetadata *header);
 psArray *pmSourcesRead_CMF_PS1_V3 (psFits *fits, psMetadata *header);
+psArray *pmSourcesRead_CMF_PS1_V4 (psFits *fits, psMetadata *header);
 psArray *pmSourcesRead_CMF_PS1_SV1 (psFits *fits, psMetadata *header);
+bool pmSourcesRead_CMF_PS1_SV1_XSRC (psFits *fits, psMetadata *header, psArray *sources, long *);
+bool pmSourcesRead_CMF_PS1_SV1_XFIT (psFits *fits, psMetadata *header, psArray *sources, long *);
+bool pmSourcesRead_CMF_PS1_SV1_XRAD (psFits *fits, pmReadout *readout, psMetadata *header, psArray *sources, long *);
 psArray *pmSourcesRead_CMF_PS1_DV1 (psFits *fits, psMetadata *header);
 psArray *pmSourcesRead_CMF_PS1_DV2 (psFits *fits, psMetadata *header);
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF.c.in
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF.c.in	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF.c.in	(revision 33415)
@@ -55,5 +55,4 @@
 // followed by a zero-size matrix, followed by the table data
 
-// # define MODE @CMFMODE@
 bool pmSourcesWrite_CMF_@CMFMODE@ (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe)
 {
@@ -128,5 +127,5 @@
 
         @ALL@     psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG",           PS_DATA_F32, "magnitude in standard aperture",             source->apMag);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RAW",       PS_DATA_F32, "magnitude in reported aperture",             source->apMagRaw);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RAW",       PS_DATA_F32, "magnitude in reported aperture",             source->apMagRaw);
         @ALL@     psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RADIUS",    PS_DATA_F32, "radius used for aperture mags",              outputs.apRadius);
         @<PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "PEAK_FLUX_AS_MAG", PS_DATA_F32, "Peak flux expressed as magnitude",           outputs.peakMag);
@@ -139,5 +138,5 @@
         @>PS1_V1@ psMetadataAdd (row, PS_LIST_TAIL, "DEC_PSF",          PS_DATA_F64, "PSF DEC coordinate (degrees)",               outputs.dec);
 
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "PEAK_FLUX_AS_MAG", PS_DATA_F32, "Peak flux expressed as magnitude",           outputs.peakMag);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "PEAK_FLUX_AS_MAG", PS_DATA_F32, "Peak flux expressed as magnitude",           outputs.peakMag);
         @ALL@     psMetadataAdd (row, PS_LIST_TAIL, "SKY",              PS_DATA_F32, "Sky level",                                  source->sky);
         @ALL@     psMetadataAdd (row, PS_LIST_TAIL, "SKY_SIGMA",        PS_DATA_F32, "Sigma of sky level",                         source->skyErr);
@@ -151,5 +150,5 @@
         @ALL@     psMetadataAdd (row, PS_LIST_TAIL, "PSF_THETA",        PS_DATA_F32, "PSF orientation angle",                      outputs.psfTheta);
         @ALL@     psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor (bad)",          source->pixWeightNotBad);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF_PERFECT",   PS_DATA_F32, "PSF coverage/quality factor (poor)",         source->pixWeightNotPoor);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF_PERFECT",   PS_DATA_F32, "PSF coverage/quality factor (poor)",         source->pixWeightNotPoor);
         @ALL@     psMetadataAdd (row, PS_LIST_TAIL, "PSF_NDOF",         PS_DATA_S32, "degrees of freedom",                         outputs.nDOF);
         @ALL@     psMetadataAdd (row, PS_LIST_TAIL, "PSF_NPIX",         PS_DATA_S32, "number of pixels in fit",                    outputs.nPix);
@@ -159,22 +158,23 @@
         @ALL@     psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_YY",       PS_DATA_F32, "second moments (Y*Y)",                       moments.Myy);
 
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M3C",      PS_DATA_F32, "third momemt cos theta",                     moments.M_c3);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M3S",      PS_DATA_F32, "third momemt sin theta",                     moments.M_s3);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M4C",      PS_DATA_F32, "fourth momemt cos theta",                    moments.M_c4);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M4S",      PS_DATA_F32, "fourth momemt sin theta",                    moments.M_s4);
-
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_R1",       PS_DATA_F32, "first radial moment",                        moments.Mrf);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_RH",       PS_DATA_F32, "half radial moment",                         moments.Mrh);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX",        PS_DATA_F32, "Kron Flux (in 2.5 R1)",                      moments.Krf);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_ERR",    PS_DATA_F32, "Kron Flux Error",                            moments.dKrf);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_INNER",  PS_DATA_F32, "Kron Flux (in 2.5 R1)",                      moments.Kinner);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_OUTER",  PS_DATA_F32, "Kron Flux (in 2.5 R1)",                      moments.Kouter);
-
-	// Do NOT write these : not consistent with the definition of PS1_V3 in Ohana/src/libautocode/dev/cmf-ps1-v3.d
-        // psMetadataAdd (row, PS_LIST_TAIL, "KRON_CORE_FLUX",   PS_DATA_F32, "Kron Flux (in 1.0 R1)",                      moments.KronCore);
-	// psMetadataAdd (row, PS_LIST_TAIL, "KRON_CORE_ERROR",  PS_DATA_F32, "Kron Error (in 1.0 R1)",                     moments.KronCoreErr);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M3C",      PS_DATA_F32, "third momemt cos theta",                     moments.M_c3);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M3S",      PS_DATA_F32, "third momemt sin theta",                     moments.M_s3);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M4C",      PS_DATA_F32, "fourth momemt cos theta",                    moments.M_c4);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M4S",      PS_DATA_F32, "fourth momemt sin theta",                    moments.M_s4);
+
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_R1",       PS_DATA_F32, "first radial moment",                        moments.Mrf);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_RH",       PS_DATA_F32, "half radial moment",                         moments.Mrh);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX",        PS_DATA_F32, "Kron Flux (in 2.5 R1)",                      moments.Krf);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_ERR",    PS_DATA_F32, "Kron Flux Error",                            moments.dKrf);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_INNER",  PS_DATA_F32, "Kron Flux (in 2.5 R1)",                      moments.Kinner);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_OUTER",  PS_DATA_F32, "Kron Flux (in 2.5 R1)",                      moments.Kouter);
+
+        @>PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "SKY_LIMIT_RAD",    PS_DATA_F32, "Radius where object hits sky",               source->skyRadius);
+        @>PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "SKY_LIMIT_FLUX",   PS_DATA_F32, "Flux / pix where object hits sky",           source->skyFlux);
+        @>PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "SKY_LIMIT_SLOPE",  PS_DATA_F32, "d(Flux/pix)/dRadius where object hits sky",  source->skySlope);
+
         @ALL@     psMetadataAdd (row, PS_LIST_TAIL, "FLAGS",            PS_DATA_U32, "psphot analysis flags",                      source->mode);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "FLAGS2",           PS_DATA_U32, "psphot analysis flags",                      source->mode2);
-        @=PS1_V3@ psMetadataAdd (row, PS_LIST_TAIL, "PADDING2",         PS_DATA_S32, "more padding", 0);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "FLAGS2",           PS_DATA_U32, "psphot analysis flags",                      source->mode2);
+        @>PS1_V2@ psMetadataAdd (row, PS_LIST_TAIL, "PADDING2",         PS_DATA_S32, "more padding", 0);
 
         // XXX not sure how to get this : need to load Nimages with weight?
@@ -222,5 +222,5 @@
 
 // read in a readout from the fits file
-psArray *pmSourcesRead_CMF_PS1_V3 (psFits *fits, psMetadata *header)
+psArray *pmSourcesRead_CMF_@CMFMODE@ (psFits *fits, psMetadata *header)
 {
     PS_ASSERT_PTR_NON_NULL(fits, false);
@@ -281,6 +281,11 @@
         // XXX use these to determine PAR[PM_PAR_I0]?
         @ALL@     source->psfMag    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG");
-        @ALL@     source->psfMagErr    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG_SIG");
+        @ALL@     source->psfMagErr = psMetadataLookupF32 (&status, row, "PSF_INST_MAG_SIG");
         @ALL@     source->apMag     = psMetadataLookupF32 (&status, row, "AP_MAG");
+        @>PS1_V2@ source->apMagRaw  = psMetadataLookupF32 (&status, row, "AP_MAG_RAW");
+
+        // XXX use these to determine PAR[PM_PAR_I0] if they exist?
+        @>PS1_V2@ source->psfFlux   = psMetadataLookupF32 (&status, row, "PSF_INST_FLUX");
+        @>PS1_V2@ source->psfFluxErr= psMetadataLookupF32 (&status, row, "PSF_INST_FLUX_SIG");
 
         // XXX this scaling is incorrect: does not include the 2 \pi AREA factor
@@ -288,5 +293,5 @@
         @ALL@     dPAR[PM_PAR_I0]   = (isfinite(source->psfMag)) ? PAR[PM_PAR_I0] * source->psfMagErr : NAN;
 
-        pmPSF_AxesToModel (PAR, axes);
+        pmPSF_AxesToModel (PAR, axes, modelType);
 
         @ALL@     float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
@@ -303,5 +308,5 @@
 
         @ALL@     source->pixWeightNotBad = psMetadataLookupF32 (&status, row, "PSF_QF");
-        @=PS1_V3@ source->pixWeightNotPoor = psMetadataLookupF32 (&status, row, "PSF_QF_PERFECT");
+        @>PS1_V2@ source->pixWeightNotPoor = psMetadataLookupF32 (&status, row, "PSF_QF_PERFECT");
         @ALL@     source->crNsigma  = psMetadataLookupF32 (&status, row, "CR_NSIGMA");
         @ALL@     source->extNsigma = psMetadataLookupF32 (&status, row, "EXT_NSIGMA");
@@ -321,27 +326,31 @@
         @ALL@     source->moments->Myy = psMetadataLookupF32 (&status, row, "MOMENTS_YY");
 
-        @=PS1_V3@ source->moments->Mrf         = psMetadataLookupF32 (&status, row, "MOMENTS_R1");
-        @=PS1_V3@ source->moments->Mrh         = psMetadataLookupF32 (&status, row, "MOMENTS_RH");
-        @=PS1_V3@ source->moments->KronFlux    = psMetadataLookupF32 (&status, row, "KRON_FLUX");
-        @=PS1_V3@ source->moments->KronFluxErr = psMetadataLookupF32 (&status, row, "KRON_FLUX_ERR");
-
-        @=PS1_V3@ source->moments->KronFinner  = psMetadataLookupF32 (&status, row, "KRON_FLUX_INNER");
-        @=PS1_V3@ source->moments->KronFouter  = psMetadataLookupF32 (&status, row, "KRON_FLUX_OUTER");
+        @>PS1_V2@ source->moments->Mrf         = psMetadataLookupF32 (&status, row, "MOMENTS_R1");
+        @>PS1_V2@ source->moments->Mrh         = psMetadataLookupF32 (&status, row, "MOMENTS_RH");
+        @>PS1_V2@ source->moments->KronFlux    = psMetadataLookupF32 (&status, row, "KRON_FLUX");
+        @>PS1_V2@ source->moments->KronFluxErr = psMetadataLookupF32 (&status, row, "KRON_FLUX_ERR");
+
+        @>PS1_V2@ source->moments->KronFinner  = psMetadataLookupF32 (&status, row, "KRON_FLUX_INNER");
+        @>PS1_V2@ source->moments->KronFouter  = psMetadataLookupF32 (&status, row, "KRON_FLUX_OUTER");
+
+        @>PS1_V3@ source->skyRadius            = psMetadataLookupF32 (&status, row, "SKY_LIMIT_RAD");
+        @>PS1_V3@ source->skyFlux              = psMetadataLookupF32 (&status, row, "SKY_LIMIT_FLUX");
+        @>PS1_V3@ source->skySlope             = psMetadataLookupF32 (&status, row, "SKY_LIMIT_SLOPE");
 
 	// XXX we do not save all of the 3rd and 4th moment parameters. when we load in data,
 	// we are storing enough information so the output will be consistent with the input
-        @=PS1_V3@ source->moments->Mxxx = +1.0 * psMetadataLookupF32 (&status, row, "MOMENTS_M3C");
-        @=PS1_V3@ source->moments->Mxxy = 0.0;
-        @=PS1_V3@ source->moments->Mxyy = 0.0;
-        @=PS1_V3@ source->moments->Myyy = -1.0 * psMetadataLookupF32 (&status, row, "MOMENTS_M3S");
-
-        @=PS1_V3@ source->moments->Mxxxx = +1.00 * psMetadataLookupF32 (&status, row, "MOMENTS_M4C");
-        @=PS1_V3@ source->moments->Mxxxy = 0.0;
-        @=PS1_V3@ source->moments->Mxxyy = 0.0;
-        @=PS1_V3@ source->moments->Mxyyy = -0.25 * psMetadataLookupF32 (&status, row, "MOMENTS_M4S");
-        @=PS1_V3@ source->moments->Myyyy = 0.0;
+        @>PS1_V2@ source->moments->Mxxx = +1.0 * psMetadataLookupF32 (&status, row, "MOMENTS_M3C");
+        @>PS1_V2@ source->moments->Mxxy = 0.0;
+        @>PS1_V2@ source->moments->Mxyy = 0.0;
+        @>PS1_V2@ source->moments->Myyy = -1.0 * psMetadataLookupF32 (&status, row, "MOMENTS_M3S");
+
+        @>PS1_V2@ source->moments->Mxxxx = +1.00 * psMetadataLookupF32 (&status, row, "MOMENTS_M4C");
+        @>PS1_V2@ source->moments->Mxxxy = 0.0;
+        @>PS1_V2@ source->moments->Mxxyy = 0.0;
+        @>PS1_V2@ source->moments->Mxyyy = -0.25 * psMetadataLookupF32 (&status, row, "MOMENTS_M4S");
+        @>PS1_V2@ source->moments->Myyyy = 0.0;
 
         @ALL@     source->mode = psMetadataLookupU32 (&status, row, "FLAGS");
-        @=PS1_V3@ source->mode2 = psMetadataLookupU32 (&status, row, "FLAGS2");
+        @>PS1_V2@ source->mode2 = psMetadataLookupU32 (&status, row, "FLAGS2");
         assert (status);
 
@@ -353,5 +362,5 @@
 }
 
-bool pmSourcesWrite_CMF_PS1_V3_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
+bool pmSourcesWrite_CMF_@CMFMODE@_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
 {
     bool status;
@@ -541,5 +550,5 @@
 
 // XXX this layout is still the same as PS1_DEV_1
-bool pmSourcesWrite_CMF_PS1_V3_XFIT (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname)
+bool pmSourcesWrite_CMF_@CMFMODE@_XFIT (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname)
 {
 
@@ -590,4 +599,7 @@
             assert (model);
 
+            // pmSourceExtFitPars *extPars = source->extFitPars->data[j];
+	    // assert (extPars);
+
 	    // skip models which were not actually fitted
 	    if (model->flags & PM_MODEL_STATUS_BADARGS) continue;
@@ -600,5 +612,8 @@
             yErr = dPAR[PM_PAR_YPOS];
 
-            axes = pmPSF_ModelToAxes (PAR, 20.0);
+            axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
+
+	    float kronFlux = source->moments ? source->moments->KronFlux : NAN;
+	    float kronMag = isfinite(kronFlux) ? -2.5*log10(kronFlux) : NAN;
 
             row = psMetadataAlloc ();
@@ -612,4 +627,14 @@
             psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_INST_MAG",     0, "EXT fit instrumental magnitude",             model->mag);
             psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_INST_MAG_SIG", 0, "Sigma of PSF instrumental magnitude",        model->magErr);
+
+            // psMetadataAddF32 (row, PS_LIST_TAIL, "MOMENTS_XX",       0, "second moment in x",                      extPars->Mxx);
+            // psMetadataAddF32 (row, PS_LIST_TAIL, "MOMENTS_XY",       0, "second moment in x,y",                    extPars->Mxy);
+            // psMetadataAddF32 (row, PS_LIST_TAIL, "MOMENTS_YY",       0, "second moment in y",                      extPars->Myy);
+            // psMetadataAddF32 (row, PS_LIST_TAIL, "MOMENTS_R1",       0, "first radial moment",                     extPars->Mrf);
+            // psMetadataAddF32 (row, PS_LIST_TAIL, "MOMENTS_RH",       0, "half radial moment",                      extPars->Mrh);
+
+            psMetadataAddF32 (row, PS_LIST_TAIL, "PSF_INST_MAG",     0, "PSF fit instrumental magnitude",             source->psfMag);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "AP_MAG",           0, "PSF-sized aperture magnitude",               source->apMag);
+            psMetadataAddF32 (row, PS_LIST_TAIL, "KRON_MAG",         0, "Kron Mag",                                   kronMag);
 
             psMetadataAddF32 (row, PS_LIST_TAIL, "NPARAMS",          0, "number of model parameters",                 model->params->n);
@@ -673,5 +698,5 @@
 }
 
-bool pmSourcesWrite_CMF_PS1_V3_XRAD(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
+bool pmSourcesWrite_CMF_@CMFMODE@_XRAD(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
 {
     return true;
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_DV1.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_DV1.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_DV1.c	(revision 33415)
@@ -263,5 +263,5 @@
         dPAR[PM_PAR_I0]   = (isfinite(source->psfMag)) ? PAR[PM_PAR_I0] * source->psfMagErr : NAN;
 
-        pmPSF_AxesToModel (PAR, axes);
+        pmPSF_AxesToModel (PAR, axes, modelType);
 
         float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
@@ -547,5 +547,5 @@
             yErr = dPAR[PM_PAR_YPOS];
 
-            axes = pmPSF_ModelToAxes (PAR, 20.0);
+            axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 
             row = psMetadataAlloc ();
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_DV2.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_DV2.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_DV2.c	(revision 33415)
@@ -281,5 +281,5 @@
         dPAR[PM_PAR_I0]   = (isfinite(source->psfMag)) ? PAR[PM_PAR_I0] * source->psfMagErr : NAN;
 
-        pmPSF_AxesToModel (PAR, axes);
+        pmPSF_AxesToModel (PAR, axes, modelType);
 
         float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
@@ -572,5 +572,5 @@
             yErr = dPAR[PM_PAR_YPOS];
 
-            axes = pmPSF_ModelToAxes (PAR, 20.0);
+            axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 
             row = psMetadataAlloc ();
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_SV1.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_SV1.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_SV1.c	(revision 33415)
@@ -280,5 +280,5 @@
         dPAR[PM_PAR_I0]   = (isfinite(source->psfMag)) ? PAR[PM_PAR_I0] * source->psfMagErr : NAN;
 
-        pmPSF_AxesToModel (PAR, axes);
+        pmPSF_AxesToModel (PAR, axes, modelType);
 
         float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
@@ -539,4 +539,98 @@
 }
 
+bool pmSourcesRead_CMF_PS1_SV1_XSRC(psFits *fits, psMetadata *hduHeader, psArray *sources, long *sourceIndex)
+{
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+
+    bool status;
+    long numSources = psFitsTableSize(fits); // Number of sources in table
+    if (numSources == 0) {
+        psError(psErrorCodeLast(), false, "XSRC Table contains no entries\n");
+        return false;
+    }
+
+    // petrosian mags are not saved, we need to calculate fluxes. For this we need exptime and zero point
+    float zeropt = psMetadataLookupF32(&status, hduHeader, "FPA.ZP");
+    float exptime = psMetadataLookupF32(&status, hduHeader, "EXPTIME");
+    float magOffset = zeropt + 2.5*log10(exptime);
+
+    for (long i = 0; i < numSources; i++) {
+        psMetadata *row = psFitsReadTableRow(fits, i); // Table row
+        if (!row) {
+            psError(psErrorCodeLast(), false, "Unable to read row %ld of sources", i);
+            psFree(row);
+            return false;
+        }
+        // Find the source with this sequence number. 
+        // XXX: I am assuming that sources is sorted in order of seq
+        long seq = psMetadataLookupU32 (&status, row, "IPP_IDET");
+        pmSource *source = NULL;
+#ifndef ASSUME_SORTED
+        long j = seq < sources->n ? seq : sources->n - 1;
+        for (; j >= 0; j--) {
+            source = sources->data[j];
+            if (source->seq == seq) {
+                break;
+            }
+        }
+#else
+        long j = sourceIndex[seq];
+        psAssert(j >= 0 && j < sources->n, "invalid sourceIndex");
+        source = sources->data[j];
+#endif
+        if (!source) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to find source for row %ld sequence number %ld\n", i, seq);
+            psFree(row);
+            return false;
+        }
+
+        if (!source->extpars) {
+            source->extpars = pmSourceExtendedParsAlloc ();
+        }
+        pmSourceExtendedPars *extpars = source->extpars;
+
+        // Assume that X_EXT Y_EXT and sigmas match the psf src so skip
+
+        // We don't have enough information to calculate the major and minor axis. Set major to 1. Should we scale this by
+        // psf size or something?
+        extpars->axes.major = 1.0;
+        extpars->axes.minor = extpars->axes.major * psMetadataLookupF32(&status, row, "F25_ARATIO");
+        extpars->axes.theta = psMetadataLookupF32(&status, row, "F25_THETA");
+
+        float mag = psMetadataLookupF32(&status, row, "PETRO_MAG");
+        float magErr = psMetadataLookupF32(&status, row, "PETRO_MAG_ERR");
+        if (isfinite(mag)) {
+            extpars->petrosianFlux    = pow(10., (magOffset - mag) / 2.5);
+            if (isfinite(magErr)) {
+                extpars->petrosianFluxErr = extpars->petrosianFlux / magErr;
+            }
+        }
+
+        extpars->petrosianRadius   = psMetadataLookupF32(&status, row, "PETRO_RADIUS");
+        extpars->petrosianRadiusErr= psMetadataLookupF32(&status, row, "PETRO_RADIUS_ERR");
+        extpars->petrosianR50      = psMetadataLookupF32(&status, row, "PETRO_RADIUS_50");
+        extpars->petrosianR50Err   = psMetadataLookupF32(&status, row, "PETRO_RADIUS_50_ERR");
+        extpars->petrosianR90      = psMetadataLookupF32(&status, row, "PETRO_RADIUS_90");
+        extpars->petrosianR90Err   = psMetadataLookupF32(&status, row, "PETRO_RADIUS_90_ERR");
+        extpars->petrosianFill     = psMetadataLookupF32(&status, row, "PETRO_FILL");
+
+        psVector *radSB   = psMetadataLookupVector(&status, row, "PROF_SB");
+        psVector *radFlux = psMetadataLookupVector(&status, row, "PROF_FLUX");
+        psVector *radFill = psMetadataLookupVector(&status, row, "PROF_FILL");
+
+        if (radSB && radSB->n > 0) {
+            extpars->radProfile = pmSourceRadialProfileAlloc();
+            extpars->radProfile->binSB   = psMemIncrRefCounter(radSB);
+            extpars->radProfile->binSum   = psMemIncrRefCounter(radFlux);
+            extpars->radProfile->binFill = psMemIncrRefCounter(radFill);
+        }
+
+        psFree(row);
+    }
+
+    return true;
+}
+
 // XXX this layout is still the same as PS1_DEV_1
 bool pmSourcesWrite_CMF_PS1_SV1_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname)
@@ -607,5 +701,5 @@
             yErr = dPAR[PM_PAR_YPOS];
 
-            axes = pmPSF_ModelToAxes (PAR, 20.0);
+            axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 
             row = psMetadataAlloc ();
@@ -684,4 +778,104 @@
     psFree (outhead);
     psFree (table);
+    return true;
+}
+
+bool pmSourcesRead_CMF_PS1_SV1_XFIT(psFits *fits, psMetadata *hduHeader, psArray *sources, long *sourceIndex)
+{
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+
+    bool status;
+    long numSources = psFitsTableSize(fits); // Number of sources in table
+    if (numSources == 0) {
+        psError(psErrorCodeLast(), false, "XFIT Table contains no entries\n");
+        return false;
+    }
+
+    for (long i = 0; i < numSources; i++) {
+        psMetadata *row = psFitsReadTableRow(fits, i); // Table row
+        if (!row) {
+            psError(psErrorCodeLast(), false, "Unable to read row %ld of sources", i);
+            psFree(row);
+            return false;
+        }
+        // Find the source with this sequence number. 
+        // XXX: I am assuming that sources is sorted in order of seq.
+        long seq = psMetadataLookupU32 (&status, row, "IPP_IDET");
+        long j = seq < sources->n ? seq : sources->n - 1;
+        pmSource *source = NULL;
+        for (; j >= 0; j--) {
+            source = sources->data[j];
+            if (source->seq == seq) {
+                break;
+            }
+        }
+        if (!source) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to find source for row %ld sequence number %ld\n", i, seq);
+            psFree(row);
+            return false;
+        }
+        if (!source->modelFits) {
+            // XXX: where to find the number of models to expect?
+            source->modelFits = psArrayAllocEmpty(5);
+        }
+        psString modelName = psMetadataLookupStr(&status, row, "MODEL_TYPE");
+        if (!modelName) {
+            psError(PS_ERR_UNKNOWN, true, "Failed to find model name for row %ld\n", i);
+            psFree(row);
+            return false;
+        }
+        pmModelType modelType = pmModelClassGetType(modelName);
+        if (modelType < 0) {
+            psError(PS_ERR_UNKNOWN, true, "Failed to find model type for %s\n", modelName);
+            psFree(row);
+            return false;
+        }
+        pmModel *model = pmModelAlloc(modelType);
+
+        psF32 *PAR = model->params->data.F32;
+        psF32 *dPAR = model->dparams->data.F32;
+
+        PAR[PM_PAR_XPOS] = psMetadataLookupF32(&status, row, "X_EXT");
+        PAR[PM_PAR_YPOS] = psMetadataLookupF32(&status, row, "Y_EXT");
+        dPAR[PM_PAR_XPOS] = psMetadataLookupF32(&status, row, "X_EXT_SIG");
+        dPAR[PM_PAR_YPOS] = psMetadataLookupF32(&status, row, "Y_EXT_SIG");
+
+        model->mag = psMetadataLookupF32(&status, row, "EXT_INST_MAG");
+        model->magErr = psMetadataLookupF32(&status, row, "EXT_INST_MAG_SIG");
+
+        psEllipseAxes axes;
+        axes.major = psMetadataLookupF32(&status, row, "EXT_WIDTH_MAJ");
+        axes.minor = psMetadataLookupF32(&status, row, "EXT_WIDTH_MIN");
+        axes.theta = psMetadataLookupF32(&status, row, "EXT_THETA");
+        if (!pmPSF_AxesToModel(PAR, axes, modelType)) {
+            // Do we need to fail here or can this happen?
+            psError(PS_ERR_UNKNOWN, false, "Failed to convert psf axes to model");
+            psFree(model);
+            psFree(row);
+            return false;
+        }
+        // XXX: clean this up
+        if (model->params->n > 7) {
+            PAR[7] = psMetadataLookupF32(&status, row, "EXT_PAR_07");
+        }
+        // read the covariance matrix
+        int nparams = model->params->n;
+        psImage *covar = psImageAlloc(nparams, nparams, PS_TYPE_F32);
+        for (int y = 0; y < nparams; y++) {
+            for (int x = 0; x < nparams; x++) {
+                char name[64];
+                snprintf(name, 64, "EXT_COVAR_%02d_%02d", y, x);
+                covar->data.F32[y][x] = psMetadataLookupF32(&status, row, name);
+            }
+        }
+        model->covar = covar;
+
+        psArrayAdd(source->modelFits, 1, model);
+        psFree(model);
+
+        psFree(row);
+    }
+
     return true;
 }
@@ -831,2 +1025,94 @@
     return true;
 }
+
+bool pmSourcesRead_CMF_PS1_SV1_XRAD(psFits *fits, pmReadout *readout, psMetadata *hduHeader, psArray *sources, long *sourceIndex)
+{
+    PS_ASSERT_PTR_NON_NULL(fits, false);
+    PS_ASSERT_PTR_NON_NULL(sources, false);
+
+    bool status;
+    long numSources = psFitsTableSize(fits); // Number of sources in table
+    if (numSources == 0) {
+        psError(psErrorCodeLast(), false, "XRAD Table contains no entries\n");
+        return false;
+    }
+
+    long       seq_first = -1;
+    long       seq_last = -1;
+    psVector   *fwhmValues = psVectorAllocEmpty(10, PS_TYPE_F32);
+    long       max_entries = -1;
+    long       num_entries = -1;
+
+    for (long i = 0; i < numSources; i++) {
+        psMetadata *row = psFitsReadTableRow(fits, i); // Table row
+        if (!row) {
+            psError(psErrorCodeLast(), false, "Unable to read row %ld of sources", i);
+            psFree(row);
+            return false;
+        }
+        // Find the source with this sequence number. 
+        // XXX: I am assuming that sources is sorted in order of seq.
+        long seq = psMetadataLookupU32 (&status, row, "IPP_IDET");
+        long j = seq < sources->n ? seq : sources->n - 1;
+        pmSource *source = NULL;
+        for (; j >= 0; j--) {
+            source = sources->data[j];
+            if (source->seq == seq) {
+                break;
+            }
+        }
+        if (!source) {
+            psError(PS_ERR_UNKNOWN, false, "Failed to find source for row %ld sequence number %ld\n", i, seq);
+            psFree(row);
+            return false;
+        }
+        if (seq_first == -1) {
+            seq_first = seq;
+        }
+        if (seq == seq_first) {
+            psF32 value = psMetadataLookupF32(&status, row, "PSF_FWHM");
+            psVectorAppend(fwhmValues, value);
+        }
+        if (seq == seq_last) {
+            num_entries++;
+        } else {
+            num_entries = 1;
+            seq_last = seq;
+        }
+        if (num_entries > max_entries) {
+            max_entries = num_entries;
+        }
+
+        if (!source->radialAper) {
+            // XXX: where to find the number of models to expect?
+            source->radialAper = psArrayAllocEmpty(5);
+        }
+        pmSourceRadialApertures *radialAper = pmSourceRadialAperturesAlloc();
+
+        radialAper->flux = psMemIncrRefCounter(psMetadataLookupVector(&status, row, "APER_FLUX"));
+        radialAper->fluxStdev = psMemIncrRefCounter(psMetadataLookupVector(&status, row, "APER_FLUX_STDEV"));
+        radialAper->fluxErr = psMemIncrRefCounter(psMetadataLookupVector(&status, row, "APER_FLUX_ERR"));
+        radialAper->fill = psMemIncrRefCounter(psMetadataLookupVector(&status, row, "APER_FILL"));
+
+        psArrayAdd(source->radialAper, 1, radialAper);
+
+        psFree(radialAper);
+        psFree(row);
+    }
+
+    // check for consistency between the length of fwhmValues and the maximum number of entries for each row
+    if (fwhmValues->n != max_entries) {
+        psError(PS_ERR_PROGRAMMING, true, "number of PSF_FWHM values found %ld does not match expected number: %ld\n",
+            fwhmValues->n, max_entries);
+        psAssert(0, "fixme");
+    }
+
+    if (!readout->analysis) {
+        readout->analysis = psMetadataAlloc();
+    }
+
+    psMetadataAddVector(readout->analysis, PS_LIST_TAIL, "STACK.PSF.FWHM.VALUES", PS_META_REPLACE, "PSF sizes", fwhmValues);
+    psFree(fwhmValues);
+
+    return true;
+}
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_V1.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_V1.c	(revision 32161)
+++ 	(revision )
@@ -1,664 +1,0 @@
-/** @file  pmSourceIO.c
- *
- *  @author EAM, IfA
- *
- *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2009-02-18 02:44:19 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <math.h>
-#include <string.h>
-#include <pslib.h>
-
-#include "pmConfig.h"
-#include "pmDetrendDB.h"
-
-#include "pmHDU.h"
-#include "pmFPA.h"
-#include "pmFPALevel.h"
-#include "pmFPAview.h"
-#include "pmFPAfile.h"
-
-#include "pmTrend2D.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmSpan.h"
-#include "pmFootprintSpans.h"
-#include "pmFootprint.h"
-#include "pmPeaks.h"
-#include "pmMoments.h"
-#include "pmModelFuncs.h"
-#include "pmModel.h"
-#include "pmModelUtils.h"
-#include "pmModelClass.h"
-#include "pmSourceMasks.h"
-#include "pmSourceExtendedPars.h"
-#include "pmSourceDiffStats.h"
-#include "pmSource.h"
-#include "pmSourceFitModel.h"
-#include "pmPSF.h"
-#include "pmPSFtry.h"
-
-#include "pmSourceIO.h"
-#include "pmSourceOutputs.h"
-
-// panstarrs-style FITS table output (header + table in 1st extension)
-// this format consists of a header derived from the image header
-// followed by a zero-size matrix, followed by the table data
-
-bool pmSourcesWrite_CMF_PS1_V1 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe)
-{
-    PS_ASSERT_PTR_NON_NULL(fits, false);
-    PS_ASSERT_PTR_NON_NULL(sources, false);
-    PS_ASSERT_PTR_NON_NULL(extname, false);
-
-    psArray *table;
-    psMetadata *row;
-
-    pmChip *chip = readout->parent->parent;
-
-    // if the sequence is defined, write these in seq order; otherwise
-    // write them in S/N order:
-    if (sources->n > 0) {
-        pmSource *source = (pmSource *) sources->data[0];
-        if (source->seq == -1) {
-          // let's write these out in S/N order
-          sources = psArraySort (sources, pmSourceSortByFlux);
-        } else {
-          sources = psArraySort (sources, pmSourceSortBySeq);
-        }
-    }
-
-    table = psArrayAllocEmpty (sources->n);
-
-    short nImageOverlap; 
-    float magOffset; 
-    float zeroptErr; 
-    float fwhmMajor; 
-    float fwhmMinor;
-    pmSourceOutputsCommonValues (&nImageOverlap, &magOffset, &zeroptErr, &fwhmMajor, &fwhmMinor, readout, imageHeader);
-
-    // we write out PSF-fits for all sources, regardless of quality.  the source flags tell us the state
-    // by the time we call this function, all values should be assigned.  let's use asserts to be sure in some cases.
-    for (int i = 0; i < sources->n; i++) {
-        pmSource *source = (pmSource *) sources->data[i];
-
-        // If source->seq is -1, source was generated in this analysis.  If source->seq is
-        // not -1, source was read from elsewhere: in the latter case, preserve the source
-        // ID.  source.seq is used instead of source.id since the latter is a const
-        // generated on Alloc, and would thus be wrong for read in sources.
-        if (source->seq == -1) {
-          source->seq = i;
-        }
-
-	// set the 'best' values for various output fields:
-	pmSourceOutputs outputs;
-	pmSourceOutputsSetValues (&outputs, source, chip, fwhmMajor, fwhmMinor, magOffset);
-
-	pmSourceOutputsMoments moments;
-	pmSourceOutputsSetMoments (&moments, source);
-
-        row = psMetadataAlloc ();
-        psMetadataAdd (row, PS_LIST_TAIL, "IPP_IDET",         PS_DATA_U32, "IPP detection identifier index",             source->seq);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_PSF",            PS_DATA_F32, "PSF x coordinate",                           outputs.xPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_PSF",            PS_DATA_F32, "PSF y coordinate",                           outputs.yPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_PSF_SIG",        PS_DATA_F32, "Sigma in PSF x coordinate",                  outputs.xErr); // XXX this is only measured for non-linear fits
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_PSF_SIG",        PS_DATA_F32, "Sigma in PSF y coordinate",                  outputs.yErr); // XXX this is only measured for non-linear fits
-        psMetadataAdd (row, PS_LIST_TAIL, "RA_PSF",           PS_DATA_F32, "PSF RA coordinate (degrees)",                outputs.ra);
-        psMetadataAdd (row, PS_LIST_TAIL, "DEC_PSF",          PS_DATA_F32, "PSF DEC coordinate (degrees)",               outputs.dec);
-        psMetadataAdd (row, PS_LIST_TAIL, "POSANGLE",         PS_DATA_F32, "position angle at source (degrees)",         outputs.posAngle);
-        psMetadataAdd (row, PS_LIST_TAIL, "PLTSCALE",         PS_DATA_F32, "plate scale at source (arcsec/pixel)",       outputs.pltScale);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_MAG",     PS_DATA_F32, "PSF fit instrumental magnitude",             source->psfMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_MAG_SIG", PS_DATA_F32, "Sigma of PSF instrumental magnitude",        source->psfMagErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG",           PS_DATA_F32, "magnitude in standard aperture",             source->apMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RADIUS",    PS_DATA_F32, "radius used for aperture mags",              outputs.apRadius);
-        psMetadataAdd (row, PS_LIST_TAIL, "PEAK_FLUX_AS_MAG", PS_DATA_F32, "Peak flux expressed as magnitude",           outputs.peakMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "CAL_PSF_MAG",      PS_DATA_F32, "PSF Magnitude using supplied calibration",   outputs.calMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "CAL_PSF_MAG_SIG",  PS_DATA_F32, "measured scatter of zero point calibration", zeroptErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "SKY",              PS_DATA_F32, "Sky level",                                  source->sky);
-        psMetadataAdd (row, PS_LIST_TAIL, "SKY_SIGMA",        PS_DATA_F32, "Sigma of sky level",                         source->skyErr);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_CHISQ",        PS_DATA_F32, "Chisq of PSF-fit",                           outputs.chisq);
-        psMetadataAdd (row, PS_LIST_TAIL, "CR_NSIGMA",        PS_DATA_F32, "Nsigma deviations from PSF to CF",           source->crNsigma);
-        psMetadataAdd (row, PS_LIST_TAIL, "EXT_NSIGMA",       PS_DATA_F32, "Nsigma deviations from PSF to EXT",          source->extNsigma);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_MAJOR",        PS_DATA_F32, "PSF width (major axis)",                     outputs.psfMajor);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_MINOR",        PS_DATA_F32, "PSF width (minor axis)",                     outputs.psfMinor);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_THETA",        PS_DATA_F32, "PSF orientation angle",                      outputs.psfTheta);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor",                source->pixWeightNotBad);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_NDOF",         PS_DATA_S32, "degrees of freedom",                         outputs.nDOF);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_NPIX",         PS_DATA_S32, "number of pixels in fit",                    outputs.nPix);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_XX",       PS_DATA_F32, "second moments (X^2)",                       moments.Mxx);
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_XY",       PS_DATA_F32, "second moments (X*Y)",                       moments.Mxy);
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_YY",       PS_DATA_F32, "second moments (Y*Y)",                       moments.Myy);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "FLAGS",            PS_DATA_U32, "psphot analysis flags",                      source->mode);
-
-        // XXX not sure how to get this : need to load Nimages with weight?
-        psMetadataAdd (row, PS_LIST_TAIL, "N_FRAMES",         PS_DATA_U16, "Number of frames overlapping source center", nImageOverlap);
-        psMetadataAdd (row, PS_LIST_TAIL, "PADDING",          PS_DATA_S16, "padding", 0);
-
-        psArrayAdd (table, 100, row);
-        psFree (row);
-
-        // EXT_NSIGMA will be NAN if: 1) contour ellipse is imaginary; 2) source is not
-        // subtracted
-
-        // CR_NSIGMA will be NAN if: 1) source is not subtracted; 2) source is on the image
-        // edge; 3) any pixels in the 3x3 peak region are masked;
-    }
-
-    psMetadata *header = psMetadataCopy(NULL, tableHeader);
-    pmSourceMasksHeader(header);
-
-    if (table->n == 0) {
-        if (!psFitsWriteBlank(fits, header, extname)) {
-            psError(psErrorCodeLast(), false, "Unable to write empty sources file.");
-            psFree(table);
-            psFree(header);
-            return false;
-        }
-        psFree(table);
-        psFree(header);
-        return true;
-    }
-
-    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
-    if (!psFitsWriteTable(fits, header, table, extname)) {
-        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
-        psFree(table);
-        psFree(header);
-        return false;
-    }
-    psFree(table);
-    psFree(header);
-
-    return true;
-}
-
-// read in a readout from the fits file
-psArray *pmSourcesRead_CMF_PS1_V1 (psFits *fits, psMetadata *header)
-{
-    PS_ASSERT_PTR_NON_NULL(fits, false);
-    PS_ASSERT_PTR_NON_NULL(header, false);
-
-    bool status;
-    psF32 *PAR, *dPAR;
-    psEllipseAxes axes;
-
-    // define PSF model type
-    // XXX need to carry the extra model parameters
-    int modelType = pmModelClassGetType ("PS_MODEL_GAUSS");
-
-    char *PSF_NAME = psMetadataLookupStr (&status, header, "PSF_NAME");
-    if (PSF_NAME != NULL) {
-        modelType = pmModelClassGetType (PSF_NAME);
-    }
-    assert (modelType > -1);
-
-    // We get the size of the table, and allocate the array of sources first because the table
-    // is large and ephemeral --- when the table gets blown away, whatever is allocated after
-    // the table is read blocks the free.  In fact, it's better to read the table row by row.
-    long numSources = psFitsTableSize(fits); // Number of sources in table
-    psArray *sources = psArrayAlloc(numSources); // Array of sources, to return
-
-    // convert the table to the pmSource entriesa
-    for (int i = 0; i < numSources; i++) {
-        psMetadata *row = psFitsReadTableRow(fits, i); // Table row
-
-        pmSource *source = pmSourceAlloc ();
-        pmModel *model = pmModelAlloc (modelType);
-        source->modelPSF  = model;
-        source->type = PM_SOURCE_TYPE_STAR; // XXX this should be added to the flags
-
-        // NOTE: A SEGV here because "model" is NULL is probably caused by not initialising the models.
-        PAR = model->params->data.F32;
-        dPAR = model->dparams->data.F32;
-
-        source->seq       = psMetadataLookupU32 (&status, row, "IPP_IDET");
-        PAR[PM_PAR_XPOS]  = psMetadataLookupF32 (&status, row, "X_PSF");
-        PAR[PM_PAR_YPOS]  = psMetadataLookupF32 (&status, row, "Y_PSF");
-        dPAR[PM_PAR_XPOS] = psMetadataLookupF32 (&status, row, "X_PSF_SIG");
-        dPAR[PM_PAR_YPOS] = psMetadataLookupF32 (&status, row, "Y_PSF_SIG");
-        axes.major        = psMetadataLookupF32 (&status, row, "PSF_MAJOR");
-        axes.minor        = psMetadataLookupF32 (&status, row, "PSF_MINOR");
-        axes.theta        = psMetadataLookupF32 (&status, row, "PSF_THETA");
-
-        PAR[PM_PAR_SKY]   = psMetadataLookupF32 (&status, row, "SKY");
-        dPAR[PM_PAR_SKY]  = psMetadataLookupF32 (&status, row, "SKY_SIGMA");
-        source->sky       = PAR[PM_PAR_SKY];
-        source->skyErr    = dPAR[PM_PAR_SKY];
-
-        // XXX use these to determine PAR[PM_PAR_I0]?
-        source->psfMag    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG");
-        source->psfMagErr    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG_SIG");
-        source->apMag     = psMetadataLookupF32 (&status, row, "AP_MAG");
-
-        // XXX this scaling is incorrect: does not include the 2 \pi AREA factor
-        PAR[PM_PAR_I0]    = (isfinite(source->psfMag)) ? pow(10.0, -0.4*source->psfMag) : NAN;
-        dPAR[PM_PAR_I0]   = (isfinite(source->psfMag)) ? PAR[PM_PAR_I0] * source->psfMagErr : NAN;
-
-        pmPSF_AxesToModel (PAR, axes);
-
-        float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
-        float peakFlux    = (isfinite(peakMag)) ? pow(10.0, -0.4*peakMag) : NAN;
-
-        // recreate the peak to match (xPos, yPos) +/- (xErr, yErr)
-        source->peak = pmPeakAlloc(PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], peakFlux, PM_PEAK_LONE);
-        source->peak->rawFlux = peakFlux;
-        source->peak->smoothFlux = peakFlux;
-        source->peak->xf   = PAR[PM_PAR_XPOS]; // more accurate position
-        source->peak->yf   = PAR[PM_PAR_YPOS]; // more accurate position
-        source->peak->dx   = dPAR[PM_PAR_XPOS];
-        source->peak->dy   = dPAR[PM_PAR_YPOS];
-
-        source->pixWeightNotBad = psMetadataLookupF32 (&status, row, "PSF_QF");
-        source->crNsigma  = psMetadataLookupF32 (&status, row, "CR_NSIGMA");
-        source->extNsigma = psMetadataLookupF32 (&status, row, "EXT_NSIGMA");
-        source->apRadius  = psMetadataLookupS32 (&status, row, "AP_MAG_RADIUS");
-
-        // note that some older versions used PSF_PROBABILITY: this was not well defined.
-        model->chisq      = psMetadataLookupF32 (&status, row, "PSF_CHISQ");
-        model->nDOF       = psMetadataLookupS32 (&status, row, "PSF_NDOF");
-        model->nPix       = psMetadataLookupS32 (&status, row, "PSF_NPIX");
-
-        source->moments = pmMomentsAlloc ();
-        source->moments->Mx = source->peak->xf; // we don't have both Mx,My and xf,yf in the cmf
-        source->moments->My = source->peak->yf; // we don't have both Mx,My and xf,yf in the cmf
-
-        source->moments->Mxx = psMetadataLookupF32 (&status, row, "MOMENTS_XX");
-        source->moments->Mxy = psMetadataLookupF32 (&status, row, "MOMENTS_XY");
-        source->moments->Myy = psMetadataLookupF32 (&status, row, "MOMENTS_YY");
-
-        source->mode = psMetadataLookupU32 (&status, row, "FLAGS");
-        assert (status);
-
-        sources->data[i] = source;
-        psFree(row);
-    }
-
-    return sources;
-}
-
-// XXX this layout is still the same as PS1_DEV_1
-bool pmSourcesWrite_CMF_PS1_V1_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
-{
-
-    bool status;
-    psArray *table;
-    psMetadata *row;
-    psF32 *PAR, *dPAR;
-    psF32 xPos, yPos;
-    psF32 xErr, yErr;
-
-    // create a header to hold the output data
-    psMetadata *outhead = psMetadataAlloc ();
-
-    // write the links to the image header
-    psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTNAME", PS_META_REPLACE, "xsrc table extension", extname);
-
-    // let's write these out in S/N order
-    sources = psArraySort (sources, pmSourceSortByFlux);
-
-    table = psArrayAllocEmpty (sources->n);
-
-    // which extended source analyses should we perform?
-    // bool doPetrosian    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
-    // bool doIsophotal    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ISOPHOTAL");
-    // bool doAnnuli       = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
-    // bool doKron         = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_KRON");
-
-    psVector *radialBinsLower = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.LOWER");
-    psVector *radialBinsUpper = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.UPPER");
-    assert (radialBinsLower->n == radialBinsUpper->n);
-
-    // we write out all sources, regardless of quality.  the source flags tell us the state
-    for (int i = 0; i < sources->n; i++) {
-        // skip source if it is not a ext sourc
-        // XXX we have two places that extended source parameters are measured:
-        // psphotExtendedSources, which measures the aperture-like parameters and (potentially) the psf-convolved extended source models,
-        // psphotFitEXT, which does the simple extended source model fit (not psf-convolved)
-        // should we require both?
-
-        pmSource *source = sources->data[i];
-
-        // skip sources without measurements
-        if (source->extpars == NULL) continue;
-
-        // we require a PSF model fit (ignore the real crud)
-        pmModel *model = source->modelPSF;
-        if (model == NULL) continue;
-
-        // XXX I need to split the extended models from the extended aperture measurements
-        PAR = model->params->data.F32;
-        dPAR = model->dparams->data.F32;
-        xPos = PAR[PM_PAR_XPOS];
-        yPos = PAR[PM_PAR_YPOS];
-        xErr = dPAR[PM_PAR_XPOS];
-        yErr = dPAR[PM_PAR_YPOS];
-
-        row = psMetadataAlloc ();
-
-        // XXX we are not writing out the mode (flags) or the type (psf, ext, etc)
-        psMetadataAdd (row, PS_LIST_TAIL, "IPP_IDET",         PS_DATA_U32, "IPP detection identifier index",             source->seq);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_EXT",            PS_DATA_F32, "EXT model x coordinate",                     xPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_EXT",            PS_DATA_F32, "EXT model y coordinate",                     yPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_EXT_SIG",        PS_DATA_F32, "Sigma in EXT x coordinate",                  xErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_EXT_SIG",        PS_DATA_F32, "Sigma in EXT y coordinate",                  yErr);
-
-# if (0)
-        // Petrosian measurements
-        // XXX insert header data: petrosian ref radius, flux ratio
-        if (doPetrosian) {
-            pmSourcePetrosianValues *petrosian = source->extpars->petrosian;
-            if (petrosian) {
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG",        PS_DATA_F32, "Petrosian Magnitude",       petrosian->mag);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG_ERR",    PS_DATA_F32, "Petrosian Magnitude Error", petrosian->magErr);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS",     PS_DATA_F32, "Petrosian Radius",          petrosian->rad);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_ERR", PS_DATA_F32, "Petrosian Radius Error",    petrosian->radErr);
-            } else {
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG",        PS_DATA_F32, "Petrosian Magnitude",       NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG_ERR",    PS_DATA_F32, "Petrosian Magnitude Error", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS",     PS_DATA_F32, "Petrosian Radius",          NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_ERR", PS_DATA_F32, "Petrosian Radius Error",    NAN);
-            }
-        }
-
-        // Kron measurements
-        if (doKron) {
-            pmSourceKronValues *kron = source->extpars->kron;
-            if (kron) {
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG",        PS_DATA_F32, "Kron Magnitude",       kron->mag);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG_ERR",    PS_DATA_F32, "Kron Magnitude Error", kron->magErr);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS",     PS_DATA_F32, "Kron Radius",          kron->rad);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS_ERR", PS_DATA_F32, "Kron Radius Error",    kron->radErr);
-            } else {
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG",        PS_DATA_F32, "Kron Magnitude",       NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG_ERR",    PS_DATA_F32, "Kron Magnitude Error", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS",     PS_DATA_F32, "Kron Radius",          NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS_ERR", PS_DATA_F32, "Kron Radius Error",    NAN);
-            }
-        }
-
-        // Isophot measurements
-        // XXX insert header data: isophotal level
-        if (doIsophotal) {
-            pmSourceIsophotalValues *isophot = source->extpars->isophot;
-            if (isophot) {
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG",        PS_DATA_F32, "Isophot Magnitude",       isophot->mag);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG_ERR",    PS_DATA_F32, "Isophot Magnitude Error", isophot->magErr);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS",     PS_DATA_F32, "Isophot Radius",          isophot->rad);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS_ERR", PS_DATA_F32, "Isophot Radius Error",    isophot->radErr);
-            } else {
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG",        PS_DATA_F32, "Isophot Magnitude",       NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG_ERR",    PS_DATA_F32, "Isophot Magnitude Error", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS",     PS_DATA_F32, "Isophot Radius",          NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS_ERR", PS_DATA_F32, "Isophot Radius Error",    NAN);
-            }
-        }
-
-        // Flux Annuli
-        if (doAnnuli) {
-            pmSourceAnnuli *annuli = source->extpars->annuli;
-            if (annuli) {
-                psVector *fluxVal = annuli->flux;
-                psVector *fluxErr = annuli->fluxErr;
-                psVector *fluxVar = annuli->fluxVar;
-
-                for (int j = 0; j < fluxVal->n; j++) {
-                    char name[32];
-                    sprintf (name, "FLUX_VAL_R_%02d", j);
-                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux value in annulus", fluxVal->data.F32[j]);
-                    sprintf (name, "FLUX_ERR_R_%02d", j);
-                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux error in annulus", fluxErr->data.F32[j]);
-                    sprintf (name, "FLUX_VAR_R_%02d", j);
-                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux stdev in annulus", fluxVar->data.F32[j]);
-                }
-            } else {
-                for (int j = 0; j < radialBinsLower->n; j++) {
-                    char name[32];
-                    sprintf (name, "FLUX_VAL_R_%02d", j);
-                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux value in annulus", NAN);
-                    sprintf (name, "FLUX_ERR_R_%02d", j);
-                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux error in annulus", NAN);
-                    sprintf (name, "FLUX_VAR_R_%02d", j);
-                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "flux stdev in annulus", NAN);
-                }
-            }
-        }
-
-# endif
-        psArrayAdd (table, 100, row);
-        psFree (row);
-    }
-
-    if (table->n == 0) {
-        if (!psFitsWriteBlank (fits, outhead, extname)) {
-            psError(psErrorCodeLast(), false, "Unable to write empty sources.");
-            psFree(outhead);
-            psFree(table);
-            return false;
-        }
-        psFree (outhead);
-        psFree (table);
-        return true;
-    }
-
-    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
-    if (!psFitsWriteTable (fits, outhead, table, extname)) {
-        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
-        psFree (outhead);
-        psFree(table);
-        return false;
-    }
-    psFree (outhead);
-    psFree (table);
-
-    return true;
-}
-
-// XXX this layout is still the same as PS1_DEV_1
-bool pmSourcesWrite_CMF_PS1_V1_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname)
-{
-
-    psArray *table;
-    psMetadata *row;
-    psF32 *PAR, *dPAR;
-    psEllipseAxes axes;
-    psF32 xPos, yPos;
-    psF32 xErr, yErr;
-    char name[64];
-
-    // create a header to hold the output data
-    psMetadata *outhead = psMetadataAlloc ();
-
-    // write the links to the image header
-    psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTNAME", PS_META_REPLACE, "xsrc table extension", extname);
-
-    // let's write these out in S/N order
-    sources = psArraySort (sources, pmSourceSortByFlux);
-
-    // we are writing one row per model; we need to write out same number of columns for each row: find the max Nparams
-    int nParamMax = 0;
-    for (int i = 0; i < sources->n; i++) {
-        pmSource *source = sources->data[i];
-        if (source->modelFits == NULL) continue;
-        for (int j = 0; j < source->modelFits->n; j++) {
-            pmModel *model = source->modelFits->data[j];
-            assert (model);
-            nParamMax = PS_MAX (nParamMax, model->params->n);
-        }
-    }
-
-    table = psArrayAllocEmpty (sources->n);
-
-    // we write out all sources, regardless of quality.  the source flags tell us the state
-    for (int i = 0; i < sources->n; i++) {
-
-        pmSource *source = sources->data[i];
-
-        // XXX if no model fits are saved, write out modelEXT?
-        if (source->modelFits == NULL) continue;
-
-        // We have multiple sources : need to flag the one used to subtract the light (the 'best' model)
-        for (int j = 0; j < source->modelFits->n; j++) {
-
-            // choose the convolved EXT model, if available, otherwise the simple one
-            pmModel *model = source->modelFits->data[j];
-            assert (model);
-
-	    // skip models which were not actually fitted
-	    if (model->flags & PM_MODEL_STATUS_BADARGS) continue;
-
-            PAR = model->params->data.F32;
-            dPAR = model->dparams->data.F32;
-            xPos = PAR[PM_PAR_XPOS];
-            yPos = PAR[PM_PAR_YPOS];
-            xErr = dPAR[PM_PAR_XPOS];
-            yErr = dPAR[PM_PAR_YPOS];
-
-            axes = pmPSF_ModelToAxes (PAR, 20.0);
-
-            row = psMetadataAlloc ();
-
-            // XXX we are not writing out the mode (flags) or the type (psf, ext, etc)
-            psMetadataAddU32 (row, PS_LIST_TAIL, "IPP_IDET",         0, "IPP detection identifier index",             source->seq);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "X_EXT",            0, "EXT model x coordinate",                     xPos);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "Y_EXT",            0, "EXT model y coordinate",                     yPos);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "X_EXT_SIG",        0, "Sigma in EXT x coordinate",                  xErr);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "Y_EXT_SIG",        0, "Sigma in EXT y coordinate",                  yErr);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_INST_MAG",     0, "EXT fit instrumental magnitude",             model->mag);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_INST_MAG_SIG", 0, "Sigma of PSF instrumental magnitude",        model->magErr);
-
-            psMetadataAddF32 (row, PS_LIST_TAIL, "NPARAMS",          0, "number of model parameters",                 model->params->n);
-            psMetadataAddStr (row, PS_LIST_TAIL, "MODEL_TYPE",       0, "name of model",                              pmModelClassGetName (model->type));
-
-            // XXX these should be major and minor, not 'x' and 'y'
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_WIDTH_MAJ",    0, "EXT width in x coordinate",                  axes.major);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_WIDTH_MIN",    0, "EXT width in y coordinate",                  axes.minor);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_THETA",        0, "EXT orientation angle",                      axes.theta);
-
-            // write out the other generic parameters
-            for (int k = 0; k < nParamMax; k++) {
-                if (k == PM_PAR_I0) continue;
-                if (k == PM_PAR_SKY) continue;
-                if (k == PM_PAR_XPOS) continue;
-                if (k == PM_PAR_YPOS) continue;
-                if (k == PM_PAR_SXX) continue;
-                if (k == PM_PAR_SXY) continue;
-                if (k == PM_PAR_SYY) continue;
-
-                snprintf (name, 64, "EXT_PAR_%02d", k);
-
-                if (k < model->params->n) {
-                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "", model->params->data.F32[k]);
-                } else {
-                    psMetadataAddF32 (row, PS_LIST_TAIL, name, PS_DATA_F32, "", NAN);
-                }
-            }
-
-            // XXX other parameters which may be set.
-            // XXX flag / value to define the model
-            // XXX write out the model type, fit status flags
-
-            psArrayAdd (table, 100, row);
-            psFree (row);
-        }
-    }
-
-    if (table->n == 0) {
-        if (!psFitsWriteBlank (fits, outhead, extname)) {
-            psError(psErrorCodeLast(), false, "Unable to write empty sources.");
-            psFree(outhead);
-            psFree(table);
-            return false;
-        }
-        psFree (outhead);
-        psFree (table);
-        return true;
-    }
-
-    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
-    if (!psFitsWriteTable (fits, outhead, table, extname)) {
-        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
-        psFree (outhead);
-        psFree(table);
-        return false;
-    }
-    psFree (outhead);
-    psFree (table);
-    return true;
-}
-
-bool pmSourceLocalAstrometry (psSphere *ptSky, float *posAngle, float *pltScale, pmChip *chip, float xPos, float yPos) {
-
-    pmFPA *fpa = chip->parent;
-
-    if (!chip->toFPA) goto escape;
-    if (!fpa->toTPA) goto escape;
-    if (!fpa->toSky) goto escape;
-
-    // generate RA,DEC
-    psPlane ptCH, ptFP, ptTP_o, ptTP_x, ptTP_y;
-
-    // calculate the astrometry for the coordinate of interest
-    ptCH.x = xPos;
-    ptCH.y = yPos;
-    psPlaneTransformApply (&ptFP, chip->toFPA, &ptCH);
-    psPlaneTransformApply (&ptTP_o, fpa->toTPA, &ptFP);
-    psDeproject (ptSky, &ptTP_o, fpa->toSky);
-
-    // calculate the astrometry for the coordinate + 1pix in X
-    ptCH.x = xPos + 1.0;
-    ptCH.y = yPos;
-    psPlaneTransformApply (&ptFP, chip->toFPA, &ptCH);
-    psPlaneTransformApply (&ptTP_x, fpa->toTPA, &ptFP);
-
-    // calculate the astrometry for the coordinate + 1pix in Y
-    ptCH.x = xPos;
-    ptCH.y = yPos + 1.0;
-    psPlaneTransformApply (&ptFP, chip->toFPA, &ptCH);
-    psPlaneTransformApply (&ptTP_y, fpa->toTPA, &ptFP);
-
-    // the resulting Tangent Plane coordinates are in TP pixels; convert to local Tangent Plane
-    // degrees
-
-    float dTPx_dCHx = fpa->toSky->Xs * (ptTP_x.x - ptTP_o.x);
-    float dTPy_dCHx = fpa->toSky->Ys * (ptTP_x.y - ptTP_o.y);
-
-    float dTPx_dCHy = fpa->toSky->Xs * (ptTP_y.x - ptTP_o.x);
-    float dTPy_dCHy = fpa->toSky->Ys * (ptTP_y.y - ptTP_o.y);
-
-    float pltScale_x = hypot(dTPx_dCHx, dTPy_dCHx);
-    float pltScale_y = hypot(dTPx_dCHy, dTPy_dCHy);
-    *pltScale = 0.5*(pltScale_x + pltScale_y);
-
-    float posAngle_x = atan2 (+dTPy_dCHx, +dTPx_dCHx);
-    float posAngle_y = atan2 (-dTPy_dCHy, +dTPx_dCHy);
-    *posAngle = 0.5*(posAngle_x + posAngle_y);
-
-    return true;
-
-escape:
-    // no astrometry calibration, give up
-    ptSky->r = NAN;
-    ptSky->d = NAN;
-    *posAngle = NAN;
-    *pltScale = NAN;
-
-    return false;
-}
-
-bool pmSourcesWrite_CMF_PS1_V1_XRAD(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
-{
-    return true;
-}
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_V2.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_V2.c	(revision 32161)
+++ 	(revision )
@@ -1,669 +1,0 @@
-/** @file  pmSourceIO.c
- *
- *  @author EAM, IfA
- *
- *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2009-02-18 02:44:19 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <math.h>
-#include <string.h>
-#include <pslib.h>
-
-#include "pmConfig.h"
-#include "pmDetrendDB.h"
-
-#include "pmHDU.h"
-#include "pmFPA.h"
-#include "pmFPALevel.h"
-#include "pmFPAview.h"
-#include "pmFPAfile.h"
-
-#include "pmTrend2D.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmSpan.h"
-#include "pmFootprintSpans.h"
-#include "pmFootprint.h"
-#include "pmPeaks.h"
-#include "pmMoments.h"
-#include "pmModelFuncs.h"
-#include "pmModel.h"
-#include "pmModelUtils.h"
-#include "pmModelClass.h"
-#include "pmSourceMasks.h"
-#include "pmSourceExtendedPars.h"
-#include "pmSourceDiffStats.h"
-#include "pmSource.h"
-#include "pmSourceFitModel.h"
-#include "pmPSF.h"
-#include "pmPSFtry.h"
-
-#include "pmSourceIO.h"
-#include "pmSourceOutputs.h"
-
-// panstarrs-style FITS table output (header + table in 1st extension)
-// this format consists of a header derived from the image header
-// followed by a zero-size matrix, followed by the table data
-
-bool pmSourcesWrite_CMF_PS1_V2 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe)
-{
-    PS_ASSERT_PTR_NON_NULL(fits, false);
-    PS_ASSERT_PTR_NON_NULL(sources, false);
-    PS_ASSERT_PTR_NON_NULL(extname, false);
-
-    psArray *table;
-    psMetadata *row;
-
-    pmChip *chip = readout->parent->parent;
-
-    // if the sequence is defined, write these in seq order; otherwise
-    // write them in S/N order:
-    if (sources->n > 0) {
-        pmSource *source = (pmSource *) sources->data[0];
-        if (source->seq == -1) {
-	    // let's write these out in S/N order
-	    sources = psArraySort (sources, pmSourceSortByFlux);
-        } else {
-	    sources = psArraySort (sources, pmSourceSortBySeq);
-        }
-    }
-
-    table = psArrayAllocEmpty (sources->n);
-
-    short nImageOverlap; 
-    float magOffset; 
-    float zeroptErr; 
-    float fwhmMajor; 
-    float fwhmMinor;
-    pmSourceOutputsCommonValues (&nImageOverlap, &magOffset, &zeroptErr, &fwhmMajor, &fwhmMinor, readout, imageHeader);
-
-    // we write out PSF-fits for all sources, regardless of quality.  the source flags tell us the state
-    // by the time we call this function, all values should be assigned.  let's use asserts to be sure in some cases.
-    for (int i = 0; i < sources->n; i++) {
-        pmSource *source = (pmSource *) sources->data[i];
-
-        // If source->seq is -1, source was generated in this analysis.  If source->seq is
-        // not -1, source was read from elsewhere: in the latter case, preserve the source
-        // ID.  source.seq is used instead of source.id since the latter is a const
-        // generated on Alloc, and would thus be wrong for read in sources.
-        if (source->seq == -1) {
-	    source->seq = i;
-        }
-
-	// set the 'best' values for various output fields:
-	pmSourceOutputs outputs;
-	pmSourceOutputsSetValues (&outputs, source, chip, fwhmMajor, fwhmMinor, magOffset);
-
-	pmSourceOutputsMoments moments;
-	pmSourceOutputsSetMoments (&moments, source);
-
-        row = psMetadataAlloc ();
-        psMetadataAdd (row, PS_LIST_TAIL, "IPP_IDET",         PS_DATA_U32, "IPP detection identifier index",             source->seq);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_PSF",            PS_DATA_F32, "PSF x coordinate",                           outputs.xPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_PSF",            PS_DATA_F32, "PSF y coordinate",                           outputs.yPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_PSF_SIG",        PS_DATA_F32, "Sigma in PSF x coordinate",                  outputs.xErr); // XXX this is only measured for non-linear fits
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_PSF_SIG",        PS_DATA_F32, "Sigma in PSF y coordinate",                  outputs.yErr); // XXX this is only measured for non-linear fits
-
-        psMetadataAdd (row, PS_LIST_TAIL, "POSANGLE",         PS_DATA_F32, "position angle at source (degrees)",         outputs.posAngle);
-        psMetadataAdd (row, PS_LIST_TAIL, "PLTSCALE",         PS_DATA_F32, "plate scale at source (arcsec/pixel)",       outputs.pltScale);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_MAG",     PS_DATA_F32, "PSF fit instrumental magnitude",             source->psfMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_MAG_SIG", PS_DATA_F32, "Sigma of PSF instrumental magnitude",        source->psfMagErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG",           PS_DATA_F32, "magnitude in standard aperture",             source->apMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RADIUS",    PS_DATA_F32, "radius used for aperture mags",              outputs.apRadius);
-        psMetadataAdd (row, PS_LIST_TAIL, "PEAK_FLUX_AS_MAG", PS_DATA_F32, "Peak flux expressed as magnitude",           outputs.peakMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "CAL_PSF_MAG",      PS_DATA_F32, "PSF Magnitude using supplied calibration",   outputs.calMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "CAL_PSF_MAG_SIG",  PS_DATA_F32, "measured scatter of zero point calibration", zeroptErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "RA_PSF",           PS_DATA_F64, "PSF RA coordinate (degrees)",                outputs.ra);
-        psMetadataAdd (row, PS_LIST_TAIL, "DEC_PSF",          PS_DATA_F64, "PSF DEC coordinate (degrees)",               outputs.dec);
-        psMetadataAdd (row, PS_LIST_TAIL, "SKY",              PS_DATA_F32, "Sky level",                                  source->sky);
-        psMetadataAdd (row, PS_LIST_TAIL, "SKY_SIGMA",        PS_DATA_F32, "Sigma of sky level",                         source->skyErr);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_CHISQ",        PS_DATA_F32, "Chisq of PSF-fit",                           outputs.chisq);
-        psMetadataAdd (row, PS_LIST_TAIL, "CR_NSIGMA",        PS_DATA_F32, "Nsigma deviations from PSF to CF",           source->crNsigma);
-        psMetadataAdd (row, PS_LIST_TAIL, "EXT_NSIGMA",       PS_DATA_F32, "Nsigma deviations from PSF to EXT",          source->extNsigma);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_MAJOR",        PS_DATA_F32, "PSF width (major axis)",                     outputs.psfMajor);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_MINOR",        PS_DATA_F32, "PSF width (minor axis)",                     outputs.psfMinor);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_THETA",        PS_DATA_F32, "PSF orientation angle",                      outputs.psfTheta);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor",                source->pixWeightNotBad);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_NDOF",         PS_DATA_S32, "degrees of freedom",                         outputs.nDOF);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_NPIX",         PS_DATA_S32, "number of pixels in fit",                    outputs.nPix);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_XX",       PS_DATA_F32, "second moments (X^2)",                       moments.Mxx);
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_XY",       PS_DATA_F32, "second moments (X*Y)",                       moments.Mxy);
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_YY",       PS_DATA_F32, "second moments (Y*Y)",                       moments.Myy);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "FLAGS",            PS_DATA_U32, "psphot analysis flags",                      source->mode);
-
-        // XXX not sure how to get this : need to load Nimages with weight?
-        psMetadataAdd (row, PS_LIST_TAIL, "N_FRAMES",         PS_DATA_U16, "Number of frames overlapping source center", nImageOverlap);
-        psMetadataAdd (row, PS_LIST_TAIL, "PADDING",          PS_DATA_S16, "padding", 0);
-
-        psArrayAdd (table, 100, row);
-        psFree (row);
-
-        // EXT_NSIGMA will be NAN if: 1) contour ellipse is imaginary; 2) source is not
-        // subtracted
-
-        // CR_NSIGMA will be NAN if: 1) source is not subtracted; 2) source is on the image
-        // edge; 3) any pixels in the 3x3 peak region are masked;
-    }
-
-    // XXX why do we make a copy here to be supplemented with the masks?  why not do this in the calling function?
-    psMetadata *header = psMetadataCopy(NULL, tableHeader);
-    pmSourceMasksHeader(header);
-
-    if (table->n == 0) {
-        if (!psFitsWriteBlank(fits, header, extname)) {
-            psError(psErrorCodeLast(), false, "Unable to write blank sources file.");
-            psFree(table);
-            psFree(header);
-            return false;
-        }
-        psFree(table);
-        psFree(header);
-        return true;
-    }
-
-    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
-    if (!psFitsWriteTable(fits, header, table, extname)) {
-        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
-        psFree(table);
-        psFree(header);
-        return false;
-    }
-    psFree(table);
-    psFree(header);
-
-    return true;
-}
-
-// read in a readout from the fits file
-psArray *pmSourcesRead_CMF_PS1_V2 (psFits *fits, psMetadata *header)
-{
-    PS_ASSERT_PTR_NON_NULL(fits, false);
-    PS_ASSERT_PTR_NON_NULL(header, false);
-
-    bool status;
-    psF32 *PAR, *dPAR;
-    psEllipseAxes axes;
-
-    // define PSF model type
-    // XXX need to carry the extra model parameters
-    int modelType = pmModelClassGetType ("PS_MODEL_GAUSS");
-
-    char *PSF_NAME = psMetadataLookupStr (&status, header, "PSF_NAME");
-    if (PSF_NAME != NULL) {
-        modelType = pmModelClassGetType (PSF_NAME);
-    }
-    assert (modelType > -1);
-
-    // We get the size of the table, and allocate the array of sources first because the table
-    // is large and ephemeral --- when the table gets blown away, whatever is allocated after
-    // the table is read blocks the free.  In fact, it's better to read the table row by row.
-    long numSources = psFitsTableSize(fits); // Number of sources in table
-    psArray *sources = psArrayAlloc(numSources); // Array of sources, to return
-
-    // convert the table to the pmSource entriesa
-    for (int i = 0; i < numSources; i++) {
-        psMetadata *row = psFitsReadTableRow(fits, i); // Table row
-        if (!row) {
-            psError(psErrorCodeLast(), false, "Unable to read row %d of sources", i);
-            psFree(sources);
-            return NULL;
-        }
-
-        pmSource *source = pmSourceAlloc ();
-        pmModel *model = pmModelAlloc (modelType);
-        source->modelPSF  = model;
-        source->type = PM_SOURCE_TYPE_STAR; // XXX this should be added to the flags
-
-        // NOTE: A SEGV here because "model" is NULL is probably caused by not initialising the models.
-        PAR = model->params->data.F32;
-        dPAR = model->dparams->data.F32;
-
-        source->seq       = psMetadataLookupU32 (&status, row, "IPP_IDET");
-        PAR[PM_PAR_XPOS]  = psMetadataLookupF32 (&status, row, "X_PSF");
-        PAR[PM_PAR_YPOS]  = psMetadataLookupF32 (&status, row, "Y_PSF");
-        dPAR[PM_PAR_XPOS] = psMetadataLookupF32 (&status, row, "X_PSF_SIG");
-        dPAR[PM_PAR_YPOS] = psMetadataLookupF32 (&status, row, "Y_PSF_SIG");
-        axes.major        = psMetadataLookupF32 (&status, row, "PSF_MAJOR");
-        axes.minor        = psMetadataLookupF32 (&status, row, "PSF_MINOR");
-        axes.theta        = psMetadataLookupF32 (&status, row, "PSF_THETA");
-
-        PAR[PM_PAR_SKY]   = psMetadataLookupF32 (&status, row, "SKY");
-        dPAR[PM_PAR_SKY]  = psMetadataLookupF32 (&status, row, "SKY_SIGMA");
-        source->sky       = PAR[PM_PAR_SKY];
-        source->skyErr    = dPAR[PM_PAR_SKY];
-
-        // XXX use these to determine PAR[PM_PAR_I0]?
-        source->psfMag    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG");
-        source->psfMagErr    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG_SIG");
-        source->apMag     = psMetadataLookupF32 (&status, row, "AP_MAG");
-
-        // XXX this scaling is incorrect: does not include the 2 \pi AREA factor
-        PAR[PM_PAR_I0]    = (isfinite(source->psfMag)) ? pow(10.0, -0.4*source->psfMag) : NAN;
-        dPAR[PM_PAR_I0]   = (isfinite(source->psfMag)) ? PAR[PM_PAR_I0] * source->psfMagErr : NAN;
-
-        pmPSF_AxesToModel (PAR, axes);
-
-        float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
-        float peakFlux    = (isfinite(peakMag)) ? pow(10.0, -0.4*peakMag) : NAN;
-
-        // recreate the peak to match (xPos, yPos) +/- (xErr, yErr)
-        source->peak = pmPeakAlloc(PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], peakFlux, PM_PEAK_LONE);
-        source->peak->rawFlux = peakFlux;
-        source->peak->smoothFlux = peakFlux;
-        source->peak->xf   = PAR[PM_PAR_XPOS]; // more accurate position
-        source->peak->yf   = PAR[PM_PAR_YPOS]; // more accurate position
-        source->peak->dx   = dPAR[PM_PAR_XPOS];
-        source->peak->dy   = dPAR[PM_PAR_YPOS];
-
-        source->pixWeightNotBad = psMetadataLookupF32 (&status, row, "PSF_QF");
-        source->crNsigma  = psMetadataLookupF32 (&status, row, "CR_NSIGMA");
-        source->extNsigma = psMetadataLookupF32 (&status, row, "EXT_NSIGMA");
-        source->apRadius  = psMetadataLookupS32 (&status, row, "AP_MAG_RADIUS");
-
-        // note that some older versions used PSF_PROBABILITY: this was not well defined.
-        model->chisq      = psMetadataLookupF32 (&status, row, "PSF_CHISQ");
-        model->nDOF       = psMetadataLookupS32 (&status, row, "PSF_NDOF");
-        model->nPix       = psMetadataLookupS32 (&status, row, "PSF_NPIX");
-
-        source->moments = pmMomentsAlloc ();
-        source->moments->Mx = source->peak->xf; // we don't have both Mx,My and xf,yf in the cmf
-        source->moments->My = source->peak->yf; // we don't have both Mx,My and xf,yf in the cmf
-
-        source->moments->Mxx = psMetadataLookupF32 (&status, row, "MOMENTS_XX");
-        source->moments->Mxy = psMetadataLookupF32 (&status, row, "MOMENTS_XY");
-        source->moments->Myy = psMetadataLookupF32 (&status, row, "MOMENTS_YY");
-
-        source->mode = psMetadataLookupU32 (&status, row, "FLAGS");
-        assert (status);
-
-        sources->data[i] = source;
-        psFree(row);
-    }
-
-    return sources;
-}
-
-bool pmSourcesWrite_CMF_PS1_V2_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
-{
-
-    bool status;
-    psArray *table;
-    psMetadata *row;
-    psF32 *PAR, *dPAR;
-    psF32 xPos, yPos;
-    psF32 xErr, yErr;
-    int nRow = -1;
-
-    // create a header to hold the output data
-    psMetadata *outhead = psMetadataAlloc ();
-
-    // write the links to the image header
-    psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTNAME", PS_META_REPLACE, "xsrc table extension", extname);
-
-    pmChip *chip = readout->parent->parent;
-    pmFPA  *fpa  = chip->parent;
-
-    // zero point corrections
-    bool status1 = false;
-    bool status2 = false;
-    float magOffset = 0.0;
-    float exptime   = psMetadataLookupF32(&status1, fpa->concepts, "FPA.EXPOSURE");
-    float zeropt    = psMetadataLookupF32(&status2, fpa->concepts, "FPA.ZP");
-    if (!isfinite(zeropt)) {
-        zeropt    = psMetadataLookupF32 (&status2, imageHeader, "ZPT_OBS");
-    }
-    if (status1 && status2 && (exptime > 0.0)) {
-        magOffset = zeropt + 2.5*log10(exptime);
-    }
-
-    // let's write these out in S/N order
-    sources = psArraySort (sources, pmSourceSortByFlux);
-
-    table = psArrayAllocEmpty (sources->n);
-
-    // which extended source analyses should we perform?
-    bool doAnnuli       = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
-    bool doPetrosian    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
-    // bool doIsophotal    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ISOPHOTAL");
-    // bool doKron         = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_KRON");
-
-    psVector *radMin = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.LOWER");
-    psVector *radMax = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.UPPER");
-    psAssert (radMin->n == radMax->n, "inconsistent annular bins");
-
-    // int nRadialBins = 0;
-    // if (doAnnuli) {
-    // 	// get the max count of radial bins
-    // 	for (int i = 0; i < sources->n; i++) {
-    // 	    pmSource *source = sources->data[i];
-    // 	    if (!source->extpars) continue;
-    // 	    if (!source->extpars->radProfile ) continue;
-    //         if (!source->extpars->radProfile->binSB) continue;
-    // 	    nRadialBins = PS_MAX(nRadialBins, source->extpars->radProfile->binSB->n);
-    // 	}
-    // }
-
-    // we write out all sources, regardless of quality.  the source flags tell us the state
-    for (int i = 0; i < sources->n; i++) {
-        // skip source if it is not a ext sourc
-        // XXX we have two places that extended source parameters are measured:
-        // psphotExtendedSources, which measures the aperture-like parameters and (potentially) the psf-convolved extended source models,
-        // psphotFitEXT, which does the simple extended source model fit (not psf-convolved)
-        // should we require both?
-
-        pmSource *source = sources->data[i];
-
-        // skip sources without measurements
-        if (source->extpars == NULL) continue;
-
-        // we require a PSF model fit (ignore the real crud)
-        pmModel *model = source->modelPSF;
-        if (model == NULL) continue;
-
-        // XXX I need to split the extended models from the extended aperture measurements
-        PAR = model->params->data.F32;
-        dPAR = model->dparams->data.F32;
-        xPos = PAR[PM_PAR_XPOS];
-        yPos = PAR[PM_PAR_YPOS];
-        xErr = dPAR[PM_PAR_XPOS];
-        yErr = dPAR[PM_PAR_YPOS];
-
-        row = psMetadataAlloc ();
-
-        // XXX we are not writing out the mode (flags) or the type (psf, ext, etc)
-        psMetadataAdd (row, PS_LIST_TAIL, "IPP_IDET",         PS_DATA_U32, "IPP detection identifier index",             source->seq);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_EXT",            PS_DATA_F32, "EXT model x coordinate",                     xPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_EXT",            PS_DATA_F32, "EXT model y coordinate",                     yPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_EXT_SIG",        PS_DATA_F32, "Sigma in EXT x coordinate",                  xErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_EXT_SIG",        PS_DATA_F32, "Sigma in EXT y coordinate",                  yErr);
-
-	float AxialRatio = NAN;
-	float AxialTheta = NAN;
-	pmSourceExtendedPars *extpars = source->extpars;
-	if (extpars) {
-	    AxialRatio = extpars->axes.minor / extpars->axes.major;
-	    AxialTheta = extpars->axes.theta;
-	}
-        psMetadataAdd (row, PS_LIST_TAIL, "F25_ARATIO",       PS_DATA_F32, "Axial Ratio of radial profile",              AxialRatio);
-        psMetadataAdd (row, PS_LIST_TAIL, "F25_THETA",        PS_DATA_F32, "Angle of radial profile ellipse",                  AxialTheta);
-
-        // Petrosian measurements
-        // XXX insert header data: petrosian ref radius, flux ratio
-	// XXX check flags to see if Pet was measured
-        if (doPetrosian) {
-	    pmSourceExtendedPars *extpars = source->extpars;
-            if (extpars) {
-		// XXX note that this mag is either calibrated or instrumental depending on existence of zero point 
-		float mag = (extpars->petrosianFlux > 0.0) ? -2.5*log10(extpars->petrosianFlux) + magOffset : NAN; // XXX zero point
-		float magErr = (extpars->petrosianFlux > 0.0) ? extpars->petrosianFlux / extpars->petrosianFluxErr : NAN; // XXX zero point
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG",        PS_DATA_F32, "Petrosian Magnitude", mag);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG_ERR",    PS_DATA_F32, "Petrosian Magnitude Error", magErr);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS",     PS_DATA_F32, "Petrosian Radius (pix)", extpars->petrosianRadius);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_ERR", PS_DATA_F32, "Petrosian Radius Error (pix)", extpars->petrosianRadiusErr);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50",     PS_DATA_F32, "Petrosian R50 (pix)", extpars->petrosianR50);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50_ERR", PS_DATA_F32, "Petrosian R50 Error (pix)", extpars->petrosianR50Err);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90",     PS_DATA_F32, "Petrosian R90 (pix)", extpars->petrosianR90);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90_ERR", PS_DATA_F32, "Petrosian R90 Error (pix)", extpars->petrosianR90Err);
-            } else {
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG",        PS_DATA_F32, "Petrosian Magnitude",       NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG_ERR",    PS_DATA_F32, "Petrosian Magnitude Error", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS",     PS_DATA_F32, "Petrosian Radius",          NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_ERR", PS_DATA_F32, "Petrosian Radius Error",    NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50",     PS_DATA_F32, "Petrosian R50 (pix)", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50_ERR", PS_DATA_F32, "Petrosian R50 Error (pix)",NAN); 
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90",     PS_DATA_F32, "Petrosian R90 (pix)", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90_ERR", PS_DATA_F32, "Petrosian R90 Error (pix)",NAN); 
-            }
-        }
-
-# if (0)
-        // Kron measurements
-        if (doKron) {
-            pmSourceKronValues *kron = source->extpars->kron;
-            if (kron) {
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG",        PS_DATA_F32, "Kron Magnitude",       kron->mag);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG_ERR",    PS_DATA_F32, "Kron Magnitude Error", kron->magErr);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS",     PS_DATA_F32, "Kron Radius",          kron->rad);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS_ERR", PS_DATA_F32, "Kron Radius Error",    kron->radErr);
-            } else {
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG",        PS_DATA_F32, "Kron Magnitude",       NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_MAG_ERR",    PS_DATA_F32, "Kron Magnitude Error", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS",     PS_DATA_F32, "Kron Radius",          NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "KRON_RADIUS_ERR", PS_DATA_F32, "Kron Radius Error",    NAN);
-            }
-        }
-
-        // Isophot measurements
-        // XXX insert header data: isophotal level
-        if (doIsophotal) {
-            pmSourceIsophotalValues *isophot = source->extpars->isophot;
-            if (isophot) {
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG",        PS_DATA_F32, "Isophot Magnitude",       isophot->mag);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG_ERR",    PS_DATA_F32, "Isophot Magnitude Error", isophot->magErr);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS",     PS_DATA_F32, "Isophot Radius",          isophot->rad);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS_ERR", PS_DATA_F32, "Isophot Radius Error",    isophot->radErr);
-            } else {
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG",        PS_DATA_F32, "Isophot Magnitude",       NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_MAG_ERR",    PS_DATA_F32, "Isophot Magnitude Error", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS",     PS_DATA_F32, "Isophot Radius",          NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "ISOPHOT_RADIUS_ERR", PS_DATA_F32, "Isophot Radius Error",    NAN);
-            }
-        }
-# endif
-
-        // Flux Annuli (if we have extended source measurements, we have these.  only optionally save them)
-        if (doAnnuli) {
-	    psVector *radSB   = psVectorAlloc(radMin->n, PS_TYPE_F32);
-	    psVector *radFlux = psVectorAlloc(radMin->n, PS_TYPE_F32);
-	    psVector *radFill = psVectorAlloc(radMin->n, PS_TYPE_F32);
-	    psVectorInit (radSB, NAN);
-	    psVectorInit (radFlux, NAN);
-	    psVectorInit (radFill, NAN);
-	    if (!source->extpars) goto empty_annuli;
-	    if (!source->extpars->radProfile) goto empty_annuli;
-	    if (!source->extpars->radProfile->binSB) goto empty_annuli;
-	    psAssert (source->extpars->radProfile->binSum, "programming error");
-	    psAssert (source->extpars->radProfile->binFill, "programming error");
-	    psAssert (source->extpars->radProfile->binSB->n <= radFlux->n, "inconsistent vector lengths");
-	    psAssert (source->extpars->radProfile->binSum->n <= radFlux->n, "inconsistent vector lengths");
-	    psAssert (source->extpars->radProfile->binFill->n <= radFlux->n, "inconsistent vector lengths");
-
-	    // copy the data from fluxVal (which is not guaranteed to be the full length) to radFlux
-	    for (int j = 0; j < source->extpars->radProfile->binSB->n; j++) {
-		radSB->data.F32[j]   = source->extpars->radProfile->binSB->data.F32[j];
-		radFlux->data.F32[j] = source->extpars->radProfile->binSum->data.F32[j];
-		radFill->data.F32[j] = source->extpars->radProfile->binFill->data.F32[j];
-	    }
-
-	empty_annuli:
-	    psMetadataAdd (row, PS_LIST_TAIL, "PROF_SB", PS_DATA_VECTOR, "mean surface brightness annuli", radSB);
-	    psMetadataAdd (row, PS_LIST_TAIL, "PROF_FLUX", PS_DATA_VECTOR, "flux within annuli", radFlux);
-	    psMetadataAdd (row, PS_LIST_TAIL, "PROF_FILL", PS_DATA_VECTOR, "fill factor of annuli", radFill);
-	    psFree (radSB);
-	    psFree (radFlux);
-	    psFree (radFill);
-	}
-	if (nRow < 0) {
-	    nRow = row->list->n;
-	} else {
-	    psAssert (nRow == row->list->n, "inconsistent row lengths");
-	}
-	psArrayAdd (table, 100, row);
-	psFree (row);
-    }
-    
-    if (table->n == 0) {
-	if (!psFitsWriteBlank (fits, outhead, extname)) {
-	    psError(psErrorCodeLast(), false, "Unable to write empty sources file.");
-	    psFree(outhead);
-	    psFree(table);
-	    return false;
-	}
-	psFree (outhead);
-	psFree (table);
-	return true;
-    }
-    
-    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
-    if (!psFitsWriteTable (fits, outhead, table, extname)) {
-	psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
-	psFree (outhead);
-    psFree(table);
-    return false;
-    }
-    psFree (outhead);
-    psFree (table);
-    
-    return true;
-}
-
-// XXX this layout is still the same as PS1_DEV_1
-bool pmSourcesWrite_CMF_PS1_V2_XFIT(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname)
-{
-
-    psArray *table;
-    psMetadata *row;
-    psF32 *PAR, *dPAR;
-    psEllipseAxes axes;
-    psF32 xPos, yPos;
-    psF32 xErr, yErr;
-    char name[64];
-
-    // create a header to hold the output data
-    psMetadata *outhead = psMetadataAlloc ();
-
-    // write the links to the image header
-    psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTNAME", PS_META_REPLACE, "xsrc table extension", extname);
-
-    // let's write these out in S/N order
-    sources = psArraySort (sources, pmSourceSortByFlux);
-
-    // we are writing one row per model; we need to write out same number of columns for each row: find the max Nparams
-    int nParamMax = 0;
-    for (int i = 0; i < sources->n; i++) {
-        pmSource *source = sources->data[i];
-        if (source->modelFits == NULL) continue;
-        for (int j = 0; j < source->modelFits->n; j++) {
-            pmModel *model = source->modelFits->data[j];
-            assert (model);
-            nParamMax = PS_MAX (nParamMax, model->params->n);
-        }
-    }
-
-    table = psArrayAllocEmpty (sources->n);
-
-    // we write out all sources, regardless of quality.  the source flags tell us the state
-    for (int i = 0; i < sources->n; i++) {
-
-        pmSource *source = sources->data[i];
-
-        // XXX if no model fits are saved, write out modelEXT?
-        if (source->modelFits == NULL) continue;
-
-        // We have multiple sources : need to flag the one used to subtract the light (the 'best' model)
-        for (int j = 0; j < source->modelFits->n; j++) {
-
-            // choose the convolved EXT model, if available, otherwise the simple one
-            pmModel *model = source->modelFits->data[j];
-            assert (model);
-
-	    // skip models which were not actually fitted
-	    if (model->flags & PM_MODEL_STATUS_BADARGS) continue;
-
-            PAR = model->params->data.F32;
-            dPAR = model->dparams->data.F32;
-            xPos = PAR[PM_PAR_XPOS];
-            yPos = PAR[PM_PAR_YPOS];
-            xErr = dPAR[PM_PAR_XPOS];
-            yErr = dPAR[PM_PAR_YPOS];
-
-            axes = pmPSF_ModelToAxes (PAR, 20.0);
-
-            row = psMetadataAlloc ();
-
-            // XXX we are not writing out the mode (flags) or the type (psf, ext, etc)
-            psMetadataAddU32 (row, PS_LIST_TAIL, "IPP_IDET",         0, "IPP detection identifier index",             source->seq);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "X_EXT",            0, "EXT model x coordinate",                     xPos);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "Y_EXT",            0, "EXT model y coordinate",                     yPos);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "X_EXT_SIG",        0, "Sigma in EXT x coordinate",                  xErr);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "Y_EXT_SIG",        0, "Sigma in EXT y coordinate",                  yErr);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_INST_MAG",     0, "EXT fit instrumental magnitude",             model->mag);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_INST_MAG_SIG", 0, "Sigma of PSF instrumental magnitude",        model->magErr);
-
-            psMetadataAddF32 (row, PS_LIST_TAIL, "NPARAMS",          0, "number of model parameters",                 model->params->n);
-            psMetadataAddStr (row, PS_LIST_TAIL, "MODEL_TYPE",       0, "name of model",                              pmModelClassGetName (model->type));
-
-            // XXX these should be major and minor, not 'x' and 'y'
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_WIDTH_MAJ",    0, "EXT width in x coordinate",                  axes.major);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_WIDTH_MIN",    0, "EXT width in y coordinate",                  axes.minor);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_THETA",        0, "EXT orientation angle",                      axes.theta);
-
-            // write out the other generic parameters
-            for (int k = 0; k < nParamMax; k++) {
-                if (k == PM_PAR_I0) continue;
-                if (k == PM_PAR_SKY) continue;
-                if (k == PM_PAR_XPOS) continue;
-                if (k == PM_PAR_YPOS) continue;
-                if (k == PM_PAR_SXX) continue;
-                if (k == PM_PAR_SXY) continue;
-                if (k == PM_PAR_SYY) continue;
-
-                snprintf (name, 64, "EXT_PAR_%02d", k);
-
-                if (k < model->params->n) {
-                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "", model->params->data.F32[k]);
-                } else {
-                    psMetadataAddF32 (row, PS_LIST_TAIL, name, PS_DATA_F32, "", NAN);
-                }
-            }
-
-            // XXX other parameters which may be set.
-            // XXX flag / value to define the model
-            // XXX write out the model type, fit status flags
-
-            psArrayAdd (table, 100, row);
-            psFree (row);
-        }
-    }
-
-    if (table->n == 0) {
-        if (!psFitsWriteBlank (fits, outhead, extname)) {
-            psError(psErrorCodeLast(), false, "Unable to write empty sources file.");
-            psFree(outhead);
-            psFree(table);
-            return false;
-        }
-        psFree (outhead);
-        psFree (table);
-        return true;
-    }
-
-    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
-    if (!psFitsWriteTable (fits, outhead, table, extname)) {
-        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
-        psFree (outhead);
-        psFree(table);
-        return false;
-    }
-    psFree (outhead);
-    psFree (table);
-    return true;
-}
-
-bool pmSourcesWrite_CMF_PS1_V2_XRAD(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
-{
-    return true;
-}
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_V3.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMF_PS1_V3.c	(revision 32161)
+++ 	(revision )
@@ -1,680 +1,0 @@
-/** @file  pmSourceIO.c
- *
- *  @author EAM, IfA
- *
- *  @version $Revision: 1.3 $ $Name: not supported by cvs2svn $
- *  @date $Date: 2009-02-18 02:44:19 $
- *
- *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <math.h>
-#include <string.h>
-#include <pslib.h>
-
-#include "pmConfig.h"
-#include "pmDetrendDB.h"
-
-#include "pmHDU.h"
-#include "pmFPA.h"
-#include "pmFPALevel.h"
-#include "pmFPAview.h"
-#include "pmFPAfile.h"
-
-#include "pmTrend2D.h"
-#include "pmResiduals.h"
-#include "pmGrowthCurve.h"
-#include "pmSpan.h"
-#include "pmFootprintSpans.h"
-#include "pmFootprint.h"
-#include "pmPeaks.h"
-#include "pmMoments.h"
-#include "pmModelFuncs.h"
-#include "pmModel.h"
-#include "pmModelUtils.h"
-#include "pmModelClass.h"
-#include "pmSourceMasks.h"
-#include "pmSourceExtendedPars.h"
-#include "pmSourceDiffStats.h"
-#include "pmSource.h"
-#include "pmSourceFitModel.h"
-#include "pmPSF.h"
-#include "pmPSFtry.h"
-
-#include "pmSourceIO.h"
-#include "pmSourceOutputs.h"
-
-// panstarrs-style FITS table output (header + table in 1st extension)
-// this format consists of a header derived from the image header
-// followed by a zero-size matrix, followed by the table data
-
-bool pmSourcesWrite_CMF_PS1_V3 (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, psMetadata *tableHeader, char *extname, psMetadata *recipe)
-{
-    PS_ASSERT_PTR_NON_NULL(fits, false);
-    PS_ASSERT_PTR_NON_NULL(sources, false);
-    PS_ASSERT_PTR_NON_NULL(extname, false);
-
-    psArray *table;
-    psMetadata *row;
-
-    pmChip *chip = readout->parent->parent;
-
-    // if the sequence is defined, write these in seq order; otherwise
-    // write them in S/N order:
-    if (sources->n > 0) {
-        pmSource *source = (pmSource *) sources->data[0];
-        if (source->seq == -1) {
-	    // let's write these out in S/N order
-	    sources = psArraySort (sources, pmSourceSortByFlux);
-        } else {
-	    sources = psArraySort (sources, pmSourceSortBySeq);
-        }
-    }
-
-    table = psArrayAllocEmpty (sources->n);
-
-    short nImageOverlap; 
-    float magOffset; 
-    float zeroptErr; 
-    float fwhmMajor; 
-    float fwhmMinor;
-    pmSourceOutputsCommonValues (&nImageOverlap, &magOffset, &zeroptErr, &fwhmMajor, &fwhmMinor, readout, imageHeader);
-
-    // we write out PSF-fits for all sources, regardless of quality.  the source flags tell us the state
-    // by the time we call this function, all values should be assigned.  let's use asserts to be sure in some cases.
-    for (int i = 0; i < sources->n; i++) {
-        pmSource *source = (pmSource *) sources->data[i];
-
-        // If source->seq is -1, source was generated in this analysis.  If source->seq is
-        // not -1, source was read from elsewhere: in the latter case, preserve the source
-        // ID.  source.seq is used instead of source.id since the latter is a const
-        // generated on Alloc, and would thus be wrong for read in sources.
-        if (source->seq == -1) {
-	    source->seq = i;
-        }
-
-	// set the 'best' values for various output fields:
-	pmSourceOutputs outputs;
-	pmSourceOutputsSetValues (&outputs, source, chip, fwhmMajor, fwhmMinor, magOffset);
-
-	pmSourceOutputsMoments moments;
-	pmSourceOutputsSetMoments (&moments, source);
-
-        row = psMetadataAlloc ();
-        psMetadataAdd (row, PS_LIST_TAIL, "IPP_IDET",         PS_DATA_U32, "IPP detection identifier index",             source->seq);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_PSF",            PS_DATA_F32, "PSF x coordinate",                           outputs.xPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_PSF",            PS_DATA_F32, "PSF y coordinate",                           outputs.yPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_PSF_SIG",        PS_DATA_F32, "Sigma in PSF x coordinate",                  outputs.xErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_PSF_SIG",        PS_DATA_F32, "Sigma in PSF y coordinate",                  outputs.yErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "POSANGLE",         PS_DATA_F32, "position angle at source (degrees)",         outputs.posAngle);
-        psMetadataAdd (row, PS_LIST_TAIL, "PLTSCALE",         PS_DATA_F32, "plate scale at source (arcsec/pixel)",       outputs.pltScale);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_MAG",     PS_DATA_F32, "PSF fit instrumental magnitude",             source->psfMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_MAG_SIG", PS_DATA_F32, "Sigma of PSF instrumental magnitude",        source->psfMagErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_FLUX",    PS_DATA_F32, "PSF fit instrumental flux (counts)",         source->psfFlux);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_INST_FLUX_SIG",PS_DATA_F32, "Sigma of PSF instrumental flux",             source->psfFluxErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG",           PS_DATA_F32, "magnitude in standard aperture",             source->apMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RAW",       PS_DATA_F32, "magnitude in reported aperture",             source->apMagRaw);
-        psMetadataAdd (row, PS_LIST_TAIL, "AP_MAG_RADIUS",    PS_DATA_F32, "radius used for aperture mags",              outputs.apRadius);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "CAL_PSF_MAG",      PS_DATA_F32, "PSF Magnitude using supplied calibration",   outputs.calMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "CAL_PSF_MAG_SIG",  PS_DATA_F32, "measured scatter of zero point calibration", zeroptErr);
-	
-	// NOTE: RA & DEC (both double) need to be on an 8-byte boundary...
-        psMetadataAdd (row, PS_LIST_TAIL, "RA_PSF",           PS_DATA_F64, "PSF RA coordinate (degrees)",                outputs.ra);
-        psMetadataAdd (row, PS_LIST_TAIL, "DEC_PSF",          PS_DATA_F64, "PSF DEC coordinate (degrees)",               outputs.dec);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "PEAK_FLUX_AS_MAG", PS_DATA_F32, "Peak flux expressed as magnitude",           outputs.peakMag);
-        psMetadataAdd (row, PS_LIST_TAIL, "SKY",              PS_DATA_F32, "Sky level",                                  source->sky);
-        psMetadataAdd (row, PS_LIST_TAIL, "SKY_SIGMA",        PS_DATA_F32, "Sigma of sky level",                         source->skyErr);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_CHISQ",        PS_DATA_F32, "Chisq of PSF-fit",                           outputs.chisq);
-        psMetadataAdd (row, PS_LIST_TAIL, "CR_NSIGMA",        PS_DATA_F32, "Nsigma deviations from PSF to CF",           source->crNsigma);
-        psMetadataAdd (row, PS_LIST_TAIL, "EXT_NSIGMA",       PS_DATA_F32, "Nsigma deviations from PSF to EXT",          source->extNsigma);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_MAJOR",        PS_DATA_F32, "PSF width (major axis)",                     outputs.psfMajor);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_MINOR",        PS_DATA_F32, "PSF width (minor axis)",                     outputs.psfMinor);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_THETA",        PS_DATA_F32, "PSF orientation angle",                      outputs.psfTheta);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF",           PS_DATA_F32, "PSF coverage/quality factor (bad)",          source->pixWeightNotBad);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_QF_PERFECT",   PS_DATA_F32, "PSF coverage/quality factor (poor)",         source->pixWeightNotPoor);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_NDOF",         PS_DATA_S32, "degrees of freedom",                         outputs.nDOF);
-        psMetadataAdd (row, PS_LIST_TAIL, "PSF_NPIX",         PS_DATA_S32, "number of pixels in fit",                    outputs.nPix);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_XX",       PS_DATA_F32, "second moments (X^2)",                       moments.Mxx);
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_XY",       PS_DATA_F32, "second moments (X*Y)",                       moments.Mxy);
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_YY",       PS_DATA_F32, "second moments (Y*Y)",                       moments.Myy);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M3C",      PS_DATA_F32, "third momemt cos theta",                     moments.M_c3);
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M3S",      PS_DATA_F32, "third momemt sin theta",                     moments.M_s3);
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M4C",      PS_DATA_F32, "fourth momemt cos theta",                    moments.M_c4);
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_M4S",      PS_DATA_F32, "fourth momemt sin theta",                    moments.M_s4);
-
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_R1",       PS_DATA_F32, "first radial moment",                        moments.Mrf);
-        psMetadataAdd (row, PS_LIST_TAIL, "MOMENTS_RH",       PS_DATA_F32, "half radial moment",                         moments.Mrh);
-        psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX",        PS_DATA_F32, "Kron Flux (in 2.5 R1)",                      moments.Krf);
-        psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_ERR",    PS_DATA_F32, "Kron Flux Error",                            moments.dKrf);
-        psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_INNER",  PS_DATA_F32, "Kron Flux (in 2.5 R1)",                      moments.Kinner);
-        psMetadataAdd (row, PS_LIST_TAIL, "KRON_FLUX_OUTER",  PS_DATA_F32, "Kron Flux (in 2.5 R1)",                      moments.Kouter);
-
-	// Do NOT write these : not consistent with the definition of PS1_V3 in Ohana/src/libautocode/dev/cmf-ps1-v3.d
-        // psMetadataAdd (row, PS_LIST_TAIL, "KRON_CORE_FLUX",   PS_DATA_F32, "Kron Flux (in 1.0 R1)",                      moments.KronCore);
-	// psMetadataAdd (row, PS_LIST_TAIL, "KRON_CORE_ERROR",  PS_DATA_F32, "Kron Error (in 1.0 R1)",                     moments.KronCoreErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "FLAGS",            PS_DATA_U32, "psphot analysis flags",                      source->mode);
-        psMetadataAdd (row, PS_LIST_TAIL, "FLAGS2",           PS_DATA_U32, "psphot analysis flags",                      source->mode2);
-        psMetadataAdd (row, PS_LIST_TAIL, "PADDING2",         PS_DATA_S32, "more padding", 0);
-
-        // XXX not sure how to get this : need to load Nimages with weight?
-        psMetadataAdd (row, PS_LIST_TAIL, "N_FRAMES",         PS_DATA_U16, "Number of frames overlapping source center", nImageOverlap);
-        psMetadataAdd (row, PS_LIST_TAIL, "PADDING",          PS_DATA_S16, "padding", 0);
-
-        psArrayAdd (table, 100, row);
-        psFree (row);
-
-        // EXT_NSIGMA will be NAN if: 1) contour ellipse is imaginary; 2) source is not
-        // subtracted
-
-        // CR_NSIGMA will be NAN if: 1) source is not subtracted; 2) source is on the image
-        // edge; 3) any pixels in the 3x3 peak region are masked;
-    }
-
-    // XXX why do we make a copy here to be supplemented with the masks?  why not do this in the calling function?
-    psMetadata *header = psMetadataCopy(NULL, tableHeader);
-    pmSourceMasksHeader(header);
-
-    if (table->n == 0) {
-        if (!psFitsWriteBlank(fits, header, extname)) {
-            psError(psErrorCodeLast(), false, "Unable to write blank sources file.");
-            psFree(table);
-            psFree(header);
-            return false;
-        }
-        psFree(table);
-        psFree(header);
-        return true;
-    }
-
-    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
-    if (!psFitsWriteTable(fits, header, table, extname)) {
-        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
-        psFree(table);
-        psFree(header);
-        return false;
-    }
-    psFree(table);
-    psFree(header);
-
-    return true;
-}
-
-// read in a readout from the fits file
-psArray *pmSourcesRead_CMF_PS1_V3 (psFits *fits, psMetadata *header)
-{
-    PS_ASSERT_PTR_NON_NULL(fits, false);
-    PS_ASSERT_PTR_NON_NULL(header, false);
-
-    bool status;
-    psF32 *PAR, *dPAR;
-    psEllipseAxes axes;
-
-    // define PSF model type
-    // XXX need to carry the extra model parameters
-    int modelType = pmModelClassGetType ("PS_MODEL_GAUSS");
-
-    char *PSF_NAME = psMetadataLookupStr (&status, header, "PSF_NAME");
-    if (PSF_NAME != NULL) {
-        modelType = pmModelClassGetType (PSF_NAME);
-    }
-    assert (modelType > -1);
-
-    // We get the size of the table, and allocate the array of sources first because the table
-    // is large and ephemeral --- when the table gets blown away, whatever is allocated after
-    // the table is read blocks the free.  In fact, it's better to read the table row by row.
-    long numSources = psFitsTableSize(fits); // Number of sources in table
-    psArray *sources = psArrayAlloc(numSources); // Array of sources, to return
-
-    // convert the table to the pmSource entriesa
-    for (int i = 0; i < numSources; i++) {
-        psMetadata *row = psFitsReadTableRow(fits, i); // Table row
-        if (!row) {
-            psError(psErrorCodeLast(), false, "Unable to read row %d of sources", i);
-            psFree(sources);
-            return NULL;
-        }
-
-        pmSource *source = pmSourceAlloc ();
-        pmModel *model = pmModelAlloc (modelType);
-        source->modelPSF  = model;
-        source->type = PM_SOURCE_TYPE_STAR; // XXX this should be added to the flags
-
-        // NOTE: A SEGV here because "model" is NULL is probably caused by not initialising the models.
-        PAR = model->params->data.F32;
-        dPAR = model->dparams->data.F32;
-
-        source->seq       = psMetadataLookupU32 (&status, row, "IPP_IDET");
-        PAR[PM_PAR_XPOS]  = psMetadataLookupF32 (&status, row, "X_PSF");
-        PAR[PM_PAR_YPOS]  = psMetadataLookupF32 (&status, row, "Y_PSF");
-        dPAR[PM_PAR_XPOS] = psMetadataLookupF32 (&status, row, "X_PSF_SIG");
-        dPAR[PM_PAR_YPOS] = psMetadataLookupF32 (&status, row, "Y_PSF_SIG");
-        axes.major        = psMetadataLookupF32 (&status, row, "PSF_MAJOR");
-        axes.minor        = psMetadataLookupF32 (&status, row, "PSF_MINOR");
-        axes.theta        = psMetadataLookupF32 (&status, row, "PSF_THETA");
-
-        PAR[PM_PAR_SKY]   = psMetadataLookupF32 (&status, row, "SKY");
-        dPAR[PM_PAR_SKY]  = psMetadataLookupF32 (&status, row, "SKY_SIGMA");
-        source->sky       = PAR[PM_PAR_SKY];
-        source->skyErr    = dPAR[PM_PAR_SKY];
-
-        source->psfMag    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG");
-        source->psfMagErr = psMetadataLookupF32 (&status, row, "PSF_INST_MAG_SIG");
-        source->apMag     = psMetadataLookupF32 (&status, row, "AP_MAG");
-        source->apMagRaw  = psMetadataLookupF32 (&status, row, "AP_MAG_RAW");
-
-        // XXX use these to determine PAR[PM_PAR_I0] if they exist?
-        source->psfFlux   = psMetadataLookupF32 (&status, row, "PSF_INST_FLUX");
-        source->psfFluxErr = psMetadataLookupF32 (&status, row, "PSF_INST_FLUX_SIG");
-
-        // XXX this scaling is incorrect: does not include the 2 \pi AREA factor
-        PAR[PM_PAR_I0]    = (isfinite(source->psfMag)) ? pow(10.0, -0.4*source->psfMag) : NAN;
-        dPAR[PM_PAR_I0]   = (isfinite(source->psfMag)) ? PAR[PM_PAR_I0] * source->psfMagErr : NAN;
-
-        pmPSF_AxesToModel (PAR, axes);
-
-        float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
-        float peakFlux    = (isfinite(peakMag)) ? pow(10.0, -0.4*peakMag) : NAN;
-
-        // recreate the peak to match (xPos, yPos) +/- (xErr, yErr)
-        source->peak = pmPeakAlloc(PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], peakFlux, PM_PEAK_LONE);
-        source->peak->rawFlux = peakFlux;
-        source->peak->smoothFlux = peakFlux;
-        source->peak->xf   = PAR[PM_PAR_XPOS]; // more accurate position
-        source->peak->yf   = PAR[PM_PAR_YPOS]; // more accurate position
-        source->peak->dx   = dPAR[PM_PAR_XPOS];
-        source->peak->dy   = dPAR[PM_PAR_YPOS];
-
-	// we no longer sort by S/N, only flux
-        // if (isfinite (source->psfMagErr) && (source->psfMagErr > 0.0)) {
-        //   source->peak->SN = 1.0 / source->psfMagErr;
-        // } else {
-        //   source->peak->SN = sqrt(source->peak->flux); // an alternate proxy: various functions sort by peak S/N
-        // }
-
-        source->pixWeightNotBad = psMetadataLookupF32 (&status, row, "PSF_QF");
-        source->pixWeightNotPoor = psMetadataLookupF32 (&status, row, "PSF_QF_PERFECT");
-        source->crNsigma  = psMetadataLookupF32 (&status, row, "CR_NSIGMA");
-        source->extNsigma = psMetadataLookupF32 (&status, row, "EXT_NSIGMA");
-        source->apRadius  = psMetadataLookupS32 (&status, row, "AP_MAG_RADIUS");
-
-        // note that some older versions used PSF_PROBABILITY: this was not well defined.
-        model->chisq      = psMetadataLookupF32 (&status, row, "PSF_CHISQ");
-        model->nDOF       = psMetadataLookupS32 (&status, row, "PSF_NDOF");
-        model->nPix       = psMetadataLookupS32 (&status, row, "PSF_NPIX");
-
-        source->moments = pmMomentsAlloc ();
-        source->moments->Mx = source->peak->xf; // we don't have both Mx,My and xf,yf in the cmf
-        source->moments->My = source->peak->yf; // we don't have both Mx,My and xf,yf in the cmf
-
-        source->moments->Mxx = psMetadataLookupF32 (&status, row, "MOMENTS_XX");
-        source->moments->Mxy = psMetadataLookupF32 (&status, row, "MOMENTS_XY");
-        source->moments->Myy = psMetadataLookupF32 (&status, row, "MOMENTS_YY");
-
-        source->moments->Mrf         = psMetadataLookupF32 (&status, row, "MOMENTS_R1");
-        source->moments->Mrh         = psMetadataLookupF32 (&status, row, "MOMENTS_RH");
-        source->moments->KronFlux    = psMetadataLookupF32 (&status, row, "KRON_FLUX");
-        source->moments->KronFluxErr = psMetadataLookupF32 (&status, row, "KRON_FLUX_ERR");
-
-        source->moments->KronFinner  = psMetadataLookupF32 (&status, row, "KRON_FLUX_INNER");
-        source->moments->KronFouter  = psMetadataLookupF32 (&status, row, "KRON_FLUX_OUTER");
-
-	// XXX we do not save all of the 3rd and 4th moment parameters. when we load in data,
-	// we are storing enough information so the output will be consistent with the input
-        source->moments->Mxxx = +1.0 * psMetadataLookupF32 (&status, row, "MOMENTS_M3C");
-        source->moments->Mxxy = 0.0;
-        source->moments->Mxyy = 0.0;
-        source->moments->Myyy = -1.0 * psMetadataLookupF32 (&status, row, "MOMENTS_M3S");
-
-        source->moments->Mxxxx = +1.00 * psMetadataLookupF32 (&status, row, "MOMENTS_M4C");
-        source->moments->Mxxxy = 0.0;
-        source->moments->Mxxyy = 0.0;
-        source->moments->Mxyyy = -0.25 * psMetadataLookupF32 (&status, row, "MOMENTS_M4S");
-        source->moments->Myyyy = 0.0;
-
-        source->mode = psMetadataLookupU32 (&status, row, "FLAGS");
-        source->mode2 = psMetadataLookupU32 (&status, row, "FLAGS2");
-        assert (status);
-
-        sources->data[i] = source;
-        psFree(row);
-    }
-
-    return sources;
-}
-
-bool pmSourcesWrite_CMF_PS1_V3_XSRC (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
-{
-    bool status;
-    psArray *table;
-    psMetadata *row;
-    psF32 *PAR, *dPAR;
-    psF32 xPos, yPos;
-    psF32 xErr, yErr;
-    int nRow = -1;
-    char keyword1[80], keyword2[80];
-
-    // create a header to hold the output data
-    psMetadata *outhead = psMetadataAlloc ();
-
-    // write the links to the image header
-    psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTNAME", PS_META_REPLACE, "xsrc table extension", extname);
-
-    pmChip *chip = readout->parent->parent;
-    pmFPA  *fpa  = chip->parent;
-
-    // zero point corrections
-    bool status1 = false;
-    bool status2 = false;
-    float magOffset = 0.0;
-    float exptime   = psMetadataLookupF32(&status1, fpa->concepts, "FPA.EXPOSURE");
-    float zeropt    = psMetadataLookupF32(&status2, fpa->concepts, "FPA.ZP");
-    if (!isfinite(zeropt)) {
-        zeropt    = psMetadataLookupF32 (&status2, imageHeader, "ZPT_OBS");
-    }
-    if (status1 && status2 && (exptime > 0.0)) {
-        magOffset = zeropt + 2.5*log10(exptime);
-    }
-
-    // let's write these out in S/N order
-    sources = psArraySort (sources, pmSourceSortByFlux);
-
-    table = psArrayAllocEmpty (sources->n);
-
-    // which extended source analyses should we perform?
-    bool doAnnuli       = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_ANNULI");
-    bool doPetrosian    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
-
-    psVector *radMin = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.LOWER");
-    psVector *radMax = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.UPPER");
-    psAssert (radMin->n == radMax->n, "inconsistent annular bins");
-
-    // write the radial profile apertures to header
-    for (int i = 0; i < radMax->n; i++) {
-      sprintf (keyword1, "RMIN_%02d", i);
-      sprintf (keyword2, "RMAX_%02d", i);
-      psMetadataAddF32 (outhead, PS_LIST_TAIL, keyword1, PS_META_REPLACE, "min radius for SB profile", radMin->data.F32[i]);
-      psMetadataAddF32 (outhead, PS_LIST_TAIL, keyword2, PS_META_REPLACE, "min radius for SB profile", radMax->data.F32[i]);
-    }
-
-    // we write out all sources, regardless of quality.  the source flags tell us the state
-    for (int i = 0; i < sources->n; i++) {
-        pmSource *source = sources->data[i];
-
-        // skip sources without measurements
-        if (source->extpars == NULL) continue;
-
-        // we require a PSF model fit (ignore the real crud)
-        pmModel *model = source->modelPSF;
-        if (model == NULL) continue;
-
-        // XXX I need to split the extended models from the extended aperture measurements
-        PAR = model->params->data.F32;
-        dPAR = model->dparams->data.F32;
-        xPos = PAR[PM_PAR_XPOS];
-        yPos = PAR[PM_PAR_YPOS];
-        xErr = dPAR[PM_PAR_XPOS];
-        yErr = dPAR[PM_PAR_YPOS];
-
-        row = psMetadataAlloc ();
-
-        // XXX we are not writing out the mode (flags) or the type (psf, ext, etc)
-        psMetadataAdd (row, PS_LIST_TAIL, "IPP_IDET",         PS_DATA_U32, "IPP detection identifier index",             source->seq);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_EXT",            PS_DATA_F32, "EXT model x coordinate",                     xPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_EXT",            PS_DATA_F32, "EXT model y coordinate",                     yPos);
-        psMetadataAdd (row, PS_LIST_TAIL, "X_EXT_SIG",        PS_DATA_F32, "Sigma in EXT x coordinate",                  xErr);
-        psMetadataAdd (row, PS_LIST_TAIL, "Y_EXT_SIG",        PS_DATA_F32, "Sigma in EXT y coordinate",                  yErr);
-
-	float AxialRatio = NAN;
-	float AxialTheta = NAN;
-	pmSourceExtendedPars *extpars = source->extpars;
-	if (extpars) {
-	    AxialRatio = extpars->axes.minor / extpars->axes.major;
-	    AxialTheta = extpars->axes.theta;
-	}
-        psMetadataAdd (row, PS_LIST_TAIL, "F25_ARATIO",       PS_DATA_F32, "Axial Ratio of radial profile",              AxialRatio);
-        psMetadataAdd (row, PS_LIST_TAIL, "F25_THETA",        PS_DATA_F32, "Angle of radial profile ellipse",                  AxialTheta);
-
-        // Petrosian measurements
-        // XXX insert header data: petrosian ref radius, flux ratio
-	// XXX check flags to see if Pet was measured
-        if (doPetrosian) {
-	    pmSourceExtendedPars *extpars = source->extpars;
-            if (extpars) {
-		// XXX note that this mag is either calibrated or instrumental depending on existence of zero point 
-		float mag = (extpars->petrosianFlux > 0.0) ? -2.5*log10(extpars->petrosianFlux) + magOffset : NAN; // XXX zero point
-		float magErr = (extpars->petrosianFlux > 0.0) ? extpars->petrosianFlux / extpars->petrosianFluxErr : NAN; // XXX zero point
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG",        PS_DATA_F32, "Petrosian Magnitude", mag);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG_ERR",    PS_DATA_F32, "Petrosian Magnitude Error", magErr);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS",     PS_DATA_F32, "Petrosian Radius (pix)", extpars->petrosianRadius);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_ERR", PS_DATA_F32, "Petrosian Radius Error (pix)", extpars->petrosianRadiusErr);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50",     PS_DATA_F32, "Petrosian R50 (pix)", extpars->petrosianR50);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50_ERR", PS_DATA_F32, "Petrosian R50 Error (pix)", extpars->petrosianR50Err);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90",     PS_DATA_F32, "Petrosian R90 (pix)", extpars->petrosianR90);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90_ERR", PS_DATA_F32, "Petrosian R90 Error (pix)", extpars->petrosianR90Err);
-            } else {
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG",        PS_DATA_F32, "Petrosian Magnitude",       NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_MAG_ERR",    PS_DATA_F32, "Petrosian Magnitude Error", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS",     PS_DATA_F32, "Petrosian Radius",          NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_ERR", PS_DATA_F32, "Petrosian Radius Error",    NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50",     PS_DATA_F32, "Petrosian R50 (pix)", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_50_ERR", PS_DATA_F32, "Petrosian R50 Error (pix)",NAN); 
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90",     PS_DATA_F32, "Petrosian R90 (pix)", NAN);
-                psMetadataAdd (row, PS_LIST_TAIL, "PETRO_RADIUS_90_ERR", PS_DATA_F32, "Petrosian R90 Error (pix)",NAN); 
-            }
-        }
-
-        // Flux Annuli (if we have extended source measurements, we have these.  only optionally save them)
-        if (doAnnuli) {
-	    psVector *radSB   = psVectorAlloc(radMin->n, PS_TYPE_F32);
-	    psVector *radFlux = psVectorAlloc(radMin->n, PS_TYPE_F32);
-	    psVector *radFill = psVectorAlloc(radMin->n, PS_TYPE_F32);
-	    psVectorInit (radSB, NAN);
-	    psVectorInit (radFlux, NAN);
-	    psVectorInit (radFill, NAN);
-	    if (!source->extpars) goto empty_annuli;
-	    if (!source->extpars->radProfile) goto empty_annuli;
-	    if (!source->extpars->radProfile->binSB) goto empty_annuli;
-	    psAssert (source->extpars->radProfile->binSum, "programming error");
-	    psAssert (source->extpars->radProfile->binFill, "programming error");
-	    psAssert (source->extpars->radProfile->binSB->n <= radFlux->n, "inconsistent vector lengths");
-	    psAssert (source->extpars->radProfile->binSum->n <= radFlux->n, "inconsistent vector lengths");
-	    psAssert (source->extpars->radProfile->binFill->n <= radFlux->n, "inconsistent vector lengths");
-
-	    // copy the data from fluxVal (which is not guaranteed to be the full length) to radFlux
-	    for (int j = 0; j < source->extpars->radProfile->binSB->n; j++) {
-		radSB->data.F32[j]   = source->extpars->radProfile->binSB->data.F32[j];
-		radFlux->data.F32[j] = source->extpars->radProfile->binSum->data.F32[j];
-		radFill->data.F32[j] = source->extpars->radProfile->binFill->data.F32[j];
-	    }
-
-	empty_annuli:
-	    psMetadataAdd (row, PS_LIST_TAIL, "PROF_SB", PS_DATA_VECTOR, "mean surface brightness annuli", radSB);
-	    psMetadataAdd (row, PS_LIST_TAIL, "PROF_FLUX", PS_DATA_VECTOR, "flux within annuli", radFlux);
-	    psMetadataAdd (row, PS_LIST_TAIL, "PROF_FILL", PS_DATA_VECTOR, "fill factor of annuli", radFill);
-	    psFree (radSB);
-	    psFree (radFlux);
-	    psFree (radFill);
-	}
-	if (nRow < 0) {
-	    nRow = row->list->n;
-	} else {
-	    psAssert (nRow == row->list->n, "inconsistent row lengths");
-	}
-	psArrayAdd (table, 100, row);
-	psFree (row);
-    }
-    
-    if (table->n == 0) {
-	if (!psFitsWriteBlank (fits, outhead, extname)) {
-	    psError(psErrorCodeLast(), false, "Unable to write empty sources file.");
-	    psFree(outhead);
-	    psFree(table);
-	    return false;
-	}
-	psFree (outhead);
-	psFree (table);
-	return true;
-    }
-    
-    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
-    if (!psFitsWriteTable (fits, outhead, table, extname)) {
-	psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
-	psFree (outhead);
-    psFree(table);
-    return false;
-    }
-    psFree (outhead);
-    psFree (table);
-    
-    return true;
-}
-
-// XXX this layout is still the same as PS1_DEV_1
-bool pmSourcesWrite_CMF_PS1_V3_XFIT (psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname)
-{
-
-    psArray *table;
-    psMetadata *row;
-    psF32 *PAR, *dPAR;
-    psEllipseAxes axes;
-    psF32 xPos, yPos;
-    psF32 xErr, yErr;
-    char name[64];
-
-    // create a header to hold the output data
-    psMetadata *outhead = psMetadataAlloc ();
-
-    // write the links to the image header
-    psMetadataAddStr (outhead, PS_LIST_TAIL, "EXTNAME", PS_META_REPLACE, "xsrc table extension", extname);
-
-    // let's write these out in S/N order
-    sources = psArraySort (sources, pmSourceSortByFlux);
-
-    // we are writing one row per model; we need to write out same number of columns for each row: find the max Nparams
-    int nParamMax = 0;
-    for (int i = 0; i < sources->n; i++) {
-        pmSource *source = sources->data[i];
-        if (source->modelFits == NULL) continue;
-        for (int j = 0; j < source->modelFits->n; j++) {
-            pmModel *model = source->modelFits->data[j];
-            assert (model);
-            nParamMax = PS_MAX (nParamMax, model->params->n);
-        }
-    }
-
-    table = psArrayAllocEmpty (sources->n);
-
-    // we write out all sources, regardless of quality.  the source flags tell us the state
-    for (int i = 0; i < sources->n; i++) {
-
-        pmSource *source = sources->data[i];
-
-        // XXX if no model fits are saved, write out modelEXT?
-        if (source->modelFits == NULL) continue;
-
-        // We have multiple sources : need to flag the one used to subtract the light (the 'best' model)
-        for (int j = 0; j < source->modelFits->n; j++) {
-
-            // choose the convolved EXT model, if available, otherwise the simple one
-            pmModel *model = source->modelFits->data[j];
-            assert (model);
-
-	    // skip models which were not actually fitted
-	    if (model->flags & PM_MODEL_STATUS_BADARGS) continue;
-
-            PAR = model->params->data.F32;
-            dPAR = model->dparams->data.F32;
-            xPos = PAR[PM_PAR_XPOS];
-            yPos = PAR[PM_PAR_YPOS];
-            xErr = dPAR[PM_PAR_XPOS];
-            yErr = dPAR[PM_PAR_YPOS];
-
-            axes = pmPSF_ModelToAxes (PAR, 20.0);
-
-            row = psMetadataAlloc ();
-
-            // XXX we are not writing out the mode (flags) or the type (psf, ext, etc)
-            psMetadataAddU32 (row, PS_LIST_TAIL, "IPP_IDET",         0, "IPP detection identifier index",             source->seq);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "X_EXT",            0, "EXT model x coordinate",                     xPos);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "Y_EXT",            0, "EXT model y coordinate",                     yPos);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "X_EXT_SIG",        0, "Sigma in EXT x coordinate",                  xErr);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "Y_EXT_SIG",        0, "Sigma in EXT y coordinate",                  yErr);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_INST_MAG",     0, "EXT fit instrumental magnitude",             model->mag);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_INST_MAG_SIG", 0, "Sigma of PSF instrumental magnitude",        model->magErr);
-
-            psMetadataAddF32 (row, PS_LIST_TAIL, "NPARAMS",          0, "number of model parameters",                 model->params->n);
-            psMetadataAddStr (row, PS_LIST_TAIL, "MODEL_TYPE",       0, "name of model",                              pmModelClassGetName (model->type));
-
-            // XXX these should be major and minor, not 'x' and 'y'
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_WIDTH_MAJ",    0, "EXT width in x coordinate",                  axes.major);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_WIDTH_MIN",    0, "EXT width in y coordinate",                  axes.minor);
-            psMetadataAddF32 (row, PS_LIST_TAIL, "EXT_THETA",        0, "EXT orientation angle",                      axes.theta);
-
-            // write out the other generic parameters
-            for (int k = 0; k < nParamMax; k++) {
-                if (k == PM_PAR_I0) continue;
-                if (k == PM_PAR_SKY) continue;
-                if (k == PM_PAR_XPOS) continue;
-                if (k == PM_PAR_YPOS) continue;
-                if (k == PM_PAR_SXX) continue;
-                if (k == PM_PAR_SXY) continue;
-                if (k == PM_PAR_SYY) continue;
-
-                snprintf (name, 64, "EXT_PAR_%02d", k);
-
-                if (k < model->params->n) {
-                    psMetadataAdd (row, PS_LIST_TAIL, name, PS_DATA_F32, "", model->params->data.F32[k]);
-                } else {
-                    psMetadataAddF32 (row, PS_LIST_TAIL, name, PS_DATA_F32, "", NAN);
-                }
-            }
-
-            // XXX other parameters which may be set.
-            // XXX flag / value to define the model
-            // XXX write out the model type, fit status flags
-
-            psArrayAdd (table, 100, row);
-            psFree (row);
-        }
-    }
-
-    if (table->n == 0) {
-        if (!psFitsWriteBlank (fits, outhead, extname)) {
-            psError(psErrorCodeLast(), false, "Unable to write empty sources file.");
-            psFree(outhead);
-            psFree(table);
-            return false;
-        }
-        psFree (outhead);
-        psFree (table);
-        return true;
-    }
-
-    psTrace ("pmFPAfile", 5, "writing ext data %s\n", extname);
-    if (!psFitsWriteTable (fits, outhead, table, extname)) {
-        psError(psErrorCodeLast(), false, "writing ext data %s\n", extname);
-        psFree (outhead);
-        psFree(table);
-        return false;
-    }
-    psFree (outhead);
-    psFree (table);
-    return true;
-}
-
-bool pmSourcesWrite_CMF_PS1_V3_XRAD(psFits *fits, pmReadout *readout, psArray *sources, psMetadata *imageHeader, char *extname, psMetadata *recipe)
-{
-    return true;
-}
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMP.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMP.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_CMP.c	(revision 33415)
@@ -135,5 +135,5 @@
         lsky = (source->sky < 1.0) ? 0.0 : log10(source->sky);
 
-        axes = pmPSF_ModelToAxes (PAR, 20.0);
+        axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 
         float psfMagErr = isfinite(source->psfMagErr) ? source->psfMagErr : 999;
@@ -293,5 +293,5 @@
                 goto skip_source;
 
-            pmPSF_AxesToModel (PAR, axes);
+            pmPSF_AxesToModel (PAR, axes, modelType);
 
             psArrayAdd (sources, 100, source);
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_OBJ.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_OBJ.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_OBJ.c	(revision 33415)
@@ -91,5 +91,5 @@
         }
 
-        axes = pmPSF_ModelToAxes (PAR, 20.0);
+        axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 
         psLineInit (line);
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_PS1_CAL_0.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_PS1_CAL_0.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_PS1_CAL_0.c	(revision 33415)
@@ -113,5 +113,5 @@
             yErr = dPAR[PM_PAR_YPOS];
 	    if (isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SXX])) {
-		axes = pmPSF_ModelToAxes (PAR, 20.0);
+		axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 	    } else {
 		axes.major = NAN;
@@ -288,5 +288,5 @@
 	dPAR[PM_PAR_I0]   = (isfinite(source->psfMag)) ? PAR[PM_PAR_I0] * source->psfMagErr : NAN;
 
-        pmPSF_AxesToModel (PAR, axes);
+        pmPSF_AxesToModel (PAR, axes, modelType);
 
         float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
@@ -618,5 +618,5 @@
 	    yErr = dPAR[PM_PAR_YPOS];
 
-	    axes = pmPSF_ModelToAxes (PAR, 20.0);
+	    axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 
 	    // generate RA,DEC
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_PS1_DEV_0.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_PS1_DEV_0.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_PS1_DEV_0.c	(revision 33415)
@@ -89,5 +89,5 @@
             yErr = dPAR[PM_PAR_YPOS];
 
-            axes = pmPSF_ModelToAxes (PAR, 20.0);
+            axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
         } else {
             // XXX: This code seg faults if source->peak is NULL.
@@ -214,5 +214,5 @@
         source->psfMagErr    = psMetadataLookupF32 (&status, row, "PSF_INST_MAG_SIG");
 
-        pmPSF_AxesToModel (PAR, axes);
+        pmPSF_AxesToModel (PAR, axes, modelType);
 
         float peakMag = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_PS1_DEV_1.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_PS1_DEV_1.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_PS1_DEV_1.c	(revision 33415)
@@ -95,5 +95,5 @@
             yErr = dPAR[PM_PAR_YPOS];
 	    if (isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SXX])) {
-		axes = pmPSF_ModelToAxes (PAR, 20.0);
+		axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 	    } else {
 		axes.major = NAN;
@@ -257,5 +257,5 @@
 	dPAR[PM_PAR_I0]   = (isfinite(source->psfMag)) ? PAR[PM_PAR_I0] * source->psfMagErr : NAN;
 
-        pmPSF_AxesToModel (PAR, axes);
+        pmPSF_AxesToModel (PAR, axes, modelType);
 
         float peakMag     = psMetadataLookupF32 (&status, row, "PEAK_FLUX_AS_MAG");
@@ -522,5 +522,5 @@
 	    yErr = dPAR[PM_PAR_YPOS];
 
-	    axes = pmPSF_ModelToAxes (PAR, 20.0);
+	    axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 
 	    row = psMetadataAlloc ();
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_SMPDATA.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_SMPDATA.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_SMPDATA.c	(revision 33415)
@@ -92,5 +92,5 @@
 	    lsky = (source->sky < 1.0) ? 0.0 : log10(source->sky);
 
-	    axes = pmPSF_ModelToAxes (PAR, 20.0);
+	    axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 
 	} else {
@@ -190,5 +190,5 @@
         axes.theta       = psMetadataLookupF32 (&status, row, "THETA");
 
-	pmPSF_AxesToModel (PAR, axes);
+	pmPSF_AxesToModel (PAR, axes, modelType);
 
         source->psfMag = psMetadataLookupF32 (&status, row, "MAG_RAW") - ZERO_POINT;
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_SX.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_SX.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceIO_SX.c	(revision 33415)
@@ -81,5 +81,5 @@
         // pmSourceSextractType (source, &type, &flags);
 
-        axes = pmPSF_ModelToAxes (PAR, 20.0);
+        axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 
         psLineInit (line);
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceMasks.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceMasks.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceMasks.h	(revision 33415)
@@ -46,4 +46,5 @@
     PM_SOURCE_MODE2_DIFF_WITH_DOUBLE = 0x00000002, ///< diff source matched to positive detections in both images
     PM_SOURCE_MODE2_MATCHED          = 0x00000004, ///< diff source matched to positive detections in both images
+    PM_SOURCE_MODE2_DIFF_SELF_MATCH  = 0x00000800, ///< positive detection match is probably this source 
 
     PM_SOURCE_MODE2_ON_SPIKE         = 0x00000008, ///< > 25% of (PSF-weighted) pixels land on diffraction spike
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceMoments.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceMoments.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceMoments.c	(revision 33415)
@@ -96,11 +96,18 @@
     // (int) so they can be used in the image index below.
 
-    // do 2 passes : the first pass should use a somewhat smaller radius and no sigma window to 
-    // get an unbiased (but probably noisy) centroid
-    if (!pmSourceMomentsGetCentroid (source, 0.75*radius, 0.0, minSN, maskVal, source->peak->xf, source->peak->yf)) {
-	return false;
-    }
-    // second pass applies the Gaussian window and uses the centroid from the first pass
-    if (!pmSourceMomentsGetCentroid (source, radius, sigma, minSN, maskVal, source->moments->Mx, source->moments->My)) {
+    // XXX // do 2 passes : the first pass should use a somewhat smaller radius and no sigma window to 
+    // XXX // get an unbiased (but probably noisy) centroid
+    // XXX if (!pmSourceMomentsGetCentroid (source, 0.75*radius, 0.0, minSN, maskVal, source->peak->xf, source->peak->yf)) {
+    // XXX 	return false;
+    // XXX }
+    // XXX // second pass applies the Gaussian window and uses the centroid from the first pass
+    // XXX if (!pmSourceMomentsGetCentroid (source, radius, sigma, minSN, maskVal, source->moments->Mx, source->moments->My)) {
+    // XXX 	return false;
+    // XXX }
+
+    // If we use a large radius for the centroid, it will be biased by any neighbors.  The flux
+    // of any object drops pretty quickly outside 1-2 sigmas.  (The exception is bright
+    // saturated stars, for which we need to use a very large radius here)
+    if (!pmSourceMomentsGetCentroid (source, 1.5*sigma, 0.0, minSN, maskVal, source->peak->xf, source->peak->yf)) {
 	return false;
     }
@@ -108,11 +115,4 @@
     // Now calculate higher-order moments, using the above-calculated first moments to adjust coordinates
     // Xn  = SUM (x - xc)^n * (z - sky)
-
-    float RFW = 0.0;
-    float RHW = 0.0;
-
-    float RF = 0.0;
-    float RH = 0.0;
-    float RS = 0.0;
     float XX = 0.0;
     float XY = 0.0;
@@ -143,4 +143,5 @@
     float yCM = Yo - 0.5 - source->pixels->row0; // coord of peak in subimage
 
+    // calculate the higher-order moments using Xo,Yo
     for (psS32 row = 0; row < source->pixels->numRows ; row++) {
 
@@ -194,12 +195,5 @@
 	    Sum += pDiff;
 
-	    // Kron Flux uses the 1st radial moment (NOT Gaussian windowed?)
 	    float r = sqrt(r2);
-	    float rf = r * fDiff;
-	    float rh = sqrt(r) * fDiff;
-	    float rs = fDiff;
-
-	    float rfw = r * pDiff;
-	    float rhw = sqrt(r) * pDiff;
 
 	    float x = xDiff * pDiff;
@@ -221,11 +215,4 @@
 	    float yyyy = yDiff * yyy / r2;
 
-	    RF  += rf;
-	    RH  += rh;
-	    RS  += rs;
-
-	    RFW  += rfw;
-	    RHW  += rhw;
-
 	    XX  += xx;
 	    XY  += xy;
@@ -242,10 +229,22 @@
 	    XYYY  += xyyy;
 	    YYYY  += yyyy;
+
+	    // Kron Flux uses the 1st radial moment (NOT Gaussian windowed?)
+	    // XXX float r = sqrt(r2);
+	    // XXX float rf = r * fDiff;
+	    // XXX float rh = sqrt(r) * fDiff;
+	    // XXX float rs = fDiff;
+	    // XXX 
+	    // XXX float rfw = r * pDiff;
+	    // XXX float rhw = sqrt(r) * pDiff;
+	    // XXX 
+	    // XXX RF  += rf;
+	    // XXX RH  += rh;
+	    // XXX RS  += rs;
+	    // XXX 
+	    // XXX RFW  += rfw;
+	    // XXX RHW  += rhw;
 	}
     }
-
-    source->moments->Mrf = RF/RS;
-    source->moments->Mrh = RH/RS;
-
     source->moments->Mxx = XX/Sum;
     source->moments->Mxy = XY/Sum;
@@ -262,4 +261,64 @@
     source->moments->Mxyyy = XYYY/Sum;
     source->moments->Myyyy = YYYY/Sum;
+
+    // *** now calculate the 1st radial moment (for kron flux) -- symmetrical averaging
+
+    float **vPix = source->pixels->data.F32;
+    float **vWgt = source->variance->data.F32;
+    psImageMaskType  **vMsk = (source->maskObj == NULL) ? NULL : source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA;
+
+    float RF = 0.0;
+    float RH = 0.0;
+    float RS = 0.0;
+
+    for (psS32 row = 0; row < source->pixels->numRows ; row++) {
+
+	float yDiff = row - yCM;
+	if (fabs(yDiff) > radius) continue;
+
+	// coordinate of mirror pixel
+	int yFlip = yCM - yDiff;
+	if (yFlip < 0) continue;
+	if (yFlip >= source->pixels->numRows) continue;
+
+	for (psS32 col = 0; col < source->pixels->numCols ; col++) {
+	    // check mask and value for this pixel
+	    if (vMsk && (vMsk[row][col] & maskVal)) continue;
+	    if (isnan(vPix[row][col])) continue;
+
+	    float xDiff = col - xCM;
+	    if (fabs(xDiff) > radius) continue;
+
+	    // coordinate of mirror pixel
+	    int xFlip = xCM - xDiff;
+	    if (xFlip < 0) continue;
+	    if (xFlip >= source->pixels->numCols) continue;
+
+	    // check mask and value for mirror pixel
+	    if (vMsk && (vMsk[yFlip][xFlip] & maskVal)) continue;
+	    if (isnan(vPix[yFlip][xFlip])) continue;
+
+	    // radius is just a function of (xDiff, yDiff)
+	    float r2  = PS_SQR(xDiff) + PS_SQR(yDiff);
+	    if (r2 > R2) continue;
+
+	    float fDiff1 = vPix[row][col] - sky;
+	    float fDiff2 = vPix[yFlip][xFlip] - sky;
+	    float pDiff = (fDiff1 > 0.0) ? sqrt(fabs(fDiff1*fDiff2)) : -sqrt(fabs(fDiff1*fDiff2));
+
+	    // Kron Flux uses the 1st radial moment (NOT Gaussian windowed?)
+	    float r = sqrt(r2);
+	    float rf = r * pDiff;
+	    float rh = sqrt(r) * pDiff;
+	    float rs = 0.5 * (fDiff1 + fDiff2);
+
+	    RF  += rf;
+	    RH  += rh;
+	    RS  += rs;
+	}
+    }
+
+    source->moments->Mrf = RF/RS;
+    source->moments->Mrh = RH/RS;
 
     // if Mrf (first radial moment) is very small, we are getting into low-significance
@@ -270,4 +329,6 @@
 	kronRefRadius = MIN(radius, kronRefRadius);
     }
+
+    // *** now calculate the kron flux values using the 1st radial moment
 
     float radKinner = 1.0*kronRefRadius;
@@ -283,14 +344,130 @@
     float SumOuter = 0.0;
 
+    // calculate the Kron flux, and related fluxes (NO symmetrical averaging)
     for (psS32 row = 0; row < source->pixels->numRows ; row++) {
-
+	
 	float yDiff = row - yCM;
 	if (fabs(yDiff) > radKouter) continue;
+	
+	for (psS32 col = 0; col < source->pixels->numCols ; col++) {
+	    // check mask and value for this pixel
+	    if (vMsk && (vMsk[row][col] & maskVal)) continue;
+	    if (isnan(vPix[row][col])) continue;
+	    
+	    float xDiff = col - xCM;
+	    if (fabs(xDiff) > radKouter) continue;
+	    
+	    // radKron is just a function of (xDiff, yDiff)
+	    float r2  = PS_SQR(xDiff) + PS_SQR(yDiff);
+
+	    float fDiff1 = vPix[row][col] - sky;
+	    float pDiff = fDiff1;
+	    float wDiff = vWgt[row][col];
+				    
+	    // skip pixels below specified significance level.  this is allowed, but should be
+	    // avoided -- the over-weights the wings of bright stars compared to those of faint
+	    // stars.
+	    if (PS_SQR(pDiff) < minSN2*wDiff) continue;
+	    
+	    float r  = sqrt(r2);
+	    if (r < radKron) {
+		Sum += pDiff;
+		Var += wDiff;
+		nKronPix ++;
+		// if (beVerbose) fprintf (stderr, "mome: %d %d  %f  %f  %f\n", col, row, sky, *vPix, Sum);
+	    }
+
+	    // use sigma (fixed by psf) not a radKron based value
+	    if (r < sigma) {
+		SumCore += pDiff;
+		VarCore += wDiff;
+		nCorePix ++;
+	    }
+
+	    if ((r > radKinner) && (r < radKron)) {
+		SumInner += pDiff;
+		nInner ++;
+	    }
+	    if ((r > radKron)  && (r < radKouter)) {
+		SumOuter += pDiff;
+		nOuter ++;
+	    }
+	}
+    }
+    // *** should I rescale these fluxes by pi R^2 / nNpix?
+    // XXX source->moments->KronCore    = SumCore       * M_PI * PS_SQR(sigma) / nCorePix;
+    // XXX source->moments->KronCoreErr = sqrt(VarCore) * M_PI * PS_SQR(sigma) / nCorePix;
+    // XXX source->moments->KronFlux    = Sum       * M_PI * PS_SQR(radKron) / nKronPix;
+    // XXX source->moments->KronFluxErr = sqrt(Var) * M_PI * PS_SQR(radKron) / nKronPix;
+    // XXX source->moments->KronFinner = SumInner * M_PI * (PS_SQR(radKron)   - PS_SQR(radKinner)) / nInner;
+    // XXX source->moments->KronFouter = SumOuter * M_PI * (PS_SQR(radKouter) -   PS_SQR(radKron)) / nOuter;
+
+    source->moments->KronCore    = SumCore;
+    source->moments->KronCoreErr = sqrt(VarCore);
+    source->moments->KronFlux    = Sum;
+    source->moments->KronFluxErr = sqrt(Var);
+    source->moments->KronFinner = SumInner;
+    source->moments->KronFouter = SumOuter;
+
+    // XXX not sure I should save this here...
+    source->moments->KronFluxPSF    = source->moments->KronFlux;
+    source->moments->KronFluxPSFErr = source->moments->KronFluxErr;
+    source->moments->KronRadiusPSF  = source->moments->Mrf;
+
+    psTrace ("psModules.objects", 4, "Mrf: %f  KronFlux: %f  Mxx: %f  Mxy: %f  Myy: %f  Mxxx: %f  Mxxy: %f  Mxyy: %f  Myyy: %f  Mxxxx: %f  Mxxxy: %f  Mxxyy: %f  Mxyyy: %f  Mxyyy: %f\n",
+	     source->moments->Mrf,   source->moments->KronFlux, 
+	     source->moments->Mxx,   source->moments->Mxy,   source->moments->Myy,
+	     source->moments->Mxxx,  source->moments->Mxxy,  source->moments->Mxyy,  source->moments->Myyy,
+	     source->moments->Mxxxx, source->moments->Mxxxy, source->moments->Mxxyy, source->moments->Mxyyy, source->moments->Myyyy);
+
+    psTrace ("psModules.objects", 3, "peak %f %f (%f = %f) Mx: %f  My: %f  Sum: %f  Mxx: %f  Mxy: %f  Myy: %f  sky: %f  Npix: %d\n",
+	     source->peak->xf, source->peak->yf, source->peak->rawFlux, sqrt(source->peak->detValue), source->moments->Mx,   source->moments->My, Sum, source->moments->Mxx,   source->moments->Mxy,   source->moments->Myy, sky, source->moments->nPixels);
+
+    return(true);
+}
+
+bool pmSourceMomentsGetCentroid(pmSource *source, float radius, float sigma, float minSN, psImageMaskType maskVal, float xGuess, float yGuess) { 
+
+    // First Pass: calculate the first moments (these are subtracted from the coordinates below)
+    // Sum = SUM (z - sky)
+    // X1  = SUM (x - xc)*(z - sky)
+    // .. etc
+
+    float sky = 0.0;
+
+    float peakPixel = -PS_MAX_F32;
+    psS32 numPixels = 0;
+    float Sum = 0.0;
+    float Var = 0.0;
+    float X1 = 0.0;
+    float Y1 = 0.0;
+    float R2 = PS_SQR(radius);
+    float minSN2 = PS_SQR(minSN);
+    float rsigma2 = 0.5 / PS_SQR(sigma);
+
+    float xPeak = xGuess - source->pixels->col0; // coord of peak in subimage
+    float yPeak = yGuess - source->pixels->row0; // coord of peak in subimage
+
+    // we are guaranteed to have a valid pixel and variance at this location (right? right?)
+    // float weightNorm = source->pixels->data.F32[yPeak][xPeak] / sqrt (source->variance->data.F32[yPeak][xPeak]);
+    // psAssert (isfinite(source->pixels->data.F32[yPeak][xPeak]), "peak must be on valid pixel");
+    // psAssert (isfinite(source->variance->data.F32[yPeak][xPeak]), "peak must be on valid pixel");
+    // psAssert (source->variance->data.F32[yPeak][xPeak] > 0, "peak must be on valid pixel");
+
+    // the moments [Sum(x*f) / Sum(f)] are calculated in pixel index values, and should
+    // not depend on the fractional pixel location of the source.  However, the aperture
+    // (radius) and the Gaussian window (sigma) depend subtly on the fractional pixel
+    // position of the expected centroid
+
+    for (psS32 row = 0; row < source->pixels->numRows ; row++) {
+
+	float yDiff = row + 0.5 - yPeak;
+	if (fabs(yDiff) > radius) continue;
 
 	float *vPix = source->pixels->data.F32[row];
 	float *vWgt = source->variance->data.F32[row];
 
-	psImageMaskType  *vMsk = (source->maskObj == NULL) ? NULL : source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[row];
-	// psImageMaskType  *vMsk = (source->maskView == NULL) ? NULL : source->maskView->data.PS_TYPE_IMAGE_MASK_DATA[row];
+	psImageMaskType *vMsk = (source->maskObj == NULL) ? NULL : source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[row];
+	// psImageMaskType *vMsk = (source->maskView == NULL) ? NULL : source->maskView->data.PS_TYPE_IMAGE_MASK_DATA[row];
 
 	for (psS32 col = 0; col < source->pixels->numCols ; col++, vPix++, vWgt++) {
@@ -304,117 +481,4 @@
 	    if (isnan(*vPix)) continue;
 
-	    float xDiff = col - xCM;
-	    if (fabs(xDiff) > radKouter) continue;
-
-	    // radKron is just a function of (xDiff, yDiff)
-	    float r2  = PS_SQR(xDiff) + PS_SQR(yDiff);
-
-	    float pDiff = *vPix - sky;
-	    float wDiff = *vWgt;
-
-	    // skip pixels below specified significance level.  this is allowed, but should be
-	    // avoided -- the over-weights the wings of bright stars compared to those of faint
-	    // stars.
-	    if (PS_SQR(pDiff) < minSN2*wDiff) continue;
-
-	    float r  = sqrt(r2);
-	    if (r < radKron) {
-		Sum += pDiff;
-		Var += wDiff;
-		nKronPix ++;
-		// if (beVerbose) fprintf (stderr, "mome: %d %d  %f  %f  %f\n", col, row, sky, *vPix, Sum);
-	    }
-
-	    // use sigma (fixed by psf) not a radKron based value
-	    if (r < sigma) {
-		SumCore += pDiff;
-		VarCore += wDiff;
-		nCorePix ++;
-	    }
-
-	    if ((r > radKinner) && (r < radKron)) {
-		SumInner += pDiff;
-		nInner ++;
-	    }
-	    if ((r > radKron)  && (r < radKouter)) {
-		SumOuter += pDiff;
-		nOuter ++;
-	    }
-	}
-    }
-    // *** should I rescale these fluxes by pi R^2 / nNpix?
-    source->moments->KronCore    = SumCore       * M_PI * PS_SQR(sigma) / nCorePix;
-    source->moments->KronCoreErr = sqrt(VarCore) * M_PI * PS_SQR(sigma) / nCorePix;
-    source->moments->KronFlux    = Sum       * M_PI * PS_SQR(radKron) / nKronPix;
-    source->moments->KronFluxErr = sqrt(Var) * M_PI * PS_SQR(radKron) / nKronPix;
-    source->moments->KronFinner = SumInner * M_PI * (PS_SQR(radKron)   - PS_SQR(radKinner)) / nInner;
-    source->moments->KronFouter = SumOuter * M_PI * (PS_SQR(radKouter) -   PS_SQR(radKron)) / nOuter;
-
-    psTrace ("psModules.objects", 4, "Mrf: %f  KronFlux: %f  Mxx: %f  Mxy: %f  Myy: %f  Mxxx: %f  Mxxy: %f  Mxyy: %f  Myyy: %f  Mxxxx: %f  Mxxxy: %f  Mxxyy: %f  Mxyyy: %f  Mxyyy: %f\n",
-	     source->moments->Mrf,   source->moments->KronFlux, 
-	     source->moments->Mxx,   source->moments->Mxy,   source->moments->Myy,
-	     source->moments->Mxxx,  source->moments->Mxxy,  source->moments->Mxyy,  source->moments->Myyy,
-	     source->moments->Mxxxx, source->moments->Mxxxy, source->moments->Mxxyy, source->moments->Mxyyy, source->moments->Myyyy);
-
-    psTrace ("psModules.objects", 3, "peak %f %f (%f = %f) Mx: %f  My: %f  Sum: %f  Mxx: %f  Mxy: %f  Myy: %f  sky: %f  Npix: %d\n",
-	     source->peak->xf, source->peak->yf, source->peak->rawFlux, sqrt(source->peak->detValue), source->moments->Mx,   source->moments->My, Sum, source->moments->Mxx,   source->moments->Mxy,   source->moments->Myy, sky, source->moments->nPixels);
-
-    return(true);
-}
-
-bool pmSourceMomentsGetCentroid(pmSource *source, float radius, float sigma, float minSN, psImageMaskType maskVal, float xGuess, float yGuess) { 
-
-    // First Pass: calculate the first moments (these are subtracted from the coordinates below)
-    // Sum = SUM (z - sky)
-    // X1  = SUM (x - xc)*(z - sky)
-    // .. etc
-
-    float sky = 0.0;
-
-    float peakPixel = -PS_MAX_F32;
-    psS32 numPixels = 0;
-    float Sum = 0.0;
-    float Var = 0.0;
-    float X1 = 0.0;
-    float Y1 = 0.0;
-    float R2 = PS_SQR(radius);
-    float minSN2 = PS_SQR(minSN);
-    float rsigma2 = 0.5 / PS_SQR(sigma);
-
-    float xPeak = xGuess - source->pixels->col0; // coord of peak in subimage
-    float yPeak = yGuess - source->pixels->row0; // coord of peak in subimage
-
-    // we are guaranteed to have a valid pixel and variance at this location (right? right?)
-    // float weightNorm = source->pixels->data.F32[yPeak][xPeak] / sqrt (source->variance->data.F32[yPeak][xPeak]);
-    // psAssert (isfinite(source->pixels->data.F32[yPeak][xPeak]), "peak must be on valid pixel");
-    // psAssert (isfinite(source->variance->data.F32[yPeak][xPeak]), "peak must be on valid pixel");
-    // psAssert (source->variance->data.F32[yPeak][xPeak] > 0, "peak must be on valid pixel");
-
-    // the moments [Sum(x*f) / Sum(f)] are calculated in pixel index values, and should
-    // not depend on the fractional pixel location of the source.  However, the aperture
-    // (radius) and the Gaussian window (sigma) depend subtly on the fractional pixel
-    // position of the expected centroid
-
-    for (psS32 row = 0; row < source->pixels->numRows ; row++) {
-
-	float yDiff = row + 0.5 - yPeak;
-	if (fabs(yDiff) > radius) continue;
-
-	float *vPix = source->pixels->data.F32[row];
-	float *vWgt = source->variance->data.F32[row];
-
-	psImageMaskType *vMsk = (source->maskObj == NULL) ? NULL : source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[row];
-	// psImageMaskType *vMsk = (source->maskView == NULL) ? NULL : source->maskView->data.PS_TYPE_IMAGE_MASK_DATA[row];
-
-	for (psS32 col = 0; col < source->pixels->numCols ; col++, vPix++, vWgt++) {
-	    if (vMsk) {
-		if (*vMsk & maskVal) {
-		    vMsk++;
-		    continue;
-		}
-		vMsk++;
-	    }
-	    if (isnan(*vPix)) continue;
-
 	    float xDiff = col + 0.5 - xPeak;
 	    if (fabs(xDiff) > radius) continue;
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceOutputs.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceOutputs.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceOutputs.c	(revision 33415)
@@ -107,5 +107,5 @@
 	}
 	if (isfinite(PAR[PM_PAR_SXX]) && isfinite(PAR[PM_PAR_SXY]) && isfinite(PAR[PM_PAR_SYY])) {
-	    axes = pmPSF_ModelToAxes (PAR, 20.0);
+	    axes = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 	    outputs->psfMajor = axes.major;
 	    outputs->psfMinor = axes.minor;
@@ -178,5 +178,67 @@
     moments->KronCore    = source->moments ? source->moments->KronCore : NAN;
     moments->KronCoreErr = source->moments ? source->moments->KronCoreErr : NAN;
-
-    return true;
-}
+    moments->KronPSF    = source->moments ? source->moments->KronFluxPSF : NAN;
+    moments->KronPSFErr = source->moments ? source->moments->KronFluxPSFErr : NAN;
+
+    return true;
+}
+
+bool pmSourceLocalAstrometry (psSphere *ptSky, float *posAngle, float *pltScale, pmChip *chip, float xPos, float yPos) {
+
+    pmFPA *fpa = chip->parent;
+
+    if (!chip->toFPA) goto escape;
+    if (!fpa->toTPA) goto escape;
+    if (!fpa->toSky) goto escape;
+
+    // generate RA,DEC
+    psPlane ptCH, ptFP, ptTP_o, ptTP_x, ptTP_y;
+
+    // calculate the astrometry for the coordinate of interest
+    ptCH.x = xPos;
+    ptCH.y = yPos;
+    psPlaneTransformApply (&ptFP, chip->toFPA, &ptCH);
+    psPlaneTransformApply (&ptTP_o, fpa->toTPA, &ptFP);
+    psDeproject (ptSky, &ptTP_o, fpa->toSky);
+
+    // calculate the astrometry for the coordinate + 1pix in X
+    ptCH.x = xPos + 1.0;
+    ptCH.y = yPos;
+    psPlaneTransformApply (&ptFP, chip->toFPA, &ptCH);
+    psPlaneTransformApply (&ptTP_x, fpa->toTPA, &ptFP);
+
+    // calculate the astrometry for the coordinate + 1pix in Y
+    ptCH.x = xPos;
+    ptCH.y = yPos + 1.0;
+    psPlaneTransformApply (&ptFP, chip->toFPA, &ptCH);
+    psPlaneTransformApply (&ptTP_y, fpa->toTPA, &ptFP);
+
+    // the resulting Tangent Plane coordinates are in TP pixels; convert to local Tangent Plane
+    // degrees
+
+    float dTPx_dCHx = fpa->toSky->Xs * (ptTP_x.x - ptTP_o.x);
+    float dTPy_dCHx = fpa->toSky->Ys * (ptTP_x.y - ptTP_o.y);
+
+    float dTPx_dCHy = fpa->toSky->Xs * (ptTP_y.x - ptTP_o.x);
+    float dTPy_dCHy = fpa->toSky->Ys * (ptTP_y.y - ptTP_o.y);
+
+    float pltScale_x = hypot(dTPx_dCHx, dTPy_dCHx);
+    float pltScale_y = hypot(dTPx_dCHy, dTPy_dCHy);
+    *pltScale = 0.5*(pltScale_x + pltScale_y);
+
+    float posAngle_x = atan2 (+dTPy_dCHx, +dTPx_dCHx);
+    float posAngle_y = atan2 (-dTPy_dCHy, +dTPx_dCHy);
+    *posAngle = 0.5*(posAngle_x + posAngle_y);
+
+    return true;
+
+escape:
+    // no astrometry calibration, give up
+    ptSky->r = NAN;
+    ptSky->d = NAN;
+    *posAngle = NAN;
+    *pltScale = NAN;
+
+    return false;
+}
+
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceOutputs.h
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceOutputs.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourceOutputs.h	(revision 33415)
@@ -52,4 +52,6 @@
     float KronCore;
     float KronCoreErr;
+    float KronPSF;
+    float KronPSFErr;
 } pmSourceOutputsMoments;
 
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourcePhotometry.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourcePhotometry.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourcePhotometry.c	(revision 33415)
@@ -56,4 +56,6 @@
 static psImageMaskType maskBurntool = 0;
 static psImageMaskType maskConvPoor = 0;
+static psImageMaskType maskGhost    = 0;
+static psImageMaskType maskGlint    = 0;
 
 bool pmSourceMagnitudesInit (pmConfig *config, psMetadata *recipe)
@@ -68,4 +70,6 @@
 	maskBurntool = pmConfigMaskGet("BURNTOOL", config);
 	maskConvPoor = pmConfigMaskGet("CONV.POOR", config);
+	maskGhost    = pmConfigMaskGet("GHOST", config);
+	maskGlint    = pmConfigMaskGet("GHOST", config);
 	maskSuspect  = maskSpike | maskStarCore | maskBurntool | maskConvPoor;
     }
@@ -94,5 +98,5 @@
 {
     PS_ASSERT_PTR_NON_NULL(source, false);
-    PS_ASSERT_PTR_NON_NULL(psf, false);
+    // PS_ASSERT_PTR_NON_NULL(psf, false);
 
     int status = false;
@@ -533,4 +537,36 @@
     }
 
+    // Check that if the peak is on/off a ghost, glint, or diffraction spike.  In regular IPP
+    // processing, these values are only set in the image mask after the 'camera' stage
+
+    int xChip = source->peak->x;
+    int yChip = source->peak->y;
+
+    // need to access the parent if we are looking at a subimage (likely)
+    psImage *chipImage = (source->pixels == NULL) ? source->pixels : (psImage *) source->pixels->parent;
+
+    bool onChip = true;
+    onChip &= (xChip >= 0);
+    onChip &= (xChip < chipImage->numCols);
+    onChip &= (yChip >= 0);
+    onChip &= (yChip < chipImage->numRows);
+    if (!onChip) {
+	// if the source is off the edge of the chip, raise a different bit?
+	source->mode |= PM_SOURCE_MODE_OFF_CHIP;
+    } else {
+	int xMask = xChip - mask->col0;
+	int yMask = yChip - mask->row0;
+	psImageMaskType maskValue = mask->data.PS_TYPE_IMAGE_MASK_DATA[yMask][xMask];
+	if (maskValue & maskGhost) {
+	    source->mode |= PM_SOURCE_MODE_ON_GHOST;
+	}
+	pmSourceMode PM_SOURCE_MODE_ON_GLINT = PM_SOURCE_MODE_ON_GHOST;
+	if (maskValue & maskGlint) {
+	    source->mode |= PM_SOURCE_MODE_ON_GLINT;
+	}
+	if (maskValue & maskSpike) {
+	    source->mode |= PM_SOURCE_MODE_ON_SPIKE;
+	}
+    }
     return (true);
 }
Index: branches/meh_branches/ppstack_test/psModules/src/objects/pmSourcePlotPSFModel.c
===================================================================
--- branches/meh_branches/ppstack_test/psModules/src/objects/pmSourcePlotPSFModel.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psModules/src/objects/pmSourcePlotPSFModel.c	(revision 33415)
@@ -145,5 +145,5 @@
         // force the axis ratio to be < 20.0
         psEllipseAxes axes_mnt = psEllipseMomentsToAxes (moments, 20.0);
-        psEllipseAxes axes_psf = pmPSF_ModelToAxes (PAR, 20.0);
+        psEllipseAxes axes_psf = pmPSF_ModelToAxes (PAR, 20.0, model->type);
 
         // moments major axis
Index: branches/meh_branches/ppstack_test/psastro/src/psastroDefineFiles.c
===================================================================
--- branches/meh_branches/ppstack_test/psastro/src/psastroDefineFiles.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psastro/src/psastroDefineFiles.c	(revision 33415)
@@ -38,5 +38,5 @@
     bool useModel = psMetadataLookupBool (&status, config->arguments, "PSASTRO.USE.MODEL");
     if (!status) {
-	fixChips = psMetadataLookupBool (&status, recipe, "PSASTRO.USE.MODEL");
+	useModel = psMetadataLookupBool (&status, recipe, "PSASTRO.USE.MODEL");
     }
     if (fixChips || useModel) {
Index: branches/meh_branches/ppstack_test/psastro/src/psastroLoadRefstars.c
===================================================================
--- branches/meh_branches/ppstack_test/psastro/src/psastroLoadRefstars.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psastro/src/psastroLoadRefstars.c	(revision 33415)
@@ -33,6 +33,12 @@
 
     float dRA = RAmax - RAmin;
-    RAmin -= dRA * fieldPadding;
-    RAmax += dRA * fieldPadding;
+    if (dRA * (1. + 2 * fieldPadding) < 180.) {
+        RAmin -= dRA * fieldPadding;
+        RAmax += dRA * fieldPadding;
+    } else {
+        // if dRA > 180 getstar has problems. Just search the entire range
+        RAmin = 0;
+        RAmax = 360;
+    }
 
     float dDEC = DECmax - DECmin;
Index: branches/meh_branches/ppstack_test/psphot/doc/sourcepixels.txt
===================================================================
--- branches/meh_branches/ppstack_test/psphot/doc/sourcepixels.txt	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/doc/sourcepixels.txt	(revision 33415)
@@ -0,0 +1,78 @@
+
+I was having some bad results because of poor choices for the window
+used for the fit or the subtraction.  In the past, these were the same
+size (or coupled), and as a result I would either use too many pixels
+(slow) or too small of a subtraction window (bad residuals).  But, the
+fit does not need to operate on all of the pixels used for the
+subtraction.  
+
+I would like to subtract extended sources out to (say) 4 kron radii,
+but fit out to (say) 1 or 2 kron radii.
+
+Here is where the window and/or the fit / subtraction apertures are
+set:
+
+* psphotSourceStats : when a source if first detected (new peak), the
+  source is allocated and the window pixels are defined in the initial
+  loop of psphotSourceStatsReadout.  The window is defined based on a
+  fixed radius of SKY_OUTER_RADIUS (a rather outdated name).  There is
+  no circular mask set at this point.
+
+  If the source seems saturated, I generate a new set of pixels using
+  a fixed radius (3 * PSF_MOMENTS_RADIUS = 12 * Sigma = 12 * 1.6 *
+  PSF_Sigma = 19*PSF sigma.  this is a bit arbitrary, but sure seems
+  big enough..)
+
+* psphotGuessModels : this function sets up the initial PSF-based
+  guess, and in the process sets the fit radius and window based on
+  either the value 'PSF_FIT_RADIUS' or on the radius at which the
+  model flux is a specified fraction of the sky noise.
+
+  NOTE : this also sets a separate radius for the SAT stars, but it is
+  probably too small (2 x fitRadius)
+
+* psphotFitSourcesLinear : this function sets a masked-based aperture
+  based on the value of model->fitRadius (currently disabled)
+
+* psphotBlendFits : EXT models have the fit radius and subtraction
+  window set by calls to psphotSetRadiusFootprint and
+  psphotSetRadiusModel.
+
+
+-- other notes -- 
+
+pmSourceRedefinePixels has a somewhat silly API: there is no need to
+pass in the readout since the current pixels point to their parent
+images.
+
+---
+
+psphot/src/psphotSourceStats.c:        pmSourceDefinePixels (source, readout, source->peak->x, source->peak->y, OUTER);
+psphot/src/psphotSourceStats.c:        pmSourceDefinePixels (source, readout, source->peak->x, source->peak->y, OUTER);
+psphot/src/psphotSourceStats.c:	    // only slightly: pmSourceRedefinePixels uses the readout to pass the pointers to
+psphot/src/psphotSourceStats.c:	    pmSourceRedefinePixels (source, &tmpReadout, source->peak->x, source->peak->y, BIG_RADIUS + 2);
+
+psphot/src/psphotMergeSources.c:      pmSourceDefinePixels (newSource, readout, newSource->peak->x, newSource->peak->y, OUTER);
+psphot/src/psphotMergeSources.c:	pmSourceRedefinePixels (sourceOut, readoutOut, sourceOut->peak->x, sourceOut->peak->y, sourceOut->modelPSF->fitRadius);
+psphot/src/psphotMergeSources.c:	      pmSourceRedefinePixels (sourceOut, readout, sourceOut->peak->x, sourceOut->peak->y, sourceOut->modelPSF->fitRadius);
+psphot/src/psphotEfficiency.c:                if (!pmSourceDefinePixels(source, readout, x, y, sourceRadius)) {
+psphot/src/psphotSourceMatch.c:	    pmSourceDefinePixels (source, readout, source->peak->x, source->peak->y, OUTER);
+psphot/src/psphotExtendedSourceAnalysisByObject.c:	    pmSourceRedefinePixels (source, readout, source->peak->xf, source->peak->yf, 1.5*radius);
+psphot/src/psphotPetrosianStudy.c:      pmSourceDefinePixels (source, readout, Xo, Yo, 128);
+psphot/src/psphotReplaceUnfit.c:bool psphotRedefinePixels (pmConfig *config, const pmFPAview *view, const char *filerule)
+psphot/src/psphotReplaceUnfit.c:	if (!psphotRedefinePixelsReadout (config, view, filerule, i, recipe)) {
+psphot/src/psphotReplaceUnfit.c:bool psphotRedefinePixelsReadout (pmConfig *config, const pmFPAview *view, const char *filerule, int index, psMetadata *recipe) {
+psphot/src/psphotReplaceUnfit.c:      pmSourceRedefinePixels (source, readout, Xo, Yo, radius);
+psphot/src/psphotRadialAperturesByObject.c:	    // the original view; the following elements get destroyed by pmSourceRedefinePixels so save them:
+psphot/src/psphotRadialAperturesByObject.c:	    pmSourceRedefinePixels (source, readout, source->peak->xf, source->peak->yf, outerRadius + 2);
+psphot/src/psphotRadialAperturesByObject.c:	    pmSourceRedefinePixelsByRegion (source, readout, oldRegion);
+
+psphot/src/psphotRadiusChecks.c:    bool status = pmSourceRedefinePixels (source, readout, PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], model->fitRadius);
+psphot/src/psphotRadiusChecks.c:    bool status = pmSourceRedefinePixels (source, readout, PAR[PM_PAR_XPOS], PAR[PM_PAR_YPOS], model->fitRadius);
+psphot/src/psphotRadiusChecks.c:    pmSourceRedefinePixels (source, readout, peak->xf, peak->yf, rawRadius);
+psphot/src/psphotRadiusChecks.c:    pmSourceRedefinePixels (source, readout, peak->xf, peak->yf, model->fitRadius);
+
+psphot/src/psphotExtendedSourceAnalysis.c:	pmSourceRedefinePixels (source, readout, source->peak->xf, source->peak->yf, 1.5*radius);
+psphot/src/psphotRadialApertures.c:	// the original view; the following elements get destroyed by pmSourceRedefinePixels so save them:
+psphot/src/psphotRadialApertures.c:	pmSourceRedefinePixels (source, readout, source->peak->xf, source->peak->yf, outerRadius + 2);
+psphot/src/psphotRadialApertures.c:	pmSourceRedefinePixelsByRegion (source, readout, oldRegion);
Index: branches/meh_branches/ppstack_test/psphot/src/Makefile.am
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/Makefile.am	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/Makefile.am	(revision 33415)
@@ -25,5 +25,5 @@
 libpsphot_la_LDFLAGS = $(PSPHOT_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
 
-bin_PROGRAMS = psphot psphotForced psphotMakePSF psphotStack
+bin_PROGRAMS = psphot psphotForced psphotMakePSF psphotStack psphotModelTest
 # bin_PROGRAMS = psphotPetrosianStudy psphotTest psphotMomentsStudy 
 
@@ -43,4 +43,8 @@
 psphotStack_LDFLAGS = $(PSPHOT_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
 psphotStack_LDADD = libpsphot.la
+
+psphotModelTest_CFLAGS = $(PSPHOT_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+psphotModelTest_LDFLAGS = $(PSPHOT_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+psphotModelTest_LDADD = libpsphot.la
 
 # psphotMomentsStudy_CFLAGS = $(PSPHOT_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
@@ -102,4 +106,13 @@
 	psphotCleanup.c
 
+# a psphot-variant that simply generates the PSF model
+psphotModelTest_SOURCES = \
+        psphotModelTest.c            \
+	psphotModelTestArguments.c   \
+	psphotParseCamera.c        \
+	psphotImageLoop.c   \
+	psphotMosaicChip.c	   \
+	psphotCleanup.c
+
 # psphotTest_SOURCES = \
 #         psphotTest.c
@@ -117,4 +130,5 @@
 	psphotVisual.c                 \
 	psphotCullPeaks.c	       \
+	psphotFootprintSaddles.c       \
 	psphotVersion.c		       \
 	psphotModelGroupInit.c	       \
@@ -129,4 +143,5 @@
 	psphotMakePSFReadout.c	       \
 	psphotModelBackground.c	       \
+	psphotModelTestReadout.c       \
 	psphotMaskBackground.c	       \
 	psphotSubtractBackground.c     \
@@ -157,4 +172,5 @@
 	psphotExtendedSourceAnalysisByObject.c \
 	psphotExtendedSourceFits.c     \
+	psphotSersicModelClass.c       \
 	psphotKernelFromPSF.c	       \
 	psphotFitSet.c		       \
@@ -168,4 +184,6 @@
 	psphotRadialPlot.c	       \
 	psphotKronMasked.c             \
+	psphotKronIterate.c            \
+	psphotRadialProfileWings.c     \
 	psphotDeblendSatstars.c	       \
 	psphotMosaicSubimage.c	       \
@@ -184,5 +202,4 @@
 	psphotRadialBins.c	       \
 	psphotRadialApertures.c	       \
-	psphotRadialAperturesByObject.c \
 	psphotPetrosian.c	       \
         psphotPetrosianRadialBins.c    \
@@ -190,7 +207,4 @@
         psphotPetrosianVisual.c        \
 	psphotEfficiency.c
-
-# XXX need to fix this for the new apis
-#	psphotModelTest.c	       
 
 # re-instate these
Index: branches/meh_branches/ppstack_test/psphot/src/psphot.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphot.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphot.c	(revision 33415)
@@ -27,5 +27,5 @@
     }
 
-    psLogMsg ("psphot", 3, "complete psphot run: %f sec\n", psTimerMark ("complete"));
+    psLogMsg ("psphot", PS_LOG_WARN, "complete psphot run: %f sec\n", psTimerMark ("complete"));
 
     psErrorCode exit_status = psphotGetExitStatus();
Index: branches/meh_branches/ppstack_test/psphot/src/psphot.h
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphot.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphot.h	(revision 33415)
@@ -19,4 +19,5 @@
     PSPHOT_FORCED,
     PSPHOT_MAKE_PSF,
+    PSPHOT_MODEL_TEST,
 } psphotImageLoopMode;
 
@@ -107,6 +108,6 @@
 bool            psphotBlendFit_Threaded (psThreadJob *job);
 
-bool            psphotReplaceAllSources (pmConfig *config, const pmFPAview *view, const char *filerule);
-bool            psphotReplaceAllSourcesReadout (pmConfig *config, const pmFPAview *view, const char *filerule, int index, psMetadata *recipe);
+bool            psphotReplaceAllSources (pmConfig *config, const pmFPAview *view, const char *filerule, bool ignoreState);
+bool            psphotReplaceAllSourcesReadout (pmConfig *config, const pmFPAview *view, const char *filerule, int index, psMetadata *recipe, bool ignoreState);
 
 bool            psphotAddNoise (pmConfig *config, const pmFPAview *view, const char *filerule);
@@ -117,4 +118,5 @@
 bool            psphotExtendedSourceAnalysis (pmConfig *config, const pmFPAview *view, const char *filerule);
 bool            psphotExtendedSourceAnalysisReadout (pmConfig *config, const pmFPAview *view, const char *filerule, int index, psMetadata *recipe);
+bool            psphotExtendedSourceAnalysis_Threaded (psThreadJob *job);
 
 bool            psphotExtendedSourceFits (pmConfig *config, const pmFPAview *view, const char *filerule);
@@ -174,5 +176,7 @@
 
 // in psphotReplaceUnfit.c:
-bool            psphotRemoveAllSources (const psArray *sources, const psMetadata *recipe);
+bool            psphotRemoveAllSourcesByArray (const psArray *sources, const psMetadata *recipe);
+bool            psphotRemoveAllSources (pmConfig *config, const pmFPAview *view, const char *filerule, bool ignoreState);
+bool            psphotRemoveAllSourcesReadout (pmConfig *config, const pmFPAview *view, const char *filename, int index, psMetadata *recipe, bool ignoreState);
 bool            psphotReplaceUnfitSources (psArray *sources, psImageMaskType maskVal);
 
@@ -327,4 +331,7 @@
 bool psphotMakePSFReadout(pmConfig *config, const pmFPAview *view, const char *filerule);
 
+pmConfig *psphotModelTestArguments(int argc, char **argv);
+bool psphotModelTestReadout(pmConfig *config, const pmFPAview *view, const char *filerule);
+
 int psphotFileruleCount(const pmConfig *config, const char *filerule);
 bool psphotFileruleCountSet(const pmConfig *config, const char *filerule, int num);
@@ -372,13 +379,4 @@
     bool convolve;                      // Convolve images?
     psphotStackConvolveSource convolveSource;
-    // psArray *convImages, *convMasks, *convVariances; // Filenames for the temporary convolved images
-
-    // bool matchZPs;                      // Adjust relative fluxes based on transparency analysis?
-    // bool photometry;                    // Perform photometry?
-    // psMetadata *stats;                  // Statistics for output
-    // FILE *statsFile;                    // File to which to write statistics
-    // psArray *origImages, *origMasks, *origVariances; // Filenames of the original images
-    // psArray *origCovars;                // Original covariances matrices
-    // int quality;                        // Bad data quality flag
 
     // Prepare
@@ -387,14 +385,8 @@
     psVector *inputMask;                // Mask for inputs
 
-    float targetSeeing;                 // Target seeing FWHM
+    psVector *targetSeeing;		// Target seeing FWHMs
     psArray *sourceLists;               // Individual lists of sources for matching
     psVector *norm;                     // Normalisation for each image
     psArray *psfs;
-
-    // psVector *exposures;                // Exposure times
-    // float sumExposure;                  // Sum of exposure times
-    // float zp;                           // Zero point for output
-    // psVector *inputMask;                // Mask for inputs
-    // psArray *sources;                   // Matched sources
 
     // Convolve
@@ -402,16 +394,4 @@
     psArray *regions;                   // PSF-matching regions --- required in the stacking
     psVector *matchChi2;                // chi^2 for stamps from matching
-    psVector *weightings;               // Combination weightings for images (1/noise^2)
-    // psArray *cells;                     // Cells for convolved images --- a handle for reading again
-    // int numCols, numRows;               // Size of image
-    // psArray *convCovars;                // Convolved covariance matrices
-
-    // Combine initial
-    // pmReadout *outRO;                   // Output readout
-    // pmReadout *expRO;                   // Exposure readout
-    // psArray *inspect;                   // Array of arrays of pixels to inspect
-
-    // Rejection
-    // psArray *rejected;                  // Rejected pixels
 } psphotStackOptions;
 
@@ -420,19 +400,23 @@
 bool psphotStackMatchPSFsReadout (pmConfig *config, const pmFPAview *view, psphotStackOptions *options, int index);
 bool psphotStackMatchPSFsPrepare (pmConfig *config, const pmFPAview *view, psphotStackOptions *options, int index);
-bool psphotStackMatchPSFsNext (bool *smoothAgain, pmConfig *config, const pmFPAview *view, const char *filerule, int lastSize);
-bool psphotStackMatchPSFsNextReadout (pmConfig *config, const pmFPAview *view, const char *filerule, int index, psMetadata *recipe, psVector *fwhmValues, int lastSize);
+
+bool psphotStackMatchPSFsNext (pmConfig *config, const pmFPAview *view, const char *filerule, int lastSize);
+bool psphotStackMatchPSFsNextReadout (pmConfig *config, const pmFPAview *view, const char *filerule, int index, int lastSize);
+int psphotStackMatchPSFsEntries (pmConfig *config, const pmFPAview *view, const char *filerule);
 
 // psphotStackMatchPSFsUtils
-psVector *SetOptWidths (bool *optimum, psMetadata *recipe);
-pmReadout *makeFakeReadout(pmConfig *config, pmReadout *raw, psArray *sources, pmPSF *psf, psImageMaskType maskVal, int fullSize);
-bool rescaleData(pmReadout *readout, pmConfig *config, psphotStackOptions *options, int index);
-bool renormKernel(pmReadout *readout, psphotStackOptions *options, int index);
+// psVector *SetOptWidths (bool *optimum, psMetadata *recipe);
+// pmReadout *makeFakeReadout(pmConfig *config, pmReadout *raw, psArray *sources, pmPSF *psf, psImageMaskType maskVal, int fullSize);
+// bool rescaleData(pmReadout *readout, pmConfig *config, psphotStackOptions *options, int index);
+// bool renormKernel(pmReadout *readout, psphotStackOptions *options, int index);
 bool saveMatchData (pmReadout *readout, psphotStackOptions *options, int index);
 bool matchKernel(pmConfig *config, pmReadout *cnv, pmReadout *raw, psphotStackOptions *options, int index);
-bool dumpImageDiff(pmReadout *readoutConv, pmReadout *readoutFake, pmReadout *readoutRef, int index, char *rootname);
-bool dumpImage(pmReadout *readoutOut, pmReadout *readoutRef, int index, char *rootname);
-bool loadKernel (pmConfig *config, pmReadout *readoutCnv, psphotStackOptions *options, int index);
-
-pmPSF *psphotStackPSF(const pmConfig *config, int numCols, int numRows, const psArray *psfs, const psVector *inputMask);
+// bool dumpImageDiff(pmReadout *readoutConv, pmReadout *readoutFake, pmReadout *readoutRef, int index, char *rootname);
+// bool dumpImage(pmReadout *readoutOut, pmReadout *readoutRef, int index, char *rootname);
+// bool loadKernel (pmConfig *config, pmReadout *readoutCnv, psphotStackOptions *options, int index);
+
+bool psphotStackRenormaliseVariance(const pmConfig *config, pmReadout *readout);
+
+bool psphotStackPSF(const pmConfig *config, psphotStackOptions *options);
 
 psphotStackOptions *psphotStackOptionsAlloc (int num);
@@ -443,7 +427,9 @@
 bool psphotCopySourcesReadout (pmConfig *config, const pmFPAview *view, const char *ruleOut, const char *ruleSrc, int index);
 
-bool psphotRadialApertures (pmConfig *config, const pmFPAview *view, const char *filerule);
-bool psphotRadialAperturesReadout (pmConfig *config, const pmFPAview *view, const char *filerule, int index, psMetadata *recipe);
-bool psphotRadialApertureSource (pmSource *source, psMetadata *recipe, psImageMaskType maskVal, const psVector *radMax, int entry);
+bool psphotRadialApertures (pmConfig *config, const pmFPAview *view, const char *filerule, int entry);
+bool psphotRadialAperturesReadout (pmConfig *config, const pmFPAview *view, const char *filerule, int index, psMetadata *recipe, int entry);
+bool psphotRadialApertures_Threaded (psThreadJob *job);
+bool psphotRadialApertureSource (pmSource *source, pmReadout *readout, int entry, psVector *pixRadius2, psVector *pixFlux, psVector *pixVer);
+// bool psphotRadialApertureSource (pmSource *source, int entry);
 
 bool psphotExtendedSourceAnalysisByObject (pmConfig *config, psArray *objects, const pmFPAview *view, const char *filerule);
@@ -469,3 +455,29 @@
 psArray *psphotSourceChildrenByObject (pmConfig *config, const pmFPAview *view, const char *filerule, psArray *objectsSrc);
 
+bool psphotSourceParents (pmConfig *config, const pmFPAview *view, const char *ruleOut, const char *ruleSrc);
+bool psphotSourceParentsReadout (pmConfig *config, const pmFPAview *view, const char *ruleOut, const char *ruleSrc, int index);
+
+bool psphotCopyPeaks (pmConfig *config, const pmFPAview *view, const char *ruleOut, const char *ruleSrc);
+bool psphotCopyPeaksReadout (pmConfig *config, const pmFPAview *view, const char *ruleOut, const char *ruleSrc, int index);
+
+bool psphotSersicModelClassGuessPCM (pmPCMdata *pcm, pmSource *source);
+void psphotSersicModelClassInit ();
+void psphotSersicModelClassCleanup ();
+
+bool psphotSetRadiusMoments (float *fitRadius, float *windowRadius, pmReadout *readout, pmSource *source, psImageMaskType markVal);
+bool psphotSetRadiusMomentsExact (float *fitRadius, float *windowRadius, pmReadout *readout, pmSource *source, psImageMaskType markVal);
+
+bool psphotFootprintSaddles(pmReadout *readout, psArray *footprints);
+bool psphotMaskFootprint (pmReadout *readout, pmSource *source, psImageMaskType markVal);
+
+bool psphotKronIterate (pmConfig *config, const pmFPAview *view, const char *filerule);
+bool psphotKronIterateReadout(pmConfig *config, psMetadata *recipe, const pmFPAview *view, pmReadout *readout, psArray *sources, pmPSF *psf);
+bool psphotKronIterate_Threaded (psThreadJob *job);
+
+bool psphotRadialProfileWings (pmConfig *config, const pmFPAview *view, const char *filerule);
+bool psphotRadialProfileWingsReadout(pmConfig *config, psMetadata *recipe, const pmFPAview *view, pmReadout *readout, psArray *sources);
+bool psphotRadialProfileWings_Threaded (psThreadJob *job);
+
+bool psphotStackObjectsSelectForAnalysis (pmConfig *config, const pmFPAview *view, const char *filerule, psArray *objects);
+
 #endif
Index: branches/meh_branches/ppstack_test/psphot/src/psphotAddNoise.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotAddNoise.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotAddNoise.c	(revision 33415)
@@ -85,7 +85,7 @@
     }
     if (add) {
-        psLogMsg ("psphot.noise", PS_LOG_INFO, "add noise for %ld objects: %f sec\n", sources->n, psTimerMark ("psphot.noise"));
+        psLogMsg ("psphot.noise", PS_LOG_WARN, "add noise for %ld objects: %f sec\n", sources->n, psTimerMark ("psphot.noise"));
     } else {
-        psLogMsg ("psphot.noise", PS_LOG_INFO, "sub noise for %ld objects: %f sec\n", sources->n, psTimerMark ("psphot.noise"));
+        psLogMsg ("psphot.noise", PS_LOG_WARN, "sub noise for %ld objects: %f sec\n", sources->n, psTimerMark ("psphot.noise"));
     }
 
Index: branches/meh_branches/ppstack_test/psphot/src/psphotApResid.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotApResid.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotApResid.c	(revision 33415)
@@ -1,4 +1,4 @@
 # include "psphotInternal.h"
-# define DEBUG
+// # define DEBUG
 
 # define SKIPSTAR(MSG) { psTrace ("psphot", 3, "invalid : %s", MSG); continue; }
@@ -9,4 +9,7 @@
 {
     bool status = true;
+
+    fprintf (stdout, "\n");
+    psLogMsg ("psphot", PS_LOG_INFO, "--- psphot Aperture Residuals ---");
 
     // select the appropriate recipe information
@@ -151,6 +154,7 @@
 
         // wait for the threads to finish and manage results
-        if (!psThreadPoolWait (false)) {
+        if (!psThreadPoolWait (false, true)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
+            psFree(cellGroups);
             return false;
         }
@@ -347,5 +351,5 @@
 
     psLogMsg ("psphot.apresid", PS_LOG_DETAIL, "aperture residual: %f +/- %f\n", psf->ApResid, psf->dApResid);
-    psLogMsg ("psphot.apresid", PS_LOG_INFO, "measure full-frame aperture residuals for %d sources: %f sec\n", Npsf, psTimerMark ("psphot.apresid"));
+    psLogMsg ("psphot.apresid", PS_LOG_WARN, "measure full-frame aperture residuals for %d sources: %f sec\n", Npsf, psTimerMark ("psphot.apresid"));
 
     psFree (xPos);
Index: branches/meh_branches/ppstack_test/psphot/src/psphotBlendFit.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotBlendFit.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotBlendFit.c	(revision 33415)
@@ -5,4 +5,7 @@
 {
     bool status = true;
+
+    fprintf (stdout, "\n");
+    psLogMsg ("psphot", PS_LOG_INFO, "--- psphot Fit Sources (Non-Linear) ---");
 
     // select the appropriate recipe information
@@ -148,5 +151,5 @@
 
         // wait for the threads to finish and manage results
-        if (!psThreadPoolWait (false)) {
+        if (!psThreadPoolWait (false, true)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
 	    psFree (fitOptions);
@@ -186,5 +189,5 @@
     psFree (fitOptions);
 
-    psLogMsg ("psphot.psphotBlendFit", PS_LOG_INFO, "fit models: %f sec for %d objects (%d psf, %d ext, %d failed, %ld skipped)\n", psTimerMark ("psphot.fit.nonlinear"), Nfit, Npsf, Next, Nfail, sources->n - Nfit);
+    psLogMsg ("psphot.psphotBlendFit", PS_LOG_WARN, "fit models: %f sec for %d objects (%d psf, %d ext, %d failed, %ld skipped)\n", psTimerMark ("psphot.fit.nonlinear"), Nfit, Npsf, Next, Nfail, sources->n - Nfit);
 
     psphotVisualShowResidualImage (readout, false);
@@ -233,4 +236,16 @@
         pmSource *source = sources->data[i];
 
+# if (0)
+# define TEST_X 34
+# define TEST_Y 28
+   
+	if ((fabs(source->peak->xf - TEST_X) < 5) && (fabs(source->peak->yf - TEST_Y) < 5)) {
+	    fprintf (stderr, "test object\n");
+	}
+
+# undef TEST_X
+# undef TEST_Y
+# endif
+
         // skip non-astronomical objects (very likely defects)
         if (source->mode &  PM_SOURCE_MODE_BLEND) continue;
@@ -246,6 +261,9 @@
 
         // limit selection to some SN limit
-        if (sqrt(source->peak->detValue) < FIT_SN_LIM) continue;
-
+        if (source->mode & PM_SOURCE_MODE_EXT_LIMIT) {
+	    if (source->moments->KronFlux < FIT_SN_LIM * source->moments->KronFluxErr) continue;
+	} else {
+	    if (sqrt(source->peak->detValue) < FIT_SN_LIM) continue;
+	}
         // exclude sources outside optional analysis region
         if (source->peak->xf < AnalysisRegion.x0) continue;
@@ -267,5 +285,5 @@
         }
 
-        // replace object in image
+        // replace object in image & remove excess noise
         if (source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED) {
             pmSourceAdd (source, PM_MODEL_OP_FULL, maskVal);
@@ -305,5 +323,5 @@
         Nfail ++;
 
-        // re-subtract the object, leave local sky
+        // re-subtract the object, leave local sky, re-bump noise
         pmSourceCacheModel (source, maskVal);
         pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
Index: branches/meh_branches/ppstack_test/psphot/src/psphotChoosePSF.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotChoosePSF.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotChoosePSF.c	(revision 33415)
@@ -5,4 +5,7 @@
 {
     bool status = true;
+
+    fprintf (stdout, "\n");
+    psLogMsg ("psphot", PS_LOG_INFO, "--- psphot Choose PSF ---");
 
     // select the appropriate recipe information
@@ -376,5 +379,5 @@
 
     char *modelName = pmModelClassGetName (psf->type);
-    psLogMsg ("psphot.pspsf", PS_LOG_INFO, "select psf model: %f sec\n", psTimerMark ("psphot.choose.psf"));
+    psLogMsg ("psphot.pspsf", PS_LOG_WARN, "select psf model: %f sec\n", psTimerMark ("psphot.choose.psf"));
     psLogMsg ("psphot.pspsf", PS_LOG_INFO, "psf model %s, ApResid: %f +/- %f\n", modelName, psf->ApResid, psf->dApResid);
 
@@ -443,8 +446,8 @@
             psVectorAppend (fwhmMinor, FWHM_MINOR);
 
-	    if (modelPSF->params->n >= 7) {
+	    if (modelPSF->params->n > 7) {
 	      psVectorAppend (psfExtra1, modelPSF->params->data.F32[7]);
 	    }
-	    if (modelPSF->params->n >= 8) {
+	    if (modelPSF->params->n > 8) {
 	      psVectorAppend (psfExtra2, modelPSF->params->data.F32[8]);
 	    }
@@ -517,5 +520,5 @@
     }
 
-    // psMetadataAddF32 (readout->analysis, PS_LIST_TAIL, "ANGLE",    PS_META_REPLACE, "PSF angle",           axes.theta);
+    psMetadataAddF32 (readout->analysis, PS_LIST_TAIL, "ANGLE",    PS_META_REPLACE, "PSF angle",           axes.theta);
     psMetadataAddS32 (readout->analysis, PS_LIST_TAIL, "NPSFSTAR", PS_META_REPLACE, "Number of stars used to make PSF", psf->nPSFstars);
 
Index: branches/meh_branches/ppstack_test/psphot/src/psphotEfficiency.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotEfficiency.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotEfficiency.c	(revision 33415)
@@ -99,5 +99,5 @@
 
 /// Generate a fake image and add it in to the existing readout
-static bool effGenerate(psImage **xSrc, psImage **ySrc, // Positions of sources
+static pmReadout *effGenerate(psImage **xSrc, psImage **ySrc, // Positions of sources
                         const pmReadout *ro,            // Readout of interest
                         const pmPSF *psf,               // Point-spread function
@@ -152,5 +152,5 @@
         psFree(xAll);
         psFree(yAll);
-        return false;
+        return NULL;
     }
     psFree(magAll);
@@ -161,7 +161,7 @@
 
     psBinaryOp(ro->image, ro->image, "+", fakeRO->image);
-    psFree(fakeRO);
-
-    return true;
+
+    // return the readout so we can subtract it later
+    return fakeRO;
 }
 
@@ -169,4 +169,7 @@
 {
     bool status = true;
+
+    fprintf (stdout, "\n");
+    psLogMsg ("psphot", PS_LOG_INFO, "--- psphot Efficiency ---");
 
     // select the appropriate recipe information
@@ -252,5 +255,5 @@
 
     // remove all sources, adding noise for subtracted sources
-    psphotRemoveAllSources(realSources, recipe);
+    psphotRemoveAllSourcesByArray(realSources, recipe);
 
 #if TESTING
@@ -290,6 +293,6 @@
 
     psImage *xFake = NULL, *yFake = NULL; // Coordinates of sources, each bin in a row
-    if (!effGenerate(&xFake, &yFake, readout, psf, magOffsets,
-                     numSources, magLim, radius, minFlux)) {
+    pmReadout *fakeRO = effGenerate(&xFake, &yFake, readout, psf, magOffsets, numSources, magLim, radius, minFlux);
+    if (!fakeRO) {
         psError(PS_ERR_UNKNOWN, false, "Unable to generate fake sources");
         psFree(xFake);
@@ -416,4 +419,5 @@
     psFree(significance);
 
+    // psphotFitSourcesLinearReadout subtracts the model fits
     if (!psphotFitSourcesLinearReadout(recipe, readout, fakeSourcesAll, psf, true)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to perform linear fit on fake sources.");
@@ -427,4 +431,5 @@
     psf->ApTrend = NULL;
 
+    // measure the magnitudes and fluxes for the sources
     if (!psphotMagnitudesReadout(config, recipe, view, readout, fakeSourcesAll, psf)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to measure magnitudes of fake sources.");
@@ -433,4 +438,14 @@
         psf->ApTrend = apTrend; // Casting away const!
         return false;
+    }
+
+    // replace the subtracted model fits
+    for (int i = 0; i < fakeSourcesAll->n; i++) {
+	pmSource *source = fakeSourcesAll->data[i];
+
+	// replace other sources?
+	if (!(source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED)) continue;
+	
+	pmSourceAdd (source, PM_MODEL_OP_FULL, maskVal);
     }
     psFree(fakeSourcesAll);
@@ -515,4 +530,8 @@
     psFree(fakeSources);
 
+    // subtract the faked sources from the original image
+    psBinaryOp(readout->image, readout->image, "-", fakeRO->image);
+    psFree(fakeRO);
+
     pmDetEff *de = pmDetEffAlloc(magLim, numSources, numBins); // Detection efficiency
     de->magOffsets = psVectorCopy(NULL, magOffsets, PS_TYPE_F32);
@@ -529,5 +548,5 @@
     psFree(de);
 
-    psLogMsg("psphot", PS_LOG_INFO, "Detection efficiency: %lf sec\n", psTimerClear("psphot.fake"));
+    psLogMsg("psphot", PS_LOG_WARN, "Detection efficiency: %lf sec\n", psTimerClear("psphot.fake"));
 
     return true;
Index: branches/meh_branches/ppstack_test/psphot/src/psphotEllipticalContour.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotEllipticalContour.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotEllipticalContour.c	(revision 33415)
@@ -127,6 +127,4 @@
 psF32 psphotEllipticalContourFunc (psVector *deriv, const psVector *params, const psVector *coord) {
 
-    static int pass = 0;
-
     psF32 *par = params->data.F32;
 
@@ -145,8 +143,5 @@
 
     // value is X
-    // if (coord->data.F32[1] == 0) {
-    if (pass == 0) {
-	pass = 1;
-
+    if (coord->data.F32[1] < 0.5) {
 	float value = par[PAR_RMIN]*cs_alpha*r;
 
@@ -161,8 +156,5 @@
 
     // value is Y
-    // if (coord->data.F32[1] == 1) {
-    if (pass == 1) {
-	pass = 0;
-
+    if (coord->data.F32[1] > 0.5) {
 	float value = par[PAR_RMIN]*sn_alpha*r;
 
Index: branches/meh_branches/ppstack_test/psphot/src/psphotExtendedSourceAnalysis.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotExtendedSourceAnalysis.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotExtendedSourceAnalysis.c	(revision 33415)
@@ -2,5 +2,4 @@
 
 // measure the elliptical radial profile and use this to measure the petrosian parameters for the sources
-// XXX this function needs to be threaded
 
 // for now, let's store the detections on the readout->analysis for each readout
@@ -8,4 +7,7 @@
 {
     bool status = true;
+
+    fprintf (stdout, "\n");
+    psLogMsg ("psphot", PS_LOG_INFO, "--- psphot Extended Source Analysis (Petrosians) ---");
 
     // select the appropriate recipe information
@@ -59,7 +61,9 @@
     }
 
-    // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
-    psImageMaskType maskVal = psMetadataLookupImageMask(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels
-    assert (maskVal);
+    // determine the number of allowed threads
+    int nThreads = psMetadataLookupS32(&status, config->arguments, "NTHREADS"); // Number of threads
+    if (!status) {
+        nThreads = 0;
+    }
 
     // get the sky noise from the background analysis; if this is missing, get the user-supplied value
@@ -70,4 +74,120 @@
     }
 
+    // source analysis is done in S/N order (brightest first)
+    sources = psArraySort (sources, pmSourceSortByFlux);
+
+    // option to limit analysis to a specific region
+    char *region = psMetadataLookupStr (&status, recipe, "ANALYSIS_REGION");
+    psRegion *AnalysisRegion = psRegionAlloc(0,0,0,0);
+    *AnalysisRegion = psRegionForImage (readout->image, psRegionFromString (region));
+    if (psRegionIsNaN (*AnalysisRegion)) psAbort("analysis region mis-defined");
+
+    // choose Cx, Cy (see psphotThreadTools.c for overview of the concepts)
+    int Cx = 1, Cy = 1;
+    psphotChooseCellSizes (&Cx, &Cy, readout, nThreads);
+
+    psArray *cellGroups = psphotAssignSources (Cx, Cy, sources);
+
+    for (int i = 0; i < cellGroups->n; i++) {
+
+        psArray *cells = cellGroups->data[i];
+
+        for (int j = 0; j < cells->n; j++) {
+
+            // allocate a job -- if threads are not defined, this just runs the job
+            psThreadJob *job = psThreadJobAlloc ("PSPHOT_EXTENDED_ANALYSIS");
+
+            psArrayAdd(job->args, 1, readout);
+            psArrayAdd(job->args, 1, cells->data[j]); // sources
+            psArrayAdd(job->args, 1, AnalysisRegion);
+            psArrayAdd(job->args, 1, recipe);
+
+            PS_ARRAY_ADD_SCALAR(job->args, skynoise, PS_TYPE_F32);
+
+            PS_ARRAY_ADD_SCALAR(job->args, 0, PS_TYPE_S32); // this is used as a return value for Next
+            PS_ARRAY_ADD_SCALAR(job->args, 0, PS_TYPE_S32); // this is used as a return value for Npetro
+            PS_ARRAY_ADD_SCALAR(job->args, 0, PS_TYPE_S32); // this is used as a return value for Nannuli
+
+// set this to 0 to run without threading
+# if (1)	    
+            if (!psThreadJobAddPending(job)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
+		psFree(AnalysisRegion);
+                return false;
+            } 
+# else
+	    if (!psphotExtendedSourceAnalysis_Threaded(job)) {
+		psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
+		psFree(AnalysisRegion);
+		return false;
+	    }
+	    psScalar *scalar = NULL;
+	    scalar = job->args->data[5];
+	    Next += scalar->data.S32;
+	    scalar = job->args->data[6];
+	    Npetro += scalar->data.S32;
+	    scalar = job->args->data[7];
+	    Nannuli += scalar->data.S32;
+	    psFree(job);
+# endif
+	}
+
+        // wait for the threads to finish and manage results
+        if (!psThreadPoolWait (false, true)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
+	    psFree(AnalysisRegion);
+            return false;
+        }
+
+        // we have only supplied one type of job, so we can assume the types here
+        psThreadJob *job = NULL;
+        while ((job = psThreadJobGetDone()) != NULL) {
+            if (job->args->n < 1) {
+                fprintf (stderr, "error with job\n");
+            } else {
+		psScalar *scalar = NULL;
+		scalar = job->args->data[5];
+		Next += scalar->data.S32;
+		scalar = job->args->data[6];
+		Npetro += scalar->data.S32;
+		scalar = job->args->data[7];
+		Nannuli += scalar->data.S32;
+            }
+            psFree(job);
+	}
+    }
+    psFree (cellGroups);
+    psFree(AnalysisRegion);
+
+    psLogMsg ("psphot", PS_LOG_WARN, "extended source analysis: %f sec for %d objects\n", psTimerMark ("psphot.extended"), Next);
+    psLogMsg ("psphot", PS_LOG_INFO, "  %d petrosian\n", Npetro);
+    psLogMsg ("psphot", PS_LOG_INFO, "  %d annuli\n", Nannuli);
+
+    psphotVisualShowResidualImage (readout, false);
+
+    bool doPetrosian    = psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_PETROSIAN");
+    if (doPetrosian) {
+	psphotVisualShowPetrosians (sources);
+    }
+
+    return true;
+}
+
+bool psphotExtendedSourceAnalysis_Threaded (psThreadJob *job) {
+
+    bool status;
+
+    int Next = 0;
+    int Npetro = 0;
+    int Nannuli = 0;
+
+    // arguments: readout, sources, models, region, psfSize, maskVal, markVal
+    pmReadout *readout      = job->args->data[0];
+    psArray *sources        = job->args->data[1];
+    psRegion *region        = job->args->data[2];
+    psMetadata *recipe      = job->args->data[3];
+
+    float skynoise          = PS_SCALAR_VALUE(job->args->data[4],F32);
+
     // S/N limit to perform full non-linear fits
     float SN_LIM = psMetadataLookupF32 (&status, recipe, "EXTENDED_SOURCE_SN_LIM");
@@ -78,11 +198,7 @@
     bool doPetroStars   = psMetadataLookupBool (&status, recipe, "PETROSIAN_FOR_STARS");
 
-    // source analysis is done in S/N order (brightest first)
-    sources = psArraySort (sources, pmSourceSortByFlux);
-
-    // option to limit analysis to a specific region
-    char *region = psMetadataLookupStr (&status, recipe, "ANALYSIS_REGION");
-    psRegion AnalysisRegion = psRegionForImage (readout->image, psRegionFromString (region));
-    if (psRegionIsNaN (AnalysisRegion)) psAbort("analysis region mis-defined");
+    // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
+    psImageMaskType maskVal = psMetadataLookupImageMask(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels
+    assert (maskVal);
 
     // choose the sources of interest
@@ -90,4 +206,9 @@
 
 	pmSource *source = sources->data[i];
+
+	// if we have checked the source validity on the basis of the object set, then 
+	// we either skip these tests below or we skip the source completely
+	if (source->tmpFlags & PM_SOURCE_TMPF_PETRO_SKIP) continue;
+	if (source->tmpFlags & PM_SOURCE_TMPF_PETRO_KEEP) goto keepSource;
 
 	// skip PSF-like and non-astronomical objects
@@ -104,12 +225,21 @@
 
 	// limit selection to some SN limit
-	assert (source->peak); // how can a source not have a peak?
-	if (sqrt(source->peak->detValue) < SN_LIM) continue;
-
-	// limit selection by analysis region
-	if (source->peak->x < AnalysisRegion.x0) continue;
-	if (source->peak->y < AnalysisRegion.y0) continue;
-	if (source->peak->x > AnalysisRegion.x1) continue;
-	if (source->peak->y > AnalysisRegion.y1) continue;
+	// assert (source->peak); // how can a source not have a peak?
+	// limit selection to some SN limit
+	bool skipSource = false;
+	if (source->mode & PM_SOURCE_MODE_EXT_LIMIT) {
+	    skipSource = (source->moments->KronFlux < SN_LIM * source->moments->KronFluxErr);
+	} else {
+	    skipSource = (sqrt(source->peak->detValue) < SN_LIM);
+	}
+	if (skipSource) continue;
+
+	// limit selection by analysis region (this automatically apply
+	if (source->peak->x < region->x0) continue;
+	if (source->peak->y < region->y0) continue;
+	if (source->peak->x > region->x1) continue;
+	if (source->peak->y > region->y1) continue;
+
+    keepSource:
 
 	// replace object in image
@@ -159,15 +289,15 @@
     }
 
-    psLogMsg ("psphot", PS_LOG_INFO, "extended source analysis: %f sec for %d objects\n", psTimerMark ("psphot.extended"), Next);
-    psLogMsg ("psphot", PS_LOG_INFO, "  %d petrosian\n", Npetro);
-    psLogMsg ("psphot", PS_LOG_INFO, "  %d annuli\n", Nannuli);
-
-    psphotVisualShowResidualImage (readout, false);
-
-    if (doPetrosian) {
-	psphotVisualShowPetrosians (sources);
-    }
-
-    // fprintf (stderr, "xsrc : tried %ld objects\n", sources->n);
+    psScalar *scalar = NULL;
+
+    // change the value of a scalar on the array (wrap this and put it in psArray.h)
+    scalar = job->args->data[5];
+    scalar->data.S32 = Next;
+
+    scalar = job->args->data[6];
+    scalar->data.S32 = Npetro;
+
+    scalar = job->args->data[7];
+    scalar->data.S32 = Nannuli;
 
     return true;
Index: branches/meh_branches/ppstack_test/psphot/src/psphotExtendedSourceFits.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotExtendedSourceFits.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotExtendedSourceFits.c	(revision 33415)
@@ -6,4 +6,7 @@
     bool status = true;
 
+    fprintf (stdout, "\n");
+    psLogMsg ("psphot", PS_LOG_INFO, "--- psphot Extended Source Fits ---");
+
     // select the appropriate recipe information
     psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
@@ -12,5 +15,5 @@
     // perform full extended source non-linear fits?
     if (!psMetadataLookupBool (&status, recipe, "EXTENDED_SOURCE_FITS")) {
-        psLogMsg ("psphot", PS_LOG_INFO, "skipping extended source measurements\n");
+        psLogMsg ("psphot", PS_LOG_INFO, "skipping extended source fits\n");
         return true;
     }
@@ -45,4 +48,6 @@
     int Nfail = 0;
 
+    psphotSersicModelClassInit();
+
     psTimerStart ("psphot.extended");
 
@@ -103,6 +108,6 @@
 
       if (item->type != PS_DATA_METADATA) {
+        // XXX we could cull the bad entries or build a validated model folder
         psAbort ("Invalid type for EXTENDED_SOURCE_MODEL entry %s, not a metadata folder", item->name);
-        // XXX we could cull the bad entries or build a validated model folder
       }
 
@@ -213,5 +218,5 @@
 
         // wait for the threads to finish and manage results
-        if (!psThreadPoolWait (false)) {
+        if (!psThreadPoolWait (false, true)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
 	    psFree(AnalysisRegion);
@@ -247,5 +252,9 @@
     psFree(AnalysisRegion);
 
-    psLogMsg ("psphot", PS_LOG_INFO, "extended source model fits: %f sec for %d objects\n", psTimerMark ("psphot.extended"), Next);
+    psphotSersicModelClassCleanup();
+
+    psphotVisualShowResidualImage (readout, false);
+
+    psLogMsg ("psphot", PS_LOG_WARN, "extended source model fits: %f sec for %d objects\n", psTimerMark ("psphot.extended"), Next);
     psLogMsg ("psphot", PS_LOG_INFO, "  %d convolved models (%d passed)\n", Nconvolve, NconvolvePass);
     psLogMsg ("psphot", PS_LOG_INFO, "  %d plain models (%d passed)\n", Nplain, NplainPass);
@@ -265,8 +274,6 @@
     int Nplain = 0;
     int NplainPass = 0;
-    bool savePics = false;
-    float radius;
+    float fitRadius, windowRadius;
     psScalar *scalar = NULL;
-    pmMoments psfMoments;
 
     // arguments: readout, sources, models, region, psfSize, maskVal, markVal
@@ -275,9 +282,7 @@
     psMetadata *models      = job->args->data[2];
     psRegion *region        = job->args->data[3];
-    int psfSize             = PS_SCALAR_VALUE(job->args->data[4],PS_TYPE_IMAGE_MASK_DATA);
+    int psfSize             = PS_SCALAR_VALUE(job->args->data[4],S32);
     psImageMaskType maskVal = PS_SCALAR_VALUE(job->args->data[5],PS_TYPE_IMAGE_MASK_DATA);
     psImageMaskType markVal = PS_SCALAR_VALUE(job->args->data[6],PS_TYPE_IMAGE_MASK_DATA);
-
-    // pthread_t tid = pthread_self();     // Thread identifier
 
     // Define source fitting parameters for extended source fits
@@ -299,5 +304,5 @@
 
         // skip PSF-like and non-astronomical objects
-        if (source->type == PM_SOURCE_TYPE_STAR) continue;
+        if (!(source->mode & PM_SOURCE_MODE_EXT_LIMIT)) continue;
         if (source->type == PM_SOURCE_TYPE_DEFECT) continue;
         if (source->type == PM_SOURCE_TYPE_SATURATED) continue;
@@ -309,10 +314,4 @@
         if (source->peak->y > region->y1) continue;
 
-        // if model is NULL, we don't have a starting guess
-        // XXX use the parameters guessed from moments
-        // if (source->modelEXT == NULL) continue;
-
-	// fprintf (stderr, "fit %d,%d in thread %d\n", source->peak->x, source->peak->y, (int) tid);
-
         // replace object in image
         if (source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED) {
@@ -321,47 +320,11 @@
         Next ++;
 
-	// set the radius based on the footprint (also sets the mask pixels)
-	if (!psphotSetRadiusFootprint(&radius, readout, source, markVal, 1.0)) {
-	    fprintf (stderr, "skipping (1) %f, %f\n", source->peak->xf, source->peak->yf);
-	    pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
-	    // XXX raise an error of some kind
-	    continue;
-	}
-
-	// XXX note that this changes the source moments that are published...
-	// recalculate the source moments using the larger extended-source moments radius
-	// at this stage, skip Gaussian windowing, and do not clip pixels by S/N
-	// this uses the footprint to judge both radius and aperture?
-	// XXX save the psf-based moments for output
-	psfMoments = *source->moments;
-	if (!pmSourceMoments (source, radius, 0.0, 0.0, 0.0, maskVal)) {
-	    // subtract the best fit from the object, leave local sky
-	    fprintf (stderr, "skipping (2) %f, %f\n", source->peak->xf, source->peak->yf);
-	    *source->moments = psfMoments;
-	    pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
-	    // XXX raise an error flag of some kind
-	    continue;
-	}
-
-        // save the modelFlux here in case we need to subtract it (for failure)
-        psImage *modelFluxStart = psMemIncrRefCounter (source->modelFlux);
-	if (!modelFluxStart) {
-	    pmSourceCacheModel (source, maskVal);
-	    modelFluxStart = psMemIncrRefCounter (source->modelFlux);
-	    if (!modelFluxStart) {
-		fprintf (stderr, "skipping (3) %f, %f\n", source->peak->xf, source->peak->yf);
-		*source->moments = psfMoments;
-		pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
-		// XXX raise an error of some kind?
-		continue;
-	    }
-	}
-	
-        if (savePics) {
-          psphotSaveImage (NULL, readout->image, "image.xp.fits");
-        }
-
-        // array to store the pointers to the model flux images while the models are being fitted
-        psArray *modelFluxes = psArrayAllocEmpty (models->list->n);
+	// set the fit radius based on the first radial moment (also sets the mask pixels)
+	psphotSetRadiusMomentsExact(&fitRadius, &windowRadius, readout, source, markVal); // NOTE : 6 allocs
+
+	// UPDATE : we have changed the moments calculation.  There is now an iteration within 
+	// psphotKronMasked to determine moments appropriate for a larger object.  The values
+	// Mrf, KronFlux, and KronFluxErr are calculated for the iterated radius.  The other
+	// values are left at the psf-based values.
 
         // allocate the array to store the model fits
@@ -380,10 +343,17 @@
 
           // check the SNlim and skip model if source is too faint
-          float SNlim = psMetadataLookupF32 (&status, model, "SNLIM_VALUE");
+          float FIT_SN_LIM = psMetadataLookupF32 (&status, model, "SNLIM_VALUE");
           assert (status);
 
           // limit selection to some SN limit
           // assert (source->peak); // how can a source not have a peak?
-          if (sqrt(source->peak->detValue) < SNlim) {
+	  // limit selection to some SN limit
+	  bool skipSource = false;
+	  if (source->mode & PM_SOURCE_MODE_EXT_LIMIT) {
+	      skipSource = (source->moments->KronFlux < FIT_SN_LIM * source->moments->KronFluxErr);
+	  } else {
+	      skipSource = (sqrt(source->peak->detValue) < FIT_SN_LIM);
+	  }
+          if (skipSource) {
 	      Nfaint ++;
 	      continue;
@@ -398,11 +368,9 @@
           assert (status);
 
-          // XXX psTraceSetLevel ("psLib.math.psMinimizeLMChi2", 6);
-          // XXX psTraceSetLevel ("psphot.psphotModelWithPSF_LMM", 6);
-
           // fit the model as convolved or not
           pmModel *modelFit = NULL;
           if (convolved) {
-              modelFit = psphotFitPCM (readout, source, fitOptions, modelType, maskVal, markVal, psfSize);
+	      // NOTE : 4 more allocs to here
+              modelFit = psphotFitPCM (readout, source, fitOptions, modelType, maskVal, markVal, psfSize); // NOTE : 2313 allocs in here
               if (!modelFit) {
                   psTrace ("psphot", 5, "failed to fit psf-conv model for object at %f, %f", source->moments->Mx, source->moments->My);
@@ -410,5 +378,6 @@
                   continue;
               }
-              psTrace ("psphot", 4, "fit psf-conv model for %f, %f : %s chisq = %f\n", source->moments->Mx, source->moments->My, pmModelClassGetName (modelFit->type), modelFit->chisq);
+              psTrace ("psphot", 4, "fit psf-conv model for %f, %f : %s chisq = %f (npix: %d, niter: %d)\n", 
+		       source->moments->Mx, source->moments->My, pmModelClassGetName (modelFit->type), modelFit->chisq, modelFit->nPix, modelFit->nIter);
               Nconvolve ++;
               if (!(modelFit->flags & (PM_MODEL_STATUS_BADARGS | PM_MODEL_STATUS_NONCONVERGE | PM_MODEL_STATUS_OFFIMAGE))) {
@@ -425,6 +394,6 @@
                   continue;
               }
-              pmSourceCacheModel (source, maskVal);
-              psTrace ("psphot", 4, "fit plain model for %f, %f : %s chisq = %f\n", source->moments->Mx, source->moments->My, pmModelClassGetName (modelFit->type), modelFit->chisq);
+              psTrace ("psphot", 4, "fit plain model for %f, %f : %s chisq = %f (npix: %d, niter: %d)\n", 
+		       source->moments->Mx, source->moments->My, pmModelClassGetName (modelFit->type), modelFit->chisq, modelFit->nPix, modelFit->nIter);
               Nplain ++;
               if (!(modelFit->flags & (PM_MODEL_STATUS_BADARGS | PM_MODEL_STATUS_NONCONVERGE | PM_MODEL_STATUS_OFFIMAGE))) {
@@ -434,9 +403,21 @@
           }
 
-          // save each of the model flux images and store the best
-          psArrayAdd (modelFluxes, 4, source->modelFlux);
+	  // XXX deprecate?
+	  // XXX pmSourceExtFitPars *extFitPars = pmSourceExtFitParsAlloc();
+	  // XXX extFitPars->Mxx = source->moments->Mxx;
+	  // XXX extFitPars->Mxy = source->moments->Mxy;
+	  // XXX extFitPars->Myy = source->moments->Myy;
+	  // XXX extFitPars->Mrf = source->moments->Mrf;
+	  // XXX extFitPars->Mrh = source->moments->Mrh;
+	  // XXX extFitPars->peakMag = (source->peak->rawFlux > 0) ? -2.5*log10(source->peak->rawFlux) : NAN;
+	  
+	  // save kron mag, but assign apMag & psfMag on output (not yet calculated)
+	  // XXX extFitPars->krMag = -2.5*log10(source->moments->KronFlux);
+
+          // psArrayAdd (source->extFitPars, 4, extFitPars);
+	  // psFree (extFitPars);
 
           // test for fit quality / result
-	  modelFit->fitRadius = radius;
+	  modelFit->fitRadius = fitRadius;
           psArrayAdd (source->modelFits, 4, modelFit);
 
@@ -463,27 +444,16 @@
         }
 
+	// clear the circular mask
+	psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(markVal)); 
+
         if (minModel == -1) {
-          // re-subtract the object, leave local sky
+          // no valid extended fit; re-subtract the object, leave local sky
           psTrace ("psphot", 5, "failed to fit extended source model to object at %f, %f", source->moments->Mx, source->moments->My);
 
-          // replace original model, subtract it
-          psFree (source->modelFlux);
-          source->modelFlux = modelFluxStart;
-
-	  *source->moments = psfMoments;
+	  // ensure the modelEXT is cached
+	  pmSourceCacheModel (source, maskVal);
+
           pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
 
-          psFree (modelFluxes);
-
-          if (savePics) {
-              psphotSaveImage (NULL, readout->image, "image.xp.fits");
-              char key[10];
-              fprintf (stdout, "continue? ");
-              if (!fgets (key, 8, stdin)) {
-                  psWarning("Couldn't read anything.");
-              } else if (key[0] == 'n') {
-                  savePics = false;
-              }
-          }
           continue;
         }
@@ -493,35 +463,21 @@
         source->modelEXT = psMemIncrRefCounter (source->modelFits->data[minModel]);
 
-        // save the modelFlux for the best model
-        psFree (source->modelFlux);
-        source->modelFlux = psMemIncrRefCounter (modelFluxes->data[minModel]);
-
-	// replace the original moments
-	*source->moments = psfMoments;
+	// adjust the window so the subtraction covers the faint wings
+	psphotSetRadiusMoments(&fitRadius, &windowRadius, readout, source, markVal);
+
+	// cache the model flux
+	if (source->modelEXT->isPCM) {
+	    pmPCMCacheModel (source, maskVal, psfSize);
+	} else {
+	    pmSourceCacheModel (source, maskVal);
+	}
 
         // subtract the best fit from the object, leave local sky
         pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
 
-        // the initial model flux is no longer needed
-        psFree (modelFluxStart);
-        psFree (modelFluxes);
-
         psTrace ("psphot", 4, "best ext model for %f, %f : %s chisq = %f\n", source->moments->Mx, source->moments->My, pmModelClassGetName (source->modelEXT->type), source->modelEXT->chisq);
         psTrace ("psphot", 5, "extended source model for source at %7.1f, %7.1f", source->moments->Mx, source->moments->My);
-
-        if (savePics) {
-          psphotSaveImage (NULL, readout->image, "image.xm.fits");
-          char key[10];
-          fprintf (stdout, "continue? ");
-          if (!fgets (key, 8, stdin)) {
-              psWarning("Couldn't read anything.");
-          } else if (key[0] == 'n') {
-              savePics = false;
-          }
-        }
     }
     psFree (fitOptions);
-
-    // fprintf (stderr, "xfit : tried %ld objects\n", sources->n);
 
     // change the value of a scalar on the array (wrap this and put it in psArray.h)
Index: branches/meh_branches/ppstack_test/psphot/src/psphotFake.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotFake.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotFake.c	(revision 33415)
@@ -166,5 +166,5 @@
 
     // remove all sources, adding noise for subtracted sources
-    psphotRemoveAllSources(realSources, recipe);
+    psphotRemoveAllSourcesByArray(realSources, recipe);
     psphotAddNoise(readout, realSources, recipe);
 
Index: branches/meh_branches/ppstack_test/psphot/src/psphotFindDetections.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotFindDetections.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotFindDetections.c	(revision 33415)
@@ -7,4 +7,7 @@
 {
     bool status = true;
+
+    fprintf (stdout, "\n");
+    psLogMsg ("psphot", PS_LOG_INFO, "--- psphot Find Detections ---");
 
     // select the appropriate recipe information
Index: branches/meh_branches/ppstack_test/psphot/src/psphotFindFootprints.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotFindFootprints.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotFindFootprints.c	(revision 33415)
@@ -35,7 +35,12 @@
         psArray *tmp = pmFootprintArrayGrow(footprints, growRadius);
         psImageConvolveSetThreads(oldThreads);
-        psLogMsg ("psphot", PS_LOG_MINUTIA, "grow footprint coverage by %d pixels, %ld footprints become %ld footprints: %f sec\n", growRadius, footprints->n, tmp->n, psTimerMark ("psphot.footprints"));
-        psFree(footprints);
-        footprints = tmp;
+        psLogMsg ("psphot", PS_LOG_MINUTIA, "grow footprint coverage by %d pixels, %ld footprints become %ld footprints: %f sec\n", growRadius, footprints->n, tmp ? tmp->n : 0, psTimerMark ("psphot.footprints"));
+        if (tmp) {
+            psFree(footprints);
+            footprints = tmp;
+        } else {
+            psLogMsg ("psphot", PS_LOG_WARN, "pmFootprintArray grow returned NULL\n");
+        }
+            
     }
 
@@ -58,5 +63,16 @@
     psphotCullPeaks(readout, significance, recipe, detections->footprints);
     detections->peaks = pmFootprintArrayToPeaks(detections->footprints);
-    psLogMsg ("psphot", PS_LOG_INFO, "%ld peaks, %ld total footprints: %f sec\n", detections->peaks->n, detections->footprints->n, psTimerMark ("psphot.footprints"));
+
+    // psphotFootprintSaddles (readout, detections->footprints);
+    // 
+    // int nSaddle = 0;
+    // for (int i = 0; i < detections->peaks->n; i++) {
+    // 	pmPeak *peak = detections->peaks->data[i];
+    // 	
+    // 	if (peak->saddlePoints) nSaddle += peak->saddlePoints->n;
+    // }
+    // fprintf (stderr, "%d saddle points for %ld peaks\n", nSaddle, detections->peaks->n);
+
+    psLogMsg ("psphot", PS_LOG_WARN, "%ld peaks, %ld total footprints: %f sec\n", detections->peaks->n, detections->footprints->n, psTimerMark ("psphot.footprints"));
 
     return detections;
Index: branches/meh_branches/ppstack_test/psphot/src/psphotFindPeaks.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotFindPeaks.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotFindPeaks.c	(revision 33415)
@@ -68,5 +68,5 @@
         pmPeaksWriteText (peaks, output);
     }
-    psLogMsg ("psphot", PS_LOG_INFO, "%ld peaks: %f sec\n", peaks->n, psTimerMark ("psphot.peaks"));
+    psLogMsg ("psphot", PS_LOG_WARN, "%ld peaks: %f sec\n", peaks->n, psTimerMark ("psphot.peaks"));
 
     return peaks;
Index: branches/meh_branches/ppstack_test/psphot/src/psphotFitSourcesLinear.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotFitSourcesLinear.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotFitSourcesLinear.c	(revision 33415)
@@ -17,4 +17,7 @@
     bool status = true;
 
+    fprintf (stdout, "\n");
+    psLogMsg ("psphot", PS_LOG_INFO, "--- psphot Fit Source (Linear) ---");
+
     // select the appropriate recipe information
     psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
@@ -23,6 +26,11 @@
     int num = psphotFileruleCount(config, filerule);
 
+    // skip the chisq image (optionally?)
+    int chisqNum = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.CHISQ.NUM");
+    if (!status) chisqNum = -1;
+
     // loop over the available readouts
     for (int i = 0; i < num; i++) {
+        if (i == chisqNum) continue; // skip chisq image
 
         // find the currently selected readout
@@ -126,4 +134,7 @@
     for (int i = 0; i < sources->n; i++) {
         pmSource *source = sources->data[i];
+
+	// psAssert (source->peak, "source without peak??");
+	// psAssert (source->peak->footprint, "peak without footprint??");
 
         // turn this bit off and turn it on again if we pass this test
@@ -190,4 +201,5 @@
         pmModel *model = pmSourceGetModel (&isPSF, source);
 
+	// clear the 'mark' pixels and remask on the fit aperture 
         psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(markVal));
         psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, model->fitRadius, "OR", markVal);
@@ -203,6 +215,4 @@
     }
     psLogMsg ("psphot.ensemble", PS_LOG_MINUTIA, "built fitSources: %f sec (%ld objects)\n", psTimerMark ("psphot.linear"), sources->n);
-
-    // fprintf (stderr, "****** Nsat : %d ********\n", Nsat);
 
     if (fitSources->n == 0) {
@@ -330,9 +340,8 @@
     psLogMsg ("psphot.ensemble", PS_LOG_MINUTIA, "solve matrix: %f sec (%d elements)\n", psTimerMark ("psphot.linear"), sparse->Nelem);
 
-    // XXXX **** philosophical question:
-    // we measure bright objects in three passes: 1) linear fit; 2) non-linear fit; 3) linear fit:
-    // should retain the chisq and errors from the intermediate non-linear fit?
-    // the non-linear fit provides better values for the position errors, and for
-    // extended sources, the shape errors
+    // Philosophical question: we measure bright objects in three passes: 1) linear fit; 2)
+    // non-linear fit; 3) linear fit: should we retain the chisq and errors from the
+    // intermediate non-linear fit?  the non-linear fit provides better values for the position
+    // errors, and for extended sources, the shape errors
 
     // adjust I0 for fitSources and subtract
@@ -349,5 +358,8 @@
         model->dparams->data.F32[PM_PAR_I0] = errors->data.F32[i];
 
-        // subtract object
+	// clear the 'mark' pixels so the subtraction covers the full window
+        psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(markVal));
+
+        // subtract object & add noise
         pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
     }
@@ -374,5 +386,5 @@
     psFree (border);
 
-    psLogMsg ("psphot.ensemble", PS_LOG_INFO, "measure ensemble of PSFs: %f sec\n", psTimerMark ("psphot.linear"));
+    psLogMsg ("psphot.ensemble", PS_LOG_WARN, "measure ensemble of PSFs: %f sec\n", psTimerMark ("psphot.linear"));
 
     psphotVisualPlotChisq (sources);
Index: branches/meh_branches/ppstack_test/psphot/src/psphotFitSourcesLinearStack.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotFitSourcesLinearStack.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotFitSourcesLinearStack.c	(revision 33415)
@@ -64,4 +64,20 @@
           // turn this bit off and turn it on again if we keep this source
           source->mode &= ~PM_SOURCE_MODE_LINEAR_FIT;
+
+	  // skip non-astronomical objects (very likely defects)
+	  if (source->type == PM_SOURCE_TYPE_DEFECT) continue;
+	  if (source->type == PM_SOURCE_TYPE_SATURATED) continue;
+
+	  // do not include CRs in the full ensemble fit
+	  if (source->mode & PM_SOURCE_MODE_CR_LIMIT) continue;
+
+	  // do not include MOMENTS_FAILURES in the fit
+	  if (source->mode & PM_SOURCE_MODE_MOMENTS_FAILURE) continue;
+
+	  if (final) {
+	      if (source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED) continue;
+	  } else {
+	      // if (source->mode & PM_SOURCE_MODE_BLEND) continue;
+	  }
 
           // generate model for sources without, or skip if we can't
@@ -83,5 +99,17 @@
 	  }
 
-          source->mode |= PM_SOURCE_MODE_LINEAR_FIT;
+	  bool isPSF = false;
+	  pmModel *model = pmSourceGetModel (&isPSF, source);
+
+	  // clear the 'mark' pixels and remask on the fit aperture 
+	  psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(markVal));
+	  psImageKeepCircle (source->maskObj, source->peak->x, source->peak->y, model->fitRadius, "OR", markVal);
+
+	  // we call this function multiple times. for the first time, we have only PSF models for all objects
+	  // the second time has extended sources.  If we ever fit the PSF model, we should raise this bit
+	  source->mode |= PM_SOURCE_MODE_LINEAR_FIT;
+	  if (isPSF) {
+	      source->mode |= PM_SOURCE_MODE_PSFMODEL;
+	  }	    
           psArrayAdd (fitSources, 100, source);
         }
@@ -166,4 +194,7 @@
         model->params->data.F32[PM_PAR_I0] = norm->data.F32[i];
         model->dparams->data.F32[PM_PAR_I0] = errors->data.F32[i];
+
+	// clear the 'mark' pixels so the subtraction covers the full window
+        psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(markVal));
 
         // subtract object
@@ -190,5 +221,5 @@
     psFree (errors);
 
-    psLogMsg ("psphot.ensemble", PS_LOG_INFO, "measure ensemble of PSFs: %f sec\n", psTimerMark ("psphot.linear"));
+    psLogMsg ("psphot.ensemble", PS_LOG_WARN, "measure ensemble of PSFs: %f sec\n", psTimerMark ("psphot.linear"));
     return true;
 }
Index: branches/meh_branches/ppstack_test/psphot/src/psphotFootprintSaddles.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotFootprintSaddles.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotFootprintSaddles.c	(revision 33415)
@@ -0,0 +1,129 @@
+# include "psphotInternal.h"
+# ifndef ROUND
+# define ROUND(X) ((int) ((X) + 0.5*SIGN(X)))
+# endif
+
+psVector *getSaddlePoint (pmReadout *readout, int X1, int Y1, int X2, int Y2, bool swapcoords);
+
+// for each footprint with multiple detections, find the complete set of saddles, that is, the
+// local minima which lie on the lines between the peaks (same as a 'Coll' but less homophonic
+// with 'cull')
+bool psphotFootprintSaddles(pmReadout *readout, psArray *footprints) {  // array of pmFootprints
+    
+# if (PM_PEAKS_CULL_WITH_SMOOTHED_IMAGE)
+    psLogMsg ("psphot", PS_LOG_INFO, "Finding saddle points from footprints using the smoothed image");
+# else
+    psLogMsg ("psphot", PS_LOG_INFO, "Finding saddle points from footprints using the raw (unsmoothed) image");
+# endif
+
+    int xMax = readout->image->numCols - 1;
+    int yMax = readout->image->numRows - 1;
+
+    // FILE *f = fopen ("saddlepoints.dat", "w");
+
+    for (int i = 0; i < footprints->n; i++) {
+	pmFootprint *fp = footprints->data[i];
+	if (fp->peaks == NULL) continue;
+	if (fp->peaks->n < 2) continue;
+
+	// loop over all peaks in the footprint:
+	for (int is = 0; is < fp->peaks->n; is++) {
+
+	    pmPeak *startPeak = fp->peaks->data[is];
+	    if (!startPeak->saddlePoints) startPeak->saddlePoints = psArrayAllocEmpty (4);
+	
+	    // loop over all peaks in the footprint, skipping this one
+	    for (int ie = is + 1; ie < fp->peaks->n; ie++) {
+		if (ie == is) continue;
+
+		pmPeak *endPeak = fp->peaks->data[ie];
+		if (!endPeak->saddlePoints) endPeak->saddlePoints = psArrayAllocEmpty (4);
+
+		// step across all pixels between the startPeak and the endPeak, using a
+		// bresenham-like line-drawing operation
+		
+		int X1 = PS_MIN(xMax, PS_MAX(0, ROUND(startPeak->xf)));
+		int Y1 = PS_MIN(yMax, PS_MAX(0, ROUND(startPeak->yf)));
+		int X2 = PS_MIN(xMax, PS_MAX(0, ROUND(endPeak->xf)));
+		int Y2 = PS_MIN(yMax, PS_MAX(0, ROUND(endPeak->yf)));
+		
+		int dX = X2 - X1;
+		int dY = Y2 - Y1;
+
+		bool FlipCoords = (abs(dX) < abs(dY));
+		bool FlipDirect = FlipCoords ? (Y1 > Y2) : (X1 > X2);
+
+		psVector *saddlePoint = NULL;
+		if (!FlipDirect && !FlipCoords) saddlePoint = getSaddlePoint (readout, X1, Y1, X2, Y2, false);
+		if ( FlipDirect && !FlipCoords) saddlePoint = getSaddlePoint (readout, X2, Y2, X1, Y1, false);
+		if (!FlipDirect &&  FlipCoords) saddlePoint = getSaddlePoint (readout, Y1, X1, Y2, X2, true);
+		if ( FlipDirect &&  FlipCoords) saddlePoint = getSaddlePoint (readout, Y2, X2, Y1, X1, true);
+		psAssert (saddlePoint, "failed to get saddlePoint");
+
+		// fprintf (f, "%f %f | %d %d | %f %f\n", startPeak->xf, startPeak->yf, saddlePoint->data.S32[0], saddlePoint->data.S32[1], endPeak->xf, endPeak->yf);
+
+		psArrayAdd (startPeak->saddlePoints, 4, saddlePoint);
+		psArrayAdd (endPeak->saddlePoints, 4, saddlePoint);
+		psFree (saddlePoint);
+		// NOTE : all saddlePoints for the startPeak are also saddlePoints for the endPeak
+	    }
+	}
+    }
+    // fclose (f);
+    return true;
+}
+
+// psLogMsg ("psphot", PS_LOG_INFO, "%ld peaks, %ld total footprints: %f sec\n", detections->peaks->n, detections->footprints->n, 
+
+psVector *getSaddlePoint (pmReadout *readout, int X1, int Y1, int X2, int Y2, bool swapcoords) {
+
+    psAssert (X1 >= 0, "invalid");
+    psAssert (Y1 >= 0, "invalid");
+    psAssert (X1 < readout->image->numCols, "invalid");
+    psAssert (Y1 < readout->image->numRows, "invalid");
+
+    psAssert (X2 >= 0, "invalid");
+    psAssert (Y2 >= 0, "invalid");
+    psAssert (X2 < readout->image->numCols, "invalid");
+    psAssert (Y2 < readout->image->numRows, "invalid");
+
+    psVector *saddlePoint = psVectorAlloc(2, PS_TYPE_F32);
+
+    int dX = X2 - X1;
+    int dY = Y2 - Y1;
+		
+    float minValue = swapcoords ? readout->image->data.F32[X1][Y1] : readout->image->data.F32[Y1][X1];
+    saddlePoint->data.S32[0] = swapcoords ? Y1 : X1;
+    saddlePoint->data.S32[1] = swapcoords ? X1 : Y1;
+
+    int Y = Y1;
+    int e = 0;
+    for (int X = X1; X <= X2; X++) {
+	if (swapcoords) {
+	    float newValue = readout->image->data.F32[X][Y];
+	    if (newValue < minValue) {
+		minValue = newValue;
+		saddlePoint->data.S32[0] = Y;
+		saddlePoint->data.S32[1] = X;
+	    }
+	} else {
+	    float newValue = readout->image->data.F32[Y][X];
+	    if (newValue < minValue) {
+		minValue = newValue;
+		saddlePoint->data.S32[0] = X;
+		saddlePoint->data.S32[1] = Y;
+	    }
+	}
+	e += dY;
+	int e2 = 2 * e;
+	if (e2 > dX) {
+	    Y++;
+	    e -= dX;
+	} 
+	if (e2 < -dX) {
+	    Y--;
+	    e += dX;
+	}
+    }
+    return (saddlePoint);
+}
Index: branches/meh_branches/ppstack_test/psphot/src/psphotForcedReadout.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotForcedReadout.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotForcedReadout.c	(revision 33415)
@@ -55,10 +55,11 @@
     psphotLoadExtSources (config, view, filerule);
 
-    // construct an initial model for each object, set the radius to fitRadius, set circular fit mask
-    psphotGuessModels (config, view, filerule);
-
     // merge the newly selected sources into the existing list
     // NOTE: merge OLD and NEW
     psphotMergeSources (config, view, filerule);
+
+    // Construct an initial model for each object, set the radius to fitRadius, set circular
+    // fit mask.  NOTE: only applied to sources without guess models
+    psphotGuessModels (config, view, filerule);
 
     // linear PSF fit to source peaks, subtract the models from the image (in PSF mask)
Index: branches/meh_branches/ppstack_test/psphot/src/psphotGuessModels.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotGuessModels.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotGuessModels.c	(revision 33415)
@@ -46,5 +46,5 @@
     psAssert (detections, "missing detections?");
 
-    psArray *sources = detections->newSources;
+    psArray *sources = detections->allSources;
     psAssert (sources, "missing sources?");
 
@@ -112,5 +112,7 @@
         // wait here for the threaded jobs to finish
         // fprintf (stderr, "wait for threads (%d, %d)\n", jx, jy);
-        if (!psThreadPoolWait (false)) {
+        if (!psThreadPoolWait (false, true)) {
+            // harvest our jobs
+            psFree(cellGroups);
             psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
             return false;
@@ -141,5 +143,5 @@
     psFree (cellGroups);
 
-    psLogMsg ("psphot.models", 4, "built models for %ld objects: %f sec\n", sources->n, psTimerMark ("psphot.models"));
+    psLogMsg ("psphot.models", PS_LOG_WARN, "built models for %ld objects: %f sec\n", sources->n, psTimerMark ("psphot.models"));
     return true;
 }
@@ -159,4 +161,7 @@
     for (int i = 0; i < sources->n; i++) {
         pmSource *source = sources->data[i];
+
+	// do not redo sources already guessed
+	if (source->tmpFlags & PM_SOURCE_TMPF_MODEL_GUESS) continue;
 
         // this is used to mark sources for which the model is measured. We check later that
@@ -169,4 +174,6 @@
         if (source->type == PM_SOURCE_TYPE_SATURATED) continue;
         if (!source->peak) continue;
+
+	// psAssert (source->peak->footprint, "peak without footprint??");
 
         nSrc ++;
Index: branches/meh_branches/ppstack_test/psphot/src/psphotImageLoop.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotImageLoop.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotImageLoop.c	(revision 33415)
@@ -129,4 +129,11 @@
 		    }
 		    break;
+		  case PSPHOT_MODEL_TEST:
+		    if (!psphotModelTestReadout (config, view, "PSPHOT.INPUT")) {
+			psError(psErrorCodeLast(), false, "failure in psphotReadout for chip %d, cell %d, readout %d\n", view->chip, view->cell, view->readout);
+			psFree (view);
+			return false;
+		    }
+		    break;
 		}
             }
Index: branches/meh_branches/ppstack_test/psphot/src/psphotImageQuality.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotImageQuality.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotImageQuality.c	(revision 33415)
@@ -289,5 +289,5 @@
 #endif
 
-    psLogMsg ("psphot", PS_LOG_INFO, "Image Quality Stats from %ld psf stars : FWHM (major, minor) [pixels]: %f, %f\n",
+    psLogMsg ("psphot", PS_LOG_WARN, "Image Quality Stats from %ld psf stars : FWHM (major, minor) [pixels]: %f, %f\n",
               M2->n, fwhm_major, fwhm_minor);
 
Index: branches/meh_branches/ppstack_test/psphot/src/psphotKronIterate.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotKronIterate.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotKronIterate.c	(revision 33415)
@@ -0,0 +1,453 @@
+# include "psphotInternal.h"
+# ifndef ROUND
+# define ROUND(X) ((int) ((X) + 0.5*SIGN(X)))
+# endif
+
+bool psphotKronWindowSetSource(pmSource *source, psImage *kronWindow, bool useKronRadius, bool insert);
+bool psphotKronWindowMag (pmSource *source, psImage *kronWindow, float radius, float minKronRadius, psImageMaskType maskVal);
+
+bool psphotKronIterate (pmConfig *config, const pmFPAview *view, const char *filerule)
+{
+    bool status = true;
+
+    // return true;
+
+    fprintf (stdout, "\n");
+    psLogMsg ("psphot", PS_LOG_INFO, "--- psphot Kron Iterate ---");
+
+    // select the appropriate recipe information
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
+    psAssert (recipe, "missing recipe?");
+
+    int num = psphotFileruleCount(config, filerule);
+
+    // skip the chisq image (optionally?)
+    int chisqNum = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.CHISQ.NUM");
+    if (!status) chisqNum = -1;
+
+    // loop over the available readouts
+    for (int i = 0; i < num; i++) {
+        if (i == chisqNum) continue; // skip chisq image
+
+        // find the currently selected readout
+        pmFPAfile *file = pmFPAfileSelectSingle(config->files, filerule, i); // File of interest
+        psAssert (file, "missing file?");
+
+        pmReadout *readout = pmFPAviewThisReadout(view, file->fpa);
+        psAssert (readout, "missing readout?");
+
+        pmDetections *detections = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.DETECTIONS");
+        psAssert (detections, "missing detections?");
+
+        psArray *sources = detections->newSources ? detections->newSources : detections->allSources;
+        psAssert (sources, "missing sources?");
+
+        pmPSF *psf = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.PSF");
+        // psAssert (psf, "missing psf?");
+
+        if (!psphotKronIterateReadout (config, recipe, view, readout, sources, psf)) {
+            psError (PSPHOT_ERR_CONFIG, false, "failed to measure magnitudes for %s entry %d", filerule, i);
+            return false;
+        }
+    }
+    return true;
+}
+
+int psphotKapaChannel (int channel);
+bool psphotVisualShowMask (int kapaFD, psImage *inImage, const char *name, int channel);
+bool psphotVisualRangeImage (int kapaFD, psImage *inImage, const char *name, int channel, float min, float max);
+
+bool psphotKronIterateReadout(pmConfig *config, psMetadata *recipe, const pmFPAview *view, pmReadout *readout, psArray *sources, pmPSF *psf) {
+
+    bool status = false;
+
+    if (!sources->n) {
+        psLogMsg ("psphot", PS_LOG_INFO, "no sources, skipping masked kron");
+        return true;
+    }
+
+    psTimerStart ("psphot.kron");
+
+    // determine the number of allowed threads
+    int nThreads = psMetadataLookupS32(&status, config->arguments, "NTHREADS"); // Number of threads
+    if (!status) {
+        nThreads = 0;
+    }
+
+    float RADIUS = psMetadataLookupF32 (&status, readout->analysis, "PSF_MOMENTS_RADIUS");
+    if (!status) {
+        RADIUS = psMetadataLookupF32 (&status, recipe, "PSF_MOMENTS_RADIUS");
+    }
+
+    float MIN_KRON_RADIUS = psMetadataLookupF32 (&status, readout->analysis, "MOMENTS_MIN_KRON");
+    if (!status) {
+        MIN_KRON_RADIUS = 0.25*RADIUS;
+    }
+
+    // bit-masks to test for good/bad pixels
+    psImageMaskType maskVal = psMetadataLookupImageMask(&status, recipe, "MASK.PSPHOT");
+    assert (maskVal);
+
+    // bit-mask to mark pixels not used in analysis
+    psImageMaskType markVal = psMetadataLookupImageMask(&status, recipe, "MARK.PSPHOT");
+    assert (markVal);
+
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    // source analysis is done in S/N order (brightest first)
+    sources = psArraySort (sources, pmSourceSortByFlux);
+    if (!sources->n) {
+        psLogMsg ("psphot", PS_LOG_INFO, "no sources, skipping blend");
+        return true;
+    }
+
+    // generate the window image: multiply the flux by this to downweight neighbors
+    psImage *kronWindow = psImageAlloc (readout->image->numCols, readout->image->numRows, PS_TYPE_F32);
+    psImageInit (kronWindow, 1.0);
+
+    // start with the currently known moments (Mxx, Myy, Mxy) and generate a window image
+    for (int i = 0; i < sources->n; i++) {
+
+        pmSource *source = sources->data[i];
+
+	// set a window function for each source based on the moments
+	// (this skips really bad sources (no peak, no moments, DEFECT)
+	psphotKronWindowSetSource (source, kronWindow, false, true);
+    }
+
+    // threaded measurement of the source magnitudes
+    // choose Cx, Cy (see psphotThreadTools.c for overview of the concepts)
+    int Cx = 1, Cy = 1;
+    psphotChooseCellSizes (&Cx, &Cy, readout, nThreads);
+
+    psArray *cellGroups = psphotAssignSources (Cx, Cy, sources);
+
+    for (int i = 0; i < cellGroups->n; i++) {
+
+        psArray *cells = cellGroups->data[i];
+
+        for (int j = 0; j < cells->n; j++) {
+
+            // allocate a job -- if threads are not defined, this just runs the job
+            psThreadJob *job = psThreadJobAlloc ("PSPHOT_KRON_ITERATE");
+
+            psArrayAdd(job->args, 1, readout);
+            psArrayAdd(job->args, 1, kronWindow);
+            psArrayAdd(job->args, 1, cells->data[j]); // sources
+            PS_ARRAY_ADD_SCALAR(job->args, markVal,            PS_TYPE_IMAGE_MASK);
+            PS_ARRAY_ADD_SCALAR(job->args, maskVal,            PS_TYPE_IMAGE_MASK);
+            PS_ARRAY_ADD_SCALAR(job->args, RADIUS,             PS_TYPE_F32);
+            PS_ARRAY_ADD_SCALAR(job->args, MIN_KRON_RADIUS,    PS_TYPE_F32);
+
+// set this to 0 to run without threading
+# if (1)
+            if (!psThreadJobAddPending(job)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
+                return false;
+            }
+# else
+	    if (!psphotKronIterate_Threaded(job)) {
+		psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
+		psFree(AnalysisRegion);
+		return false;
+	    }
+	    psFree(job);
+# endif
+        }
+
+        // wait for the threads to finish and manage results
+        if (!psThreadPoolWait (false, true)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
+            return false;
+        }
+
+        // we have only supplied one type of job, so we can assume the types here
+        psThreadJob *job = NULL;
+        while ((job = psThreadJobGetDone()) != NULL) {
+            if (job->args->n < 1) {
+                fprintf (stderr, "error with job\n");
+            }
+            psFree(job);
+        }
+    }
+    psFree (cellGroups);
+    psFree (kronWindow);
+
+    psLogMsg ("psphot.kron", PS_LOG_WARN, "measure masked kron magnitudes : %f sec for %ld objects\n", psTimerMark ("psphot.kron"), sources->n);
+    return true;
+}
+
+bool psphotKronIterate_Threaded (psThreadJob *job) {
+
+    pmReadout *readout              = job->args->data[0];
+    psImage *kronWindow             = job->args->data[1];
+    psArray *sources                = job->args->data[2];
+    psImageMaskType markVal         = PS_SCALAR_VALUE(job->args->data[3],PS_TYPE_IMAGE_MASK_DATA);
+    psImageMaskType maskVal         = PS_SCALAR_VALUE(job->args->data[4],PS_TYPE_IMAGE_MASK_DATA);
+    float RADIUS                    = PS_SCALAR_VALUE(job->args->data[5],F32);
+    float MIN_KRON_RADIUS           = PS_SCALAR_VALUE(job->args->data[6],F32);
+
+    // XXX TEST : set iteration to 1
+    for (int j = 0; j < 1; j++) {
+	for (int i = 0; i < sources->n; i++) {
+
+	    pmSource *source = sources->data[i];
+	    if (!source->peak) continue; // XXX how can we have a peak-less source?
+
+	    // allocate space for moments
+	    if (!source->moments) continue;
+
+	    // replace object in image
+	    bool reSubtract = false;
+	    if (source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED) {
+		pmSourceAdd (source, PM_MODEL_OP_FULL, maskVal);
+		reSubtract = true;
+	    }
+
+	    // use S/N to control max window size
+	    // float kronSN = source->moments->KronFlux / source->moments->KronFluxErr;
+
+	    // maxWindow -> 1.5*RADIUS for kronSN = 5.0, keeping S.B. constant (kronSN ~ flux)
+	    // (kronSN / maxWindow^2) = (5.0 / (1.5 RADIUS)^2)
+	    // maxWindow = 1.5 * RADIUS * sqrt(kronSN / 5.0)
+	    // XXX float maxWindow = (isfinite(kronSN) && (kronSN > 5.0)) ? 1.5 * RADIUS * sqrt(kronSN / 5.0) : 1.5*RADIUS;
+
+	    // iterate to the window radius
+	    // XXX float windowRadius = PS_MIN(PS_MAX(RADIUS, 4.0*source->moments->Mrf), maxWindow);
+
+	    // XXX TEST : use a window based on the radial profile numbers: max is skyRadius, min is RADIUS
+	    // if we lack the skyRadius (eg MATCHED sources), go to the default value
+	    float maxWindow = isfinite(source->skyRadius) ? source->skyRadius : RADIUS;
+	    float windowRadius = PS_MAX(RADIUS, maxWindow);
+
+	    // re-allocate image, weight, mask arrays for each peak with box big enough to fit BIG_RADIUS
+	    pmSourceRedefinePixels (source, readout, source->peak->x, source->peak->y, windowRadius + 2);
+	    psAssert (source->pixels, "WTF?");
+
+	    // clear the window function for this source based on the moments
+	    psphotKronWindowSetSource (source, kronWindow, (j > 0), false);
+
+	    // this function populates moments->Mrf,KronFlux,KronFluxErr
+	    psphotKronWindowMag (source, kronWindow, windowRadius, MIN_KRON_RADIUS, maskVal);
+	    psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(markVal));
+
+	    // set a window function for each source based on the moments
+	    psphotKronWindowSetSource (source, kronWindow, true, true);
+
+	    // if we subtracted it above, re-subtract the object, leave local sky
+	    if (reSubtract) {
+		pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
+	    }
+	}
+    }
+    return true;
+}
+
+bool psphotKronWindowMag (pmSource *source, psImage *kronWindow, float radius, float minKronRadius, psImageMaskType maskVal) {
+
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_PTR_NON_NULL(source->peak, false);
+    PS_ASSERT_PTR_NON_NULL(source->pixels, false);
+    PS_ASSERT_FLOAT_LARGER_THAN(radius, 0.0, false);
+
+    psF32 R2 = PS_SQR(radius);
+    float rsigma2 = 0.5 / PS_SQR(radius/2.0);
+
+    // a note about coordinates: coordinates of objects throughout psphot refer to the primary
+    // image coordinates.  the source->pixels image has an offset relative to its parent of
+    // col0,row0: a pixel (x,y) in the primary image has coordinates of (x-col0, y-row0) in
+    // this subimage.  we subtract off the peak coordinates, adjusted to this subimage, to have
+    // minimal round-off error in the sums.  since these values are subtracted just to minimize
+    // the dynamic range and are added back below, the exact value does not matter. these are
+    // (int) so they can be used in the image index below.
+
+    // Now calculate higher-order moments, using the above-calculated first moments to adjust coordinates
+    // Xn  = SUM (x - xc)^n * (z - sky)
+
+    psF32 RF = 0.0;
+    psF32 RS = 0.0;
+
+    // the peak position is less accurate but less subject to extreme deviations
+    float dX = source->moments->Mx - source->peak->xf;
+    float dY = source->moments->My - source->peak->yf;
+    float dR = hypot(dX, dY);
+    float Xo = (dR < 2.0) ? source->moments->Mx : source->peak->xf;
+    float Yo = (dR < 2.0) ? source->moments->My : source->peak->yf;
+
+    // center of mass in subimage.  Note: the calculation below uses pixel index, so we correct
+    // xCM, yCM from pixel coords to pixel index here.
+    psF32 xCM = Xo - 0.5 - source->pixels->col0; // coord of peak in subimage
+    psF32 yCM = Yo - 0.5 - source->pixels->row0; // coord of peak in subimage
+
+    int Xwo = source->pixels->col0;
+    int Ywo = source->pixels->row0;
+
+    psF32 **vPix = source->pixels->data.F32;
+    psF32 **vWin = kronWindow->data.F32;
+    psF32 **vWgt = source->variance->data.F32;
+    
+    psImageMaskType **vMsk = (source->maskObj == NULL) ? NULL : source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA;
+
+    for (psS32 row = 0; row < source->pixels->numRows ; row++) {
+
+	psF32 yDiff = row - yCM;
+	if (fabs(yDiff) > radius) continue;
+
+	// coordinate of mirror pixel
+	int yFlip = yCM - yDiff;
+	if (yFlip < 0) continue;
+	if (yFlip >= source->pixels->numRows) continue;
+
+	for (psS32 col = 0; col < source->pixels->numCols ; col++) {
+	    // check mask and value for this pixel
+	    if (vMsk && (vMsk[row][col] & maskVal)) continue;
+	    if (isnan(vPix[row][col])) continue;
+
+	    psF32 xDiff = col - xCM;
+	    if (fabs(xDiff) > radius) continue;
+
+	    // coordinate of mirror pixel
+	    int xFlip = xCM - xDiff;
+	    if (xFlip < 0) continue;
+	    if (xFlip >= source->pixels->numCols) continue;
+
+	    // check mask and value for mirror pixel
+	    if (vMsk && (vMsk[yFlip][xFlip] & maskVal)) continue;
+	    if (isnan(vPix[yFlip][xFlip])) continue;
+
+	    // radius is just a function of (xDiff, yDiff)
+	    psF32 r2  = PS_SQR(xDiff) + PS_SQR(yDiff);
+	    if (r2 > R2) continue;
+
+	    // flux * window
+	    float z = r2 * rsigma2;
+	    assert (z >= 0.0);
+
+	    // weight by window image and wide Gaussian
+	    float weight1  = vWin[row+Ywo][col+Xwo]*exp(-z);
+	    float weight2  = vWin[yFlip+Ywo][xFlip+Xwo]*exp(-z);
+
+	    float fDiff1 = vPix[row][col]*weight1;
+	    float fDiff2 = vPix[yFlip][xFlip]*weight2;
+
+	    float pDiff = (fDiff1 > 0.0) ? sqrt(fabs(fDiff1*fDiff2)) : -sqrt(fabs(fDiff1*fDiff2));
+
+	    // Kron Flux uses the 1st radial moment (maybe Gaussian windowed?)
+	    psF32 rf = pDiff * sqrt(r2);
+	    psF32 rs = 0.5 * (fDiff1 + fDiff2);
+
+	    RF  += rf;
+	    RS  += rs;
+	}
+    }
+
+    // Saturate the 1st radial moment
+    float Mrf = MAX(minKronRadius, RF/RS);
+    if (sqrt(source->peak->detValue) < 10.0) {
+	Mrf = MIN (radius, Mrf);
+    }
+
+    // Calculate the Kron magnitude (make this block optional?)
+    float radKron  = 2.5*Mrf;
+    float radKron2 = radKron*radKron;
+
+    int nKronPix = 0;
+    float Sum = 0.0;
+    float Var = 0.0;
+    float Win = 0.0;
+
+    for (psS32 row = 0; row < source->pixels->numRows ; row++) {
+
+	psF32 yDiff = row - yCM;
+	if (fabs(yDiff) > radKron) continue;
+
+	for (psS32 col = 0; col < source->pixels->numCols ; col++) {
+	    // check mask and value for this pixel
+	    if (vMsk && (vMsk[row][col] & maskVal)) continue;
+	    if (isnan(vPix[row][col])) continue;
+
+	    psF32 xDiff = col - xCM;
+	    if (fabs(xDiff) > radKron) continue;
+
+	    // radKron is just a function of (xDiff, yDiff)
+	    psF32 r2  = PS_SQR(xDiff) + PS_SQR(yDiff);
+	    if (r2 > radKron2) continue;
+
+	    float weight1  = vWin[row+Ywo][col+Xwo];
+	    float fDiff1 = vPix[row][col]*weight1;
+
+	    float pDiff = fDiff1;
+	    psF32 wDiff = vWgt[row][col] * weight1;
+
+	    Sum += pDiff;
+	    Var += wDiff;
+	    Win += weight1;
+	    nKronPix ++;
+	}
+    }
+
+    source->moments->Mrf = Mrf;
+    source->moments->KronFlux    = Sum;
+    source->moments->KronFluxErr = sqrt(Var);
+
+    return true;
+}
+
+bool psphotKronWindowSetSource(pmSource *source, psImage *kronWindow, bool useKronRadius, bool insert) {
+
+    if (!source) return false;
+    if (!source->peak) return false; // XXX how can we have a peak-less source?
+    if (!source->moments) return false;
+    if (source->type == PM_SOURCE_TYPE_DEFECT) return false;
+    if (source->type == PM_SOURCE_TYPE_SATURATED) return false;
+    psAssert(kronWindow, "need a window");
+
+    // we have a source with moments Mx, My, Mxx, Mxy, Myy.  we just need to define a Gaussian that has 
+    // these values (and peak of 1.0)
+
+    int Nx = kronWindow->numCols;
+    int Ny = kronWindow->numRows;
+
+    float Xo = source->moments->Mx;
+    float Yo = source->moments->My;
+
+    float Mxx = source->moments->Mxx;
+    float Mxy = source->moments->Mxy;
+    float Myy = source->moments->Myy;
+
+    float Mmajor = 0.5*(Mxx + Myy) + 0.5*sqrt(PS_SQR(Mxx - Myy) + 4.0*PS_SQR(Mxy));
+    float Mminor = 0.5*(Mxx + Myy) - 0.5*sqrt(PS_SQR(Mxx - Myy) + 4.0*PS_SQR(Mxy));
+
+    // Mxx, Mxy, Myy define the elliptical shape, but Mrf defines the width 
+    float scale = PS_SQR(0.5 * source->moments->Mrf) / Mmajor;
+
+    float Sxx = scale * Mmajor * Mminor / Myy; // sigma_x^2
+    float Sxy = Mxy / (scale * Mmajor * Mminor);
+    float Syy = scale * Mmajor * Mminor / Mxx; // sigma_y^2
+
+    float Smajor = sqrt(Mmajor);
+
+    int minX = PS_MIN(PS_MAX(Xo - 5*Smajor, 0), Nx - 1);
+    int maxX = PS_MIN(PS_MAX(Xo + 5*Smajor, 0), Nx - 1);
+    int minY = PS_MIN(PS_MAX(Yo - 5*Smajor, 0), Ny - 1);
+    int maxY = PS_MIN(PS_MAX(Yo + 5*Smajor, 0), Ny - 1);
+
+    float rMxx = 0.5 / Sxx;
+    float rMyy = 0.5 / Syy;
+    
+    for (int iy = minY; iy < maxY; iy++) {
+	for (int ix = minX; ix < maxX; ix++) {
+
+	    float dX = (ix + 0.5 - Xo);
+	    float dY = (iy + 0.5 - Yo);
+
+	    float z = rMxx * PS_SQR(dX) + rMyy * PS_SQR(dY) + Sxy*dX*dY;
+
+	    float f = insert ? 1.001 - exp(-z) : 1.0 / (1.001 - exp(-z));
+
+	    kronWindow->data.F32[iy][ix] *= f;
+	}
+    }
+    return true;
+}
Index: branches/meh_branches/ppstack_test/psphot/src/psphotKronMasked.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotKronMasked.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotKronMasked.c	(revision 33415)
@@ -1,4 +1,8 @@
 # include "psphotInternal.h"
-
+# ifndef ROUND
+# define ROUND(X) ((int) ((X) + 0.5*SIGN(X)))
+# endif
+
+bool psphotKronMask (pmSource *source, psImage *kronMask, psImageMaskType markVal);
 bool psphotKronMag (pmSource *source, float radius, float minKronRadius, psImageMaskType maskVal);
 
@@ -45,4 +49,8 @@
 }
 
+int psphotKapaChannel (int channel);
+bool psphotVisualShowMask (int kapaFD, psImage *inImage, const char *name, int channel);
+bool psphotVisualRangeImage (int kapaFD, psImage *inImage, const char *name, int channel, float min, float max);
+
 bool psphotKronMaskedReadout(pmConfig *config, psMetadata *recipe, const pmFPAview *view, pmReadout *readout, psArray *sources, pmPSF *psf) {
 
@@ -72,4 +80,6 @@
     }
 
+    float EXT_FIT_MAX_RADIUS = psMetadataLookupF32 (&status, recipe, "EXT_FIT_MAX_RADIUS");
+
     // bit-masks to test for good/bad pixels
     psImageMaskType maskVal = psMetadataLookupImageMask(&status, recipe, "MASK.PSPHOT");
@@ -83,9 +93,14 @@
     maskVal |= markVal;
 
+    // psphotSaveImage (NULL, readout->mask, "kron.unmasked.fits");
+
+    pmSourcePhotometryMode photMode = PM_SOURCE_PHOT_PSFONLY;
+
+    // XXX tmp visualization
+    // int kapa = psphotKapaChannel (1);
+
     // generate the mask image: increment counter for every source overlapping the pixel
-    psImage *kronMask = psImageAlloc (readout->image->numCols, readout->image->numRows, PS_TYPE_U8);
+    psImage *kronMask = psImageAlloc (readout->image->numCols, readout->image->numRows, PS_TYPE_S32);
     psImageInit (kronMask, 0);
-    int Nx = kronMask->numCols;
-    int Ny = kronMask->numRows;
     for (int i = 0; i < sources->n; i++) {
 
@@ -96,45 +111,59 @@
         if (!source->moments) continue;
 
-        // replace object in image
-        if (source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED) {
-            pmSourceAdd (source, PM_MODEL_OP_FULL, maskVal);
-        }
-
-	psphotKronMag (source, RADIUS, MIN_KRON_RADIUS, maskVal);
-
-        // re-subtract the object, leave local sky
-        pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
-
-	continue;
-
-	// XXX skip this code
-	// generate the pixel masks
-	// int Xo = source->moments->Mx;
-	// int Yo = source->moments->My;
-	float dX = source->moments->Mx - source->peak->xf;
-	float dY = source->moments->My - source->peak->yf;
-	float dR = hypot(dX, dY);
-	
-	float Xo = (dR < 2.0) ? source->moments->Mx : source->peak->xf;
-	float Yo = (dR < 2.0) ? source->moments->My : source->peak->yf;
-	
-	int Kr = 2.5*source->moments->Mrf;
-	int Kr2 = Kr*Kr;
-	
-	for (int iy = Yo - Kr; iy < Yo + Kr + 1; iy++) {
-	    if (iy < 0) continue;
-	    if (iy >= Ny) continue;
-	    for (int ix = Xo - Kr; ix < Xo + Kr + 1; ix++) {
-		if (ix < 0) continue;
-		if (ix >= Nx) continue;
-
-		if (PS_SQR(ix - Xo) + PS_SQR(iy - Yo) > Kr2) continue;
-		if (kronMask->data.U8[iy][ix] < 0xff) {
-		    kronMask->data.U8[iy][ix] ++;
-		}
-	    }
-	}
-    }		
+	// replace object in image
+	if (source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED) {
+	    pmSourceAdd (source, PM_MODEL_OP_FULL, maskVal);
+	}
+
+	// save the PSF-based values
+	// source->moments->KronFluxPSF    = source->moments->KronFlux;
+	// source->moments->KronFluxPSFErr = source->moments->KronFluxErr;
+	// source->moments->KronRadiusPSF  = source->moments->Mrf;
+
+	// iterate to the window radius
+	float windowRadius = RADIUS;
+	for (int j = 0; j < 4; j++) {
+	    // XXX use some S/N criterion to limit?
+	    // if (source->moments->KronFlux / source->moments->KronFluxErr > 10.0) {
+	    // windowRadius = PS_MAX (RADIUS, 10*source->moments->Mrf);
+	    // }
+
+	    // re-allocate image, weight, mask arrays for each peak with box big enough to fit BIG_RADIUS
+	    pmSourceRedefinePixels (source, readout, source->peak->x, source->peak->y, windowRadius + 2);
+
+	    // mask the pixels not contained by the footprint
+	    // if (psphotMaskFootprint (readout, source, markVal)) {
+	    // 	// psphotVisualShowMask (kapa, source->maskObj, "mask", 2);
+	    // 	// psphotVisualRangeImage (kapa, source->pixels, "image", 1, -50, 300);
+	    // 	// fprintf (stderr, "masked\n");
+	    // }	    
+
+	    // mask the pixels associated with near neighbors
+	    if (psphotKronMask (source, kronMask, markVal)) {
+		// psphotVisualShowMask (kapa, source->maskObj, "mask", 2);
+		// psphotVisualRangeImage (kapa, source->pixels, "image", 1, -50, 300);
+	     	// fprintf (stderr, "masked\n");
+	    }	    
+	    // this function populates moments->Mrf,KronFlux,KronFluxErr
+	    // XXX what about KronInner, KronOuter, etc?
+	    psphotKronMag (source, windowRadius, MIN_KRON_RADIUS, maskVal);
+	    windowRadius = PS_MIN(PS_MAX(RADIUS, 4.0*source->moments->Mrf), EXT_FIT_MAX_RADIUS);
+	    psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(markVal));
+	}
+        pmSourceMagnitudes (source, psf, photMode, maskVal, markVal, source->apRadius);
+	float kmag = -2.5*log10(source->moments->KronFlux);
+	if (source->psfMag - kmag > 0.25) {
+	    // psphotVisualShowMask (kapa, source->maskObj, "mask", 1);
+	    // psphotVisualRangeImage (kapa, source->pixels, "image", 0, -50, 300);
+	    // fprintf (stderr, "psf: %f, kron: %f, dmag: %f\n", source->psfMag, kmag, source->psfMag - kmag);
+	    // fprintf (stderr, "continue\n");
+	}
+	// re-subtract the object, leave local sky
+	pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
+    }
+
+    // psphotSaveImage (NULL, readout->mask, "kron.masked.fits");
     // psphotSaveImage (NULL, kronMask, "kronmask.fits");
+
     psLogMsg ("psphot.kron", PS_LOG_DETAIL, "measure masked kron magnitudes : %f sec for %ld objects\n", psTimerMark ("psphot.kron"), sources->n);
 
@@ -142,4 +171,6 @@
     return true;
 }
+
+# define WEIGHTED 0
 
 bool psphotKronMag (pmSource *source, float radius, float minKronRadius, psImageMaskType maskVal) {
@@ -150,7 +181,9 @@
     PS_ASSERT_FLOAT_LARGER_THAN(radius, 0.0, false);
 
-    psF32 Sum = 0.0;
-    psF32 Var = 0.0;
     psF32 R2 = PS_SQR(radius);
+
+# if (WEIGHTED)
+    float rsigma2 = 0.5 / PS_SQR(radius / 4.0); // use a Gaussian window with sigma = R_window / 2
+# endif
 
     // a note about coordinates: coordinates of objects throughout psphot refer to the primary
@@ -168,5 +201,5 @@
     psF32 RS = 0.0;
 
-# if (1)
+    // the peak position is less accurate but less subject to extreme deviations
     float dX = source->moments->Mx - source->peak->xf;
     float dY = source->moments->My - source->peak->yf;
@@ -174,8 +207,4 @@
     float Xo = (dR < 2.0) ? source->moments->Mx : source->peak->xf;
     float Yo = (dR < 2.0) ? source->moments->My : source->peak->yf;
-# else
-    float Xo = source->moments->Mx;
-    float Yo = source->moments->My;
-# endif
 
     // center of mass in subimage.  Note: the calculation below uses pixel index, so we correct
@@ -210,9 +239,14 @@
 	    if (r2 > R2) continue;
 
+# if (WEIGHTED)
+	    float z = r2 * rsigma2;
+	    assert (z >= 0.0);
+	    float weight  = exp(-z);
+	    psF32 pDiff = *vPix * weight;
+# else
 	    psF32 pDiff = *vPix;
-
-	    Sum += pDiff;
-
-	    // Kron Flux uses the 1st radial moment (NOT Gaussian windowed)
+# endif
+
+	    // Kron Flux uses the 1st radial moment (maybe Gaussian windowed?)
 	    psF32 rf = pDiff * sqrt(r2);
 	    psF32 rs = pDiff;
@@ -234,5 +268,6 @@
 
     int nKronPix = 0;
-    Sum = Var = 0.0;
+    float Sum = 0.0;
+    float Var = 0.0;
 
     for (psS32 row = 0; row < source->pixels->numRows ; row++) {
@@ -263,6 +298,14 @@
 	    if (r2 > radKron2) continue;
 
+# if (WEIGHTED)
+	    float z = r2 * rsigma2;
+	    assert (z >= 0.0);
+	    float weight  = exp(-z);
+	    psF32 pDiff = *vPix * weight;
+	    psF32 wDiff = *vWgt * weight;
+# else
 	    psF32 pDiff = *vPix;
 	    psF32 wDiff = *vWgt;
+# endif
 
 	    Sum += pDiff;
@@ -272,13 +315,173 @@
     }
 
-    source->moments->Mrh = Mrf;
-
-    // XXX for a test, save the old values here:
-    source->moments->KronFouter    = source->moments->KronFlux;
-    source->moments->KronCoreErr = source->moments->KronFluxErr;
-
-    source->moments->KronFlux    = Sum       * M_PI * PS_SQR(radKron) / nKronPix;
-    source->moments->KronFluxErr = sqrt(Var) * M_PI * PS_SQR(radKron) / nKronPix;
+    source->moments->Mrf = Mrf;
+    source->moments->KronFlux    = Sum;
+    source->moments->KronFluxErr = sqrt(Var);
+    // source->moments->KronFlux    = Sum       * M_PI * PS_SQR(radKron) / nKronPix;
+    // source->moments->KronFluxErr = sqrt(Var) * M_PI * PS_SQR(radKron) / nKronPix;
 
     return true;
 }
+
+enum {Y_P, Y_M, X_P, X_M};
+
+bool psphotKronMask (pmSource *source, psImage *kronMask, psImageMaskType markVal) {
+
+    int X, Y, e, dXs, dYs;
+
+    if (!source->peak) return false;
+    if (!source->peak->saddlePoints) return false;
+    if (!source->peak->saddlePoints->n) return false;
+
+    // return true;
+
+    int xMax = source->maskObj->numCols;
+    int yMax = source->maskObj->numRows;
+    // int xOff = source->maskObj->col0;
+    // int yOff = source->maskObj->row0;
+
+    int Xo = ROUND(source->peak->xf) - source->maskObj->col0;
+    int Yo = ROUND(source->peak->yf) - source->maskObj->row0;
+
+    for (int i = 0; i < source->peak->saddlePoints->n; i++) {
+
+	psVector *saddlePoint = source->peak->saddlePoints->data[i];
+	int Xs = ROUND(saddlePoint->data.S32[0]) - source->maskObj->col0;
+	int Ys = ROUND(saddlePoint->data.S32[1]) - source->maskObj->row0;
+	
+	// fprintf (stderr, "mask %d,%d @ %d,%d (%d,%d)\n", Xo, Yo, Xs, Ys, saddlePoint->data.S32[0], saddlePoint->data.S32[1]);
+
+	// We want to mask the pixels between the edge of the image and the line perpendicular
+	// to the connecting line.  Call dX,dY = (Xs-Xo,Ys-Yo).  There are 4 cases: 
+	// +y : dY > 0, fabs(dY) > fabs(dX)
+	// -y : dY < 0, fabs(dY) > fabs(dX)
+	// +x : dX > 0, fabs(dX) > fabs(dY)
+	// -x : dX < 0, fabs(dX) > fabs(dY)
+
+	int dX = Xs - Xo;
+	int dY = Ys - Yo;
+
+	// +y : dY > 0, fabs(dY) > fabs(dX)
+	// -y : dY < 0, fabs(dY) > fabs(dX)
+	if (fabs(dY) > fabs(dX)) {
+	    // points to right of saddle
+	    Y = Ys;
+	    e = 0;
+	    dXs = (dY > 0) ? +dY : -dY;  // this forces dXs > 0
+	    dYs = (dY > 0) ? -dX : +dX;
+	    for (X = Xs; X < xMax; X++) {
+		if (dY > 0) {
+		    for (int Ym = Y; Ym < yMax; Ym++) {
+			source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[Ym][X] |= markVal;
+			// kronMask->data.S32[Ym+yOff][X+xOff] = source->id;
+		    }
+		} else {
+		    for (int Ym = Y; Ym >= 0; Ym--) {
+			source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[Ym][X] |= markVal;
+			// kronMask->data.S32[Ym+yOff][X+xOff] = source->id;
+		    }
+		}
+		e += dYs;
+		int e2 = 2 * e;
+		if (e2 > dXs) {
+		    Y ++;
+		    e -= dXs;
+		}
+		if (e2 < -dXs) {
+		    Y --;
+		    e += dXs;
+		}
+		if (Y >= yMax) break;
+		if (Y < 0) break;
+	    }
+	    // points to left of saddle
+	    Y = Ys;
+	    e = 0;
+	    for (X = Xs; X >= 0; X--) {
+		if (dY > 0) {
+		    for (int Ym = Y; Ym < yMax; Ym++) {
+			source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[Ym][X] |= markVal;
+			// kronMask->data.S32[Ym+yOff][X+xOff] = source->id;
+		    }
+		} else {
+		    for (int Ym = Y; Ym >= 0; Ym--) {
+			source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[Ym][X] |= markVal;
+			// kronMask->data.S32[Ym+yOff][X+xOff] = source->id;
+		    }
+		}
+		e -= dYs;
+		int e2 = 2 * e;
+		if (e2 > dXs) {
+		    Y ++;
+		    e -= dXs;
+		}
+		if (e2 < -dXs) {
+		    Y --;
+		    e += dXs;
+		}
+		if (Y >= yMax) break;
+		if (Y < 0) break;
+	    }
+	} else {
+	    // points to right of saddle
+	    X = Xs;
+	    e = 0;
+	    dXs = (dX > 0) ? -dY : +dY;
+	    dYs = (dX > 0) ? +dX : -dX;  // this forces dYs > 0
+	    for (Y = Ys; Y < yMax; Y++) {
+		if (dX > 0) {
+		    for (int Xm = X; Xm < xMax; Xm++) {
+			source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[Y][Xm] |= markVal;
+			// kronMask->data.S32[Y+yOff][Xm+xOff] = source->id;
+		    }
+		} else {
+		    for (int Xm = X; Xm >= 0; Xm--) {
+			source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[Y][Xm] |= markVal;
+			// kronMask->data.S32[Y+yOff][Xm+xOff] = source->id;
+		    }
+		}
+		e += dXs;
+		int e2 = 2 * e;
+		if (e2 > dYs) {
+		    X ++;
+		    e -= dYs;
+		}
+		if (e2 < -dYs) {
+		    X --;
+		    e += dYs;
+		}
+		if (X >= yMax) break;
+		if (X < 0) break;
+	    }
+	    // points to left of saddle
+	    X = Xs;
+	    e = 0;
+	    for (Y = Ys; Y >= 0; Y--) {
+		if (dX > 0) {
+		    for (int Xm = X; Xm < xMax; Xm++) {
+			source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[Y][Xm] |= markVal;
+			// kronMask->data.S32[Y+yOff][Xm+xOff] = source->id;
+		    }
+		} else {
+		    for (int Xm = X; Xm >= 0; Xm--) {
+			source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[Y][Xm] |= markVal;
+			// kronMask->data.S32[Y+yOff][Xm+xOff] = source->id;
+		    }
+		}
+		e -= dXs;
+		int e2 = 2 * e;
+		if (e2 > dYs) {
+		    X ++;
+		    e -= dYs;
+		}
+		if (e2 < -dYs) {
+		    X --;
+		    e += dYs;
+		}
+		if (X >= yMax) break;
+		if (X < 0) break;
+	    }
+	}
+    }
+    return true;
+}
Index: branches/meh_branches/ppstack_test/psphot/src/psphotLoadSRCTEXT.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotLoadSRCTEXT.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotLoadSRCTEXT.c	(revision 33415)
@@ -73,5 +73,5 @@
 	    dPAR[PM_PAR_I0]   = 0.0;
 
-	    pmPSF_AxesToModel (PAR, axes);
+	    pmPSF_AxesToModel (PAR, axes, modelType);
 
 	    float peakFlux    = 1.0;
Index: branches/meh_branches/ppstack_test/psphot/src/psphotMagnitudes.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotMagnitudes.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotMagnitudes.c	(revision 33415)
@@ -4,4 +4,7 @@
 {
     bool status = true;
+
+    fprintf (stdout, "\n");
+    psLogMsg ("psphot", PS_LOG_INFO, "--- psphot Magnitudes ---");
 
     // select the appropriate recipe information
@@ -126,5 +129,5 @@
 
         // wait for the threads to finish and manage results
-        if (!psThreadPoolWait (false)) {
+        if (!psThreadPoolWait (false, true)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
             return false;
@@ -146,5 +149,5 @@
     psFree (cellGroups);
 
-    psLogMsg ("psphot.magnitudes", PS_LOG_DETAIL, "measure magnitudes : %f sec for %ld objects (%d with apertures)\n", psTimerMark ("psphot.mags"), sources->n, Nap);
+    psLogMsg ("psphot.magnitudes", PS_LOG_WARN, "measure magnitudes : %f sec for %ld objects (%d with apertures)\n", psTimerMark ("psphot.mags"), sources->n, Nap);
     return true;
 }
@@ -306,5 +309,5 @@
 
         // wait for the threads to finish and manage results
-        if (!psThreadPoolWait (false)) {
+        if (!psThreadPoolWait (false, true)) {
             psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
             return false;
Index: branches/meh_branches/ppstack_test/psphot/src/psphotMergeSources.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotMergeSources.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotMergeSources.c	(revision 33415)
@@ -520,4 +520,176 @@
 }
 
+// copy the newPeaks from the detections of one pmFPAfile to another
+bool psphotCopyPeaks (pmConfig *config, const pmFPAview *view, const char *ruleOut, const char *ruleSrc)
+{
+    bool status = true;
+
+    int num = psphotFileruleCount(config, ruleSrc);
+
+    // skip the chisq image because it is a duplicate of the detection version
+    int chisqNum = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.CHISQ.NUM");
+    if (!status) chisqNum = -1;
+
+    // loop over the available readouts
+    for (int i = 0; i < num; i++) {
+	if (i == chisqNum) continue; // skip chisq image
+        if (!psphotCopyPeaksReadout (config, view, ruleOut, ruleSrc, i)) {
+            psError (PSPHOT_ERR_CONFIG, false, "failed to copy peaks from %s to %s entry %d", ruleSrc, ruleOut, i);
+            return false;
+        }
+    }
+    return true;
+}
+
+// add newly detected peaks to the existing list of sources
+bool psphotCopyPeaksReadout (pmConfig *config, const pmFPAview *view, const char *ruleOut, const char *ruleSrc, int index) {
+
+    bool status;
+
+    // find the currently selected readout
+    pmFPAfile *fileSrc = pmFPAfileSelectSingle(config->files, ruleSrc, index); // File of interest
+    psAssert (fileSrc, "missing file?");
+
+    pmReadout *readoutSrc = pmFPAviewThisReadout(view, fileSrc->fpa);
+    psAssert (readoutSrc, "missing readout?");
+
+    pmDetections *detections = psMetadataLookupPtr (&status, readoutSrc->analysis, "PSPHOT.DETECTIONS");
+    psAssert (detections, "missing detections?");
+
+    // find the currently selected readout
+    pmFPAfile *fileOut = pmFPAfileSelectSingle(config->files, ruleOut, index); // File of interest
+    psAssert (fileOut, "missing file?");
+
+    pmReadout *readoutOut = pmFPAviewThisReadout(view, fileOut->fpa);
+    psAssert (readoutOut, "missing readout?");
+
+    // generate a new detection structure for the output filerule
+    pmDetections *detectionsOut = psMetadataLookupPtr (&status, readoutOut->analysis, "PSPHOT.DETECTIONS");
+    psAssert (detectionsOut, "missing PSPHOT.DETECTIONS?");
+
+    psAssert (detectionsOut->peaks, "programming error");
+    psAssert (!detectionsOut->oldPeaks, "programming error");
+    psAssert (detections->peaks, "programming error");
+
+    // save the OUT existing peaks on oldPeaks
+    detectionsOut->oldPeaks = detectionsOut->peaks;
+    detectionsOut->peaks = psArrayAllocEmpty(detections->peaks->n);
+
+    for (int i = 0; i < detections->peaks->n; i++) {
+	psAssert (detections->peaks->data[i], "programming error");
+	pmPeak *peak = pmPeakAlloc (0, 0, 0.0, PM_PEAK_LONE);
+	pmPeakCopy(peak, detections->peaks->data[i]);
+	psArrayAdd (detectionsOut->peaks, 100, peak);
+	psFree (peak);
+    }
+    return true;
+}
+
+// create source parents children from ruleSrc for ruleOut for orphans
+bool psphotSourceParents (pmConfig *config, const pmFPAview *view, const char *ruleOut, const char *ruleSrc)
+{
+    bool status = true;
+
+    int num = psphotFileruleCount(config, ruleSrc);
+
+    // skip the chisq image because it is a duplicate of the detection version
+    int chisqNum = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.CHISQ.NUM");
+    if (!status) chisqNum = -1;
+
+    // loop over the available readouts
+    for (int i = 0; i < num; i++) {
+	if (i == chisqNum) continue; // skip chisq image
+        if (!psphotSourceParentsReadout (config, view, ruleOut, ruleSrc, i)) {
+            psError (PSPHOT_ERR_CONFIG, false, "failed to copy sources from %s to %s entry %d", ruleSrc, ruleOut, i);
+            return false;
+        }
+    }
+    return true;
+}
+
+// create source parents from ruleSrc for ruleOut for orphaned children for this readout.  
+bool psphotSourceParentsReadout (pmConfig *config, const pmFPAview *view, const char *ruleOut, const char *ruleSrc, int index) {
+
+    bool status;
+    int nParents = 0;
+    int nNonOrphans = 0;
+
+    // find the currently selected readout
+    pmFPAfile *fileSrc = pmFPAfileSelectSingle(config->files, ruleSrc, index); // File of interest
+    psAssert (fileSrc, "missing file?");
+
+    pmReadout *readoutSrc = pmFPAviewThisReadout(view, fileSrc->fpa);
+    psAssert (readoutSrc, "missing readout?");
+
+    pmDetections *detectionsSrc = psMetadataLookupPtr (&status, readoutSrc->analysis, "PSPHOT.DETECTIONS");
+    psAssert (detectionsSrc, "missing detections?");
+
+    psArray *sourcesSrc = detectionsSrc->allSources;
+    psAssert (sourcesSrc, "missing sources?");
+
+    // find the currently selected readout
+    pmFPAfile *fileOut = pmFPAfileSelectSingle(config->files, ruleOut, index); // File of interest
+    psAssert (fileOut, "missing file?");
+
+    pmReadout *readoutOut = pmFPAviewThisReadout(view, fileOut->fpa);
+    psAssert (readoutOut, "missing readout?");
+
+    // generate a new detection structure for the output filerule
+    pmDetections *detectionsOut = psMetadataLookupPtr (&status, readoutOut->analysis, "PSPHOT.DETECTIONS");
+    psAssert (detectionsOut, "missing PSPHOT.DETECTIONS?");
+
+    // loop over the sources, redefine their pixels to point at the new filerule image,
+    // copy the source data, and add a reference back to the original source
+    
+    // copy the sources from sourceSrcs to the new detection structure
+    for (int i = 0; i < sourcesSrc->n; i++) {
+      pmSource *sourceSrc = sourcesSrc->data[i];
+      if (sourceSrc->parent) {
+	  nNonOrphans ++;
+	  continue; // Not an orphan
+      }
+
+      pmSource *sourceOut = pmSourceCopy(sourceSrc);
+      sourceOut->parent = sourceSrc;
+      
+      // keep the original source flags
+      sourceOut->seq      = sourceSrc->seq;
+      sourceOut->type     = sourceSrc->type;
+      sourceOut->mode     = sourceSrc->mode;
+      sourceOut->mode2    = sourceSrc->mode2;
+      sourceOut->tmpFlags = sourceSrc->tmpFlags;
+
+      // does this copy all model data? (NO)
+      sourceOut->modelPSF = pmModelCopy(sourceSrc->modelPSF);
+      sourceOut->modelEXT = pmModelCopy(sourceSrc->modelEXT);
+
+      if (sourceSrc->modelFits) {
+	  sourceOut->modelFits = psArrayAlloc(sourceSrc->modelFits->n);
+	  for (int j = 0; j < sourceSrc->modelFits->n; j++) {
+	      sourceOut->modelFits->data[j] = pmModelCopy(sourceSrc->modelFits->data[j]);
+	  }
+      }
+
+      // drop the references to the original image pixels:
+      pmSourceFreePixels (sourceOut);
+
+      // allocate image, weight, mask for the new image for each peak
+      if (sourceOut->modelPSF) {
+	pmSourceRedefinePixels (sourceOut, readoutOut, sourceOut->peak->x, sourceOut->peak->y, sourceOut->modelPSF->fitRadius);
+      }
+
+      // child sources have not been subtracted in this image, but this flag may be raised if
+      // they were subtracted in the parent's image
+      sourceOut->tmpFlags &= ~PM_SOURCE_TMPF_SUBTRACTED;
+
+      nParents ++;
+      psArrayAdd (detectionsOut->allSources, 100, sourceOut);
+      psFree (sourceOut);
+    }
+    psLogMsg ("psphot", 3, "%d parents created, %d unorphaned children, %ld input vs %ld output", nParents, nNonOrphans, sourcesSrc->n, detectionsOut->allSources->n);
+
+    return true;
+}
+
 // create source children from ruleSrc for ruleOut
 bool psphotSourceChildren (pmConfig *config, const pmFPAview *view, const char *ruleOut, const char *ruleSrc)
@@ -542,7 +714,7 @@
 }
 
-// create source children from ruleSrc for ruleOut for this entry.  XXX currently, this is only
+// Create source children from ruleSrc for ruleOut for this entry.  Currently, this is only
 // used by psphotStackReadout (sources go on allSources so that psphotChoosePSF can be called
-// repeatedly)
+// repeatedly).
 bool psphotSourceChildrenReadout (pmConfig *config, const pmFPAview *view, const char *ruleOut, const char *ruleSrc, int index) {
 
@@ -569,15 +741,10 @@
     psAssert (readoutOut, "missing readout?");
 
-    // generate a new detection structure for the output filerule
-    pmDetections *detectionsOut = psMetadataLookupPtr (&status, readoutOut->analysis, "PSPHOT.DETECTIONS");
-    if (!detectionsOut) {
-	detectionsOut = pmDetectionsAlloc();
-	detectionsOut->allSources = psArrayAllocEmpty (100);
-	// save detections on the readout->analysis
-	if (!psMetadataAddPtr (readoutOut->analysis, PS_LIST_TAIL, "PSPHOT.DETECTIONS", PS_META_REPLACE | PS_DATA_UNKNOWN, "psphot detections", detectionsOut)) {
-	    psError (PSPHOT_ERR_CONFIG, false, "problem saving detections on readout");
-	    return false;
-	}
-	psFree(detectionsOut); // a copy remains on the analysis metadata
+    // replace any existing DETECTION container on readoutOut->analysis with the new one
+    pmDetections *detectionsOut = pmDetectionsAlloc();
+    detectionsOut->allSources = psArrayAllocEmpty (100);
+    if (!psMetadataAddPtr (readoutOut->analysis, PS_LIST_TAIL, "PSPHOT.DETECTIONS", PS_META_REPLACE | PS_DATA_UNKNOWN, "psphot detections", detectionsOut)) {
+	psError (PSPHOT_ERR_CONFIG, false, "problem saving detections on readout");
+	return false;
     }
 
@@ -593,4 +760,5 @@
       
       // keep the original source flags
+      sourceOut->seq      = sourceSrc->seq;
       sourceOut->type     = sourceSrc->type;
       sourceOut->mode     = sourceSrc->mode;
@@ -612,8 +780,8 @@
       pmSourceFreePixels (sourceOut);
 
+      // XXX do we need to skip the Chisq image sources?
+
       // allocate image, weight, mask for the new image for each peak
-      if (sourceOut->modelPSF) {
-	pmSourceRedefinePixels (sourceOut, readoutOut, sourceOut->peak->x, sourceOut->peak->y, sourceOut->modelPSF->fitRadius);
-      }
+      pmSourceRedefinePixels (sourceOut, readoutOut, sourceOut->peak->x, sourceOut->peak->y, sourceOut->windowRadius);
 
       // child sources have not been subtracted in this image, but this flag may be raised if
@@ -624,13 +792,14 @@
       psFree (sourceOut);
     }
-    psLogMsg ("psphot", 3, "%ld known sources supplied", detectionsOut->allSources->n);
-
-
-    return true;
-}
-
-// create source children associated with 'filerule' from the objectsSrc.  XXX currently, this
-// is only used by psphotStackReadout (sources go on allSources so that psphotChoosePSF can be
-// called repeatedly)
+    psLogMsg ("psphot", 3, "created %ld children", detectionsOut->allSources->n);
+
+    psFree(detectionsOut); // a copy remains on the analysis metadata
+
+    return true;
+}
+
+// create source children associated with 'filerule' from the objectsSrc.  returns a new object
+// array containing the child sources.  XXX currently, this is only used by psphotStackReadout
+// (sources go on allSources so that psphotChoosePSF can be called repeatedly)
 psArray *psphotSourceChildrenByObject (pmConfig *config, const pmFPAview *view, const char *filerule, psArray *objectsSrc) {
 
@@ -652,4 +821,5 @@
 	psAssert (readout, "missing readout?");
 
+	// create DETECTIONS containers for each image, in case one lacks it
 	pmDetections *detections = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.DETECTIONS");
 	if (!detections) {
@@ -665,4 +835,6 @@
 	    psAssert (detections, "missing detections?");
 	}
+
+	// we need to save the new sources on the detection arrays of the appropriate image
 	detArrays->data[i] = psMemIncrRefCounter(detections);
 	readouts->data[i] = psMemIncrRefCounter(readout);
@@ -695,7 +867,8 @@
 
 	    pmSource *sourceOut = pmSourceCopy(sourceSrc);
+	    sourceOut->parent = sourceSrc;
+
+	    // save on the output object array at the same location
 	    objectOut->sources->data[i] = sourceOut;
-
-	    sourceOut->parent = sourceSrc;
 
 	    // keep the original source flags and sequence ID (if set)
@@ -720,5 +893,5 @@
 	    pmSourceFreePixels (sourceOut);
 
-	    // set the output readotu
+	    // set the output readout
 	    int index = sourceOut->imageID;
 	    if (index >= readouts->n) continue; // skip the sources generated by the chisq image
Index: branches/meh_branches/ppstack_test/psphot/src/psphotModelBackground.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotModelBackground.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotModelBackground.c	(revision 33415)
@@ -145,4 +145,5 @@
     psMetadataAddPtr(analysis, PS_LIST_TAIL, "PSPHOT.BACKGROUND.BINNING", PS_DATA_UNKNOWN | PS_META_REPLACE, "Background binning", binning);
 
+    psVector *dQ = psVectorAllocEmpty (100, PS_TYPE_F32);
     psF32 **modelData = model->data.F32;
     psF32 **modelStdevData = modelStdev->data.F32;
@@ -216,4 +217,6 @@
                 }
                 modelStdevData[iy][ix] = psStatsGetValue(stats, statsOptionWidth);
+		// fprintf (stderr, "dQ : %f - %f - %f = %f\n", stats->robustLQ, stats->robustMedian, stats->robustUQ, stats->robustUQ + stats->robustLQ - 2*stats->robustMedian);
+		psVectorAppend (dQ, stats->robustUQ + stats->robustLQ - 2*stats->robustMedian);
 
                 // supply sample to plotting routing
@@ -322,9 +325,13 @@
     }
 
-    psLogMsg ("psphot", PS_LOG_INFO, "built background image: %f sec\n", psTimerMark ("psphot.background"));
-
-    psMetadataAddF32(readout->analysis, PS_LIST_TAIL, "SKY_MEAN", PS_META_REPLACE, "sky mean", Value);
+    psLogMsg ("psphot", PS_LOG_WARN, "built background image: %f sec\n", psTimerMark ("psphot.background"));
+
+    psStats *statsDQ = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
+    psVectorStats (statsDQ, dQ, NULL, NULL, 0);
+
+    psMetadataAddF32(readout->analysis, PS_LIST_TAIL, "SKY_MEAN",  PS_META_REPLACE, "sky mean", Value);
     psMetadataAddF32(readout->analysis, PS_LIST_TAIL, "SKY_STDEV", PS_META_REPLACE, "sky stdev", ValueStdev);
-    psLogMsg ("psphot", PS_LOG_INFO, "image sky : mean %f stdev %f", Value, ValueStdev);
+    psMetadataAddF32(readout->analysis, PS_LIST_TAIL, "SKY_DQ",    PS_META_REPLACE, "sky quartile slope", statsDQ->sampleMedian);
+    psLogMsg ("psphot", PS_LOG_INFO, "image sky : mean %f, stdev %f, dQ %f", Value, ValueStdev, statsDQ->sampleMedian);
 
     // measure image and background stats and save for later output
@@ -334,6 +341,8 @@
                                       PS_STAT_MAX);
     psImageStats (statsBck, model, NULL, 0);
-    psMetadataAddF32 (readout->analysis, PS_LIST_TAIL, "MSKY_MN", PS_META_REPLACE, "sky model mean",          statsBck->sampleMean);
+    psMetadataAddF32 (readout->analysis, PS_LIST_TAIL, "MSKY_MN",  PS_META_REPLACE, "sky model mean",          statsBck->sampleMean);
     psMetadataAddF32 (readout->analysis, PS_LIST_TAIL, "MSKY_SIG", PS_META_REPLACE, "sky model stdev",         statsBck->sampleStdev);
+    psMetadataAddF32 (readout->analysis, PS_LIST_TAIL, "MSKY_DEV", PS_META_REPLACE, "sky stdev",               ValueStdev);
+    psMetadataAddF32 (readout->analysis, PS_LIST_TAIL, "MSKY_DQ",  PS_META_REPLACE, "sky quartile slope",      statsDQ->sampleMedian);
     psMetadataAddF32 (readout->analysis, PS_LIST_TAIL, "MSKY_MAX", PS_META_REPLACE, "sky model maximum value", statsBck->max);
     psMetadataAddF32 (readout->analysis, PS_LIST_TAIL, "MSKY_MIN", PS_META_REPLACE, "sky model minimum value", statsBck->min);
@@ -343,4 +352,7 @@
               statsBck->min, statsBck->sampleMean, statsBck->max, statsBck->sampleStdev);
 
+    psFree(statsDQ);
+    psFree(dQ);
+
     psFree(stats);
     psFree(statsBck);
@@ -401,4 +413,7 @@
     int num = psphotFileruleCount(config, filerule);
 
+    fprintf (stdout, "\n");
+    psLogMsg ("psphot", PS_LOG_INFO, "--- psphot Model Background ---");
+
     // loop over the available readouts
     for (int i = 0; i < num; i++) {
Index: branches/meh_branches/ppstack_test/psphot/src/psphotModelTest.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotModelTest.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotModelTest.c	(revision 33415)
@@ -1,250 +1,36 @@
-# include "psphotInternal.h"
-# define PM_SOURCE_FIT_PSF_X_EXT PM_SOURCE_FIT_PSF_AND_SKY
+# include "psphotStandAlone.h"
 
-// XXX add more test information?
-bool psphotModelTest (pmConfig *config, const pmFPAview *view, const char *filerule, psMetadata *recipe) {
+int main (int argc, char **argv) {
 
-    bool status;
-    int modelType = -1;
-    float obsMag, fitMag, value;
-    char name[64];
-    pmPSF *psf = NULL;
-    pmSourceFitMode fitMode;
+    psMemInit();
+    psTimerStart ("complete");
+    pmErrorRegister();                  // register psModule's error codes/messages
+    psphotInit();
 
-    // bit-masks to test for good/bad pixels
-    psImageMaskType maskVal = psMetadataLookupImageMask(&status, recipe, "MASK.PSPHOT");
-    assert (maskVal);
+    // load command-line arguments, options, and system config data
+    pmConfig *config = psphotModelTestArguments (argc, argv);
+    assert(config);
 
-    // bit-mask to mark pixels not used in analysis
-    psImageMaskType markVal = psMetadataLookupImageMask(&status, recipe, "MARK.PSPHOT");
-    assert (markVal);
+    psphotVersionPrint();
 
-    // maskVal is used to test for rejected pixels, and must include markVal
-    maskVal |= markVal;
-
-    // run model fitting tests on a single source?
-    if (!psMetadataLookupBool (&status, recipe, "TEST_FIT")) return false;
-
-    psTimerStart ("modelTest");
-
-    // find the currently selected readout
-    pmReadout  *readout = pmFPAfileThisReadout (config->files, view, "PSPHOT.INPUT");
-    PS_ASSERT_PTR_NON_NULL (readout, false);
-
-    // use poissonian errors or local-sky errors
-    bool POISSON_ERRORS = psMetadataLookupBool (&status, recipe, filerule);
-    if (!status) POISSON_ERRORS = true;
-    pmSourceFitModelInit (15, 0.1, 1.0, POISSON_ERRORS);
-
-    // find the various fitting parameters (try test values first)
-    float INNER = psMetadataLookupF32 (&status, recipe, "TEST_FIT_INNER_RADIUS");
-    if (!status || !isfinite(INNER)) {
-        INNER = psMetadataLookupF32 (&status, recipe, "SKY_INNER_RADIUS");
-    }
-    float OUTER = psMetadataLookupF32 (&status, recipe, "TEST_FIT_OUTER_RADIUS");
-    if (!status || !isfinite(OUTER)) {
-        OUTER = psMetadataLookupF32 (&status, recipe, "SKY_OUTER_RADIUS");
-    }
-    float RADIUS = psMetadataLookupF32 (&status, recipe, "TEST_FIT_RADIUS");
-    if (!status || !isfinite(RADIUS)) {
-        RADIUS = psMetadataLookupF32 (&status, recipe, "PSF_FIT_RADIUS");
-    }
-    float mRADIUS = psMetadataLookupF32 (&status, recipe, "TEST_MOMENTS_RADIUS");
-    if (!status || !isfinite(mRADIUS)) {
-        mRADIUS = psMetadataLookupF32 (&status, recipe, "PSF_MOMENTS_RADIUS");
+    // load input data (config and images (signal, noise, mask)
+    if (!psphotParseCamera (config)) {
+        psErrorStackPrint(stderr, "Error setting up the camera\n");
+        exit (psphotGetExitStatus());
     }
 
-    // define the source of interest
-    float xObj     = psMetadataLookupF32 (&status, recipe, "TEST_FIT_X");
-    float yObj     = psMetadataLookupF32 (&status, recipe, "TEST_FIT_Y");
-    if (!isfinite(xObj) || !isfinite(yObj)) psAbort ("object position is not defined");
-
-    // what fitting mode to use?
-    fitMode = PM_SOURCE_FIT_EXT;
-    char *fitModeWord = psMetadataLookupStr (&status, recipe, "TEST_FIT_MODE");
-    if (fitModeWord && !strcasecmp (fitModeWord, "PSF")) fitMode = PM_SOURCE_FIT_PSF;
-    if (fitModeWord && !strcasecmp (fitModeWord, "CONV")) fitMode = PM_SOURCE_FIT_PSF_X_EXT;
-    if (fitModeWord && !strcasecmp (fitModeWord, "DEFAULT")) fitMode = PM_SOURCE_FIT_EXT;
-
-    // construct the source structures
-    pmSource *source = pmSourceAlloc();
-    source->peak = pmPeakAlloc (xObj, yObj, 0, 0);
-    pmSourceDefinePixels (source, readout, xObj, yObj, OUTER);
-
-    // in fitMode, psf sets the model type
-    if (fitMode == PM_SOURCE_FIT_PSF) {
-        psf = psphotLoadPSF (config, view, recipe);
-        if (!psf) psAbort("PSF_INPUT_FILE not supplied");
-        modelType = psf->type;
-        source->type = PM_SOURCE_TYPE_STAR;
-    }
-    if (fitMode == PM_SOURCE_FIT_EXT) {
-        // find the model: supplied by user or first in the PSF_MODEL list
-        char *modelName  = psMetadataLookupStr (&status, recipe, "TEST_FIT_MODEL");
-        if (!status || !strcasecmp (modelName, "DEFAULT")) {
-            // get the list pointers for the PSF_MODEL entries
-
-            psList *list = NULL;
-            psMetadataItem *mdi = psMetadataLookup (recipe, "PSF_MODEL");
-            if (mdi == NULL) psAbort("missing PSF_MODEL selection");
-            if (mdi->type == PS_DATA_STRING) {
-                list = psListAlloc(NULL);
-                psListAdd (list, PS_LIST_HEAD, mdi);
-            } else {
-                if (mdi->type != PS_DATA_METADATA_MULTI) psAbort("missing PSF_MODEL selection");
-                list = psMemIncrRefCounter(mdi->data.list);
-            }
-
-            // take the first list element
-            psMetadataItem *item = psListGet (list, PS_LIST_HEAD);
-            modelName = item->data.V;
-        }
-        modelType = pmModelClassGetType (modelName);
-        if (modelType < 0) psAbort("unknown model %s", modelName);
-        source->type = PM_SOURCE_TYPE_EXTENDED;
-    }
-    if (fitMode == PM_SOURCE_FIT_PSF_X_EXT) {
-        // we need to load BOTH a psf and an ext model
-        psf = psphotLoadPSF (config, view, recipe);
-        if (!psf) psAbort("PSF_INPUT_FILE not supplied");
-
-        // find the model: supplied by user or first in the PSF_MODEL list
-        char *modelName  = psMetadataLookupStr (&status, recipe, "TEST_FIT_MODEL");
-        if (!status || !strcasecmp (modelName, "DEFAULT")) {
-            // get the list pointers for the PSF_MODEL entries
-
-            psList *list = NULL;
-            psMetadataItem *mdi = psMetadataLookup (recipe, "PSF_MODEL");
-            if (mdi == NULL) psAbort("missing PSF_MODEL selection");
-            if (mdi->type == PS_DATA_STRING) {
-                list = psListAlloc(NULL);
-                psListAdd (list, PS_LIST_HEAD, mdi);
-            } else {
-                if (mdi->type != PS_DATA_METADATA_MULTI) psAbort("missing PSF_MODEL selection");
-                list = psMemIncrRefCounter(mdi->data.list);
-            }
-
-            // take the first list element
-            psMetadataItem *item = psListGet (list, PS_LIST_HEAD);
-            modelName = item->data.V;
-        }
-        modelType = pmModelClassGetType (modelName);
-        if (modelType < 0) psAbort("unknown model %s", modelName);
-        source->type = PM_SOURCE_TYPE_EXTENDED;
+    // call psphot for each readout
+    if (!psphotImageLoop (config, PSPHOT_MODEL_TEST)) {
+        psErrorStackPrint(stderr, "Error in the psphot image loop\n");
+        exit (psphotGetExitStatus());
     }
 
-    // find the local sky
-    status = pmSourceLocalSky (source, PS_STAT_SAMPLE_MEDIAN, INNER, maskVal, markVal);
-    if (!status) psAbort("pmSourceLocalSky error");
+    psLogMsg ("psphot", 3, "complete psphot run: %f sec\n", psTimerMark ("complete"));
 
-    // get the source moments
-    status = pmSourceMoments (source, mRADIUS, 0.0, 1.0, maskVal);
-    if (!status) psAbort("psSourceMoments error");
-    source->peak->value = source->moments->Peak;
+    psErrorCode exit_status = psphotGetExitStatus();
+    psphotCleanup (config);
+    exit (exit_status);
+}
 
-    fprintf (stderr, "sum: %f @ (%f, %f)\n", source->moments->Sum, source->moments->Mx, source->moments->My);
-    fprintf (stderr, "moments: %f, %f - %f\n", source->moments->Mxx, source->moments->Myy, source->moments->Mxy);
-
-    psEllipseMoments moments;
-    moments.x2 = source->moments->Mxx;
-    moments.y2 = source->moments->Myy;
-    moments.xy = source->moments->Mxy;
-    psEllipseAxes axes = psEllipseMomentsToAxes (moments, 20.0);
-
-    fprintf (stderr, "axes: %f @ (%f, %f)\n", axes.theta*180/M_PI, axes.major, axes.minor);
-
-    // get the initial model parameter guess
-    pmModel *model = pmSourceModelGuess (source, modelType);
-    source->modelEXT = model;
-
-    // if any parameters are defined by the user, take those values
-    int nParams = pmModelClassParameterCount (modelType);
-    psF32 *params = model->params->data.F32;
-    params[PM_PAR_XPOS] = xObj; // XXX use the user-supplied value,
-    params[PM_PAR_YPOS] = yObj; // XXX or use the centroid
-    for (int i = 0; i < nParams; i++) {
-        if (i == PM_PAR_XPOS) continue;
-        if (i == PM_PAR_YPOS) continue;
-
-        sprintf (name, "TEST_FIT_PAR%d", i);
-        value = psMetadataLookupF32 (&status, recipe, name);
-        if (status && isfinite (value)) {
-            params[i] = value;
-        }
-    }
-
-    float area = params[4]*params[5];
-    fprintf (stderr, "peak: %f @ (%f, %f)\n", source->moments->Sum*area, (double)source->peak->x, (double)source->peak->y);
-
-    // for PSF fitting, set the shape parameters based on the PSF & source position
-    if (fitMode == PM_SOURCE_FIT_PSF) {
-        source->modelPSF = pmModelFromPSF (model, psf);
-        psFree (model);
-        model = source->modelPSF;
-        params = model->params->data.F32;
-    }
-
-    // list model input shape
-    psEllipseShape shape;
-    shape.sx  = 1.4 / model->params->data.F32[4];
-    shape.sy  = 1.4 / model->params->data.F32[5];
-    shape.sxy = model->params->data.F32[6];
-    axes = psEllipseShapeToAxes (shape, 20.0);
-
-    fprintf (stderr, "guess: %f @ (%f, %f)\n", axes.theta*180/M_PI, axes.major, axes.minor);
-
-    fprintf (stderr, "input parameters: \n");
-    for (int i = 0; i < nParams; i++) {
-        fprintf (stderr, "%d : %f\n", i, params[i]);
-    }
-
-    // define the pixels used for the fit
-    psImageKeepCircle (source->maskObj, xObj, yObj, RADIUS, "OR", markVal);
-    psphotSaveImage (NULL, source->maskObj, "mask1.fits");
-
-    char *fitset = psMetadataLookupStr (&status, recipe, "TEST_FIT_SET");
-    if (status) {
-        status = psphotFitSet (source, model, fitset, fitMode, maskVal);
-        exit (0);
-    }
-
-    if (fitMode == PM_SOURCE_FIT_PSF_X_EXT) {
-        // build the psf for the object
-        source->modelPSF = pmModelFromPSF (model, psf);
-        source->modelEXT = model;
-
-	// what fraction of the PSF is used? (radius in pixels : 2 -> 5x5 box)
-	int psfSize = psMetadataLookupS32 (&status, recipe, "PCM_BOX_SIZE");
-	assert (status);
-
-        model = psphotPSFConvModel (readout, source, modelType, maskVal, markVal, psfSize);
-        params = model->params->data.F32;
-    } else {
-        status = pmSourceFitModel (source, model, fitMode, maskVal);
-    }
-
-    // measure the source mags
-    pmSourcePhotometryModel (&fitMag, model);
-    pmSourcePhotometryAper  (NULL, &obsMag, NULL, NULL, model, source->pixels, source->variance, source->maskObj, maskVal);
-    fprintf (stderr, "ap: %f, fit: %f, apmifit: %f, nIter: %d\n", obsMag, fitMag, obsMag - fitMag, model->nIter);
-
-    // write out positive object
-    psphotSaveImage (NULL, source->pixels, "object.fits");
-
-    // subtract object, leave local sky
-    // pmModelSub (source->pixels, source->maskObj, model, PM_MODEL_OP_FULL, maskVal);
-    pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
-
-    fprintf (stderr, "output parameters: \n");
-    for (int i = 0; i < nParams; i++) {
-        fprintf (stderr, "%d : %f\n", i, params[i]);
-    }
-
-    // write out
-    psphotSaveImage (NULL, source->pixels, "resid.fits");
-    psphotSaveImage (NULL, source->maskObj, "mask.fits");
-
-    psLogMsg ("psphot", PS_LOG_INFO, "model test : %f sec\n", psTimerMark ("modelTest"));
-
-    exit (0);
-}
+// all functions which return to this level must raise one of the top-level error codes if they
+// exit with an error.  these error codes are used to specify the program exit status
Index: branches/meh_branches/ppstack_test/psphot/src/psphotModelTestArguments.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotModelTestArguments.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotModelTestArguments.c	(revision 33415)
@@ -0,0 +1,189 @@
+# include "psphotStandAlone.h"
+
+static void writeHelpInfo(const char* program, pmConfig* config, FILE* ofile)
+{
+  fprintf(ofile,
+	  "Usage: one of the following\n"
+	  "%s -file fname1[,fname2,...] -mask maskfile1[,maskfile2,...]\n"
+	  "     -variance varfile1[,varfile2,...] OutFileBaseName\n"
+	  "\n"
+	  "%s -list FileNameList [-masklist MaskFileNameList] \n"
+	  "     -variancelist VarFileNameList OutFileBaseName\n"
+	  "\n"
+	  "%s -help\n"
+	  "\n"
+	  "%s -version\n"
+	  "\n"
+	  "where:\n"
+	  "  FileNameList is a text file containing filenames, one per line\n"
+	  "  MaskFileNameList is a text file of mask filenames, one per line\n"
+	  "  VarFileNameList is a text file of variance filenames, one per line\n"
+	  "  OutFileBaseName is the 'root name' for output files\n"
+	  "\n"
+	  "additional options:\n"
+	  "  -psf PsfFile1[,PsfFile2,...] or -psflist PsfFileNameList\n"
+	  "     specify PSF rather than letting %s estimate it\n"
+	  "  -src SrcFile1[,SrcFile2,...] or -srclist SrcFileNameList\n"
+	  "     specify additional sources for PSF generation\n"
+	  "  -chip nn[,nn,...]\n"
+	  "     select detector chips to process; default is all.\n"
+	  "     Indices correspond to zero-based offset in the FPA metadata table.\n"
+	  "  -photcode PhotoCodeName\n"
+	  "     specify photocode\n"
+	  "  -region RegionString\n"
+	  "     specify analysis region.  String is of form '[x0:x1,y0:y1]'\n"
+	  "     To use this option you must define a default in psphot.config\n"
+	  "  -visual\n"
+	  "     turns on interactive display mode\n"
+	  "  -dumpconfig CfgFileName\n"
+          "     causes config info to be dumped to the named file.\n"
+	  "  -break NOTHING|BACKMDL|PEAKS|MOMENTS|PSFMODEL|ENSEMBLE|PASS1\n"
+	  "     choose a point at which to exit processing early\n"
+	  "  -nthreads n\n"
+	  "     set number of parallel threads of execution\n"
+	  "  -F OldFileRule ReplacementFileRule\n"
+	  "     change file naming rule; e.g. '-F PSPHOT.OUTPUT PSPHOT.OUT.CMF.MEF'\n"
+	  "  -D name stringval\n"
+	  "     set a string-valued config parameter\n"
+	  "  -Di name intval\n"
+	  "     set an integer-valued config parameter\n"
+	  "  -Df name fval\n"
+	  "     set a float-valued config parameter\n"
+	  "  -Db name boolval\n"
+	  "     set a boolean-valued config parameter\n"
+	  "  -v, -vv, -vvv\n"
+	  "     set increasing levels of verbosity\n"
+	  "  -logfmt FormatString\n"
+	  "     set format string used for log messages\n"
+	  "  -trace Fac Lvl\n"
+	  "     set tracing for facility Fac to integer Lvl, e.g. '-trace err 10'\n"
+	  "  -trace-levels\n"
+	  "     print current trace levels\n",
+	  program,program,program,program,program);
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+    exit(PS_EXIT_SUCCESS);
+}
+
+static void usage(const char *program,  // Name of the program
+                  psMetadata *arguments, // Command-line arguments
+                  pmConfig *config,      // Configuration
+		  int exitCode
+		  ) 
+{
+  fprintf(stderr,
+	  "Usage: one of the following\n"
+	  "%s -file fname1[,fname2,...] -mask maskfile1[,maskfile2,...]\n"
+	  "     -variance varfile1[,varfile2,...] OutFileBaseName\n"
+	  "\n"
+	  "%s -list FileNameList [-masklist MaskFileNameList] \n"
+	  "     -variancelist VarFileNameList OutFileBaseName\n"
+	  "\n"
+	  "Try '%s -help' for more options and explanation\n",
+	  program,program,program);
+    if (exitCode != PS_EXIT_SUCCESS)
+      psErrorStackPrint(stderr, "Error reading arguments\n");
+    psFree(config);
+    pmConfigDone();
+    psLibFinalize();
+    exit(exitCode);
+}
+
+pmConfig *psphotModelTestArguments(int argc, char **argv) {
+
+    int N;
+    bool status;
+
+    // load config data from default locations
+    pmConfig *config = pmConfigRead(&argc, argv, PSPHOT_RECIPE);
+    if (config == NULL) {
+      psErrorStackPrint(stderr, "Can't read site configuration");
+	exit(PS_EXIT_CONFIG_ERROR);
+    }
+
+    PS_ARGUMENTS_GENERIC( psphot, config, argc, argv );
+
+    // save the following additional recipe values based on command-line options
+    // these options override the PSPHOT recipe values loaded from recipe files
+    psMetadata *options = pmConfigRecipeOptions (config, PSPHOT_RECIPE);
+
+    // Number of threads is handled
+    PS_ARGUMENTS_THREADS( psphot, config, argc, argv )
+
+    // visual : interactive display mode
+    if ((N = psArgumentGet (argc, argv, "-visual"))) {
+        psArgumentRemove (N, &argc, argv);
+        pmVisualSetVisual(true);
+    }
+
+    // break : used from recipe throughout psphotReadout
+    if ((N = psArgumentGet (argc, argv, "-break"))) {
+	if (argc<=N+1) {
+	  psErrorStackPrint(stderr, "Expected to see 1 more argument; saw %d", argc - 1);
+	  exit(PS_EXIT_CONFIG_ERROR);
+	}
+        psArgumentRemove (N, &argc, argv);
+        psMetadataAddStr (options, PS_LIST_TAIL, "BREAK_POINT", PS_META_REPLACE, "", argv[N]);
+        psArgumentRemove (N, &argc, argv);
+    }
+
+    // analysis region : overrides recipe value, used in psphotReadout/psphotEnsemblePSF
+    if ((N = psArgumentGet (argc, argv, "-region"))) {
+	if (argc<=N+1) {
+	  psErrorStackPrint(stderr, "Expected to see 1 more argument; saw %d", argc - 1);
+	  exit(PS_EXIT_CONFIG_ERROR);
+	}
+        psArgumentRemove (N, &argc, argv);
+        psMetadataAddStr (options, PS_LIST_TAIL, "ANALYSIS_REGION", 0, "", argv[N]);
+        psArgumentRemove (N, &argc, argv);
+    }
+
+    // chip selection is used to limit chips to be processed
+    if ((N = psArgumentGet (argc, argv, "-chip"))) {
+	if (argc<=N+1) {
+	  psErrorStackPrint(stderr, "Expected to see 1 more argument; saw %d", argc - 1);
+	  exit(PS_EXIT_CONFIG_ERROR);
+	}
+        psArgumentRemove (N, &argc, argv);
+        psMetadataAddStr (config->arguments, PS_LIST_TAIL, "CHIP_SELECTIONS", PS_DATA_STRING, "", argv[N]);
+        psArgumentRemove (N, &argc, argv);
+    }
+
+    // if these command-line options are supplied, load the file name lists into config->arguments
+    // override any configuration-specified source for these files
+    //
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "MASK",       "-mask",     "-masklist");
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "VARIANCE",   "-variance", "-variancelist");
+
+    // an input list of sources to use is allowed, but not required
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "SRC", "-src", "-srclist");
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "SRCTEXT", "-srctext", "-srctextlist");
+
+    if (argc == 1) {
+        psError(PSPHOT_ERR_ARGUMENTS, true, "Too few arguments: %d", argc);
+	usage(argv[0], config->arguments, config, PS_EXIT_CONFIG_ERROR);
+    }
+
+    if (psArgumentGet(argc, argv, "-help") ||
+	psArgumentGet(argc, argv, "-h"))
+      writeHelpInfo(argv[0], config, stdout);
+      
+    // the input file is a required argument; if not found, we will exit
+    status = pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT", "-file", "-list");
+    if (!status) {
+        psError(PSPHOT_ERR_ARGUMENTS, false, "pmConfigFileSetsMD failed to parse arguments");
+	usage(argv[0], config->arguments, config, PS_EXIT_CONFIG_ERROR);
+    }
+
+    if (argc != 2) {
+        psError(PSPHOT_ERR_ARGUMENTS, true, "Expected to see one more argument; saw %d", argc - 1);
+	usage(argv[0], config->arguments, config, PS_EXIT_CONFIG_ERROR);
+    }
+
+    // output position is fixed
+    psMetadataAddStr (config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "", argv[1]);
+
+    psTrace("psphot", 1, "Done with psphotModelTestArguments...\n");
+    return (config);
+}
Index: branches/meh_branches/ppstack_test/psphot/src/psphotModelTestReadout.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotModelTestReadout.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotModelTestReadout.c	(revision 33415)
@@ -0,0 +1,221 @@
+# include "psphotInternal.h"
+# define PM_SOURCE_FIT_PSF_X_EXT PM_SOURCE_FIT_PSF_AND_SKY
+
+bool psphotModelTestReadout (pmConfig *config, const pmFPAview *view, const char *filerule) {
+
+    bool status;
+
+    pmModelClassSetLimits(PM_MODEL_LIMITS_LAX);
+
+    // select the current recipe
+    psMetadata *recipe = psMetadataLookupPtr (NULL, config->recipes, PSPHOT_RECIPE);
+    if (!recipe) {
+        psError(PSPHOT_ERR_CONFIG, false, "missing recipe %s", PSPHOT_RECIPE);
+        return false;
+    }
+
+    // remove cruft from the input analysis structure
+    if (!psphotCleanInputs (config, view, filerule)) {
+        psError (PSPHOT_ERR_PROG, false, "trouble setting up the inputs");
+        return false;
+    }
+
+    // set the photcode for this image
+    if (!psphotAddPhotcode (config, view, filerule)) {
+        psError (PSPHOT_ERR_CONFIG, false, "trouble defining the photcode");
+        return false;
+    }
+
+    // Generate the mask and weight images, including the user-defined analysis region of interest
+    if (!psphotSetMaskAndVariance (config, view, filerule)) {
+        return psphotReadoutCleanup(config, view, filerule);
+    }
+
+    // generate a background model (median, smoothed image)
+    if (!psphotModelBackground (config, view, filerule)) {
+        return psphotReadoutCleanup (config, view, filerule);
+    }
+
+    if (!psphotSubtractBackground (config, view, filerule)) {
+        return psphotReadoutCleanup (config, view, filerule);
+    }
+
+    float MIN_KRON_RADIUS = 5.0;
+
+    // bit-masks to test for good/bad pixels
+    psImageMaskType maskVal = psMetadataLookupImageMask(&status, recipe, "MASK.PSPHOT");
+    assert (maskVal);
+
+    // bit-mask to mark pixels not used in analysis
+    psImageMaskType markVal = psMetadataLookupImageMask(&status, recipe, "MARK.PSPHOT");
+    assert (markVal);
+
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    // find the currently selected readout
+    pmFPAfile *file = pmFPAfileSelectSingle(config->files, filerule, 0); // File of interest
+    psAssert (file, "missing file?");
+
+    pmReadout *readout = pmFPAviewThisReadout(view, file->fpa);
+    psAssert (readout, "missing readout?");
+
+    // use poissonian errors or local-sky errors
+    bool POISSON_ERRORS = psMetadataLookupBool (&status, recipe, filerule);
+    if (!status) POISSON_ERRORS = true;
+
+    // find the various fitting parameters (try test values first)
+    float INNER = psMetadataLookupF32 (&status, recipe, "TEST_FIT_INNER_RADIUS");
+    if (!status || !isfinite(INNER)) {
+        INNER = psMetadataLookupF32 (&status, recipe, "SKY_INNER_RADIUS");
+    }
+    float OUTER = psMetadataLookupF32 (&status, recipe, "TEST_FIT_OUTER_RADIUS");
+    if (!status || !isfinite(OUTER)) {
+        OUTER = psMetadataLookupF32 (&status, recipe, "SKY_OUTER_RADIUS");
+    }
+    float RADIUS = psMetadataLookupF32 (&status, recipe, "TEST_FIT_RADIUS");
+    if (!status || !isfinite(RADIUS)) {
+        RADIUS = psMetadataLookupF32 (&status, recipe, "PSF_FIT_RADIUS");
+    }
+    float mRADIUS = psMetadataLookupF32 (&status, recipe, "TEST_MOMENTS_RADIUS");
+    if (!status || !isfinite(mRADIUS)) {
+        mRADIUS = psMetadataLookupF32 (&status, recipe, "PSF_MOMENTS_RADIUS");
+    }
+
+    // define the source of interest
+   float xObj     = psMetadataLookupF32 (&status, recipe, "TEST_FIT_X");
+    float yObj     = psMetadataLookupF32 (&status, recipe, "TEST_FIT_Y");
+    if (!isfinite(xObj) || !isfinite(yObj)) psAbort ("object position is not defined");
+
+    // construct the source structures
+    pmSource *source = pmSourceAlloc();
+    source->peak = pmPeakAlloc (xObj, yObj, 0, 0);
+    pmSourceDefinePixels (source, readout, xObj, yObj, OUTER);
+
+    // find the model: supplied by user or first in the PSF_MODEL list
+    char *modelName  = psMetadataLookupStr (&status, recipe, "TEST_FIT_MODEL");
+    int modelType = pmModelClassGetType (modelName);
+    if (modelType < 0) psAbort("unknown model %s", modelName);
+    source->type = PM_SOURCE_TYPE_EXTENDED;
+
+    // find the local sky
+    status = pmSourceLocalSky (source, PS_STAT_SAMPLE_MEDIAN, INNER, maskVal, markVal);
+    if (!status) psAbort("pmSourceLocalSky error");
+
+    { 
+	// XXX I want to test an iterative aperture for brighter sources
+	float radius = mRADIUS;
+	for (int i = 0; i < 10; i++) {
+
+	    // get the source moments
+	    status = pmSourceMoments (source, radius, 0.0, 0.0, MIN_KRON_RADIUS, maskVal);
+	    if (!status) psAbort("psSourceMoments error");
+
+	    float oldRadius = radius;
+	    radius = source->moments->Mrf * RADIUS;
+	    
+	    fprintf (stderr, "%d %f  %f  %f\n", i, oldRadius, radius, source->moments->Mrf);
+	}
+	exit (0);
+    }
+
+    // get the source moments
+    status = pmSourceMoments (source, mRADIUS, 0.0, 0.0, MIN_KRON_RADIUS, maskVal);
+    if (!status) psAbort("psSourceMoments error");
+
+    fprintf (stderr, "sum: %f @ (%f, %f)\n", source->moments->Sum, source->moments->Mx, source->moments->My);
+    fprintf (stderr, "moments: %f, %f - %f\n", source->moments->Mxx, source->moments->Myy, source->moments->Mxy);
+
+    psEllipseMoments moments;
+    moments.x2 = source->moments->Mxx;
+    moments.y2 = source->moments->Myy;
+    moments.xy = source->moments->Mxy;
+    psEllipseAxes axes = psEllipseMomentsToAxes (moments, 20.0);
+
+    fprintf (stderr, "axes: %f @ (%f, %f)\n", axes.theta*180/M_PI, axes.major, axes.minor);
+
+    // get the initial model parameter guess
+    pmModel *model = pmSourceModelGuess (source, modelType);
+    source->modelEXT = model;
+
+    // if any parameters are defined by the user, take those values
+    int nParams = pmModelClassParameterCount (modelType);
+    psF32 *params = model->params->data.F32;
+    params[PM_PAR_XPOS] = xObj; // XXX use the user-supplied value,
+    params[PM_PAR_YPOS] = yObj; // XXX or use the centroid
+    for (int i = 0; i < nParams; i++) {
+        if (i == PM_PAR_XPOS) continue;
+        if (i == PM_PAR_YPOS) continue;
+
+	char name[32];
+        sprintf (name, "TEST_FIT_PAR%d", i);
+        float value = psMetadataLookupF32 (&status, recipe, name);
+        if (status && isfinite (value)) {
+            params[i] = value;
+        }
+    }
+
+    float area = params[4]*params[5];
+    fprintf (stderr, "peak: %f @ (%f, %f)\n", source->moments->Sum*area, (double)source->peak->x, (double)source->peak->y);
+
+    // list model input shape
+    psEllipseShape shape;
+    shape.sx  = 1.4 / model->params->data.F32[4];
+    shape.sy  = 1.4 / model->params->data.F32[5];
+    shape.sxy = model->params->data.F32[6];
+    axes = psEllipseShapeToAxes (shape, 20.0);
+
+    fprintf (stderr, "guess: %f @ (%f, %f)\n", axes.theta*180/M_PI, axes.major, axes.minor);
+
+    fprintf (stderr, "input parameters: \n");
+    for (int i = 0; i < nParams; i++) {
+        fprintf (stderr, "%d : %f\n", i, params[i]);
+    }
+
+    // define the pixels used for the fit
+    psImageKeepCircle (source->maskObj, xObj, yObj, RADIUS, "OR", markVal);
+    psphotSaveImage (NULL, source->maskObj, "mask1.fits");
+
+    float SKY_SIG = psMetadataLookupF32(&status, readout->analysis, "SKY_STDEV");
+
+    // options which modify the behavior of the model fitting
+    pmSourceFitOptions *fitOptions = pmSourceFitOptionsAlloc();
+    fitOptions->nIter         = psMetadataLookupS32(&status, recipe, "PSF_FIT_ITER"); // Maximum number of fit iterations
+    fitOptions->minTol        = psMetadataLookupF32 (&status, recipe, "PSF_FIT_MIN_TOL"); // Fit tolerance
+    fitOptions->maxTol        = psMetadataLookupF32 (&status, recipe, "PSF_FIT_MAX_TOL"); // Fit tolerance
+    fitOptions->maxChisqDOF   = psMetadataLookupF32 (&status, recipe, "PSF_FIT_MAX_CHISQ"); // Fit tolerance
+    fitOptions->poissonErrors = POISSON_ERRORS;
+    fitOptions->weight        = PS_SQR(SKY_SIG);
+    fitOptions->mode          = PM_SOURCE_FIT_EXT;
+    fitOptions->covarFactor   = psImageCovarianceFactorForAperture(readout->covariance, 10.0); // Covariance matrix
+
+    status = pmSourceFitModel (source, model, fitOptions, maskVal);
+
+    // measure the source mags
+    float fitMag = NAN;
+    float fitFlux = NAN;
+    float obsMag = NAN;
+    pmSourcePhotometryModel (&fitMag, &fitFlux, model);
+    pmSourcePhotometryAper  (NULL, &obsMag, NULL, NULL, model, source->pixels, source->variance, source->maskObj, maskVal);
+    fprintf (stderr, "ap: %f, fit: %f, apmifit: %f, nIter: %d\n", obsMag, fitMag, obsMag - fitMag, model->nIter);
+
+    // write out positive object
+    psphotSaveImage (NULL, source->pixels, "object.fits");
+
+    // subtract object, leave local sky
+    // pmModelSub (source->pixels, source->maskObj, model, PM_MODEL_OP_FULL, maskVal);
+    pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
+
+    fprintf (stderr, "output parameters: \n");
+    for (int i = 0; i < nParams; i++) {
+        fprintf (stderr, "%d : %f\n", i, params[i]);
+    }
+
+    // write out
+    psphotSaveImage (NULL, source->pixels, "resid.fits");
+    psphotSaveImage (NULL, source->maskObj, "mask.fits");
+
+    psLogMsg ("psphot", PS_LOG_INFO, "model test : %f sec\n", psTimerMark ("modelTest"));
+
+    exit (0);
+}
Index: branches/meh_branches/ppstack_test/psphot/src/psphotOutput.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotOutput.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotOutput.c	(revision 33415)
@@ -325,4 +325,7 @@
     psMetadataItemSupplement (&status, header, analysis, "MSKY_NY");
 
+    psMetadataItemSupplement (&status, header, analysis, "MSKY_DEV");
+    psMetadataItemSupplement (&status, header, analysis, "MSKY_DQ");
+
     psMetadataItemSupplement (&status, header, analysis, "DETEFF.MAGREF");
 
@@ -404,4 +407,71 @@
     return true;
 }
+
+// for now, let's store the detections on the readout->analysis for each readout
+bool psphotDumpTest (pmConfig *config, const pmFPAview *view, const char *filerule)
+{
+    static int npass = 0;
+    char filename[64];
+
+    // XXX dump tests are disabled unless this is commented out:
+    return true;
+
+    bool status = true;
+
+    int num = psphotFileruleCount(config, filerule);
+
+    snprintf (filename, 64, "testdump.%02d.dat", npass);
+    FILE *f = fopen (filename, "w");
+
+    // loop over the available readouts
+    for (int i = 0; i < num; i++) {
+
+        // find the currently selected readout
+        pmFPAfile *file = pmFPAfileSelectSingle(config->files, filerule, i); // File of interest
+        psAssert (file, "missing file?");
+
+        pmReadout *readout = pmFPAviewThisReadout(view, file->fpa);
+        psAssert (readout, "missing readout?");
+
+        pmDetections *detections = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.DETECTIONS");
+        psAssert (detections, "missing detections?");
+
+        psArray *sources = detections->newSources ? detections->newSources : detections->allSources;
+        psAssert (sources, "missing sources?");
+
+	if (detections->newSources) {
+	    fprintf (f, "## --- from new sources ---\n");
+	} else {
+	    fprintf (f, "## --- from all sources ---\n");
+	}
+
+	for (int i = 0; i < sources->n; i++) {
+	    pmSource *source = sources->data[i];
+	    if (!source) continue;
+
+	    pmPeak *peak = source->peak;
+	    if (!peak) continue;
+
+	    // XXX only dump a given region
+	    // if (peak->xf < 20) continue;
+	    // if (peak->yf < 20) continue;
+	    // if (peak->xf > 40) continue;
+	    // if (peak->yf > 70) continue;
+
+	    float Msum = source->moments ? source->moments->Sum : NAN;
+	    float Mx   = source->moments ? source->moments->Mx : NAN;
+	    float My   = source->moments ? source->moments->My : NAN;
+	    // float Npix = source->moments ? source->moments->nPixels : NAN;
+	    float Io = source->modelPSF ? source->modelPSF->params->data.F32[PM_PAR_I0] : NAN;
+	    fprintf (f, "%d %f %f  : %f %f : %f %f\n", source->imageID, peak->xf, peak->yf, Mx, My, Msum, Io);
+	}
+    }
+    fclose (f);
+    npass ++;
+
+    return true;
+}
+
+# if (0)
 bool psphotDumpTest (pmConfig *config, const pmFPAview *view, const char *filerule, char *filename) {
 
@@ -446,3 +516,3 @@
     return true;
 }
-
+# endif
Index: branches/meh_branches/ppstack_test/psphot/src/psphotPSFConvModel.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotPSFConvModel.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotPSFConvModel.c	(revision 33415)
@@ -43,5 +43,5 @@
     // adjust the pixels based on the footprint
     float radius = psphotSetRadiusEXT (readout, source, markVal);
-    if (!pmSourceMoments (source, radius, 0.0, 0.0, maskVal)) return false;
+    if (!pmSourceMoments (source, radius, 0.25*radius, 0.0, maskVal)) return false;
 
     // XXX test : modify the Io, SXX, SYY terms based on the psf SXX, SYY terms:
Index: branches/meh_branches/ppstack_test/psphot/src/psphotPetrosianProfile.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotPetrosianProfile.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotPetrosianProfile.c	(revision 33415)
@@ -38,5 +38,5 @@
     // convert the isophotal radius vs angle measurements to an elliptical contour
     if (!psphotEllipticalContour (source, petrosian)) {
-	psLogMsg ("psphot", 3, "failed to measure elliptical contour");
+	// psLogMsg ("psphot", 3, "failed to measure elliptical contour");
 	psFree (petrosian);
 	return false;
Index: branches/meh_branches/ppstack_test/psphot/src/psphotPetrosianStats.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotPetrosianStats.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotPetrosianStats.c	(revision 33415)
@@ -143,15 +143,8 @@
     // if we failed to reach the PETROSIAN_RATIO, use the lowest significant ratio instead (flag this!)
     if (!anyPetro) {
-	// interpolate Rvec between i-1 and i to PETROSIAN_RATIO to get flux (Fvec) and radius (rvec)
-	if (lowestSignificantRadius == 0) { 
-	    // assume Fmax @ R = 0.0
-	    petRadius    = InterpolateValues     (1.0, 0.0, petRatio->data.F32[lowestSignificantRadius], refRadius->data.F32[lowestSignificantRadius], PETROSIAN_RATIO);
-	    petRadiusErr = InterpolateValuesErrX (1.0, 0.0, petRatio->data.F32[lowestSignificantRadius], refRadius->data.F32[lowestSignificantRadius], PETROSIAN_RATIO, 0.0, petRatioErr->data.F32[lowestSignificantRadius]);
-
-	} else {
-	    int n0 = lowestSignificantRadius-1;
-	    int n1 = lowestSignificantRadius;
-	    petRadius    = InterpolateValues     (petRatio->data.F32[n0], refRadius->data.F32[n0], petRatio->data.F32[n1], refRadius->data.F32[n1], PETROSIAN_RATIO);
-	    petRadiusErr = InterpolateValuesErrX (petRatio->data.F32[n0], refRadius->data.F32[n0], petRatio->data.F32[n1], refRadius->data.F32[n1], PETROSIAN_RATIO, petRatioErr->data.F32[n0], petRatioErr->data.F32[n1]);
+	petRadius = refRadius->data.F32[lowestSignificantRadius];
+	petRadiusErr = NAN;
+	if (!isfinite(petRadius)) {
+	    fprintf (stderr, "nan pet radius\n");
 	}
     }
@@ -170,4 +163,7 @@
 		petArea    = InterpolateValues     (refRadius->data.F32[i-1], areaSum->data.F32[i-1], refRadius->data.F32[i], areaSum->data.F32[i], apRadius);
 		petApix    = InterpolateValues     (refRadius->data.F32[i-1], apixSum->data.F32[i-1], refRadius->data.F32[i], apixSum->data.F32[i], apRadius);
+		if (!isfinite(petFlux)) {
+		    fprintf (stderr, "nan pet flux\n");
+		}
 		break;
 	    }
@@ -188,6 +184,7 @@
 	if (!found50 && (fluxSum->data.F32[i] > flux50)) {
 	    if (i == 0) {
-		psWarning ("does this case make any sense? (fluxSum[0] %f > flux50 %f)", fluxSum->data.F32[i], flux50);
-		continue;
+		R50    = InterpolateValues     (fluxSum->data.F32[i], refRadius->data.F32[i], fluxSum->data.F32[i+1], refRadius->data.F32[i+1], flux50);
+		R50err = InterpolateValuesErrX (fluxSum->data.F32[i], refRadius->data.F32[i], fluxSum->data.F32[i+1], refRadius->data.F32[i+1], flux50, sqrt(fluxSumErr2->data.F32[i]), sqrt(fluxSumErr2->data.F32[i+1]));
+		found50 = true;
 	    } else {
 		R50    = InterpolateValues     (fluxSum->data.F32[i-1], refRadius->data.F32[i-1], fluxSum->data.F32[i], refRadius->data.F32[i], flux50);
@@ -198,6 +195,7 @@
 	if (!found90 && (fluxSum->data.F32[i] > flux90)) {
 	    if (i == 0) {
-		psWarning ("does this case make any sense? (fluxSum[0] %f > flux90 %f)", fluxSum->data.F32[i], flux90);
-		continue;
+		R90    = InterpolateValues     (fluxSum->data.F32[i], refRadius->data.F32[i], fluxSum->data.F32[i+1], refRadius->data.F32[i+1], flux90);
+		R90err = InterpolateValuesErrX (fluxSum->data.F32[i], refRadius->data.F32[i], fluxSum->data.F32[i+1], refRadius->data.F32[i+1], flux90, sqrt(fluxSumErr2->data.F32[i]), sqrt(fluxSumErr2->data.F32[i+1]));
+		found90 = true;
 	    } else {
 		R90    = InterpolateValues     (fluxSum->data.F32[i-1], refRadius->data.F32[i-1], fluxSum->data.F32[i], refRadius->data.F32[i], flux90);
Index: branches/meh_branches/ppstack_test/psphot/src/psphotRadialApertures.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotRadialApertures.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotRadialApertures.c	(revision 33415)
@@ -3,8 +3,14 @@
 bool psphotRadialAperturesSortFlux (psVector *radius, psVector *pixFlux, psVector *pixVar);
 
+// this function measures the radial aperture fluxes for the set of readouts.  this function
+// may be called multiple times, presumably for different versions of PSF-matched or unmatched images.  
+
 // for now, let's store the detections on the readout->analysis for each readout
-bool psphotRadialApertures (pmConfig *config, const pmFPAview *view, const char *filerule)
+bool psphotRadialApertures (pmConfig *config, const pmFPAview *view, const char *filerule, int entry)
 {
     bool status = true;
+
+    fprintf (stdout, "\n");
+    psLogMsg ("psphot", PS_LOG_INFO, "--- psphot Radial Apertures ---");
 
     // select the appropriate recipe information
@@ -20,7 +26,13 @@
     int num = psphotFileruleCount(config, filerule);
 
+    // skip the chisq image (optionally?)
+    int chisqNum = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.CHISQ.NUM");
+    if (!status) chisqNum = -1;
+
     // loop over the available readouts
     for (int i = 0; i < num; i++) {
-	if (!psphotRadialAperturesReadout (config, view, filerule, i, recipe)) {
+        if (i == chisqNum) continue; // skip chisq image
+
+	if (!psphotRadialAperturesReadout (config, view, filerule, i, recipe, entry)) {
             psError (PSPHOT_ERR_CONFIG, false, "failed on measure extended source aperture-like parameters for %s entry %d", filerule, i);
 	    return false;
@@ -30,7 +42,15 @@
 }
 
+// these values are used by all threads repeatedly (and are not modified)
+static psVector *aperRadii = NULL;
+static psVector *aperRadii2 = NULL;
+static float outerRadius = NAN;
+static float SN_LIM = NAN;
+static psImageMaskType maskVal = 0;
+
 // aperture-like measurements for extended sources
 // flux in simple, circular apertures
-bool psphotRadialAperturesReadout (pmConfig *config, const pmFPAview *view, const char *filerule, int index, psMetadata *recipe) {
+// 'entry' tells us which of the matched-PSF images we are working on (0 == unmatched image, also non-stack psphot)
+bool psphotRadialAperturesReadout (pmConfig *config, const pmFPAview *view, const char *filerule, int index, psMetadata *recipe, int entry) {
 
     bool status;
@@ -63,17 +83,30 @@
     }
 
-    // radMax stores the upper bounds of the annuli
+    // determine the number of allowed threads
+    int nThreads = psMetadataLookupS32(&status, config->arguments, "NTHREADS"); // Number of threads
+    if (!status) {
+        nThreads = 0;
+    }
+
+    // aperRadii stores the upper bounds of the annuli
     // XXX keep the same name here as for the petrosian / elliptical apertures?
-    psVector *radMax = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.UPPER");
-    psAssert (radMax, "annular bins (RADIAL.ANNULAR.BINS.UPPER) are not defined in the recipe");
-    psAssert (radMax->n, "no valid annular bins (RADIAL.ANNULAR.BINS.UPPER) are define");
-    float outerRadius = radMax->data.F32[radMax->n - 1];
+    aperRadii = psMetadataLookupPtr (&status, recipe, "RADIAL.ANNULAR.BINS.UPPER");
+    psAssert (aperRadii, "annular bins (RADIAL.ANNULAR.BINS.UPPER) are not defined in the recipe");
+    psAssert (aperRadii->n, "no valid annular bins (RADIAL.ANNULAR.BINS.UPPER) are define");
+
+    outerRadius = aperRadii->data.F32[aperRadii->n - 1];
+
+    // save the R^2 values as well for quicker comparison
+    aperRadii2 = psVectorAlloc(aperRadii->n, PS_TYPE_F32);
+    for (int i = 0; i < aperRadii->n; i++) {
+	aperRadii2->data.F32[i] = PS_SQR(aperRadii->data.F32[i]);
+    }
 
     // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
-    psImageMaskType maskVal = psMetadataLookupImageMask(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels
+    maskVal = psMetadataLookupImageMask(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels
     assert (maskVal);
 
     // S/N limit to perform full non-linear fits
-    float SN_LIM = psMetadataLookupF32 (&status, recipe, "RADIAL_APERTURES_SN_LIM");
+    SN_LIM = psMetadataLookupF32 (&status, recipe, "RADIAL_APERTURES_SN_LIM");
 
     // source analysis is done in S/N order (brightest first)
@@ -81,8 +114,108 @@
     sources = psArraySort (sources, pmSourceSortByFlux);
 
+    // XXX make this consistent with entry 0 == unmatched
+    int nEntry = 1;
+    psVector *fwhmValues = psMetadataLookupVector(&status, readout->analysis, "STACK.PSF.FWHM.VALUES");
+    if (fwhmValues) {
+	psAssert (entry < fwhmValues->n, "inconsistent matched-PSF entry");
+	nEntry = fwhmValues->n;
+    }
+    if (entry > 0) {
+	psLogMsg ("psphot", PS_LOG_DETAIL, "Radial Apertures for matched image %s : PSF FWHM = %f pixels\n", file->name, fwhmValues->data.F32[entry]);
+    } else {
+	psLogMsg ("psphot", PS_LOG_DETAIL, "Radial Apertures for unmatched image %s\n", file->name);
+    }
+
     // option to limit analysis to a specific region
     char *region = psMetadataLookupStr (&status, recipe, "ANALYSIS_REGION");
-    psRegion AnalysisRegion = psRegionForImage (readout->image, psRegionFromString (region));
-    if (psRegionIsNaN (AnalysisRegion)) psAbort("analysis region mis-defined");
+    psRegion *AnalysisRegion = psRegionAlloc(0,0,0,0);
+    *AnalysisRegion = psRegionForImage (readout->image, psRegionFromString (region));
+    if (psRegionIsNaN (*AnalysisRegion)) psAbort("analysis region mis-defined");
+
+    // choose Cx, Cy (see psphotThreadTools.c for overview of the concepts)
+    int Cx = 1, Cy = 1;
+    psphotChooseCellSizes (&Cx, &Cy, readout, nThreads);
+
+    psArray *cellGroups = psphotAssignSources (Cx, Cy, sources);
+
+    for (int i = 0; i < cellGroups->n; i++) {
+
+        psArray *cells = cellGroups->data[i];
+
+        for (int j = 0; j < cells->n; j++) {
+
+            // allocate a job -- if threads are not defined, this just runs the job
+            psThreadJob *job = psThreadJobAlloc ("PSPHOT_RADIAL_APERTURES");
+
+            psArrayAdd(job->args, 1, readout);
+            psArrayAdd(job->args, 1, cells->data[j]); // sources
+            psArrayAdd(job->args, 1, AnalysisRegion);
+            PS_ARRAY_ADD_SCALAR(job->args, entry,  PS_TYPE_S32);
+            PS_ARRAY_ADD_SCALAR(job->args, nEntry, PS_TYPE_S32);
+            PS_ARRAY_ADD_SCALAR(job->args, 0, PS_TYPE_S32); // this is used as a return value for Nradial
+
+	    // set this to 0 to run without threading
+# if (1)	    
+            if (!psThreadJobAddPending(job)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
+		psFree(AnalysisRegion);
+                return false;
+            } 
+# else
+	    if (!psphotRadialApertures_Threaded(job)) {
+		psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
+		psFree(AnalysisRegion);
+		return false;
+	    }
+	    psScalar *scalar = NULL;
+	    scalar = job->args->data[5];
+	    Nradial += scalar->data.S32;
+	    psFree(job);
+# endif
+	}
+
+        // wait for the threads to finish and manage results
+        if (!psThreadPoolWait (false, true)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
+	    psFree(AnalysisRegion);
+            return false;
+        }
+
+        // we have only supplied one type of job, so we can assume the types here
+        psThreadJob *job = NULL;
+        while ((job = psThreadJobGetDone()) != NULL) {
+            if (job->args->n < 1) {
+                fprintf (stderr, "error with job\n");
+            } else {
+		psScalar *scalar = NULL;
+		scalar = job->args->data[5];
+		Nradial += scalar->data.S32;
+            }
+            psFree(job);
+	}
+    }
+    psFree (cellGroups);
+    psFree(AnalysisRegion);
+    psFree (aperRadii2);
+
+    psLogMsg ("psphot", PS_LOG_WARN, "radial source apertures: %f sec for %d objects\n", psTimerMark ("psphot.radial"), Nradial);
+    return true;
+}
+ 
+bool psphotRadialApertures_Threaded (psThreadJob *job) {
+
+    int Nradial = 0;
+
+    // arguments: readout, sources, models, region, psfSize, maskVal, markVal
+    pmReadout *readout      = job->args->data[0];
+    psArray *sources        = job->args->data[1];
+    psRegion *region        = job->args->data[2];
+    int entry               = PS_SCALAR_VALUE(job->args->data[3],S32); // which psf-matched image are we working on? (0 == unmatched)
+    int nEntry              = PS_SCALAR_VALUE(job->args->data[4],S32); // total number of psf-matched images + 1 unmatched
+
+    // storage for the derived pixel values (these are passed into psphotRadialApertureSource)
+    psVector *pixRadius2 = psVectorAllocEmpty(100, PS_TYPE_F32);
+    psVector *pixFlux    = psVectorAllocEmpty(100, PS_TYPE_F32);
+    psVector *pixVar     = psVectorAllocEmpty(100, PS_TYPE_F32);
 
     // choose the sources of interest
@@ -90,4 +223,9 @@
 
 	pmSource *source = sources->data[i];
+
+	// if we have checked the source validity on the basis of the object set, then 
+	// we either skip these tests below or we skip the source completely
+	if (source->tmpFlags & PM_SOURCE_TMPF_RADIAL_SKIP) continue;
+	if (source->tmpFlags & PM_SOURCE_TMPF_RADIAL_KEEP) goto keepSource;
 
 	// skip PSF-like and non-astronomical objects
@@ -104,12 +242,21 @@
 
 	// limit selection by analysis region
-	if (source->peak->x < AnalysisRegion.x0) continue;
-	if (source->peak->y < AnalysisRegion.y0) continue;
-	if (source->peak->x > AnalysisRegion.x1) continue;
-	if (source->peak->y > AnalysisRegion.y1) continue;
+	if (source->peak->x < region->x0) continue;
+	if (source->peak->y < region->y0) continue;
+	if (source->peak->x > region->x1) continue;
+	if (source->peak->y > region->y1) continue;
+
+    keepSource:
 
 	// allocate pmSourceExtendedParameters, if not already defined
-	if (!source->radialAper) {
-	    source->radialAper = psArrayAlloc(1);
+	// XXX check that nPSFsizes is consistent with targets
+	if (source->parent) {
+	    if (!source->parent->radialAper) {
+		source->parent->radialAper = psArrayAlloc(nEntry);
+	    }
+	} else {
+	    if (!source->radialAper) {
+		source->radialAper = psArrayAlloc(nEntry);
+	    }
 	}
 
@@ -119,17 +266,7 @@
 	}
 
-	// we need to change the view for the radial aperture analysis, but we want to recover exactly
-	// the original view; the following elements get destroyed by pmSourceRedefinePixels so save them:
-	psImage *oldMaskObj   = psMemIncrRefCounter(source->maskObj);
-	psImage *oldModelFlux = psMemIncrRefCounter(source->modelFlux);
-	psImage *oldPSFimage  = psMemIncrRefCounter(source->psfImage);
-	psRegion oldRegion    = source->region;
-
 	Nradial ++;
 
-	// force source image to be a bit larger...
-	pmSourceRedefinePixels (source, readout, source->peak->xf, source->peak->yf, outerRadius + 2);
-
-	if (!psphotRadialApertureSource (source, recipe, maskVal, radMax, 0)) {
+	if (!psphotRadialApertureSource (source, readout, entry, pixRadius2, pixFlux, pixVar)) {
 	    psTrace ("psphot", 5, "failed to extract radial profile for source at %7.1f, %7.1f", source->moments->Mx, source->moments->My);
 	} else {
@@ -137,19 +274,19 @@
 	}
 
-	pmSourceRedefinePixelsByRegion (source, readout, oldRegion);
-	psFree(source->maskObj);   source->maskObj   = oldMaskObj;
-	psFree(source->modelFlux); source->modelFlux = oldModelFlux;
-	psFree(source->psfImage);  source->psfImage  = oldPSFimage;
-	
 	// re-subtract the object, leave local sky
 	pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
     }
-
-    psLogMsg ("psphot", PS_LOG_INFO, "radial source apertures: %f sec for %d objects\n", psTimerMark ("psphot.radial"), Nradial);
+    psScalar *scalar = job->args->data[5];
+    scalar->data.S32 = Nradial;
+
+    psFree (pixRadius2);
+    psFree (pixFlux);
+    psFree (pixVar);
+
     return true;
 }
 
-bool psphotRadialApertureSource (pmSource *source, psMetadata *recipe, psImageMaskType maskVal, const psVector *aperRadii, int entry) {
-
+bool psphotRadialApertureSource (pmSource *source, pmReadout *readout, int entry, psVector *pixRadius2, psVector *pixFlux, psVector *pixVar) {
+					    
     // if we are a child source, save the results to the parent source radial aperture array
     psArray *radialAperSet = source->radialAper;
@@ -163,59 +300,68 @@
     radialAperSet->data[entry] = radialAper;
 
-    // storage for the derived pixel values
-    psVector *pixRadius2 = psVectorAllocEmpty(100, PS_TYPE_F32);
-    psVector *pixFlux    = psVectorAllocEmpty(100, PS_TYPE_F32);
-    psVector *pixVar     = psVectorAllocEmpty(100, PS_TYPE_F32);
+    // find the largest aperture of interest (use only apertures with inner radii <=
+    // source->skyRadius)
+    int lastAp = aperRadii->n;
+    for (int i = 0; i < aperRadii->n; i++) {
+	if (aperRadii->data.F32[i] < source->skyRadius) continue;
+	lastAp = i + 1;
+	break;
+    }
 
     // outer-most radius for initial truncation
-    float Rmax  = aperRadii->data.F32[aperRadii->n - 1];
+    float Rmax  = aperRadii->data.F32[lastAp - 1];
     float Rmax2 = PS_SQR(Rmax);
 
-    // store the R^2 values for the apertures
-    psVector *aperRadii2 = psVectorAlloc(aperRadii->n, PS_TYPE_F32);
-    for (int i = 0; i < aperRadii->n; i++) {
-	aperRadii2->data.F32[i] = PS_SQR(aperRadii->data.F32[i]);
-    }
+    // in this function, the operatins are relative to the full image (readout->image, etc)
 
     float xCM = NAN, yCM = NAN;
     if (pmSourcePositionUseMoments(source)) {
-	xCM = source->moments->Mx - 0.5 - source->pixels->col0; // coord of peak in subimage
-	yCM = source->moments->My - 0.5 - source->pixels->row0; // coord of peak in subimage
+	xCM = source->moments->Mx; // index coord of peak in readout
+	yCM = source->moments->My; // index coord of peak in readout
     } else {
-	xCM = source->peak->xf - 0.5 - source->pixels->col0; // coord of peak in subimage
-	yCM = source->peak->yf - 0.5 - source->pixels->row0; // coord of peak in subimage
-    }
+	xCM = source->peak->xf; // index coord of peak in readout
+	yCM = source->peak->yf; // index coord of peak in readout
+    }
+
+    int Nx = readout->image->numCols;
+    int Ny = readout->image->numRows;
+
+    pixRadius2->n = 0;
+    pixFlux->n = 0;
+    pixVar->n = 0;
 
     // one pass through the pixels to select the valid pixels and calculate R^2
-    for (int iy = 0; iy < source->pixels->numRows; iy++) {
-
-	float yDiff = iy - yCM;
-	if (fabs(yDiff) > Rmax) continue;
-
-	float *vPix = source->pixels->data.F32[iy];
-	float *vWgt = source->variance->data.F32[iy];
-	psImageMaskType  *vMsk = (source->maskObj == NULL) ? NULL : source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[iy];
-
-	for (int ix = 0; ix < source->pixels->numCols; ix++, vPix++, vWgt++) {
-
-	    if (vMsk) {
-		if (*vMsk & maskVal) {
-		    vMsk++;
-		    continue;
-		}
-		vMsk++;
-	    }
-	    if (isnan(*vPix)) continue;
-
-	    float xDiff = ix - xCM;
-	    if (fabs(xDiff) > Rmax) continue;
+    for (int iy = -Rmax; iy < Rmax + 1; iy++) {
+
+	float yDiff = iy + 0.5 + yCM;  // y-coordinate at this offse
+	int yPix = (int) yDiff;
+
+	if (yPix < 0) continue;
+	if (yPix > Ny - 1) continue;
+	if (fabs(iy) > Rmax) continue;
+
+	float *vPix = readout->image->data.F32[yPix];
+	float *vWgt = readout->variance->data.F32[yPix];
+	psImageMaskType  *vMsk = readout->mask->data.PS_TYPE_IMAGE_MASK_DATA[yPix];
+
+	for (int ix = -Rmax; ix < Rmax + 1; ix++) {
+
+	    float xDiff = ix + 0.5 + xCM;  // x-coordinate at this offse
+	    int xPix = (int) xDiff;
+	    
+	    if (xPix < 0) continue;
+	    if (xPix > Nx - 1) continue;
+	    if (fabs(ix) > Rmax) continue;
+	    
+	    if (vMsk[xPix] & maskVal) continue;
+	    if (isnan(vPix[xPix])) continue;
 
 	    // radius is just a function of (xDiff, yDiff)
-	    float r2  = PS_SQR(xDiff) + PS_SQR(yDiff);
+	    float r2  = PS_SQR(ix) + PS_SQR(iy);
 	    if (r2 > Rmax2) continue;
 
 	    psVectorAppend(pixRadius2, r2);
-	    psVectorAppend(pixFlux, *vPix);
-	    psVectorAppend(pixVar, *vWgt);
+	    psVectorAppend(pixFlux, vPix[xPix]);
+	    psVectorAppend(pixVar, vWgt[xPix]);
 	}
     }
@@ -226,8 +372,15 @@
     psVector *fill    = psVectorAlloc(aperRadii->n, PS_TYPE_F32); // surface brightness of radial bin
 
+    // init the apertures of interest to 0.0, the rest go to NAN
     psVectorInit (flux,    0.0);
     psVectorInit (fluxStd, 0.0);
     psVectorInit (fluxErr, 0.0);
     psVectorInit (fill,    0.0);
+    for (int i = lastAp; i < flux->n; i++) {
+	flux->data.F32[i] = NAN;
+	fluxStd->data.F32[i] = NAN;
+	fluxErr->data.F32[i] = NAN;
+	fill->data.F32[i] = NAN;
+    }
 
     float *rPix2 = pixRadius2->data.F32;
@@ -236,6 +389,8 @@
 	int j = 0;
 	float *aRad2 = aperRadii2->data.F32;
-	for (; (*aRad2 < *rPix2) && (j < aperRadii2->n); j++, aRad2++);
-	for (; j < aperRadii2->n; j++, aRad2++) {
+	for (; (*aRad2 < *rPix2) && (j < lastAp); j++, aRad2++);
+
+	// XXX I can speed this up by only saving this single aperture
+	for (; j < lastAp; j++, aRad2++) {
 	    flux->data.F32[j]    += pixFlux->data.F32[i];
 	    fluxStd->data.F32[j] += PS_SQR(pixFlux->data.F32[i]);
@@ -249,7 +404,7 @@
        2) the fractional fill factor (count of valid pixels / effective area of the aperture
        3) the error on the flux within that aperture
-     */
-
-    for (int i = 0; i < flux->n; i++) {
+    */
+
+    for (int i = 0; i < lastAp; i++) {
 	// calculate the total flux for bin 'nOut'
 	float Area = M_PI*aperRadii2->data.F32[i];
@@ -261,5 +416,5 @@
 	// XXX report the total flux or the mask-corrected flux?
 	// flux->data.F32[i]    = SBmean * Area;
-	// fluxErr->data.F32[i] = sqrt(fluxErr->data.F32[i]) * Area / nPix;
+	// fluxErr->data.F32[i] = sqrt(fluxErr->data.F32[i]) * Area / Pinx;
 
 	fluxErr->data.F32[i] = sqrt(fluxErr->data.F32[i]);
@@ -271,16 +426,21 @@
     }
     
+# if (1)
     radialAper->flux = flux;
     radialAper->fluxStdev = fluxStd;
     radialAper->fluxErr = fluxErr;
     radialAper->fill = fill;
-
-    psFree (aperRadii2);
-    psFree (pixRadius2);
-    psFree (pixFlux);
-    psFree (pixVar);
+# else
+    // XXX TEST
+    psFree(flux);
+    psFree(fluxStd);
+    psFree(fluxErr);
+    psFree(fill);
+# endif
 
     return true;
 }
+
+/*** below is a test to use a sort to speed this up, not very successfully ***/
 
 static int nCalls = 0;
@@ -373,18 +533,18 @@
 // *** pmSourceRadialProfileSortPair is a utility function for sorting a pair of vectors
 # define COMPARE_VECT(A,B) (radius->data.F32[A] < radius->data.F32[B])
-# define SWAP_VECT(TYPE,A,B) { \
-  float tmp; \
-  if (A != B) { \
-    tmp = radius->data.F32[A]; \
-    radius->data.F32[A] = radius->data.F32[B];	\
-    radius->data.F32[B] = tmp; \
-    tmp = pixFlux->data.F32[A]; \
-    pixFlux->data.F32[A] = pixFlux->data.F32[B]; \
-    pixFlux->data.F32[B] = tmp; \
-    tmp = pixVar->data.F32[A]; \
-    pixVar->data.F32[A] = pixVar->data.F32[B]; \
-    pixVar->data.F32[B] = tmp; \
-  } \
-}
+# define SWAP_VECT(TYPE,A,B) {					\
+	float tmp;						\
+	if (A != B) {						\
+	    tmp = radius->data.F32[A];				\
+	    radius->data.F32[A] = radius->data.F32[B];		\
+	    radius->data.F32[B] = tmp;				\
+	    tmp = pixFlux->data.F32[A];				\
+	    pixFlux->data.F32[A] = pixFlux->data.F32[B];	\
+	    pixFlux->data.F32[B] = tmp;				\
+	    tmp = pixVar->data.F32[A];				\
+	    pixVar->data.F32[A] = pixVar->data.F32[B];		\
+	    pixVar->data.F32[B] = tmp;				\
+	}							\
+    }
 
 bool psphotRadialAperturesSortFlux (psVector *radius, psVector *pixFlux, psVector *pixVar) {
Index: branches/meh_branches/ppstack_test/psphot/src/psphotRadialAperturesByObject.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotRadialAperturesByObject.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotRadialAperturesByObject.c	(revision 33415)
@@ -162,7 +162,4 @@
 	    // re-subtract the object, leave local sky
 	    pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
-
-	    // psLogMsg("psphot", PS_LOG_INFO, "radial apertures for %d", index);
-	    // psphotVisualShowImage(readout);
 	}
     }
Index: branches/meh_branches/ppstack_test/psphot/src/psphotRadialProfile.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotRadialProfile.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotRadialProfile.c	(revision 33415)
@@ -39,5 +39,5 @@
     // convert the isophotal radius vs angle measurements to an elliptical contour
     if (!psphotEllipticalContour (source)) {
-	psLogMsg ("psphot", 3, "failed to measure elliptical contour");
+	// psLogMsg ("psphot", 3, "failed to measure elliptical contour");
 	return false;
     }
Index: branches/meh_branches/ppstack_test/psphot/src/psphotRadialProfileWings.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotRadialProfileWings.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotRadialProfileWings.c	(revision 33415)
@@ -0,0 +1,414 @@
+# include "psphotInternal.h"
+
+// measure the radial profile outside of the core.  the goal is to find the point at which we
+// reach sky + X sigma
+
+bool psphotRadialProfileWingsSource (pmSource *source, pmReadout *readout, psImageMaskType maskVal);
+bool psphotRadialProfileFluxAtRadius (psVector *flux, psVector *fluxVar, pmSource *source, pmReadout *readout, float Radius, float dRadius, psImageMaskType maskVal);
+
+bool psphotRadialProfileWings (pmConfig *config, const pmFPAview *view, const char *filerule)
+{
+    bool status = true;
+
+    // return true;
+
+    fprintf (stdout, "\n");
+    psLogMsg ("psphot", PS_LOG_INFO, "--- psphot Radial Profile Wings ---");
+
+    // select the appropriate recipe information
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
+    psAssert (recipe, "missing recipe?");
+
+    int num = psphotFileruleCount(config, filerule);
+
+    // skip the chisq image (optionally?)
+    int chisqNum = psMetadataLookupS32 (&status, config->arguments, "PSPHOT.CHISQ.NUM");
+    if (!status) chisqNum = -1;
+
+    // loop over the available readouts
+    for (int i = 0; i < num; i++) {
+        if (i == chisqNum) continue; // skip chisq image
+
+        // find the currently selected readout
+        pmFPAfile *file = pmFPAfileSelectSingle(config->files, filerule, i); // File of interest
+        psAssert (file, "missing file?");
+
+        pmReadout *readout = pmFPAviewThisReadout(view, file->fpa);
+        psAssert (readout, "missing readout?");
+
+        pmDetections *detections = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.DETECTIONS");
+        psAssert (detections, "missing detections?");
+
+        psArray *sources = detections->newSources ? detections->newSources : detections->allSources;
+        psAssert (sources, "missing sources?");
+
+        if (!psphotRadialProfileWingsReadout (config, recipe, view, readout, sources)) {
+            psError (PSPHOT_ERR_CONFIG, false, "failed to measure magnitudes for %s entry %d", filerule, i);
+            return false;
+        }
+    }
+    return true;
+}
+
+// these are set before we fork off to threads and used by all threads as constant values
+static float MAX_RADIUS = NAN;
+static float MIN_RADIUS = NAN;
+static float SKY_STDEV  = NAN;
+// static FILE *file = NULL;
+
+bool psphotRadialProfileWingsReadout(pmConfig *config, psMetadata *recipe, const pmFPAview *view, pmReadout *readout, psArray *sources) {
+
+    bool status = false;
+
+    if (!sources->n) {
+        psLogMsg ("psphot", PS_LOG_INFO, "no sources, skipping radial profile wings");
+        return true;
+    }
+
+    psTimerStart ("psphot.wings");
+
+    // determine the number of allowed threads
+    int nThreads = psMetadataLookupS32(&status, config->arguments, "NTHREADS"); // Number of threads
+    if (!status) {
+        nThreads = 0;
+    }
+
+    // XXX is this a good recipe value to use for MAX RADIUS??
+    MAX_RADIUS = psMetadataLookupF32 (&status, recipe, "EXT_FIT_MAX_RADIUS");
+    if (!status) {
+        MAX_RADIUS = 50.0;
+    }
+
+    MIN_RADIUS = psMetadataLookupF32 (&status, readout->analysis, "PSF_MOMENTS_RADIUS");
+    if (!status) {
+        MIN_RADIUS = psMetadataLookupF32 (&status, recipe, "PSF_MOMENTS_RADIUS");
+    }
+
+    // SKY_STDEV is the sigma of the sky model (ie, smoothed on large scales)
+    SKY_STDEV = psMetadataLookupF32 (&status, readout->analysis, "MSKY_SIG");
+    if (!status) {
+	SKY_STDEV = 1.0; // a crude default value (why would this not exist?)
+    }
+
+    // bit-masks to test for good/bad pixels
+    psImageMaskType maskVal = psMetadataLookupImageMask(&status, recipe, "MASK.PSPHOT");
+    assert (maskVal);
+
+    // bit-mask to mark pixels not used in analysis
+    psImageMaskType markVal = psMetadataLookupImageMask(&status, recipe, "MARK.PSPHOT");
+    assert (markVal);
+
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    // source analysis is done in S/N order (brightest first)
+    sources = psArraySort (sources, pmSourceSortByFlux);
+    if (!sources->n) {
+        psLogMsg ("psphot", PS_LOG_INFO, "no sources, skipping blend");
+        return true;
+    }
+
+    // file = fopen ("radii.dat", "w");
+
+    // threaded measurement of the source magnitudes
+    // choose Cx, Cy (see psphotThreadTools.c for overview of the concepts)
+    int Cx = 1, Cy = 1;
+    psphotChooseCellSizes (&Cx, &Cy, readout, nThreads);
+
+    psArray *cellGroups = psphotAssignSources (Cx, Cy, sources);
+
+    for (int i = 0; i < cellGroups->n; i++) {
+
+        psArray *cells = cellGroups->data[i];
+
+        for (int j = 0; j < cells->n; j++) {
+
+            // allocate a job -- if threads are not defined, this just runs the job
+            psThreadJob *job = psThreadJobAlloc ("PSPHOT_RADIAL_PROFILE_WINGS");
+
+            psArrayAdd(job->args, 1, readout);
+            psArrayAdd(job->args, 1, cells->data[j]); // sources
+            PS_ARRAY_ADD_SCALAR(job->args, maskVal,       PS_TYPE_IMAGE_MASK);
+
+// set this to 0 to run without threading
+# if (1)
+            if (!psThreadJobAddPending(job)) {
+                psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
+                return false;
+            }
+# else
+	    if (!psphotRadialProfileWings_Threaded(job)) {
+		psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
+		return false;
+	    }
+	    psFree(job);
+# endif
+        }
+
+        // wait for the threads to finish and manage results
+        if (!psThreadPoolWait (false, true)) {
+            psError(PS_ERR_UNKNOWN, false, "Unable to guess model.");
+            return false;
+        }
+
+        // we have only supplied one type of job, so we can assume the types here
+        psThreadJob *job = NULL;
+        while ((job = psThreadJobGetDone()) != NULL) {
+            if (job->args->n < 1) {
+                fprintf (stderr, "error with job\n");
+            }
+            psFree(job);
+        }
+    }
+    psFree (cellGroups);
+
+    // fclose (file);
+
+    psLogMsg ("psphot.wings", PS_LOG_WARN, "measure radial profile wings : %f sec for %ld objects\n", psTimerMark ("psphot.wings"), sources->n);
+    return true;
+}
+
+bool psphotRadialProfileWings_Threaded (psThreadJob *job) {
+
+    pmReadout *readout              = job->args->data[0];
+    psArray *sources                = job->args->data[1];
+    psImageMaskType maskVal         = PS_SCALAR_VALUE(job->args->data[2],PS_TYPE_IMAGE_MASK_DATA);
+
+    for (int i = 0; i < sources->n; i++) {
+
+	pmSource *source = sources->data[i];
+	if (!source->peak) continue; // XXX how can we have a peak-less source?
+
+	// allocate space for moments
+	if (!source->moments) continue;
+
+	// replace object in image
+	bool reSubtract = false;
+	if (source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED) {
+	    pmSourceAdd (source, PM_MODEL_OP_FULL, maskVal);
+	    reSubtract = true;
+	}
+
+	// re-allocate image, weight, mask arrays for each peak with box big enough to fit BIG_RADIUS
+	// XXX don't measure on subraster images:
+	// pmSourceRedefinePixels (source, readout, source->peak->x, source->peak->y, windowRadius + 2);
+	// psAssert (source->pixels, "WTF?");
+
+	// this function populates moments->Mrf,KronFlux,KronFluxErr
+	psphotRadialProfileWingsSource (source, readout, maskVal);
+
+	// if we subtracted it above, re-subtract the object, leave local sky
+	if (reSubtract) {
+	    pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
+	}
+    }
+    return true;
+}
+
+# define TEST_X 3158
+# define TEST_Y 3096
+
+float InterpolateValues (float X0, float Y0, float X1, float Y1, float X);
+
+// XXX use integer radius values?  the rings assume integer values, right? or do they?
+bool psphotRadialProfileWingsSource (pmSource *source, pmReadout *readout, psImageMaskType maskVal) {
+
+    PS_ASSERT_PTR_NON_NULL(source, false);
+    PS_ASSERT_PTR_NON_NULL(source->peak, false);
+    PS_ASSERT_PTR_NON_NULL(source->pixels, false);
+
+    // psImageMaskType **vMsk = (source->maskObj == NULL) ? NULL : source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA;
+
+    // XXX unclear if I should run this analysis on both 1st and 2nd pass for 1st pass objects,
+    // or just use the 1st pass value.  I think I should just use the 1st pass value, so skip
+    // any that have already been assigned
+    if (isfinite(source->skyRadius)) return true;
+
+    if (source->mode2 & PM_SOURCE_MODE2_MATCHED) return true; // skip matched sources (no signal)
+
+    // radii will be MIN_RADIUS to MAX_RADIUS in NN log steps:
+    float NSTEP = 25.0;
+    float MIN_DR = 2;
+    float NSIGMA = 1.0;
+    float THRESHOLD = 2.0*SKY_STDEV;
+    float alpha = pow ((MAX_RADIUS / MIN_RADIUS), 1.0/NSTEP) - 1.0;
+    float dRmax = MAX_RADIUS * alpha / (1.0 + alpha); // approximate size of last annulus, to get a rough size for vector allocation
+
+    int iter = 0;
+
+    psVector *flux = psVectorAllocEmpty(7*MAX_RADIUS*dRmax, PS_TYPE_F32);
+    psVector *fluxVar = psVectorAllocEmpty(7*MAX_RADIUS*dRmax, PS_TYPE_F32);
+
+    // should I just use sample median here?
+    psStats *fluxStats = psStatsAlloc(PS_STAT_ROBUST_MEDIAN);
+    psStats *varStats = psStatsAlloc(PS_STAT_SAMPLE_MEAN);
+
+    float lastFlux = NAN; 
+    float lastRadius = NAN; 
+    float lastSlope = NAN; 
+    bool limit = false;
+    float limitRadius = NAN;
+    float limitFlux = NAN;
+    float limitSlope = NAN;
+
+    // note: radius is the inner radius of the annulus; outer radius = inner radius + dR
+    for (float radius = MIN_RADIUS; !limit && (radius < MAX_RADIUS); iter ++) {
+
+	float dR = (int)(radius * alpha);
+	if (dR < MIN_DR) dR = MIN_DR;
+	float outerRadius = radius + dR;
+	float meanRadius = (2.0/3.0) * (outerRadius*outerRadius*outerRadius - radius*radius*radius) / (PS_SQR(outerRadius) - PS_SQR(radius));
+
+	// extract a vector of the pixel values (signal, variance) at this radius + dR
+	psphotRadialProfileFluxAtRadius (flux, fluxVar, source, readout, radius, dR, maskVal);
+
+	psStatsInit (fluxStats);
+	psStatsInit (varStats);
+
+	if ((flux->n == 0) || (fluxVar->n == 0)) {
+	    flux->n = 0;
+	    fluxVar->n = 0;
+	    radius += dR;
+	    continue;
+	}
+
+	psVectorStats (fluxStats, flux, NULL, NULL, 0);
+	psVectorStats (varStats, fluxVar, NULL, NULL, 0);
+	
+	// get the mean / median flux statistic and significance here
+	float meanFlux = fluxStats->robustMedian;
+	float meanFluxError = sqrt(varStats->sampleMean / fluxVar->n);
+	// this is a bit crude on the flux error: the error. technically, it should be
+	// sqrt(\sum(variance)) / Npts.  I am using the identity \sum(variance) =
+	// \mean(variance) * Npts and cancelling the Npts term inside and out of the sqrt()
+	
+	float slope = NAN;
+	if (isfinite(lastFlux)) {
+	    slope = (meanFlux - lastFlux) / (meanRadius - lastRadius);
+	}
+
+	// fprintf (stderr, "%f %f : %f : %f %f  :  %f\n", source->peak->xf, source->peak->yf, radius, meanFlux, meanFluxError, slope);
+
+	if (!limit && (meanFlux - NSIGMA * meanFluxError < THRESHOLD)) {
+	    // dropped to sky level
+	    limit = true;
+	    // linearly interpolate to the radius at which we hit the sky
+	    if (isfinite(lastFlux)) {
+		limitRadius = InterpolateValues(lastFlux, lastRadius, meanFlux, meanRadius, 0.0);
+	    } else {
+		limitRadius = meanRadius;
+	    }
+	    limitFlux = meanFlux;
+	    limitSlope = slope;
+	}
+	if (!limit && isfinite(slope) && (fabs(slope) < 3.0)) { 
+	    // SB no longer changing.	    
+	    limit = true;
+	    // linearly interpolate to the radius at which we hit the sky, using the last flux and the limiting slope
+	    if (isfinite(lastFlux)) {
+		limitRadius = lastRadius + lastFlux / 3.0;
+	    } else {
+		limitRadius = meanRadius;
+	    }
+	    limitFlux = meanFlux;
+	    limitSlope = slope;
+	}
+
+	// completion criteria:
+	// 1) flux - NSIGMA * dflux <= sky
+	// 2) flux rising?
+	// 3) flux flat?
+
+	lastFlux = meanFlux;
+	lastRadius = meanRadius;
+	lastSlope = slope;
+
+	// reset the flux & fluxVar vector length to zero for re-use above:
+	flux->n = 0;
+	fluxVar->n = 0;
+	radius += dR;
+    }
+
+    if (!limit) {
+	limitRadius = lastRadius;
+	limitFlux = lastFlux;
+	limitSlope = lastSlope;
+    }
+    // fprintf (file, "%f %f : %f %f : %f\n", source->peak->xf, source->peak->yf, limitRadius, limitFlux, limitSlope);
+
+    psFree (flux);
+    psFree (fluxVar);
+    psFree (fluxStats);
+    psFree (varStats);
+
+    source->skyRadius = limitRadius;
+    source->skyFlux   = limitFlux;
+    source->skySlope  = limitSlope;
+
+    // save the max radius (and anything else?)
+    // source->moments->Mrf = Mrf;
+
+    return true;
+}
+
+// inline this?  macro this?
+bool psphotRadialProfileGetFlux (psVector *flux, psVector *fluxVar, pmReadout *readout, int xc, int yc, psImageMaskType maskVal) {
+
+    int Nx = readout->image->numCols;
+    int Ny = readout->image->numRows;
+
+    if (xc < 0) return false;
+    if (xc >= Nx) return false;
+    if (yc < 0) return false;
+    if (yc >= Ny) return false;
+    
+    if (readout->mask && (readout->mask->data.PS_TYPE_IMAGE_MASK_DATA[yc][xc] & maskVal)) return false;
+    psVectorAppend (flux, readout->image->data.F32[yc][xc]);
+    psVectorAppend (fluxVar, readout->variance->data.F32[yc][xc]);
+    return true;
+}
+
+// select the pixels at the given radius and extract the flux and variance into the given vectors
+// XXX should radius be 'int'?
+bool psphotRadialProfileFluxAtRadius (psVector *flux, psVector *fluxVar, pmSource *source, pmReadout *readout, float Radius, float dRadius, psImageMaskType maskVal) {
+
+    psAssert (flux, "must allocate output flux vector");
+    psAssert (fluxVar, "must allocate output fluxVar vector");
+
+    // the peak position is less accurate but less subject to extreme deviations
+    float dX = source->moments->Mx - source->peak->xf;
+    float dY = source->moments->My - source->peak->yf;
+    float dR = hypot(dX, dY);
+    float Xo = (dR < 2.0) ? source->moments->Mx : source->peak->xf;
+    float Yo = (dR < 2.0) ? source->moments->My : source->peak->yf;
+
+    for (int radius = Radius; radius < Radius + dRadius; radius ++) {
+
+	int x = 0;
+	int y = radius;
+	int d = 5 - 4*radius;
+
+	while (x <= y) {
+	    psphotRadialProfileGetFlux(flux, fluxVar, readout, (Xo + x), (Yo + y), maskVal);
+	    psphotRadialProfileGetFlux(flux, fluxVar, readout, (Xo + x), (Yo - y), maskVal);
+	    psphotRadialProfileGetFlux(flux, fluxVar, readout, (Xo + y), (Yo + x), maskVal);
+	    psphotRadialProfileGetFlux(flux, fluxVar, readout, (Xo - y), (Yo + x), maskVal);
+	    
+	    if (x > 0) {
+		psphotRadialProfileGetFlux(flux, fluxVar, readout, (Xo - x), (Yo + y), maskVal);
+		psphotRadialProfileGetFlux(flux, fluxVar, readout, (Xo - x), (Yo - y), maskVal);
+		psphotRadialProfileGetFlux(flux, fluxVar, readout, (Xo - y), (Yo - x), maskVal);
+		psphotRadialProfileGetFlux(flux, fluxVar, readout, (Xo + y), (Yo - x), maskVal);
+	    }
+	    
+	    if (d < 0) {
+		d = d + 8*x + 4;
+	    } else {
+		d = d + 8*(x-y) + 8;
+		y--;
+	    }
+	    x++;
+	}
+    }
+    return true;
+}
Index: branches/meh_branches/ppstack_test/psphot/src/psphotRadiusChecks.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotRadiusChecks.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotRadiusChecks.c	(revision 33415)
@@ -142,4 +142,64 @@
 }
 
+# define MIN_WINDOW 5.0
+# define SCALE1 5.0
+# define SCALE2 12.0
+
+// call this function whenever you (re)-define the EXT model
+// XXX this function does not shrink the window
+bool psphotSetRadiusMoments (float *fitRadius, float *windowRadius, pmReadout *readout, pmSource *source, psImageMaskType markVal) {
+
+    psAssert (source, "source not defined??");
+    psAssert (source->moments, "moments not defined??");
+
+    *fitRadius = SCALE1 * source->moments->Mrf;
+    *fitRadius = PS_MIN (PS_MAX(*fitRadius, MIN_WINDOW), EXT_FIT_MAX_RADIUS);
+
+    *windowRadius = SCALE2 * source->moments->Mrf;
+    *windowRadius = PS_MIN (PS_MAX(*windowRadius, 2.5*MIN_WINDOW), 2.5*EXT_FIT_MAX_RADIUS);
+
+    // redefine the pixels if needed
+    pmSourceRedefinePixels (source, readout, source->peak->xf, source->peak->yf, *windowRadius);
+
+    // set the mask to flag the excluded pixels
+    psImageKeepCircle (source->maskObj, source->peak->xf, source->peak->yf, *fitRadius, "OR", markVal);
+
+    return true;
+}
+# undef SCALE1
+# undef SCALE2
+# undef MIN_WINDOW
+
+# define MIN_WINDOW 5.0
+# define SCALE1 5.0
+# define PAD_WINDOW 3.0
+
+// call this function whenever you (re)-define the EXT model
+// XXX alternate function to set exactly the desired window size
+bool psphotSetRadiusMomentsExact (float *fitRadius, float *windowRadius, pmReadout *readout, pmSource *source, psImageMaskType markVal) {
+
+    psRegion newRegion;
+
+    psAssert (source, "source not defined??");
+    psAssert (source->moments, "moments not defined??");
+
+    *fitRadius = SCALE1 * source->moments->Mrf;
+    *fitRadius = PS_MIN (PS_MAX(*fitRadius, MIN_WINDOW), EXT_FIT_MAX_RADIUS);
+
+    *windowRadius = *fitRadius + PAD_WINDOW;
+
+    // check to see if new region is completely contained within old region
+    newRegion = psRegionForSquare (source->peak->xf, source->peak->yf, *windowRadius);
+    newRegion = psRegionForImage (readout->image, newRegion);
+
+    // redefine the pixels to match
+    pmSourceRedefinePixelsByRegion (source, readout, newRegion);
+
+    // set the mask to flag the excluded pixels
+    psImageKeepCircle (source->maskObj, source->peak->xf, source->peak->yf, *fitRadius, "OR", markVal);
+
+    return true;
+}
+
 // call this function whenever you (re)-define the EXT model
 bool psphotSetRadiusFootprint (float *radius, pmReadout *readout, pmSource *source, psImageMaskType markVal, float factor) {
@@ -181,4 +241,46 @@
 }
 
+// call this function whenever you (re)-define the EXT model
+bool psphotMaskFootprint (pmReadout *readout, pmSource *source, psImageMaskType markVal) {
+
+    psAssert (source, "source not defined??");
+    psAssert (source->peak, "peak not defined??");
+
+    pmPeak *peak = source->peak;
+
+    // set the radius based on the footprint:
+    if (!peak->footprint) return false;
+    pmFootprint *footprint = peak->footprint;
+    if (!footprint->spans) return false;
+    if (footprint->spans->n < 1) return false;
+
+    int Xo = source->maskObj->col0;
+    int Yo = source->maskObj->row0;
+
+    // mark all pixels 
+    for (int j = 0; j < source->maskObj->numRows; j++) {
+	for (int i = 0; i < source->maskObj->numCols; i++) {
+	    source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[j][i] |= markVal;
+	}
+    }
+
+    psImageMaskType clearVal = PS_NOT_IMAGE_MASK(markVal);
+
+    for (int j = 0; j < footprint->spans->n; j++) {
+        pmSpan *span = footprint->spans->data[j];
+
+	// mask the rows before and after each span
+	int minX = span->x0 - Xo - 2;
+	int maxX = span->x1 - Xo + 2;
+	int myY = span->y - Yo;
+
+	// unmark pixels inside the footprint
+	for (int i = minX; i <= maxX; i++) {
+	    source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[myY][i] &= clearVal;
+	}
+    }
+    return true;
+}
+
 // alternative EXT radius based on model guess (for use without footprints)
 bool psphotSetRadiusModel (pmModel *model, pmReadout *readout, pmSource *source, psImageMaskType markVal, bool deep) {
Index: branches/meh_branches/ppstack_test/psphot/src/psphotReadout.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotReadout.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotReadout.c	(revision 33415)
@@ -9,55 +9,4 @@
 }
 
-// for now, let's store the detections on the readout->analysis for each readout
-bool psphotDumpChisqs (pmConfig *config, const pmFPAview *view, const char *filerule)
-{
-    static int npass = 0;
-    char filename[64];
-
-    return true;
-
-    bool status = true;
-
-    int num = psphotFileruleCount(config, filerule);
-
-    snprintf (filename, 64, "chisq.%02d.dat", npass);
-    FILE *f = fopen (filename, "w");
-
-    // loop over the available readouts
-    for (int i = 0; i < num; i++) {
-
-        // find the currently selected readout
-        pmFPAfile *file = pmFPAfileSelectSingle(config->files, filerule, i); // File of interest
-        psAssert (file, "missing file?");
-
-        pmReadout *readout = pmFPAviewThisReadout(view, file->fpa);
-        psAssert (readout, "missing readout?");
-
-        pmDetections *detections = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.DETECTIONS");
-        psAssert (detections, "missing detections?");
-
-        psArray *sources = detections->allSources;
-        psAssert (sources, "missing sources?");
-
-	for (int i = 0; i < sources->n; i++) {
-	    pmSource *source = sources->data[i];
-	    if (!source) continue;
-
-	    pmModel *model = pmSourceGetModel (NULL, source);
-	    if (!model) continue;
-	
-	    if (source->mode & PM_SOURCE_MODE_NONLINEAR_FIT) {
-		fprintf (f, "%f %f %f %d %d %f  1 NONLINEAR\n", model->mag, model->params->data.F32[1], model->chisq, model->nDOF, model->nPix, model->chisqNorm);
-	    } else {
-		fprintf (f, "%f %f %f %d %d %f  0 LINEAR\n", model->mag, model->params->data.F32[1], model->chisq, model->nDOF, model->nPix, model->chisqNorm);
-	    }
-	}
-    }
-    fclose (f);
-    npass ++;
-
-    return true;
-}
-
 bool psphotReadout(pmConfig *config, const pmFPAview *view, const char *filerule) {
 
@@ -102,18 +51,4 @@
         return psphotReadoutCleanup (config, view, filerule);
     }
-
-# if (0)
-    // XXX test to mask outliers, not very helpful
-    // mask the high values in the image (with MARK)
-    if (!psphotMaskBackground (config, view, filerule)) {
-        return psphotReadoutCleanup (config, view, filerule);
-    }
-
-    // generate a background model (median, smoothed image)
-    if (!psphotModelBackground (config, view, filerule)) {
-        return psphotReadoutCleanup (config, view, filerule);
-    }
-# endif
-
     if (!psphotSubtractBackground (config, view, filerule)) {
         return psphotReadoutCleanup (config, view, filerule);
@@ -137,5 +72,7 @@
     }
 
-    // construct sources and measure basic stats (saved on detections->newSources)
+    // construct sources and measure moments and other basic stats (saved on detections->newSources)
+    // all sources use the auto-scaled window appropriate to a PSF, except for the saturated
+    // stars : these use a larger window (3x the basic window)
     if (!psphotSourceStats (config, view, filerule, true)) { // pass 1
         psError(PSPHOT_ERR_UNKNOWN, false, "failure to generate sources");
@@ -153,5 +90,7 @@
 
     // mark blended peaks PS_SOURCE_BLEND (detections->newSources)
-    if (!psphotBasicDeblend (config, view, filerule)) {
+    // XXX I've deactivated this because it was preventing galaxies close to stars from being
+    // XXX fitted as an extended source.
+    if (false && !psphotBasicDeblend (config, view, filerule)) {
         psError (PSPHOT_ERR_UNKNOWN, false, "failed on deblend analysis");
         return psphotReadoutCleanup (config, view, filerule);
@@ -188,18 +127,27 @@
     // psphotLoadExtSources (config, view, filerule); // pass 1
 
-    // construct an initial model for each object, set the radius to fitRadius, set circular
-    // fit mask (detections->newSources)
-    psphotGuessModels (config, view, filerule); // pass 1
-
     // merge the newly selected sources into the existing list
     // NOTE: merge OLD and NEW
     psphotMergeSources (config, view, filerule);
 
+    // Construct an initial model for each object, set the radius to fitRadius, set circular
+    // fit mask.  NOTE: only applied to sources without guess models
+    // pass 1
+    if (!psphotGuessModels (config, view, filerule)) {
+        psLogMsg ("psphot", 3, "failure to Guess Model - pass 1");
+        return psphotReadoutCleanup (config, view, filerule);
+    }
+
     // linear PSF fit to source peaks, subtract the models from the image (in PSF mask)
     psphotFitSourcesLinear (config, view, filerule, false); // pass 1 (detections->allSources)
-    psphotDumpChisqs (config, view, filerule);
-
-    // XXX re-measure the kron mags with models subtracted
-    psphotKronMasked(config, view, filerule);
+
+    // measure the radial profiles to the sky
+    psphotRadialProfileWings (config, view, filerule);
+
+    // re-measure the kron mags with models subtracted.  this pass uses a circular window of size PSF_MOMENTS_RADIUS (same window used to measure the psf-scale moments)
+    
+    // but this is chosen above to be appropriate for the PSF objects (not galaxies)
+    // psphotKronMasked(config, view, filerule);
+    psphotKronIterate(config, view, filerule);
 
     // identify CRs and extended sources (only unmeasured sources are measured)
@@ -211,14 +159,13 @@
     // non-linear PSF and EXT fit to brighter sources
     // replace model flux, adjust mask as needed, fit, subtract the models (full stamp)
+    // XXX: can leave faulted job in done queue
     psphotBlendFit (config, view, filerule); // pass 1 (detections->allSources)
-    psphotDumpChisqs (config, view, filerule);
 
     // replace all sources
-    psphotReplaceAllSources (config, view, filerule); // pass 1 (detections->allSources)
+    psphotReplaceAllSources (config, view, filerule, false); // pass 1 (detections->allSources)
 
     // linear fit to include all sources (subtract again)
     // NOTE : apply to ALL sources (extended + psf)
     psphotFitSourcesLinear (config, view, filerule, true); // pass 2 (detections->allSources)
-    psphotDumpChisqs (config, view, filerule);
 
     // if we only do one pass, skip to extended source analysis
@@ -240,5 +187,5 @@
 	psphotSubNoise (config, view, filerule); // pass 1 (detections->allSources)
 
-	// define new sources based on only the new peaks
+	// define new sources based on only the new peaks & measure moments
 	// NOTE: new sources are saved on detections->newSources
 	psphotSourceStats (config, view, filerule, false); // pass 2 (detections->newSources)
@@ -251,11 +198,7 @@
 	}
 
-	// create full input models, set the radius to fitRadius, set circular fit mask
-	// NOTE: apply only to detections->newSources
-	psphotGuessModels (config, view, filerule); // pass 2 (detections->newSources)
-
 	// replace all sources so fit below applies to all at once
 	// NOTE: apply only to OLD sources (which have been subtracted)
-	psphotReplaceAllSources (config, view, filerule); // pass 2
+	psphotReplaceAllSources (config, view, filerule, false); // pass 2
 
 	// merge the newly selected sources into the existing list
@@ -264,7 +207,10 @@
 	psphotMergeSources (config, view, filerule); // (detections->newSources + detections->allSources -> detections->allSources)
 
+	// Construct an initial model for each object, set the radius to fitRadius, set circular
+	// fit mask.  NOTE: only applied to sources without guess models
+	psphotGuessModels (config, view, filerule); // pass 1
+
 	// NOTE: apply to ALL sources
 	psphotFitSourcesLinear (config, view, filerule, true); // pass 3 (detections->allSources)
-	psphotDumpChisqs (config, view, filerule);
     }
 
@@ -293,11 +239,7 @@
 	}
 
-	// create full input models, set the radius to fitRadius, set circular fit mask
-	// NOTE: apply only to detections->newSources
-	psphotGuessModels (config, view, filerule); // pass 2 (detections->newSources)
-
 	// replace all sources so fit below applies to all at once
 	// NOTE: apply only to OLD sources (which have been subtracted)
-	psphotReplaceAllSources (config, view, filerule); // pass 2
+	psphotReplaceAllSources (config, view, filerule, false); // pass 2
 
 	// merge the newly selected sources into the existing list
@@ -306,4 +248,8 @@
 	psphotMergeSources (config, view, filerule); // (detections->newSources + detections->allSources -> detections->allSources)
 
+	// Construct an initial model for each object, set the radius to fitRadius, set circular
+	// fit mask.  NOTE: only applied to sources without guess models
+	psphotGuessModels (config, view, filerule); // pass 1
+
 	// NOTE: apply to ALL sources
 	psphotFitSourcesLinear (config, view, filerule, true); // pass 3 (detections->allSources)
@@ -312,6 +258,10 @@
 pass1finish:
 
-    // XXX re-measure the kron mags with models subtracted
-    psphotKronMasked(config, view, filerule);
+    // measure the radial profiles to the sky (only measures new objects)
+    psphotRadialProfileWings (config, view, filerule);
+
+    // re-measure the kron mags with models subtracted
+    // psphotKronMasked(config, view, filerule);
+    psphotKronIterate(config, view, filerule);
 
     // measure source size for the remaining sources
@@ -321,5 +271,5 @@
     psphotExtendedSourceAnalysis (config, view, filerule); // pass 1 (detections->allSources)
     psphotExtendedSourceFits (config, view, filerule); // pass 1 (detections->allSources)
-    psphotRadialApertures(config, view, filerule);
+    psphotRadialApertures(config, view, filerule, 0);
 
 finish:
@@ -359,4 +309,6 @@
     }
 
+    psLogMsg ("psphot.readout", PS_LOG_WARN, "complete psphot readout : %f sec\n", psTimerMark ("psphotReadout"));
+
     // create the exported-metadata and free local data
     return psphotReadoutCleanup(config, view, filerule);
Index: branches/meh_branches/ppstack_test/psphot/src/psphotReadoutFindPSF.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotReadoutFindPSF.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotReadoutFindPSF.c	(revision 33415)
@@ -54,4 +54,8 @@
     }
 
+    // merge the newly selected sources into the existing list
+    // NOTE: merge OLD and NEW
+    psphotMergeSources (config, view, filerule); 
+
 # if 0
     // XXX if we want to determine the aperture residual correction here, we either
@@ -61,8 +65,4 @@
     psphotGuessModels (config, view, filerule);
 # endif
-
-    // merge the newly selected sources into the existing list
-    // NOTE: merge OLD and NEW
-    psphotMergeSources (config, view, filerule); 
 
 # if 0
Index: branches/meh_branches/ppstack_test/psphot/src/psphotReadoutForcedKnownSources.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotReadoutForcedKnownSources.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotReadoutForcedKnownSources.c	(revision 33415)
@@ -36,10 +36,11 @@
     }
 
-    // construct an initial model for each object
-    psphotGuessModels (config, view, filerule);
-
     // merge the newly selected sources into the existing list
     // NOTE: merge OLD and NEW
     psphotMergeSources (config, view, filerule); 
+
+    // Construct an initial model for each object, set the radius to fitRadius, set circular
+    // fit mask.  NOTE: only applied to sources without guess models
+    psphotGuessModels (config, view, filerule);
 
     // linear PSF fit to source peaks
Index: branches/meh_branches/ppstack_test/psphot/src/psphotReadoutKnownSources.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotReadoutKnownSources.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotReadoutKnownSources.c	(revision 33415)
@@ -48,10 +48,11 @@
     }
 
-    // construct an initial model for each object
-    psphotGuessModels (config, view, filerule);
-
     // merge the newly selected sources into the existing list
     // NOTE: merge OLD and NEW
     psphotMergeSources (config, view, filerule); 
+
+    // Construct an initial model for each object, set the radius to fitRadius, set circular
+    // fit mask.  NOTE: only applied to sources without guess models
+    psphotGuessModels (config, view, filerule);
 
     // linear PSF fit to source peaks
Index: branches/meh_branches/ppstack_test/psphot/src/psphotReadoutMinimal.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotReadoutMinimal.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotReadoutMinimal.c	(revision 33415)
@@ -59,9 +59,10 @@
     }
 
-    // construct an initial model for each object
-    psphotGuessModels (config, view, filerule);
-
     // merge the newly selected sources into the existing list
     psphotMergeSources (config, view, filerule);
+
+    // Construct an initial model for each object, set the radius to fitRadius, set circular
+    // fit mask.  NOTE: only applied to sources without guess models
+    psphotGuessModels (config, view, filerule);
 
     // linear PSF fit to source peaks
Index: branches/meh_branches/ppstack_test/psphot/src/psphotReplaceUnfit.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotReplaceUnfit.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotReplaceUnfit.c	(revision 33415)
@@ -9,9 +9,9 @@
 
     for (int i = 0; i < sources->n; i++) {
-      source = sources->data[i];
-
-      // replace other sources?
-      if (source->mode & PM_SOURCE_MODE_FAIL) goto replace;
-      continue;
+	source = sources->data[i];
+
+	// replace other sources?
+	if (source->mode & PM_SOURCE_MODE_FAIL) goto replace;
+	continue;
 
     replace:
@@ -23,5 +23,5 @@
 
 // for now, let's store the detections on the readout->analysis for each readout
-bool psphotReplaceAllSources (pmConfig *config, const pmFPAview *view, const char *filerule)
+bool psphotReplaceAllSources (pmConfig *config, const pmFPAview *view, const char *filerule, bool ignoreState)
 {
     bool status = true;
@@ -35,5 +35,5 @@
     // loop over the available readouts
     for (int i = 0; i < num; i++) {
-	if (!psphotReplaceAllSourcesReadout (config, view, filerule, i, recipe)) {
+	if (!psphotReplaceAllSourcesReadout (config, view, filerule, i, recipe, ignoreState)) {
 	    psError (PSPHOT_ERR_CONFIG, false, "failed to replace all sources for %s entry %d", filerule, i);
 	    return false;
@@ -43,8 +43,7 @@
 }
 
-bool psphotReplaceAllSourcesReadout (pmConfig *config, const pmFPAview *view, const char *filename, int index, psMetadata *recipe) {
-
-    bool status;
-    pmSource *source;
+bool psphotReplaceAllSourcesReadout (pmConfig *config, const pmFPAview *view, const char *filename, int index, psMetadata *recipe, bool ignoreState) {
+
+    bool status;
 
     psTimerStart ("psphot.replace");
@@ -75,10 +74,24 @@
 
     for (int i = 0; i < sources->n; i++) {
-      source = sources->data[i];
-
-      // replace other sources?
-      if (!(source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED)) continue;
-
-      pmSourceAdd (source, PM_MODEL_OP_FULL, maskVal);
+	pmSource *source = sources->data[i];
+
+	if (ignoreState) {
+	    // rely on the type of source to decide if we subtract it or not
+
+	    // skip non-astronomical objects (very likely defects)
+	    if (source->type == PM_SOURCE_TYPE_DEFECT) continue;
+	    if (source->type == PM_SOURCE_TYPE_SATURATED) continue;
+      
+	    // do not include CRs in the full ensemble fit
+	    if (source->mode & PM_SOURCE_MODE_CR_LIMIT) continue;
+	
+	    // do not include MOMENTS_FAILURES in the fit
+	    if (source->mode & PM_SOURCE_MODE_MOMENTS_FAILURE) continue;
+	} else {
+	    // if we respect the state, do not replace unsubtracted sources
+	    if (!(source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED)) continue;
+	}
+
+	pmSourceAdd (source, PM_MODEL_OP_FULL, maskVal);
     }
 
@@ -88,5 +101,84 @@
 }
 
-bool psphotRemoveAllSources (const psArray *sources, const psMetadata *recipe) {
+// for now, let's store the detections on the readout->analysis for each readout
+bool psphotRemoveAllSources (pmConfig *config, const pmFPAview *view, const char *filerule, bool ignoreState)
+{
+    bool status = true;
+
+    // select the appropriate recipe information
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
+    psAssert (recipe, "missing recipe?");
+
+    int num = psphotFileruleCount(config, filerule);
+
+    // loop over the available readouts
+    for (int i = 0; i < num; i++) {
+	if (!psphotRemoveAllSourcesReadout (config, view, filerule, i, recipe, ignoreState)) {
+	    psError (PSPHOT_ERR_CONFIG, false, "failed to replace all sources for %s entry %d", filerule, i);
+	    return false;
+	}
+    }
+    return true;
+}
+
+bool psphotRemoveAllSourcesReadout (pmConfig *config, const pmFPAview *view, const char *filename, int index, psMetadata *recipe, bool ignoreState) {
+
+    bool status;
+
+    psTimerStart ("psphot.replace");
+
+    // find the currently selected readout
+    pmFPAfile *file = pmFPAfileSelectSingle(config->files, filename, index); // File of interest
+    psAssert (file, "missing file?");
+
+    pmReadout *readout = pmFPAviewThisReadout(view, file->fpa);
+    psAssert (readout, "missing readout?");
+
+    pmDetections *detections = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.DETECTIONS");
+    psAssert (detections, "missing detections?");
+
+    psArray *sources = detections->allSources;
+    psAssert (sources, "missing sources?");
+
+    // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
+    psImageMaskType maskVal = psMetadataLookupImageMask(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels
+    psAssert (maskVal, "missing mask value?");
+
+    // bit-mask to mark pixels not used in analysis
+    psImageMaskType markVal = psMetadataLookupImageMask(&status, recipe, "MARK.PSPHOT");
+    assert (markVal);
+
+    // maskVal is used to test for rejected pixels, and must include markVal
+    maskVal |= markVal;
+
+    for (int i = 0; i < sources->n; i++) {
+	pmSource *source = sources->data[i];
+
+	if (ignoreState) {
+	    // rely on the type of source to decide if we subtract it or not
+
+	    // skip non-astronomical objects (very likely defects)
+	    if (source->type == PM_SOURCE_TYPE_DEFECT) continue;
+	    if (source->type == PM_SOURCE_TYPE_SATURATED) continue;
+      
+	    // do not include CRs in the full ensemble fit
+	    if (source->mode & PM_SOURCE_MODE_CR_LIMIT) continue;
+	
+	    // do not include MOMENTS_FAILURES in the fit
+	    if (source->mode & PM_SOURCE_MODE_MOMENTS_FAILURE) continue;
+	} else {
+	    // if we respect the state, only remove unsubtracted sources
+	    if ((source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED)) continue;
+	}
+
+	pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
+    }
+
+    psphotVisualShowImage(readout);
+    psLogMsg ("psphot.replace", PS_LOG_INFO, "replaced models for %ld objects: %f sec\n", sources->n, psTimerMark ("psphot.replace"));
+    return true;
+}
+
+bool psphotRemoveAllSourcesByArray (const psArray *sources, const psMetadata *recipe) {
 
     bool status;
@@ -100,10 +192,10 @@
 
     for (int i = 0; i < sources->n; i++) {
-      source = sources->data[i];
-
-      // replace other sources?
-      if (source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED) continue;
-
-      pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
+	source = sources->data[i];
+
+	// replace other sources?
+	if (source->tmpFlags & PM_SOURCE_TMPF_SUBTRACTED) continue;
+
+	pmSourceSub (source, PM_MODEL_OP_FULL, maskVal);
     }
     psLogMsg ("psphot.replace", PS_LOG_INFO, "replaced models for %ld objects: %f sec\n", sources->n, psTimerMark ("psphot.replace"));
@@ -158,17 +250,17 @@
 
     for (int i = 0; i < sources->n; i++) {
-      source = sources->data[i];
-
-      // sources have not yet been subtracted in this image (but this flag may be raised)
-      source->tmpFlags &= ~PM_SOURCE_TMPF_SUBTRACTED;
-      if (!source->modelPSF) continue;
-
-      float Xo = source->modelPSF->params->data.F32[PM_PAR_XPOS];
-      float Yo = source->modelPSF->params->data.F32[PM_PAR_YPOS];
-      float radius = source->modelPSF->fitRadius;
-
-      // force a redefine to this image
-      pmSourceFreePixels(source);
-      pmSourceRedefinePixels (source, readout, Xo, Yo, radius);
+	source = sources->data[i];
+
+	// sources have not yet been subtracted in this image (but this flag may be raised)
+	source->tmpFlags &= ~PM_SOURCE_TMPF_SUBTRACTED;
+	if (!source->modelPSF) continue;
+
+	float Xo = source->modelPSF->params->data.F32[PM_PAR_XPOS];
+	float Yo = source->modelPSF->params->data.F32[PM_PAR_YPOS];
+	float radius = source->modelPSF->fitRadius;
+
+	// force a redefine to this image
+	pmSourceFreePixels(source);
+	pmSourceRedefinePixels (source, readout, Xo, Yo, radius);
     }
     return true;
@@ -229,62 +321,63 @@
 
     for (int i = 0; i < sources->n; i++) {
-      source = sources->data[i];
-
-      // *** we need to cache the 'best' model, and we have 3 cases:
-      // 1) model is the psf model --> generate from the new psf
-      // 2) model is an unconvolved extended model --> just cache the copy (not perfect)
-      // 3) model is a convolved extended model --> re-generate
-
-      // use the 'best' model to cache the model (PSF or EXT : EXT may point at one of modelFits
-      bool isPSF = false;
-      pmModel *model = pmSourceGetModel(&isPSF, source);
-      if (!model) continue;
-
-      float radius = model->fitRadius; // save for future use below
-
-      // regenerate the PSF if the model is a PSF, or if we need the PSF for a PCM
-      if (isPSF || model->isPCM) {
-	  // the guess central intensity comes from the peak:
-	  float Io = source->peak->rawFlux;
-	  float Xo = source->modelPSF->params->data.F32[PM_PAR_XPOS];
-	  float Yo = source->modelPSF->params->data.F32[PM_PAR_YPOS];
-
-	  // generate a model for this object with Io = 1.0
-	  pmModel *modelPSF = pmModelFromPSFforXY(psf, Xo, Yo, Io);
-	  if (modelPSF == NULL) {
-	      psWarning ("Failed to determine PSF model for source at (%f,%f), skipping", Xo, Yo);
-	      continue;
-	  }
-
-	  // set the source PSF model
-	  psFree (source->modelPSF);
-	  source->modelPSF = modelPSF;
-	  source->modelPSF->fitRadius = radius;
-      }
-
-      if (model->isPCM) {
-	  psAssert(false, "this section is not complete");
-
-	  pmSourceCachePSF (source, maskVal);
-
-	  psKernel *psfKernel = pmPCMkernelFromPSF(source, psfSize);
-	  if (!psfKernel) { 
-	      psWarning ("no psf kernel");
-	  }
-
-	  // generate an image of the right size
-	  psImage *rawModelFlux = psImageCopy (NULL, source->pixels, PS_TYPE_F32);
-	  psImageInit (rawModelFlux, 0.0);
+	source = sources->data[i];
+
+	// *** we need to cache the 'best' model, and we have 3 cases:
+	// 1) model is the psf model --> generate from the new psf
+	// 2) model is an unconvolved extended model --> just cache the copy (not perfect)
+	// 3) model is a convolved extended model --> re-generate
+
+	// use the 'best' model to cache the model (PSF or EXT : EXT may point at one of modelFits
+	bool isPSF = false;
+	pmModel *model = pmSourceGetModel(&isPSF, source);
+	if (!model) continue;
+
+	float radius = model->fitRadius; // save for future use below
+
+	// regenerate the PSF if the model is a PSF, or if we need the PSF for a PCM
+	if (isPSF || model->isPCM) {
+	    // the guess central intensity comes from the peak:
+	    float Io = source->peak->rawFlux;
+	    float Xo = source->modelPSF->params->data.F32[PM_PAR_XPOS];
+	    float Yo = source->modelPSF->params->data.F32[PM_PAR_YPOS];
+
+	    // generate a model for this object with Io = 1.0
+	    pmModel *modelPSF = pmModelFromPSFforXY(psf, Xo, Yo, Io);
+	    if (modelPSF == NULL) {
+		psWarning ("Failed to determine PSF model for source at (%f,%f), skipping", Xo, Yo);
+		continue;
+	    }
+
+	    // set the source PSF model
+	    psFree (source->modelPSF);
+	    source->modelPSF = modelPSF;
+	    source->modelPSF->fitRadius = radius;
+	    model = source->modelPSF;
+	}
+
+	if (model->isPCM) {
+	    psAssert(false, "this section is not complete");
+
+	    pmSourceCachePSF (source, maskVal);
+
+	    psKernel *psfKernel = pmPCMkernelFromPSF(source, psfSize);
+	    if (!psfKernel) { 
+		psWarning ("no psf kernel");
+	    }
+
+	    // generate an image of the right size
+	    psImage *rawModelFlux = psImageCopy (NULL, source->pixels, PS_TYPE_F32);
+	    psImageInit (rawModelFlux, 0.0);
 	  
-	  // insert the model image normalized to 1.0
-	  pmModelAdd (rawModelFlux, NULL, model, PM_MODEL_OP_FULL | PM_MODEL_OP_NORM, maskVal);
-
-	  psImageConvolveFFT (source->modelFlux, rawModelFlux, NULL, 0, psfKernel);
+	    // insert the model image normalized to 1.0
+	    pmModelAdd (rawModelFlux, NULL, model, PM_MODEL_OP_FULL | PM_MODEL_OP_NORM, maskVal);
+
+	    psImageConvolveFFT (source->modelFlux, rawModelFlux, NULL, 0, psfKernel);
 	  
-	  psFree (psfKernel);
-	  psFree (rawModelFlux);
-      }
-
-      pmSourceCacheModel (source, maskVal);  // ALLOC x14 (!)
+	    psFree (psfKernel);
+	    psFree (rawModelFlux);
+	}
+
+	pmSourceCacheModel (source, maskVal);  // ALLOC x14 (!)
     }
 
Index: branches/meh_branches/ppstack_test/psphot/src/psphotRoughClass.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotRoughClass.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotRoughClass.c	(revision 33415)
@@ -11,4 +11,7 @@
 {
     bool status = true;
+
+    fprintf (stdout, "\n");
+    psLogMsg ("psphot", PS_LOG_INFO, "--- psphot Rough Class ---");
 
     // select the appropriate recipe information
@@ -128,5 +131,5 @@
     psphotDumpMoments (recipe, sources);
 
-    psLogMsg ("psphot.roughclass", PS_LOG_INFO, "rough classification: %f sec\n", psTimerMark ("psphot.rough"));
+    psLogMsg ("psphot.roughclass", PS_LOG_WARN, "rough classification: %f sec\n", psTimerMark ("psphot.rough"));
 
     psphotVisualPlotMoments (recipe, readout->analysis, sources);
Index: branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.00.00.h
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.00.00.h	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.00.00.h	(revision 33415)
@@ -0,0 +1,17 @@
+ // major: C0: -0.445996271922, C1: 0.00257774203952
+ class->IndexToMajor->coeff[0] = -0.445996271922;
+ class->IndexToMajor->coeff[1] = 0.00257774203952;
+
+ // minor: C0: -0.442221950965, C1: 0.00310744939505
+ class->IndexToMinor->coeff[0] = -0.442221950965;
+ class->IndexToMinor->coeff[1] = 0.00310744939505;
+
+ // MagOffset: C0: -0.0096577152343, C1: -0.04709419632
+ class->IndexToTotalMag->coeff[0] = -0.0096577152343;
+ class->IndexToTotalMag->coeff[1] = -0.04709419632;
+
+ // KronMag: C0: -0.396760779784, C1: -0.0144654449553, C2: 0.00688206755937
+ class->KronToIndex->coeff[0] = -0.396760779784;
+ class->KronToIndex->coeff[1] = -0.0144654449553;
+ class->KronToIndex->coeff[2] = 0.00688206755937;
+
Index: branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.00.01.h
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.00.01.h	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.00.01.h	(revision 33415)
@@ -0,0 +1,17 @@
+ // major: C0: -0.150653602233, C1: 0.0583316775309
+ class->IndexToMajor->coeff[0] = -0.150653602233;
+ class->IndexToMajor->coeff[1] = 0.0583316775309;
+
+ // minor: C0: -0.146015984633, C1: 0.0584414894493
+ class->IndexToMinor->coeff[0] = -0.146015984633;
+ class->IndexToMinor->coeff[1] = 0.0584414894493;
+
+ // MagOffset: C0: -0.0516576246324, C1: -0.06308349488
+ class->IndexToTotalMag->coeff[0] = -0.0516576246324;
+ class->IndexToTotalMag->coeff[1] = -0.06308349488;
+
+ // KronMag: C0: -1.28498429043, C1: 0.255293518143, C2: -0.0209474725898
+ class->KronToIndex->coeff[0] = -1.28498429043;
+ class->KronToIndex->coeff[1] = 0.255293518143;
+ class->KronToIndex->coeff[2] = -0.0209474725898;
+
Index: branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.00.02.h
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.00.02.h	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.00.02.h	(revision 33415)
@@ -0,0 +1,17 @@
+ // major: C0: 0.0700235952113, C1: 0.0832659513906
+ class->IndexToMajor->coeff[0] = 0.0700235952113;
+ class->IndexToMajor->coeff[1] = 0.0832659513906;
+
+ // minor: C0: 0.0778318261346, C1: 0.082428734924
+ class->IndexToMinor->coeff[0] = 0.0778318261346;
+ class->IndexToMinor->coeff[1] = 0.082428734924;
+
+ // MagOffset: C0: -0.392754912322, C1: -0.0271438531326
+ class->IndexToTotalMag->coeff[0] = -0.392754912322;
+ class->IndexToTotalMag->coeff[1] = -0.0271438531326;
+
+ // KronMag: C0: -2.24835974564, C1: 0.543586359075, C2: -0.0521214274524
+ class->KronToIndex->coeff[0] = -2.24835974564;
+ class->KronToIndex->coeff[1] = 0.543586359075;
+ class->KronToIndex->coeff[2] = -0.0521214274524;
+
Index: branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.00.03.h
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.00.03.h	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.00.03.h	(revision 33415)
@@ -0,0 +1,17 @@
+ // major: C0: 0.302234153612, C1: 0.0812072518454
+ class->IndexToMajor->coeff[0] = 0.302234153612;
+ class->IndexToMajor->coeff[1] = 0.0812072518454;
+
+ // minor: C0: 0.318735595658, C1: 0.0782450906783
+ class->IndexToMinor->coeff[0] = 0.318735595658;
+ class->IndexToMinor->coeff[1] = 0.0782450906783;
+
+ // MagOffset: C0: -0.442722048421, C1: -0.0779760825812
+ class->IndexToTotalMag->coeff[0] = -0.442722048421;
+ class->IndexToTotalMag->coeff[1] = -0.0779760825812;
+
+ // KronMag: C0: -4.13616754915, C1: 1.31952307132, C2: -0.135246127816
+ class->KronToIndex->coeff[0] = -4.13616754915;
+ class->KronToIndex->coeff[1] = 1.31952307132;
+ class->KronToIndex->coeff[2] = -0.135246127816;
+
Index: branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.01.00.h
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.01.00.h	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.01.00.h	(revision 33415)
@@ -0,0 +1,17 @@
+ // major: C0: -0.206834286782, C1: -0.00161061048758
+ class->IndexToMajor->coeff[0] = -0.206834286782;
+ class->IndexToMajor->coeff[1] = -0.00161061048758;
+
+ // minor: C0: -0.206834286782, C1: -0.00161061048758
+ class->IndexToMinor->coeff[0] = -0.206834286782;
+ class->IndexToMinor->coeff[1] = -0.00161061048758;
+
+ // MagOffset: C0: -0.019943291705, C1: -0.032180165617
+ class->IndexToTotalMag->coeff[0] = -0.019943291705;
+ class->IndexToTotalMag->coeff[1] = -0.032180165617;
+
+ // KronMag: C0: -0.184713930007, C1: -0.0151666534408, C2: 0.00506733444285
+ class->KronToIndex->coeff[0] = -0.184713930007;
+ class->KronToIndex->coeff[1] = -0.0151666534408;
+ class->KronToIndex->coeff[2] = 0.00506733444285;
+
Index: branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.01.01.h
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.01.01.h	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.01.01.h	(revision 33415)
@@ -0,0 +1,17 @@
+ // major: C0: 0.10199171121, C1: 0.0370901734428
+ class->IndexToMajor->coeff[0] = 0.10199171121;
+ class->IndexToMajor->coeff[1] = 0.0370901734428;
+
+ // minor: C0: -0.64374610969, C1: 0.0615101610281
+ class->IndexToMinor->coeff[0] = -0.64374610969;
+ class->IndexToMinor->coeff[1] = 0.0615101610281;
+
+ // MagOffset: C0: -0.0424533119058, C1: -0.051484524864
+ class->IndexToTotalMag->coeff[0] = -0.0424533119058;
+ class->IndexToTotalMag->coeff[1] = -0.051484524864;
+
+ // KronMag: C0: -0.79489754688, C1: 0.140962833359, C2: -0.0081614929638
+ class->KronToIndex->coeff[0] = -0.79489754688;
+ class->KronToIndex->coeff[1] = 0.140962833359;
+ class->KronToIndex->coeff[2] = -0.0081614929638;
+
Index: branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.01.02.h
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.01.02.h	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.01.02.h	(revision 33415)
@@ -0,0 +1,17 @@
+ // major: C0: 0.253878808908, C1: 0.063184490483
+ class->IndexToMajor->coeff[0] = 0.253878808908;
+ class->IndexToMajor->coeff[1] = 0.063184490483;
+
+ // minor: C0: -0.386724307864, C1: 0.116445334002
+ class->IndexToMinor->coeff[0] = -0.386724307864;
+ class->IndexToMinor->coeff[1] = 0.116445334002;
+
+ // MagOffset: C0: -0.0872953973638, C1: -0.0728614138338
+ class->IndexToTotalMag->coeff[0] = -0.0872953973638;
+ class->IndexToTotalMag->coeff[1] = -0.0728614138338;
+
+ // KronMag: C0: -1.85587783211, C1: 0.447839373687, C2: -0.0374898116232
+ class->KronToIndex->coeff[0] = -1.85587783211;
+ class->KronToIndex->coeff[1] = 0.447839373687;
+ class->KronToIndex->coeff[2] = -0.0374898116232;
+
Index: branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.01.03.h
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.01.03.h	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.01.03.h	(revision 33415)
@@ -0,0 +1,17 @@
+ // major: C0: 0.440682409806, C1: 0.0655243267517
+ class->IndexToMajor->coeff[0] = 0.440682409806;
+ class->IndexToMajor->coeff[1] = 0.0655243267517;
+
+ // minor: C0: -0.0588380249835, C1: 0.125842487938
+ class->IndexToMinor->coeff[0] = -0.0588380249835;
+ class->IndexToMinor->coeff[1] = 0.125842487938;
+
+ // MagOffset: C0: -0.559338014369, C1: -0.0595570268259
+ class->IndexToTotalMag->coeff[0] = -0.559338014369;
+ class->IndexToTotalMag->coeff[1] = -0.0595570268259;
+
+ // KronMag: C0: -3.19558263789, C1: 1.10848141303, C2: -0.127301553664
+ class->KronToIndex->coeff[0] = -3.19558263789;
+ class->KronToIndex->coeff[1] = 1.10848141303;
+ class->KronToIndex->coeff[2] = -0.127301553664;
+
Index: branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.02.00.h
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.02.00.h	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.02.00.h	(revision 33415)
@@ -0,0 +1,17 @@
+ // major: C0: -0.0912134941416, C1: -0.000669340733451
+ class->IndexToMajor->coeff[0] = -0.0912134941416;
+ class->IndexToMajor->coeff[1] = -0.000669340733451;
+
+ // minor: C0: -0.0912134941416, C1: -0.000669340733451
+ class->IndexToMinor->coeff[0] = -0.0912134941416;
+ class->IndexToMinor->coeff[1] = -0.000669340733451;
+
+ // MagOffset: C0: -0.0221727399583, C1: -0.0223898759145
+ class->IndexToTotalMag->coeff[0] = -0.0221727399583;
+ class->IndexToTotalMag->coeff[1] = -0.0223898759145;
+
+ // KronMag: C0: -0.104030916493, C1: 0.000705683288561, C2: 0.00180404239128
+ class->KronToIndex->coeff[0] = -0.104030916493;
+ class->KronToIndex->coeff[1] = 0.000705683288561;
+ class->KronToIndex->coeff[2] = 0.00180404239128;
+
Index: branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.02.01.h
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.02.01.h	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.02.01.h	(revision 33415)
@@ -0,0 +1,17 @@
+ // major: C0: 0.231254014758, C1: 0.0296484411276
+ class->IndexToMajor->coeff[0] = 0.231254014758;
+ class->IndexToMajor->coeff[1] = 0.0296484411276;
+
+ // minor: C0: -1.02822862517, C1: 0.0541915833825
+ class->IndexToMinor->coeff[0] = -1.02822862517;
+ class->IndexToMinor->coeff[1] = 0.0541915833825;
+
+ // MagOffset: C0: -0.038775941514, C1: -0.0429605069959
+ class->IndexToTotalMag->coeff[0] = -0.038775941514;
+ class->IndexToTotalMag->coeff[1] = -0.0429605069959;
+
+ // KronMag: C0: -0.598266540255, C1: 0.144570984447, C2: -0.0107033922884
+ class->KronToIndex->coeff[0] = -0.598266540255;
+ class->KronToIndex->coeff[1] = 0.144570984447;
+ class->KronToIndex->coeff[2] = -0.0107033922884;
+
Index: branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.02.02.h
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.02.02.h	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.02.02.h	(revision 33415)
@@ -0,0 +1,17 @@
+ // major: C0: 0.390176771343, C1: 0.047444773551
+ class->IndexToMajor->coeff[0] = 0.390176771343;
+ class->IndexToMajor->coeff[1] = 0.047444773551;
+
+ // minor: C0: -0.65406916551, C1: 0.124100778941
+ class->IndexToMinor->coeff[0] = -0.65406916551;
+ class->IndexToMinor->coeff[1] = 0.124100778941;
+
+ // MagOffset: C0: -0.0701907061504, C1: -0.0669032246792
+ class->IndexToTotalMag->coeff[0] = -0.0701907061504;
+ class->IndexToTotalMag->coeff[1] = -0.0669032246792;
+
+ // KronMag: C0: -1.41116916222, C1: 0.339914508589, C2: -0.0257471931693
+ class->KronToIndex->coeff[0] = -1.41116916222;
+ class->KronToIndex->coeff[1] = 0.339914508589;
+ class->KronToIndex->coeff[2] = -0.0257471931693;
+
Index: branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.02.03.h
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.02.03.h	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.02.03.h	(revision 33415)
@@ -0,0 +1,17 @@
+ // major: C0: 0.56620879654, C1: 0.0423613195169
+ class->IndexToMajor->coeff[0] = 0.56620879654;
+ class->IndexToMajor->coeff[1] = 0.0423613195169;
+
+ // minor: C0: -0.206040045432, C1: 0.120238710256
+ class->IndexToMinor->coeff[0] = -0.206040045432;
+ class->IndexToMinor->coeff[1] = 0.120238710256;
+
+ // MagOffset: C0: -0.620063142691, C1: 0.00335965878994
+ class->IndexToTotalMag->coeff[0] = -0.620063142691;
+ class->IndexToTotalMag->coeff[1] = 0.00335965878994;
+
+ // KronMag: C0: -2.34864255166, C1: 0.766720904286, C2: -0.0877245543247
+ class->KronToIndex->coeff[0] = -2.34864255166;
+ class->KronToIndex->coeff[1] = 0.766720904286;
+ class->KronToIndex->coeff[2] = -0.0877245543247;
+
Index: branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.03.00.h
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.03.00.h	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.03.00.h	(revision 33415)
@@ -0,0 +1,17 @@
+ // major: C0: 0.00265274254899, C1: -0.00199264363239
+ class->IndexToMajor->coeff[0] = 0.00265274254899;
+ class->IndexToMajor->coeff[1] = -0.00199264363239;
+
+ // minor: C0: 0.00265274254899, C1: -0.00199264363239
+ class->IndexToMinor->coeff[0] = 0.00265274254899;
+ class->IndexToMinor->coeff[1] = -0.00199264363239;
+
+ // MagOffset: C0: -0.0238043163179, C1: -0.00786669933119
+ class->IndexToTotalMag->coeff[0] = -0.0238043163179;
+ class->IndexToTotalMag->coeff[1] = -0.00786669933119;
+
+ // KronMag: C0: -0.0238043163179, C1: -0.00786669933119, C2: -0.0877245543247
+ class->KronToIndex->coeff[0] = -0.0238043163179;
+ class->KronToIndex->coeff[1] = -0.00786669933119;
+ class->KronToIndex->coeff[2] = -0.0877245543247;
+
Index: branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.03.01.h
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.03.01.h	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.03.01.h	(revision 33415)
@@ -0,0 +1,17 @@
+ // major: C0: 0.391301293845, C1: 0.0144842856382
+ class->IndexToMajor->coeff[0] = 0.391301293845;
+ class->IndexToMajor->coeff[1] = 0.0144842856382;
+
+ // minor: C0: 0.391301293845, C1: 0.0144842856382
+ class->IndexToMinor->coeff[0] = 0.391301293845;
+ class->IndexToMinor->coeff[1] = 0.0144842856382;
+
+ // MagOffset: C0: -0.0471179132852, C1: -0.0233493549339
+ class->IndexToTotalMag->coeff[0] = -0.0471179132852;
+ class->IndexToTotalMag->coeff[1] = -0.0233493549339;
+
+ // KronMag: C0: -0.264947544849, C1: 0.0691808996903, C2: -0.00434994287966
+ class->KronToIndex->coeff[0] = -0.264947544849;
+ class->KronToIndex->coeff[1] = 0.0691808996903;
+ class->KronToIndex->coeff[2] = -0.00434994287966;
+
Index: branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.03.02.h
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.03.02.h	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.03.02.h	(revision 33415)
@@ -0,0 +1,17 @@
+ // major: C0: 0.526962575335, C1: 0.0325858343301
+ class->IndexToMajor->coeff[0] = 0.526962575335;
+ class->IndexToMajor->coeff[1] = 0.0325858343301;
+
+ // minor: C0: -1.02251162695, C1: 0.118560104626
+ class->IndexToMinor->coeff[0] = -1.02251162695;
+ class->IndexToMinor->coeff[1] = 0.118560104626;
+
+ // MagOffset: C0: -0.0716688127431, C1: -0.0502414483962
+ class->IndexToTotalMag->coeff[0] = -0.0716688127431;
+ class->IndexToTotalMag->coeff[1] = -0.0502414483962;
+
+ // KronMag: C0: -0.985092751487, C1: 0.272660295656, C2: -0.0202614753082
+ class->KronToIndex->coeff[0] = -0.985092751487;
+ class->KronToIndex->coeff[1] = 0.272660295656;
+ class->KronToIndex->coeff[2] = -0.0202614753082;
+
Index: branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.03.03.h
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.03.03.h	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotSersicGuess.03.03.h	(revision 33415)
@@ -0,0 +1,17 @@
+ // major: C0: 0.694732619604, C1: 0.0222519100811
+ class->IndexToMajor->coeff[0] = 0.694732619604;
+ class->IndexToMajor->coeff[1] = 0.0222519100811;
+
+ // minor: C0: -0.346745057966, C1: 0.0949650739266
+ class->IndexToMinor->coeff[0] = -0.346745057966;
+ class->IndexToMinor->coeff[1] = 0.0949650739266;
+
+ // MagOffset: C0: -0.754034109564, C1: 0.071069480015
+ class->IndexToTotalMag->coeff[0] = -0.754034109564;
+ class->IndexToTotalMag->coeff[1] = 0.071069480015;
+
+ // KronMag: C0: -1.16844402687, C1: 0.174859870338, C2: -0.00252564582698
+ class->KronToIndex->coeff[0] = -1.16844402687;
+ class->KronToIndex->coeff[1] = 0.174859870338;
+ class->KronToIndex->coeff[2] = -0.00252564582698;
+
Index: branches/meh_branches/ppstack_test/psphot/src/psphotSersicModelClass.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotSersicModelClass.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotSersicModelClass.c	(revision 33415)
@@ -0,0 +1,658 @@
+# include "psphotInternal.h"
+
+psF64 psPolynomial1DSolve(const psPolynomial1D* poly, psF64 y, bool upper);
+void psphotSersicModelNorm (pmPCMdata *pcm, const pmSource *source);
+
+static psArray *classArray = NULL;
+static float PSFratioMin =  1.0;
+static float PSFratioMax =  4.0;
+static float ASPratioMin =  0.2;
+static float ASPratioMax =  1.0;
+
+// structure to define the sersic model guess information
+typedef struct {
+    float PSFratioMin;
+    float PSFratioMax;
+    float ASPratioMin;
+    float ASPratioMax;
+
+    float indexMin;
+    float indexMax;
+    float kronMin;
+    float kronMax;
+
+    bool KronUpper;
+    psPolynomial1D *KronToIndex;
+    psPolynomial1D *IndexToTotalMag;
+    psPolynomial1D *IndexToMajor;
+    psPolynomial1D *IndexToMinor;
+} psphotSersicModelClass;
+
+static void psphotSersicModelClassFree (psphotSersicModelClass *class) {
+    psFree(class->KronToIndex);
+    psFree(class->IndexToTotalMag);
+    psFree(class->IndexToMajor);
+    psFree(class->IndexToMinor);
+    return;
+}
+
+psphotSersicModelClass *psphotSersicModelClassAlloc() {
+
+    psphotSersicModelClass *class = (psphotSersicModelClass *) psAlloc(sizeof(psphotSersicModelClass));
+    psMemSetDeallocator(class, (psFreeFunc) psphotSersicModelClassFree);
+    
+    class->PSFratioMin = NAN;
+    class->PSFratioMax = NAN;
+    class->ASPratioMin = NAN;
+    class->ASPratioMax = NAN;
+    
+    class->KronUpper = false;
+    class->KronToIndex = NULL;
+    class->IndexToTotalMag = NULL;
+    class->IndexToMajor = NULL;
+    class->IndexToMinor = NULL;
+
+    return class;
+}
+
+void psphotSersicModelClassSetIndexRange(psphotSersicModelClass *class, float indexMin, float indexMax) {
+
+    class->indexMin = indexMin;
+    class->indexMax = indexMax;
+    float modelMinX = -0.5 * class->KronToIndex->coeff[1] / class->KronToIndex->coeff[2];
+    float modelMinY = psPolynomial1DEval (class->KronToIndex, modelMinX);
+    float kronIndexMin = psPolynomial1DEval (class->KronToIndex, class->indexMin);
+    float kronIndexMax = psPolynomial1DEval (class->KronToIndex, class->indexMax);
+
+    // if modelMinX is between indexMin and indexMax, use the modelMinY as the min/max kron value
+    // else, use the kron values at the min and max positions
+
+    if ((modelMinX > class->indexMin) && (modelMinX < class->indexMax)) {
+	if (class->KronToIndex->coeff[2] < 0.0) {
+	    class->kronMax = modelMinY - 0.001; // pad slightly to avoid falling off the curve
+	    class->kronMin = PS_MIN(kronIndexMin, kronIndexMax);
+	} else {
+	    class->kronMin = modelMinY + 0.001; // pad slightly to avoid falling off the curve
+	    class->kronMax = PS_MAX(kronIndexMin, kronIndexMax);
+	}
+    } else {
+	class->kronMin = PS_MIN(kronIndexMin, kronIndexMax);
+	class->kronMax = PS_MAX(kronIndexMin, kronIndexMax);
+    }
+
+    // KronUpper specifies which of the 2 quadratic solutions to accept:
+    if (modelMinX < class->indexMin) {
+	if (class->KronToIndex->coeff[2] < 0.0)	{
+	    class->KronUpper = false;
+	} else {
+	    class->KronUpper = true;
+	}	
+	return;
+    }
+    if (modelMinX > class->indexMax) {
+	if (class->KronToIndex->coeff[2] < 0.0)	{
+	    class->KronUpper = true;
+	} else {
+	    class->KronUpper = false;
+	}	
+	return;
+    }
+
+    if (fabs(modelMinX - class->indexMin) < fabs(modelMinX - class->indexMax)) {
+	if (class->KronToIndex->coeff[2] < 0.0)	{
+	    class->KronUpper = false;
+	} else {
+	    class->KronUpper = true;
+	}	
+	return;
+    }
+    if (class->KronToIndex->coeff[2] < 0.0)	{
+	class->KronUpper = true;
+    } else {
+	class->KronUpper = false;
+    }	
+    return;
+}
+
+/* the Sersic model class guess system uses a set of empirically-determined fits to several
+ * relationships: index vs psf-kron mags, total mag vs index, major axis vs index, minor axis
+ * vs index.  all are functions of the aspect ratio and of the ratio between the measure major
+ * axis and the size of the psf.  Below we have the fits for these different ranges.  
+
+ * aspect ratio values: 1.0, 0.5, 0.33, 0.2
+ * psf ratio values: 0.5, 1.0, 2.0, 4.0
+
+ * psf ratio ranges: 0.5-0.8,  0.8-1.5, 1.5-3.0, 3.0-4.0
+ * apt ratio ranges: 1.0-0.71, 0.71-0.41, 0.41-0.25, 0.25-0.20
+
+ */
+
+void psphotSersicModelClassInit () {
+
+    psphotSersicModelClass *class;
+
+    if (classArray) return;
+
+    // hardwired trends for now (move into the recipe?)
+    classArray = psArrayAllocEmpty (4);
+
+    // image.00.00.fit.dat: PSFratio : 0.5, ASPratio : 1.0
+    class = psphotSersicModelClassAlloc();
+
+    class->PSFratioMin =  0.5;
+    class->PSFratioMax =  0.8;
+    class->ASPratioMin = 0.71;
+    class->ASPratioMax = 1.00;
+    class->KronToIndex = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+    class->IndexToTotalMag = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMajor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMinor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+
+# include "psphotSersicGuess.00.00.h"    
+    psphotSersicModelClassSetIndexRange(class, 1.0, 4.0);
+
+    psArrayAdd(classArray, 4, class);
+    psFree (class);
+
+    // image.00.01.fit.dat: PSFratio : 1.0, ASPratio : 1.0
+    class = psphotSersicModelClassAlloc();
+
+    class->PSFratioMin =  0.8;
+    class->PSFratioMax =  1.5;
+    class->ASPratioMin = 0.71;
+    class->ASPratioMax = 1.00;
+    class->KronToIndex = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+    class->IndexToTotalMag = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMajor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMinor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+
+# include "psphotSersicGuess.00.01.h"    
+    psphotSersicModelClassSetIndexRange(class, 1.0, 4.0);
+
+    psArrayAdd(classArray, 4, class);
+    psFree (class);
+
+    // image.00.02.fit.dat: PSFratio : 2.0, ASPratio : 1.0
+    class = psphotSersicModelClassAlloc();
+
+    class->PSFratioMin =  1.5;
+    class->PSFratioMax =  3.0;
+    class->ASPratioMin = 0.71;
+    class->ASPratioMax = 1.00;
+    class->KronToIndex = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+    class->IndexToTotalMag = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMajor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMinor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+
+# include "psphotSersicGuess.00.02.h"    
+    psphotSersicModelClassSetIndexRange(class, 1.0, 4.0);
+
+    psArrayAdd(classArray, 4, class);
+    psFree (class);
+    
+    // image.00.03.fit.dat: PSFratio : 4.0, ASPratio : 1.0
+    class = psphotSersicModelClassAlloc();
+
+    class->PSFratioMin =  3.0;
+    class->PSFratioMax =  4.0;
+    class->ASPratioMin = 0.71;
+    class->ASPratioMax = 1.00;
+    class->KronToIndex = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+    class->IndexToTotalMag = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMajor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMinor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+
+# include "psphotSersicGuess.00.03.h"    
+    psphotSersicModelClassSetIndexRange(class, 1.0, 4.0);
+    
+    psArrayAdd(classArray, 4, class);
+    psFree (class);
+
+    // image.01.00.fit.dat: PSFratio : 0.5, ASPratio : 0.5
+    class = psphotSersicModelClassAlloc();
+
+    class->PSFratioMin =  0.5;
+    class->PSFratioMax =  0.8;
+    class->ASPratioMin = 0.41;
+    class->ASPratioMax = 0.71;
+    class->KronToIndex = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+    class->IndexToTotalMag = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMajor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMinor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+
+# include "psphotSersicGuess.01.00.h"    
+    psphotSersicModelClassSetIndexRange(class, 1.0, 4.0);
+    
+    psArrayAdd(classArray, 4, class);
+    psFree (class);
+
+    // image.01.01.fit.dat: PSFratio : 1.0, ASPratio : 0.5
+    class = psphotSersicModelClassAlloc();
+
+    class->PSFratioMin =  0.8;
+    class->PSFratioMax =  1.5;
+    class->ASPratioMin = 0.41;
+    class->ASPratioMax = 0.71;
+    class->KronToIndex = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+    class->IndexToTotalMag = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMajor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMinor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+
+# include "psphotSersicGuess.01.01.h"    
+    psphotSersicModelClassSetIndexRange(class, 1.0, 4.0);
+
+    psArrayAdd(classArray, 4, class);
+    psFree (class);
+    
+    // image.01.02.fit.dat: PSFratio : 2.0, ASPratio : 0.5
+    class = psphotSersicModelClassAlloc();
+
+    class->PSFratioMin =  1.5;
+    class->PSFratioMax =  3.0;
+    class->ASPratioMin = 0.41;
+    class->ASPratioMax = 0.71;
+    class->KronToIndex = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+    class->IndexToTotalMag = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMajor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMinor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+
+# include "psphotSersicGuess.01.02.h"    
+    psphotSersicModelClassSetIndexRange(class, 1.0, 4.0);
+    
+    psArrayAdd(classArray, 4, class);
+    psFree (class);
+
+    // image.01.03.fit.dat: PSFratio : 4.0, ASPratio : 0.5
+    class = psphotSersicModelClassAlloc();
+
+    class->PSFratioMin =  3.0;
+    class->PSFratioMax =  4.0;
+    class->ASPratioMin = 0.41;
+    class->ASPratioMax = 0.71;
+    class->KronToIndex = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+    class->IndexToTotalMag = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMajor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMinor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+
+# include "psphotSersicGuess.01.03.h"    
+    psphotSersicModelClassSetIndexRange(class, 1.0, 4.0);
+    
+    psArrayAdd(classArray, 4, class);
+    psFree (class);
+
+    // image.02.00.fit.dat: PSFratio : 0.5, ASPratio : 0.33
+    class = psphotSersicModelClassAlloc();
+
+    class->PSFratioMin =  0.5;
+    class->PSFratioMax =  0.8;
+    class->ASPratioMin = 0.25;
+    class->ASPratioMax = 0.41;
+    class->KronToIndex = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+    class->IndexToTotalMag = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMajor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMinor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+
+# include "psphotSersicGuess.02.00.h"    
+    psphotSersicModelClassSetIndexRange(class, 1.0, 4.0);
+    
+    psArrayAdd(classArray, 4, class);
+    psFree (class);
+
+    // image.02.01.fit.dat: PSFratio : 1.0, ASPratio : 0.33
+    class = psphotSersicModelClassAlloc();
+
+    class->PSFratioMin =  0.8;
+    class->PSFratioMax =  1.5;
+    class->ASPratioMin = 0.25;
+    class->ASPratioMax = 0.41;
+    class->KronToIndex = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+    class->IndexToTotalMag = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMajor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMinor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+
+# include "psphotSersicGuess.02.01.h"    
+    psphotSersicModelClassSetIndexRange(class, 1.0, 4.0);
+    
+    psArrayAdd(classArray, 4, class);
+    psFree (class);
+
+    // image.02.02.fit.dat: PSFratio : 2.0, ASPratio : 0.33
+    class = psphotSersicModelClassAlloc();
+
+    class->PSFratioMin =  1.5;
+    class->PSFratioMax =  3.0;
+    class->ASPratioMin = 0.25;
+    class->ASPratioMax = 0.41;
+    class->KronToIndex = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+    class->IndexToTotalMag = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMajor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMinor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+
+# include "psphotSersicGuess.02.02.h"    
+    psphotSersicModelClassSetIndexRange(class, 1.0, 4.0);
+    
+    psArrayAdd(classArray, 4, class);
+    psFree (class);
+
+    // image.02.03.fit.dat: PSFratio : 4.0, ASPratio : 0.33
+    class = psphotSersicModelClassAlloc();
+
+    class->PSFratioMin =  3.0;
+    class->PSFratioMax =  4.0;
+    class->ASPratioMin = 0.25;
+    class->ASPratioMax = 0.41;
+    class->KronToIndex = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+    class->IndexToTotalMag = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMajor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMinor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+
+# include "psphotSersicGuess.02.03.h"    
+    psphotSersicModelClassSetIndexRange(class, 1.0, 4.0);
+    
+    psArrayAdd(classArray, 4, class);
+    psFree (class);
+
+    // image.03.00.fit.dat: PSFratio : 0.5, ASPratio : 0.2
+    class = psphotSersicModelClassAlloc();
+
+    class->PSFratioMin =  0.5;
+    class->PSFratioMax =  0.8;
+    class->ASPratioMin = 0.20;
+    class->ASPratioMax = 0.25;
+    class->KronToIndex = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+    class->IndexToTotalMag = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMajor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMinor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+
+# include "psphotSersicGuess.03.00.h"    
+    psphotSersicModelClassSetIndexRange(class, 1.0, 4.0);
+    
+    psArrayAdd(classArray, 4, class);
+    psFree (class);
+
+    // image.03.01.fit.dat: PSFratio : 1.0, ASPratio : 0.2
+    class = psphotSersicModelClassAlloc();
+
+    class->PSFratioMin =  0.8;
+    class->PSFratioMax =  1.5;
+    class->ASPratioMin = 0.20;
+    class->ASPratioMax = 0.25;
+    class->KronToIndex = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+    class->IndexToTotalMag = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMajor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMinor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+
+# include "psphotSersicGuess.03.01.h"    
+    psphotSersicModelClassSetIndexRange(class, 1.0, 4.0);
+    
+    psArrayAdd(classArray, 4, class);
+    psFree (class);
+
+    // image.03.02.fit.dat: PSFratio : 2.0, ASPratio : 0.2
+    class = psphotSersicModelClassAlloc();
+
+    class->PSFratioMin =  1.5;
+    class->PSFratioMax =  3.0;
+    class->ASPratioMin = 0.20;
+    class->ASPratioMax = 0.25;
+    class->KronToIndex = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+    class->IndexToTotalMag = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMajor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMinor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+
+# include "psphotSersicGuess.03.02.h"    
+    psphotSersicModelClassSetIndexRange(class, 1.0, 4.0);
+    
+    psArrayAdd(classArray, 4, class);
+    psFree (class);
+
+    // image.03.03.fit.dat: PSFratio : 4.0, ASPratio : 0.2
+    class = psphotSersicModelClassAlloc();
+
+    class->PSFratioMin =  3.0;
+    class->PSFratioMax =  4.0;
+    class->ASPratioMin = 0.20;
+    class->ASPratioMax = 0.25;
+    class->KronToIndex = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
+    class->IndexToTotalMag = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMajor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+    class->IndexToMinor = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 1);
+
+# include "psphotSersicGuess.03.03.h"    
+    psphotSersicModelClassSetIndexRange(class, 1.0, 4.0);
+    
+    psArrayAdd(classArray, 4, class);
+    psFree (class);
+}
+ 
+void psphotSersicModelClassCleanup () {
+    psFree (classArray);
+}
+
+# define TIMING false
+
+bool psphotSersicModelClassGuessPCM (pmPCMdata *pcm, pmSource *source) {
+
+    float t1, t2, t4, t5;
+    if (TIMING) { psTimerStart ("SersicGuess"); }
+
+    psF32 *PAR = pcm->modelConv->params->data.F32;
+
+    // XXX require: moments, psfMag, moments->kronFlux
+
+    // we attempt to guess the Sersic model parameters based on a number of non-parametric
+    // measurements: moments, kron mag, etc
+
+    // * first choose the model class based on (a) ratio of Moments Major axis to PSF major
+    // * axis and (b) moments axial ratio
+
+    // convert the moments to Major,Minor,Theta
+    psEllipseMoments moments;
+
+    moments.x2 = source->moments->Mxx;
+    moments.y2 = source->moments->Myy;
+    moments.xy = source->moments->Mxy;
+    
+    // limit axis ratio < 20.0
+    psEllipseAxes momentAxes = psEllipseMomentsToAxes (moments, 20.0);
+
+    // convert the PSF shape to Major,Minor,Theta
+    psEllipseShape shape;
+
+    // XXX make sure this is consistent with the re-definition of PM_PAR_SXX
+    shape.sx  = source->modelPSF->params->data.F32[PM_PAR_SXX] / M_SQRT2;
+    shape.sy  = source->modelPSF->params->data.F32[PM_PAR_SYY] / M_SQRT2;
+    shape.sxy = source->modelPSF->params->data.F32[PM_PAR_SXY];
+    psEllipseAxes psfAxes = psEllipseShapeToAxes (shape, 20.0);
+    
+    // get the PSFratio and the ASPratio : we use these to choose the model class
+    float PSFratio = source->moments->Mrf / (2.35 * psfAxes.major) ;
+    float ASPratio = momentAxes.minor / momentAxes.major;
+
+    // saturate PSFratio and ASPratio to min / max values
+    PSFratio = PS_MAX(PSFratio, PSFratioMin);
+    PSFratio = PS_MIN(PSFratio, PSFratioMax);
+    ASPratio = PS_MAX(ASPratio, ASPratioMin);
+    ASPratio = PS_MIN(ASPratio, ASPratioMax);
+
+    // find the containing model class:
+
+    if (TIMING) { t1 = psTimerMark ("SersicGuess"); }
+
+    psphotSersicModelClass *class = NULL;    
+    for (int i = 0; i < classArray->n; i++) {
+	psphotSersicModelClass *thisClass = classArray->data[i];
+	if (PSFratio < thisClass->PSFratioMin) continue;
+	if (PSFratio > thisClass->PSFratioMax) continue;
+	if (ASPratio < thisClass->ASPratioMin) continue;
+	if (ASPratio > thisClass->ASPratioMax) continue;
+	class = thisClass;
+	break;
+    }
+	
+    if (TIMING) { t2 = psTimerMark ("SersicGuess"); }
+
+    psAssert (class, "PSFratio and ASPratio must be in range");
+
+    // get the index guess from the KronMag - psfMag:
+
+    // get dKronMag & saturate dKronMag at limits of valid range
+    float dKronMag = -2.5*log10(source->moments->KronFlux) - source->psfMag;
+    dKronMag = PS_MIN(dKronMag, class->kronMax);
+    dKronMag = PS_MAX(dKronMag, class->kronMin);
+
+    // get index (saturate at valid ends)
+    float index = psPolynomial1DSolve (class->KronToIndex, dKronMag, class->KronUpper);
+    index = PS_MIN (index, class->indexMax);
+    index = PS_MAX (index, class->indexMin);
+    
+    // float totalMagOffset = psPolynomial1DEval (class->IndexToTotalMag, index);
+    // float totalMag = -2.5*log10(source->moments->KronFlux) + totalMagOffset;
+
+    // need to go from totalMag to Io in the sersic model
+    
+    float majorAxisFactor = psPolynomial1DEval (class->IndexToMajor, index);
+    float minorAxisFactor = psPolynomial1DEval (class->IndexToMinor, index);
+
+    momentAxes.major *= (1.0 + majorAxisFactor);
+    momentAxes.minor *= (1.0 + minorAxisFactor);
+
+    psEllipseShape extShapeGuess = psEllipseAxesToShape (momentAxes);
+    if (!isfinite(extShapeGuess.sx))  return false;
+    if (!isfinite(extShapeGuess.sy))  return false;
+    if (!isfinite(extShapeGuess.sxy)) return false;
+
+    // set the actual model parameters:
+
+    // index is standard sersic index (n = 1-4), but PAR7 = 1/2n
+    PAR[PM_PAR_7] = 0.5 / index;
+
+    // sky is zero (no longer fitted, but not yet deprecated)
+    PAR[PM_PAR_SKY]  = 0.0;
+
+    // set the model position
+    if (!pmModelSetPosition(&PAR[PM_PAR_XPOS], &PAR[PM_PAR_YPOS], source)) {
+      return false;
+    }
+    PAR[PM_PAR_SXX] = extShapeGuess.sx * M_SQRT2;
+    PAR[PM_PAR_SXY] = extShapeGuess.sxy;
+    PAR[PM_PAR_SYY] = extShapeGuess.sy * M_SQRT2;
+
+    // XXX this is a bit of a waste: calculate the flux with Io = 1.0, then renormalize.
+    PAR[PM_PAR_I0]  = 1.0;
+
+    if (TIMING) { t4 = psTimerMark ("SersicGuess"); }
+
+    // set the normalization by linear fit between model and data
+    psphotSersicModelNorm (pcm, source);
+
+    if (TIMING) { t5 = psTimerMark ("SersicGuess"); }
+
+    if (TIMING) {
+    	fprintf (stderr, "guess, t1: %6.4f, t2: %6.4f, t4: %6.4f, t5: %6.4f\n", t1, t2, t4, t5);
+    }
+
+    // float flux = pcm->modelConv->modelFlux(pcm->modelConv->params);
+    // float normMag = -2.5*log10(flux);
+    // float Io = pow(10.0, -0.4*(totalMag - normMag));
+    // PAR[PM_PAR_I0] = Io;
+
+    return true;
+}
+
+void psphotSersicModelNorm (pmPCMdata *pcm, const pmSource *source) {
+
+    psVector *params = pcm->modelConv->params;
+    // XXX : not needed? psAssert (params->data.F32[PM_PAR_I0] == 1.0, "not normalized?");
+
+    // generate the convolved model image
+    // working vector to store local coordinate
+    psVector *coord = psVectorAlloc(2, PS_TYPE_F32);
+
+    // create the convolved model in situ
+    psAssert (pcm->modelConvFlux, "not already allocated?");
+    psImageInit (pcm->modelConvFlux, 0.0);
+
+    // fill in the coordinate and value entries
+    for (psS32 i = 0; i < source->pixels->numRows; i++) {
+        for (psS32 j = 0; j < source->pixels->numCols; j++) {
+
+            // Convert i/j to image space:
+            coord->data.F32[0] = (psF32) (j + source->pixels->col0);
+            coord->data.F32[1] = (psF32) (i + source->pixels->row0);
+
+            pcm->modelConvFlux->data.F32[i][j] = pcm->modelConv->modelFunc (NULL, params, coord);
+        }
+    }
+    psFree(coord);
+    
+    psImageSmooth (pcm->modelConvFlux, pcm->sigma, pcm->nsigma);
+
+    float YYmod = 0.0;
+    float Ymod2 = 0.0;
+    bool usePoisson = false;
+
+    for (psS32 i = 0; i < source->pixels->numRows; i++) {
+        for (psS32 j = 0; j < source->pixels->numCols; j++) {
+            // XXX are we doing the right thing with the mask?
+            // skip masked points
+            if (source->maskObj->data.PS_TYPE_IMAGE_MASK_DATA[i][j]) {
+                continue;
+            }
+            // skip zero-variance points
+            if (source->variance->data.F32[i][j] == 0) {
+                continue;
+            }
+            // skip nan value points
+            if (!isfinite(source->pixels->data.F32[i][j])) {
+                continue;
+            }
+
+            float ymodel  = pcm->modelConvFlux->data.F32[i][j];
+            float yweight = (usePoisson) ? 1.0 / source->variance->data.F32[i][j] : 1.0;
+
+	    YYmod += ymodel * source->pixels->data.F32[i][j] * yweight;
+	    Ymod2 += PS_SQR(ymodel) * yweight;
+        }
+    }
+
+    float Io = YYmod / Ymod2;
+
+    params->data.F32[PM_PAR_I0] *= Io;
+
+    return;
+}
+
+// Given a polynomial y = C0 + C1 x + C2 x^2 etc, solve for x given y
+// actually: this only solves 1st and 2nd order polynomials
+// for 2nd order, it always returns the (positive/negative) term
+
+psF64 psPolynomial1DSolve(
+    const psPolynomial1D* poly,
+    psF64 y,
+    bool upper)
+{
+    psF64 x, C;
+    switch (poly->nX) {
+      case 1:
+	// y = coeff[0] + coeff[1]*x 
+	// x = (y - coeff[0]) / coeff[1]
+	x = (y - poly->coeff[0]) / poly->coeff[1];
+	return x;
+	
+      case 2:
+	// y = coeff[0] + coeff[1]*x + coeff[2]*x^2
+	// x = -coeff[1] +/- sqrt(coeff[1]^2 - 4*coeff[0]*coeff[2]) / (2 coeff[0])
+	C = poly->coeff[0] - y;
+	if (upper) {
+	    x = (-poly->coeff[1] + sqrt(PS_SQR(poly->coeff[1]) - 4*poly->coeff[2]*C)) / (2.0 * poly->coeff[2]);
+	} else {
+	    x = (-poly->coeff[1] - sqrt(PS_SQR(poly->coeff[1]) - 4*poly->coeff[2]*C)) / (2.0 * poly->coeff[2]);
+	}
+	return x;
+
+      default:
+	psAbort("invalid polynomial for 1D solver");
+    }
+    psAbort("invalid polynomial for 1D solver");
+}
Index: branches/meh_branches/ppstack_test/psphot/src/psphotSetThreads.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotSetThreads.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotSetThreads.c	(revision 33415)
@@ -30,4 +30,9 @@
     psFree(task);
 
+    task = psThreadTaskAlloc("PSPHOT_KRON_ITERATE", 7);
+    task->function = &psphotKronIterate_Threaded;
+    psThreadTaskAdd(task);
+    psFree(task);
+
     task = psThreadTaskAlloc("PSPHOT_BLEND_FIT", 10);
     task->function = &psphotBlendFit_Threaded;
@@ -40,4 +45,19 @@
     psFree(task);
 
+    task = psThreadTaskAlloc("PSPHOT_EXTENDED_ANALYSIS", 8);
+    task->function = &psphotExtendedSourceAnalysis_Threaded;
+    psThreadTaskAdd(task);
+    psFree(task);
+
+    task = psThreadTaskAlloc("PSPHOT_RADIAL_APERTURES", 6);
+    task->function = &psphotRadialApertures_Threaded;
+    psThreadTaskAdd(task);
+    psFree(task);
+
+    task = psThreadTaskAlloc("PSPHOT_RADIAL_PROFILE_WINGS", 3);
+    task->function = &psphotRadialProfileWings_Threaded;
+    psThreadTaskAdd(task);
+    psFree(task);
+
     return true;
 }
Index: branches/meh_branches/ppstack_test/psphot/src/psphotSourceFits.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotSourceFits.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotSourceFits.c	(revision 33415)
@@ -211,5 +211,5 @@
 bool psphotFitBlob (pmReadout *readout, pmSource *source, psArray *newSources, pmPSF *psf, pmSourceFitOptions *fitOptions, psImageMaskType maskVal, psImageMaskType markVal) {
 
-    float radius;
+    float fitRadius, windowRadius;
     bool okEXT, okDBL;
     pmModel *ONE = NULL;
@@ -217,5 +217,5 @@
     pmModel *EXT = NULL;
     psArray *DBL = NULL;
-    pmMoments psfMoments;
+    // pmMoments psfMoments;
 
     // skip the source if we don't think it is extended
@@ -225,18 +225,17 @@
     if (source->type == PM_SOURCE_TYPE_SATURATED) return false;
 
+# define TEST_X -420.0
+# define TEST_Y 300.0
+   
+    if ((fabs(source->peak->xf - TEST_X) < 5) && (fabs(source->peak->yf - TEST_Y) < 5)) {
+	fprintf (stderr, "test galaxy\n");
+    }
+
+# undef TEST_X
+# undef TEST_Y
+
     // set the radius based on the footprint (also sets the mask pixels)
-    if (!psphotSetRadiusFootprint(&radius, readout, source, markVal, 1.0)) return false;
-
-    // XXX note that this changes the source moments that are published...
-    // XXX all published moments should use the same measurement
-    // recalculate the source moments using the larger extended-source moments radius
-    // at this stage, skip Gaussian windowing, and do not clip pixels by S/N
-    // this uses the footprint to judge both radius and aperture?
-    // XXX save the psf-based moments for output
-    psfMoments = *source->moments;
-    if (!pmSourceMoments (source, radius, 0.0, 0.5, 0.0, maskVal)) {
-      *source->moments = psfMoments;
-      return false;
-    }
+    if (!psphotSetRadiusMoments(&fitRadius, &windowRadius, readout, source, markVal)) return false;
+    // fprintf (stderr, "rad: %6.1f %6.1f  | %5.2f %5.2f %5.2f  ", source->peak->xf, source->peak->yf, source->moments->Mrf, fitRadius, windowRadius);
 
     psTrace ("psphot", 5, "trying blob...\n");
@@ -258,4 +257,5 @@
 	okDBL  = psphotEvalDBL (tmpSrc, DBL->data[0]);
 	okDBL &= psphotEvalDBL (tmpSrc, DBL->data[1]);
+	okDBL = false; // XXX this is failing badly...
 	// XXX should I keep / save the flags set in the eval functions?
 
@@ -263,7 +263,7 @@
 	ONE = DBL->data[0];
 	if (ONE) {
-	    if (!isfinite(ONE->params->data.F32[PM_PAR_I0])) psAbort("nan in fit");
+	    psAssert (isfinite(ONE->params->data.F32[PM_PAR_I0]), "nan in fit");
 	    chiDBL = ONE->chisqNorm; // save chisq for double-star/galaxy comparison
-	    ONE->fitRadius = radius;
+	    ONE->fitRadius = fitRadius;
 	}
 
@@ -271,6 +271,6 @@
 	ONE = DBL->data[1];
 	if (ONE) {
-	    if (!isfinite(ONE->params->data.F32[PM_PAR_I0])) psAbort("nan in fit");
-	    ONE->fitRadius = radius;
+	    psAssert (isfinite(ONE->params->data.F32[PM_PAR_I0]), "nan in fit");
+	    ONE->fitRadius = fitRadius;
 	}
     }
@@ -284,4 +284,5 @@
 	okEXT = psphotEvalEXT (tmpSrc, EXT);
 	chiEXT = EXT ? EXT->chisqNorm : NAN;
+	EXT->fitRadius = fitRadius;
     }
 
@@ -294,5 +295,4 @@
 
     if (okEXT && okDBL) {
-        psTrace ("psphot", 5, "blob chisq: %f vs %f\n", chiEXT, chiDBL);
         // XXX EAM : a bogus bias: need to examine this better
         if (3*chiEXT > chiDBL) goto keepDBL;
@@ -303,8 +303,10 @@
     if (!okEXT && okDBL) goto keepDBL;
 
+    psTrace ("psphot", 4, "both failed: blob chisq: %f vs %f for %f,%f\n", chiEXT, chiDBL, source->peak->xf, source->peak->yf);
+
     // both models failed; reject them both
     // XXX -- change type flags to psf in this case, and make sure we subtract it?
     // reset the psf moments
-    *source->moments = psfMoments;
+    // XXX *source->moments = psfMoments;
 
     psFree (EXT);
@@ -313,4 +315,5 @@
 
 keepEXT:
+    psTrace ("psphot", 4, "goto EXT : blob chisq: %f vs %f for %f,%f\n", chiEXT, chiDBL, source->peak->xf, source->peak->yf);
     // sub EXT
     psFree (DBL);
@@ -338,8 +341,9 @@
 
     // reset the psf moments
-    *source->moments = psfMoments;
+    // XXX *source->moments = psfMoments;
     return true;
 
 keepDBL:
+    psTrace ("psphot", 4, "goto DBL : blob chisq: %f vs %f for %f,%f\n", chiEXT, chiDBL, source->peak->xf, source->peak->yf);
     // sub DLB
     psFree (EXT);
@@ -354,4 +358,6 @@
     pmSource *newSrc = pmSourceCopy (source);
     newSrc->modelPSF = psMemIncrRefCounter (DBL->data[1]);
+    newSrc->peak->footprint = source->peak->footprint; // just a reference; the peak does not own the footprint
+    psArrayAdd(newSrc->peak->footprint->peaks, 1, newSrc->peak); // the footprint owns the peak
 
     // build cached models and subtract
@@ -372,11 +378,6 @@
 	    psLogMsg ("psphot", 1, "PAR %d : %f +/- %f\n", i, source->modelPSF->params->data.F32[i], source->modelPSF->dparams->data.F32[i]);
 	}
-	psphotVisualShowResidualImage (readout, false);
     }
 # endif
-
-    // reset the (original) psf moments
-    *source->moments = psfMoments;
-    *newSrc->moments = psfMoments;
 
     psArrayAdd (newSources, 100, newSrc);
@@ -386,6 +387,4 @@
 
 escape:
-    // reset the psf moments
-    *source->moments = psfMoments;
     psImageMaskPixels (source->maskObj, "AND", PS_NOT_IMAGE_MASK(markVal)); 
     psFree (tmpSrc);
@@ -476,11 +475,5 @@
     // for sersic models, use a grid search to choose an index, then float the params there
     if (modelType == pmModelClassGetType("PS_MODEL_SERSIC")) {
-	// for the test fits, use a somewhat smaller radius
-	psphotSetRadiusFootprint(&model->fitRadius, readout, source, markVal, 0.5);
     	psphotFitSersicIndex (model, readout, source, fitOptions, maskVal, markVal);
-    }
-
-    if (!psphotSetRadiusModel (model, readout, source, markVal, true)) {
-	psphotSetRadiusFootprint(&model->fitRadius, readout, source, markVal, 1.0);
     }
 
@@ -494,8 +487,10 @@
     pmSourceFitModel (source, model, &options, maskVal);
     // fprintf (stderr, "chisq: %f, nIter: %d, radius: %f, npix: %d\n", model->chisqNorm, model->nIter, model->fitRadius, model->nPix);
-
     // psTraceSetLevel("psLib.math.psMinimizeLMChi2", 0);
+
     return (model);
 }
+
+# define TIMING 0
 
 pmModel *psphotFitPCM (pmReadout *readout, pmSource *source, pmSourceFitOptions *fitOptions, pmModelType modelType, psImageMaskType maskVal, psImageMaskType markVal, int psfSize) {
@@ -517,4 +512,14 @@
 	return NULL;
     }
+
+# define TEST_X -540.0
+# define TEST_Y 540.0
+   
+    if ((fabs(source->peak->xf - TEST_X) < 5) && (fabs(source->peak->yf - TEST_Y) < 5)) {
+	psTraceSetLevel("psModules.objects.pmPCM_MinimizeChisq", 5);
+    }
+
+    float t1, t2, t4, t5;
+    if (TIMING) { psTimerStart ("psphotFitPCM"); }
 
     pmPCMdata *pcm = pmPCMinit (source, &options, model, maskVal, psfSize);
@@ -524,27 +529,25 @@
 	return model;
     }
-
-    // use the source moments, etc to guess basic model parameters
-    if (!pmSourceModelGuessPCM (pcm, source, maskVal, markVal)) {
-	psFree (pcm);
-        model->flags |= PM_MODEL_STATUS_BADARGS;
-	return model;
-    }
-
-    // for sersic models, use a grid search to choose an index, then float the params there
+    if (TIMING) { t1 = psTimerMark ("psphotFitPCM"); }
+
+    // NOTE : 65 allocs to here
+    // get the guess for sersic models 
     if (modelType == pmModelClassGetType("PS_MODEL_SERSIC")) {
-	// for the test fits, use a somewhat smaller radius
-	psphotSetRadiusFootprint(&model->fitRadius, readout, source, markVal, 0.5);
-
-    	if (!psphotFitSersicIndexPCM (pcm, readout, source, fitOptions, maskVal, markVal, psfSize)) {
+	// use the source moments, etc to guess basic model parameters
+	if (!psphotSersicModelClassGuessPCM (pcm, source)) {
 	    psFree (pcm);
 	    model->flags |= PM_MODEL_STATUS_BADARGS;
 	    return model;
 	}
-    }
-
-    if (!psphotSetRadiusModel (model, readout, source, markVal, true)) {
-	psphotSetRadiusFootprint(&model->fitRadius, readout, source, markVal, 1.0);
-    }
+    } else {
+	// use the source moments, etc to guess basic model parameters
+	if (!pmSourceModelGuessPCM (pcm, source, maskVal, markVal)) {
+	    psFree (pcm);
+	    model->flags |= PM_MODEL_STATUS_BADARGS;
+	    return model;
+	}
+    }
+
+    if (TIMING) { t2 = psTimerMark ("psphotFitPCM"); }
 
     if (modelType == pmModelClassGetType("PS_MODEL_SERSIC")) {
@@ -555,8 +558,18 @@
     // update the pcm elements if we have changed the circumstance (options.mode or source->pixels)
     pmPCMupdate(pcm, source, &options, model);
+    if (TIMING) { t4 = psTimerMark ("psphotFitPCM"); }
 
     // psTraceSetLevel("psLib.math.psMinimizeLMChi2", 5);
-    pmSourceFitPCM (pcm, source, &options, maskVal, markVal, psfSize);
-    // fprintf (stderr, "chisq: %f, nIter: %d, radius: %f, npix: %d\n", model->chisqNorm, model->nIter, model->fitRadius, model->nPix);
+    pmSourceFitPCM (pcm, source, &options, maskVal, markVal, psfSize);  // NOTE : 1687 allocs in here
+    if (TIMING) { t5 = psTimerMark ("psphotFitPCM"); }
+
+    if (TIMING) {
+	int nPixBig = source->pixels->numCols * source->pixels->numRows;
+    	fprintf (stderr, "psphotFitPCM : nIter: %2d, radius: %6.1f, npix: %5d of %5d, t1: %6.4f, t2: %6.4f, t4: %6.4f, t5: %6.4f\n", model->nIter, model->fitRadius, model->nPix, nPixBig, t1, t2, t4, t5);
+    }
+
+    if ((fabs(source->peak->xf - TEST_X) < 5) && (fabs(source->peak->yf - TEST_Y) < 5)) {
+	psTraceSetLevel("psModules.objects.pmPCM_MinimizeChisq", 0);
+    }
 
     // psTraceSetLevel("psLib.math.psMinimizeLMChi2", 0);
@@ -566,7 +579,12 @@
 }
 
+# undef TEST_X
+# undef TEST_Y
+
 // note that these should be 1/2n of the standard sersic index
-float indexGuess[] = {0.5, 0.33, 0.25, 0.167, 0.125, 0.083};
-# define N_INDEX_GUESS 6
+// float indexGuess[] = {0.8, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0};
+// float indexGuess[] = {0.5, 0.33, 0.25, 0.167, 0.125, 0.083};
+float indexGuess[] = {1.0, 2.0, 3.0, 4.0};
+# define N_INDEX_GUESS 4
 
 // A sersic model is very sensitive to the index.  attempt to find the index first by grid search in just the index
@@ -586,6 +604,13 @@
     float chiSquare[N_INDEX_GUESS];
 
+# define TEST_X -540.0
+# define TEST_Y 540.0
+   
+    if ((fabs(source->peak->xf - TEST_X) < 5) && (fabs(source->peak->yf - TEST_Y) < 5)) {
+	psTraceSetLevel("psModules.objects.pmPCM_MinimizeChisq", 5);
+    }
+
     for (int i = 0; i < N_INDEX_GUESS; i++) {
-	model->params->data.F32[PM_PAR_7] = indexGuess[i];
+	model->params->data.F32[PM_PAR_7] = 0.5/indexGuess[i];
 
 	if (!model->modelGuess(model, source)) {
@@ -594,12 +619,6 @@
 	}
 
-	// each time we change the model guess, we need to adjust the radius
-	// XXX this did not work : we do not need such a large radius -- just uses moments-based radius
-	if (false && !psphotSetRadiusModel (model, readout, source, markVal, false)) {
-	    psphotSetRadiusFootprint(&model->fitRadius, readout, source, markVal, 0.5);
-	}
-	
 	pmSourceFitModel (source, model, &options, maskVal);
-	// fprintf (stderr, "chisq: %f, nIter: %d, radius: %f, npix: %d\n", model->chisqNorm, model->nIter, model->fitRadius, model->nPix);
+	// fprintf (stderr, "index: %f, chisq: %f, nIter: %d, radius: %f, npix: %d\n", indexGuess[i], model->chisqNorm, model->nIter, model->fitRadius, model->nPix);
 
 	chiSquare[i] = model->chisqNorm;
@@ -616,12 +635,11 @@
     assert (iMin >= 0);
 
+    if ((fabs(source->peak->xf - TEST_X) < 5) && (fabs(source->peak->yf - TEST_Y) < 5)) {
+	psTraceSetLevel("psModules.objects.pmPCM_MinimizeChisq", 0);
+    }
+
     model->flags = PM_MODEL_STATUS_NONE; // do not attempt to handle failures here, let the next iteration deal with it
-    model->params->data.F32[PM_PAR_7] = indexGuess[iMin];
+    model->params->data.F32[PM_PAR_7] = 0.5/indexGuess[iMin];
     model->modelGuess(model, source);
-
-    // each time we change the model guess, we need to adjust the radius
-    // if (!psphotSetRadiusModel (model, readout, source, markVal, true)) {
-    // 	psphotSetRadiusFootprint(&model->fitRadius, readout, source, markVal);
-    // }
 
     return true;
@@ -648,4 +666,8 @@
     float xMin = NAN;
     float chiSquare[N_INDEX_GUESS];
+
+    if ((fabs(source->peak->xf - TEST_X) < 5) && (fabs(source->peak->yf - TEST_Y) < 5)) {
+	psTraceSetLevel("psModules.objects.pmPCM_MinimizeChisq", 5);
+    }
 
     for (int i = 0; i < N_INDEX_GUESS; i++) {
@@ -657,11 +679,6 @@
 	}
 
-	// each time we change the model guess, we need to adjust the radius
-	// XXX this did not work : we do not need such a large radius -- just uses moments-based radius
-	if (false && !psphotSetRadiusModel (model, readout, source, markVal, false)) {
-	    psphotSetRadiusFootprint(&model->fitRadius, readout, source, markVal, 0.5);
-	}
-	
 # if (0)
+	// this block is to test the relative speed of straight and PCM fits
 	pmSourceFitModel (source, model, &options, maskVal);
 # else
@@ -669,4 +686,5 @@
 	pmSourceFitPCM (pcm, source, &options, maskVal, markVal, psfSize);
 # endif
+	fprintf (stderr, "index: %f, chisq: %f, nIter: %d, radius: %f, npix: %d\n", indexGuess[i], model->chisqNorm, model->nIter, model->fitRadius, model->nPix);
 	// fprintf (stderr, "chisq: %f, nIter: %d, radius: %f, npix: %d\n", model->chisqNorm, model->nIter, model->fitRadius, model->nPix);
 
@@ -684,4 +702,8 @@
     assert (iMin >= 0);
     
+    if ((fabs(source->peak->xf - TEST_X) < 5) && (fabs(source->peak->yf - TEST_Y) < 5)) {
+	psTraceSetLevel("psModules.objects.pmPCM_MinimizeChisq", 0);
+    }
+
     model->flags = PM_MODEL_STATUS_NONE; // do not attempt to handle failures here, let the next iteration deal with it
     model->params->data.F32[PM_PAR_7] = indexGuess[iMin];
Index: branches/meh_branches/ppstack_test/psphot/src/psphotSourceMatch.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotSourceMatch.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotSourceMatch.c	(revision 33415)
@@ -48,11 +48,7 @@
     pmDetections *detections = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.DETECTIONS");
     psAssert (detections, "missing detections?");
-    psAssert (detections->newSources, "new sources not defined?");
-    psAssert (!detections->allSources, "all sources already defined?");
-
-    // XXX TEST:
-    if (detections->newSources) {
-        psphotMatchSourcesToObjects(objects, detections->newSources, RADIUS);
-    }
+    psAssert (detections->allSources, "all sources not defined?");
+
+    psphotMatchSourcesToObjects(objects, detections->allSources, RADIUS);
 
     return true;
@@ -71,6 +67,9 @@
     objects = psArraySort (objects, pmPhotObjSortByX); 
  
-    psVector *found = psVectorAlloc(sources->n, PS_TYPE_U8);
-    psVectorInit (found, 0);
+    psVector *foundSrc = psVectorAlloc(sources->n, PS_TYPE_U8);
+    psVectorInit (foundSrc, 0);
+
+    psVector *foundObj = psVectorAlloc(objects->n, PS_TYPE_U8);
+    psVectorInit (foundObj, 0);
 
     // match sources to existing objects
@@ -97,4 +96,11 @@
         if (dx > +1.02*RADIUS) NEXT2; 
  
+	/* this block will match a given detection to the closest object within range of that detection.
+	   XXX note that this matches ALL detections within range of the single object to that same object 
+	   this is bad, but I cannot just go in linear order (ie, mark off each object as they are
+	   used).  I should make a list of all Nobj * Ndet pairs in range and choose the matches
+	   based on their separations.  UGH
+	*/
+    
         // we are within match range, look for matches: 
 	int Jmin = -1;
@@ -102,4 +108,6 @@
         for (int J = j; (dx > -1.02*RADIUS) && (J < objects->n); J++) { 
  
+	    // skip objects that are already assigned:
+	    if (foundObj->data.U8[J]) continue;
 	    obj = objects->data[J]; 
 	    
@@ -121,8 +129,9 @@
 	}
 	obj = objects->data[Jmin]; 
+	foundObj->data.U8[Jmin] = 1;
 
 	// add to object
 	pmPhotObjAddSource (obj, src);
-	found->data.U8[i] = 1;
+	foundSrc->data.U8[i] = 1;
         i++; 
     } 
@@ -132,5 +141,5 @@
     for (i = 0; i < sources->n; i++) {
 
-	if (found->data.U8[i]) continue;
+	if (foundSrc->data.U8[i]) continue;
 
         pmSource *src = sources->data[i]; 
@@ -143,5 +152,6 @@
     psLogMsg ("psphot", PS_LOG_DETAIL, "matched sources (%ld vs %ld)", sources->n, objects->n);
 
-    psFree (found);
+    psFree (foundSrc);
+    psFree (foundObj);
     return true;
 } 
@@ -238,10 +248,12 @@
 	    
 	    // assign to a footprint on this readout->image
-	    peak->footprint = pmFootprintCopyData(footprint, readout->image);
-
-	    // the peak does not claim ownership of the footprint (it does not free it). save a copy of this 
-	    // footprint on detections->footprints so we can free it later
-	    psArrayAdd(detections->footprints, 100, peak->footprint); 
-	    psFree (peak->footprint);
+	    if (footprint) {
+		peak->footprint = pmFootprintCopyData(footprint, readout->image);
+
+		// the peak does not claim ownership of the footprint (it does not free it). save a copy of this 
+		// footprint on detections->footprints so we can free it later
+		psArrayAdd(detections->footprints, 100, peak->footprint); 
+		psFree (peak->footprint);
+	    }
 	    
 	    // create a new source
@@ -249,4 +261,5 @@
 	    source->imageID = index;
 	    source->mode2 |= PM_SOURCE_MODE2_MATCHED; // source is generated based on another image
+	    source->type = PM_SOURCE_TYPE_STAR; // until we know more, assume a PSF fit
 
 	    // add the peak
@@ -261,5 +274,5 @@
 	    peak->assigned = true;
 	    pmPhotObjAddSource(obj, source);
-	    psArrayAdd (detections->newSources, 100, source);
+	    psArrayAdd (detections->allSources, 100, source);
 	    psFree (source);
 	}
@@ -272,4 +285,5 @@
         pmPhotObj *obj = objects->data[i]; 
 	nSources += obj->sources->n;
+	psAssert (obj->sources->n == nImages, "failed to match sources?");
     }
     psLogMsg ("psphot", PS_LOG_DETAIL, "total of %d sources for %d images", nSources, nImages);
Index: branches/meh_branches/ppstack_test/psphot/src/psphotSourceSize.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotSourceSize.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotSourceSize.c	(revision 33415)
@@ -45,4 +45,7 @@
     bool status = true;
 
+    fprintf (stdout, "\n");
+    psLogMsg ("psphot", PS_LOG_INFO, "--- psphot Source Size ---");
+
     // select the appropriate recipe information
     psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
@@ -153,5 +156,5 @@
 
     // XXX fix this (was source->n  - first)
-    psLogMsg ("psphot.size", PS_LOG_INFO, "measure source sizes for %ld sources: %f sec\n", sources->n, psTimerMark ("psphot.size"));
+    psLogMsg ("psphot.size", PS_LOG_WARN, "measure source sizes for %ld sources: %f sec\n", sources->n, psTimerMark ("psphot.size"));
 
     psphotVisualPlotSourceSize (recipe, readout->analysis, sources);
@@ -209,5 +212,5 @@
         pmSourceMagnitudes (source, psf, photMode, maskVal, markVal, source->apRadius);
 
-        float kMag = -2.5*log10(source->moments->KronFlux);
+        float kMag = -2.5*log10(source->moments->KronFluxPSF);
         float dMag = source->psfMag - kMag;
 
@@ -334,5 +337,5 @@
         psF32 Mxy = source->moments->Mxy;
 
-        float KronMag = -2.5*log10(source->moments->KronFlux);
+        float KronMag = -2.5*log10(source->moments->KronFluxPSF);
 
 	float Mminor = 0.5*(Mxx + Myy) - 0.5*sqrt(PS_SQR(Mxx - Myy) + 4.0*PS_SQR(Mxy));
@@ -509,5 +512,5 @@
         pmSourceSub (source, PM_MODEL_OP_FULL, options->maskVal);
 
-        float kMag = -2.5*log10(source->moments->KronFlux);
+        float kMag = -2.5*log10(source->moments->KronFluxPSF);
         float dMag = source->psfMag - kMag;
 
Index: branches/meh_branches/ppstack_test/psphot/src/psphotSourceStats.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotSourceStats.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotSourceStats.c	(revision 33415)
@@ -10,4 +10,7 @@
     bool status = true;
 
+    fprintf (stdout, "\n");
+    psLogMsg ("psphot", PS_LOG_INFO, "--- psphot Source Stats ---");
+
     // select the appropriate recipe information
     psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
@@ -98,10 +101,8 @@
 
     // generate the array of sources, define the associated pixel
-    bool firstPass = false;
-    if (!detections->newSources) {
-        detections->newSources = psArrayAllocEmpty (peaks->n);
-	firstPass = true;
-    }
-    sources = detections->newSources;
+    psAssert (!detections->newSources, "programming error");
+    sources = detections->newSources = psArrayAllocEmpty (peaks->n);
+
+    bool firstPass = (detections->allSources == NULL);
 
     // if there are no peaks, we save the empty source array and return
@@ -121,4 +122,6 @@
         // add the peak
         source->peak = psMemIncrRefCounter(peak);
+
+	// psAssert (source->peak->footprint, "peak without footprint??");
 
         // allocate space for moments
@@ -213,5 +216,5 @@
 
         // wait for the threads to finish and manage results
-        if (!psThreadPoolWait (false)) {
+        if (!psThreadPoolWait (false, true)) {
             psError(PS_ERR_UNKNOWN, false, "Failure in thread job PSPHOT_SOURCE_STATS");
             psFree(detections->newSources);
@@ -239,5 +242,5 @@
     psFree (cellGroups);
 
-    psLogMsg ("psphot", PS_LOG_INFO, "%ld sources, %d moments, %d faint, %d failed: %f sec\n", sources->n, Nmoments, Nfaint, Nfail, psTimerMark ("psphot.stats"));
+    psLogMsg ("psphot", PS_LOG_WARN, "%ld sources, %d moments, %d faint, %d failed: %f sec\n", sources->n, Nmoments, Nfaint, Nfail, psTimerMark ("psphot.stats"));
 
     psphotVisualShowMoments (sources);
@@ -344,5 +347,5 @@
 
         // wait for the threads to finish and manage results
-        if (!psThreadPoolWait (false)) {
+        if (!psThreadPoolWait (false, true)) {
             psError(PS_ERR_UNKNOWN, false, "Failure in thread job PSPHOT_SOURCE_STATS");
             return NULL;
@@ -554,5 +557,5 @@
         // determine the PSF parameters from the source moment values
         pmPSFClump psfClump = pmSourcePSFClump (NULL, NULL, sources, PSF_SN_LIM, PSF_CLUMP_GRID_SCALE, MOMENTS_SX_MAX, MOMENTS_SY_MAX, MOMENTS_SX_MIN, MOMENTS_SY_MIN, MOMENTS_AR_MAX);
-        psLogMsg ("psphot", 3, "radius %.1f, nStars: %d of %d in clump, nSigma: %5.2f, X,  Y: %f, %f (%f, %f)\n", sigma[i], psfClump.nStars, psfClump.nTotal, psfClump.nSigma, psfClump.X, psfClump.Y, sqrt(psfClump.X) / sigma[i], sqrt(psfClump.Y) / sigma[i]);
+        psLogMsg ("psphot", 3, "sigma guess (pix) %.1f, nStars: %d of %d in clump, nSigma: %5.2f, X,  Y: %f, %f (%f, %f)\n", sigma[i], psfClump.nStars, psfClump.nTotal, psfClump.nSigma, psfClump.X, psfClump.Y, sqrt(psfClump.X) / sigma[i], sqrt(psfClump.Y) / sigma[i]);
 
 	Rmin[i] = pmSourceMinKronRadius(sources, PSF_SN_LIM);
Index: branches/meh_branches/ppstack_test/psphot/src/psphotStackChisqImage.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotStackChisqImage.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotStackChisqImage.c	(revision 33415)
@@ -13,4 +13,6 @@
     psAssert (chisqFile, "missing chisq image FPA?");
 
+    // the readout containing the chisq image is generated in the first pass of this loop and
+    // used by the successive passes
     pmReadout *chiReadout = NULL;
 
Index: branches/meh_branches/ppstack_test/psphot/src/psphotStackImageLoop.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotStackImageLoop.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotStackImageLoop.c	(revision 33415)
@@ -10,4 +10,5 @@
 bool UpdateHeadersForFPA (pmConfig *config, pmFPAview *view);
 bool UpdateHeadersForChip (pmConfig *config, pmFPAview *view);
+bool UpdateHeadersForReadout (pmConfig *config, pmFPAview *view);
 
 bool psphotStackImageLoop (pmConfig *config) {
@@ -52,5 +53,5 @@
 		psMemDump("load");
 
-		// PSF matching
+		// Generate the 1st PSF-matched image set (larger target PSFs are generated by smoothing this image)
 		if (!psphotStackMatchPSFs (config, view)) {
                     psError(psErrorCodeLast(), false, "failure in psphotStackMatchPSFs for chip %d, cell %d, readout %d\n", view->chip, view->cell, view->readout);
@@ -66,4 +67,7 @@
                     return false;
 		}
+
+		UpdateHeadersForReadout(config, view);
+
 		psMemDump("psphot");
 	    }
@@ -101,4 +105,33 @@
 }
 
+bool UpdateHeadersForReadout (pmConfig *config, pmFPAview *view) {
+
+    psMetadata *recipe = psMetadataLookupPtr (NULL, config->recipes, PSPHOT_RECIPE);
+    bool useRaw = psMetadataLookupBool (NULL, recipe, "PSPHOT.STACK.USE.RAW");
+
+    int num = psphotFileruleCount(config, "PSPHOT.INPUT");
+
+    // loop over the available readouts
+    for (int i = 0; i < num; i++) {
+
+	// find the currently selected readout
+	pmFPAfile *output = pmFPAfileSelectSingle(config->files, "PSPHOT.STACK.OUTPUT.IMAGE", i); // File of interest
+	psAssert (output, "missing file?");
+
+	pmFPAfile *inputRaw = pmFPAfileSelectSingle(config->files, "PSPHOT.STACK.INPUT.RAW", i); // File of interest
+	pmFPAfile *inputCnv = pmFPAfileSelectSingle(config->files, "PSPHOT.STACK.INPUT.CNV", i); // File of interest
+	pmFPAfile *input = useRaw ? inputRaw : inputCnv;
+	psAssert (input, "missing input file");
+
+	// just copy the input headers to the output headers, then update version info
+	pmReadout *inReadout = pmFPAviewThisReadout(view, input->fpa); ///< Chip in the input
+	pmReadout *outReadout = pmFPAviewThisReadout(view, output->fpa); ///< Chip in the output
+
+	psMetadata *header = psMetadataLookupPtr (NULL, inReadout->analysis, "PSPHOT.HEADER");
+	psMetadataAdd (outReadout->analysis, PS_LIST_TAIL, "PSPHOT.HEADER",  PS_DATA_METADATA, "header stats", header);
+    }
+    return true;
+}
+
 bool UpdateHeadersForChip (pmConfig *config, pmFPAview *view) {
 
@@ -131,4 +164,5 @@
 	    outHDU->header = psMetadataCopy(NULL, inHDU->header);
 	}
+	psphotVersionHeaderFull(outHDU->header);
 	outChip->toFPA = psMemIncrRefCounter(inChip->toFPA);
 	outChip->fromFPA = psMemIncrRefCounter(inChip->fromFPA);
Index: branches/meh_branches/ppstack_test/psphot/src/psphotStackMatchPSFs.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotStackMatchPSFs.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotStackMatchPSFs.c	(revision 33415)
@@ -25,6 +25,7 @@
     }
 
-    // loop over the available readouts (ignore chisq image)
+    // loop over the available readouts
     for (int i = 0; i < num; i++) {
+	// set up the PSF-matching parameters describing the input images
 	if (!psphotStackMatchPSFsPrepare (config, view, options, i)) {
             psError (PSPHOT_ERR_CONFIG, false, "failed to set PSF matching options for entry %d", i);
@@ -33,19 +34,12 @@
     }
 
-    // Generate target PSF
+    // XXX convolve == false might not be valid at the moment
     if (options->convolve) {
-        options->psf = psphotStackPSF(config, options->numCols, options->numRows, options->psfs, options->inputMask);
-        if (!options->psf) {
+	// Determine the 1st target PSF (either AUTO or defined by PSPHOT.STACK.TARGET.PSF.FWHM)
+	// NOTE: this also set the full list of target FWHMs (options->targetSeeing)
+        if (!psphotStackPSF(config, options)) {
             psError(psErrorCodeLast(), false, "Unable to determine output PSF.");
             return false;
         }
-        psMetadataAddPtr(config->arguments, PS_LIST_TAIL, "PSF.TARGET", PS_DATA_UNKNOWN, "Target PSF for stack", options->psf);
-        options->targetSeeing = pmPSFtoFWHM(options->psf, 0.5 * options->numCols, 0.5 * options->numRows); // FWHM for target
-        psLogMsg("psphotStack", PS_LOG_INFO, "Target seeing FWHM: %f\n", options->targetSeeing);
-
-	// XXX is this needed to supply the psf to psphot??
-        // pmChip *outChip = pmFPAfileThisChip(config->files, view, "PPSTACK.TARGET.PSF"); // Output chip
-        // psMetadataAddPtr(outChip->analysis, PS_LIST_TAIL, "PSPHOT.PSF", PS_DATA_UNKNOWN, "Target PSF", options->psf);
-        // outChip->data_exists = true;
     }
 
@@ -63,4 +57,5 @@
 
 // convolve the image to match desired PSF
+// XXX is this code consistent with 'convolve' = false?
 bool psphotStackMatchPSFsReadout (pmConfig *config, const pmFPAview *view, psphotStackOptions *options, int index) {
 
@@ -106,19 +101,28 @@
     // Image Matching (PSFs or just flux)
     if (options->convolve) {
-	matchKernel(config, readoutOut, readoutSrc, options, index);
+	if (!matchKernel(config, readoutOut, readoutSrc, options, index)){
+	    psError(psErrorCodeLast(), false, "Unable to match image PSF in readout.");
+	    return false;
+	}
 	saveMatchData(readoutOut, options, index);
     }
-    rescaleData(readoutOut, config, options, index);
+
+    // renormalize the stack variances to have sigma = 1.0
+    if (!psphotStackRenormaliseVariance(config, readoutOut)) {
+        psError(psErrorCodeLast(), false, "Unable to renormalise variance.");
+        return false;
+    }
 
     // save the output fwhm values in the readout->analysis.  we may have / will have multiple output PSF sizes,
     // so we save this in a vector.  if the vector is not yet defined, create it
-    bool mdok = false;
-    psVector *fwhmValues = psMetadataLookupVector(&mdok, readoutOut->analysis, "STACK.PSF.FWHM.VALUES");
-    if (!fwhmValues) {
-	fwhmValues = psVectorAllocEmpty(10, PS_TYPE_F32);
-	psMetadataAddVector(readoutOut->analysis, PS_LIST_TAIL, "STACK.PSF.FWHM.VALUES", PS_META_REPLACE, "PSF sizes", fwhmValues);
-	psFree(fwhmValues); // drops the extra copy
+    // NOTE: fwhmValues as defined here has 1 + nMatched PSF : 0 == unmatched
+    psVector *fwhmValues = psVectorAllocEmpty(10, PS_TYPE_F32);
+    psVectorAppend(fwhmValues, NAN); // XXX this corresponds to the unmatched image set
+    for (int i = 0; i < options->targetSeeing->n; i++) {
+	psVectorAppend(fwhmValues, options->targetSeeing->data.F32[i]);
     }
-    psVectorAppend(fwhmValues, options->targetSeeing);
+    psMetadataAddVector(readoutSrc->analysis, PS_LIST_TAIL, "STACK.PSF.FWHM.VALUES", PS_META_REPLACE, "PSF sizes", fwhmValues);
+    psMetadataAddVector(readoutOut->analysis, PS_LIST_TAIL, "STACK.PSF.FWHM.VALUES", PS_META_REPLACE, "PSF sizes", fwhmValues);
+    psFree(fwhmValues); // drops the extra copy
 
     return true;
Index: branches/meh_branches/ppstack_test/psphot/src/psphotStackMatchPSFsNext.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotStackMatchPSFsNext.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotStackMatchPSFsNext.c	(revision 33415)
@@ -1,3 +1,26 @@
 # include "psphotInternal.h"
+
+// NOTE : element 0 of fwhmValues if the unmatched image,  
+
+int psphotStackMatchPSFsEntries (pmConfig *config, const pmFPAview *view, const char *filerule) {
+
+    int nRadialEntries = 0;
+
+    // find the currently selected readout
+    pmFPAfile *file = pmFPAfileSelectSingle(config->files, filerule, 0); // File of interest
+    psAssert (file, "missing file?");
+    
+    pmReadout *readout = pmFPAviewThisReadout(view, file->fpa);
+    psAssert (readout, "missing readout?");
+    
+    bool status = false;
+    psVector *fwhmValues = psMetadataLookupVector(&status, readout->analysis, "STACK.PSF.FWHM.VALUES");
+    if (!fwhmValues) {
+	return 1;
+    }
+    
+    nRadialEntries = fwhmValues->n;
+    return nRadialEntries;
+}
 
 // smooth the input image to match the next target PSF
@@ -5,25 +28,6 @@
 // and that the smoothing can use a 1D Gaussian kernel of width sqrt(TARGET^2 - CURRENT^2)
 // each subsequent call
-bool psphotStackMatchPSFsNext(bool *smoothAgain, pmConfig *config, const pmFPAview *view, const char *filerule, int lastSize)
+bool psphotStackMatchPSFsNext(pmConfig *config, const pmFPAview *view, const char *filerule, int lastSize)
 {
-    bool status = true;
-
-    // select the appropriate recipe information
-    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
-    psAssert (recipe, "missing recipe?");
-
-    psVector *fwhmValues = psMetadataLookupVector(&status, recipe, "PSPHOT.STACK.TARGET.PSF.FWHM"); // Magnitude offsets
-    if (!status) {
-	// must not be a vector, only one value requested
-	*smoothAgain = false;
-	return true;
-    }
-
-    if (lastSize + 1 >= fwhmValues->n) {
-	// all done with target FWHM values 
-	*smoothAgain = false;
-	return true;
-    }
-
     int num = psphotFileruleCount(config, filerule);
 
@@ -33,6 +37,6 @@
     // loop over the available readouts
     for (int i = 0; i < num; i++) {
-	if (!psphotStackMatchPSFsNextReadout (config, view, filerule, i, recipe, fwhmValues, lastSize)) {
-            psError (PSPHOT_ERR_CONFIG, false, "failed to smooth image %s (%d) to target PSF %f", filerule, i, fwhmValues->data.F32[lastSize+1]);
+	if (!psphotStackMatchPSFsNextReadout (config, view, filerule, i, lastSize)) {
+            psError (PSPHOT_ERR_CONFIG, false, "failed to smooth image %s (%d) to target PSF", filerule, i);
 	    psImageConvolveSetThreads(oldThreads);
 	    return false;
@@ -44,7 +48,9 @@
 }
 
-bool psphotStackMatchPSFsNextReadout (pmConfig *config, const pmFPAview *view, const char *filerule, int index, psMetadata *recipe, psVector *fwhmValues, int lastSize) {
+bool psphotStackMatchPSFsNextReadout (pmConfig *config, const pmFPAview *view, const char *filerule, int index, int lastSize) {
 
     bool status = false;
+
+    psTimerStart ("psphot.smooth");
 
     // find the currently selected readout
@@ -58,4 +64,8 @@
     psphotVisualShowImage(readout);
 
+    // select the appropriate recipe information
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
+    psAssert (recipe, "missing recipe?");
+
     // user-defined masks to test for good/bad pixels (build from recipe list if not yet set)
     psImageMaskType maskVal = psMetadataLookupImageMask(&status, recipe, "MASK.PSPHOT"); // Mask value for bad pixels
@@ -66,4 +76,11 @@
         psWarning("PEAKS_MIN_GAUSS is not set in recipe; using default value");
         minGauss = 0.5;
+    }
+
+    psVector *fwhmValues = psMetadataLookupVector(&status, readout->analysis, "STACK.PSF.FWHM.VALUES");
+    psAssert (fwhmValues, "need target PSFs");
+
+    if (lastSize + 1 >= fwhmValues->n) {
+	return true;
     }
 
@@ -128,11 +145,4 @@
     // psMetadataAddF32(readout->analysis, PS_LIST_TAIL, "SIGNIFICANCE_SCALE_FACTOR", PS_META_REPLACE, "Signicance scale factor", factor);
 
-    // save the output fwhm values in the readout->analysis.  we may have / will have multiple output PSF sizes,
-    // so we save this in a vector.  if the vector is not yet defined, create it
-
-    psVector *fwhmValuesOut = psMetadataLookupVector(&status, readout->analysis, "STACK.PSF.FWHM.VALUES");
-    psAssert(fwhmValuesOut, "should already exist..");
-    psVectorAppend(fwhmValuesOut, targetFWHM);
-
     // do not generate a PSF if we already were supplied one
     pmPSF *psfOld = psMetadataLookupPtr (&status, readout->analysis, "PSPHOT.PSF");
Index: branches/meh_branches/ppstack_test/psphot/src/psphotStackMatchPSFsPrepare.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotStackMatchPSFsPrepare.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotStackMatchPSFsPrepare.c	(revision 33415)
@@ -4,5 +4,5 @@
 bool determineSeeing (pmPSF *psf, psphotStackOptions *options, int index);
 
-// set up the stacking parameters
+// set up the PSF-matching parameters describing the input images
 bool psphotStackMatchPSFsPrepare (pmConfig *config, const pmFPAview *view, psphotStackOptions *options, int index) {
 
Index: branches/meh_branches/ppstack_test/psphot/src/psphotStackMatchPSFsUtils.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotStackMatchPSFsUtils.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotStackMatchPSFsUtils.c	(revision 33415)
@@ -2,30 +2,10 @@
 # define ARRAY_BUFFER 16                 // Number to add to array at a time
 
-// XXX better name
-bool readImage(psImage **target, // Target for image
-	       const char *name, // Name of FITS file
-	       const pmConfig *config // Configuration
-    )
-{
-    psString resolved = pmConfigConvertFilename(name, config, false, false); // Resolved filename
-    psFits *fits = psFitsOpen(resolved, "r");
-    psFree(resolved);
-    if (!fits) {
-        psError(PSPHOT_ERR_IO, false, "Unable to open previously produced image: %s", name);
-        return false;
-    }
-    psImage *image = psFitsReadImage(fits, psRegionSet(0,0,0,0), 0); // Image of interest
-    if (!image) {
-        psError(PSPHOT_ERR_IO, false, "Unable to read previously produced image: %s", name);
-        psFitsClose(fits);
-        return false;
-    }
-    psFitsClose(fits);
-
-    psFree(*target);
-    *target = image;
-
-    return true;
-}
+psVector *SetOptWidths (bool *optimum, psMetadata *recipe);
+pmReadout *makeFakeReadout(pmConfig *config, pmReadout *raw, psArray *sources, pmPSF *psf, psImageMaskType maskVal, int fullSize);
+bool saveMatchData (pmReadout *readout, psphotStackOptions *options, int index);
+bool matchKernel(pmConfig *config, pmReadout *cnv, pmReadout *raw, psphotStackOptions *options, int index);
+bool dumpImageDiff(pmReadout *readoutConv, pmReadout *readoutFake, pmReadout *readoutRef, int index, char *rootname);
+bool dumpImage(pmReadout *readoutOut, pmReadout *readoutRef, int index, char *rootname);
 
 // Get coordinates from a source
@@ -148,6 +128,6 @@
 
 // Renormalise a readout's variance map
-bool stackRenormaliseReadout(const pmConfig *config, // Configuration
-                             pmReadout *readout      // Readout to renormalise
+bool psphotStackRenormaliseVariance(const pmConfig *config, // Configuration
+			      pmReadout *readout      // Readout to renormalise
     )
 {
@@ -180,59 +160,4 @@
     return pmReadoutVarianceRenormalise(readout, maskBad, num, minValid, maxValid);
 }
-
-// This is a hack to use the temporary convolved images and kernel generated previously.
-// This makes the 'matching' operation much faster, allowing debugging of the stack process easier.
-// It implicitly assumes the output root name is the same between invocations.
-
-# if (0)
-bool loadKernel (pmConfig *config, pmReadout *readoutCnv, psphotStackOptions *options, int index) {
-
-    // Read the convolution kernel from the saved file
-    pmFPAfile *file = pmFPAfileSelectSingle(config->files, "PPSTACK.CONV.KERNEL", index);
-    psAssert(file, "Require file");
-
-    pmFPAview *view = pmFPAviewAlloc(0); // View to readout of interest
-    view->chip = view->cell = view->readout = 0;
-    psString filename = pmFPAfileNameFromRule(file->filerule, file, view); // Filename of interest
-
-    // Read convolution kernel data
-    psString resolved = pmConfigConvertFilename(filename, config, false, false); // Resolved filename
-    psFree(filename);
-    psFits *fits = psFitsOpen(resolved, "r"); // FITS file for subtraction kernel
-    psFree(resolved);
-    if (!fits || !pmReadoutReadSubtractionKernels(readoutCnv, fits)) {
-	psError(PSPHOT_ERR_IO, false, "Unable to read previously produced kernel");
-	psFitsClose(fits);
-	return false;
-    }
-    psFitsClose(fits);
-
-    // read the convolved pixels (image, mask, variance) -- names are pre-defined
-    if (!readImage(&readoutCnv->image,    options->convImages->data[index],    config) ||
-	!readImage(&readoutCnv->mask,     options->convMasks->data[index],     config) ||
-	!readImage(&readoutCnv->variance, options->convVariances->data[index], config)) {
-	psError(PSPHOT_ERR_IO, false, "Unable to read previously produced image.");
-	return false;
-    }
-
-    // XXX ??? not sure what is happening here -- consult Paul
-    psRegion *region = psMetadataLookupPtr(NULL, readoutCnv->analysis, PM_SUBTRACTION_ANALYSIS_REGION); // Convolution region
-    pmSubtractionKernels *kernels = psMetadataLookupPtr(NULL, readoutCnv->analysis, PM_SUBTRACTION_ANALYSIS_KERNEL);
-
-    pmSubtractionAnalysis(readoutCnv->analysis, NULL, kernels, region, readoutCnv->image->numCols, readoutCnv->image->numRows);
-
-    psKernel *kernel = pmSubtractionKernel(kernels, 0.0, 0.0, false); // Convolution kernel
-
-    // update the covariance matrix 
-    // XXX why is this needed if we have correctly read the saved data?
-    bool oldThreads = psImageCovarianceSetThreads(true);              // Old thread setting
-    psKernel *covar = psImageCovarianceCalculate(kernel, readoutCnv->covariance); // Covariance matrix
-    psImageCovarianceSetThreads(oldThreads);
-    psFree(readoutCnv->covariance);
-    readoutCnv->covariance = covar;
-    psFree(kernel);
-    return true;
-}
-# endif
 
 bool dumpImage(pmReadout *readoutOut, pmReadout *readoutRef, int index, char *rootname) {
@@ -362,6 +287,6 @@
 	widthsCopy = psVectorCopy(NULL, widths, PS_TYPE_F32); // Copy of kernel widths
 
-	// we need to register the FWHM values for use downstream 
-	pmSubtractionSetFWHMs(options->targetSeeing, options->inputSeeing->data.F32[index]);
+	// we need to register the FWHM values for use by pmSubtraction code
+	pmSubtractionSetFWHMs(options->targetSeeing->data.F32[0], options->inputSeeing->data.F32[index]);
 
 	pmSubtractionParamScaleOptions(scale, scaleRef, scaleMin, scaleMax);
@@ -469,90 +394,4 @@
 }
 
-// Kernel normalisation for convolved readout
-bool renormKernel(pmReadout *readout, psphotStackOptions *options, int index) {
-
-    double sum = 0.0;           // Sum of chi^2
-    int num = 0;                // Number of measurements of chi^2
-    psString regex = NULL;      // Regular expression
-    psStringAppend(&regex, "^%s$", PM_SUBTRACTION_ANALYSIS_NORM);
-    psMetadataIterator *iter = psMetadataIteratorAlloc(readout->analysis, PS_LIST_HEAD, regex);
-    psFree(regex);
-    psMetadataItem *item = NULL;// Item from iteration
-    while ((item = psMetadataGetAndIncrement(iter))) {
-	assert(item->type == PS_TYPE_F32);
-	float norm = item->data.F32; // Normalisation
-	sum += norm;
-	num++;
-    }
-    psFree(iter);
-    float conv = sum/num;       // Mean normalisation from convolution
-    float stars = powf(10.0, -0.4 * options->norm->data.F32[index]); // Normalisation from stars
-    float renorm =  stars / conv; // Renormalisation to apply
-    psLogMsg("psphotStack", PS_LOG_INFO, "Renormalising image %d by %f (kernel: %f, stars: %f)\n", index, renorm, conv, stars);
-
-    psBinaryOp(readout->image, readout->image, "*", psScalarAlloc(renorm, PS_TYPE_F32));
-    psBinaryOp(readout->variance, readout->variance, "*", psScalarAlloc(PS_SQR(renorm), PS_TYPE_F32));
-    return true;
-}
-
-// adjust scaling for readout (remove background, ..., determine weighting)
-bool rescaleData(pmReadout *readout, pmConfig *config, psphotStackOptions *options, int index) {
-
-    psMetadata *stackRecipe = psMetadataLookupMetadata(NULL, config->recipes, "PPSTACK"); // ppStack recipe
-    psAssert(stackRecipe, "We've thrown an error on this before.");
-
-    // Look up appropriate values from the ppSub recipe
-    psMetadata *subRecipe = psMetadataLookupMetadata(NULL, config->recipes, "PPSUB"); // PPSUB recipe
-    psAssert(subRecipe, "recipe missing");
-
-    psString maskValStr = psMetadataLookupStr(NULL, subRecipe, "MASK.VAL"); // Name of bits to mask going in
-    psString maskBadStr = psMetadataLookupStr(NULL, stackRecipe, "MASK.BAD"); // Name of bits to mask for bad
-
-    psImageMaskType maskVal = pmConfigMaskGet(maskValStr, config); // Bits to mask going in to pmSubtractionMatch
-    psImageMaskType maskBad = pmConfigMaskGet(maskBadStr, config); // Bits to mask for bad pixels
-
-    // Ensure the background value is zero
-    psStats *bg = psStatsAlloc(PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV); // Statistics for background
-    psRandom *rng = psRandomAlloc(PS_RANDOM_TAUS); // Random number generator
-
-    // XXX why is this in config->arguments and not recipe?
-    if (!psMetadataLookupBool(NULL, config->arguments, "PPSTACK.SKIP.BG.SUB")) {
-	if (!psImageBackground(bg, NULL, readout->image, readout->mask, maskVal | maskBad, rng)) {
-	    psAbort("Can't measure background for image.");
-	    // XXX we used to clear error: why is this acceptable? psErrorClear(); 
-	}
-
-	float value = psStatsGetValue(bg, PS_STAT_ROBUST_MEDIAN);
-	float stdev = psStatsGetValue(bg, PS_STAT_ROBUST_STDEV);
-
-	psLogMsg("psphotStack", PS_LOG_INFO, "Correcting convolved image background by %lf (+/- %lf)", value, stdev);
-	psBinaryOp(readout->image, readout->image, "-", psScalarAlloc(value, PS_TYPE_F32));
-    }
-
-    if (!stackRenormaliseReadout(config, readout)) {
-        psFree(rng);
-        psFree(bg);
-        return false;
-    }
-
-    // Measure the variance level for the weighting
-    if (psMetadataLookupBool(NULL, stackRecipe, "WEIGHTS")) {
-        if (!psImageBackground(bg, NULL, readout->variance, readout->mask, maskVal | maskBad, rng)) {
-            psError(PSPHOT_ERR_DATA, false, "Can't measure mean variance for image.");
-            psFree(rng);
-            psFree(bg);
-            return false;
-        }
-        options->weightings->data.F32[index] = 1.0 / (psStatsGetValue(bg, PS_STAT_ROBUST_MEDIAN) * psImageCovarianceFactor(readout->covariance));
-    } else {
-        options->weightings->data.F32[index] = 1.0;
-    }
-    psLogMsg("psphotStack", PS_LOG_INFO, "Weighting for image %d is %f\n", index, options->weightings->data.F32[index]);
-
-    psFree(rng);
-    psFree(bg);
-    return true;
-}
-
 # define NOISE_FRACTION 0.01             // Set minimum flux to this fraction of noise
 # define SOURCE_MASK (PM_SOURCE_MODE_FAIL | PM_SOURCE_MODE_DEFECT | PM_SOURCE_MODE_SATURATED | PM_SOURCE_MODE_CR_LIMIT | PM_SOURCE_MODE_EXT_LIMIT) // Mask to apply to input sources
Index: branches/meh_branches/ppstack_test/psphot/src/psphotStackObjects.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotStackObjects.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotStackObjects.c	(revision 33415)
@@ -47,2 +47,107 @@
     return true;
 }
+
+// mark good vs bad objects
+bool psphotStackObjectsSelectForAnalysis (pmConfig *config, const pmFPAview *view, const char *filerule, psArray *objects) {
+
+    bool status = false;
+
+    // find the currently selected readout
+    pmFPAfile *file = pmFPAfileSelectSingle(config->files, filerule, 0); // use the 0-index image to represent the image area
+    psAssert (file, "missing file?");
+
+    pmReadout *readout = pmFPAviewThisReadout(view, file->fpa);
+    psAssert (readout, "missing readout?");
+
+    // select the appropriate recipe information
+    psMetadata *recipe  = psMetadataLookupPtr (&status, config->recipes, PSPHOT_RECIPE);
+    psAssert (recipe, "missing recipe?");
+
+    // option to limit analysis to a specific region
+    char *region = psMetadataLookupStr (&status, recipe, "ANALYSIS_REGION");
+    psRegion AnalysisRegion = psRegionForImage (readout->image, psRegionFromString (region));
+    if (psRegionIsNaN (AnalysisRegion)) psAbort("analysis region mis-defined");
+
+    // S/N limit to perform full non-linear fits
+    float SN_LIM_PETRO  = psMetadataLookupF32 (&status, recipe, "EXTENDED_SOURCE_SN_LIM");
+    float SN_LIM_RADIAL = psMetadataLookupF32 (&status, recipe, "RADIAL_APERTURES_SN_LIM");
+
+    bool doPetroStars   = psMetadataLookupBool (&status, recipe, "PETROSIAN_FOR_STARS");
+
+    for (int i = 0; i < objects->n; i++) {
+        pmPhotObj *object = objects->data[i];
+	if (!object) continue;
+	if (!object->sources) continue;
+
+	// we check each source for an object and keep the object if any source is valid
+
+	bool keepObjectRadial = false;
+	bool keepObjectPetro = false;
+	for (int j = 0; j < object->sources->n; j++) {
+
+	    pmSource *source = object->sources->data[j];
+	    if (!source) continue;
+	    if (!source->peak) continue;
+
+	    // skip PSF-like and non-astronomical objects
+	    if (source->type == PM_SOURCE_TYPE_DEFECT) continue;
+	    if (source->type == PM_SOURCE_TYPE_SATURATED) continue;
+	    if (source->mode & PM_SOURCE_MODE_DEFECT) continue;
+	    if (source->mode & PM_SOURCE_MODE_SATSTAR) continue;
+	    
+	    // limit selection by analysis region (this automatically apply
+	    if (source->peak->x < AnalysisRegion.x0) continue;
+	    if (source->peak->y < AnalysisRegion.y0) continue;
+	    if (source->peak->x > AnalysisRegion.x1) continue;
+	    if (source->peak->y > AnalysisRegion.y1) continue;
+	    
+	    // SN limit tests for RADIAL APERTURES:
+	    bool skipSourceRadial = false;
+	    if (source->mode & PM_SOURCE_MODE_EXT_LIMIT) {
+		skipSourceRadial = (source->moments->KronFlux < SN_LIM_RADIAL * source->moments->KronFluxErr);
+	    } else {
+		skipSourceRadial = (sqrt(source->peak->detValue) < SN_LIM_RADIAL);
+	    }
+	    if (!skipSourceRadial) keepObjectRadial = true;
+
+	    // SN limit tests for PETRO
+	    bool skipSourcePetro = false;
+	    if (source->mode & PM_SOURCE_MODE_EXT_LIMIT) {
+		skipSourcePetro = (source->moments->KronFlux < SN_LIM_PETRO * source->moments->KronFluxErr);
+	    } else {
+		skipSourcePetro = doPetroStars ? (sqrt(source->peak->detValue) < SN_LIM_PETRO) : true;
+	    }
+	    if (!skipSourcePetro) keepObjectPetro = true;
+
+	    keepObjectPetro = true;
+	}
+
+	for (int j = 0; j < object->sources->n; j++) {
+	    pmSource *source = object->sources->data[j];
+	    if (!source) continue;
+	    if (!source->peak) continue;
+
+	    // we have to set a bit in either case to tell psphotExtendedSourceAnalysis to
+	    // avoid the single-detection tests
+
+	    if (keepObjectPetro) {
+		source->tmpFlags |=  PM_SOURCE_TMPF_PETRO_KEEP;
+		source->tmpFlags &= ~PM_SOURCE_TMPF_PETRO_SKIP;
+	    } else {
+		source->tmpFlags |=  PM_SOURCE_TMPF_PETRO_SKIP;
+		source->tmpFlags &= ~PM_SOURCE_TMPF_PETRO_KEEP;
+	    }	    
+
+	    if (keepObjectRadial) {
+		source->tmpFlags |=  PM_SOURCE_TMPF_RADIAL_KEEP;
+		source->tmpFlags &= ~PM_SOURCE_TMPF_RADIAL_SKIP;
+	    } else {
+		source->tmpFlags |=  PM_SOURCE_TMPF_RADIAL_SKIP;
+		source->tmpFlags &= ~PM_SOURCE_TMPF_RADIAL_KEEP;
+	    }	    
+	}
+    }
+
+    psLogMsg ("psphot", PS_LOG_INFO, "marked good vs bad objects\n");
+    return true;
+}
Index: branches/meh_branches/ppstack_test/psphot/src/psphotStackOptions.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotStackOptions.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotStackOptions.c	(revision 33415)
@@ -19,5 +19,5 @@
     psFree (options->norm);
     psFree (options->matchChi2);
-    psFree (options->weightings);
+    psFree (options->targetSeeing);
 
     return;
@@ -36,5 +36,5 @@
     options->convolve = false;
     options->convolveSource = PSPHOT_CNV_SRC_NONE;
-    options->targetSeeing = NAN;
+    options->targetSeeing = NULL;
 
     options->psfs        = psArrayAlloc(num);
@@ -47,5 +47,4 @@
     options->norm        = psVectorAlloc(num, PS_TYPE_F32);
     options->matchChi2   = psVectorAlloc(num, PS_TYPE_F32); // chi^2 for stamps when matching
-    options->weightings  = psVectorAlloc(num, PS_TYPE_F32); // Combination weightings for images (1/noise^2)
 
     psVectorInit(options->inputMask,   0);
@@ -53,5 +52,4 @@
     psVectorInit(options->norm,        NAN);
     psVectorInit(options->matchChi2,   NAN);
-    psVectorInit(options->weightings,  NAN);
 
     return options;
Index: branches/meh_branches/ppstack_test/psphot/src/psphotStackPSF.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotStackPSF.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotStackPSF.c	(revision 33415)
@@ -1,8 +1,13 @@
 # include "psphotInternal.h"
 
-pmPSF *psphotStackPSF(const pmConfig *config, int numCols, int numRows, const psArray *psfs, const psVector *inputMask)
-{
+// determine the 1st target PSF (either AUTO or defined by PSPHOT.STACK.TARGET.PSF.FWHM)
+bool psphotStackPSF(const pmConfig *config, psphotStackOptions *options)  {
+
     bool mdok = false;
-    pmPSF *psf = NULL;
+
+    int numCols = options->numCols;
+    int numRows = options->numRows;
+    psArray *psfs = options->psfs;
+    psVector *inputMask = options->inputMask;
 
     // Get the recipe values
@@ -17,4 +22,6 @@
 	
     char *psfModel = psMetadataLookupStr(NULL, recipe, "PSF.MODEL"); // Model for PSF
+
+    options->targetSeeing = psVectorAllocEmpty(4, PS_TYPE_F32);
 
     if (autoPSF) {
@@ -38,58 +45,70 @@
 
 	// Solve for the target PSF
-	psf = pmPSFEnvelope(numCols, numRows, psfs, psfInstances, psfRadius, psfModel, psfOrder, psfOrder, maskVal);
-	if (!psf) {
+	options->psf = pmPSFEnvelope(numCols, numRows, psfs, psfInstances, psfRadius, psfModel, psfOrder, psfOrder, maskVal);
+	if (!options->psf) {
 	    psError(PSPHOT_ERR_PSF, false, "Unable to determine output PSF.");
-	    return NULL;
+	    return false;
 	}
 
-    } else {
-
-	// externally-defined PSF
-	// XXX need to test for compatibility of target with inputs
-
-	float targetFWHM = psMetadataLookupF32 (&mdok, psphotRecipe, "PSPHOT.STACK.TARGET.PSF.FWHM");
-	if (!mdok) {
-	    psVector *fwhmValues = psMetadataLookupVector(&mdok, psphotRecipe, "PSPHOT.STACK.TARGET.PSF.FWHM"); // Magnitude offsets
-	    psAssert (mdok, "missing psphot recipe value PSPHOT.STACK.TARGET.PSF.FWHM");
-	    targetFWHM = fwhmValues->data.F32[0];
-	}
-
-	// measured scale factors (fwhm = Sxx * 2.35 * scaleFactor / sqrt(2.0))
-	// GAUSS  : 1.000
-	// PGAUSS : 1.006
-	// QGAUSS : 1.151
-	// RGAUSS : 0.883
-	// PS1_V1 : 1.134
-	
-	float scaleFactor = NAN;
-	if (!strcmp(psfModel, "PS_MODEL_GAUSS")) {
-	    scaleFactor = 1.000;
-	}
-	if (!strcmp(psfModel, "PS_MODEL_PGAUSS")) {
-	    scaleFactor = 1.0006;
-	}
-	if (!strcmp(psfModel, "PS_MODEL_QGAUSS")) {
-	    scaleFactor = 1.151;
-	}
-	if (!strcmp(psfModel, "PS_MODEL_RGAUSS")) {
-	    scaleFactor = 0.883;
-	}
-	if (!strcmp(psfModel, "PS_MODEL_PS1_V1")) {
-	    scaleFactor = 1.134;
-	}
-	psAssert (isfinite(scaleFactor), "invalid model for PSF"); 
-
-	float Sxx = sqrt(2.0)*targetFWHM / 2.35 / scaleFactor;
-
-	// XXX probably should make the model type (and par 7) optional from recipe
-	// psf = pmPSFBuildSimple(psfModel, Sxx, Sxx, 0.0, 1.0);
-	psf = pmPSFBuildSimple(psfModel, Sxx, Sxx, 0.0, 0.2);
-	if (!psf) {
-	    psError(PSPHOT_ERR_PSF, false, "Unable to build dummy PSF.");
-	    return NULL;
-	}
+        psMetadataAddPtr(config->arguments, PS_LIST_TAIL, "PSF.TARGET", PS_DATA_UNKNOWN, "Target PSF for stack", options->psf);
+        float targetSeeing = pmPSFtoFWHM(options->psf, 0.5 * options->numCols, 0.5 * options->numRows); // FWHM for target
+        psVectorAppend(options->targetSeeing, targetSeeing);
+        psLogMsg("psphotStack", PS_LOG_INFO, "Target seeing FWHM (auto-scaled): %f\n", targetSeeing);
+	return true;
     }
 
-    return psf;
+    // externally-defined PSF
+    // XXX need to test for compatibility of target with inputs
+
+    // is a single target FWHM specified, or a set of values?  set up the vector options->targetSeeing and the local 1st value
+    float targetSeeing = psMetadataLookupF32 (&mdok, psphotRecipe, "PSPHOT.STACK.TARGET.PSF.FWHM");
+    if (!mdok) {
+	psVector *fwhmValues = psMetadataLookupVector(&mdok, psphotRecipe, "PSPHOT.STACK.TARGET.PSF.FWHM"); // Magnitude offsets
+	psAssert (mdok, "missing psphot recipe value PSPHOT.STACK.TARGET.PSF.FWHM");
+	for (int i = 0; i < fwhmValues->n; i++) {
+	    psVectorAppend(options->targetSeeing, fwhmValues->data.F32[i]);
+	}	    
+	targetSeeing = fwhmValues->data.F32[0];
+    } else {
+        psVectorAppend(options->targetSeeing, targetSeeing);
+    }
+
+    // measured scale factors (fwhm = Sxx * 2.35 * scaleFactor / sqrt(2.0))
+    // GAUSS  : 1.000
+    // PGAUSS : 1.006
+    // QGAUSS : 1.151
+    // RGAUSS : 0.883
+    // PS1_V1 : 1.134
+	
+    float scaleFactor = NAN;
+    if (!strcmp(psfModel, "PS_MODEL_GAUSS")) {
+	scaleFactor = 1.000;
+    }
+    if (!strcmp(psfModel, "PS_MODEL_PGAUSS")) {
+	scaleFactor = 1.0006;
+    }
+    if (!strcmp(psfModel, "PS_MODEL_QGAUSS")) {
+	scaleFactor = 1.151;
+    }
+    if (!strcmp(psfModel, "PS_MODEL_RGAUSS")) {
+	scaleFactor = 0.883;
+    }
+    if (!strcmp(psfModel, "PS_MODEL_PS1_V1")) {
+	scaleFactor = 1.134;
+    }
+    psAssert (isfinite(scaleFactor), "invalid model for PSF"); 
+
+    float Sxx = sqrt(2.0)*targetSeeing / 2.35 / scaleFactor;
+
+    // XXX probably should make the model type (and par 7) optional from recipe
+    // psf = pmPSFBuildSimple(psfModel, Sxx, Sxx, 0.0, 1.0);
+    options->psf = pmPSFBuildSimple(psfModel, Sxx, Sxx, 0.0, 0.2);
+    if (!options->psf) {
+	psError(PSPHOT_ERR_PSF, false, "Unable to build dummy PSF.");
+	return false;
+    }
+
+    psMetadataAddPtr(config->arguments, PS_LIST_TAIL, "PSF.TARGET", PS_DATA_UNKNOWN, "Target PSF for stack", options->psf);
+    psLogMsg("psphotStack", PS_LOG_INFO, "Target seeing FWHM (1 of %ld): %f\n", options->targetSeeing->n, targetSeeing);
+    return true;
 }
Index: branches/meh_branches/ppstack_test/psphot/src/psphotStackReadout.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotStackReadout.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotStackReadout.c	(revision 33415)
@@ -44,4 +44,8 @@
 bool psphotStackReadout (pmConfig *config, const pmFPAview *view) {
 
+    psArray *objects = NULL; // used below after 'pass1finish' label
+
+    // measure the total elapsed time in psphotReadout.  dtime is the elapsed time used jointly
+    // by the multiple threads, not the total time used by all threads.
     psTimerStart ("psphotReadout");
 
@@ -56,12 +60,11 @@
     // optional break-point for processing
     char *breakPt = psMetadataLookupStr (NULL, recipe, "BREAK_POINT");
-    PS_ASSERT_PTR_NON_NULL (breakPt, false);
-
-    // XXX or do I set OUT to be a pmFPAfile pointing at the input of interest?
+    psAssert (breakPt, "configuration error: set BREAK_POINT");
+
+    // we have 3 relevant files: RAW (unconvolved), CNV (convolved stack), OUT (psf-matched stack)
+    // select which image (RAW or CNV) is used for analysis (RAW always used for detection)
     bool useRaw = psMetadataLookupBool (NULL, recipe, "PSPHOT.STACK.USE.RAW");
     char *STACK_SRC = useRaw ? STACK_RAW : STACK_CNV;
-    char *STACK_DET = STACK_RAW; // XXX optionally allow this to be CNV?
-
-    // we have 3 relevant files: RAW, CNV, OUT 
+    char *STACK_DET = STACK_RAW;
 
     // set the photcode for each image
@@ -71,6 +74,5 @@
     }
 
-    // Generate the mask and weight images
-    // XXX this should be done before we perform the convolutions
+    // Generate the mask and weight images (if not supplied) and set mask bits
     if (!psphotSetMaskAndVariance (config, view, STACK_DET)) {
 	return psphotReadoutCleanup (config, view, STACK_SRC);
@@ -81,6 +83,4 @@
 
     // generate a background model (median, smoothed image)
-    // XXX I think this is not defined correctly for an array of images.
-    // XXX probably need to subtract the model (same model?) for both RAW and OUT
     if (!psphotModelBackground (config, view, STACK_DET)) {
 	return psphotReadoutCleanup (config, view, STACK_SRC);
@@ -89,8 +89,12 @@
 	return psphotReadoutCleanup (config, view, STACK_SRC);
     }
+    if (!psphotSubtractBackground (config, view, STACK_SRC)) {
+	return psphotReadoutCleanup (config, view, STACK_SRC);
+    }
     if (!strcasecmp (breakPt, "BACKMDL")) {
 	return psphotReadoutCleanup (config, view, STACK_SRC);
     }
 
+    // also make the chisq detection image
     if (!psphotStackChisqImage(config, view, STACK_DET, STACK_SRC)) {
         psError (PSPHOT_ERR_UNKNOWN, false, "failure to generate chisq image");
@@ -109,5 +113,7 @@
     }
 
-    // copy the detections from DET to SRC 
+    // If DET and SRC are different images, copy the detections from DET to SRC.  This 'copy'
+    // is just a copy of the container pointer; the sources on both DET and SRC are the same
+    // memory objects
     if (strcmp(STACK_SRC, STACK_DET)) {
 	if (!psphotCopySources (config, view, STACK_SRC, STACK_DET)) {
@@ -122,77 +128,35 @@
         return psphotReadoutCleanup (config, view, STACK_SRC);
     }
-
-    if (!strcasecmp (breakPt, "TEST1")) {
-	return psphotReadoutCleanup (config, view, STACK_SRC);
-    }
-
+    if (!strcasecmp (breakPt, "PEAKS")) {
+	return psphotReadoutCleanup (config, view, STACK_SRC);
+    }
+    // psphotDumpTest (config, view, STACK_SRC);
     psMemDump("sourcestats");
-
-    // generate the objects (object unify the sources from the different images)
-    // XXX this could just match the detections for the chisq image, and not bother measuring the
-    // source stats in that case...
-    psArray *objects = psphotMatchSources (config, view, STACK_SRC);
-
-    psMemDump("matchsources");
-
-    if (!strcasecmp (breakPt, "TEST2")) {
-	psFree(objects);
-	return psphotReadoutCleanup (config, view, STACK_SRC);
-    }
-
-    // construct sources for the newly-generated sources (from other images)
-    if (!psphotSourceStats (config, view, STACK_SRC, false)) { // pass 1
-	psFree(objects);
-        psError(PSPHOT_ERR_UNKNOWN, false, "failure to generate sources");
-        return psphotReadoutCleanup (config, view, STACK_SRC);
-    }
-
-    psMemDump("sourcestats");
-
-    // find blended neighbors of very saturated stars (detections->newSources)
-    // if (!psphotDeblendSatstars (config, view)) {
-    //     psError (PSPHOT_ERR_UNKNOWN, false, "failed on satstar deblend analysis");
-    //     return psphotReadoutCleanup (config, view, STACK_SRC);
-    // }
-
-    // mark blended peaks PS_SOURCE_BLEND (detections->newSources)
-    // if (!psphotBasicDeblend (config, view)) {
-    //     psError (PSPHOT_ERR_UNKNOWN, false, "failed on deblend analysis");
-    //     return psphotReadoutCleanup (config, view, STACK_SRC);
-    // }
 
     // classify sources based on moments, brightness
     // only run this on detections from the input images, not chisq image
     if (!psphotRoughClass (config, view, STACK_SRC)) {
-	psFree(objects);
         psError (PSPHOT_ERR_UNKNOWN, false, "failed to determine rough classifications");
 	return psphotReadoutCleanup (config, view, STACK_SRC);
     }
+
     // if we were not supplied a PSF model, determine the IQ stats here (detections->newSources)
     // only run this on detections from the input images, not chisq image
     if (!psphotImageQuality (config, view, STACK_SRC)) { // pass 1
-	psFree(objects);
         psError (PSPHOT_ERR_UNKNOWN, false, "failed to measure image quality");
         return psphotReadoutCleanup (config, view, STACK_SRC);
     }
     if (!strcasecmp (breakPt, "MOMENTS")) {
-	psFree(objects);
-	return psphotReadoutCleanup (config, view, STACK_SRC);
-    }
-
-    // use bright stellar objects to measure PSF if we were supplied a PSF for any input file,
-    // this step is skipped
+	return psphotReadoutCleanup (config, view, STACK_SRC);
+    }
+
+    // use bright stellar objects to measure PSF
     if (!psphotChoosePSF (config, view, STACK_SRC, true)) { // pass 1
-	psFree(objects);
         psLogMsg ("psphot", 3, "failure to construct a psf model");
         return psphotReadoutCleanup (config, view, STACK_SRC);
     }
     if (!strcasecmp (breakPt, "PSFMODEL")) {
-	psFree(objects);
-        return psphotReadoutCleanup (config, view, STACK_SRC);
-    }
-
-    // construct an initial model for each object, set the radius to fitRadius, set circular fit mask
-    psphotGuessModels (config, view, STACK_SRC);
+        return psphotReadoutCleanup (config, view, STACK_SRC);
+    }
 
     // merge the newly selected sources into the existing list
@@ -200,33 +164,141 @@
     psphotMergeSources (config, view, STACK_SRC);
 
+    // Construct an initial model for each object, set the radius to fitRadius, set circular
+    // fit mask.  NOTE: only applied to sources without guess models
+    psphotGuessModels (config, view, STACK_SRC);
+
     // linear PSF fit to source peaks, subtract the models from the image (in PSF mask)
-    psphotFitSourcesLinearStack (config, objects, FALSE);
+    psphotFitSourcesLinear (config, view, STACK_SRC, false);
     psphotStackVisualFilerule(config, view, STACK_SRC);
 
+    // measure the radial profiles to the sky
+    psphotRadialProfileWings (config, view, STACK_SRC);
+
+    // re-measure the kron mags with models subtracted.  this pass starts with a circular
+    // window of size PSF_MOMENTS_RADIUS (same window used to measure the psf-scale moments)
+    // but iterates to an appropriately larger size
+    psphotKronIterate(config, view, STACK_SRC);
+
     // identify CRs and extended sources
-    psphotSourceSize (config, view, STACK_SRC, TRUE);
-
-    // XXX do we want to do a preliminary (unconvolved) model fit here, and then 
-    // do a second detection pass? (like standard psphot)
-
-    // measure aperture photometry corrections
-    if (!psphotApResid (config, view, STACK_SRC)) {
-	psFree (objects);
-        psLogMsg ("psphot", 3, "failed on psphotApResid");
-	return psphotReadoutCleanup (config, view, STACK_SRC);
-    }
+    psphotSourceSize (config, view, STACK_SRC, true);
+
+    // non-linear PSF and EXT fit to brighter sources
+    // replace model flux, adjust mask as needed, fit, subtract the models (full stamp)
+    psphotBlendFit (config, view, STACK_SRC); // pass 1 (detections->allSources)
+
+    // replace all sources (do NOT ignore subtraction state)
+    psphotReplaceAllSources (config, view, STACK_SRC, false); // pass 1 (detections->allSources)
+
+    // if we only do one pass, skip to extended source analysis
+    if (!strcasecmp (breakPt, "PASS1")) goto pass1finish;
+
+    // linear fit to include all sources (subtract again)
+    // NOTE : apply to ALL sources (extended + psf)
+    // NOTE 2 : this function subtracts the models from the given filerule (SRC), not DET
+    psphotFitSourcesLinear (config, view, STACK_SRC, true); // pass 2 (detections->allSources)
+
+    // NOTE: possibly re-measure background model here with objects subtracted / or masked
+
+    // NOTE: this block performs the 2nd pass low-significance PSF detection stage
+    { 
+	// if DET and SRC are different images, generate children sources for all sources in
+	// the SRC image.  This operation replaces the existing DETECTION container on DET
+	// which is currently a view to the one on SRC).  children sources go to
+	// det->allSources
+	if (strcmp(STACK_SRC, STACK_DET)) {
+	    psphotSourceChildren (config, view, STACK_DET, STACK_SRC); 
+
+	    //  subtract all sources from DET (this will subtract using the psf model for SRC, which
+	    //  will somewhat oversubtract the sources -- this is OK
+	    psphotRemoveAllSources (config, view, STACK_DET, false); // ignore subtraction state for sources
+	}
+
+	// add noise for subtracted objects
+	psphotAddNoise (config, view, STACK_DET); // pass 1 (detections->allSources)
+
+	// find fainter sources
+	// NOTE: finds new peaks and new footprints, OLD and FULL set are saved on detections
+	psphotFindDetections (config, view, STACK_DET, false); // pass 2 (detections->peaks, detections->footprints)
+
+	// remove noise for subtracted objects (ie, return to normal noise level)
+	// NOTE: this needs to operate only on the OLD sources
+	psphotSubNoise (config, view, STACK_DET); // pass 1 (detections->allSources)
+
+	// if DET and SRC are different images, copy the detections from DET to SRC 
+	// (this operation just ensures the metadata container has a view on SRC as well
+	if (strcmp(STACK_SRC, STACK_DET)) {
+	    // replace all sources in DET
+	    psphotReplaceAllSources (config, view, STACK_DET, false); // ignore subtraction state for sources
+
+	    // copy the newly detected peaks from DET to SRC so SourceStats below can operate on them
+	    if (!psphotCopyPeaks (config, view, STACK_SRC, STACK_DET)) {
+		psError (PSPHOT_ERR_UNKNOWN, false, "failure in peak analysis");
+		return psphotReadoutCleanup (config, view, STACK_SRC);
+	    }
+	}
+
+	// define new sources based on only the new peaks & measure moments
+	// NOTE: new sources are saved on detections->newSources
+	psphotSourceStats (config, view, STACK_SRC, false); // pass 2 (detections->newSources)
+
+	// set source type
+	// NOTE: apply only to detections->newSources
+	if (!psphotRoughClass (config, view, STACK_SRC)) { // pass 2 (detections->newSources)
+	    psLogMsg ("psphot", 3, "failed to find a valid PSF clump for image");
+	    return psphotReadoutCleanup (config, view, STACK_SRC);
+	}
+
+	// replace all sources so fit below applies to all at once
+	// NOTE: apply only to OLD sources (which have been subtracted)
+	psphotReplaceAllSources (config, view, STACK_SRC, false); // pass 2
+
+	// merge the newly selected sources into the existing list
+	// NOTE: merge OLD and NEW
+	// XXX check on free of sources...
+	psphotMergeSources (config, view, STACK_SRC); // (detections->newSources + detections->allSources -> detections->allSources)
+
+	// Construct an initial model for each object, set the radius to fitRadius, set circular
+	// fit mask.  NOTE: only applied to sources without guess models
+	psphotGuessModels (config, view, STACK_SRC);
+    }
+
+pass1finish:
+
+    // generate the objects (objects unify the sources from the different images) NOTE: could
+    // this just match the detections for the chisq image, and not bother measuring the source
+    // stats in that case...?
+    objects = psphotMatchSources (config, view, STACK_SRC);
+    psMemDump("matchsources");
+
+    // Construct an initial model for each object, set the radius to fitRadius, set circular
+    // fit mask.  NOTE: only applied to sources without guess models
+    psphotGuessModels (config, view, STACK_SRC);
+
+    psphotStackObjectsUnifyPosition (objects);
+
+    psphotStackObjectsSelectForAnalysis (config, view, STACK_SRC, objects);
+
+    // NOTE: apply to ALL sources
+    psphotFitSourcesLinear (config, view, STACK_SRC, true); // pass 3 (detections->allSources)
+
+    // measure the radial profiles to the sky (only measures new objects)
+    psphotRadialProfileWings (config, view, STACK_SRC);
+
+    // re-measure the kron mags with models subtracted
+    // psphotKronMasked(config, view, STACK_SRC);
+    psphotKronIterate(config, view, STACK_SRC);
+
+    // measure source size for the remaining sources
+    // NOTE: applies only to NEW (unmeasured) sources
+    psphotSourceSize (config, view, STACK_SRC, false); // pass 2 (detections->allSources)
 
     psMemDump("psfstats");
 
-    psphotStackObjectsUnifyPosition (objects);
-
     // measure elliptical apertures, petrosians (objects sorted by S/N)
-    psphotExtendedSourceAnalysisByObject (config, objects, view, STACK_SRC); // pass 1 (detections->allSources)
+    // psphotExtendedSourceAnalysisByObject (config, objects, view, STACK_SRC); // pass 1 (detections->allSources)
+    psphotExtendedSourceAnalysis (config, view, STACK_SRC); // pass 1 (detections->allSources)
 
     // measure non-linear extended source models (exponential, deVaucouleur, Sersic) (sources sorted by S/N)
     psphotExtendedSourceFits (config, view, STACK_SRC); // pass 1 (detections->allSources)
-
-    // calculate source magnitudes
-    psphotMagnitudes(config, view, STACK_SRC);
 
     // create source children for the OUT filerule (for radial aperture photometry) 
@@ -238,8 +310,13 @@
     }
 
+    // measure circular, radial apertures (objects sorted by S/N)
+    // this forces photometry on the undetected sources from other images
+    psphotRadialApertures (config, view, STACK_SRC, 0); // XXX entry 0 == unmatched?
     psMemDump("extmeas");
 
-    bool smoothAgain = true;
-    for (int nMatchedPSF = 0; smoothAgain; nMatchedPSF++) {
+    int nRadialEntries = psphotStackMatchPSFsEntries(config, view, STACK_OUT);
+
+    for (int entry = 1; entry < nRadialEntries; entry++) {
+	// NOTE: entry 0 is the unmatched image set
 
 	// re-measure the PSF for the smoothed image (using entries in 'allSources')
@@ -253,15 +330,28 @@
 
 	// measure circular, radial apertures (objects sorted by S/N)
-	psphotRadialAperturesByObject (config, objectsRadial, view, STACK_OUT, nMatchedPSF); 
+	// entry 0 == unmatched? pass entry + 1?
+	psphotRadialApertures (config, view, STACK_OUT, entry); 
 
 	// replace the flux in the image so it is returned to its original state
-	psphotReplaceAllSources (config, view, STACK_OUT);
+	psphotReplaceAllSources (config, view, STACK_OUT, false);
 
 	// smooth to the next FWHM, or set 'smoothAgain' to false if no more 
-	psphotStackMatchPSFsNext(&smoothAgain, config, view, STACK_OUT, nMatchedPSF);
+	psphotStackMatchPSFsNext(config, view, STACK_OUT, entry);
 	psMemDump("matched");
     }
 
-    if (0 && !psphotEfficiency(config, view, STACK_OUT)) {
+    // measure aperture photometry corrections
+    if (!psphotApResid (config, view, STACK_SRC)) {
+	psFree (objects);
+        psLogMsg ("psphot", 3, "failed on psphotApResid");
+	return psphotReadoutCleanup (config, view, STACK_SRC);
+    }
+
+    // calculate source magnitudes
+    psphotMagnitudes(config, view, STACK_SRC);
+
+    // XXX NOTE: this function wants to have the PSF of the image, but we (so far) only measure the 
+    // PSF of the SRC image.  can we fake it by generating the PSF for DET as well (up above)? 
+    if (false && !psphotEfficiency(config, view, STACK_DET)) {
         psErrorStackPrint(stderr, "Unable to determine detection efficiencies from fake sources");
         psErrorClear();
Index: branches/meh_branches/ppstack_test/psphot/src/psphotSubtractBackground.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotSubtractBackground.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotSubtractBackground.c	(revision 33415)
@@ -103,5 +103,5 @@
         psphotSaveImage (NULL, image, name);
     }
-    psLogMsg ("psphot", PS_LOG_INFO, "subtracted background model: %f sec\n", psTimerMark ("psphot.background"));
+    psLogMsg ("psphot", PS_LOG_WARN, "subtracted background model: %f sec\n", psTimerMark ("psphot.background"));
 
     // the pmReadout selected in this function are all view on entries in config->files
Index: branches/meh_branches/ppstack_test/psphot/src/psphotTest.c
===================================================================
--- branches/meh_branches/ppstack_test/psphot/src/psphotTest.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psphot/src/psphotTest.c	(revision 33415)
@@ -85,5 +85,5 @@
 
     // wait for the threads to finish and manage results
-    if (!psThreadPoolWait (true)) {
+    if (!psThreadPoolWait (true, true)) {
         fprintf (stderr, "failure to run FillImage (2)");
         exit (1);
Index: branches/meh_branches/ppstack_test/psphot/test/tap_dense.pro
===================================================================
--- branches/meh_branches/ppstack_test/psphot/test/tap_dense.pro	(revision 33415)
+++ branches/meh_branches/ppstack_test/psphot/test/tap_dense.pro	(revision 33415)
@@ -0,0 +1,788 @@
+#!/usr/bin/env mana
+# -*-sh-*-
+
+$KAPA = kapa -noX
+
+# config for ppImage to generate chip, mask, weight
+$ppImageConfig = -recipe PPIMAGE PPIMAGE_N
+$ppImageConfig = $ppImageConfig -Db BACKGROUND T
+$ppImageConfig = $ppImageConfig -Db CHIP.FITS T
+$ppImageConfig = $ppImageConfig -Db CHIP.MASK.FITS T
+$ppImageConfig = $ppImageConfig -Db CHIP.VARIANCE.FITS T
+$ppImageConfig = $ppImageConfig -Db BASE.FITS F
+$ppImageConfig = $ppImageConfig -Db VARIANCE.BUILD T
+$ppImageConfig = $ppImageConfig -Db PHOTOM F
+
+# basic options for the these images (filter, location, obstype)
+$BaseOptions = -type OBJECT -filter r -skymags 20.86 -ra 270.70 -dec -23.70 -pa 0.0
+$BaseOptions = $BaseOptions -Df PSASTRO:DVO.GETSTAR.MAX.RHO 50000.0
+
+# options for the reference image
+$RefOptions = $BaseOptions -exptime 100.0 -seeing 1.0 -D PSF.MODEL PS_MODEL_GAUSS -Df STARS.DENSITY 10.0 -Df STARS.SIGMA.LIM 0.5
+
+# options for the repeated images
+$FakeOptions = $BaseOptions -exptime 30.0
+  
+# basic config for ppSim with randomly distributed stars and gridded galaxies
+$RealConfig = -camera SIMTEST -recipe PPSIM STACKTEST.RUN -D PSASTRO:PSASTRO.CATDIR catdir.ref
+$RealConfig = $RealConfig -Db STARS.FAKE T -Db STARS.REAL F -Db MATCH.DENSITY F -Db PSF.CONVOLVE F
+$RealConfig = $RealConfig -Df STARS.DENSITY 100.0
+$RealConfig = $RealConfig -Df STARS.SIGMA.LIM 2.5
+$RealConfig = $RealConfig -Db GALAXY.FAKE F -Df GALAXY.MAG 17.0
+$RealConfig = $RealConfig -Db GALAXY.GRID F -D GALAXY.MODEL PS_MODEL_SERSIC
+$RealConfig = $RealConfig -Df GALAXY.ARATIO.MIN 0.5
+$RealConfig = $RealConfig -Df GALAXY.ARATIO.MAX 0.5
+$RealConfig = $RealConfig -Df GALAXY.THETA.MAX 180
+$RealConfig = $RealConfig -Df GALAXY.INDEX.MIN 1
+$RealConfig = $RealConfig -Df GALAXY.INDEX.MAX 1
+$RealConfig = $RealConfig -Di GALAXY.GRID.DX 120
+$RealConfig = $RealConfig -Di GALAXY.GRID.DY 120
+
+# sample alternate options:
+# $ppSimOptions = $FakeOptions -D PSF.MODEL PS_MODEL_PS1_V1
+# $ppSimOptions = $FakeOptions -Df PSF.ARATIO 1.2
+# $ppSimOptions = $FakeOptions -Df PSF.THETA +30.0
+$ppSimOptions = $FakeOptions -D PSF.MODEL PS_MODEL_GAUSS
+
+list fwhm 
+ 1.0 
+ 1.1 
+ 1.2 
+ 1.5
+end
+
+macro fulltest
+  if ($0 != 2)
+    echo "USAGE: fulltest (Npass)"
+    break
+  end
+
+  mkref 
+
+  local i Npass imname stname
+
+  $Npass = $1
+
+  $imname = image.fg
+  $stname = stack.fg
+
+  for i 0 $Npass
+    sprintf N "%02d" $i
+    onestack $N
+  end
+end
+
+macro summary.stats
+  if ($0 != 2)
+    echo "USAGE: summary.stats (Npass)"
+    break
+  end
+
+  local i Npass base stack imname stname
+
+  $Npass = $1
+
+  exec /bin/rm -f psf.image.summary.stats 
+  exec /bin/rm -f psf.stack.inputs.summary.stats
+  exec /bin/rm -f psf.stack.conv.summary.stats
+  exec /bin/rm -f psf.stack.unconv.summary.stats
+
+  $imname = image.fg
+  $stname = stack.fg
+
+  for i 0 $Npass
+    sprintf base  "$imname.%02d/$imname.stats" $i
+    sprintf stack "$stname.%02d/$stname" $i
+    echo $i $base $stack
+    exec grep PSF_INST_MAG $base                   >> psf.image.summary.stats
+    exec grep PSF_INST_MAG $stack.conv.stats       >> psf.stack.inputs.summary.stats
+    exec grep PSF_INST_MAG $stack.pht.stats        >> psf.stack.conv.summary.stats
+    exec grep PSF_INST_MAG $stack.unconv.pht.stats >> psf.stack.unconv.summary.stats
+  end
+end
+
+# create a reference database of fake stars to be used by ppSim below
+macro mkref
+  exec rm -rf catdir.ref
+  exec rm -f refimage.fits
+  
+  # create an image with fake sources and insert the resulting cmf file into a dvodb
+  $RefConfig = -camera SIMTEST -recipe PPSIM STACKTEST.MAKE -D PSASTRO:PSASTRO.CATDIR catdir.ref -Db PSF.CONVOLVE T
+
+  exec ppSim $RefOptions $RefConfig refimage -nx 3000 -ny 3000
+  
+  file synth.photcodes found
+  if (not($found))
+    echo "making photcodes file"
+    mkphotcodes synth.photcodes
+  end
+
+  exec addstar -D CAMERA simtest -D CATDIR catdir.ref -accept-astrom -photcode SYNTH.r -D PHOTCODE_FILE synth.photcodes refimage.cmf
+  exec relphot -averages -D CATDIR catdir.ref -update -region 260 280 -33 -13
+end
+
+macro onestack
+  if ($0 != 2)
+    echo "USAGE: onestack (N)"
+    break
+  end
+
+  mkstack image.$1/image stack.$1/stack
+  ckstack image.$1/image stack.$1/stack
+  # rmstack image.$1/image stack.$1/stack
+end
+
+# create a realistic distribution of fake stars, GAUSS PSF
+macro mkstack
+  if ($0 != 3)
+    echo "USAGE: mkstack (rawbase) (stackbase)"
+    break
+  end
+
+  local i base stack
+
+  $base = $1
+  $stack = $2
+
+  dirname $base -var dir  
+  mkdir $dir
+
+  dirname $stack -var dir  
+  mkdir $dir
+
+  exec echo "INPUT MULTI" > $stack.mdc
+  exec echo "" >> $stack.mdc
+
+  for i 0 $fwhm:n
+    mkexp $base.$i $fwhm:$i
+
+    exec echo "INPUT METADATA" >> $stack.mdc
+    exec echo "IMAGE STR $base.$i.wrp.fits" >> $stack.mdc
+    exec echo "MASK STR $base.$i.wrp.mask.fits" >> $stack.mdc
+    exec echo "VARIANCE STR $base.$i.wrp.wt.fits" >> $stack.mdc
+    exec echo "PSF STR $base.$i.wrp.psf" >> $stack.mdc
+    exec echo "SOURCES STR $base.$i.wrp.cmf" >> $stack.mdc
+    exec echo "END" >> $stack.mdc
+    exec echo "" >> $stack.mdc
+  end
+
+  exec ppStack -threads 4 -input $stack.mdc $stack -Db TEMP.DELETE F -R PPSTACK.OUTPUT.VARIANCE FITS.TYPE NONE
+
+  basename $stack -var stackbase
+  for i 0 $fwhm:n
+    exec /bin/cp -f /tmp/$stackbase.$i.conv.im.fits $stack.$i.conv.im.fits
+    exec /bin/cp -f /tmp/$stackbase.$i.conv.var.fits $stack.$i.conv.var.fits
+    exec /bin/cp -f /tmp/$stackbase.$i.conv.mk.fits $stack.$i.conv.mk.fits
+  end  
+
+  # XXX note that the output convolved variance is missing the covariance
+  for i 0 $fwhm:n
+    exec psphot -file $stack.$i.conv.im.fits -mask $stack.$i.conv.mk.fits $stack.$i.conv
+  end  
+
+  # basic photometry for the stack and unconvolved stack
+  exec psphot -file $stack.fits -mask $stack.mask.fits -variance $stack.weight.fits $stack.pht
+  exec psphot -file $stack.unconv.fits -mask $stack.unconv.mask.fits -variance $stack.unconv.wt.fits $stack.unconv.pht
+end
+
+# run this on the files created with mkstack
+macro ckstack
+  if ($0 != 3)
+    echo "USAGE: mkstack (rawbase) (stackbase)"
+    break
+  end
+
+  local i ZPT base stack
+
+  $base = $1
+  $stack = $2
+
+  # generate plots and stats for the per-image chip analysis
+  # and the psf-matched convolved images
+  $ZPT = -2.5*log($fwhm:n)
+  for i 0 $fwhm:n
+    ckchip $base.$i.in.cmf $base.$i.cmf       $base.$i       0.0
+    ckwarp $base.$i.in.cmf $stack.$i.conv.cmf $stack.$i.conv $ZPT
+
+    stchip $base.$i.in.cmf $base.$i.cmf       $base.stats       0.0
+    stwarp $base.$i.in.cmf $stack.$i.conv.cmf $stack.conv.stats $ZPT
+
+    completeness $base.$i.in.cmf $base.$i.cmf $base.$i.complete.png
+  end
+
+  ckwarp $base.$i.in.cmf $stack.pht.cmf        $stack.pht        $ZPT
+  ckwarp $base.$i.in.cmf $stack.unconv.pht.cmf $stack.unconv.pht $ZPT
+
+  stwarp $base.$i.in.cmf $stack.pht.cmf        $stack.pht.stats  $ZPT
+  stwarp $base.$i.in.cmf $stack.unconv.pht.cmf $stack.unconv.pht.stats $ZPT
+end
+
+macro rmstack
+  if ($0 != 3)
+    echo "USAGE: mkstack (rawbase) (stackbase)"
+    break
+  end
+
+  local base stack
+  $base = $1
+  $stack = $2
+
+  for i 0 $fwhm:n
+    exec rm -f $base.$i.fits
+    exec rm -f $base.$i.ch.fits
+    exec rm -f $base.$i.ch.mk.fits
+    exec rm -f $base.$i.ch.wt.fits
+
+    exec rm -f $base.$i.wrp.fits
+    exec rm -f $base.$i.wrp.mask.fits
+    exec rm -f $base.$i.wrp.wt.fits
+
+    exec rm -f $stack.$i.conv.im.fits
+    exec rm -f $stack.$i.conv.mk.fits
+    exec rm -f $stack.$i.conv.wt.fits
+  end
+
+  exec rm -f $stack.fits
+  exec rm -f $stack.mask.fits
+  exec rm -f $stack.weight.fits
+
+  exec rm -f $stack.exp.fits
+  exec rm -f $stack.num.fits
+  exec rm -f $stack.expwt.fits
+
+  exec rm -f $stack.unconv.fits
+  exec rm -f $stack.unconv.mask.fits
+  exec rm -f $stack.unconv.wt.fits
+
+  exec rm -f $stack.unconv.exp.fits
+  exec rm -f $stack.unconv.num.fits
+  exec rm -f $stack.unconv.expwt.fits
+end
+
+# create a realistic distribution of fake stars, GAUSS PSF
+macro mkexp
+  if ($0 != 3)
+    echo "USAGE: mkexp basename fwhm"
+    break
+  end
+
+  local fwhm basename
+  $basename = $1
+  $fwhm = $2
+
+  # create the raw image
+  echo ppSim $ppSimOptions $RealConfig $basename -seeing $fwhm -nx 3000 -ny 3000
+  exec ppSim $ppSimOptions $RealConfig $basename -seeing $fwhm -nx 3000 -ny 3000
+  exec /bin/mv -f $basename.cmf $basename.in.cmf
+
+  # create the chip output
+  echo ppImage $ppImageConfig -file $basename.fits $basename
+  exec ppImage $ppImageConfig -file $basename.fits $basename
+end
+
+# compare two cmf files with extname Chip.psf 
+# things to compare:
+# * completeness (which sources in (1) are not detected in (2)
+# * positions (X_PSF, Y_PSF) 
+# * instrumental psf mags
+# * position errors (no input errors; use a model?)
+# * measured FWHM?
+# * kron mags (fluxes)
+# * etc, etc
+macro ckchip
+  if ($0 != 5)
+    echo "USAGE: ckchip (raw) (out) (output) (zpt_off)"
+    break
+  end
+
+  load.cmf $1 Chip.psf raw
+  load.cmf $2 Chip.psf out
+
+  # images generated with convolution will not have the right output positions
+  set X_raw = int(X_PSF_raw) + 0.5
+  set Y_raw = int(Y_PSF_raw) + 0.5
+  set M_raw = PSF_INST_MAG_raw + $4
+  set K_out = -2.5*log(KRON_FLUX_out)
+  match2d X_PSF_out Y_PSF_out X_PSF_raw Y_PSF_raw 1.5 -index1 index1 -index2 index2
+
+  local i NX NY nx ny N
+
+  device -n compare
+  resize 1000 1000
+
+  # plot trends as a function of mag
+  $NX = 2
+  $NY = 5
+  $nx = 0
+  $ny = 0
+  $N = 0
+  clear -s
+  for i 0 $pairs:n
+    section a$nx\$ny {$nx/$NX} {$ny/$NY} {1/$NX} {1/$NY}
+    show.pair $i
+    $ny ++
+    if ($ny == $NY)
+      $ny = 0
+      $nx ++
+    end
+    if ($nx == $NX)
+      png -name $3.$N.png
+      clear -s
+      $nx = 0
+      $ny = 0
+      $N ++
+    end
+  end
+
+  # plot (input - output) vs mag
+end
+
+macro stchip
+  if ($0 != 5)
+    echo "USAGE: stchip (raw) (out) (output) (zpt_off)"
+    break
+  end
+
+  load.cmf $1 Chip.psf raw
+  load.cmf $2 Chip.psf out
+
+  # images generated with convolution will not have the right output positions
+  set X_raw = int(X_PSF_raw) + 0.5
+  set Y_raw = int(Y_PSF_raw) + 0.5
+  set M_raw = PSF_INST_MAG_raw + $4
+  set K_out = -2.5*log(KRON_FLUX_out)
+  match2d X_PSF_out Y_PSF_out X_PSF_raw Y_PSF_raw 1.5 -index1 index1 -index2 index2
+
+  local i
+
+  for i 0 $spairs:n
+    stats.pair $i $3
+  end
+end
+
+# compare chip to warp
+macro ckwarp
+  if ($0 != 5)
+    echo "USAGE: ckwarp (raw) (out) (output) (zpt_off)"
+    break
+  end
+
+  load.cmf $1 Chip.psf raw
+  load.cmf $2 SkyChip.psf out
+  set X_raw = int(X_PSF_raw) + 0.5
+  set Y_raw = int(Y_PSF_raw) + 0.5
+  set M_raw = PSF_INST_MAG_raw + $4
+  set K_out = -2.5*log(KRON_FLUX_out)
+  match2d X_PSF_out Y_PSF_out X_PSF_raw Y_PSF_raw 1.0 -index1 index1 -index2 index2
+
+  local i nx ny NX NY N
+
+  device -n compare
+  resize 1000 1000
+
+  # plot trends as a function of mag
+  $NX = 2
+  $NY = 5
+  $nx = 0
+  $ny = 0
+  $N = 0
+  clear -s
+
+  for i 0 $pairs:n
+    section a$nx\$ny {$nx/$NX} {$ny/$NY} {1/$NX} {1/$NY}
+    show.pair $i
+    $ny ++
+    if ($ny == $NY)
+      $ny = 0
+      $nx ++
+    end
+    if ($nx == $NX)
+      png -name $3.$N.png
+      clear -s
+      $nx = 0
+      $ny = 0
+      $N ++
+    end
+  end
+end
+
+macro stwarp
+  if ($0 != 5)
+    echo "USAGE: stwarp (raw) (out) (output) (zpt_off)"
+    break
+  end
+
+  load.cmf $1 Chip.psf raw
+  load.cmf $2 SkyChip.psf out
+
+  # images generated with convolution will not have the right output positions
+  set X_raw = int(X_PSF_raw) + 0.5
+  set Y_raw = int(Y_PSF_raw) + 0.5
+  set M_raw = PSF_INST_MAG_raw + $4
+  set K_out = -2.5*log(KRON_FLUX_out)
+  match2d X_PSF_out Y_PSF_out X_PSF_raw Y_PSF_raw 1.5 -index1 index1 -index2 index2
+
+  local i
+
+  for i 0 $spairs:n
+    stats.pair $i $3
+  end
+end
+
+macro stats.pair
+  if ($0 != 3)
+    echo "USAGE: stats.pair (N) (output)"
+    break
+  end
+
+  list word -split $spairs:$1
+  if ($word:n != 8)
+    echo "invalid pair $1"
+    break
+  end
+
+  $Nr = $word:3
+
+  reindex v1 = $word:0 using index1
+  reindex v2 = $word:1 using index2
+  reindex rv = $word:2 using index$Nr
+
+  set delta = v1 - v2
+  subset d1 = delta if ($word:4 < rv) && (rv < $word:5) && (abs(delta) < $word:7)
+  subset d2 = delta if ($word:5 < rv) && (rv < $word:6) && (abs(delta) < $word:7)
+
+  vstats -q d1 -sigma-clip 3.0
+  $M1 = $MEAN
+  $S1 = $SIGMA
+  vstats -q d2 -sigma-clip 3.0
+  $M2 = $MEAN
+  $S2 = $SIGMA
+
+  output $2
+  fprintf "%-18s  %7.4f %7.4f  %7.4f %7.4f" $word:0  $M1 $S1  $M2 $S2
+  output stdout
+end
+
+macro show.pair
+  if ($0 != 2)
+    echo "USAGE: show.pair (N)"
+    break
+  end
+
+  list word -split $pairs:$1
+  if ($word:n != 7)
+    echo "invalid pair $1"
+    break
+  end
+
+  $Nr = $word:3
+
+  reindex v1 = $word:0 using index1
+  if ("$word:6" == "V") 
+    reindex v2 = $word:1 using index2
+  end
+  if ("$word:6" == "S") 
+    set v2 = $word:1 + zero(index1)
+  end
+  reindex rv = $word:2 using index$Nr
+
+  set delta = v1 - v2
+  if (("$word:4" == "def") || ("$word:5" == "def"))
+    lim rv delta; box; plot rv delta
+  else
+    lim rv $word:4 $word:5; box; plot rv delta
+  end
+  label -y '$word:0' -x '$word:2'
+end
+
+# This list is used to compare a pair of vectors (sans error) or a
+# vector and an expected (constant) value.  The last field defines a
+# vector or constant for the comparison.  It is assumed that the
+# vector sets have been loaded and matched with match2d to generate
+# index vectors 'index1' and index2'.  The macro 'show.pair' generates
+# a plot of the range vector vs (v1 - v2).  The indices for v1, v2 are
+# index1 and 2 respectively.  The index for the range vector is defined
+# by the integer following that vector.  the y-limits of the plot are
+# given by the last two numbers
+list pairs
+  X_PSF_out             X_raw                 PSF_INST_MAG_raw 2 -1.01 1.01 V
+  Y_PSF_out             Y_raw                 PSF_INST_MAG_raw 2 -1.01 1.01 V
+  X_PSF_out             X_PSF_raw             PSF_INST_MAG_raw 2 -1.01 1.01 V
+  Y_PSF_out             Y_PSF_raw             PSF_INST_MAG_raw 2 -1.01 1.01 V
+  X_PSF_SIG_out         X_PSF_SIG_raw         PSF_INST_MAG_raw 2 -1.01 1.01 V
+  Y_PSF_SIG_out         Y_PSF_SIG_raw         PSF_INST_MAG_raw 2 -1.01 1.01 V
+  #PSF_INST_MAG_out      PSF_INST_MAG_raw      PSF_INST_MAG_raw 2 -1.01 1.01 V
+  PSF_INST_MAG_out      M_raw                 PSF_INST_MAG_raw 2 -1.01 1.01 V
+  PSF_INST_MAG_SIG_out  PSF_INST_MAG_SIG_raw  PSF_INST_MAG_raw 2 -1.01 1.01 V
+  #PSF_INST_FLUX_out     PSF_INST_FLUX_raw     PSF_INST_MAG_raw 2   def  def V
+  #PSF_INST_FLUX_SIG_out PSF_INST_FLUX_SIG_raw PSF_INST_MAG_raw 2   def  def V
+  AP_MAG_out            M_raw                 PSF_INST_MAG_raw 2 -1.01 1.01 V
+  AP_MAG_RAW_out        M_raw                 PSF_INST_MAG_raw 2 -1.01 1.01 V
+  AP_MAG_RADIUS_out     0.0                   PSF_INST_MAG_raw 2 -0.01 20.1 S
+  SKY_out               0.0                   PSF_INST_MAG_raw 2   def  def S
+  SKY_SIGMA_out         0.0                   PSF_INST_MAG_raw 2   def  def S
+  PSF_CHISQ_out         1.0                   PSF_INST_MAG_raw 2   def  def S
+  CR_NSIGMA_out         0.0   		      PSF_INST_MAG_raw 2   def  def S
+  EXT_NSIGMA_out        0.0   		      PSF_INST_MAG_raw 2 -5.01 5.01 S
+  PSF_MAJOR_out         0.0   		      PSF_INST_MAG_raw 2 -0.01 5.01 S
+  PSF_MINOR_out         0.0   		      PSF_INST_MAG_raw 2 -0.01 5.01 S
+  PSF_THETA_out         0.0   		      PSF_INST_MAG_raw 2 -1.61 1.61 S
+  PSF_QF_out            0.0   		      PSF_INST_MAG_raw 2 -0.10 1.10 S
+  PSF_QF_PERFECT_out    0.0   		      PSF_INST_MAG_raw 2 -0.10 1.10 S
+  PSF_NDOF_out          0.0   		      PSF_INST_MAG_raw 2  def  def  S
+  PSF_NPIX_out          0.0   		      PSF_INST_MAG_raw 2  def  def  S
+  MOMENTS_XX_out        0.0   		      PSF_INST_MAG_raw 2 -0.01 3.01 S
+  MOMENTS_XY_out        0.0   		      PSF_INST_MAG_raw 2 -3.01 3.01 S
+  MOMENTS_YY_out        0.0   		      PSF_INST_MAG_raw 2 -0.01 3.01 S
+  MOMENTS_M3C_out       0.0   		      PSF_INST_MAG_raw 2 -3.01 3.01 S
+  MOMENTS_M3S_out       0.0   		      PSF_INST_MAG_raw 2 -3.01 3.01 S
+  MOMENTS_M4C_out       0.0   		      PSF_INST_MAG_raw 2 -2.01 2.01 S
+  MOMENTS_M4S_out       0.0   		      PSF_INST_MAG_raw 2 -2.01 2.01 S
+  MOMENTS_R1_out        0.0   		      PSF_INST_MAG_raw 2 -5.01 5.01 S
+  MOMENTS_RH_out        0.0   		      PSF_INST_MAG_raw 2 -5.01 5.01 S
+  K_out                 M_raw  		      PSF_INST_MAG_raw 2  def  def  V
+  KRON_FLUX_ERR_out     0.0   		      PSF_INST_MAG_raw 2  def  def  S
+  KRON_FLUX_INNER_out   0.0   		      PSF_INST_MAG_raw 2  def  def  S
+  KRON_FLUX_OUTER_out   0.0   		      PSF_INST_MAG_raw 2  def  def  S
+# CAL_PSF_MAG          CAL_PSF_MAG          none Mraw 2 -1.01 1.01 V
+# CAL_PSF_MAG_SIG      CAL_PSF_MAG_SIG      none Mraw 2 -1.01 1.01 V
+# RA_PSF               RA_PSF               none Mraw 2 -1.01 1.01 V
+# DEC_PSF              DEC_PSF              none Mraw 2 -1.01 1.01 V
+# PEAK_FLUX_AS_MAG     PEAK_FLUX_AS_MAG     none Mraw 2 -1.01 1.01 V
+# FLAGS                FLAGS                0.0  Mraw 2 -1.01 1.01 S
+# FLAGS2               FLAGS2               0.0  Mraw 2 -1.01 1.01 S
+end
+
+macro load.cmf
+  if ($0 != 4)
+   echo "load.cmf (filename) (ext) (label)"
+   break
+  end
+
+  data $1
+
+  # create the list of fields to load
+  delete -q myFields
+  for i 0 $fields:n
+    if ($?myFields) 
+      $myFields = $myFields $fields:$i
+    else
+      $myFields = $fields:$i
+    end
+  end
+
+  read -fits $2 $myFields
+
+  # rename the loaded vectors appending the supplied lable
+  for i 0 $fields:n
+    set $fields:$i\_$3 = $fields:$i
+    delete $fields:$i
+  end
+end
+
+# this list defines the fields to be loaded from file
+list fields
+  X_PSF              
+  Y_PSF              
+  X_PSF_SIG          
+  Y_PSF_SIG          
+  PSF_INST_MAG       
+  PSF_INST_MAG_SIG   
+  PSF_INST_FLUX      
+  PSF_INST_FLUX_SIG  
+  AP_MAG             
+  AP_MAG_RAW         
+  AP_MAG_RADIUS      
+  SKY                
+  SKY_SIGMA          
+  PSF_CHISQ          
+  CR_NSIGMA          
+  EXT_NSIGMA         
+  PSF_MAJOR          
+  PSF_MINOR          
+  PSF_THETA          
+  PSF_QF             
+  PSF_QF_PERFECT     
+  PSF_NDOF           
+  PSF_NPIX           
+  MOMENTS_XX         
+  MOMENTS_XY         
+  MOMENTS_YY         
+  MOMENTS_M3C        
+  MOMENTS_M3S        
+  MOMENTS_M4C        
+  MOMENTS_M4S        
+  MOMENTS_R1         
+  MOMENTS_RH         
+  KRON_FLUX          
+  KRON_FLUX_ERR      
+  KRON_FLUX_INNER    
+  KRON_FLUX_OUTER    
+#   CAL_PSF_MAG      
+#   CAL_PSF_MAG_SIG  
+#   RA_PSF           
+#   DEC_PSF          
+#   PEAK_FLUX_AS_MAG 
+#  FLAGS            
+#  FLAGS2           
+end
+
+# use these cmf entries to measure average stats of the given pairs
+list spairs
+  X_PSF_out             X_raw                 PSF_INST_MAG_raw 2 -15 -10 -8.5  0.1
+  Y_PSF_out             Y_raw                 PSF_INST_MAG_raw 2 -15 -10 -8.5  0.1
+  PSF_INST_MAG_out      M_raw                 PSF_INST_MAG_raw 2 -15 -10 -8.5  0.1
+  AP_MAG_out            M_raw                 PSF_INST_MAG_raw 2 -15 -10 -8.5  0.1
+end
+
+#  # XXX this is a hack: the cmf file created by ppSim is not
+#  # compatible with the image for warp (because one is a chip-mosaic
+#  # and the other is not)
+#  exec rm -f fix.hdr
+#  output fix.hdr
+#  echo "PSMOSAIC= 'CHIP    '           / Mosaicked level"
+#  output stdout 
+# fix the header to be compatible with the chip file (to avoid running psphot)
+# exec fits_insert $basename.cmf fix.hdr
+
+macro completeness
+ if ($0 != 4)
+   echo "USAGE: completeness (raw) (out) (output)"
+   break
+ end
+
+ load.cmf $1 Chip.psf raw
+ load.cmf $2 Chip.psf out
+ set X_raw = int(X_PSF_raw) + 0.5
+ set Y_raw = int(Y_PSF_raw) + 0.5
+ match2d X_PSF_raw Y_PSF_raw X_PSF_out Y_PSF_out 1.5 -index1 index1 -index2 index2 -closest
+
+ histogram PSF_INST_MAG_raw nMag -16.0 -3.0 0.25 -range dMag
+ set fMag = zero(dMag) 
+ for i 0 {dMag[]-1}
+  set inrange = (PSF_INST_MAG_raw > dMag[$i]) && (PSF_INST_MAG_raw <= dMag[$i+1])
+  subset all = index1 if (inrange)
+  subset got = index1 if (inrange) && (index1 >= 0)
+  if (all[] == 0)
+    fMag[$i] = 0
+  else 
+   fMag[$i] = got[] / all[]
+  end
+ end
+
+ device -n complete
+ resize 1000 600
+
+ clear -s
+
+ section default 0 0 1 1
+ lim dMag fMag; clear; box -ypad 5 +ypad 5 -ticks 1110; plot -x 1 dMag fMag
+ label -x mag_inst -y det_frac 
+
+ set found = (index1 >= 0)
+ plot PSF_INST_MAG_raw found -c red
+
+ set ldmag = log(PSF_INST_MAG_SIG_raw)
+ section overlay 0 0 1 1; lim dMag -5 1.2; box -ypad 5 +ypad 5 -ticks 1011 -labels 1001; plot PSF_INST_MAG_raw ldmag
+ label +y log(S/N)
+
+ png -name $3
+end
+
+macro show.dpair
+  if ($0 != 2)
+    echo "USAGE: show.dpair (N)"
+    break
+  end
+
+  list word -split $dpairs:$1
+  if ($word:n != 7)
+    echo "invalid dpair $1"
+    break
+  end
+
+  $Nr = $word:4
+
+  reindex v1 = $word:0 using index1
+  reindex dv = $word:1 using index1
+  reindex v2 = $word:2 using index2
+  reindex rv = $word:3 using index$Nr
+
+  set delta = (v1 - v2) / dv
+  if (("$word:5" == "def") || ("$word:6" == "def"))
+    lim rv delta; box; plot rv delta
+  else
+    lim rv $word:5 $word:6; box; plot rv delta
+  end
+  label -y '$word:0' -x '$word:3'
+end
+
+# this list is used to compare a pair of vectors with an error it is
+# assumed that the vector sets have been loaded and matched with
+# match2d to generate index vectors 'index1' and index2'.  the macro
+# show.dpair generates a plot of the range vector vs (v1 - v2) / dv1.
+# The indices for v1, dv1, and v2 are index1,1, and 2 respectively.  The
+# index for the range vector is defined by the integer following that
+# vector.  The y-limits of the plot are given by the last two numbers
+# (use 'def') for the full default range of the delta vector
+list dpairs
+  # v1              dv                v2   range 
+  X_PSF             X_PSF_SIG         Xraw Mraw  2 -10.0 10.0
+  Y_PSF             Y_PSF_SIG         Yraw Mraw  2 -10.0 10.0
+  PSF_INST_MAG      PSF_INST_MAG_SIG  Mraw Mraw  2 -10.0 10.0
+  PSF_INST_FLUX     PSF_INST_FLUX_SIG Fraw Mraw  2 -10.0 10.0
+  AP_MAG            PSF_INST_MAG_SIG  Mraw Mraw  2 -10.0 10.0
+  AP_MAG_RAW        PSF_INST_MAG_SIG  Mraw Mraw  2 -10.0 10.0
+end
+
+# if we run this test as a stand-alone program somewhere, we may need to create a local copy of the photcode file:
+macro mkphotcodes
+  if ($0 != 2)
+    echo "USAGE: mkphotcodes (filename)"
+    break
+  end
+
+  exec /bin/rm -f $1
+  output $1
+  echo "#                                           airmass      color                         astrometry  mag    photom  astrom mask    photom mask"
+  echo "# code  name                type    zero  slope offset c1    c2   slope   zero  equiv  sys scale   scale  sys     poor   bad     poor   bad"
+  echo "  1     g_SYNTH              sec   0.000  0.000 0.000     1     3 0.0000     0    21   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000"
+  echo "  2     r_SYNTH              sec   0.000  0.000 0.000     2     3 0.0000     0    22   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000"
+  echo "  3     i_SYNTH              sec   0.000  0.000 0.000     2     3 0.0000     0    23   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000"
+  echo "  4     z_SYNTH              sec   0.000  0.000 0.000     3     4 0.0000     0    24   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000"
+  echo "  5     y_SYNTH              sec   0.000  0.000 0.000     4     5 0.0000     0    25   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000"
+  echo "  3001  SYNTH.g              ref   0.000  0.000 0.000     -     - 0.0000     0     1   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000"
+  echo "  3002  SYNTH.r              ref   0.000  0.000 0.000     -     - 0.0000     0     2   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000"
+  echo "  3003  SYNTH.i              ref   0.000  0.000 0.000     -     - 0.0000     0     3   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000"
+  echo "  3004  SYNTH.z              ref   0.000  0.000 0.000     -     - 0.0000     0     4   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000"
+  echo "  3005  SYNTH.y              ref   0.000  0.000 0.000     -     - 0.0000     0     5   0.000 0.000 0.000  0.000   0x0000 0x0000  0x0000 0x0000"
+  output stdout
+end
+
+macro ckradialflux
+ data $1
+ read -fits Chip.xrad X_APER Y_APER PSF_FWHM APER_FLUX APER_FLUX_ERR APER_FLUX_STDEV APER_FILL
+ read -fits Chip.psf X_PSF Y_PSF PSF_INST_MAG
+ set mag = PSF_INST_MAG
+ # XXX include nradii in header
+ for i 0 11
+   set mA$i = -2.5*log(APER_FLUX:$i)
+   set dM$i = mA$i - PSF_INST_MAG
+   vstat dM$i
+   $DM$i = $MEDIAN
+ end
+ for i 1 11
+   $j = $i - 1
+   echo {$DM$i - $DM$j}
+ end
+
+ lim mag -2 2; clear; box
+ for i 0 11
+   plot mag dM$i -c black
+ end
+end
+
+if ($SCRIPT)
+  fulltest 4
+  exit 0
+end
Index: branches/meh_branches/ppstack_test/pstamp/scripts/Makefile.am
===================================================================
--- branches/meh_branches/ppstack_test/pstamp/scripts/Makefile.am	(revision 32161)
+++ branches/meh_branches/ppstack_test/pstamp/scripts/Makefile.am	(revision 33415)
@@ -29,4 +29,5 @@
 	detect_query_create \
 	detect_response_create \
+        dquery_job_run.pl \
         dquery_finish.pl \
         dqueryparse.pl \
Index: branches/meh_branches/ppstack_test/pstamp/scripts/detect_query_read
===================================================================
--- branches/meh_branches/ppstack_test/pstamp/scripts/detect_query_read	(revision 32161)
+++ branches/meh_branches/ppstack_test/pstamp/scripts/detect_query_read	(revision 33415)
@@ -222,6 +222,7 @@
     if (($colData{FPA_ID}[$i] eq "Not_Set")&&(($colData{'FILTER'}[$i] ne "Not_Set")&&
 					      ($colData{'MJD-OBS'}[$i] ne "Not_Set"))) {
-	my $dateobs_begin = mjd_to_dateobs($colData{'MJD-OBS'}[$i]);
-	my $dateobs_end = mjd_to_dateobs($colData{'MJD-OBS'}[$i] + 1);
+        my $mjd_obs = int $colData{'MJD-OBS'}[$i];
+	my $dateobs_begin = mjd_to_dateobs($mjd_obs);
+	my $dateobs_end = mjd_to_dateobs($mjd_obs + 1);
 	my $ra = $colData{'RA1_DEG'}[$i];
 	my $dec = $colData{'DEC1_DEG'}[$i];
@@ -229,7 +230,13 @@
 	my $cmd;
 	if ($colData{STAGE}[$i] eq 'SSdiff') {
-	    $cmd = "$difftool -listrun -dbname $dbname -filter $filter ";
-	    $cmd .= " -dateobs_begin $dateobs_begin -dateobs_end $dateobs_end ";
-	    $cmd .= " -ra $ra -decl $dec -radius 1.5 -limit 1 -diff_mode 4";
+            # Assumption: All Stack Stack diffs are for a medium deep field
+            # go find which one the coordinates are in
+            # XXX: If we don't find one we'll just get the first SSdiff
+            # in the filter on the day in question
+            my $field = find_md_field($ra, $dec, 1.6);
+	    $cmd = "$difftool -listssrun -dbname $dbname -filter $filter ";
+	    $cmd .= " -mjd_obs_begin $mjd_obs -mjd_obs_end " . (1 + $mjd_obs);
+	    $cmd .= " -limit 1";
+            $cmd .= " -tess_id $field%" if $field;
 	}
 	elsif ($colData{STAGE}[$i] eq 'WSdiff') {
@@ -366,3 +373,35 @@
 
 
+# Given an RA and  DEC return the MD field that contains it if any.
+# This is much faster than actuallly examining the tessellation
+# using dvoImagesAtCoords
+
+sub find_md_field {
+    my $ra_deg = shift;
+    my $dec_deg = shift;
+    my $radius_deg = shift;
+
+    my @MD     = qw(MD01  MD02    MD03     MD04      MD05    MD06      MD07    MD08    MD09     MD10);
+    my @MD_RA  = (35.875, 53.10, 130.592, 150.000, 161.917, 185.000, 213.454, 242.787, 333.98, 352.312);
+    my @MD_DEC = (-4.25,  -27.8,   44.317,   2.20,   58.083,  47.117,  53.243, 54.95,    0.283, -0.433);
+    my $PS_DEG_RAD = 0.017453292519943;
+
+    my $ra     = $ra_deg * $PS_DEG_RAD;
+    my $dec    = $dec_deg * $PS_DEG_RAD;
+    my $radius = $radius_deg * $PS_DEG_RAD;
+
+    for (my $i = 0; $i < scalar @MD; $i++) {
+        
+        my $dist = acos((cos($PS_DEG_RAD * $MD_DEC[$i]) * cos($dec) * cos($PS_DEG_RAD * $MD_RA[$i] - $ra)) +(sin($PS_DEG_RAD * $MD_DEC[$i]) * sin($dec)));
+
+        if ($dist < $radius) {
+            return $MD[$i];
+        }
+    }
+    print STDERR "failed to find MD field containing $ra_deg $dec_deg\n";
+
+    return undef;
+}
+
+
 __END__
Index: branches/meh_branches/ppstack_test/pstamp/scripts/detectability_respond.pl
===================================================================
--- branches/meh_branches/ppstack_test/pstamp/scripts/detectability_respond.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/pstamp/scripts/detectability_respond.pl	(revision 33415)
@@ -20,15 +20,7 @@
 use PS::IPP::PStamp::RequestFile qw( :standard );
 use PS::IPP::PStamp::Job qw( :standard );
-use PS::IPP::Config qw($PS_EXIT_SUCCESS
-                       $PS_EXIT_UNKNOWN_ERROR
-                       $PS_EXIT_SYS_ERROR
-                       $PS_EXIT_CONFIG_ERROR
-                       $PS_EXIT_PROG_ERROR
-                       $PS_EXIT_DATA_ERROR
-                       $PS_EXIT_TIMEOUT_ERROR
-                       metadataLookupStr
-                       metadataLookupBool
-                       caturi
-                       );
+use PS::IPP::Config qw( :standard );
+use PS::IPP::Metadata::List qw( parse_md_list );
+
 use Astro::FITS::CFITSIO qw( :constants );
 Astro::FITS::CFITSIO::PerlyUnpacking(1);
@@ -41,5 +33,5 @@
 my $EXTVER = 1.0;
 my $EXTNAME = 'MOPS_DETECTABILITY_RESPONSE';
-my ($req_id,$req_name,$product,$need_magic,$missing_tools,$project);
+my ($req_id,$job_id,$req_name,$product,$need_magic,$missing_tools,$project);
 my ($request_file,$output,$workdir,$dbname,$dbserver,$verbose,$save_temps,$ignore_wisdom);
 GetOptions(
@@ -47,4 +39,5 @@
     'output=s'        =>      \$output,
     'workdir=s'       =>      \$workdir,
+    'job_id=s'        =>      \$job_id,
     'dbname=s'        =>      \$dbname,
     'dbserver=s'      =>      \$dbserver,
@@ -74,4 +67,6 @@
 
 my $ipprc = PS::IPP::Config->new();
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
 
 if (!$dbserver) {
@@ -108,7 +103,7 @@
 } # End reading wisdom.
 else {
-#
-# Parse input request file using detect_query_read (as it's already written).
-#
+    #
+    # Parse input request file using detect_query_read (as it's already written).
+    #
     my $dqr_command = "$detect_query_read --dbname $imagedb --input $request_file";
     my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
@@ -123,4 +118,6 @@
     {
 	my @column_names = ();
+        # split output into lines skip until the line which lists the column names is found
+        # Parse subsequent lines
 	foreach my $entry (split /\n/, (join "", @$stdout_buf)) {
 	    if ($entry =~ /^#/) {
@@ -128,19 +125,19 @@
 		shift(@column_names);  # Dump the hash sign.
 	    }
-	    else {
+	    elsif (scalar @column_names) {
 		# ROWNUM RA1_DEG DEC1_DEG RA2_DEG DEC2_DEG MAG QUERY_ID FPA_ID MJD-OBS FILTER OBSCODE STAGE
 		my %row_data;
 		@row_data{@column_names} = (split /\s+/, $entry);
 		for (my $i = 0; $i <= $#column_names; $i++) {
-		push @{ $query{$row_data{"FPA_ID"}}{$column_names[$i]} }, $row_data{$column_names[$i]};
-		$Nrows = scalar(keys(%query));
-#		print "$row_data{'FPA_ID'} $Nrows $i $column_names[$i] $row_data{$column_names[$i]}\n";
-	    }
-	    }
-	}
-    }
-#
-# Identify target images.  This should properly collate targets on a single imfile.
-#
+                    push @{ $query{$row_data{"FPA_ID"}}{$column_names[$i]} }, $row_data{$column_names[$i]};
+                    $Nrows = scalar(keys(%query));
+                    # print "$row_data{'FPA_ID'} $Nrows $i $column_names[$i] $row_data{$column_names[$i]}\n";
+                }
+	    }
+	}
+    }
+    #
+    # Identify target images.  This should properly collate targets on a single imfile.
+    #
     foreach my $fpa_id (keys %query) {
 	my %temp_hash;
@@ -212,4 +209,5 @@
 	}
 	elsif ($fpa_id eq 'Not_Set') {
+            # no matching file was found skip
 	    next;
 	}
@@ -227,7 +225,11 @@
 	    $need_magic = 0;
 	}
-	my $mjd_min = $mjd;
+	my $mjd_min = int $mjd;
 	my $mjd_max = $mjd + 1;
 	
+        my $req_filter;
+        if ($filter ne 'Not_Set') {
+            $req_filter = $filter . '%';
+        }
 	
 	# Call the PStamp code to find the images that contain the target on the given MJD in the specified filter.
@@ -237,86 +239,89 @@
 					  $fpa_id,undef,undef,
 					  $option_mask,$need_magic,
-					  # $ra,$dec,
-					  $mjd_min,$mjd_max,$filter . ".00000",undef,$verbose);  
+					  $mjd_min,$mjd_max,$req_filter,undef,$verbose);  
 	
 	foreach my $this_image_ref (@{ $pstamp_images_ref }) {
-	    foreach my $key (sort (keys %{ $this_image_ref } )) {
-		my $value = ${ $this_image_ref }{$key};
-		if ($key eq 'row_index') {
-		    $value = join ' ', @{ $this_image_ref->{$key} };
-		}
-
-#		print "$this_image_ref $key $value\n";
-		foreach my $valid_index (@{ $this_image_ref->{row_index} }) {
-		    $query{$fpa_id}{IMAGE}[$valid_index] = $this_image_ref->{image};
-		    $query{$fpa_id}{MASK}[$valid_index] = $this_image_ref->{mask};
-		    $query{$fpa_id}{WEIGHT}[$valid_index] = $this_image_ref->{weight};
-		    $query{$fpa_id}{PSF}[$valid_index] = $this_image_ref->{psf};
-		    $query{$fpa_id}{STAGE_ID}[$valid_index] = $this_image_ref->{stage_id};
-		    $query{$fpa_id}{IMAGE_DB}[$valid_index] = $this_image_ref->{imagedb};
-		    $query{$fpa_id}{NEED_MAGIC}[$valid_index] = $need_magic;
-		    $query{$fpa_id}{BAD_COMPONENT}[$valid_index] = 0;
-		    
-		    if (exists($this_image_ref->{astrom})) {
-			$query{$fpa_id}{CATALOG}[$valid_index] = $this_image_ref->{astrom};
-		    }
-		    else {
-			$query{$fpa_id}{CATALOG}[$valid_index] = $this_image_ref->{cmf};
-		    }
-		    if (exists($this_image_ref->{class_id})) {
-			$query{$fpa_id}{COMPONENT_ID}[$valid_index] = $this_image_ref->{class_id};
-			$query{$fpa_id}{CLASS_ID}[$valid_index] = $this_image_ref->{class_id};
-			
-		    }
-		    else {
-			$query{$fpa_id}{COMPONENT_ID}[$valid_index] = $this_image_ref->{skycell_id};
-			$query{$fpa_id}{CLASS_ID}[$valid_index] = 'fpa';
-		    }
-		    
-		    $query{$fpa_id}{STATE}[$valid_index] = $this_image_ref->{state};
-		    if (exists($this_image_ref->{data_state})) {
-			$query{$fpa_id}{DATA_STATE}[$valid_index] = $this_image_ref->{data_state};
-		    }
-		    else {
-			$query{$fpa_id}{DATA_STATE}[$valid_index] = $this_image_ref->{state};
-		    }
-		    $query{$fpa_id}{FAULT}[$valid_index] = 0;
+
+            if (0) {
+                foreach my $key (sort (keys %{ $this_image_ref } )) {
+                    my $value = ${ $this_image_ref }{$key};
+                    if ($key eq 'row_index') {
+                        $value = join ' ', @{ $this_image_ref->{$key} };
+                    }
+                    print "$this_image_ref $key $value\n";
+                }
+            }
+
+
+            foreach my $valid_index (@{ $this_image_ref->{row_index} }) {
+                $query{$fpa_id}{IMAGE}[$valid_index] = $this_image_ref->{image};
+                $query{$fpa_id}{MASK}[$valid_index] = $this_image_ref->{mask};
+                $query{$fpa_id}{WEIGHT}[$valid_index] = $this_image_ref->{weight};
+                $query{$fpa_id}{PSF}[$valid_index] = $this_image_ref->{psf};
+                $query{$fpa_id}{STAGE_ID}[$valid_index] = $this_image_ref->{stage_id};
+                $query{$fpa_id}{IMAGE_DB}[$valid_index] = $this_image_ref->{imagedb};
+                $query{$fpa_id}{NEED_MAGIC}[$valid_index] = $need_magic;
+                $query{$fpa_id}{BAD_COMPONENT}[$valid_index] = 0;
+                
+                if (exists($this_image_ref->{astrom})) {
+                    $query{$fpa_id}{CATALOG}[$valid_index] = $this_image_ref->{astrom};
+                }
+                else {
+                    $query{$fpa_id}{CATALOG}[$valid_index] = $this_image_ref->{cmf};
+                }
+                if (exists($this_image_ref->{class_id})) {
+                    $query{$fpa_id}{COMPONENT_ID}[$valid_index] = $this_image_ref->{class_id};
+                    $query{$fpa_id}{CLASS_ID}[$valid_index] = $this_image_ref->{class_id};
+                    
+                }
+                else {
+                    $query{$fpa_id}{COMPONENT_ID}[$valid_index] = $this_image_ref->{skycell_id};
+                    $query{$fpa_id}{CLASS_ID}[$valid_index] = 'fpa';
+                }
+                
+                $query{$fpa_id}{STATE}[$valid_index] = $this_image_ref->{state};
+                if (exists($this_image_ref->{data_state})) {
+                    $query{$fpa_id}{DATA_STATE}[$valid_index] = $this_image_ref->{data_state};
+                }
+                else {
+                    $query{$fpa_id}{DATA_STATE}[$valid_index] = $this_image_ref->{state};
+                }
+                $query{$fpa_id}{FAULT}[$valid_index] = 0;
 		    $query{$fpa_id}{MAGICKED}[$valid_index] = $this_image_ref->{magicked};
-		    if ($stage eq 'chip') {
-			$query{$fpa_id}{BURNTOOL_STATE}[$valid_index] = $this_image_ref->{burntool_state};
-		    }
-		    
-		    # Determine if the data exists.
-		    if (($query{$fpa_id}{STATE}[$valid_index] eq 'goto_purged') or 
-			($query{$fpa_id}{DATA_STATE}[$valid_index] eq 'purged') or
-			($query{$fpa_id}{STATE}[$valid_index] eq 'drop') or 
-			($query{$fpa_id}{STATE}[$valid_index] eq 'error_cleaned') or 
-			($query{$fpa_id}{STATE}[$valid_index] eq 'goto_scrubbed') or 
-			($query{$fpa_id}{DATA_STATE}[$valid_index] eq 'scrubbed')) {
-			
-			# image is gone and it's not coming back
-			$query{$fpa_id}{FAULT}[$valid_index] = $PSTAMP_GONE;
-		    }
-		    elsif ($need_magic and ($query{$fpa_id}{MAGICKED}[$valid_index] = 0)) {
-			$query{$fpa_id}{FAULT}[$valid_index] = $PSTAMP_NOT_DESTREAKED;
-		    }
-		    elsif (($query{$fpa_id}{DATA_STATE}[$valid_index] ne 'full')) {		      
-			$query{$fpa_id}{FAULT}[$valid_index] = $PSTAMP_NOT_AVAILABLE;
-			
-			# updating stacks isn't implemented
-			if (($stage eq 'stack')) {
-			    $query{$fpa_id}{FAULT}[$valid_index] = $PSTAMP_NOT_IMPLEMENTED;
-			}
-			# updating old burntool data isn't implemented
-			elsif ($stage eq 'chip') {
-			    if ($query{$fpa_id}{BURNTOOL_STATE}[$valid_index] and 
-				(abs($query{$fpa_id}{BURNTOOL_STATE}[$valid_index]) < 14)) {
-				$query{$fpa_id}{FAULT}[$valid_index] = $PSTAMP_NOT_IMPLEMENTED;
+                if ($stage eq 'chip') {
+                    $query{$fpa_id}{BURNTOOL_STATE}[$valid_index] = $this_image_ref->{burntool_state};
+                }
+                
+                # Determine if the data exists.
+                if (($query{$fpa_id}{STATE}[$valid_index] eq 'goto_purged') or 
+                    ($query{$fpa_id}{DATA_STATE}[$valid_index] eq 'purged') or
+                    ($query{$fpa_id}{STATE}[$valid_index] eq 'drop') or 
+                    ($query{$fpa_id}{STATE}[$valid_index] eq 'error_cleaned') or 
+                    ($query{$fpa_id}{STATE}[$valid_index] eq 'goto_scrubbed') or 
+                    ($query{$fpa_id}{DATA_STATE}[$valid_index] eq 'scrubbed')) {
+                    
+                    # image is gone and it's not coming back
+                    $query{$fpa_id}{FAULT}[$valid_index] = $PSTAMP_GONE;
+                }
+                elsif ($need_magic and ($query{$fpa_id}{MAGICKED}[$valid_index] eq 0)) {
+                    $query{$fpa_id}{FAULT}[$valid_index] = $PSTAMP_NOT_DESTREAKED;
+                }
+                elsif (($query{$fpa_id}{DATA_STATE}[$valid_index] ne 'full')) {		      
+                    $query{$fpa_id}{FAULT}[$valid_index] = $PSTAMP_NOT_AVAILABLE;
+                    
+                    # updating stacks isn't implemented
+                    if (($stage eq 'stack')) {
+                        $query{$fpa_id}{FAULT}[$valid_index] = $PSTAMP_NOT_IMPLEMENTED;
+                    }
+                    # updating old burntool data isn't implemented
+                    elsif ($stage eq 'chip') {
+                        if ($query{$fpa_id}{BURNTOOL_STATE}[$valid_index] and 
+                            (abs($query{$fpa_id}{BURNTOOL_STATE}[$valid_index]) < 14)) {
+                            $query{$fpa_id}{FAULT}[$valid_index] = $PSTAMP_NOT_IMPLEMENTED;
 			    }
-			}
-		    } # End determining error faults.
-		}
-	    }
-	}
+                    }
+                } # End determining error faults.
+            }
+        }
     }
 } # End calculating wisdom
@@ -324,41 +329,50 @@
 my %processing_request;
 
+if ($job_id) {
+    $wisdom_file = "$wisdom_file.$job_id";
+}
 open(WISDOM,">$wisdom_file") or my_die("failed to open wisdom file $wisdom_file");
 foreach my $fpa_id (keys %query) {
     for (my $i = 0; $i <= $#{ $query{$fpa_id}{ROWNUM} }; $i++) {
-	print WISDOM "$fpa_id\t";
-	foreach my $key (keys %{ $query{$fpa_id} }) {
-	    print WISDOM "$key $query{$fpa_id}{$key}[$i]\t";
-	}
-	print WISDOM "\n";
-	if ($query{$fpa_id}{BAD_COMPONENT}[$i] == 0) {
-	    @{ $update_request{$query{$fpa_id}{IMAGE}[$i]}{$query{$fpa_id}{FAULT}[$i]} } = 
-		($query{$fpa_id}{STATE}[$i],$query{$fpa_id}{STAGE}[$i],$query{$fpa_id}{STAGE_ID}[$i],
-		 $query{$fpa_id}{COMPONENT_ID}[$i],$query{$fpa_id}{NEED_MAGIC}[$i],$query{$fpa_id}{IMAGE_DB}[$i]);
-	    push @{ $processing_request{$fpa_id}{$query{$fpa_id}{IMAGE}[$i]} }, $i;
-	}
+        print WISDOM "$fpa_id\t";
+        foreach my $key (keys %{ $query{$fpa_id} }) {
+            print WISDOM "$key $query{$fpa_id}{$key}[$i]\t";
+        }
+        print WISDOM "\n";
+        my $data_state = $query{$fpa_id}{DATA_STATE}[$i];
+        if ($query{$fpa_id}{BAD_COMPONENT}[$i] == 0) {
+            if ($data_state ne 'full') {
+                
+                @{ $update_request{$query{$fpa_id}{IMAGE}[$i]}{$query{$fpa_id}{FAULT}[$i]} } = 
+                    ($query{$fpa_id}{STATE}[$i],$query{$fpa_id}{STAGE}[$i],$query{$fpa_id}{STAGE_ID}[$i],
+                     $query{$fpa_id}{COMPONENT_ID}[$i],$query{$fpa_id}{NEED_MAGIC}[$i],$query{$fpa_id}{IMAGE_DB}[$i]);
+            }
+            push @{ $processing_request{$fpa_id}{$query{$fpa_id}{IMAGE}[$i]} }, $i;
+        }
     }
 }
 close(WISDOM);
 
-# If there is anything that needs to be updated, create the update request list, and then exit the program.
-my $exit_code = 0;
-my $update_request_file = "${workdir}/update_request.dat";
-open(UPDATE_REQUEST,">$update_request_file") or my_die("failed to open update request_file $update_request_file");
-foreach my $images (keys %update_request) {
-    foreach my $fault (keys %{ $update_request{$images} }) {
-	if ($fault == 25) {
-	    $exit_code = 25;
-	}
-	elsif ($fault != 0) {
-	    $exit_code = 21;
-	}
-	my $update_request = join ' ', @{ $update_request{$images}{$fault} };
-	print UPDATE_REQUEST "$update_request\n";
-    }
-}
-close(UPDATE_REQUEST);
-if ($exit_code != 0) {
-    exit($exit_code);
+if (! defined $job_id) {
+    # If there is anything that needs to be updated, create the update request list, and then exit the program.
+    my $exit_code = 0;
+    my $update_request_file = "${workdir}/update_request.dat";
+    open(UPDATE_REQUEST,">$update_request_file") or my_die("failed to open update request_file $update_request_file");
+    foreach my $images (keys %update_request) {
+        foreach my $fault (keys %{ $update_request{$images} }) {
+            if ($fault == 25) {
+                $exit_code = 25;
+            }
+            elsif ($fault != 0) {
+                $exit_code = 21;
+            }
+            my $update_request = join ' ', @{ $update_request{$images}{$fault} };
+            print UPDATE_REQUEST "$update_request\n";
+        }
+    }
+    close(UPDATE_REQUEST);
+    if ($exit_code != 0) {
+        exit($exit_code);
+    }
 }
 
@@ -388,12 +402,19 @@
 	my $weight= $query{$fpa_id}{WEIGHT}[$index];
 	my $stage = $query{$fpa_id}{STAGE}[$index];
-	# if there's a fault, then we can't process this image.
-	if (($fault != 0)||($query{$fpa_id}{BAD_COMPONENT}[$index] == 1)) {
-	    $query{$fpa_id}{PROC_ERROR}[$index] = 23;
-	    
-	    $query{$fpa_id}{NPIX}[$index] = 0;
-	    $query{$fpa_id}{QFACTOR}[$index] = 0.0;
-	    $query{$fpa_id}{FLUX}[$index] = 0.0;
-	    $query{$fpa_id}{FLUX_SIG}[$index] = 0.0;
+	my $stage_id = $query{$fpa_id}{STAGE_ID}[$index];
+	my $component = $query{$fpa_id}{COMPONENT_ID}[$index];
+        # print "Input is from $stage $stage_id $component\n";
+
+	# if there's a fault or quality problem, then we can't process this image.
+	# if (($fault != 0)||($query{$fpa_id}{BAD_COMPONENT}[$index] == 1)) {
+	if ($fault = check_component($stage, $stage_id, $component, $imagedb)) {
+            foreach my $i (@{ $processing_request{$fpa_id}{$image} }) {
+                $query{$fpa_id}{PROC_ERROR}[$i] = $fault;
+                
+                $query{$fpa_id}{NPIX}[$i] = 0;
+                $query{$fpa_id}{QFACTOR}[$i] = 0.0;
+                $query{$fpa_id}{FLUX}[$i] = 0.0;
+                $query{$fpa_id}{FLUX_SIG}[$i] = 0.0;
+            }
 
 	    next;
@@ -695,4 +716,8 @@
 	    $inHeader->{FPA_ID}->{value} = $fpa_id;
 	}
+
+        my @keysa = keys %{$query{$fpa_id}};
+
+        print "$fpa_id: @keysa\n";
 	
 	push @{$colData{'ROWNUM'}}, @{ $query{$fpa_id}{ROWNUM} };
@@ -710,4 +735,7 @@
     $status = 0;
 #    print "$output\n";
+    if (-e $output) {
+        unlink $output or die "failed to unlink existing response file $output\n";
+    }
     my $outFits = Astro::FITS::CFITSIO::create_file( $output, $status );
     check_fitsio( $status );
@@ -779,4 +807,62 @@
 }
 
+sub check_component {
+    my ($stage, $stage_id, $component, $imagedb) = @_;
+
+    print "Checking status of component $stage $stage_id $component\n";
+
+    my $command;
+    if ($stage eq 'diff') {
+        $command = "difftool -diffskyfile -diff_id $stage_id -skycell_id $component";
+    } elsif ($stage eq 'stack') {
+        $command = "stacktool -sumskyfile -stack_id $stage_id";
+    } elsif ($stage eq 'warp') {
+        $command = "warptool -warped -warp_id $stage_id -skycell_id $component";
+    } elsif ($stage eq 'chip') {
+        $command = "chiptool -processedimfile -chip_id $stage_id -class_id $component";
+    } else {
+        die("check_component not implemented for stage $stage yet");
+    }
+
+    $command .= " -dbname $imagedb";
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $command, verbose => $verbose);
+    unless ($success) {
+        my $rc = $error_code >> 8;
+        carp "$command failed $error_code $rc";
+        exit $PS_EXIT_UNKNOWN_ERROR;
+    }
+    my $output = join "", @$stdout_buf;
+    my $metadata = $mdcParser->parse($output);
+    my $results = parse_md_list($metadata);
+    if (scalar @$results != 1) {
+        carp "$command returned too many components: " . scalar @$results;
+        exit $PS_EXIT_UNKNOWN_ERROR;
+    }
+    my $it = $results->[0];
+
+    if ($stage eq 'stack') {
+        $it->{data_state} = $it->{state};
+    }
+
+    my $return_status = 0;
+    if ($it->{quality}) {
+        print "  Bad quality: $it->{quality}\n";
+        $return_status = $PSTAMP_GONE;
+    } elsif ($it->{fault}) {
+        print "  Faulted: $it->{fault}\n";
+        $return_status = $PSTAMP_GONE;
+    } elsif ($it->{data_state} ne 'full') {
+        # XXX does this work for stack?
+        carp "  Faulted: $it->{data_state}\n";
+        $return_status = $PSTAMP_GONE;
+    } else {
+        print "  Component ok.\n";
+    }
+
+    return $return_status;
+}
+
 sub my_die {
     my $message = shift;
Index: branches/meh_branches/ppstack_test/pstamp/scripts/dquery_finish.pl
===================================================================
--- branches/meh_branches/ppstack_test/pstamp/scripts/dquery_finish.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/pstamp/scripts/dquery_finish.pl	(revision 33415)
@@ -13,4 +13,6 @@
 use File::Temp qw( tempfile );
 use File::Copy;
+use Astro::FITS::CFITSIO qw( :constants );
+Astro::FITS::CFITSIO::PerlyUnpacking(1);
 
 use PS::IPP::Metadata::Config;
@@ -66,25 +68,17 @@
 if ($product eq "NULL") {
     # parsing failed just with fault = 0 (this leaves previously set fault in place
-    stop_request($req_id, 0, $verbose);
+    update_request($req_id, 0, $verbose);
     exit 0;
 }
-if (0) {
-my $prod_dir = "$outputDataStoreRoot/$product";
-if (! -e $prod_dir ) {
-    # something must have gone wrong at the parse stage
-    stop_request($req_id, $PS_EXIT_SYS_ERROR, $verbose);
-    die "product directory does not exist $prod_dir";
-}
-my $outdir = "$prod_dir/$req_name";
-}
+
 if (! -e $outdir ) {
     # something must have gone wrong at the parse stage
     print STDERR "output fileset directory $outdir does not exist\n" if $verbose;
     if (! mkdir $outdir ) {
-        stop_request($req_id, $PS_EXIT_SYS_ERROR, $verbose);
+        update_request($req_id, $PS_EXIT_SYS_ERROR, $verbose);
         die "cannot create output directory $outdir";
     }
 } elsif (! -d $outdir) {
-    stop_request($req_id, $PS_EXIT_SYS_ERROR, $verbose);
+    update_request($req_id, $PS_EXIT_SYS_ERROR, $verbose);
     die "output fileset directory $outdir exists but is not a directory";
 }
@@ -97,4 +91,5 @@
 {
     my $command = "$pstamptool -listjob -req_id $req_id";
+    $command .= " -jobType detect_query";   # temporary
     $command .= " -dbname $dbname" if $dbname;
     $command .= " -dbserver $dbserver" if $dbserver;
@@ -117,27 +112,33 @@
 }
 
-# XXX: have the jobs produce the reglist as with postage stamp requests
-my ($REGLIST, $reg_list) = tempfile("$outdir/reqlist.XXXX", UNLINK => !$save_temps);
-
+my ($REGLIST, $reg_list) = tempfile("$outdir/reglist.XXXX", UNLINK => !$save_temps);
+my $response_file = "response.$req_id.fits";
+my $response_path = "$outdir/$response_file";
+
+print $REGLIST "$response_file|||table|\n";
+
+my @columns = qw(ROWNUM PROC_ERROR NPIX QFACTOR FLUX FLUX_SIG FPA_ID STAGE STAGE_ID COMPONENT CMFFILE);
+my %colData;
+foreach my $col (@columns) {
+    $colData{$col} = [];
+}
+    
+my $i = 0;
 foreach my $job (@jobs) {
     my $job_id = $job->{job_id};
-    my $response_file = "response${job_id}.fits";
-    my $response_path = "$outdir/$response_file";
-
-    if (-e $response_path) {
-        # the job generated a response file put it into the Data Store
-        print $REGLIST "$response_file|||table|\n";
-
-	# do the same if we have an error file.  Should the parse data be uploaded as well?
-	my $err_file = "parse_error.txt";
-	if (-e "$outdir/$err_file") {
-	    print $REGLIST "$err_file|||text|\n";
-	}
-    } else  {
-        print STDERR "detect_query response file for job $job_id not found\n" if $verbose;
-        $request_fault = $PS_EXIT_UNKNOWN_ERROR;
-    }
+
+    my $outputBase = $job->{outputBase};
+    my $results = "${outputBase}results.txt";
+
+    read_results($results, \%colData);
+
+    my $cmf = $colData{CMFFILE}->[$i];
+    print $REGLIST "$cmf|||table|\n";
+
+    $i++;
 }
 close $REGLIST;
+
+write_response_file($response_path, $req_name, \%colData);
 
 if (-s $reg_list) {
@@ -157,15 +158,221 @@
 }
 
-stop_request($req_id, $request_fault, $verbose);
+update_request($req_id, $request_fault, $verbose);
 
 exit 0;
 
-sub stop_request {
+sub read_results {
+    my $results_file = shift;
+    my $results = shift;
+
+    open IN, "<$results_file" or my_die("failed to open $results_file", $PS_EXIT_UNKNOWN_ERROR);
+
+    foreach my $line (<IN>) {
+        # skip header
+        next if $line =~ /ROWNUM/;
+        chomp $line;
+        my @words = split " ", $line;
+        my ($rownum, $proc_error, $npix, $qfactor, $flux, $flux_sig, $fpa_id, $stage, $stage_id, $component, $cmf) = @words;
+        push @{$results->{ROWNUM}}, $rownum;
+        push @{$results->{PROC_ERROR}}, $proc_error;
+        push @{$results->{NPIX}}, $npix;
+        push @{$results->{QFACTOR}}, $qfactor;
+        push @{$results->{FLUX}}, $flux;
+        push @{$results->{FLUX_SIG}}, $flux_sig;
+        push @{$results->{FPA_ID}}, $fpa_id;
+        push @{$results->{STAGE}}, $stage;
+        push @{$results->{STAGE_ID}}, $stage_id;
+        push @{$results->{COMPONENT}}, $component;
+        push @{$results->{CMFFILE}}, $cmf;
+    }
+    # XXX: make sure that the size of each of these arrays is the same
+}
+
+sub write_response_file {
+    my $output = shift;
+    my $query_id = shift;
+    my $results = shift;
+
+    my $columns;
+    my $headers;
+
+    my $EXTVER_IS_1 = 0 ; # (scalar(keys(%query)) == 1);
+#    print "EXTVER: $EXTVER_IS_1\n";
+    my ($FPA_ID,$MJD_OBS,$filter,$obscode,$status);
+    if ($EXTVER_IS_1 == 1) {
+	# Specification of columns to write
+	$columns = [
+	    # matching rownum from detectability original request
+	    { name => 'ROWNUM',   type => '20A', writetype => TSTRING }, 
+	    # any errors that occurred during processing
+	    { name => 'ERROR_CODE',   type => 'V', writetype => TULONG }, 
+	    # number of pixels used in hypothetical PSF for the query detection
+	    { name => 'DETECT_N', type => 'V',   writetype => TULONG },
+	    # detectibility, indicating the fraction of PSF pixels detetable by IPP
+	    { name => 'DETECT_F', type => 'D',   writetype => TDOUBLE },
+	    # flux of the target source
+	    { name => 'TARGET_FLUX', type => 'D', writetype => TDOUBLE },
+	    # error in the flux of the target source
+	    { name => 'TARGET_FLUX_SIG', type => 'D', writetype => TDOUBLE },
+	    ];
+	
+	# Header translation table
+	$headers = {
+	    'QUERY_ID' => { name => 'QUERY_ID', type => TSTRING, 
+			    comment => 'MOPS Query ID for this batch query' },
+	    'FPA_ID' => { name => 'FPA_ID',   type => TSTRING, 
+			  comment => 'original FPA_ID used at ingest' },
+# 	    'MJD-OBS' => { name => 'FPA_ID',   type => TSTRING, 
+# 			  comment => 'original FPA_ID used at ingest' },
+# 	    'FILTER' => { name => 'FPA_ID',   type => TSTRING, 
+# 			  comment => 'original FPA_ID used at ingest' },
+# 	    'OBSCODE' => { name => 'FPA_ID',   type => TSTRING, 
+# 			  comment => 'original FPA_ID used at ingest' },
+	};
+    }
+    else {
+	# Specification of columns to write
+	$columns = [
+	    # matching rownum from detectability original request
+	    { name => 'ROWNUM',   type => '20A', writetype => TSTRING}, 
+	    # any errors that occurred during processing
+	    { name => 'ERROR_CODE',   type => 'V', writetype => TULONG }, 
+	    # number of pixels used in hypothetical PSF for the query detection
+	    { name => 'DETECT_N', type => 'V',   writetype => TULONG },
+	    # detectibility, indicating the fraction of PSF pixels detetable by IPP
+	    { name => 'DETECT_F', type => 'D',   writetype => TDOUBLE },
+	    # flux of the target source
+	    { name => 'TARGET_FLUX', type => 'D', writetype => TDOUBLE },
+	    # error in the flux of the target source
+	    { name => 'TARGET_FLUX_SIG', type => 'D', writetype => TDOUBLE },
+	    # The FPA That would be in the header if it were to be there.
+	    { name => 'FPA_ID',   type => '20A', writetype => TSTRING },
+	    ];
+	
+	# Header translation table
+	$headers = {
+	    'QUERY_ID' => { name => 'QUERY_ID', type => TSTRING, 
+			    comment => 'MOPS Query ID for this batch query' },
+	};
+    }	
+
+    # Parse the list of columns
+    my @colNames;                   # Names of columns
+    my @colTypes;                   # Types of columns
+    my %colData;                    # Data for each column
+    my @colWriteType;                 # type to use to write
+    foreach my $colSpec ( @$columns) {
+	push @colNames, $colSpec->{name};
+	push @colTypes, $colSpec->{type};
+	push @colWriteType, $colSpec->{writetype};
+	$colData{$colSpec->{name}} = [];
+    }
+
+    my $inHeader = { };
+
+    # Hack to force the data to match the detect_response_create formats
+
+    $inHeader->{QUERY_ID}->{value} = $query_id;
+    if ($EXTVER_IS_1 == 1) {
+	my $fpa_id = $results->{FPA_ID}->[0];
+	$inHeader->{FPA_ID}->{value} = $fpa_id;
+    }
+    
+    # Fill the table columns with the data, making sure the flux is defined
+
+    @{$colData{'ROWNUM'}} = @{ $results->{ROWNUM} };
+    @{$colData{'ERROR_CODE'}} = @{ $results->{PROC_ERROR} };
+    @{$colData{'DETECT_N'}} = @{ $results->{NPIX} };
+    @{$colData{'DETECT_F'}} = @{ $results->{QFACTOR} };
+    @{$colData{'TARGET_FLUX'}} = @{ $results->{FLUX} };
+    @{$colData{'TARGET_FLUX_SIG'}} = @{ $results->{FLUX_SIG} };
+    @{$colData{'FPA_ID'}} = @{ $results->{FPA_ID} };
+
+    my $numRows = 0;
+    # Back to detect_response_create:
+    $status = 0;
+#    print "$output\n";
+    if (-e $output) {
+        unlink $output or die "failed to unlink existing response file $output\n";
+    }
+    my $outFits = Astro::FITS::CFITSIO::create_file( $output, $status );
+    check_fitsio( $status );
+    $outFits->create_img( 16, 0, undef, $status );
+    check_fitsio( $status );
+    my $EXTNAME = 'MOPS_DETECTABILITY_RESPONSE';
+    
+    $outFits->create_tbl( BINARY_TBL(), $numRows, scalar @colNames, \@colNames, \@colTypes, undef, $EXTNAME, $status);
+    check_fitsio( $status );
+
+    # TODO: get the extension version number from somewhere common
+    $outFits->write_key( TINT, 'EXTVER', 2, 'Version of this Extension', $status );
+    check_fitsio( $status );
+
+    # Write the Extension keywords
+    foreach my $keyword ( keys %$headers ) {
+	my $value = $inHeader->{$keyword}->{value}; # Header keyword value
+	unless (defined $value) {
+	    print "Can't find header keyword $keyword\n";
+	    next;
+	}
+	$value =~ s/\'//g;
+	my $name    = $headers->{$keyword}->{name}; # New name
+	my $type    = $headers->{$keyword}->{type}; # Type
+	my $comment = $headers->{$keyword}->{comment}; # Comment
+	$outFits->write_key( $type, $name, $value, $comment, $status );
+	check_fitsio( $status );
+    }
+
+    for (my $i = 0; $i < scalar @colNames; $i++) {
+	my $colName = $colNames[$i];# Column name
+	my $writeType = $colWriteType[$i];
+	my $numRows = scalar(@{$colData{$colName}});
+	unless(defined($writeType)) {
+	    print "write type undefined for $colName\n";
+	}
+	unless(defined($numRows)) {
+	    print "num Rows undefined for $colName\n";
+	}
+	unless(defined($status)) {
+	    print "status undefined for $colName\n";
+	}
+	unless(defined($colData{$colName})) {
+	    print "col data undefined for $colName\n";
+	}
+	unless(defined($colName)) {
+	    print "column name undefined for $i\n";
+	}
+	print STDERR "$writeType $i $numRows $colName $status @{ $colData{$colName} }\n";
+	$outFits->write_col( $writeType, $i + 1, 1, 1, $numRows, $colData{$colName}, $status );
+	print STDERR "$writeType $i $numRows $colName $status\n";
+	check_fitsio( $status );
+    }
+    $outFits->close_file( $status );
+    return($status);
+}
+
+# From Astro::FITS::CFITSIO demo
+sub check_fitsio
+{
+    my $status = shift;         # Status of FITSIO calls
+
+    if ($status != 0) {
+        my $msg;                # Message to output
+        Astro::FITS::CFITSIO::fits_get_errstatus( $status , $msg );
+	carp("CFITSIO error: $status => $msg");
+        die "CFITSIO error: $msg\n";
+    }
+}
+sub update_request {
     my $req_id = shift;
     my $fault = shift;
     my $verbose = shift;
     
-    my $command = "$pstamptool -updatereq -req_id $req_id -set_state stop";
-    $command   .= " -set_fault $fault" if $fault;
+    my $command = "$pstamptool -updatereq -req_id $req_id";
+    if ($fault) {
+        $command .= " -set_fault $fault";
+    } else {
+        $command .= " -set_state stop";
+    }
     $command   .= " -dbname $dbname" if $dbname;
     $command   .= " -dbserver $dbserver" if $dbserver;
@@ -176,2 +383,13 @@
     }
 }
+
+sub my_die {
+    my $msg = shift;
+    my $fault = shift;
+
+    print STDERR $msg;
+
+    update_request($req_id, $fault);
+
+    exit $fault;
+}
Index: branches/meh_branches/ppstack_test/pstamp/scripts/dquery_job_run.pl
===================================================================
--- branches/meh_branches/ppstack_test/pstamp/scripts/dquery_job_run.pl	(revision 33415)
+++ branches/meh_branches/ppstack_test/pstamp/scripts/dquery_job_run.pl	(revision 33415)
@@ -0,0 +1,400 @@
+#! /usr/bin/env perl
+#
+#
+# Run a detectability query job
+#
+#
+
+use strict;
+use warnings;
+
+use Sys::Hostname;
+use Carp;
+use File::Basename;
+use File::Copy;
+use IPC::Cmd 0.36 qw( can_run run );
+use Getopt::Long qw( GetOptions );
+use Pod::Usage qw( pod2usage );
+use File::Temp qw( tempfile tempdir);
+
+use PS::IPP::PStamp::RequestFile qw( :standard );
+use PS::IPP::PStamp::Job qw( :standard );
+use PS::IPP::Config qw( :standard );
+use PS::IPP::Metadata::List qw( parse_md_list );
+
+use Astro::FITS::CFITSIO qw( :constants );
+Astro::FITS::CFITSIO::PerlyUnpacking(1);
+
+#
+# Set up
+#
+my $host = hostname();
+
+my $EXTVER = 1.0;
+my $EXTNAME = 'MOPS_DETECTABILITY_RESPONSE';
+my ($req_id,$job_id,$req_name,$product,$need_magic,$missing_tools,$project);
+my ($output_base,$dbname,$dbserver,$verbose,$save_temps);
+GetOptions(
+    'output_base=s'   =>      \$output_base,
+    'job_id=s'        =>      \$job_id,
+    'dbname=s'        =>      \$dbname,
+    'dbserver=s'      =>      \$dbserver,
+    'verbose'         =>      \$verbose,
+    'save-temps'      =>      \$save_temps,
+    ) or pod2usage(2);
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --job_id --output_base",
+	   -exitval => 3,
+    ) unless
+    defined $job_id and defined $output_base;
+
+my $psphotForced      = can_run('psphotForced') or (warn "Can't find psphotForced" and $missing_tools = 1);
+my $ppCoord           = can_run('ppCoord') or (warn "Can't find ppCoord" and $missing_tools = 1);
+if ($missing_tools) {
+    warn("Can't find required tools.");
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+my $workdir = dirname($output_base);
+my $params_file = "${output_base}params.mdc";
+
+unless (defined $verbose) {
+    $verbose = 0;
+}
+
+my $ipprc = PS::IPP::Config->new();
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
+if (!$dbserver) {
+    $dbserver = metadataLookupStr($ipprc->{_siteConfig}, 'PS_DBSERVER');
+}
+
+my $imagedb = 'gpc1';
+my %args;
+
+open IN, "<$params_file" 
+    or my_die("failed to open params file: $params_file", "", "", "", "", "", "", $PS_EXIT_UNKNOWN_ERROR);
+
+my $data = $mdcParser->parse(join "", (<IN>))
+    or my_die("failed to parse paramsfile: $params_file", "", "", "", "", "", "", $PS_EXIT_UNKNOWN_ERROR);
+my $refs =  parse_md_list($data);
+if (scalar @$refs < 2) {
+    my_die("invalid paramsfile: $params_file", "", "", "", "", "", "", $PS_EXIT_UNKNOWN_ERROR);
+}
+
+my $params = shift @$refs;
+my @rows = @$refs;
+
+my $nRows = scalar @rows;
+print "Read $nRows rows from $params_file\n";
+    
+#exit 0;
+
+# run ppCoord and psphotForced to calculate the required data.
+my $fpa_id = $params->{FPA_ID};
+my $fault = $params->{FAULT};
+my $catalog = $params->{CATALOG};
+my $psf   = $params->{PSF};
+my $image  = $params->{IMAGE};
+my $mask  = $params->{MASK};
+my $weight= $params->{WEIGHT};
+my $stage = $params->{STAGE};
+my $stage_id = $params->{STAGE_ID};
+my $component = $params->{COMPONENT_ID};
+# print "Input is from $stage $stage_id $component\n";
+
+# XXX: TODO: if there's a fault or quality problem, then we can't process this image.
+# Perhaps we shouldn't even get here
+
+# Create coordinate file to convert to positions.
+my ($coordfile,$coordname) = tempfile("${workdir}/detect.coords.$job_id.XXXX", 
+                                      UNLINK => !$save_temps);
+my ($targetfile,$targetname) = tempfile("${workdir}/detect.targets.$job_id.XXXX", 
+                                        UNLINK => !$save_temps);
+foreach my $row (@rows) {
+    print $coordfile "$row->{RA1_DEG} $row->{DEC1_DEG}\n";
+}
+
+# Convert the sky coordinates to image coordinates with ppCoord.
+my $command = "ppCoord -astrom $catalog -radec $coordname";
+my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+    run(command => $command, verbose => $verbose);
+unless ($success) {
+    my_die("Unable to perform $command. Error_code: $error_code",
+           $params->{QUERY_ID},$fpa_id,$params->{'MJD-OBS'},
+           $params->{FILTER},$params->{OBSCODE},$params->{STAGE},
+           $error_code);
+}
+my @response = split /\n/, (join "", @$stdout_buf);
+my $i = 0;
+foreach my $line (@response) {
+    my ($r_ra,$r_dec,$trash,$r_x,$r_y,$r_chip) = split /\s+/, $line;
+    print $targetfile "$r_x $r_y\n";
+    my $row = $rows[$i];
+    if ((abs($r_ra - $row->{RA1_DEG}) < 1e-8) && (abs($r_dec - $row->{DEC1_DEG}))) {
+        $row->{X_PXL} = $r_x;
+        $row->{Y_PXL} = $r_y;
+        $row->{EXTENSION_BASE} = $r_chip;
+    } else {
+        $error_code = $PS_EXIT_PROG_ERROR;
+        my_die("Unable to match input RA/DEC with output RA/DEC: ($row->{RA1_DEG},$row->{DEC1_DEG}) -> ($r_ra,$r_dec) $error_code",
+           $params->{QUERY_ID},$fpa_id,$params->{'MJD-OBS'},
+           $params->{FILTER},$params->{OBSCODE},$params->{STAGE},
+           $error_code);
+    }
+    $i++;
+}
+
+# Run psphotForced on the target list.
+my $outroot = "${output_base}detectability.${stage}.${stage_id}";
+$outroot .= ".$component" unless $stage eq 'chip';
+
+$params->{PROC_ERROR} = 0;
+my $psphot_cmd = "$psphotForced -psf $psf ";
+$psphot_cmd .= "-file $image ";
+$psphot_cmd .= "-mask $mask ";
+$psphot_cmd .= "-variance $weight ";
+$psphot_cmd .= "-srctext $targetname $outroot ";
+$psphot_cmd .= "-D OUTPUT.FORMAT PS1_DV1 ";
+
+( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+    run(command => $psphot_cmd, verbose => $verbose);
+unless ($success) {
+    # $params->{PROC_ERROR} = $PSTAMP_SYSTEM_ERROR;
+    my $rc = $error_code >> 8;
+    my_die("$command failed: $error_code $rc",
+           $params->{QUERY_ID},$fpa_id,$params->{'MJD-OBS'},
+           $params->{FILTER},$params->{OBSCODE},$params->{STAGE},
+           $rc);
+}
+
+my $results = "${output_base}results.txt";
+open OUT, ">$results" or 
+        my_die("Failed to open results file $results",
+           $params->{QUERY_ID},$fpa_id,$params->{'MJD-OBS'},
+           $params->{FILTER},$params->{OBSCODE},$params->{STAGE},
+           $PS_EXIT_UNKNOWN_ERROR);
+
+# Read the output cmf and produce the data we need for the response file
+my $class_id = $params->{CLASS_ID};
+my $cmf = "${outroot}.${class_id}.cmf";
+my $cmfbase = basename($cmf);
+my ($tmp_Npix,$tmp_Qfact,$tmp_flux,$tmp_flux_error) = read_cmf_file($cmf,$rows[0]->{EXTENSION_BASE});
+$i = 0;
+print OUT "ROWNUM PROC_ERROR NPIX QFACTOR FLUX FLUX_SIG FPA_ID STAGE STAGE_ID COMPONENT CMFFILE\n";
+foreach my $row (@rows) {
+    $row->{PROC_ERROR} = $params->{PROC_ERROR};
+
+    $row->{NPIX} = ${ $tmp_Npix }[$i];
+    $row->{QFACTOR} = ${ $tmp_Qfact }[$i];
+    $row->{FLUX} = ${ $tmp_flux }[$i];
+    $row->{FLUX_SIG} = ${ $tmp_flux_error }[$i];
+    
+    print OUT "$row->{ROWNUM} $row->{PROC_ERROR} $row->{NPIX} $row->{QFACTOR} $row->{FLUX} $row->{FLUX_SIG} $fpa_id $stage $stage_id $component $cmfbase\n";
+    $i++;
+}
+close(OUT);
+
+exit 0;
+
+#
+# Utilities
+#
+
+# Taken largely from detect_query_read
+sub read_cmf_file {
+    my $cmf_file = shift;
+    my $class_id = shift;
+    my $extname = $class_id . ".psf";
+
+    my @tmp_Npix = ();
+    my @tmp_Qfact = ();
+    my @tmp_flux = ();
+    my @tmp_flux_err = ();
+
+    my ($detect_F_col,$detect_N_col,$detect_flux_col,$detect_mag_col) = (-1, -1, -1, -1);
+
+    
+    my $status = 0;
+    my $inFits = Astro::FITS::CFITSIO::open_file( $cmf_file, READONLY, $status ); # Open CMF
+    check_fitsio($status);
+    $inFits->movnam_hdu(BINARY_TBL, $extname, 0, $status) and check_fitsio($status);
+
+    my $inHeader = $inFits->read_header();
+
+    my $CMFversion = $inHeader->{EXTTYPE};
+    $CMFversion =~ s/\'//g;
+    $CMFversion =~ s/\s+//g;
+
+    # This is the only data we actually care about
+    my $column_defs;
+    if ($CMFversion eq 'PS1_V2') {
+	$column_defs = [
+	    { name => 'PSF_QF',   type => '1E', writetype => TDOUBLE },
+	    { name => 'PSF_NPIX', type => '1J', writetype => TLONG },
+	    { name => 'PSF_INST_MAG', type => '1E', writetype => TDOUBLE },
+	    { name => 'PSF_INST_MAG_SIG', type => '1E', writetype => TDOUBLE },
+	    ];
+    }
+    elsif ($CMFversion eq 'PS1_DV1') {
+	$column_defs = [
+	    { name => 'PSF_QF',   type => '1E', writetype => TDOUBLE },
+	    { name => 'PSF_NPIX', type => '1J', writetype => TLONG },
+	    { name => 'PSF_INST_FLUX', type => '1E', writetype => TDOUBLE },
+	    { name => 'PSF_INST_FLUX_SIG', type => '1E', writetype => TDOUBLE },
+	    ];
+    }
+
+    my @colNames; 
+    my @colTypes;
+    my @colWriteType;
+    my %colData;
+    foreach my $colSpec (@$column_defs) {
+	push @colNames, $colSpec->{name};
+	push @colTypes, $colSpec->{type};
+	push @colWriteType, $colSpec->{writetype};
+    }
+    my $numRows;
+    $inFits->get_num_rows($numRows, $status) and check_fitsio($status);
+
+    my $correct_error = 0;
+#    print STDERR "Ncols:" .  $#{ $column_defs } . "\n";
+    foreach my $col (@$column_defs) {
+	my ($col_num,$col_type,$col_data);
+	$inFits->get_colnum(0, $col->{name}, $col_num, $status) and check_fitsio($status);
+	if ($status != 0) {
+	    $status = 0;
+	    next;
+	}
+	$inFits->get_coltype($col_num, $col_type, undef, undef, $status) and check_fitsio($status);
+	$inFits->read_col($col_type, $col_num, 1, 1, $numRows, 0, $col_data, undef, $status) and check_fitsio($status);
+#	print STDERR "$col\t>>" . $col->{name} . "<<\t>>" . @{ $col_data } . "<<\n";
+	if ($col->{name} eq 'PSF_QF') {
+	    @tmp_Qfact = @{ $col_data };
+	}
+	elsif ($col->{name} eq 'PSF_NPIX') {
+	    @tmp_Npix = @{ $col_data };
+	}
+	elsif ($col->{name} eq 'PSF_INST_MAG') {
+	    @tmp_flux = map { $_ = 10**(-0.4 * $_) } @{ $col_data };
+	}
+	elsif ($col->{name} eq 'PSF_INST_MAG_SIG') {
+	    @tmp_flux_err = map { $_ = $_ / (2.5 * (log(exp(1)) / log(10))) } @{ $col_data };
+	    $correct_error = 1;
+	}
+	elsif ($col->{name} eq 'PSF_INST_FLUX') {
+	    @tmp_flux = @{ $col_data };
+	}
+	elsif ($col->{name} eq 'PSF_INST_FLUX_SIG') {
+	    @tmp_flux_err = @{ $col_data };
+	}
+    }
+    if ($correct_error) {
+	for (my $c = 0; $c <= $#tmp_flux_err; $c++) {
+	    $tmp_flux_err[$c] = $tmp_flux_err[$c] * $tmp_flux[$c];
+	}
+    }
+    $inFits->close_file( $status ) and check_fitsio($status);    
+#    print STDERR "$CMFversion\n";
+#    print STDERR "Q: $#tmp_Qfact\t@tmp_Qfact\nN: $#tmp_Npix\t@tmp_Npix\n";
+    return(\@tmp_Npix, \@tmp_Qfact, \@tmp_flux, \@tmp_flux_err);
+}
+
+
+# From Astro::FITS::CFITSIO demo
+sub check_fitsio
+{
+    my $status = shift;         # Status of FITSIO calls
+
+    if ($status != 0) {
+        my $msg;                # Message to output
+        Astro::FITS::CFITSIO::fits_get_errstatus( $status , $msg );
+	carp("CFITSIO error: $status => $msg");
+        die "CFITSIO error: $msg\n";
+    }
+}
+
+sub check_component {
+    my ($stage, $stage_id, $component, $imagedb) = @_;
+
+    print "Checking status of component $stage $stage_id $component\n";
+
+    my $command;
+    if ($stage eq 'diff') {
+        $command = "difftool -diffskyfile -diff_id $stage_id -skycell_id $component";
+    } elsif ($stage eq 'stack') {
+        $command = "stacktool -sumskyfile -stack_id $stage_id";
+    } elsif ($stage eq 'warp') {
+        $command = "warptool -warped -warp_id $stage_id -skycell_id $component";
+    } elsif ($stage eq 'chip') {
+        $command = "chiptool -processedimfile -chip_id $stage_id -class_id $component";
+    } else {
+        die("check_component not implemented for stage $stage yet");
+    }
+
+    $command .= " -dbname $imagedb";
+
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+	run(command => $command, verbose => $verbose);
+    unless ($success) {
+        my $rc = $error_code >> 8;
+        carp "$command failed $error_code $rc";
+        exit $PS_EXIT_UNKNOWN_ERROR;
+    }
+    my $output = join "", @$stdout_buf;
+    my $metadata = $mdcParser->parse($output);
+    my $results = parse_md_list($metadata);
+    if (scalar @$results != 1) {
+        carp "$command returned too many components: " . scalar @$results;
+        exit $PS_EXIT_UNKNOWN_ERROR;
+    }
+    my $it = $results->[0];
+
+    if ($stage eq 'stack') {
+        $it->{data_state} = $it->{state};
+    }
+
+    my $return_status = 0;
+    if ($it->{quality}) {
+        print "  Bad quality: $it->{quality}\n";
+        $return_status = $PSTAMP_GONE;
+    } elsif ($it->{fault}) {
+        print "  Faulted: $it->{fault}\n";
+        $return_status = $PSTAMP_GONE;
+    } elsif ($it->{data_state} ne 'full') {
+        # XXX does this work for stack?
+        carp "  Faulted: $it->{data_state}\n";
+        $return_status = $PSTAMP_GONE;
+    } else {
+        print "  Component ok.\n";
+    }
+
+    return $return_status;
+}
+
+sub my_die {
+    my $message = shift;
+    my $QUERY_ID = shift;
+    my $FPA_ID = shift;
+    my $MJD_OBS = shift;
+    my $FILTER = shift;
+    my $OBSCODE = shift;
+    my $STAGE = shift;
+
+    my $exit_code = shift;      # Exit code to add
+
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
+    carp("$message : $QUERY_ID $FPA_ID $MJD_OBS $FILTER $OBSCODE $STAGE");
+    exit($exit_code);
+
+}
+
+sub exit_with_failure {
+    my $status = shift;
+    my $message = shift;
+    carp("$message");
+    exit($status);
+}
+
Index: branches/meh_branches/ppstack_test/pstamp/scripts/dqueryparse.pl
===================================================================
--- branches/meh_branches/ppstack_test/pstamp/scripts/dqueryparse.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/pstamp/scripts/dqueryparse.pl	(revision 33415)
@@ -1,87 +1,83 @@
-#!/usr/bin/env perl
-#
-# parse a MOPS_DETCTABILITY_QUERY table and create a results file
+#! /usr/bin/env perl
+#
+#
+# Create a response to a MOPS_DETECTABILITY_QUERY
+#
 #
 
 use strict;
 use warnings;
+
+use Sys::Hostname;
 use Carp;
+use File::Basename;
+use File::Copy;
+use IPC::Cmd 0.36 qw( can_run run );
 use Getopt::Long qw( GetOptions );
 use Pod::Usage qw( pod2usage );
-use IPC::Cmd 0.36 qw( can_run run );
+use File::Temp qw( tempfile tempdir);
+use POSIX;  # for strftime
 
 use PS::IPP::PStamp::RequestFile qw( :standard );
 use PS::IPP::PStamp::Job qw( :standard );
-use PS::IPP::Config qw($PS_EXIT_SUCCESS
-		       $PS_EXIT_UNKNOWN_ERROR
-		       $PS_EXIT_SYS_ERROR
-		       $PS_EXIT_CONFIG_ERROR
-		       $PS_EXIT_PROG_ERROR
-		       $PS_EXIT_DATA_ERROR
-		       $PS_EXIT_TIMEOUT_ERROR
-		       metadataLookupStr
-		       metadataLookupBool
-		       caturi
-		       );
-
-my ($no_update, $imagedb, $label);
-my ($req_file, $req_id, $outdir, $product, $mode, $dbname, $dbserver, $verbose, $save_temps);
-my ($job_id,$rownum); # stuff from the post-update world
-#
-# parse args
-#
-
+use PS::IPP::Config qw( :standard );
+use PS::IPP::Metadata::List qw( parse_md_list );
+
+# use Astro::FITS::CFITSIO qw( :constants );
+# Astro::FITS::CFITSIO::PerlyUnpacking(1);
+
+my $host = hostname();
+
+my $mode = 'queue_job';
+
+my ($req_id, $product, $need_magic, $missing_tools, $project, $label);
+my ($request_file, $outdir, $dbname, $dbserver, $verbose, $save_temps, $no_update);
 GetOptions(
-        'file=s'          =>      \$req_file,
-        'req_id=s'        =>      \$req_id,
-        'job_id=s'        =>      \$job_id,
-        'rownum=s'        =>      \$rownum,
-        'outdir=s'       =>      \$outdir,
-        'label=s'         =>      \$label,
-        'product=s'       =>      \$product,
-        'mode=s'          =>      \$mode,
-        'dbname=s'        =>      \$dbname,
-        'dbserver=s'      =>      \$dbserver,
-        'verbose'         =>      \$verbose,
-        'save-temps'      =>      \$save_temps,
-) or pod2usage(2);
-
-die "invalid mode '$mode'" unless ($mode eq "list_uri") or ($mode eq "queue_job");
-die "--file or --job_id is required" if !$req_file;
-
-if ($mode ne "list_uri") {
-    die "req_id is required" if !$req_id;
-    die "outdir is required" if !$outdir;
-    die "product is required" if !$product;
-}
-my $missing_tools;
-my $pstamptool = can_run('pstamptool') or (warn "Can't find pstamptool" and $missing_tools =1);
-my $detectresponse = can_run('detectability_respond.pl') or 
-    (warn "Can't find detectability_respond.pl" and $missing_tools = 1);
-my $fields = can_run('fields') or (warn "Can't find fields" and $missing_tools =1);
-
+    'file=s'          =>      \$request_file,
+    'req_id=s'        =>      \$req_id,
+    'outdir=s'        =>      \$outdir,
+    'label=s'         =>      \$label,
+    'product=s'       =>      \$product,
+    'mode=s'          =>      \$mode,
+    'dbname=s'        =>      \$dbname,
+    'dbserver=s'      =>      \$dbserver,
+    'verbose'         =>      \$verbose,
+    'save-temps'      =>      \$save_temps,
+    'no-update'       =>      \$no_update,
+    ) or pod2usage(2);
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "Required options: --file --req_id --outdir --dbname",
+	   -exitval => 3,
+    ) unless
+    defined $request_file and defined $outdir and defined $dbname and defined $req_id;
+
+my $detect_query_read = can_run('detect_query_read') or (warn "Can't find detect_query_read" and $missing_tools = 1);
+my $pstamptool = can_run('pstamptool') or (warn "Can't find pstamptool" and $missing_tools = 1);
+my $fields = can_run('fields') or (warn "Can't find fields" and $missing_tools = 1);
 if ($missing_tools) {
     warn("Can't find required tools.");
-    exit ($PS_EXIT_CONFIG_ERROR);
-}
+    exit($PS_EXIT_CONFIG_ERROR);
+}
+
+unless (defined $verbose) {
+    $verbose = 0;
+}
+
+my $ipprc = PS::IPP::Config->new();
+my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
 
 if (!$dbserver) {
-    my $ipprc = PS::IPP::Config->new();
-    $dbserver =  metadataLookupStr($ipprc->{_siteConfig}, 'PS_DBSERVER');
-}
-
-# just deal with these arguments once and for all
-$pstamptool .= " -dbname $dbname" if $dbname;
-$pstamptool .= " -dbserver $dbserver" if $dbserver;
-$detectresponse .= " --dbname $dbname" if $dbname;
-
-$no_update = 1 if $mode eq "list_job";
-
-# Unless we're running as a job, write the parse arguments in case we need to rerun this parsing.
-if (!$job_id) {
-    my $argslist = "$outdir/parse.args";
-    open ARGSLIST, ">$argslist" or my_die("failed to open argslist file $argslist", $job_id, $PS_EXIT_UNKNOWN_ERROR);
-    print ARGSLIST "--label $label --mode $mode --req_id $req_id --product $product --outdir $outdir --file $req_file\n";
-    close ARGSLIST;
+    $dbserver = metadataLookupStr($ipprc->{_siteConfig}, 'PS_DBSERVER');
+}
+
+$pstamptool .= " -dbname $dbname -dbserver $dbserver";
+
+# Project name is hardcoded to gpc1 for the moment.
+$project = resolve_project($ipprc,"gpc1",$dbname,$dbserver);
+my $imagedb = $project->{dbname};
+if (!$imagedb) {
+    my_die("failed to find imagedb for project: $project", $PS_EXIT_CONFIG_ERROR);
 }
 
@@ -89,5 +85,5 @@
 my $fields_output; 
 {
-    my $command = "echo $req_file | $fields -x 0 EXTNAME EXTVER QUERY_ID";
+    my $command = "echo $request_file | $fields -x 0 EXTNAME EXTVER QUERY_ID";
     my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
         run(command => $command, verbose => $verbose);
@@ -97,123 +93,404 @@
 my (undef, $extname, $extver, $req_name) = split " ", $fields_output;
 
-my_die("$req_file is missing one of EXTNAME EXTVER or QUERY_ID", $PS_EXIT_PROG_ERROR)
+my_die("$request_file is missing one of EXTNAME EXTVER or QUERY_ID", $PS_EXIT_PROG_ERROR)
     if !(defined($extname) and defined($extver) and defined($req_name));
-my_die("$req_file has EXTNAME $extname not MOPS_DETECTABILITY_QUERY table", $PS_EXIT_PROG_ERROR)
+my_die("$request_file has EXTNAME $extname not MOPS_DETECTABILITY_QUERY table", $PS_EXIT_PROG_ERROR)
     if $extname ne "MOPS_DETECTABILITY_QUERY";
-my_die("$req_file is version $extver expecting 1", $PS_EXIT_PROG_ERROR)
+my_die("$request_file is version $extver expecting 1 or 2", $PS_EXIT_PROG_ERROR)
     if ($extver ne 1) and ($extver ne 2);
 
-# Set up the workdir for this query.
-if (! -e $outdir ) {
-    mkdir $outdir or my_die("cannot create output directory $outdir", $PS_EXIT_PROG_ERROR);
-} elsif (! -d $outdir ) {
-    my_die ("output fileset directory $outdir exists but is not a directory", $PS_EXIT_PROG_ERROR);
-}
-
-
-# Pass along the request file to the response generator.
-my $response_file = "$outdir/${req_name}.dresponse.${req_id}.fits";
-my $fault;
-my $data_to_update = '';
+if ($req_id) {
+    my $command = "$pstamptool -listreq  -name $req_name -not_req_id $req_id";
+    # set verbose to false so that error message about request not found doesn't appear in parse_error.txt
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => 0);
+    if ($success) {
+        # -listreq succeeded duplicate request name
+        print STDERR "QUERY_ID $req_name has already been used\n";
+        $command = "$pstamptool -addjob -job_type none -rownum 0 -state stop -fault $PSTAMP_DUP_REQUEST -req_id $req_id";
+        unless ($no_update) {
+            my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                run(command => $command, verbose => $verbose);
+            if (!$success) {
+                my_die("$command failed", $PS_EXIT_UNKNOWN_ERROR);
+            }
+        } else {
+            print STDERR "skipping $command\n";
+        }
+        
+        my $datestr = strftime "%Y%m%d%H%M%S.$req_id", gmtime;
+        $req_name = "ERROR.$datestr";
+    }
+    $command = "$pstamptool -updatereq -req_id $req_id  -set_name $req_name -set_outProduct $product";
+    unless ($no_update) {
+        ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            my_die("$command failed", $PS_EXIT_UNKNOWN_ERROR);
+        }
+    } else {
+        print STDERR "skipping $command\n";
+    }
+    exit (0);
+}
+
+
+
+#
+# query is a hash which uses $fpa_id as the key
+# The values are hash references which have the various parameter name as the key
+#   The values of the parameter hashes are arrays which contain the values for individual rows 
+#   in the detectabilty query request
+my %query = ();
+
 {
-    my $command = "$detectresponse --input $req_file --output $response_file --workdir $outdir";
-    $command .= " --save-temps" if $save_temps;
-    $command .= " --verbose" if $verbose;
-
+    #
+    # Parse input request file using detect_query_read
+    # result is a simple text file
+    #
+    my $dqr_command = "$detect_query_read --dbname $imagedb --input $request_file";
     my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
-        run(command => $command, verbose => $verbose);
+	run(command => $dqr_command, verbose => $verbose);
     unless ($success) {
-        warn("Warn! Unable to perform $command error code: $error_code");
-    }
-    # Use the fault code to see if we can regenerate the missing data.
-    $fault = $error_code >> 8;
-    if ($fault == $PSTAMP_NOT_AVAILABLE) {
-	unless (-e "$outdir/update_request.dat") {
-	    my_die ("Update request indicated, but unable to find actual request!", $PS_EXIT_PROG_ERROR);
-	}
-    }	
-    
-}
-
-my $result;
-# If we returned correctly with a valid response file, get a job ID
-# for the completed work, and move the response to a standardized name.
-if ($fault == 0) {
-    my $command = "$pstamptool -addjob -req_id $req_id -outputBase $outdir"; 
-    $command .= " -job_type detect_query -state stop -fault 0";
-    $command .= " -rownum 1";
-
-    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
-        run(command => $command, verbose => $verbose);
-    if ($success) {
-        $job_id = join "", @$stdout_buf;
-        chomp $job_id;
-        if ($job_id && -e $response_file) {
-            rename $response_file, "$outdir/response${job_id}.fits";
-        }
-        $result = 0;
+	# This is a problem, because I'm not sure how we handle a failure to read something.
+	# We need to return a $PSTAMP_INVALID_REQUEST, I think, but if we can't read it, 
+	# we can't send that response back.
+	die("Unable to perform $dqr_command error code: $error_code");
+    }
+    my $Nrows = 0;
+    {
+	my @column_names = ();
+        # split output into lines skip until the line which lists the column names is found
+        # Parse subsequent lines
+	foreach my $entry (split /\n/, (join "", @$stdout_buf)) {
+	    if ($entry =~ /^#/) {
+		@column_names = split /\s+/, $entry;
+		shift(@column_names);  # Dump the hash sign.
+	    }
+	    elsif (scalar @column_names) {
+		# ROWNUM RA1_DEG DEC1_DEG RA2_DEG DEC2_DEG MAG QUERY_ID FPA_ID MJD-OBS FILTER OBSCODE STAGE
+		my %row_data;
+		@row_data{@column_names} = (split /\s+/, $entry);
+		for (my $i = 0; $i <= $#column_names; $i++) {
+                    push @{ $query{$row_data{"FPA_ID"}}{$column_names[$i]} }, $row_data{$column_names[$i]};
+                    $Nrows = scalar(keys(%query));
+                    # print "$row_data{'FPA_ID'} $Nrows $i $column_names[$i] $row_data{$column_names[$i]}\n";
+                }
+	    }
+	}
+    }
+    #
+    # Identify target components.  This should properly collate targets on a single imfile.
+    #
+    my $query_id;
+    foreach my $fpa_id (keys %query) {
+	my %temp_hash;
+	my $query_style = 'byexp';
+	my $stage;
+	my $filter;
+	my $mjd;
+	# Confirm that we only have one stage/filter/mjd
+	if ($fpa_id ne 'Not_Set') {   # We only need to check things that aren't the known odd case.
+	    for (my $i = 0; $i <= $#{ $query{$fpa_id}{STAGE} }; $i++) {
+		$temp_hash{STAGE}{$query{$fpa_id}{STAGE}[$i]} = 1;
+		$temp_hash{FILTER}{$query{$fpa_id}{FILTER}[$i]} = 1;
+		$temp_hash{'MJD-OBS'}{$query{$fpa_id}{'MJD-OBS'}[$i]} = 1;
+	    }
+	    if (scalar(keys(%{ $temp_hash{STAGE} })) == 1) {
+		$stage = (keys(%{ $temp_hash{STAGE} }))[0];
+	    }
+	    else {
+		exit_with_failure(21,"Too many STAGEs specified");
+	    }
+	    if (scalar(keys(%{ $temp_hash{FILTER} })) == 1) {
+		$filter = (keys(%{ $temp_hash{FILTER} }))[0];
+	    }
+	    else {
+		exit_with_failure(21,"Too many FILTERs specified");
+	    }
+	    if (scalar(keys(%{ $temp_hash{'MJD-OBS'} })) == 1) {
+		$mjd = (keys(%{ $temp_hash{'MJD-OBS'} }))[0];
+	    }
+	    else {
+		exit_with_failure(21,"Too many MJD-OBS specified");
+	    }
+	}
+
+	# Set up a rowList with default values
+	my @rowList;
+	for (my $i = 0; $i <= $#{ $query{$fpa_id}{STAGE} }; $i++) {
+	    $rowList[$i]->{CENTER_X} = $query{$fpa_id}{RA1_DEG}[$i];
+	    $rowList[$i]->{CENTER_Y} = $query{$fpa_id}{DEC1_DEG}[$i];
+	    $rowList[$i]->{ID} = $query{$fpa_id}{ROWNUM}[$i];
+	    $rowList[$i]->{COORD_MASK} = 0;
+	    # Set default values
+	    $query{$fpa_id}{BAD_COMPONENT}[$i] = 1;
+	    $query{$fpa_id}{IMAGE}[$i] = 'no_image';
+	    $query{$fpa_id}{MASK}[$i] = 'no_mask';
+	    $query{$fpa_id}{WEIGHT}[$i] = 'no_weight';
+	    $query{$fpa_id}{PSF}[$i] = 'no_psf';
+
+	    $query{$fpa_id}{STAGE_ID}[$i] = 'no_id';
+	    $query{$fpa_id}{IMAGE_DB}[$i] = 'no_imdb';
+	    $query{$fpa_id}{NEED_MAGIC}[$i] = 'no_magic';
+	    $query{$fpa_id}{MAGICKED}[$i] = 'no_magic';
+	    $query{$fpa_id}{CATALOG}[$i] = 'no_catalog';
+	    $query{$fpa_id}{COMPONENT_ID}[$i] = 'no_component';
+	    $query{$fpa_id}{CLASS_ID}[$i] = 'no_class';
+
+	    $query{$fpa_id}{STATE}[$i] = 'no_state';
+	    $query{$fpa_id}{DATA_STATE}[$i] = 'no_dstate';
+	    $query{$fpa_id}{FAULT}[$i] = 'no_fault';
+	    $query{$fpa_id}{BURNTOOL_STATE}[$i] = 'no_btstate';
+	}
+
+	# Determine the query style for this fpa_id
+	if ($fpa_id =~ /o.*g.*o/) {
+	    $query_style = 'byexp';
+	}
+	elsif ($fpa_id =~ /\d+/) {
+	    $query_style = 'byid';
+	}
+	elsif ($fpa_id eq 'Not_Set') {
+            # no matching file was found skip
+	    next;
+	}
+	else {
+	    exit_with_failure(21,"Parse error in request file");
+	}
+	# Set common request components
+	my $option_mask |= 1;
+	$option_mask |= $PSTAMP_SELECT_IMAGE;
+	$option_mask |= $PSTAMP_SELECT_MASK;
+	$option_mask |= $PSTAMP_SELECT_VARIANCE;
+	$option_mask |= $PSTAMP_SELECT_PSF;
+
+        # magic is dead
+	my $need_magic = 0;
+	if ($stage eq 'stack') {
+	    $need_magic = 0;
+	}
+	my $mjd_min = int $mjd;
+	my $mjd_max = $mjd + 1;
+	
+        my $req_filter;
+        if ($filter ne 'Not_Set') {
+            $req_filter = $filter . '%';
+        }
+	
+	# Call the PStamp code to find the images that contain the target on the given MJD in the specified filter.
+	my $pstamp_images_ref = locate_images($ipprc,$imagedb,
+					  \@rowList,
+					  $query_style,$stage,
+					  $fpa_id,undef,undef,
+					  $option_mask,$need_magic,
+					  $mjd_min,$mjd_max,$req_filter,undef,$verbose);  
+	
+	foreach my $this_image_ref (@{ $pstamp_images_ref }) {
+
+            # loop over the rows that this component matched
+            foreach my $valid_index (@{ $this_image_ref->{row_index} }) {
+                $query{$fpa_id}{IMAGE}[$valid_index] = $this_image_ref->{image};
+                $query{$fpa_id}{MASK}[$valid_index] = $this_image_ref->{mask};
+                $query{$fpa_id}{WEIGHT}[$valid_index] = $this_image_ref->{weight};
+                $query{$fpa_id}{PSF}[$valid_index] = $this_image_ref->{psf};
+                $query{$fpa_id}{STAGE_ID}[$valid_index] = $this_image_ref->{stage_id};
+                $query{$fpa_id}{IMAGE_DB}[$valid_index] = $this_image_ref->{imagedb};
+                $query{$fpa_id}{NEED_MAGIC}[$valid_index] = $need_magic;
+                $query{$fpa_id}{BAD_COMPONENT}[$valid_index] = 0;
+                
+                if (exists($this_image_ref->{astrom})) {
+                    $query{$fpa_id}{CATALOG}[$valid_index] = $this_image_ref->{astrom};
+                }
+                else {
+                    $query{$fpa_id}{CATALOG}[$valid_index] = $this_image_ref->{cmf};
+                }
+                if (exists($this_image_ref->{class_id})) {
+                    $query{$fpa_id}{COMPONENT_ID}[$valid_index] = $this_image_ref->{class_id};
+                    $query{$fpa_id}{CLASS_ID}[$valid_index] = $this_image_ref->{class_id};
+                    
+                }
+                else {
+                    $query{$fpa_id}{COMPONENT_ID}[$valid_index] = $this_image_ref->{skycell_id};
+                    $query{$fpa_id}{CLASS_ID}[$valid_index] = 'fpa';
+                }
+                
+                $query{$fpa_id}{STATE}[$valid_index] = $this_image_ref->{state};
+                if (exists($this_image_ref->{data_state})) {
+                    $query{$fpa_id}{DATA_STATE}[$valid_index] = $this_image_ref->{data_state};
+                }
+                else {
+                    $query{$fpa_id}{DATA_STATE}[$valid_index] = $this_image_ref->{state};
+                }
+                $query{$fpa_id}{FAULT}[$valid_index] = 0;
+		    $query{$fpa_id}{MAGICKED}[$valid_index] = $this_image_ref->{magicked};
+                if ($stage eq 'chip') {
+                    $query{$fpa_id}{BURNTOOL_STATE}[$valid_index] = $this_image_ref->{burntool_state};
+                }
+                
+                # Determine if the data exists.
+                if (($query{$fpa_id}{STATE}[$valid_index] eq 'goto_purged') or 
+                    ($query{$fpa_id}{DATA_STATE}[$valid_index] eq 'purged') or
+                    ($query{$fpa_id}{STATE}[$valid_index] eq 'drop') or 
+                    ($query{$fpa_id}{STATE}[$valid_index] eq 'error_cleaned') or 
+                    ($query{$fpa_id}{STATE}[$valid_index] eq 'goto_scrubbed') or 
+                    ($query{$fpa_id}{DATA_STATE}[$valid_index] eq 'scrubbed')) {
+                    
+                    # image is gone and it's not coming back
+                    $query{$fpa_id}{FAULT}[$valid_index] = $PSTAMP_GONE;
+                }
+                elsif ($need_magic and ($query{$fpa_id}{MAGICKED}[$valid_index] eq 0)) {
+                    $query{$fpa_id}{FAULT}[$valid_index] = $PSTAMP_NOT_DESTREAKED;
+                }
+                elsif (($query{$fpa_id}{DATA_STATE}[$valid_index] ne 'full')) {		      
+                    $query{$fpa_id}{FAULT}[$valid_index] = $PSTAMP_NOT_AVAILABLE;
+                    
+                    # updating stacks isn't implemented
+                    if (($stage eq 'stack')) {
+                        $query{$fpa_id}{FAULT}[$valid_index] = $PSTAMP_NOT_IMPLEMENTED;
+                    }
+                    # updating old burntool data isn't implemented
+                    elsif ($stage eq 'chip') {
+                        if ($query{$fpa_id}{BURNTOOL_STATE}[$valid_index] and 
+                            (abs($query{$fpa_id}{BURNTOOL_STATE}[$valid_index]) < 14)) {
+                            $query{$fpa_id}{FAULT}[$valid_index] = $PSTAMP_NOT_IMPLEMENTED;
+			    }
+                    }
+                } # End determining error faults.
+            }
+        }
+    }
+}
+
+my %update_request;
+my %rows_for_component;
+my %faulted_rows;
+
+# now build a hash using image name as key which contains the list of rows for that image
+foreach my $fpa_id (keys %query) {
+    for (my $i = 0; $i <= $#{ $query{$fpa_id}{ROWNUM} }; $i++) {
+        if ($query{$fpa_id}{BAD_COMPONENT}[$i] == 0) {
+            push @{ $rows_for_component{$fpa_id}{$query{$fpa_id}{IMAGE}[$i]} }, $i;
+        } else {
+            $faulted_rows{$query{$fpa_id}{ROWNUM}[$i]} = $query{$fpa_id}{FAULT}[$i];
+        }
+    }
+}
+
+#
+# Finally build the parameter lists and queue jobs for each component
+#
+
+my $job_num = 1;
+foreach my $fpa_id (keys %rows_for_component) {
+    foreach my $component (keys % {$rows_for_component{$fpa_id} } ) {
+        my $i = $rows_for_component{$fpa_id}{$component}[0];
+        my $dep_id = 0;
+        my $data_state = $query{$fpa_id}{DATA_STATE}[$i];
+        if ($data_state ne 'full') {
+            # Queue this data to be updated
+            my $stage = $query{$fpa_id}{STAGE}[$i];
+            my $stage_id = $query{$fpa_id}{STAGE_ID}[$i];
+            my $component = $query{$fpa_id}{COMPONENT_ID}[$i];
+            print "Need to UPDATE $stage $stage_id $component from $data_state\n";
+            $dep_id = queue_update_run($req_id, $outdir, $label, $data_state, $stage, $stage_id, $component, 0, $imagedb);
+        }
+
+        my $outputBase = "$outdir/$job_num" . "_";
+
+        # params_file is an mdc description containing the parameters for the job
+        my $params_file = $outputBase . "params.mdc";
+
+        open PARAMS, ">$params_file" or my_die("failed to create $params_file", $PS_EXIT_UNKNOWN_ERROR);
+
+        # first the job params
+        print PARAMS "dqueryJobParams METADATA\n";
+        foreach my $key (keys %{ $query{$fpa_id} } ) {
+            my $value = $query{$fpa_id}{$key}[0];
+            print PARAMS "\t$key\t\tSTR\t$value\n";
+        }
+        print PARAMS "END\n\n";
+
+        # then one structure with the coordinates for each row
+        print PARAMS "dqueryCoord MULTI\n";
+
+        my @coordkeys = qw(ROWNUM RA1_DEG DEC1_DEG RA2_DEG DEC2_DEG MAG);
+        foreach my $row ( @{ $rows_for_component{$fpa_id}{$component} }) {
+            print PARAMS "\ndqueryCoord METADATA\n";
+            foreach my $key (@coordkeys) {
+                my $value = $query{$fpa_id}{$key}[$row];
+                print PARAMS "\t$key\t\tSTR\t$value\n";
+            }
+            print PARAMS "END\n";
+        }
+        close(PARAMS);
+
+        # use first rownum for this component as the rownum for the job
+        my $rownum = $query{$fpa_id}{ROWNUM}[$rows_for_component{$fpa_id}{$component}[0]];
+
+        my $command = "$pstamptool -addjob -req_id $req_id -outputBase $outputBase -job_type detect_query -state run -rownum $rownum";
+        $command .= " -dep_id $dep_id" if $dep_id;
+        unless ($no_update) {
+            my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+                run(command => $command, verbose => $verbose);
+            if ($success) {
+                my $job_id = join "", @$stdout_buf;
+                print "   Queued job: $job_id\n";
+            } else {
+                my $exit_status = $error_code >> 8;
+                $exit_status = $PS_EXIT_UNKNOWN_ERROR unless $exit_status;
+                my_die("Unable to perform $command error code: $error_code", $exit_status);
+            }
+        } else {
+            print STDERR "skipping $command\n";
+        }
+        $job_num++;
+    }
+}
+
+foreach my $rownum (keys %faulted_rows ) {
+    my $outputBase = "$outdir/$job_num" . "_";
+    my $fault = $faulted_rows{$rownum};
+    $fault = $PSTAMP_NO_IMAGE_MATCH if ($fault eq 'no_fault');
+    print STDERR "insert faulted job for $rownum: $fault\n";
+    my $command = "$pstamptool -addjob -req_id $req_id -outputBase $outputBase -job_type detect_query -state stop -rownum $rownum -fault $fault";
+    unless ($no_update) {
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        if ($success) {
+            my $job_id = join "", @$stdout_buf;
+            print "   Queued job: $job_id\n";
+        } else {
+            my $exit_status = $error_code >> 8;
+            $exit_status = $PS_EXIT_UNKNOWN_ERROR unless $exit_status;
+            my_die("Unable to perform $command error code: $error_code", $exit_status);
+        }
     } else {
-        warn("Unable to perform $command error code: $error_code");
-        $result = $error_code >> 8;
-    }
-}
-elsif ($fault == $PSTAMP_NOT_AVAILABLE) {
-    # Failed to run correctly, which means that we need to queue a job and flag data for updating.
-    # Get the dependency id for the data we're requesting be updated.
-    open(UPDATE_REQUEST,"$outdir/update_request.dat") || my_die ("Update request indicated, but unable to find actual request!", $PS_EXIT_PROG_ERROR);
-    while (<UPDATE_REQUEST>) {
-	my $data_to_update = $_;
-	chomp($data_to_update);
-	my $dep_id = queue_update_run($req_id,$job_id,$outdir,$label,$data_to_update);
-
-	# Link this request to a job and link that job to any dependency
-	my $command = "$pstamptool -addjob -req_id $req_id -outputBase $outdir"; 
-	$command .= " -job_type detect_query -state run -fault 0";
-	$command .= " -rownum 1";
-	$command .= " -dep_id $dep_id" if $dep_id;
-
-	my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
-	    run(command => $command, verbose => $verbose);
-
-	if ($success) {
-	    $job_id = join "", @$stdout_buf;
-	    chomp $job_id;
-	    if ($job_id && -e $response_file) {
-		# We shouldn't have a response file at this stage.
-		rename $response_file, "$outdir/response${job_id}.fits";
-	    }
-	    $result = 0;
-	} else {
-	    warn("Unable to perform $command error code: $error_code");
-	    $result = $error_code >> 8;
-	}
-    }
-    close(UPDATE_REQUEST);
-}
-else {
-    my_die ("Parse fault!!", $PS_EXIT_UNKNOWN_ERROR);
-}
-
-# This does not set the request state to stop.  That will happen with the request_finish.pl script, 
-# which will notice that we've inserted the stopped job and decide we're finished. Easy enough.
+        print STDERR "skipping $command\n";
+    }
+    $job_num++;
+}
+
 {
     my $command = "$pstamptool -updatereq -req_id $req_id -set_name $req_name -set_outProduct $product";
-    $command .= " -set_fault $result" if $result;
-
-    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
-        run(command => $command, verbose => $verbose);
-    unless ($success) {
+    # $command .= " -set_fault $result" if $result;
+
+    unless ($no_update) {
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
         my_die("$command failed",$PS_EXIT_UNKNOWN_ERROR);
-    }
-}
- 
+        }
+    } else {
+        print STDERR "Skipping $command\n";
+    }
+}
+
 exit 0;
 
-
-# If we have to queue an update run, do so and create a new dependent
 sub queue_update_run {
-    my ($req_id, $job_id, $outdir, $label, $data_to_update) = @_;
-
-    my ($state, $stage, $stage_id, $component, $need_magic, $imagedb) = split /\s+/, $data_to_update;
+    my ($req_id, $outdir, $label, $state, $stage, $stage_id, $component, $need_magic, $imagedb) = @_;
+
+#    my ($state, $stage, $stage_id, $component, $need_magic, $imagedb) = split /\s+/, $data_to_update;
     
     if (($state ne 'cleaned') and ($state ne 'update') and ($state ne 'goto_cleaned')) {
@@ -248,14 +525,20 @@
     return($dep_id);
 }
-	
-    
+
 
 sub my_die {
-    my $msg = shift;
-    my $fault = shift;
-
-    carp $msg;
-
-    # we don't fault the request here pstamp_parser_run.pl handles that if necessary
-    exit $fault;
-}
+    my $message = shift;
+    my $exit_code = shift;
+
+    $exit_code = $PS_EXIT_PROG_ERROR unless defined $exit_code;
+
+    carp("$message");
+    exit($exit_code);
+}
+
+sub exit_with_failure {
+    my $status = shift;
+    my $message = shift;
+    my_die($message, $status);
+}
+
Index: branches/meh_branches/ppstack_test/pstamp/scripts/pstamp_checkdependent.pl
===================================================================
--- branches/meh_branches/ppstack_test/pstamp/scripts/pstamp_checkdependent.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/pstamp/scripts/pstamp_checkdependent.pl	(revision 33415)
@@ -117,7 +117,10 @@
     }
 }
+
+# magic is no longer rquired
+$need_magic = 0;
+$magic_ok =  1;
 my $status = 0;
-if ((($it->{state} eq 'full') or ($it->{state} eq 'update')) and ($it->{data_state} eq 'full')
-        and (!$need_magic or $magic_ok or $it->{magicked} > 0)) {
+if ((($it->{state} eq 'full') or ($it->{state} eq 'update')) and ($it->{data_state} eq 'full')) {
 
     # This Dependency is satisfied. All done. Release the pstampJobs
@@ -197,4 +200,5 @@
     }
 
+if (0) {
     if (!$job_fault and ($stage eq 'chip')) {
         # what about "error_cleaned" ?
@@ -210,4 +214,5 @@
         }
     }
+}
 
     if ($job_fault >= $PSTAMP_FIRST_ERROR_CODE) {
@@ -240,8 +245,10 @@
         my $state = $chip->{state};
         my $data_state = $chip->{data_state};
+        my $fault = $chip->{fault};
         if (($state =~ /error/) or ($state =~ /purged/) or ($state =~ /scrubbed/) or ($state eq 'drop') or
-            ($data_state =~ /error/) or ($data_state =~ /purged/) or ($data_state =~ /scrubbed/) or ($data_state eq 'drop')) {
-
-            print "chipRun state is $chip->{chip_id} has state: $state data_state: $data_state cannot update\n";
+            ($data_state =~ /error/) or ($data_state =~ /purged/) or ($data_state =~ /scrubbed/) or ($data_state eq 'drop') or
+            ($fault eq $PSTAMP_GONE)) {
+
+            print "chipRun state is $chip->{chip_id} has state: $state data_state: $data_state fault: $fault cannot update\n";
             my $error_code;
             if (($state eq 'error_cleaned') or ($data_state ='error_cleaned')) {
@@ -253,8 +260,4 @@
             # caller will fault the jobs
             return $error_code;
-        } elsif ($chip->{dsRun_state} eq 'failed_revert') {
-            # XXX: revert failures are rarely fixed. give up but say it's just not available not GONE 
-            print "magicDSRun.state = $dsRun_state for chipRun $stage_id is in state failed_revert cannot update\n";
-            return $PSTAMP_NOT_AVAILABLE;
         } elsif (($chip->{data_state} ne 'update') and ($chip->{data_state} ne 'full')) {
 
@@ -282,25 +285,16 @@
             } elsif ($fault_count > $max_fault_count) {
                 print "$stage $stage_id has faulted $fault_count times. Giving up\n";
-                $fault = $PSTAMP_GONE;
-                faultComponent('chip', $chip->{chip_id}, $chip->{class_id}, $PSTAMP_GONE);
-                return $PSTAMP_GONE;
+                # XXX: stop faulting chips for now
+#                $fault = $PSTAMP_GONE;
+#                faultComponent('chip', $chip->{chip_id}, $chip->{class_id}, $PSTAMP_GONE);
+                $fault = $PSTAMP_NOT_AVAILABLE;
+                return $fault;
             }
             # fault the dependent
             my_die("chip $chip->{chip_id} $chip->{class_id} faulted: $chip->{fault}", $chip->{fault});
-        } elsif ($chip->{dsFile_fault} eq $PSTAMP_GONE) {
-            print STDERR "magicDSFile $chip->{magic_ds_id} $chip->{chip_id} $chip->{class_id} is GONE";
-            return $PSTAMP_GONE;
-        } elsif ($chip->{dsFile_fault} and ($chip->{dsFile_data_state} eq 'update')) {
-            # fault the dependent
-            my_die("magicDSFile $chip->{magic_ds_id} $chip->{chip_id} $chip->{class_id} faulted: $chip->{dsFile_fault}", $chip->{dsFile_fault});
-        }
-    }
-
-    my $status = 0;
-    if (!$queued_update) {
-        $status = check_states_magicDSRun('chip', $chip_id, \@chips, $rlabel, $need_magic, $raw_all_magicked, $magic_ds_id, $dsRun_state);
-    }
-
-    return $status;
+        }
+    }
+
+    return 0;
 }
 
@@ -321,8 +315,9 @@
     # The update system currently requires that data that has been magicked be destreaked
     # at chip stage so it needs magic even if the customer doesn't.
+if (0) {
     if ($metadata->{magicked} != 0) {
         $need_magic = 1;
     }
-
+}
     if (($state =~ /error/) or ($state =~ /purged/) or ($state =~ /scrubbed/) or ($state eq 'drop') or
          ($data_state =~ /error/) or ($data_state =~ /purged/) or ($data_state =~ /scrubbed/) or ($data_state eq 'drop')) {
@@ -369,8 +364,10 @@
         # if chip has been magicked before require it to be magicked again
         # because the warp pending query requires it.
+if (0) {
         if ($chip->{magicked} < 0) {
             print "Input has been destreaked so we must destreak before warping\n";
             $need_magic = 1 
         }
+}
 
         if ($need_magic and ($chip->{magicked} eq 0)) {
@@ -421,5 +418,5 @@
     my $metadata = shift;
     my $rlabel = shift;     # if defined a new label for the chipRun
-    my $need_magic = shift; 
+    my $need_magic = 0; 
 
     my $diff_id   = $metadata->{diff_id};
@@ -427,10 +424,4 @@
     my $skycell = $metadata;
     my $skycell_id = $skycell->{skycell_id};
-
-    # The update system currently requires that data that has been magicked be destreaked
-    # at chip stage so it needs magic even if the customer doesn't.
-    if ($metadata->{magicked} != 0) {
-        $need_magic = 1;
-    }
 
     if ($diff_mode == $IPP_DIFF_MODE_WARP_STACK ) {
Index: branches/meh_branches/ppstack_test/pstamp/scripts/pstamp_cleanup.pl
===================================================================
--- branches/meh_branches/ppstack_test/pstamp/scripts/pstamp_cleanup.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/pstamp/scripts/pstamp_cleanup.pl	(revision 33415)
@@ -23,4 +23,5 @@
 
 use PS::IPP::Config qw( :standard );
+use PS::IPP::PStamp::Job qw( :standard );
 
 my $req_id;
@@ -95,4 +96,22 @@
 }
 
+{
+    my $command = "$pstamptool -listfile -req_id $req_id";
+    $command   .= " -dbname $dbname" if $dbname;
+    $command   .= " -dbserver $dbserver" if $dbserver;
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        die("Unable to perform $command error code: $error_code");
+    }
+    my $output = join "", @$stdout_buf;
+    if ($output) {
+        my $files = parse_md_fast($mdcParser, $output);
+        foreach my $file (@$files) {
+            $ipprc->file_delete($file->{path}); 
+        }
+    }
+}
+
 # now go find the workdir for this request 
 # XXX: we finally *have* to store this in the database
Index: branches/meh_branches/ppstack_test/pstamp/scripts/pstamp_get_image_job.pl
===================================================================
--- branches/meh_branches/ppstack_test/pstamp/scripts/pstamp_get_image_job.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/pstamp/scripts/pstamp_get_image_job.pl	(revision 33415)
@@ -25,7 +25,8 @@
 
 my $output_base;
+my $bundleroot;
 
 my $verbose;
-my $ipprc;
+my $imagedbname;
 my $dbname;
 my $dbserver;
@@ -41,4 +42,6 @@
         'rownum=s'        =>      \$rownum,
         'output_base=s'   =>      \$output_base,
+        'bundleroot=s'    =>      \$bundleroot,
+        'imagedbname=s'   =>      \$imagedbname,
         'dbname=s'        =>      \$dbname,
         'dbserver=s'      =>      \$dbserver,
@@ -52,4 +55,6 @@
 
 my_die( $err, $PS_EXIT_PROG_ERROR) if $err;
+
+my $ipprc = PS::IPP::Config->new();
 
 my $params_file = $output_base . ".mdc";
@@ -91,4 +96,5 @@
 my $missing_tools;
 my $dist_bundle   = can_run('dist_bundle.pl') or (warn "Can't find dist_bundle.pl" and $missing_tools = 1);
+my $pstamptool   = can_run('pstamptool') or (warn "Can't find pstamptool" and $missing_tools = 1);
 if ($missing_tools) {
     warn("Can't find required tools.");
@@ -96,8 +102,23 @@
 }
 
+$pstamptool .= " -dbname $dbname" if $dbname;
+$pstamptool .= " -dbserver $dbserver" if $dbserver;
+
 my $outdir = dirname($output_base);
 my $basename = basename($path_base);
-my $outroot = $output_base ."_" . $basename;
 my $results_file = $output_base . ".bundle_results";
+my $outroot;
+if ($bundleroot) {
+    my (undef, undef, undef, $mday, $month, $year) = gmtime(time());
+    $month += 1;
+    $year += 1900;
+
+    # This will generate an outroot like:
+    # neb://any/pstamp/bundles/2011/11/22/14026916_1_1_o5739g0358o.353654.wrp.215092.skycell.2083.025
+
+    $outroot = $bundleroot . sprintf("/%4d/%02d/%02d/${job_id}_", $year, $month, $mday) . basename($output_base) . "_". $basename;
+} else {
+    $outroot = $output_base ."_" . $basename;
+}
 
 {
@@ -105,8 +126,7 @@
     $command .= " --results_file $results_file";
     $command .= " --component $component --path_base $path_base --outroot $outroot";
-#    XXX: we need to do some work if we want to support muggle bundles
-#    $command .= " --no_magic if $no_magic";
-    $command .= " --magicked" if $magicked;
-    $command .= " --dbname $dbname" if $dbname;
+    # DING DONG ....
+    $command .= " --no_magic";
+    $command .= " --dbname $imagedbname" if $imagedbname;
     $command .= " --verbose" if $verbose;
     my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
@@ -147,4 +167,36 @@
 }
 
+if ($bundleroot) {
+    {
+        # delete any existing pstampFile in case this job has faulted and
+        # been reverted
+        my $command = "$pstamptool -deletefile -job_id $job_id";
+        my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+            run(command => $command, verbose => $verbose);
+        unless ($success) {
+            my_die("Unable to perform $command: $error_code", $error_code >> 8);
+        }
+    }
+    my $linkname = "$outdir/$file_name";
+    if (-l $linkname or -e $linkname) {
+        unlink $linkname or my_die("Failed to unlink existing symlink: $linkname", $PS_EXIT_UNKNOWN_ERROR);
+    }
+    my $bundle_name = dirname($outroot) . "/$file_name";
+    my $resolved = $ipprc->file_resolve($bundle_name);
+    if (!$resolved) {
+        my_die("failed to resolve $bundle_name", $PS_EXIT_UNKNOWN_ERROR);
+    }
+    symlink $resolved, $linkname or my_die("failed to create symlink to $resolved in $outdir", 
+        $PS_EXIT_UNKNOWN_ERROR);
+
+    my $command = "$pstamptool -addfile -job_id $job_id -path $bundle_name";
+    my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
+        run(command => $command, verbose => $verbose);
+    unless ($success) {
+        my_die("Unable to perform $command: $error_code", $error_code >> 8);
+    }
+}
+
+
 print REGLIST "$file_name|$bytes|$md5sum|tgz|\n";
 
Index: branches/meh_branches/ppstack_test/pstamp/scripts/pstamp_job_run.pl
===================================================================
--- branches/meh_branches/ppstack_test/pstamp/scripts/pstamp_job_run.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/pstamp/scripts/pstamp_job_run.pl	(revision 33415)
@@ -59,4 +59,6 @@
 $options = 1 if !$options;
 
+# We don't need to muggle anymore. Ignore those options
+$options &=  ~($PSTAMP_REQUEST_UNCENSORED | $PSTAMP_REQUIRE_UNCENSORED);
 
 my $ipprc = PS::IPP::Config->new(); # IPP Configuration
@@ -77,5 +79,5 @@
 my $ppstamp    = can_run('ppstamp') or (warn "Can't find ppstamp" and $missing_tools = 1);
 my $pstamp_get_image_job    = can_run('pstamp_get_image_job.pl') or (warn "Can't find pstamp_get_image_job.pl" and $missing_tools = 1);
-my $dqueryparse = can_run('dqueryparse.pl') or (warn "Can't find dqueryparse.pl" and $missing_tools = 1);
+my $dquery_job_run = can_run('dquery_job_run.pl') or (warn "Can't find dquery_job_run.pl" and $missing_tools = 1);
 my $streaksreplace = can_run('streaksreplace')  or (warn "Can't find streaksreplace"  and $missing_tools = 1);
 my $magicdstool = can_run('magicdstool')  or (warn "Can't find magicdstool"  and $missing_tools = 1);
@@ -87,4 +89,5 @@
 
 my $mdcParser = PS::IPP::Metadata::Config->new; # Parser for metadata config files
+
 
 my $jobStatus;
@@ -107,4 +110,8 @@
         $nan_masked = 0;
         $muggle = 1;
+    }
+
+    if ($stage eq "raw") {
+        $options &= ~($PSTAMP_SELECT_MASK | $PSTAMP_SELECT_VARIANCE); 
     }
 
@@ -214,5 +221,9 @@
     }
 
+    # MAGIC IS DEAD
+    $nan_masked = 0;
+
     my $command = "$ppstamp $outputBase $argString $fileArgs";
+    $command .= " -write_jpeg" if ($options & $PSTAMP_SELECT_JPEG);
     $command .= " -dbname $dbname" if $dbname;
     $command .= " -dbserver $dbserver" if $dbserver;
@@ -239,9 +250,10 @@
 
         # Note: we are assuming the contents of the PSTAMP filerules here.
-        my %extensions = ( $PSTAMP_SELECT_IMAGE  => "fits", 
-                           $PSTAMP_SELECT_MASK   => "mk.fits",
-                           $PSTAMP_SELECT_VARIANCE => "wt.fits");
-
-        my $output_mask = $options & ($PSTAMP_SELECT_IMAGE | $PSTAMP_SELECT_MASK | $PSTAMP_SELECT_VARIANCE);
+        my %extensions = ( $PSTAMP_SELECT_IMAGE    => "fits", 
+                           $PSTAMP_SELECT_MASK     => "mk.fits",
+                           $PSTAMP_SELECT_VARIANCE => "wt.fits",
+                           $PSTAMP_SELECT_JPEG     => "jpg");
+
+        my $output_mask = $options & ($PSTAMP_SELECT_IMAGE | $PSTAMP_SELECT_MASK | $PSTAMP_SELECT_VARIANCE | $PSTAMP_SELECT_JPEG);
 
         foreach my $key (keys (%extensions)) {
@@ -272,6 +284,12 @@
 } elsif ($jobType eq "get_image") {
 
-    my $uri = "";
+    my $pstamp_bundle_root = metadataLookupStr($ipprc->{_siteConfig}, "PSTAMP_BUNDLE_ROOT");
+
+    my $params = read_params_file($outputBase);
+    my $imagedb = $params->{imagedb};
+
     my $command = "$pstamp_get_image_job --job_id $job_id --output_base $outputBase --rownum $rownum";
+    $command .= " --bundleroot $pstamp_bundle_root" if $pstamp_bundle_root;
+    $command .= " --imagedb $imagedb" if $imagedb;
     $command .= " --dbname $dbname" if $dbname;
     $command .= " --dbserver $dbserver" if $dbserver;
@@ -287,20 +305,19 @@
     }
 } elsif ($jobType eq "detect_query") {
-    # detect_query jobs are basically holders to note that we need a file updated before we can continue.
-    # Load the argument list that dqueryparse should have created the first time it ran, so we know how to
-    # run it again the same way.
-    my $argslist = "$outputBase/parse.args";
-    open ARGSLIST, "<$argslist" or my_die("failed to open argslist file $argslist", $job_id, $PS_EXIT_UNKNOWN_ERROR);
-    my $argString = <ARGSLIST>;
-    close ARGSLIST;
-    chomp $argString;
+    # my $outdir = dirname($outputBase);
+    # my $argslist = "$outdir/parse.args";
+    # open ARGSLIST, "<$argslist" or my_die("failed to open argslist file $argslist", $job_id, $PS_EXIT_UNKNOWN_ERROR);
+    # my $argString = <ARGSLIST>;
+    # close ARGSLIST;
+    # chomp $argString;
 
     # XXX: should we do any other sanity checking?
-    my_die("arglist file $argslist is empty", $job_id, $PS_EXIT_DATA_ERROR) if !$argString;
-
-    my $command = "$dqueryparse --job_id $job_id $argString";
+    # my_die("arglist file $argslist is empty", $job_id, $PS_EXIT_DATA_ERROR) if !$argString;
+
+    my $command = "$dquery_job_run --job_id $job_id --output_base $outputBase";
     $command .= " --dbname $dbname" if $dbname;
     $command .= " --dbserver $dbserver" if $dbserver;
     $command .= " --verbose" if $verbose;
+    $command .= " --save-temps" if $save_temps;
     my ( $success, $error_code, $full_buf, $stdout_buf, $stderr_buf ) =
 	run(command => $command, verbose => $verbose);
@@ -311,6 +328,9 @@
     } else {
 	$jobStatus = $error_code >> 8;
-	my_die("dqueryparse.pl failed with error code: $jobStatus", $job_id, $jobStatus);
-    }
+	my_die("dquery_job_run.pl failed with error code: $jobStatus", $job_id, $jobStatus);
+    }
+} elsif ($jobType eq "child") {
+    # the only thing jobs of jobType child is to finish
+    $jobStatus = 0;
 } else {
     my_die("unknown jobType $jobType found", $job_id, $PS_EXIT_PROG_ERROR);
@@ -597,4 +617,8 @@
 
     $exit_code = $PS_EXIT_PROG_ERROR unless $exit_code;
+    if ($exit_code > 100) {
+        carp ("invalid exit code: $exit_code changing to $PS_EXIT_UNKNOWN_ERROR");
+        $exit_code = $PS_EXIT_UNKNOWN_ERROR;
+    }
     $job_state = 'run' unless $job_state;
 
Index: branches/meh_branches/ppstack_test/pstamp/scripts/pstamp_parser_run.pl
===================================================================
--- branches/meh_branches/ppstack_test/pstamp/scripts/pstamp_parser_run.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/pstamp/scripts/pstamp_parser_run.pl	(revision 33415)
@@ -55,4 +55,6 @@
 }
 
+$need_magic = 0;
+
 my $missing_tools;
 
@@ -89,9 +91,14 @@
     # outdir is where all of the files generated for this request are placed
     # NOTE: this location needs to be kept in sync with the web interface ( request.php )
-    my $datestr = strftime "%Y%m%d", gmtime;
+    my $datestr = strftime "%Y/%m/%d", gmtime;
     my $datedir = "$pstamp_workdir/$datestr";
     if (! -e $datedir ) {
-        mkdir $datedir or my_die( "failed to create working directory $datedir for request id $req_id", $req_id,
-            $PS_EXIT_CONFIG_ERROR);
+        my $rc = system "mkdir -p $datedir";
+        if ($rc) {
+            my $status = $rc >> 8;
+            $status = $PS_EXIT_CONFIG_ERROR if !$status;
+            my_die( "failed to create working directory $datedir for request id $req_id", $req_id,
+                $status);
+        }
     }
 
@@ -287,4 +294,8 @@
     carp($msg);
 
+    if (!$req_id) {
+        exit $PS_EXIT_CONFIG_ERROR;
+    }
+
     my $command = "$pstamptool -updatereq -req_id $req_id  -set_fault $fault";
     $command   .= " -dbname $dbname" if $dbname;
Index: branches/meh_branches/ppstack_test/pstamp/scripts/pstamp_server_status
===================================================================
--- branches/meh_branches/ppstack_test/pstamp/scripts/pstamp_server_status	(revision 32161)
+++ branches/meh_branches/ppstack_test/pstamp/scripts/pstamp_server_status	(revision 33415)
@@ -71,6 +71,6 @@
         $error_code = (($error_code >> 8) or 1);
         if ($error_code == 12) {
-            #print "Postage Stamp Server is not running\n";
-            print "Postage Stamp Server will be down for maintenance it will back shortly.\n";
+            print "Postage Stamp Server is not running\n";
+            #print "Postage Stamp Server will be down for maintenance it will back shortly.\n";
             exit 0;
         } else {
Index: branches/meh_branches/ppstack_test/pstamp/scripts/pstampparse.pl
===================================================================
--- branches/meh_branches/ppstack_test/pstamp/scripts/pstampparse.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/pstamp/scripts/pstampparse.pl	(revision 33415)
@@ -148,4 +148,7 @@
     } elsif ($lcname =~ /qub/) {
         $label = "QUB";
+        $label_changed = 1;
+    } elsif ($lcname =~ /pypstamp/) {
+        $label = "PY";
         $label_changed = 1;
     }
@@ -251,4 +254,6 @@
     # If we encounter an error for a particular row add a job with the proper fault code.
 
+    my $stage = $row->{IMG_TYPE};
+
     my $rownum   = $row->{ROWNUM};
     if (!validID($rownum)) {
@@ -272,6 +277,6 @@
     }
     if ($job_type eq 'get_image') {
-        unless ($req_type eq 'byid' or $req_type eq 'byexp') {
-            print STDERR "REQ_TYPE must be 'byid' or 'byexp' for JOB_TYPE 'get_image'\n";
+        unless ($req_type eq 'byid' or $req_type eq 'byexp' or ($req_type eq 'byskycell' and $stage eq 'stack')) {
+            print STDERR "REQ_TYPE must be 'byid' or 'byexp' or byskcyell for stacks for JOB_TYPE 'get_image'\n";
             insertFakeJobForRow($row, 1, $PSTAMP_INVALID_REQUEST);
             return 0;
@@ -334,5 +339,4 @@
     }
 
-    my $stage = $row->{IMG_TYPE};
     if (!check_image_type($stage)) {
         print STDERR "invalid IMG_TYPE for row $rownum\n";
@@ -460,4 +464,8 @@
     my $numRows = scalar @$rowList;
 
+    if (($req_type eq 'byskycell') or ($req_type eq 'bycoord')) {
+        # avoid error from print below if $id isn't needed
+        $id = "" if !$id;
+    }
     print "Collected $numRows rows beginning with row $rownum. $req_type $stage $id $tess_id $component\n";
     
Index: branches/meh_branches/ppstack_test/pstamp/src/ppstampArguments.c
===================================================================
--- branches/meh_branches/ppstack_test/pstamp/src/ppstampArguments.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/pstamp/src/ppstampArguments.c	(revision 33415)
@@ -21,9 +21,11 @@
     fprintf(stderr, "\n");
     fprintf(stderr, "Optional arguments:\n");
-    fprintf(stderr, "   [-class_id class_id]     selects class_id (only used with -pixcenter)\n");
-    fprintf(stderr, "   [-astrom astrom.cmp] :   provide an alternative astrometry calibration\n");
-    fprintf(stderr, "   [-mask   mk_image] :     mask image\n");
-    fprintf(stderr, "   [-variance var_image] :  variance image\n");
-    fprintf(stderr, "   [-no_censor_masked] : do not set masked pixels to NAN\n");
+    fprintf(stderr, "   [-class_id class_id]  : selects class_id (only used with -pixcenter)\n");
+    fprintf(stderr, "   [-astrom astrom.cmp]  : provide an alternative astrometry calibration\n");
+    fprintf(stderr, "   [-mask   mk_image]    : mask image\n");
+    fprintf(stderr, "   [-variance var_image] : variance image\n");
+    fprintf(stderr, "   [-stage stage]        : stage of input image (raw, chip, warp, stack, diff)\n");
+    fprintf(stderr, "   [-write_jpeg]         : write a JPEG  format of the image stamp\n");
+    // fprintf(stderr, "   [-no_censor_masked]   : do not set masked pixels to NAN\n");
     fprintf(stderr, "\n");
 
@@ -84,4 +86,10 @@
         psArgumentRemove(argnum, &argc, argv);
     }
+    if ((argnum = psArgumentGet(argc, argv, "-write_jpeg"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        options->writeJPEG = true;
+    } else {
+        options->writeJPEG = false;
+    }
 
     pmConfigFileSetsMD(config->arguments, &argc, argv, "ASTROM", "-astrom", "-astromlist");
Index: branches/meh_branches/ppstack_test/pstamp/src/ppstampOptions.c
===================================================================
--- branches/meh_branches/ppstack_test/pstamp/src/ppstampOptions.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/pstamp/src/ppstampOptions.c	(revision 33415)
@@ -28,4 +28,6 @@
     options->roip.dDEC = 0;
     options->chipName  = NULL;
+    options->cellName  = NULL;
+    options->stage  = NULL;
     options->censorMasked = true;
 
Index: branches/meh_branches/ppstack_test/pstamp/src/ppstampOptions.h
===================================================================
--- branches/meh_branches/ppstack_test/pstamp/src/ppstampOptions.h	(revision 32161)
+++ branches/meh_branches/ppstack_test/pstamp/src/ppstampOptions.h	(revision 33415)
@@ -16,5 +16,5 @@
     psRegion    roi;            // roi in chip coordinates
     bool        censorMasked;
-
+    bool        writeJPEG;
 } ppstampOptions;
 
Index: branches/meh_branches/ppstack_test/pstamp/src/ppstampParseCamera.c
===================================================================
--- branches/meh_branches/ppstack_test/pstamp/src/ppstampParseCamera.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/pstamp/src/ppstampParseCamera.c	(revision 33415)
@@ -8,5 +8,5 @@
 
 // Set up the ppstamp output Image file
-bool setupOutput(pmConfig *config, pmFPAfile *input, psString stage, bool doMask, bool doWeight)
+bool setupOutput(pmConfig *config, pmFPAfile *input, psString stage, bool doMask, bool doWeight, pmFPAfile **pOutput)
 {
     pmFPAfile *output;
@@ -22,4 +22,5 @@
         return false;
     }
+    *pOutput = output;
 
     output->save = true;
@@ -33,4 +34,5 @@
         outWeight->save = true;
     }
+
 
     return true;
@@ -76,5 +78,4 @@
         
 
-
     // XXX: create a filerule for PPSTAMP.ASTROM
     pmFPAfile *astrom = pmFPAfileDefineFromArgs(&status, config, "PSWARP.ASTROM", "ASTROM");
@@ -95,5 +96,6 @@
 
     // Set up the output target
-    if (!setupOutput(config, input, options->stage, doMask, doWeight)) {
+    pmFPAfile *output;
+    if (!setupOutput(config, input, options->stage, doMask, doWeight, &output)) {
         psError(PS_ERR_UNKNOWN, false, "Unable to setup output.");
         return false;
@@ -110,4 +112,26 @@
     }
 
+    if (options->writeJPEG) {
+        char *filerule;
+        // If stage is chip flip the X axis otherwise don't flip.
+        // XXX: is this the right thing to do?
+        // Shouldn't the JPEG match the stamp even for chip stage?
+        if (!options->stage || strcmp(options->stage, "chip")) {
+            filerule = "PPSTAMP.OUTPUT.JPEG";
+        } else {
+            filerule = "PPSTAMP.OUTPUT.JPEG.FLIP";
+        }
+        pmFPAfile *jpg = pmFPAfileDefineOutput(config, output->fpa, filerule);
+        if (!jpg) {
+            psError(PS_ERR_IO, false, "Unable to generate new file from %s", filerule);
+            return NULL;
+        }
+        if (jpg->type != PM_FPA_FILE_JPEG) {
+            psError(PS_ERR_IO, true, "%s is not of type JPEG", filerule);
+            return NULL;
+        }
+        jpg->save = true;
+    }
+
     // XXX: TODO: only do the workaround if the file level is chip, skycells should be fine
     if (!strcmp(config->cameraName, "MEGACAM")) {
Index: branches/meh_branches/ppstack_test/psvideophot/src/Makefile.am
===================================================================
--- branches/meh_branches/ppstack_test/psvideophot/src/Makefile.am	(revision 32161)
+++ branches/meh_branches/ppstack_test/psvideophot/src/Makefile.am	(revision 33415)
@@ -1,3 +1,4 @@
-bin_PROGRAMS = psvideophot
+bin_PROGRAMS =  psvideophot \
+		dumpvideo
 
 noinst_HEADERS = \
@@ -40,4 +41,9 @@
 	psvideophotVersion.c
 
+dumpvideo_CFLAGS = $(PSVIDEOPHOT_CFLAGS) $(PPSTATS_CFLAGS) $(PSASTRO_CFLAGS) $(PSPHOT_CFLAGS) $(PSMODULE_CFLAGS) $(PSLIB_CFLAGS)
+dumpvideo_LDFLAGS = $(PSVIDEOPHOT_LIBS) $(PSASTRO_LIBS) $(PPSTATS_LIBS) $(PSPHOT_LIBS) $(PSMODULE_LIBS) $(PSLIB_LIBS)
+dumpvideo_SOURCES = \
+	dumpvideo.c
+
 CLEANFILES = *~
 
Index: branches/meh_branches/ppstack_test/psvideophot/src/dumpvideo.c
===================================================================
--- branches/meh_branches/ppstack_test/psvideophot/src/dumpvideo.c	(revision 33415)
+++ branches/meh_branches/ppstack_test/psvideophot/src/dumpvideo.c	(revision 33415)
@@ -0,0 +1,75 @@
+#include <stdio.h>
+#include <assert.h>
+#include <pslib.h>
+#include <psmodules.h>
+
+int main (int argc, char **argv) {
+
+    if (argc < 4) {
+        fprintf (stderr, "USAGE: dumpvideo (input) (extname) (output) [-includetable]\n");
+	exit (2);
+    }
+
+    pmConfig *config = pmConfigRead(&argc, argv, NULL);
+    if (config == NULL) {
+        psErrorStackPrint(stderr, "Can't find site configuration!\n");
+        exit(EXIT_FAILURE);
+    }
+
+
+
+    char *input   = argv[1];      // Input file name
+    char *extname = argv[2];      // Extension of interest
+    char *output  = argv[3];      // Output file name
+
+    bool includetable = false;
+    if (argc > 4) {
+        includetable = true;
+    }
+
+    psString inputResolved = pmConfigConvertFilename(input, config, false, false);
+    psFits *infile = psFitsOpen (inputResolved, "r"); // Input file
+    if (!infile) {
+        fprintf (stderr, "Unable to open input file %s", input);
+        exit (PS_EXIT_DATA_ERROR);
+    }
+
+    psString outputResolved = pmConfigConvertFilename(output, config, true, true);
+    psFits *outfile = psFitsOpen (outputResolved, "w"); // Output file
+    if (!outfile) {
+        fprintf (stderr, "Unable to open output file %s", output);
+        exit(PS_EXIT_DATA_ERROR);
+    }
+
+    if (!psFitsMoveExtName (infile, extname)) {
+        fprintf (stderr, "Unable to find extname %s\n", extname);
+        exit(PS_EXIT_DATA_ERROR);
+    }
+
+    psMetadata *header = psFitsReadHeader (NULL, infile);
+
+    psRegion region = {0, 0, 0, 0};
+    psArray *cube = psFitsReadImageCube (infile, region);
+
+    psFitsWriteImageCube (outfile, header, cube, extname);
+
+    if (includetable) {
+        psString video_extname = NULL;
+        psStringAppend(&video_extname, "video_table_%s", extname);
+        if (!psFitsMoveExtName (infile, video_extname)) {
+            fprintf (stderr, "Unable to find extname %s\n", video_extname);
+            exit(PS_EXIT_DATA_ERROR);
+        }
+        psArray *table = psFitsReadTable(infile);
+        if (!psFitsWriteTable(outfile, NULL, table, video_extname)) {
+            fprintf (stderr, "Unable to write video table video_extname %s\n", video_extname);
+            exit(PS_EXIT_DATA_ERROR);
+        }
+    }
+
+    psFitsClose(infile);
+    psFitsClose(outfile);
+
+    return PS_EXIT_SUCCESS;
+}
+    
Index: branches/meh_branches/ppstack_test/psvideophot/src/psvideoLoop.c
===================================================================
--- branches/meh_branches/ppstack_test/psvideophot/src/psvideoLoop.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psvideophot/src/psvideoLoop.c	(revision 33415)
@@ -44,4 +44,7 @@
     }
 
+    psString class_id = psMetadataLookupStr(&status, config->arguments, "CLASS_ID");
+    psString cell_id = psMetadataLookupStr(&status, config->arguments, "CELL_ID");
+
 
     pmConfigCamerasCull(config, NULL);
@@ -56,13 +59,20 @@
     }
 
+    psArray *output = NULL;
+
     pmChip *chip;                       // Chip from FPA
     while ((chip = pmFPAviewNextChip(view, input->fpa, 1)) != NULL) {
         psLogMsg ("psvideophotLoop", 4, "Chip %d: %x %x\n", view->chip, chip->file_exists, chip->process);
+        psString chipName = psMetadataLookupStr(&status, chip->concepts, "CHIP.NAME");
+        if (strcmp(chipName, class_id)) {
+            continue;
+        }
         if (!chip->process || !chip->file_exists) {
-            continue;
+            ESCAPE("target chip has no data");
         }
         if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
             ESCAPE("load failure for Chip");
         }
+        (void) class_id;
 
         double threshold = THRESHOLD;
@@ -72,10 +82,16 @@
         while ((cell = pmFPAviewNextCell(view, input->fpa, 1)) != NULL) {
             psLogMsg ("psvideophotLoop", 5, "Cell %d: %x %x\n", view->cell, cell->file_exists, cell->process);
+            psString cellName = psMetadataLookupStr(&status, cell->concepts, "CELL.NAME");
+            if (strcmp(cellName, cell_id)) {
+                continue;
+            }
             if (!cell->process || !cell->file_exists) {
-                continue;
+                ESCAPE("target cell has no data");
             }
             if (!pmFPAfileIOChecks(config, view, PM_FPA_BEFORE)) {
                 ESCAPE("load failure for Cell");
             }
+
+	    psLogMsg("psvideophotLoop", 1, "Found target cell %s %d. %ld readouts\n", cellName, view->cell, cell->readouts->n);
 
             // Put version information into the header
@@ -87,9 +103,7 @@
 
             if (cell->readouts->n <2) {
-	      continue; 
+              psLogMsg("psvideophotLoop", 1, "cell %s has only %ld readouts\n", cellName, cell->readouts->n);
+              ESCAPE("invalid video Cell");
 	    }
-
-            psString cellName = psMetadataLookupStr(&status, cell->concepts, "CELL.NAME");
-	    psLogMsg("psvideophotLoop", 1, "Found video cell %s %d\n", cellName, view->cell);
 
             // get the position of this cell on the chip 
@@ -114,6 +128,8 @@
 
             // go find the video extension data
-            psString video_extension_name = NULL;
-            psStringAppend(&video_extension_name, "video_table_%s", cellName);
+            psString video_extension_name = psMetadataLookupStr(&status, config->arguments, "VIDEOEXTNAME");
+            if (!video_extension_name) {
+                psStringAppend(&video_extension_name, "video_table_%s", cellName);
+            }
             psArray *video_table = readVideoTable(input, video_extension_name);
             if (!video_table) {
@@ -121,5 +137,8 @@
                 ESCAPE("invalid image file");
             }
-            psMetadataAddArray(cell->analysis, PS_LIST_TAIL, "VIDEO_DATA", 0, "", video_table);
+            psFree(video_extension_name);
+            // psMetadataAddArray(cell->analysis, PS_LIST_TAIL, "VIDEO_DATA", 0, "", video_table);
+
+            output = (psArray *) psMemIncrRefCounter(video_table);
 
             if (cell->readouts->n != video_table->n) {
@@ -180,4 +199,5 @@
                 double cnpix2 = psMetadataLookupS32(&status, row, "cnpix2");
 
+                // avoid errors from unused variables
                 (void) frame_start; (void) frame_complete;
                 (void) rowpre; (void) cnpix1; (void) cnpix2; (void) fwhm_x; (void) fwhm_y; (void) fwhm;
@@ -191,8 +211,9 @@
                 double star_readout_y = (centroid_y - cnpix2) / yBin;
 
+#ifdef notdef
                 printf("%4d %9.2f %9.2f %9.2f %.1f %.1f %9.6f %6.3f %6.3f %6.3f\n",
                         frame, flux, snr, sky, star_readout_x, star_readout_y, frame_complete - frame_start,
                             fwhm, fwhm_x, fwhm_y);
-
+#endif
                 psStatsInit(stats);
 
@@ -206,19 +227,34 @@
                 double stddev = stats->sampleStdev;
                 double median = stats->sampleMedian;
+                double vpFlux = 0;
+                double vpSum = 0;
+                double xy[2] = {0, 0};
                 if (stddev > threshold) {
 
-                    double sum = 0;
-                    // XXX: use a point data type
-                    double xy[2];
-                    Centering(readout->image, EDGE, &sum, xy, median);
-                    double ourFlux;
-                    ourFlux = SubSum(readout->image, xy, median);
-
-                    printf("%4d %9.2f %9.2f %9.2f %.1f %.1f %9.2f\n",
-                             frame, ourFlux, ourFlux/stddev, median, xy[0], xy[1], stddev);
+                    Centering(readout->image, EDGE, &vpSum, xy, median);
+
+                    if (isfinite(xy[0]) && isfinite(xy[1])) {
+                        vpFlux = SubSum(readout->image, xy, median);
+                    }
+
+//                    printf("%4d %9.2f %9.2f %9.2f %.1f %.1f %9.2f\n",
+//                             frame, vpFlux, vpFlux/median, median, xy[0], xy[1], stddev);
                 } else {
-                    printf("%4d %9.2f %9.2f %8.2f\n",
-                            frame, 0.0,   stddev, median);
-                }
+//                    printf("%4d %9.2f %9.2f %8.2f\n",
+//                            frame, 0.0,   stddev, median);
+                }
+                if (frame % 60 == 0) {
+                    printf("frame  flux        sky       snr     X    Y     dt        fwhm   fwhm_x fwhm_y  vp_flux   vp_median vp_sum    vp_stddev vp_x vp_y\n");
+                }
+                printf("%4d   %9.2f %9.2f %9.2f %.1f %.1f %9.6f %6.3f %6.3f %6.3f   %9.2f %9.2f %9.2f %9.2f %.1f %.1f\n",
+                        frame, flux, sky, snr, star_readout_x, star_readout_y, frame_complete - frame_start,
+                            fwhm, fwhm_x, fwhm_y,
+                            vpFlux, median, vpSum, stddev, xy[0], xy[1]);
+                psMetadataAddF32(row, PS_LIST_TAIL, "vp_flux", 0, "vp flux", vpFlux);
+                psMetadataAddF32(row, PS_LIST_TAIL, "vp_median", 0, "vp meidan", median);
+                psMetadataAddF32(row, PS_LIST_TAIL, "vp_stddev", 0, "vp meidan", stddev);
+                psMetadataAddF32(row, PS_LIST_TAIL, "vp_sum", 0, "vp sum of source", vpSum);
+                psMetadataAddF32(row, PS_LIST_TAIL, "vp_x", 0, "centroid x from vp", xy[0]);
+                psMetadataAddF32(row, PS_LIST_TAIL, "vp_y", 0, "centroid y from vp", xy[1]);
             }
             psLogMsg ("psvideophotLoop", 5, "Done processing video cell");
@@ -247,11 +283,22 @@
     psFree(view);
 
-    // Dump configuration
-    psString dump_file = psMetadataLookupStr(&status, config->arguments, "DUMP_CONFIG");
-    if (dump_file) {
-        if (!pmConfigDump(config, dump_file)) {
-            ESCAPE("Unable to dump configuration.");
-        }
-    }
+    psString outputName = psMetadataLookupStr(&status, config->arguments, "OUTPUT");
+    psString realName = pmConfigConvertFilename(outputName, config, true, true);
+    if (!realName) {
+        psError(PS_ERR_UNKNOWN, false, "failed to convert filename for %s", outputName);
+        ESCAPE("cannot save output");
+    }
+    psFits *outFits = psFitsOpen(realName, "w");
+    if (!outFits) {
+        psError(PS_ERR_UNKNOWN, false, "failed to open output filename for %s", outputName);
+        ESCAPE(" ");
+    }
+
+    psMetadata *outputHeader = lastHDU ? lastHDU->header : NULL;
+    if (!psFitsWriteTable(outFits, outputHeader, output, "videophotemetry")) {
+        psError(PS_ERR_UNKNOWN, false, "failed to write videophotometry table");
+        ESCAPE("cannot write output");
+    }
+    psFitsClose(outFits);
 
     return true;
Index: branches/meh_branches/ppstack_test/psvideophot/src/psvideophotArguments.c
===================================================================
--- branches/meh_branches/ppstack_test/psvideophot/src/psvideophotArguments.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psvideophot/src/psvideophotArguments.c	(revision 33415)
@@ -6,24 +6,10 @@
 
 static void usage (void) {
-    fprintf(stderr, "USAGE: psvideophot [-file INPUT.fits] [-list INPUT.txt] OUTPUT\n\n");
+    fprintf(stderr, "USAGE: psvideophot [-file INPUT.fits] [-list INPUT.txt] OUTPUT -class_id CLASS_ID -cell_id CELL_ID\n\n");
     fprintf(stderr, "Optional arguments:\n");
     fprintf(stderr, "\t-stats STATS.mdc: Output statistics into STATS.mdc\n");
-    fprintf(stderr, "\t-isfringe: The input image contains fringe data.\n");
-    fprintf(stderr, "\t-isdark: The input image contains dark data.\n");
-    fprintf(stderr, "\t-usemask MASKVAL: Use this mask value (override recipe).\n");
-    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, "\t-video: process video cells\n");
+    fprintf(stderr, "\t-video_extname VIDEOEXTNAME: EXTNAME for video table\n");
     fprintf(stderr, "\n");
     fprintf(stderr, "Input options (single file / file list):\n");
-    fprintf(stderr, "\t-noisemap/-noisemaplist: Noise Map image.\n");
-    fprintf(stderr, "\t-bias/-biaslist: Bias image.\n");
-    fprintf(stderr, "\t-dark/-darklist: Dark image.\n");
-    fprintf(stderr, "\t-shutter/-shutterlist: Shutter image.\n");
-    fprintf(stderr, "\t-flat/-flatlist: Flat image.\n");
-    fprintf(stderr, "\t-mask/-masklist: Mask image.\n");
-    fprintf(stderr, "\t-fringe/-fringelist: Fringe image and data.\n");
-    fprintf(stderr, "\t-linearity/-linearlist: linearity correction file.\n");
     fprintf(stderr, "\n");
     exit (2);
@@ -63,4 +49,29 @@
     // psMetadata *options = psMetadataAlloc ();
     // psMetadataAddPtr (config->arguments, PS_LIST_TAIL, "PSVIDEOPHOT.OPTIONS",  PS_DATA_METADATA, "", options);
+
+    if ((argnum = psArgumentGet(argc, argv, "-class_id"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        psMetadataAddStr(config->arguments, PS_LIST_TAIL, "CLASS_ID", PS_META_REPLACE,
+                         "Chip to process", argv[argnum]);
+        psArgumentRemove(argnum, &argc, argv);
+    } else {
+        fprintf(stderr, "-class_id is required\n");
+        usage();
+    }
+    if ((argnum = psArgumentGet(argc, argv, "-cell_id"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        psMetadataAddStr(config->arguments, PS_LIST_TAIL, "CELL_ID", PS_META_REPLACE,
+                         "Cell to process", argv[argnum]);
+        psArgumentRemove(argnum, &argc, argv);
+    } else {
+        fprintf(stderr, "-cell_id is required\n");
+        usage();
+    }
+    if ((argnum = psArgumentGet(argc, argv, "-video_extname"))) {
+        psArgumentRemove(argnum, &argc, argv);
+        psMetadataAddStr(config->arguments, PS_LIST_TAIL, "VIDEOEXTNAME", PS_META_REPLACE,
+                         "EXTNAME of video table", argv[argnum]);
+        psArgumentRemove(argnum, &argc, argv);
+    }
 
     if ((argnum = psArgumentGet(argc, argv, "-stats"))) {
@@ -117,4 +128,5 @@
     // the input file is a required argument; if not found, we will exit
     pmConfigFileSetsMD (config->arguments, &argc, argv, "INPUT", "-file", "-list");
+    pmConfigFileSetsMD (config->arguments, &argc, argv, "ASTROM", "-astrom", "-astromlist");
 
     // if these command-line options are supplied, load the file name lists into config->arguments
@@ -168,5 +180,5 @@
 
     // Add the input and output images (which remain on the command-line) to the arguments list
-    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Name of the output image", argv[1]);
+    psMetadataAddStr(config->arguments, PS_LIST_TAIL, "OUTPUT", 0, "Name of the output table", argv[1]);
 
     return config;
Index: branches/meh_branches/ppstack_test/psvideophot/src/psvideophotParseCamera.c
===================================================================
--- branches/meh_branches/ppstack_test/psvideophot/src/psvideophotParseCamera.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/psvideophot/src/psvideophotParseCamera.c	(revision 33415)
@@ -7,6 +7,4 @@
 psvideophotOptions *psvideophotParseCamera(pmConfig *config)
 {
-    bool status = false;
-
     if (!psvideophotDefineFile(config, NULL, "PSVIDEOPHOT.INPUT", "INPUT", PM_FPA_FILE_IMAGE, PM_DETREND_TYPE_NONE)) {
         psError(PS_ERR_IO, false, "Can't find an input image source");
@@ -16,155 +14,13 @@
     psAssert(input, "We just put it there!");
 
+#ifdef notyet
     // add recipe options supplied on command line
+    bool status = false;
     psMetadata *recipe  = psMetadataLookupPtr(&status, config->recipes, RECIPE_NAME);
+#endif
 
     // parse the options from the metadata format to the psvideophotOptions structure
     psvideophotOptions *options = psvideophotOptionsParse(config);
 
-    // the following are defined from the argument list, if given,
-    // otherwise they revert to the config information
-    // not all input or output images are used in a given recipe
-    
-    int nThreads = psMetadataLookupS32(&status, config->arguments, "NTHREADS");
-    if (nThreads > 0) {
-        int nScanRows = psMetadataLookupS32(&status, recipe, "SCAN.ROWS");
-        pmDetrendSetThreadTasks(nScanRows);
-    }
-
-    // the following files are output targets
-    pmFPAfile *outImage = pmFPAfileDefineOutput(config, input->fpa, "PSVIDEOPHOT.OUTPUT");
-    if (!outImage) {
-        psError(PS_ERR_IO, false, _("Unable to generate output file from PSVIDEOPHOT.OUTPUT"));
-        psFree(options);
-        return NULL;
-    }
-    if (outImage->type != PM_FPA_FILE_OBJ) {
-        psError(PS_ERR_IO, true, "PSVIDEOPHOT.OUTPUT is not of type OBJ");
-        psFree(options);
-        return NULL;
-    }
-    
-    // XXX should these be bound explicitly to the outImage->fpa rather than the input->fpa?
-    pmFPAfile *chipImage = pmFPAfileDefineChipMosaic(config, input->fpa, "PSVIDEOPHOT.CHIP");
-    if (!chipImage) {
-        psError(PS_ERR_IO, false, _("Unable to generate new file from PSVIDEOPHOT.CHIP"));
-        psFree(options);
-        return NULL;
-    }
-    if (chipImage->type != PM_FPA_FILE_OBJ) {
-        psError(PS_ERR_IO, true, "PSVIDEOPHOT.CHIP is not of type OBJ");
-        psFree(options);
-        return NULL;
-    }
-    
-
-    // chipImage    -> psphotInput  (pmFPAfileDefineFromFile)       : fpa is constructed
-    // psphotInput  -> psphotOutput (pmFPAfileDefineOutputFromFile) : fpa is equated
-    // psphotOutput -> psastroInput (pmFPAfileDefineInput)          : fpa is ref-copied
-    // psastroInput -> psastroModel (pmFPAfileDefineFromArgs        : fpa is ref-copied
-    // psastroInput -> psastroModel (pmFPAfileDefineFromConf        : fpa is constructed
-    // psastroInput -> psastroModel (pmFPAfileDefineFromDetDB       : fpa is constructed (pmDetrendSelect uses input concepts )
-
-    // For photometry, we operate on the chip-mosaicked image
-    // we create a copy of the mosaicked image for psphot so we can write out a clean image
-    
-
-        // this file is just used as a carrier; output files (eg, PSPHOT.RESID) are defined by
-        // psphotDefineFiles
-        pmFPAfile *psphotInput = pmFPAfileDefineFromFile (config, chipImage, 1, 1, "PSPHOT.INPUT");
-        PS_ASSERT (psphotInput, false);
-
-        // specify the number of psphot input images
-        psMetadataAddS32 (config->arguments, PS_LIST_TAIL, "PSPHOT.INPUT.NUM", PS_META_REPLACE, "number of inputs", 1);
-
-        // define associated psphot input/output files
-        if (!psphotDefineFiles (config, psphotInput)) {
-            psError(PSPHOT_ERR_CONFIG, false,
-                    "Trouble defining the additional input/output files for psphot");
-            return false;
-        }
-    
-
-    // For photometry, we operate on the chip-mosaicked image
-    
-
-    // save any of these files?
-    outImage->save   = options->BaseFITS;
-    
-
-    chipImage->save  = options->ChipFITS;
-    
-
-    
-    // outImage is used as a carrier: input to chipImage -> require the data to remain at the CHIP level
-    outImage->freeLevel = PS_MIN(outImage->freeLevel, PM_FPA_LEVEL_CHIP);
-    outImage->dataLevel = outImage->freeLevel;
-    outImage->fileLevel = PS_MIN(outImage->fileLevel, outImage->dataLevel);
-
-    // outMask and outVariance must be freed at the same level as outImage (all freed by pmFPAFreeData)
-    
-
-    // the input data is the same as the outImage data : force the free levels to match
-    input->freeLevel = PS_MIN(outImage->freeLevel, input->freeLevel);
-
-    // define the binned target files (which may just be carriers for some camera configurations)
-    
-    // Chip selection: turn on only the chips specified (pass status to suppress missing-key log msg)
-    char *chipLine = psMetadataLookupStr(&status, config->arguments, "CHIP_SELECTIONS");
-    psArray *chips = psStringSplitArray(chipLine, ",", false);
-    if (chips->n > 0) {
-        pmFPASelectChip (input->fpa, -1, true); // deselect all chips
-        for (int i = 0; i < chips->n; i++) {
-            int chipNum = atoi(chips->data[i]);
-            if (! pmFPASelectChip(input->fpa, chipNum, false)) {
-                psError(PS_ERR_IO, false, "Chip number %d doesn't exist in camera.\n", chipNum);
-                psFree(options);
-                return false;
-            }
-        }
-    }
-    psFree (chips);
-
-    if (psMetadataLookupBool(NULL, config->arguments, "INPUT_IS_FRINGE")) {
-        // It's a fringe file, so change the file type
-        input->type = PM_FPA_FILE_FRINGE;
-        outImage->type = PM_FPA_FILE_FRINGE;
-    }
-    if (psMetadataLookupBool(NULL, config->arguments, "INPUT_IS_DARK")) {
-        // It's a dark file, so change the file type
-        input->type = PM_FPA_FILE_DARK;
-        outImage->type = PM_FPA_FILE_DARK;
-        // Turn off compression --- there are just too many nasties that can happen
-        psFree(outImage->compression);
-        outImage->compression = NULL;
-        psFree(outImage->options);
-        outImage->options = NULL;
-    }
-
-    // Turn off mask and variance output if we're not doing anything interesting
-    
-    if (psTraceGetLevel("psvideophot.config") > 0) {
-        // Get a look inside all the files.
-        psMetadataIterator *filesIter = psMetadataIteratorAlloc(config->files, PS_LIST_HEAD, NULL);
-        psMetadataItem *item;               // Item from iteration
-        fprintf(stderr, "Files:\n");
-        while ((item = psMetadataGetAndIncrement(filesIter))) {
-            pmFPAfile *file = item->data.V; // File of interest
-            fprintf(stderr, "%s: %p %p %p (%p) %p\n", file->name,
-                    file->src, file->fpa,
-                    file->camera, file->fpa->camera, file->format);
-        }
-        psFree(filesIter);
-    }
-
-    // Change the input dark type between the old (IMAGE) and new (multi-DARK).
-    // Hopefully this is a temporary change until we all move over to using the new dark types
-    bool mdok;                          // Status of MD lookup
-    if (options->doDark && psMetadataLookupBool(&mdok, recipe, "OLDDARK")) {
-        pmFPAfile *dark = psMetadataLookupPtr(NULL, config->files, "PSVIDEOPHOT.DARK");
-        assert(dark);
-        dark->type = PM_FPA_FILE_IMAGE;
-    }
-
     return (options);
 }
Index: branches/meh_branches/ppstack_test/pswarp/src/pswarpTransformReadout.c
===================================================================
--- branches/meh_branches/ppstack_test/pswarp/src/pswarpTransformReadout.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/pswarp/src/pswarpTransformReadout.c	(revision 33415)
@@ -143,5 +143,5 @@
     // wait for the threads to finish and manage results
     // wait here for the threaded jobs to finish
-    if (!psThreadPoolWait (false)) {
+    if (!psThreadPoolWait (false, true)) {
         psError(psErrorCodeLast(), false, "Unable to interpolate image.");
         return false;
Index: branches/meh_branches/ppstack_test/pswarp/src/pswarpTransformSources.c
===================================================================
--- branches/meh_branches/ppstack_test/pswarp/src/pswarpTransformSources.c	(revision 32161)
+++ branches/meh_branches/ppstack_test/pswarp/src/pswarpTransformSources.c	(revision 33415)
@@ -120,6 +120,6 @@
 #if 0
         // XXX Note that this will not set the correct axes
-        pmPSF_AxesToModel(new->modelPSF->params->data.F32,
-                          pmPSF_ModelToAxes(source->modelPSF->params->data.F32, 20.0));
+	psEllipseAxes axes = pmPSF_ModelToAxes(source->modelPSF->params->data.F32, 20.0, source->modelPSF->type);
+        pmPSF_AxesToModel(new->modelPSF->params->data.F32, axes, new->modelPSF->type);
 #endif
 
Index: branches/meh_branches/ppstack_test/tools/LAP_regiontool.pl
===================================================================
--- branches/meh_branches/ppstack_test/tools/LAP_regiontool.pl	(revision 33415)
+++ branches/meh_branches/ppstack_test/tools/LAP_regiontool.pl	(revision 33415)
@@ -0,0 +1,2754 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings FATAL => qw( all );
+use Getopt::Long qw( GetOptions :config auto_help auto_version );
+
+use Pod::Usage qw( pod2usage );
+
+
+my $seq_id = 8;
+my $label  = 'LAP.ThreePi.20110809';
+my $dist_group = 'LAP.ThreePi';
+my $tess_id = 'RINGS.V3';
+my $radius = 5;
+my $dbname = 'gpc1';
+my @filters = ('g.00000','r.00000','i.00000','z.00000','y.00000');
+my $degrees = 0;
+
+my ($ra_min,$ra_max,$dec_min,$dec_max);
+
+GetOptions(
+    'dbname=s'     => \$dbname,
+    'label|s=s'    => \$label,
+    'dist_group=s' => \$dist_group,
+    'tess_id=s'    => \$tess_id,
+    'radius=s'     => \$radius,
+    'seq_id=s'     => \$seq_id,
+    'ra_min|r=s'   => \$ra_min,
+    'ra_max|R=s'   => \$ra_max,
+    'dec_min|d=s'  => \$dec_min,
+    'dec_max|D=s'  => \$dec_max,
+    'degrees'      => \$degrees,
+    ) || pod2usage( 2 );
+
+die("Unknown option: @ARGV") if @ARGV;
+
+unless (defined($ra_min)&&
+	defined($ra_max)&&
+	defined($dec_min)&&
+	defined($dec_max)) {
+    die("Region not defined");
+}
+
+# Convert to degrees and add overlap boundaries.
+
+if ($degrees) {
+    $ra_min = ($ra_min) - 1;
+    $ra_max = ($ra_max) + 1;
+}
+else {
+    $ra_min = (15 * $ra_min) - 1;
+    $ra_max = (15 * $ra_max) + 1;
+}
+$dec_min = $dec_min - 1;
+$dec_max = $dec_max + 1;
+
+
+
+# Create a list of projection cells to be included in this region
+my @included = ();
+my %iRA = ();
+my %iDEC = ();
+while(<DATA>) {
+    chomp;
+    my ($ra,$dec,$proj) = split /\s+/;
+    
+    if (($ra > $ra_min)&&($ra < $ra_max)&&
+	($dec > $dec_min)&&($dec < $dec_max)) {
+	push @included, $proj;
+	$iRA{$proj} = $ra;
+	$iDEC{$proj} = $dec;
+    }
+}
+
+
+my $command_base = "laptool -definerun -dbname $dbname -seq_id $seq_id -label $label -dist_group $dist_group -tess_id $tess_id -radius $radius";
+
+
+while ($#included > -1) {
+    my @blocks = ();
+    my %Ndec = ();
+    my $proj;
+    
+    while (scalar(keys %Ndec) < 4) {
+	$proj = shift(@included);
+	$Ndec{$iDEC{$proj}} = 1;
+	push @blocks, $proj;
+	if ($#included == -1) {
+	    last;
+	}
+    } 
+	
+    if ($#included > -1) {
+	unshift @included, $proj;
+	pop @blocks;
+    }
+
+    foreach my $filter (@filters) {
+	foreach my $proj (@blocks) {
+	    print "$command_base -filter $filter -projection_cell $proj -ra $iRA{$proj} -decl $iDEC{$proj}\n";
+	}
+    }
+    my $last_filter = pop(@filters);
+    unshift(@filters,$last_filter);
+#    print @blocks ;
+}
+
+
+
+__DATA__
+0 -90 skycell.0000
+0 -86 skycell.0001
+40 -86 skycell.0002
+80 -86 skycell.0003
+120 -86 skycell.0004
+160 -86 skycell.0005
+200 -86 skycell.0006
+240 -86 skycell.0007
+280 -86 skycell.0008
+320 -86 skycell.0009
+0 -82 skycell.0010
+24 -82 skycell.0011
+48 -82 skycell.0012
+72 -82 skycell.0013
+96 -82 skycell.0014
+120 -82 skycell.0015
+144 -82 skycell.0016
+168 -82 skycell.0017
+192 -82 skycell.0018
+216 -82 skycell.0019
+240 -82 skycell.0020
+264 -82 skycell.0021
+288 -82 skycell.0022
+312 -82 skycell.0023
+336 -82 skycell.0024
+0 -78 skycell.0025
+17.1429 -78 skycell.0026
+34.2857 -78 skycell.0027
+51.4286 -78 skycell.0028
+68.5714 -78 skycell.0029
+85.7143 -78 skycell.0030
+102.857 -78 skycell.0031
+120 -78 skycell.0032
+137.143 -78 skycell.0033
+154.286 -78 skycell.0034
+171.429 -78 skycell.0035
+188.571 -78 skycell.0036
+205.714 -78 skycell.0037
+222.857 -78 skycell.0038
+240 -78 skycell.0039
+257.143 -78 skycell.0040
+274.286 -78 skycell.0041
+291.429 -78 skycell.0042
+308.571 -78 skycell.0043
+325.714 -78 skycell.0044
+342.857 -78 skycell.0045
+0 -74 skycell.0046
+13.3333 -74 skycell.0047
+26.6667 -74 skycell.0048
+40 -74 skycell.0049
+53.3333 -74 skycell.0050
+66.6667 -74 skycell.0051
+80 -74 skycell.0052
+93.3333 -74 skycell.0053
+106.667 -74 skycell.0054
+120 -74 skycell.0055
+133.333 -74 skycell.0056
+146.667 -74 skycell.0057
+160 -74 skycell.0058
+173.333 -74 skycell.0059
+186.667 -74 skycell.0060
+200 -74 skycell.0061
+213.333 -74 skycell.0062
+226.667 -74 skycell.0063
+240 -74 skycell.0064
+253.333 -74 skycell.0065
+266.667 -74 skycell.0066
+280 -74 skycell.0067
+293.333 -74 skycell.0068
+306.667 -74 skycell.0069
+320 -74 skycell.0070
+333.333 -74 skycell.0071
+346.667 -74 skycell.0072
+0 -70 skycell.0073
+10.9091 -70 skycell.0074
+21.8182 -70 skycell.0075
+32.7273 -70 skycell.0076
+43.6364 -70 skycell.0077
+54.5455 -70 skycell.0078
+65.4545 -70 skycell.0079
+76.3636 -70 skycell.0080
+87.2727 -70 skycell.0081
+98.1818 -70 skycell.0082
+109.091 -70 skycell.0083
+120 -70 skycell.0084
+130.909 -70 skycell.0085
+141.818 -70 skycell.0086
+152.727 -70 skycell.0087
+163.636 -70 skycell.0088
+174.545 -70 skycell.0089
+185.455 -70 skycell.0090
+196.364 -70 skycell.0091
+207.273 -70 skycell.0092
+218.182 -70 skycell.0093
+229.091 -70 skycell.0094
+240 -70 skycell.0095
+250.909 -70 skycell.0096
+261.818 -70 skycell.0097
+272.727 -70 skycell.0098
+283.636 -70 skycell.0099
+294.545 -70 skycell.0100
+305.455 -70 skycell.0101
+316.364 -70 skycell.0102
+327.273 -70 skycell.0103
+338.182 -70 skycell.0104
+349.091 -70 skycell.0105
+0 -66 skycell.0106
+9.23077 -66 skycell.0107
+18.4615 -66 skycell.0108
+27.6923 -66 skycell.0109
+36.9231 -66 skycell.0110
+46.1538 -66 skycell.0111
+55.3846 -66 skycell.0112
+64.6154 -66 skycell.0113
+73.8462 -66 skycell.0114
+83.0769 -66 skycell.0115
+92.3077 -66 skycell.0116
+101.538 -66 skycell.0117
+110.769 -66 skycell.0118
+120 -66 skycell.0119
+129.231 -66 skycell.0120
+138.462 -66 skycell.0121
+147.692 -66 skycell.0122
+156.923 -66 skycell.0123
+166.154 -66 skycell.0124
+175.385 -66 skycell.0125
+184.615 -66 skycell.0126
+193.846 -66 skycell.0127
+203.077 -66 skycell.0128
+212.308 -66 skycell.0129
+221.538 -66 skycell.0130
+230.769 -66 skycell.0131
+240 -66 skycell.0132
+249.231 -66 skycell.0133
+258.462 -66 skycell.0134
+267.692 -66 skycell.0135
+276.923 -66 skycell.0136
+286.154 -66 skycell.0137
+295.385 -66 skycell.0138
+304.615 -66 skycell.0139
+313.846 -66 skycell.0140
+323.077 -66 skycell.0141
+332.308 -66 skycell.0142
+341.538 -66 skycell.0143
+350.769 -66 skycell.0144
+0 -62 skycell.0145
+8 -62 skycell.0146
+16 -62 skycell.0147
+24 -62 skycell.0148
+32 -62 skycell.0149
+40 -62 skycell.0150
+48 -62 skycell.0151
+56 -62 skycell.0152
+64 -62 skycell.0153
+72 -62 skycell.0154
+80 -62 skycell.0155
+88 -62 skycell.0156
+96 -62 skycell.0157
+104 -62 skycell.0158
+112 -62 skycell.0159
+120 -62 skycell.0160
+128 -62 skycell.0161
+136 -62 skycell.0162
+144 -62 skycell.0163
+152 -62 skycell.0164
+160 -62 skycell.0165
+168 -62 skycell.0166
+176 -62 skycell.0167
+184 -62 skycell.0168
+192 -62 skycell.0169
+200 -62 skycell.0170
+208 -62 skycell.0171
+216 -62 skycell.0172
+224 -62 skycell.0173
+232 -62 skycell.0174
+240 -62 skycell.0175
+248 -62 skycell.0176
+256 -62 skycell.0177
+264 -62 skycell.0178
+272 -62 skycell.0179
+280 -62 skycell.0180
+288 -62 skycell.0181
+296 -62 skycell.0182
+304 -62 skycell.0183
+312 -62 skycell.0184
+320 -62 skycell.0185
+328 -62 skycell.0186
+336 -62 skycell.0187
+344 -62 skycell.0188
+352 -62 skycell.0189
+0 -58 skycell.0190
+7.2 -58 skycell.0191
+14.4 -58 skycell.0192
+21.6 -58 skycell.0193
+28.8 -58 skycell.0194
+36 -58 skycell.0195
+43.2 -58 skycell.0196
+50.4 -58 skycell.0197
+57.6 -58 skycell.0198
+64.8 -58 skycell.0199
+72 -58 skycell.0200
+79.2 -58 skycell.0201
+86.4 -58 skycell.0202
+93.6 -58 skycell.0203
+100.8 -58 skycell.0204
+108 -58 skycell.0205
+115.2 -58 skycell.0206
+122.4 -58 skycell.0207
+129.6 -58 skycell.0208
+136.8 -58 skycell.0209
+144 -58 skycell.0210
+151.2 -58 skycell.0211
+158.4 -58 skycell.0212
+165.6 -58 skycell.0213
+172.8 -58 skycell.0214
+180 -58 skycell.0215
+187.2 -58 skycell.0216
+194.4 -58 skycell.0217
+201.6 -58 skycell.0218
+208.8 -58 skycell.0219
+216 -58 skycell.0220
+223.2 -58 skycell.0221
+230.4 -58 skycell.0222
+237.6 -58 skycell.0223
+244.8 -58 skycell.0224
+252 -58 skycell.0225
+259.2 -58 skycell.0226
+266.4 -58 skycell.0227
+273.6 -58 skycell.0228
+280.8 -58 skycell.0229
+288 -58 skycell.0230
+295.2 -58 skycell.0231
+302.4 -58 skycell.0232
+309.6 -58 skycell.0233
+316.8 -58 skycell.0234
+324 -58 skycell.0235
+331.2 -58 skycell.0236
+338.4 -58 skycell.0237
+345.6 -58 skycell.0238
+352.8 -58 skycell.0239
+0 -54 skycell.0240
+6.54545 -54 skycell.0241
+13.0909 -54 skycell.0242
+19.6364 -54 skycell.0243
+26.1818 -54 skycell.0244
+32.7273 -54 skycell.0245
+39.2727 -54 skycell.0246
+45.8182 -54 skycell.0247
+52.3636 -54 skycell.0248
+58.9091 -54 skycell.0249
+65.4545 -54 skycell.0250
+72 -54 skycell.0251
+78.5455 -54 skycell.0252
+85.0909 -54 skycell.0253
+91.6364 -54 skycell.0254
+98.1818 -54 skycell.0255
+104.727 -54 skycell.0256
+111.273 -54 skycell.0257
+117.818 -54 skycell.0258
+124.364 -54 skycell.0259
+130.909 -54 skycell.0260
+137.455 -54 skycell.0261
+144 -54 skycell.0262
+150.545 -54 skycell.0263
+157.091 -54 skycell.0264
+163.636 -54 skycell.0265
+170.182 -54 skycell.0266
+176.727 -54 skycell.0267
+183.273 -54 skycell.0268
+189.818 -54 skycell.0269
+196.364 -54 skycell.0270
+202.909 -54 skycell.0271
+209.455 -54 skycell.0272
+216 -54 skycell.0273
+222.545 -54 skycell.0274
+229.091 -54 skycell.0275
+235.636 -54 skycell.0276
+242.182 -54 skycell.0277
+248.727 -54 skycell.0278
+255.273 -54 skycell.0279
+261.818 -54 skycell.0280
+268.364 -54 skycell.0281
+274.909 -54 skycell.0282
+281.455 -54 skycell.0283
+288 -54 skycell.0284
+294.545 -54 skycell.0285
+301.091 -54 skycell.0286
+307.636 -54 skycell.0287
+314.182 -54 skycell.0288
+320.727 -54 skycell.0289
+327.273 -54 skycell.0290
+333.818 -54 skycell.0291
+340.364 -54 skycell.0292
+346.909 -54 skycell.0293
+353.455 -54 skycell.0294
+0 -50 skycell.0295
+6 -50 skycell.0296
+12 -50 skycell.0297
+18 -50 skycell.0298
+24 -50 skycell.0299
+30 -50 skycell.0300
+36 -50 skycell.0301
+42 -50 skycell.0302
+48 -50 skycell.0303
+54 -50 skycell.0304
+60 -50 skycell.0305
+66 -50 skycell.0306
+72 -50 skycell.0307
+78 -50 skycell.0308
+84 -50 skycell.0309
+90 -50 skycell.0310
+96 -50 skycell.0311
+102 -50 skycell.0312
+108 -50 skycell.0313
+114 -50 skycell.0314
+120 -50 skycell.0315
+126 -50 skycell.0316
+132 -50 skycell.0317
+138 -50 skycell.0318
+144 -50 skycell.0319
+150 -50 skycell.0320
+156 -50 skycell.0321
+162 -50 skycell.0322
+168 -50 skycell.0323
+174 -50 skycell.0324
+180 -50 skycell.0325
+186 -50 skycell.0326
+192 -50 skycell.0327
+198 -50 skycell.0328
+204 -50 skycell.0329
+210 -50 skycell.0330
+216 -50 skycell.0331
+222 -50 skycell.0332
+228 -50 skycell.0333
+234 -50 skycell.0334
+240 -50 skycell.0335
+246 -50 skycell.0336
+252 -50 skycell.0337
+258 -50 skycell.0338
+264 -50 skycell.0339
+270 -50 skycell.0340
+276 -50 skycell.0341
+282 -50 skycell.0342
+288 -50 skycell.0343
+294 -50 skycell.0344
+300 -50 skycell.0345
+306 -50 skycell.0346
+312 -50 skycell.0347
+318 -50 skycell.0348
+324 -50 skycell.0349
+330 -50 skycell.0350
+336 -50 skycell.0351
+342 -50 skycell.0352
+348 -50 skycell.0353
+354 -50 skycell.0354
+0 -46 skycell.0355
+5.625 -46 skycell.0356
+11.25 -46 skycell.0357
+16.875 -46 skycell.0358
+22.5 -46 skycell.0359
+28.125 -46 skycell.0360
+33.75 -46 skycell.0361
+39.375 -46 skycell.0362
+45 -46 skycell.0363
+50.625 -46 skycell.0364
+56.25 -46 skycell.0365
+61.875 -46 skycell.0366
+67.5 -46 skycell.0367
+73.125 -46 skycell.0368
+78.75 -46 skycell.0369
+84.375 -46 skycell.0370
+90 -46 skycell.0371
+95.625 -46 skycell.0372
+101.25 -46 skycell.0373
+106.875 -46 skycell.0374
+112.5 -46 skycell.0375
+118.125 -46 skycell.0376
+123.75 -46 skycell.0377
+129.375 -46 skycell.0378
+135 -46 skycell.0379
+140.625 -46 skycell.0380
+146.25 -46 skycell.0381
+151.875 -46 skycell.0382
+157.5 -46 skycell.0383
+163.125 -46 skycell.0384
+168.75 -46 skycell.0385
+174.375 -46 skycell.0386
+180 -46 skycell.0387
+185.625 -46 skycell.0388
+191.25 -46 skycell.0389
+196.875 -46 skycell.0390
+202.5 -46 skycell.0391
+208.125 -46 skycell.0392
+213.75 -46 skycell.0393
+219.375 -46 skycell.0394
+225 -46 skycell.0395
+230.625 -46 skycell.0396
+236.25 -46 skycell.0397
+241.875 -46 skycell.0398
+247.5 -46 skycell.0399
+253.125 -46 skycell.0400
+258.75 -46 skycell.0401
+264.375 -46 skycell.0402
+270 -46 skycell.0403
+275.625 -46 skycell.0404
+281.25 -46 skycell.0405
+286.875 -46 skycell.0406
+292.5 -46 skycell.0407
+298.125 -46 skycell.0408
+303.75 -46 skycell.0409
+309.375 -46 skycell.0410
+315 -46 skycell.0411
+320.625 -46 skycell.0412
+326.25 -46 skycell.0413
+331.875 -46 skycell.0414
+337.5 -46 skycell.0415
+343.125 -46 skycell.0416
+348.75 -46 skycell.0417
+354.375 -46 skycell.0418
+0 -42 skycell.0419
+5.29412 -42 skycell.0420
+10.5882 -42 skycell.0421
+15.8824 -42 skycell.0422
+21.1765 -42 skycell.0423
+26.4706 -42 skycell.0424
+31.7647 -42 skycell.0425
+37.0588 -42 skycell.0426
+42.3529 -42 skycell.0427
+47.6471 -42 skycell.0428
+52.9412 -42 skycell.0429
+58.2353 -42 skycell.0430
+63.5294 -42 skycell.0431
+68.8235 -42 skycell.0432
+74.1176 -42 skycell.0433
+79.4118 -42 skycell.0434
+84.7059 -42 skycell.0435
+90 -42 skycell.0436
+95.2941 -42 skycell.0437
+100.588 -42 skycell.0438
+105.882 -42 skycell.0439
+111.176 -42 skycell.0440
+116.471 -42 skycell.0441
+121.765 -42 skycell.0442
+127.059 -42 skycell.0443
+132.353 -42 skycell.0444
+137.647 -42 skycell.0445
+142.941 -42 skycell.0446
+148.235 -42 skycell.0447
+153.529 -42 skycell.0448
+158.824 -42 skycell.0449
+164.118 -42 skycell.0450
+169.412 -42 skycell.0451
+174.706 -42 skycell.0452
+180 -42 skycell.0453
+185.294 -42 skycell.0454
+190.588 -42 skycell.0455
+195.882 -42 skycell.0456
+201.176 -42 skycell.0457
+206.471 -42 skycell.0458
+211.765 -42 skycell.0459
+217.059 -42 skycell.0460
+222.353 -42 skycell.0461
+227.647 -42 skycell.0462
+232.941 -42 skycell.0463
+238.235 -42 skycell.0464
+243.529 -42 skycell.0465
+248.824 -42 skycell.0466
+254.118 -42 skycell.0467
+259.412 -42 skycell.0468
+264.706 -42 skycell.0469
+270 -42 skycell.0470
+275.294 -42 skycell.0471
+280.588 -42 skycell.0472
+285.882 -42 skycell.0473
+291.176 -42 skycell.0474
+296.471 -42 skycell.0475
+301.765 -42 skycell.0476
+307.059 -42 skycell.0477
+312.353 -42 skycell.0478
+317.647 -42 skycell.0479
+322.941 -42 skycell.0480
+328.235 -42 skycell.0481
+333.529 -42 skycell.0482
+338.824 -42 skycell.0483
+344.118 -42 skycell.0484
+349.412 -42 skycell.0485
+354.706 -42 skycell.0486
+0 -38 skycell.0487
+5 -38 skycell.0488
+10 -38 skycell.0489
+15 -38 skycell.0490
+20 -38 skycell.0491
+25 -38 skycell.0492
+30 -38 skycell.0493
+35 -38 skycell.0494
+40 -38 skycell.0495
+45 -38 skycell.0496
+50 -38 skycell.0497
+55 -38 skycell.0498
+60 -38 skycell.0499
+65 -38 skycell.0500
+70 -38 skycell.0501
+75 -38 skycell.0502
+80 -38 skycell.0503
+85 -38 skycell.0504
+90 -38 skycell.0505
+95 -38 skycell.0506
+100 -38 skycell.0507
+105 -38 skycell.0508
+110 -38 skycell.0509
+115 -38 skycell.0510
+120 -38 skycell.0511
+125 -38 skycell.0512
+130 -38 skycell.0513
+135 -38 skycell.0514
+140 -38 skycell.0515
+145 -38 skycell.0516
+150 -38 skycell.0517
+155 -38 skycell.0518
+160 -38 skycell.0519
+165 -38 skycell.0520
+170 -38 skycell.0521
+175 -38 skycell.0522
+180 -38 skycell.0523
+185 -38 skycell.0524
+190 -38 skycell.0525
+195 -38 skycell.0526
+200 -38 skycell.0527
+205 -38 skycell.0528
+210 -38 skycell.0529
+215 -38 skycell.0530
+220 -38 skycell.0531
+225 -38 skycell.0532
+230 -38 skycell.0533
+235 -38 skycell.0534
+240 -38 skycell.0535
+245 -38 skycell.0536
+250 -38 skycell.0537
+255 -38 skycell.0538
+260 -38 skycell.0539
+265 -38 skycell.0540
+270 -38 skycell.0541
+275 -38 skycell.0542
+280 -38 skycell.0543
+285 -38 skycell.0544
+290 -38 skycell.0545
+295 -38 skycell.0546
+300 -38 skycell.0547
+305 -38 skycell.0548
+310 -38 skycell.0549
+315 -38 skycell.0550
+320 -38 skycell.0551
+325 -38 skycell.0552
+330 -38 skycell.0553
+335 -38 skycell.0554
+340 -38 skycell.0555
+345 -38 skycell.0556
+350 -38 skycell.0557
+355 -38 skycell.0558
+0 -34 skycell.0559
+4.73684 -34 skycell.0560
+9.47368 -34 skycell.0561
+14.2105 -34 skycell.0562
+18.9474 -34 skycell.0563
+23.6842 -34 skycell.0564
+28.4211 -34 skycell.0565
+33.1579 -34 skycell.0566
+37.8947 -34 skycell.0567
+42.6316 -34 skycell.0568
+47.3684 -34 skycell.0569
+52.1053 -34 skycell.0570
+56.8421 -34 skycell.0571
+61.5789 -34 skycell.0572
+66.3158 -34 skycell.0573
+71.0526 -34 skycell.0574
+75.7895 -34 skycell.0575
+80.5263 -34 skycell.0576
+85.2632 -34 skycell.0577
+90 -34 skycell.0578
+94.7368 -34 skycell.0579
+99.4737 -34 skycell.0580
+104.211 -34 skycell.0581
+108.947 -34 skycell.0582
+113.684 -34 skycell.0583
+118.421 -34 skycell.0584
+123.158 -34 skycell.0585
+127.895 -34 skycell.0586
+132.632 -34 skycell.0587
+137.368 -34 skycell.0588
+142.105 -34 skycell.0589
+146.842 -34 skycell.0590
+151.579 -34 skycell.0591
+156.316 -34 skycell.0592
+161.053 -34 skycell.0593
+165.789 -34 skycell.0594
+170.526 -34 skycell.0595
+175.263 -34 skycell.0596
+180 -34 skycell.0597
+184.737 -34 skycell.0598
+189.474 -34 skycell.0599
+194.211 -34 skycell.0600
+198.947 -34 skycell.0601
+203.684 -34 skycell.0602
+208.421 -34 skycell.0603
+213.158 -34 skycell.0604
+217.895 -34 skycell.0605
+222.632 -34 skycell.0606
+227.368 -34 skycell.0607
+232.105 -34 skycell.0608
+236.842 -34 skycell.0609
+241.579 -34 skycell.0610
+246.316 -34 skycell.0611
+251.053 -34 skycell.0612
+255.789 -34 skycell.0613
+260.526 -34 skycell.0614
+265.263 -34 skycell.0615
+270 -34 skycell.0616
+274.737 -34 skycell.0617
+279.474 -34 skycell.0618
+284.211 -34 skycell.0619
+288.947 -34 skycell.0620
+293.684 -34 skycell.0621
+298.421 -34 skycell.0622
+303.158 -34 skycell.0623
+307.895 -34 skycell.0624
+312.632 -34 skycell.0625
+317.368 -34 skycell.0626
+322.105 -34 skycell.0627
+326.842 -34 skycell.0628
+331.579 -34 skycell.0629
+336.316 -34 skycell.0630
+341.053 -34 skycell.0631
+345.789 -34 skycell.0632
+350.526 -34 skycell.0633
+355.263 -34 skycell.0634
+0 -30 skycell.0635
+4.55696 -30 skycell.0636
+9.11392 -30 skycell.0637
+13.6709 -30 skycell.0638
+18.2278 -30 skycell.0639
+22.7848 -30 skycell.0640
+27.3418 -30 skycell.0641
+31.8987 -30 skycell.0642
+36.4557 -30 skycell.0643
+41.0127 -30 skycell.0644
+45.5696 -30 skycell.0645
+50.1266 -30 skycell.0646
+54.6835 -30 skycell.0647
+59.2405 -30 skycell.0648
+63.7975 -30 skycell.0649
+68.3544 -30 skycell.0650
+72.9114 -30 skycell.0651
+77.4684 -30 skycell.0652
+82.0253 -30 skycell.0653
+86.5823 -30 skycell.0654
+91.1392 -30 skycell.0655
+95.6962 -30 skycell.0656
+100.253 -30 skycell.0657
+104.81 -30 skycell.0658
+109.367 -30 skycell.0659
+113.924 -30 skycell.0660
+118.481 -30 skycell.0661
+123.038 -30 skycell.0662
+127.595 -30 skycell.0663
+132.152 -30 skycell.0664
+136.709 -30 skycell.0665
+141.266 -30 skycell.0666
+145.823 -30 skycell.0667
+150.38 -30 skycell.0668
+154.937 -30 skycell.0669
+159.494 -30 skycell.0670
+164.051 -30 skycell.0671
+168.608 -30 skycell.0672
+173.165 -30 skycell.0673
+177.722 -30 skycell.0674
+182.278 -30 skycell.0675
+186.835 -30 skycell.0676
+191.392 -30 skycell.0677
+195.949 -30 skycell.0678
+200.506 -30 skycell.0679
+205.063 -30 skycell.0680
+209.62 -30 skycell.0681
+214.177 -30 skycell.0682
+218.734 -30 skycell.0683
+223.291 -30 skycell.0684
+227.848 -30 skycell.0685
+232.405 -30 skycell.0686
+236.962 -30 skycell.0687
+241.519 -30 skycell.0688
+246.076 -30 skycell.0689
+250.633 -30 skycell.0690
+255.19 -30 skycell.0691
+259.747 -30 skycell.0692
+264.304 -30 skycell.0693
+268.861 -30 skycell.0694
+273.418 -30 skycell.0695
+277.975 -30 skycell.0696
+282.532 -30 skycell.0697
+287.089 -30 skycell.0698
+291.646 -30 skycell.0699
+296.203 -30 skycell.0700
+300.759 -30 skycell.0701
+305.316 -30 skycell.0702
+309.873 -30 skycell.0703
+314.43 -30 skycell.0704
+318.987 -30 skycell.0705
+323.544 -30 skycell.0706
+328.101 -30 skycell.0707
+332.658 -30 skycell.0708
+337.215 -30 skycell.0709
+341.772 -30 skycell.0710
+346.329 -30 skycell.0711
+350.886 -30 skycell.0712
+355.443 -30 skycell.0713
+0 -26 skycell.0714
+4.39024 -26 skycell.0715
+8.78049 -26 skycell.0716
+13.1707 -26 skycell.0717
+17.561 -26 skycell.0718
+21.9512 -26 skycell.0719
+26.3415 -26 skycell.0720
+30.7317 -26 skycell.0721
+35.122 -26 skycell.0722
+39.5122 -26 skycell.0723
+43.9024 -26 skycell.0724
+48.2927 -26 skycell.0725
+52.6829 -26 skycell.0726
+57.0732 -26 skycell.0727
+61.4634 -26 skycell.0728
+65.8537 -26 skycell.0729
+70.2439 -26 skycell.0730
+74.6341 -26 skycell.0731
+79.0244 -26 skycell.0732
+83.4146 -26 skycell.0733
+87.8049 -26 skycell.0734
+92.1951 -26 skycell.0735
+96.5854 -26 skycell.0736
+100.976 -26 skycell.0737
+105.366 -26 skycell.0738
+109.756 -26 skycell.0739
+114.146 -26 skycell.0740
+118.537 -26 skycell.0741
+122.927 -26 skycell.0742
+127.317 -26 skycell.0743
+131.707 -26 skycell.0744
+136.098 -26 skycell.0745
+140.488 -26 skycell.0746
+144.878 -26 skycell.0747
+149.268 -26 skycell.0748
+153.659 -26 skycell.0749
+158.049 -26 skycell.0750
+162.439 -26 skycell.0751
+166.829 -26 skycell.0752
+171.22 -26 skycell.0753
+175.61 -26 skycell.0754
+180 -26 skycell.0755
+184.39 -26 skycell.0756
+188.78 -26 skycell.0757
+193.171 -26 skycell.0758
+197.561 -26 skycell.0759
+201.951 -26 skycell.0760
+206.341 -26 skycell.0761
+210.732 -26 skycell.0762
+215.122 -26 skycell.0763
+219.512 -26 skycell.0764
+223.902 -26 skycell.0765
+228.293 -26 skycell.0766
+232.683 -26 skycell.0767
+237.073 -26 skycell.0768
+241.463 -26 skycell.0769
+245.854 -26 skycell.0770
+250.244 -26 skycell.0771
+254.634 -26 skycell.0772
+259.024 -26 skycell.0773
+263.415 -26 skycell.0774
+267.805 -26 skycell.0775
+272.195 -26 skycell.0776
+276.585 -26 skycell.0777
+280.976 -26 skycell.0778
+285.366 -26 skycell.0779
+289.756 -26 skycell.0780
+294.146 -26 skycell.0781
+298.537 -26 skycell.0782
+302.927 -26 skycell.0783
+307.317 -26 skycell.0784
+311.707 -26 skycell.0785
+316.098 -26 skycell.0786
+320.488 -26 skycell.0787
+324.878 -26 skycell.0788
+329.268 -26 skycell.0789
+333.659 -26 skycell.0790
+338.049 -26 skycell.0791
+342.439 -26 skycell.0792
+346.829 -26 skycell.0793
+351.22 -26 skycell.0794
+355.61 -26 skycell.0795
+0 -22 skycell.0796
+4.28571 -22 skycell.0797
+8.57143 -22 skycell.0798
+12.8571 -22 skycell.0799
+17.1429 -22 skycell.0800
+21.4286 -22 skycell.0801
+25.7143 -22 skycell.0802
+30 -22 skycell.0803
+34.2857 -22 skycell.0804
+38.5714 -22 skycell.0805
+42.8571 -22 skycell.0806
+47.1429 -22 skycell.0807
+51.4286 -22 skycell.0808
+55.7143 -22 skycell.0809
+60 -22 skycell.0810
+64.2857 -22 skycell.0811
+68.5714 -22 skycell.0812
+72.8571 -22 skycell.0813
+77.1429 -22 skycell.0814
+81.4286 -22 skycell.0815
+85.7143 -22 skycell.0816
+90 -22 skycell.0817
+94.2857 -22 skycell.0818
+98.5714 -22 skycell.0819
+102.857 -22 skycell.0820
+107.143 -22 skycell.0821
+111.429 -22 skycell.0822
+115.714 -22 skycell.0823
+120 -22 skycell.0824
+124.286 -22 skycell.0825
+128.571 -22 skycell.0826
+132.857 -22 skycell.0827
+137.143 -22 skycell.0828
+141.429 -22 skycell.0829
+145.714 -22 skycell.0830
+150 -22 skycell.0831
+154.286 -22 skycell.0832
+158.571 -22 skycell.0833
+162.857 -22 skycell.0834
+167.143 -22 skycell.0835
+171.429 -22 skycell.0836
+175.714 -22 skycell.0837
+180 -22 skycell.0838
+184.286 -22 skycell.0839
+188.571 -22 skycell.0840
+192.857 -22 skycell.0841
+197.143 -22 skycell.0842
+201.429 -22 skycell.0843
+205.714 -22 skycell.0844
+210 -22 skycell.0845
+214.286 -22 skycell.0846
+218.571 -22 skycell.0847
+222.857 -22 skycell.0848
+227.143 -22 skycell.0849
+231.429 -22 skycell.0850
+235.714 -22 skycell.0851
+240 -22 skycell.0852
+244.286 -22 skycell.0853
+248.571 -22 skycell.0854
+252.857 -22 skycell.0855
+257.143 -22 skycell.0856
+261.429 -22 skycell.0857
+265.714 -22 skycell.0858
+270 -22 skycell.0859
+274.286 -22 skycell.0860
+278.571 -22 skycell.0861
+282.857 -22 skycell.0862
+287.143 -22 skycell.0863
+291.429 -22 skycell.0864
+295.714 -22 skycell.0865
+300 -22 skycell.0866
+304.286 -22 skycell.0867
+308.571 -22 skycell.0868
+312.857 -22 skycell.0869
+317.143 -22 skycell.0870
+321.429 -22 skycell.0871
+325.714 -22 skycell.0872
+330 -22 skycell.0873
+334.286 -22 skycell.0874
+338.571 -22 skycell.0875
+342.857 -22 skycell.0876
+347.143 -22 skycell.0877
+351.429 -22 skycell.0878
+355.714 -22 skycell.0879
+0 -18 skycell.0880
+4.18605 -18 skycell.0881
+8.37209 -18 skycell.0882
+12.5581 -18 skycell.0883
+16.7442 -18 skycell.0884
+20.9302 -18 skycell.0885
+25.1163 -18 skycell.0886
+29.3023 -18 skycell.0887
+33.4884 -18 skycell.0888
+37.6744 -18 skycell.0889
+41.8605 -18 skycell.0890
+46.0465 -18 skycell.0891
+50.2326 -18 skycell.0892
+54.4186 -18 skycell.0893
+58.6047 -18 skycell.0894
+62.7907 -18 skycell.0895
+66.9767 -18 skycell.0896
+71.1628 -18 skycell.0897
+75.3488 -18 skycell.0898
+79.5349 -18 skycell.0899
+83.7209 -18 skycell.0900
+87.907 -18 skycell.0901
+92.093 -18 skycell.0902
+96.2791 -18 skycell.0903
+100.465 -18 skycell.0904
+104.651 -18 skycell.0905
+108.837 -18 skycell.0906
+113.023 -18 skycell.0907
+117.209 -18 skycell.0908
+121.395 -18 skycell.0909
+125.581 -18 skycell.0910
+129.767 -18 skycell.0911
+133.953 -18 skycell.0912
+138.14 -18 skycell.0913
+142.326 -18 skycell.0914
+146.512 -18 skycell.0915
+150.698 -18 skycell.0916
+154.884 -18 skycell.0917
+159.07 -18 skycell.0918
+163.256 -18 skycell.0919
+167.442 -18 skycell.0920
+171.628 -18 skycell.0921
+175.814 -18 skycell.0922
+180 -18 skycell.0923
+184.186 -18 skycell.0924
+188.372 -18 skycell.0925
+192.558 -18 skycell.0926
+196.744 -18 skycell.0927
+200.93 -18 skycell.0928
+205.116 -18 skycell.0929
+209.302 -18 skycell.0930
+213.488 -18 skycell.0931
+217.674 -18 skycell.0932
+221.86 -18 skycell.0933
+226.047 -18 skycell.0934
+230.233 -18 skycell.0935
+234.419 -18 skycell.0936
+238.605 -18 skycell.0937
+242.791 -18 skycell.0938
+246.977 -18 skycell.0939
+251.163 -18 skycell.0940
+255.349 -18 skycell.0941
+259.535 -18 skycell.0942
+263.721 -18 skycell.0943
+267.907 -18 skycell.0944
+272.093 -18 skycell.0945
+276.279 -18 skycell.0946
+280.465 -18 skycell.0947
+284.651 -18 skycell.0948
+288.837 -18 skycell.0949
+293.023 -18 skycell.0950
+297.209 -18 skycell.0951
+301.395 -18 skycell.0952
+305.581 -18 skycell.0953
+309.767 -18 skycell.0954
+313.953 -18 skycell.0955
+318.14 -18 skycell.0956
+322.326 -18 skycell.0957
+326.512 -18 skycell.0958
+330.698 -18 skycell.0959
+334.884 -18 skycell.0960
+339.07 -18 skycell.0961
+343.256 -18 skycell.0962
+347.442 -18 skycell.0963
+351.628 -18 skycell.0964
+355.814 -18 skycell.0965
+0 -14 skycell.0966
+4.09091 -14 skycell.0967
+8.18182 -14 skycell.0968
+12.2727 -14 skycell.0969
+16.3636 -14 skycell.0970
+20.4545 -14 skycell.0971
+24.5455 -14 skycell.0972
+28.6364 -14 skycell.0973
+32.7273 -14 skycell.0974
+36.8182 -14 skycell.0975
+40.9091 -14 skycell.0976
+45 -14 skycell.0977
+49.0909 -14 skycell.0978
+53.1818 -14 skycell.0979
+57.2727 -14 skycell.0980
+61.3636 -14 skycell.0981
+65.4545 -14 skycell.0982
+69.5455 -14 skycell.0983
+73.6364 -14 skycell.0984
+77.7273 -14 skycell.0985
+81.8182 -14 skycell.0986
+85.9091 -14 skycell.0987
+90 -14 skycell.0988
+94.0909 -14 skycell.0989
+98.1818 -14 skycell.0990
+102.273 -14 skycell.0991
+106.364 -14 skycell.0992
+110.455 -14 skycell.0993
+114.545 -14 skycell.0994
+118.636 -14 skycell.0995
+122.727 -14 skycell.0996
+126.818 -14 skycell.0997
+130.909 -14 skycell.0998
+135 -14 skycell.0999
+139.091 -14 skycell.1000
+143.182 -14 skycell.1001
+147.273 -14 skycell.1002
+151.364 -14 skycell.1003
+155.455 -14 skycell.1004
+159.545 -14 skycell.1005
+163.636 -14 skycell.1006
+167.727 -14 skycell.1007
+171.818 -14 skycell.1008
+175.909 -14 skycell.1009
+180 -14 skycell.1010
+184.091 -14 skycell.1011
+188.182 -14 skycell.1012
+192.273 -14 skycell.1013
+196.364 -14 skycell.1014
+200.455 -14 skycell.1015
+204.545 -14 skycell.1016
+208.636 -14 skycell.1017
+212.727 -14 skycell.1018
+216.818 -14 skycell.1019
+220.909 -14 skycell.1020
+225 -14 skycell.1021
+229.091 -14 skycell.1022
+233.182 -14 skycell.1023
+237.273 -14 skycell.1024
+241.364 -14 skycell.1025
+245.455 -14 skycell.1026
+249.545 -14 skycell.1027
+253.636 -14 skycell.1028
+257.727 -14 skycell.1029
+261.818 -14 skycell.1030
+265.909 -14 skycell.1031
+270 -14 skycell.1032
+274.091 -14 skycell.1033
+278.182 -14 skycell.1034
+282.273 -14 skycell.1035
+286.364 -14 skycell.1036
+290.455 -14 skycell.1037
+294.545 -14 skycell.1038
+298.636 -14 skycell.1039
+302.727 -14 skycell.1040
+306.818 -14 skycell.1041
+310.909 -14 skycell.1042
+315 -14 skycell.1043
+319.091 -14 skycell.1044
+323.182 -14 skycell.1045
+327.273 -14 skycell.1046
+331.364 -14 skycell.1047
+335.455 -14 skycell.1048
+339.545 -14 skycell.1049
+343.636 -14 skycell.1050
+347.727 -14 skycell.1051
+351.818 -14 skycell.1052
+355.909 -14 skycell.1053
+0 -10 skycell.1054
+4.04494 -10 skycell.1055
+8.08989 -10 skycell.1056
+12.1348 -10 skycell.1057
+16.1798 -10 skycell.1058
+20.2247 -10 skycell.1059
+24.2697 -10 skycell.1060
+28.3146 -10 skycell.1061
+32.3596 -10 skycell.1062
+36.4045 -10 skycell.1063
+40.4494 -10 skycell.1064
+44.4944 -10 skycell.1065
+48.5393 -10 skycell.1066
+52.5843 -10 skycell.1067
+56.6292 -10 skycell.1068
+60.6742 -10 skycell.1069
+64.7191 -10 skycell.1070
+68.764 -10 skycell.1071
+72.809 -10 skycell.1072
+76.8539 -10 skycell.1073
+80.8989 -10 skycell.1074
+84.9438 -10 skycell.1075
+88.9888 -10 skycell.1076
+93.0337 -10 skycell.1077
+97.0787 -10 skycell.1078
+101.124 -10 skycell.1079
+105.169 -10 skycell.1080
+109.213 -10 skycell.1081
+113.258 -10 skycell.1082
+117.303 -10 skycell.1083
+121.348 -10 skycell.1084
+125.393 -10 skycell.1085
+129.438 -10 skycell.1086
+133.483 -10 skycell.1087
+137.528 -10 skycell.1088
+141.573 -10 skycell.1089
+145.618 -10 skycell.1090
+149.663 -10 skycell.1091
+153.708 -10 skycell.1092
+157.753 -10 skycell.1093
+161.798 -10 skycell.1094
+165.843 -10 skycell.1095
+169.888 -10 skycell.1096
+173.933 -10 skycell.1097
+177.978 -10 skycell.1098
+182.022 -10 skycell.1099
+186.067 -10 skycell.1100
+190.112 -10 skycell.1101
+194.157 -10 skycell.1102
+198.202 -10 skycell.1103
+202.247 -10 skycell.1104
+206.292 -10 skycell.1105
+210.337 -10 skycell.1106
+214.382 -10 skycell.1107
+218.427 -10 skycell.1108
+222.472 -10 skycell.1109
+226.517 -10 skycell.1110
+230.562 -10 skycell.1111
+234.607 -10 skycell.1112
+238.652 -10 skycell.1113
+242.697 -10 skycell.1114
+246.742 -10 skycell.1115
+250.787 -10 skycell.1116
+254.831 -10 skycell.1117
+258.876 -10 skycell.1118
+262.921 -10 skycell.1119
+266.966 -10 skycell.1120
+271.011 -10 skycell.1121
+275.056 -10 skycell.1122
+279.101 -10 skycell.1123
+283.146 -10 skycell.1124
+287.191 -10 skycell.1125
+291.236 -10 skycell.1126
+295.281 -10 skycell.1127
+299.326 -10 skycell.1128
+303.371 -10 skycell.1129
+307.416 -10 skycell.1130
+311.461 -10 skycell.1131
+315.506 -10 skycell.1132
+319.551 -10 skycell.1133
+323.596 -10 skycell.1134
+327.64 -10 skycell.1135
+331.685 -10 skycell.1136
+335.73 -10 skycell.1137
+339.775 -10 skycell.1138
+343.82 -10 skycell.1139
+347.865 -10 skycell.1140
+351.91 -10 skycell.1141
+355.955 -10 skycell.1142
+0 -6 skycell.1143
+4.04494 -6 skycell.1144
+8.08989 -6 skycell.1145
+12.1348 -6 skycell.1146
+16.1798 -6 skycell.1147
+20.2247 -6 skycell.1148
+24.2697 -6 skycell.1149
+28.3146 -6 skycell.1150
+32.3596 -6 skycell.1151
+36.4045 -6 skycell.1152
+40.4494 -6 skycell.1153
+44.4944 -6 skycell.1154
+48.5393 -6 skycell.1155
+52.5843 -6 skycell.1156
+56.6292 -6 skycell.1157
+60.6742 -6 skycell.1158
+64.7191 -6 skycell.1159
+68.764 -6 skycell.1160
+72.809 -6 skycell.1161
+76.8539 -6 skycell.1162
+80.8989 -6 skycell.1163
+84.9438 -6 skycell.1164
+88.9888 -6 skycell.1165
+93.0337 -6 skycell.1166
+97.0787 -6 skycell.1167
+101.124 -6 skycell.1168
+105.169 -6 skycell.1169
+109.213 -6 skycell.1170
+113.258 -6 skycell.1171
+117.303 -6 skycell.1172
+121.348 -6 skycell.1173
+125.393 -6 skycell.1174
+129.438 -6 skycell.1175
+133.483 -6 skycell.1176
+137.528 -6 skycell.1177
+141.573 -6 skycell.1178
+145.618 -6 skycell.1179
+149.663 -6 skycell.1180
+153.708 -6 skycell.1181
+157.753 -6 skycell.1182
+161.798 -6 skycell.1183
+165.843 -6 skycell.1184
+169.888 -6 skycell.1185
+173.933 -6 skycell.1186
+177.978 -6 skycell.1187
+182.022 -6 skycell.1188
+186.067 -6 skycell.1189
+190.112 -6 skycell.1190
+194.157 -6 skycell.1191
+198.202 -6 skycell.1192
+202.247 -6 skycell.1193
+206.292 -6 skycell.1194
+210.337 -6 skycell.1195
+214.382 -6 skycell.1196
+218.427 -6 skycell.1197
+222.472 -6 skycell.1198
+226.517 -6 skycell.1199
+230.562 -6 skycell.1200
+234.607 -6 skycell.1201
+238.652 -6 skycell.1202
+242.697 -6 skycell.1203
+246.742 -6 skycell.1204
+250.787 -6 skycell.1205
+254.831 -6 skycell.1206
+258.876 -6 skycell.1207
+262.921 -6 skycell.1208
+266.966 -6 skycell.1209
+271.011 -6 skycell.1210
+275.056 -6 skycell.1211
+279.101 -6 skycell.1212
+283.146 -6 skycell.1213
+287.191 -6 skycell.1214
+291.236 -6 skycell.1215
+295.281 -6 skycell.1216
+299.326 -6 skycell.1217
+303.371 -6 skycell.1218
+307.416 -6 skycell.1219
+311.461 -6 skycell.1220
+315.506 -6 skycell.1221
+319.551 -6 skycell.1222
+323.596 -6 skycell.1223
+327.64 -6 skycell.1224
+331.685 -6 skycell.1225
+335.73 -6 skycell.1226
+339.775 -6 skycell.1227
+343.82 -6 skycell.1228
+347.865 -6 skycell.1229
+351.91 -6 skycell.1230
+355.955 -6 skycell.1231
+0 -2 skycell.1232
+4 -2 skycell.1233
+8 -2 skycell.1234
+12 -2 skycell.1235
+16 -2 skycell.1236
+20 -2 skycell.1237
+24 -2 skycell.1238
+28 -2 skycell.1239
+32 -2 skycell.1240
+36 -2 skycell.1241
+40 -2 skycell.1242
+44 -2 skycell.1243
+48 -2 skycell.1244
+52 -2 skycell.1245
+56 -2 skycell.1246
+60 -2 skycell.1247
+64 -2 skycell.1248
+68 -2 skycell.1249
+72 -2 skycell.1250
+76 -2 skycell.1251
+80 -2 skycell.1252
+84 -2 skycell.1253
+88 -2 skycell.1254
+92 -2 skycell.1255
+96 -2 skycell.1256
+100 -2 skycell.1257
+104 -2 skycell.1258
+108 -2 skycell.1259
+112 -2 skycell.1260
+116 -2 skycell.1261
+120 -2 skycell.1262
+124 -2 skycell.1263
+128 -2 skycell.1264
+132 -2 skycell.1265
+136 -2 skycell.1266
+140 -2 skycell.1267
+144 -2 skycell.1268
+148 -2 skycell.1269
+152 -2 skycell.1270
+156 -2 skycell.1271
+160 -2 skycell.1272
+164 -2 skycell.1273
+168 -2 skycell.1274
+172 -2 skycell.1275
+176 -2 skycell.1276
+180 -2 skycell.1277
+184 -2 skycell.1278
+188 -2 skycell.1279
+192 -2 skycell.1280
+196 -2 skycell.1281
+200 -2 skycell.1282
+204 -2 skycell.1283
+208 -2 skycell.1284
+212 -2 skycell.1285
+216 -2 skycell.1286
+220 -2 skycell.1287
+224 -2 skycell.1288
+228 -2 skycell.1289
+232 -2 skycell.1290
+236 -2 skycell.1291
+240 -2 skycell.1292
+244 -2 skycell.1293
+248 -2 skycell.1294
+252 -2 skycell.1295
+256 -2 skycell.1296
+260 -2 skycell.1297
+264 -2 skycell.1298
+268 -2 skycell.1299
+272 -2 skycell.1300
+276 -2 skycell.1301
+280 -2 skycell.1302
+284 -2 skycell.1303
+288 -2 skycell.1304
+292 -2 skycell.1305
+296 -2 skycell.1306
+300 -2 skycell.1307
+304 -2 skycell.1308
+308 -2 skycell.1309
+312 -2 skycell.1310
+316 -2 skycell.1311
+320 -2 skycell.1312
+324 -2 skycell.1313
+328 -2 skycell.1314
+332 -2 skycell.1315
+336 -2 skycell.1316
+340 -2 skycell.1317
+344 -2 skycell.1318
+348 -2 skycell.1319
+352 -2 skycell.1320
+356 -2 skycell.1321
+0 2 skycell.1322
+4 2 skycell.1323
+8 2 skycell.1324
+12 2 skycell.1325
+16 2 skycell.1326
+20 2 skycell.1327
+24 2 skycell.1328
+28 2 skycell.1329
+32 2 skycell.1330
+36 2 skycell.1331
+40 2 skycell.1332
+44 2 skycell.1333
+48 2 skycell.1334
+52 2 skycell.1335
+56 2 skycell.1336
+60 2 skycell.1337
+64 2 skycell.1338
+68 2 skycell.1339
+72 2 skycell.1340
+76 2 skycell.1341
+80 2 skycell.1342
+84 2 skycell.1343
+88 2 skycell.1344
+92 2 skycell.1345
+96 2 skycell.1346
+100 2 skycell.1347
+104 2 skycell.1348
+108 2 skycell.1349
+112 2 skycell.1350
+116 2 skycell.1351
+120 2 skycell.1352
+124 2 skycell.1353
+128 2 skycell.1354
+132 2 skycell.1355
+136 2 skycell.1356
+140 2 skycell.1357
+144 2 skycell.1358
+148 2 skycell.1359
+152 2 skycell.1360
+156 2 skycell.1361
+160 2 skycell.1362
+164 2 skycell.1363
+168 2 skycell.1364
+172 2 skycell.1365
+176 2 skycell.1366
+180 2 skycell.1367
+184 2 skycell.1368
+188 2 skycell.1369
+192 2 skycell.1370
+196 2 skycell.1371
+200 2 skycell.1372
+204 2 skycell.1373
+208 2 skycell.1374
+212 2 skycell.1375
+216 2 skycell.1376
+220 2 skycell.1377
+224 2 skycell.1378
+228 2 skycell.1379
+232 2 skycell.1380
+236 2 skycell.1381
+240 2 skycell.1382
+244 2 skycell.1383
+248 2 skycell.1384
+252 2 skycell.1385
+256 2 skycell.1386
+260 2 skycell.1387
+264 2 skycell.1388
+268 2 skycell.1389
+272 2 skycell.1390
+276 2 skycell.1391
+280 2 skycell.1392
+284 2 skycell.1393
+288 2 skycell.1394
+292 2 skycell.1395
+296 2 skycell.1396
+300 2 skycell.1397
+304 2 skycell.1398
+308 2 skycell.1399
+312 2 skycell.1400
+316 2 skycell.1401
+320 2 skycell.1402
+324 2 skycell.1403
+328 2 skycell.1404
+332 2 skycell.1405
+336 2 skycell.1406
+340 2 skycell.1407
+344 2 skycell.1408
+348 2 skycell.1409
+352 2 skycell.1410
+356 2 skycell.1411
+0 6 skycell.1412
+4.04494 6 skycell.1413
+8.08989 6 skycell.1414
+12.1348 6 skycell.1415
+16.1798 6 skycell.1416
+20.2247 6 skycell.1417
+24.2697 6 skycell.1418
+28.3146 6 skycell.1419
+32.3596 6 skycell.1420
+36.4045 6 skycell.1421
+40.4494 6 skycell.1422
+44.4944 6 skycell.1423
+48.5393 6 skycell.1424
+52.5843 6 skycell.1425
+56.6292 6 skycell.1426
+60.6742 6 skycell.1427
+64.7191 6 skycell.1428
+68.764 6 skycell.1429
+72.809 6 skycell.1430
+76.8539 6 skycell.1431
+80.8989 6 skycell.1432
+84.9438 6 skycell.1433
+88.9888 6 skycell.1434
+93.0337 6 skycell.1435
+97.0787 6 skycell.1436
+101.124 6 skycell.1437
+105.169 6 skycell.1438
+109.213 6 skycell.1439
+113.258 6 skycell.1440
+117.303 6 skycell.1441
+121.348 6 skycell.1442
+125.393 6 skycell.1443
+129.438 6 skycell.1444
+133.483 6 skycell.1445
+137.528 6 skycell.1446
+141.573 6 skycell.1447
+145.618 6 skycell.1448
+149.663 6 skycell.1449
+153.708 6 skycell.1450
+157.753 6 skycell.1451
+161.798 6 skycell.1452
+165.843 6 skycell.1453
+169.888 6 skycell.1454
+173.933 6 skycell.1455
+177.978 6 skycell.1456
+182.022 6 skycell.1457
+186.067 6 skycell.1458
+190.112 6 skycell.1459
+194.157 6 skycell.1460
+198.202 6 skycell.1461
+202.247 6 skycell.1462
+206.292 6 skycell.1463
+210.337 6 skycell.1464
+214.382 6 skycell.1465
+218.427 6 skycell.1466
+222.472 6 skycell.1467
+226.517 6 skycell.1468
+230.562 6 skycell.1469
+234.607 6 skycell.1470
+238.652 6 skycell.1471
+242.697 6 skycell.1472
+246.742 6 skycell.1473
+250.787 6 skycell.1474
+254.831 6 skycell.1475
+258.876 6 skycell.1476
+262.921 6 skycell.1477
+266.966 6 skycell.1478
+271.011 6 skycell.1479
+275.056 6 skycell.1480
+279.101 6 skycell.1481
+283.146 6 skycell.1482
+287.191 6 skycell.1483
+291.236 6 skycell.1484
+295.281 6 skycell.1485
+299.326 6 skycell.1486
+303.371 6 skycell.1487
+307.416 6 skycell.1488
+311.461 6 skycell.1489
+315.506 6 skycell.1490
+319.551 6 skycell.1491
+323.596 6 skycell.1492
+327.64 6 skycell.1493
+331.685 6 skycell.1494
+335.73 6 skycell.1495
+339.775 6 skycell.1496
+343.82 6 skycell.1497
+347.865 6 skycell.1498
+351.91 6 skycell.1499
+355.955 6 skycell.1500
+0 10 skycell.1501
+4.04494 10 skycell.1502
+8.08989 10 skycell.1503
+12.1348 10 skycell.1504
+16.1798 10 skycell.1505
+20.2247 10 skycell.1506
+24.2697 10 skycell.1507
+28.3146 10 skycell.1508
+32.3596 10 skycell.1509
+36.4045 10 skycell.1510
+40.4494 10 skycell.1511
+44.4944 10 skycell.1512
+48.5393 10 skycell.1513
+52.5843 10 skycell.1514
+56.6292 10 skycell.1515
+60.6742 10 skycell.1516
+64.7191 10 skycell.1517
+68.764 10 skycell.1518
+72.809 10 skycell.1519
+76.8539 10 skycell.1520
+80.8989 10 skycell.1521
+84.9438 10 skycell.1522
+88.9888 10 skycell.1523
+93.0337 10 skycell.1524
+97.0787 10 skycell.1525
+101.124 10 skycell.1526
+105.169 10 skycell.1527
+109.213 10 skycell.1528
+113.258 10 skycell.1529
+117.303 10 skycell.1530
+121.348 10 skycell.1531
+125.393 10 skycell.1532
+129.438 10 skycell.1533
+133.483 10 skycell.1534
+137.528 10 skycell.1535
+141.573 10 skycell.1536
+145.618 10 skycell.1537
+149.663 10 skycell.1538
+153.708 10 skycell.1539
+157.753 10 skycell.1540
+161.798 10 skycell.1541
+165.843 10 skycell.1542
+169.888 10 skycell.1543
+173.933 10 skycell.1544
+177.978 10 skycell.1545
+182.022 10 skycell.1546
+186.067 10 skycell.1547
+190.112 10 skycell.1548
+194.157 10 skycell.1549
+198.202 10 skycell.1550
+202.247 10 skycell.1551
+206.292 10 skycell.1552
+210.337 10 skycell.1553
+214.382 10 skycell.1554
+218.427 10 skycell.1555
+222.472 10 skycell.1556
+226.517 10 skycell.1557
+230.562 10 skycell.1558
+234.607 10 skycell.1559
+238.652 10 skycell.1560
+242.697 10 skycell.1561
+246.742 10 skycell.1562
+250.787 10 skycell.1563
+254.831 10 skycell.1564
+258.876 10 skycell.1565
+262.921 10 skycell.1566
+266.966 10 skycell.1567
+271.011 10 skycell.1568
+275.056 10 skycell.1569
+279.101 10 skycell.1570
+283.146 10 skycell.1571
+287.191 10 skycell.1572
+291.236 10 skycell.1573
+295.281 10 skycell.1574
+299.326 10 skycell.1575
+303.371 10 skycell.1576
+307.416 10 skycell.1577
+311.461 10 skycell.1578
+315.506 10 skycell.1579
+319.551 10 skycell.1580
+323.596 10 skycell.1581
+327.64 10 skycell.1582
+331.685 10 skycell.1583
+335.73 10 skycell.1584
+339.775 10 skycell.1585
+343.82 10 skycell.1586
+347.865 10 skycell.1587
+351.91 10 skycell.1588
+355.955 10 skycell.1589
+0 14 skycell.1590
+4.09091 14 skycell.1591
+8.18182 14 skycell.1592
+12.2727 14 skycell.1593
+16.3636 14 skycell.1594
+20.4545 14 skycell.1595
+24.5455 14 skycell.1596
+28.6364 14 skycell.1597
+32.7273 14 skycell.1598
+36.8182 14 skycell.1599
+40.9091 14 skycell.1600
+45 14 skycell.1601
+49.0909 14 skycell.1602
+53.1818 14 skycell.1603
+57.2727 14 skycell.1604
+61.3636 14 skycell.1605
+65.4545 14 skycell.1606
+69.5455 14 skycell.1607
+73.6364 14 skycell.1608
+77.7273 14 skycell.1609
+81.8182 14 skycell.1610
+85.9091 14 skycell.1611
+90 14 skycell.1612
+94.0909 14 skycell.1613
+98.1818 14 skycell.1614
+102.273 14 skycell.1615
+106.364 14 skycell.1616
+110.455 14 skycell.1617
+114.545 14 skycell.1618
+118.636 14 skycell.1619
+122.727 14 skycell.1620
+126.818 14 skycell.1621
+130.909 14 skycell.1622
+135 14 skycell.1623
+139.091 14 skycell.1624
+143.182 14 skycell.1625
+147.273 14 skycell.1626
+151.364 14 skycell.1627
+155.455 14 skycell.1628
+159.545 14 skycell.1629
+163.636 14 skycell.1630
+167.727 14 skycell.1631
+171.818 14 skycell.1632
+175.909 14 skycell.1633
+180 14 skycell.1634
+184.091 14 skycell.1635
+188.182 14 skycell.1636
+192.273 14 skycell.1637
+196.364 14 skycell.1638
+200.455 14 skycell.1639
+204.545 14 skycell.1640
+208.636 14 skycell.1641
+212.727 14 skycell.1642
+216.818 14 skycell.1643
+220.909 14 skycell.1644
+225 14 skycell.1645
+229.091 14 skycell.1646
+233.182 14 skycell.1647
+237.273 14 skycell.1648
+241.364 14 skycell.1649
+245.455 14 skycell.1650
+249.545 14 skycell.1651
+253.636 14 skycell.1652
+257.727 14 skycell.1653
+261.818 14 skycell.1654
+265.909 14 skycell.1655
+270 14 skycell.1656
+274.091 14 skycell.1657
+278.182 14 skycell.1658
+282.273 14 skycell.1659
+286.364 14 skycell.1660
+290.455 14 skycell.1661
+294.545 14 skycell.1662
+298.636 14 skycell.1663
+302.727 14 skycell.1664
+306.818 14 skycell.1665
+310.909 14 skycell.1666
+315 14 skycell.1667
+319.091 14 skycell.1668
+323.182 14 skycell.1669
+327.273 14 skycell.1670
+331.364 14 skycell.1671
+335.455 14 skycell.1672
+339.545 14 skycell.1673
+343.636 14 skycell.1674
+347.727 14 skycell.1675
+351.818 14 skycell.1676
+355.909 14 skycell.1677
+0 18 skycell.1678
+4.18605 18 skycell.1679
+8.37209 18 skycell.1680
+12.5581 18 skycell.1681
+16.7442 18 skycell.1682
+20.9302 18 skycell.1683
+25.1163 18 skycell.1684
+29.3023 18 skycell.1685
+33.4884 18 skycell.1686
+37.6744 18 skycell.1687
+41.8605 18 skycell.1688
+46.0465 18 skycell.1689
+50.2326 18 skycell.1690
+54.4186 18 skycell.1691
+58.6047 18 skycell.1692
+62.7907 18 skycell.1693
+66.9767 18 skycell.1694
+71.1628 18 skycell.1695
+75.3488 18 skycell.1696
+79.5349 18 skycell.1697
+83.7209 18 skycell.1698
+87.907 18 skycell.1699
+92.093 18 skycell.1700
+96.2791 18 skycell.1701
+100.465 18 skycell.1702
+104.651 18 skycell.1703
+108.837 18 skycell.1704
+113.023 18 skycell.1705
+117.209 18 skycell.1706
+121.395 18 skycell.1707
+125.581 18 skycell.1708
+129.767 18 skycell.1709
+133.953 18 skycell.1710
+138.14 18 skycell.1711
+142.326 18 skycell.1712
+146.512 18 skycell.1713
+150.698 18 skycell.1714
+154.884 18 skycell.1715
+159.07 18 skycell.1716
+163.256 18 skycell.1717
+167.442 18 skycell.1718
+171.628 18 skycell.1719
+175.814 18 skycell.1720
+180 18 skycell.1721
+184.186 18 skycell.1722
+188.372 18 skycell.1723
+192.558 18 skycell.1724
+196.744 18 skycell.1725
+200.93 18 skycell.1726
+205.116 18 skycell.1727
+209.302 18 skycell.1728
+213.488 18 skycell.1729
+217.674 18 skycell.1730
+221.86 18 skycell.1731
+226.047 18 skycell.1732
+230.233 18 skycell.1733
+234.419 18 skycell.1734
+238.605 18 skycell.1735
+242.791 18 skycell.1736
+246.977 18 skycell.1737
+251.163 18 skycell.1738
+255.349 18 skycell.1739
+259.535 18 skycell.1740
+263.721 18 skycell.1741
+267.907 18 skycell.1742
+272.093 18 skycell.1743
+276.279 18 skycell.1744
+280.465 18 skycell.1745
+284.651 18 skycell.1746
+288.837 18 skycell.1747
+293.023 18 skycell.1748
+297.209 18 skycell.1749
+301.395 18 skycell.1750
+305.581 18 skycell.1751
+309.767 18 skycell.1752
+313.953 18 skycell.1753
+318.14 18 skycell.1754
+322.326 18 skycell.1755
+326.512 18 skycell.1756
+330.698 18 skycell.1757
+334.884 18 skycell.1758
+339.07 18 skycell.1759
+343.256 18 skycell.1760
+347.442 18 skycell.1761
+351.628 18 skycell.1762
+355.814 18 skycell.1763
+0 22 skycell.1764
+4.28571 22 skycell.1765
+8.57143 22 skycell.1766
+12.8571 22 skycell.1767
+17.1429 22 skycell.1768
+21.4286 22 skycell.1769
+25.7143 22 skycell.1770
+30 22 skycell.1771
+34.2857 22 skycell.1772
+38.5714 22 skycell.1773
+42.8571 22 skycell.1774
+47.1429 22 skycell.1775
+51.4286 22 skycell.1776
+55.7143 22 skycell.1777
+60 22 skycell.1778
+64.2857 22 skycell.1779
+68.5714 22 skycell.1780
+72.8571 22 skycell.1781
+77.1429 22 skycell.1782
+81.4286 22 skycell.1783
+85.7143 22 skycell.1784
+90 22 skycell.1785
+94.2857 22 skycell.1786
+98.5714 22 skycell.1787
+102.857 22 skycell.1788
+107.143 22 skycell.1789
+111.429 22 skycell.1790
+115.714 22 skycell.1791
+120 22 skycell.1792
+124.286 22 skycell.1793
+128.571 22 skycell.1794
+132.857 22 skycell.1795
+137.143 22 skycell.1796
+141.429 22 skycell.1797
+145.714 22 skycell.1798
+150 22 skycell.1799
+154.286 22 skycell.1800
+158.571 22 skycell.1801
+162.857 22 skycell.1802
+167.143 22 skycell.1803
+171.429 22 skycell.1804
+175.714 22 skycell.1805
+180 22 skycell.1806
+184.286 22 skycell.1807
+188.571 22 skycell.1808
+192.857 22 skycell.1809
+197.143 22 skycell.1810
+201.429 22 skycell.1811
+205.714 22 skycell.1812
+210 22 skycell.1813
+214.286 22 skycell.1814
+218.571 22 skycell.1815
+222.857 22 skycell.1816
+227.143 22 skycell.1817
+231.429 22 skycell.1818
+235.714 22 skycell.1819
+240 22 skycell.1820
+244.286 22 skycell.1821
+248.571 22 skycell.1822
+252.857 22 skycell.1823
+257.143 22 skycell.1824
+261.429 22 skycell.1825
+265.714 22 skycell.1826
+270 22 skycell.1827
+274.286 22 skycell.1828
+278.571 22 skycell.1829
+282.857 22 skycell.1830
+287.143 22 skycell.1831
+291.429 22 skycell.1832
+295.714 22 skycell.1833
+300 22 skycell.1834
+304.286 22 skycell.1835
+308.571 22 skycell.1836
+312.857 22 skycell.1837
+317.143 22 skycell.1838
+321.429 22 skycell.1839
+325.714 22 skycell.1840
+330 22 skycell.1841
+334.286 22 skycell.1842
+338.571 22 skycell.1843
+342.857 22 skycell.1844
+347.143 22 skycell.1845
+351.429 22 skycell.1846
+355.714 22 skycell.1847
+0 26 skycell.1848
+4.39024 26 skycell.1849
+8.78049 26 skycell.1850
+13.1707 26 skycell.1851
+17.561 26 skycell.1852
+21.9512 26 skycell.1853
+26.3415 26 skycell.1854
+30.7317 26 skycell.1855
+35.122 26 skycell.1856
+39.5122 26 skycell.1857
+43.9024 26 skycell.1858
+48.2927 26 skycell.1859
+52.6829 26 skycell.1860
+57.0732 26 skycell.1861
+61.4634 26 skycell.1862
+65.8537 26 skycell.1863
+70.2439 26 skycell.1864
+74.6341 26 skycell.1865
+79.0244 26 skycell.1866
+83.4146 26 skycell.1867
+87.8049 26 skycell.1868
+92.1951 26 skycell.1869
+96.5854 26 skycell.1870
+100.976 26 skycell.1871
+105.366 26 skycell.1872
+109.756 26 skycell.1873
+114.146 26 skycell.1874
+118.537 26 skycell.1875
+122.927 26 skycell.1876
+127.317 26 skycell.1877
+131.707 26 skycell.1878
+136.098 26 skycell.1879
+140.488 26 skycell.1880
+144.878 26 skycell.1881
+149.268 26 skycell.1882
+153.659 26 skycell.1883
+158.049 26 skycell.1884
+162.439 26 skycell.1885
+166.829 26 skycell.1886
+171.22 26 skycell.1887
+175.61 26 skycell.1888
+180 26 skycell.1889
+184.39 26 skycell.1890
+188.78 26 skycell.1891
+193.171 26 skycell.1892
+197.561 26 skycell.1893
+201.951 26 skycell.1894
+206.341 26 skycell.1895
+210.732 26 skycell.1896
+215.122 26 skycell.1897
+219.512 26 skycell.1898
+223.902 26 skycell.1899
+228.293 26 skycell.1900
+232.683 26 skycell.1901
+237.073 26 skycell.1902
+241.463 26 skycell.1903
+245.854 26 skycell.1904
+250.244 26 skycell.1905
+254.634 26 skycell.1906
+259.024 26 skycell.1907
+263.415 26 skycell.1908
+267.805 26 skycell.1909
+272.195 26 skycell.1910
+276.585 26 skycell.1911
+280.976 26 skycell.1912
+285.366 26 skycell.1913
+289.756 26 skycell.1914
+294.146 26 skycell.1915
+298.537 26 skycell.1916
+302.927 26 skycell.1917
+307.317 26 skycell.1918
+311.707 26 skycell.1919
+316.098 26 skycell.1920
+320.488 26 skycell.1921
+324.878 26 skycell.1922
+329.268 26 skycell.1923
+333.659 26 skycell.1924
+338.049 26 skycell.1925
+342.439 26 skycell.1926
+346.829 26 skycell.1927
+351.22 26 skycell.1928
+355.61 26 skycell.1929
+0 30 skycell.1930
+4.55696 30 skycell.1931
+9.11392 30 skycell.1932
+13.6709 30 skycell.1933
+18.2278 30 skycell.1934
+22.7848 30 skycell.1935
+27.3418 30 skycell.1936
+31.8987 30 skycell.1937
+36.4557 30 skycell.1938
+41.0127 30 skycell.1939
+45.5696 30 skycell.1940
+50.1266 30 skycell.1941
+54.6835 30 skycell.1942
+59.2405 30 skycell.1943
+63.7975 30 skycell.1944
+68.3544 30 skycell.1945
+72.9114 30 skycell.1946
+77.4684 30 skycell.1947
+82.0253 30 skycell.1948
+86.5823 30 skycell.1949
+91.1392 30 skycell.1950
+95.6962 30 skycell.1951
+100.253 30 skycell.1952
+104.81 30 skycell.1953
+109.367 30 skycell.1954
+113.924 30 skycell.1955
+118.481 30 skycell.1956
+123.038 30 skycell.1957
+127.595 30 skycell.1958
+132.152 30 skycell.1959
+136.709 30 skycell.1960
+141.266 30 skycell.1961
+145.823 30 skycell.1962
+150.38 30 skycell.1963
+154.937 30 skycell.1964
+159.494 30 skycell.1965
+164.051 30 skycell.1966
+168.608 30 skycell.1967
+173.165 30 skycell.1968
+177.722 30 skycell.1969
+182.278 30 skycell.1970
+186.835 30 skycell.1971
+191.392 30 skycell.1972
+195.949 30 skycell.1973
+200.506 30 skycell.1974
+205.063 30 skycell.1975
+209.62 30 skycell.1976
+214.177 30 skycell.1977
+218.734 30 skycell.1978
+223.291 30 skycell.1979
+227.848 30 skycell.1980
+232.405 30 skycell.1981
+236.962 30 skycell.1982
+241.519 30 skycell.1983
+246.076 30 skycell.1984
+250.633 30 skycell.1985
+255.19 30 skycell.1986
+259.747 30 skycell.1987
+264.304 30 skycell.1988
+268.861 30 skycell.1989
+273.418 30 skycell.1990
+277.975 30 skycell.1991
+282.532 30 skycell.1992
+287.089 30 skycell.1993
+291.646 30 skycell.1994
+296.203 30 skycell.1995
+300.759 30 skycell.1996
+305.316 30 skycell.1997
+309.873 30 skycell.1998
+314.43 30 skycell.1999
+318.987 30 skycell.2000
+323.544 30 skycell.2001
+328.101 30 skycell.2002
+332.658 30 skycell.2003
+337.215 30 skycell.2004
+341.772 30 skycell.2005
+346.329 30 skycell.2006
+350.886 30 skycell.2007
+355.443 30 skycell.2008
+0 34 skycell.2009
+4.73684 34 skycell.2010
+9.47368 34 skycell.2011
+14.2105 34 skycell.2012
+18.9474 34 skycell.2013
+23.6842 34 skycell.2014
+28.4211 34 skycell.2015
+33.1579 34 skycell.2016
+37.8947 34 skycell.2017
+42.6316 34 skycell.2018
+47.3684 34 skycell.2019
+52.1053 34 skycell.2020
+56.8421 34 skycell.2021
+61.5789 34 skycell.2022
+66.3158 34 skycell.2023
+71.0526 34 skycell.2024
+75.7895 34 skycell.2025
+80.5263 34 skycell.2026
+85.2632 34 skycell.2027
+90 34 skycell.2028
+94.7368 34 skycell.2029
+99.4737 34 skycell.2030
+104.211 34 skycell.2031
+108.947 34 skycell.2032
+113.684 34 skycell.2033
+118.421 34 skycell.2034
+123.158 34 skycell.2035
+127.895 34 skycell.2036
+132.632 34 skycell.2037
+137.368 34 skycell.2038
+142.105 34 skycell.2039
+146.842 34 skycell.2040
+151.579 34 skycell.2041
+156.316 34 skycell.2042
+161.053 34 skycell.2043
+165.789 34 skycell.2044
+170.526 34 skycell.2045
+175.263 34 skycell.2046
+180 34 skycell.2047
+184.737 34 skycell.2048
+189.474 34 skycell.2049
+194.211 34 skycell.2050
+198.947 34 skycell.2051
+203.684 34 skycell.2052
+208.421 34 skycell.2053
+213.158 34 skycell.2054
+217.895 34 skycell.2055
+222.632 34 skycell.2056
+227.368 34 skycell.2057
+232.105 34 skycell.2058
+236.842 34 skycell.2059
+241.579 34 skycell.2060
+246.316 34 skycell.2061
+251.053 34 skycell.2062
+255.789 34 skycell.2063
+260.526 34 skycell.2064
+265.263 34 skycell.2065
+270 34 skycell.2066
+274.737 34 skycell.2067
+279.474 34 skycell.2068
+284.211 34 skycell.2069
+288.947 34 skycell.2070
+293.684 34 skycell.2071
+298.421 34 skycell.2072
+303.158 34 skycell.2073
+307.895 34 skycell.2074
+312.632 34 skycell.2075
+317.368 34 skycell.2076
+322.105 34 skycell.2077
+326.842 34 skycell.2078
+331.579 34 skycell.2079
+336.316 34 skycell.2080
+341.053 34 skycell.2081
+345.789 34 skycell.2082
+350.526 34 skycell.2083
+355.263 34 skycell.2084
+0 38 skycell.2085
+5 38 skycell.2086
+10 38 skycell.2087
+15 38 skycell.2088
+20 38 skycell.2089
+25 38 skycell.2090
+30 38 skycell.2091
+35 38 skycell.2092
+40 38 skycell.2093
+45 38 skycell.2094
+50 38 skycell.2095
+55 38 skycell.2096
+60 38 skycell.2097
+65 38 skycell.2098
+70 38 skycell.2099
+75 38 skycell.2100
+80 38 skycell.2101
+85 38 skycell.2102
+90 38 skycell.2103
+95 38 skycell.2104
+100 38 skycell.2105
+105 38 skycell.2106
+110 38 skycell.2107
+115 38 skycell.2108
+120 38 skycell.2109
+125 38 skycell.2110
+130 38 skycell.2111
+135 38 skycell.2112
+140 38 skycell.2113
+145 38 skycell.2114
+150 38 skycell.2115
+155 38 skycell.2116
+160 38 skycell.2117
+165 38 skycell.2118
+170 38 skycell.2119
+175 38 skycell.2120
+180 38 skycell.2121
+185 38 skycell.2122
+190 38 skycell.2123
+195 38 skycell.2124
+200 38 skycell.2125
+205 38 skycell.2126
+210 38 skycell.2127
+215 38 skycell.2128
+220 38 skycell.2129
+225 38 skycell.2130
+230 38 skycell.2131
+235 38 skycell.2132
+240 38 skycell.2133
+245 38 skycell.2134
+250 38 skycell.2135
+255 38 skycell.2136
+260 38 skycell.2137
+265 38 skycell.2138
+270 38 skycell.2139
+275 38 skycell.2140
+280 38 skycell.2141
+285 38 skycell.2142
+290 38 skycell.2143
+295 38 skycell.2144
+300 38 skycell.2145
+305 38 skycell.2146
+310 38 skycell.2147
+315 38 skycell.2148
+320 38 skycell.2149
+325 38 skycell.2150
+330 38 skycell.2151
+335 38 skycell.2152
+340 38 skycell.2153
+345 38 skycell.2154
+350 38 skycell.2155
+355 38 skycell.2156
+0 42 skycell.2157
+5.29412 42 skycell.2158
+10.5882 42 skycell.2159
+15.8824 42 skycell.2160
+21.1765 42 skycell.2161
+26.4706 42 skycell.2162
+31.7647 42 skycell.2163
+37.0588 42 skycell.2164
+42.3529 42 skycell.2165
+47.6471 42 skycell.2166
+52.9412 42 skycell.2167
+58.2353 42 skycell.2168
+63.5294 42 skycell.2169
+68.8235 42 skycell.2170
+74.1176 42 skycell.2171
+79.4118 42 skycell.2172
+84.7059 42 skycell.2173
+90 42 skycell.2174
+95.2941 42 skycell.2175
+100.588 42 skycell.2176
+105.882 42 skycell.2177
+111.176 42 skycell.2178
+116.471 42 skycell.2179
+121.765 42 skycell.2180
+127.059 42 skycell.2181
+132.353 42 skycell.2182
+137.647 42 skycell.2183
+142.941 42 skycell.2184
+148.235 42 skycell.2185
+153.529 42 skycell.2186
+158.824 42 skycell.2187
+164.118 42 skycell.2188
+169.412 42 skycell.2189
+174.706 42 skycell.2190
+180 42 skycell.2191
+185.294 42 skycell.2192
+190.588 42 skycell.2193
+195.882 42 skycell.2194
+201.176 42 skycell.2195
+206.471 42 skycell.2196
+211.765 42 skycell.2197
+217.059 42 skycell.2198
+222.353 42 skycell.2199
+227.647 42 skycell.2200
+232.941 42 skycell.2201
+238.235 42 skycell.2202
+243.529 42 skycell.2203
+248.824 42 skycell.2204
+254.118 42 skycell.2205
+259.412 42 skycell.2206
+264.706 42 skycell.2207
+270 42 skycell.2208
+275.294 42 skycell.2209
+280.588 42 skycell.2210
+285.882 42 skycell.2211
+291.176 42 skycell.2212
+296.471 42 skycell.2213
+301.765 42 skycell.2214
+307.059 42 skycell.2215
+312.353 42 skycell.2216
+317.647 42 skycell.2217
+322.941 42 skycell.2218
+328.235 42 skycell.2219
+333.529 42 skycell.2220
+338.824 42 skycell.2221
+344.118 42 skycell.2222
+349.412 42 skycell.2223
+354.706 42 skycell.2224
+0 46 skycell.2225
+5.625 46 skycell.2226
+11.25 46 skycell.2227
+16.875 46 skycell.2228
+22.5 46 skycell.2229
+28.125 46 skycell.2230
+33.75 46 skycell.2231
+39.375 46 skycell.2232
+45 46 skycell.2233
+50.625 46 skycell.2234
+56.25 46 skycell.2235
+61.875 46 skycell.2236
+67.5 46 skycell.2237
+73.125 46 skycell.2238
+78.75 46 skycell.2239
+84.375 46 skycell.2240
+90 46 skycell.2241
+95.625 46 skycell.2242
+101.25 46 skycell.2243
+106.875 46 skycell.2244
+112.5 46 skycell.2245
+118.125 46 skycell.2246
+123.75 46 skycell.2247
+129.375 46 skycell.2248
+135 46 skycell.2249
+140.625 46 skycell.2250
+146.25 46 skycell.2251
+151.875 46 skycell.2252
+157.5 46 skycell.2253
+163.125 46 skycell.2254
+168.75 46 skycell.2255
+174.375 46 skycell.2256
+180 46 skycell.2257
+185.625 46 skycell.2258
+191.25 46 skycell.2259
+196.875 46 skycell.2260
+202.5 46 skycell.2261
+208.125 46 skycell.2262
+213.75 46 skycell.2263
+219.375 46 skycell.2264
+225 46 skycell.2265
+230.625 46 skycell.2266
+236.25 46 skycell.2267
+241.875 46 skycell.2268
+247.5 46 skycell.2269
+253.125 46 skycell.2270
+258.75 46 skycell.2271
+264.375 46 skycell.2272
+270 46 skycell.2273
+275.625 46 skycell.2274
+281.25 46 skycell.2275
+286.875 46 skycell.2276
+292.5 46 skycell.2277
+298.125 46 skycell.2278
+303.75 46 skycell.2279
+309.375 46 skycell.2280
+315 46 skycell.2281
+320.625 46 skycell.2282
+326.25 46 skycell.2283
+331.875 46 skycell.2284
+337.5 46 skycell.2285
+343.125 46 skycell.2286
+348.75 46 skycell.2287
+354.375 46 skycell.2288
+0 50 skycell.2289
+6 50 skycell.2290
+12 50 skycell.2291
+18 50 skycell.2292
+24 50 skycell.2293
+30 50 skycell.2294
+36 50 skycell.2295
+42 50 skycell.2296
+48 50 skycell.2297
+54 50 skycell.2298
+60 50 skycell.2299
+66 50 skycell.2300
+72 50 skycell.2301
+78 50 skycell.2302
+84 50 skycell.2303
+90 50 skycell.2304
+96 50 skycell.2305
+102 50 skycell.2306
+108 50 skycell.2307
+114 50 skycell.2308
+120 50 skycell.2309
+126 50 skycell.2310
+132 50 skycell.2311
+138 50 skycell.2312
+144 50 skycell.2313
+150 50 skycell.2314
+156 50 skycell.2315
+162 50 skycell.2316
+168 50 skycell.2317
+174 50 skycell.2318
+180 50 skycell.2319
+186 50 skycell.2320
+192 50 skycell.2321
+198 50 skycell.2322
+204 50 skycell.2323
+210 50 skycell.2324
+216 50 skycell.2325
+222 50 skycell.2326
+228 50 skycell.2327
+234 50 skycell.2328
+240 50 skycell.2329
+246 50 skycell.2330
+252 50 skycell.2331
+258 50 skycell.2332
+264 50 skycell.2333
+270 50 skycell.2334
+276 50 skycell.2335
+282 50 skycell.2336
+288 50 skycell.2337
+294 50 skycell.2338
+300 50 skycell.2339
+306 50 skycell.2340
+312 50 skycell.2341
+318 50 skycell.2342
+324 50 skycell.2343
+330 50 skycell.2344
+336 50 skycell.2345
+342 50 skycell.2346
+348 50 skycell.2347
+354 50 skycell.2348
+0 54 skycell.2349
+6.54545 54 skycell.2350
+13.0909 54 skycell.2351
+19.6364 54 skycell.2352
+26.1818 54 skycell.2353
+32.7273 54 skycell.2354
+39.2727 54 skycell.2355
+45.8182 54 skycell.2356
+52.3636 54 skycell.2357
+58.9091 54 skycell.2358
+65.4545 54 skycell.2359
+72 54 skycell.2360
+78.5455 54 skycell.2361
+85.0909 54 skycell.2362
+91.6364 54 skycell.2363
+98.1818 54 skycell.2364
+104.727 54 skycell.2365
+111.273 54 skycell.2366
+117.818 54 skycell.2367
+124.364 54 skycell.2368
+130.909 54 skycell.2369
+137.455 54 skycell.2370
+144 54 skycell.2371
+150.545 54 skycell.2372
+157.091 54 skycell.2373
+163.636 54 skycell.2374
+170.182 54 skycell.2375
+176.727 54 skycell.2376
+183.273 54 skycell.2377
+189.818 54 skycell.2378
+196.364 54 skycell.2379
+202.909 54 skycell.2380
+209.455 54 skycell.2381
+216 54 skycell.2382
+222.545 54 skycell.2383
+229.091 54 skycell.2384
+235.636 54 skycell.2385
+242.182 54 skycell.2386
+248.727 54 skycell.2387
+255.273 54 skycell.2388
+261.818 54 skycell.2389
+268.364 54 skycell.2390
+274.909 54 skycell.2391
+281.455 54 skycell.2392
+288 54 skycell.2393
+294.545 54 skycell.2394
+301.091 54 skycell.2395
+307.636 54 skycell.2396
+314.182 54 skycell.2397
+320.727 54 skycell.2398
+327.273 54 skycell.2399
+333.818 54 skycell.2400
+340.364 54 skycell.2401
+346.909 54 skycell.2402
+353.455 54 skycell.2403
+0 58 skycell.2404
+7.2 58 skycell.2405
+14.4 58 skycell.2406
+21.6 58 skycell.2407
+28.8 58 skycell.2408
+36 58 skycell.2409
+43.2 58 skycell.2410
+50.4 58 skycell.2411
+57.6 58 skycell.2412
+64.8 58 skycell.2413
+72 58 skycell.2414
+79.2 58 skycell.2415
+86.4 58 skycell.2416
+93.6 58 skycell.2417
+100.8 58 skycell.2418
+108 58 skycell.2419
+115.2 58 skycell.2420
+122.4 58 skycell.2421
+129.6 58 skycell.2422
+136.8 58 skycell.2423
+144 58 skycell.2424
+151.2 58 skycell.2425
+158.4 58 skycell.2426
+165.6 58 skycell.2427
+172.8 58 skycell.2428
+180 58 skycell.2429
+187.2 58 skycell.2430
+194.4 58 skycell.2431
+201.6 58 skycell.2432
+208.8 58 skycell.2433
+216 58 skycell.2434
+223.2 58 skycell.2435
+230.4 58 skycell.2436
+237.6 58 skycell.2437
+244.8 58 skycell.2438
+252 58 skycell.2439
+259.2 58 skycell.2440
+266.4 58 skycell.2441
+273.6 58 skycell.2442
+280.8 58 skycell.2443
+288 58 skycell.2444
+295.2 58 skycell.2445
+302.4 58 skycell.2446
+309.6 58 skycell.2447
+316.8 58 skycell.2448
+324 58 skycell.2449
+331.2 58 skycell.2450
+338.4 58 skycell.2451
+345.6 58 skycell.2452
+352.8 58 skycell.2453
+0 62 skycell.2454
+8 62 skycell.2455
+16 62 skycell.2456
+24 62 skycell.2457
+32 62 skycell.2458
+40 62 skycell.2459
+48 62 skycell.2460
+56 62 skycell.2461
+64 62 skycell.2462
+72 62 skycell.2463
+80 62 skycell.2464
+88 62 skycell.2465
+96 62 skycell.2466
+104 62 skycell.2467
+112 62 skycell.2468
+120 62 skycell.2469
+128 62 skycell.2470
+136 62 skycell.2471
+144 62 skycell.2472
+152 62 skycell.2473
+160 62 skycell.2474
+168 62 skycell.2475
+176 62 skycell.2476
+184 62 skycell.2477
+192 62 skycell.2478
+200 62 skycell.2479
+208 62 skycell.2480
+216 62 skycell.2481
+224 62 skycell.2482
+232 62 skycell.2483
+240 62 skycell.2484
+248 62 skycell.2485
+256 62 skycell.2486
+264 62 skycell.2487
+272 62 skycell.2488
+280 62 skycell.2489
+288 62 skycell.2490
+296 62 skycell.2491
+304 62 skycell.2492
+312 62 skycell.2493
+320 62 skycell.2494
+328 62 skycell.2495
+336 62 skycell.2496
+344 62 skycell.2497
+352 62 skycell.2498
+0 66 skycell.2499
+9.23077 66 skycell.2500
+18.4615 66 skycell.2501
+27.6923 66 skycell.2502
+36.9231 66 skycell.2503
+46.1538 66 skycell.2504
+55.3846 66 skycell.2505
+64.6154 66 skycell.2506
+73.8462 66 skycell.2507
+83.0769 66 skycell.2508
+92.3077 66 skycell.2509
+101.538 66 skycell.2510
+110.769 66 skycell.2511
+120 66 skycell.2512
+129.231 66 skycell.2513
+138.462 66 skycell.2514
+147.692 66 skycell.2515
+156.923 66 skycell.2516
+166.154 66 skycell.2517
+175.385 66 skycell.2518
+184.615 66 skycell.2519
+193.846 66 skycell.2520
+203.077 66 skycell.2521
+212.308 66 skycell.2522
+221.538 66 skycell.2523
+230.769 66 skycell.2524
+240 66 skycell.2525
+249.231 66 skycell.2526
+258.462 66 skycell.2527
+267.692 66 skycell.2528
+276.923 66 skycell.2529
+286.154 66 skycell.2530
+295.385 66 skycell.2531
+304.615 66 skycell.2532
+313.846 66 skycell.2533
+323.077 66 skycell.2534
+332.308 66 skycell.2535
+341.538 66 skycell.2536
+350.769 66 skycell.2537
+0 70 skycell.2538
+10.9091 70 skycell.2539
+21.8182 70 skycell.2540
+32.7273 70 skycell.2541
+43.6364 70 skycell.2542
+54.5455 70 skycell.2543
+65.4545 70 skycell.2544
+76.3636 70 skycell.2545
+87.2727 70 skycell.2546
+98.1818 70 skycell.2547
+109.091 70 skycell.2548
+120 70 skycell.2549
+130.909 70 skycell.2550
+141.818 70 skycell.2551
+152.727 70 skycell.2552
+163.636 70 skycell.2553
+174.545 70 skycell.2554
+185.455 70 skycell.2555
+196.364 70 skycell.2556
+207.273 70 skycell.2557
+218.182 70 skycell.2558
+229.091 70 skycell.2559
+240 70 skycell.2560
+250.909 70 skycell.2561
+261.818 70 skycell.2562
+272.727 70 skycell.2563
+283.636 70 skycell.2564
+294.545 70 skycell.2565
+305.455 70 skycell.2566
+316.364 70 skycell.2567
+327.273 70 skycell.2568
+338.182 70 skycell.2569
+349.091 70 skycell.2570
+0 74 skycell.2571
+13.3333 74 skycell.2572
+26.6667 74 skycell.2573
+40 74 skycell.2574
+53.3333 74 skycell.2575
+66.6667 74 skycell.2576
+80 74 skycell.2577
+93.3333 74 skycell.2578
+106.667 74 skycell.2579
+120 74 skycell.2580
+133.333 74 skycell.2581
+146.667 74 skycell.2582
+160 74 skycell.2583
+173.333 74 skycell.2584
+186.667 74 skycell.2585
+200 74 skycell.2586
+213.333 74 skycell.2587
+226.667 74 skycell.2588
+240 74 skycell.2589
+253.333 74 skycell.2590
+266.667 74 skycell.2591
+280 74 skycell.2592
+293.333 74 skycell.2593
+306.667 74 skycell.2594
+320 74 skycell.2595
+333.333 74 skycell.2596
+346.667 74 skycell.2597
+0 78 skycell.2598
+17.1429 78 skycell.2599
+34.2857 78 skycell.2600
+51.4286 78 skycell.2601
+68.5714 78 skycell.2602
+85.7143 78 skycell.2603
+102.857 78 skycell.2604
+120 78 skycell.2605
+137.143 78 skycell.2606
+154.286 78 skycell.2607
+171.429 78 skycell.2608
+188.571 78 skycell.2609
+205.714 78 skycell.2610
+222.857 78 skycell.2611
+240 78 skycell.2612
+257.143 78 skycell.2613
+274.286 78 skycell.2614
+291.429 78 skycell.2615
+308.571 78 skycell.2616
+325.714 78 skycell.2617
+342.857 78 skycell.2618
+0 82 skycell.2619
+24 82 skycell.2620
+48 82 skycell.2621
+72 82 skycell.2622
+96 82 skycell.2623
+120 82 skycell.2624
+144 82 skycell.2625
+168 82 skycell.2626
+192 82 skycell.2627
+216 82 skycell.2628
+240 82 skycell.2629
+264 82 skycell.2630
+288 82 skycell.2631
+312 82 skycell.2632
+336 82 skycell.2633
+0 86 skycell.2634
+40 86 skycell.2635
+80 86 skycell.2636
+120 86 skycell.2637
+160 86 skycell.2638
+200 86 skycell.2639
+240 86 skycell.2640
+280 86 skycell.2641
+320 86 skycell.2642
+0 90 skycell.2643
Index: branches/meh_branches/ppstack_test/tools/czarcal
===================================================================
--- branches/meh_branches/ppstack_test/tools/czarcal	(revision 33415)
+++ branches/meh_branches/ppstack_test/tools/czarcal	(revision 33415)
@@ -0,0 +1,136 @@
+#!/usr/bin/env perl
+
+# create an iCalendar with IPP czar assignments for a given number of days
+
+use strict;
+use warnings;
+
+use Time::Local;
+use Time::localtime;
+use Time::gmtime;
+
+
+# number of days that an individual is assinged to be czar
+my $czar_days = 2;
+my $first_czar = shift;
+my $start_date = shift;
+my $num_assignments = shift;
+
+$num_assignments = 1 unless $num_assignments;
+
+
+# list of all czars
+my @staff = qw(
+mark
+serge
+roy
+chris
+gene
+heather
+bill
+);
+
+die "usage: $0 <first czar> <start_date> <num assignments>\n  czar list: @staff\n"
+    unless $start_date and $first_czar;
+
+my @list = get_czars($first_czar);
+die "failed to find $first_czar in @list\n" if !scalar @list;
+
+my $begin_vcalendar =
+"BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+VERSION:2.0
+X-WR-CALNAME:IPP Czar calendar for import
+METHOD:PUBLISH
+PRODID:-//IfA.hawaii.edu//czarcal 1.0//EN";
+
+my $end_vcalendar =
+"END:VCALENDAR
+";
+
+
+
+my $year = substr $start_date, 0, 4;
+my $month = substr $start_date, 4, 2;
+my $day = substr $start_date, 6, 2;
+
+my $tm = localtime(timelocal(0, 0, 0, $day, $month - 1, $year - 1900));
+my $time = timelocal(0, 0, 0, $day, $month - 1, $year - 1900);
+
+print "$begin_vcalendar\n";
+
+my $j = 0;
+my $this_czar = shift @list;
+for (my $i = 0; $i < $num_assignments; $i++) {
+    $tm = localtime($time);
+    if ($tm->wday != 6 and $tm->wday != 0) {
+
+        assign_czar($this_czar, $tm->year+1900, $tm->mon+1, $tm->mday);
+
+        if (++$j == $czar_days) {
+            if (!scalar @list) {
+                @list = get_czars();
+            }
+            $this_czar = shift @list;
+            $j = 0;
+        }
+    } else {
+        # bump counter to account for weekend day
+        $num_assignments++;
+    }
+    $time += 86400;
+}
+
+
+print "$end_vcalendar\n";
+
+sub get_czars {
+    my $czar = shift;
+
+    my @czarlist = @staff;
+    if ($czar) {
+        for (my $i = 0; $i < scalar @staff; $i++) {
+            my $c = $staff[$i];
+            last if $c eq $czar;
+            shift @czarlist;
+        }
+    }
+    return @czarlist;
+}
+
+my $num_events = 0;
+sub assign_czar {
+#    print STDERR "assign: @_\n";
+    my $czar = shift;
+    my $year = shift;
+    my $month = shift;
+    my $day = shift;
+
+    $num_events++;
+
+    my $start = sprintf "%4d%02d%02d", $year, $month, $day;
+    my $start_ticks = timelocal(0, 0, 0, $day, $month - 1, $year - 1900);
+    my $end_ticks = $start_ticks + 86400;
+
+    my $tm = localtime($end_ticks);
+    my $end = sprintf "%4d%02d%02d", $tm->year+1900, $tm->mon + 1, $tm->mday;
+
+    $tm = gmtime(time());
+
+    my $now = sprintf "%4d%02d%02dT%02d%02d%02dZ", $tm->year+1900, $tm->mon + 1, $tm->mday, $tm->hour, $tm->min, $tm->sec;
+
+    my $uid = "$now-$num_events". '@czarcal.ipp.ifa.hawaii.edu';
+
+    print "BEGIN:VEVENT\n";
+    print "CREATED:$now\n";
+    print "UID:$uid\n";
+    print "DTEND;VALUE=DATE:$end\n";
+    print "TRANSP:TRANSPARENT\n";
+    print "SUMMARY:czar $czar\n";
+    print "DTSTART;VALUE=DATE:$start\n";
+    print "DTSTAMP:$now\n";
+    print "SEQUENCE:0\n";
+    print "END:VEVENT\n";
+}
+
+
Index: branches/meh_branches/ppstack_test/tools/diskspace/generate_mappings.pl
===================================================================
--- branches/meh_branches/ppstack_test/tools/diskspace/generate_mappings.pl	(revision 33415)
+++ branches/meh_branches/ppstack_test/tools/diskspace/generate_mappings.pl	(revision 33415)
@@ -0,0 +1,38 @@
+#! /usr/local/bin/perl -w
+my $summary = shift;
+my $runs = shift;
+
+open(S,"$summary") || die "Cannot open summary $summary\n";
+while (<S>) {
+    chomp;
+    unless ($_ =~ /^#/) {
+        ($stage_product,$size_in_gb) = split /\s+/;
+        ($stage,$product) = split /_/, $stage_product;
+
+        $size{$stage}{$product} = $size_in_gb;
+    }
+}
+close(S);
+close(S);
+
+open(C,"$runs") || die "Cannot find runs $runs\n";
+while(<C>) {
+    chomp;
+    unless ($_ =~ /^#/) {
+        ($stage,$state,$N) = split /\s/;
+        if ($state eq '') {
+            $state = 'NULL';
+        }
+        $count{$stage}{$state} = $N;
+    }
+}
+close(C);
+
+foreach $stage (keys (%size)) {
+    foreach $product (keys (%{ $size{$stage} })) {
+        foreach $state (keys (%{ $count{$stage} })) {
+	    print "$stage $product $state\n";
+	}
+    }
+}
+
Index: branches/meh_branches/ppstack_test/tools/diskspace/merge_counts_and_sizes.pl
===================================================================
--- branches/meh_branches/ppstack_test/tools/diskspace/merge_counts_and_sizes.pl	(revision 33415)
+++ branches/meh_branches/ppstack_test/tools/diskspace/merge_counts_and_sizes.pl	(revision 33415)
@@ -0,0 +1,208 @@
+#! /usr/local/bin/perl -w
+
+use Getopt::Std;
+use DateTime;
+use PS::IPP::Config;
+
+getopts('ND:M:',\%opt);
+
+my $ipprc = PS::IPP::Config->new("GPC1");
+my $nebcon = $ipprc->nebulous();
+
+
+my $datestring;
+if (exists($opt{D})) {
+    $datestring = $opt{D};
+}
+else {
+    my @lt = gmtime;
+    my $dt = DateTime->now;
+    $dt->subtract( days => $lt[6] );
+    $datestring = $dt->ymd;
+}
+
+my $summary_file = "neb:///ipp_diskspace/${datestring}/summary.dat";
+my $S_real_file = $ipprc->file_resolve($summary_file);
+open(S,$S_real_file) || die "Cannot find summary.dat\n";
+
+while (<S>) {
+    chomp;
+    unless ($_ =~ /^#/) {
+	($stage_product,$size_in_gb,$count) = split /\s+/;
+	($stage,$product) = split /_/, $stage_product;
+
+	$size{$stage}{$product} = $size_in_gb;
+	$stage_min{$stage} += $size_in_gb;
+	$diskcount{$stage}{$product} = $count;
+	$total += $size_in_gb;
+    }
+}
+close(S);
+
+$diskcount{"EXTERNAL"}{"UNKNOWN"} = 1;
+
+my $count_file = "neb:///ipp_diskspace/${datestring}/run_im_counts.dat";
+my $C_real_file = $ipprc->file_resolve($count_file);
+open(C,$C_real_file) || die "Cannot find run_counts.dat\n";
+while(<C>) {
+    chomp;
+    unless ($_ =~ /^#/) {
+	($stage,$state,$N) = split /\s/;
+	if ($state eq '') {
+	    $state = 'NULL';
+	}
+	$count{$stage}{$state} = $N;
+    }
+}
+close(C);
+
+my $mappings_file = "neb:///ipp_diskspace/mappings_im.dat";
+my $M_real_file = $ipprc->file_resolve($mappings_file);
+if (exists($opt{M})) {
+    $M_real_file = $opt{M};
+}
+open(M,$M_real_file) || die "Cannot find mappings.dat\n";
+while (<M>) {
+    chomp;
+    unless ($_ =~ /^#/) {
+	($stage,$product,$state,$mult) = split /\s+/;
+	if (defined($mult)&&($mult !~ /^$/)) {
+	    $map{$stage}{$product}{$state} = $mult;
+	}
+	else {
+	    $map{$stage}{$product}{$state} = 1;
+	    if ($stage eq 'RAW' && $product eq 'FITS') {
+		if (($state eq 'full')||($state eq 'pending_burntool')||($state eq 'keep')||($state eq 'compressed')||($state eq 'error_compressed')||
+		    ($state eq 'goto_lossy')||($state eq 'check_burntool')) {
+		    $map{$stage}{$product}{$state} = 2;
+		}
+	    }
+	}
+    }
+}
+close(M);
+
+foreach $stage (keys (%size)) {
+    foreach $state (keys (%{ $count{$stage} })) {
+	foreach $product (keys (%{ $size{$stage} })) {
+	    unless (exists($map{$stage}{$product}{$state})) {
+		$map{$stage}{$product}{$state} = 0;
+	    }
+	}
+    }
+}
+
+foreach $stage (keys (%size)) {
+    foreach $state (keys (%{ $count{$stage} })) {
+	$VpN{$stage}{$state} = 0;
+	foreach $product (keys (%{ $size{$stage} })) {
+	    $N{$stage}{$product} += $count{$stage}{$state}; # $map{$stage}{$product}{$state} * $count{$stage}{$state};
+	}		
+    }
+    foreach $product (keys (%{ $size{$stage} })) {
+	foreach $state (keys (%{ $count{$stage} })) {
+	    if ($N{$stage}{$product} > 0) {
+		$VpN{$stage}{$state} += $size{$stage}{$product} / $N{$stage}{$product};#$map{$stage}{$product}{$state} * $size{$stage}{$product} / $N{$stage}{$product};
+		$DVpN{$stage}{$state} += $map{$stage}{$product}{$state} * $size{$stage}{$product} / $diskcount{$stage}{$product};
+	    }
+#	    print STDERR ">>$stage $product $state $map{$stage}{$product}{$state} $N{$stage}{$product} $count{$stage}{$state} $size{$stage}{$product} $VpN{$stage}{$state}\n";	    
+	}
+    }
+}
+
+my $out_fh;
+
+if (exists($opt{N})) {
+#     my $dt = DateTime->now;
+
+#     my @lt = gmtime;
+#     my $dt_data = DateTime->now;
+#     $dt->subtract( days => $lt[6]);
+#     my $datestring = $dt->ymd;
+
+    my $ipprc = PS::IPP::Config->new("GPC1");
+    my $nebcon = $ipprc->nebulous();
+
+    my $outfile = "neb:///ipp_diskspace/${datestring}/usage_report.dat";
+
+    if ($ipprc->file_exists($outfile)) {
+        my $U_file = $ipprc->file_resolve($outfile);
+        open($out_fh,">$U_file");
+    }
+    else {
+        $out_fh = $ipprc->file_create_open($outfile);
+    }
+}
+
+foreach $stage (sort (keys %VpN)) {
+    $measuredA = 0;
+    $measuredB = 0;
+    foreach $state (sort (keys %{ $VpN{$stage} })) {
+	unless(exists($count{$stage}{$state})) {
+	    $count{$stage}{$state} = 0;
+	}
+	$V = $VpN{$stage}{$state} * $count{$stage}{$state};
+
+	if ($stage eq 'RAW') {
+	    $label = "PERMANENT";
+	}
+	elsif ($stage eq 'EXTERNAL') {
+	    $label = 'EXTERNAL';
+	}
+	else {
+	    if (($state eq 'goto_cleaned')||
+		($state eq 'goto_purged')||
+		($state eq 'goto_scrubbed')||
+		($state eq 'update')) {
+		$label = "TRANSIENT";
+	    }
+	    elsif (($state eq 'full')&&(
+		       ($stage eq 'CHIP')||
+		       ($stage eq 'WARP')||
+		       ($stage eq 'DIFF')||
+		       ($stage eq 'DIST')||
+		       ($stage eq 'DESTREAK')
+		   )) {
+		$label = 'TRANSIENT';
+	    }
+	    else {
+		$label = 'PRODUCT';
+	    }
+	}
+	$labels{$label} += $V;
+	$alt_labels{$label} += $DVpN{$stage}{$state} * $count{$stage}{$state};
+	$measuredA += $V;
+	$measuredB += $DVpN{$stage}{$state} * $count{$stage}{$state};
+	printf("%-8s %-18s %-10s\t%14.4f %12.6f\t%14.6f %12.6f\t%9d\n",
+		$stage,$state,$label,$V,$VpN{$stage}{$state},$DVpN{$stage}{$state} * $count{$stage}{$state},$DVpN{$stage}{$state},$count{$stage}{$state});
+	if (exists($opt{N})) {
+	    printf($out_fh "%-8s %-18s %-10s\t%14.4f %12.6f\t%14.6f %12.6f\t%9d\n",
+		   $stage,$state,$label,$V,$VpN{$stage}{$state},$DVpN{$stage}{$state} * $count{$stage}{$state},$DVpN{$stage}{$state},$count{$stage}{$state});
+	}
+    }
+    printf("%-8s %-18s %-10s\t%14.4f %-12s\t%14.6f\n",
+	   $stage,"","MINIMUM",$stage_min{$stage},"",$stage_min{$stage});
+    printf("%-8s %-18s %-10s\t%14.4f %-12s\t%14.6f\n",
+	   $stage,"","MEASURED",$measuredA,"",$measuredB);
+    if (exists($opt{N})) {
+	printf($out_fh "%-8s %-18s %-10s\t%14.4f %-12s\t%14.6f\n",
+	       $stage,"","MINIMUM",$stage_min{$stage},"",$stage_min{$stage});
+    }
+}
+$sum = 0;
+foreach $l (keys %labels) {
+    $sum += $labels{$l};
+    $alt_sum += $alt_labels{$l};
+    print STDERR "\t$l  \t$labels{$l}\t$alt_labels{$l}\n";
+    if (exists($opt{N})) {
+	print $out_fh "\t$l  \t$labels{$l}\t$alt_labels{$l}\n";
+    }
+}
+print STDERR "\tTOTAL    \t$sum\t$alt_sum\n";
+print STDERR "\tEXP_TOT  \t$total\n";
+
+if (exists($opt{N})) {
+    print $out_fh "\tTOTAL    \t$sum\t$alt_sum\n";
+    print $out_fh "\tEXP_TOT  \t$total\n";
+    close($out_fh);
+}
Index: branches/meh_branches/ppstack_test/tools/diskspace/nebulous_data.pl
===================================================================
--- branches/meh_branches/ppstack_test/tools/diskspace/nebulous_data.pl	(revision 33415)
+++ branches/meh_branches/ppstack_test/tools/diskspace/nebulous_data.pl	(revision 33415)
@@ -0,0 +1,122 @@
+#! /usr/bin/env perl
+use warnings;
+
+use Sys::Hostname;
+use PS::IPP::Config;
+%stages = ("RAW" => '\.ota',
+	   "DIST" => 'dist',
+	   "DESTREAK" => 'destreak',
+	   "CHIP" => '\.ch\.',
+	   "CAMERA" => '\.cm\.',
+	   "WARP" => '\.wrp\.',
+	   "DIFF" => '\.dif\.',
+	   "STACK" => '\.stk\.',
+	   "MAGIC" => '\.mgc\.',
+	   "DETREND" => '\.det'
+    );
+
+@products = ('\.log','\.trace','\.mdc$','\.stats$',
+	     '\.smf$','\.cmf$',
+	     '\.burn\.tbl$','\.tgz$',
+	     '\.b1\.fits$','\.b2\.fits',
+	     '\.b1\.jpg$','\.b2\.jpg$',
+	     '\.b1\.jpeg$','\.b2\.jpeg$',
+	     '\.png$',
+	     '\.mdl\.fits$','\.ptn$','\.psf$',
+	     '\.skycell$','kernel$',
+	     '\.mk\.fits$','\.mask\.fits$','\.wt\.fits$',
+	     '\.exp\.fits$','\.num\.fits$','\.expwt\.fits$',
+	     '\.fits\.fz$',
+	     '\.fits$');
+
+%product_names = ('\.log' => "LOG",'\.trace' => "TRACE",'\.mdc$' => "MDC",'\.stats$' => "STATS",
+		  '\.smf$' => "CATALOG",'\.cmf$' => "CATALOG",
+		  '\.burn\.tbl$' => "BTTABLE",'\.tgz$' => "BUNDLE",
+		  '\.b1\.fits$' => "B1FITS",'\.b2\.fits' => "B2FITS",
+		  '\.b1\.jpg$' => "B1JPG",'\.b2\.jpg$' => "B2JPG",
+		  '\.b1\.jpeg$' => "B1JPG",'\.b2\.jpeg$' => "B2JPG",
+		  '\.png$' => "PNG",
+		  '\.mdl\.fits$' => "MDL",'\.ptn$' => "PTN" ,'\.psf$' => "PSF",
+		  '\.skycell$' => "SKYCELL",'kernel$' => "KERNEL",
+		  '\.mk\.fits$' => "MASK",'\.mask\.fits$' => "MASK",'\.wt\.fits$' => "WEIGHT",
+		  '\.exp\.fits$' => "MISCFITS",'\.num\.fits$' => "MISCFITS",'\.expwt\.fits$' => "MISCFITS",
+		  '\.fits\.fz$' => "FITSCOMP",
+		  '\.fits$' => "FITS");
+
+my $hostname = hostname();
+my $neb = shift(@ARGV);
+unless(defined($neb)) {
+    $neb = "${hostname}.0";
+}
+my $ipprc = PS::IPP::Config->new("GPC1");
+my $nebcon = $ipprc->nebulous();
+
+my @lt = gmtime;
+$dt = DateTime->now;
+$dt->subtract( days => $lt[6]);
+my $datestring = $dt->ymd;
+my $outfile = "neb:///ipp_diskspace/${datestring}/${neb}.neb_usage.dat";
+my $out_fh = $ipprc->file_create_open($outfile);
+
+my $neb_dir = "/data/${neb}/nebulous/";
+
+my $total_volume = 0;
+my $used_volume  = 0;
+open(DF,"df -B 1 /export/${neb}/ |") || die "Cannot run df\n";
+while(<DF>) {
+    chomp;
+    if ($_ =~ /${neb}/) {
+	$used_volume = (split /\s+/, $_)[2];
+    }
+}
+close(DF);
+
+open(FIND,"find $neb_dir -type f -printf \"%f %s\n\" |") || die "Cannot run find.\n";
+
+
+while (<FIND>) {
+    chomp;
+    ($file,$size) = split /\s+/;
+    
+    my $stage = 'OTHER';
+    my $product = 'UNKNOWN';
+
+    foreach $k (keys %stages) {
+	if ($file =~ /$stages{$k}/) {
+	    $stage = $k;
+	    last;
+	}
+    }
+    foreach $k (@products) {
+	if ($file =~ /$k/) {
+	    $product = $product_names{$k};
+	    unless (defined($product)) {
+		print "$k $product\n";
+		die;
+	    }
+	    last;
+	}
+    }
+    
+    $count{$stage}{$product} += 1;
+    $volume{$stage}{$product} += $size;
+    $total_volume += $size;
+}
+close(FIND);
+
+
+
+
+foreach $s (sort (keys %count)) {
+    foreach $p (sort (keys %{ $count{$s} })) {
+	print $out_fh "$s $p $count{$s}{$p} $volume{$s}{$p}\n";
+    }
+}
+if ($used_volume > 0) {
+    my $external_volume = $used_volume - $total_volume;
+    print $out_fh "EXTERNAL UNKNOWN 1 $external_volume\n";
+}
+close($out_fh);
+
+
+	
Index: branches/meh_branches/ppstack_test/tools/diskspace/nebulous_wiki.pl
===================================================================
--- branches/meh_branches/ppstack_test/tools/diskspace/nebulous_wiki.pl	(revision 33415)
+++ branches/meh_branches/ppstack_test/tools/diskspace/nebulous_wiki.pl	(revision 33415)
@@ -0,0 +1,103 @@
+#! /usr/local/bin/perl -w
+
+use DateTime;
+use Getopt::Std;
+use PS::IPP::Config;
+
+
+$opt{S} = 0;
+getopts('SD:',\%opt);
+
+my $datestring;
+if (exists($opt{D})) {
+    $datestring = $opt{D};
+}
+else {
+    my @lt = gmtime;
+    my $dt = DateTime->now;
+    $dt->subtract( days => $lt[6] );
+    $datestring = $dt->ymd;
+}
+
+@files = sort ( split /\n/, `neb-ls -p neb:///ipp_diskspace/${datestring}/%neb_usage.dat`);
+
+foreach $f (@files) {
+    $volume = $f;
+    $volume =~ s/.*://;
+    $volume =~ s/.neb_usage.dat//;
+    open(F,$f);
+    while(<F>) {
+	chomp;
+	($stage,$product,$count,$size) = split /\s+/;
+	$what = "${stage}_${product}";
+	$num{$volume}{$what} = $count;
+	$vol{$volume}{$what} = $size;
+	
+	$sum{$volume} += $size;
+	$sizes{$what}   += $size;
+	$counts{$what}  += $count;
+    }
+    close(F);
+}
+foreach $v (keys %sum) {
+    foreach $w (keys %sizes) {
+	unless (exists($vol{$v}{$w})) {
+	    $num{$v}{$w} = 0;
+	}
+	unless (exists($vol{$v}{$w})) {
+	    $vol{$v}{$w} = 0;
+	}
+    }
+}
+
+$sep    = '||';
+$presep = '||';
+
+if ($opt{S} == 0) {
+    print "${presep}Volume${sep}";
+    foreach $w (sort (keys %sizes)) {
+	print "$w$sep";
+    }
+    print "\n";
+    foreach $v (sort (keys %sum)) {
+	print "${presep}${v}${sep}";
+	foreach $w (sort (keys %sizes)) {
+	    $val = $vol{$v}{$w} / 1024 / 1024 / 1024;
+	    printf "%.2f%s",$val,$sep;
+	}
+	$val = $sum{$v} / 1024 / 1024 / 1024;
+	printf "%.2f%s",$val,$sep;
+	print "\n";
+    }
+    my $dt = DateTime->now;
+    $date = $dt->ymd;
+    print "${presep}${date}${sep}";
+    foreach $w (sort (keys %sizes)) {
+	$val = $sizes{$w} / 1024 / 1024 / 1024;
+	printf "%.2f%s",$val,$sep;
+    }
+    print "\n";
+}
+else {
+    my $ipprc = PS::IPP::Config->new("GPC1");
+    my $nebcon = $ipprc->nebulous();
+
+    my $outfile = "neb:///ipp_diskspace/${datestring}/summary.dat";
+    my $out_fh;
+    if ($ipprc->file_exists($outfile)) {
+	my $S_file = $ipprc->file_resolve($outfile);
+	open($out_fh,">$S_file");
+    }
+    else {
+	$out_fh = $ipprc->file_create_open($outfile);
+    }
+    foreach $w (sort (keys %sizes)) {
+	$val = $sizes{$w} / 1024 / 1024 / 1024;
+	$v2  = $counts{$w};
+	print $out_fh "$w $val $v2\n";
+    }
+    close($out_fh);
+}
+
+
+
Index: branches/meh_branches/ppstack_test/tools/diskspace/run_counter.pl
===================================================================
--- branches/meh_branches/ppstack_test/tools/diskspace/run_counter.pl	(revision 33415)
+++ branches/meh_branches/ppstack_test/tools/diskspace/run_counter.pl	(revision 33415)
@@ -0,0 +1,81 @@
+#! /usr/bin/env perl
+use warnings;
+
+use DateTime;
+use Sys::Hostname;
+use PS::IPP::Config;
+use DBI;
+
+# Set up the database
+use constant DB_SOCKET => '/var/run/mysqld/mysqld.sock';
+$dbname = 'gpc1';
+$dbserver = 'ippdb01';
+$dbuser = 'ippuser';
+$dbpass = 'ippuser';
+$db = DBI->connect("DBI:mysql:database=${dbname};host=${dbserver};" .
+                   "mysql_socket=" . DB_SOCKET(),
+                   ${dbuser},${dbpass}, 
+		   { RaiseError => 1, AutoCommit => 1}
+    ) or die "Unable to connect to database $DBI::errstr\n";
+
+
+%stages = ("RAW" => 'rawExp',
+	   "CHIP" => 'chipRun',
+	   'CAMERA' => 'camRun',
+	   'WARP' => 'warpRun',
+	   'DIFF' => 'diffRun',
+	   'STACK' => 'stackRun',
+	   'DIST' => 'distRun',
+	   'DESTREAK' => 'magicDSRun',
+	   'MAGIC' => 'magicRun',
+	   'DETREND' => 'detRun');
+
+$base_query = 'select state,count(state) AS N from TABLETABLE GROUP BY state';
+
+
+%im_stages = ("RAW" => 'rawImfile',
+	      "CHIP" => 'chipProcessedImfile',
+	      "WARP" => 'warpSkyfile',
+	      "DIFF" => 'diffSkyfile'
+    );
+
+
+$im_query = 'select data_state,count(data_state) AS N from TABLETABLE GROUP BY data_state';
+
+foreach $k (keys %im_stages) {
+    $stages{$k} = $im_stages{$k};
+}
+
+my $ipprc = PS::IPP::Config->new("GPC1");
+my $nebcon = $ipprc->nebulous();
+
+my @lt = gmtime;
+$dt = DateTime->now;
+$dt->subtract( days => $lt[6]);
+my $datestring = $dt->ymd;
+my $outfile = "neb:///ipp_diskspace/${datestring}/run_im_counts.dat";
+my $out_fh;
+if ($ipprc->file_exists($outfile)) {
+    my $U_file = $ipprc->file_resolve($outfile);
+    open($out_fh,">$U_file");
+}
+else {
+    $out_fh = $ipprc->file_create_open($outfile);
+}
+
+foreach $s (keys %stages) {
+    my $query = $base_query;
+    if (exists($im_stages{$s})) {
+	$query = $im_query;
+    }
+    $query =~ s/TABLETABLE/$stages{$s}/;
+    print STDERR "$s $query\n";
+    my $dr = $db->selectall_arrayref( $query );
+    foreach $rr (@{ $dr }) {
+	my ($state,$count) = @{ $rr };
+	print $out_fh "$s $state $count\n";
+    }
+}
+print $out_fh "EXTERNAL any 1\n";
+close($out_fh);
+
Index: branches/meh_branches/ppstack_test/tools/diskspace/sizes_from_counts.pl
===================================================================
--- branches/meh_branches/ppstack_test/tools/diskspace/sizes_from_counts.pl	(revision 33415)
+++ branches/meh_branches/ppstack_test/tools/diskspace/sizes_from_counts.pl	(revision 33415)
@@ -0,0 +1,132 @@
+#! /usr/local/bin/perl -w
+
+
+use Getopt::Std;
+use DateTime;
+use PS::IPP::Config;
+
+getopts('D:',\%opt);
+
+my $count_list = shift;
+my $ipprc = PS::IPP::Config->new("GPC1");
+my $nebcon = $ipprc->nebulous();
+
+
+if (exists($opt{D})) {
+    my $datestring = $opt{D};
+    my $usage_data = "neb:///ipp_diskspace/${datestring}/usage_report.dat";
+    my $real_file  = $ipprc->file_resolve($usage_data);
+    open(DATA,$real_file) || die "Cannot find usage_report.dat\n";
+}
+
+while(<DATA>) {
+    chomp;
+    if ($_ =~ /^\s+/) {
+	next;
+    }
+    ($stage,$state,$label,undef,$rate,$count) = split /\s+/;
+    $data{$stage}{$state}{L} = $label;
+    $data{$stage}{$state}{R} = $rate;
+    $cumulative{$label} = 0;
+}
+$total = 0;
+open(D,"$count_list");
+while(<D>) {
+    chomp;
+    ($stage,$state,$count) = split /\s+/;
+    unless(exists($data{$stage}{$state})) {
+	next;
+    }
+    $label = $data{$stage}{$state}{L};
+    $VpN   = $data{$stage}{$state}{R};
+    $V = $count * $VpN;
+    printf("%-8s %-18s %-10s %14.4f %12.6f %9d\n",
+	   $stage,$state,$label,$V,$VpN,$count);
+    $cumulative{$label} += $V;
+    $total += $V;
+}
+foreach $l (keys %cumulative) {
+    print STDERR "\t$l  \t$cumulative{$l}\n";
+}
+print STDERR "\tSUM      \t$total\n";
+
+    
+	
+
+
+__DATA__
+CAMERA   cleaned            PRODUCT          296.4215     0.171540      1728
+CAMERA   drop               PRODUCT            0.1375     0.000671       205
+CAMERA   full               PRODUCT        96885.7234     0.388503    249382
+CAMERA   goto_purged        TRANSIENT         54.3905     0.388503       140
+CAMERA   new                PRODUCT            0.7720     0.000671      1151
+CAMERA   purged             PRODUCT            0.1715     0.171540         1
+CAMERA   wait               PRODUCT            0.7036     0.000671      1049
+CHIP     cleaned            PRODUCT        26777.0553     0.001840  14549732
+CHIP     error_cleaned      PRODUCT         2163.6842     0.105432     20522
+CHIP     error_scrubbed     PRODUCT          232.8603     0.039798      5851
+CHIP     full               TRANSIENT      39127.3368     0.119770    326686
+CHIP     purged             PRODUCT          514.9939     0.001840    279830
+CHIP     scrubbed           PRODUCT           11.0294     0.001840      5993
+CHIP     update             TRANSIENT         17.0801     0.105432       162
+DESTREAK censored           PRODUCT            0.6473     0.029424        22
+DESTREAK cleaned            PRODUCT           36.6759     0.000076    482521
+DESTREAK drop               PRODUCT            0.0000     0.000000      1045
+DESTREAK error_cleaned      PRODUCT            7.9444     0.029424       270
+DESTREAK failed_revert      PRODUCT            0.0000     0.000000         1
+DESTREAK full               TRANSIENT       4597.8025     0.029424    156261
+DESTREAK goto_cleaned       TRANSIENT          9.7099     0.029424       330
+DESTREAK new                PRODUCT            0.0000     0.000000        18
+DESTREAK new.hold           PRODUCT            0.0000     0.000000        10
+DESTREAK restored           PRODUCT            4.2959     0.029424       146
+DESTREAK todebug            PRODUCT            0.0294     0.029424         1
+DESTREAK update             TRANSIENT        351.6446     0.029424     11951
+DESTREAK wait               PRODUCT            0.0000     0.000000         4
+DETREND  drop               PRODUCT           18.5816     0.120660       154
+DETREND  register           PRODUCT           89.4761    44.738033         2
+DETREND  stop               PRODUCT        15692.5025    49.976122       314
+DETREND  wait               PRODUCT           38.3698     0.120660       318
+DIFF     cleaned            PRODUCT        20727.1251     0.002301   9009076
+DIFF     full               TRANSIENT      48536.1541     0.147039    330091
+DIFF     purged             PRODUCT          362.3386     0.002301    157491
+DIFF     update             TRANSIENT        159.2223     0.125967      1264
+DIST     cleaned            PRODUCT           12.6523     0.000027    471189
+DIST     drop               PRODUCT            0.0000     0.000000      9070
+DIST     full               TRANSIENT      10451.6987     0.052522    198998
+DIST     goto_cleaned       TRANSIENT         27.8890     0.052522       531
+DIST     new                PRODUCT            0.0000     0.000000         2
+MAGIC    cancel             PRODUCT            0.0040     0.004025         1
+MAGIC    censored           PRODUCT            0.3139     0.004025        78
+MAGIC    drop               PRODUCT            0.0000     0.000000       438
+MAGIC    full               PRODUCT          771.2421     0.004025    191634
+MAGIC    new                PRODUCT            0.0000     0.000000       297
+MAGIC    wait               PRODUCT            0.0000     0.000000         9
+RAW      NULL               PERMANENT          0.0000     0.000394         0
+RAW      check_burntool     PERMANENT         19.4277     0.043269       449
+RAW      compressed         PERMANENT        720.8267     0.048541     14850
+RAW      corrupt            PERMANENT          0.0174     0.000471        37
+RAW      error_compressed   PERMANENT        292.1491     0.043346      6740
+RAW      error_lossy        PERMANENT        120.6904     0.027103      4453
+RAW      full               PERMANENT     607420.2114     0.043346  14013433
+RAW      goto_lossy         PERMANENT        130.8652     0.048541      2696
+RAW      keep               PERMANENT      24975.4561     0.043346    576194
+RAW      lossy              PERMANENT      24720.0428     0.005666   4363026
+RAW      missing            PERMANENT          0.0099     0.000394        25
+RAW      pending_burntool   PERMANENT       9220.6406     0.043269    213101
+RAW      roc                PERMANENT      73422.4326     0.021908   3351366
+STACK    cleaned            PRODUCT           86.8525     0.003704     23447
+STACK    drop               PRODUCT            0.0000     0.000000      8862
+STACK    error_cleaned      PRODUCT          914.9882     0.164241      5571
+STACK    full               PRODUCT        53913.0776     0.175869    306553
+STACK    goto_cleaned       TRANSIENT         14.7747     0.175889        84
+STACK    goto_purged        TRANSIENT         26.9356     0.164241       164
+STACK    new                PRODUCT            0.0000     0.000000     12917
+STACK    purged             PRODUCT           18.2914     0.003704      4938
+STACK    wait               PRODUCT            0.0000     0.000000       120
+WARP     cleaned            PRODUCT         6547.3188     0.000306  21418513
+WARP     drop               PRODUCT            0.0000     0.000000         1
+WARP     error_cleaned      PRODUCT         2210.2057     0.066446     33263
+WARP     error_scrubbed     PRODUCT            4.4519     0.066446        67
+WARP     full               TRANSIENT      36791.1577     0.066446    553697
+WARP     purged             PRODUCT           18.1011     0.000306     59215
+WARP     scrubbed           PRODUCT           11.6047     0.000306     37963
Index: branches/meh_branches/ppstack_test/tools/dlapstacks
===================================================================
--- branches/meh_branches/ppstack_test/tools/dlapstacks	(revision 33415)
+++ branches/meh_branches/ppstack_test/tools/dlapstacks	(revision 33415)
@@ -0,0 +1,174 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+# given an RA and DEC lookup find the LAP stack images
+
+# XXX: consider making these options these could be made options I guess
+
+my $tess_id = 'RINGS.V3';
+my $label = 'LAP.ThreePi.20110809';
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+use DBI;
+use PS::IPP::Config 1.01 qw( :standard );
+
+my $ipprc =  PS::IPP::Config->new();
+my $dbh = getDBHandle();
+
+
+my ($requested_filter, $no_display, $skycell_id, $verbose, $ra, $dec, $convolved);
+
+
+GetOptions(
+    'filter|f=s'    => \$requested_filter,
+    'convolved'     => \$convolved,
+    'no-display|n'  => \$no_display,
+    'skycell_id=s'  => \$skycell_id,
+    'verbose|v'     => \$verbose,
+) or pod2usage( 2 );
+
+pod2usage( -msg => '<ra> and <dec> or --skycell_id is required', -exitval => 2)
+    unless defined $skycell_id or scalar @ARGV == 2;
+
+$ra = shift;
+$dec = shift;
+
+my $extension;
+if ($convolved) {
+    $extension = 'fits';
+} else {
+    $extension = 'unconv.fits';
+}
+
+# run whichimage to find a list of skycells containing the given coordinates
+my $whichimage_output = `whichimage --listchipcoords --tess_id $tess_id $ra $dec`;
+
+print $whichimage_output if $verbose;
+
+my @results = split "\n", $whichimage_output;
+
+die "whichimage returned no output\n" if scalar @results == 0;
+
+my $query = "SELECT skycell_id, filter, stack_id, quality, path_base
+    FROM stackRun join stackSumSkyfile USING(stack_id) 
+    WHERE stackRun.state ='full' AND label = '$label' AND skycell_id = ?";
+
+if ($requested_filter) {
+    $requested_filter .= '%' if length($requested_filter) == 1;
+}
+$query .= " AND filter LIKE '$requested_filter'" if defined $requested_filter;
+
+# This will "sort of" put the sets of images for skycells that were processed multiple times
+# in temporal order
+$query .= "ORDER BY stack_id";
+
+my $stmt = $dbh->prepare($query) or die $dbh->errstr;
+
+# @files will contain the list of skycells found in order of skycell_id, filter.
+# These will be displayed in this order so that the image frames are in order of increasing bandpass
+my @files = ();
+
+# give each filter an index value
+my %index = ( 'g.00000' => 0, 'r.00000' => 1, 'i.00000', => 2, 'z.00000' => 3, 'y.00000' =>4 );
+my $num_filters = scalar keys %index;
+
+my $total_files = 0;
+my $skycells = 0;
+# loop over the list of skycells returned by whichimage
+foreach (split "\n", $whichimage_output) {
+    chomp;
+    my ($radeg, $decdeg, undef, $skycell, $x, $y) = split " ";
+    print "$skycell $radeg $decdeg\n" if $verbose;
+    $stmt->execute($skycell) or die $stmt->errstr;
+    my $files_found = 0;
+    while (my $f = $stmt->fetchrow_hashref()) {
+        next if $f->{quality};
+        my $filter = $f->{filter};
+        my $stack_id = $f->{stack_id};
+        my $path_base = $f->{path_base};
+        # save the skycell coordinates so we can pan efficiently below
+        $f->{X} = $x;
+        $f->{Y} = $y;
+
+        # Find where this filter goes in the array
+        my $i = $index{$filter};
+        if (!defined $i) {
+            print STDERR "stack_id $stack_id has unknown filter $filter! Skipping\n";
+            next;
+        }
+        # if multiple skycells match the coordinates bump the index
+        $i += $skycells * $num_filters;
+        $files[$i] = $f;
+        $files_found++;
+    }
+    if ($files_found) {
+        $total_files += $files_found;
+        $skycells++;
+    }
+}
+
+if (!$total_files) {
+    print "no files found\n";
+    exit 1;
+}
+
+
+if (!$no_display) {
+    # estimate of image size for the case where we have 5 frames
+    my $imagesize = 680;
+    # my $panargs = "-pan to $ra $dec wcs fk5";
+    my @args;
+    foreach my $f (@files) {
+        next if ! $f;
+        my $filename = $f->{path_base} . ".$extension";
+        my $resolved = $ipprc->file_resolve($filename);
+        next if !$resolved;
+        my $panargs = "-pan to $f->{X} $f->{Y}";
+
+        my $xlabel = $f->{X} + $imagesize / 2;
+        my $ylabel = $f->{Y} + $imagesize / 2;
+        # The following is an attempt put the filter name in the image using a region with text a label.
+        # I couldn't make this work. The circle would appear but without the text
+        # my $label = " -regions command  'circle($xlabel,$ylabel,1)' # text={ $f->{filter} }";
+
+        # This didn't work either. Generates an X Errror "RenderBadPicture (invalid Picture parameter)"
+        # we could easily create a temporary region file and load that with -regions filename
+        my $label = " -regions command  text $xlabel $ylabel {$f->{filter}}";
+        # $panargs .= $label;
+
+        push @args, ($resolved, $panargs);
+    }
+    my $ds9cmd = "ds9 -geom 1024x1024 @args";
+    print "$ds9cmd\n" if $verbose;
+    system $ds9cmd;
+} else {
+    foreach my $f (@files) {
+        next if !$f;
+        print "$f->{skycell_id} $f->{stack_id} $f->{filter} $f->{path_base}\n";
+    }
+}
+
+exit 0;
+
+#############################################################################
+
+sub getDBHandle {
+    my $dbuser = metadataLookupStr($ipprc->{_siteConfig}, "DBUSER");
+    my $dbpassword = metadataLookupStr($ipprc->{_siteConfig}, "DBPASSWORD");
+    my $dbserver = metadataLookupStr($ipprc->{_siteConfig}, "DBSERVER");
+    my $dbname = 'gpc1';
+
+    die "database configuration set up" unless defined($dbserver) and defined($dbuser)
+        and defined($dbpassword) and defined($dbname);
+
+    my $dsn = "DBI:mysql:host=$dbserver;database=$dbname";
+
+    my $dbh = DBI->connect($dsn, $dbuser, $dbpassword) 
+        or die "Cannot connect to database.\n";
+
+    return $dbh;
+}
+
Index: branches/meh_branches/ppstack_test/tools/makedistdest
===================================================================
--- branches/meh_branches/ppstack_test/tools/makedistdest	(revision 32161)
+++ branches/meh_branches/ppstack_test/tools/makedistdest	(revision 33415)
@@ -36,5 +36,5 @@
 $cmd = "disttool -dbname gpc1 -definedestination -ds_dbname ippRequestServer -ds_dbhost $dbhost -name $prod_name";
 
-$rc = system $cmd;
+# $rc = system $cmd;
 die "disttool failed: $rc" if $rc;
 
Index: branches/meh_branches/ppstack_test/tools/mysql-dump/ops_dump.csh
===================================================================
--- branches/meh_branches/ppstack_test/tools/mysql-dump/ops_dump.csh	(revision 32161)
+++ branches/meh_branches/ppstack_test/tools/mysql-dump/ops_dump.csh	(revision 33415)
@@ -33,7 +33,17 @@
 set DATEVAR=`date +%FT%T`                                    #date
 set FILENAME = $DUMP_PATH/mysql-isp-ipp001-$DATEVAR.dump.bz
-mysqldump --compress -hipp001 -uipp -pipp --flush-logs --single-transaction isp | bzip2 > $FILENAME
+mysqldump --compress -hippdb01 -uipp -pipp --flush-logs --single-transaction isp | bzip2 > $FILENAME
 if ($status != 0) then
    /bin/mail -s "Warning isp dump failed" $EMAILTO < msg  
+endif
+
+################################## ssp ##############################################
+# dump ssp from ippdb01
+set DUMP_PATH = /data/ipp001.0/ipp/mysql-dumps               #path(SAB)
+set DATEVAR=`date +%FT%T`                                    #date
+set FILENAME = $DUMP_PATH/mysql-ssp-ipp001-$DATEVAR.dump.bz
+mysqldump --compress -hippdb01 -uipp -pipp --flush-logs --single-transaction ssp | bzip2 > $FILENAME
+if ($status != 0) then
+   /bin/mail -s "Warning ssp dump failed" $EMAILTO < msg
 endif
 
Index: branches/meh_branches/ppstack_test/tools/neb-reverse
===================================================================
--- branches/meh_branches/ppstack_test/tools/neb-reverse	(revision 32161)
+++ branches/meh_branches/ppstack_test/tools/neb-reverse	(revision 33415)
@@ -19,4 +19,5 @@
     # Convert colons to slashes.
     $neb_key =~ s%:%/%g;
+    $neb_key =~ s%.fz$%%;
     print "$neb_key\n";
 }
Index: branches/meh_branches/ppstack_test/tools/refstacktool
===================================================================
--- branches/meh_branches/ppstack_test/tools/refstacktool	(revision 32161)
+++ branches/meh_branches/ppstack_test/tools/refstacktool	(revision 33415)
@@ -103,9 +103,21 @@
 my $n_exps = 0;
 my $exp_id_last = 0;
+
+my %exp_hash;
 while (my $exp = $stmt->fetchrow_hashref()) {
     # avoid exposures processed multiple times
     my $exp_id = $exp->{exp_id};
-    next if $exp_id_last == $exp_id;
+
+    if ($exp_hash{$exp_id}) {
+#        print "skipping duplicate for $exp_id\n";
+        next;
+    }
+#    This doesn't work since we are ordered by fwhm_major
+#    if ($exp_id_last == $exp_id) {
+#        print "skipping duplicate for $exp_id\n";
+#    }
     $exp_id_last = $exp_id;
+
+    $exp_hash{$exp_id} = 1;
 
     $n_exps++;
Index: branches/meh_branches/ppstack_test/tools/regpeek.pl
===================================================================
--- branches/meh_branches/ppstack_test/tools/regpeek.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/tools/regpeek.pl	(revision 33415)
@@ -43,5 +43,9 @@
 foreach my $row (@{ $data }) {
     my ($exp_name,$class_id,$bt_state,$data_state,$uri,$exp_id) = @{ $row };
-    print "$exp_name  $class_id $bt_state $data_state $uri\n";
+    print "$exp_name  $class_id $bt_state $data_state $uri\t";
+    if (($bt_state > -10)&&($data_state eq 'check_burntool')) {
+	print "#??? regtool -updateprocessedimfile -exp_id $exp_id -class_id $class_id -set_state pending_burntool -dbname gpc1";
+    }
+    print "\n";
     if ($logs) {
 	$reg_log = $uri;
Index: branches/meh_branches/ppstack_test/tools/repair_bad_instance
===================================================================
--- branches/meh_branches/ppstack_test/tools/repair_bad_instance	(revision 33415)
+++ branches/meh_branches/ppstack_test/tools/repair_bad_instance	(revision 33415)
@@ -0,0 +1,160 @@
+#!/bin/env perl
+
+use strict;
+use warnings;
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+use DBI;
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Config 1.01 qw( :standard );
+use File::Basename;
+
+my $dbname = 'gpc1';
+
+
+my ( $exp_id,
+     $class_id,
+     $repair_lost,
+     $go,
+     );
+
+# the char to the right of the bar may be used as a single - alias for the longer name
+# for example --input and -i are equivalent
+GetOptions(
+	   'exp_id|e=s'     => \$exp_id,
+	   'class_id|c=s'   => \$class_id,
+	   'repair|r'       => \$go,
+           'repair-lost|l'  => \$repair_lost,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+# pod2usage( -msg => "Required options: --exp_id --class_id",
+ #           -exitval => 3)
+unless (defined $exp_id and defined $class_id ) {
+    my $prog = basename($0);
+    print STDERR "Usage:\n\t$prog --exp_id <exp_id> --class_id <class_id> [--repair --repair-lost]\n";
+    exit 1;
+}
+
+my $ipprc =  PS::IPP::Config->new();
+my $dbh = getDBHandle();
+
+my $stmt = $dbh->prepare("SELECT ignored, uri from rawImfile where exp_id = ? and class_id = ?") or die $dbh->errstr;
+
+$stmt->execute($exp_id, $class_id);
+
+my $file = $stmt->fetchrow_hashref();
+
+if (!$file) {
+    print STDERR "failed to find rawImfile for $exp_id $class_id\n";
+    exit 1;
+}
+my $uri = $file->{uri};
+my $ignored = $file->{ignored};
+
+if (!$uri) {
+    print STDERR "failed to find uri for $exp_id $class_id\n";
+    exit 1;
+}
+
+my $output = `neb-locate -p -a $uri`;
+
+if (!$output) {
+    print STDERR "WARNING failed to locate any instances for $uri\n";
+}
+
+my @files = split "\n", $output;
+
+my $n = scalar @files;
+print "$n instances for $uri:\n";
+my $good_instance;
+my @bad_instances;
+foreach my $instance (@files) {
+    if (check_file($instance)) {
+        print "  $instance is ok\n";
+        $good_instance = $instance;
+    } else {
+        print "  $instance has problems\n";
+        push @bad_instances, $instance;
+    }
+}
+
+my $fixed = 0;
+if ($good_instance) {
+    foreach my $bad (@bad_instances) {
+        my $cmd =  "cp   $good_instance $bad";
+        print "    $cmd\n";
+        if ($go) {
+            system $cmd;
+            $fixed = 1;
+        }
+    }
+} else {
+    my $cmd = "regtool -dbname $dbname -updateprocessedimfile -set_ignored -exp_id $exp_id -class_id $class_id\n";
+    print "No good instances of $uri found. ";
+    if ($repair_lost) {
+        print "Executing: ";
+    } else {
+        print "To fix run:\n";
+    }
+    print "\t$cmd\n";
+    if ($repair_lost) {
+        $fixed = 1;
+        system $cmd;
+    }
+}
+
+if ($fixed) {
+    my $cmd =  "chiptool -dbname $dbname -revertprocessedimfile -exp_id $exp_id";
+    print "$cmd\n";
+    system $cmd;
+}
+
+
+sub check_file {
+    my $file = shift;
+
+    my $command = "fhead $file | grep NEXTEND | awk '{print \$3}'";
+    my $nextend = `$command`;
+    chomp $nextend;
+    if (!$nextend) {
+        return 0;
+    }
+    print "\t$file: $nextend extensions\n";
+
+    $command = 'fhead -x ' . ($nextend - 1) . " $file | grep EXTNAME | awk '{print \$3}'";
+    my $extname = `$command`;
+    chomp $extname;
+    if (!$extname) {
+        print "\tno EXTNAME found in extension $nextend\n";
+        return 0;
+    }
+
+    return 1;
+}
+
+
+
+
+
+sub getDBHandle {
+    my $dbserver = metadataLookupStr($ipprc->{_siteConfig}, "DBSERVER");
+    my $dbuser = metadataLookupStr($ipprc->{_siteConfig}, "DBUSER");
+    my $dbpassword = metadataLookupStr($ipprc->{_siteConfig}, "DBPASSWORD");
+    if (!$dbname) {
+        $dbname = metadataLookupStr($ipprc->{_siteConfig}, "DBNAME");
+    }
+
+    die "database configuration set up" unless defined($dbserver) and defined($dbuser)
+        and defined($dbpassword) and defined($dbname);
+
+    my $dsn = "DBI:mysql:host=$dbserver;database=$dbname";
+
+    my $dbh = DBI->connect($dsn, $dbuser, $dbpassword) 
+        or die "Cannot connect to database.\n";
+
+    return $dbh;
+}
+
Index: branches/meh_branches/ppstack_test/tools/runbgchip.pl
===================================================================
--- branches/meh_branches/ppstack_test/tools/runbgchip.pl	(revision 33415)
+++ branches/meh_branches/ppstack_test/tools/runbgchip.pl	(revision 33415)
@@ -0,0 +1,180 @@
+#!/bin/env perl
+
+use strict;
+use warnings;
+use DBI;
+use IPC::Cmd 0.36 qw( can_run run );
+use PS::IPP::Metadata::Config;
+use PS::IPP::Config 1.01 qw( :standard );
+
+use Getopt::Long qw( GetOptions :config auto_help auto_version gnu_getopt );
+use Pod::Usage qw( pod2usage );
+use File::Basename;
+use IO::File;
+
+my ($first, $last, $stage, $alt_workdir, $no_verbose);
+
+my $dbname = "gpc1";
+my ($chip_bg_id, $class_id, $threads, $update, $redirect, $pretend, $save_temps);
+
+my $zaplog;
+
+GetOptions(
+    'chip_bg_id=i'         => \$chip_bg_id,
+    'class_id=s'      => \$class_id,
+    'threads=i'         => \$threads,
+    'pretend'           => \$pretend,
+    'zaplog'            => \$zaplog,
+    'redirect-output'   => \$redirect,
+    'update'            => \$update,
+    'dbname=s'          => \$dbname,
+    'no-verbose'        => \$no_verbose, 
+    'save-temps'        => \$save_temps,
+) or pod2usage( 2 );
+
+pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
+pod2usage( -msg => "--chip_bg_id and --class_id are required", -exitval => 2 )
+    if !$chip_bg_id or !$class_id;
+
+my $ipprc =  PS::IPP::Config->new();
+my $dbh = getDBHandle();
+
+my $query1 = "SELECT chipBackgroundImfile.path_base, camera, exp_id, chipBackgroundImfile.fault, chipBackgroundRun.reduction, chipBackgroundRun.state";
+$query1 .= " FROM chipBackgroundRun join chipBackgroundImfile using(chip_bg_id) JOIN chipRun using(chip_id) JOIN rawExp using(exp_id) WHERE chip_bg_id = $chip_bg_id AND class_id = '$class_id'";
+
+my $stmt1 = $dbh->prepare($query1);
+$stmt1->execute();
+my $results = $stmt1->fetchrow_hashref();
+die "query returned no results\n" if !$results;
+my $path_base = $results->{path_base};
+my $reduction = $results->{reduction};
+my $camera = $results->{camera};
+my $state = $results->{state};
+my $fault = $results->{fault};
+my $deburned = $results->{deburned};
+my $exp_id = $results->{exp_id};
+
+die "cannot update database for a chip that is not faulted\n" if $update and !$fault;
+
+die "path_base not found\n" if !$path_base;
+die "camera not found\n" if !$camera;
+die "state not found\n" if !$state;
+
+my  $run_state;
+if ($state eq 'full' or $state eq 'new') {
+    $run_state = 'new';
+} elsif ($state eq 'update') {
+    $run_state = 'update';
+} else {
+    die "unexpected chipRun.state found: $state\n";
+}
+
+my $command = "background_chip.pl --chip_bg_id $chip_bg_id --class_id $class_id --camera $camera --outroot $path_base" ;
+
+$command .= " --reduction $reduction" if $reduction and ($reduction ne "NULL");
+$command .= " --redirect-output" if $redirect;
+$command .= " --save-temps" if $save_temps;
+$command .= " --no-update" unless $update;
+$command .= " --verbose" unless $no_verbose;
+$command .= " --dbname $dbname" if $dbname;
+
+if ($update) {
+    my $revert_command = "bgtool -revertchip -chip_bg_id $chip_bg_id -class_id $class_id -dbname $dbname";
+    if ($pretend) {
+        print "skipping $revert_command\n";
+    } else {
+        my $rc = system $revert_command;
+        if ($rc != 0) {
+            my $status = $rc >> 8;
+            print STDERR "$revert_command failed: $rc $status\n";
+            exit $status;
+        }
+    }
+}
+print "command to process this chip\n";
+print "$command\n";
+
+exit 0 if $pretend;
+
+exit system $command;
+
+
+sub getDBHandle {
+    my $dbserver = metadataLookupStr($ipprc->{_siteConfig}, "DBSERVER");
+    my $dbuser = metadataLookupStr($ipprc->{_siteConfig}, "DBUSER");
+    my $dbpassword = metadataLookupStr($ipprc->{_siteConfig}, "DBPASSWORD");
+    if (!$dbname) {
+        $dbname = metadataLookupStr($ipprc->{_siteConfig}, "DBNAME");
+    }
+
+    die "database configuration set up" unless defined($dbserver) and defined($dbuser)
+        and defined($dbpassword) and defined($dbname);
+
+    my $dsn = "DBI:mysql:host=$dbserver;database=$dbname";
+
+    my $dbh = DBI->connect($dsn, $dbuser, $dbpassword) 
+        or die "Cannot connect to database.\n";
+
+    return $dbh;
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+runchipimfile.pl - gather the parameters for and execute chip_imfile.pl
+
+=head1 SYNOPSIS
+    
+    XXX: pod TODO
+
+    perl runwarpskyfile.pl --warp_id <warp_id> --skycell_id <skycell_id> [--threads <num_threads>] [--update] [--redirect-output] [--pretend] [--dbname <dbname>]
+
+Query the database for the results of a warp skycell and rerun the processing, optionally reverting a faulted
+run.
+
+=over 4
+
+=item * --warp_id <warp_id>
+
+The id of the warpSkyfile run to process.
+
+=item * --skycell_id <skycell_id>
+
+The skycell_id of the warpSkyfile run to process.
+
+=item * --threads <num_threads>
+
+The number of threads to use. Default 1.
+Optional.
+
+
+=item * --update
+
+Revert a faulted skycell before processing. If the skycell is not faulted no processing is done.
+WARNING: insure that the standard science procesing either has warp stage turned off or the
+label of this warpRun ommited from the list of labels otherwise it may attempt to processes this run at the same time.
+Optional.
+
+=item *  --redirect-output
+
+Send the output of the command to the logfile. (This is the default if --update is supplied).
+Optional.
+
+=item * --pretend
+
+Just print the commands that would be executed, but do not run them.
+Optional.
+
+=item * --dbname <dbname>
+
+Name of the IPP databse to query. Default is 'gpc1'.
+Optional.
+
+
+=head1 SEE ALSO
+L<runchipimfile.pl>, L<runcameraexp.pl>, L<rundiffskyfile.pl>
+
+=cut
Index: branches/meh_branches/ppstack_test/tools/runchipimfile.pl
===================================================================
--- branches/meh_branches/ppstack_test/tools/runchipimfile.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/tools/runchipimfile.pl	(revision 33415)
@@ -16,5 +16,5 @@
 
 my $dbname = "gpc1";
-my ($chip_id, $class_id, $threads, $update, $redirect, $pretend, $save_temps);
+my ($chip_id, $class_id, $threads, $update, $redirect, $pretend, $save_temps, $outdir);
 
 my $zaplog;
@@ -27,4 +27,5 @@
     'zaplog'            => \$zaplog,
     'redirect-output'   => \$redirect,
+    'outdir=s'          => \$outdir,
     'update'            => \$update,
     'dbname=s'          => \$dbname,
@@ -73,4 +74,9 @@
 } else {
     die "unexpected chipRun.state found: $state\n";
+}
+
+if ($outdir) {
+    my $base = basename($path_base);
+    $path_base = "$outdir/$base";
 }
 
Index: branches/meh_branches/ppstack_test/tools/rundiffskycell.pl
===================================================================
--- branches/meh_branches/ppstack_test/tools/rundiffskycell.pl	(revision 32161)
+++ branches/meh_branches/ppstack_test/tools/rundiffskycell.pl	(revision 33415)
@@ -25,5 +25,5 @@
 
 my $dbname = "gpc1";
-my ($diff_id, $skycell_id, $threads, $update, $redirect);
+my ($diff_id, $skycell_id, $threads, $update, $no_redirect);
 my $zaplog;
 
@@ -32,5 +32,5 @@
     'skycell_id=s'      => \$skycell_id,
     'threads=i'         => \$threads,
-    'redirect-output'   => \$redirect,
+    'no-redirect-output'  => \$no_redirect,
     'zaplog'            => \$zaplog,
     'update'            => \$update,
@@ -44,6 +44,4 @@
     if !$diff_id or !$skycell_id;
 
-# if we're asked to update redirect the output
-$redirect = 1 if $update;
 
 my $ipprc =  PS::IPP::Config->new();
@@ -69,5 +67,5 @@
 my $command = "diff_skycell.pl --diff_id $diff_id --skycell_id $skycell_id --diff_skyfile_id $diff_skyfile_id --outroot $path_base --run-state new";
 
-$command .= " --redirect-output" if $redirect;
+$command .= " --redirect-output" unless $no_redirect;
 $command .= " --no-update" unless $update;
 $command .= " --verbose" unless $no_verbose;
@@ -80,4 +78,6 @@
 print "command to process this skycell\n";
 print "$command\n";
+
+exit 0 if $pretend;
 
 if ($update) {
@@ -95,8 +95,4 @@
 }
 
-print "command to process this skycell\n";
-print "$command\n";
-
-exit 0 if $pretend;
 
 exit system $command;
@@ -133,5 +129,5 @@
 =head1 SYNOPSIS
     
-    perl rundiffskyfile.pl --diff_id <diff_id> --skycell_id <skycell_id> [--threads <num_threads>] [--update] [--redirect-output] [--pretend] [--dbname <dbname>]
+    perl rundiffskyfile.pl --diff_id <diff_id> --skycell_id <skycell_id> [--threads <num_threads>] [--update] [--no-redirect-output] [--pretend] [--dbname <dbname>]
 
 Query the database for the results of a diff skycell and rerun the processing, optionally reverting a faulted
@@ -161,7 +157,7 @@
 Optional.
 
-=item *  --redirect-output
+=item *  --no-redirect-output
 
-Send the output of the command to the logfile. (This is the default if --update is supplied).
+Don't redirect script output to logfile. 
 Optional.
 
